Skip to content

Commit cf8fce2

Browse files
Synchronously write HTTP responses
1 parent d953d75 commit cf8fce2

6 files changed

Lines changed: 170 additions & 216 deletions

File tree

Engine/Internal/Protocol/ChunkedStream.cs

Lines changed: 24 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
1-
using GenHTTP.Modules.IO.Streaming;
2-
3-
namespace GenHTTP.Engine.Internal.Protocol;
1+
namespace GenHTTP.Engine.Internal.Protocol;
42

53
/// <summary>
64
/// Implements chunked transfer encoding by letting the client
@@ -11,18 +9,8 @@ namespace GenHTTP.Engine.Internal.Protocol;
119
/// soon as there is no known content length. To avoid this overhead,
1210
/// specify the length of your content whenever possible.
1311
/// </remarks>
14-
public sealed class ChunkedStream : Stream
12+
public sealed class ChunkedStream(Stream target) : Stream
1513
{
16-
private static readonly string NL = "\r\n";
17-
18-
#region Initialization
19-
20-
public ChunkedStream(Stream target)
21-
{
22-
Target = target;
23-
}
24-
25-
#endregion
2614

2715
#region Get-/Setters
2816

@@ -36,7 +24,7 @@ public ChunkedStream(Stream target)
3624

3725
public override long Position { get => throw new NotSupportedException(); set => throw new NotSupportedException(); }
3826

39-
private Stream Target { get; }
27+
private Stream Target { get; } = target;
4028

4129
#endregion
4230

@@ -59,37 +47,37 @@ public override void Write(byte[] buffer, int offset, int count)
5947

6048
Target.Write(buffer, offset, count);
6149

62-
NL.Write(Target);
50+
Target.Write("\r\n"u8);
6351
}
6452
}
6553

6654
public override async Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
6755
{
6856
if (count > 0)
6957
{
70-
await WriteAsync(count);
58+
Write(count);
7159

7260
await Target.WriteAsync(buffer.AsMemory(offset, count), cancellationToken);
7361

74-
await WriteAsync(NL);
62+
Target.Write("\r\n"u8);
7563
}
7664
}
7765

7866
public override async ValueTask WriteAsync(ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken = default)
7967
{
8068
if (!buffer.IsEmpty)
8169
{
82-
await WriteAsync(buffer.Length);
70+
Write(buffer.Length);
8371

8472
await Target.WriteAsync(buffer, cancellationToken);
8573

86-
await WriteAsync(NL);
74+
Target.Write("\r\n"u8);
8775
}
8876
}
8977

90-
public async ValueTask FinishAsync()
78+
public void Finish()
9179
{
92-
await WriteAsync("0\r\n\r\n");
80+
Target.Write("0\r\n\r\n"u8);
9381
}
9482

9583
public override void Flush()
@@ -99,11 +87,22 @@ public override void Flush()
9987

10088
public override Task FlushAsync(CancellationToken cancellationToken) => Target.FlushAsync(cancellationToken);
10189

102-
private void Write(int value) => $"{value:X}\r\n".Write(Target);
90+
private void Write(int value)
91+
{
92+
Span<byte> buffer = stackalloc byte[8 + 2];
10393

104-
private ValueTask WriteAsync(string text) => text.WriteAsync(Target);
94+
if (value.TryFormat(buffer, out var written, "X"))
95+
{
96+
buffer[written++] = (byte)'\r';
97+
buffer[written++] = (byte)'\n';
10598

106-
private ValueTask WriteAsync(int value) => $"{value:X}\r\n".WriteAsync(Target);
99+
Target.Write(buffer[..written]);
100+
}
101+
else
102+
{
103+
throw new InvalidOperationException("Failed to format chunk size");
104+
}
105+
}
107106

108107
#endregion
109108

0 commit comments

Comments
 (0)