Introducing an OWIN Web API Adapter - System.Web.Http.Owin

This commit is contained in:
youssefm 2013-02-06 16:38:25 -08:00
Родитель 556493386c
Коммит 18cb600a3f
30 изменённых файлов: 1862 добавлений и 4 удалений

Просмотреть файл

@ -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" />

Просмотреть файл

@ -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")]

99
src/System.Web.Http.Owin/Properties/SRResources.Designer.cs сгенерированный Normal file
Просмотреть файл

@ -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 &apos;Host&apos; 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 &apos;{0}&apos; in the OWIN environment is not of the expected type &apos;{1}&apos;..
/// </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 &apos;{0}&apos;..
/// </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]