/
HttpClientFactory.cs
167 lines (150 loc) · 7.38 KB
/
HttpClientFactory.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
/*
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
{
/// <summary>The default implementation of the HTTP client factory.</summary>
public class HttpClientFactory : IHttpClientFactory
{
/// <summary>
/// Creates a new instance of <see cref="HttpClientFactory"/> that
/// will set the given proxy on HTTP clients created by this factory.
/// </summary>
/// <param name="proxy">The proxy to set on HTTP clients created by this factory.
/// May be null, in which case no proxy will be used.</param>
public static HttpClientFactory ForProxy(IWebProxy proxy) =>
new HttpClientFactory(proxy);
/// <summary>
/// Creates a new instance of <see cref="HttpClientFactory"/>.
/// </summary>
public HttpClientFactory() : this(null)
{ }
/// <summary>
/// Creates a new instance of <see cref="HttpClientFactory"/> that
/// will set the given proxy on HTTP clients created by this factory.
/// </summary>
/// <param name="proxy">The proxy to set on HTTP clients created by this factory.
/// May be null, in which case no proxy will be used.</param>
protected HttpClientFactory(IWebProxy proxy) => Proxy = proxy;
/// <summary>
/// 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.
/// </summary>
public IWebProxy Proxy { get; }
/// <inheritdoc/>
public ConfigurableHttpClient CreateHttpClient(CreateHttpClientArgs args)
{
// Create the handler.
var handler = CreateHandler(args);
var configurableHandler = new ConfigurableMessageHandler(handler)
{
ApplicationName = args.ApplicationName,
GoogleApiClientHeader = args.GoogleApiClientHeader
};
// Create the client.
var client = new ConfigurableHttpClient(configurableHandler);
foreach (var initializer in args.Initializers)
{
initializer.Initialize(client);
}
return client;
}
/// <summary>Creates a HTTP message handler. Override this method to mock a message handler.</summary>
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);
}
}
/// <summary>
/// Creates a simple client handler with redirection and compression disabled.
/// </summary>
private HttpClientHandler CreateAndConfigureClientHandler()
{
var handler = CreateClientHandler();
if (handler.SupportsRedirectConfiguration)
{
handler.AllowAutoRedirect = false;
}
if (handler.SupportsAutomaticDecompression)
{
handler.AutomaticDecompression = DecompressionMethods.None;
}
return handler;
}
/// <summary>
/// Create a <see cref="HttpClientHandler"/> for use when communicating with the server.
/// Please read the remarks closely before overriding this method.
/// </summary>
/// <remarks>
/// When overriding this method, please observe the following:
/// <list type="bullet">
/// <item><description>
/// <see cref="HttpClientHandler.AllowAutoRedirect"/> and
/// <see cref="HttpClientHandler.AutomaticDecompression"/>
/// of the returned instance are configured after this method returns.
/// Configuring these within this method will have no effect.
/// </description></item>
/// <item><description>
/// <see cref="HttpClientHandler.Proxy"/> is set in this method to <see cref="Proxy"/>
/// if <see cref="Proxy"/> value is not null. You may override that behaviour.
/// </description></item>
/// <item><description>
/// Return a new instance of an <see cref="HttpClientHandler"/> for each call to this method.
/// </description></item>
/// <item><description>
/// This method may be called once, or more than once, when initializing a single client service.
/// </description></item>
/// </list>
/// </remarks>
/// <returns>A suitable <see cref="HttpClientHandler"/>.</returns>
protected virtual HttpClientHandler CreateClientHandler()
{
var client = new HttpClientHandler();
if (Proxy != null)
{
client.Proxy = Proxy;
}
return client;
}
}
}