Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code | Sign in
(3429)

Delta Between Two Patch Sets: Src/GoogleApis/Apis/Services/BaseClientService.cs

Issue 13096044: Issue 146: Pass override HTTP header when request URI too long (Closed)
Left Patch Set: More comments Created 10 years, 7 months ago
Right Patch Set: Refactor namespace Created 10 years, 6 months ago
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments. Please Sign in to add in-line comments.
Jump to:
Left: Side by side diff | Download
Right: Side by side diff | Download
« no previous file with change/comment | « Src/GoogleApis/Apis/Http/MaxUrlLengthInterceptor.cs ('k') | Src/GoogleApis/GoogleApis.csproj » ('j') | no next file with change/comment »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
LEFTRIGHT
1 /* 1 /*
2 Copyright 2013 Google Inc 2 Copyright 2013 Google Inc
3 3
4 Licensed under the Apache License, Version 2.0 (the "License"); 4 Licensed under the Apache License, Version 2.0 (the "License");
5 you may not use this file except in compliance with the License. 5 you may not use this file except in compliance with the License.
6 You may obtain a copy of the License at 6 You may obtain a copy of the License at
7 7
8 http://www.apache.org/licenses/LICENSE-2.0 8 http://www.apache.org/licenses/LICENSE-2.0
9 9
10 Unless required by applicable law or agreed to in writing, software 10 Unless required by applicable law or agreed to in writing, software
(...skipping 25 matching lines...) Expand all
36 36
37 namespace Google.Apis.Services 37 namespace Google.Apis.Services
38 { 38 {
39 /// <summary> 39 /// <summary>
40 /// A thread-safe base class for a client service which provides common mech anism for all services, like· 40 /// A thread-safe base class for a client service which provides common mech anism for all services, like·
41 /// serialization and GZip support. 41 /// serialization and GZip support.
42 /// This class adds a special <see cref="Google.Apis.Http.IHttpExecuteInterc eptor"/> to the· 42 /// This class adds a special <see cref="Google.Apis.Http.IHttpExecuteInterc eptor"/> to the·
43 /// <see cref="Google.Apis.Http.ConfigurableMessageHandler"/> execute interc eptor list, which uses the given· 43 /// <see cref="Google.Apis.Http.ConfigurableMessageHandler"/> execute interc eptor list, which uses the given·
44 /// Authenticator. It calls to its applying authentication method, and injec ts the "Authorization" header in the· 44 /// Authenticator. It calls to its applying authentication method, and injec ts the "Authorization" header in the·
45 /// request. 45 /// request.
46 /// If the given Authenticator implements <see cref="Google.Apis.Http.IUnsuc cessfulReponseHandler"/>, this class 46 /// If the given Authenticator implements <see cref="Google.Apis.Http.IHttpU nsuccessfulResponseHandler"/>, this·
47 /// adds the Authenticator to the <see cref="Google.Apis.Http.ConfigurableMe ssageHandler"/>'s unsuccessful response 47 /// class adds the Authenticator to the <see cref="Google.Apis.Http.Configur ableMessageHandler"/>'s unsuccessful·
48 /// handler list. 48 /// response handler list.
49 /// </summary> 49 /// </summary>
50 public abstract class BaseClientService : IClientService 50 public abstract class BaseClientService : IClientService
51 { 51 {
52 /// <summary> The class logger. </summary> 52 /// <summary>The class logger.</summary>
53 private static readonly ILogger Logger = ApplicationContext.Logger.ForTy pe<BaseClientService>(); 53 private static readonly ILogger Logger = ApplicationContext.Logger.ForTy pe<BaseClientService>();
54 54
55 /// <summary>The default maximum allowed length of a URL string for GET requests.</summary>
55 [VisibleForTestOnly] 56 [VisibleForTestOnly]
56 /// <summary>The default maximum allowed length of a URL string for GET requests.</summary>
57 internal const uint DefaultMaxUrlLength = 2048; 57 internal const uint DefaultMaxUrlLength = 2048;
58 58
59 #region Initializer 59 #region Initializer
60 60
61 /// <summary>· 61 /// <summary>An initializer class for the client service.</summary>
62 /// Indicates if exponential back-off is used automatically on exception in a service request and\or when 503·
63 /// response is returned form the server.
64 /// </summary>
65 [Flags]
66 public enum ExponentialBackOffPolicy
67 {
68 None = 0,
69 Exception = 1,
70 UnsuccessfulResponse503 = 2
71 }
72
73 /// <summary> An initializer class for the client service. </summary>
74 public class Initializer 62 public class Initializer
75 { 63 {
76 /// <summary> 64 /// <summary>
77 /// Gets or sets the factory for creating <see cref="System.Net.Http .HttpClient"/> instance. If this· 65 /// Gets or sets the factory for creating <see cref="System.Net.Http .HttpClient"/> instance. If this·
78 /// property is not set the service uses a new <see cref="Google.Api s.Http.HttpClientFactory"/> instance. 66 /// property is not set the service uses a new <see cref="Google.Api s.Http.HttpClientFactory"/> instance.
79 /// </summary> 67 /// </summary>
80 public IHttpClientFactory HttpClientFactory { get; set; } 68 public IHttpClientFactory HttpClientFactory { get; set; }
81 69
82 /// <summary> 70 /// <summary>
83 /// Gets or sets an Http client initializer which is able to customi ze properties on· 71 /// Gets or sets a HTTP client initializer which is able to customiz e properties on·
84 /// <see cref="Google.Apis.Http.ConfigurableHttpClient"/> and· 72 /// <see cref="Google.Apis.Http.ConfigurableHttpClient"/> and·
85 /// <see cref="Google.Apis.Http.ConfigurableMessageHandler"/>. 73 /// <see cref="Google.Apis.Http.ConfigurableMessageHandler"/>.
86 /// </summary> 74 /// </summary>
87 public IConfigurableHttpClientInitializer HttpClientInitializer { ge t; set; } 75 public IConfigurableHttpClientInitializer HttpClientInitializer { ge t; set; }
88 76
89 /// <summary> 77 /// <summary>
90 /// Get or sets the exponential back-off policy used by the service. Default value is· 78 /// Get or sets the exponential back-off policy used by the service. Default value is·
91 /// <c>UnsuccessfulResponse503</c>, which means that exponential bac k-off is used on 503 abnormal HTTP 79 /// <c>UnsuccessfulResponse503</c>, which means that exponential bac k-off is used on 503 abnormal HTTP
92 /// response. 80 /// response.
93 /// If the value is set to <c>None</c>, no exponential back-off poli cy is used, and it's up to user to 81 /// If the value is set to <c>None</c>, no exponential back-off poli cy is used, and it's up to user to
94 /// configure the <seealso cref="Google.Apis.Http.ConfigurableMessag eHandler"/> in an 82 /// configure the <seealso cref="Google.Apis.Http.ConfigurableMessag eHandler"/> in an
95 /// <seealso cref="Google.Apis.Http.IConfigurableHttpClientInitializ er"/> to set a specific back-off 83 /// <seealso cref="Google.Apis.Http.IConfigurableHttpClientInitializ er"/> to set a specific back-off
96 /// implementation (using <seealso cref="Google.Api.Http.BackOffHand ler"/>). 84 /// implementation (using <seealso cref="Google.Apis.Http.BackOffHan dler"/>).
97 /// </summary> 85 /// </summary>
98 public ExponentialBackOffPolicy DefaultExponentialBackOffPolicy { ge t; set; } 86 public ExponentialBackOffPolicy DefaultExponentialBackOffPolicy { ge t; set; }
99 87
100 /// <summary> Gets or sets whether this service supports GZip. Defau lt value is <c>true</c>. </summary> 88 /// <summary>Gets or sets whether this service supports GZip. Defaul t value is <c>true</c>.</summary>
101 public bool GZipEnabled { get; set; } 89 public bool GZipEnabled { get; set; }
102 90
103 /// <summary> 91 /// <summary>
104 /// Gets and Sets the Serializer. Default value is <see cref="Google .Apis.Json.NewtonsoftJsonSerializer"/>. 92 /// Gets and Sets the serializer. Default value is <see cref="Google .Apis.Json.NewtonsoftJsonSerializer"/>.
105 /// </summary> 93 /// </summary>
106 public ISerializer Serializer { get; set; } 94 public ISerializer Serializer { get; set; }
107 95
108 /// <summary> Gets or sets the API Key. Default value is <c>null</c> . </summary> 96 /// <summary>Gets or sets the API Key. Default value is <c>null</c>. </summary>
109 public string ApiKey { get; set; } 97 public string ApiKey { get; set; }
110 98
111 /// <summary> 99 /// <summary>
112 /// Gets or sets the Authenticator. Default value is· 100 /// Gets or sets the Authenticator. Default value is·
113 /// <see cref="Google.Apis.Authentication.NullAuthenticator.Instance "/>. 101 /// <see cref="Google.Apis.Authentication.NullAuthenticator.Instance "/>.
114 /// </summary> 102 /// </summary>
115 public IAuthenticator Authenticator { get; set; } 103 public IAuthenticator Authenticator { get; set; }
116 104
117 /// <summary> 105 /// <summary>
118 /// Gets or sets Application name to be used in the User-Agent heade r. Default value is <c>null</c>.· 106 /// Gets or sets Application name to be used in the User-Agent heade r. Default value is <c>null</c>.·
119 /// </summary> 107 /// </summary>
120 public string ApplicationName { get; set; } 108 public string ApplicationName { get; set; }
121 109
122 /// <summary> 110 /// <summary>
123 /// Maximum allowed length of a URL string for GET requests. Default value is <c>2048</c>. 111 /// Maximum allowed length of a URL string for GET requests. Default value is <c>2048</c>. If the value is
112 /// set to <c>0</c>, requests will never be modified due to URL stri ng length.
124 /// </summary> 113 /// </summary>
125 public uint MaxUrlLength { get; set; } 114 public uint MaxUrlLength { get; set; }
126 115
127 /// <summary> Constructs a new initializer with default values. </su mmary> 116 /// <summary>Constructs a new initializer with default values.</summ ary>
128 public Initializer() 117 public Initializer()
129 { 118 {
130 GZipEnabled = true; 119 GZipEnabled = true;
131 Serializer = new NewtonsoftJsonSerializer(); 120 Serializer = new NewtonsoftJsonSerializer();
132 Authenticator = NullAuthenticator.Instance; 121 Authenticator = NullAuthenticator.Instance;
133 DefaultExponentialBackOffPolicy = ExponentialBackOffPolicy.Unsuc cessfulResponse503; 122 DefaultExponentialBackOffPolicy = ExponentialBackOffPolicy.Unsuc cessfulResponse503;
134 MaxUrlLength = DefaultMaxUrlLength; 123 MaxUrlLength = DefaultMaxUrlLength;
135 } 124 }
136 } 125 }
137 126
138 /// <summary>
139 /// An initializer which adds exponential back-off as exception handler and\or unsuccessful response handler by
140 /// the given <seealso cref="BaseClientService.ExponentialBackOffPolicy" />.
141 /// </summary>
142 private class ExponentialBackOffInitializer : IConfigurableHttpClientIni tializer
143 {
144 private ExponentialBackOffPolicy Policy { get; set; }
145 private Func<BackOffHandler> CreateBackOff { get; set; }
146
147 /// <summary>
148 /// Constructs a new back-off initializer with the given policy and back-off handler create function.
149 /// </summary>
150 public ExponentialBackOffInitializer(ExponentialBackOffPolicy policy , Func<BackOffHandler> createBackOff)
151 {
152 Policy = policy;
153 CreateBackOff = createBackOff;
154 }
155
156 public void Initialize(ConfigurableHttpClient httpClient)
157 {
158 var backOff = CreateBackOff();
159
160 // add exception handler and\or unsuccessful response handler
161 if ((Policy & ExponentialBackOffPolicy.Exception) == Exponential BackOffPolicy.Exception)
162 {
163 httpClient.MessageHandler.ExceptionHandlers.Add(backOff);
164 }
165
166 if ((Policy & ExponentialBackOffPolicy.UnsuccessfulResponse503) ==
167 ExponentialBackOffPolicy.UnsuccessfulResponse503)
168 {
169 httpClient.MessageHandler.UnsuccessfulResponseHandlers.Add(b ackOff);
170 }
171 }
172 }
173
174 #endregion 127 #endregion
175 128
176 /// <summary> Constructs a new base client with the specified initialize r. </summary> 129 /// <summary>Constructs a new base client with the specified initializer .</summary>
177 protected BaseClientService(Initializer initializer) 130 protected BaseClientService(Initializer initializer)
178 { 131 {
179 // sets the right properties by the initializer's properties 132 // Set the right properties by the initializer's properties.
180 GZipEnabled = initializer.GZipEnabled; 133 GZipEnabled = initializer.GZipEnabled;
181 Serializer = initializer.Serializer; 134 Serializer = initializer.Serializer;
182 ApiKey = initializer.ApiKey; 135 ApiKey = initializer.ApiKey;
183 Authenticator = initializer.Authenticator; 136 Authenticator = initializer.Authenticator;
184 ApplicationName = initializer.ApplicationName; 137 ApplicationName = initializer.ApplicationName;
185 if (ApplicationName == null) 138 if (ApplicationName == null)
186 { 139 {
187 Logger.Warning("Application name is not set. Please set Initiali zer.ApplicationName property"); 140 Logger.Warning("Application name is not set. Please set Initiali zer.ApplicationName property");
188 } 141 }
189 HttpClientInitializer = initializer.HttpClientInitializer; 142 HttpClientInitializer = initializer.HttpClientInitializer;
190 143
191 // create an Http client for this service 144 // Create a HTTP client for this service.
192 HttpClient = CreateHttpClient(initializer); 145 HttpClient = CreateHttpClient(initializer);
193 } 146 }
194 147
195 /// <summary> Return <c>true</c> if this service contains the specified feature. </summary> 148 /// <summary>Returns <c>true</c> if this service contains the specified feature.</summary>
196 private bool HasFeature(Features feature) 149 private bool HasFeature(Features feature)
197 { 150 {
198 return Features.Contains(feature.GetStringValue()); 151 return Features.Contains(feature.GetStringValue());
199 } 152 }
200 153
201 private ConfigurableHttpClient CreateHttpClient(Initializer initializer) 154 private ConfigurableHttpClient CreateHttpClient(Initializer initializer)
202 { 155 {
203 // if factory wasn't set use the default Http client factory 156 // If factory wasn't set use the default HTTP client factory.
204 var factory = initializer.HttpClientFactory ?? new HttpClientFactory (); 157 var factory = initializer.HttpClientFactory ?? new HttpClientFactory ();
205 var args = new CreateHttpClientArgs 158 var args = new CreateHttpClientArgs
206 { 159 {
207 GZipEnabled = GZipEnabled, 160 GZipEnabled = GZipEnabled,
208 ApplicationName = ApplicationName, 161 ApplicationName = ApplicationName,
209 }; 162 };
210 163
211 // add the user's input initializer 164 // Add the user's input initializer.
212 if (HttpClientInitializer != null) 165 if (HttpClientInitializer != null)
213 { 166 {
214 args.Initializers.Add(HttpClientInitializer); 167 args.Initializers.Add(HttpClientInitializer);
215 } 168 }
216 169
217 // add exponential back-off initializer if necessary 170 // Add exponential back-off initializer if necessary.
218 if (initializer.DefaultExponentialBackOffPolicy != ExponentialBackOf fPolicy.None) 171 if (initializer.DefaultExponentialBackOffPolicy != ExponentialBackOf fPolicy.None)
219 { 172 {
220 args.Initializers.Add(new ExponentialBackOffInitializer(initiali zer.DefaultExponentialBackOffPolicy, 173 args.Initializers.Add(new ExponentialBackOffInitializer(initiali zer.DefaultExponentialBackOffPolicy,
221 CreateBackOffHandler)); 174 CreateBackOffHandler));
222 } 175 }
223 176
224 // add authenticator initializer to intercept a request and add the "Authorization" header and also handle 177 // Add authenticator initializer to intercept a request and add the "Authorization" header and also handle
225 // abnormal 401 responses in case the authenticator is an instance o f unsuccessful response handler. 178 // abnormal 401 responses in case the authenticator is an instance o f unsuccessful response handler.
226 args.Initializers.Add(new AuthenticatorMessageHandlerInitializer(Aut henticator)); 179 args.Initializers.Add(new AuthenticatorMessageHandlerInitializer(Aut henticator));
227 ············ 180 ············
228 var httpClient = factory.CreateHttpClient(args); 181 var httpClient = factory.CreateHttpClient(args);
229 httpClient.MessageHandler.ExecuteInterceptors.Add(new MaxUrlLengthIn terceptor(initializer.MaxUrlLength)); 182 if (initializer.MaxUrlLength > 0)
183 {
184 httpClient.MessageHandler.ExecuteInterceptors.Add(
185 new MaxUrlLengthInterceptor(initializer.MaxUrlLength));
186 }
230 return httpClient; 187 return httpClient;
231 } 188 }
232 189
233 /// <summary> 190 /// <summary>
234 /// Creates the back-off handler with <seealso cref="Google.Apis.Util.Ex ponentialBackOff"/>.· 191 /// Creates the back-off handler with <seealso cref="Google.Apis.Util.Ex ponentialBackOff"/>.·
235 /// Overrides this method to change the default behavior of back-off han dler (e.g. you can change the maximum 192 /// Overrides this method to change the default behavior of back-off han dler (e.g. you can change the maximum
236 /// waited request's time span, or create a back-off handler with you ow n implementation of· 193 /// waited request's time span, or create a back-off handler with you ow n implementation of·
237 /// <seealso cref="Google.Apis.Util.IBackOff"/>). 194 /// <seealso cref="Google.Apis.Util.IBackOff"/>).
238 /// </summary> 195 /// </summary>
239 protected virtual BackOffHandler CreateBackOffHandler() 196 protected virtual BackOffHandler CreateBackOffHandler()
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after
290 public ISerializer Serializer { get; private set; } 247 public ISerializer Serializer { get; private set; }
291 248
292 public virtual string SerializeObject(object obj) 249 public virtual string SerializeObject(object obj)
293 { 250 {
294 if (HasFeature(Discovery.Features.LegacyDataResponse)) 251 if (HasFeature(Discovery.Features.LegacyDataResponse))
295 { 252 {
296 // Legacy path 253 // Legacy path
297 var request = new StandardResponse<object> { Data = obj }; 254 var request = new StandardResponse<object> { Data = obj };
298 return Serializer.Serialize(request); 255 return Serializer.Serialize(request);
299 } 256 }
300
301 // New v1.0 path
302 return Serializer.Serialize(obj); 257 return Serializer.Serialize(obj);
303 } 258 }
304 259
305 public virtual async Task<T> DeserializeResponse<T>(HttpResponseMessage response) 260 public virtual async Task<T> DeserializeResponse<T>(HttpResponseMessage response)
306 { 261 {
307 var text = await response.Content.ReadAsStringAsync().ConfigureAwait (false); 262 var text = await response.Content.ReadAsStringAsync().ConfigureAwait (false);
308 263
309 // If a string is request, don't parse the response. 264 // If a string is request, don't parse the response.
310 if (typeof(T).Equals(typeof(string))) 265 if (typeof(T).Equals(typeof(string)))
311 { 266 {
(...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after
380 // Standard response (which contains data and error properties) 335 // Standard response (which contains data and error properties)
381 throw new GoogleApiException(Name, 336 throw new GoogleApiException(Name,
382 "An Error occurred, but the error response could not be dese rialized", ex); 337 "An Error occurred, but the error response could not be dese rialized", ex);
383 } 338 }
384 339
385 return errorResponse.Error; 340 return errorResponse.Error;
386 } 341 }
387 342
388 #endregion 343 #endregion
389 344
390 #region Abstract Memebrs 345 #region Abstract Members
391 346
392 public abstract string Name { get; } 347 public abstract string Name { get; }
393 public abstract string BaseUri { get; } 348 public abstract string BaseUri { get; }
394 public abstract string BasePath { get; } 349 public abstract string BasePath { get; }
395 350
396 public abstract IList<string> Features { get; } 351 public abstract IList<string> Features { get; }
397 352
398 #endregion 353 #endregion
399 354
400 #endregion 355 #endregion
401 356
402 /// <summary> Creates a GZip stream by the given serialized object. </su mmary> 357 /// <summary>Creates a GZip stream by the given serialized object.</summ ary>
403 private static Stream CreateGZipStream(string serializedObject) 358 private static Stream CreateGZipStream(string serializedObject)
404 { 359 {
405 byte[] bytes = System.Text.Encoding.UTF8.GetBytes(serializedObject); 360 byte[] bytes = System.Text.Encoding.UTF8.GetBytes(serializedObject);
406 using (System.IO.MemoryStream ms = new System.IO.MemoryStream()) 361 using (System.IO.MemoryStream ms = new System.IO.MemoryStream())
407 { 362 {
408 using (GZipStream gzip = new GZipStream(ms, CompressionMode.Comp ress, true)) 363 using (GZipStream gzip = new GZipStream(ms, CompressionMode.Comp ress, true))
409 { 364 {
410 gzip.Write(bytes, 0, bytes.Length); 365 gzip.Write(bytes, 0, bytes.Length);
411 } 366 }
412 367
413 // reset the stream to the beginning. It doesn't work otherwise! 368 // reset the stream to the beginning. It doesn't work otherwise!
414 ms.Position = 0; 369 ms.Position = 0;
415 byte[] compressed = new byte[ms.Length]; 370 byte[] compressed = new byte[ms.Length];
416 ms.Read(compressed, 0, compressed.Length); 371 ms.Read(compressed, 0, compressed.Length);
417 return new MemoryStream(compressed); 372 return new MemoryStream(compressed);
418 } 373 }
419 } 374 }
420 375
421 public virtual void Dispose() 376 public virtual void Dispose()
422 { 377 {
423 if (HttpClient != null) 378 if (HttpClient != null)
424 { 379 {
425 HttpClient.Dispose(); 380 HttpClient.Dispose();
426 } 381 }
427 } 382 }
428 } 383 }
429
430 [VisibleForTestOnly]
431 /// <summary>
432 /// Intercepts HTTP GET requests with a URLs longer than <see cref="Initiali zer.MaxUrlLength" />.
433 /// The interceptor will change such requests as follows:·
434 /// <list type="bullet">
435 /// <item>The request's method will be changed to POST</item>
436 /// <item>A <c>X-HTTP-Method-Override</c> header will be added with the valu e <c>GET</c></item>
437 /// <item>Any query parameters from the URI will be moved into the body of t he request.
438 /// <item>If query parameters are moved, the content type is set to <c>appli cation/x-www-form-urlencoded</c></item>
439 /// </item></list>
440 /// </summary>
441 internal class MaxUrlLengthInterceptor : IHttpExecuteInterceptor·
442 {
443 private readonly uint maxUrlLength;
444
445 ///<summary>Constructs a new Max URL length interceptor with the given m ax length.</summary>
446 public MaxUrlLengthInterceptor(uint maxUrlLength)
447 {
448 this.maxUrlLength = maxUrlLength;
449 }
450
451 public void Intercept(HttpRequestMessage request)
452 {
453 if (request.Method != HttpMethod.Get || request.RequestUri.AbsoluteU ri.Length <= maxUrlLength)
454 {
455 return;
456 }
457 // Change the method to POST
458 request.Method = HttpMethod.Post;
459 var query = request.RequestUri.Query;
460 if (!String.IsNullOrEmpty(query))
461 {
462 // Move query parameters to the body (without the "?")
463 request.Content = new StringContent(query.Substring(1));
464 request.Content.Headers.ContentType = new MediaTypeHeaderValue(" application/x-www-form-urlencoded");
465 var requestString = request.RequestUri.ToString();
466 request.RequestUri = new Uri(requestString.Remove(requestString. IndexOf("?") - 1));
467 }
468 request.Headers.Add("X-HTTP-Method-Override", "GET");
469 }
470 }
471 } 384 }
LEFTRIGHT

Powered by Google App Engine
RSS Feeds Recent Issues | This issue
This is Rietveld f62528b