Skip to content

Commit

Permalink
HTTP agnostic loopback server. (dotnet#40144)
Browse files Browse the repository at this point in the history
  • Loading branch information
ManickaP authored Aug 3, 2020
1 parent 995224d commit 3218c12
Show file tree
Hide file tree
Showing 14 changed files with 423 additions and 111 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,9 @@ public abstract class GenericLoopbackConnection : IDisposable
/// <summary>Sends Response body after SendResponse was called with isFinal: false.</summary>
public abstract Task SendResponseBodyAsync(byte[] content, bool isFinal = true, int requestId = 0);

/// <summary>Reads Request, sends Response and closes connection.</summary>
public abstract Task<HttpRequestData> HandleRequestAsync(HttpStatusCode statusCode = HttpStatusCode.OK, IList<HttpHeaderData> headers = null, string content = "");

/// <summary>Waits for the client to signal cancellation.</summary>
public abstract Task WaitForCancellationAsync(bool ignoreIncomingData = true, int requestId = 0);

Expand All @@ -95,6 +98,8 @@ public class GenericLoopbackOptions
SslProtocols.Tls13 |
#endif
SslProtocols.Tls12;

public int ListenBacklog { get; set; } = 1;
}

public struct HttpHeaderData
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ namespace System.Net.Test.Common
{
public class Http2LoopbackConnection : GenericLoopbackConnection
{
public const string Http2Prefix = "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n";

private Socket _connectionSocket;
private Stream _connectionStream;
private TaskCompletionSource<bool> _ignoredSettingsAckPromise;
Expand Down Expand Up @@ -58,7 +60,6 @@ public static async Task<Http2LoopbackConnection> CreateAsync(Socket socket, Str

var protocols = new List<SslApplicationProtocol>();
protocols.Add(SslApplicationProtocol.Http2);
protocols.Add(SslApplicationProtocol.Http11);
options.ApplicationProtocols = protocols;

options.ServerCertificate = cert;
Expand Down Expand Up @@ -697,7 +698,11 @@ public async Task SendResponseBodyAsync(int streamId, ReadOnlyMemory<byte> respo

public override void Dispose()
{
ShutdownIgnoringErrorsAsync(_lastStreamId).GetAwaiter().GetResult();
// Might have been already shutdown manually via WaitForConnectionShutdownAsync which nulls the _connectionStream.
if (_connectionStream != null)
{
ShutdownIgnoringErrorsAsync(_lastStreamId).GetAwaiter().GetResult();
}
}

//
Expand Down Expand Up @@ -783,6 +788,29 @@ public override Task SendResponseBodyAsync(byte[] body, bool isFinal = true, int
return SendResponseBodyAsync(streamId, body, isFinal);
}

public override async Task<HttpRequestData> HandleRequestAsync(HttpStatusCode statusCode = HttpStatusCode.OK, IList<HttpHeaderData> headers = null, string content = "")
{
(int streamId, HttpRequestData requestData) = await ReadAndParseRequestHeaderAsync().ConfigureAwait(false);

// We are about to close the connection, after we send the response.
// So, send a GOAWAY frame now so the client won't inadvertantly try to reuse the connection.
await SendGoAway(streamId).ConfigureAwait(false);

if (string.IsNullOrEmpty(content))
{
await SendResponseHeadersAsync(streamId, endStream: true, statusCode, isTrailingHeader: false, headers : headers).ConfigureAwait(false);
}
else
{
await SendResponseHeadersAsync(streamId, endStream: false, statusCode, isTrailingHeader: false, headers : headers).ConfigureAwait(false);
await SendResponseBodyAsync(streamId, Encoding.ASCII.GetBytes(content)).ConfigureAwait(false);
}

await WaitForConnectionShutdownAsync().ConfigureAwait(false);

return requestData;
}

public override async Task WaitForCancellationAsync(bool ignoreIncomingData = true, int requestId = 0)
{
int streamId = requestId == 0 ? _lastStreamId : requestId;
Expand Down
28 changes: 5 additions & 23 deletions src/libraries/Common/tests/System/Net/Http/Http2LoopbackServer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -143,27 +143,10 @@ public override void Dispose()

public override async Task<HttpRequestData> HandleRequestAsync(HttpStatusCode statusCode = HttpStatusCode.OK, IList<HttpHeaderData> headers = null, string content = "")
{
Http2LoopbackConnection connection = await EstablishConnectionAsync().ConfigureAwait(false);

(int streamId, HttpRequestData requestData) = await connection.ReadAndParseRequestHeaderAsync().ConfigureAwait(false);

// We are about to close the connection, after we send the response.
// So, send a GOAWAY frame now so the client won't inadvertantly try to reuse the connection.
await connection.SendGoAway(streamId).ConfigureAwait(false);

if (string.IsNullOrEmpty(content))
{
await connection.SendResponseHeadersAsync(streamId, endStream: true, statusCode, isTrailingHeader: false, headers : headers).ConfigureAwait(false);
}
else
using (Http2LoopbackConnection connection = await EstablishConnectionAsync().ConfigureAwait(false))
{
await connection.SendResponseHeadersAsync(streamId, endStream: false, statusCode, isTrailingHeader: false, headers : headers).ConfigureAwait(false);
await connection.SendResponseBodyAsync(streamId, Encoding.ASCII.GetBytes(content)).ConfigureAwait(false);
}

await connection.WaitForConnectionShutdownAsync().ConfigureAwait(false);

return requestData;
return await connection.HandleRequestAsync(statusCode, headers, content).ConfigureAwait(false);
}
}

public override async Task AcceptConnectionAsync(Func<GenericLoopbackConnection, Task> funcAsync)
Expand Down Expand Up @@ -193,8 +176,6 @@ public static async Task CreateClientAndServerAsync(Func<Uri, Task> clientFunc,

public class Http2Options : GenericLoopbackOptions
{
public int ListenBacklog { get; set; } = 1;

public bool ClientCertificateRequired { get; set; }

public Http2Options()
Expand Down Expand Up @@ -233,6 +214,7 @@ private static Http2Options CreateOptions(GenericLoopbackOptions options)
http2Options.Address = options.Address;
http2Options.UseSsl = options.UseSsl;
http2Options.SslProtocols = options.SslProtocols;
http2Options.ListenBacklog = options.ListenBacklog;
}
return http2Options;
}
Expand All @@ -245,7 +227,7 @@ public override async Task CreateServerAsync(Func<GenericLoopbackServer, Uri, Ta
}
}

public override Version Version => HttpVersion20.Value;
public override Version Version => HttpVersion20.Value;
}

public enum ProtocolErrors
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,14 @@ private async Task SendResponseHeadersAsync(HttpStatusCode? statusCode = HttpSta
await GetOpenRequest(requestId).SendHeadersFrameAsync(headers).ConfigureAwait(false);
}

public override async Task<HttpRequestData> HandleRequestAsync(HttpStatusCode statusCode = HttpStatusCode.OK, IList<HttpHeaderData> headers = null, string content = "")
{
HttpRequestData request = await ReadRequestDataAsync().ConfigureAwait(false);
await SendResponseAsync(statusCode, headers, content).ConfigureAwait(false);
await CloseAsync(Http3LoopbackConnection.H3_NO_ERROR);
return request;
}

public override async Task WaitForCancellationAsync(bool ignoreIncomingData = true, int requestId = 0)
{
await GetOpenRequest(requestId).WaitForCancellationAsync(ignoreIncomingData).ConfigureAwait(false);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,7 @@ public override async Task AcceptConnectionAsync(Func<GenericLoopbackConnection,
public override async Task<HttpRequestData> HandleRequestAsync(HttpStatusCode statusCode = HttpStatusCode.OK, IList<HttpHeaderData> headers = null, string content = "")
{
using var con = (Http3LoopbackConnection)await EstablishGenericConnectionAsync().ConfigureAwait(false);

HttpRequestData request = await con.ReadRequestDataAsync().ConfigureAwait(false);
await con.SendResponseAsync(statusCode, headers, content).ConfigureAwait(false);
await con.CloseAsync(Http3LoopbackConnection.H3_NO_ERROR);
return request;
return await con.HandleRequestAsync(statusCode, headers, content).ConfigureAwait(false);
}
}

Expand Down
Loading

0 comments on commit 3218c12

Please sign in to comment.