Add WebSocket tests. Fix Connection header.

This commit is contained in:
Chris Ross 2014-06-17 16:11:44 -07:00
Родитель 6a810fd648
Коммит a3dfa41372
5 изменённых файлов: 271 добавлений и 8 удалений

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

@ -377,7 +377,7 @@ namespace Microsoft.Net.Server
*/
uint statusCode;
uint bytesSent;
List<GCHandle> pinnedHeaders = SerializeHeaders();
List<GCHandle> pinnedHeaders = SerializeHeaders(isOpaqueUpgrade);
try
{
if (pDataChunk != null)
@ -589,7 +589,7 @@ namespace Microsoft.Net.Server
return flags;
}
private List<GCHandle> SerializeHeaders()
private List<GCHandle> SerializeHeaders(bool isOpaqueUpgrade)
{
UnsafeNclNativeMethods.HttpApi.HTTP_UNKNOWN_HEADER[] unknownHeaders = null;
UnsafeNclNativeMethods.HttpApi.HTTP_RESPONSE_INFO[] knownHeaderInfo = null;
@ -623,7 +623,9 @@ namespace Microsoft.Net.Server
// See if this is an unknown header
lookup = UnsafeNclNativeMethods.HttpApi.HTTP_RESPONSE_HEADER_ID.IndexOfKnownHeader(headerPair.Key);
if (lookup == -1)
// Http.Sys doesn't let us send the Connection: Upgrade header as a Known header.
if (lookup == -1 ||
(isOpaqueUpgrade && lookup == (int)UnsafeNclNativeMethods.HttpApi.HTTP_RESPONSE_HEADER_ID.Enum.HttpHeaderConnection))
{
numUnknownHeaders += headerPair.Value.Length;
}
@ -649,7 +651,9 @@ namespace Microsoft.Net.Server
string[] headerValues = headerPair.Value;
lookup = UnsafeNclNativeMethods.HttpApi.HTTP_RESPONSE_HEADER_ID.IndexOfKnownHeader(headerName);
if (lookup == -1)
// Http.Sys doesn't let us send the Connection: Upgrade header as a Known header.
if (lookup == -1 ||
(isOpaqueUpgrade && lookup == (int)UnsafeNclNativeMethods.HttpApi.HTTP_RESPONSE_HEADER_ID.Enum.HttpHeaderConnection))
{
if (unknownHeaders == null)
{

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

@ -20,6 +20,7 @@
<ItemGroup>
<Compile Include="AuthenticationTests.cs" />
<Compile Include="HttpsTests.cs" />
<Compile Include="WebSocketTests.cs" />
<Compile Include="OpaqueUpgradeTests.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="RequestBodyTests.cs" />
@ -36,5 +37,4 @@
<Content Include="project.json" />
</ItemGroup>
<Import Project="$(VSToolsPath)\AspNet\Microsoft.Web.AspNet.targets" Condition="'$(VSToolsPath)' != ''" />
</Project>
</Project>

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

@ -0,0 +1,160 @@
// Copyright (c) Microsoft Open Technologies, Inc.
// All Rights Reserved
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
// WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF
// TITLE, FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR
// NON-INFRINGEMENT.
// See the Apache 2 License for the specific language governing
// permissions and limitations under the License.
using System;
using System.Net.Http;
using System.Net.WebSockets;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNet.FeatureModel;
using Microsoft.AspNet.HttpFeature;
using Microsoft.AspNet.PipelineCore;
using Xunit;
namespace Microsoft.AspNet.Server.WebListener
{
public class WebSocketTests
{
private const string Address = "http://localhost:8080/";
private const string WsAddress = "ws://localhost:8080/";
[Fact]
public async Task WebSocketTests_SupportKeys_Present()
{
using (Utilities.CreateHttpServer(env =>
{
var httpContext = new DefaultHttpContext((IFeatureCollection)env);
try
{
var webSocketFeature = httpContext.GetFeature<IHttpWebSocketFeature>();
Assert.NotNull(webSocketFeature);
}
catch (Exception ex)
{
return httpContext.Response.WriteAsync(ex.ToString());
}
return Task.FromResult(0);
}))
{
HttpResponseMessage response = await SendRequestAsync(Address);
Assert.Equal(200, (int)response.StatusCode);
Assert.False(response.Headers.TransferEncodingChunked.HasValue, "Chunked");
Assert.Equal(0, response.Content.Headers.ContentLength);
Assert.Equal(string.Empty, response.Content.ReadAsStringAsync().Result);
}
}
[Fact]
public async Task WebSocketTests_AfterHeadersSent_Throws()
{
bool? upgradeThrew = null;
using (Utilities.CreateHttpServer(async env =>
{
var httpContext = new DefaultHttpContext((IFeatureCollection)env);
await httpContext.Response.WriteAsync("Hello World");
try
{
var webSocketFeature = httpContext.GetFeature<IHttpWebSocketFeature>();
Assert.NotNull(webSocketFeature);
await webSocketFeature.AcceptAsync(null);
upgradeThrew = false;
}
catch (InvalidOperationException)
{
upgradeThrew = true;
}
}))
{
HttpResponseMessage response = await SendRequestAsync(Address);
Assert.Equal(200, (int)response.StatusCode);
Assert.True(response.Headers.TransferEncodingChunked.Value, "Chunked");
Assert.True(upgradeThrew.Value);
}
}
[Fact]
public async Task WebSocketAccept_Success()
{
ManualResetEvent waitHandle = new ManualResetEvent(false);
bool? upgraded = null;
using (Utilities.CreateHttpServer(async env =>
{
var httpContext = new DefaultHttpContext((IFeatureCollection)env);
var webSocketFeature = httpContext.GetFeature<IHttpWebSocketFeature>();
Assert.NotNull(webSocketFeature);
Assert.True(webSocketFeature.IsWebSocketRequest);
await webSocketFeature.AcceptAsync(null);
upgraded = true;
waitHandle.Set();
}))
{
using (WebSocket clientWebSocket = await SendWebSocketRequestAsync(WsAddress))
{
Assert.True(waitHandle.WaitOne(TimeSpan.FromSeconds(1)), "Timed out");
Assert.True(upgraded.HasValue, "Upgraded not set");
Assert.True(upgraded.Value, "Upgrade failed");
}
}
}
[Fact]
public async Task WebSocketAccept_SendAndReceive_Success()
{
byte[] clientBuffer = new byte[] { 0x00, 0x01, 0xFF, 0x00, 0x00 };
using (Utilities.CreateHttpServer(async env =>
{
var httpContext = new DefaultHttpContext((IFeatureCollection)env);
var webSocketFeature = httpContext.GetFeature<IHttpWebSocketFeature>();
Assert.NotNull(webSocketFeature);
Assert.True(webSocketFeature.IsWebSocketRequest);
var serverWebSocket = await webSocketFeature.AcceptAsync(null);
byte[] serverBuffer = new byte[clientBuffer.Length];
var result = await serverWebSocket.ReceiveAsync(new ArraySegment<byte>(serverBuffer, 0, serverBuffer.Length), CancellationToken.None);
Assert.Equal(clientBuffer, serverBuffer);
await serverWebSocket.SendAsync(new ArraySegment<byte>(serverBuffer, 0, result.Count), result.MessageType, result.EndOfMessage, CancellationToken.None);
}))
{
using (WebSocket clientWebSocket = await SendWebSocketRequestAsync(WsAddress))
{
await clientWebSocket.SendAsync(new ArraySegment<byte>(clientBuffer, 0, 3), WebSocketMessageType.Binary, true, CancellationToken.None);
byte[] clientEchoBuffer = new byte[clientBuffer.Length];
var result = await clientWebSocket.ReceiveAsync(new ArraySegment<byte>(clientEchoBuffer), CancellationToken.None);
Assert.Equal(clientBuffer, clientEchoBuffer);
}
}
}
private async Task<HttpResponseMessage> SendRequestAsync(string uri)
{
using (HttpClient client = new HttpClient())
{
return await client.GetAsync(uri);
}
}
private async Task<WebSocket> SendWebSocketRequestAsync(string address)
{
ClientWebSocket client = new ClientWebSocket();
await client.ConnectAsync(new Uri(address), CancellationToken.None);
return client;
}
}
}

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

@ -20,6 +20,7 @@
<ItemGroup>
<Compile Include="AuthenticationTests.cs" />
<Compile Include="HttpsTests.cs" />
<Compile Include="WebSocketTests.cs" />
<Compile Include="OpaqueUpgradeTests.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="RequestBodyTests.cs" />
@ -36,5 +37,4 @@
<Content Include="project.json" />
</ItemGroup>
<Import Project="$(VSToolsPath)\AspNet\Microsoft.Web.AspNet.targets" Condition="'$(VSToolsPath)' != ''" />
</Project>
</Project>

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

@ -0,0 +1,99 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System;
using System.Net.Http;
using System.Net.WebSockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Xunit;
namespace Microsoft.Net.Server
{
public class WebSocketTests
{
private const string Address = "http://localhost:8080/";
private const string WsAddress = "ws://localhost:8080/";
[Fact]
public async Task WebSocketAccept_AfterHeadersSent_Throws()
{
using (var server = Utilities.CreateHttpServer())
{
Task<HttpResponseMessage> clientTask = SendRequestAsync(Address);
var context = await server.GetContextAsync();
byte[] body = Encoding.UTF8.GetBytes("Hello World");
context.Response.Body.Write(body, 0, body.Length);
Assert.ThrowsAsync<InvalidOperationException>(async () => await context.AcceptWebSocketAsync());
context.Dispose();
HttpResponseMessage response = await clientTask;
Assert.Equal(200, (int)response.StatusCode);
Assert.True(response.Headers.TransferEncodingChunked.Value, "Chunked");
Assert.Equal("Hello World", await response.Content.ReadAsStringAsync());
}
}
[Fact]
public async Task WebSocketAccept_Success()
{
using (var server = Utilities.CreateHttpServer())
{
Task<WebSocket> clientTask = SendWebSocketRequestAsync(WsAddress);
var context = await server.GetContextAsync();
Assert.True(context.IsUpgradableRequest);
WebSocket serverWebSocket = await context.AcceptWebSocketAsync();
WebSocket clientWebSocket = await clientTask;
serverWebSocket.Dispose();
clientWebSocket.Dispose();
}
}
[Fact]
public async Task WebSocketAccept_SendAndReceive_Success()
{
using (var server = Utilities.CreateHttpServer())
{
Task<WebSocket> clientTask = SendWebSocketRequestAsync(WsAddress);
var context = await server.GetContextAsync();
Assert.True(context.IsWebSocketRequest);
WebSocket serverWebSocket = await context.AcceptWebSocketAsync();
WebSocket clientWebSocket = await clientTask;
byte[] clientBuffer = new byte[] { 0x00, 0x01, 0xFF, 0x00, 0x00 };
await clientWebSocket.SendAsync(new ArraySegment<byte>(clientBuffer, 0, 3), WebSocketMessageType.Binary, true, CancellationToken.None);
byte[] serverBuffer = new byte[clientBuffer.Length];
var result = await serverWebSocket.ReceiveAsync(new ArraySegment<byte>(serverBuffer, 0, serverBuffer.Length), CancellationToken.None);
Assert.Equal(clientBuffer, serverBuffer);
await serverWebSocket.SendAsync(new ArraySegment<byte>(serverBuffer, 0, result.Count), result.MessageType, result.EndOfMessage, CancellationToken.None);
byte[] clientEchoBuffer = new byte[clientBuffer.Length];
result = await clientWebSocket.ReceiveAsync(new ArraySegment<byte>(clientEchoBuffer), CancellationToken.None);
Assert.Equal(clientBuffer, clientEchoBuffer);
serverWebSocket.Dispose();
clientWebSocket.Dispose();
}
}
private async Task<HttpResponseMessage> SendRequestAsync(string uri)
{
using (HttpClient client = new HttpClient())
{
return await client.GetAsync(uri);
}
}
private async Task<WebSocket> SendWebSocketRequestAsync(string address)
{
ClientWebSocket client = new ClientWebSocket();
await client.ConnectAsync(new Uri(address), CancellationToken.None);
return client;
}
}
}