Viewing the adapter pattern from the HTTP MessageHandler Adapter of System.Web.Http.Owin

Keywords: ASP.NET github encoding

Copyright of this article belongs to Blog Garden and author Wu Shuang himself. Please indicate the original address at www.cnblogs.com/tdws.

I. Write in front

Adapter mode

It can be used to adapt between existing interfaces and incompatible classes. It helps to avoid large-scale rewriting of existing client code by wrapping the interfaces of existing classes so that clients can use the classes that are not tailored for them without major surgery. JS Design Patterns

Convert the interface of a class into another interface that the customer expects. The adapter allows classes with incompatible interfaces to cooperate.

Head First Design Patterns

This is the definition of adapter pattern in these two books. The adapter pattern is one of the design patterns that is easy to understand. The purpose or problem that can be solved is new function/new type, which is not compatible with the original type/method/function. With the ingenious experience of adapter, we can ensure that the modification is closed and open to expansion. To achieve this goal, we need to face the interface and maintain the single character of responsibility. Perhaps for C# developers, SqlDataAdapter is the most common.

                  

2. Understanding UseWebApi

This article deals with OWIN,.Net Framework, Webapi open source download address is:

https://github.com/aspnet/AspNetKatana

https://github.com/ASP-NET-MVC/aspnetwebstack

https://github.com/dotnet/corefx 

Small partners familiar with OWIN system must have seen and used app.UseWebApi in Startip.cs. App is the object of IAppBuilder

Startup.cs is the startup class implemented by OWIN katana. As the name implies, Use WebApi is placed in the whole process as an OWIN middleware. app is the object of IAppBuilder, which is created by IAppBuilderFactory. IAppBuilder defines three methods, Build,New and Use. What are the three methods responsible for?

Build, which returns an instance of the OWIN pipe entry point, is called by the Init method in Microsoft. OWIN.Host. System Web. Its return instance will be converted to AppFun type. What is AppFun (using AppFunc = Func < IDictionary < string, object >, Task>)? It's an application delegation that the OWIN server interacts with the application. We can probably guess why this method is called in OWIN.Host.

New, used to return an AppBuilder instance, called and returned by IAppBuilderFactory.

Use is the method we often use in OWIN system. We can define our own OWIN middleware, according to its definition specification, and use it in processing pipelines, such as user operation log middleware, user identity verification middleware, etc.

At this point, we should clearly understand that WebApi is a middleware of OWIN. Take a chestnut:

 1 public partial class Startup
 2     {
 3 
 4         public void Configuration(IAppBuilder app)
 5         {
 6             // This must happen FIRST otherwise CORS will not work. 
 7             // Introduce OWin.Cors Solving cross-domain access problems
 8             app.UseCors(CorsOptions.AllowAll);
 9 
10             GlobalConfiguration.Configure(WebApiConfig.Register);
11             FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
12             
13             ConfigureAuth(app);
14 
15             app.Use<UserContextMiddleware>();
16             app.Use<UserOperationMiddleware>();
17             app.UseWebApi(GlobalConfiguration.Configuration);
18         }
19     }

Implementation of UseWebApi

Seeing this, you must ask why the UseWebapi method is not defined in IAppBuilder. UseWebapi is implemented in System.Web.Http.Owin's WebApiAppBuilder Extensions. cs. UseWebApi is a C this extension method, and the answer you think is no different. In its implementation, builder. Use (typeof (HttpMessageHandler Adapter, options) is called;

At this point, do not blame me me for a few words, the implementation steps of Adapter: in order to make a class or a function compatible with existing classes/interfaces, then

1. The adapter implements the interface or abstract method of the target customer for parameter input

2. Calling the method of the target customer in the method of the implemented interface/abstract class

The protagonist HttpMessageHandler Adapter finally appears, and the little friends who have learned about the Adapter pattern can certainly imagine that since HttpMessageHandler is Adadapter, there must be a private field defined in the class, and the type is HttpMessageHandler. You can also imagine that the adapter inherits the abstract type of Owing Middleware and implements its In-In. The voke Abstract method, in a method of HttpMessageHandler Adapter, must call the method of HttpMessageHandler. So from the source code, we know that the field of HttpMessageHandler is named _messageHandler. (Is it similar to the above-mentioned Adapter implementation, which may not be well summarized? I suggest referring to more articles and examples.)

Asp.Net Webapi's message processing pipeline is a processing pipeline composed of HttpMessageHandler's delegation chain

The top one and only abstract method in the abstract class of HttpMessageHandler is used for implementation. Its input is HttpRequestMessage and its output is HttpResponseMessage.

1 protected internal abstract Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken);

Delegating Handler implements HttpMessageHandler, whose constructor passes in HttpMessageHandler, and a delegation chain is formed by innerHandler of the same kind.

Recommend an MS document h ttps://docs.microsoft.com/en-us/aspnet/web-api/overview/advanced/http-message-handlers If you are interested, you can refer to it a little.

 1         protected DelegatingHandler(HttpMessageHandler innerHandler)
 2         {
 3             InnerHandler = innerHandler;
 4         }
 5 
 6         protected internal override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
 7         {
 8             if (request == null)
 9             {
10                 throw new ArgumentNullException(nameof(request), SR.net_http_handler_norequest);
11             }
12             SetOperationStarted();
13             return _innerHandler.SendAsync(request, cancellationToken);
14         }

In the middle, we have a long list to illustrate the role of HttpMessageHandler, so that we can further understand why HttpMessageHandler Adapter exists and pass this type in when using (WebApi middleware).

In the HttpMessageHandler Adapter constructor, _messageHandler is wrapped as the HttpMessageInvoker type, which is intended to provide a specialized class for invoking the SendAsync method.

We have just learned that HttpMessageHandler Adapter implements OWinMiddleware, so we can understand from the source code what we have done in the abstract method Invoke: it calls the InvokeCore method of the same kind, and creates HttpRequestMessage in InvokeCore, and takes its object as SendAsync's reference, and finally gets the HttpResponseMessage object.

IV. Write at the end

In this way, once through the source code reading, once again on the understanding of the Adapter, HttpMessageHandler Adapter finally through the implementation of Owin Middleware, in Invoke through the HttpMessageInvoker call SendAsync, dropped into HttpRequestMessage, get ResponseMessage. Finally attached with the HttpMessageHandler Adapter source code, more source code, or from the connection in the second paragraph down. Take it.

  1 // Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
  2 
  3 using System.Collections.Generic;
  4 using System.Diagnostics;
  5 using System.Diagnostics.CodeAnalysis;
  6 using System.Diagnostics.Contracts;
  7 using System.IO;
  8 using System.Net;
  9 using System.Net.Http;
 10 using System.Net.Http.Headers;
 11 using System.Runtime.ExceptionServices;
 12 using System.Security.Principal;
 13 using System.Threading;
 14 using System.Threading.Tasks;
 15 using System.Web.Http.Controllers;
 16 using System.Web.Http.ExceptionHandling;
 17 using System.Web.Http.Hosting;
 18 using System.Web.Http.Owin.ExceptionHandling;
 19 using System.Web.Http.Owin.Properties;
 20 using Microsoft.Owin;
 21 
 22 namespace System.Web.Http.Owin
 23 {
 24     /// <summary>
 25     /// Represents an OWIN component that submits requests to an <see cref="HttpMessageHandler"/> when invoked.
 26     /// </summary>
 27     public class HttpMessageHandlerAdapter : OwinMiddleware, IDisposable
 28     {
 29         private readonly HttpMessageHandler _messageHandler;
 30         private readonly HttpMessageInvoker _messageInvoker;
 31         private readonly IHostBufferPolicySelector _bufferPolicySelector;
 32         private readonly IExceptionLogger _exceptionLogger;
 33         private readonly IExceptionHandler _exceptionHandler;
 34         private readonly CancellationToken _appDisposing;
 35 
 36         private bool _disposed;
 37 
 38         /// <summary>Initializes a new instance of the <see cref="HttpMessageHandlerAdapter"/> class.</summary>
 39         /// <param name="next">The next component in the pipeline.</param>
 40         /// <param name="options">The options to configure this adapter.</param>
 41         public HttpMessageHandlerAdapter(OwinMiddleware next, HttpMessageHandlerOptions options)
 42             : base(next)
 43         {
 44             if (options == null)
 45             {
 46                 throw new ArgumentNullException("options");
 47             }
 48 
 49             _messageHandler = options.MessageHandler;
 50 
 51             if (_messageHandler == null)
 52             {
 53                 throw new ArgumentException(Error.Format(OwinResources.TypePropertyMustNotBeNull,
 54                     typeof(HttpMessageHandlerOptions).Name, "MessageHandler"), "options");
 55             }
 56 
 57             _messageInvoker = new HttpMessageInvoker(_messageHandler);
 58             _bufferPolicySelector = options.BufferPolicySelector;
 59 
 60             if (_bufferPolicySelector == null)
 61             {
 62                 throw new ArgumentException(Error.Format(OwinResources.TypePropertyMustNotBeNull,
 63                     typeof(HttpMessageHandlerOptions).Name, "BufferPolicySelector"), "options");
 64             }
 65 
 66             _exceptionLogger = options.ExceptionLogger;
 67 
 68             if (_exceptionLogger == null)
 69             {
 70                 throw new ArgumentException(Error.Format(OwinResources.TypePropertyMustNotBeNull,
 71                     typeof(HttpMessageHandlerOptions).Name, "ExceptionLogger"), "options");
 72             }
 73 
 74             _exceptionHandler = options.ExceptionHandler;
 75 
 76             if (_exceptionHandler == null)
 77             {
 78                 throw new ArgumentException(Error.Format(OwinResources.TypePropertyMustNotBeNull,
 79                     typeof(HttpMessageHandlerOptions).Name, "ExceptionHandler"), "options");
 80             }
 81 
 82             _appDisposing = options.AppDisposing;
 83 
 84             if (_appDisposing.CanBeCanceled)
 85             {
 86                 _appDisposing.Register(OnAppDisposing);
 87             }
 88         }
 89 
 90         /// <summary>Initializes a new instance of the <see cref="HttpMessageHandlerAdapter"/> class.</summary>
 91         /// <param name="next">The next component in the pipeline.</param>
 92         /// <param name="messageHandler">The <see cref="HttpMessageHandler"/> to submit requests to.</param>
 93         /// <param name="bufferPolicySelector">
 94         /// The <see cref="IHostBufferPolicySelector"/> that determines whether or not to buffer requests and
 95         /// responses.
 96         /// </param>
 97         /// <remarks>
 98         /// This constructor is retained for backwards compatibility. The constructor taking
 99         /// <see cref="HttpMessageHandlerOptions"/> should be used instead.
100         /// </remarks>
101         [Obsolete("Use the HttpMessageHandlerAdapter(OwinMiddleware, HttpMessageHandlerOptions) constructor instead.")]
102         public HttpMessageHandlerAdapter(OwinMiddleware next, HttpMessageHandler messageHandler,
103             IHostBufferPolicySelector bufferPolicySelector)
104             : this(next, CreateOptions(messageHandler, bufferPolicySelector))
105         {
106         }
107 
108         /// <summary>Gets the <see cref="HttpMessageHandler"/> to submit requests to.</summary>
109         public HttpMessageHandler MessageHandler
110         {
111             get { return _messageHandler; }
112         }
113 
114         /// <summary>
115         /// Gets the <see cref="IHostBufferPolicySelector"/> that determines whether or not to buffer requests and
116         /// responses.
117         /// </summary>
118         public IHostBufferPolicySelector BufferPolicySelector
119         {
120             get { return _bufferPolicySelector; }
121         }
122 
123         /// <summary>Gets the <see cref="IExceptionLogger"/> to use to log unhandled exceptions.</summary>
124         public IExceptionLogger ExceptionLogger
125         {
126             get { return _exceptionLogger; }
127         }
128 
129         /// <summary>Gets the <see cref="IExceptionHandler"/> to use to process unhandled exceptions.</summary>
130         public IExceptionHandler ExceptionHandler
131         {
132             get { return _exceptionHandler; }
133         }
134 
135         /// <summary>Gets the <see cref="CancellationToken"/> that triggers cleanup of this component.</summary>
136         public CancellationToken AppDisposing
137         {
138             get { return _appDisposing; }
139         }
140 
141         /// <inheritdoc />
142         public override Task Invoke(IOwinContext context)
143         {
144             if (context == null)
145             {
146                 throw new ArgumentNullException("context");
147             }
148 
149             IOwinRequest owinRequest = context.Request;
150             IOwinResponse owinResponse = context.Response;
151 
152             if (owinRequest == null)
153             {
154                 throw Error.InvalidOperation(OwinResources.OwinContext_NullRequest);
155             }
156             if (owinResponse == null)
157             {
158                 throw Error.InvalidOperation(OwinResources.OwinContext_NullResponse);
159             }
160 
161             return InvokeCore(context, owinRequest, owinResponse);
162         }
163 
164         private async Task InvokeCore(IOwinContext context, IOwinRequest owinRequest, IOwinResponse owinResponse)
165         {
166             CancellationToken cancellationToken = owinRequest.CallCancelled;
167             HttpContent requestContent;
168 
169             bool bufferInput = _bufferPolicySelector.UseBufferedInputStream(hostContext: context);
170 
171             if (!bufferInput)
172             {
173                 owinRequest.DisableBuffering();
174             }
175 
176             if (!owinRequest.Body.CanSeek && bufferInput)
177             {
178                 requestContent = await CreateBufferedRequestContentAsync(owinRequest, cancellationToken);
179             }
180             else
181             {
182                 requestContent = CreateStreamedRequestContent(owinRequest);
183             }
184 
185             HttpRequestMessage request = CreateRequestMessage(owinRequest, requestContent);
186             MapRequestProperties(request, context);
187 
188             SetPrincipal(owinRequest.User);
189 
190             HttpResponseMessage response = null;
191             bool callNext;
192 
193             try
194             {
195                 response = await _messageInvoker.SendAsync(request, cancellationToken);
196 
197                 // Handle null responses
198                 if (response == null)
199                 {
200                     throw Error.InvalidOperation(OwinResources.SendAsync_ReturnedNull);
201                 }
202 
203                 // Handle soft 404s where no route matched - call the next component
204                 if (IsSoftNotFound(request, response))
205                 {
206                     callNext = true;
207                 }
208                 else
209                 {
210                     callNext = false;
211 
212                     // Compute Content-Length before calling UseBufferedOutputStream because the default implementation
213                     // accesses that header and we want to catch any exceptions calling TryComputeLength here.
214 
215                     if (response.Content == null
216                         || await ComputeContentLengthAsync(request, response, owinResponse, cancellationToken))
217                     {
218                         bool bufferOutput = _bufferPolicySelector.UseBufferedOutputStream(response);
219 
220                         if (!bufferOutput)
221                         {
222                             owinResponse.DisableBuffering();
223                         }
224                         else if (response.Content != null)
225                         {
226                             response = await BufferResponseContentAsync(request, response, cancellationToken);
227                         }
228 
229                         if (await PrepareHeadersAsync(request, response, owinResponse, cancellationToken))
230                         {
231                             await SendResponseMessageAsync(request, response, owinResponse, cancellationToken);
232                         }
233                     }
234                 }
235             }
236             finally
237             {
238                 request.DisposeRequestResources();
239                 request.Dispose();
240                 if (response != null)
241                 {
242                     response.Dispose();
243                 }
244             }
245 
246             // Call the next component if no route matched
247             if (callNext && Next != null)
248             {
249                 await Next.Invoke(context);
250             }
251         }
252 
253         private static HttpContent CreateStreamedRequestContent(IOwinRequest owinRequest)
254         {
255             // Note that we must NOT dispose owinRequest.Body in this case. Disposing it would close the input
256             // stream and prevent cascaded components from accessing it. The server MUST handle any necessary
257             // cleanup upon request completion. NonOwnedStream prevents StreamContent (or its callers including
258             // HttpRequestMessage) from calling Close or Dispose on owinRequest.Body.
259             return new StreamContent(new NonOwnedStream(owinRequest.Body));
260         }
261 
262         private static async Task<HttpContent> CreateBufferedRequestContentAsync(IOwinRequest owinRequest,
263             CancellationToken cancellationToken)
264         {
265             // We need to replace the request body with a buffered stream so that other components can read the stream.
266             // For this stream to be useful, it must NOT be diposed along with the request. Streams created by
267             // StreamContent do get disposed along with the request, so use MemoryStream to buffer separately.
268             MemoryStream buffer;
269             int? contentLength = owinRequest.GetContentLength();
270 
271             if (!contentLength.HasValue)
272             {
273                 buffer = new MemoryStream();
274             }
275             else
276             {
277                 buffer = new MemoryStream(contentLength.Value);
278             }
279 
280             cancellationToken.ThrowIfCancellationRequested();
281 
282             using (StreamContent copier = new StreamContent(owinRequest.Body))
283             {
284                 await copier.CopyToAsync(buffer);
285             }
286 
287             // Provide the non-disposing, buffered stream to later OWIN components (set to the stream's beginning).
288             buffer.Position = 0;
289             owinRequest.Body = buffer;
290 
291             // For MemoryStream, Length is guaranteed to be an int.
292             return new ByteArrayContent(buffer.GetBuffer(), 0, (int)buffer.Length);
293         }
294 
295         private static HttpRequestMessage CreateRequestMessage(IOwinRequest owinRequest, HttpContent requestContent)
296         {
297             // Create the request
298             HttpRequestMessage request = new HttpRequestMessage(new HttpMethod(owinRequest.Method), owinRequest.Uri);
299 
300             try
301             {
302                 // Set the body
303                 request.Content = requestContent;
304 
305                 // Copy the headers
306                 foreach (KeyValuePair<string, string[]> header in owinRequest.Headers)
307                 {
308                     if (!request.Headers.TryAddWithoutValidation(header.Key, header.Value))
309                     {
310                         bool success = requestContent.Headers.TryAddWithoutValidation(header.Key, header.Value);
311                         Contract.Assert(success,
312                             "Every header can be added either to the request headers or to the content headers");
313                     }
314                 }
315             }
316             catch
317             {
318                 request.Dispose();
319                 throw;
320             }
321 
322             return request;
323         }
324 
325         private static void MapRequestProperties(HttpRequestMessage request, IOwinContext context)
326         {
327             // Set the OWIN context on the request
328             request.SetOwinContext(context);
329 
330             // Set a request context on the request that lazily populates each property.
331             HttpRequestContext requestContext = new OwinHttpRequestContext(context, request);
332             request.SetRequestContext(requestContext);
333         }
334 
335         private static void SetPrincipal(IPrincipal user)
336         {
337             if (user != null)
338             {
339                 Thread.CurrentPrincipal = user;
340             }
341         }
342 
343         private static bool IsSoftNotFound(HttpRequestMessage request, HttpResponseMessage response)
344         {
345             if (response.StatusCode == HttpStatusCode.NotFound)
346             {
347                 bool routingFailure;
348                 if (request.Properties.TryGetValue<bool>(HttpPropertyKeys.NoRouteMatched, out routingFailure)
349                     && routingFailure)
350                 {
351                     return true;
352                 }
353             }
354             return false;
355         }
356 
357         private async Task<HttpResponseMessage> BufferResponseContentAsync(HttpRequestMessage request,
358             HttpResponseMessage response, CancellationToken cancellationToken)
359         {
360             ExceptionDispatchInfo exceptionInfo;
361 
362             cancellationToken.ThrowIfCancellationRequested();
363 
364             try
365             {
366                 await response.Content.LoadIntoBufferAsync();
367                 return response;
368             }
369             catch (OperationCanceledException)
370             {
371                 // Propogate the canceled task without calling exception loggers or handlers.
372                 throw;
373             }
374             catch (Exception exception)
375             {
376                 exceptionInfo = ExceptionDispatchInfo.Capture(exception);
377             }
378 
379             // If the content can't be buffered, create a buffered error response for the exception
380             // This code will commonly run when a formatter throws during the process of serialization
381 
382             Debug.Assert(exceptionInfo.SourceException != null);
383 
384             ExceptionContext exceptionContext = new ExceptionContext(exceptionInfo.SourceException,
385                 OwinExceptionCatchBlocks.HttpMessageHandlerAdapterBufferContent, request, response);
386 
387             await _exceptionLogger.LogAsync(exceptionContext, cancellationToken);
388             HttpResponseMessage errorResponse = await _exceptionHandler.HandleAsync(exceptionContext,
389                 cancellationToken);
390 
391             response.Dispose();
392 
393             if (errorResponse == null)
394             {
395                 exceptionInfo.Throw();
396                 return null;
397             }
398 
399             // We have an error response to try to buffer and send back.
400 
401             response = errorResponse;
402             cancellationToken.ThrowIfCancellationRequested();
403 
404             Exception errorException;
405 
406             try
407             {
408                 // Try to buffer the error response and send it back.
409                 await response.Content.LoadIntoBufferAsync();
410                 return response;
411             }
412             catch (OperationCanceledException)
413             {
414                 // Propogate the canceled task without calling exception loggers.
415                 throw;
416             }
417             catch (Exception exception)
418             {
419                 errorException = exception;
420             }
421 
422             // We tried to send back an error response with content, but we couldn't. It's an edge case; the best we
423             // can do is to log that exception and send back an empty 500.
424 
425             ExceptionContext errorExceptionContext = new ExceptionContext(errorException,
426                 OwinExceptionCatchBlocks.HttpMessageHandlerAdapterBufferError, request, response);
427             await _exceptionLogger.LogAsync(errorExceptionContext, cancellationToken);
428 
429             response.Dispose();
430             return request.CreateResponse(HttpStatusCode.InternalServerError);
431         }
432 
433         // Prepares Content-Length and Transfer-Encoding headers.
434         private Task<bool> PrepareHeadersAsync(HttpRequestMessage request, HttpResponseMessage response,
435             IOwinResponse owinResponse, CancellationToken cancellationToken)
436         {
437             Contract.Assert(response != null);
438             HttpResponseHeaders responseHeaders = response.Headers;
439             Contract.Assert(responseHeaders != null);
440             HttpContent content = response.Content;
441             bool isTransferEncodingChunked = responseHeaders.TransferEncodingChunked == true;
442             HttpHeaderValueCollection<TransferCodingHeaderValue> transferEncoding = responseHeaders.TransferEncoding;
443 
444             if (content != null)
445             {
446                 HttpContentHeaders contentHeaders = content.Headers;
447                 Contract.Assert(contentHeaders != null);
448 
449                 if (isTransferEncodingChunked)
450                 {
451                     // According to section 4.4 of the HTTP 1.1 spec, HTTP responses that use chunked transfer
452                     // encoding must not have a content length set. Chunked should take precedence over content
453                     // length in this case because chunked is always set explicitly by users while the Content-Length
454                     // header can be added implicitly by System.Net.Http.
455                     contentHeaders.ContentLength = null;
456                 }
457                 else
458                 {
459                     // Copy the response content headers only after ensuring they are complete.
460                     // We ask for Content-Length first because HttpContent lazily computes this header and only
461                     // afterwards writes the value into the content headers.
462                     return ComputeContentLengthAsync(request, response, owinResponse, cancellationToken);
463                 }
464             }
465 
466             // Ignore the Transfer-Encoding header if it is just "chunked"; the host will likely provide it when no
467             // Content-Length is present (and if the host does not, there's not much better this code could do to
468             // transmit the current response, since HttpContent is assumed to be unframed; in that case, silently drop
469             // the Transfer-Encoding: chunked header).
470             // HttpClient sets this header when it receives chunked content, but HttpContent does not include the
471             // frames. The OWIN contract is to set this header only when writing chunked frames to the stream.
472             // A Web API caller who desires custom framing would need to do a different Transfer-Encoding (such as
473             // "identity, chunked").
474             if (isTransferEncodingChunked && transferEncoding.Count == 1)
475             {
476                 transferEncoding.Clear();
477             }
478 
479             return Task.FromResult(true);
480         }
481 
482         [SuppressMessage("Microsoft.Performance", "CA1804:RemoveUnusedLocals", MessageId = "unused",
483             Justification = "unused variable necessary to call getter")]
484         [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes",
485             Justification = "Exception is turned into an error response.")]
486         private Task<bool> ComputeContentLengthAsync(HttpRequestMessage request, HttpResponseMessage response,
487             IOwinResponse owinResponse, CancellationToken cancellationToken)
488         {
489             Contract.Assert(response != null);
490             HttpResponseHeaders responseHeaders = response.Headers;
491             Contract.Assert(responseHeaders != null);
492             HttpContent content = response.Content;
493             Contract.Assert(content != null);
494             HttpContentHeaders contentHeaders = content.Headers;
495             Contract.Assert(contentHeaders != null);
496 
497             Exception exception;
498 
499             try
500             {
501                 var unused = contentHeaders.ContentLength;
502 
503                 return Task.FromResult(true);
504             }
505             catch (Exception ex)
506             {
507                 exception = ex;
508             }
509 
510             return HandleTryComputeLengthExceptionAsync(exception, request, response, owinResponse, cancellationToken);
511         }
512 
513         private async Task<bool> HandleTryComputeLengthExceptionAsync(Exception exception, HttpRequestMessage request,
514             HttpResponseMessage response, IOwinResponse owinResponse, CancellationToken cancellationToken)
515         {
516             Contract.Assert(owinResponse != null);
517 
518             ExceptionContext exceptionContext = new ExceptionContext(exception,
519                 OwinExceptionCatchBlocks.HttpMessageHandlerAdapterComputeContentLength, request, response);
520             await _exceptionLogger.LogAsync(exceptionContext, cancellationToken);
521 
522             // Send back an empty error response if TryComputeLength throws.
523             owinResponse.StatusCode = (int)HttpStatusCode.InternalServerError;
524             SetHeadersForEmptyResponse(owinResponse.Headers);
525             return false;
526         }
527 
528         private Task SendResponseMessageAsync(HttpRequestMessage request, HttpResponseMessage response,
529             IOwinResponse owinResponse, CancellationToken cancellationToken)
530         {
531             owinResponse.StatusCode = (int)response.StatusCode;
532             owinResponse.ReasonPhrase = response.ReasonPhrase;
533 
534             // Copy non-content headers
535             IDictionary<string, string[]> responseHeaders = owinResponse.Headers;
536             foreach (KeyValuePair<string, IEnumerable<string>> header in response.Headers)
537             {
538                 responseHeaders[header.Key] = header.Value.AsArray();
539             }
540 
541             HttpContent responseContent = response.Content;
542             if (responseContent == null)
543             {
544                 SetHeadersForEmptyResponse(responseHeaders);
545                 return TaskHelpers.Completed();
546             }
547             else
548             {
549                 // Copy content headers
550                 foreach (KeyValuePair<string, IEnumerable<string>> contentHeader in responseContent.Headers)
551                 {
552                     responseHeaders[contentHeader.Key] = contentHeader.Value.AsArray();
553                 }
554 
555                 // Copy body
556                 return SendResponseContentAsync(request, response, owinResponse, cancellationToken);
557             }
558         }
559 
560         private static void SetHeadersForEmptyResponse(IDictionary<string, string[]> headers)
561         {
562             // Set the content-length to 0 to prevent the server from sending back the response chunked
563             headers["Content-Length"] = new string[] { "0" };
564         }
565 
566         private async Task SendResponseContentAsync(HttpRequestMessage request, HttpResponseMessage response,
567             IOwinResponse owinResponse, CancellationToken cancellationToken)
568         {
569             Contract.Assert(response != null);
570             Contract.Assert(response.Content != null);
571 
572             Exception exception;
573             cancellationToken.ThrowIfCancellationRequested();
574 
575             try
576             {
577                 await response.Content.CopyToAsync(owinResponse.Body);
578                 return;
579             }
580             catch (OperationCanceledException)
581             {
582                 // Propogate the canceled task without calling exception loggers;
583                 throw;
584             }
585             catch (Exception ex)
586             {
587                 exception = ex;
588             }
589 
590             // We're streaming content, so we can only call loggers, not handlers, as we've already (possibly) send the
591             // status code and headers across the wire. Log the exception, but then just abort.
592             ExceptionContext exceptionContext = new ExceptionContext(exception,
593                 OwinExceptionCatchBlocks.HttpMessageHandlerAdapterStreamContent, request, response);
594             await _exceptionLogger.LogAsync(exceptionContext, cancellationToken);
595             await AbortResponseAsync();
596         }
597 
598         private static Task AbortResponseAsync()
599         {
600             // OWIN doesn't yet support an explicit Abort event. Returning a canceled task is the best contract at the
601             // moment.
602             return TaskHelpers.Canceled();
603         }
604 
605         // Provides HttpMessageHandlerOptions for callers using the old constructor.
606         private static HttpMessageHandlerOptions CreateOptions(HttpMessageHandler messageHandler,
607             IHostBufferPolicySelector bufferPolicySelector)
608         {
609             if (messageHandler == null)
610             {
611                 throw new ArgumentNullException("messageHandler");
612             }
613 
614             if (bufferPolicySelector == null)
615             {
616                 throw new ArgumentNullException("bufferPolicySelector");
617             }
618 
619             // Callers using the old constructor get the default exception handler, no exception logging support, and no
620             // app cleanup support.
621 
622             return new HttpMessageHandlerOptions
623             {
624                 MessageHandler = messageHandler,
625                 BufferPolicySelector = bufferPolicySelector,
626                 ExceptionLogger = new EmptyExceptionLogger(),
627                 ExceptionHandler = new DefaultExceptionHandler(),
628                 AppDisposing = CancellationToken.None
629             };
630         }
631 
632         /// <summary>
633         /// Releases unmanaged and optionally managed resources.
634         /// </summary>
635         /// <param name="disposing">
636         /// <see langword="true"/> to release both managed and unmanaged resources; <see langword="false"/> to release
637         /// only unmanaged resources.
638         /// </param>
639         /// <remarks>
640         /// This class implements <see cref="IDisposable"/> for legacy reasons. New callers should instead provide a
641         /// cancellation token via <see cref="AppDisposing"/> using the constructor that takes
642         /// <see cref="HttpMessageHandlerOptions"/>.
643         /// </remarks>
644         protected virtual void Dispose(bool disposing)
645         {
646             if (disposing)
647             {
648                 OnAppDisposing();
649             }
650         }
651 
652         /// <inheritdoc />
653         /// <remarks>
654         /// This class implements <see cref="IDisposable"/> for legacy reasons. New callers should instead provide a
655         /// cancellation token via <see cref="AppDisposing"/> using the constructor that takes
656         /// <see cref="HttpMessageHandlerOptions"/>.
657         /// </remarks>
658         public void Dispose()
659         {
660             Dispose(true);
661             GC.SuppressFinalize(this);
662         }
663 
664         private void OnAppDisposing()
665         {
666             if (!_disposed)
667             {
668                 _messageInvoker.Dispose();
669                 _disposed = true;
670             }
671         }
672     }
673 }

 

If you think reading this blog will give you some benefit, click on the bottom right to add [Recommendation] Button.
If you want to find my new blog more easily, click on the red one below.
Because my enthusiasm for sharing can not be separated from your affirmative support.

Thank you for your reading. I will continue to export and share. I am a snail. Keep learning and remember to be modest. It's funny and dreamy.

Posted by idire on Thu, 13 Dec 2018 12:48:23 -0800