/* Copyright 2013 Google Inc Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ using System.Net; using System.Net.Http; namespace Google.Apis.Http { /// The default implementation of the HTTP client factory. public class HttpClientFactory : IHttpClientFactory { /// /// Creates a new instance of that /// will set the given proxy on HTTP clients created by this factory. /// /// The proxy to set on HTTP clients created by this factory. /// May be null, in which case no proxy will be used. public static HttpClientFactory ForProxy(IWebProxy proxy) => new HttpClientFactory(proxy); /// /// Creates a new instance of . /// public HttpClientFactory() : this(null) { } /// /// Creates a new instance of that /// will set the given proxy on HTTP clients created by this factory. /// /// The proxy to set on HTTP clients created by this factory. /// May be null, in which case no proxy will be used. protected HttpClientFactory(IWebProxy proxy) => Proxy = proxy; /// /// Gets the proxy to use when creating HTTP clients, if any. /// May be null, in which case, no proxy will be set for HTTP clients /// created by this factory. /// public IWebProxy Proxy { get; } /// public ConfigurableHttpClient CreateHttpClient(CreateHttpClientArgs args) { // Create the handler. var handler = CreateHandler(args); var configurableHandler = new ConfigurableMessageHandler(handler) { ApplicationName = args.ApplicationName, GoogleApiClientHeader = args.GoogleApiClientHeader, UniverseDomain = args.UniverseDomain, }; // Create the client. var client = new ConfigurableHttpClient(configurableHandler); foreach (var initializer in args.Initializers) { initializer.Initialize(client); } return client; } /// Creates a HTTP message handler. Override this method to mock a message handler. protected virtual HttpMessageHandler CreateHandler(CreateHttpClientArgs args) { // We need to handle three situations in order to intercept uncompressed data where necessary // while using the built-in decompression where possible. // - No compression requested // - Compression requested but not supported by HttpClientHandler (easy; just GzipDeflateHandler on top of an interceptor on top of HttpClientHandler) // - Compression requested and HttpClientHandler (complex: create two different handlers and decide which to use on a per-request basis) var clientHandler = CreateAndConfigureClientHandler(); if (!args.GZipEnabled) { // Simple: nothing will be decompressing content, so we can just intercept the original handler. return new StreamInterceptionHandler(clientHandler); } else if (!clientHandler.SupportsAutomaticDecompression) { // Simple: we have to create our own decompression handler anyway, so there's still just a single chain. var interceptionHandler = new StreamInterceptionHandler(clientHandler); return new GzipDeflateHandler(interceptionHandler); } else { // Complex: we want to use a simple handler with no interception but with built-in decompression // for requests that wouldn't perform interception anyway, and a longer chain for interception cases. clientHandler.AutomaticDecompression = DecompressionMethods.Deflate | DecompressionMethods.GZip; return new TwoWayDelegatingHandler( // Normal handler (with built-in decompression) when there's no interception. clientHandler, // Alternative handler for requests that might be intercepted, and need that interception to happen // before decompression. We need to delegate to a new client handler that *doesn't* new GzipDeflateHandler(new StreamInterceptionHandler(CreateAndConfigureClientHandler())), request => StreamInterceptionHandler.GetInterceptorProvider(request) != null); } } /// /// Creates a simple client handler with redirection and compression disabled. /// private HttpClientHandler CreateAndConfigureClientHandler() { var handler = CreateClientHandler(); if (handler.SupportsRedirectConfiguration) { handler.AllowAutoRedirect = false; } if (handler.SupportsAutomaticDecompression) { handler.AutomaticDecompression = DecompressionMethods.None; } return handler; } /// /// Create a for use when communicating with the server. /// Please read the remarks closely before overriding this method. /// /// /// When overriding this method, please observe the following: /// /// /// and /// /// of the returned instance are configured after this method returns. /// Configuring these within this method will have no effect. /// /// /// is set in this method to /// if value is not null. You may override that behaviour. /// /// /// Return a new instance of an for each call to this method. /// /// /// This method may be called once, or more than once, when initializing a single client service. /// /// /// /// A suitable . protected virtual HttpClientHandler CreateClientHandler() { var client = new HttpClientHandler(); if (Proxy != null) { client.Proxy = Proxy; } return client; } } }