Exemple #1
0
        ///// <summary>
        ///// Create a new HTTP request.
        ///// </summary>
        ///// <param name="HTTPMethod">A HTTP method.</param>
        ///// <param name="URI">An URL path.</param>
        ///// <param name="BuilderAction">A delegate to configure the new HTTP request builder.</param>
        ///// <returns>A new HTTPRequest object.</returns>
        //public HTTPRequestBuilder CreateRequest(String                      HTTPMethod,
        //                                        String                      URI        = "/",
        //                                        Action<HTTPRequestBuilder>  BuilderAction  = null)
        //{

        //    var Builder = new HTTPRequestBuilder(this) {
        //        HTTPMethod  = new HTTPMethod(HTTPMethod),
        //        URI         = URI
        //    };

        //    BuilderAction.FailSafeInvoke(Builder);

        //    return Builder;

        //}

        #endregion


        #region Execute(HTTPRequestDelegate, RequestLogDelegate = null, ResponseLogDelegate = null, Timeout = null, CancellationToken = null)

        /// <summary>
        /// Execute the given HTTP request and return its result.
        /// </summary>
        /// <param name="HTTPRequestDelegate">A delegate for producing a HTTP request for a given HTTP client.</param>
        /// <param name="RequestLogDelegate">A delegate for logging the HTTP request.</param>
        /// <param name="ResponseLogDelegate">A delegate for logging the HTTP request/response.</param>
        /// <param name="Timeout">An optional timeout.</param>
        /// <param name="CancellationToken">A cancellation token.</param>
        public async Task <HTTPResponse> Execute(Func <HTTPClient, HTTPRequest> HTTPRequestDelegate,
                                                 ClientRequestLogHandler RequestLogDelegate   = null,
                                                 ClientResponseLogHandler ResponseLogDelegate = null,
                                                 TimeSpan?Timeout = null,
                                                 CancellationToken?CancellationToken = null)
        {
            #region Initial checks

            if (HTTPRequestDelegate == null)
            {
                throw new ArgumentNullException(nameof(HTTPRequestDelegate), "The given delegate must not be null!");
            }

            #endregion

            return(await Execute(HTTPRequestDelegate(this), RequestLogDelegate, ResponseLogDelegate, Timeout, CancellationToken));
        }
Exemple #2
0
        /// <summary>
        /// Execute the given HTTP request and return its result.
        /// </summary>
        /// <param name="Request">A HTTP request.</param>
        /// <param name="RequestLogDelegate">A delegate for logging the HTTP request.</param>
        /// <param name="ResponseLogDelegate">A delegate for logging the HTTP request/response.</param>
        /// <param name="Timeout">An optional timeout.</param>
        /// <param name="CancellationToken">A cancellation token.</param>
        public async Task<HTTPResponse> Execute(HTTPRequest               Request,
                                                ClientRequestLogHandler   RequestLogDelegate   = null,
                                                ClientResponseLogHandler  ResponseLogDelegate  = null,
                                                TimeSpan?                 Timeout              = null,
                                                CancellationToken?        CancellationToken    = null)
        {

            #region Call the optional HTTP request log delegate

            try
            {

                RequestLogDelegate?.Invoke(DateTime.Now, this, Request);

            }
            catch (Exception e)
            {
                e.Log(nameof(HTTPClient) + "." + nameof(RequestLogDelegate));
            }

            #endregion

            var task = Task<HTTPResponse>.Factory.StartNew(() => {

                HTTPResponse Response = null;

                try
                {

                    if (Environment.MachineName.Contains("QUAD2QUANTOR") && Request.URI.Contains("eRoaming"))
                        throw new Exception("Catch me if you can!");

                    Thread.CurrentThread.Priority = ThreadPriority.BelowNormal;

                    #region Data

                    var HTTPHeaderBytes = new Byte[0];
                    var sw = new Stopwatch();

                    if (!Timeout.HasValue)
                        Timeout = TimeSpan.FromSeconds(60);

                    #endregion

                    #region Create TCP connection (possibly also do DNS lookups)

                    if (TCPClient == null)
                    {

                        System.Net.IPEndPoint _FinalIPEndPoint          = null;
                        IIPAddress            _ResolvedRemoteIPAddress  = null;

                        if (RemoteIPAddress == null)
                        {

                            if (Hostname.Trim() == "127.0.0.1")
                                _ResolvedRemoteIPAddress = IPv4Address.Localhost;

                            else
                            {

                                var RegExpr = new Regex(@"\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b");

                                if (RegExpr.IsMatch(Hostname))
                                    _ResolvedRemoteIPAddress = IPv4Address.Parse(Hostname);

                            }

                            #region DNS lookup...

                            if (_ResolvedRemoteIPAddress == null)
                            {

                                try
                                {

                                    var IPv4AddressTask = DNSClient.
                                                              Query<A>(Hostname).
                                                                  ContinueWith(QueryTask => QueryTask.Result.
                                                                                                Select(ARecord => ARecord.IPv4Address).
                                                                                                FirstOrDefault());

                                    IPv4AddressTask.Wait();

                                    _ResolvedRemoteIPAddress = IPv4AddressTask.Result;

                                }
                                catch (Exception e)
                                {
                                    Debug.WriteLine("[" + DateTime.Now + "] " + e.Message);
                                }

                            }

                            #endregion

                        }

                        else
                            _ResolvedRemoteIPAddress = RemoteIPAddress;

                        _FinalIPEndPoint = new System.Net.IPEndPoint(new System.Net.IPAddress(_ResolvedRemoteIPAddress.GetBytes()), RemotePort.ToInt32());

                        sw.Start();

                        TCPClient = new TcpClient();
                        TCPClient.Connect(_FinalIPEndPoint);
                        TCPClient.ReceiveTimeout = (Int32) Timeout.Value.TotalMilliseconds;

                    }

                    #endregion

                    #region Create (Crypto-)Stream

                    TCPStream = TCPClient.GetStream();
                    TCPStream.ReadTimeout = (Int32)Timeout.Value.TotalMilliseconds;

                    TLSStream = RemoteCertificateValidator != null
                                     ? new SslStream(TCPStream,
                                                     false,
                                                     RemoteCertificateValidator)
                                     //    ClientCertificateSelector,
                                     //EncryptionPolicy.RequireEncryption)
                                     : null;

                    if (TLSStream != null)
                        TLSStream.ReadTimeout = (Int32)Timeout.Value.TotalMilliseconds;

                    HTTPStream = null;

                    if (RemoteCertificateValidator != null)
                    {
                        HTTPStream = TLSStream;
                        TLSStream.AuthenticateAsClient(Hostname);//, new X509CertificateCollection(new X509Certificate[] { ClientCert }), SslProtocols.Default, false);
                    }

                    else
                        HTTPStream = TCPStream;

                    HTTPStream.ReadTimeout = (Int32)Timeout.Value.TotalMilliseconds;

                    #endregion

                    #region Send Request

                    HTTPStream.Write(String.Concat(Request.EntireRequestHeader,
                                                   Environment.NewLine,
                                                   Environment.NewLine).
                                     ToUTF8Bytes());

                    var RequestBodyLength = Request.HTTPBody == null
                                                ? Request.ContentLength.HasValue ? (Int32) Request.ContentLength.Value : 0
                                                : Request.ContentLength.HasValue ? Math.Min((Int32) Request.ContentLength.Value, Request.HTTPBody.Length) : Request.HTTPBody.Length;

                    if (RequestBodyLength > 0)
                        HTTPStream.Write(Request.HTTPBody, 0, RequestBodyLength);

                    var _MemoryStream = new MemoryStream();
                    var _Buffer = new Byte[10485760]; // 10 MBytes, a smaller value leads to read errors!

                    #endregion

                    #region Wait timeout for the server to react!

                    //Debug.WriteLine("[" + DateTime.Now + "] HTTPClient timeout: " + Timeout.Value.ToString());

                    while (!TCPStream.DataAvailable)
                    {

                        if (sw.ElapsedMilliseconds >= Timeout.Value.TotalMilliseconds)
                        {
                            TCPClient.Close();
                            throw new Exception("[" + DateTime.Now + "] Could not read from the TCP stream for " + sw.ElapsedMilliseconds + "ms!");
                        }

                        Thread.Sleep(1);

                    }

                    //Debug.WriteLine("[" + DateTime.Now + "] HTTPClient (" + TCPClient.Client.LocalEndPoint.ToString() + " -> " + RemoteSocket.ToString() + ") got first response after " + sw.ElapsedMilliseconds + "ms!");

                    #endregion

                    #region Read the entire HTTP header, and maybe some of the HTTP body

                    var CurrentDataLength = 0;

                    do
                    {

                        #region When data available, write it to the buffer...

                        while (TCPStream.DataAvailable)
                        {

                            CurrentDataLength = HTTPStream.Read(_Buffer, 0, _Buffer.Length);

                            if (CurrentDataLength > -1)
                            {
                                _MemoryStream.Write(_Buffer, 0, CurrentDataLength);
        //                        Debug.WriteLine("[" + DateTime.Now + "] Read " + CurrentDataLength + " bytes from HTTP connection (" + TCPClient.Client.LocalEndPoint.ToString() + " -> " + RemoteSocket.ToString() + ") (" + sw.ElapsedMilliseconds + "ms)!");
                            }

                        }

                        #endregion

                        #region Check if the entire HTTP header was already read into the buffer

                        if (_MemoryStream.Length > 4)
                        {

                            var MemoryCopy = _MemoryStream.ToArray();

                            for (var pos = 3; pos < MemoryCopy.Length; pos++)
                            {

                                if (MemoryCopy[pos    ] == 0x0a &&
                                    MemoryCopy[pos - 1] == 0x0d &&
                                    MemoryCopy[pos - 2] == 0x0a &&
                                    MemoryCopy[pos - 3] == 0x0d)
                                {
                                    Array.Resize(ref HTTPHeaderBytes, pos - 3);
                                    Array.Copy(MemoryCopy, 0, HTTPHeaderBytes, 0, pos - 3);
                                    break;
                                }

                            }

                            //if (HTTPHeaderBytes.Length > 0)
                            //    Debug.WriteLine("[" + DateTime.Now + "] End of (" + TCPClient.Client.LocalEndPoint.ToString() + " -> " + RemoteSocket.ToString() + ") HTTP header at " + HTTPHeaderBytes.Length + " bytes (" + sw.ElapsedMilliseconds + "ms)!");

                        }

                        #endregion

                        Thread.Sleep(1);

                    }
                    // Note: Delayed parts of the HTTP body may not be read into the buffer
                    //       => Must be read later!
                    while (TCPStream.DataAvailable ||
                           ((sw.ElapsedMilliseconds < HTTPStream.ReadTimeout) && HTTPHeaderBytes.Length == 0));

                    //Debug.WriteLine("[" + DateTime.Now + "] Finally read " + _MemoryStream.Length + " bytes of HTTP client (" + TCPClient.Client.LocalEndPoint.ToString() + " -> " + RemoteSocket.ToString() + ") data (" + sw.ElapsedMilliseconds + "ms)!");

                    #endregion

                    #region Copy HTTP header data and create HTTP response

                    if (HTTPHeaderBytes.Length == 0)
                        throw new ApplicationException(DateTime.Now + " Could not find the end of the HTTP protocol header!");

                    Response = HTTPResponse.Parse(HTTPHeaderBytes.ToUTF8String(),
                                                       Request);

                    #endregion

                    #region Read 'Content-Length' bytes...

                    // Copy only the number of bytes given within
                    // the HTTP header element 'Content-Length'!
                    if (Response.ContentLength.HasValue && Response.ContentLength.Value > 0)
                    {

                        _MemoryStream.Seek(HTTPHeaderBytes.Length + 4, SeekOrigin.Begin);
                        var _Read = _MemoryStream.Read(_Buffer, 0, _Buffer.Length);
                        var _StillToRead = (Int32)Response.ContentLength.Value - _Read;
                        Response.HTTPBodyStream.Write(_Buffer, 0, _Read);
                        var _CurrentBufferSize = 0;

                        do
                        {

                            while (TCPStream.DataAvailable && _StillToRead > 0)
                            {
                                _CurrentBufferSize = Math.Min(_Buffer.Length, (Int32)_StillToRead);
                                _Read = HTTPStream.Read(_Buffer, 0, _CurrentBufferSize);
                                Response.HTTPBodyStream.Write(_Buffer, 0, _Read);
                                _StillToRead -= _Read;
                            }

                            if (_StillToRead <= 0)
                                break;

                            Thread.Sleep(1);

                        }
                        while (sw.ElapsedMilliseconds < HTTPStream.ReadTimeout);

                        Response.ContentStreamToArray();

                    }

                    #endregion

                    #region ...or read till timeout (e.g. for chunked transport)!

                    else
                    {

                        try
                        {

                            _MemoryStream.Seek(HTTPHeaderBytes.Length + 4, SeekOrigin.Begin);
                            Response.NewContentStream();
                            Response.HTTPBodyStream.Write(_Buffer, 0, _MemoryStream.Read(_Buffer, 0, _Buffer.Length));

                            var Retries = 0;

                            while (Retries < 10)
                            {

                                while (TCPStream.DataAvailable)
                                {
                                    Response.HTTPBodyStream.Write(_Buffer, 0, HTTPStream.Read(_Buffer, 0, _Buffer.Length));
                                    Retries = 0;
                                }

                                Thread.Sleep(10);
                                Retries++;

                            }

                            if (Response.TransferEncoding == "chunked")
                            {

                                //Debug.WriteLine(DateTime.Now + " Chunked encoding detected");

                                var TEContent = ((MemoryStream)Response.HTTPBodyStream).ToArray();
                                var TEString = TEContent.ToUTF8String();
                                var ReadBlockLength = true;
                                var TEMemStram = new MemoryStream();
                                var LastPos = 0;

                                var i = 0;
                                do
                                {

                                    if (i > 2 &&
                                        ReadBlockLength &&
                                        TEContent[i - 1] == '\n' &&
                                        TEContent[i - 2] == '\r')
                                    {

                                        var len = TEContent.ReadTEBlockLength(LastPos, i - LastPos - 2);

                                        //Debug.WriteLine(DateTime.Now + " Chunked encoded block of length " + len + " bytes detected");

                                        if (len == 0)
                                            break;

                                        if (i + len <= TEContent.Length)
                                        {

                                            TEMemStram.Write(TEContent, i, len);
                                            i = i + len;

                                            if (TEContent[i] == 0x0d)
                                                i++;

                                            if (i < TEContent.Length - 1)
                                            {
                                                if (TEContent[i] == 0x0a)
                                                    i++;
                                            }
                                            else
                                            {
                                            }

                                            LastPos = i;

                                            ReadBlockLength = false;

                                        }

                                        else
                                        {
                                            // Reaching this point seems to be an endless loop!
                                            break;

                                        }

                                    }

                                    else
                                    {
                                        ReadBlockLength = true;
                                        i++;
                                    }

                                } while (i < TEContent.Length);

                                Response.ContentStreamToArray(TEMemStram);

                            }

                            else
                                Response.ContentStreamToArray();

                        }

                        catch (Exception e)
                        {
                            Debug.WriteLine(DateTime.Now + " " + e.Message);
                        }

                    }

                    #endregion

                    #region Close connection if requested!

                    if (Response.Connection == null ||
                        Response.Connection == "close")
                    {
                        TCPClient.Close();
                        HTTPStream  = null;
                        TCPClient   = null;
                    }

                    #endregion

                }
                catch (Exception e)
                {

                    #region Create a HTTP response for the exception...

                    while (e.InnerException != null)
                        e = e.InnerException;

                    Response = new HTTPResponseBuilder(Request,
                                                       HTTPStatusCode.BadRequest)
                    {

                        ContentType  = HTTPContentType.JSON_UTF8,
                        Content      = JSONObject.Create(new JProperty("Message",     e.Message),
                                                         new JProperty("StackTrace",  e.StackTrace)).
                                                  ToUTF8Bytes()

                    };

                    #endregion

                }

                #region Call the optional HTTP response log delegate

                try
                {

                    ResponseLogDelegate?.Invoke(DateTime.Now, this, Request, Response);

                }
                catch (Exception e2)
                {
                    e2.Log(nameof(HTTPClient) + "." + nameof(ResponseLogDelegate));
                }

                #endregion


                return Response;


            }, TaskCreationOptions.AttachedToParent);

            return await task;

        }
Exemple #3
0
        ///// <summary>
        ///// Create a new HTTP request.
        ///// </summary>
        ///// <param name="HTTPMethod">A HTTP method.</param>
        ///// <param name="URI">An URL path.</param>
        ///// <param name="BuilderAction">A delegate to configure the new HTTP request builder.</param>
        ///// <returns>A new HTTPRequest object.</returns>
        //public HTTPRequestBuilder CreateRequest(String                      HTTPMethod,
        //                                        String                      URI        = "/",
        //                                        Action<HTTPRequestBuilder>  BuilderAction  = null)
        //{

        //    var Builder = new HTTPRequestBuilder(this) {
        //        HTTPMethod  = new HTTPMethod(HTTPMethod),
        //        URI         = URI
        //    };

        //    BuilderAction.FailSafeInvoke(Builder);

        //    return Builder;

        //}

        #endregion


        #region Execute(HTTPRequestDelegate, RequestLogDelegate = null, ResponseLogDelegate = null, Timeout = null, CancellationToken = null)

        /// <summary>
        /// Execute the given HTTP request and return its result.
        /// </summary>
        /// <param name="HTTPRequestDelegate">A delegate for producing a HTTP request for a given HTTP client.</param>
        /// <param name="RequestLogDelegate">A delegate for logging the HTTP request.</param>
        /// <param name="ResponseLogDelegate">A delegate for logging the HTTP request/response.</param>
        /// <param name="Timeout">An optional timeout.</param>
        /// <param name="CancellationToken">A cancellation token.</param>
        public async Task<HTTPResponse> Execute(Func<HTTPClient, HTTPRequest>  HTTPRequestDelegate,
                                                ClientRequestLogHandler        RequestLogDelegate   = null,
                                                ClientResponseLogHandler       ResponseLogDelegate  = null,
                                                TimeSpan?                      Timeout              = null,
                                                CancellationToken?             CancellationToken    = null)
        {

            #region Initial checks

            if (HTTPRequestDelegate == null)
                throw new ArgumentNullException(nameof(HTTPRequestDelegate), "The given delegate must not be null!");

            #endregion

            return await Execute(HTTPRequestDelegate(this), RequestLogDelegate, ResponseLogDelegate, Timeout, CancellationToken);

        }
Exemple #4
0
        Query <T>(JObject JSONRequest,
                  Func <HTTPResponse <JObject>, HTTPResponse <T> > OnSuccess,
                  Func <DateTime, Object, HTTPResponse <JObject>, HTTPResponse <T> > OnJSONFault,
                  Func <DateTime, Object, HTTPResponse, HTTPResponse <T> > OnHTTPError,
                  Func <DateTime, Object, Exception, HTTPResponse <T> > OnException,
                  Action <HTTPRequestBuilder> HTTPRequestBuilder = null,
                  ClientRequestLogHandler RequestLogDelegate     = null,
                  ClientResponseLogHandler ResponseLogDelegate   = null,
                  CancellationToken?CancellationToken            = null,
                  EventTracking_Id EventTrackingId = null,
                  TimeSpan?RequestTimeout          = null)

        {
            #region Initial checks

            if (JSONRequest == null)
            {
                throw new ArgumentNullException(nameof(JSONRequest), "The JSON request must not be null!");
            }

            if (OnSuccess == null)
            {
                throw new ArgumentNullException(nameof(OnSuccess), "The 'OnSuccess'-delegate must not be null!");
            }

            if (OnJSONFault == null)
            {
                throw new ArgumentNullException(nameof(OnJSONFault), "The 'OnJSONFault'-delegate must not be null!");
            }

            if (OnHTTPError == null)
            {
                throw new ArgumentNullException(nameof(OnHTTPError), "The 'OnHTTPError'-delegate must not be null!");
            }

            if (OnException == null)
            {
                throw new ArgumentNullException(nameof(OnException), "The 'OnException'-delegate must not be null!");
            }

            #endregion

            var _RequestBuilder = this.POST(URIPrefix);
            _RequestBuilder.Host          = HTTPVirtualHost;
            _RequestBuilder.Content       = JSONRequest.ToUTF8Bytes();
            _RequestBuilder.ContentType   = HTTPContentType.JSON_UTF8;
            _RequestBuilder.UserAgent     = UserAgent;
            _RequestBuilder.FakeURIPrefix = "https://" + HTTPVirtualHost;

            HTTPRequestBuilder?.Invoke(_RequestBuilder);

            return(this.Execute(_RequestBuilder,
                                RequestLogDelegate,
                                ResponseLogDelegate,
                                RequestTimeout ?? TimeSpan.FromSeconds(60),
                                CancellationToken.HasValue  ? CancellationToken.Value : new CancellationTokenSource().Token).

                   ContinueWith(HttpResponseTask => {
                if (HttpResponseTask.Result == null ||
                    HttpResponseTask.Result.HTTPStatusCode != HTTPStatusCode.OK ||
                    HttpResponseTask.Result.HTTPBody == null ||
                    HttpResponseTask.Result.HTTPBody.Length == 0)
                {
                    var OnHTTPErrorLocal = OnHTTPError;
                    if (OnHTTPErrorLocal != null)
                    {
                        return OnHTTPErrorLocal(DateTime.Now, this, HttpResponseTask?.Result);
                    }

                    return new HTTPResponse <JObject>(HttpResponseTask?.Result,
                                                      new JObject(new JProperty("HTTPError", "")),
                                                      IsFault: true) as HTTPResponse <T>;
                }

                try
                {
                    var JSON = JObject.Parse(HttpResponseTask.Result.HTTPBody.ToUTF8String());

                    var OnSuccessLocal = OnSuccess;
                    if (OnSuccessLocal != null)
                    {
                        return OnSuccessLocal(new HTTPResponse <JObject>(HttpResponseTask.Result, JSON));
                    }

                    //var OnSOAPFaultLocal = OnSOAPFault;
                    //if (OnSOAPFaultLocal != null)
                    //    return OnSOAPFaultLocal(DateTime.Now, this, new HTTPResponse<XElement>(HttpResponseTask.Result, SOAPXML));

                    return new HTTPResponse <JObject>(HttpResponseTask.Result,
                                                      new JObject(new JProperty("fault", "")),
                                                      IsFault: true) as HTTPResponse <T>;
                } catch (Exception e)
                {
                    OnException?.Invoke(DateTime.Now, this, e);

                    //var OnFaultLocal = OnSOAPFault;
                    //if (OnFaultLocal != null)
                    //    return OnFaultLocal(new HTTPResponse<XElement>(HttpResponseTask.Result, e));

                    return new HTTPResponse <JObject>(HttpResponseTask.Result,
                                                      new JObject(new JProperty("exception", e.Message)),
                                                      IsFault: true) as HTTPResponse <T>;
                }
            }));
        }
Exemple #5
0
        /// <summary>
        /// Execute the given HTTP request and return its result.
        /// </summary>
        /// <param name="Request">A HTTP request.</param>
        /// <param name="RequestLogDelegate">A delegate for logging the HTTP request.</param>
        /// <param name="ResponseLogDelegate">A delegate for logging the HTTP request/response.</param>
        /// <param name="Timeout">An optional timeout.</param>
        /// <param name="CancellationToken">A cancellation token.</param>
        public async Task <HTTPResponse> Execute(HTTPRequest Request,
                                                 ClientRequestLogHandler RequestLogDelegate   = null,
                                                 ClientResponseLogHandler ResponseLogDelegate = null,
                                                 TimeSpan?Timeout = null,
                                                 CancellationToken?CancellationToken = null)
        {
            #region Call the optional HTTP request log delegate

            try
            {
                RequestLogDelegate?.Invoke(DateTime.Now, this, Request);
            }
            catch (Exception e)
            {
                e.Log(nameof(HTTPClient) + "." + nameof(RequestLogDelegate));
            }

            #endregion

            var task = Task <HTTPResponse> .Factory.StartNew(() => {
                HTTPResponse Response = null;

                try
                {
                    if (Environment.MachineName.Contains("QUAD2QUANTOR") && Request.URI.Contains("eRoaming"))
                    {
                        throw new Exception("Catch me if you can!");
                    }

                    Thread.CurrentThread.Priority = ThreadPriority.BelowNormal;

                    #region Data

                    var HTTPHeaderBytes = new Byte[0];
                    var sw = new Stopwatch();

                    if (!Timeout.HasValue)
                    {
                        Timeout = TimeSpan.FromSeconds(60);
                    }

                    #endregion

                    #region Create TCP connection (possibly also do DNS lookups)

                    if (TCPClient == null)
                    {
                        System.Net.IPEndPoint _FinalIPEndPoint = null;
                        IIPAddress _ResolvedRemoteIPAddress    = null;

                        if (RemoteIPAddress == null)
                        {
                            if (Hostname.Trim() == "127.0.0.1")
                            {
                                _ResolvedRemoteIPAddress = IPv4Address.Localhost;
                            }

                            else
                            {
                                var RegExpr = new Regex(@"\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b");

                                if (RegExpr.IsMatch(Hostname))
                                {
                                    _ResolvedRemoteIPAddress = IPv4Address.Parse(Hostname);
                                }
                            }

                            #region DNS lookup...

                            if (_ResolvedRemoteIPAddress == null)
                            {
                                try
                                {
                                    var IPv4AddressTask = DNSClient.
                                                          Query <A>(Hostname).
                                                          ContinueWith(QueryTask => QueryTask.Result.
                                                                       Select(ARecord => ARecord.IPv4Address).
                                                                       FirstOrDefault());

                                    IPv4AddressTask.Wait();

                                    _ResolvedRemoteIPAddress = IPv4AddressTask.Result;
                                }
                                catch (Exception e)
                                {
                                    Debug.WriteLine("[" + DateTime.Now + "] " + e.Message);
                                }
                            }

                            #endregion
                        }

                        else
                        {
                            _ResolvedRemoteIPAddress = RemoteIPAddress;
                        }

                        _FinalIPEndPoint = new System.Net.IPEndPoint(new System.Net.IPAddress(_ResolvedRemoteIPAddress.GetBytes()), RemotePort.ToInt32());

                        sw.Start();

                        TCPClient = new TcpClient();
                        TCPClient.Connect(_FinalIPEndPoint);
                        TCPClient.ReceiveTimeout = (Int32)Timeout.Value.TotalMilliseconds;
                    }

                    #endregion

                    #region Create (Crypto-)Stream

                    TCPStream             = TCPClient.GetStream();
                    TCPStream.ReadTimeout = (Int32)Timeout.Value.TotalMilliseconds;

                    TLSStream = RemoteCertificateValidator != null
                                     ? new SslStream(TCPStream,
                                                     false,
                                                     RemoteCertificateValidator)
                                //    ClientCertificateSelector,
                                //EncryptionPolicy.RequireEncryption)
                                     : null;

                    if (TLSStream != null)
                    {
                        TLSStream.ReadTimeout = (Int32)Timeout.Value.TotalMilliseconds;
                    }

                    HTTPStream = null;

                    if (RemoteCertificateValidator != null)
                    {
                        HTTPStream = TLSStream;
                        TLSStream.AuthenticateAsClient(Hostname);//, new X509CertificateCollection(new X509Certificate[] { ClientCert }), SslProtocols.Default, false);
                    }

                    else
                    {
                        HTTPStream = TCPStream;
                    }

                    HTTPStream.ReadTimeout = (Int32)Timeout.Value.TotalMilliseconds;

                    #endregion

                    #region Send Request

                    HTTPStream.Write(String.Concat(Request.EntireRequestHeader,
                                                   Environment.NewLine,
                                                   Environment.NewLine).
                                     ToUTF8Bytes());

                    var RequestBodyLength = Request.HTTPBody == null
                                                ? Request.ContentLength.HasValue ? (Int32)Request.ContentLength.Value : 0
                                                : Request.ContentLength.HasValue ? Math.Min((Int32)Request.ContentLength.Value, Request.HTTPBody.Length) : Request.HTTPBody.Length;

                    if (RequestBodyLength > 0)
                    {
                        HTTPStream.Write(Request.HTTPBody, 0, RequestBodyLength);
                    }

                    var _MemoryStream = new MemoryStream();
                    var _Buffer       = new Byte[10485760]; // 10 MBytes, a smaller value leads to read errors!

                    #endregion

                    #region Wait timeout for the server to react!

                    //Debug.WriteLine("[" + DateTime.Now + "] HTTPClient timeout: " + Timeout.Value.ToString());

                    while (!TCPStream.DataAvailable)
                    {
                        if (sw.ElapsedMilliseconds >= Timeout.Value.TotalMilliseconds)
                        {
                            TCPClient.Close();
                            throw new Exception("[" + DateTime.Now + "] Could not read from the TCP stream for " + sw.ElapsedMilliseconds + "ms!");
                        }

                        Thread.Sleep(1);
                    }

                    //Debug.WriteLine("[" + DateTime.Now + "] HTTPClient (" + TCPClient.Client.LocalEndPoint.ToString() + " -> " + RemoteSocket.ToString() + ") got first response after " + sw.ElapsedMilliseconds + "ms!");

                    #endregion

                    #region Read the entire HTTP header, and maybe some of the HTTP body

                    var CurrentDataLength = 0;

                    do
                    {
                        #region When data available, write it to the buffer...

                        while (TCPStream.DataAvailable)
                        {
                            CurrentDataLength = HTTPStream.Read(_Buffer, 0, _Buffer.Length);

                            if (CurrentDataLength > -1)
                            {
                                _MemoryStream.Write(_Buffer, 0, CurrentDataLength);
                                //                        Debug.WriteLine("[" + DateTime.Now + "] Read " + CurrentDataLength + " bytes from HTTP connection (" + TCPClient.Client.LocalEndPoint.ToString() + " -> " + RemoteSocket.ToString() + ") (" + sw.ElapsedMilliseconds + "ms)!");
                            }
                        }

                        #endregion

                        #region Check if the entire HTTP header was already read into the buffer

                        if (_MemoryStream.Length > 4)
                        {
                            var MemoryCopy = _MemoryStream.ToArray();

                            for (var pos = 3; pos < MemoryCopy.Length; pos++)
                            {
                                if (MemoryCopy[pos] == 0x0a &&
                                    MemoryCopy[pos - 1] == 0x0d &&
                                    MemoryCopy[pos - 2] == 0x0a &&
                                    MemoryCopy[pos - 3] == 0x0d)
                                {
                                    Array.Resize(ref HTTPHeaderBytes, pos - 3);
                                    Array.Copy(MemoryCopy, 0, HTTPHeaderBytes, 0, pos - 3);
                                    break;
                                }
                            }

                            //if (HTTPHeaderBytes.Length > 0)
                            //    Debug.WriteLine("[" + DateTime.Now + "] End of (" + TCPClient.Client.LocalEndPoint.ToString() + " -> " + RemoteSocket.ToString() + ") HTTP header at " + HTTPHeaderBytes.Length + " bytes (" + sw.ElapsedMilliseconds + "ms)!");
                        }

                        #endregion

                        Thread.Sleep(1);
                    }
                    // Note: Delayed parts of the HTTP body may not be read into the buffer
                    //       => Must be read later!
                    while (TCPStream.DataAvailable ||
                           ((sw.ElapsedMilliseconds < HTTPStream.ReadTimeout) && HTTPHeaderBytes.Length == 0));

                    //Debug.WriteLine("[" + DateTime.Now + "] Finally read " + _MemoryStream.Length + " bytes of HTTP client (" + TCPClient.Client.LocalEndPoint.ToString() + " -> " + RemoteSocket.ToString() + ") data (" + sw.ElapsedMilliseconds + "ms)!");

                    #endregion

                    #region Copy HTTP header data and create HTTP response

                    if (HTTPHeaderBytes.Length == 0)
                    {
                        throw new ApplicationException(DateTime.Now + " Could not find the end of the HTTP protocol header!");
                    }

                    Response = HTTPResponse.Parse(HTTPHeaderBytes.ToUTF8String(),
                                                  Request);

                    #endregion

                    #region Read 'Content-Length' bytes...

                    // Copy only the number of bytes given within
                    // the HTTP header element 'Content-Length'!
                    if (Response.ContentLength.HasValue && Response.ContentLength.Value > 0)
                    {
                        _MemoryStream.Seek(HTTPHeaderBytes.Length + 4, SeekOrigin.Begin);
                        var _Read        = _MemoryStream.Read(_Buffer, 0, _Buffer.Length);
                        var _StillToRead = (Int32)Response.ContentLength.Value - _Read;
                        Response.HTTPBodyStream.Write(_Buffer, 0, _Read);
                        var _CurrentBufferSize = 0;

                        do
                        {
                            while (TCPStream.DataAvailable && _StillToRead > 0)
                            {
                                _CurrentBufferSize = Math.Min(_Buffer.Length, (Int32)_StillToRead);
                                _Read = HTTPStream.Read(_Buffer, 0, _CurrentBufferSize);
                                Response.HTTPBodyStream.Write(_Buffer, 0, _Read);
                                _StillToRead -= _Read;
                            }

                            if (_StillToRead <= 0)
                            {
                                break;
                            }

                            Thread.Sleep(1);
                        }while (sw.ElapsedMilliseconds < HTTPStream.ReadTimeout);

                        Response.ContentStreamToArray();
                    }

                    #endregion

                    #region ...or read till timeout (e.g. for chunked transport)!

                    else
                    {
                        try
                        {
                            _MemoryStream.Seek(HTTPHeaderBytes.Length + 4, SeekOrigin.Begin);
                            Response.NewContentStream();
                            Response.HTTPBodyStream.Write(_Buffer, 0, _MemoryStream.Read(_Buffer, 0, _Buffer.Length));

                            var Retries = 0;

                            while (Retries < 10)
                            {
                                while (TCPStream.DataAvailable)
                                {
                                    Response.HTTPBodyStream.Write(_Buffer, 0, HTTPStream.Read(_Buffer, 0, _Buffer.Length));
                                    Retries = 0;
                                }

                                Thread.Sleep(10);
                                Retries++;
                            }

                            if (Response.TransferEncoding == "chunked")
                            {
                                //Debug.WriteLine(DateTime.Now + " Chunked encoding detected");

                                var TEContent       = ((MemoryStream)Response.HTTPBodyStream).ToArray();
                                var TEString        = TEContent.ToUTF8String();
                                var ReadBlockLength = true;
                                var TEMemStram      = new MemoryStream();
                                var LastPos         = 0;

                                var i = 0;
                                do
                                {
                                    if (i > 2 &&
                                        ReadBlockLength &&
                                        TEContent[i - 1] == '\n' &&
                                        TEContent[i - 2] == '\r')
                                    {
                                        var len = TEContent.ReadTEBlockLength(LastPos, i - LastPos - 2);

                                        //Debug.WriteLine(DateTime.Now + " Chunked encoded block of length " + len + " bytes detected");

                                        if (len == 0)
                                        {
                                            break;
                                        }

                                        if (i + len <= TEContent.Length)
                                        {
                                            TEMemStram.Write(TEContent, i, len);
                                            i = i + len;

                                            if (TEContent[i] == 0x0d)
                                            {
                                                i++;
                                            }

                                            if (i < TEContent.Length - 1)
                                            {
                                                if (TEContent[i] == 0x0a)
                                                {
                                                    i++;
                                                }
                                            }
                                            else
                                            {
                                            }

                                            LastPos = i;

                                            ReadBlockLength = false;
                                        }

                                        else
                                        {
                                            // Reaching this point seems to be an endless loop!
                                            break;
                                        }
                                    }

                                    else
                                    {
                                        ReadBlockLength = true;
                                        i++;
                                    }
                                } while (i < TEContent.Length);

                                Response.ContentStreamToArray(TEMemStram);
                            }

                            else
                            {
                                Response.ContentStreamToArray();
                            }
                        }

                        catch (Exception e)
                        {
                            Debug.WriteLine(DateTime.Now + " " + e.Message);
                        }
                    }

                    #endregion

                    #region Close connection if requested!

                    if (Response.Connection == null ||
                        Response.Connection == "close")
                    {
                        TCPClient.Close();
                        HTTPStream = null;
                        TCPClient  = null;
                    }

                    #endregion
                }
                catch (Exception e)
                {
                    #region Create a HTTP response for the exception...

                    while (e.InnerException != null)
                    {
                        e = e.InnerException;
                    }

                    Response = new HTTPResponseBuilder(Request,
                                                       HTTPStatusCode.BadRequest)
                    {
                        ContentType = HTTPContentType.JSON_UTF8,
                        Content     = JSONObject.Create(new JProperty("Message", e.Message),
                                                        new JProperty("StackTrace", e.StackTrace)).
                                      ToUTF8Bytes()
                    };

                    #endregion
                }

                #region Call the optional HTTP response log delegate

                try
                {
                    ResponseLogDelegate?.Invoke(DateTime.Now, this, Request, Response);
                }
                catch (Exception e2)
                {
                    e2.Log(nameof(HTTPClient) + "." + nameof(ResponseLogDelegate));
                }

                #endregion


                return(Response);
            }, TaskCreationOptions.AttachedToParent);

            return(await task);
        }
Exemple #6
0
        Query <T>(XElement QueryXML,
                  String SOAPAction,
                  Func <HTTPResponse <XElement>, HTTPResponse <T> > OnSuccess,
                  Func <DateTime, Object, HTTPResponse <XElement>, HTTPResponse <T> > OnSOAPFault,
                  Func <DateTime, Object, HTTPResponse, HTTPResponse <T> > OnHTTPError,
                  Func <DateTime, Object, Exception, HTTPResponse <T> > OnException,
                  Action <HTTPRequestBuilder> HTTPRequestBuilder = null,
                  ClientRequestLogHandler RequestLogDelegate     = null,
                  ClientResponseLogHandler ResponseLogDelegate   = null,
                  CancellationToken?CancellationToken            = null,
                  EventTracking_Id EventTrackingId = null,
                  TimeSpan?QueryTimeout            = null)

        {
            #region Initial checks

            if (QueryXML == null)
            {
                throw new ArgumentNullException(nameof(QueryXML), "The 'Query'-string must not be null!");
            }

            if (SOAPAction.IsNullOrEmpty())
            {
                throw new ArgumentNullException(nameof(SOAPAction), "The 'SOAPAction'-string must not be null or empty!");
            }

            if (OnSuccess == null)
            {
                throw new ArgumentNullException(nameof(OnSuccess), "The 'OnSuccess'-delegate must not be null!");
            }

            if (OnSOAPFault == null)
            {
                throw new ArgumentNullException(nameof(OnSOAPFault), "The 'OnSOAPFault'-delegate must not be null!");
            }

            if (OnHTTPError == null)
            {
                throw new ArgumentNullException(nameof(OnHTTPError), "The 'OnHTTPError'-delegate must not be null!");
            }

            if (OnException == null)
            {
                throw new ArgumentNullException(nameof(OnException), "The 'OnException'-delegate must not be null!");
            }

            #endregion

            var _RequestBuilder = this.POST(_URIPrefix);
            _RequestBuilder.Host        = HTTPVirtualHost;
            _RequestBuilder.Content     = QueryXML.ToUTF8Bytes();
            _RequestBuilder.ContentType = HTTPContentType.XMLTEXT_UTF8;
            _RequestBuilder.Set("SOAPAction", @"""" + SOAPAction + @"""");
            _RequestBuilder.UserAgent     = UserAgent;
            _RequestBuilder.FakeURIPrefix = "https://" + HTTPVirtualHost;

            HTTPRequestBuilder?.Invoke(_RequestBuilder);

            return(this.Execute(_RequestBuilder,
                                RequestLogDelegate,
                                ResponseLogDelegate,
                                QueryTimeout ?? TimeSpan.FromSeconds(60),
                                CancellationToken.HasValue  ? CancellationToken.Value : new CancellationTokenSource().Token).

                   ContinueWith(HttpResponseTask => {
                if (HttpResponseTask.Result == null ||
                    HttpResponseTask.Result.HTTPStatusCode != HTTPStatusCode.OK ||
                    HttpResponseTask.Result.HTTPBody == null ||
                    HttpResponseTask.Result.HTTPBody.Length == 0)
                {
                    var OnHTTPErrorLocal = OnHTTPError;
                    if (OnHTTPErrorLocal != null)
                    {
                        return OnHTTPErrorLocal(DateTime.Now, this, HttpResponseTask?.Result);
                    }

                    return new HTTPResponse <XElement>(HttpResponseTask?.Result,
                                                       new XElement("HTTPError"),
                                                       IsFault: true) as HTTPResponse <T>;
                }

                try
                {
                    var SOAPXML = XDocument.Parse(HttpResponseTask.Result.HTTPBody.ToUTF8String()).
                                  Root.
                                  Element(NS.SOAPEnvelope_v1_1 + "Body").
                                  Descendants().
                                  FirstOrDefault();

                    // <S:Fault xmlns:ns4="http://www.w3.org/2003/05/soap-envelope" xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
                    //   <faultcode>S:Client</faultcode>
                    //   <faultstring>Validation error: The request message is invalid</faultstring>
                    //   <detail>
                    //     <Validation>
                    //       <Errors>
                    //         <Error column="65" errorXpath="/OICP:Envelope/OICP:Body/EVSEStatus:eRoamingPullEvseStatusById/EVSEStatus:EvseId" line="3">Value '+45*045*010*0A96296' is not facet-valid with respect to pattern '([A-Za-z]{2}\*?[A-Za-z0-9]{3}\*?E[A-Za-z0-9\*]{1,30})|(\+?[0-9]{1,3}\*[0-9]{3,6}\*[0-9\*]{1,32})' for type 'EvseIDType'.</Error>
                    //         <Error column="65" errorXpath="/OICP:Envelope/OICP:Body/EVSEStatus:eRoamingPullEvseStatusById/EVSEStatus:EvseId" line="3">The value '+45*045*010*0A96296' of element 'EVSEStatus:EvseId' is not valid.</Error>
                    //       </Errors>
                    //       <OriginalDocument>
                    //         ...
                    //       </OriginalDocument>
                    //     </Validation>
                    //   </detail>
                    // </S:Fault>

                    if (SOAPXML.Name.LocalName != "Fault")
                    {
                        var OnSuccessLocal = OnSuccess;
                        if (OnSuccessLocal != null)
                        {
                            return OnSuccessLocal(new HTTPResponse <XElement>(HttpResponseTask.Result, SOAPXML));
                        }
                    }

                    var OnSOAPFaultLocal = OnSOAPFault;
                    if (OnSOAPFaultLocal != null)
                    {
                        return OnSOAPFaultLocal(DateTime.Now, this, new HTTPResponse <XElement>(HttpResponseTask.Result, SOAPXML));
                    }

                    return new HTTPResponse <XElement>(HttpResponseTask.Result,
                                                       new XElement("SOAPFault"),
                                                       IsFault: true) as HTTPResponse <T>;
                } catch (Exception e)
                {
                    OnException?.Invoke(DateTime.Now, this, e);

                    //var OnFaultLocal = OnSOAPFault;
                    //if (OnFaultLocal != null)
                    //    return OnFaultLocal(new HTTPResponse<XElement>(HttpResponseTask.Result, e));

                    return new HTTPResponse <XElement>(HttpResponseTask.Result,
                                                       new XElement("exception", e.Message),
                                                       IsFault: true) as HTTPResponse <T>;
                }
            }));
        }