Skip to content

Commit

Permalink
Refresh network tests server code to Kestrel (#52642)
Browse files Browse the repository at this point in the history
* copy from davidsh/corefx-net-endpoint@999f835
* license headers
* missing md5 test
* issue for missing functionality
  • Loading branch information
pavelsavara authored May 13, 2021
1 parent ce396bb commit 509ed7a
Show file tree
Hide file tree
Showing 23 changed files with 1,242 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.28307.438
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NetCoreServer", "NetCoreServer\NetCoreServer.csproj", "{86E9A13D-9F4A-45DE-B0BB-CBB6A6533867}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{86E9A13D-9F4A-45DE-B0BB-CBB6A6533867}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{86E9A13D-9F4A-45DE-B0BB-CBB6A6533867}.Debug|Any CPU.Build.0 = Debug|Any CPU
{86E9A13D-9F4A-45DE-B0BB-CBB6A6533867}.Release|Any CPU.ActiveCfg = Release|Any CPU
{86E9A13D-9F4A-45DE-B0BB-CBB6A6533867}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {2F9A0637-452E-4FB9-9403-CB52944982DA}
EndGlobalSection
EndGlobal
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;

namespace NetCoreServer
{
public class GenericHandler
{
// Must have constructor with this signature, otherwise exception at run time.
public GenericHandler(RequestDelegate next)
{
// This is an HTTP Handler, so no need to store next.
}

public async Task Invoke(HttpContext context)
{
PathString path = context.Request.Path;
if (path.Equals(new PathString("/deflate.ashx")))
{
await DeflateHandler.InvokeAsync(context);
return;
}

if (path.Equals(new PathString("/echo.ashx")))
{
await EchoHandler.InvokeAsync(context);
return;
}

if (path.Equals(new PathString("/emptycontent.ashx")))
{
EmptyContentHandler.Invoke(context);
return;
}

if (path.Equals(new PathString("/gzip.ashx")))
{
await GZipHandler.InvokeAsync(context);
return;
}

if (path.Equals(new PathString("/redirect.ashx")))
{
RedirectHandler.Invoke(context);
return;
}

if (path.Equals(new PathString("/statuscode.ashx")))
{
StatusCodeHandler.Invoke(context);
return;
}

if (path.Equals(new PathString("/verifyupload.ashx")))
{
await VerifyUploadHandler.InvokeAsync(context);
return;
}

if (path.Equals(new PathString("/version")))
{
await VersionHandler.InvokeAsync(context);
return;
}

if (path.Equals(new PathString("/websocket/echowebsocket.ashx")))
{
await EchoWebSocketHandler.InvokeAsync(context);
return;
}

if (path.Equals(new PathString("/websocket/echowebsocketheaders.ashx")))
{
await EchoWebSocketHeadersHandler.InvokeAsync(context);
return;
}
if (path.Equals(new PathString("/test.ashx")))
{
await TestHandler.InvokeAsync(context);
return;
}

// Default handling.
await EchoHandler.InvokeAsync(context);
}
}

public static class GenericHandlerExtensions
{
public static IApplicationBuilder UseGenericHandler(this IApplicationBuilder builder)
{
return builder.UseMiddleware<GenericHandler>();
}

public static void SetStatusDescription(this HttpResponse response, string description)
{
response.HttpContext.Features.Get<IHttpResponseFeature>().ReasonPhrase = description;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;

namespace NetCoreServer
{
public class DeflateHandler
{
public static async Task InvokeAsync(HttpContext context)
{
string responseBody = "Sending DEFLATE compressed";

context.Response.Headers.Add("Content-MD5", Convert.ToBase64String(ContentHelper.ComputeMD5Hash(responseBody)));
context.Response.Headers.Add("Content-Encoding", "deflate");

context.Response.ContentType = "text/plain";

byte[] bytes = ContentHelper.GetDeflateBytes(responseBody);
await context.Response.Body.WriteAsync(bytes);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;

namespace NetCoreServer
{
public class EchoHandler
{
public static async Task InvokeAsync(HttpContext context)
{
RequestHelper.AddResponseCookies(context);

if (!AuthenticationHelper.HandleAuthentication(context))
{
return;
}

// Add original request method verb as a custom response header.
context.Response.Headers.Add("X-HttpRequest-Method", context.Request.Method);

// Echo back JSON encoded payload.
RequestInformation info = await RequestInformation.CreateAsync(context.Request);
string echoJson = info.SerializeToJson();

// Compute MD5 hash so that clients can verify the received data.
using (MD5 md5 = MD5.Create())
{
byte[] bytes = Encoding.UTF8.GetBytes(echoJson);
byte[] hash = md5.ComputeHash(bytes);
string encodedHash = Convert.ToBase64String(hash);

context.Response.Headers.Add("Content-MD5", encodedHash);
context.Response.ContentType = "application/json";
context.Response.ContentLength = bytes.Length;
await context.Response.Body.WriteAsync(bytes, 0, bytes.Length);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Net.WebSockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;

namespace NetCoreServer
{
public class EchoWebSocketHandler
{
private const int MaxBufferSize = 128 * 1024;

public static async Task InvokeAsync(HttpContext context)
{
QueryString queryString = context.Request.QueryString;
bool replyWithPartialMessages = queryString.HasValue && queryString.Value.Contains("replyWithPartialMessages");
bool replyWithEnhancedCloseMessage = queryString.HasValue && queryString.Value.Contains("replyWithEnhancedCloseMessage");

string subProtocol = context.Request.Query["subprotocol"];

if (context.Request.QueryString.HasValue && context.Request.QueryString.Value.Contains("delay10sec"))
{
Thread.Sleep(10000);
}

try
{
if (!context.WebSockets.IsWebSocketRequest)
{
context.Response.StatusCode = 200;
context.Response.ContentType = "text/plain";
await context.Response.WriteAsync("Not a websocket request");

return;
}

WebSocket socket;
if (!string.IsNullOrEmpty(subProtocol))
{
socket = await context.WebSockets.AcceptWebSocketAsync(subProtocol);
}
else
{
socket = await context.WebSockets.AcceptWebSocketAsync();
}

await ProcessWebSocketRequest(socket, replyWithPartialMessages, replyWithEnhancedCloseMessage);
}
catch (Exception)
{
// We might want to log these exceptions. But for now we ignore them.
}
}

private static async Task ProcessWebSocketRequest(
WebSocket socket,
bool replyWithPartialMessages,
bool replyWithEnhancedCloseMessage)
{
var receiveBuffer = new byte[MaxBufferSize];
var throwAwayBuffer = new byte[MaxBufferSize];

// Stay in loop while websocket is open
while (socket.State == WebSocketState.Open || socket.State == WebSocketState.CloseSent)
{
var receiveResult = await socket.ReceiveAsync(new ArraySegment<byte>(receiveBuffer), CancellationToken.None);
if (receiveResult.MessageType == WebSocketMessageType.Close)
{
if (receiveResult.CloseStatus == WebSocketCloseStatus.Empty)
{
await socket.CloseAsync(WebSocketCloseStatus.Empty, null, CancellationToken.None);
}
else
{
WebSocketCloseStatus closeStatus = receiveResult.CloseStatus.GetValueOrDefault();
await socket.CloseAsync(
closeStatus,
replyWithEnhancedCloseMessage ?
$"Server received: {(int)closeStatus} {receiveResult.CloseStatusDescription}" :
receiveResult.CloseStatusDescription,
CancellationToken.None);
}

continue;
}

// Keep reading until we get an entire message.
int offset = receiveResult.Count;
while (receiveResult.EndOfMessage == false)
{
if (offset < MaxBufferSize)
{
receiveResult = await socket.ReceiveAsync(
new ArraySegment<byte>(receiveBuffer, offset, MaxBufferSize - offset),
CancellationToken.None);
}
else
{
receiveResult = await socket.ReceiveAsync(
new ArraySegment<byte>(throwAwayBuffer),
CancellationToken.None);
}

offset += receiveResult.Count;
}

// Close socket if the message was too big.
if (offset > MaxBufferSize)
{
await socket.CloseAsync(
WebSocketCloseStatus.MessageTooBig,
String.Format("{0}: {1} > {2}", WebSocketCloseStatus.MessageTooBig.ToString(), offset, MaxBufferSize),
CancellationToken.None);

continue;
}

bool sendMessage = false;
if (receiveResult.MessageType == WebSocketMessageType.Text)
{
string receivedMessage = Encoding.UTF8.GetString(receiveBuffer, 0, offset);
if (receivedMessage == ".close")
{
await socket.CloseAsync(WebSocketCloseStatus.NormalClosure, receivedMessage, CancellationToken.None);
}
if (receivedMessage == ".shutdown")
{
await socket.CloseOutputAsync(WebSocketCloseStatus.NormalClosure, receivedMessage, CancellationToken.None);
}
else if (receivedMessage == ".abort")
{
socket.Abort();
}
else if (receivedMessage == ".delay5sec")
{
await Task.Delay(5000);
}
else if (socket.State == WebSocketState.Open)
{
sendMessage = true;
}
}
else
{
sendMessage = true;
}

if (sendMessage)
{
await socket.SendAsync(
new ArraySegment<byte>(receiveBuffer, 0, offset),
receiveResult.MessageType,
!replyWithPartialMessages,
CancellationToken.None);
}
}
}
}
}
Loading

0 comments on commit 509ed7a

Please sign in to comment.