Ejemplo n.º 1
0
        private static void HandleAsyncException(RequestCompletionSource state, Exception ex)
        {
            if ((null == ex) && state.CancellationToken.IsCancellationRequested)
            {
                state.TrySetCanceled(state.CancellationToken);
            }
            if (null == ex)
            {
                return;
            }
            var oce = (ex as OperationCanceledException);

            if (oce != null)
            {
                // If the exception was due to the cancellation token being canceled, throw cancellation exception.
                Debug.Assert(state.CancellationToken.IsCancellationRequested);
                state.TrySetCanceled(oce.CancellationToken);
            }
            else if (ex is HttpRequestException)
            {
                state.TrySetException(ex);
            }
            else
            {
                state.TrySetException(new HttpRequestException(SR.net_http_client_execution_error, ex));
            }
        }
Ejemplo n.º 2
0
        private static void RemoveEasyHandle(SafeCurlMultiHandle multiHandle, GCHandle stateHandle, bool onMultiStack)
        {
            RequestCompletionSource state         = (RequestCompletionSource)stateHandle.Target;
            SafeCurlHandle          requestHandle = state.RequestHandle;

            if (onMultiStack)
            {
                lock (multiHandle)
                {
                    Interop.libcurl.curl_multi_remove_handle(multiHandle, requestHandle);
                }
                state.SessionHandle = null;
                requestHandle.DangerousRelease();
            }

            if (!state.RequestHeaderHandle.IsInvalid)
            {
                SafeCurlSlistHandle headerHandle = state.RequestHeaderHandle;
                SafeCurlSlistHandle.DisposeAndClearHandle(ref headerHandle);
            }

            SafeCurlHandle.DisposeAndClearHandle(ref requestHandle);

            stateHandle.Free();
        }
Ejemplo n.º 3
0
        private static void EndRequest(SafeCurlMultiHandle multiHandle, IntPtr statePtr, int result)
        {
            GCHandle stateHandle          = GCHandle.FromIntPtr(statePtr);
            RequestCompletionSource state = (RequestCompletionSource)stateHandle.Target;

            try
            {
                // No more callbacks so no more data
                state.ResponseMessage.ContentStream.SignalComplete();

                if (CURLcode.CURLE_OK == result)
                {
                    state.TrySetResult(state.ResponseMessage);
                }
                else
                {
                    state.TrySetException(new HttpRequestException(SR.net_http_client_execution_error,
                                                                   GetCurlException(result)));
                }
            }
            catch (Exception ex)
            {
                HandleAsyncException(state, ex);
            }
            finally
            {
                RemoveEasyHandle(multiHandle, stateHandle, true);
            }
        }
Ejemplo n.º 4
0
        private void AddEasyHandle(RequestCompletionSource state)
        {
            bool           gotReference  = false;
            SafeCurlHandle requestHandle = state.RequestHandle;

            try
            {
                requestHandle.DangerousAddRef(ref gotReference);
                lock (_multiHandle)
                {
                    int result = Interop.libcurl.curl_multi_add_handle(_multiHandle, requestHandle);
                    if (result != CURLcode.CURLE_OK)
                    {
                        throw new HttpRequestException(SR.net_http_client_execution_error,
                                                       GetCurlException(result, true));
                    }
                }
                state.SessionHandle = _multiHandle;
                // Note that we are deliberately not decreasing the ref counts of
                // the multi and easy handles since that will be done in RemoveEasyHandle
                // when the request is completed and the handles are used in an
                // unmanaged context till then
                // TODO: Investigate if SafeCurlHandle is really useful since we are not
                //       avoiding any leaks due to the ref count increment
            }
            catch (Exception)
            {
                if (gotReference)
                {
                    requestHandle.DangerousRelease();
                }
                throw;
            }
        }
Ejemplo n.º 5
0
        private static void RemoveEasyHandle(SafeCurlMultiHandle multiHandle, RequestCompletionSource state, bool onMultiStack)
        {
            SafeCurlHandle requestHandle = state.RequestHandle;

            if (onMultiStack)
            {
                Debug.Assert(!requestHandle.IsInvalid);
                lock (multiHandle)
                {
                    Interop.libcurl.curl_multi_remove_handle(multiHandle, requestHandle);
                    multiHandle.RequestCount = multiHandle.RequestCount - 1;
                    multiHandle.SignalFdSetChange(state.SocketFd, true);
                }
                state.SessionHandle.DangerousRelease();
                state.SessionHandle = null;
                requestHandle.DangerousRelease();
            }

            if (state.RequestContentStream != null)
            {
                state.RequestContentStream.Dispose();
            }

            if (!state.RequestHeaderHandle.IsInvalid)
            {
                SafeCurlSlistHandle headerHandle = state.RequestHeaderHandle;
                SafeCurlSlistHandle.DisposeAndClearHandle(ref headerHandle);
            }

            if (!requestHandle.IsInvalid)
            {
                SafeCurlHandle.DisposeAndClearHandle(ref requestHandle);
            }
        }
Ejemplo n.º 6
0
        private SafeCurlHandle CreateRequestHandle(RequestCompletionSource state, GCHandle stateHandle)
        {
            // TODO: If this impacts perf, optimize using a handle pool
            SafeCurlHandle requestHandle = Interop.libcurl.curl_easy_init();

            if (requestHandle.IsInvalid)
            {
                throw new HttpRequestException(SR.net_http_client_execution_error);
            }

            SetCurlOption(requestHandle, CURLoption.CURLOPT_URL, state.RequestMessage.RequestUri.AbsoluteUri);
            if (_automaticRedirection)
            {
                SetCurlOption(requestHandle, CURLoption.CURLOPT_FOLLOWLOCATION, 1L);

                // Set maximum automatic redirection option
                SetCurlOption(requestHandle, CURLoption.CURLOPT_MAXREDIRS, _maxAutomaticRedirections);
            }

            if (state.RequestMessage.Method == HttpMethod.Put)
            {
                SetCurlOption(requestHandle, CURLoption.CURLOPT_UPLOAD, 1L);
            }
            else if (state.RequestMessage.Method == HttpMethod.Head)
            {
                SetCurlOption(requestHandle, CURLoption.CURLOPT_NOBODY, 1L);
            }
            else if (state.RequestMessage.Method == HttpMethod.Post)
            {
                SetCurlOption(requestHandle, CURLoption.CURLOPT_POST, 1L);
                if (state.RequestMessage.Content == null)
                {
                    SetCurlOption(requestHandle, CURLoption.CURLOPT_POSTFIELDSIZE, 0L);
                    SetCurlOption(requestHandle, CURLoption.CURLOPT_COPYPOSTFIELDS, String.Empty);
                }
            }

            IntPtr statePtr = GCHandle.ToIntPtr(stateHandle);

            SetCurlOption(requestHandle, CURLoption.CURLOPT_PRIVATE, statePtr);

            SetCurlCallbacks(requestHandle, state.RequestMessage, statePtr);

            if (_supportsAutomaticDecompression)
            {
                SetRequestHandleDecompressionOptions(requestHandle);
            }

            SetProxyOptions(requestHandle, state.RequestMessage.RequestUri);

            SetRequestHandleCredentialsOptions(requestHandle, state);

            SetCookieOption(requestHandle, state.RequestMessage.RequestUri);

            state.RequestHeaderHandle = SetRequestHeaders(requestHandle, state.RequestMessage);

            return(requestHandle);
        }
Ejemplo n.º 7
0
        private static void RemoveEasyHandle(SafeCurlMultiHandle multiHandle, GCHandle stateHandle, bool onMultiStack)
        {
            Debug.Assert(stateHandle.IsAllocated);

            RequestCompletionSource state = (RequestCompletionSource)stateHandle.Target;

            stateHandle.Free();
            RemoveEasyHandle(multiHandle, state, onMultiStack);
        }
Ejemplo n.º 8
0
        protected internal override Task <HttpResponseMessage> SendAsync(
            HttpRequestMessage request,
            CancellationToken cancellationToken)
        {
            if (request == null)
            {
                throw new ArgumentNullException("request", SR.net_http_handler_norequest);
            }

            if ((request.RequestUri.Scheme != UriSchemeHttp) && (request.RequestUri.Scheme != UriSchemeHttps))
            {
                throw NotImplemented.ByDesignWithMessage(SR.net_http_client_http_baseaddress_required);
            }

            if (request.RequestUri.Scheme == UriSchemeHttps && !_supportsSSL)
            {
                throw new PlatformNotSupportedException(SR.net_http_unix_https_support_unavailable_libcurl);
            }

            if (request.Headers.TransferEncodingChunked.GetValueOrDefault() && (request.Content == null))
            {
                throw new InvalidOperationException(SR.net_http_chunked_not_allowed_with_empty_content);
            }

            // TODO: Check that SendAsync is not being called again for same request object.
            //       Probably fix is needed in WinHttpHandler as well

            CheckDisposed();

            SetOperationStarted();

            if (cancellationToken.IsCancellationRequested)
            {
                return(Task.FromCanceled <HttpResponseMessage>(cancellationToken));
            }

            // Create RequestCompletionSource object and save current values of handler settings.
            RequestCompletionSource state = new RequestCompletionSource
            {
                CancellationToken = cancellationToken,
                RequestMessage    = request,
            };

            BeginRequest(state);

            return(state.Task);
        }
Ejemplo n.º 9
0
        private void AddEasyHandle(RequestCompletionSource state)
        {
            bool           gotReference  = false;
            SafeCurlHandle requestHandle = state.RequestHandle;

            try
            {
                requestHandle.DangerousAddRef(ref gotReference);
                lock (_multiHandle)
                {
                    int result = Interop.libcurl.curl_multi_add_handle(_multiHandle, requestHandle);
                    if (result != CURLcode.CURLE_OK)
                    {
                        throw new HttpRequestException(SR.net_http_client_execution_error,
                                                       GetCurlException(result, true));
                    }
                    state.SessionHandle       = _multiHandle;
                    _multiHandle.RequestCount = _multiHandle.RequestCount + 1;
                    if (_multiHandle.PollCancelled)
                    {
                        // TODO: Create single polling thread for all HttpClientHandler objects
                        Task.Factory.StartNew(s => PollFunction(((CurlHandler)s)._multiHandle), this,
                                              CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
                        _multiHandle.PollCancelled = false;
                    }
                    bool ignored = false;
                    _multiHandle.DangerousAddRef(ref ignored);
                }
                // Note that we are deliberately not decreasing the ref counts of
                // the multi and easy handles since that will be done in RemoveEasyHandle
                // when the request is completed and the handles are used in an
                // unmanaged context till then
                // TODO: Investigate if SafeCurlHandle is really useful since we are not
                //       avoiding any leaks due to the ref count increment
            }
            catch (Exception)
            {
                if (gotReference)
                {
                    requestHandle.DangerousRelease();
                }
                throw;
            }
        }
Ejemplo n.º 10
0
        private static void EndRequest(SafeCurlMultiHandle multiHandle, IntPtr statePtr, int result)
        {
            GCHandle stateHandle          = GCHandle.FromIntPtr(statePtr);
            RequestCompletionSource state = (RequestCompletionSource)stateHandle.Target;

            try
            {
                // No more callbacks so no more data
                state.ResponseMessage.ContentStream.SignalComplete();

                if (CURLcode.CURLE_OK == result)
                {
                    state.TrySetResult(state.ResponseMessage);
                }
                else
                {
                    state.TrySetException(new HttpRequestException(SR.net_http_client_execution_error,
                                                                   GetCurlException(result)));
                }

                if (state.ResponseMessage.StatusCode != HttpStatusCode.Unauthorized && state.Handler.PreAuthenticate)
                {
                    ulong availedAuth;
                    if (Interop.libcurl.curl_easy_getinfo(state.RequestHandle, CURLINFO.CURLINFO_HTTPAUTH_AVAIL, out availedAuth) == CURLcode.CURLE_OK)
                    {
                        state.Handler.AddCredentialToCache(state.RequestMessage.RequestUri, availedAuth, state.NetworkCredential);
                    }
                    // ignoring the exception in this case.
                    // There is no point in killing the request for the sake of putting the credentials into the cache
                }
            }
            catch (Exception ex)
            {
                HandleAsyncException(state, ex);
            }
            finally
            {
                RemoveEasyHandle(multiHandle, stateHandle, true);
            }
        }
Ejemplo n.º 11
0
        private SafeCurlHandle CreateRequestHandle(RequestCompletionSource state, GCHandle stateHandle)
        {
            // TODO: If this impacts perf, optimize using a handle pool
            SafeCurlHandle requestHandle = Interop.libcurl.curl_easy_init();

            if (requestHandle.IsInvalid)
            {
                throw new HttpRequestException(SR.net_http_client_execution_error);
            }

            SetCurlOption(requestHandle, CURLoption.CURLOPT_URL, state.RequestMessage.RequestUri.AbsoluteUri);
            if (_automaticRedirection)
            {
                SetCurlOption(requestHandle, CURLoption.CURLOPT_FOLLOWLOCATION, 1L);
            }
            if (state.RequestMessage.Content != null)
            {
                SetCurlOption(requestHandle, CURLoption.CURLOPT_UPLOAD, 1L);
            }
            if (state.RequestMessage.Method == HttpMethod.Head)
            {
                SetCurlOption(requestHandle, CURLoption.CURLOPT_NOBODY, 1L);
            }

            IntPtr statePtr = GCHandle.ToIntPtr(stateHandle);

            SetCurlOption(requestHandle, CURLoption.CURLOPT_PRIVATE, statePtr);

            SetCurlCallbacks(requestHandle, state.RequestMessage, statePtr);

            SetRequestHandleDecompressionOptions(requestHandle);

            SetProxyOptions(requestHandle, state.RequestMessage.RequestUri);

            state.RequestHeaderHandle = SetRequestHeaders(requestHandle, state.RequestMessage);

            // TODO: Handle other options

            return(requestHandle);
        }
Ejemplo n.º 12
0
        private async void BeginRequest(RequestCompletionSource state)
        {
            SafeCurlHandle requestHandle = new SafeCurlHandle();
            GCHandle       stateHandle   = new GCHandle();

            try
            {
                // Prepare context objects
                state.ResponseMessage = new CurlResponseMessage(state.RequestMessage);
                stateHandle           = GCHandle.Alloc(state);
                requestHandle         = CreateRequestHandle(state, stateHandle);
                state.RequestHandle   = requestHandle;

                if (state.RequestMessage.Content != null)
                {
                    Stream requestContentStream =
                        await state.RequestMessage.Content.ReadAsStreamAsync().ConfigureAwait(false);

                    state.RequestContentStream = requestContentStream;
                    if (state.CancellationToken.IsCancellationRequested)
                    {
                        RemoveEasyHandle(_multiHandle, stateHandle, state);
                        state.TrySetCanceled(state.CancellationToken);
                        return;
                    }
                    state.RequestContentBuffer = new byte[s_requestBufferSize];
                }

                AddEasyHandle(state);
            }
            catch (Exception ex)
            {
                RemoveEasyHandle(_multiHandle, stateHandle, state);
                HandleAsyncException(state, ex);
            }
        }
Ejemplo n.º 13
0
 private static void RemoveEasyHandle(SafeCurlMultiHandle multiHandle, GCHandle stateHandle, RequestCompletionSource state)
 {
     if (stateHandle.IsAllocated)
     {
         stateHandle.Free();
     }
     RemoveEasyHandle(multiHandle, state, false);
 }
Ejemplo n.º 14
0
        private void SetRequestHandleCredentialsOptions(SafeCurlHandle requestHandle, RequestCompletionSource state)
        {
            NetworkCredential credentials = GetNetworkCredentials(state.Handler._serverCredentials, state.RequestMessage.RequestUri);

            if (credentials != null)
            {
                string userName = string.IsNullOrEmpty(credentials.Domain) ?
                                  credentials.UserName :
                                  string.Format("{0}\\{1}", credentials.Domain, credentials.UserName);

                SetCurlOption(requestHandle, CURLoption.CURLOPT_USERNAME, userName);
                SetCurlOption(requestHandle, CURLoption.CURLOPT_HTTPAUTH, CURLAUTH.AuthAny);
                if (credentials.Password != null)
                {
                    SetCurlOption(requestHandle, CURLoption.CURLOPT_PASSWORD, credentials.Password);
                }

                state.NetworkCredential = credentials;
            }
        }
Ejemplo n.º 15
0
        private async void BeginRequest(RequestCompletionSource state)
        {
            SafeCurlHandle requestHandle = new SafeCurlHandle();
            GCHandle       stateHandle   = new GCHandle();
            bool           needCleanup   = false;

            try
            {
                // Prepare context objects
                state.ResponseMessage = new CurlResponseMessage(state.RequestMessage);
                stateHandle           = GCHandle.Alloc(state);
                requestHandle         = CreateRequestHandle(state, stateHandle);
                state.RequestHandle   = requestHandle;
                needCleanup           = true;

                if (state.CancellationToken.IsCancellationRequested)
                {
                    state.TrySetCanceled(state.CancellationToken);
                    return;
                }

                if (state.RequestMessage.Content != null)
                {
                    Stream requestContentStream =
                        await state.RequestMessage.Content.ReadAsStreamAsync().ConfigureAwait(false);

                    if (state.CancellationToken.IsCancellationRequested)
                    {
                        state.TrySetCanceled(state.CancellationToken);
                        return;
                    }
                    state.RequestContentStream = requestContentStream;
                    state.RequestContentBuffer = new byte[s_requestBufferSize];
                }

                AddEasyHandle(state);
                needCleanup = false;
            }
            catch (Exception ex)
            {
                HandleAsyncException(state, ex);
            }
            finally
            {
                if (needCleanup)
                {
                    RemoveEasyHandle(_multiHandle, stateHandle, false);
                }
                else if (state.Task.IsCompleted)
                {
                    if (stateHandle.IsAllocated)
                    {
                        stateHandle.Free();
                    }
                    if (!requestHandle.IsInvalid)
                    {
                        SafeCurlHandle.DisposeAndClearHandle(ref requestHandle);
                    }
                }
            }
        }
Ejemplo n.º 16
0
        private static bool TryParseStatusLine(HttpResponseMessage response, string responseHeader, RequestCompletionSource state)
        {
            if (!responseHeader.StartsWith(s_httpPrefix, StringComparison.OrdinalIgnoreCase))
            {
                return(false);
            }

            // Clear the header if status line is recieved again. This signifies that there are multiple response headers (like in redirection).
            response.Headers.Clear();

            response.Content.Headers.Clear();

            int responseHeaderLength = responseHeader.Length;

            // Check if line begins with HTTP/1.1 or HTTP/1.0
            int prefixLength = s_httpPrefix.Length;
            int versionIndex = prefixLength + 2;

            if ((versionIndex < responseHeaderLength) && (responseHeader[prefixLength] == '1') && (responseHeader[prefixLength + 1] == '.'))
            {
                if (responseHeader[versionIndex] == '1')
                {
                    response.Version = HttpVersion.Version11;
                }
                else if (responseHeader[versionIndex] == '0')
                {
                    response.Version = HttpVersion.Version10;
                }
                else
                {
                    response.Version = new Version(0, 0);
                }
            }
            else
            {
                response.Version = new Version(0, 0);
            }

            // TODO: Parsing errors are treated as fatal. Find right behaviour

            int spaceIndex = responseHeader.IndexOf(SpaceChar);

            if (spaceIndex > -1)
            {
                int codeStartIndex = spaceIndex + 1;
                int statusCode     = 0;

                // Parse first 3 characters after a space as status code
                if (TryParseStatusCode(responseHeader, codeStartIndex, out statusCode))
                {
                    response.StatusCode = (HttpStatusCode)statusCode;

                    // For security reasons, we drop the server credential if it is a
                    // NetworkCredential.  But we allow credentials in a CredentialCache
                    // since they are specifically tied to URI's.
                    if ((response.StatusCode == HttpStatusCode.Redirect) && !(state.Handler.Credentials is CredentialCache))
                    {
                        state.Handler.SetCurlOption(state.RequestHandle, CURLoption.CURLOPT_HTTPAUTH, CURLAUTH.None);
                        state.Handler.SetCurlOption(state.RequestHandle, CURLoption.CURLOPT_USERNAME, IntPtr.Zero);
                        state.Handler.SetCurlOption(state.RequestHandle, CURLoption.CURLOPT_PASSWORD, IntPtr.Zero);
                        state.NetworkCredential = null;
                    }

                    int codeEndIndex = codeStartIndex + StatusCodeLength;

                    int reasonPhraseIndex = codeEndIndex + 1;

                    if (reasonPhraseIndex < responseHeaderLength && responseHeader[codeEndIndex] == SpaceChar)
                    {
                        int newLineCharIndex = responseHeader.IndexOfAny(s_newLineCharArray, reasonPhraseIndex);
                        int reasonPhraseEnd  = newLineCharIndex >= 0 ? newLineCharIndex : responseHeaderLength;
                        response.ReasonPhrase = responseHeader.Substring(reasonPhraseIndex, reasonPhraseEnd - reasonPhraseIndex);
                    }
                }
            }

            return(true);
        }
Ejemplo n.º 17
0
        private static bool TryParseStatusLine(HttpResponseMessage response, string responseHeader, RequestCompletionSource state)
        {
            if (!responseHeader.StartsWith(s_httpPrefix, StringComparison.OrdinalIgnoreCase))
            {
                return false;
            }

            // Clear the header if status line is recieved again. This signifies that there are multiple response headers (like in redirection).
            response.Headers.Clear();
            
            response.Content.Headers.Clear();        

            int responseHeaderLength = responseHeader.Length;

            // Check if line begins with HTTP/1.1 or HTTP/1.0
            int prefixLength = s_httpPrefix.Length;
            int versionIndex = prefixLength + 2;

            if ((versionIndex < responseHeaderLength) && (responseHeader[prefixLength] == '1') && (responseHeader[prefixLength + 1] == '.'))
            {
                if (responseHeader[versionIndex] == '1')
                {
                    response.Version = HttpVersion.Version11;
                }
                else if (responseHeader[versionIndex] == '0')
                {
                    response.Version = HttpVersion.Version10;
                }
                else
                {
                    response.Version = new Version(0, 0);
                }
            }
            else
            {
                response.Version = new Version(0, 0);
            }
         
            // TODO: Parsing errors are treated as fatal. Find right behaviour
           
            int spaceIndex = responseHeader.IndexOf(SpaceChar);

            if (spaceIndex > -1)
            {
                int codeStartIndex = spaceIndex + 1;              
                int statusCode = 0;

                // Parse first 3 characters after a space as status code
                if (TryParseStatusCode(responseHeader, codeStartIndex, out statusCode))
                {
                    response.StatusCode = (HttpStatusCode)statusCode;

                    // For security reasons, we drop the server credential if it is a
                    // NetworkCredential.  But we allow credentials in a CredentialCache
                    // since they are specifically tied to URI's.
                    if ((response.StatusCode == HttpStatusCode.Redirect) && !(state.Handler.Credentials is CredentialCache))
                    {
                        state.Handler.SetCurlOption(state.RequestHandle, CURLoption.CURLOPT_HTTPAUTH, CURLAUTH.None);
                        state.Handler.SetCurlOption(state.RequestHandle, CURLoption.CURLOPT_USERNAME, IntPtr.Zero );
                        state.Handler.SetCurlOption(state.RequestHandle, CURLoption.CURLOPT_PASSWORD, IntPtr.Zero);
                        state.NetworkCredential = null;
                    }

                    int codeEndIndex = codeStartIndex + StatusCodeLength;

                    int reasonPhraseIndex = codeEndIndex + 1;

                    if (reasonPhraseIndex < responseHeaderLength && responseHeader[codeEndIndex] == SpaceChar)
                    {
                        int newLineCharIndex = responseHeader.IndexOfAny(s_newLineCharArray, reasonPhraseIndex);
                        int reasonPhraseEnd = newLineCharIndex >= 0 ? newLineCharIndex : responseHeaderLength;
                        response.ReasonPhrase = responseHeader.Substring(reasonPhraseIndex, reasonPhraseEnd - reasonPhraseIndex);
                    }
                }               
            }
            
            return true;
        }