protected virtual Task <Stream> CreateContentReadStreamAsync() { TaskCompletionSource <Stream> tcs = new TaskCompletionSource <Stream>(); // By default just buffer the content to a memory stream. Derived classes can override this behavior // if there is a better way to retrieve the content as stream (e.g. byte array/string use a more efficient // way, like wrapping a read-only MemoryStream around the bytes/string) LoadIntoBufferAsync().ContinueWithStandard(task => { if (!HttpUtilities.HandleFaultsAndCancelation(task, tcs)) { tcs.TrySetResult(bufferedContent); } }); return(tcs.Task); }
private static void CheckBaseAddress(Uri baseAddress, string parameterName) { if (baseAddress == null) { return; // It's OK to not have a base address specified. } if (!baseAddress.IsAbsoluteUri) { throw new ArgumentException("The base address must be an absolute URI.", parameterName); } if (!HttpUtilities.IsHttpUri(baseAddress)) { throw new ArgumentException("Only 'http' and 'https' schemes are allowed.", parameterName); } }
private Task <Stream> ReadAsStreamAsyncCore() { TaskCompletionSource <Stream> tcs = new TaskCompletionSource <Stream>(this); CreateContentReadStreamAsync().ContinueWithStandard(tcs, (task, state) => { var innerTcs = (TaskCompletionSource <Stream>)state; var innerThis = (HttpContent)innerTcs.Task.AsyncState; if (!HttpUtilities.HandleFaultsAndCancelation(task, innerTcs)) { innerThis._contentReadStream = task.Result; innerTcs.TrySetResult(innerThis._contentReadStream); } }); return(tcs.Task); }
private static void CheckBaseAddress(Uri baseAddress, string parameterName) { if (baseAddress == null) { return; // It's OK to not have a base address specified. } if (!baseAddress.IsAbsoluteUri) { throw new ArgumentException(SR.net_http_client_absolute_baseaddress_required, parameterName); } if (!HttpUtilities.IsHttpUri(baseAddress)) { throw new ArgumentException(SR.net_http_client_http_baseaddress_required, parameterName); } }
private static string GetSslHostName(HttpRequestMessage request) { Uri uri = request.RequestUri; if (!HttpUtilities.IsSupportedSecureScheme(uri.Scheme)) { // Not using SSL. return(null); } string hostHeader = request.Headers.Host; if (hostHeader == null) { // No explicit Host header. Use host from uri. return(request.RequestUri.IdnHost); } // There is a host header. Use it, but first see if we need to trim off a port. int colonPos = hostHeader.IndexOf(':'); if (colonPos >= 0) { // There is colon, which could either be a port separator or a separator in // an IPv6 address. See if this is an IPv6 address; if it's not, use everything // before the colon as the host name, and if it is, use everything before the last // colon iff the last colon is after the end of the IPv6 address (otherwise it's a // part of the address). int ipV6AddressEnd = hostHeader.IndexOf(']'); if (ipV6AddressEnd == -1) { return(hostHeader.Substring(0, colonPos)); } else { colonPos = hostHeader.LastIndexOf(':'); if (colonPos > ipV6AddressEnd) { return(hostHeader.Substring(0, colonPos)); } } } return(hostHeader); }
public Task <byte[]> ReadAsByteArrayAsync() { CheckDisposed(); var tcs = new TaskCompletionSource <byte[]>(this); LoadIntoBufferAsync().ContinueWithStandard(tcs, (task, state) => { var innerTcs = (TaskCompletionSource <byte[]>)state; var innerThis = (HttpContent)innerTcs.Task.AsyncState; if (!HttpUtilities.HandleFaultsAndCancelation(task, innerTcs)) { innerTcs.TrySetResult(innerThis._bufferedContent.ToArray()); } }); return(tcs.Task); }
private async ValueTask <HttpConnection> CreateConnection(HttpRequestMessage request, HttpConnectionKey key, HttpConnectionPool pool) { Uri uri = request.RequestUri; Stream stream = await ConnectHelper.ConnectAsync(uri.IdnHost, uri.Port).ConfigureAwait(false); TransportContext transportContext = null; if (HttpUtilities.IsSupportedSecureScheme(uri.Scheme)) { SslStream sslStream = await EstablishSslConnection(uri.IdnHost, request, stream).ConfigureAwait(false); stream = sslStream; transportContext = sslStream.TransportContext; } return(new HttpConnection(pool, key, uri.IdnHost, stream, transportContext, false)); }
private Uri GetUriForRedirect(Uri requestUri, HttpResponseMessage response) { switch (response.StatusCode) { case HttpStatusCode.Moved: case HttpStatusCode.Found: case HttpStatusCode.SeeOther: case HttpStatusCode.TemporaryRedirect: case HttpStatusCode.MultipleChoices: break; default: return(null); } Uri location = response.Headers.Location; if (location == null) { return(null); } if (!location.IsAbsoluteUri) { location = new Uri(requestUri, location); } // Disallow automatic redirection from secure to non-secure schemes if (HttpUtilities.IsSupportedSecureScheme(requestUri.Scheme) && !HttpUtilities.IsSupportedSecureScheme(location.Scheme)) { if (NetEventSource.IsEnabled) { NetEventSource.Info(this, $"Insecure https to http redirect from {requestUri} to {location} blocked."); } return(null); } return(location); }
private Task <T> GetContentAsync <T>(Uri requestUri, HttpCompletionOption completionOption, T defaultValue, Func <HttpContent, Task <T> > readAs) { TaskCompletionSource <T> tcs = new TaskCompletionSource <T>(); GetAsync(requestUri, completionOption).ContinueWithStandard(requestTask => { if (HandleRequestFaultsAndCancelation(requestTask, tcs)) { return; } HttpResponseMessage response = requestTask.Result; if (response.Content == null) { tcs.TrySetResult(defaultValue); return; } try { readAs(response.Content).ContinueWithStandard(contentTask => { if (!HttpUtilities.HandleFaultsAndCancelation(contentTask, tcs)) { tcs.TrySetResult(contentTask.Result); } }); } catch (Exception ex) { tcs.TrySetException(ex); } }); return(tcs.Task); }
public Task <Stream> ReadAsStreamAsync() { CheckDisposed(); TaskCompletionSource <Stream> tcs = new TaskCompletionSource <Stream>(this); if (_contentReadStream == null && IsBuffered) { byte[] data = this.GetDataBuffer(_bufferedContent); // We can cast bufferedContent.Length to 'int' since the length will always be in the 'int' range // The .NET Framework doesn't support array lengths > int.MaxValue. Debug.Assert(_bufferedContent.Length <= (long)int.MaxValue); _contentReadStream = new MemoryStream(data, 0, (int)_bufferedContent.Length, false); } if (_contentReadStream != null) { tcs.TrySetResult(_contentReadStream); return(tcs.Task); } CreateContentReadStreamAsync().ContinueWithStandard(tcs, (task, state) => { var innerTcs = (TaskCompletionSource <Stream>)state; var innerThis = (HttpContent)innerTcs.Task.AsyncState; if (!HttpUtilities.HandleFaultsAndCancelation(task, innerTcs)) { innerThis._contentReadStream = task.Result; innerTcs.TrySetResult(innerThis._contentReadStream); } }); return(tcs.Task); }
private async Task <HttpResponseMessage> SendWithProxyAsync( Uri proxyUri, HttpRequestMessage request, CancellationToken cancellationToken) { if (proxyUri.Scheme != UriScheme.Http) { throw new InvalidOperationException(SR.net_http_invalid_proxy_scheme); } if (!HttpUtilities.IsSupportedNonSecureScheme(request.RequestUri.Scheme)) { // TODO #23136: Implement SSL tunneling through proxy throw new NotImplementedException("no support for SSL tunneling through proxy"); } HttpConnection connection = await GetOrCreateConnection(request, proxyUri, cancellationToken).ConfigureAwait(false); HttpResponseMessage response = await connection.SendAsync(request, cancellationToken).ConfigureAwait(false); // Handle proxy authentication if (response.StatusCode == HttpStatusCode.ProxyAuthenticationRequired) { foreach (AuthenticationHeaderValue h in response.Headers.ProxyAuthenticate) { // We only support Basic auth, ignore others if (h.Scheme == AuthenticationHelper.Basic) { NetworkCredential credential = _proxy.Credentials?.GetCredential(proxyUri, AuthenticationHelper.Basic) ?? _defaultCredentials?.GetCredential(proxyUri, AuthenticationHelper.Basic); if (credential != null) { response.Dispose(); request.Headers.ProxyAuthorization = new AuthenticationHeaderValue(AuthenticationHelper.Basic, AuthenticationHelper.GetBasicTokenForCredential(credential)); connection = await GetOrCreateConnection(request, proxyUri, cancellationToken).ConfigureAwait(false); response = await connection.SendAsync(request, cancellationToken).ConfigureAwait(false); } break; } else if (h.Scheme == AuthenticationHelper.Digest) { NetworkCredential credential = _proxy.Credentials?.GetCredential(proxyUri, AuthenticationHelper.Digest) ?? _defaultCredentials?.GetCredential(proxyUri, AuthenticationHelper.Digest); if (credential != null) { // Update digest response with new parameter from Proxy-Authenticate AuthenticationHelper.DigestResponse digestResponse = new AuthenticationHelper.DigestResponse(h.Parameter); if (await AuthenticationHelper.TrySetDigestAuthToken(request, credential, digestResponse, HttpKnownHeaderNames.ProxyAuthorization).ConfigureAwait(false)) { response.Dispose(); response = await _innerHandler.SendAsync(request, cancellationToken).ConfigureAwait(false); // Retry in case of nonce timeout in server. if (response.StatusCode == HttpStatusCode.Unauthorized) { foreach (AuthenticationHeaderValue ahv in response.Headers.ProxyAuthenticate) { if (ahv.Scheme == AuthenticationHelper.Digest) { digestResponse = new AuthenticationHelper.DigestResponse(ahv.Parameter); if (AuthenticationHelper.IsServerNonceStale(digestResponse) && await AuthenticationHelper.TrySetDigestAuthToken(request, credential, digestResponse, HttpKnownHeaderNames.ProxyAuthorization).ConfigureAwait(false)) { response.Dispose(); response = await _innerHandler.SendAsync(request, cancellationToken).ConfigureAwait(false); } break; } } } } break; } } } } return(response); }
protected internal override async Task <HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { HttpResponseMessage response; uint redirectCount = 0; while (true) { // Just as with WinHttpHandler and CurlHandler, for security reasons, we drop the server credential if it is // anything other than a CredentialCache on redirection. We allow credentials in a CredentialCache since they // are specifically tied to URIs. ICredentials currentCredential = redirectCount > 0 ? _credentials as CredentialCache : _credentials; if (currentCredential != null && _preAuthenticate) { // Try using previous digest response WWWAuthenticate header if (_digestResponse != null) { await AuthenticationHelper.TrySetDigestAuthToken(request, currentCredential, _digestResponse, HttpKnownHeaderNames.Authorization).ConfigureAwait(false); } else { AuthenticationHelper.TrySetBasicAuthToken(request, currentCredential); } } response = await _innerHandler.SendAsync(request, cancellationToken).ConfigureAwait(false); if (currentCredential != null && !_preAuthenticate && response.StatusCode == HttpStatusCode.Unauthorized) { AuthenticationHeaderValue selectedAuth = GetSupportedAuthScheme(response.Headers.WwwAuthenticate); if (selectedAuth != null) { switch (selectedAuth.Scheme) { case AuthenticationHelper.Digest: // Update digest response with new parameter from WWWAuthenticate _digestResponse = new AuthenticationHelper.DigestResponse(selectedAuth.Parameter); if (await AuthenticationHelper.TrySetDigestAuthToken(request, currentCredential, _digestResponse, HttpKnownHeaderNames.Authorization).ConfigureAwait(false)) { response.Dispose(); response = await _innerHandler.SendAsync(request, cancellationToken).ConfigureAwait(false); // Retry in case of nonce timeout in server. if (response.StatusCode == HttpStatusCode.Unauthorized) { foreach (AuthenticationHeaderValue ahv in response.Headers.WwwAuthenticate) { if (ahv.Scheme == AuthenticationHelper.Digest) { _digestResponse = new AuthenticationHelper.DigestResponse(ahv.Parameter); if (AuthenticationHelper.IsServerNonceStale(_digestResponse) && await AuthenticationHelper.TrySetDigestAuthToken(request, currentCredential, _digestResponse, HttpKnownHeaderNames.Authorization).ConfigureAwait(false)) { response.Dispose(); response = await _innerHandler.SendAsync(request, cancellationToken).ConfigureAwait(false); } break; } } } } break; case AuthenticationHelper.Basic: if (AuthenticationHelper.TrySetBasicAuthToken(request, currentCredential)) { response.Dispose(); response = await _innerHandler.SendAsync(request, cancellationToken).ConfigureAwait(false); } break; } } } if (!RequestNeedsRedirect(response)) { break; } // Clear the authorization header, if the request requires redirect. request.Headers.Authorization = null; Uri location = response.Headers.Location; if (location == null) { // No location header. Nothing to redirect to. break; } if (!location.IsAbsoluteUri) { location = new Uri(request.RequestUri, location); } // Disallow automatic redirection from secure to non-secure schemes bool allowed = (HttpUtilities.IsSupportedNonSecureScheme(request.RequestUri.Scheme) && HttpUtilities.IsSupportedScheme(location.Scheme)) || (HttpUtilities.IsSupportedSecureScheme(request.RequestUri.Scheme) && HttpUtilities.IsSupportedSecureScheme(location.Scheme)); if (!allowed) { break; } redirectCount++; if (redirectCount > _maxAutomaticRedirections) { throw new HttpRequestException(SR.net_http_max_redirects); } // Set up for the automatic redirect request.RequestUri = location; if (RequestRequiresForceGet(response.StatusCode, request.Method)) { request.Method = HttpMethod.Get; request.Content = null; } // Do the redirect. response.Dispose(); } return(response); }
private Uri GetUriForRedirect(Uri requestUri, HttpResponseMessage response) { switch (response.StatusCode) { case HttpStatusCode.Moved: case HttpStatusCode.Found: case HttpStatusCode.SeeOther: case HttpStatusCode.TemporaryRedirect: case HttpStatusCode.MultipleChoices: case (HttpStatusCode)308: // HttpStatusCode.PermanentRedirect break; default: return(null); } Uri location = response.Headers.Location; if (location == null) { return(null); } // Ensure the redirect location is an absolute URI. if (!location.IsAbsoluteUri) { location = new Uri(requestUri, location); } // Per https://tools.ietf.org/html/rfc7231#section-7.1.2, a redirect location without a // fragment should inherit the fragment from the original URI. string requestFragment = requestUri.Fragment; if (!string.IsNullOrEmpty(requestFragment)) { string redirectFragment = location.Fragment; if (string.IsNullOrEmpty(redirectFragment)) { #if NETSTANDARD2_0 // .NET Framework 4.7.2 / 4.8 UriBuilder will always append the fragment marker ('#') to fragment starting with '#', // while .NET Core will only append the fragment marker if not already present. if (requestFragment.StartsWith("#")) { requestFragment = requestFragment.Substring(1); } #endif location = new UriBuilder(location) { Fragment = requestFragment }.Uri; } } // Disallow automatic redirection from secure to non-secure schemes if (HttpUtilities.IsSupportedSecureScheme(requestUri.Scheme) && !HttpUtilities.IsSupportedSecureScheme(location.Scheme)) { if (NetEventSource.IsEnabled) { NetEventSource.Error(this, $"Insecure https to http redirect from '{requestUri}' to '{location}' blocked."); } return(null); } return(location); }
public Task <string> ReadAsStringAsync() { CheckDisposed(); var tcs = new TaskCompletionSource <string>(this); LoadIntoBufferAsync().ContinueWithStandard(tcs, (task, state) => { var innerTcs = (TaskCompletionSource <string>)state; var innerThis = (HttpContent)innerTcs.Task.AsyncState; if (HttpUtilities.HandleFaultsAndCancelation(task, innerTcs)) { return; } if (innerThis._bufferedContent.Length == 0) { innerTcs.TrySetResult(string.Empty); return; } // We don't validate the Content-Encoding header: If the content was encoded, it's the caller's // responsibility to make sure to only call ReadAsString() on already decoded content. E.g. if the // Content-Encoding is 'gzip' the user should set HttpClientHandler.AutomaticDecompression to get a // decoded response stream. Encoding encoding = null; int bomLength = -1; byte[] data = innerThis.GetDataBuffer(innerThis._bufferedContent); int dataLength = (int)innerThis._bufferedContent.Length; // Data is the raw buffer, it may not be full. // If we do have encoding information in the 'Content-Type' header, use that information to convert // the content to a string. if ((innerThis.Headers.ContentType != null) && (innerThis.Headers.ContentType.CharSet != null)) { try { encoding = Encoding.GetEncoding(innerThis.Headers.ContentType.CharSet); } catch (ArgumentException e) { innerTcs.TrySetException(new InvalidOperationException(SR.net_http_content_invalid_charset, e)); return; } } // If no content encoding is listed in the ContentType HTTP header, or no Content-Type header present, // then check for a byte-order-mark (BOM) in the data to figure out the encoding. if (encoding == null) { byte[] preamble; foreach (Encoding testEncoding in s_encodingsWithBom) { preamble = testEncoding.GetPreamble(); if (ByteArrayHasPrefix(data, dataLength, preamble)) { encoding = testEncoding; bomLength = preamble.Length; break; } } } // Use the default encoding if we couldn't detect one. encoding = encoding ?? DefaultStringEncoding; // BOM characters may be present even if a charset was specified. if (bomLength == -1) { byte[] preamble = encoding.GetPreamble(); if (ByteArrayHasPrefix(data, dataLength, preamble)) { bomLength = preamble.Length; } else { bomLength = 0; } } try { // Drop the BOM when decoding the data. string result = encoding.GetString(data, bomLength, dataLength - bomLength); innerTcs.TrySetResult(result); } catch (Exception ex) { innerTcs.TrySetException(ex); } }); return(tcs.Task); }
protected internal override async Task <HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { HttpResponseMessage response; uint redirectCount = 0; while (true) { response = await _innerHandler.SendAsync(request, cancellationToken).ConfigureAwait(false); if (!RequestNeedsRedirect(response)) { break; } Uri location = response.Headers.Location; if (location == null) { // No location header. Nothing to redirect to. break; } if (!location.IsAbsoluteUri) { location = new Uri(request.RequestUri, location); } // Disallow automatic redirection from secure to non-secure schemes bool allowed = (HttpUtilities.IsSupportedNonSecureScheme(request.RequestUri.Scheme) && HttpUtilities.IsSupportedScheme(location.Scheme)) || (HttpUtilities.IsSupportedSecureScheme(request.RequestUri.Scheme) && HttpUtilities.IsSupportedSecureScheme(location.Scheme)); if (!allowed) { break; } redirectCount++; if (redirectCount > _maxAutomaticRedirections) { throw new HttpRequestException(SR.net_http_max_redirects); } // Set up for the automatic redirect request.RequestUri = location; if (RequestRequiresForceGet(response.StatusCode, request.Method)) { request.Method = HttpMethod.Get; request.Content = null; } // Do the redirect. response.Dispose(); } return(response); }
private HttpConnectionKey GetConnectionKey(HttpRequestMessage request, Uri?proxyUri, bool isProxyConnect) { Uri?uri = request.RequestUri; Debug.Assert(uri != null); if (isProxyConnect) { Debug.Assert(uri == proxyUri); return(new HttpConnectionKey(HttpConnectionKind.ProxyConnect, uri.IdnHost, uri.Port, null, proxyUri, GetIdentityIfDefaultCredentialsUsed(_settings._defaultCredentialsUsedForProxy))); } string?sslHostName = null; if (HttpUtilities.IsSupportedSecureScheme(uri.Scheme)) { string?hostHeader = request.Headers.Host; if (hostHeader != null) { sslHostName = ParseHostNameFromHeader(hostHeader); } else { // No explicit Host header. Use host from uri. sslHostName = uri.IdnHost; } } string identity = GetIdentityIfDefaultCredentialsUsed(proxyUri != null ? _settings._defaultCredentialsUsedForProxy : _settings._defaultCredentialsUsedForServer); if (proxyUri != null) { Debug.Assert(HttpUtilities.IsSupportedNonSecureScheme(proxyUri.Scheme)); if (sslHostName == null) { if (HttpUtilities.IsNonSecureWebSocketScheme(uri.Scheme)) { // Non-secure websocket connection through proxy to the destination. return(new HttpConnectionKey(HttpConnectionKind.ProxyTunnel, uri.IdnHost, uri.Port, null, proxyUri, identity)); } else { // Standard HTTP proxy usage for non-secure requests // The destination host and port are ignored here, since these connections // will be shared across any requests that use the proxy. return(new HttpConnectionKey(HttpConnectionKind.Proxy, null, 0, null, proxyUri, identity)); } } else { // Tunnel SSL connection through proxy to the destination. return(new HttpConnectionKey(HttpConnectionKind.SslProxyTunnel, uri.IdnHost, uri.Port, sslHostName, proxyUri, identity)); } } else if (sslHostName != null) { return(new HttpConnectionKey(HttpConnectionKind.Https, uri.IdnHost, uri.Port, sslHostName, null, identity)); } else { return(new HttpConnectionKey(HttpConnectionKind.Http, uri.IdnHost, uri.Port, null, null, identity)); } }
static bool IsAllowedAbsoluteUri(Uri requestUri) { return(!requestUri.IsAbsoluteUri || HttpUtilities.IsHttpUri(requestUri)); }
/// <summary> /// Gets the proxy URI. (iWebProxy interface) /// </summary> public Uri?GetProxy(Uri uri) { return(HttpUtilities.IsSupportedNonSecureScheme(uri.Scheme) ? _httpProxyUri : _httpsProxyUri); }
private static HttpConnectionKey GetConnectionKey(HttpRequestMessage request, Uri proxyUri, bool isProxyConnect) { Uri uri = request.RequestUri; // If the hostname is an IPv6 address, uri.IdnHost will return the address without enclosing []. // In this case, use uri.Host instead, which will correctly enclose with []. // Note we don't need punycode encoding if it's an IP address, so using uri.Host is fine. bool isIPv6Address = uri.HostNameType == UriHostNameType.IPv6; if (isProxyConnect) { Debug.Assert(uri == proxyUri); return(new HttpConnectionKey(HttpConnectionKind.ProxyConnect, isIPv6Address ? uri.Host : uri.IdnHost, uri.Port, null, proxyUri)); } string sslHostName = null; if (HttpUtilities.IsSupportedSecureScheme(uri.Scheme)) { string hostHeader = request.Headers.Host; if (hostHeader != null) { sslHostName = ParseHostNameFromHeader(hostHeader); } else { // No explicit Host header. Use host from uri. sslHostName = uri.IdnHost; } } if (proxyUri != null) { Debug.Assert(HttpUtilities.IsSupportedNonSecureScheme(proxyUri.Scheme)); if (sslHostName == null) { if (HttpUtilities.IsNonSecureWebSocketScheme(uri.Scheme)) { // Non-secure websocket connection through proxy to the destination. return(new HttpConnectionKey(HttpConnectionKind.ProxyTunnel, isIPv6Address ? uri.Host : uri.IdnHost, uri.Port, null, proxyUri)); } else { // Standard HTTP proxy usage for non-secure requests // The destination host and port are ignored here, since these connections // will be shared across any requests that use the proxy. return(new HttpConnectionKey(HttpConnectionKind.Proxy, null, 0, null, proxyUri)); } } else { // Tunnel SSL connection through proxy to the destination. return(new HttpConnectionKey(HttpConnectionKind.SslProxyTunnel, isIPv6Address ? uri.Host : uri.IdnHost, uri.Port, sslHostName, proxyUri)); } } else if (sslHostName != null) { return(new HttpConnectionKey(HttpConnectionKind.Https, isIPv6Address ? uri.Host : uri.IdnHost, uri.Port, sslHostName, null)); } else { return(new HttpConnectionKey(HttpConnectionKind.Http, isIPv6Address ? uri.Host : uri.IdnHost, uri.Port, null, null)); } }