Skip to content
This repository was archived by the owner on Dec 18, 2018. It is now read-only.

Commit f074943

Browse files
committed
Precomputed header bytes
1 parent 55f6f21 commit f074943

File tree

11 files changed

+822
-370
lines changed

11 files changed

+822
-370
lines changed

src/Microsoft.AspNet.Server.Kestrel/Http/Frame.cs

Lines changed: 107 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,13 @@ public partial class Frame : FrameContext, IFrameControl
2828
private static readonly ArraySegment<byte> _emptyData = new ArraySegment<byte>(new byte[0]);
2929
private static readonly byte[] _hex = Encoding.ASCII.GetBytes("0123456789abcdef");
3030

31+
private static readonly byte[] _bytesEndLine = Encoding.ASCII.GetBytes("\r\n");
32+
private static readonly byte[] _bytesConnectionClose = Encoding.ASCII.GetBytes("Connection: close\r\n\r\n");
33+
private static readonly byte[] _bytesConnectionKeepAlive = Encoding.ASCII.GetBytes("Connection: keep-alive\r\n\r\n");
34+
private static readonly byte[] _bytesTransferEncodingChunked = Encoding.ASCII.GetBytes("Transfer-Encoding: chunked\r\n");
35+
private static readonly byte[] _bytesContentLengthZero = Encoding.ASCII.GetBytes("Content-Length: 0\r\n");
36+
private static readonly byte[] _bytesSpace = Encoding.ASCII.GetBytes(" ");
37+
3138
private readonly object _onStartingSync = new Object();
3239
private readonly object _onCompletedSync = new Object();
3340
private readonly FrameRequestHeaders _requestHeaders = new FrameRequestHeaders();
@@ -471,19 +478,14 @@ public async Task ProduceStartAndFireOnStarting(bool immediate = true)
471478
await ProduceStart(immediate, appCompleted: false);
472479
}
473480

474-
private async Task ProduceStart(bool immediate, bool appCompleted)
481+
private Task ProduceStart(bool immediate, bool appCompleted)
475482
{
476-
if (_responseStarted) return;
483+
if (_responseStarted) return TaskUtilities.CompletedTask;
477484
_responseStarted = true;
478485

479-
var status = ReasonPhrases.ToStatus(StatusCode, ReasonPhrase);
486+
var statusBytes = ReasonPhrases.ToStatusBytes(StatusCode, ReasonPhrase);
480487

481-
var responseHeader = CreateResponseHeader(status, appCompleted);
482-
483-
using (responseHeader.Item2)
484-
{
485-
await SocketOutput.WriteAsync(responseHeader.Item1, immediate: immediate);
486-
}
488+
return CreateResponseHeader(statusBytes, appCompleted, immediate);
487489
}
488490

489491
private async Task ProduceEnd()
@@ -521,99 +523,128 @@ private async Task ProduceEnd()
521523
}
522524
}
523525

524-
private Tuple<ArraySegment<byte>, IDisposable> CreateResponseHeader(
525-
string status,
526-
bool appCompleted)
526+
private static void OutputAsciiBlock(string data, MemoryPoolBlock2 memoryBlock, ISocketOutput output)
527527
{
528-
var writer = new MemoryPoolTextWriter(Memory);
529-
writer.Write(HttpVersion);
530-
writer.Write(' ');
531-
writer.Write(status);
532-
writer.Write('\r');
533-
writer.Write('\n');
534-
535-
var hasConnection = false;
536-
var hasTransferEncoding = false;
537-
var hasContentLength = false;
528+
var end = memoryBlock.Start + memoryBlock.Data.Count;
538529

539-
foreach (var header in _responseHeaders)
530+
foreach (var chr in data)
540531
{
541-
var isConnection = false;
542-
if (!hasConnection &&
543-
string.Equals(header.Key, "Connection", StringComparison.OrdinalIgnoreCase))
544-
{
545-
hasConnection = isConnection = true;
546-
}
547-
else if (!hasTransferEncoding &&
548-
string.Equals(header.Key, "Transfer-Encoding", StringComparison.OrdinalIgnoreCase))
549-
{
550-
hasTransferEncoding = true;
551-
}
552-
else if (!hasContentLength &&
553-
string.Equals(header.Key, "Content-Length", StringComparison.OrdinalIgnoreCase))
532+
memoryBlock.Array[memoryBlock.End] = (byte)chr;
533+
534+
memoryBlock.End++;
535+
536+
if (memoryBlock.End == end)
554537
{
555-
hasContentLength = true;
538+
output.Write(memoryBlock.Data, immediate: false);
539+
memoryBlock.End = memoryBlock.Start;
556540
}
541+
}
542+
}
557543

558-
foreach (var value in header.Value)
544+
private static void OutputAsciiBlock(byte[] data, MemoryPoolBlock2 memoryBlock, ISocketOutput output)
545+
{
546+
var offset = 0;
547+
var remaining = data.Length;
548+
var end = memoryBlock.Start + memoryBlock.Data.Count;
549+
550+
while (remaining > 0)
551+
{
552+
var blockRemaining = end - memoryBlock.End;
553+
var copyAmount = blockRemaining >= remaining ? remaining : blockRemaining;
554+
Buffer.BlockCopy(data, offset, memoryBlock.Array, memoryBlock.End, copyAmount);
555+
556+
memoryBlock.End += copyAmount;
557+
remaining -= copyAmount;
558+
offset += copyAmount;
559+
560+
if (memoryBlock.End == end)
559561
{
560-
writer.Write(header.Key);
561-
writer.Write(':');
562-
writer.Write(' ');
563-
writer.Write(value);
564-
writer.Write('\r');
565-
writer.Write('\n');
566-
567-
if (isConnection && value.IndexOf("close", StringComparison.OrdinalIgnoreCase) != -1)
568-
{
569-
_keepAlive = false;
570-
}
562+
output.Write(memoryBlock.Data, immediate: false);
563+
memoryBlock.End = memoryBlock.Start;
571564
}
572-
573565
}
566+
}
574567

575-
if (_keepAlive && !hasTransferEncoding && !hasContentLength)
568+
private Task CreateResponseHeader(
569+
byte[] statusBytes,
570+
bool appCompleted,
571+
bool immediate)
572+
{
573+
var memoryBlock = Memory2.Lease();
574+
try
576575
{
577-
if (appCompleted)
576+
var blockRemaining = memoryBlock.Data.Count;
577+
578+
OutputAsciiBlock(HttpVersion, memoryBlock, SocketOutput);
579+
OutputAsciiBlock(_bytesSpace, memoryBlock, SocketOutput);
580+
OutputAsciiBlock(statusBytes, memoryBlock, SocketOutput);
581+
582+
foreach (var header in _responseHeaders.AsOutputEnumerable())
578583
{
579-
// Don't set the Content-Length or Transfer-Encoding headers
580-
// automatically for HEAD requests or 101, 204, 205, 304 responses.
581-
if (Method != "HEAD" && StatusCanHaveBody(StatusCode))
584+
foreach (var value in header.Value)
582585
{
583-
// Since the app has completed and we are only now generating
584-
// the headers we can safely set the Content-Length to 0.
585-
writer.Write("Content-Length: 0\r\n");
586+
OutputAsciiBlock(header.Key, memoryBlock, SocketOutput);
587+
OutputAsciiBlock(value, memoryBlock, SocketOutput);
588+
OutputAsciiBlock(_bytesEndLine, memoryBlock, SocketOutput);
589+
590+
if (_responseHeaders.HasConnection && value.IndexOf("close", StringComparison.OrdinalIgnoreCase) != -1)
591+
{
592+
_keepAlive = false;
593+
}
586594
}
595+
587596
}
588-
else
597+
598+
if (_keepAlive && !_responseHeaders.HasTransferEncoding && !_responseHeaders.HasContentLength)
589599
{
590-
if (HttpVersion == "HTTP/1.1")
600+
if (appCompleted)
591601
{
592-
_autoChunk = true;
593-
writer.Write("Transfer-Encoding: chunked\r\n");
602+
// Don't set the Content-Length or Transfer-Encoding headers
603+
// automatically for HEAD requests or 101, 204, 205, 304 responses.
604+
if (Method != "HEAD" && StatusCanHaveBody(StatusCode))
605+
{
606+
// Since the app has completed and we are only now generating
607+
// the headers we can safely set the Content-Length to 0.
608+
OutputAsciiBlock(_bytesContentLengthZero, memoryBlock, SocketOutput);
609+
}
594610
}
595611
else
596612
{
597-
_keepAlive = false;
613+
if (HttpVersion == "HTTP/1.1")
614+
{
615+
_autoChunk = true;
616+
OutputAsciiBlock(_bytesTransferEncodingChunked, memoryBlock, SocketOutput);
617+
}
618+
else
619+
{
620+
_keepAlive = false;
621+
}
598622
}
599623
}
600-
}
601624

602-
if (_keepAlive == false && hasConnection == false && HttpVersion == "HTTP/1.1")
603-
{
604-
writer.Write("Connection: close\r\n\r\n");
605-
}
606-
else if (_keepAlive && hasConnection == false && HttpVersion == "HTTP/1.0")
607-
{
608-
writer.Write("Connection: keep-alive\r\n\r\n");
625+
if (_keepAlive == false && _responseHeaders.HasConnection == false && HttpVersion == "HTTP/1.1")
626+
{
627+
OutputAsciiBlock(_bytesConnectionClose, memoryBlock, SocketOutput);
628+
}
629+
else if (_keepAlive && _responseHeaders.HasConnection == false && HttpVersion == "HTTP/1.0")
630+
{
631+
OutputAsciiBlock(_bytesConnectionKeepAlive, memoryBlock, SocketOutput);
632+
}
633+
else
634+
{
635+
OutputAsciiBlock(_bytesEndLine, memoryBlock, SocketOutput);
636+
}
637+
638+
return SocketOutput.WriteAsync(
639+
(memoryBlock.Start == memoryBlock.End) ?
640+
default(ArraySegment<byte>) :
641+
new ArraySegment<byte>(memoryBlock.Array, memoryBlock.Start, memoryBlock.End - memoryBlock.Start),
642+
immediate);
609643
}
610-
else
644+
finally
611645
{
612-
writer.Write('\r');
613-
writer.Write('\n');
646+
Memory2.Return(memoryBlock);
614647
}
615-
writer.Flush();
616-
return new Tuple<ArraySegment<byte>, IDisposable>(writer.Buffer, writer);
617648
}
618649

619650
private bool TakeStartLine(SocketInput input)

0 commit comments

Comments
 (0)