private static void RemoveEasyHandle(SafeCurlMultiHandle multiHandle, GCHandle stateHandle, bool onMultiStack) { RequestCompletionSource state = (RequestCompletionSource)stateHandle.Target; SafeCurlHandle requestHandle = state.RequestHandle; if (onMultiStack) { lock (multiHandle) { Interop.libcurl.curl_multi_remove_handle(multiHandle, requestHandle); } state.SessionHandle = null; requestHandle.DangerousRelease(); } if (!state.RequestHeaderHandle.IsInvalid) { SafeCurlSlistHandle headerHandle = state.RequestHeaderHandle; SafeCurlSlistHandle.DisposeAndClearHandle(ref headerHandle); } SafeCurlHandle.DisposeAndClearHandle(ref requestHandle); stateHandle.Free(); }
private void AddEasyHandle(RequestCompletionSource state) { bool gotReference = false; SafeCurlHandle requestHandle = state.RequestHandle; try { requestHandle.DangerousAddRef(ref gotReference); lock (_multiHandle) { int result = Interop.libcurl.curl_multi_add_handle(_multiHandle, requestHandle); if (result != CURLcode.CURLE_OK) { throw new HttpRequestException(SR.net_http_client_execution_error, GetCurlException(result, true)); } } state.SessionHandle = _multiHandle; // Note that we are deliberately not decreasing the ref counts of // the multi and easy handles since that will be done in RemoveEasyHandle // when the request is completed and the handles are used in an // unmanaged context till then // TODO: Investigate if SafeCurlHandle is really useful since we are not // avoiding any leaks due to the ref count increment } catch (Exception) { if (gotReference) { requestHandle.DangerousRelease(); } throw; } }
private static void RemoveEasyHandle(SafeCurlMultiHandle multiHandle, RequestCompletionSource state, bool onMultiStack) { SafeCurlHandle requestHandle = state.RequestHandle; if (onMultiStack) { Debug.Assert(!requestHandle.IsInvalid); lock (multiHandle) { Interop.libcurl.curl_multi_remove_handle(multiHandle, requestHandle); multiHandle.RequestCount = multiHandle.RequestCount - 1; multiHandle.SignalFdSetChange(state.SocketFd, true); } state.SessionHandle.DangerousRelease(); state.SessionHandle = null; requestHandle.DangerousRelease(); } if (state.RequestContentStream != null) { state.RequestContentStream.Dispose(); } if (!state.RequestHeaderHandle.IsInvalid) { SafeCurlSlistHandle headerHandle = state.RequestHeaderHandle; SafeCurlSlistHandle.DisposeAndClearHandle(ref headerHandle); } if (!requestHandle.IsInvalid) { SafeCurlHandle.DisposeAndClearHandle(ref requestHandle); } }
public EasyRequest(CurlHandler handler, HttpRequestMessage requestMessage, CancellationToken cancellationToken) : base(TaskCreationOptions.RunContinuationsAsynchronously) { // Store supplied arguments _handler = handler; _requestMessage = requestMessage; _responseMessage = new CurlResponseMessage(requestMessage); _cancellationToken = cancellationToken; // Create the underlying easy handle _easyHandle = Interop.libcurl.curl_easy_init(); if (_easyHandle.IsInvalid) { throw new OutOfMemoryException(); } // Configure the easy handle SetUrl(); SetDebugging(); SetMultithreading(); SetRedirection(); SetVerb(); SetDecompressionOptions(); SetProxyOptions(); SetCredentialsOptions(); SetCookieOption(); SetRequestHeaders(); }
public static void DisposeAndClearHandle(ref SafeCurlHandle curlHandle) { if (curlHandle != null) { curlHandle.Dispose(); curlHandle = null; } }
private SafeCurlHandle CreateRequestHandle(RequestCompletionSource state, GCHandle stateHandle) { // TODO: If this impacts perf, optimize using a handle pool SafeCurlHandle requestHandle = Interop.libcurl.curl_easy_init(); if (requestHandle.IsInvalid) { throw new HttpRequestException(SR.net_http_client_execution_error); } SetCurlOption(requestHandle, CURLoption.CURLOPT_URL, state.RequestMessage.RequestUri.AbsoluteUri); if (_automaticRedirection) { SetCurlOption(requestHandle, CURLoption.CURLOPT_FOLLOWLOCATION, 1L); // Set maximum automatic redirection option SetCurlOption(requestHandle, CURLoption.CURLOPT_MAXREDIRS, _maxAutomaticRedirections); } if (state.RequestMessage.Method == HttpMethod.Put) { SetCurlOption(requestHandle, CURLoption.CURLOPT_UPLOAD, 1L); } else if (state.RequestMessage.Method == HttpMethod.Head) { SetCurlOption(requestHandle, CURLoption.CURLOPT_NOBODY, 1L); } else if (state.RequestMessage.Method == HttpMethod.Post) { SetCurlOption(requestHandle, CURLoption.CURLOPT_POST, 1L); if (state.RequestMessage.Content == null) { SetCurlOption(requestHandle, CURLoption.CURLOPT_POSTFIELDSIZE, 0L); SetCurlOption(requestHandle, CURLoption.CURLOPT_COPYPOSTFIELDS, String.Empty); } } IntPtr statePtr = GCHandle.ToIntPtr(stateHandle); SetCurlOption(requestHandle, CURLoption.CURLOPT_PRIVATE, statePtr); SetCurlCallbacks(requestHandle, state.RequestMessage, statePtr); if (_supportsAutomaticDecompression) { SetRequestHandleDecompressionOptions(requestHandle); } SetProxyOptions(requestHandle, state.RequestMessage.RequestUri); SetRequestHandleCredentialsOptions(requestHandle, state); SetCookieOption(requestHandle, state.RequestMessage.RequestUri); state.RequestHeaderHandle = SetRequestHeaders(requestHandle, state.RequestMessage); return(requestHandle); }
private void SetCurlOption(SafeCurlHandle handle, int option, IntPtr value) { int result = Interop.libcurl.curl_easy_setopt(handle, option, value); if (result != CURLcode.CURLE_OK) { throw new HttpRequestException(SR.net_http_client_execution_error, GetCurlException(result)); } }
private void SetProxyOptions(SafeCurlHandle requestHandle, Uri requestUri) { if (_proxyPolicy == ProxyUsePolicy.DoNotUseProxy) { SetCurlOption(requestHandle, CURLoption.CURLOPT_PROXY, string.Empty); return; } if ((_proxyPolicy == ProxyUsePolicy.UseDefaultProxy) || (Proxy == null)) { return; } Debug.Assert((Proxy != null) && (_proxyPolicy == ProxyUsePolicy.UseCustomProxy)); if (Proxy.IsBypassed(requestUri)) { SetCurlOption(requestHandle, CURLoption.CURLOPT_PROXY, string.Empty); return; } var proxyUri = Proxy.GetProxy(requestUri); if (proxyUri == null) { return; } SetCurlOption(requestHandle, CURLoption.CURLOPT_PROXYTYPE, CURLProxyType.CURLPROXY_HTTP); SetCurlOption(requestHandle, CURLoption.CURLOPT_PROXY, proxyUri.AbsoluteUri); SetCurlOption(requestHandle, CURLoption.CURLOPT_PROXYPORT, proxyUri.Port); NetworkCredential credentials = GetCredentials(Proxy.Credentials, requestUri); if (credentials != null) { if (string.IsNullOrEmpty(credentials.UserName)) { throw new ArgumentException(SR.net_http_argument_empty_string, "UserName"); } string credentialText; if (string.IsNullOrEmpty(credentials.Domain)) { credentialText = string.Format("{0}:{1}", credentials.UserName, credentials.Password); } else { credentialText = string.Format("{2}\\{0}:{1}", credentials.UserName, credentials.Password, credentials.Domain); } SetCurlOption(requestHandle, CURLoption.CURLOPT_PROXYUSERPWD, credentialText); } }
private void SetRequestHandleDecompressionOptions(SafeCurlHandle requestHandle) { bool gzip = (AutomaticDecompression & DecompressionMethods.GZip) != 0; bool deflate = (AutomaticDecompression & DecompressionMethods.Deflate) != 0; if (gzip || deflate) { string encoding = (gzip && deflate) ? EncodingNameGzip + "," + EncodingNameDeflate : gzip ? EncodingNameGzip : EncodingNameDeflate; SetCurlOption(requestHandle, CURLoption.CURLOPT_ACCEPTENCODING, encoding); } }
private void SetCookieOption(SafeCurlHandle requestHandle, Uri requestUri) { if (!_useCookie) { return; } else if (_cookieContainer == null) { throw new InvalidOperationException(SR.net_http_invalid_cookiecontainer); } string cookieValues = _cookieContainer.GetCookieHeader(requestUri); if (cookieValues != null) { SetCurlOption(requestHandle, CURLoption.CURLOPT_COOKIE, cookieValues); } }
private void AddEasyHandle(RequestCompletionSource state) { bool gotReference = false; SafeCurlHandle requestHandle = state.RequestHandle; try { requestHandle.DangerousAddRef(ref gotReference); lock (_multiHandle) { int result = Interop.libcurl.curl_multi_add_handle(_multiHandle, requestHandle); if (result != CURLcode.CURLE_OK) { throw new HttpRequestException(SR.net_http_client_execution_error, GetCurlException(result, true)); } state.SessionHandle = _multiHandle; _multiHandle.RequestCount = _multiHandle.RequestCount + 1; if (_multiHandle.PollCancelled) { // TODO: Create single polling thread for all HttpClientHandler objects Task.Factory.StartNew(s => PollFunction(((CurlHandler)s)._multiHandle), this, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default); _multiHandle.PollCancelled = false; } bool ignored = false; _multiHandle.DangerousAddRef(ref ignored); } // Note that we are deliberately not decreasing the ref counts of // the multi and easy handles since that will be done in RemoveEasyHandle // when the request is completed and the handles are used in an // unmanaged context till then // TODO: Investigate if SafeCurlHandle is really useful since we are not // avoiding any leaks due to the ref count increment } catch (Exception) { if (gotReference) { requestHandle.DangerousRelease(); } throw; } }
private void SetCurlCallbacks(SafeCurlHandle requestHandle, HttpRequestMessage request, IntPtr stateHandle) { SetCurlOption(requestHandle, CURLoption.CURLOPT_HEADERDATA, stateHandle); SetCurlOption(requestHandle, CURLoption.CURLOPT_HEADERFUNCTION, s_receiveHeadersCallback); if (request.Method != HttpMethod.Head) { SetCurlOption(requestHandle, CURLoption.CURLOPT_WRITEDATA, stateHandle); unsafe { SetCurlOption(requestHandle, CURLoption.CURLOPT_WRITEFUNCTION, s_receiveBodyCallback); } } if (request.Content != null) { SetCurlOption(requestHandle, CURLoption.CURLOPT_READDATA, stateHandle); SetCurlOption(requestHandle, CURLoption.CURLOPT_READFUNCTION, s_sendCallback); SetCurlOption(requestHandle, CURLoption.CURLOPT_IOCTLDATA, stateHandle); SetCurlOption(requestHandle, CURLoption.CURLOPT_IOCTLFUNCTION, s_sendIoCtlCallback); } }
private void SetRequestHandleCredentialsOptions(SafeCurlHandle requestHandle, RequestCompletionSource state) { NetworkCredential credentials = GetNetworkCredentials(state.Handler._serverCredentials, state.RequestMessage.RequestUri); if (credentials != null) { string userName = string.IsNullOrEmpty(credentials.Domain) ? credentials.UserName : string.Format("{0}\\{1}", credentials.Domain, credentials.UserName); SetCurlOption(requestHandle, CURLoption.CURLOPT_USERNAME, userName); SetCurlOption(requestHandle, CURLoption.CURLOPT_HTTPAUTH, CURLAUTH.AuthAny); if (credentials.Password != null) { SetCurlOption(requestHandle, CURLoption.CURLOPT_PASSWORD, credentials.Password); } state.NetworkCredential = credentials; } }
private SafeCurlHandle CreateRequestHandle(RequestCompletionSource state, GCHandle stateHandle) { // TODO: If this impacts perf, optimize using a handle pool SafeCurlHandle requestHandle = Interop.libcurl.curl_easy_init(); if (requestHandle.IsInvalid) { throw new HttpRequestException(SR.net_http_client_execution_error); } SetCurlOption(requestHandle, CURLoption.CURLOPT_URL, state.RequestMessage.RequestUri.AbsoluteUri); if (_automaticRedirection) { SetCurlOption(requestHandle, CURLoption.CURLOPT_FOLLOWLOCATION, 1L); } if (state.RequestMessage.Content != null) { SetCurlOption(requestHandle, CURLoption.CURLOPT_UPLOAD, 1L); } if (state.RequestMessage.Method == HttpMethod.Head) { SetCurlOption(requestHandle, CURLoption.CURLOPT_NOBODY, 1L); } IntPtr statePtr = GCHandle.ToIntPtr(stateHandle); SetCurlOption(requestHandle, CURLoption.CURLOPT_PRIVATE, statePtr); SetCurlCallbacks(requestHandle, state.RequestMessage, statePtr); SetRequestHandleDecompressionOptions(requestHandle); SetProxyOptions(requestHandle, state.RequestMessage.RequestUri); state.RequestHeaderHandle = SetRequestHeaders(requestHandle, state.RequestMessage); // TODO: Handle other options return(requestHandle); }
/// <summary> /// Initialize the underlying libcurl support for this EasyRequest. /// This is separated out of the constructor so that we can take into account /// any additional configuration needed based on the request message /// after the EasyRequest is configured and so that error handling /// can be better handled in the caller. /// </summary> internal void InitializeCurl() { // Create the underlying easy handle SafeCurlHandle easyHandle = Interop.libcurl.curl_easy_init(); if (easyHandle.IsInvalid) { throw new OutOfMemoryException(); } _easyHandle = easyHandle; // Configure the handle SetUrl(); SetDebugging(); SetMultithreading(); SetRedirection(); SetVerb(); SetDecompressionOptions(); SetProxyOptions(_requestMessage.RequestUri); SetCredentialsOptions(_handler.GetNetworkCredentials(_handler._serverCredentials, _requestMessage.RequestUri)); SetCookieOption(_requestMessage.RequestUri); SetRequestHeaders(); }
private async void BeginRequest(RequestCompletionSource state) { SafeCurlHandle requestHandle = new SafeCurlHandle(); GCHandle stateHandle = new GCHandle(); try { // Prepare context objects state.ResponseMessage = new CurlResponseMessage(state.RequestMessage); stateHandle = GCHandle.Alloc(state); requestHandle = CreateRequestHandle(state, stateHandle); state.RequestHandle = requestHandle; if (state.RequestMessage.Content != null) { Stream requestContentStream = await state.RequestMessage.Content.ReadAsStreamAsync().ConfigureAwait(false); state.RequestContentStream = requestContentStream; if (state.CancellationToken.IsCancellationRequested) { RemoveEasyHandle(_multiHandle, stateHandle, state); state.TrySetCanceled(state.CancellationToken); return; } state.RequestContentBuffer = new byte[s_requestBufferSize]; } AddEasyHandle(state); } catch (Exception ex) { RemoveEasyHandle(_multiHandle, stateHandle, state); HandleAsyncException(state, ex); } }
private static byte[] DownloadAsset(string uri, ref TimeSpan remainingDownloadTime) { if (remainingDownloadTime <= TimeSpan.Zero) { return(null); } List <byte[]> dataPieces = new List <byte[]>(); using (Interop.libcurl.SafeCurlHandle curlHandle = Interop.libcurl.curl_easy_init()) { GCHandle gcHandle = GCHandle.Alloc(dataPieces); try { IntPtr dataHandlePtr = GCHandle.ToIntPtr(gcHandle); Interop.libcurl.curl_easy_setopt(curlHandle, Interop.libcurl.CURLoption.CURLOPT_URL, uri); Interop.libcurl.curl_easy_setopt(curlHandle, Interop.libcurl.CURLoption.CURLOPT_WRITEDATA, dataHandlePtr); Interop.libcurl.curl_easy_setopt(curlHandle, Interop.libcurl.CURLoption.CURLOPT_WRITEFUNCTION, s_writeCallback); Interop.libcurl.curl_easy_setopt(curlHandle, Interop.libcurl.CURLoption.CURLOPT_FOLLOWLOCATION, 1L); Stopwatch stopwatch = Stopwatch.StartNew(); int res = Interop.libcurl.curl_easy_perform(curlHandle); stopwatch.Stop(); // TimeSpan.Zero isn't a worrisome value on the subtraction, it only // means "no limit" on the original input. remainingDownloadTime -= stopwatch.Elapsed; if (res != Interop.libcurl.CURLcode.CURLE_OK) { return(null); } } finally { gcHandle.Free(); } } if (dataPieces.Count == 0) { return(null); } if (dataPieces.Count == 1) { return(dataPieces[0]); } int dataLen = 0; for (int i = 0; i < dataPieces.Count; i++) { dataLen += dataPieces[i].Length; } byte[] data = new byte[dataLen]; int offset = 0; for (int i = 0; i < dataPieces.Count; i++) { byte[] piece = dataPieces[i]; Buffer.BlockCopy(piece, 0, data, offset, piece.Length); offset += piece.Length; } return(data); }
private SafeCurlSlistHandle SetRequestHeaders(SafeCurlHandle handle, HttpRequestMessage request) { SafeCurlSlistHandle retVal = new SafeCurlSlistHandle(); if (request.Headers == null) { return(retVal); } HttpHeaders contentHeaders = null; if (request.Content != null) { SetChunkedModeForSend(request); // TODO: Content-Length header isn't getting correctly placed using ToString() // This is a bug in HttpContentHeaders that needs to be fixed. if (request.Content.Headers.ContentLength.HasValue) { long contentLength = request.Content.Headers.ContentLength.Value; request.Content.Headers.ContentLength = null; request.Content.Headers.ContentLength = contentLength; } contentHeaders = request.Content.Headers; } string[] allHeaders = HeaderUtilities.DumpHeaders(request.Headers, contentHeaders) .Split(s_headerDelimiters, StringSplitOptions.RemoveEmptyEntries); bool gotReference = false; try { retVal.DangerousAddRef(ref gotReference); IntPtr rawHandle = IntPtr.Zero; for (int i = 0; i < allHeaders.Length; i++) { string header = allHeaders[i].Trim(); if (header.Equals("{") || header.Equals("}")) { continue; } rawHandle = Interop.libcurl.curl_slist_append(rawHandle, header); retVal.SetHandle(rawHandle); } // Since libcurl always adds a Transfer-Encoding header, we need to explicitly block // it if caller specifically does not want to set the header if (request.Headers.TransferEncodingChunked.HasValue && !request.Headers.TransferEncodingChunked.Value) { rawHandle = Interop.libcurl.curl_slist_append(rawHandle, NoTransferEncoding); retVal.SetHandle(rawHandle); } if (!retVal.IsInvalid) { SetCurlOption(handle, CURLoption.CURLOPT_HTTPHEADER, rawHandle); } } finally { if (gotReference) { retVal.DangerousRelease(); } } return(retVal); }
private SafeCurlSlistHandle SetRequestHeaders(SafeCurlHandle handle, HttpRequestMessage request) { SafeCurlSlistHandle retVal = new SafeCurlSlistHandle(); if (request.Headers == null) { return(retVal); } HttpHeaders contentHeaders = null; if (request.Content != null) { SetChunkedModeForSend(request); // TODO: Content-Length header isn't getting correctly placed using ToString() // This is a bug in HttpContentHeaders that needs to be fixed. if (request.Content.Headers.ContentLength.HasValue) { long contentLength = request.Content.Headers.ContentLength.Value; request.Content.Headers.ContentLength = null; request.Content.Headers.ContentLength = contentLength; } contentHeaders = request.Content.Headers; } bool gotReference = false; try { retVal.DangerousAddRef(ref gotReference); IntPtr rawHandle = IntPtr.Zero; if (request.Headers != null) { // Add request headers AddRequestHeaders(request.Headers, retVal, ref rawHandle); } if (contentHeaders != null) { // Add content request headers AddRequestHeaders(contentHeaders, retVal, ref rawHandle); } // Since libcurl always adds a Transfer-Encoding header, we need to explicitly block // it if caller specifically does not want to set the header if (request.Headers.TransferEncodingChunked.HasValue && !request.Headers.TransferEncodingChunked.Value) { rawHandle = Interop.libcurl.curl_slist_append(rawHandle, NoTransferEncoding); if (rawHandle == null) { throw new HttpRequestException(SR.net_http_client_execution_error); } retVal.SetHandle(rawHandle); } if (!retVal.IsInvalid) { SetCurlOption(handle, CURLoption.CURLOPT_HTTPHEADER, rawHandle); } } finally { if (gotReference) { retVal.DangerousRelease(); } } return(retVal); }
private async void BeginRequest(RequestCompletionSource state) { SafeCurlHandle requestHandle = new SafeCurlHandle(); GCHandle stateHandle = new GCHandle(); bool needCleanup = false; try { // Prepare context objects state.ResponseMessage = new CurlResponseMessage(state.RequestMessage); stateHandle = GCHandle.Alloc(state); requestHandle = CreateRequestHandle(state, stateHandle); state.RequestHandle = requestHandle; needCleanup = true; if (state.CancellationToken.IsCancellationRequested) { state.TrySetCanceled(state.CancellationToken); return; } if (state.RequestMessage.Content != null) { Stream requestContentStream = await state.RequestMessage.Content.ReadAsStreamAsync().ConfigureAwait(false); if (state.CancellationToken.IsCancellationRequested) { state.TrySetCanceled(state.CancellationToken); return; } state.RequestContentStream = requestContentStream; state.RequestContentBuffer = new byte[s_requestBufferSize]; } AddEasyHandle(state); needCleanup = false; } catch (Exception ex) { HandleAsyncException(state, ex); } finally { if (needCleanup) { RemoveEasyHandle(_multiHandle, stateHandle, false); } else if (state.Task.IsCompleted) { if (stateHandle.IsAllocated) { stateHandle.Free(); } if (!requestHandle.IsInvalid) { SafeCurlHandle.DisposeAndClearHandle(ref requestHandle); } } } }