-
Notifications
You must be signed in to change notification settings - Fork 25
/
HttpConfigurationBuilder.cs
271 lines (255 loc) · 11.6 KB
/
HttpConfigurationBuilder.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
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using LaunchDarkly.Sdk.Internal;
using LaunchDarkly.Sdk.Internal.Http;
using LaunchDarkly.Sdk.Server.Subsystems;
using static LaunchDarkly.Sdk.Internal.Events.DiagnosticConfigProperties;
namespace LaunchDarkly.Sdk.Server.Integrations
{
/// <summary>
/// Contains methods for configuring the SDK's networking behavior.
/// </summary>
/// <remarks>
/// <para>
/// If you want to set non-default values for any of these properties, create a builder with
/// <see cref="Components.HttpConfiguration"/>, change its properties with the methods of this class, and
/// pass it to <see cref="ConfigurationBuilder.Http"/>:
/// </para>
/// <example>
/// <code>
/// var config = Configuration.Builder(sdkKey)
/// .Http(
/// Components.HttpConfiguration()
/// .ConnectTimeout(TimeSpan.FromMilliseconds(3000))
/// )
/// .Build();
/// </code>
/// </example>
/// </remarks>
public sealed class HttpConfigurationBuilder : IComponentConfigurer<HttpConfiguration>, IDiagnosticDescription
{
/// <summary>
/// The default value for <see cref="ConnectTimeout(TimeSpan)"/>: two seconds.
/// </summary>
public static readonly TimeSpan DefaultConnectTimeout = TimeSpan.FromSeconds(2);
/// <summary>
/// The default value for <see cref="ReadTimeout(TimeSpan)"/>: 10 seconds.
/// </summary>
public static readonly TimeSpan DefaultReadTimeout = TimeSpan.FromSeconds(10);
/// <summary>
/// The default value for <see cref="ResponseStartTimeout(TimeSpan)"/>: 10 seconds.
/// </summary>
public static readonly TimeSpan DefaultResponseStartTimeout = TimeSpan.FromSeconds(10);
internal TimeSpan _connectTimeout = DefaultConnectTimeout;
internal List<KeyValuePair<string, string>> _customHeaders = new List<KeyValuePair<string, string>>();
internal HttpMessageHandler _messageHandler = null;
internal IWebProxy _proxy = null;
internal TimeSpan _readTimeout = DefaultReadTimeout;
internal TimeSpan _responseStartTimeout = DefaultResponseStartTimeout;
internal string _wrapperName = null;
internal string _wrapperVersion = null;
/// <summary>
/// Sets the network connection timeout.
/// </summary>
/// <remarks>
/// <para>
/// This is the time allowed for the underlying HTTP client to connect to the
/// LaunchDarkly server, for any individual network connection.
/// </para>
/// <para>
/// It is not the same as <see cref="ConfigurationBuilder.StartWaitTime(TimeSpan)"/>, which
/// limits the time for initializing the SDK regardless of how many individual HTTP requests
/// are done in that time.
/// </para>
/// <para>
/// Not all .NET platforms support setting a connection timeout. It is implemented as
/// a property of <c>System.Net.Http.SocketsHttpHandler</c> in .NET Core 2.1+ and .NET
/// 5+, but is unavailable in .NET Framework and .NET Standard. On platforms where it
/// is not supported, only <see cref="ResponseStartTimeout"/> will be used.
/// </para>
/// <para>
/// Also, since this is implemented only in <c>SocketsHttpHandler</c>, if you have
/// specified some other HTTP handler implementation with <see cref="HttpMessageHandler"/>,
/// the <see cref="ConnectTimeout"/> here will be ignored.
/// </para>
/// </remarks>
/// <param name="connectTimeout">the timeout</param>
/// <returns>the builder</returns>
/// <seealso cref="ResponseStartTimeout"/>
public HttpConfigurationBuilder ConnectTimeout(TimeSpan connectTimeout)
{
_connectTimeout = connectTimeout;
return this;
}
/// <summary>
/// Specifies a custom HTTP header that should be added to all SDK requests.
/// </summary>
/// <remarks>
/// This may be helpful if you are using a gateway or proxy server that requires a specific header in
/// requests. You may add any number of headers.
/// </remarks>
/// <param name="name">the header name</param>
/// <param name="value">the header value</param>
/// <returns>the builder</returns>
public HttpConfigurationBuilder CustomHeader(string name, string value)
{
_customHeaders.Add(new KeyValuePair<string, string>(name, value));
return this;
}
/// <summary>
/// Specifies a custom HTTP message handler implementation.
/// </summary>
/// <remarks>
/// This is mainly useful for testing, to cause the SDK to use custom logic instead of actual HTTP requests,
/// but can also be used to customize HTTP behavior on platforms where .NET's default handler is not optimal.
/// </remarks>
/// <param name="messageHandler">the message handler, or null to use the platform's default handler</param>
/// <returns>the builder</returns>
public HttpConfigurationBuilder MessageHandler(HttpMessageHandler messageHandler)
{
_messageHandler = messageHandler;
return this;
}
/// <summary>
/// Sets an HTTP proxy for making connections to LaunchDarkly.
/// </summary>
/// <remarks>
/// <para>
/// This is ignored if you have specified a custom message handler with <see cref="MessageHandler(HttpMessageHandler)"/>,
/// since proxy behavior is implemented by the message handler.
/// </para>
/// <para>
/// Note that this is not the same as the <see href="https://docs.launchdarkly.com/home/relay-proxy">LaunchDarkly
/// Relay Proxy</see>, which would be set with
/// <see cref="ServiceEndpointsBuilder.RelayProxy(Uri)"/>.
/// </para>
/// </remarks>
/// <example>
/// <code>
/// // Example of using an HTTP proxy with basic authentication
///
/// var proxyUri = new Uri("http://my-proxy-host:8080");
/// var proxy = new System.Net.WebProxy(proxyUri);
/// var credentials = new System.Net.CredentialCache();
/// credentials.Add(proxyUri, "Basic",
/// new System.Net.NetworkCredential("username", "password"));
/// proxy.Credentials = credentials;
///
/// var config = Configuration.Builder("my-sdk-key")
/// .Http(
/// Components.HttpConfiguration().Proxy(proxy)
/// )
/// .Build();
/// </code>
/// </example>
/// <param name="proxy">any implementation of <c>System.Net.IWebProxy</c></param>
/// <returns>the builder</returns>
public HttpConfigurationBuilder Proxy(IWebProxy proxy)
{
_proxy = proxy;
return this;
}
/// <summary>
/// Sets the socket read timeout.
/// </summary>
/// <remarks>
/// Sets the socket timeout. This is the amount of time without receiving data on a connection that the
/// SDK will tolerate before signaling an error. This does <i>not</i> apply to the streaming connection
/// used by <see cref="Components.StreamingDataSource"/>, which has its own non-configurable read timeout
/// based on the expected behavior of the LaunchDarkly streaming service.
/// </remarks>
/// <param name="readTimeout">the socket read timeout</param>
/// <returns>the builder</returns>
public HttpConfigurationBuilder ReadTimeout(TimeSpan readTimeout)
{
_readTimeout = readTimeout;
return this;
}
/// <summary>
/// Sets the maximum amount of time to wait for the beginning of an HTTP response.
/// </summary>
/// <remarks>
/// <para>
/// This limits how long the SDK will wait from the time it begins trying to make a
/// network connection for an individual HTTP request to the time it starts receiving
/// any data from the server. It is equivalent to the <c>Timeout</c> property in
/// <c>HttpClient</c>.
/// </para>
/// <para>
/// It is not the same as <see cref="ConfigurationBuilder.StartWaitTime(TimeSpan)"/>, which
/// limits the time for initializing the SDK regardless of how many individual HTTP requests
/// are done in that time.
/// </para>
/// </remarks>
/// <param name="responseStartTimeout">the timeout</param>
/// <returns>the builder</returns>
/// <seealso cref="ConnectTimeout"/>
public HttpConfigurationBuilder ResponseStartTimeout(TimeSpan responseStartTimeout)
{
_responseStartTimeout = responseStartTimeout;
return this;
}
/// <summary>
/// For use by wrapper libraries to set an identifying name for the wrapper being used.
/// </summary>
/// <remarks>
/// This will be included in a header during requests to the LaunchDarkly servers to allow recording
/// metrics on the usage of these wrapper libraries.
/// </remarks>
/// <param name="wrapperName">an identifying name for the wrapper library</param>
/// <param name="wrapperVersion">version string for the wrapper library</param>
/// <returns>the builder</returns>
public HttpConfigurationBuilder Wrapper(string wrapperName, string wrapperVersion)
{
_wrapperName = wrapperName;
_wrapperVersion = wrapperVersion;
return this;
}
/// <inheritdoc/>
public HttpConfiguration Build(LdClientContext context)
{
var httpProperties = MakeHttpProperties(context);
return new HttpConfiguration(
httpProperties,
_messageHandler,
_responseStartTimeout
);
}
private HttpProperties MakeHttpProperties(LdClientContext context)
{
string wrapperName;
string wrapperVersion;
if (context.WrapperInfo != null)
{
wrapperName = context.WrapperInfo.Name;
wrapperVersion = context.WrapperInfo.Version;
}
else
{
wrapperName = _wrapperName;
wrapperVersion = _wrapperVersion;
}
var httpProperties = HttpProperties.Default
.WithAuthorizationKey(context.SdkKey)
.WithConnectTimeout(_connectTimeout)
.WithHttpMessageHandlerFactory(_messageHandler is null ?
(Func<HttpProperties, HttpMessageHandler>)null :
_ => _messageHandler)
.WithProxy(_proxy)
.WithReadTimeout(_readTimeout)
.WithUserAgent("DotNetClient/" + AssemblyVersions.GetAssemblyVersionStringForType(typeof(LdClient)))
.WithApplicationTags(context.ApplicationInfo)
.WithWrapper(wrapperName, wrapperVersion);
return _customHeaders.Aggregate(httpProperties, (current, kv)
=> current.WithHeader(kv.Key, kv.Value));
}
/// <inheritdoc/>
public LdValue DescribeConfiguration(LdClientContext context) =>
LdValue.BuildObject()
.WithHttpProperties(MakeHttpProperties(context))
.Build();
}
}