From 71333212535257ad8915a182b405d98ad920f1c6 Mon Sep 17 00:00:00 2001 From: Yann Crumeyrolle Date: Tue, 19 Feb 2019 17:45:49 +0100 Subject: [PATCH] Use ReadOnlySpan optimization for static data in Kestrel --- .../src/Internal/Http2/Http2Connection.cs | 50 ++++++++++++------- .../Http2/Http2TestBase.cs | 2 +- 2 files changed, 32 insertions(+), 20 deletions(-) diff --git a/src/Servers/Kestrel/Core/src/Internal/Http2/Http2Connection.cs b/src/Servers/Kestrel/Core/src/Internal/Http2/Http2Connection.cs index c2b4ffc494ef..81c432e4c528 100644 --- a/src/Servers/Kestrel/Core/src/Internal/Http2/Http2Connection.cs +++ b/src/Servers/Kestrel/Core/src/Internal/Http2/Http2Connection.cs @@ -10,7 +10,6 @@ using System.IO.Pipelines; using System.Runtime.CompilerServices; using System.Security.Authentication; -using System.Text; using System.Threading; using System.Threading.Tasks; using Microsoft.AspNetCore.Connections; @@ -22,26 +21,39 @@ using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2.HPack; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure; using Microsoft.Extensions.Logging; -using Microsoft.Net.Http.Headers; namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2 { public class Http2Connection : IHttp2StreamLifetimeHandler, IHttpHeadersHandler, IRequestProcessor { - public static byte[] ClientPreface { get; } = Encoding.ASCII.GetBytes("PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n"); + public static ReadOnlySpan ClientPreface => new byte[] { + (byte)'P', (byte)'R', (byte)'I', (byte)' ', (byte)'*', (byte)' ', + (byte)'H', (byte)'T', (byte)'T', (byte)'P', (byte)'/', (byte)'2', + (byte)'.', (byte)'0', (byte)'\r', (byte)'\n', (byte)'\r', (byte)'\n', + (byte)'S', (byte)'M', (byte)'\r', (byte)'\n', (byte)'\r', (byte)'\n'}; private static readonly PseudoHeaderFields _mandatoryRequestPseudoHeaderFields = PseudoHeaderFields.Method | PseudoHeaderFields.Path | PseudoHeaderFields.Scheme; - private static readonly byte[] _authorityBytes = Encoding.ASCII.GetBytes(HeaderNames.Authority); - private static readonly byte[] _methodBytes = Encoding.ASCII.GetBytes(HeaderNames.Method); - private static readonly byte[] _pathBytes = Encoding.ASCII.GetBytes(HeaderNames.Path); - private static readonly byte[] _schemeBytes = Encoding.ASCII.GetBytes(HeaderNames.Scheme); - private static readonly byte[] _statusBytes = Encoding.ASCII.GetBytes(HeaderNames.Status); - private static readonly byte[] _connectionBytes = Encoding.ASCII.GetBytes("connection"); - private static readonly byte[] _teBytes = Encoding.ASCII.GetBytes("te"); - private static readonly byte[] _trailersBytes = Encoding.ASCII.GetBytes("trailers"); - private static readonly byte[] _connectBytes = Encoding.ASCII.GetBytes("CONNECT"); + private static ReadOnlySpan Authority => new byte[] { + (byte)':', (byte)'a', (byte)'u', (byte)'t', (byte)'h', (byte)'o', (byte)'r', + (byte)'i', (byte)'t', (byte)'y' }; + private static ReadOnlySpan Method => new byte[] { + (byte)':', (byte)'m', (byte)'e', (byte)'t', (byte)'h', (byte)'o', (byte)'d' }; + private static ReadOnlySpan Path => new byte[] { + (byte)':', (byte)'p', (byte)'a', (byte)'t', (byte)'h' }; + private static ReadOnlySpan Scheme => new byte[] { + (byte)':', (byte)'s', (byte)'c', (byte)'h', (byte)'e', (byte)'m', (byte)'e' }; + private static ReadOnlySpan Status => new byte[] { + (byte)':', (byte)'s', (byte)'t', (byte)'a', (byte)'t', (byte)'u', (byte)'s' }; + private static ReadOnlySpan Connection => new byte[] { + (byte)'c', (byte)'o', (byte)'n', (byte)'n', (byte)'e', (byte)'c', (byte)'t', (byte)'i', (byte)'o', (byte)'n' }; + private static ReadOnlySpan TE => new byte[] { + (byte)'t', (byte)'e' }; + private static ReadOnlySpan Trailers => new byte[] { + (byte)'t', (byte)'r', (byte)'a', (byte)'i', (byte)'l', (byte)'e', (byte)'r', (byte)'s' }; + private static ReadOnlySpan Connect => new byte[] { + (byte)'C', (byte)'O', (byte)'N', (byte)'N', (byte)'E', (byte)'C', (byte)'T' }; private readonly HttpConnectionContext _context; private readonly Http2FrameWriter _frameWriter; @@ -1149,7 +1161,7 @@ implementations to these vulnerabilities.*/ if (headerField == PseudoHeaderFields.Method) { - _isMethodConnect = value.SequenceEqual(_connectBytes); + _isMethodConnect = value.SequenceEqual(Connect); } _parsedPseudoHeaderFields |= headerField; @@ -1191,23 +1203,23 @@ private bool IsPseudoHeaderField(Span name, out PseudoHeaderFields headerF return false; } - if (name.SequenceEqual(_pathBytes)) + if (name.SequenceEqual(Path)) { headerField = PseudoHeaderFields.Path; } - else if (name.SequenceEqual(_methodBytes)) + else if (name.SequenceEqual(Method)) { headerField = PseudoHeaderFields.Method; } - else if (name.SequenceEqual(_schemeBytes)) + else if (name.SequenceEqual(Scheme)) { headerField = PseudoHeaderFields.Scheme; } - else if (name.SequenceEqual(_statusBytes)) + else if (name.SequenceEqual(Status)) { headerField = PseudoHeaderFields.Status; } - else if (name.SequenceEqual(_authorityBytes)) + else if (name.SequenceEqual(Authority)) { headerField = PseudoHeaderFields.Authority; } @@ -1221,7 +1233,7 @@ private bool IsPseudoHeaderField(Span name, out PseudoHeaderFields headerF private static bool IsConnectionSpecificHeaderField(Span name, Span value) { - return name.SequenceEqual(_connectionBytes) || (name.SequenceEqual(_teBytes) && !value.SequenceEqual(_trailersBytes)); + return name.SequenceEqual(Connection) || (name.SequenceEqual(TE) && !value.SequenceEqual(Trailers)); } private bool TryClose() diff --git a/src/Servers/Kestrel/test/InMemory.FunctionalTests/Http2/Http2TestBase.cs b/src/Servers/Kestrel/test/InMemory.FunctionalTests/Http2/Http2TestBase.cs index be7d6a3ad664..18b0d0f815b7 100644 --- a/src/Servers/Kestrel/test/InMemory.FunctionalTests/Http2/Http2TestBase.cs +++ b/src/Servers/Kestrel/test/InMemory.FunctionalTests/Http2/Http2TestBase.cs @@ -666,7 +666,7 @@ protected static async Task FlushAsync(PipeWriter writableBuffer) await writableBuffer.FlushAsync().AsTask().DefaultTimeout(); } - protected Task SendPreambleAsync() => SendAsync(new ArraySegment(Http2Connection.ClientPreface)); + protected Task SendPreambleAsync() => SendAsync(Http2Connection.ClientPreface); protected async Task SendSettingsAsync() {