Skip to content

Commit 8616601

Browse files
Do not send a keep-alive header for HTTP/1.1 (#701)
1 parent 2f35871 commit 8616601

4 files changed

Lines changed: 68 additions & 8 deletions

File tree

Engine/Internal/Protocol/ClientHandler.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ private async ValueTask<ConnectionStatus> HandleRequest(RequestBuilder builder,
166166
return ConnectionStatus.Upgraded;
167167
}
168168

169-
var active = await ResponseHandler.Handle(request, response, keepAlive, dataRemaining);
169+
var active = await ResponseHandler.Handle(request, response, request.ProtocolType, keepAlive, dataRemaining);
170170

171171
return (active && keepAlive) ? ConnectionStatus.KeepAlive : ConnectionStatus.Close;
172172
}
@@ -181,7 +181,7 @@ private async ValueTask SendError(Exception e, ResponseStatus status)
181181
.Content(new StringContent(message))
182182
.Build();
183183

184-
await ResponseHandler.Handle(null, response, false, false);
184+
await ResponseHandler.Handle(null, response, HttpProtocol.Http10, false, false);
185185
}
186186
catch
187187
{

Engine/Internal/Protocol/ResponseHandler.cs

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,13 +48,13 @@ internal ResponseHandler(IServer server, Socket socket, Stream outputStream, Net
4848

4949
#region Functionality
5050

51-
internal async ValueTask<bool> Handle(IRequest? request, IResponse response, bool keepAlive, bool dataRemaining)
51+
internal async ValueTask<bool> Handle(IRequest? request, IResponse response, HttpProtocol version, bool keepAlive, bool dataRemaining)
5252
{
5353
try
5454
{
5555
await WriteStatus(request, response);
5656

57-
await WriteHeader(response, keepAlive);
57+
await WriteHeader(response, version, keepAlive);
5858

5959
await Write(NL);
6060

@@ -99,7 +99,7 @@ private ValueTask WriteStatus(IRequest? request, IResponse response)
9999
return Write("HTTP/", version, " ", NumberStringCache.Convert(response.Status.RawStatus), " ", response.Status.Phrase, NL);
100100
}
101101

102-
private async ValueTask WriteHeader(IResponse response, bool keepAlive)
102+
private async ValueTask WriteHeader(IResponse response, HttpProtocol version, bool keepAlive)
103103
{
104104
if (response.Headers.TryGetValue(ServerHeader, out var server))
105105
{
@@ -112,7 +112,15 @@ private async ValueTask WriteHeader(IResponse response, bool keepAlive)
112112

113113
await WriteHeaderLine("Date", DateHeader.GetValue());
114114

115-
await WriteHeaderLine("Connection", keepAlive ? "Keep-Alive" : "Close");
115+
if (version == HttpProtocol.Http10)
116+
{
117+
await WriteHeaderLine("Connection", keepAlive ? "Keep-Alive" : "Close");
118+
}
119+
else if (!keepAlive)
120+
{
121+
// HTTP/1.1 connections are persistent by default so we do not need to send a Keep-Alive header
122+
await WriteHeaderLine("Connection", "Close");
123+
}
116124

117125
if (response.ContentType is not null)
118126
{

Testing/Acceptance/Engine/BasicTests.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,13 +65,13 @@ public async Task TestEmptyQuery(TestEngine engine)
6565
}
6666

6767
[TestMethod]
68-
public async Task TestKeepalive()
68+
public async Task TestNoKeepAliveHeaderOn11()
6969
{
7070
await using var runner = await TestHost.RunAsync(Layout.Create());
7171

7272
using var response = await runner.GetResponseAsync();
7373

74-
Assert.Contains("Keep-Alive", response.Headers.Connection);
74+
Assert.DoesNotContain("Keep-Alive", response.Headers.Connection);
7575
}
7676

7777
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
using System.Net;
2+
using System.Net.Sockets;
3+
4+
using GenHTTP.Api.Content;
5+
using GenHTTP.Api.Protocol;
6+
7+
using GenHTTP.Modules.Functional;
8+
9+
namespace GenHTTP.Testing.Acceptance.Engine;
10+
11+
[TestClass]
12+
public class KeepAliveTests
13+
{
14+
15+
/// <summary>
16+
/// This is a white box test that verifies that two subsequent requests
17+
/// to the server from the same client reuse the same connection,
18+
/// thus verifying that keep-alive connections are supported.
19+
/// </summary>
20+
[TestMethod]
21+
public async Task TestKeepAlive()
22+
{
23+
Socket? connection = null;
24+
25+
var tester = Inline.Create()
26+
.Get((IRequest r) =>
27+
{
28+
var socket = r.Upgrade().Socket;
29+
30+
if (connection == null)
31+
{
32+
connection = socket;
33+
}
34+
else
35+
{
36+
if (connection != socket)
37+
{
38+
throw new ProviderException(ResponseStatus.BadRequest, "Client connection did change, so this is not a keep-alive request");
39+
}
40+
}
41+
});
42+
43+
await using var runner = await TestHost.RunAsync(tester, engine: TestEngine.Internal);
44+
45+
using var r1 = await runner.GetResponseAsync();
46+
await r1.AssertStatusAsync(HttpStatusCode.NoContent);
47+
48+
using var r2 = await runner.GetResponseAsync();
49+
await r2.AssertStatusAsync(HttpStatusCode.NoContent);
50+
}
51+
52+
}

0 commit comments

Comments
 (0)