[ Web Proxy ]
URL:
Viewing: https://raw.githubusercontent.com/EthanAnro/NetCoreServer/master/source/NetCoreServer/HttpClient.cs [Back]  [Original]

using System;
using System.Net;
using System.Threading;
using System.Threading.Tasks;

namespace NetCoreServer
{
    /// 
    /// HTTP client is used to communicate with HTTP Web server. It allows to send GET, POST, PUT, DELETE requests and receive HTTP result.
    /// 
    /// Thread-safe.
    public class HttpClient : TcpClient
    {
        /// 
        /// Initialize HTTP client with a given IP address and port number
        /// 
        /// IP address
        /// Port number
        public HttpClient(IPAddress address, int port) : base(address, port) { Request = new HttpRequest(); Response = new HttpResponse(); }
        /// 
        /// Initialize HTTP client with a given IP address and port number
        /// 
        /// IP address
        /// Port number
        public HttpClient(string address, int port) : base(address, port) { Request = new HttpRequest(); Response = new HttpResponse(); }
        /// 
        /// Initialize HTTP client with a given DNS endpoint
        /// 
        /// DNS endpoint
        public HttpClient(DnsEndPoint endpoint) : base(endpoint) { Request = new HttpRequest(); Response = new HttpResponse(); }
        /// 
        /// Initialize HTTP client with a given IP endpoint
        /// 
        /// IP endpoint
        public HttpClient(IPEndPoint endpoint) : base(endpoint) { Request = new HttpRequest(); Response = new HttpResponse(); }

        /// 
        /// Get the HTTP request
        /// 
        public HttpRequest Request { get; protected set; }

        /// 
        /// Get the HTTP response
        /// 
        protected HttpResponse Response { get; set; }

        #region Send request / Send request body

        /// 
        /// Send the current HTTP request (synchronous)
        /// 
        /// Size of sent data
        public long SendRequest() => SendRequest(Request);
        /// 
        /// Send the HTTP request (synchronous)
        /// 
        /// HTTP request
        /// Size of sent data
        public long SendRequest(HttpRequest request) => Send(request.Cache.Data, request.Cache.Offset, request.Cache.Size);

        /// 
        /// Send the HTTP request body (synchronous)
        /// 
        /// HTTP request body
        /// Size of sent data
        public long SendRequestBody(string body) => Send(body);
        /// 
        /// Send the HTTP request body (synchronous)
        /// 
        /// HTTP request body as a span of characters
        /// Size of sent data
        public long SendRequestBody(ReadOnlySpan body) => Send(body);
        /// 
        /// Send the HTTP request body (synchronous)
        /// 
        /// HTTP request body buffer
        /// Size of sent data
        public long SendRequestBody(byte[] buffer) => Send(buffer);
        /// 
        /// Send the HTTP request body (synchronous)
        /// 
        /// HTTP request body buffer
        /// HTTP request body buffer offset
        /// HTTP request body size
        /// Size of sent data
        public long SendRequestBody(byte[] buffer, long offset, long size) => Send(buffer, offset, size);
        /// 
        /// Send the HTTP request body (synchronous)
        /// 
        /// HTTP request body buffer as a span of bytes
        /// Size of sent data
        public long SendRequestBody(ReadOnlySpan buffer) => Send(buffer);

        /// 
        /// Send the current HTTP request (asynchronous)
        /// 
        /// 'true' if the current HTTP request was successfully sent, 'false' if the session is not connected
        public bool SendRequestAsync() => SendRequestAsync(Request);
        /// 
        /// Send the HTTP request (asynchronous)
        /// 
        /// HTTP request
        /// 'true' if the current HTTP request was successfully sent, 'false' if the session is not connected
        public bool SendRequestAsync(HttpRequest request) => SendAsync(request.Cache.Data, request.Cache.Offset, request.Cache.Size);

        /// 
        /// Send the HTTP request body (asynchronous)
        /// 
        /// HTTP request body
        /// 'true' if the HTTP request body was successfully sent, 'false' if the session is not connected
        public bool SendRequestBodyAsync(string body) => SendAsync(body);
        /// 
        /// Send the HTTP request body (asynchronous)
        /// 
        /// HTTP request body as a span of characters
        /// 'true' if the HTTP request body was successfully sent, 'false' if the session is not connected
        public bool SendRequestBodyAsync(ReadOnlySpan body) => SendAsync(body);
        /// 
        /// Send the HTTP request body (asynchronous)
        /// 
        /// HTTP request body buffer
        /// 'true' if the HTTP request body was successfully sent, 'false' if the session is not connected
        public bool SendRequestBodyAsync(byte[] buffer) => SendAsync(buffer);
        /// 
        /// Send the HTTP request body (asynchronous)
        /// 
        /// HTTP request body buffer
        /// HTTP request body buffer offset
        /// HTTP request body size
        /// 'true' if the HTTP request body was successfully sent, 'false' if the session is not connected
        public bool SendRequestBodyAsync(byte[] buffer, long offset, long size) => SendAsync(buffer, offset, size);
        /// 
        /// Send the HTTP request body (asynchronous)
        /// 
        /// HTTP request body buffer as a span of bytes
        /// 'true' if the HTTP request body was successfully sent, 'false' if the session is not connected
        public bool SendRequestBodyAsync(ReadOnlySpan buffer) => SendAsync(buffer);

        #endregion

        #region Session handlers

        protected override void OnReceived(byte[] buffer, long offset, long size)
        {
            // Receive HTTP response header
            if (Response.IsPendingHeader())
            {
                if (Response.ReceiveHeader(buffer, (int)offset, (int)size))
                    OnReceivedResponseHeader(Response);

                size = 0;
            }

            // Check for HTTP response error
            if (Response.IsErrorSet)
            {
                OnReceivedResponseError(Response, "Invalid HTTP response!");
                Response.Clear();
                Disconnect();
                return;
            }

            // Receive HTTP response body
            if (Response.ReceiveBody(buffer, (int)offset, (int)size))
            {
                OnReceivedResponse(Response);
                Response.Clear();
                return;
            }

            // Check for HTTP response error
            if (Response.IsErrorSet)
            {
                OnReceivedResponseError(Response, "Invalid HTTP response!");
                Response.Clear();
                Disconnect();
                return;
            }
        }

        protected override void OnDisconnected()
        {
            // Receive HTTP response body
            if (Response.IsPendingBody())
            {
                OnReceivedResponse(Response);
                Response.Clear();
                return;
            }
        }

        /// 
        /// Handle HTTP response header received notification
        /// 
        /// Notification is called when HTTP response header was received from the server.
        /// HTTP request
        protected virtual void OnReceivedResponseHeader(HttpResponse response) {}

        /// 
        /// Handle HTTP response received notification
        /// 
        /// Notification is called when HTTP response was received from the server.
        /// HTTP response
        protected virtual void OnReceivedResponse(HttpResponse response) {}

        /// 
        /// Handle HTTP response error notification
        /// 
        /// Notification is called when HTTP response error was received from the server.
        /// HTTP response
        /// HTTP response error
        protected virtual void OnReceivedResponseError(HttpResponse response, string error) {}

        #endregion
    }

    /// 
    /// HTTP extended client make requests to HTTP Web server with returning Task as a synchronization primitive.
    /// 
    /// Thread-safe.
    public class HttpClientEx : HttpClient
    {
        /// 
        /// Initialize HTTP client with a given IP address and port number
        /// 
        /// IP address
        /// Port number
        public HttpClientEx(IPAddress address, int port) : base(address, port) {}
        /// 
        /// Initialize HTTP client with a given IP address and port number
        /// 
        /// IP address
        /// Port number
        public HttpClientEx(string address, int port) : base(address, port) {}
        /// 
        /// Initialize HTTP client with a given DNS endpoint
        /// 
        /// DNS endpoint
        public HttpClientEx(DnsEndPoint endpoint) : base(endpoint) {}
        /// 
        /// Initialize HTTP client with a given IP endpoint
        /// 
        /// IP endpoint
        public HttpClientEx(IPEndPoint endpoint) : base(endpoint) {}

        #region Send request

        /// 
        /// Send current HTTP request
        /// 
        /// Current HTTP request timeout (default is 1 minute)
        /// HTTP request Task
        public Task SendRequest(TimeSpan? timeout = null) => SendRequest(Request, timeout);
        /// 
        /// Send HTTP request
        /// 
        /// HTTP request
        /// HTTP request timeout (default is 1 minute)
        /// HTTP request Task
        public Task SendRequest(HttpRequest request, TimeSpan? timeout = null)
        {
            timeout ??= TimeSpan.FromMinutes(1);

            _tcs = new TaskCompletionSource();
            Request = request;

            // Check if the HTTP request is valid
            if (Request.IsEmpty || Request.IsErrorSet)
            {
                SetPromiseError("Invalid HTTP request!");
                return _tcs.Task;
            }

            if (!IsConnected)
            {
                // Connect to the Web server
                if (!ConnectAsync())
                {
                    SetPromiseError("Connection failed!");
                    return _tcs.Task;
                }
            }
            else
            {
                // Send prepared HTTP request
                if (!SendRequestAsync())
                {
                    SetPromiseError("Failed to send HTTP request!");
                    return _tcs.Task;
                }
            }

            void TimeoutHandler(object state)
            {
                // Disconnect on timeout
                OnReceivedResponseError(Response, "Timeout!");
                Response.Clear();
                DisconnectAsync();
            }

            // Create a new timeout timer
            if (_timer == null)
                _timer = new Timer(TimeoutHandler, null, Timeout.Infinite, Timeout.Infinite);

            // Start the timeout timer
            _timer.Change((int)timeout.Value.TotalMilliseconds, Timeout.Infinite);

            return _tcs.Task;
        }

        /// 
        /// Send HEAD request
        /// 
        /// URL to request
        /// Current HTTP request timeout (default is 1 minute)
        /// HTTP request Task
        public Task SendHeadRequest(string url, TimeSpan? timeout = null) => SendRequest(Request.MakeHeadRequest(url), timeout);
        /// 
        /// Send GET request
        /// 
        /// URL to request
        /// Current HTTP request timeout (default is 1 minute)
        /// HTTP request Task
        public Task SendGetRequest(string url, TimeSpan? timeout = null) => SendRequest(Request.MakeGetRequest(url), timeout);
        /// 
        /// Send POST request
        /// 
        /// URL to request
        /// Content
        /// Current HTTP request timeout (default is 1 minute)
        /// HTTP request Task
        public Task SendPostRequest(string url, string content, TimeSpan? timeout = null) => SendRequest(Request.MakePostRequest(url, content), timeout);
        /// 
        /// Send PUT request
        /// 
        /// URL to request
        /// Content
        /// Current HTTP request timeout (default is 1 minute)
        /// HTTP request Task
        public Task SendPutRequest(string url, string content, TimeSpan? timeout = null) => SendRequest(Request.MakePutRequest(url, content), timeout);
        /// 
        /// Send DELETE request
        /// 
        /// URL to request
        /// Current HTTP request timeout (default is 1 minute)
        /// HTTP request Task
        public Task SendDeleteRequest(string url, TimeSpan? timeout = null) => SendRequest(Request.MakeDeleteRequest(url), timeout);
        /// 
        /// Send OPTIONS request
        /// 
        /// URL to request
        /// Current HTTP request timeout (default is 1 minute)
        /// HTTP request Task
        public Task SendOptionsRequest(string url, TimeSpan? timeout = null) => SendRequest(Request.MakeOptionsRequest(url), timeout);
        /// 
        /// Send TRACE request
        /// 
        /// URL to request
        /// Current HTTP request timeout (default is 1 minute)
        /// HTTP request Task
        public Task SendTraceRequest(string url, TimeSpan? timeout = null) => SendRequest(Request.MakeTraceRequest(url), timeout);

        #endregion

        #region Session handlers

        protected override void OnConnected()
        {
            // Send prepared HTTP request on connect
            if (!Request.IsEmpty && !Request.IsErrorSet)
                if (!SendRequestAsync())
                    SetPromiseError("Failed to send HTTP request!");
        }

        protected override void OnDisconnected()
        {
            // Cancel timeout check timer
            _timer?.Change(Timeout.Infinite, Timeout.Infinite);

            base.OnDisconnected();
        }

        protected override void OnReceivedResponse(HttpResponse response)
        {
            // Cancel timeout check timer
            _timer?.Change(Timeout.Infinite, Timeout.Infinite);

            SetPromiseValue(response);
        }

        protected override void OnReceivedResponseError(HttpResponse response, string error)
        {
            // Cancel timeout check timer
            _timer?.Change(Timeout.Infinite, Timeout.Infinite);

            SetPromiseError(error);
        }

        #endregion

        private TaskCompletionSource _tcs = new TaskCompletionSource();
        private Timer _timer;

        private void SetPromiseValue(HttpResponse response)
        {
            Response = new HttpResponse();
            _tcs.SetResult(response);
            Request.Clear();
        }

        private void SetPromiseError(string error)
        {
            _tcs.SetException(new Exception(error));
            Request.Clear();
        }

        #region IDisposable implementation

        // Disposed flag.
        private bool _disposed;

        protected override void Dispose(bool disposingManagedResources)
        {
            if (!_disposed)
            {
                if (disposingManagedResources)
                {
                    // Dispose managed resources here...
                    _timer?.Dispose();
                }

                // Dispose unmanaged resources here...

                // Set large fields to null here...

                // Mark as disposed.
                _disposed = true;
            }

            // Call Dispose in the base class.
            base.Dispose(disposingManagedResources);
        }

        // The derived class does not have a Finalize method
        // or a Dispose method without parameters because it inherits
        // them from the base class.

        #endregion
    }
}

Web Proxy Viewer  |  New URL  |  Original Page