Add WebSocket tests. Fix Connection header.
This commit is contained in:
Родитель
6a810fd648
Коммит
a3dfa41372
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
Загрузка…
Ссылка в новой задаче