Example #1
0
 private static void EventSourceTrace <TArg0, TArg1, TArg2>
     (string formatMessage, TArg0 arg0, TArg1 arg1, TArg2 arg2,
     MultiAgent agent = null, EasyRequest easy = null, [CallerMemberName] string memberName = null)
 {
     if (EventSourceTracingEnabled)
     {
         EventSourceTraceCore(string.Format(formatMessage, arg0, arg1, arg2), agent, easy, memberName);
     }
 }
Example #2
0
 private static void EventSourceTrace(
     string message,
     MultiAgent agent = null, EasyRequest easy = null, [CallerMemberName] string memberName = null)
 {
     if (EventSourceTracingEnabled)
     {
         EventSourceTraceCore(message, agent, easy, memberName);
     }
 }
 /// <summary>Queues a request for the multi handle to process.</summary>
 /// <param name="request"></param>
 public void Queue(EasyRequest request)
 {
     lock (_incomingRequests)
     {
         // Add the request, then initiate processing.
         _incomingRequests.Enqueue(request);
         EnsureWorkerIsRunning();
     }
 }
Example #4
0
            private static void SetSslVersion(EasyRequest easy, IntPtr sslCtx = default(IntPtr))
            {
                // Get the requested protocols.
                SslProtocols protocols = easy._handler.SslProtocols;

                if (protocols == SslProtocols.None)
                {
                    // Let libcurl use its defaults if None is set.
                    return;
                }

                // libcurl supports options for either enabling all of the TLS1.* protocols or enabling
                // just one protocol; it doesn't currently support enabling two of the three, e.g. you can't
                // pick TLS1.1 and TLS1.2 but not TLS1.0, but you can select just TLS1.2.
                Interop.Http.CurlSslVersion curlSslVersion;
                switch (protocols)
                {
#pragma warning disable 0618 // SSL2/3 are deprecated
                case SslProtocols.Ssl2:
                    curlSslVersion = Interop.Http.CurlSslVersion.CURL_SSLVERSION_SSLv2;
                    break;

                case SslProtocols.Ssl3:
                    curlSslVersion = Interop.Http.CurlSslVersion.CURL_SSLVERSION_SSLv3;
                    break;
#pragma warning restore 0618

                case SslProtocols.Tls:
                    curlSslVersion = Interop.Http.CurlSslVersion.CURL_SSLVERSION_TLSv1_0;
                    break;

                case SslProtocols.Tls11:
                    curlSslVersion = Interop.Http.CurlSslVersion.CURL_SSLVERSION_TLSv1_1;
                    break;

                case SslProtocols.Tls12:
                    curlSslVersion = Interop.Http.CurlSslVersion.CURL_SSLVERSION_TLSv1_2;
                    break;

                case SslProtocols.Tls | SslProtocols.Tls11 | SslProtocols.Tls12:
                    curlSslVersion = Interop.Http.CurlSslVersion.CURL_SSLVERSION_TLSv1;
                    break;

                default:
                    throw new NotSupportedException(SR.net_securityprotocolnotsupported);
                }

                try
                {
                    easy.SetCurlOption(Interop.Http.CURLoption.CURLOPT_SSLVERSION, (long)curlSslVersion);
                }
                catch (CurlException e) when(e.HResult == (int)CURLcode.CURLE_UNKNOWN_OPTION)
                {
                    throw new NotSupportedException(SR.net_securityprotocolnotsupported, e);
                }
            }
Example #5
0
            private void ActivateNewRequest(SafeCurlMultiHandle multiHandle, EasyRequest easy)
            {
                Debug.Assert(easy != null, "We should never get a null request");
                Debug.Assert(easy._associatedMultiAgent == null, "New requests should not be associated with an agent yet");

                // If cancellation has been requested, complete the request proactively
                if (easy._cancellationToken.IsCancellationRequested)
                {
                    easy.FailRequest(new OperationCanceledException(easy._cancellationToken));
                    easy.Cleanup(); // no active processing remains, so cleanup
                    return;
                }

                // Otherwise, configure it.  Most of the configuration was already done when the EasyRequest
                // was created, but there's additional configuration we need to do specific to this
                // multi agent, specifically telling the easy request about its own GCHandle and setting
                // up callbacks for data processing.  Once it's configured, add it to the multi handle.
                GCHandle gcHandle = GCHandle.Alloc(easy);
                IntPtr gcHandlePtr = GCHandle.ToIntPtr(gcHandle);
                try
                {
                    easy._associatedMultiAgent = this;
                    easy.SetCurlOption(CURLoption.CURLOPT_PRIVATE, gcHandlePtr);
                    easy.SetCurlCallbacks(gcHandlePtr, s_receiveHeadersCallback, s_sendCallback, s_seekCallback, s_receiveBodyCallback);
                    ThrowIfCURLMError(Interop.Http.MultiAddHandle(multiHandle, easy._easyHandle));
                }
                catch (Exception exc)
                {
                    gcHandle.Free();
                    easy.FailRequest(exc);
                    easy.Cleanup();  // no active processing remains, so cleanup
                    return;
                }

                // And if cancellation can be requested, hook up a cancellation callback.
                // This callback will put the easy request back into the queue, which will
                // ensure that a wake-up request has been issued.  When we pull
                // the easy request out of the request queue, we'll see that it's already
                // associated with this agent, meaning that it's a cancellation request,
                // and we'll deal with it appropriately.
                var cancellationReg = default(CancellationTokenRegistration);
                if (easy._cancellationToken.CanBeCanceled)
                {
                    cancellationReg = easy._cancellationToken.Register(s =>
                    {
                        var state = (Tuple<MultiAgent, EasyRequest>)s;
                        state.Item1.Queue(new IncomingRequest { Easy = state.Item2, Type = IncomingRequestType.Cancel });
                    }, Tuple.Create<MultiAgent, EasyRequest>(this, easy));
                }

                // Finally, add it to our map.
                _activeOperations.Add(
                    gcHandlePtr, 
                    new ActiveRequest { Easy = easy, CancellationRegistration = cancellationReg });
            }
Example #6
0
        private static void EventSourceTraceCore(string message, MultiAgent agent, EasyRequest easy, string memberName)
        {
            // If we weren't handed a multi agent, see if we can get one from the EasyRequest
            if (agent == null && easy != null)
            {
                agent = easy._associatedMultiAgent;
            }

            NetEventSource.Log.HandlerMessage(
                (agent?.RunningWorkerId).GetValueOrDefault(),
                easy?.Task.Id ?? 0,
                memberName,
                message);
        }
Example #7
0
            private static void SetSslVersion(EasyRequest easy)
            {
                // Get the requested protocols.
                SslProtocols protocols = easy._handler.SslProtocols;

                if (protocols == SslProtocols.None)
                {
                    // Let libcurl use its defaults if None is set.
                    return;
                }

                // We explicitly disallow choosing SSL2/3. Make sure they were filtered out.
                Debug.Assert((protocols & ~SecurityProtocol.AllowedSecurityProtocols) == 0,
                             "Disallowed protocols should have been filtered out.");

                // libcurl supports options for either enabling all of the TLS1.* protocols or enabling
                // just one of them; it doesn't currently support enabling two of the three, e.g. you can't
                // pick TLS1.1 and TLS1.2 but not TLS1.0, but you can select just TLS1.2.
                Interop.Http.CurlSslVersion curlSslVersion;
                switch (protocols)
                {
                case SslProtocols.Tls:
                    curlSslVersion = Interop.Http.CurlSslVersion.CURL_SSLVERSION_TLSv1_0;
                    break;

                case SslProtocols.Tls11:
                    curlSslVersion = Interop.Http.CurlSslVersion.CURL_SSLVERSION_TLSv1_1;
                    break;

                case SslProtocols.Tls12:
                    curlSslVersion = Interop.Http.CurlSslVersion.CURL_SSLVERSION_TLSv1_2;
                    break;

                case SslProtocols.Tls | SslProtocols.Tls11 | SslProtocols.Tls12:
                    curlSslVersion = Interop.Http.CurlSslVersion.CURL_SSLVERSION_TLSv1;
                    break;

                default:
                    throw new NotSupportedException(SR.net_securityprotocolnotsupported);
                }

                try
                {
                    easy.SetCurlOption(Interop.Http.CURLoption.CURLOPT_SSLVERSION, (long)curlSslVersion);
                }
                catch (CurlException e) when(e.HResult == (int)CURLcode.CURLE_UNKNOWN_OPTION)
                {
                    throw new NotSupportedException(SR.net_securityprotocolnotsupported, e);
                }
            }
Example #8
0
 private void ConfigureAndQueue(EasyRequest easy)
 {
     try
     {
         easy.InitializeCurl();
         _agent.Queue(new MultiAgent.IncomingRequest {
             Easy = easy, Type = MultiAgent.IncomingRequestType.New
         });
     }
     catch (Exception exc)
     {
         easy.FailRequest(exc);
         easy.Cleanup(); // no active processing remains, so we can cleanup
     }
 }
Example #9
0
            private static bool TryGetEasyRequest(IntPtr curlPtr, out EasyRequest easy)
            {
                Debug.Assert(curlPtr != IntPtr.Zero, "curlPtr is not null");

                IntPtr   gcHandlePtr;
                CURLcode getInfoResult = Interop.Http.EasyGetInfoPointer(curlPtr, CURLINFO.CURLINFO_PRIVATE, out gcHandlePtr);

                if (getInfoResult == CURLcode.CURLE_OK)
                {
                    return(MultiAgent.TryGetEasyRequestFromGCHandle(gcHandlePtr, out easy));
                }

                Debug.Fail($"Failed to get info on a completing easy handle: {getInfoResult}");
                easy = null;
                return(false);
            }
Example #10
0
            private void FindAndFailActiveRequest(SafeCurlMultiHandle multiHandle, EasyRequest easy, Exception error)
            {
                VerboseTrace("Error: " + error.Message, easy: easy);

                IntPtr gcHandlePtr;
                ActiveRequest activeRequest;
                if (FindActiveRequest(easy, out gcHandlePtr, out activeRequest))
                {
                    DeactivateActiveRequest(multiHandle, easy, gcHandlePtr, activeRequest.CancellationRegistration);
                    easy.FailRequest(error);
                    easy.Cleanup(); // no active processing remains, so we can cleanup
                }
                else
                {
                    Debug.Assert(easy.Task.IsCompleted, "We should only not be able to find the request if it failed or we started to send back the response.");
                }
            }
Example #11
0
            private static bool TryGetEasyRequest(IntPtr curlPtr, out EasyRequest easy)
            {
                Debug.Assert(curlPtr != IntPtr.Zero, "curlPtr is not null");
                IntPtr gcHandlePtr;
                CURLcode getInfoResult = Interop.Http.EasyGetInfoPointer(curlPtr, CURLINFO.CURLINFO_PRIVATE, out gcHandlePtr);
                Debug.Assert(getInfoResult == CURLcode.CURLE_OK, "Failed to get info on a completing easy handle");
                if (getInfoResult == CURLcode.CURLE_OK)
                {
                    GCHandle handle = GCHandle.FromIntPtr(gcHandlePtr);
                    easy = handle.Target as EasyRequest;
                    Debug.Assert(easy != null, "Expected non-null EasyRequest in GCHandle");
                    return easy != null;
                }

                easy = null;
                return false;
            }
Example #12
0
            internal static void SetSslOptions(EasyRequest easy, ClientCertificateOption clientCertOption)
            {
                // Disable SSLv2/SSLv3, allow TLSv1.*
                easy.SetCurlOption(Interop.Http.CURLoption.CURLOPT_SSLVERSION, (long)Interop.Http.CurlSslVersion.CURL_SSLVERSION_TLSv1);

                IntPtr userPointer = IntPtr.Zero;

                if (clientCertOption == ClientCertificateOption.Automatic)
                {
                    ClientCertificateProvider certProvider = new ClientCertificateProvider();
                    userPointer = GCHandle.ToIntPtr(certProvider._gcHandle);
                    easy.Task.ContinueWith((_, state) => ((IDisposable)state).Dispose(),
                                           certProvider,
                                           CancellationToken.None,
                                           TaskContinuationOptions.ExecuteSynchronously,
                                           TaskScheduler.Default);
                }
                else
                {
                    Debug.Assert(clientCertOption == ClientCertificateOption.Manual, "ClientCertificateOption is manual or automatic");
                }

                CURLcode answer = easy.SetSslCtxCallback(s_sslCtxCallback, userPointer);

                switch (answer)
                {
                case CURLcode.CURLE_OK:
                    break;

                // Curl 7.38 and prior
                case CURLcode.CURLE_UNKNOWN_OPTION:
                // Curl 7.39 and later
                case CURLcode.CURLE_NOT_BUILT_IN:
                    EventSourceTrace("CURLOPT_SSL_CTX_FUNCTION not supported. Platform default HTTPS chain building in use");
                    if (clientCertOption == ClientCertificateOption.Automatic)
                    {
                        throw new PlatformNotSupportedException(SR.net_http_unix_invalid_client_cert_option);
                    }

                    break;

                default:
                    ThrowIfCURLEError(answer);
                    break;
                }
            }
Example #13
0
 private bool FindActiveRequest(EasyRequest easy, out IntPtr gcHandlePtr, out ActiveRequest activeRequest)
 {
     // We maintain an IntPtr=>ActiveRequest mapping, which makes it cheap to look-up by GCHandle ptr but
     // expensive to look up by EasyRequest.  If we find this becoming a bottleneck, we can add a reverse
     // map that stores the other direction as well.
     foreach (KeyValuePair<IntPtr, ActiveRequest> pair in _activeOperations)
     {
         if (pair.Value.Easy == easy)
         {
             gcHandlePtr = pair.Key;
             activeRequest = pair.Value;
             return true;
         }
     }
     gcHandlePtr = IntPtr.Zero;
     activeRequest = default(ActiveRequest);
     return false;
 }
Example #14
0
            private static void AddChannelBindingToken(X509Certificate2 certificate, IntPtr curlPtr)
            {
                Debug.Assert(curlPtr != IntPtr.Zero, "curlPtr is not null");
                IntPtr   gcHandlePtr;
                CURLcode getInfoResult = Interop.Http.EasyGetInfoPointer(curlPtr, CURLINFO.CURLINFO_PRIVATE, out gcHandlePtr);

                Debug.Assert(getInfoResult == CURLcode.CURLE_OK, "Failed to get info on a completing easy handle");
                if (getInfoResult == CURLcode.CURLE_OK)
                {
                    GCHandle    handle = GCHandle.FromIntPtr(gcHandlePtr);
                    EasyRequest easy   = handle.Target as EasyRequest;
                    Debug.Assert(easy != null, "Expected non-null EasyRequest in GCHandle");
                    if (easy._requestContentStream != null)
                    {
                        easy._requestContentStream.SetChannelBindingToken(certificate);
                    }
                }
            }
            private void HandleIncomingRequest(SafeCurlMultiHandle multiHandle, IncomingRequest request)
            {
                Debug.Assert(!Monitor.IsEntered(_incomingRequests), "Incoming requests lock should only be held while accessing the queue");
                VerboseTrace("Type: " + request.Type, easy: request.Easy);

                EasyRequest easy = request.Easy;

                switch (request.Type)
                {
                case IncomingRequestType.New:
                    ActivateNewRequest(multiHandle, easy);
                    break;

                case IncomingRequestType.Cancel:
                    Debug.Assert(easy._associatedMultiAgent == this, "Should only cancel associated easy requests");
                    Debug.Assert(easy._cancellationToken.IsCancellationRequested, "Cancellation should have been requested");
                    FindAndFailActiveRequest(multiHandle, easy, new OperationCanceledException(easy._cancellationToken));
                    break;

                case IncomingRequestType.Unpause:
                    Debug.Assert(easy._associatedMultiAgent == this, "Should only unpause associated easy requests");
                    if (!easy._easyHandle.IsClosed)
                    {
                        IntPtr        gcHandlePtr;
                        ActiveRequest ar;
                        Debug.Assert(FindActiveRequest(easy, out gcHandlePtr, out ar), "Couldn't find active request for unpause");

                        CURLcode unpauseResult = Interop.Http.EasyUnpause(easy._easyHandle);
                        try
                        {
                            ThrowIfCURLEError(unpauseResult);
                        }
                        catch (Exception exc)
                        {
                            FindAndFailActiveRequest(multiHandle, easy, exc);
                        }
                    }
                    break;

                default:
                    Debug.Fail("Invalid request type: " + request.Type);
                    break;
                }
            }
Example #16
0
        private static void HandleRedirectLocationHeader(EasyRequest state, string locationValue)
        {
            Debug.Assert(state._isRedirect);
            Debug.Assert(state._handler.AutomaticRedirection);

            string location = locationValue.Trim();
            //only for absolute redirects
            Uri forwardUri;

            if (Uri.TryCreate(location, UriKind.RelativeOrAbsolute, out forwardUri) && forwardUri.IsAbsoluteUri)
            {
                // Just as with WinHttpHandler, for security reasons, we drop the server credential if it is
                // anything other than a CredentialCache. We allow credentials in a CredentialCache since they
                // are specifically tied to URIs.
                var creds = state._handler.Credentials as CredentialCache;
                KeyValuePair <NetworkCredential, CURLAUTH> ncAndScheme = GetCredentials(forwardUri, creds, AuthTypesPermittedByCredentialKind(creds));
                if (ncAndScheme.Key != null)
                {
                    state.SetCredentialsOptions(ncAndScheme);
                }
                else
                {
                    state.SetCurlOption(CURLoption.CURLOPT_USERNAME, IntPtr.Zero);
                    state.SetCurlOption(CURLoption.CURLOPT_PASSWORD, IntPtr.Zero);
                }

                // reset proxy - it is possible that the proxy has different credentials for the new URI
                state.SetProxyOptions(forwardUri);

                if (state._handler._useCookie)
                {
                    // reset cookies.
                    state.SetCurlOption(CURLoption.CURLOPT_COOKIE, IntPtr.Zero);

                    // set cookies again
                    state.SetCookieOption(forwardUri);
                }

                state.SetRedirectUri(forwardUri);
            }

            // set the headers again. This is a workaround for libcurl's limitation in handling headers with empty values
            state.SetRequestHeaders();
        }
Example #17
0
            internal static void SetSslOptions(EasyRequest easy, ClientCertificateOption clientCertOption)
            {
                // Disable SSLv2/SSLv3, allow TLSv1.*
                easy.SetCurlOption(Interop.Http.CURLoption.CURLOPT_SSLVERSION, (long)Interop.Http.CurlSslVersion.CURL_SSLVERSION_TLSv1);

                IntPtr userPointer = IntPtr.Zero;
                if (clientCertOption == ClientCertificateOption.Automatic)
                {
                    ClientCertificateProvider certProvider = new ClientCertificateProvider();
                    userPointer = GCHandle.ToIntPtr(certProvider._gcHandle);
                    easy.Task.ContinueWith((_, state) => ((IDisposable)state).Dispose(),
                                           certProvider,
                                           CancellationToken.None,
                                           TaskContinuationOptions.ExecuteSynchronously,
                                           TaskScheduler.Default);
                }
                else
                {
                    Debug.Assert(clientCertOption == ClientCertificateOption.Manual, "ClientCertificateOption is manual or automatic");
                }

                CURLcode answer = easy.SetSslCtxCallback(s_sslCtxCallback, userPointer);

                switch (answer)
                {
                    case CURLcode.CURLE_OK:
                        break;
                    // Curl 7.38 and prior
                    case CURLcode.CURLE_UNKNOWN_OPTION:
                    // Curl 7.39 and later
                    case CURLcode.CURLE_NOT_BUILT_IN:
                        EventSourceTrace("CURLOPT_SSL_CTX_FUNCTION not supported. Platform default HTTPS chain building in use");
                        if (clientCertOption == ClientCertificateOption.Automatic)
                        {
                            throw new PlatformNotSupportedException(SR.net_http_unix_invalid_client_cert_option);
                        }

                        break;
                    default:
                        ThrowIfCURLEError(answer);
                        break;
                }
            }
            internal static void SetSslOptions(EasyRequest easy)
            {
                CURLcode answer = easy.SetSslCtxCallback(s_sslCtxCallback);

                switch (answer)
                {
                    case CURLcode.CURLE_OK:
                        break;
                    // Curl 7.38 and prior
                    case CURLcode.CURLE_UNKNOWN_OPTION:
                    // Curl 7.39 and later
                    case CURLcode.CURLE_NOT_BUILT_IN:
                        VerboseTrace("CURLOPT_SSL_CTX_FUNCTION is not supported, platform default https chain building in use");
                        break;
                    default:
                        ThrowIfCURLEError(answer);
                        break;
                }
            }
Example #19
0
            internal static void SetSslOptions(EasyRequest easy)
            {
                int answer = Interop.libcurl.curl_easy_setopt(
                    easy._easyHandle,
                    Interop.libcurl.CURLoption.CURLOPT_SSL_CTX_FUNCTION,
                    s_sslCtxCallback);

                switch (answer)
                {
                    case Interop.libcurl.CURLcode.CURLE_OK:
                        break;
                    case Interop.libcurl.CURLcode.CURLE_NOT_BUILT_IN:
                        VerboseTrace("CURLOPT_SSL_CTX_FUNCTION is not supported, platform default https chain building in use");
                        break;
                    default:
                        ThrowIfCURLEError(answer);
                        break;
                }
            }
Example #20
0
        private void AddResponseCookies(EasyRequest state, string cookieHeader)
        {
            if (!_useCookie)
            {
                return;
            }

            try
            {
                _cookieContainer.SetCookies(state._targetUri, cookieHeader);
                state.SetCookieOption(state._requestMessage.RequestUri);
            }
            catch (CookieException e)
            {
                EventSourceTrace(
                    "Malformed cookie parsing failed: {0}, server: {1}, cookie: {2}",
                    e.Message, state._requestMessage.RequestUri, cookieHeader);
            }
        }
Example #21
0
            internal static void SetSslOptions(EasyRequest easy, ClientCertificateOption clientCertOption)
            {
                EventSourceTrace("ClientCertificateOption: {0}", clientCertOption, easy: easy);
                Debug.Assert(clientCertOption == ClientCertificateOption.Automatic || clientCertOption == ClientCertificateOption.Manual);

                // Create a client certificate provider if client certs may be used.
                X509Certificate2Collection clientCertificates = easy._handler._clientCertificates;
                ClientCertificateProvider  certProvider       =
                    clientCertOption == ClientCertificateOption.Automatic ? new ClientCertificateProvider(null) : // automatic
                    clientCertificates?.Count > 0 ? new ClientCertificateProvider(clientCertificates) :           // manual with certs
                    null;                                                                                         // manual without certs
                IntPtr userPointer = IntPtr.Zero;

                if (certProvider != null)
                {
                    EventSourceTrace("Created certificate provider", easy: easy);

                    // The client cert provider needs to be passed through to the callback, and thus
                    // we create a GCHandle to keep it rooted.  This handle needs to be cleaned up
                    // when the request has completed, and a simple and pay-for-play way to do that
                    // is by cleaning it up in a continuation off of the request.
                    userPointer = GCHandle.ToIntPtr(certProvider._gcHandle);
                    easy.Task.ContinueWith((_, state) => ((IDisposable)state).Dispose(), certProvider,
                                           CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default);
                }

                // Configure the options.  Our best support is when targeting OpenSSL/1.0.  For other backends,
                // we fall back to a minimal amount of support, and may throw a PNSE based on the options requested.
                if (Interop.Http.HasMatchingOpenSslVersion)
                {
                    // Register the callback with libcurl.  We need to register even if there's no user-provided
                    // server callback and even if there are no client certificates, because we support verifying
                    // server certificates against more than those known to OpenSSL.
                    SetSslOptionsForSupportedBackend(easy, certProvider, userPointer);
                }
                else
                {
                    // Newer versions of OpenSSL, and other non-OpenSSL backends, do not currently support callbacks.
                    // That means we'll throw a PNSE if a callback is required.
                    SetSslOptionsForUnsupportedBackend(easy, certProvider);
                }
            }
Example #22
0
            internal CurlResponseMessage(EasyRequest easy)
            {
                Debug.Assert(easy != null, "Expected non-null EasyRequest");
                RequestMessage = easy._requestMessage;
                ResponseStream = new CurlResponseStream(easy);
                Content        = new StreamContent(ResponseStream);

                // On Windows, we pass the equivalent of the easy._cancellationToken
                // in to StreamContent's ctor.  This in turn passes that token through
                // to ReadAsync operations on the stream response stream when HttpClient
                // reads from the response stream to buffer it with ResponseContentRead.
                // We don't need to do that here in the Unix implementation as, until the
                // SendAsync task completes, the handler will have registered with the
                // CancellationToken with an action that will cancel all work related to
                // the easy handle if cancellation occurs, and that includes canceling any
                // pending reads on the response stream.  It wouldn't hurt anything functionally
                // to still pass easy._cancellationToken here, but it will increase costs
                // a bit, as each read will then need to allocate a larger state object as
                // well as register with and unregister from the cancellation token.
            }
Example #23
0
            private static void SetSslOptionsForCertificateStore(EasyRequest easy)
            {
                // Support specifying certificate directory/bundle via environment variables: SSL_CERT_DIR, SSL_CERT_FILE.
                GetSslCaLocations(out string sslCaPath, out string sslCaInfo);

                if (sslCaPath != string.Empty)
                {
                    easy.SetCurlOption(Interop.Http.CURLoption.CURLOPT_CAPATH, sslCaPath);

                    // https proxy support requires libcurl 7.52.0+
                    easy.TrySetCurlOption(Interop.Http.CURLoption.CURLOPT_PROXY_CAPATH, sslCaPath);
                }

                if (sslCaInfo != string.Empty)
                {
                    easy.SetCurlOption(Interop.Http.CURLoption.CURLOPT_CAINFO, sslCaInfo);

                    // https proxy support requires libcurl 7.52.0+
                    easy.TrySetCurlOption(Interop.Http.CURLoption.CURLOPT_PROXY_CAINFO, sslCaInfo);
                }
            }
            internal static void SetSslOptions(EasyRequest easy)
            {
                int answer = Interop.libcurl.curl_easy_setopt(
                    easy._easyHandle,
                    Interop.libcurl.CURLoption.CURLOPT_SSL_CTX_FUNCTION,
                    s_sslCtxCallback);

                switch (answer)
                {
                case Interop.libcurl.CURLcode.CURLE_OK:
                    break;

                case Interop.libcurl.CURLcode.CURLE_NOT_BUILT_IN:
                    VerboseTrace("CURLOPT_SSL_CTX_FUNCTION is not supported, platform default https chain building in use");
                    break;

                default:
                    ThrowIfCURLEError(answer);
                    break;
                }
            }
Example #25
0
        private void AddResponseCookies(EasyRequest state, string cookieHeader)
        {
            if (!_useCookie)
            {
                return;
            }

            try
            {
                _cookieContainer.SetCookies(state._targetUri, cookieHeader);
                state.SetCookieOption(state._requestMessage.RequestUri);
            }
            catch (CookieException e)
            {
                string msg = string.Format("Malformed cookie: SetCookies Failed with {0}, server: {1}, cookie:{2}",
                                           e.Message,
                                           state._requestMessage.RequestUri,
                                           cookieHeader);
                VerboseTrace(msg);
            }
        }
            private void FinishRequest(EasyRequest completedOperation, CURLcode messageResult)
            {
                VerboseTrace("messageResult: " + messageResult, easy: completedOperation);

                if (completedOperation._responseMessage.StatusCode != HttpStatusCode.Unauthorized)
                {
                    if (completedOperation._handler.PreAuthenticate)
                    {
                        long authAvailable;
                        if (Interop.Http.EasyGetInfoLong(completedOperation._easyHandle, CURLINFO.CURLINFO_HTTPAUTH_AVAIL, out authAvailable) == CURLcode.CURLE_OK)
                        {
                            completedOperation._handler.AddCredentialToCache(
                                completedOperation._requestMessage.RequestUri, (CURLAUTH)authAvailable, completedOperation._networkCredential);
                        }
                        // Ignore errors: no need to fail for the sake of putting the credentials into the cache
                    }

                    completedOperation._handler.AddResponseCookies(
                        completedOperation._requestMessage.RequestUri, completedOperation._responseMessage);
                }

                // Complete or fail the request
                try
                {
                    bool unsupportedProtocolRedirect = messageResult == CURLcode.CURLE_UNSUPPORTED_PROTOCOL && completedOperation._isRedirect;
                    if (!unsupportedProtocolRedirect)
                    {
                        ThrowIfCURLEError(messageResult);
                    }
                    completedOperation.EnsureResponseMessagePublished();
                }
                catch (Exception exc)
                {
                    completedOperation.FailRequest(exc);
                }

                // At this point, we've completed processing the entire request, either due to error
                // or due to completing the entire response.
                completedOperation.Cleanup();
            }
Example #27
0
        private static void HandleRedirectLocationHeader(EasyRequest state, string locationValue)
        {
            Debug.Assert(state._isRedirect);
            Debug.Assert(state._handler.AutomaticRedirection);

            string location = locationValue.Trim();
            //only for absolute redirects
            Uri forwardUri;

            if (Uri.TryCreate(location, UriKind.RelativeOrAbsolute, out forwardUri) && forwardUri.IsAbsoluteUri)
            {
                KeyValuePair <NetworkCredential, CURLAUTH> ncAndScheme = GetCredentials(state._handler.Credentials as CredentialCache, forwardUri);
                if (ncAndScheme.Key != null)
                {
                    state.SetCredentialsOptions(ncAndScheme);
                }
                else
                {
                    state.SetCurlOption(CURLoption.CURLOPT_USERNAME, IntPtr.Zero);
                    state.SetCurlOption(CURLoption.CURLOPT_PASSWORD, IntPtr.Zero);
                }

                // reset proxy - it is possible that the proxy has different credentials for the new URI
                state.SetProxyOptions(forwardUri);

                if (state._handler._useCookie)
                {
                    // reset cookies.
                    state.SetCurlOption(CURLoption.CURLOPT_COOKIE, IntPtr.Zero);

                    // set cookies again
                    state.SetCookieOption(forwardUri);
                }

                state.SetRedirectUri(forwardUri);
            }

            // set the headers again. This is a workaround for libcurl's limitation in handling headers with empty values
            state.SetRequestHeaders();
        }
Example #28
0
            private static bool TryGetEasyRequestFromContext(IntPtr context, out EasyRequest easy)
            {
                // Get the EasyRequest from the context
                try
                {
                    GCHandle handle = GCHandle.FromIntPtr(context);
                    easy = (EasyRequest)handle.Target;
                    Debug.Assert(easy != null, "Expected non-null EasyRequest in GCHandle");
                    return easy != null;
                }
                catch (InvalidCastException)
                {
                    Debug.Fail("EasyRequest wasn't the GCHandle's Target");
                }
                catch (InvalidOperationException)
                {
                    Debug.Fail("Invalid GCHandle");
                }

                easy = null;
                return false;
            }
            private static void SetSslOptionsForCertificateStore(EasyRequest easy)
            {
                // Support specifying certificate directory/bundle via environment variables: SSL_CERT_DIR, SSL_CERT_FILE.
                string sslCertDir = Environment.GetEnvironmentVariable("SSL_CERT_DIR");

                if (sslCertDir != null)
                {
                    easy.SetCurlOption(Interop.Http.CURLoption.CURLOPT_CAPATH, sslCertDir);

                    // https proxy support requires libcurl 7.52.0+
                    easy.TrySetCurlOption(Interop.Http.CURLoption.CURLOPT_PROXY_CAPATH, sslCertDir);
                }

                string sslCertFile = Environment.GetEnvironmentVariable("SSL_CERT_FILE");

                if (sslCertFile != null)
                {
                    easy.SetCurlOption(Interop.Http.CURLoption.CURLOPT_CAINFO, sslCertFile);

                    // https proxy support requires libcurl 7.52.0+
                    easy.TrySetCurlOption(Interop.Http.CURLoption.CURLOPT_PROXY_CAINFO, sslCertFile);
                }
            }
Example #30
0
            private static void SetCurlCallbacks(EasyRequest easy, IntPtr easyGCHandle)
            {
                // Add callback for processing headers
                easy.SetCurlOption(CURLoption.CURLOPT_HEADERFUNCTION, s_receiveHeadersCallback);
                easy.SetCurlOption(CURLoption.CURLOPT_HEADERDATA, easyGCHandle);

                // If we're sending data as part of the request, add callbacks for sending request data
                if (easy.RequestMessage.Content != null)
                {
                    easy.SetCurlOption(CURLoption.CURLOPT_READFUNCTION, s_sendCallback);
                    easy.SetCurlOption(CURLoption.CURLOPT_READDATA, easyGCHandle);

                    easy.SetCurlOption(CURLoption.CURLOPT_SEEKFUNCTION, s_seekCallback);
                    easy.SetCurlOption(CURLoption.CURLOPT_SEEKDATA, easyGCHandle);
                }

                // If we're expecting any data in response, add a callback for receiving body data
                if (easy.RequestMessage.Method != HttpMethod.Head)
                {
                    easy.SetCurlOption(CURLoption.CURLOPT_WRITEFUNCTION, s_receiveBodyCallback);
                    easy.SetCurlOption(CURLoption.CURLOPT_WRITEDATA, easyGCHandle);
                }
            }
Example #31
0
            internal static void SetSslOptions(EasyRequest easy)
            {
                CURLcode answer = Interop.Http.EasySetOptionPointer(
                    easy._easyHandle,
                    Interop.Http.CURLoption.CURLOPT_SSL_CTX_FUNCTION,
                    s_sslCtxCallback);

                switch (answer)
                {
                case CURLcode.CURLE_OK:
                    break;

                // Curl 7.38 and prior
                case CURLcode.CURLE_UNKNOWN_OPTION:
                // Curl 7.39 and later
                case CURLcode.CURLE_NOT_BUILT_IN:
                    VerboseTrace("CURLOPT_SSL_CTX_FUNCTION is not supported, platform default https chain building in use");
                    break;

                default:
                    ThrowIfCURLEError(answer);
                    break;
                }
            }
Example #32
0
            internal static void SetSslOptions(EasyRequest easy, ClientCertificateOption clientCertOption)
            {
                Debug.Assert(clientCertOption == ClientCertificateOption.Automatic || clientCertOption == ClientCertificateOption.Manual);

                // Create a client certificate provider if client certs may be used.
                X509Certificate2Collection clientCertificates = easy._handler._clientCertificates;
                ClientCertificateProvider certProvider =
                    clientCertOption == ClientCertificateOption.Automatic ? new ClientCertificateProvider(null) : // automatic
                    clientCertificates?.Count > 0 ? new ClientCertificateProvider(clientCertificates) : // manual with certs
                    null; // manual without certs
                IntPtr userPointer = IntPtr.Zero;
                if (certProvider != null)
                {
                    // The client cert provider needs to be passed through to the callback, and thus
                    // we create a GCHandle to keep it rooted.  This handle needs to be cleaned up
                    // when the request has completed, and a simple and pay-for-play way to do that
                    // is by cleaning it up in a continuation off of the request.
                    userPointer = GCHandle.ToIntPtr(certProvider._gcHandle);
                    easy.Task.ContinueWith((_, state) => ((IDisposable)state).Dispose(), certProvider,
                        CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default);
                }

                // Register the callback with libcurl.  We need to register even if there's no user-provided
                // server callback and even if there are no client certificates, because we support verifying
                // server certificates against more than those known to OpenSSL.
                CURLcode answer = easy.SetSslCtxCallback(s_sslCtxCallback, userPointer);
                switch (answer)
                {
                    case CURLcode.CURLE_OK:
                        // We successfully registered.  If we'll be invoking a user-provided callback to verify the server
                        // certificate as part of that, disable libcurl's verification of the host name.  The user's callback
                        // needs to be given the opportunity to examine the cert, and our logic will determine whether
                        // the host name matches and will inform the callback of that.
                        if (easy._handler.ServerCertificateValidationCallback != null)
                        {
                            easy.SetCurlOption(Interop.Http.CURLoption.CURLOPT_SSL_VERIFYHOST, 0); // don't verify the peer cert's hostname
                            // We don't change the SSL_VERIFYPEER setting, as setting it to 0 will cause
                            // SSL and libcurl to ignore the result of the server callback.
                        }

                        // The allowed SSL protocols will be set in the configuration callback.
                        break;

                    case CURLcode.CURLE_UNKNOWN_OPTION: // Curl 7.38 and prior
                    case CURLcode.CURLE_NOT_BUILT_IN:   // Curl 7.39 and later
                        // It's ok if we failed to register the callback if all of the defaults are in play
                        // with relation to handling of certificates.  But if that's not the case, failing to 
                        // register the callback will result in those options not being factored in, which is
                        // a significant enough error that we need to fail.
                        EventSourceTrace("CURLOPT_SSL_CTX_FUNCTION not supported: {0}", answer, easy: easy);
                        if (certProvider != null ||
                            easy._handler.ServerCertificateValidationCallback != null ||
                            easy._handler.CheckCertificateRevocationList)
                        {
                            throw new PlatformNotSupportedException(
                                SR.Format(SR.net_http_unix_invalid_certcallback_option, CurlVersionDescription, CurlSslVersionDescription));
                        }

                        // Since there won't be a callback to configure the allowed SSL protocols, configure them here.
                        SetSslVersion(easy);

                        break;

                    default:
                        ThrowIfCURLEError(answer);
                        break;
                }
            }
Example #33
0
            private static bool TryGetEasyRequest(IntPtr curlPtr, out EasyRequest easy)
            {
                Debug.Assert(curlPtr != IntPtr.Zero, "curlPtr is not null");

                IntPtr gcHandlePtr;
                CURLcode getInfoResult = Interop.Http.EasyGetInfoPointer(curlPtr, CURLINFO.CURLINFO_PRIVATE, out gcHandlePtr);
                if (getInfoResult == CURLcode.CURLE_OK)
                {
                    return MultiAgent.TryGetEasyRequestFromGCHandle(gcHandlePtr, out easy);
                }

                Debug.Fail($"Failed to get info on a completing easy handle: {getInfoResult}");
                easy = null;
                return false;
            }
Example #34
0
 internal CurlResponseStream(EasyRequest easy)
 {
     Debug.Assert(easy != null, "Expected non-null associated EasyRequest");
     _easy = easy;
 }
Example #35
0
        private static bool TryParseStatusLine(HttpResponseMessage response, string responseHeader, EasyRequest state)
        {
            if (!responseHeader.StartsWith(CurlResponseParseUtils.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();

            CurlResponseParseUtils.ReadStatusLine(response, responseHeader);
            state._isRedirect = state._handler.AutomaticRedirection &&
                                (response.StatusCode == HttpStatusCode.Redirect ||
                                 response.StatusCode == HttpStatusCode.RedirectKeepVerb ||
                                 response.StatusCode == HttpStatusCode.RedirectMethod);
            return(true);
        }
Example #36
0
 /// <summary>Requests that libcurl unpause the connection associated with this request.</summary>
 internal void RequestUnpause(EasyRequest easy)
 {
     VerboseTrace(easy: easy);
     Queue(new IncomingRequest { Easy = easy, Type = IncomingRequestType.Unpause });
 }
Example #37
0
            internal static void SetSslOptions(EasyRequest easy, ClientCertificateOption clientCertOption)
            {
                Debug.Assert(
                    clientCertOption == ClientCertificateOption.Automatic ||
                    clientCertOption == ClientCertificateOption.Manual);

                // Create a client certificate provider if client certs may be used.
                X509Certificate2Collection clientCertificates = easy._handler._clientCertificates;

                if (clientCertOption != ClientCertificateOption.Manual || clientCertificates?.Count > 0)
                {
                    // libcurl does not have an option of accepting a SecIdentityRef via an input option,
                    // only via writing it to a file and letting it load the PFX.
                    // This would require that a) we write said file, and b) that it contaminate the default
                    // keychain (because their PFX loader loads to the default keychain).
                    throw new PlatformNotSupportedException(SR.net_http_libcurl_clientcerts_notsupported_os);
                }

                // Revocation checking is always on for darwinssl (SecureTransport).
                // If any other backend is used and revocation is requested, we can't guarantee
                // that assertion.
                if (easy._handler.CheckCertificateRevocationList &&
                    !CurlSslVersionDescription.Equals(Interop.Http.SecureTransportDescription))
                {
                    throw new PlatformNotSupportedException(
                              SR.Format(
                                  SR.net_http_libcurl_revocation_notsupported_sslbackend,
                                  CurlVersionDescription,
                                  CurlSslVersionDescription,
                                  Interop.Http.SecureTransportDescription));
                }

                if (easy._handler.ServerCertificateCustomValidationCallback != null)
                {
                    // libcurl (as of 7.49.1) does not have any callback which can be registered which fires
                    // between the time that a TLS/SSL handshake has offered up the server certificate and the
                    // time that the HTTP request headers are written.  Were there any callback, the option
                    // CURLINFO_TLS_SSL_PTR could be queried (and the backend identifier validated to be
                    // CURLSSLBACKEND_DARWINSSL). Then the SecTrustRef could be extracted to build the chain,
                    // a la SslStream.
                    //
                    // Without the callback the matrix looks like:
                    // * If default-trusted and callback-would-trust: No difference (except side effects, like logging).
                    // * If default-trusted and callback-would-block: Data would have been sent in violation of user trust.
                    // * If not-default-trusted and callback-would-not-trust: No difference (except side effects).
                    // * If not-default-trusted and callback-would-trust: No data sent, which doesn't match user desires.
                    //
                    // Of the two "different" cases, sending when we shouldn't is worse, so that's the direction we
                    // have to cater to. So we'll use default trust, and throw on any custom callback.
                    //
                    // The situation where system trust fails can be remedied by including the certificate into the
                    // user's keychain and setting the SSL policy trust for it to "Always Trust".
                    // Similarly, the "block this" could be attained by setting the SSL policy for a cert in the
                    // keychain to "Never Trust".
                    //
                    // However, one case we can support is when we know all certificates will pass validation.
                    // We can detect a key case of that: whether DangerousAcceptAnyServerCertificateValidator was used.
                    if (easy.ServerCertificateValidationCallbackAcceptsAll)
                    {
                        EventSourceTrace("Warning: Disabling peer verification per {0}", nameof(HttpClientHandler.DangerousAcceptAnyServerCertificateValidator), easy: easy);
                        easy.SetCurlOption(Interop.Http.CURLoption.CURLOPT_SSL_VERIFYPEER, 0); // don't verify the peer

                        // Don't set CURLOPT_SSL_VERIFHOST to 0; doing so disables SNI with SecureTransport backend.
                        if (!CurlSslVersionDescription.Equals(Interop.Http.SecureTransportDescription))
                        {
                            easy.SetCurlOption(Interop.Http.CURLoption.CURLOPT_SSL_VERIFYHOST, 0); // don't verify the hostname
                        }
                    }
                    else
                    {
                        throw new PlatformNotSupportedException(SR.net_http_libcurl_callback_notsupported_os);
                    }
                }

                SetSslVersion(easy);
            }
 private bool FindActiveRequest(EasyRequest easy, out IntPtr gcHandlePtr, out ActiveRequest activeRequest)
 {
     // We maintain an IntPtr=>ActiveRequest mapping, which makes it cheap to look-up by GCHandle ptr but
     // expensive to look up by EasyRequest.  If we find this becoming a bottleneck, we can add a reverse
     // map that stores the other direction as well.
     foreach (KeyValuePair<IntPtr, ActiveRequest> pair in _activeOperations)
     {
         if (pair.Value.Easy == easy)
         {
             gcHandlePtr = pair.Key;
             activeRequest = pair.Value;
             return true;
         }
     }
     gcHandlePtr = IntPtr.Zero;
     activeRequest = default(ActiveRequest);
     return false;
 }
            private void ActivateNewRequest(SafeCurlMultiHandle multiHandle, EasyRequest easy)
            {
                Debug.Assert(easy != null, "We should never get a null request");
                Debug.Assert(easy._associatedMultiAgent == null, "New requests should not be associated with an agent yet");

                // If cancellation has been requested, complete the request proactively
                if (easy._cancellationToken.IsCancellationRequested)
                {
                    easy.FailRequest(new OperationCanceledException(easy._cancellationToken));
                    easy.Cleanup(); // no active processing remains, so cleanup
                    return;
                }

                // Otherwise, configure it.  Most of the configuration was already done when the EasyRequest
                // was created, but there's additional configuration we need to do specific to this
                // multi agent, specifically telling the easy request about its own GCHandle and setting
                // up callbacks for data processing.  Once it's configured, add it to the multi handle.
                GCHandle gcHandle = GCHandle.Alloc(easy);
                IntPtr gcHandlePtr = GCHandle.ToIntPtr(gcHandle);
                try
                {
                    easy._associatedMultiAgent = this;
                    easy.SetCurlOption(CURLoption.CURLOPT_PRIVATE, gcHandlePtr);
                    SetCurlCallbacks(easy, gcHandlePtr);
                    ThrowIfCURLMError(Interop.libcurl.curl_multi_add_handle(multiHandle, easy._easyHandle));
                }
                catch (Exception exc)
                {
                    gcHandle.Free();
                    easy.FailRequest(exc);
                    easy.Cleanup();  // no active processing remains, so cleanup
                    return;
                }

                // And if cancellation can be requested, hook up a cancellation callback.
                // This callback will put the easy request back into the queue, which will
                // ensure that a wake-up request has been issued.  When we pull
                // the easy request out of the request queue, we'll see that it's already
                // associated with this agent, meaning that it's a cancellation request,
                // and we'll deal with it appropriately.
                var cancellationReg = default(CancellationTokenRegistration);
                if (easy._cancellationToken.CanBeCanceled)
                {
                    cancellationReg = easy._cancellationToken.Register(s =>
                    {
                        var state = (Tuple<MultiAgent, EasyRequest>)s;
                        state.Item1.Queue(new IncomingRequest { Easy = state.Item2, Type = IncomingRequestType.Cancel });
                    }, Tuple.Create<MultiAgent, EasyRequest>(this, easy));
                }

                // Finally, add it to our map.
                _activeOperations.Add(
                    gcHandlePtr, 
                    new ActiveRequest { Easy = easy, CancellationRegistration = cancellationReg });
            }
Example #40
0
            /// <summary>
            /// Transfers up to <paramref name="length"/> data from the <paramref name="easy"/>'s
            /// request content (non-memory) stream to the buffer.
            /// </summary>
            /// <returns>The number of bytes transferred.</returns>
            private static size_t TransferDataFromRequestStream(IntPtr buffer, int length, EasyRequest easy)
            {
                MultiAgent multi = easy._associatedMultiAgent;

                // First check to see whether there's any data available from a previous asynchronous read request.
                // If there is, the transfer state's Task field will be non-null, with its Result representing
                // the number of bytes read.  The Buffer will then contain all of that read data.  If the Count
                // is 0, then this is the first time we're checking that Task, and so we populate the Count
                // from that read result.  After that, we can transfer as much data remains between Offset and
                // Count.  Multiple callbacks may pull from that one read.

                EasyRequest.SendTransferState sts = easy._sendTransferState;
                if (sts != null)
                {
                    // Is there a previous read that may still have data to be consumed?
                    if (sts._task != null)
                    {
                        Debug.Assert(sts._task.IsCompleted, "The task must have completed if we're getting called back.");

                        // Determine how many bytes were read on the last asynchronous read.
                        // If nothing was read, then we're done and can simply return 0 to indicate
                        // the end of the stream.
                        int bytesRead = sts._task.GetAwaiter().GetResult(); // will throw if read failed
                        Debug.Assert(bytesRead >= 0 && bytesRead <= sts._buffer.Length, "ReadAsync returned an invalid result length: " + bytesRead);
                        if (bytesRead == 0)
                        {
                            multi.VerboseTrace("End of stream from stored task", easy: easy);
                            sts.SetTaskOffsetCount(null, 0, 0);
                            return 0;
                        }

                        // If Count is still 0, then this is the first time after the task completed
                        // that we're examining the data: transfer the bytesRead to the Count.
                        if (sts._count == 0)
                        {
                            multi.VerboseTrace("ReadAsync completed with bytes: " + bytesRead, easy: easy);
                            sts._count = bytesRead;
                        }

                        // Now Offset and Count are both accurate.  Determine how much data we can copy to libcurl...
                        int availableData = sts._count - sts._offset;
                        Debug.Assert(availableData > 0, "There must be some data still available.");

                        // ... and copy as much of that as libcurl will allow.
                        int bytesToCopy = Math.Min(availableData, length);
                        Marshal.Copy(sts._buffer, sts._offset, buffer, bytesToCopy);
                        multi.VerboseTrace("Copied " + bytesToCopy + " bytes from request stream", easy: easy);

                        // Update the offset.  If we've gone through all of the data, reset the state 
                        // so that the next time we're called back we'll do a new read.
                        sts._offset += bytesToCopy;
                        Debug.Assert(sts._offset <= sts._count, "Offset should never exceed count");
                        if (sts._offset == sts._count)
                        {
                            sts.SetTaskOffsetCount(null, 0, 0);
                        }

                        // Return the amount of data copied
                        Debug.Assert(bytesToCopy > 0, "We should never return 0 bytes here.");
                        return (size_t)bytesToCopy;
                    }

                    // sts was non-null but sts.Task was null, meaning there was no previous task/data
                    // from which to satisfy any of this request.
                }
                else // sts == null
                {
                    // Allocate a transfer state object to use for the remainder of this request.
                    easy._sendTransferState = sts = new EasyRequest.SendTransferState();
                }

                Debug.Assert(sts != null, "By this point we should have a transfer object");
                Debug.Assert(sts._task == null, "There shouldn't be a task now.");
                Debug.Assert(sts._count == 0, "Count should be zero.");
                Debug.Assert(sts._offset == 0, "Offset should be zero.");

                // If we get here, there was no previously read data available to copy.
                // Initiate a new asynchronous read.
                Task<int> asyncRead = easy._requestContentStream.ReadAsyncInternal(
                    sts._buffer, 0, Math.Min(sts._buffer.Length, length), easy._cancellationToken);
                Debug.Assert(asyncRead != null, "Badly implemented stream returned a null task from ReadAsync");

                // Even though it's "Async", it's possible this read could complete synchronously or extremely quickly.  
                // Check to see if it did, in which case we can also satisfy the libcurl request synchronously in this callback.
                if (asyncRead.IsCompleted)
                {
                    multi.VerboseTrace("ReadAsync completed immediately", easy: easy);

                    // Get the amount of data read.
                    int bytesRead = asyncRead.GetAwaiter().GetResult(); // will throw if read failed
                    if (bytesRead == 0)
                    {
                        multi.VerboseTrace("End of stream from quick returning ReadAsync", easy: easy);
                        return 0;
                    }

                    // Copy as much as we can.
                    int bytesToCopy = Math.Min(bytesRead, length);
                    Debug.Assert(bytesToCopy > 0 && bytesToCopy <= sts._buffer.Length, "ReadAsync quickly returned an invalid result length: " + bytesToCopy);
                    Marshal.Copy(sts._buffer, 0, buffer, bytesToCopy);
                    multi.VerboseTrace("Copied " + bytesToCopy + " from quick returning ReadAsync", easy: easy);

                    // If we read more than we were able to copy, stash it away for the next read.
                    if (bytesToCopy < bytesRead)
                    {
                        multi.VerboseTrace("Stashing away " + (bytesRead - bytesToCopy) + " bytes for next read.", easy: easy);
                        sts.SetTaskOffsetCount(asyncRead, bytesToCopy, bytesRead);
                    }

                    // Return the number of bytes read.
                    return (size_t)bytesToCopy;
                }

                // Otherwise, the read completed asynchronously.  Store the task, and hook up a continuation 
                // such that the connection will be unpaused once the task completes.
                sts.SetTaskOffsetCount(asyncRead, 0, 0);
                asyncRead.ContinueWith((t, s) =>
                {
                    EasyRequest easyRef = (EasyRequest)s;
                    easyRef._associatedMultiAgent.RequestUnpause(easyRef);
                }, easy, CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default);

                // Then pause the connection.
                multi.VerboseTrace("Pausing the connection", easy: easy);
                return Interop.libcurl.CURL_READFUNC_PAUSE;
            }
Example #41
0
 private void ConfigureAndQueue(EasyRequest easy)
 {
     try
     {
         easy.InitializeCurl();
         _agent.Queue(new MultiAgent.IncomingRequest { Easy = easy, Type = MultiAgent.IncomingRequestType.New });
     }
     catch (Exception exc)
     {
         easy.FailRequest(exc);
         easy.Cleanup(); // no active processing remains, so we can cleanup
     }
 }
Example #42
0
        /// <summary>
        /// Loads the request's request content stream asynchronously and 
        /// then submits the request to the multi agent.
        /// </summary>
        private async Task<HttpResponseMessage> QueueOperationWithRequestContentAsync(EasyRequest easy)
        {
            Debug.Assert(easy._requestMessage.Content != null, "Expected request to have non-null request content");

            easy._requestContentStream = await easy._requestMessage.Content.ReadAsStreamAsync().ConfigureAwait(false);
            if (easy._cancellationToken.IsCancellationRequested)
            {
                easy.FailRequest(new OperationCanceledException(easy._cancellationToken));
                easy.Cleanup(); // no active processing remains, so we can cleanup
            }
            else
            {
                ConfigureAndQueue(easy);
            }
            return await easy.Task.ConfigureAwait(false);
        }
            private static bool TryGetEasyRequest(IntPtr curlPtr, out EasyRequest easy)
            {
                Debug.Assert(curlPtr != IntPtr.Zero, "curlPtr is not null");
                IntPtr gcHandlePtr;
                CURLcode getInfoResult = Interop.Http.EasyGetInfoPointer(curlPtr, CURLINFO.CURLINFO_PRIVATE, out gcHandlePtr);
                Debug.Assert(getInfoResult == CURLcode.CURLE_OK, "Failed to get info on a completing easy handle");
                if (getInfoResult == CURLcode.CURLE_OK)
                {
                    try
                    {
                        GCHandle handle = GCHandle.FromIntPtr(gcHandlePtr);
                        easy = (EasyRequest)handle.Target;
                        Debug.Assert(easy != null, "Expected non-null EasyRequest in GCHandle");
                        return easy != null;
                    }
                    catch (Exception e)
                    {
                        EventSourceTrace("Error getting state from GCHandle: {0}", e);
                        Debug.Fail($"Exception in {nameof(TryGetEasyRequest)}", e.ToString());
                    }
                }

                easy = null;
                return false;
            }
Example #44
0
            private static void SetSslVersion(EasyRequest easy, IntPtr sslCtx = default(IntPtr))
            {
                // Get the requested protocols.
                System.Security.Authentication.SslProtocols protocols = easy._handler.ActualSslProtocols;

                // We explicitly disallow choosing SSL2/3. Make sure they were filtered out.
                Debug.Assert((protocols & ~SecurityProtocol.AllowedSecurityProtocols) == 0, 
                    "Disallowed protocols should have been filtered out.");

                // libcurl supports options for either enabling all of the TLS1.* protocols or enabling 
                // just one of them; it doesn't currently support enabling two of the three, e.g. you can't 
                // pick TLS1.1 and TLS1.2 but not TLS1.0, but you can select just TLS1.2.
                Interop.Http.CurlSslVersion curlSslVersion;
                switch (protocols)
                {
                    case System.Security.Authentication.SslProtocols.Tls:
                        curlSslVersion = Interop.Http.CurlSslVersion.CURL_SSLVERSION_TLSv1_0;
                        break;
                    case System.Security.Authentication.SslProtocols.Tls11:
                        curlSslVersion = Interop.Http.CurlSslVersion.CURL_SSLVERSION_TLSv1_1;
                        break;
                    case System.Security.Authentication.SslProtocols.Tls12:
                        curlSslVersion = Interop.Http.CurlSslVersion.CURL_SSLVERSION_TLSv1_2;
                        break;

                    case System.Security.Authentication.SslProtocols.Tls | System.Security.Authentication.SslProtocols.Tls11 | System.Security.Authentication.SslProtocols.Tls12:
                        curlSslVersion = Interop.Http.CurlSslVersion.CURL_SSLVERSION_TLSv1;
                        break;

                    default:
                        throw new NotSupportedException(SR.net_securityprotocolnotsupported);
                }

                try
                {
                    easy.SetCurlOption(Interop.Http.CURLoption.CURLOPT_SSLVERSION, (long)curlSslVersion);
                }
                catch (CurlException e) when (e.HResult == (int)CURLcode.CURLE_UNKNOWN_OPTION)
                {
                    throw new NotSupportedException(SR.net_securityprotocolnotsupported, e);
                }
            }
Example #45
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 && !s_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);
            }

            if (_useCookie && _cookieContainer == null)
            {
                throw new InvalidOperationException(SR.net_http_invalid_cookiecontainer);
            }

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

            CheckDisposed();
            SetOperationStarted();

            // Do an initial cancellation check to avoid initiating the async operation if
            // cancellation has already been requested.  After this, we'll rely on CancellationToken.Register
            // to notify us of cancellation requests and shut down the operation if possible.
            if (cancellationToken.IsCancellationRequested)
            {
                return Task.FromCanceled<HttpResponseMessage>(cancellationToken);
            }

            // Create the easy request.  This associates the easy request with this handler and configures
            // it based on the settings configured for the handler.
            var easy = new EasyRequest(this, request, cancellationToken);

            // Submit the easy request to the multi agent.
            if (request.Content != null)
            {
                // If there is request content to be sent, preload the stream
                // and submit the request to the multi agent.  This is separated
                // out into a separate async method to avoid associated overheads
                // in the case where there is no request content stream.
                return QueueOperationWithRequestContentAsync(easy);
            }
            else
            {
                // Otherwise, just submit the request.
                ConfigureAndQueue(easy);
                return easy.Task;
            }
        }
            private void FinishRequest(EasyRequest completedOperation, int messageResult)
            {
                VerboseTrace("messageResult: " + messageResult, easy: completedOperation);

                if (completedOperation._responseMessage.StatusCode != HttpStatusCode.Unauthorized && completedOperation._handler.PreAuthenticate)
                {
                    ulong availedAuth;
                    if (Interop.libcurl.curl_easy_getinfo(completedOperation._easyHandle, CURLINFO.CURLINFO_HTTPAUTH_AVAIL, out availedAuth) == CURLcode.CURLE_OK)
                    {
                        // TODO: fix locking in AddCredentialToCache
                        completedOperation._handler.AddCredentialToCache(
                            completedOperation._requestMessage.RequestUri, availedAuth, completedOperation._networkCredential);
                    }
                    // Ignore errors: no need to fail for the sake of putting the credentials into the cache
                }

                switch (messageResult)
                {
                    case CURLcode.CURLE_OK:
                        completedOperation.EnsureResponseMessagePublished();
                        break;
                    default:
                        completedOperation.FailRequest(CreateHttpRequestException(new CurlException(messageResult, isMulti: false)));
                        break;
                }

                // At this point, we've completed processing the entire request, either due to error
                // or due to completing the entire response.
                completedOperation.Cleanup();
            }
            private static void SetCurlCallbacks(EasyRequest easy, IntPtr easyGCHandle)
            {
                // Add callback for processing headers
                easy.SetCurlOption(CURLoption.CURLOPT_HEADERFUNCTION, s_receiveHeadersCallback);
                easy.SetCurlOption(CURLoption.CURLOPT_HEADERDATA, easyGCHandle);

                // If we're sending data as part of the request, add callbacks for sending request data
                if (easy._requestMessage.Content != null)
                {
                    easy.SetCurlOption(CURLoption.CURLOPT_READFUNCTION, s_sendCallback);
                    easy.SetCurlOption(CURLoption.CURLOPT_READDATA, easyGCHandle);

                    easy.SetCurlOption(CURLoption.CURLOPT_SEEKFUNCTION, s_seekCallback);
                    easy.SetCurlOption(CURLoption.CURLOPT_SEEKDATA, easyGCHandle);
                }

                // If we're expecting any data in response, add a callback for receiving body data
                if (easy._requestMessage.Method != HttpMethod.Head)
                {
                    easy.SetCurlOption(CURLoption.CURLOPT_WRITEFUNCTION, s_receiveBodyCallback);
                    easy.SetCurlOption(CURLoption.CURLOPT_WRITEDATA, easyGCHandle);
                }
            }
Example #48
0
        private static bool TryParseStatusLine(HttpResponseMessage response, string responseHeader, EasyRequest state)
        {
            if (!responseHeader.StartsWith(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 = HttpPrefix.Length;
            int versionIndex = prefixLength + 2;

            if ((versionIndex < responseHeaderLength) && (responseHeader[prefixLength] == '1') && (responseHeader[prefixLength + 1] == '.'))
            {
                response.Version =
                    responseHeader[versionIndex] == '1' ? HttpVersion.Version11 :
                    responseHeader[versionIndex] == '0' ? HttpVersion.Version10 :
                    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.SetCurlOption(CURLoption.CURLOPT_HTTPAUTH, CURLAUTH.None);
                        state.SetCurlOption(CURLoption.CURLOPT_USERNAME, IntPtr.Zero);
                        state.SetCurlOption(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;
        }
Example #49
0
            private void FinishRequest(EasyRequest completedOperation, CURLcode messageResult)
            {
                VerboseTrace("messageResult: " + messageResult, easy: completedOperation);

                if (completedOperation._responseMessage.StatusCode != HttpStatusCode.Unauthorized)
                {
                    if (completedOperation._handler.PreAuthenticate)
                    {
                        long authAvailable;
                        if (Interop.Http.EasyGetInfoLong(completedOperation._easyHandle, CURLINFO.CURLINFO_HTTPAUTH_AVAIL, out authAvailable) == CURLcode.CURLE_OK)
                        {
                            completedOperation._handler.AddCredentialToCache(
                               completedOperation._requestMessage.RequestUri, (CURLAUTH)authAvailable, completedOperation._networkCredential);
                        }
                        // Ignore errors: no need to fail for the sake of putting the credentials into the cache
                    }

                    completedOperation._handler.AddResponseCookies(
                        completedOperation._requestMessage.RequestUri, completedOperation._responseMessage);
                }

                // Complete or fail the request
                try
                {
                    bool unsupportedProtocolRedirect = messageResult == CURLcode.CURLE_UNSUPPORTED_PROTOCOL && completedOperation._isRedirect;
                    if (!unsupportedProtocolRedirect)
                    {
                        ThrowIfCURLEError(messageResult);
                    }
                    completedOperation.EnsureResponseMessagePublished();
                }
                catch (Exception exc)
                {
                    completedOperation.FailRequest(exc);
                }

                // At this point, we've completed processing the entire request, either due to error
                // or due to completing the entire response.
                completedOperation.Cleanup();
            }
Example #50
0
        private static bool TryParseStatusLine(HttpResponseMessage response, string responseHeader, EasyRequest state)
        {
            if (!responseHeader.StartsWith(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 = HttpPrefix.Length;
            int versionIndex = prefixLength + 2;

            if ((versionIndex < responseHeaderLength) && (responseHeader[prefixLength] == '1') && (responseHeader[prefixLength + 1] == '.'))
            {
                response.Version =
                    responseHeader[versionIndex] == '1' ? HttpVersion.Version11 :
                    responseHeader[versionIndex] == '0' ? HttpVersion.Version10 :
                    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;

                    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);
                    }
                    state._isRedirect = state._handler.AutomaticRedirection &&
                         (response.StatusCode == HttpStatusCode.Redirect ||
                         response.StatusCode == HttpStatusCode.RedirectKeepVerb ||
                         response.StatusCode == HttpStatusCode.RedirectMethod) ;
                }
            }

            return true;
        }
Example #51
0
 private void VerboseTraceIf(bool condition, string text = null, [CallerMemberName] string memberName = null, EasyRequest easy = null)
 {
     if (condition)
     {
         CurlHandler.VerboseTrace(text, memberName, easy, agent: this);
     }
 }
Example #52
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 == UriSchemeHttps)
            {
                if (!s_supportsSSL)
                {
                    throw new PlatformNotSupportedException(SR.net_http_unix_https_support_unavailable_libcurl);
                }
            }
            else
            {
                Debug.Assert(request.RequestUri.Scheme == UriSchemeHttp, "HttpClient expected to validate scheme as http or https.");
            }

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

            if (_useCookie && _cookieContainer == null)
            {
                throw new InvalidOperationException(SR.net_http_invalid_cookiecontainer);
            }

            CheckDisposed();
            SetOperationStarted();

            // Do an initial cancellation check to avoid initiating the async operation if
            // cancellation has already been requested.  After this, we'll rely on CancellationToken.Register
            // to notify us of cancellation requests and shut down the operation if possible.
            if (cancellationToken.IsCancellationRequested)
            {
                return Task.FromCanceled<HttpResponseMessage>(cancellationToken);
            }

            // Create the easy request.  This associates the easy request with this handler and configures
            // it based on the settings configured for the handler.
            var easy = new EasyRequest(this, request, cancellationToken);
            try
            {
                easy.InitializeCurl();
                if (easy._requestContentStream != null)
                {
                    easy._requestContentStream.Run();
                }
                _agent.Queue(new MultiAgent.IncomingRequest { Easy = easy, Type = MultiAgent.IncomingRequestType.New });
            }
            catch (Exception exc)
            {
                easy.FailRequest(exc);
                easy.Cleanup(); // no active processing remains, so we can cleanup
            }
            return easy.Task;
        }
            private void DeactivateActiveRequest(
                SafeCurlMultiHandle multiHandle, EasyRequest easy, 
                IntPtr gcHandlePtr, CancellationTokenRegistration cancellationRegistration)
            {
                // Remove the operation from the multi handle so we can shut down the multi handle cleanly
                int removeResult = Interop.libcurl.curl_multi_remove_handle(multiHandle, easy._easyHandle);
                Debug.Assert(removeResult == CURLMcode.CURLM_OK, "Failed to remove easy handle"); // ignore cleanup errors in release

                // Release the associated GCHandle so that it's not kept alive forever
                if (gcHandlePtr != IntPtr.Zero)
                {
                    try
                    {
                        GCHandle.FromIntPtr(gcHandlePtr).Free();
                        _activeOperations.Remove(gcHandlePtr);
                    }
                    catch (InvalidOperationException)
                    {
                        Debug.Fail("Couldn't get/free the GCHandle for an active operation while shutting down due to failure");
                    }
                }

                // Undo cancellation registration
                cancellationRegistration.Dispose();
            }
Example #54
0
        private static void VerboseTrace(string text = null, [CallerMemberName] string memberName = null, EasyRequest easy = null, MultiAgent agent = null)
        {
            // If we weren't handed a multi agent, see if we can get one from the EasyRequest
            if (agent == null && easy != null && easy._associatedMultiAgent != null)
            {
                agent = easy._associatedMultiAgent;
            }
            int? agentId = agent != null ? agent.RunningWorkerId : null;

            // Get an ID string that provides info about which MultiAgent worker and which EasyRequest this trace is about
            string ids = "";
            if (agentId != null || easy != null)
            {
                ids = "(" +
                    (agentId != null ? "M#" + agentId : "") +
                    (agentId != null && easy != null ? ", " : "") +
                    (easy != null ? "E#" + easy.Task.Id : "") +
                    ")";
            }

            // Create the message and trace it out
            string msg = string.Format("[{0, -30}]{1, -16}: {2}", memberName, ids, text);
            Interop.Sys.PrintF("%s\n", msg);
        }
            private void FindAndFailActiveRequest(SafeCurlMultiHandle multiHandle, EasyRequest easy, Exception error)
            {
                VerboseTrace("Error: " + error.Message, easy: easy);

                IntPtr gcHandlePtr;
                ActiveRequest activeRequest;
                if (FindActiveRequest(easy, out gcHandlePtr, out activeRequest))
                {
                    DeactivateActiveRequest(multiHandle, easy, gcHandlePtr, activeRequest.CancellationRegistration);
                    easy.FailRequest(error);
                    easy.Cleanup(); // no active processing remains, so we can cleanup
                }
                else
                {
                    Debug.Assert(easy.Task.IsCompleted, "We should only not be able to find the request if it failed or we started to send back the response.");
                }
            }
Example #56
0
 private static void VerboseTraceIf(bool condition, string text = null, [CallerMemberName] string memberName = null, EasyRequest easy = null)
 {
     if (condition)
     {
         VerboseTrace(text, memberName, easy, agent: null);
     }
 }
            private static bool TryGetEasyRequestFromContext(IntPtr context, out EasyRequest easy)
            {
                // Get the EasyRequest from the context
                try
                {
                    GCHandle handle = GCHandle.FromIntPtr(context);
                    easy = (EasyRequest)handle.Target;
                    Debug.Assert(easy != null, "Expected non-null EasyRequest in GCHandle");
                    return easy != null;
                }
                catch (InvalidCastException)
                {
                    Debug.Fail("EasyRequest wasn't the GCHandle's Target");
                }
                catch (InvalidOperationException)
                {
                    Debug.Fail("Invalid GCHandle");
                }

                easy = null;
                return false;
            }
Example #58
0
        private static bool TryParseStatusLine(HttpResponseMessage response, string responseHeader, EasyRequest state)
        {
            if (!responseHeader.StartsWith(CurlResponseParseUtils.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();

            CurlResponseParseUtils.ReadStatusLine(response, responseHeader);
            state._isRedirect = state._handler.AutomaticRedirection &&
                         (response.StatusCode == HttpStatusCode.Redirect ||
                         response.StatusCode == HttpStatusCode.RedirectKeepVerb ||
                         response.StatusCode == HttpStatusCode.RedirectMethod) ;
            return true;
        }
 private void VerboseTrace(string text = null, [CallerMemberName] string memberName = null, EasyRequest easy = null)
 {
     CurlHandler.VerboseTrace(text, memberName, easy, agent: this);
 }
Example #60
0
        private static void HandleRedirectLocationHeader(EasyRequest state, string locationValue)
        {
            Debug.Assert(state._isRedirect);
            Debug.Assert(state._handler.AutomaticRedirection);

            string location = locationValue.Trim();
            //only for absolute redirects
            Uri forwardUri;
            if (Uri.TryCreate(location, UriKind.RelativeOrAbsolute, out forwardUri) && forwardUri.IsAbsoluteUri)
            {
                KeyValuePair<NetworkCredential, CURLAUTH> ncAndScheme = GetCredentials(state._handler.Credentials as CredentialCache, forwardUri);
                if (ncAndScheme.Key != null)
                {
                    state.SetCredentialsOptions(ncAndScheme);
                }
                else
                {
                    state.SetCurlOption(CURLoption.CURLOPT_USERNAME, IntPtr.Zero);
                    state.SetCurlOption(CURLoption.CURLOPT_PASSWORD, IntPtr.Zero);
                }

                // reset proxy - it is possible that the proxy has different credentials for the new URI
                state.SetProxyOptions(forwardUri);

                if (state._handler._useCookie)
                {
                    // reset cookies.
                    state.SetCurlOption(CURLoption.CURLOPT_COOKIE, IntPtr.Zero);

                    // set cookies again
                    state.SetCookieOption(forwardUri);
                }
            }
        }