Add opaque upgrade tests.

This commit is contained in:
Chris Ross 2014-06-16 17:02:01 -07:00
parent d1dab1665e
commit 6a810fd648
2 changed files with 108 additions and 277 deletions

View File

@ -15,7 +15,6 @@
// See the Apache 2 License for the specific language governing // See the Apache 2 License for the specific language governing
// permissions and limitations under the License. // permissions and limitations under the License.
/* TODO: Opaque
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
@ -24,14 +23,13 @@ using System.Net.Sockets;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNet.FeatureModel;
using Microsoft.AspNet.HttpFeature;
using Microsoft.AspNet.PipelineCore;
using Xunit; using Xunit;
using Xunit.Extensions;
namespace Microsoft.AspNet.Server.WebListener namespace Microsoft.AspNet.Server.WebListener
{ {
using AppFunc = Func<object, Task>;
using OpaqueUpgrade = Action<IDictionary<string, object>, Func<IDictionary<string, object>, Task>>;
public class OpaqueUpgradeTests public class OpaqueUpgradeTests
{ {
private const string Address = "http://localhost:8080/"; private const string Address = "http://localhost:8080/";
@ -39,22 +37,17 @@ namespace Microsoft.AspNet.Server.WebListener
[Fact] [Fact]
public async Task OpaqueUpgrade_SupportKeys_Present() public async Task OpaqueUpgrade_SupportKeys_Present()
{ {
using (CreateServer(env => using (Utilities.CreateHttpServer(env =>
{ {
var httpContext = new DefaultHttpContext((IFeatureCollection)env);
try try
{ {
IDictionary<string, object> capabilities = env.Get<IDictionary<string, object>>("server.Capabilities"); var opaqueFeature = httpContext.GetFeature<IHttpOpaqueUpgradeFeature>();
Assert.NotNull(capabilities); Assert.NotNull(opaqueFeature);
Assert.Equal("1.0", capabilities.Get<string>("opaque.Version"));
OpaqueUpgrade opaqueUpgrade = env.Get<OpaqueUpgrade>("opaque.Upgrade");
Assert.NotNull(opaqueUpgrade);
} }
catch (Exception ex) catch (Exception ex)
{ {
byte[] body = Encoding.UTF8.GetBytes(ex.ToString()); return httpContext.Response.WriteAsync(ex.ToString());
env.Get<Stream>("owin.ResponseBody").Write(body, 0, body.Length);
} }
return Task.FromResult(0); return Task.FromResult(0);
})) }))
@ -67,50 +60,25 @@ namespace Microsoft.AspNet.Server.WebListener
} }
} }
[Fact]
public async Task OpaqueUpgrade_NullCallback_Throws()
{
using (CreateServer(env =>
{
try
{
OpaqueUpgrade opaqueUpgrade = env.Get<OpaqueUpgrade>("opaque.Upgrade");
opaqueUpgrade(new Dictionary<string, object>(), null);
}
catch (Exception ex)
{
byte[] body = Encoding.UTF8.GetBytes(ex.ToString());
env.Get<Stream>("owin.ResponseBody").Write(body, 0, body.Length);
}
return Task.FromResult(0);
}))
{
HttpResponseMessage response = await SendRequestAsync(Address);
Assert.Equal(200, (int)response.StatusCode);
Assert.True(response.Headers.TransferEncodingChunked.Value, "Chunked");
Assert.Contains("callback", response.Content.ReadAsStringAsync().Result);
}
}
[Fact] [Fact]
public async Task OpaqueUpgrade_AfterHeadersSent_Throws() public async Task OpaqueUpgrade_AfterHeadersSent_Throws()
{ {
bool? upgradeThrew = null; bool? upgradeThrew = null;
using (CreateServer(env => using (Utilities.CreateHttpServer(async env =>
{ {
byte[] body = Encoding.UTF8.GetBytes("Hello World"); var httpContext = new DefaultHttpContext((IFeatureCollection)env);
env.Get<Stream>("owin.ResponseBody").Write(body, 0, body.Length); await httpContext.Response.WriteAsync("Hello World");
OpaqueUpgrade opaqueUpgrade = env.Get<OpaqueUpgrade>("opaque.Upgrade");
try try
{ {
opaqueUpgrade(null, _ => Task.FromResult(0)); var opaqueFeature = httpContext.GetFeature<IHttpOpaqueUpgradeFeature>();
Assert.NotNull(opaqueFeature);
await opaqueFeature.UpgradeAsync();
upgradeThrew = false; upgradeThrew = false;
} }
catch (InvalidOperationException) catch (InvalidOperationException)
{ {
upgradeThrew = true; upgradeThrew = true;
} }
return Task.FromResult(0);
})) }))
{ {
HttpResponseMessage response = await SendRequestAsync(Address); HttpResponseMessage response = await SendRequestAsync(Address);
@ -124,26 +92,24 @@ namespace Microsoft.AspNet.Server.WebListener
public async Task OpaqueUpgrade_GetUpgrade_Success() public async Task OpaqueUpgrade_GetUpgrade_Success()
{ {
ManualResetEvent waitHandle = new ManualResetEvent(false); ManualResetEvent waitHandle = new ManualResetEvent(false);
bool? callbackInvoked = null; bool? upgraded = null;
using (CreateServer(env => using (Utilities.CreateHttpServer(async env =>
{ {
var responseHeaders = env.Get<IDictionary<string, string[]>>("owin.ResponseHeaders"); var httpContext = new DefaultHttpContext((IFeatureCollection)env);
responseHeaders["Upgrade"] = new string[] { "websocket" }; // Win8.1 blocks anything but WebSockets httpContext.Response.Headers["Upgrade"] = "websocket"; // Win8.1 blocks anything but WebSockets
OpaqueUpgrade opaqueUpgrade = env.Get<OpaqueUpgrade>("opaque.Upgrade"); var opaqueFeature = httpContext.GetFeature<IHttpOpaqueUpgradeFeature>();
opaqueUpgrade(null, opqEnv => Assert.NotNull(opaqueFeature);
{ Assert.True(opaqueFeature.IsUpgradableRequest);
callbackInvoked = true; await opaqueFeature.UpgradeAsync();
waitHandle.Set(); upgraded = true;
return Task.FromResult(0); waitHandle.Set();
});
return Task.FromResult(0);
})) }))
{ {
using (Stream stream = await SendOpaqueRequestAsync("GET", Address)) using (Stream stream = await SendOpaqueRequestAsync("GET", Address))
{ {
Assert.True(waitHandle.WaitOne(TimeSpan.FromSeconds(1)), "Timed out"); Assert.True(waitHandle.WaitOne(TimeSpan.FromSeconds(1)), "Timed out");
Assert.True(callbackInvoked.HasValue, "CallbackInvoked not set"); Assert.True(upgraded.HasValue, "Upgraded not set");
Assert.True(callbackInvoked.Value, "Callback not invoked"); Assert.True(upgraded.Value, "Upgrade failed");
} }
} }
} }
@ -173,21 +139,26 @@ namespace Microsoft.AspNet.Server.WebListener
[InlineData("PUT", "Content-Length: 0")] [InlineData("PUT", "Content-Length: 0")]
public async Task OpaqueUpgrade_VariousMethodsUpgradeSendAndReceive_Success(string method, string extraHeader) public async Task OpaqueUpgrade_VariousMethodsUpgradeSendAndReceive_Success(string method, string extraHeader)
{ {
using (CreateServer(env => using (Utilities.CreateHttpServer(async env =>
{ {
var responseHeaders = env.Get<IDictionary<string, string[]>>("owin.ResponseHeaders"); var httpContext = new DefaultHttpContext((IFeatureCollection)env);
responseHeaders["Upgrade"] = new string[] { "WebSocket" }; // Win8.1 blocks anything but WebSockets try
OpaqueUpgrade opaqueUpgrade = env.Get<OpaqueUpgrade>("opaque.Upgrade");
opaqueUpgrade(null, async opqEnv =>
{ {
Stream opaqueStream = opqEnv.Get<Stream>("opaque.Stream"); httpContext.Response.Headers["Upgrade"] = "websocket"; // Win8.1 blocks anything but WebSockets
var opaqueFeature = httpContext.GetFeature<IHttpOpaqueUpgradeFeature>();
Assert.NotNull(opaqueFeature);
Assert.True(opaqueFeature.IsUpgradableRequest);
var opaqueStream = await opaqueFeature.UpgradeAsync();
byte[] buffer = new byte[100]; byte[] buffer = new byte[100];
int read = await opaqueStream.ReadAsync(buffer, 0, buffer.Length); int read = await opaqueStream.ReadAsync(buffer, 0, buffer.Length);
await opaqueStream.WriteAsync(buffer, 0, read); await opaqueStream.WriteAsync(buffer, 0, read);
}); }
return Task.FromResult(0); catch (Exception ex)
{
await httpContext.Response.WriteAsync(ex.ToString());
}
})) }))
{ {
using (Stream stream = await SendOpaqueRequestAsync(method, Address, extraHeader)) using (Stream stream = await SendOpaqueRequestAsync(method, Address, extraHeader))
@ -208,54 +179,27 @@ namespace Microsoft.AspNet.Server.WebListener
[InlineData("PUT", "Transfer-Encoding: chunked")] [InlineData("PUT", "Transfer-Encoding: chunked")]
[InlineData("CUSTOMVERB", "Content-Length: 10")] [InlineData("CUSTOMVERB", "Content-Length: 10")]
[InlineData("CUSTOMVERB", "Transfer-Encoding: chunked")] [InlineData("CUSTOMVERB", "Transfer-Encoding: chunked")]
public void OpaqueUpgrade_InvalidMethodUpgrade_Disconnected(string method, string extraHeader) public async Task OpaqueUpgrade_InvalidMethodUpgrade_Disconnected(string method, string extraHeader)
{ {
OpaqueUpgrade opaqueUpgrade = null; using (Utilities.CreateHttpServer(async env =>
using (CreateServer(env =>
{ {
opaqueUpgrade = env.Get<OpaqueUpgrade>("opaque.Upgrade"); var httpContext = new DefaultHttpContext((IFeatureCollection)env);
if (opaqueUpgrade == null) try
{ {
throw new NotImplementedException(); var opaqueFeature = httpContext.GetFeature<IHttpOpaqueUpgradeFeature>();
Assert.NotNull(opaqueFeature);
Assert.False(opaqueFeature.IsUpgradableRequest);
}
catch (Exception ex)
{
await httpContext.Response.WriteAsync(ex.ToString());
} }
opaqueUpgrade(null, opqEnv => Task.FromResult(0));
return Task.FromResult(0);
})) }))
{ {
Assert.Throws<InvalidOperationException>(() => await Assert.ThrowsAsync<InvalidOperationException>(async () => await SendOpaqueRequestAsync(method, Address, extraHeader));
{
try
{
return SendOpaqueRequestAsync(method, Address, extraHeader).Result;
}
catch (AggregateException ag)
{
throw ag.GetBaseException();
}
});
Assert.Null(opaqueUpgrade);
} }
} }
private IDisposable CreateServer(AppFunc app)
{
IDictionary<string, object> properties = new Dictionary<string, object>();
IList<IDictionary<string, object>> addresses = new List<IDictionary<string, object>>();
properties["host.Addresses"] = addresses;
IDictionary<string, object> address = new Dictionary<string, object>();
addresses.Add(address);
address["scheme"] = "http";
address["host"] = "localhost";
address["port"] = "8080";
address["path"] = string.Empty;
OwinServerFactory.Initialize(properties);
return OwinServerFactory.Create(app, properties);
}
private async Task<HttpResponseMessage> SendRequestAsync(string uri) private async Task<HttpResponseMessage> SendRequestAsync(string uri)
{ {
using (HttpClient client = new HttpClient()) using (HttpClient client = new HttpClient())
@ -333,5 +277,4 @@ namespace Microsoft.AspNet.Server.WebListener
} }
} }
} }
} }
*/

View File

@ -1,135 +1,57 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. // Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
/* TODO: Opaque
using System; using System;
using System.Collections.Generic;
using System.IO; using System.IO;
using System.Net.Http; using System.Net.Http;
using System.Net.Sockets; using System.Net.Sockets;
using System.Text; using System.Text;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Xunit; using Xunit;
using Xunit.Extensions;
namespace Microsoft.Net.Server namespace Microsoft.Net.Server
{ {
using AppFunc = Func<object, Task>;
using OpaqueUpgrade = Action<IDictionary<string, object>, Func<IDictionary<string, object>, Task>>;
public class OpaqueUpgradeTests public class OpaqueUpgradeTests
{ {
private const string Address = "http://localhost:8080/"; private const string Address = "http://localhost:8080/";
[Fact]
public async Task OpaqueUpgrade_SupportKeys_Present()
{
using (CreateServer(env =>
{
try
{
IDictionary<string, object> capabilities = env.Get<IDictionary<string, object>>("server.Capabilities");
Assert.NotNull(capabilities);
Assert.Equal("1.0", capabilities.Get<string>("opaque.Version"));
OpaqueUpgrade opaqueUpgrade = env.Get<OpaqueUpgrade>("opaque.Upgrade");
Assert.NotNull(opaqueUpgrade);
}
catch (Exception ex)
{
byte[] body = Encoding.UTF8.GetBytes(ex.ToString());
env.Get<Stream>("owin.ResponseBody").Write(body, 0, body.Length);
}
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 OpaqueUpgrade_NullCallback_Throws()
{
using (CreateServer(env =>
{
try
{
OpaqueUpgrade opaqueUpgrade = env.Get<OpaqueUpgrade>("opaque.Upgrade");
opaqueUpgrade(new Dictionary<string, object>(), null);
}
catch (Exception ex)
{
byte[] body = Encoding.UTF8.GetBytes(ex.ToString());
env.Get<Stream>("owin.ResponseBody").Write(body, 0, body.Length);
}
return Task.FromResult(0);
}))
{
HttpResponseMessage response = await SendRequestAsync(Address);
Assert.Equal(200, (int)response.StatusCode);
Assert.True(response.Headers.TransferEncodingChunked.Value, "Chunked");
Assert.Contains("callback", response.Content.ReadAsStringAsync().Result);
}
}
[Fact] [Fact]
public async Task OpaqueUpgrade_AfterHeadersSent_Throws() public async Task OpaqueUpgrade_AfterHeadersSent_Throws()
{ {
bool? upgradeThrew = null; using (var server = Utilities.CreateHttpServer())
using (CreateServer(env =>
{ {
Task<HttpResponseMessage> clientTask = SendRequestAsync(Address);
var context = await server.GetContextAsync();
byte[] body = Encoding.UTF8.GetBytes("Hello World"); byte[] body = Encoding.UTF8.GetBytes("Hello World");
env.Get<Stream>("owin.ResponseBody").Write(body, 0, body.Length); context.Response.Body.Write(body, 0, body.Length);
OpaqueUpgrade opaqueUpgrade = env.Get<OpaqueUpgrade>("opaque.Upgrade");
try context.Response.Headers["Upgrade"] = new[] { "WebSocket" }; // Win8.1 blocks anything but WebSocket
{ Assert.ThrowsAsync<InvalidOperationException>(async () => await context.UpgradeAsync());
opaqueUpgrade(null, _ => Task.FromResult(0)); context.Dispose();
upgradeThrew = false; HttpResponseMessage response = await clientTask;
}
catch (InvalidOperationException)
{
upgradeThrew = true;
}
return Task.FromResult(0);
}))
{
HttpResponseMessage response = await SendRequestAsync(Address);
Assert.Equal(200, (int)response.StatusCode); Assert.Equal(200, (int)response.StatusCode);
Assert.True(response.Headers.TransferEncodingChunked.Value, "Chunked"); Assert.True(response.Headers.TransferEncodingChunked.Value, "Chunked");
Assert.True(upgradeThrew.Value); Assert.Equal("Hello World", await response.Content.ReadAsStringAsync());
} }
} }
[Fact] [Fact]
public async Task OpaqueUpgrade_GetUpgrade_Success() public async Task OpaqueUpgrade_GetUpgrade_Success()
{ {
ManualResetEvent waitHandle = new ManualResetEvent(false); using (var server = Utilities.CreateHttpServer())
bool? callbackInvoked = null;
using (CreateServer(env =>
{ {
var responseHeaders = env.Get<IDictionary<string, string[]>>("owin.ResponseHeaders"); Task<Stream> clientTask = SendOpaqueRequestAsync("GET", Address);
responseHeaders["Upgrade"] = new string[] { "websocket" }; // Win8.1 blocks anything but WebSockets
OpaqueUpgrade opaqueUpgrade = env.Get<OpaqueUpgrade>("opaque.Upgrade"); var context = await server.GetContextAsync();
opaqueUpgrade(null, opqEnv => Assert.True(context.IsUpgradableRequest);
{ context.Response.Headers["Upgrade"] = new[] { "WebSocket" }; // Win8.1 blocks anything but WebSocket
callbackInvoked = true; Stream serverStream = await context.UpgradeAsync();
waitHandle.Set(); Assert.True(serverStream.CanRead);
return Task.FromResult(0); Assert.True(serverStream.CanWrite);
}); Stream clientStream = await clientTask;
return Task.FromResult(0); serverStream.Dispose();
})) context.Dispose();
{ clientStream.Dispose();
using (Stream stream = await SendOpaqueRequestAsync("GET", Address))
{
Assert.True(waitHandle.WaitOne(TimeSpan.FromSeconds(1)), "Timed out");
Assert.True(callbackInvoked.HasValue, "CallbackInvoked not set");
Assert.True(callbackInvoked.Value, "Callback not invoked");
}
} }
} }
@ -158,30 +80,32 @@ namespace Microsoft.Net.Server
[InlineData("PUT", "Content-Length: 0")] [InlineData("PUT", "Content-Length: 0")]
public async Task OpaqueUpgrade_VariousMethodsUpgradeSendAndReceive_Success(string method, string extraHeader) public async Task OpaqueUpgrade_VariousMethodsUpgradeSendAndReceive_Success(string method, string extraHeader)
{ {
using (CreateServer(env => using (var server = Utilities.CreateHttpServer())
{ {
var responseHeaders = env.Get<IDictionary<string, string[]>>("owin.ResponseHeaders"); Task<Stream> clientTask = SendOpaqueRequestAsync(method, Address, extraHeader);
responseHeaders["Upgrade"] = new string[] { "WebSocket" }; // Win8.1 blocks anything but WebSockets
OpaqueUpgrade opaqueUpgrade = env.Get<OpaqueUpgrade>("opaque.Upgrade");
opaqueUpgrade(null, async opqEnv =>
{
Stream opaqueStream = opqEnv.Get<Stream>("opaque.Stream");
byte[] buffer = new byte[100]; var context = await server.GetContextAsync();
int read = await opaqueStream.ReadAsync(buffer, 0, buffer.Length); Assert.True(context.IsUpgradableRequest);
context.Response.Headers["Upgrade"] = new[] { "WebSocket" }; // Win8.1 blocks anything but WebSocket
Stream serverStream = await context.UpgradeAsync();
Stream clientStream = await clientTask;
await opaqueStream.WriteAsync(buffer, 0, read); byte[] clientBuffer = new byte[] { 0x00, 0x01, 0xFF, 0x00, 0x00 };
}); await clientStream.WriteAsync(clientBuffer, 0, 3);
return Task.FromResult(0);
})) byte[] serverBuffer = new byte[clientBuffer.Length];
{ int read = await serverStream.ReadAsync(serverBuffer, 0, serverBuffer.Length);
using (Stream stream = await SendOpaqueRequestAsync(method, Address, extraHeader)) Assert.Equal(clientBuffer, serverBuffer);
{
byte[] data = new byte[100]; await serverStream.WriteAsync(serverBuffer, 0, read);
stream.WriteAsync(data, 0, 49).Wait();
int read = stream.ReadAsync(data, 0, data.Length).Result; byte[] clientEchoBuffer = new byte[clientBuffer.Length];
Assert.Equal(49, read); read = await clientStream.ReadAsync(clientEchoBuffer, 0, clientEchoBuffer.Length);
} Assert.Equal(clientBuffer, clientEchoBuffer);
serverStream.Dispose();
context.Dispose();
clientStream.Dispose();
} }
} }
@ -193,54 +117,19 @@ namespace Microsoft.Net.Server
[InlineData("PUT", "Transfer-Encoding: chunked")] [InlineData("PUT", "Transfer-Encoding: chunked")]
[InlineData("CUSTOMVERB", "Content-Length: 10")] [InlineData("CUSTOMVERB", "Content-Length: 10")]
[InlineData("CUSTOMVERB", "Transfer-Encoding: chunked")] [InlineData("CUSTOMVERB", "Transfer-Encoding: chunked")]
public void OpaqueUpgrade_InvalidMethodUpgrade_Disconnected(string method, string extraHeader) public async Task OpaqueUpgrade_InvalidMethodUpgrade_Disconnected(string method, string extraHeader)
{ {
OpaqueUpgrade opaqueUpgrade = null; using (var server = Utilities.CreateHttpServer())
using (CreateServer(env =>
{ {
opaqueUpgrade = env.Get<OpaqueUpgrade>("opaque.Upgrade"); var clientTask = SendOpaqueRequestAsync(method, Address, extraHeader);
if (opaqueUpgrade == null) var context = await server.GetContextAsync();
{ Assert.False(context.IsUpgradableRequest);
throw new NotImplementedException(); context.Dispose();
}
opaqueUpgrade(null, opqEnv => Task.FromResult(0)); await Assert.ThrowsAsync<InvalidOperationException>(async () => await clientTask);
return Task.FromResult(0);
}))
{
Assert.Throws<InvalidOperationException>(() =>
{
try
{
return SendOpaqueRequestAsync(method, Address, extraHeader).Result;
}
catch (AggregateException ag)
{
throw ag.GetBaseException();
}
});
Assert.Null(opaqueUpgrade);
} }
} }
private IDisposable CreateServer(AppFunc app)
{
IDictionary<string, object> properties = new Dictionary<string, object>();
IList<IDictionary<string, object>> addresses = new List<IDictionary<string, object>>();
properties["host.Addresses"] = addresses;
IDictionary<string, object> address = new Dictionary<string, object>();
addresses.Add(address);
address["scheme"] = "http";
address["host"] = "localhost";
address["port"] = "8080";
address["path"] = string.Empty;
OwinServerFactory.Initialize(properties);
return OwinServerFactory.Create(app, properties);
}
private async Task<HttpResponseMessage> SendRequestAsync(string uri) private async Task<HttpResponseMessage> SendRequestAsync(string uri)
{ {
using (HttpClient client = new HttpClient()) using (HttpClient client = new HttpClient())
@ -318,5 +207,4 @@ namespace Microsoft.Net.Server
} }
} }
} }
} }
*/