Introducing an OWIN Web API Adapter - System.Web.Http.Owin
This commit is contained in:
Родитель
556493386c
Коммит
18cb600a3f
|
@ -74,6 +74,7 @@
|
|||
Include="src\Microsoft.AspNet.Mvc.Facebook\*.csproj;
|
||||
src\Microsoft.Web.WebPages.OAuth\*.csproj;
|
||||
src\System.Net.Http.Formatting.NetCore\*.csproj;
|
||||
src\System.Web.Http.Owin\*.csproj;
|
||||
src\System.Web.Http.SignalR\*.csproj;
|
||||
src\System.Web.WebPages.Administration\*.csproj;
|
||||
test\System.Web.Http.OData.Test\*.csproj" />
|
||||
|
|
18
Runtime.sln
18
Runtime.sln
|
@ -97,6 +97,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebApiHelpPage.VB.Test", "t
|
|||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNet.Mvc.Facebook.Test", "test\Microsoft.AspNet.Mvc.Facebook.Test\Microsoft.AspNet.Mvc.Facebook.Test.csproj", "{C3BEF382-C7C4-454D-B017-1EAC03E9A82C}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Web.Http.Owin", "src\System.Web.Http.Owin\System.Web.Http.Owin.csproj", "{66DD7CD7-C68F-4D0E-9F3D-3B58C49D1467}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Web.Http.Owin.Test", "test\System.Web.Http.Owin.Test\System.Web.Http.Owin.Test.csproj", "{C19267DD-3984-430C-AE18-4034F85DE4E5}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{CB34D534-9A09-4EE4-B350-C1C23AFBF5EE}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
.nuget\NuGet.Config = .nuget\NuGet.Config
|
||||
|
@ -420,6 +424,18 @@ Global
|
|||
{1E89A3E9-0A7F-418F-B4BE-6E38A6315373}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{1E89A3E9-0A7F-418F-B4BE-6E38A6315373}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{1E89A3E9-0A7F-418F-B4BE-6E38A6315373}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{66DD7CD7-C68F-4D0E-9F3D-3B58C49D1467}.CodeAnalysis|Any CPU.ActiveCfg = CodeAnalysis|Any CPU
|
||||
{66DD7CD7-C68F-4D0E-9F3D-3B58C49D1467}.CodeAnalysis|Any CPU.Build.0 = CodeAnalysis|Any CPU
|
||||
{66DD7CD7-C68F-4D0E-9F3D-3B58C49D1467}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{66DD7CD7-C68F-4D0E-9F3D-3B58C49D1467}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{66DD7CD7-C68F-4D0E-9F3D-3B58C49D1467}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{66DD7CD7-C68F-4D0E-9F3D-3B58C49D1467}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{C19267DD-3984-430C-AE18-4034F85DE4E5}.CodeAnalysis|Any CPU.ActiveCfg = CodeAnalysis|Any CPU
|
||||
{C19267DD-3984-430C-AE18-4034F85DE4E5}.CodeAnalysis|Any CPU.Build.0 = CodeAnalysis|Any CPU
|
||||
{C19267DD-3984-430C-AE18-4034F85DE4E5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{C19267DD-3984-430C-AE18-4034F85DE4E5}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{C19267DD-3984-430C-AE18-4034F85DE4E5}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{C19267DD-3984-430C-AE18-4034F85DE4E5}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
@ -449,6 +465,7 @@ Global
|
|||
{22075D5A-E9A1-4E2D-ABA7-8E7CBF2A049E} = {A9836F9E-6DB3-4D9F-ADCA-CF42D8C8BA93}
|
||||
{43C1B979-D593-4A32-BB3A-4316F1C66D66} = {A9836F9E-6DB3-4D9F-ADCA-CF42D8C8BA93}
|
||||
{25DEF6F6-7F99-4EB7-91ED-5181A346AFE1} = {A9836F9E-6DB3-4D9F-ADCA-CF42D8C8BA93}
|
||||
{66DD7CD7-C68F-4D0E-9F3D-3B58C49D1467} = {A9836F9E-6DB3-4D9F-ADCA-CF42D8C8BA93}
|
||||
{0BB62A1D-E6B5-49FA-9E3C-6AF679A66DFE} = {C40883CD-366D-4534-8B58-3EA0D13136DF}
|
||||
{268DEE9D-F323-4A00-8ED8-3784388C3E3A} = {C40883CD-366D-4534-8B58-3EA0D13136DF}
|
||||
{0F4870DB-A799-4DBA-99DF-0D74BB52FEC2} = {C40883CD-366D-4534-8B58-3EA0D13136DF}
|
||||
|
@ -475,5 +492,6 @@ Global
|
|||
{C3BEF382-C7C4-454D-B017-1EAC03E9A82C} = {C40883CD-366D-4534-8B58-3EA0D13136DF}
|
||||
{BF07E947-120D-4E93-93DA-A4BF121753EA} = {C40883CD-366D-4534-8B58-3EA0D13136DF}
|
||||
{1E89A3E9-0A7F-418F-B4BE-6E38A6315373} = {C40883CD-366D-4534-8B58-3EA0D13136DF}
|
||||
{C19267DD-3984-430C-AE18-4034F85DE4E5} = {C40883CD-366D-4534-8B58-3EA0D13136DF}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
<repository path="..\src\System.Net.Http.Formatting.NetCore\packages.config" />
|
||||
<repository path="..\src\System.Net.Http.Formatting\packages.config" />
|
||||
<repository path="..\src\System.Web.Http.OData\packages.config" />
|
||||
<repository path="..\src\System.Web.Http.Owin\packages.config" />
|
||||
<repository path="..\src\System.Web.Http.SignalR\packages.config" />
|
||||
<repository path="..\src\System.Web.Http.Tracing\packages.config" />
|
||||
<repository path="..\src\System.Web.Http.WebHost\packages.config" />
|
||||
|
@ -27,6 +28,7 @@
|
|||
<repository path="..\test\System.Web.Http.Cors.Test\packages.config" />
|
||||
<repository path="..\test\System.Web.Http.Integration.Test\packages.config" />
|
||||
<repository path="..\test\System.Web.Http.OData.Test\packages.config" />
|
||||
<repository path="..\test\System.Web.Http.Owin.Test\packages.config" />
|
||||
<repository path="..\test\System.Web.Http.SelfHost.Test\packages.config" />
|
||||
<repository path="..\test\System.Web.Http.SignalR.Test\packages.config" />
|
||||
<repository path="..\test\System.Web.Http.Test\packages.config" />
|
||||
|
|
|
@ -48,6 +48,7 @@
|
|||
<Word>Auth</Word>
|
||||
<Word>bg</Word>
|
||||
<Word>Cors</Word>
|
||||
<Word>Owin</Word>
|
||||
</Recognized>
|
||||
<Compound>
|
||||
<Term CompoundAlternate="WebPage">WebPage</Term>
|
||||
|
|
|
@ -118,7 +118,9 @@ namespace System.Web.Http
|
|||
// If there are, fall back to the slow path.
|
||||
if (indexOfFirstOpenBracket == odataPathTemplateIndex)
|
||||
{
|
||||
string virtualPathRoot = config.VirtualPathRoot;
|
||||
// Use the virtual path root on the request if one is specified
|
||||
// Otherwise, fall back on the virtual path root for the configuration
|
||||
string virtualPathRoot = request.GetVirtualPathRoot() ?? config.VirtualPathRoot;
|
||||
if (!virtualPathRoot.EndsWith("/", StringComparison.Ordinal))
|
||||
{
|
||||
virtualPathRoot += "/";
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
|
||||
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
[assembly: SuppressMessage("Microsoft.Design", "CA2210:AssembliesShouldHaveValidStrongNames", Justification = "The assembly is delay signed")]
|
||||
[assembly: SuppressMessage("Microsoft.Design", "CA1020:AvoidNamespacesWithFewTypes", Scope = "namespace", Target = "Owin", Justification = "More types exist in this namespace within other assemblies")]
|
||||
[assembly: SuppressMessage("Microsoft.Design", "CA1020:AvoidNamespacesWithFewTypes", Scope = "namespace", Target = "System.Net.Http", Justification = "More types exist in this namespace within other assemblies")]
|
||||
[assembly: SuppressMessage("Microsoft.Design", "CA1020:AvoidNamespacesWithFewTypes", Scope = "namespace", Target = "System.Web.Http.Owin")]
|
|
@ -0,0 +1,320 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Diagnostics.Contracts;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Security.Principal;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Web.Http.Hosting;
|
||||
using System.Web.Http.Owin.Properties;
|
||||
using AppFunc = System.Func<System.Collections.Generic.IDictionary<string, object>, System.Threading.Tasks.Task>;
|
||||
|
||||
namespace System.Web.Http.Owin
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents an OWIN component that submits requests to an <see cref="HttpMessageHandler"/> when invoked.
|
||||
/// </summary>
|
||||
public class HttpMessageHandlerAdapter : IDisposable
|
||||
{
|
||||
private AppFunc _next;
|
||||
private HttpMessageInvoker _messageInvoker;
|
||||
private IHostBufferPolicySelector _bufferPolicySelector;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="HttpMessageHandlerAdapter" /> class.
|
||||
/// </summary>
|
||||
/// <param name="next">The next component in the pipeline.</param>
|
||||
/// <param name="messageHandler">The <see cref="HttpMessageHandler" /> to submit requests to.</param>
|
||||
/// <param name="bufferPolicySelector">The <see cref="IHostBufferPolicySelector"/> that determines whether or not to buffer requests and responses.</param>
|
||||
[SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "In accordance with OWIN design")]
|
||||
public HttpMessageHandlerAdapter(AppFunc next, HttpMessageHandler messageHandler, IHostBufferPolicySelector bufferPolicySelector)
|
||||
{
|
||||
if (next == null)
|
||||
{
|
||||
throw new ArgumentNullException("next");
|
||||
}
|
||||
if (messageHandler == null)
|
||||
{
|
||||
throw new ArgumentNullException("messageHandler");
|
||||
}
|
||||
if (bufferPolicySelector == null)
|
||||
{
|
||||
throw new ArgumentNullException("bufferPolicySelector");
|
||||
}
|
||||
|
||||
_next = next;
|
||||
_messageInvoker = new HttpMessageInvoker(messageHandler);
|
||||
_bufferPolicySelector = bufferPolicySelector;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invokes the component within the OWIN pipeline.
|
||||
/// </summary>
|
||||
/// <param name="environment">The OWIN environment for the request.</param>
|
||||
/// <returns>A <see cref="Task"/> that will complete when the request is processed.</returns>
|
||||
[SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "See comment below")]
|
||||
public async Task Invoke(IDictionary<string, object> environment)
|
||||
{
|
||||
if (environment == null)
|
||||
{
|
||||
throw new ArgumentNullException("environment");
|
||||
}
|
||||
|
||||
Stream requestBody = environment.GetOwinValue<Stream>(OwinConstants.RequestBodyKey);
|
||||
HttpRequestMessage request = CreateRequestMessage(environment, requestBody);
|
||||
if (!requestBody.CanSeek && _bufferPolicySelector.UseBufferedInputStream(hostContext: environment))
|
||||
{
|
||||
await BufferRequestBodyAsync(environment, request.Content);
|
||||
}
|
||||
CancellationToken cancellationToken = environment.GetOwinValue<CancellationToken>(OwinConstants.CallCancelledKey);
|
||||
|
||||
SetPrincipal(environment);
|
||||
|
||||
HttpResponseMessage response = null;
|
||||
bool callNext = false;
|
||||
try
|
||||
{
|
||||
response = await _messageInvoker.SendAsync(request, cancellationToken);
|
||||
|
||||
// Handle null responses
|
||||
if (response == null)
|
||||
{
|
||||
throw Error.InvalidOperation(SRResources.SendAsync_ReturnedNull);
|
||||
}
|
||||
|
||||
// Handle soft 404s where no route matched - call the next component
|
||||
if (IsSoftNotFound(request, response))
|
||||
{
|
||||
callNext = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (response.Content != null && _bufferPolicySelector.UseBufferedOutputStream(response))
|
||||
{
|
||||
response = await BufferResponseBodyAsync(request, response);
|
||||
}
|
||||
|
||||
await SendResponseMessageAsync(environment, response);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
// Note that the HttpRequestMessage is explicitly NOT disposed. Disposing it would close the input stream
|
||||
// and prevent cascaded components from accessing it. The server MUST handle any necessary cleanup upon
|
||||
// request completion.
|
||||
request.DisposeRequestResources();
|
||||
if (response != null)
|
||||
{
|
||||
response.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
// Call the next component if no route matched
|
||||
if (callNext)
|
||||
{
|
||||
await _next.Invoke(environment);
|
||||
}
|
||||
}
|
||||
|
||||
[SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "Not out of scope")]
|
||||
private static HttpRequestMessage CreateRequestMessage(IDictionary<string, object> environment, Stream requestBody)
|
||||
{
|
||||
string requestMethod = environment.GetOwinValue<string>(OwinConstants.RequestMethodKey);
|
||||
IDictionary<string, string[]> requestHeaders = environment.GetOwinValue<IDictionary<string, string[]>>(OwinConstants.RequestHeadersKey);
|
||||
string requestPathBase;
|
||||
Uri requestUri = CreateRequestUri(environment, requestHeaders, out requestPathBase);
|
||||
|
||||
// Create the request
|
||||
HttpRequestMessage request = new HttpRequestMessage(new HttpMethod(requestMethod), requestUri);
|
||||
|
||||
// Set the body
|
||||
HttpContent content = new StreamContent(requestBody);
|
||||
request.Content = content;
|
||||
|
||||
// Copy the headers
|
||||
foreach (KeyValuePair<string, string[]> header in requestHeaders)
|
||||
{
|
||||
if (!request.Headers.TryAddWithoutValidation(header.Key, header.Value))
|
||||
{
|
||||
bool success = request.Content.Headers.TryAddWithoutValidation(header.Key, header.Value);
|
||||
Contract.Assert(success, "Every header can be added either to the request headers or to the content headers");
|
||||
}
|
||||
}
|
||||
|
||||
// Map the OWIN environment keys to the request properties keys that Web API expects
|
||||
MapRequestProperties(request, environment, requestPathBase);
|
||||
|
||||
return request;
|
||||
}
|
||||
|
||||
// Implements the algorithm for reconstructing a URI according to section 5.4 of the OWIN specification
|
||||
private static Uri CreateRequestUri(IDictionary<string, object> environment, IDictionary<string, string[]> requestHeaders, out string requestPathBase)
|
||||
{
|
||||
StringBuilder uriBuilder = new StringBuilder();
|
||||
|
||||
// Append request scheme
|
||||
string requestScheme = environment.GetOwinValue<string>(OwinConstants.RequestSchemeKey);
|
||||
uriBuilder.Append(requestScheme);
|
||||
|
||||
uriBuilder.Append("://");
|
||||
|
||||
// Append host and port
|
||||
string[] hostHeaderValues;
|
||||
if (requestHeaders.TryGetValue("Host", out hostHeaderValues) && hostHeaderValues.Length > 0)
|
||||
{
|
||||
uriBuilder.Append(hostHeaderValues[0]);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw Error.InvalidOperation(SRResources.CreateRequestURI_MissingHostHeader);
|
||||
}
|
||||
|
||||
// Append request path
|
||||
requestPathBase = environment.GetOwinValue<string>(OwinConstants.RequestPathBaseKey);
|
||||
uriBuilder.Append(requestPathBase);
|
||||
string requestPath = environment.GetOwinValue<string>(OwinConstants.RequestPathKey);
|
||||
uriBuilder.Append(requestPath);
|
||||
|
||||
// Append query string
|
||||
string requestQueryString = environment.GetOwinValue<string>(OwinConstants.RequestQueryStringKey);
|
||||
if (requestQueryString.Length > 0)
|
||||
{
|
||||
uriBuilder.Append('?');
|
||||
uriBuilder.Append(requestQueryString);
|
||||
}
|
||||
|
||||
return new Uri(uriBuilder.ToString(), UriKind.Absolute);
|
||||
}
|
||||
|
||||
private static void MapRequestProperties(HttpRequestMessage request, IDictionary<string, object> environment, string requestPathBase)
|
||||
{
|
||||
// Set the environment on the request
|
||||
request.SetOwinEnvironment(environment);
|
||||
|
||||
// Set the virtual path root for link resolution and link generation to work
|
||||
// OWIN spec requires request path base to be either the empty string or start with "/"
|
||||
request.SetVirtualPathRoot(requestPathBase.Length == 0 ? "/" : requestPathBase);
|
||||
|
||||
// Set a delegate to get the client certificate
|
||||
request.Properties[HttpPropertyKeys.RetrieveClientCertificateDelegateKey] = new Func<HttpRequestMessage, X509Certificate2>(
|
||||
req =>
|
||||
{
|
||||
X509Certificate2 clientCertificate;
|
||||
return environment.TryGetValue<X509Certificate2>(OwinConstants.ClientCertifiateKey, out clientCertificate) ? clientCertificate : null;
|
||||
});
|
||||
|
||||
// Set a lazily-evaluated way of determining whether the request is local or not
|
||||
Lazy<bool> isLocal = new Lazy<bool>(() =>
|
||||
{
|
||||
bool local;
|
||||
if (environment.TryGetValue(OwinConstants.IsLocalKey, out local))
|
||||
{
|
||||
return local;
|
||||
}
|
||||
return false;
|
||||
}, isThreadSafe: false);
|
||||
request.Properties[HttpPropertyKeys.IsLocalKey] = isLocal;
|
||||
}
|
||||
|
||||
private static async Task BufferRequestBodyAsync(IDictionary<string, object> environment, HttpContent content)
|
||||
{
|
||||
await content.LoadIntoBufferAsync();
|
||||
// We need to replace the request body with a buffered stream so that other
|
||||
// components can read the stream
|
||||
environment[OwinConstants.RequestBodyKey] = await content.ReadAsStreamAsync();
|
||||
}
|
||||
|
||||
private static void SetPrincipal(IDictionary<string, object> environment)
|
||||
{
|
||||
// Set the principal
|
||||
IPrincipal user;
|
||||
if (environment.TryGetValue<IPrincipal>(OwinConstants.UserKey, out user))
|
||||
{
|
||||
Thread.CurrentPrincipal = user;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsSoftNotFound(HttpRequestMessage request, HttpResponseMessage response)
|
||||
{
|
||||
if (response.StatusCode == HttpStatusCode.NotFound)
|
||||
{
|
||||
bool routingFailure;
|
||||
if (request.Properties.TryGetValue<bool>(HttpPropertyKeys.NoRouteMatched, out routingFailure) && routingFailure)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static async Task<HttpResponseMessage> BufferResponseBodyAsync(HttpRequestMessage request, HttpResponseMessage response)
|
||||
{
|
||||
try
|
||||
{
|
||||
await response.Content.LoadIntoBufferAsync();
|
||||
return response;
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
response.Dispose();
|
||||
return request.CreateErrorResponse(HttpStatusCode.InternalServerError, exception);
|
||||
}
|
||||
}
|
||||
|
||||
private static Task SendResponseMessageAsync(IDictionary<string, object> environment, HttpResponseMessage response)
|
||||
{
|
||||
environment[OwinConstants.ResponseStatusCodeKey] = response.StatusCode;
|
||||
environment[OwinConstants.ResponseReasonPhraseKey] = response.ReasonPhrase;
|
||||
|
||||
// Copy non-content headers
|
||||
IDictionary<string, string[]> responseHeaders = environment.GetOwinValue<IDictionary<string, string[]>>(OwinConstants.ResponseHeadersKey);
|
||||
foreach (KeyValuePair<string, IEnumerable<string>> header in response.Headers)
|
||||
{
|
||||
responseHeaders[header.Key] = header.Value.ToArray();
|
||||
}
|
||||
|
||||
if (response.Content == null)
|
||||
{
|
||||
return TaskHelpers.Completed();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Trigger delayed content-length calculations before enumerating the headers.
|
||||
response.Content.Headers.ContentLength = response.Content.Headers.ContentLength;
|
||||
|
||||
// Copy content headers
|
||||
foreach (KeyValuePair<string, IEnumerable<string>> contentHeader in response.Content.Headers)
|
||||
{
|
||||
responseHeaders[contentHeader.Key] = contentHeader.Value.ToArray();
|
||||
}
|
||||
|
||||
// Copy body
|
||||
Stream responseBody = environment.GetOwinValue<Stream>(OwinConstants.ResponseBodyKey);
|
||||
return response.Content.CopyToAsync(responseBody);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Releases unmanaged and optionally managed resources.
|
||||
/// </summary>
|
||||
/// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
_messageInvoker.Dispose();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
|
||||
|
||||
using System.Net.Http;
|
||||
using System.Web.Http.Hosting;
|
||||
|
||||
namespace System.Web.Http.Owin
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides the default implementation of <see cref="IHostBufferPolicySelector"/> used by the OWIN Web API adapter.
|
||||
/// </summary>
|
||||
public class OwinBufferPolicySelector : IHostBufferPolicySelector
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public bool UseBufferedInputStream(object hostContext)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool UseBufferedOutputStream(HttpResponseMessage response)
|
||||
{
|
||||
if (response == null)
|
||||
{
|
||||
throw Error.ArgumentNull("response");
|
||||
}
|
||||
|
||||
HttpContent content = response.Content;
|
||||
if (content == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Any HttpContent that knows its length is presumably already buffered internally.
|
||||
long? contentLength = content.Headers.ContentLength;
|
||||
if (contentLength.HasValue && contentLength.Value >= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// If the response is meant to use chunked transfer encoding, don't buffer.
|
||||
bool? transferEncodingChunked = response.Headers.TransferEncodingChunked;
|
||||
if (transferEncodingChunked.HasValue && transferEncodingChunked.Value)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Content length is null or -1 (meaning not known).
|
||||
// Buffer any HttpContent except StreamContent and PushStreamContent
|
||||
if (content is StreamContent || content is PushStreamContent)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
|
||||
|
||||
namespace System.Web.Http.Owin
|
||||
{
|
||||
/// <summary>
|
||||
/// Standard keys and values for use within the OWIN interfaces.
|
||||
/// </summary>
|
||||
internal static class OwinConstants
|
||||
{
|
||||
public const string CallCancelledKey = "owin.CallCancelled";
|
||||
|
||||
// Request keys
|
||||
public const string RequestMethodKey = "owin.RequestMethod";
|
||||
public const string RequestSchemeKey = "owin.RequestScheme";
|
||||
public const string RequestPathBaseKey = "owin.RequestPathBase";
|
||||
public const string RequestPathKey = "owin.RequestPath";
|
||||
public const string RequestQueryStringKey = "owin.RequestQueryString";
|
||||
public const string RequestHeadersKey = "owin.RequestHeaders";
|
||||
public const string RequestBodyKey = "owin.RequestBody";
|
||||
public const string ClientCertifiateKey = "ssl.ClientCertificate";
|
||||
public const string IsLocalKey = "server.IsLocal";
|
||||
public const string UserKey = "server.User";
|
||||
|
||||
// Response keys
|
||||
public const string ResponseStatusCodeKey = "owin.ResponseStatusCode";
|
||||
public const string ResponseReasonPhraseKey = "owin.ResponseReasonPhrase";
|
||||
public const string ResponseHeadersKey = "owin.ResponseHeaders";
|
||||
public const string ResponseBodyKey = "owin.ResponseBody";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics.Contracts;
|
||||
using System.Web.Http.Owin.Properties;
|
||||
|
||||
namespace System.Web.Http.Owin
|
||||
{
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
internal static class OwinEnvironmentExtensions
|
||||
{
|
||||
public static T GetOwinValue<T>(this IDictionary<string, object> environment, string key)
|
||||
{
|
||||
Contract.Assert(environment != null);
|
||||
Contract.Assert(key != null);
|
||||
|
||||
object value;
|
||||
if (environment.TryGetValue(key, out value))
|
||||
{
|
||||
if (value is T)
|
||||
{
|
||||
return (T)value;
|
||||
}
|
||||
throw Error.InvalidOperation(SRResources.GetOwinValue_IncorrectType, key, typeof(T).Name);
|
||||
}
|
||||
throw Error.InvalidOperation(SRResources.GetOwinValue_MissingRequiredValue, key);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Web.Http;
|
||||
|
||||
namespace System.Net.Http
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides extension methods for the <see cref="HttpRequestMessage"/> class.
|
||||
/// </summary>
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public static class OwinHttpRequestMessageExtensions
|
||||
{
|
||||
private const string OwinEnvironmentKey = "MS_OwinEnvironment";
|
||||
|
||||
/// <summary>
|
||||
/// Gets the OWIN environment for the specified request.
|
||||
/// </summary>
|
||||
/// <param name="request">The HTTP request.</param>
|
||||
/// <returns>The OWIN environment for the specified request.</returns>
|
||||
public static IDictionary<string, object> GetOwinEnvironment(this HttpRequestMessage request)
|
||||
{
|
||||
if (request == null)
|
||||
{
|
||||
throw Error.ArgumentNull("request");
|
||||
}
|
||||
|
||||
IDictionary<string, object> environment;
|
||||
request.Properties.TryGetValue<IDictionary<string, object>>(OwinEnvironmentKey, out environment);
|
||||
return environment;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the OWIN environment for the specified request.
|
||||
/// </summary>
|
||||
/// <param name="request">The HTTP request.</param>
|
||||
/// <param name="environment">The OWIN environment to set.</param>
|
||||
public static void SetOwinEnvironment(this HttpRequestMessage request, IDictionary<string, object> environment)
|
||||
{
|
||||
if (request == null)
|
||||
{
|
||||
throw Error.ArgumentNull("request");
|
||||
}
|
||||
if (environment == null)
|
||||
{
|
||||
throw Error.ArgumentNull("environment");
|
||||
}
|
||||
|
||||
request.Properties[OwinEnvironmentKey] = environment;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
|
||||
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
[assembly: AssemblyTitle("System.Web.Http.Owin")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: Guid("41f9da53-4a52-4cd7-af85-77a214c5b13b")]
|
||||
[assembly: InternalsVisibleTo("System.Web.Http.Owin.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")]
|
|
@ -0,0 +1,99 @@
|
|||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
// Runtime Version:4.0.30319.18010
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace System.Web.Http.Owin.Properties {
|
||||
using System;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// A strongly-typed resource class, for looking up localized strings, etc.
|
||||
/// </summary>
|
||||
// This class was auto-generated by the StronglyTypedResourceBuilder
|
||||
// class via a tool like ResGen or Visual Studio.
|
||||
// To add or remove a member, edit your .ResX file then rerun ResGen
|
||||
// with the /str option, or rebuild your VS project.
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
internal class SRResources {
|
||||
|
||||
private static global::System.Resources.ResourceManager resourceMan;
|
||||
|
||||
private static global::System.Globalization.CultureInfo resourceCulture;
|
||||
|
||||
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||
internal SRResources() {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the cached ResourceManager instance used by this class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Resources.ResourceManager ResourceManager {
|
||||
get {
|
||||
if (object.ReferenceEquals(resourceMan, null)) {
|
||||
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("System.Web.Http.Owin.Properties.SRResources", typeof(SRResources).Assembly);
|
||||
resourceMan = temp;
|
||||
}
|
||||
return resourceMan;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overrides the current thread's CurrentUICulture property for all
|
||||
/// resource lookups using this strongly typed resource class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Globalization.CultureInfo Culture {
|
||||
get {
|
||||
return resourceCulture;
|
||||
}
|
||||
set {
|
||||
resourceCulture = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to The OWIN environment does not contain a value for the required 'Host' header..
|
||||
/// </summary>
|
||||
internal static string CreateRequestURI_MissingHostHeader {
|
||||
get {
|
||||
return ResourceManager.GetString("CreateRequestURI_MissingHostHeader", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to The value for key '{0}' in the OWIN environment is not of the expected type '{1}'..
|
||||
/// </summary>
|
||||
internal static string GetOwinValue_IncorrectType {
|
||||
get {
|
||||
return ResourceManager.GetString("GetOwinValue_IncorrectType", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to The OWIN environment does not contain a value for the required key '{0}'..
|
||||
/// </summary>
|
||||
internal static string GetOwinValue_MissingRequiredValue {
|
||||
get {
|
||||
return ResourceManager.GetString("GetOwinValue_MissingRequiredValue", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to The message handler did not return a response message..
|
||||
/// </summary>
|
||||
internal static string SendAsync_ReturnedNull {
|
||||
get {
|
||||
return ResourceManager.GetString("SendAsync_ReturnedNull", resourceCulture);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,132 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="CreateRequestURI_MissingHostHeader" xml:space="preserve">
|
||||
<value>The OWIN environment does not contain a value for the required 'Host' header.</value>
|
||||
</data>
|
||||
<data name="GetOwinValue_IncorrectType" xml:space="preserve">
|
||||
<value>The value for key '{0}' in the OWIN environment is not of the expected type '{1}'.</value>
|
||||
</data>
|
||||
<data name="GetOwinValue_MissingRequiredValue" xml:space="preserve">
|
||||
<value>The OWIN environment does not contain a value for the required key '{0}'.</value>
|
||||
</data>
|
||||
<data name="SendAsync_ReturnedNull" xml:space="preserve">
|
||||
<value>The message handler did not return a response message.</value>
|
||||
</data>
|
||||
</root>
|
|
@ -0,0 +1,94 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory),Runtime.sln))\tools\WebStack.settings.targets" />
|
||||
<PropertyGroup>
|
||||
<ProjectGuid>{66DD7CD7-C68F-4D0E-9F3D-3B58C49D1467}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>System.Web.Http.Owin</RootNamespace>
|
||||
<AssemblyName>System.Web.Http.Owin</AssemblyName>
|
||||
<OutputPath>..\..\bin\$(Configuration)\</OutputPath>
|
||||
<DocumentationFile>$(OutputPath)$(AssemblyName).xml</DocumentationFile>
|
||||
<RunCodeAnalysis>$(CodeAnalysis)</RunCodeAnalysis>
|
||||
<CodeAnalysisRuleSet>..\Strict.ruleset</CodeAnalysisRuleSet>
|
||||
<DefineConstants>$(DefineConstants);ASPNETMVC</DefineConstants>
|
||||
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\</SolutionDir>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Owin">
|
||||
<HintPath>..\..\packages\Owin.1.0\lib\net40\Owin.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Net.Http" />
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="..\CommonAssemblyInfo.cs">
|
||||
<Link>Properties\CommonAssemblyInfo.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Common\DictionaryExtensions.cs">
|
||||
<Link>Common\DictionaryExtensions.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Common\Error.cs">
|
||||
<Link>Common\Error.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Common\TaskHelpers.cs">
|
||||
<Link>Common\TaskHelpers.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Common\TaskHelpersExtensions.cs">
|
||||
<Link>Common\TaskHelpersExtensions.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="OwinBufferPolicySelector.cs" />
|
||||
<Compile Include="OwinEnvironmentExtensions.cs" />
|
||||
<Compile Include="OwinHttpRequestMessageExtensions.cs" />
|
||||
<Compile Include="OwinConstants.cs" />
|
||||
<Compile Include="GlobalSuppressions.cs" />
|
||||
<Compile Include="HttpMessageHandlerAdapter.cs" />
|
||||
<Compile Include="WebApiAppBuilderExtensions.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Properties\SRResources.Designer.cs">
|
||||
<AutoGen>True</AutoGen>
|
||||
<DesignTime>True</DesignTime>
|
||||
<DependentUpon>SRResources.resx</DependentUpon>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="..\Common\CommonWebApiResources.Designer.cs">
|
||||
<Link>Properties\CommonWebApiResources.Designer.cs</Link>
|
||||
<AutoGen>True</AutoGen>
|
||||
<DesignTime>True</DesignTime>
|
||||
<DependentUpon>CommonWebApiResources.resx</DependentUpon>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="..\Common\CommonWebApiResources.resx">
|
||||
<Link>Properties\CommonWebApiResources.resx</Link>
|
||||
<Generator>ResXFileCodeGenerator</Generator>
|
||||
<LastGenOutput>CommonWebApiResources.Designer.cs</LastGenOutput>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Include="Properties\SRResources.resx">
|
||||
<Generator>ResXFileCodeGenerator</Generator>
|
||||
<LastGenOutput>SRResources.Designer.cs</LastGenOutput>
|
||||
</EmbeddedResource>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\System.Net.Http.Formatting\System.Net.Http.Formatting.csproj">
|
||||
<Project>{668e9021-ce84-49d9-98fb-df125a9fcdb0}</Project>
|
||||
<Name>System.Net.Http.Formatting</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\System.Web.Http\System.Web.Http.csproj">
|
||||
<Project>{ddc1ce0c-486e-4e35-bb3b-eab61f8f9440}</Project>
|
||||
<Name>System.Web.Http</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<CodeAnalysisDictionary Include="..\CodeAnalysisDictionary.xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config">
|
||||
<SubType>Designer</SubType>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<Import Project="$(SolutionDir)\.nuget\nuget.targets" />
|
||||
</Project>
|
|
@ -0,0 +1,58 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
|
||||
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Net.Http;
|
||||
using System.Web.Http;
|
||||
using System.Web.Http.Hosting;
|
||||
using System.Web.Http.Owin;
|
||||
|
||||
namespace Owin
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides extension methods for the <see cref="IAppBuilder"/> class.
|
||||
/// </summary>
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public static class WebApiAppBuilderExtensions
|
||||
{
|
||||
private static readonly IHostBufferPolicySelector _defaultBufferPolicySelector = new OwinBufferPolicySelector();
|
||||
|
||||
/// <summary>
|
||||
/// Adds a component to the OWIN pipeline for running a Web API endpoint.
|
||||
/// </summary>
|
||||
/// <param name="builder">The application builder.</param>
|
||||
/// <param name="configuration">The <see cref="HttpConfiguration"/> used to configure the endpoint.</param>
|
||||
/// <returns>The application builder.</returns>
|
||||
[SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "Not out of scope")]
|
||||
public static IAppBuilder UseWebApi(this IAppBuilder builder, HttpConfiguration configuration)
|
||||
{
|
||||
IHostBufferPolicySelector bufferPolicySelector = configuration.Services.GetHostBufferPolicySelector() ?? _defaultBufferPolicySelector;
|
||||
return builder.Use(typeof(HttpMessageHandlerAdapter), new HttpServer(configuration), bufferPolicySelector);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a component to the OWIN pipeline for running a Web API endpoint.
|
||||
/// </summary>
|
||||
/// <param name="builder">The application builder.</param>
|
||||
/// <param name="configuration">The <see cref="HttpConfiguration"/> used to configure the endpoint.</param>
|
||||
/// <param name="dispatcher">The dispatcher responsible for handling incoming requests.</param>
|
||||
/// <returns>The application builder.</returns>
|
||||
[SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "Not out of scope")]
|
||||
public static IAppBuilder UseWebApi(this IAppBuilder builder, HttpConfiguration configuration, HttpMessageHandler dispatcher)
|
||||
{
|
||||
IHostBufferPolicySelector bufferPolicySelector = configuration.Services.GetHostBufferPolicySelector() ?? _defaultBufferPolicySelector;
|
||||
return builder.Use(typeof(HttpMessageHandlerAdapter), new HttpServer(configuration, dispatcher), bufferPolicySelector);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a component to the OWIN pipeline for running an <see cref="HttpMessageHandler"/>.
|
||||
/// </summary>
|
||||
/// <param name="builder">The application builder.</param>
|
||||
/// <param name="messageHandler">The message handler.</param>
|
||||
/// <returns>The application builder.</returns>
|
||||
public static IAppBuilder UseHttpMessageHandler(this IAppBuilder builder, HttpMessageHandler messageHandler)
|
||||
{
|
||||
return builder.Use(typeof(HttpMessageHandlerAdapter), messageHandler, _defaultBufferPolicySelector);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Owin" version="1.0" targetFramework="net45" />
|
||||
</packages>
|
|
@ -71,6 +71,7 @@ namespace System.Web.Http.Dispatcher
|
|||
}
|
||||
else
|
||||
{
|
||||
request.Properties.Add(HttpPropertyKeys.NoRouteMatched, true);
|
||||
return TaskHelpers.FromResult(request.CreateErrorResponse(
|
||||
HttpStatusCode.NotFound,
|
||||
Error.Format(SRResources.ResourceNotFound, request.RequestUri),
|
||||
|
|
|
@ -68,6 +68,16 @@ namespace System.Web.Http.Hosting
|
|||
/// </summary>
|
||||
public static readonly string IsLocalKey = "MS_IsLocal";
|
||||
|
||||
/// <summary>
|
||||
/// Provides a key that indicates whether the request failed to match a route.
|
||||
/// </summary>
|
||||
public static readonly string NoRouteMatched = "MS_NoRouteMatched";
|
||||
|
||||
/// <summary>
|
||||
/// Provides a key for the root virtual path for a request.
|
||||
/// </summary>
|
||||
public static readonly string VirtualPathRoot = "MS_VirtualPathRoot";
|
||||
|
||||
/// <summary>
|
||||
/// Provides a key that indicates whether error details are to be included in the response for this HTTP request.
|
||||
/// </summary>
|
||||
|
|
|
@ -744,6 +744,41 @@ namespace System.Net.Http
|
|||
|
||||
request.Properties[HttpPropertyKeys.UrlHelperKey] = urlHelper;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the root virtual path associated with this request.
|
||||
/// </summary>
|
||||
/// <param name="request">The <see cref="HttpRequestMessage"/>.</param>
|
||||
/// <returns>The root virtual path associated with this request.</returns>
|
||||
public static string GetVirtualPathRoot(this HttpRequestMessage request)
|
||||
{
|
||||
if (request == null)
|
||||
{
|
||||
throw Error.ArgumentNull("request");
|
||||
}
|
||||
|
||||
return request.GetProperty<string>(HttpPropertyKeys.VirtualPathRoot);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the root virtual path associated with this request.
|
||||
/// </summary>
|
||||
/// <param name="request">The <see cref="HttpRequestMessage"/>.</param>
|
||||
/// <param name="virtualPathRoot">The virtual path root to associate with this request.</param>
|
||||
public static void SetVirtualPathRoot(this HttpRequestMessage request, string virtualPathRoot)
|
||||
{
|
||||
if (request == null)
|
||||
{
|
||||
throw Error.ArgumentNull("request");
|
||||
}
|
||||
|
||||
if (virtualPathRoot == null)
|
||||
{
|
||||
throw Error.ArgumentNull("virtualPathRoot");
|
||||
}
|
||||
|
||||
request.Properties[HttpPropertyKeys.VirtualPathRoot] = virtualPathRoot;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the request originates from a local address or not.
|
||||
|
|
|
@ -77,7 +77,8 @@ namespace System.Web.Http
|
|||
|
||||
for (int i = 0; i < _collection.Count; i++)
|
||||
{
|
||||
IHttpRouteData routeData = _collection[i].GetRouteData(_virtualPathRoot, request);
|
||||
string virtualPathRoot = GetVirtualPathRoot(request);
|
||||
IHttpRouteData routeData = _collection[i].GetRouteData(virtualPathRoot, request);
|
||||
if (routeData != null)
|
||||
{
|
||||
return routeData;
|
||||
|
@ -111,8 +112,7 @@ namespace System.Web.Http
|
|||
}
|
||||
|
||||
// Construct a new VirtualPathData with the resolved app path
|
||||
|
||||
string virtualPathRoot = _virtualPathRoot;
|
||||
string virtualPathRoot = GetVirtualPathRoot(request);
|
||||
if (!virtualPathRoot.EndsWith("/", StringComparison.Ordinal))
|
||||
{
|
||||
virtualPathRoot += "/";
|
||||
|
@ -123,6 +123,13 @@ namespace System.Web.Http
|
|||
return new HttpVirtualPathData(virtualPath.Route, virtualPathRoot + virtualPath.VirtualPath);
|
||||
}
|
||||
|
||||
// Returns the virtual path root on the request if one is specified
|
||||
// Otherwise, fall back on the virtual path root for the route collection
|
||||
private string GetVirtualPathRoot(HttpRequestMessage request)
|
||||
{
|
||||
return request.GetVirtualPathRoot() ?? _virtualPathRoot;
|
||||
}
|
||||
|
||||
public IHttpRoute CreateRoute(string routeTemplate, object defaults, object constraints)
|
||||
{
|
||||
IDictionary<string, object> dataTokens = new Dictionary<string, object>();
|
||||
|
|
|
@ -0,0 +1,447 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Formatting;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Security.Principal;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Web.Http.Hosting;
|
||||
using Microsoft.TestCommon;
|
||||
using Moq;
|
||||
|
||||
namespace System.Web.Http.Owin
|
||||
{
|
||||
public class HttpMessageHandlerAdapterTest
|
||||
{
|
||||
[Fact]
|
||||
public void Invoke_BuildsAppropriateRequestMessage()
|
||||
{
|
||||
var response = new HttpResponseMessage(HttpStatusCode.OK);
|
||||
var handler = new MockHandler() { Response = response };
|
||||
var bufferPolicySelector = CreateBufferPolicySelector(bufferInput: false, bufferOutput: false);
|
||||
var environment = CreateEnvironment("GET", "http", "localhost", "/vroot", "/api/customers");
|
||||
|
||||
new HttpMessageHandlerAdapter(env => TaskHelpers.Completed(), handler, bufferPolicySelector).Invoke(environment).Wait();
|
||||
|
||||
var request = handler.Request;
|
||||
Assert.Equal(HttpMethod.Get, request.Method);
|
||||
Assert.Equal("http://localhost/vroot/api/customers", request.RequestUri.AbsoluteUri);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Invoke_BuildsUriWithQueryStringIfPresent()
|
||||
{
|
||||
var response = new HttpResponseMessage(HttpStatusCode.OK);
|
||||
var handler = new MockHandler() { Response = response };
|
||||
var bufferPolicySelector = CreateBufferPolicySelector(bufferInput: false, bufferOutput: false);
|
||||
var environment = CreateEnvironment("GET", "http", "localhost", "/vroot", "/api/customers");
|
||||
environment["owin.RequestQueryString"] ="id=45";
|
||||
|
||||
new HttpMessageHandlerAdapter(env => TaskHelpers.Completed(), handler, bufferPolicySelector).Invoke(environment).Wait();
|
||||
|
||||
var request = handler.Request;
|
||||
Assert.Equal(HttpMethod.Get, request.Method);
|
||||
Assert.Equal("http://localhost/vroot/api/customers?id=45", request.RequestUri.AbsoluteUri);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Invoke_BuildsUriWithHostAndPort()
|
||||
{
|
||||
var response = new HttpResponseMessage(HttpStatusCode.OK);
|
||||
var handler = new MockHandler() { Response = response };
|
||||
var bufferPolicySelector = CreateBufferPolicySelector(bufferInput: false, bufferOutput: false);
|
||||
var environment = CreateEnvironment("GET", "http", "localhost:12345", "/vroot", "/api/customers");
|
||||
|
||||
new HttpMessageHandlerAdapter(env => TaskHelpers.Completed(), handler, bufferPolicySelector).Invoke(environment).Wait();
|
||||
|
||||
var request = handler.Request;
|
||||
Assert.Equal(HttpMethod.Get, request.Method);
|
||||
Assert.Equal("http://localhost:12345/vroot/api/customers", request.RequestUri.AbsoluteUri);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Invoke_Throws_IfHostHeaderMissing()
|
||||
{
|
||||
var response = new HttpResponseMessage(HttpStatusCode.OK);
|
||||
var handler = new MockHandler() { Response = response };
|
||||
var bufferPolicySelector = CreateBufferPolicySelector(bufferInput: false, bufferOutput: false);
|
||||
var environment = CreateEnvironment("GET", "http", "localhost", "/vroot", "/api/customers");
|
||||
var requestHeaders = environment["owin.RequestHeaders"] as IDictionary<string, string[]>;
|
||||
requestHeaders.Remove("Host");
|
||||
|
||||
Assert.Throws<InvalidOperationException>(
|
||||
() => new HttpMessageHandlerAdapter(env => TaskHelpers.Completed(), handler, bufferPolicySelector).Invoke(environment).Wait(),
|
||||
"The OWIN environment does not contain a value for the required 'Host' header.");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Invoke_Throws_IfHostHeaderHasNoValues()
|
||||
{
|
||||
var response = new HttpResponseMessage(HttpStatusCode.OK);
|
||||
var handler = new MockHandler() { Response = response };
|
||||
var bufferPolicySelector = CreateBufferPolicySelector(bufferInput: false, bufferOutput: false);
|
||||
var environment = CreateEnvironment("GET", "http", "localhost", "/vroot", "/api/customers");
|
||||
var requestHeaders = environment["owin.RequestHeaders"] as IDictionary<string, string[]>;
|
||||
requestHeaders["Host"] = new string[0];
|
||||
|
||||
Assert.Throws<InvalidOperationException>(
|
||||
() => new HttpMessageHandlerAdapter(env => TaskHelpers.Completed(), handler, bufferPolicySelector).Invoke(environment).Wait(),
|
||||
"The OWIN environment does not contain a value for the required 'Host' header.");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Invoke_AddsRequestHeadersToRequestMessage()
|
||||
{
|
||||
var response = new HttpResponseMessage(HttpStatusCode.OK);
|
||||
var handler = new MockHandler() { Response = response };
|
||||
var bufferPolicySelector = CreateBufferPolicySelector(bufferInput: false, bufferOutput: false);
|
||||
var environment = CreateEnvironment("GET", "http", "localhost", "/vroot", "/api/customers");
|
||||
var requestHeaders = environment["owin.RequestHeaders"] as IDictionary<string, string[]>;
|
||||
requestHeaders["Accept"] = new string[] { "application/json", "application/xml" };
|
||||
requestHeaders["Content-Length"] = new string[] { "45" };
|
||||
|
||||
new HttpMessageHandlerAdapter(env => TaskHelpers.Completed(), handler, bufferPolicySelector).Invoke(environment).Wait();
|
||||
|
||||
var request = handler.Request;
|
||||
Assert.Equal(2, request.Headers.Count());
|
||||
Assert.Equal(new string[] { "application/json", "application/xml" }, request.Headers.Accept.Select(mediaType => mediaType.ToString()).ToArray());
|
||||
Assert.Equal("localhost", request.Headers.Host);
|
||||
Assert.Single(request.Content.Headers);
|
||||
Assert.Equal(45, request.Content.Headers.ContentLength);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Invoke_SetsRequestBodyOnRequestMessage()
|
||||
{
|
||||
var response = new HttpResponseMessage(HttpStatusCode.OK);
|
||||
var handler = new MockHandler() { Response = response };
|
||||
var bufferPolicySelector = CreateBufferPolicySelector(bufferInput: false, bufferOutput: false);
|
||||
var environment = CreateEnvironment("GET", "http", "localhost", "/vroot", "/api/customers");
|
||||
environment["owin.RequestBody"] = new MemoryStream(Encoding.UTF8.GetBytes("This is the request body."));
|
||||
|
||||
new HttpMessageHandlerAdapter(env => TaskHelpers.Completed(), handler, bufferPolicySelector).Invoke(environment).Wait();
|
||||
|
||||
var request = handler.Request;
|
||||
Assert.Equal("This is the request body.", request.Content.ReadAsStringAsync().Result);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(false)]
|
||||
[InlineData(true)]
|
||||
public void Invoke_RespectsInputBufferingSetting(bool bufferInput)
|
||||
{
|
||||
var response = new HttpResponseMessage(HttpStatusCode.OK);
|
||||
var handler = new MockHandler() { Response = response };
|
||||
var bufferPolicySelector = CreateBufferPolicySelector(bufferInput: bufferInput, bufferOutput: false);
|
||||
var environment = CreateEnvironment("GET", "http", "localhost", "/vroot", "/api/customers");
|
||||
var requestBodyMock = new Mock<MemoryStream>(Encoding.UTF8.GetBytes("This is the request body."));
|
||||
requestBodyMock.CallBase = true;
|
||||
requestBodyMock.Setup(s => s.CanSeek).Returns(false);
|
||||
MemoryStream requestBody = requestBodyMock.Object;
|
||||
environment["owin.RequestBody"] = requestBody;
|
||||
|
||||
new HttpMessageHandlerAdapter(env => TaskHelpers.Completed(), handler, bufferPolicySelector).Invoke(environment).Wait();
|
||||
|
||||
if (bufferInput)
|
||||
{
|
||||
Assert.False(requestBody.CanRead);
|
||||
// Assert that the OWIN environment still has a request body that can be read
|
||||
var owinRequestBody = environment["owin.RequestBody"] as Stream;
|
||||
byte[] bodyBytes = new byte[25];
|
||||
int charsRead = owinRequestBody.Read(bodyBytes, 0, 25);
|
||||
Assert.Equal("This is the request body.", Encoding.UTF8.GetString(bodyBytes));
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.True(requestBody.CanRead);
|
||||
}
|
||||
// Assert that Web API gets the right body
|
||||
var request = handler.Request;
|
||||
Assert.Equal("This is the request body.", request.Content.ReadAsStringAsync().Result);
|
||||
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Invoke_SetsOwinEnvironment()
|
||||
{
|
||||
var response = new HttpResponseMessage(HttpStatusCode.OK);
|
||||
var handler = new MockHandler() { Response = response };
|
||||
var bufferPolicySelector = CreateBufferPolicySelector(bufferInput: false, bufferOutput: false);
|
||||
var environment = CreateEnvironment("GET", "http", "localhost", "/vroot", "/api/customers");
|
||||
|
||||
new HttpMessageHandlerAdapter(env => TaskHelpers.Completed(), handler, bufferPolicySelector).Invoke(environment).Wait();
|
||||
|
||||
var request = handler.Request;
|
||||
Assert.Same(environment, request.GetOwinEnvironment());
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(null, false)]
|
||||
[InlineData(false, false)]
|
||||
[InlineData(true, true)]
|
||||
public void Invoke_SetsRequestIsLocalProperty(bool? isLocal, bool expectedRequestLocal)
|
||||
{
|
||||
var response = new HttpResponseMessage(HttpStatusCode.OK);
|
||||
var handler = new MockHandler() { Response = response };
|
||||
var bufferPolicySelector = CreateBufferPolicySelector(bufferInput: false, bufferOutput: false);
|
||||
var environment = CreateEnvironment("GET", "http", "localhost", "/vroot", "/api/customers");
|
||||
if (isLocal.HasValue)
|
||||
{
|
||||
environment["server.IsLocal"] = isLocal.Value;
|
||||
}
|
||||
|
||||
new HttpMessageHandlerAdapter(env => TaskHelpers.Completed(), handler, bufferPolicySelector).Invoke(environment).Wait();
|
||||
|
||||
var request = handler.Request;
|
||||
Assert.Equal(expectedRequestLocal, request.IsLocal());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Invoke_SetsClientCertificate()
|
||||
{
|
||||
var response = new HttpResponseMessage(HttpStatusCode.OK);
|
||||
var handler = new MockHandler() { Response = response };
|
||||
var bufferPolicySelector = CreateBufferPolicySelector(bufferInput: false, bufferOutput: false);
|
||||
var environment = CreateEnvironment("GET", "http", "localhost", "/vroot", "/api/customers");
|
||||
var clientCert = new Mock<X509Certificate2>().Object;
|
||||
environment["ssl.ClientCertificate"] = clientCert;
|
||||
|
||||
new HttpMessageHandlerAdapter(env => TaskHelpers.Completed(), handler, bufferPolicySelector).Invoke(environment).Wait();
|
||||
|
||||
var request = handler.Request;
|
||||
Assert.Equal(clientCert, request.GetClientCertificate());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Invoke_CallsMessageHandler_WithEnvironmentCancellationToken()
|
||||
{
|
||||
var response = new HttpResponseMessage(HttpStatusCode.OK);
|
||||
var handler = new MockHandler() { Response = response };
|
||||
var bufferPolicySelector = CreateBufferPolicySelector(bufferInput: false, bufferOutput: false);
|
||||
var environment = CreateEnvironment("GET", "http", "localhost", "/vroot", "/api/customers");
|
||||
var cancellationToken = new CancellationToken();
|
||||
environment["owin.CallCancelled"] = cancellationToken;
|
||||
|
||||
new HttpMessageHandlerAdapter(env => TaskHelpers.Completed(), handler, bufferPolicySelector).Invoke(environment).Wait();
|
||||
|
||||
Assert.Equal(cancellationToken, handler.CancellationToken);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Invoke_CallsMessageHandler_WithEnvironmentUser()
|
||||
{
|
||||
var response = new HttpResponseMessage(HttpStatusCode.OK);
|
||||
var handler = new MockHandler() { Response = response };
|
||||
var bufferPolicySelector = CreateBufferPolicySelector(bufferInput: false, bufferOutput: false);
|
||||
var environment = CreateEnvironment("GET", "http", "localhost", "/vroot", "/api/customers");
|
||||
var user = new Mock<IPrincipal>().Object;
|
||||
environment["server.User"] = user;
|
||||
|
||||
new HttpMessageHandlerAdapter(env => TaskHelpers.Completed(), handler, bufferPolicySelector).Invoke(environment).Wait();
|
||||
|
||||
Assert.Equal(user, handler.User);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Invoke_Throws_IfMessageHandlerReturnsNull()
|
||||
{
|
||||
HttpResponseMessage response = null;
|
||||
var handler = new MockHandler() { Response = response };
|
||||
var bufferPolicySelector = CreateBufferPolicySelector(bufferInput: false, bufferOutput: false);
|
||||
var environment = CreateEnvironment("GET", "http", "localhost", "/vroot", "/api/customers");
|
||||
|
||||
Assert.Throws<InvalidOperationException>(
|
||||
() => new HttpMessageHandlerAdapter(env => TaskHelpers.Completed(), handler, bufferPolicySelector).Invoke(environment).Wait(),
|
||||
"The message handler did not return a response message.");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Invoke_DoesNotCallNext_IfMessageHandlerDoesNotReturn404()
|
||||
{
|
||||
bool nextCalled = false;
|
||||
var next = new Func<IDictionary<string, object>, Task>(env =>
|
||||
{
|
||||
nextCalled = true;
|
||||
return TaskHelpers.Completed();
|
||||
});
|
||||
HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.OK);
|
||||
var handler = new MockHandler() { Response = response, AddNoRouteMatchedKey = true };
|
||||
var bufferPolicySelector = CreateBufferPolicySelector(bufferInput: false, bufferOutput: false);
|
||||
var environment = CreateEnvironment("GET", "http", "localhost", "/vroot", "/api/customers");
|
||||
|
||||
new HttpMessageHandlerAdapter(next, handler, bufferPolicySelector).Invoke(environment).Wait();
|
||||
|
||||
Assert.False(nextCalled);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Invoke_DoesNotCallNext_IfMessageHandlerDoesNotAddNoRouteMatchedProperty()
|
||||
{
|
||||
bool nextCalled = false;
|
||||
var next = new Func<IDictionary<string, object>, Task>(env =>
|
||||
{
|
||||
nextCalled = true;
|
||||
return TaskHelpers.Completed();
|
||||
});
|
||||
HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.NotFound);
|
||||
var handler = new MockHandler() { Response = response, AddNoRouteMatchedKey = false };
|
||||
var bufferPolicySelector = CreateBufferPolicySelector(bufferInput: false, bufferOutput: false);
|
||||
var environment = CreateEnvironment("GET", "http", "localhost", "/vroot", "/api/customers");
|
||||
|
||||
new HttpMessageHandlerAdapter(next, handler, bufferPolicySelector).Invoke(environment).Wait();
|
||||
|
||||
Assert.False(nextCalled);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Invoke_CallsNext_IfMessageHandlerReturns404WithNoRouteMatched()
|
||||
{
|
||||
bool nextCalled = false;
|
||||
var next = new Func<IDictionary<string, object>, Task>(env =>
|
||||
{
|
||||
nextCalled = true;
|
||||
return TaskHelpers.Completed();
|
||||
});
|
||||
HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.NotFound);
|
||||
var handler = new MockHandler() { Response = response, AddNoRouteMatchedKey = true };
|
||||
var bufferPolicySelector = CreateBufferPolicySelector(bufferInput: false, bufferOutput: false);
|
||||
var environment = CreateEnvironment("GET", "http", "localhost", "/vroot", "/api/customers");
|
||||
|
||||
new HttpMessageHandlerAdapter(next, handler, bufferPolicySelector).Invoke(environment).Wait();
|
||||
|
||||
Assert.True(nextCalled);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Invoke_SetsResponseStatusCodeAndReasonPhrase()
|
||||
{
|
||||
var response = new HttpResponseMessage(HttpStatusCode.ServiceUnavailable) { ReasonPhrase = "OH NO!" };
|
||||
var handler = new MockHandler() { Response = response };
|
||||
var bufferPolicySelector = CreateBufferPolicySelector(bufferInput: false, bufferOutput: false);
|
||||
var environment = CreateEnvironment("GET", "http", "localhost", "/vroot", "/api/customers");
|
||||
|
||||
new HttpMessageHandlerAdapter(env => TaskHelpers.Completed(), handler, bufferPolicySelector).Invoke(environment).Wait();
|
||||
|
||||
Assert.Equal(HttpStatusCode.ServiceUnavailable, environment["owin.ResponseStatusCode"]);
|
||||
Assert.Equal("OH NO!", environment["owin.ResponseReasonPhrase"]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Invoke_SetsResponseHeaders()
|
||||
{
|
||||
var response = new HttpResponseMessage(HttpStatusCode.OK);
|
||||
response.Headers.Location = new Uri("http://www.location.com/");
|
||||
response.Content = new StringContent(@"{""x"":""y""}", Encoding.UTF8, "application/json");
|
||||
var handler = new MockHandler() { Response = response };
|
||||
var bufferPolicySelector = CreateBufferPolicySelector(bufferInput: false, bufferOutput: false);
|
||||
var environment = CreateEnvironment("GET", "http", "localhost", "/vroot", "/api/customers");
|
||||
|
||||
new HttpMessageHandlerAdapter(env => TaskHelpers.Completed(), handler, bufferPolicySelector).Invoke(environment).Wait();
|
||||
|
||||
var responseHeaders = environment["owin.ResponseHeaders"] as IDictionary<string, string[]>;
|
||||
Assert.Equal(3, responseHeaders.Count);
|
||||
Assert.Equal("http://www.location.com/", Assert.Single(responseHeaders["Location"]));
|
||||
Assert.Equal("9", Assert.Single(responseHeaders["Content-Length"]));
|
||||
Assert.Equal("application/json; charset=utf-8", Assert.Single(responseHeaders["Content-Type"]));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Invoke_SetsResponseBody()
|
||||
{
|
||||
var response = new HttpResponseMessage(HttpStatusCode.OK);
|
||||
response.Content = new StringContent(@"{""x"":""y""}", Encoding.UTF8, "application/json");
|
||||
var handler = new MockHandler() { Response = response };
|
||||
var bufferPolicySelector = CreateBufferPolicySelector(bufferInput: false, bufferOutput: false);
|
||||
var environment = CreateEnvironment("GET", "http", "localhost", "/vroot", "/api/customers");
|
||||
var responseStream = new MemoryStream();
|
||||
environment["owin.ResponseBody"] = responseStream;
|
||||
|
||||
new HttpMessageHandlerAdapter(env => TaskHelpers.Completed(), handler, bufferPolicySelector).Invoke(environment).Wait();
|
||||
|
||||
responseStream.Seek(0, SeekOrigin.Begin);
|
||||
byte[] bodyBytes = new byte[9];
|
||||
int charsRead = responseStream.Read(bodyBytes, 0, 9);
|
||||
// Assert that we can read 9 characters and no more characters after that
|
||||
Assert.Equal(9, charsRead);
|
||||
Assert.Equal(-1, responseStream.ReadByte());
|
||||
Assert.Equal(@"{""x"":""y""}", Encoding.UTF8.GetString(bodyBytes));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(false)]
|
||||
[InlineData(true)]
|
||||
public void Invoke_RespectsOutputBufferingSetting(bool bufferOutput)
|
||||
{
|
||||
var response = new HttpResponseMessage(HttpStatusCode.OK);
|
||||
response.Content = new ObjectContent<string>("blue", new JsonMediaTypeFormatter());
|
||||
var handler = new MockHandler() { Response = response };
|
||||
var bufferPolicySelector = CreateBufferPolicySelector(bufferInput: false, bufferOutput: bufferOutput);
|
||||
var environment = CreateEnvironment("GET", "http", "localhost", "/vroot", "/api/customers");
|
||||
|
||||
new HttpMessageHandlerAdapter(env => TaskHelpers.Completed(), handler, bufferPolicySelector).Invoke(environment).Wait();
|
||||
|
||||
var responseHeaders = environment["owin.ResponseHeaders"] as IDictionary<string, string[]>;
|
||||
if (bufferOutput)
|
||||
{
|
||||
Assert.True(responseHeaders.ContainsKey("Content-Length"));
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.False(responseHeaders.ContainsKey("Content-Length"));
|
||||
}
|
||||
}
|
||||
|
||||
private static Dictionary<string, object> CreateEnvironment(string method, string scheme, string hostHeaderValue, string pathBase, string path)
|
||||
{
|
||||
var environment = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
|
||||
environment["owin.RequestMethod"] = method;
|
||||
environment["owin.RequestScheme"] = scheme;
|
||||
environment["owin.RequestHeaders"] = new Dictionary<string, string[]>(StringComparer.OrdinalIgnoreCase) { { "Host", new string[] { hostHeaderValue } } };
|
||||
environment["owin.RequestPathBase"] = pathBase;
|
||||
environment["owin.RequestPath"] = path;
|
||||
environment["owin.RequestQueryString"] = "";
|
||||
environment["owin.RequestBody"] = new MemoryStream();
|
||||
environment["owin.CallCancelled"] = new CancellationToken();
|
||||
environment["owin.ResponseHeaders"] = new Dictionary<string, string[]>(StringComparer.OrdinalIgnoreCase);
|
||||
environment["owin.ResponseBody"] = new MemoryStream();
|
||||
return environment;
|
||||
}
|
||||
|
||||
private static IHostBufferPolicySelector CreateBufferPolicySelector(bool bufferInput, bool bufferOutput)
|
||||
{
|
||||
var bufferPolicySelector = new Mock<IHostBufferPolicySelector>();
|
||||
bufferPolicySelector.Setup(bps => bps.UseBufferedInputStream(It.IsAny<object>())).Returns(bufferInput);
|
||||
bufferPolicySelector.Setup(bps => bps.UseBufferedOutputStream(It.IsAny<HttpResponseMessage>())).Returns(bufferOutput);
|
||||
return bufferPolicySelector.Object;
|
||||
}
|
||||
}
|
||||
|
||||
public class MockHandler : HttpMessageHandler
|
||||
{
|
||||
public HttpRequestMessage Request { get; private set; }
|
||||
public CancellationToken CancellationToken { get; private set; }
|
||||
public HttpResponseMessage Response { get; set; }
|
||||
public IPrincipal User { get; set; }
|
||||
public bool AddNoRouteMatchedKey { get; set; }
|
||||
|
||||
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
|
||||
{
|
||||
Request = request;
|
||||
CancellationToken = cancellationToken;
|
||||
User = Thread.CurrentPrincipal;
|
||||
|
||||
if (AddNoRouteMatchedKey)
|
||||
{
|
||||
request.Properties["MS_NoRouteMatched"] = true;
|
||||
}
|
||||
|
||||
return TaskHelpers.FromResult<HttpResponseMessage>(Response);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,104 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
|
||||
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using Microsoft.Owin.Hosting;
|
||||
using Microsoft.TestCommon;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Owin;
|
||||
|
||||
namespace System.Web.Http.Owin
|
||||
{
|
||||
public class IntegrationTest
|
||||
{
|
||||
[Fact]
|
||||
public void SimpleGet_Works()
|
||||
{
|
||||
using (WebApplication.Start<IntegrationTest>(url: "http://localhost:12345/vroot"))
|
||||
{
|
||||
HttpClient client = new HttpClient();
|
||||
|
||||
var response = client.GetAsync("http://localhost:12345/vroot/HelloWorld").Result;
|
||||
|
||||
Assert.True(response.IsSuccessStatusCode);
|
||||
Assert.Equal("\"Hello from OWIN\"", response.Content.ReadAsStringAsync().Result);
|
||||
Assert.Null(response.Headers.TransferEncodingChunked);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SimplePost_Works()
|
||||
{
|
||||
using (WebApplication.Start<IntegrationTest>(url: "http://localhost:12345/vroot"))
|
||||
{
|
||||
HttpClient client = new HttpClient();
|
||||
var content = new StringContent("\"Echo this\"", Encoding.UTF8, "application/json");
|
||||
|
||||
var response = client.PostAsync("http://localhost:12345/vroot/Echo", content).Result;
|
||||
|
||||
Assert.True(response.IsSuccessStatusCode);
|
||||
Assert.Equal("\"Echo this\"", response.Content.ReadAsStringAsync().Result);
|
||||
Assert.Null(response.Headers.TransferEncodingChunked);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetThatThrowsDuringSerializations_RespondsWith500()
|
||||
{
|
||||
using (WebApplication.Start<IntegrationTest>(url: "http://localhost:12345/vroot"))
|
||||
{
|
||||
HttpClient client = new HttpClient();
|
||||
|
||||
var response = client.GetAsync("http://localhost:12345/vroot/Error").Result;
|
||||
|
||||
Assert.Equal(HttpStatusCode.InternalServerError, response.StatusCode);
|
||||
JObject json = Assert.IsType<JObject>(JToken.Parse(response.Content.ReadAsStringAsync().Result));
|
||||
JToken exceptionMessage;
|
||||
Assert.True(json.TryGetValue("ExceptionMessage", out exceptionMessage));
|
||||
}
|
||||
}
|
||||
|
||||
public void Configuration(IAppBuilder appBuilder)
|
||||
{
|
||||
var config = new HttpConfiguration();
|
||||
config.Routes.MapHttpRoute("Default", "{controller}");
|
||||
appBuilder.UseWebApi(config);
|
||||
}
|
||||
}
|
||||
|
||||
public class HelloWorldController : ApiController
|
||||
{
|
||||
public string Get()
|
||||
{
|
||||
return "Hello from OWIN";
|
||||
}
|
||||
}
|
||||
|
||||
public class EchoController : ApiController
|
||||
{
|
||||
public string Post([FromBody] string s)
|
||||
{
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
public class ErrorController : ApiController
|
||||
{
|
||||
public ExceptionThrower Get()
|
||||
{
|
||||
return new ExceptionThrower();
|
||||
}
|
||||
|
||||
public class ExceptionThrower
|
||||
{
|
||||
public string Throws
|
||||
{
|
||||
get
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
|
||||
|
||||
using System.IO;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Formatting;
|
||||
using System.Threading;
|
||||
using Microsoft.TestCommon;
|
||||
|
||||
namespace System.Web.Http.Owin
|
||||
{
|
||||
public class OwinBufferPolicySelectorTest
|
||||
{
|
||||
[Fact]
|
||||
public void UseBufferedInputStream_ReturnsFalse()
|
||||
{
|
||||
Assert.False(new OwinBufferPolicySelector().UseBufferedInputStream(null));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void UseBufferedOutputStream_ReturnsTrue_ForObjectContent()
|
||||
{
|
||||
HttpResponseMessage response = new HttpResponseMessage();
|
||||
response.Content = new ObjectContent<string>("blue", new JsonMediaTypeFormatter());
|
||||
|
||||
Assert.True(new OwinBufferPolicySelector().UseBufferedOutputStream(response));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void UseBufferedOutputStream_ReturnsFalse_ForSpecifiedContentLength()
|
||||
{
|
||||
HttpResponseMessage response = new HttpResponseMessage();
|
||||
response.Content = new ObjectContent<string>("blue", new JsonMediaTypeFormatter());
|
||||
response.Content.Headers.ContentLength = 5;
|
||||
|
||||
Assert.False(new OwinBufferPolicySelector().UseBufferedOutputStream(response));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void UseBufferedOutputStream_ReturnsFalse_ForChunkedTransferEncoding()
|
||||
{
|
||||
HttpResponseMessage response = new HttpResponseMessage();
|
||||
response.Headers.TransferEncodingChunked = true;
|
||||
response.Content = new ObjectContent<string>("blue", new JsonMediaTypeFormatter());
|
||||
|
||||
Assert.False(new OwinBufferPolicySelector().UseBufferedOutputStream(response));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void UseBufferedOutputStream_ReturnsFalse_ForStreamContent()
|
||||
{
|
||||
HttpResponseMessage response = new HttpResponseMessage();
|
||||
response.Content = new StreamContent(new MemoryStream());
|
||||
|
||||
Assert.False(new OwinBufferPolicySelector().UseBufferedOutputStream(response));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void UseBufferedOutputStream_ReturnsFalse_ForPushStreamContent()
|
||||
{
|
||||
HttpResponseMessage response = new HttpResponseMessage();
|
||||
response.Content = new PushStreamContent((s, c, tc) => Thread.Sleep(0));
|
||||
|
||||
Assert.False(new OwinBufferPolicySelector().UseBufferedOutputStream(response));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.TestCommon;
|
||||
|
||||
namespace System.Web.Http.Owin
|
||||
{
|
||||
public class OwinEnvironmentExtensionsTest
|
||||
{
|
||||
[Fact]
|
||||
public void GetOwinValue_GetsValues()
|
||||
{
|
||||
var env = new Dictionary<string, object>();
|
||||
env.Add("key", "value");
|
||||
|
||||
string value = env.GetOwinValue<string>("key");
|
||||
|
||||
Assert.Equal("value", value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetOwinValue_Throws_ForMissingKey()
|
||||
{
|
||||
var env = new Dictionary<string, object>();
|
||||
|
||||
Assert.Throws<InvalidOperationException>(
|
||||
() => env.GetOwinValue<string>("key"),
|
||||
"The OWIN environment does not contain a value for the required key 'key'.");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetOwinValue_Throws_ForKeyWithUnexpectedType()
|
||||
{
|
||||
var env = new Dictionary<string, object>();
|
||||
env.Add("key", new object());
|
||||
|
||||
Assert.Throws<InvalidOperationException>(
|
||||
() => env.GetOwinValue<string>("key"),
|
||||
"The value for key 'key' in the OWIN environment is not of the expected type 'String'.");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Http;
|
||||
using Microsoft.TestCommon;
|
||||
|
||||
namespace System.Web.Http.Owin
|
||||
{
|
||||
public class OwinHttpRequestMessageExtensionsTest
|
||||
{
|
||||
[Fact]
|
||||
public void GetOwinEnvironment_ReturnsNull_WhenNotSet()
|
||||
{
|
||||
Assert.Null(new HttpRequestMessage().GetOwinEnvironment());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetOwinEnvironment_ReturnsSetEnvironment()
|
||||
{
|
||||
var request = new HttpRequestMessage();
|
||||
var environment = new Dictionary<string, object>();
|
||||
request.SetOwinEnvironment(environment);
|
||||
|
||||
Assert.Equal(environment, request.GetOwinEnvironment());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory),Runtime.sln))\tools\WebStack.settings.targets" />
|
||||
<PropertyGroup>
|
||||
<ProjectGuid>{C19267DD-3984-430C-AE18-4034F85DE4E5}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>System.Web.Http.Owin</RootNamespace>
|
||||
<AssemblyName>System.Web.Http.Owin.Test</AssemblyName>
|
||||
<OutputPath>..\..\bin\$(Configuration)\Test\</OutputPath>
|
||||
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\</SolutionDir>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Microsoft.Owin.Host.HttpListener, Version=0.21.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\..\packages\Microsoft.Owin.Host.HttpListener.0.21.0-pre\lib\net45\Microsoft.Owin.Host.HttpListener.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Owin.Hosting, Version=0.21.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\..\packages\Microsoft.Owin.Hosting.0.21.0-pre\lib\net40\Microsoft.Owin.Hosting.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Moq, Version=4.0.10827.0, Culture=neutral, PublicKeyToken=69f491c39445e920, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\..\packages\Moq.4.0.10827\lib\NET40\Moq.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Newtonsoft.Json, Version=4.5.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\..\packages\Newtonsoft.Json.4.5.11\lib\net40\Newtonsoft.Json.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Owin">
|
||||
<HintPath>..\..\packages\Owin.1.0\lib\net40\Owin.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Net.Http" />
|
||||
<Reference Include="xunit, Version=1.9.1.1600, Culture=neutral, PublicKeyToken=8d05b1bb7a6fdb6c, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\..\packages\xunit.1.9.1\lib\net20\xunit.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="xunit.extensions, Version=1.9.1.1600, Culture=neutral, PublicKeyToken=8d05b1bb7a6fdb6c, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\..\packages\xunit.extensions.1.9.1\lib\net20\xunit.extensions.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="WebApiAppBuilderExtensionsTest.cs" />
|
||||
<Compile Include="OwinBufferPolicySelectorTest.cs" />
|
||||
<Compile Include="HttpMessageHandlerAdapterTest.cs" />
|
||||
<Compile Include="OwinEnvironmentExtensionsTest.cs" />
|
||||
<Compile Include="OwinHttpRequestMessageExtensionsTest.cs" />
|
||||
<Compile Include="IntegrationTest.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\System.Net.Http.Formatting\System.Net.Http.Formatting.csproj">
|
||||
<Project>{668e9021-ce84-49d9-98fb-df125a9fcdb0}</Project>
|
||||
<Name>System.Net.Http.Formatting</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\src\System.Web.Http.Owin\System.Web.Http.Owin.csproj">
|
||||
<Project>{66dd7cd7-c68f-4d0e-9f3d-3b58c49d1467}</Project>
|
||||
<Name>System.Web.Http.Owin</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\src\System.Web.Http\System.Web.Http.csproj">
|
||||
<Project>{ddc1ce0c-486e-4e35-bb3b-eab61f8f9440}</Project>
|
||||
<Name>System.Web.Http</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\Microsoft.TestCommon\Microsoft.TestCommon.csproj">
|
||||
<Project>{fccc4cb7-baf7-4a57-9f89-e5766fe536c0}</Project>
|
||||
<Name>Microsoft.TestCommon</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<Import Project="$(SolutionDir)\.nuget\nuget.targets" />
|
||||
</Project>
|
|
@ -0,0 +1,114 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
|
||||
|
||||
using System.Net.Http;
|
||||
using System.Web.Http.Hosting;
|
||||
using Microsoft.TestCommon;
|
||||
using Moq;
|
||||
using Owin;
|
||||
|
||||
namespace System.Web.Http.Owin
|
||||
{
|
||||
public class WebApiAppBuilderExtensionsTest
|
||||
{
|
||||
[Fact]
|
||||
public void UseWebApi_UsesAdapter()
|
||||
{
|
||||
var config = new HttpConfiguration();
|
||||
var appBuilder = new Mock<IAppBuilder>();
|
||||
appBuilder
|
||||
.Setup(ab => ab.Use(
|
||||
typeof(HttpMessageHandlerAdapter),
|
||||
It.Is<HttpServer>(s => s.Configuration == config),
|
||||
It.IsAny<OwinBufferPolicySelector>()))
|
||||
.Returns(appBuilder.Object)
|
||||
.Verifiable();
|
||||
|
||||
IAppBuilder returnedAppBuilder = appBuilder.Object.UseWebApi(config);
|
||||
|
||||
Assert.Equal(appBuilder.Object, returnedAppBuilder);
|
||||
appBuilder.Verify();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void UseWebApi_UsesAdapterAndConfigBufferPolicySelector()
|
||||
{
|
||||
var config = new HttpConfiguration();
|
||||
var bufferPolicySelector = new Mock<IHostBufferPolicySelector>().Object;
|
||||
config.Services.Replace(typeof(IHostBufferPolicySelector), bufferPolicySelector);
|
||||
var appBuilder = new Mock<IAppBuilder>();
|
||||
appBuilder
|
||||
.Setup(ab => ab.Use(
|
||||
typeof(HttpMessageHandlerAdapter),
|
||||
It.Is<HttpServer>(s => s.Configuration == config),
|
||||
bufferPolicySelector))
|
||||
.Returns(appBuilder.Object)
|
||||
.Verifiable();
|
||||
|
||||
IAppBuilder returnedAppBuilder = appBuilder.Object.UseWebApi(config);
|
||||
|
||||
Assert.Equal(appBuilder.Object, returnedAppBuilder);
|
||||
appBuilder.Verify();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void UseWebApiWithMessageHandler_UsesAdapter()
|
||||
{
|
||||
var config = new HttpConfiguration();
|
||||
var dispatcher = new Mock<HttpMessageHandler>().Object;
|
||||
var appBuilder = new Mock<IAppBuilder>();
|
||||
appBuilder
|
||||
.Setup(ab => ab.Use(
|
||||
typeof(HttpMessageHandlerAdapter),
|
||||
It.Is<HttpServer>(s => s.Configuration == config && s.Dispatcher == dispatcher),
|
||||
It.IsAny<OwinBufferPolicySelector>()))
|
||||
.Returns(appBuilder.Object)
|
||||
.Verifiable();
|
||||
|
||||
IAppBuilder returnedAppBuilder = appBuilder.Object.UseWebApi(config, dispatcher);
|
||||
|
||||
Assert.Equal(appBuilder.Object, returnedAppBuilder);
|
||||
appBuilder.Verify();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void UseWebApiWithMessageHandler_UsesAdapterAndConfigBufferPolicySelector()
|
||||
{
|
||||
var config = new HttpConfiguration();
|
||||
var bufferPolicySelector = new Mock<IHostBufferPolicySelector>().Object;
|
||||
config.Services.Replace(typeof(IHostBufferPolicySelector), bufferPolicySelector);
|
||||
var dispatcher = new Mock<HttpMessageHandler>().Object;
|
||||
var appBuilder = new Mock<IAppBuilder>();
|
||||
appBuilder
|
||||
.Setup(ab => ab.Use(
|
||||
typeof(HttpMessageHandlerAdapter),
|
||||
It.Is<HttpServer>(s => s.Configuration == config && s.Dispatcher == dispatcher),
|
||||
bufferPolicySelector))
|
||||
.Returns(appBuilder.Object)
|
||||
.Verifiable();
|
||||
|
||||
IAppBuilder returnedAppBuilder = appBuilder.Object.UseWebApi(config, dispatcher);
|
||||
|
||||
Assert.Equal(appBuilder.Object, returnedAppBuilder);
|
||||
appBuilder.Verify();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void UseHttpMessageHandler_UsesAdapter()
|
||||
{
|
||||
var messageHandler = new Mock<HttpMessageHandler>().Object;
|
||||
var appBuilder = new Mock<IAppBuilder>();
|
||||
appBuilder
|
||||
.Setup(ab => ab.Use(
|
||||
typeof(HttpMessageHandlerAdapter),
|
||||
messageHandler,
|
||||
It.IsAny<OwinBufferPolicySelector>()))
|
||||
.Returns(appBuilder.Object)
|
||||
.Verifiable();
|
||||
|
||||
IAppBuilder returnedAppBuilder = appBuilder.Object.UseHttpMessageHandler(messageHandler);
|
||||
|
||||
Assert.Equal(appBuilder.Object, returnedAppBuilder);
|
||||
appBuilder.Verify();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Microsoft.Owin.Host.HttpListener" version="0.21.0-pre" targetFramework="net45" />
|
||||
<package id="Microsoft.Owin.Hosting" version="0.21.0-pre" targetFramework="net45" />
|
||||
<package id="Moq" version="4.0.10827" targetFramework="net45" />
|
||||
<package id="Newtonsoft.Json" version="4.5.11" targetFramework="net45" />
|
||||
<package id="Owin" version="1.0" targetFramework="net45" />
|
||||
<package id="xunit" version="1.9.1" targetFramework="net45" />
|
||||
<package id="xunit.extensions" version="1.9.1" targetFramework="net45" />
|
||||
</packages>
|
|
@ -71,6 +71,7 @@ namespace System.Web.Http.Dispatcher
|
|||
responseTask.WaitUntilCompleted();
|
||||
|
||||
Assert.Equal(HttpStatusCode.NotFound, responseTask.Result.StatusCode);
|
||||
Assert.True((bool)request.Properties["MS_NoRouteMatched"]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
Загрузка…
Ссылка в новой задаче