Merge code paths duplicated between HTTP/1.x and HTTP/2 implementations (#2017).

- Most of the shared code is in the HttpProtocol class (former Frame)
- Virtual calls handle protocol-specific things
- Move the ProcessRequestsAsync loop to HttpProtocol
- Implement HTTP/1.x request processing in Http1Connection and HTTP/2
  in Http2Stream, with Http1Connection<T> and Http2Stream<T> subclassing
  those classes in order to handle the generic HttpContext parameter
- Split MessageBody into Http1MessageBody and Http2MessageBody,
  with MessageBody containing shared member variables and methods
This commit is contained in:
Cesar Blum Silveira 2017-09-14 17:35:57 -07:00 committed by GitHub
parent d28b413390
commit a31d1e024c
87 changed files with 2695 additions and 4401 deletions

View File

@ -12,22 +12,22 @@ using Microsoft.AspNetCore.Server.Kestrel.Performance.Mocks;
namespace Microsoft.AspNetCore.Server.Kestrel.Performance
{
[Config(typeof(CoreConfig))]
public class FrameParsingOverheadBenchmark
public class Http1ConnectionParsingOverheadBenchmark
{
private const int InnerLoopCount = 512;
public ReadableBuffer _buffer;
public Frame<object> _frame;
public Http1Connection<object> _http1Connection;
[IterationSetup]
public void Setup()
{
var serviceContext = new ServiceContext
{
HttpParserFactory = _ => NullParser<FrameAdapter>.Instance,
HttpParserFactory = _ => NullParser<Http1ParsingHandler>.Instance,
ServerOptions = new KestrelServerOptions()
};
var frameContext = new FrameContext
var http1ConnectionContext = new Http1ConnectionContext
{
ServiceContext = serviceContext,
ConnectionFeatures = new FeatureCollection(),
@ -35,11 +35,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Performance
TimeoutControl = new MockTimeoutControl()
};
_frame = new Frame<object>(application: null, frameContext: frameContext);
_http1Connection = new Http1Connection<object>(application: null, context: http1ConnectionContext);
}
[Benchmark(Baseline = true, OperationsPerInvoke = InnerLoopCount)]
public void FrameOverheadTotal()
public void Http1ConnectionOverheadTotal()
{
for (var i = 0; i < InnerLoopCount; i++)
{
@ -48,7 +48,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Performance
}
[Benchmark(OperationsPerInvoke = InnerLoopCount)]
public void FrameOverheadRequestLine()
public void Http1ConnectionOverheadRequestLine()
{
for (var i = 0; i < InnerLoopCount; i++)
{
@ -57,7 +57,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Performance
}
[Benchmark(OperationsPerInvoke = InnerLoopCount)]
public void FrameOverheadRequestHeaders()
public void Http1ConnectionOverheadRequestHeaders()
{
for (var i = 0; i < InnerLoopCount; i++)
{
@ -67,14 +67,14 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Performance
private void ParseRequest()
{
_frame.Reset();
_http1Connection.Reset();
if (!_frame.TakeStartLine(_buffer, out var consumed, out var examined))
if (!_http1Connection.TakeStartLine(_buffer, out var consumed, out var examined))
{
ErrorUtilities.ThrowInvalidRequestLine();
}
if (!_frame.TakeMessageHeaders(_buffer, out consumed, out examined))
if (!_http1Connection.TakeMessageHeaders(_buffer, out consumed, out examined))
{
ErrorUtilities.ThrowInvalidRequestHeaders();
}
@ -82,9 +82,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Performance
private void ParseRequestLine()
{
_frame.Reset();
_http1Connection.Reset();
if (!_frame.TakeStartLine(_buffer, out var consumed, out var examined))
if (!_http1Connection.TakeStartLine(_buffer, out var consumed, out var examined))
{
ErrorUtilities.ThrowInvalidRequestLine();
}
@ -92,9 +92,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Performance
private void ParseRequestHeaders()
{
_frame.Reset();
_http1Connection.Reset();
if (!_frame.TakeMessageHeaders(_buffer, out var consumed, out var examined))
if (!_http1Connection.TakeMessageHeaders(_buffer, out var consumed, out var examined))
{
ErrorUtilities.ThrowInvalidRequestHeaders();
}

View File

@ -16,7 +16,7 @@ using Microsoft.AspNetCore.Testing;
namespace Microsoft.AspNetCore.Server.Kestrel.Performance
{
[Config(typeof(CoreConfig))]
public class FrameWritingBenchmark
public class Http1WritingBenchmark
{
// Standard completed task
private static readonly Func<object, Task> _syncTaskFunc = (obj) => Task.CompletedTask;
@ -24,14 +24,14 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Performance
private static readonly Task _psuedoAsyncTask = Task.FromResult(27);
private static readonly Func<object, Task> _psuedoAsyncTaskFunc = (obj) => _psuedoAsyncTask;
private readonly TestFrame<object> _frame;
private readonly TestHttp1Connection<object> _http1Connection;
private (IPipeConnection Transport, IPipeConnection Application) _pair;
private readonly byte[] _writeData;
public FrameWritingBenchmark()
public Http1WritingBenchmark()
{
_frame = MakeFrame();
_http1Connection = MakeHttp1Connection();
_writeData = Encoding.ASCII.GetBytes("Hello, World!");
}
@ -47,19 +47,19 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Performance
[IterationSetup]
public void Setup()
{
_frame.Reset();
_http1Connection.Reset();
if (Chunked)
{
_frame.RequestHeaders.Add("Transfer-Encoding", "chunked");
_http1Connection.RequestHeaders.Add("Transfer-Encoding", "chunked");
}
else
{
_frame.RequestHeaders.ContentLength = _writeData.Length;
_http1Connection.RequestHeaders.ContentLength = _writeData.Length;
}
if (!WithHeaders)
{
_frame.FlushAsync().GetAwaiter().GetResult();
_http1Connection.FlushAsync().GetAwaiter().GetResult();
}
ResetState();
@ -69,15 +69,15 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Performance
{
if (WithHeaders)
{
_frame.ResetState();
_http1Connection.ResetState();
switch (OnStarting)
{
case Startup.Sync:
_frame.OnStarting(_syncTaskFunc, null);
_http1Connection.OnStarting(_syncTaskFunc, null);
break;
case Startup.Async:
_frame.OnStarting(_psuedoAsyncTaskFunc, null);
_http1Connection.OnStarting(_psuedoAsyncTaskFunc, null);
break;
}
}
@ -88,10 +88,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Performance
{
ResetState();
return _frame.ResponseBody.WriteAsync(_writeData, 0, _writeData.Length, default(CancellationToken));
return _http1Connection.ResponseBody.WriteAsync(_writeData, 0, _writeData.Length, default(CancellationToken));
}
private TestFrame<object> MakeFrame()
private TestHttp1Connection<object> MakeHttp1Connection()
{
var pipeFactory = new PipeFactory();
var pair = pipeFactory.CreateConnectionPair();
@ -102,10 +102,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Performance
DateHeaderValueManager = new DateHeaderValueManager(),
ServerOptions = new KestrelServerOptions(),
Log = new MockTrace(),
HttpParserFactory = f => new HttpParser<FrameAdapter>()
HttpParserFactory = f => new HttpParser<Http1ParsingHandler>()
};
var frame = new TestFrame<object>(application: null, context: new FrameContext
var http1Connection = new TestHttp1Connection<object>(application: null, context: new Http1ConnectionContext
{
ServiceContext = serviceContext,
ConnectionFeatures = new FeatureCollection(),
@ -114,10 +114,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Performance
Transport = pair.Transport
});
frame.Reset();
frame.InitializeStreams(MessageBody.ZeroContentLengthKeepAlive);
http1Connection.Reset();
http1Connection.InitializeStreams(MessageBody.ZeroContentLengthKeepAlive);
return frame;
return http1Connection;
}
[IterationCleanup]

View File

@ -10,7 +10,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Performance
{
[Config(typeof(CoreConfig))]
public class KestrelHttpParserBenchmark : IHttpRequestLineHandler, IHttpHeadersHandler
public class HttpParserBenchmark : IHttpRequestLineHandler, IHttpHeadersHandler
{
private readonly HttpParser<Adapter> _parser = new HttpParser<Adapter>();
@ -76,9 +76,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Performance
private struct Adapter : IHttpRequestLineHandler, IHttpHeadersHandler
{
public KestrelHttpParserBenchmark RequestHandler;
public HttpParserBenchmark RequestHandler;
public Adapter(KestrelHttpParserBenchmark requestHandler)
public Adapter(HttpParserBenchmark requestHandler)
{
RequestHandler = requestHandler;
}

View File

@ -12,9 +12,9 @@ using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http;
namespace Microsoft.AspNetCore.Server.Kestrel.Performance
{
[Config(typeof(CoreConfig))]
public class FrameFeatureCollection
public class HttpProtocolFeatureCollection
{
private readonly Frame<object> _frame;
private readonly Http1Connection<object> _http1Connection;
private IFeatureCollection _collection;
[Benchmark(Baseline = true)]
@ -72,30 +72,30 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Performance
private object GetFastFeature(Type type)
{
return _frame.FastFeatureGet(type);
return _http1Connection.FastFeatureGet(type);
}
public FrameFeatureCollection()
public HttpProtocolFeatureCollection()
{
var serviceContext = new ServiceContext
{
HttpParserFactory = _ => NullParser<FrameAdapter>.Instance,
HttpParserFactory = _ => NullParser<Http1ParsingHandler>.Instance,
ServerOptions = new KestrelServerOptions()
};
var frameContext = new FrameContext
var http1ConnectionContext = new Http1ConnectionContext
{
ServiceContext = serviceContext,
ConnectionFeatures = new FeatureCollection(),
PipeFactory = new PipeFactory()
};
_frame = new Frame<object>(application: null, frameContext: frameContext);
_http1Connection = new Http1Connection<object>(application: null, context: http1ConnectionContext);
}
[IterationSetup]
public void Setup()
{
_collection = _frame;
_collection = _http1Connection;
}
}

View File

@ -14,7 +14,7 @@
<ItemGroup>
<Compile Include="..\..\test\shared\TestApplicationErrorLogger.cs" />
<Compile Include="..\..\test\shared\TestFrame.cs" />
<Compile Include="..\..\test\shared\TestHttp1Connection.cs" />
<Compile Include="..\..\test\shared\TestKestrelTrace.cs" />
</ItemGroup>

View File

@ -19,7 +19,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Performance
private readonly byte[] _connectionHeaderName = Encoding.ASCII.GetBytes("Connection");
private readonly byte[] _connectionHeaderValue = Encoding.ASCII.GetBytes("keep-alive");
public static readonly NullParser<FrameAdapter> Instance = new NullParser<FrameAdapter>();
public static readonly NullParser<Http1ParsingHandler> Instance = new NullParser<Http1ParsingHandler>();
public bool ParseHeaders(TRequestHandler handler, ReadableBuffer buffer, out ReadCursor consumed, out ReadCursor examined, out int consumedBytes)
{

View File

@ -16,7 +16,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Performance
{
public IPipe Pipe { get; set; }
public Frame<object> Frame { get; set; }
public Http1Connection<object> Http1Connection { get; set; }
public PipeFactory PipeFactory { get; set; }
@ -28,10 +28,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Performance
var serviceContext = new ServiceContext
{
HttpParserFactory = f => new HttpParser<FrameAdapter>(),
HttpParserFactory = f => new HttpParser<Http1ParsingHandler>(),
ServerOptions = new KestrelServerOptions(),
};
var frameContext = new FrameContext
var http1ConnectionContext = new Http1ConnectionContext
{
ServiceContext = serviceContext,
ConnectionFeatures = new FeatureCollection(),
@ -39,7 +39,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Performance
TimeoutControl = new MockTimeoutControl()
};
Frame = new Frame<object>(application: null, frameContext: frameContext);
Http1Connection = new Http1Connection<object>(application: null, context: http1ConnectionContext);
}
[Benchmark(Baseline = true, OperationsPerInvoke = RequestParsingData.InnerLoopCount)]
@ -142,16 +142,16 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Performance
var readableBuffer = awaitable.GetResult().Buffer;
do
{
Frame.Reset();
Http1Connection.Reset();
if (!Frame.TakeStartLine(readableBuffer, out var consumed, out var examined))
if (!Http1Connection.TakeStartLine(readableBuffer, out var consumed, out var examined))
{
ErrorUtilities.ThrowInvalidRequestLine();
}
readableBuffer = readableBuffer.Slice(consumed);
if (!Frame.TakeMessageHeaders(readableBuffer, out consumed, out examined))
if (!Http1Connection.TakeMessageHeaders(readableBuffer, out consumed, out examined))
{
ErrorUtilities.ThrowInvalidRequestHeaders();
}
@ -177,9 +177,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Performance
var result = awaitable.GetAwaiter().GetResult();
var readableBuffer = result.Buffer;
Frame.Reset();
Http1Connection.Reset();
if (!Frame.TakeStartLine(readableBuffer, out var consumed, out var examined))
if (!Http1Connection.TakeStartLine(readableBuffer, out var consumed, out var examined))
{
ErrorUtilities.ThrowInvalidRequestLine();
}
@ -188,7 +188,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Performance
result = Pipe.Reader.ReadAsync().GetAwaiter().GetResult();
readableBuffer = result.Buffer;
if (!Frame.TakeMessageHeaders(readableBuffer, out consumed, out examined))
if (!Http1Connection.TakeMessageHeaders(readableBuffer, out consumed, out examined))
{
ErrorUtilities.ThrowInvalidRequestHeaders();
}

View File

@ -21,7 +21,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Performance
private static readonly byte[] _bytesServer = Encoding.ASCII.GetBytes("\r\nServer: Kestrel");
private static readonly DateHeaderValueManager _dateHeaderValueManager = new DateHeaderValueManager();
private FrameResponseHeaders _responseHeadersDirect;
private HttpResponseHeaders _responseHeadersDirect;
private HttpResponse _response;
public enum BenchmarkTypes
@ -172,21 +172,21 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Performance
{
var serviceContext = new ServiceContext
{
HttpParserFactory = f => new HttpParser<FrameAdapter>(),
HttpParserFactory = f => new HttpParser<Http1ParsingHandler>(),
ServerOptions = new KestrelServerOptions()
};
var frameContext = new FrameContext
var http1ConnectionContext = new Http1ConnectionContext
{
ServiceContext = serviceContext,
ConnectionFeatures = new FeatureCollection(),
PipeFactory = new PipeFactory()
};
var frame = new Frame<object>(application: null, frameContext: frameContext);
var http1Connection = new Http1Connection<object>(application: null, context: http1ConnectionContext);
frame.Reset();
_responseHeadersDirect = (FrameResponseHeaders)frame.ResponseHeaders;
var context = new DefaultHttpContext(frame);
http1Connection.Reset();
_responseHeadersDirect = (HttpResponseHeaders)http1Connection.ResponseHeaders;
var context = new DefaultHttpContext(http1Connection);
_response = new DefaultHttpResponse(context);
switch (Type)

View File

@ -21,7 +21,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Performance
{
private static readonly byte[] _helloWorldPayload = Encoding.ASCII.GetBytes("Hello, World!");
private TestFrame<object> _frame;
private TestHttp1Connection<object> _http1Connection;
[Params(
BenchmarkTypes.TechEmpowerPlaintext,
@ -35,10 +35,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Performance
[Benchmark]
public async Task Output()
{
_frame.Reset();
_frame.StatusCode = 200;
_frame.HttpVersionEnum = HttpVersion.Http11;
_frame.KeepAlive = true;
_http1Connection.Reset();
_http1Connection.StatusCode = 200;
_http1Connection.HttpVersionEnum = HttpVersion.Http11;
_http1Connection.KeepAlive = true;
Task writeTask = Task.CompletedTask;
switch (Type)
@ -61,50 +61,50 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Performance
}
await writeTask;
await _frame.ProduceEndAsync();
await _http1Connection.ProduceEndAsync();
}
private Task TechEmpowerPlaintext()
{
var responseHeaders = _frame.ResponseHeaders;
var responseHeaders = _http1Connection.ResponseHeaders;
responseHeaders["Content-Type"] = "text/plain";
responseHeaders.ContentLength = _helloWorldPayload.Length;
return _frame.WriteAsync(new ArraySegment<byte>(_helloWorldPayload), default(CancellationToken));
return _http1Connection.WriteAsync(new ArraySegment<byte>(_helloWorldPayload), default(CancellationToken));
}
private Task PlaintextChunked()
{
var responseHeaders = _frame.ResponseHeaders;
var responseHeaders = _http1Connection.ResponseHeaders;
responseHeaders["Content-Type"] = "text/plain";
return _frame.WriteAsync(new ArraySegment<byte>(_helloWorldPayload), default(CancellationToken));
return _http1Connection.WriteAsync(new ArraySegment<byte>(_helloWorldPayload), default(CancellationToken));
}
private Task LiveAspNet()
{
var responseHeaders = _frame.ResponseHeaders;
var responseHeaders = _http1Connection.ResponseHeaders;
responseHeaders["Content-Encoding"] = "gzip";
responseHeaders["Content-Type"] = "text/html; charset=utf-8";
responseHeaders["Strict-Transport-Security"] = "max-age=31536000; includeSubdomains";
responseHeaders["Vary"] = "Accept-Encoding";
responseHeaders["X-Powered-By"] = "ASP.NET";
return _frame.WriteAsync(new ArraySegment<byte>(_helloWorldPayload), default(CancellationToken));
return _http1Connection.WriteAsync(new ArraySegment<byte>(_helloWorldPayload), default(CancellationToken));
}
private Task PlaintextWithCookie()
{
var responseHeaders = _frame.ResponseHeaders;
var responseHeaders = _http1Connection.ResponseHeaders;
responseHeaders["Content-Type"] = "text/plain";
responseHeaders["Set-Cookie"] = "prov=20629ccd-8b0f-e8ef-2935-cd26609fc0bc; __qca=P0-1591065732-1479167353442; _ga=GA1.2.1298898376.1479167354; _gat=1; sgt=id=9519gfde_3347_4762_8762_df51458c8ec2; acct=t=why-is-%e0%a5%a7%e0%a5%a8%e0%a5%a9-numeric&s=why-is-%e0%a5%a7%e0%a5%a8%e0%a5%a9-numeric";
responseHeaders.ContentLength = _helloWorldPayload.Length;
return _frame.WriteAsync(new ArraySegment<byte>(_helloWorldPayload), default(CancellationToken));
return _http1Connection.WriteAsync(new ArraySegment<byte>(_helloWorldPayload), default(CancellationToken));
}
private Task PlaintextChunkedWithCookie()
{
var responseHeaders = _frame.ResponseHeaders;
var responseHeaders = _http1Connection.ResponseHeaders;
responseHeaders["Content-Type"] = "text/plain";
responseHeaders["Set-Cookie"] = "prov=20629ccd-8b0f-e8ef-2935-cd26609fc0bc; __qca=P0-1591065732-1479167353442; _ga=GA1.2.1298898376.1479167354; _gat=1; sgt=id=9519gfde_3347_4762_8762_df51458c8ec2; acct=t=why-is-%e0%a5%a7%e0%a5%a8%e0%a5%a9-numeric&s=why-is-%e0%a5%a7%e0%a5%a8%e0%a5%a9-numeric";
return _frame.WriteAsync(new ArraySegment<byte>(_helloWorldPayload), default(CancellationToken));
return _http1Connection.WriteAsync(new ArraySegment<byte>(_helloWorldPayload), default(CancellationToken));
}
[IterationSetup]
@ -118,10 +118,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Performance
DateHeaderValueManager = new DateHeaderValueManager(),
ServerOptions = new KestrelServerOptions(),
Log = new MockTrace(),
HttpParserFactory = f => new HttpParser<FrameAdapter>()
HttpParserFactory = f => new HttpParser<Http1ParsingHandler>()
};
var frame = new TestFrame<object>(application: null, context: new FrameContext
var http1Connection = new TestHttp1Connection<object>(application: null, context: new Http1ConnectionContext
{
ServiceContext = serviceContext,
ConnectionFeatures = new FeatureCollection(),
@ -131,9 +131,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Performance
Transport = pair.Transport
});
frame.Reset();
http1Connection.Reset();
_frame = frame;
_http1Connection = http1Connection;
}
public enum BenchmarkTypes

View File

@ -1,362 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
{
public partial class Frame
{
private static readonly Type IHttpRequestFeatureType = typeof(global::Microsoft.AspNetCore.Http.Features.IHttpRequestFeature);
private static readonly Type IHttpResponseFeatureType = typeof(global::Microsoft.AspNetCore.Http.Features.IHttpResponseFeature);
private static readonly Type IHttpRequestIdentifierFeatureType = typeof(global::Microsoft.AspNetCore.Http.Features.IHttpRequestIdentifierFeature);
private static readonly Type IServiceProvidersFeatureType = typeof(global::Microsoft.AspNetCore.Http.Features.IServiceProvidersFeature);
private static readonly Type IHttpRequestLifetimeFeatureType = typeof(global::Microsoft.AspNetCore.Http.Features.IHttpRequestLifetimeFeature);
private static readonly Type IHttpConnectionFeatureType = typeof(global::Microsoft.AspNetCore.Http.Features.IHttpConnectionFeature);
private static readonly Type IHttpAuthenticationFeatureType = typeof(global::Microsoft.AspNetCore.Http.Features.Authentication.IHttpAuthenticationFeature);
private static readonly Type IQueryFeatureType = typeof(global::Microsoft.AspNetCore.Http.Features.IQueryFeature);
private static readonly Type IFormFeatureType = typeof(global::Microsoft.AspNetCore.Http.Features.IFormFeature);
private static readonly Type IHttpUpgradeFeatureType = typeof(global::Microsoft.AspNetCore.Http.Features.IHttpUpgradeFeature);
private static readonly Type IResponseCookiesFeatureType = typeof(global::Microsoft.AspNetCore.Http.Features.IResponseCookiesFeature);
private static readonly Type IItemsFeatureType = typeof(global::Microsoft.AspNetCore.Http.Features.IItemsFeature);
private static readonly Type ITlsConnectionFeatureType = typeof(global::Microsoft.AspNetCore.Http.Features.ITlsConnectionFeature);
private static readonly Type IHttpWebSocketFeatureType = typeof(global::Microsoft.AspNetCore.Http.Features.IHttpWebSocketFeature);
private static readonly Type ISessionFeatureType = typeof(global::Microsoft.AspNetCore.Http.Features.ISessionFeature);
private static readonly Type IHttpMaxRequestBodySizeFeatureType = typeof(global::Microsoft.AspNetCore.Http.Features.IHttpMaxRequestBodySizeFeature);
private static readonly Type IHttpMinRequestBodyDataRateFeatureType = typeof(global::Microsoft.AspNetCore.Server.Kestrel.Core.Features.IHttpMinRequestBodyDataRateFeature);
private static readonly Type IHttpMinResponseDataRateFeatureType = typeof(global::Microsoft.AspNetCore.Server.Kestrel.Core.Features.IHttpMinResponseDataRateFeature);
private static readonly Type IHttpBodyControlFeatureType = typeof(global::Microsoft.AspNetCore.Http.Features.IHttpBodyControlFeature);
private static readonly Type IHttpSendFileFeatureType = typeof(global::Microsoft.AspNetCore.Http.Features.IHttpSendFileFeature);
private object _currentIHttpRequestFeature;
private object _currentIHttpResponseFeature;
private object _currentIHttpRequestIdentifierFeature;
private object _currentIServiceProvidersFeature;
private object _currentIHttpRequestLifetimeFeature;
private object _currentIHttpConnectionFeature;
private object _currentIHttpAuthenticationFeature;
private object _currentIQueryFeature;
private object _currentIFormFeature;
private object _currentIHttpUpgradeFeature;
private object _currentIResponseCookiesFeature;
private object _currentIItemsFeature;
private object _currentITlsConnectionFeature;
private object _currentIHttpWebSocketFeature;
private object _currentISessionFeature;
private object _currentIHttpMaxRequestBodySizeFeature;
private object _currentIHttpMinRequestBodyDataRateFeature;
private object _currentIHttpMinResponseDataRateFeature;
private object _currentIHttpBodyControlFeature;
private object _currentIHttpSendFileFeature;
private void FastReset()
{
_currentIHttpRequestFeature = this;
_currentIHttpResponseFeature = this;
_currentIHttpUpgradeFeature = this;
_currentIHttpRequestIdentifierFeature = this;
_currentIHttpRequestLifetimeFeature = this;
_currentIHttpConnectionFeature = this;
_currentIHttpMaxRequestBodySizeFeature = this;
_currentIHttpMinRequestBodyDataRateFeature = this;
_currentIHttpMinResponseDataRateFeature = this;
_currentIHttpBodyControlFeature = this;
_currentIServiceProvidersFeature = null;
_currentIHttpAuthenticationFeature = null;
_currentIQueryFeature = null;
_currentIFormFeature = null;
_currentIResponseCookiesFeature = null;
_currentIItemsFeature = null;
_currentITlsConnectionFeature = null;
_currentIHttpWebSocketFeature = null;
_currentISessionFeature = null;
_currentIHttpSendFileFeature = null;
}
internal object FastFeatureGet(Type key)
{
if (key == IHttpRequestFeatureType)
{
return _currentIHttpRequestFeature;
}
if (key == IHttpResponseFeatureType)
{
return _currentIHttpResponseFeature;
}
if (key == IHttpRequestIdentifierFeatureType)
{
return _currentIHttpRequestIdentifierFeature;
}
if (key == IServiceProvidersFeatureType)
{
return _currentIServiceProvidersFeature;
}
if (key == IHttpRequestLifetimeFeatureType)
{
return _currentIHttpRequestLifetimeFeature;
}
if (key == IHttpConnectionFeatureType)
{
return _currentIHttpConnectionFeature;
}
if (key == IHttpAuthenticationFeatureType)
{
return _currentIHttpAuthenticationFeature;
}
if (key == IQueryFeatureType)
{
return _currentIQueryFeature;
}
if (key == IFormFeatureType)
{
return _currentIFormFeature;
}
if (key == IHttpUpgradeFeatureType)
{
return _currentIHttpUpgradeFeature;
}
if (key == IResponseCookiesFeatureType)
{
return _currentIResponseCookiesFeature;
}
if (key == IItemsFeatureType)
{
return _currentIItemsFeature;
}
if (key == ITlsConnectionFeatureType)
{
return _currentITlsConnectionFeature;
}
if (key == IHttpWebSocketFeatureType)
{
return _currentIHttpWebSocketFeature;
}
if (key == ISessionFeatureType)
{
return _currentISessionFeature;
}
if (key == IHttpMaxRequestBodySizeFeatureType)
{
return _currentIHttpMaxRequestBodySizeFeature;
}
if (key == IHttpMinRequestBodyDataRateFeatureType)
{
return _currentIHttpMinRequestBodyDataRateFeature;
}
if (key == IHttpMinResponseDataRateFeatureType)
{
return _currentIHttpMinResponseDataRateFeature;
}
if (key == IHttpBodyControlFeatureType)
{
return _currentIHttpBodyControlFeature;
}
if (key == IHttpSendFileFeatureType)
{
return _currentIHttpSendFileFeature;
}
return ExtraFeatureGet(key);
}
internal void FastFeatureSet(Type key, object feature)
{
_featureRevision++;
if (key == IHttpRequestFeatureType)
{
_currentIHttpRequestFeature = feature;
return;
}
if (key == IHttpResponseFeatureType)
{
_currentIHttpResponseFeature = feature;
return;
}
if (key == IHttpRequestIdentifierFeatureType)
{
_currentIHttpRequestIdentifierFeature = feature;
return;
}
if (key == IServiceProvidersFeatureType)
{
_currentIServiceProvidersFeature = feature;
return;
}
if (key == IHttpRequestLifetimeFeatureType)
{
_currentIHttpRequestLifetimeFeature = feature;
return;
}
if (key == IHttpConnectionFeatureType)
{
_currentIHttpConnectionFeature = feature;
return;
}
if (key == IHttpAuthenticationFeatureType)
{
_currentIHttpAuthenticationFeature = feature;
return;
}
if (key == IQueryFeatureType)
{
_currentIQueryFeature = feature;
return;
}
if (key == IFormFeatureType)
{
_currentIFormFeature = feature;
return;
}
if (key == IHttpUpgradeFeatureType)
{
_currentIHttpUpgradeFeature = feature;
return;
}
if (key == IResponseCookiesFeatureType)
{
_currentIResponseCookiesFeature = feature;
return;
}
if (key == IItemsFeatureType)
{
_currentIItemsFeature = feature;
return;
}
if (key == ITlsConnectionFeatureType)
{
_currentITlsConnectionFeature = feature;
return;
}
if (key == IHttpWebSocketFeatureType)
{
_currentIHttpWebSocketFeature = feature;
return;
}
if (key == ISessionFeatureType)
{
_currentISessionFeature = feature;
return;
}
if (key == IHttpMaxRequestBodySizeFeatureType)
{
_currentIHttpMaxRequestBodySizeFeature = feature;
return;
}
if (key == IHttpMinRequestBodyDataRateFeatureType)
{
_currentIHttpMinRequestBodyDataRateFeature = feature;
return;
}
if (key == IHttpMinResponseDataRateFeatureType)
{
_currentIHttpMinResponseDataRateFeature = feature;
return;
}
if (key == IHttpBodyControlFeatureType)
{
_currentIHttpBodyControlFeature = feature;
return;
}
if (key == IHttpSendFileFeatureType)
{
_currentIHttpSendFileFeature = feature;
return;
};
ExtraFeatureSet(key, feature);
}
private IEnumerable<KeyValuePair<Type, object>> FastEnumerable()
{
if (_currentIHttpRequestFeature != null)
{
yield return new KeyValuePair<Type, object>(IHttpRequestFeatureType, _currentIHttpRequestFeature as global::Microsoft.AspNetCore.Http.Features.IHttpRequestFeature);
}
if (_currentIHttpResponseFeature != null)
{
yield return new KeyValuePair<Type, object>(IHttpResponseFeatureType, _currentIHttpResponseFeature as global::Microsoft.AspNetCore.Http.Features.IHttpResponseFeature);
}
if (_currentIHttpRequestIdentifierFeature != null)
{
yield return new KeyValuePair<Type, object>(IHttpRequestIdentifierFeatureType, _currentIHttpRequestIdentifierFeature as global::Microsoft.AspNetCore.Http.Features.IHttpRequestIdentifierFeature);
}
if (_currentIServiceProvidersFeature != null)
{
yield return new KeyValuePair<Type, object>(IServiceProvidersFeatureType, _currentIServiceProvidersFeature as global::Microsoft.AspNetCore.Http.Features.IServiceProvidersFeature);
}
if (_currentIHttpRequestLifetimeFeature != null)
{
yield return new KeyValuePair<Type, object>(IHttpRequestLifetimeFeatureType, _currentIHttpRequestLifetimeFeature as global::Microsoft.AspNetCore.Http.Features.IHttpRequestLifetimeFeature);
}
if (_currentIHttpConnectionFeature != null)
{
yield return new KeyValuePair<Type, object>(IHttpConnectionFeatureType, _currentIHttpConnectionFeature as global::Microsoft.AspNetCore.Http.Features.IHttpConnectionFeature);
}
if (_currentIHttpAuthenticationFeature != null)
{
yield return new KeyValuePair<Type, object>(IHttpAuthenticationFeatureType, _currentIHttpAuthenticationFeature as global::Microsoft.AspNetCore.Http.Features.Authentication.IHttpAuthenticationFeature);
}
if (_currentIQueryFeature != null)
{
yield return new KeyValuePair<Type, object>(IQueryFeatureType, _currentIQueryFeature as global::Microsoft.AspNetCore.Http.Features.IQueryFeature);
}
if (_currentIFormFeature != null)
{
yield return new KeyValuePair<Type, object>(IFormFeatureType, _currentIFormFeature as global::Microsoft.AspNetCore.Http.Features.IFormFeature);
}
if (_currentIHttpUpgradeFeature != null)
{
yield return new KeyValuePair<Type, object>(IHttpUpgradeFeatureType, _currentIHttpUpgradeFeature as global::Microsoft.AspNetCore.Http.Features.IHttpUpgradeFeature);
}
if (_currentIResponseCookiesFeature != null)
{
yield return new KeyValuePair<Type, object>(IResponseCookiesFeatureType, _currentIResponseCookiesFeature as global::Microsoft.AspNetCore.Http.Features.IResponseCookiesFeature);
}
if (_currentIItemsFeature != null)
{
yield return new KeyValuePair<Type, object>(IItemsFeatureType, _currentIItemsFeature as global::Microsoft.AspNetCore.Http.Features.IItemsFeature);
}
if (_currentITlsConnectionFeature != null)
{
yield return new KeyValuePair<Type, object>(ITlsConnectionFeatureType, _currentITlsConnectionFeature as global::Microsoft.AspNetCore.Http.Features.ITlsConnectionFeature);
}
if (_currentIHttpWebSocketFeature != null)
{
yield return new KeyValuePair<Type, object>(IHttpWebSocketFeatureType, _currentIHttpWebSocketFeature as global::Microsoft.AspNetCore.Http.Features.IHttpWebSocketFeature);
}
if (_currentISessionFeature != null)
{
yield return new KeyValuePair<Type, object>(ISessionFeatureType, _currentISessionFeature as global::Microsoft.AspNetCore.Http.Features.ISessionFeature);
}
if (_currentIHttpMaxRequestBodySizeFeature != null)
{
yield return new KeyValuePair<Type, object>(IHttpMaxRequestBodySizeFeatureType, _currentIHttpMaxRequestBodySizeFeature as global::Microsoft.AspNetCore.Http.Features.IHttpMaxRequestBodySizeFeature);
}
if (_currentIHttpMinRequestBodyDataRateFeature != null)
{
yield return new KeyValuePair<Type, object>(IHttpMinRequestBodyDataRateFeatureType, _currentIHttpMinRequestBodyDataRateFeature as global::Microsoft.AspNetCore.Server.Kestrel.Core.Features.IHttpMinRequestBodyDataRateFeature);
}
if (_currentIHttpMinResponseDataRateFeature != null)
{
yield return new KeyValuePair<Type, object>(IHttpMinResponseDataRateFeatureType, _currentIHttpMinResponseDataRateFeature as global::Microsoft.AspNetCore.Server.Kestrel.Core.Features.IHttpMinResponseDataRateFeature);
}
if (_currentIHttpBodyControlFeature != null)
{
yield return new KeyValuePair<Type, object>(IHttpBodyControlFeatureType, _currentIHttpBodyControlFeature as global::Microsoft.AspNetCore.Http.Features.IHttpBodyControlFeature);
}
if (_currentIHttpSendFileFeature != null)
{
yield return new KeyValuePair<Type, object>(IHttpSendFileFeatureType, _currentIHttpSendFileFeature as global::Microsoft.AspNetCore.Http.Features.IHttpSendFileFeature);
}
if (MaybeExtra != null)
{
foreach(var item in MaybeExtra)
{
yield return item;
}
}
}
}
}

View File

@ -1,252 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.IO;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting.Server;
using Microsoft.AspNetCore.Protocols;
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure;
using Microsoft.Extensions.Logging;
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
{
public class Frame<TContext> : Frame
{
private readonly IHttpApplication<TContext> _application;
public Frame(IHttpApplication<TContext> application, FrameContext frameContext)
: base(frameContext)
{
_application = application;
}
/// <summary>
/// Primary loop which consumes socket input, parses it for protocol framing, and invokes the
/// application delegate for as long as the socket is intended to remain open.
/// The resulting Task from this loop is preserved in a field which is used when the server needs
/// to drain and close all currently active connections.
/// </summary>
public override async Task ProcessRequestsAsync()
{
try
{
while (_keepAlive)
{
Reset();
TimeoutControl.SetTimeout(_keepAliveTicks, TimeoutAction.StopProcessingNextRequest);
while (_requestProcessingStatus != RequestProcessingStatus.AppStarted)
{
var result = await Input.ReadAsync();
var examined = result.Buffer.End;
var consumed = result.Buffer.End;
try
{
ParseRequest(result.Buffer, out consumed, out examined);
}
catch (InvalidOperationException)
{
if (_requestProcessingStatus == RequestProcessingStatus.ParsingHeaders)
{
throw BadHttpRequestException.GetException(RequestRejectionReason
.MalformedRequestInvalidHeaders);
}
throw;
}
finally
{
Input.Advance(consumed, examined);
}
if (result.IsCompleted)
{
switch (_requestProcessingStatus)
{
case RequestProcessingStatus.RequestPending:
return;
case RequestProcessingStatus.ParsingRequestLine:
throw BadHttpRequestException.GetException(
RequestRejectionReason.InvalidRequestLine);
case RequestProcessingStatus.ParsingHeaders:
throw BadHttpRequestException.GetException(
RequestRejectionReason.MalformedRequestInvalidHeaders);
}
}
else if (!_keepAlive && _requestProcessingStatus == RequestProcessingStatus.RequestPending)
{
// Stop the request processing loop if the server is shutting down or there was a keep-alive timeout
// and there is no ongoing request.
return;
}
else if (RequestTimedOut)
{
// In this case, there is an ongoing request but the start line/header parsing has timed out, so send
// a 408 response.
throw BadHttpRequestException.GetException(RequestRejectionReason.RequestTimeout);
}
}
EnsureHostHeaderExists();
var messageBody = MessageBody.For(_httpVersion, FrameRequestHeaders, this);
if (!messageBody.RequestKeepAlive)
{
_keepAlive = false;
}
_upgradeAvailable = messageBody.RequestUpgrade;
InitializeStreams(messageBody);
var context = _application.CreateContext(this);
try
{
try
{
KestrelEventSource.Log.RequestStart(this);
await _application.ProcessRequestAsync(context);
if (_requestAborted == 0)
{
VerifyResponseContentLength();
}
}
catch (Exception ex)
{
ReportApplicationError(ex);
if (ex is BadHttpRequestException)
{
throw;
}
}
finally
{
KestrelEventSource.Log.RequestStop(this);
// Trigger OnStarting if it hasn't been called yet and the app hasn't
// already failed. If an OnStarting callback throws we can go through
// our normal error handling in ProduceEnd.
// https://github.com/aspnet/KestrelHttpServer/issues/43
if (!HasResponseStarted && _applicationException == null && _onStarting != null)
{
await FireOnStarting();
}
PauseStreams();
if (_onCompleted != null)
{
await FireOnCompleted();
}
}
// If _requestAbort is set, the connection has already been closed.
if (_requestAborted == 0)
{
if (HasResponseStarted)
{
// If the response has already started, call ProduceEnd() before
// consuming the rest of the request body to prevent
// delaying clients waiting for the chunk terminator:
//
// https://github.com/dotnet/corefx/issues/17330#issuecomment-288248663
//
// ProduceEnd() must be called before _application.DisposeContext(), to ensure
// HttpContext.Response.StatusCode is correctly set when
// IHttpContextFactory.Dispose(HttpContext) is called.
await ProduceEnd();
}
// ForZeroContentLength does not complete the reader nor the writer
if (!messageBody.IsEmpty && _keepAlive)
{
// Finish reading the request body in case the app did not.
TimeoutControl.SetTimeout(Constants.RequestBodyDrainTimeout.Ticks, TimeoutAction.SendTimeoutResponse);
await messageBody.ConsumeAsync();
TimeoutControl.CancelTimeout();
}
if (!HasResponseStarted)
{
await ProduceEnd();
}
}
else if (!HasResponseStarted)
{
// If the request was aborted and no response was sent, there's no
// meaningful status code to log.
StatusCode = 0;
}
}
catch (BadHttpRequestException ex)
{
// Handle BadHttpRequestException thrown during app execution or remaining message body consumption.
// This has to be caught here so StatusCode is set properly before disposing the HttpContext
// (DisposeContext logs StatusCode).
SetBadRequestState(ex);
}
finally
{
_application.DisposeContext(context, _applicationException);
// StopStreams should be called before the end of the "if (!_requestProcessingStopping)" block
// to ensure InitializeStreams has been called.
StopStreams();
if (HasStartedConsumingRequestBody)
{
RequestBodyPipe.Reader.Complete();
// Wait for MessageBody.PumpAsync() to call RequestBodyPipe.Writer.Complete().
await messageBody.StopAsync();
// At this point both the request body pipe reader and writer should be completed.
RequestBodyPipe.Reset();
}
}
}
}
catch (BadHttpRequestException ex)
{
// Handle BadHttpRequestException thrown during request line or header parsing.
// SetBadRequestState logs the error.
SetBadRequestState(ex);
}
catch (ConnectionResetException ex)
{
// Don't log ECONNRESET errors made between requests. Browsers like IE will reset connections regularly.
if (_requestProcessingStatus != RequestProcessingStatus.RequestPending)
{
Log.RequestProcessingError(ConnectionId, ex);
}
}
catch (IOException ex)
{
Log.RequestProcessingError(ConnectionId, ex);
}
catch (Exception ex)
{
Log.LogWarning(0, ex, CoreStrings.RequestProcessingEndError);
}
finally
{
try
{
Input.Complete();
await TryProduceInvalidRequestResponse();
Output.Dispose();
}
catch (Exception ex)
{
Log.LogWarning(0, ex, CoreStrings.ConnectionShutdownError);
}
}
}
}
}

View File

@ -0,0 +1,57 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Server.Kestrel.Core.Features;
using Microsoft.Extensions.Primitives;
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
{
public partial class Http1Connection : IHttpUpgradeFeature
{
bool IHttpUpgradeFeature.IsUpgradableRequest => IsUpgradableRequest;
async Task<Stream> IHttpUpgradeFeature.UpgradeAsync()
{
if (!((IHttpUpgradeFeature)this).IsUpgradableRequest)
{
throw new InvalidOperationException(CoreStrings.CannotUpgradeNonUpgradableRequest);
}
if (IsUpgraded)
{
throw new InvalidOperationException(CoreStrings.UpgradeCannotBeCalledMultipleTimes);
}
if (!ServiceContext.ConnectionManager.UpgradedConnectionCount.TryLockOne())
{
throw new InvalidOperationException(CoreStrings.UpgradedConnectionLimitReached);
}
IsUpgraded = true;
ConnectionFeatures.Get<IDecrementConcurrentConnectionCountFeature>()?.ReleaseConnection();
StatusCode = StatusCodes.Status101SwitchingProtocols;
ReasonPhrase = "Switching Protocols";
ResponseHeaders["Connection"] = "Upgrade";
if (!ResponseHeaders.ContainsKey("Upgrade"))
{
StringValues values;
if (RequestHeaders.TryGetValue("Upgrade", out values))
{
ResponseHeaders["Upgrade"] = values;
}
}
await FlushAsync(default(CancellationToken));
return _streams.Upgrade();
}
}
}

View File

@ -0,0 +1,491 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Diagnostics;
using System.IO.Pipelines;
using System.Text;
using System.Text.Encodings.Web.Utf8;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure;
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
{
public abstract partial class Http1Connection : HttpProtocol
{
private const byte ByteAsterisk = (byte)'*';
private const byte ByteForwardSlash = (byte)'/';
private const string Asterisk = "*";
private readonly Http1ConnectionContext _context;
private readonly IHttpParser<Http1ParsingHandler> _parser;
protected readonly long _keepAliveTicks;
private readonly long _requestHeadersTimeoutTicks;
private volatile bool _requestTimedOut;
private uint _requestCount;
private HttpRequestTarget _requestTargetForm = HttpRequestTarget.Unknown;
private Uri _absoluteRequestTarget;
private int _remainingRequestHeadersBytesAllowed;
public Http1Connection(Http1ConnectionContext context)
: base(context)
{
_context = context;
_parser = ServiceContext.HttpParserFactory(new Http1ParsingHandler(this));
_keepAliveTicks = ServerOptions.Limits.KeepAliveTimeout.Ticks;
_requestHeadersTimeoutTicks = ServerOptions.Limits.RequestHeadersTimeout.Ticks;
Output = new Http1OutputProducer(_context.Application.Input, _context.Transport.Output, _context.ConnectionId, _context.ServiceContext.Log, _context.TimeoutControl);
}
public IPipeReader Input => _context.Transport.Input;
public ITimeoutControl TimeoutControl => _context.TimeoutControl;
public bool RequestTimedOut => _requestTimedOut;
public override bool IsUpgradableRequest => _upgradeAvailable;
/// <summary>
/// Stops the request processing loop between requests.
/// Called on all active connections when the server wants to initiate a shutdown
/// and after a keep-alive timeout.
/// </summary>
public void StopProcessingNextRequest()
{
_keepAlive = false;
Input.CancelPendingRead();
}
public void SendTimeoutResponse()
{
_requestTimedOut = true;
Input.CancelPendingRead();
}
public void ParseRequest(ReadableBuffer buffer, out ReadCursor consumed, out ReadCursor examined)
{
consumed = buffer.Start;
examined = buffer.End;
switch (_requestProcessingStatus)
{
case RequestProcessingStatus.RequestPending:
if (buffer.IsEmpty)
{
break;
}
TimeoutControl.ResetTimeout(_requestHeadersTimeoutTicks, TimeoutAction.SendTimeoutResponse);
_requestProcessingStatus = RequestProcessingStatus.ParsingRequestLine;
goto case RequestProcessingStatus.ParsingRequestLine;
case RequestProcessingStatus.ParsingRequestLine:
if (TakeStartLine(buffer, out consumed, out examined))
{
buffer = buffer.Slice(consumed, buffer.End);
_requestProcessingStatus = RequestProcessingStatus.ParsingHeaders;
goto case RequestProcessingStatus.ParsingHeaders;
}
else
{
break;
}
case RequestProcessingStatus.ParsingHeaders:
if (TakeMessageHeaders(buffer, out consumed, out examined))
{
_requestProcessingStatus = RequestProcessingStatus.AppStarted;
}
break;
}
}
public bool TakeStartLine(ReadableBuffer buffer, out ReadCursor consumed, out ReadCursor examined)
{
var overLength = false;
if (buffer.Length >= ServerOptions.Limits.MaxRequestLineSize)
{
buffer = buffer.Slice(buffer.Start, ServerOptions.Limits.MaxRequestLineSize);
overLength = true;
}
var result = _parser.ParseRequestLine(new Http1ParsingHandler(this), buffer, out consumed, out examined);
if (!result && overLength)
{
ThrowRequestRejected(RequestRejectionReason.RequestLineTooLong);
}
return result;
}
public bool TakeMessageHeaders(ReadableBuffer buffer, out ReadCursor consumed, out ReadCursor examined)
{
// Make sure the buffer is limited
bool overLength = false;
if (buffer.Length >= _remainingRequestHeadersBytesAllowed)
{
buffer = buffer.Slice(buffer.Start, _remainingRequestHeadersBytesAllowed);
// If we sliced it means the current buffer bigger than what we're
// allowed to look at
overLength = true;
}
var result = _parser.ParseHeaders(new Http1ParsingHandler(this), buffer, out consumed, out examined, out var consumedBytes);
_remainingRequestHeadersBytesAllowed -= consumedBytes;
if (!result && overLength)
{
ThrowRequestRejected(RequestRejectionReason.HeadersExceedMaxTotalSize);
}
if (result)
{
TimeoutControl.CancelTimeout();
}
return result;
}
public void OnStartLine(HttpMethod method, HttpVersion version, Span<byte> target, Span<byte> path, Span<byte> query, Span<byte> customMethod, bool pathEncoded)
{
Debug.Assert(target.Length != 0, "Request target must be non-zero length");
var ch = target[0];
if (ch == ByteForwardSlash)
{
// origin-form.
// The most common form of request-target.
// https://tools.ietf.org/html/rfc7230#section-5.3.1
OnOriginFormTarget(method, version, target, path, query, customMethod, pathEncoded);
}
else if (ch == ByteAsterisk && target.Length == 1)
{
OnAsteriskFormTarget(method);
}
else if (target.GetKnownHttpScheme(out var scheme))
{
OnAbsoluteFormTarget(target, query);
}
else
{
// Assume anything else is considered authority form.
// FYI: this should be an edge case. This should only happen when
// a client mistakenly thinks this server is a proxy server.
OnAuthorityFormTarget(method, target);
}
Method = method != HttpMethod.Custom
? HttpUtilities.MethodToString(method) ?? string.Empty
: customMethod.GetAsciiStringNonNullCharacters();
_httpVersion = version;
Debug.Assert(RawTarget != null, "RawTarget was not set");
Debug.Assert(Method != null, "Method was not set");
Debug.Assert(Path != null, "Path was not set");
Debug.Assert(QueryString != null, "QueryString was not set");
Debug.Assert(HttpVersion != null, "HttpVersion was not set");
}
private void OnOriginFormTarget(HttpMethod method, HttpVersion version, Span<byte> target, Span<byte> path, Span<byte> query, Span<byte> customMethod, bool pathEncoded)
{
Debug.Assert(target[0] == ByteForwardSlash, "Should only be called when path starts with /");
_requestTargetForm = HttpRequestTarget.OriginForm;
// URIs are always encoded/escaped to ASCII https://tools.ietf.org/html/rfc3986#page-11
// Multibyte Internationalized Resource Identifiers (IRIs) are first converted to utf8;
// then encoded/escaped to ASCII https://www.ietf.org/rfc/rfc3987.txt "Mapping of IRIs to URIs"
string requestUrlPath = null;
string rawTarget = null;
try
{
// Read raw target before mutating memory.
rawTarget = target.GetAsciiStringNonNullCharacters();
if (pathEncoded)
{
// URI was encoded, unescape and then parse as UTF-8
var pathLength = UrlEncoder.Decode(path, path);
// Removing dot segments must be done after unescaping. From RFC 3986:
//
// URI producing applications should percent-encode data octets that
// correspond to characters in the reserved set unless these characters
// are specifically allowed by the URI scheme to represent data in that
// component. If a reserved character is found in a URI component and
// no delimiting role is known for that character, then it must be
// interpreted as representing the data octet corresponding to that
// character's encoding in US-ASCII.
//
// https://tools.ietf.org/html/rfc3986#section-2.2
pathLength = PathNormalizer.RemoveDotSegments(path.Slice(0, pathLength));
requestUrlPath = GetUtf8String(path.Slice(0, pathLength));
}
else
{
var pathLength = PathNormalizer.RemoveDotSegments(path);
if (path.Length == pathLength && query.Length == 0)
{
// If no decoding was required, no dot segments were removed and
// there is no query, the request path is the same as the raw target
requestUrlPath = rawTarget;
}
else
{
requestUrlPath = path.Slice(0, pathLength).GetAsciiStringNonNullCharacters();
}
}
}
catch (InvalidOperationException)
{
ThrowRequestTargetRejected(target);
}
QueryString = query.GetAsciiStringNonNullCharacters();
RawTarget = rawTarget;
Path = requestUrlPath;
}
private void OnAuthorityFormTarget(HttpMethod method, Span<byte> target)
{
_requestTargetForm = HttpRequestTarget.AuthorityForm;
// This is not complete validation. It is just a quick scan for invalid characters
// but doesn't check that the target fully matches the URI spec.
for (var i = 0; i < target.Length; i++)
{
var ch = target[i];
if (!UriUtilities.IsValidAuthorityCharacter(ch))
{
ThrowRequestTargetRejected(target);
}
}
// The authority-form of request-target is only used for CONNECT
// requests (https://tools.ietf.org/html/rfc7231#section-4.3.6).
if (method != HttpMethod.Connect)
{
ThrowRequestRejected(RequestRejectionReason.ConnectMethodRequired);
}
// When making a CONNECT request to establish a tunnel through one or
// more proxies, a client MUST send only the target URI's authority
// component (excluding any userinfo and its "@" delimiter) as the
// request-target.For example,
//
// CONNECT www.example.com:80 HTTP/1.1
//
// Allowed characters in the 'host + port' section of authority.
// See https://tools.ietf.org/html/rfc3986#section-3.2
RawTarget = target.GetAsciiStringNonNullCharacters();
Path = string.Empty;
QueryString = string.Empty;
}
private void OnAsteriskFormTarget(HttpMethod method)
{
_requestTargetForm = HttpRequestTarget.AsteriskForm;
// The asterisk-form of request-target is only used for a server-wide
// OPTIONS request (https://tools.ietf.org/html/rfc7231#section-4.3.7).
if (method != HttpMethod.Options)
{
ThrowRequestRejected(RequestRejectionReason.OptionsMethodRequired);
}
RawTarget = Asterisk;
Path = string.Empty;
QueryString = string.Empty;
}
private void OnAbsoluteFormTarget(Span<byte> target, Span<byte> query)
{
_requestTargetForm = HttpRequestTarget.AbsoluteForm;
// absolute-form
// https://tools.ietf.org/html/rfc7230#section-5.3.2
// This code should be the edge-case.
// From the spec:
// a server MUST accept the absolute-form in requests, even though
// HTTP/1.1 clients will only send them in requests to proxies.
RawTarget = target.GetAsciiStringNonNullCharacters();
// Validation of absolute URIs is slow, but clients
// should not be sending this form anyways, so perf optimization
// not high priority
if (!Uri.TryCreate(RawTarget, UriKind.Absolute, out var uri))
{
ThrowRequestTargetRejected(target);
}
_absoluteRequestTarget = uri;
Path = uri.LocalPath;
// don't use uri.Query because we need the unescaped version
QueryString = query.GetAsciiStringNonNullCharacters();
}
private unsafe static string GetUtf8String(Span<byte> path)
{
// .NET 451 doesn't have pointer overloads for Encoding.GetString so we
// copy to an array
fixed (byte* pointer = &path.DangerousGetPinnableReference())
{
return Encoding.UTF8.GetString(pointer, path.Length);
}
}
public void OnHeader(Span<byte> name, Span<byte> value)
{
_requestHeadersParsed++;
if (_requestHeadersParsed > ServerOptions.Limits.MaxRequestHeaderCount)
{
ThrowRequestRejected(RequestRejectionReason.TooManyHeaders);
}
var valueString = value.GetAsciiStringNonNullCharacters();
HttpRequestHeaders.Append(name, valueString);
}
protected void EnsureHostHeaderExists()
{
if (_httpVersion == Http.HttpVersion.Http10)
{
return;
}
// https://tools.ietf.org/html/rfc7230#section-5.4
// A server MUST respond with a 400 (Bad Request) status code to any
// HTTP/1.1 request message that lacks a Host header field and to any
// request message that contains more than one Host header field or a
// Host header field with an invalid field-value.
var host = HttpRequestHeaders.HeaderHost;
if (host.Count <= 0)
{
ThrowRequestRejected(RequestRejectionReason.MissingHostHeader);
}
else if (host.Count > 1)
{
ThrowRequestRejected(RequestRejectionReason.MultipleHostHeaders);
}
else if (_requestTargetForm == HttpRequestTarget.AuthorityForm)
{
if (!host.Equals(RawTarget))
{
ThrowRequestRejected(RequestRejectionReason.InvalidHostHeader, host.ToString());
}
}
else if (_requestTargetForm == HttpRequestTarget.AbsoluteForm)
{
// If the target URI includes an authority component, then a
// client MUST send a field - value for Host that is identical to that
// authority component, excluding any userinfo subcomponent and its "@"
// delimiter.
// System.Uri doesn't not tell us if the port was in the original string or not.
// When IsDefaultPort = true, we will allow Host: with or without the default port
var authorityAndPort = _absoluteRequestTarget.Authority + ":" + _absoluteRequestTarget.Port;
if ((host != _absoluteRequestTarget.Authority || !_absoluteRequestTarget.IsDefaultPort)
&& host != authorityAndPort)
{
ThrowRequestRejected(RequestRejectionReason.InvalidHostHeader, host.ToString());
}
}
}
protected override void OnReset()
{
FastFeatureSet(typeof(IHttpUpgradeFeature), this);
_requestTimedOut = false;
_requestTargetForm = HttpRequestTarget.Unknown;
_absoluteRequestTarget = null;
_remainingRequestHeadersBytesAllowed = ServerOptions.Limits.MaxRequestHeadersTotalSize + 2;
_requestCount++;
}
protected override void OnRequestProcessingEnding()
{
Input.Complete();
}
protected override string CreateRequestId()
=> StringUtilities.ConcatAsHexSuffix(ConnectionId, ':', _requestCount);
protected override MessageBody CreateMessageBody()
=> Http1MessageBody.For(_httpVersion, HttpRequestHeaders, this);
protected override async Task<bool> ParseRequestAsync()
{
Reset();
TimeoutControl.SetTimeout(_keepAliveTicks, TimeoutAction.StopProcessingNextRequest);
while (_requestProcessingStatus != RequestProcessingStatus.AppStarted)
{
var result = await Input.ReadAsync();
var examined = result.Buffer.End;
var consumed = result.Buffer.End;
try
{
ParseRequest(result.Buffer, out consumed, out examined);
}
catch (InvalidOperationException)
{
if (_requestProcessingStatus == RequestProcessingStatus.ParsingHeaders)
{
throw BadHttpRequestException.GetException(RequestRejectionReason
.MalformedRequestInvalidHeaders);
}
throw;
}
finally
{
Input.Advance(consumed, examined);
}
if (result.IsCompleted)
{
switch (_requestProcessingStatus)
{
case RequestProcessingStatus.RequestPending:
return false;
case RequestProcessingStatus.ParsingRequestLine:
throw BadHttpRequestException.GetException(
RequestRejectionReason.InvalidRequestLine);
case RequestProcessingStatus.ParsingHeaders:
throw BadHttpRequestException.GetException(
RequestRejectionReason.MalformedRequestInvalidHeaders);
}
}
else if (!_keepAlive && _requestProcessingStatus == RequestProcessingStatus.RequestPending)
{
// Stop the request processing loop if the server is shutting down or there was a keep-alive timeout
// and there is no ongoing request.
return false;
}
else if (RequestTimedOut)
{
// In this case, there is an ongoing request but the start line/header parsing has timed out, so send
// a 408 response.
throw BadHttpRequestException.GetException(RequestRejectionReason.RequestTimeout);
}
}
EnsureHostHeaderExists();
return true;
}
}
}

View File

@ -9,7 +9,7 @@ using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure;
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
{
public class FrameContext
public class Http1ConnectionContext : IHttpProtocolContext
{
public string ConnectionId { get; set; }
public ServiceContext ServiceContext { get; set; }

View File

@ -0,0 +1,27 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting.Server;
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
{
public class Http1Connection<TContext> : Http1Connection
{
private readonly IHttpApplication<TContext> _application;
private TContext _httpContext;
public Http1Connection(IHttpApplication<TContext> application, Http1ConnectionContext context)
: base(context)
{
_application = application;
}
protected override void CreateHttpContext() => _httpContext = _application.CreateContext(this);
protected override void DisposeHttpContext() => _application.DisposeContext(_httpContext, _applicationException);
protected override Task InvokeApplicationAsync() => _application.ProcessRequestAsync(_httpContext);
}
}

View File

@ -0,0 +1,689 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.IO;
using System.IO.Pipelines;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure;
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
{
public abstract class Http1MessageBody : MessageBody
{
private readonly Http1Connection _context;
private bool _send100Continue = true;
private volatile bool _canceled;
private Task _pumpTask;
protected Http1MessageBody(Http1Connection context)
: base(context)
{
_context = context;
}
private async Task PumpAsync()
{
Exception error = null;
try
{
var awaitable = _context.Input.ReadAsync();
if (!awaitable.IsCompleted)
{
TryProduceContinue();
}
TryStartTimingReads();
while (true)
{
var result = await awaitable;
if (_context.RequestTimedOut)
{
_context.ThrowRequestRejected(RequestRejectionReason.RequestTimeout);
}
var readableBuffer = result.Buffer;
var consumed = readableBuffer.Start;
var examined = readableBuffer.End;
try
{
if (_canceled)
{
break;
}
if (!readableBuffer.IsEmpty)
{
var writableBuffer = _context.RequestBodyPipe.Writer.Alloc(1);
bool done;
try
{
done = Read(readableBuffer, writableBuffer, out consumed, out examined);
}
finally
{
writableBuffer.Commit();
}
var writeAwaitable = writableBuffer.FlushAsync();
var backpressure = false;
if (!writeAwaitable.IsCompleted)
{
// Backpressure, stop controlling incoming data rate until data is read.
backpressure = true;
TryPauseTimingReads();
}
await writeAwaitable;
if (backpressure)
{
TryResumeTimingReads();
}
if (done)
{
break;
}
}
else if (result.IsCompleted)
{
_context.ThrowRequestRejected(RequestRejectionReason.UnexpectedEndOfRequestContent);
}
awaitable = _context.Input.ReadAsync();
}
finally
{
_context.Input.Advance(consumed, examined);
}
}
}
catch (Exception ex)
{
error = ex;
}
finally
{
_context.RequestBodyPipe.Writer.Complete(error);
TryStopTimingReads();
}
}
public override Task StopAsync()
{
if (!_context.HasStartedConsumingRequestBody)
{
return Task.CompletedTask;
}
_canceled = true;
_context.Input.CancelPendingRead();
return _pumpTask;
}
protected override async Task OnConsumeAsync()
{
_context.TimeoutControl.SetTimeout(Constants.RequestBodyDrainTimeout.Ticks, TimeoutAction.SendTimeoutResponse);
try
{
ReadResult result;
do
{
result = await _context.RequestBodyPipe.Reader.ReadAsync();
_context.RequestBodyPipe.Reader.Advance(result.Buffer.End);
} while (!result.IsCompleted);
}
finally
{
_context.TimeoutControl.CancelTimeout();
}
}
private void TryProduceContinue()
{
if (_send100Continue)
{
_context.HttpResponseControl.ProduceContinue();
_send100Continue = false;
}
}
protected void Copy(ReadableBuffer readableBuffer, WritableBuffer writableBuffer)
{
_context.TimeoutControl.BytesRead(readableBuffer.Length);
if (readableBuffer.IsSingleSpan)
{
writableBuffer.Write(readableBuffer.First.Span);
}
else
{
foreach (var memory in readableBuffer)
{
writableBuffer.Write(memory.Span);
}
}
}
protected override void OnReadStarted()
{
_pumpTask = PumpAsync();
}
protected virtual bool Read(ReadableBuffer readableBuffer, WritableBuffer writableBuffer, out ReadCursor consumed, out ReadCursor examined)
{
throw new NotImplementedException();
}
private void TryStartTimingReads()
{
if (!RequestUpgrade)
{
Log.RequestBodyStart(_context.ConnectionIdFeature, _context.TraceIdentifier);
_context.TimeoutControl.StartTimingReads();
}
}
private void TryPauseTimingReads()
{
if (!RequestUpgrade)
{
_context.TimeoutControl.PauseTimingReads();
}
}
private void TryResumeTimingReads()
{
if (!RequestUpgrade)
{
_context.TimeoutControl.ResumeTimingReads();
}
}
private void TryStopTimingReads()
{
if (!RequestUpgrade)
{
Log.RequestBodyDone(_context.ConnectionIdFeature, _context.TraceIdentifier);
_context.TimeoutControl.StopTimingReads();
}
}
public static MessageBody For(
HttpVersion httpVersion,
HttpRequestHeaders headers,
Http1Connection context)
{
// see also http://tools.ietf.org/html/rfc2616#section-4.4
var keepAlive = httpVersion != HttpVersion.Http10;
var connection = headers.HeaderConnection;
var upgrade = false;
if (connection.Count > 0)
{
var connectionOptions = HttpHeaders.ParseConnection(connection);
upgrade = (connectionOptions & ConnectionOptions.Upgrade) == ConnectionOptions.Upgrade;
keepAlive = (connectionOptions & ConnectionOptions.KeepAlive) == ConnectionOptions.KeepAlive;
}
var transferEncoding = headers.HeaderTransferEncoding;
if (transferEncoding.Count > 0)
{
var transferCoding = HttpHeaders.GetFinalTransferCoding(headers.HeaderTransferEncoding);
// https://tools.ietf.org/html/rfc7230#section-3.3.3
// If a Transfer-Encoding header field
// is present in a request and the chunked transfer coding is not
// the final encoding, the message body length cannot be determined
// reliably; the server MUST respond with the 400 (Bad Request)
// status code and then close the connection.
if (transferCoding != TransferCoding.Chunked)
{
context.ThrowRequestRejected(RequestRejectionReason.FinalTransferCodingNotChunked, transferEncoding.ToString());
}
if (upgrade)
{
context.ThrowRequestRejected(RequestRejectionReason.UpgradeRequestCannotHavePayload);
}
return new ForChunkedEncoding(keepAlive, context);
}
if (headers.ContentLength.HasValue)
{
var contentLength = headers.ContentLength.Value;
if (contentLength == 0)
{
return keepAlive ? MessageBody.ZeroContentLengthKeepAlive : MessageBody.ZeroContentLengthClose;
}
else if (upgrade)
{
context.ThrowRequestRejected(RequestRejectionReason.UpgradeRequestCannotHavePayload);
}
return new ForContentLength(keepAlive, contentLength, context);
}
// Avoid slowing down most common case
if (!object.ReferenceEquals(context.Method, HttpMethods.Get))
{
// If we got here, request contains no Content-Length or Transfer-Encoding header.
// Reject with 411 Length Required.
if (HttpMethods.IsPost(context.Method) || HttpMethods.IsPut(context.Method))
{
var requestRejectionReason = httpVersion == HttpVersion.Http11 ? RequestRejectionReason.LengthRequired : RequestRejectionReason.LengthRequiredHttp10;
context.ThrowRequestRejected(requestRejectionReason, context.Method);
}
}
if (upgrade)
{
return new ForUpgrade(context);
}
return keepAlive ? MessageBody.ZeroContentLengthKeepAlive : MessageBody.ZeroContentLengthClose;
}
private class ForUpgrade : Http1MessageBody
{
public ForUpgrade(Http1Connection context)
: base(context)
{
RequestUpgrade = true;
}
protected override bool Read(ReadableBuffer readableBuffer, WritableBuffer writableBuffer, out ReadCursor consumed, out ReadCursor examined)
{
Copy(readableBuffer, writableBuffer);
consumed = readableBuffer.End;
examined = readableBuffer.End;
return false;
}
}
private class ForContentLength : Http1MessageBody
{
private readonly long _contentLength;
private long _inputLength;
public ForContentLength(bool keepAlive, long contentLength, Http1Connection context)
: base(context)
{
RequestKeepAlive = keepAlive;
_contentLength = contentLength;
_inputLength = _contentLength;
}
protected override bool Read(ReadableBuffer readableBuffer, WritableBuffer writableBuffer, out ReadCursor consumed, out ReadCursor examined)
{
if (_inputLength == 0)
{
throw new InvalidOperationException("Attempted to read from completed Content-Length request body.");
}
var actual = (int)Math.Min(readableBuffer.Length, _inputLength);
_inputLength -= actual;
consumed = readableBuffer.Move(readableBuffer.Start, actual);
examined = consumed;
Copy(readableBuffer.Slice(0, actual), writableBuffer);
return _inputLength == 0;
}
protected override void OnReadStarting()
{
if (_contentLength > _context.MaxRequestBodySize)
{
_context.ThrowRequestRejected(RequestRejectionReason.RequestBodyTooLarge);
}
}
}
/// <summary>
/// http://tools.ietf.org/html/rfc2616#section-3.6.1
/// </summary>
private class ForChunkedEncoding : Http1MessageBody
{
// byte consts don't have a data type annotation so we pre-cast it
private const byte ByteCR = (byte)'\r';
// "7FFFFFFF\r\n" is the largest chunk size that could be returned as an int.
private const int MaxChunkPrefixBytes = 10;
private long _inputLength;
private long _consumedBytes;
private Mode _mode = Mode.Prefix;
public ForChunkedEncoding(bool keepAlive, Http1Connection context)
: base(context)
{
RequestKeepAlive = keepAlive;
}
protected override bool Read(ReadableBuffer readableBuffer, WritableBuffer writableBuffer, out ReadCursor consumed, out ReadCursor examined)
{
consumed = default(ReadCursor);
examined = default(ReadCursor);
while (_mode < Mode.Trailer)
{
if (_mode == Mode.Prefix)
{
ParseChunkedPrefix(readableBuffer, out consumed, out examined);
if (_mode == Mode.Prefix)
{
return false;
}
readableBuffer = readableBuffer.Slice(consumed);
}
if (_mode == Mode.Extension)
{
ParseExtension(readableBuffer, out consumed, out examined);
if (_mode == Mode.Extension)
{
return false;
}
readableBuffer = readableBuffer.Slice(consumed);
}
if (_mode == Mode.Data)
{
ReadChunkedData(readableBuffer, writableBuffer, out consumed, out examined);
if (_mode == Mode.Data)
{
return false;
}
readableBuffer = readableBuffer.Slice(consumed);
}
if (_mode == Mode.Suffix)
{
ParseChunkedSuffix(readableBuffer, out consumed, out examined);
if (_mode == Mode.Suffix)
{
return false;
}
readableBuffer = readableBuffer.Slice(consumed);
}
}
// Chunks finished, parse trailers
if (_mode == Mode.Trailer)
{
ParseChunkedTrailer(readableBuffer, out consumed, out examined);
if (_mode == Mode.Trailer)
{
return false;
}
readableBuffer = readableBuffer.Slice(consumed);
}
// _consumedBytes aren't tracked for trailer headers, since headers have seperate limits.
if (_mode == Mode.TrailerHeaders)
{
if (_context.TakeMessageHeaders(readableBuffer, out consumed, out examined))
{
_mode = Mode.Complete;
}
}
return _mode == Mode.Complete;
}
private void AddAndCheckConsumedBytes(long consumedBytes)
{
_consumedBytes += consumedBytes;
if (_consumedBytes > _context.MaxRequestBodySize)
{
_context.ThrowRequestRejected(RequestRejectionReason.RequestBodyTooLarge);
}
}
private void ParseChunkedPrefix(ReadableBuffer buffer, out ReadCursor consumed, out ReadCursor examined)
{
consumed = buffer.Start;
examined = buffer.Start;
var reader = new ReadableBufferReader(buffer);
var ch1 = reader.Take();
var ch2 = reader.Take();
if (ch1 == -1 || ch2 == -1)
{
examined = reader.Cursor;
return;
}
var chunkSize = CalculateChunkSize(ch1, 0);
ch1 = ch2;
while (reader.ConsumedBytes < MaxChunkPrefixBytes)
{
if (ch1 == ';')
{
consumed = reader.Cursor;
examined = reader.Cursor;
AddAndCheckConsumedBytes(reader.ConsumedBytes);
_inputLength = chunkSize;
_mode = Mode.Extension;
return;
}
ch2 = reader.Take();
if (ch2 == -1)
{
examined = reader.Cursor;
return;
}
if (ch1 == '\r' && ch2 == '\n')
{
consumed = reader.Cursor;
examined = reader.Cursor;
AddAndCheckConsumedBytes(reader.ConsumedBytes);
_inputLength = chunkSize;
_mode = chunkSize > 0 ? Mode.Data : Mode.Trailer;
return;
}
chunkSize = CalculateChunkSize(ch1, chunkSize);
ch1 = ch2;
}
// At this point, 10 bytes have been consumed which is enough to parse the max value "7FFFFFFF\r\n".
_context.ThrowRequestRejected(RequestRejectionReason.BadChunkSizeData);
}
private void ParseExtension(ReadableBuffer buffer, out ReadCursor consumed, out ReadCursor examined)
{
// Chunk-extensions not currently parsed
// Just drain the data
consumed = buffer.Start;
examined = buffer.Start;
do
{
ReadCursor extensionCursor;
if (ReadCursorOperations.Seek(buffer.Start, buffer.End, out extensionCursor, ByteCR) == -1)
{
// End marker not found yet
consumed = buffer.End;
examined = buffer.End;
AddAndCheckConsumedBytes(buffer.Length);
return;
};
var charsToByteCRExclusive = buffer.Slice(0, extensionCursor).Length;
var sufixBuffer = buffer.Slice(extensionCursor);
if (sufixBuffer.Length < 2)
{
consumed = extensionCursor;
examined = buffer.End;
AddAndCheckConsumedBytes(charsToByteCRExclusive);
return;
}
sufixBuffer = sufixBuffer.Slice(0, 2);
var sufixSpan = sufixBuffer.ToSpan();
if (sufixSpan[1] == '\n')
{
// We consumed the \r\n at the end of the extension, so switch modes.
_mode = _inputLength > 0 ? Mode.Data : Mode.Trailer;
consumed = sufixBuffer.End;
examined = sufixBuffer.End;
AddAndCheckConsumedBytes(charsToByteCRExclusive + 2);
}
else
{
// Don't consume suffixSpan[1] in case it is also a \r.
buffer = buffer.Slice(charsToByteCRExclusive + 1);
consumed = extensionCursor;
AddAndCheckConsumedBytes(charsToByteCRExclusive + 1);
}
} while (_mode == Mode.Extension);
}
private void ReadChunkedData(ReadableBuffer buffer, WritableBuffer writableBuffer, out ReadCursor consumed, out ReadCursor examined)
{
var actual = Math.Min(buffer.Length, _inputLength);
consumed = buffer.Move(buffer.Start, actual);
examined = consumed;
Copy(buffer.Slice(0, actual), writableBuffer);
_inputLength -= actual;
AddAndCheckConsumedBytes(actual);
if (_inputLength == 0)
{
_mode = Mode.Suffix;
}
}
private void ParseChunkedSuffix(ReadableBuffer buffer, out ReadCursor consumed, out ReadCursor examined)
{
consumed = buffer.Start;
examined = buffer.Start;
if (buffer.Length < 2)
{
examined = buffer.End;
return;
}
var suffixBuffer = buffer.Slice(0, 2);
var suffixSpan = suffixBuffer.ToSpan();
if (suffixSpan[0] == '\r' && suffixSpan[1] == '\n')
{
consumed = suffixBuffer.End;
examined = suffixBuffer.End;
AddAndCheckConsumedBytes(2);
_mode = Mode.Prefix;
}
else
{
_context.ThrowRequestRejected(RequestRejectionReason.BadChunkSuffix);
}
}
private void ParseChunkedTrailer(ReadableBuffer buffer, out ReadCursor consumed, out ReadCursor examined)
{
consumed = buffer.Start;
examined = buffer.Start;
if (buffer.Length < 2)
{
examined = buffer.End;
return;
}
var trailerBuffer = buffer.Slice(0, 2);
var trailerSpan = trailerBuffer.ToSpan();
if (trailerSpan[0] == '\r' && trailerSpan[1] == '\n')
{
consumed = trailerBuffer.End;
examined = trailerBuffer.End;
AddAndCheckConsumedBytes(2);
_mode = Mode.Complete;
}
else
{
_mode = Mode.TrailerHeaders;
}
}
private int CalculateChunkSize(int extraHexDigit, int currentParsedSize)
{
try
{
checked
{
if (extraHexDigit >= '0' && extraHexDigit <= '9')
{
return currentParsedSize * 0x10 + (extraHexDigit - '0');
}
else if (extraHexDigit >= 'A' && extraHexDigit <= 'F')
{
return currentParsedSize * 0x10 + (extraHexDigit - ('A' - 10));
}
else if (extraHexDigit >= 'a' && extraHexDigit <= 'f')
{
return currentParsedSize * 0x10 + (extraHexDigit - ('a' - 10));
}
}
}
catch (OverflowException ex)
{
throw new IOException(CoreStrings.BadRequest_BadChunkSizeData, ex);
}
_context.ThrowRequestRejected(RequestRejectionReason.BadChunkSizeData);
return -1; // can't happen, but compiler complains
}
private enum Mode
{
Prefix,
Extension,
Data,
Suffix,
Trailer,
TrailerHeaders,
Complete
};
}
}
}

View File

@ -5,15 +5,20 @@ using System;
using System.Diagnostics;
using System.IO.Pipelines;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure;
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
{
public class OutputProducer : IDisposable
public class Http1OutputProducer : IHttpOutputProducer
{
private static readonly ArraySegment<byte> _emptyData = new ArraySegment<byte>(new byte[0]);
private static readonly ArraySegment<byte> _continueBytes = new ArraySegment<byte>(Encoding.ASCII.GetBytes("HTTP/1.1 100 Continue\r\n\r\n"));
private static readonly byte[] _bytesHttpVersion11 = Encoding.ASCII.GetBytes("HTTP/1.1 ");
private static readonly byte[] _bytesEndHeaders = Encoding.ASCII.GetBytes("\r\n\r\n");
private static readonly ArraySegment<byte> _endChunkedResponseBytes = new ArraySegment<byte>(Encoding.ASCII.GetBytes("0\r\n\r\n"));
private readonly string _connectionId;
private readonly ITimeoutControl _timeoutControl;
@ -34,7 +39,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
private readonly object _flushLock = new object();
private Action _flushCompleted;
public OutputProducer(
public Http1OutputProducer(
IPipeReader outputPipeReader,
IPipeWriter pipeWriter,
string connectionId,
@ -49,14 +54,19 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
_flushCompleted = OnFlushCompleted;
}
public Task WriteAsync(ArraySegment<byte> buffer, bool chunk = false, CancellationToken cancellationToken = default(CancellationToken))
public Task WriteDataAsync(ArraySegment<byte> buffer, CancellationToken cancellationToken = default(CancellationToken))
{
if (cancellationToken.IsCancellationRequested)
{
return Task.FromCanceled(cancellationToken);
}
return WriteAsync(buffer, cancellationToken, chunk);
return WriteAsync(buffer, cancellationToken);
}
public Task WriteStreamSuffixAsync(CancellationToken cancellationToken)
{
return WriteAsync(_endChunkedResponseBytes, cancellationToken);
}
public Task FlushAsync(CancellationToken cancellationToken = default(CancellationToken))
@ -79,6 +89,44 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
}
}
public Task WriteAsync<T>(Action<WritableBuffer, T> callback, T state)
{
lock (_contextLock)
{
if (_completed)
{
return Task.CompletedTask;
}
var buffer = _pipeWriter.Alloc(1);
callback(buffer, state);
buffer.Commit();
}
return FlushAsync();
}
public void WriteResponseHeaders(int statusCode, string reasonPhrase, HttpResponseHeaders responseHeaders)
{
lock (_contextLock)
{
if (_completed)
{
return;
}
var buffer = _pipeWriter.Alloc(1);
var writer = new WritableBufferWriter(buffer);
writer.Write(_bytesHttpVersion11);
var statusBytes = ReasonPhrases.ToStatusBytes(statusCode, reasonPhrase);
writer.Write(statusBytes);
responseHeaders.CopyTo(ref writer);
writer.Write(_bytesEndHeaders);
buffer.Commit();
}
}
public void Dispose()
{
lock (_contextLock)
@ -111,10 +159,14 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
}
}
public Task Write100ContinueAsync(CancellationToken cancellationToken)
{
return WriteAsync(_continueBytes, default(CancellationToken));
}
private Task WriteAsync(
ArraySegment<byte> buffer,
CancellationToken cancellationToken,
bool chunk = false)
CancellationToken cancellationToken)
{
var writableBuffer = default(WritableBuffer);
@ -129,17 +181,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
var writer = new WritableBufferWriter(writableBuffer);
if (buffer.Count > 0)
{
if (chunk)
{
ChunkWriter.WriteBeginChunkBytes(ref writer, buffer.Count);
}
writer.Write(buffer.Array, buffer.Offset, buffer.Count);
if (chunk)
{
ChunkWriter.WriteEndChunkBytes(ref writer);
}
}
writableBuffer.Commit();

View File

@ -5,19 +5,19 @@ using System;
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
{
public struct FrameAdapter : IHttpRequestLineHandler, IHttpHeadersHandler
public struct Http1ParsingHandler : IHttpRequestLineHandler, IHttpHeadersHandler
{
public Frame Frame;
public Http1Connection Connection;
public FrameAdapter(Frame frame)
public Http1ParsingHandler(Http1Connection connection)
{
Frame = frame;
Connection = connection;
}
public void OnHeader(Span<byte> name, Span<byte> value)
=> Frame.OnHeader(name, value);
=> Connection.OnHeader(name, value);
public void OnStartLine(HttpMethod method, HttpVersion version, Span<byte> target, Span<byte> path, Span<byte> query, Span<byte> customMethod, bool pathEncoded)
=> Frame.OnStartLine(method, version, target, path, query, customMethod, pathEncoded);
=> Connection.OnStartLine(method, version, target, path, query, customMethod, pathEncoded);
}
}

View File

@ -11,7 +11,7 @@ using Microsoft.Net.Http.Headers;
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
{
public partial class FrameRequestHeaders
public partial class HttpRequestHeaders
{
private long _bits = 0;
@ -2655,7 +2655,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
_contentLength = null;
var tempBits = _bits;
_bits = 0;
if(FrameHeaders.BitCount(tempBits) > 12)
if(HttpHeaders.BitCount(tempBits) > 12)
{
_headers = default(HeaderReferences);
return;
@ -4784,7 +4784,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
}
}
public partial class FrameResponseHeaders
public partial class HttpResponseHeaders
{
private static byte[] _headerBytes = new byte[]
{
@ -7059,7 +7059,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
_contentLength = null;
var tempBits = _bits;
_bits = 0;
if(FrameHeaders.BitCount(tempBits) > 12)
if(HttpHeaders.BitCount(tempBits) > 12)
{
_headers = default(HeaderReferences);
return;

View File

@ -11,7 +11,7 @@ using Microsoft.Extensions.Primitives;
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
{
public abstract class FrameHeaders : IHeaderDictionary
public abstract class HttpHeaders : IHeaderDictionary
{
protected long? _contentLength;
protected bool _isReadOnly;

View File

@ -15,21 +15,20 @@ using Microsoft.Extensions.Primitives;
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
{
public partial class Frame : IFeatureCollection,
IHttpRequestFeature,
IHttpResponseFeature,
IHttpUpgradeFeature,
IHttpConnectionFeature,
IHttpRequestLifetimeFeature,
IHttpRequestIdentifierFeature,
IHttpBodyControlFeature,
IHttpMaxRequestBodySizeFeature,
IHttpMinRequestBodyDataRateFeature,
IHttpMinResponseDataRateFeature
public partial class HttpProtocol : IFeatureCollection,
IHttpRequestFeature,
IHttpResponseFeature,
IHttpConnectionFeature,
IHttpRequestLifetimeFeature,
IHttpRequestIdentifierFeature,
IHttpBodyControlFeature,
IHttpMaxRequestBodySizeFeature,
IHttpMinRequestBodyDataRateFeature,
IHttpMinResponseDataRateFeature
{
// NOTE: When feature interfaces are added to or removed from this Frame class implementation,
// NOTE: When feature interfaces are added to or removed from this HttpProtocol class implementation,
// then the list of `implementedFeatures` in the generated code project MUST also be updated.
// See also: tools/Microsoft.AspNetCore.Server.Kestrel.GeneratedCode/FrameFeatureCollection.cs
// See also: tools/Microsoft.AspNetCore.Server.Kestrel.GeneratedCode/HttpProtocolFeatureCollection.cs
private int _featureRevision;
@ -77,8 +76,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
MaybeExtra.Add(new KeyValuePair<Type, object>(key, value));
}
public IFrameControl FrameControl { get; set; }
string IHttpRequestFeature.Protocol
{
get => HttpVersion;
@ -165,8 +162,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
bool IHttpResponseFeature.HasStarted => HasResponseStarted;
bool IHttpUpgradeFeature.IsUpgradableRequest => _upgradeAvailable;
bool IFeatureCollection.IsReadOnly => false;
int IFeatureCollection.Revision => _featureRevision;
@ -213,7 +208,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
set => AllowSynchronousIO = value;
}
bool IHttpMaxRequestBodySizeFeature.IsReadOnly => HasStartedConsumingRequestBody || _wasUpgraded;
bool IHttpMaxRequestBodySizeFeature.IsReadOnly => HasStartedConsumingRequestBody || IsUpgraded;
long? IHttpMaxRequestBodySizeFeature.MaxRequestBodySize
{
@ -224,7 +219,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
{
throw new InvalidOperationException(CoreStrings.MaxRequestBodySizeCannotBeModifiedAfterRead);
}
if (_wasUpgraded)
if (IsUpgraded)
{
throw new InvalidOperationException(CoreStrings.MaxRequestBodySizeCannotBeModifiedForUpgradedRequests);
}
@ -275,44 +270,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
OnCompleted(callback, state);
}
async Task<Stream> IHttpUpgradeFeature.UpgradeAsync()
{
if (!((IHttpUpgradeFeature)this).IsUpgradableRequest)
{
throw new InvalidOperationException(CoreStrings.CannotUpgradeNonUpgradableRequest);
}
if (_wasUpgraded)
{
throw new InvalidOperationException(CoreStrings.UpgradeCannotBeCalledMultipleTimes);
}
if (!ServiceContext.ConnectionManager.UpgradedConnectionCount.TryLockOne())
{
throw new InvalidOperationException(CoreStrings.UpgradedConnectionLimitReached);
}
_wasUpgraded = true;
ConnectionFeatures.Get<IDecrementConcurrentConnectionCountFeature>()?.ReleaseConnection();
StatusCode = StatusCodes.Status101SwitchingProtocols;
ReasonPhrase = "Switching Protocols";
ResponseHeaders["Connection"] = "Upgrade";
if (!ResponseHeaders.ContainsKey("Upgrade"))
{
StringValues values;
if (RequestHeaders.TryGetValue("Upgrade", out values))
{
ResponseHeaders["Upgrade"] = values;
}
}
await FlushAsync(default(CancellationToken));
return _frameStreams.Upgrade();
}
IEnumerator<KeyValuePair<Type, object>> IEnumerable<KeyValuePair<Type, object>>.GetEnumerator() => FastEnumerable().GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => FastEnumerable().GetEnumerator();

View File

@ -4,9 +4,9 @@
using System;
using System.Collections.Generic;
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
{
public partial class Http2Stream
public partial class HttpProtocol
{
private static readonly Type IHttpRequestFeatureType = typeof(global::Microsoft.AspNetCore.Http.Features.IHttpRequestFeature);
private static readonly Type IHttpResponseFeatureType = typeof(global::Microsoft.AspNetCore.Http.Features.IHttpResponseFeature);
@ -18,6 +18,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
private static readonly Type IQueryFeatureType = typeof(global::Microsoft.AspNetCore.Http.Features.IQueryFeature);
private static readonly Type IFormFeatureType = typeof(global::Microsoft.AspNetCore.Http.Features.IFormFeature);
private static readonly Type IHttpUpgradeFeatureType = typeof(global::Microsoft.AspNetCore.Http.Features.IHttpUpgradeFeature);
private static readonly Type IHttp2StreamIdFeatureType = typeof(global::Microsoft.AspNetCore.Server.Kestrel.Core.Features.IHttp2StreamIdFeature);
private static readonly Type IResponseCookiesFeatureType = typeof(global::Microsoft.AspNetCore.Http.Features.IResponseCookiesFeature);
private static readonly Type IItemsFeatureType = typeof(global::Microsoft.AspNetCore.Http.Features.IItemsFeature);
private static readonly Type ITlsConnectionFeatureType = typeof(global::Microsoft.AspNetCore.Http.Features.ITlsConnectionFeature);
@ -28,7 +29,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
private static readonly Type IHttpMinResponseDataRateFeatureType = typeof(global::Microsoft.AspNetCore.Server.Kestrel.Core.Features.IHttpMinResponseDataRateFeature);
private static readonly Type IHttpBodyControlFeatureType = typeof(global::Microsoft.AspNetCore.Http.Features.IHttpBodyControlFeature);
private static readonly Type IHttpSendFileFeatureType = typeof(global::Microsoft.AspNetCore.Http.Features.IHttpSendFileFeature);
private static readonly Type IHttp2StreamIdFeatureType = typeof(global::Microsoft.AspNetCore.Server.Kestrel.Core.Features.IHttp2StreamIdFeature);
private object _currentIHttpRequestFeature;
private object _currentIHttpResponseFeature;
@ -40,6 +40,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
private object _currentIQueryFeature;
private object _currentIFormFeature;
private object _currentIHttpUpgradeFeature;
private object _currentIHttp2StreamIdFeature;
private object _currentIResponseCookiesFeature;
private object _currentIItemsFeature;
private object _currentITlsConnectionFeature;
@ -50,13 +51,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
private object _currentIHttpMinResponseDataRateFeature;
private object _currentIHttpBodyControlFeature;
private object _currentIHttpSendFileFeature;
private object _currentIHttp2StreamIdFeature;
private void FastReset()
{
_currentIHttpRequestFeature = this;
_currentIHttpResponseFeature = this;
_currentIHttpUpgradeFeature = this;
_currentIHttpRequestIdentifierFeature = this;
_currentIHttpRequestLifetimeFeature = this;
_currentIHttpConnectionFeature = this;
@ -64,12 +63,13 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
_currentIHttpMinRequestBodyDataRateFeature = this;
_currentIHttpMinResponseDataRateFeature = this;
_currentIHttpBodyControlFeature = this;
_currentIHttp2StreamIdFeature = this;
_currentIServiceProvidersFeature = null;
_currentIHttpAuthenticationFeature = null;
_currentIQueryFeature = null;
_currentIFormFeature = null;
_currentIHttpUpgradeFeature = null;
_currentIHttp2StreamIdFeature = null;
_currentIResponseCookiesFeature = null;
_currentIItemsFeature = null;
_currentITlsConnectionFeature = null;
@ -120,6 +120,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
{
return _currentIHttpUpgradeFeature;
}
if (key == IHttp2StreamIdFeatureType)
{
return _currentIHttp2StreamIdFeature;
}
if (key == IResponseCookiesFeatureType)
{
return _currentIResponseCookiesFeature;
@ -160,14 +164,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
{
return _currentIHttpSendFileFeature;
}
if (key == IHttp2StreamIdFeatureType)
{
return _currentIHttp2StreamIdFeature;
}
return ExtraFeatureGet(key);
}
internal void FastFeatureSet(Type key, object feature)
protected void FastFeatureSet(Type key, object feature)
{
_featureRevision++;
@ -221,6 +221,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
_currentIHttpUpgradeFeature = feature;
return;
}
if (key == IHttp2StreamIdFeatureType)
{
_currentIHttp2StreamIdFeature = feature;
return;
}
if (key == IResponseCookiesFeatureType)
{
_currentIResponseCookiesFeature = feature;
@ -270,11 +275,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
{
_currentIHttpSendFileFeature = feature;
return;
}
if (key == IHttp2StreamIdFeatureType)
{
_currentIHttp2StreamIdFeature = feature;
return;
};
ExtraFeatureSet(key, feature);
}
@ -321,6 +321,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
{
yield return new KeyValuePair<Type, object>(IHttpUpgradeFeatureType, _currentIHttpUpgradeFeature as global::Microsoft.AspNetCore.Http.Features.IHttpUpgradeFeature);
}
if (_currentIHttp2StreamIdFeature != null)
{
yield return new KeyValuePair<Type, object>(IHttp2StreamIdFeatureType, _currentIHttp2StreamIdFeature as global::Microsoft.AspNetCore.Server.Kestrel.Core.Features.IHttp2StreamIdFeature);
}
if (_currentIResponseCookiesFeature != null)
{
yield return new KeyValuePair<Type, object>(IResponseCookiesFeatureType, _currentIResponseCookiesFeature as global::Microsoft.AspNetCore.Http.Features.IResponseCookiesFeature);
@ -361,10 +365,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
{
yield return new KeyValuePair<Type, object>(IHttpSendFileFeatureType, _currentIHttpSendFileFeature as global::Microsoft.AspNetCore.Http.Features.IHttpSendFileFeature);
}
if (_currentIHttp2StreamIdFeature != null)
{
yield return new KeyValuePair<Type, object>(IHttp2StreamIdFeatureType, _currentIHttp2StreamIdFeature as global::Microsoft.AspNetCore.Server.Kestrel.Core.Features.IHttp2StreamIdFeature);
}
if (MaybeExtra != null)
{

View File

@ -11,7 +11,7 @@ using Microsoft.Net.Http.Headers;
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
{
public partial class FrameRequestHeaders : FrameHeaders
public partial class HttpRequestHeaders : HttpHeaders
{
private static long ParseContentLength(string value)
{
@ -77,14 +77,14 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
public partial struct Enumerator : IEnumerator<KeyValuePair<string, StringValues>>
{
private readonly FrameRequestHeaders _collection;
private readonly HttpRequestHeaders _collection;
private readonly long _bits;
private int _state;
private KeyValuePair<string, StringValues> _current;
private readonly bool _hasUnknown;
private Dictionary<string, StringValues>.Enumerator _unknownEnumerator;
internal Enumerator(FrameRequestHeaders collection)
internal Enumerator(HttpRequestHeaders collection)
{
_collection = collection;
_bits = collection._bits;

View File

@ -12,17 +12,17 @@ using Microsoft.AspNetCore.Protocols;
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
{
internal class FrameRequestStream : ReadOnlyStream
internal class HttpRequestStream : ReadOnlyStream
{
private readonly IHttpBodyControlFeature _bodyControl;
private IMessageBody _body;
private FrameStreamState _state;
private MessageBody _body;
private HttpStreamState _state;
private Exception _error;
public FrameRequestStream(IHttpBodyControlFeature bodyControl)
public HttpRequestStream(IHttpBodyControlFeature bodyControl)
{
_bodyControl = bodyControl;
_state = FrameStreamState.Closed;
_state = HttpStreamState.Closed;
}
public override bool CanSeek => false;
@ -159,26 +159,26 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
}
}
public void StartAcceptingReads(IMessageBody body)
public void StartAcceptingReads(MessageBody body)
{
// Only start if not aborted
if (_state == FrameStreamState.Closed)
if (_state == HttpStreamState.Closed)
{
_state = FrameStreamState.Open;
_state = HttpStreamState.Open;
_body = body;
}
}
public void PauseAcceptingReads()
{
_state = FrameStreamState.Closed;
_state = HttpStreamState.Closed;
}
public void StopAcceptingReads()
{
// Can't use dispose (or close) as can be disposed too early by user code
// As exampled in EngineTests.ZeroContentLengthNotSetAutomaticallyForCertainStatusCodes
_state = FrameStreamState.Closed;
_state = HttpStreamState.Closed;
_body = null;
}
@ -187,9 +187,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
// We don't want to throw an ODE until the app func actually completes.
// If the request is aborted, we throw a TaskCanceledException instead,
// unless error is not null, in which case we throw it.
if (_state != FrameStreamState.Closed)
if (_state != HttpStreamState.Closed)
{
_state = FrameStreamState.Aborted;
_state = HttpStreamState.Aborted;
_error = error;
}
}
@ -198,15 +198,15 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
{
switch (_state)
{
case FrameStreamState.Open:
case HttpStreamState.Open:
if (cancellationToken.IsCancellationRequested)
{
return Task.FromCanceled<int>(cancellationToken);
}
break;
case FrameStreamState.Closed:
throw new ObjectDisposedException(nameof(FrameRequestStream));
case FrameStreamState.Aborted:
case HttpStreamState.Closed:
throw new ObjectDisposedException(nameof(HttpRequestStream));
case HttpStreamState.Aborted:
return _error != null ?
Task.FromException<int>(_error) :
Task.FromCanceled<int>(new CancellationToken(true));

View File

@ -0,0 +1,15 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
{
public enum HttpRequestTarget
{
Unknown = -1,
// origin-form is the most common
OriginForm,
AbsoluteForm,
AuthorityForm,
AsteriskForm
}
}

View File

@ -11,7 +11,7 @@ using Microsoft.Net.Http.Headers;
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
{
public partial class FrameResponseHeaders : FrameHeaders
public partial class HttpResponseHeaders : HttpHeaders
{
private static readonly byte[] _CrLf = new[] { (byte)'\r', (byte)'\n' };
private static readonly byte[] _colonSpace = new[] { (byte)':', (byte)' ' };
@ -80,14 +80,14 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
public partial struct Enumerator : IEnumerator<KeyValuePair<string, StringValues>>
{
private readonly FrameResponseHeaders _collection;
private readonly HttpResponseHeaders _collection;
private readonly long _bits;
private int _state;
private KeyValuePair<string, StringValues> _current;
private readonly bool _hasUnknown;
private Dictionary<string, StringValues>.Enumerator _unknownEnumerator;
internal Enumerator(FrameResponseHeaders collection)
internal Enumerator(HttpResponseHeaders collection)
{
_collection = collection;
_bits = collection._bits;

View File

@ -10,17 +10,17 @@ using Microsoft.AspNetCore.Http.Features;
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
{
internal class FrameResponseStream : WriteOnlyStream
internal class HttpResponseStream : WriteOnlyStream
{
private readonly IHttpBodyControlFeature _bodyControl;
private readonly IFrameControl _frameControl;
private FrameStreamState _state;
private readonly IHttpResponseControl _httpResponseControl;
private HttpStreamState _state;
public FrameResponseStream(IHttpBodyControlFeature bodyControl, IFrameControl frameControl)
public HttpResponseStream(IHttpBodyControlFeature bodyControl, IHttpResponseControl httpResponseControl)
{
_bodyControl = bodyControl;
_frameControl = frameControl;
_state = FrameStreamState.Closed;
_httpResponseControl = httpResponseControl;
_state = HttpStreamState.Closed;
}
public override bool CanSeek => false;
@ -44,7 +44,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
var task = ValidateState(cancellationToken);
if (task == null)
{
return _frameControl.FlushAsync(cancellationToken);
return _httpResponseControl.FlushAsync(cancellationToken);
}
return task;
}
@ -112,7 +112,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
var task = ValidateState(cancellationToken);
if (task == null)
{
return _frameControl.WriteAsync(new ArraySegment<byte>(buffer, offset, count), cancellationToken);
return _httpResponseControl.WriteAsync(new ArraySegment<byte>(buffer, offset, count), cancellationToken);
}
return task;
}
@ -120,30 +120,30 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
public void StartAcceptingWrites()
{
// Only start if not aborted
if (_state == FrameStreamState.Closed)
if (_state == HttpStreamState.Closed)
{
_state = FrameStreamState.Open;
_state = HttpStreamState.Open;
}
}
public void PauseAcceptingWrites()
{
_state = FrameStreamState.Closed;
_state = HttpStreamState.Closed;
}
public void StopAcceptingWrites()
{
// Can't use dispose (or close) as can be disposed too early by user code
// As exampled in EngineTests.ZeroContentLengthNotSetAutomaticallyForCertainStatusCodes
_state = FrameStreamState.Closed;
_state = HttpStreamState.Closed;
}
public void Abort()
{
// We don't want to throw an ODE until the app func actually completes.
if (_state != FrameStreamState.Closed)
if (_state != HttpStreamState.Closed)
{
_state = FrameStreamState.Aborted;
_state = HttpStreamState.Aborted;
}
}
@ -151,15 +151,15 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
{
switch (_state)
{
case FrameStreamState.Open:
case HttpStreamState.Open:
if (cancellationToken.IsCancellationRequested)
{
return Task.FromCanceled(cancellationToken);
}
break;
case FrameStreamState.Closed:
throw new ObjectDisposedException(nameof(FrameResponseStream));
case FrameStreamState.Aborted:
case HttpStreamState.Closed:
throw new ObjectDisposedException(nameof(HttpResponseStream));
case HttpStreamState.Aborted:
if (cancellationToken.IsCancellationRequested)
{
// Aborted state only throws on write if cancellationToken requests it

View File

@ -3,7 +3,7 @@
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
{
enum FrameStreamState
enum HttpStreamState
{
Open,
Closed,

View File

@ -8,12 +8,12 @@ using System.Threading.Tasks;
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
{
internal class FrameDuplexStream : Stream
internal class HttpUpgradeStream : Stream
{
private readonly Stream _requestStream;
private readonly Stream _responseStream;
public FrameDuplexStream(Stream requestStream, Stream responseStream)
public HttpUpgradeStream(Stream requestStream, Stream responseStream)
{
_requestStream = requestStream;
_responseStream = responseStream;

View File

@ -7,6 +7,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
{
Unknown = -1,
Http10 = 0,
Http11 = 1
Http11 = 1,
Http2
}
}

View File

@ -0,0 +1,21 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.IO.Pipelines;
using System.Threading;
using System.Threading.Tasks;
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
{
public interface IHttpOutputProducer : IDisposable
{
void Abort(Exception error);
Task WriteAsync<T>(Action<WritableBuffer, T> callback, T state);
Task FlushAsync(CancellationToken cancellationToken);
Task Write100ContinueAsync(CancellationToken cancellationToken);
void WriteResponseHeaders(int statusCode, string ReasonPhrase, HttpResponseHeaders responseHeaders);
Task WriteDataAsync(ArraySegment<byte> data, CancellationToken cancellationToken);
Task WriteStreamSuffixAsync(CancellationToken cancellationToken);
}
}

View File

@ -0,0 +1,19 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.IO.Pipelines;
using System.Net;
using Microsoft.AspNetCore.Http.Features;
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
{
public interface IHttpProtocolContext
{
string ConnectionId { get; set; }
ServiceContext ServiceContext { get; set; }
IFeatureCollection ConnectionFeatures { get; set; }
PipeFactory PipeFactory { get; set; }
IPEndPoint RemoteEndPoint { get; set; }
IPEndPoint LocalEndPoint { get; set; }
}
}

View File

@ -7,7 +7,7 @@ using System.Threading.Tasks;
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
{
public interface IFrameControl
public interface IHttpResponseControl
{
void ProduceContinue();
Task WriteAsync(ArraySegment<byte> data, CancellationToken cancellationToken);

View File

@ -1,17 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
{
public interface IMessageBody
{
Task<int> ReadAsync(ArraySegment<byte> buffer, CancellationToken cancellationToken = default(CancellationToken));
Task CopyToAsync(Stream destination, CancellationToken cancellationToken = default(CancellationToken));
}
}

View File

@ -11,129 +11,29 @@ using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure;
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
{
public abstract class MessageBody : IMessageBody
public abstract class MessageBody
{
private static readonly MessageBody _zeroContentLengthClose = new ForZeroContentLength(keepAlive: false);
private static readonly MessageBody _zeroContentLengthKeepAlive = new ForZeroContentLength(keepAlive: true);
// For testing
internal static MessageBody ZeroContentLengthKeepAlive => _zeroContentLengthKeepAlive;
private readonly HttpProtocol _context;
private readonly Frame _context;
private bool _send100Continue = true;
private volatile bool _canceled;
private Task _pumpTask;
protected MessageBody(Frame context)
protected MessageBody(HttpProtocol context)
{
_context = context;
}
public static MessageBody ZeroContentLengthClose => _zeroContentLengthClose;
public static MessageBody ZeroContentLengthKeepAlive => _zeroContentLengthKeepAlive;
public bool RequestKeepAlive { get; protected set; }
public bool RequestUpgrade { get; protected set; }
public virtual bool IsEmpty => false;
private IKestrelTrace Log => _context.ServiceContext.Log;
private async Task PumpAsync()
{
Exception error = null;
try
{
var awaitable = _context.Input.ReadAsync();
if (!awaitable.IsCompleted)
{
TryProduceContinue();
}
TryStartTimingReads();
while (true)
{
var result = await awaitable;
if (_context.RequestTimedOut)
{
_context.ThrowRequestRejected(RequestRejectionReason.RequestTimeout);
}
var readableBuffer = result.Buffer;
var consumed = readableBuffer.Start;
var examined = readableBuffer.End;
try
{
if (_canceled)
{
break;
}
if (!readableBuffer.IsEmpty)
{
var writableBuffer = _context.RequestBodyPipe.Writer.Alloc(1);
bool done;
try
{
done = Read(readableBuffer, writableBuffer, out consumed, out examined);
}
finally
{
writableBuffer.Commit();
}
var writeAwaitable = writableBuffer.FlushAsync();
var backpressure = false;
if (!writeAwaitable.IsCompleted)
{
// Backpressure, stop controlling incoming data rate until data is read.
backpressure = true;
TryPauseTimingReads();
}
await writeAwaitable;
if (backpressure)
{
TryResumeTimingReads();
}
if (done)
{
break;
}
}
else if (result.IsCompleted)
{
_context.ThrowRequestRejected(RequestRejectionReason.UnexpectedEndOfRequestContent);
}
awaitable = _context.Input.ReadAsync();
}
finally
{
_context.Input.Advance(consumed, examined);
}
}
}
catch (Exception ex)
{
error = ex;
}
finally
{
_context.RequestBodyPipe.Writer.Complete(error);
TryStopTimingReads();
}
}
protected IKestrelTrace Log => _context.ServiceContext.Log;
public virtual async Task<int> ReadAsync(ArraySegment<byte> buffer, CancellationToken cancellationToken = default(CancellationToken))
{
@ -200,202 +100,33 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
}
}
public virtual async Task ConsumeAsync(CancellationToken cancellationToken = default(CancellationToken))
public virtual Task ConsumeAsync()
{
TryInit();
ReadResult result;
do
{
result = await _context.RequestBodyPipe.Reader.ReadAsync();
_context.RequestBodyPipe.Reader.Advance(result.Buffer.End);
} while (!result.IsCompleted);
return OnConsumeAsync();
}
public virtual Task StopAsync()
{
if (!_context.HasStartedConsumingRequestBody)
{
return Task.CompletedTask;
}
protected abstract Task OnConsumeAsync();
_canceled = true;
_context.Input.CancelPendingRead();
return _pumpTask;
}
protected void Copy(ReadableBuffer readableBuffer, WritableBuffer writableBuffer)
{
_context.TimeoutControl.BytesRead(readableBuffer.Length);
if (readableBuffer.IsSingleSpan)
{
writableBuffer.Write(readableBuffer.First.Span);
}
else
{
foreach (var memory in readableBuffer)
{
writableBuffer.Write(memory.Span);
}
}
}
private void TryProduceContinue()
{
if (_send100Continue)
{
_context.FrameControl.ProduceContinue();
_send100Continue = false;
}
}
public abstract Task StopAsync();
private void TryInit()
{
if (!_context.HasStartedConsumingRequestBody)
{
OnReadStart();
OnReadStarting();
_context.HasStartedConsumingRequestBody = true;
_pumpTask = PumpAsync();
OnReadStarted();
}
}
protected virtual bool Read(ReadableBuffer readableBuffer, WritableBuffer writableBuffer, out ReadCursor consumed, out ReadCursor examined)
{
throw new NotImplementedException();
}
protected virtual void OnReadStart()
protected virtual void OnReadStarting()
{
}
private void TryStartTimingReads()
protected virtual void OnReadStarted()
{
if (!RequestUpgrade)
{
Log.RequestBodyStart(_context.ConnectionIdFeature, _context.TraceIdentifier);
_context.TimeoutControl.StartTimingReads();
}
}
private void TryPauseTimingReads()
{
if (!RequestUpgrade)
{
_context.TimeoutControl.PauseTimingReads();
}
}
private void TryResumeTimingReads()
{
if (!RequestUpgrade)
{
_context.TimeoutControl.ResumeTimingReads();
}
}
private void TryStopTimingReads()
{
if (!RequestUpgrade)
{
Log.RequestBodyDone(_context.ConnectionIdFeature, _context.TraceIdentifier);
_context.TimeoutControl.StopTimingReads();
}
}
public static MessageBody For(
HttpVersion httpVersion,
FrameRequestHeaders headers,
Frame context)
{
// see also http://tools.ietf.org/html/rfc2616#section-4.4
var keepAlive = httpVersion != HttpVersion.Http10;
var connection = headers.HeaderConnection;
var upgrade = false;
if (connection.Count > 0)
{
var connectionOptions = FrameHeaders.ParseConnection(connection);
upgrade = (connectionOptions & ConnectionOptions.Upgrade) == ConnectionOptions.Upgrade;
keepAlive = (connectionOptions & ConnectionOptions.KeepAlive) == ConnectionOptions.KeepAlive;
}
var transferEncoding = headers.HeaderTransferEncoding;
if (transferEncoding.Count > 0)
{
var transferCoding = FrameHeaders.GetFinalTransferCoding(headers.HeaderTransferEncoding);
// https://tools.ietf.org/html/rfc7230#section-3.3.3
// If a Transfer-Encoding header field
// is present in a request and the chunked transfer coding is not
// the final encoding, the message body length cannot be determined
// reliably; the server MUST respond with the 400 (Bad Request)
// status code and then close the connection.
if (transferCoding != TransferCoding.Chunked)
{
context.ThrowRequestRejected(RequestRejectionReason.FinalTransferCodingNotChunked, transferEncoding.ToString());
}
if (upgrade)
{
context.ThrowRequestRejected(RequestRejectionReason.UpgradeRequestCannotHavePayload);
}
return new ForChunkedEncoding(keepAlive, context);
}
if (headers.ContentLength.HasValue)
{
var contentLength = headers.ContentLength.Value;
if (contentLength == 0)
{
return keepAlive ? _zeroContentLengthKeepAlive : _zeroContentLengthClose;
}
else if (upgrade)
{
context.ThrowRequestRejected(RequestRejectionReason.UpgradeRequestCannotHavePayload);
}
return new ForContentLength(keepAlive, contentLength, context);
}
// Avoid slowing down most common case
if (!object.ReferenceEquals(context.Method, HttpMethods.Get))
{
// If we got here, request contains no Content-Length or Transfer-Encoding header.
// Reject with 411 Length Required.
if (HttpMethods.IsPost(context.Method) || HttpMethods.IsPut(context.Method))
{
var requestRejectionReason = httpVersion == HttpVersion.Http11 ? RequestRejectionReason.LengthRequired : RequestRejectionReason.LengthRequiredHttp10;
context.ThrowRequestRejected(requestRejectionReason, context.Method);
}
}
if (upgrade)
{
return new ForUpgrade(context);
}
return keepAlive ? _zeroContentLengthKeepAlive : _zeroContentLengthClose;
}
private class ForUpgrade : MessageBody
{
public ForUpgrade(Frame context)
: base(context)
{
RequestUpgrade = true;
}
protected override bool Read(ReadableBuffer readableBuffer, WritableBuffer writableBuffer, out ReadCursor consumed, out ReadCursor examined)
{
Copy(readableBuffer, writableBuffer);
consumed = readableBuffer.End;
examined = readableBuffer.End;
return false;
}
}
private class ForZeroContentLength : MessageBody
@ -408,396 +139,15 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
public override bool IsEmpty => true;
public override Task<int> ReadAsync(ArraySegment<byte> buffer, CancellationToken cancellationToken = default(CancellationToken))
{
return Task.FromResult(0);
}
public override Task<int> ReadAsync(ArraySegment<byte> buffer, CancellationToken cancellationToken = default(CancellationToken)) => Task.FromResult(0);
public override Task CopyToAsync(Stream destination, CancellationToken cancellationToken = default(CancellationToken))
{
return Task.CompletedTask;
}
public override Task CopyToAsync(Stream destination, CancellationToken cancellationToken = default(CancellationToken)) => Task.CompletedTask;
public override Task ConsumeAsync(CancellationToken cancellationToken = default(CancellationToken))
{
return Task.CompletedTask;
}
public override Task ConsumeAsync() => Task.CompletedTask;
public override Task StopAsync()
{
return Task.CompletedTask;
}
}
public override Task StopAsync() => Task.CompletedTask;
private class ForContentLength : MessageBody
{
private readonly long _contentLength;
private long _inputLength;
public ForContentLength(bool keepAlive, long contentLength, Frame context)
: base(context)
{
RequestKeepAlive = keepAlive;
_contentLength = contentLength;
_inputLength = _contentLength;
}
protected override bool Read(ReadableBuffer readableBuffer, WritableBuffer writableBuffer, out ReadCursor consumed, out ReadCursor examined)
{
if (_inputLength == 0)
{
throw new InvalidOperationException("Attempted to read from completed Content-Length request body.");
}
var actual = (int)Math.Min(readableBuffer.Length, _inputLength);
_inputLength -= actual;
consumed = readableBuffer.Move(readableBuffer.Start, actual);
examined = consumed;
Copy(readableBuffer.Slice(0, actual), writableBuffer);
return _inputLength == 0;
}
protected override void OnReadStart()
{
if (_contentLength > _context.MaxRequestBodySize)
{
_context.ThrowRequestRejected(RequestRejectionReason.RequestBodyTooLarge);
}
}
}
/// <summary>
/// http://tools.ietf.org/html/rfc2616#section-3.6.1
/// </summary>
private class ForChunkedEncoding : MessageBody
{
// byte consts don't have a data type annotation so we pre-cast it
private const byte ByteCR = (byte)'\r';
// "7FFFFFFF\r\n" is the largest chunk size that could be returned as an int.
private const int MaxChunkPrefixBytes = 10;
private long _inputLength;
private long _consumedBytes;
private Mode _mode = Mode.Prefix;
public ForChunkedEncoding(bool keepAlive, Frame context)
: base(context)
{
RequestKeepAlive = keepAlive;
}
protected override bool Read(ReadableBuffer readableBuffer, WritableBuffer writableBuffer, out ReadCursor consumed, out ReadCursor examined)
{
consumed = default(ReadCursor);
examined = default(ReadCursor);
while (_mode < Mode.Trailer)
{
if (_mode == Mode.Prefix)
{
ParseChunkedPrefix(readableBuffer, out consumed, out examined);
if (_mode == Mode.Prefix)
{
return false;
}
readableBuffer = readableBuffer.Slice(consumed);
}
if (_mode == Mode.Extension)
{
ParseExtension(readableBuffer, out consumed, out examined);
if (_mode == Mode.Extension)
{
return false;
}
readableBuffer = readableBuffer.Slice(consumed);
}
if (_mode == Mode.Data)
{
ReadChunkedData(readableBuffer, writableBuffer, out consumed, out examined);
if (_mode == Mode.Data)
{
return false;
}
readableBuffer = readableBuffer.Slice(consumed);
}
if (_mode == Mode.Suffix)
{
ParseChunkedSuffix(readableBuffer, out consumed, out examined);
if (_mode == Mode.Suffix)
{
return false;
}
readableBuffer = readableBuffer.Slice(consumed);
}
}
// Chunks finished, parse trailers
if (_mode == Mode.Trailer)
{
ParseChunkedTrailer(readableBuffer, out consumed, out examined);
if (_mode == Mode.Trailer)
{
return false;
}
readableBuffer = readableBuffer.Slice(consumed);
}
// _consumedBytes aren't tracked for trailer headers, since headers have seperate limits.
if (_mode == Mode.TrailerHeaders)
{
if (_context.TakeMessageHeaders(readableBuffer, out consumed, out examined))
{
_mode = Mode.Complete;
}
}
return _mode == Mode.Complete;
}
private void AddAndCheckConsumedBytes(long consumedBytes)
{
_consumedBytes += consumedBytes;
if (_consumedBytes > _context.MaxRequestBodySize)
{
_context.ThrowRequestRejected(RequestRejectionReason.RequestBodyTooLarge);
}
}
private void ParseChunkedPrefix(ReadableBuffer buffer, out ReadCursor consumed, out ReadCursor examined)
{
consumed = buffer.Start;
examined = buffer.Start;
var reader = new ReadableBufferReader(buffer);
var ch1 = reader.Take();
var ch2 = reader.Take();
if (ch1 == -1 || ch2 == -1)
{
examined = reader.Cursor;
return;
}
var chunkSize = CalculateChunkSize(ch1, 0);
ch1 = ch2;
while (reader.ConsumedBytes < MaxChunkPrefixBytes)
{
if (ch1 == ';')
{
consumed = reader.Cursor;
examined = reader.Cursor;
AddAndCheckConsumedBytes(reader.ConsumedBytes);
_inputLength = chunkSize;
_mode = Mode.Extension;
return;
}
ch2 = reader.Take();
if (ch2 == -1)
{
examined = reader.Cursor;
return;
}
if (ch1 == '\r' && ch2 == '\n')
{
consumed = reader.Cursor;
examined = reader.Cursor;
AddAndCheckConsumedBytes(reader.ConsumedBytes);
_inputLength = chunkSize;
_mode = chunkSize > 0 ? Mode.Data : Mode.Trailer;
return;
}
chunkSize = CalculateChunkSize(ch1, chunkSize);
ch1 = ch2;
}
// At this point, 10 bytes have been consumed which is enough to parse the max value "7FFFFFFF\r\n".
_context.ThrowRequestRejected(RequestRejectionReason.BadChunkSizeData);
}
private void ParseExtension(ReadableBuffer buffer, out ReadCursor consumed, out ReadCursor examined)
{
// Chunk-extensions not currently parsed
// Just drain the data
consumed = buffer.Start;
examined = buffer.Start;
do
{
ReadCursor extensionCursor;
if (ReadCursorOperations.Seek(buffer.Start, buffer.End, out extensionCursor, ByteCR) == -1)
{
// End marker not found yet
consumed = buffer.End;
examined = buffer.End;
AddAndCheckConsumedBytes(buffer.Length);
return;
};
var charsToByteCRExclusive = buffer.Slice(0, extensionCursor).Length;
var sufixBuffer = buffer.Slice(extensionCursor);
if (sufixBuffer.Length < 2)
{
consumed = extensionCursor;
examined = buffer.End;
AddAndCheckConsumedBytes(charsToByteCRExclusive);
return;
}
sufixBuffer = sufixBuffer.Slice(0, 2);
var sufixSpan = sufixBuffer.ToSpan();
if (sufixSpan[1] == '\n')
{
// We consumed the \r\n at the end of the extension, so switch modes.
_mode = _inputLength > 0 ? Mode.Data : Mode.Trailer;
consumed = sufixBuffer.End;
examined = sufixBuffer.End;
AddAndCheckConsumedBytes(charsToByteCRExclusive + 2);
}
else
{
// Don't consume suffixSpan[1] in case it is also a \r.
buffer = buffer.Slice(charsToByteCRExclusive + 1);
consumed = extensionCursor;
AddAndCheckConsumedBytes(charsToByteCRExclusive + 1);
}
} while (_mode == Mode.Extension);
}
private void ReadChunkedData(ReadableBuffer buffer, WritableBuffer writableBuffer, out ReadCursor consumed, out ReadCursor examined)
{
var actual = Math.Min(buffer.Length, _inputLength);
consumed = buffer.Move(buffer.Start, actual);
examined = consumed;
Copy(buffer.Slice(0, actual), writableBuffer);
_inputLength -= actual;
AddAndCheckConsumedBytes(actual);
if (_inputLength == 0)
{
_mode = Mode.Suffix;
}
}
private void ParseChunkedSuffix(ReadableBuffer buffer, out ReadCursor consumed, out ReadCursor examined)
{
consumed = buffer.Start;
examined = buffer.Start;
if (buffer.Length < 2)
{
examined = buffer.End;
return;
}
var suffixBuffer = buffer.Slice(0, 2);
var suffixSpan = suffixBuffer.ToSpan();
if (suffixSpan[0] == '\r' && suffixSpan[1] == '\n')
{
consumed = suffixBuffer.End;
examined = suffixBuffer.End;
AddAndCheckConsumedBytes(2);
_mode = Mode.Prefix;
}
else
{
_context.ThrowRequestRejected(RequestRejectionReason.BadChunkSuffix);
}
}
private void ParseChunkedTrailer(ReadableBuffer buffer, out ReadCursor consumed, out ReadCursor examined)
{
consumed = buffer.Start;
examined = buffer.Start;
if (buffer.Length < 2)
{
examined = buffer.End;
return;
}
var trailerBuffer = buffer.Slice(0, 2);
var trailerSpan = trailerBuffer.ToSpan();
if (trailerSpan[0] == '\r' && trailerSpan[1] == '\n')
{
consumed = trailerBuffer.End;
examined = trailerBuffer.End;
AddAndCheckConsumedBytes(2);
_mode = Mode.Complete;
}
else
{
_mode = Mode.TrailerHeaders;
}
}
private int CalculateChunkSize(int extraHexDigit, int currentParsedSize)
{
try
{
checked
{
if (extraHexDigit >= '0' && extraHexDigit <= '9')
{
return currentParsedSize * 0x10 + (extraHexDigit - '0');
}
else if (extraHexDigit >= 'A' && extraHexDigit <= 'F')
{
return currentParsedSize * 0x10 + (extraHexDigit - ('A' - 10));
}
else if (extraHexDigit >= 'a' && extraHexDigit <= 'f')
{
return currentParsedSize * 0x10 + (extraHexDigit - ('a' - 10));
}
}
}
catch (OverflowException ex)
{
throw new IOException(CoreStrings.BadRequest_BadChunkSizeData, ex);
}
_context.ThrowRequestRejected(RequestRejectionReason.BadChunkSizeData);
return -1; // can't happen, but compiler complains
}
private enum Mode
{
Prefix,
Extension,
Data,
Suffix,
Trailer,
TrailerHeaders,
Complete
};
protected override Task OnConsumeAsync() => Task.CompletedTask;
}
}
}

View File

@ -235,9 +235,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
throw new Http2ConnectionErrorException(Http2ErrorCode.PROTOCOL_ERROR);
}
if (_streams.TryGetValue(_incomingFrame.StreamId, out var stream) && !stream.MessageBody.IsCompleted)
if (_streams.TryGetValue(_incomingFrame.StreamId, out var stream) && !stream.HasReceivedEndStream)
{
return stream.MessageBody.OnDataAsync(_incomingFrame.DataPayload,
return stream.OnDataAsync(_incomingFrame.DataPayload,
endStream: (_incomingFrame.DataFlags & Http2DataFrameFlags.END_STREAM) == Http2DataFrameFlags.END_STREAM);
}
@ -272,7 +272,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
StreamLifetimeHandler = this,
FrameWriter = _frameWriter
});
_currentHeadersStream.ExpectBody = (_incomingFrame.HeadersFlags & Http2HeadersFrameFlags.END_STREAM) == 0;
_currentHeadersStream.ExpectData = (_incomingFrame.HeadersFlags & Http2HeadersFrameFlags.END_STREAM) == 0;
_currentHeadersStream.Reset();
_streams[_incomingFrame.StreamId] = _currentHeadersStream;
@ -282,7 +282,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
if ((_incomingFrame.HeadersFlags & Http2HeadersFrameFlags.END_HEADERS) == Http2HeadersFrameFlags.END_HEADERS)
{
_lastStreamId = _incomingFrame.StreamId;
_ = _currentHeadersStream.ProcessRequestAsync();
_ = _currentHeadersStream.ProcessRequestsAsync();
_currentHeadersStream = null;
}
@ -441,7 +441,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
if ((_incomingFrame.ContinuationFlags & Http2ContinuationFrameFlags.END_HEADERS) == Http2ContinuationFrameFlags.END_HEADERS)
{
_lastStreamId = _currentHeadersStream.StreamId;
_ = _currentHeadersStream.ProcessRequestAsync();
_ = _currentHeadersStream.ProcessRequestsAsync();
_currentHeadersStream = null;
}

View File

@ -7,6 +7,7 @@ using System.IO.Pipelines;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http;
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2.HPack;
using Microsoft.Extensions.Logging;
@ -50,10 +51,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
return Task.CompletedTask;
}
public Task WriteHeadersAsync(int streamId, int statusCode, IHeaderDictionary headers)
public void WriteResponseHeaders(int streamId, int statusCode, IHeaderDictionary headers)
{
var tasks = new List<Task>();
lock (_writeLock)
{
_outgoingFrame.PrepareHeaders(Http2HeadersFrameFlags.NONE, streamId);
@ -66,7 +65,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
_outgoingFrame.HeadersFlags = Http2HeadersFrameFlags.END_HEADERS;
}
tasks.Add(WriteAsync(_outgoingFrame.Raw));
Append(_outgoingFrame.Raw);
while (!done)
{
@ -80,10 +79,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
_outgoingFrame.ContinuationFlags = Http2ContinuationFrameFlags.END_HEADERS;
}
tasks.Add(WriteAsync(_outgoingFrame.Raw));
Append(_outgoingFrame.Raw);
}
return Task.WhenAll(tasks);
}
}
@ -168,6 +165,20 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
}
}
// Must be called with _writeLock
private void Append(ArraySegment<byte> data)
{
if (_completed)
{
return;
}
var writeableBuffer = _outputWriter.Alloc(1);
writeableBuffer.Write(data);
writeableBuffer.Commit();
}
// Must be called with _writeLock
private async Task WriteAsync(ArraySegment<byte> data, CancellationToken cancellationToken = default(CancellationToken))
{
if (_completed)

View File

@ -1,262 +1,48 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.IO;
using System.IO.Pipelines;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http;
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
{
public abstract class Http2MessageBody : IMessageBody
public abstract class Http2MessageBody : MessageBody
{
private static readonly Http2MessageBody _emptyMessageBody = new ForEmpty();
private readonly Http2Stream _context;
private bool _send100Continue = true;
protected Http2MessageBody(Http2Stream context)
: base(context)
{
_context = context;
}
public bool IsCompleted { get; protected set; }
protected override Task OnConsumeAsync() => Task.CompletedTask;
public virtual async Task OnDataAsync(ArraySegment<byte> data, bool endStream)
{
try
{
if (data.Count > 0)
{
var writableBuffer = _context.RequestBodyPipe.Writer.Alloc(1);
bool done;
try
{
done = Read(data, writableBuffer);
}
finally
{
writableBuffer.Commit();
}
await writableBuffer.FlushAsync();
}
if (endStream)
{
IsCompleted = true;
_context.RequestBodyPipe.Writer.Complete();
}
}
catch (Exception ex)
{
_context.RequestBodyPipe.Writer.Complete(ex);
}
}
public virtual async Task<int> ReadAsync(ArraySegment<byte> buffer, CancellationToken cancellationToken = default(CancellationToken))
{
TryInit();
while (true)
{
var result = await _context.RequestBodyPipe.Reader.ReadAsync();
var readableBuffer = result.Buffer;
var consumed = readableBuffer.End;
try
{
if (!readableBuffer.IsEmpty)
{
// buffer.Count is int
var actual = (int)Math.Min(readableBuffer.Length, buffer.Count);
var slice = readableBuffer.Slice(0, actual);
consumed = readableBuffer.Move(readableBuffer.Start, actual);
slice.CopyTo(buffer);
return actual;
}
else if (result.IsCompleted)
{
return 0;
}
}
finally
{
_context.RequestBodyPipe.Reader.Advance(consumed);
}
}
}
public virtual async Task CopyToAsync(Stream destination, CancellationToken cancellationToken = default(CancellationToken))
{
TryInit();
while (true)
{
var result = await _context.RequestBodyPipe.Reader.ReadAsync();
var readableBuffer = result.Buffer;
var consumed = readableBuffer.End;
try
{
if (!readableBuffer.IsEmpty)
{
foreach (var memory in readableBuffer)
{
var array = memory.GetArray();
await destination.WriteAsync(array.Array, array.Offset, array.Count, cancellationToken);
}
}
else if (result.IsCompleted)
{
return;
}
}
finally
{
_context.RequestBodyPipe.Reader.Advance(consumed);
}
}
}
public virtual Task StopAsync()
public override Task StopAsync()
{
_context.RequestBodyPipe.Reader.Complete();
_context.RequestBodyPipe.Writer.Complete();
return Task.CompletedTask;
}
protected void Copy(Span<byte> data, WritableBuffer writableBuffer)
{
writableBuffer.Write(data);
}
private void TryProduceContinue()
{
if (_send100Continue)
{
_context.HttpStreamControl.ProduceContinue();
_send100Continue = false;
}
}
private void TryInit()
{
if (!_context.HasStartedConsumingRequestBody)
{
OnReadStart();
_context.HasStartedConsumingRequestBody = true;
}
}
protected virtual bool Read(Span<byte> readableBuffer, WritableBuffer writableBuffer)
{
throw new NotImplementedException();
}
protected virtual void OnReadStart()
{
}
public static Http2MessageBody For(
FrameRequestHeaders headers,
public static MessageBody For(
HttpRequestHeaders headers,
Http2Stream context)
{
if (!context.ExpectBody)
if (!context.ExpectData)
{
return _emptyMessageBody;
return MessageBody.ZeroContentLengthClose;
}
if (headers.ContentLength.HasValue)
{
var contentLength = headers.ContentLength.Value;
return new ForContentLength(contentLength, context);
}
return new ForRemainingData(context);
return new ForHttp2(context);
}
private class ForEmpty : Http2MessageBody
private class ForHttp2 : Http2MessageBody
{
public ForEmpty()
: base(context: null)
{
IsCompleted = true;
}
public override Task OnDataAsync(ArraySegment<byte> data, bool endStream)
{
throw new NotImplementedException();
}
public override Task<int> ReadAsync(ArraySegment<byte> buffer, CancellationToken cancellationToken)
{
return Task.FromResult(0);
}
public override Task CopyToAsync(Stream destination, CancellationToken cancellationToken)
{
return Task.CompletedTask;
}
}
private class ForRemainingData : Http2MessageBody
{
public ForRemainingData(Http2Stream context)
public ForHttp2(Http2Stream context)
: base(context)
{
}
protected override bool Read(Span<byte> data, WritableBuffer writableBuffer)
{
Copy(data, writableBuffer);
return false;
}
}
private class ForContentLength : Http2MessageBody
{
private readonly long _contentLength;
private long _inputLength;
public ForContentLength(long contentLength, Http2Stream context)
: base(context)
{
_contentLength = contentLength;
_inputLength = _contentLength;
}
protected override bool Read(Span<byte> data, WritableBuffer writableBuffer)
{
if (_inputLength == 0)
{
throw new InvalidOperationException("Attempted to read from completed Content-Length request body.");
}
if (data.Length > _inputLength)
{
_context.ThrowRequestRejected(RequestRejectionReason.RequestBodyExceedsContentLength);
}
_inputLength -= data.Length;
Copy(data, writableBuffer);
return _inputLength == 0;
}
protected override void OnReadStart()
{
if (_contentLength > _context.MaxRequestBodySize)
{
_context.ThrowRequestRejected(RequestRejectionReason.RequestBodyTooLarge);
}
}
}
}
}

View File

@ -0,0 +1,58 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.IO.Pipelines;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http;
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
{
public class Http2OutputProducer : IHttpOutputProducer
{
private static readonly ArraySegment<byte> _emptyData = new ArraySegment<byte>(new byte[0]);
private readonly int _streamId;
private readonly IHttp2FrameWriter _frameWriter;
public Http2OutputProducer(int streamId, IHttp2FrameWriter frameWriter)
{
_streamId = streamId;
_frameWriter = frameWriter;
}
public void Dispose()
{
}
public void Abort(Exception error)
{
// TODO: RST_STREAM?
}
public Task WriteAsync<T>(Action<WritableBuffer, T> callback, T state)
{
throw new NotImplementedException();
}
public Task FlushAsync(CancellationToken cancellationToken) => _frameWriter.FlushAsync(cancellationToken);
public Task Write100ContinueAsync(CancellationToken cancellationToken) => _frameWriter.Write100ContinueAsync(_streamId);
public Task WriteDataAsync(ArraySegment<byte> data, CancellationToken cancellationToken)
{
return _frameWriter.WriteDataAsync(_streamId, data, cancellationToken);
}
public Task WriteStreamSuffixAsync(CancellationToken cancellationToken)
{
return _frameWriter.WriteDataAsync(_streamId, _emptyData, endStream: true, cancellationToken: cancellationToken);
}
public void WriteResponseHeaders(int statusCode, string ReasonPhrase, HttpResponseHeaders responseHeaders)
{
_frameWriter.WriteResponseHeaders(_streamId, statusCode, responseHeaders);
}
}
}

View File

@ -1,289 +1,12 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Server.Kestrel.Core.Features;
using Microsoft.Extensions.Primitives;
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
{
public partial class Http2Stream : IFeatureCollection,
IHttpRequestFeature,
IHttpResponseFeature,
IHttpUpgradeFeature,
IHttpConnectionFeature,
IHttpRequestLifetimeFeature,
IHttpRequestIdentifierFeature,
IHttpBodyControlFeature,
IHttpMaxRequestBodySizeFeature,
IHttpMinRequestBodyDataRateFeature,
IHttpMinResponseDataRateFeature,
IHttp2StreamIdFeature
public partial class Http2Stream : IHttp2StreamIdFeature
{
// NOTE: When feature interfaces are added to or removed from this Frame class implementation,
// then the list of `implementedFeatures` in the generated code project MUST also be updated.
// See also: tools/Microsoft.AspNetCore.Server.Kestrel.GeneratedCode/FrameFeatureCollection.cs
private int _featureRevision;
private List<KeyValuePair<Type, object>> MaybeExtra;
public void ResetFeatureCollection()
{
FastReset();
MaybeExtra?.Clear();
_featureRevision++;
}
private object ExtraFeatureGet(Type key)
{
if (MaybeExtra == null)
{
return null;
}
for (var i = 0; i < MaybeExtra.Count; i++)
{
var kv = MaybeExtra[i];
if (kv.Key == key)
{
return kv.Value;
}
}
return null;
}
private void ExtraFeatureSet(Type key, object value)
{
if (MaybeExtra == null)
{
MaybeExtra = new List<KeyValuePair<Type, object>>(2);
}
for (var i = 0; i < MaybeExtra.Count; i++)
{
if (MaybeExtra[i].Key == key)
{
MaybeExtra[i] = new KeyValuePair<Type, object>(key, value);
return;
}
}
MaybeExtra.Add(new KeyValuePair<Type, object>(key, value));
}
string IHttpRequestFeature.Protocol
{
get => HttpVersion;
set => throw new InvalidOperationException();
}
string IHttpRequestFeature.Scheme
{
get => Scheme ?? "http";
set => Scheme = value;
}
string IHttpRequestFeature.Method
{
get => Method;
set => Method = value;
}
string IHttpRequestFeature.PathBase
{
get => PathBase ?? "";
set => PathBase = value;
}
string IHttpRequestFeature.Path
{
get => Path;
set => Path = value;
}
string IHttpRequestFeature.QueryString
{
get => QueryString;
set => QueryString = value;
}
string IHttpRequestFeature.RawTarget
{
get => RawTarget;
set => RawTarget = value;
}
IHeaderDictionary IHttpRequestFeature.Headers
{
get => RequestHeaders;
set => RequestHeaders = value;
}
Stream IHttpRequestFeature.Body
{
get => RequestBody;
set => RequestBody = value;
}
int IHttpResponseFeature.StatusCode
{
get => StatusCode;
set => StatusCode = value;
}
string IHttpResponseFeature.ReasonPhrase
{
get => ReasonPhrase;
set => ReasonPhrase = value;
}
IHeaderDictionary IHttpResponseFeature.Headers
{
get => ResponseHeaders;
set => ResponseHeaders = value;
}
Stream IHttpResponseFeature.Body
{
get => ResponseBody;
set => ResponseBody = value;
}
CancellationToken IHttpRequestLifetimeFeature.RequestAborted
{
get => RequestAborted;
set => RequestAborted = value;
}
bool IHttpResponseFeature.HasStarted => HasResponseStarted;
bool IHttpUpgradeFeature.IsUpgradableRequest => false;
bool IFeatureCollection.IsReadOnly => false;
int IFeatureCollection.Revision => _featureRevision;
IPAddress IHttpConnectionFeature.RemoteIpAddress
{
get => RemoteIpAddress;
set => RemoteIpAddress = value;
}
IPAddress IHttpConnectionFeature.LocalIpAddress
{
get => LocalIpAddress;
set => LocalIpAddress = value;
}
int IHttpConnectionFeature.RemotePort
{
get => RemotePort;
set => RemotePort = value;
}
int IHttpConnectionFeature.LocalPort
{
get => LocalPort;
set => LocalPort = value;
}
string IHttpConnectionFeature.ConnectionId
{
get => ConnectionIdFeature;
set => ConnectionIdFeature = value;
}
string IHttpRequestIdentifierFeature.TraceIdentifier
{
get => TraceIdentifier;
set => TraceIdentifier = value;
}
bool IHttpBodyControlFeature.AllowSynchronousIO
{
get => AllowSynchronousIO;
set => AllowSynchronousIO = value;
}
bool IHttpMaxRequestBodySizeFeature.IsReadOnly => HasStartedConsumingRequestBody;
long? IHttpMaxRequestBodySizeFeature.MaxRequestBodySize
{
get => MaxRequestBodySize;
set
{
if (HasStartedConsumingRequestBody)
{
throw new InvalidOperationException(CoreStrings.MaxRequestBodySizeCannotBeModifiedAfterRead);
}
if (value < 0)
{
throw new ArgumentOutOfRangeException(nameof(value), CoreStrings.NonNegativeNumberOrNullRequired);
}
MaxRequestBodySize = value;
}
}
MinDataRate IHttpMinRequestBodyDataRateFeature.MinDataRate
{
get => throw new NotImplementedException();
set => throw new NotImplementedException();
}
MinDataRate IHttpMinResponseDataRateFeature.MinDataRate
{
get => throw new NotImplementedException();
set => throw new NotImplementedException();
}
object IFeatureCollection.this[Type key]
{
get => FastFeatureGet(key) ?? ConnectionFeatures?[key];
set => FastFeatureSet(key, value);
}
TFeature IFeatureCollection.Get<TFeature>()
{
return (TFeature)(FastFeatureGet(typeof(TFeature)) ?? ConnectionFeatures?[typeof(TFeature)]);
}
void IFeatureCollection.Set<TFeature>(TFeature instance)
{
FastFeatureSet(typeof(TFeature), instance);
}
void IHttpResponseFeature.OnStarting(Func<object, Task> callback, object state)
{
OnStarting(callback, state);
}
void IHttpResponseFeature.OnCompleted(Func<object, Task> callback, object state)
{
OnCompleted(callback, state);
}
Task<Stream> IHttpUpgradeFeature.UpgradeAsync()
{
throw new NotImplementedException();
}
IEnumerator<KeyValuePair<Type, object>> IEnumerable<KeyValuePair<Type, object>>.GetEnumerator() => FastEnumerable().GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => FastEnumerable().GetEnumerator();
void IHttpRequestLifetimeFeature.Abort()
{
Abort(error: null);
}
int IHttp2StreamIdFeature.StreamId => StreamId;
int IHttp2StreamIdFeature.StreamId => _context.StreamId;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -3,14 +3,17 @@
using System.IO.Pipelines;
using System.Net;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http;
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
{
public class Http2StreamContext
public class Http2StreamContext : IHttpProtocolContext
{
public string ConnectionId { get; set; }
public int StreamId { get; set; }
public ServiceContext ServiceContext { get; set; }
public IFeatureCollection ConnectionFeatures { get; set; }
public PipeFactory PipeFactory { get; set; }
public IPEndPoint RemoteEndPoint { get; set; }
public IPEndPoint LocalEndPoint { get; set; }

View File

@ -1,14 +1,8 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting.Server;
using Microsoft.AspNetCore.Protocols;
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http;
using Microsoft.Extensions.Logging;
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
{
@ -16,157 +10,18 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
{
private readonly IHttpApplication<TContext> _application;
private TContext _httpContext;
public Http2Stream(IHttpApplication<TContext> application, Http2StreamContext context)
: base(context)
{
_application = application;
}
public override async Task ProcessRequestAsync()
{
try
{
Method = RequestHeaders[":method"];
Scheme = RequestHeaders[":scheme"];
protected override void CreateHttpContext() => _httpContext = _application.CreateContext(this);
var path = RequestHeaders[":path"].ToString();
var queryIndex = path.IndexOf('?');
protected override void DisposeHttpContext() => _application.DisposeContext(_httpContext, _applicationException);
Path = queryIndex == -1 ? path : path.Substring(0, queryIndex);
QueryString = queryIndex == -1 ? string.Empty : path.Substring(queryIndex);
RequestHeaders["Host"] = RequestHeaders[":authority"];
// TODO: figure out what the equivalent for HTTP/2 is
// EnsureHostHeaderExists();
MessageBody = Http2MessageBody.For(FrameRequestHeaders, this);
InitializeStreams(MessageBody);
var context = _application.CreateContext(this);
try
{
try
{
//KestrelEventSource.Log.RequestStart(this);
await _application.ProcessRequestAsync(context);
if (Volatile.Read(ref _requestAborted) == 0)
{
VerifyResponseContentLength();
}
}
catch (Exception ex)
{
ReportApplicationError(ex);
if (ex is BadHttpRequestException)
{
throw;
}
}
finally
{
//KestrelEventSource.Log.RequestStop(this);
// Trigger OnStarting if it hasn't been called yet and the app hasn't
// already failed. If an OnStarting callback throws we can go through
// our normal error handling in ProduceEnd.
// https://github.com/aspnet/KestrelHttpServer/issues/43
if (!HasResponseStarted && _applicationException == null && _onStarting != null)
{
await FireOnStarting();
}
PauseStreams();
if (_onCompleted != null)
{
await FireOnCompleted();
}
}
// If _requestAbort is set, the connection has already been closed.
if (Volatile.Read(ref _requestAborted) == 0)
{
await ProduceEnd();
}
else if (!HasResponseStarted)
{
// If the request was aborted and no response was sent, there's no
// meaningful status code to log.
StatusCode = 0;
}
}
catch (BadHttpRequestException ex)
{
// Handle BadHttpRequestException thrown during app execution or remaining message body consumption.
// This has to be caught here so StatusCode is set properly before disposing the HttpContext
// (DisposeContext logs StatusCode).
SetBadRequestState(ex);
}
finally
{
_application.DisposeContext(context, _applicationException);
// StopStreams should be called before the end of the "if (!_requestProcessingStopping)" block
// to ensure InitializeStreams has been called.
StopStreams();
if (HasStartedConsumingRequestBody)
{
RequestBodyPipe.Reader.Complete();
// Wait for MessageBody.PumpAsync() to call RequestBodyPipe.Writer.Complete().
await MessageBody.StopAsync();
// At this point both the request body pipe reader and writer should be completed.
RequestBodyPipe.Reset();
}
}
}
catch (BadHttpRequestException ex)
{
// Handle BadHttpRequestException thrown during request line or header parsing.
// SetBadRequestState logs the error.
SetBadRequestState(ex);
}
catch (ConnectionResetException ex)
{
// Don't log ECONNRESET errors made between requests. Browsers like IE will reset connections regularly.
if (_requestProcessingStatus != RequestProcessingStatus.RequestPending)
{
Log.RequestProcessingError(ConnectionId, ex);
}
}
catch (IOException ex)
{
Log.RequestProcessingError(ConnectionId, ex);
}
catch (Exception ex)
{
Log.LogWarning(0, ex, CoreStrings.RequestProcessingEndError);
}
finally
{
try
{
if (Volatile.Read(ref _requestAborted) == 0)
{
await TryProduceInvalidRequestResponse();
}
}
catch (Exception ex)
{
Log.LogWarning(0, ex, CoreStrings.ConnectionShutdownError);
}
finally
{
StreamLifetimeHandler.OnStreamCompleted(StreamId);
}
}
}
protected override Task InvokeApplicationAsync() => _application.ProcessRequestAsync(_httpContext);
}
}

View File

@ -1,48 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.IO;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http;
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
{
internal class Http2Streams
{
private readonly FrameResponseStream _response;
private readonly FrameRequestStream _request;
public Http2Streams(IHttpBodyControlFeature bodyControl, IFrameControl httpStreamControl)
{
_request = new FrameRequestStream(bodyControl);
_response = new FrameResponseStream(bodyControl, httpStreamControl);
}
public (Stream request, Stream response) Start(Http2MessageBody body)
{
_request.StartAcceptingReads(body);
_response.StartAcceptingWrites();
return (_request, _response);
}
public void Pause()
{
_request.PauseAcceptingReads();
_response.PauseAcceptingWrites();
}
public void Stop()
{
_request.StopAcceptingReads();
_response.StopAcceptingWrites();
}
public void Abort(Exception error)
{
_request.Abort(error);
_response.Abort();
}
}
}

View File

@ -11,9 +11,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
public interface IHttp2FrameWriter
{
void Abort(Exception error);
Task FlushAsync(CancellationToken cancellationToken);
Task FlushAsync(CancellationToken cancellationToken = default(CancellationToken));
Task Write100ContinueAsync(int streamId);
Task WriteHeadersAsync(int streamId, int statusCode, IHeaderDictionary headers);
void WriteResponseHeaders(int streamId, int statusCode, IHeaderDictionary headers);
Task WriteDataAsync(int streamId, Span<byte> data, CancellationToken cancellationToken);
Task WriteDataAsync(int streamId, Span<byte> data, bool endStream, CancellationToken cancellationToken);
Task WriteRstStreamAsync(int streamId, Http2ErrorCode errorCode);

View File

@ -19,16 +19,16 @@ using Microsoft.Extensions.Logging;
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
{
public class FrameConnection : ITimeoutControl, IConnectionTimeoutFeature
public class HttpConnection : ITimeoutControl, IConnectionTimeoutFeature
{
private const int Http2ConnectionNotStarted = 0;
private const int Http2ConnectionStarted = 1;
private const int Http2ConnectionClosed = 2;
private readonly FrameConnectionContext _context;
private readonly HttpConnectionContext _context;
private IList<IAdaptedConnection> _adaptedConnections;
private readonly TaskCompletionSource<object> _socketClosedTcs = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
private Frame _frame;
private Http1Connection _http1Connection;
private Http2Connection _http2Connection;
private volatile int _http2ConnectionState;
@ -48,13 +48,13 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
private Task _lifetimeTask;
public FrameConnection(FrameConnectionContext context)
public HttpConnection(HttpConnectionContext context)
{
_context = context;
}
// For testing
internal Frame Frame => _frame;
internal HttpProtocol Http1Connection => _http1Connection;
internal IDebugger Debugger { get; set; } = DebuggerWrapper.Singleton;
// For testing
@ -112,8 +112,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
transport = adaptedPipeline;
}
// _frame must be initialized before adding the connection to the connection manager
CreateFrame(httpApplication, transport, application);
// _http1Connection must be initialized before adding the connection to the connection manager
CreateHttp1Connection(httpApplication, transport, application);
// _http2Connection must be initialized before yield control to the transport thread,
// to prevent a race condition where _http2Connection.Abort() is called just as
@ -131,10 +131,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
// Do this before the first await so we don't yield control to the transport until we've
// added the connection to the connection manager
_context.ServiceContext.ConnectionManager.AddConnection(_context.FrameConnectionId, this);
_context.ServiceContext.ConnectionManager.AddConnection(_context.HttpConnectionId, this);
_lastTimestamp = _context.ServiceContext.SystemClock.UtcNow.Ticks;
_frame.ConnectionFeatures.Set<IConnectionTimeoutFeature>(this);
_http1Connection.ConnectionFeatures.Set<IConnectionTimeoutFeature>(this);
if (adaptedPipeline != null)
{
@ -143,14 +143,14 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
adaptedPipelineTask = adaptedPipeline.RunAsync(stream);
}
if (_frame.ConnectionFeatures.Get<ITlsApplicationProtocolFeature>()?.ApplicationProtocol == "h2" &&
if (_http1Connection.ConnectionFeatures.Get<ITlsApplicationProtocolFeature>()?.ApplicationProtocol == "h2" &&
Interlocked.CompareExchange(ref _http2ConnectionState, Http2ConnectionStarted, Http2ConnectionNotStarted) == Http2ConnectionNotStarted)
{
await _http2Connection.ProcessAsync(httpApplication);
}
else
{
await _frame.ProcessRequestsAsync();
await _http1Connection.ProcessRequestsAsync();
}
await adaptedPipelineTask;
@ -158,14 +158,14 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
}
catch (Exception ex)
{
Log.LogError(0, ex, $"Unexpected exception in {nameof(FrameConnection)}.{nameof(ProcessRequestsAsync)}.");
Log.LogError(0, ex, $"Unexpected exception in {nameof(HttpConnection)}.{nameof(ProcessRequestsAsync)}.");
}
finally
{
_context.ServiceContext.ConnectionManager.RemoveConnection(_context.FrameConnectionId);
_context.ServiceContext.ConnectionManager.RemoveConnection(_context.HttpConnectionId);
DisposeAdaptedConnections();
if (_frame.WasUpgraded)
if (_http1Connection.IsUpgraded)
{
_context.ServiceContext.ConnectionManager.UpgradedConnectionCount.ReleaseOne();
}
@ -174,9 +174,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
}
}
internal void CreateFrame<TContext>(IHttpApplication<TContext> httpApplication, IPipeConnection transport, IPipeConnection application)
internal void CreateHttp1Connection<TContext>(IHttpApplication<TContext> httpApplication, IPipeConnection transport, IPipeConnection application)
{
_frame = new Frame<TContext>(httpApplication, new FrameContext
_http1Connection = new Http1Connection<TContext>(httpApplication, new Http1ConnectionContext
{
ConnectionId = _context.ConnectionId,
ConnectionFeatures = _context.ConnectionFeatures,
@ -199,7 +199,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
public Task StopProcessingNextRequestAsync()
{
Debug.Assert(_frame != null, $"{nameof(_frame)} is null");
Debug.Assert(_http1Connection != null, $"{nameof(_http1Connection)} is null");
if (Interlocked.Exchange(ref _http2ConnectionState, Http2ConnectionClosed) == Http2ConnectionStarted)
{
@ -207,7 +207,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
}
else
{
_frame.StopProcessingNextRequest();
_http1Connection.StopProcessingNextRequest();
}
return _lifetimeTask;
@ -215,7 +215,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
public void Abort(Exception ex)
{
Debug.Assert(_frame != null, $"{nameof(_frame)} is null");
Debug.Assert(_http1Connection != null, $"{nameof(_http1Connection)} is null");
// Abort the connection (if not already aborted)
if (Interlocked.Exchange(ref _http2ConnectionState, Http2ConnectionClosed) == Http2ConnectionStarted)
@ -224,7 +224,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
}
else
{
_frame.Abort(ex);
_http1Connection.Abort(ex);
}
}
@ -237,26 +237,26 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
public void SendTimeoutResponse()
{
Debug.Assert(_frame != null, $"{nameof(_frame)} is null");
Debug.Assert(_http1Connection != null, $"{nameof(_http1Connection)} is null");
RequestTimedOut = true;
_frame.SendTimeoutResponse();
_http1Connection.SendTimeoutResponse();
}
public void StopProcessingNextRequest()
{
Debug.Assert(_frame != null, $"{nameof(_frame)} is null");
Debug.Assert(_http1Connection != null, $"{nameof(_http1Connection)} is null");
_frame.StopProcessingNextRequest();
_http1Connection.StopProcessingNextRequest();
}
private async Task<Stream> ApplyConnectionAdaptersAsync()
{
Debug.Assert(_frame != null, $"{nameof(_frame)} is null");
Debug.Assert(_http1Connection != null, $"{nameof(_http1Connection)} is null");
var connectionAdapters = _context.ConnectionAdapters;
var stream = new RawStream(_context.Transport.Input, _context.Transport.Output);
var adapterContext = new ConnectionAdapterContext(_frame.ConnectionFeatures, stream);
var adapterContext = new ConnectionAdapterContext(_http1Connection.ConnectionFeatures, stream);
_adaptedConnections = new List<IAdaptedConnection>(connectionAdapters.Count);
try
@ -265,7 +265,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
{
var adaptedConnection = await connectionAdapters[i].OnConnectionAsync(adapterContext);
_adaptedConnections.Add(adaptedConnection);
adapterContext = new ConnectionAdapterContext(_frame.ConnectionFeatures, adaptedConnection.ConnectionStream);
adapterContext = new ConnectionAdapterContext(_http1Connection.ConnectionFeatures, adaptedConnection.ConnectionStream);
}
}
catch (Exception ex)
@ -292,7 +292,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
public void Tick(DateTimeOffset now)
{
Debug.Assert(_frame != null, $"{nameof(_frame)} is null");
Debug.Assert(_http1Connection != null, $"{nameof(_http1Connection)} is null");
var timestamp = now.Ticks;
@ -343,7 +343,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
if (_readTimingEnabled)
{
// Reference in local var to avoid torn reads in case the min rate is changed via IHttpMinRequestBodyDataRateFeature
var minRequestBodyDataRate = _frame.MinRequestBodyDataRate;
var minRequestBodyDataRate = _http1Connection.MinRequestBodyDataRate;
_readTimingElapsedTicks += timestamp - _lastTimestamp;
@ -354,7 +354,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
if (rate < minRequestBodyDataRate.BytesPerSecond && !Debugger.IsAttached)
{
Log.RequestBodyMininumDataRateNotSatisfied(_context.ConnectionId, _frame.TraceIdentifier, minRequestBodyDataRate.BytesPerSecond);
Log.RequestBodyMininumDataRateNotSatisfied(_context.ConnectionId, _http1Connection.TraceIdentifier, minRequestBodyDataRate.BytesPerSecond);
SendTimeoutResponse();
}
}
@ -378,7 +378,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
if (_writeTimingWrites > 0 && timestamp > _writeTimingTimeoutTimestamp && !Debugger.IsAttached)
{
RequestTimedOut = true;
Log.ResponseMininumDataRateNotSatisfied(_frame.ConnectionIdFeature, _frame.TraceIdentifier);
Log.ResponseMininumDataRateNotSatisfied(_http1Connection.ConnectionIdFeature, _http1Connection.TraceIdentifier);
Abort(new TimeoutException());
}
}
@ -455,7 +455,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
{
lock (_writeTimingLock)
{
var minResponseDataRate = _frame.MinResponseDataRate;
var minResponseDataRate = _http1Connection.MinResponseDataRate;
if (minResponseDataRate != null)
{

View File

@ -9,10 +9,10 @@ using Microsoft.AspNetCore.Server.Kestrel.Core.Adapter.Internal;
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
{
public class FrameConnectionContext
public class HttpConnectionContext
{
public string ConnectionId { get; set; }
public long FrameConnectionId { get; set; }
public long HttpConnectionId { get; set; }
public ServiceContext ServiceContext { get; set; }
public IFeatureCollection ConnectionFeatures { get; set; }
public IList<IConnectionAdapter> ConnectionAdapters { get; set; }

View File

@ -15,7 +15,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
{
private static Action<Exception, object> _completeTcs = CompleteTcs;
private static long _lastFrameConnectionId = long.MinValue;
private static long _lastHttpConnectionId = long.MinValue;
private readonly IList<IConnectionAdapter> _connectionAdapters;
private readonly ServiceContext _serviceContext;
@ -36,12 +36,12 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
// This is a bit of a hack but it preserves the existing semantics
var transportFeature = connectionContext.Features.Get<IConnectionTransportFeature>();
var frameConnectionId = Interlocked.Increment(ref _lastFrameConnectionId);
var httpConnectionId = Interlocked.Increment(ref _lastHttpConnectionId);
var frameConnectionContext = new FrameConnectionContext
var httpConnectionContext = new HttpConnectionContext
{
ConnectionId = connectionContext.ConnectionId,
FrameConnectionId = frameConnectionId,
HttpConnectionId = httpConnectionId,
ServiceContext = _serviceContext,
ConnectionFeatures = connectionContext.Features,
PipeFactory = connectionContext.PipeFactory,
@ -56,16 +56,16 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
{
if (connectionFeature.LocalIpAddress != null)
{
frameConnectionContext.LocalEndPoint = new IPEndPoint(connectionFeature.LocalIpAddress, connectionFeature.LocalPort);
httpConnectionContext.LocalEndPoint = new IPEndPoint(connectionFeature.LocalIpAddress, connectionFeature.LocalPort);
}
if (connectionFeature.RemoteIpAddress != null)
{
frameConnectionContext.RemoteEndPoint = new IPEndPoint(connectionFeature.RemoteIpAddress, connectionFeature.RemotePort);
httpConnectionContext.RemoteEndPoint = new IPEndPoint(connectionFeature.RemoteIpAddress, connectionFeature.RemotePort);
}
}
var connection = new FrameConnection(frameConnectionContext);
var connection = new HttpConnection(httpConnectionContext);
var processingTask = connection.StartRequestProcessing(_application);
@ -79,13 +79,13 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
inputTcs.Task.ContinueWith((task, state) =>
{
((FrameConnection)state).Abort(task.Exception?.InnerException);
((HttpConnection)state).Abort(task.Exception?.InnerException);
},
connection, TaskContinuationOptions.ExecuteSynchronously);
outputTcs.Task.ContinueWith((task, state) =>
{
((FrameConnection)state).OnConnectionClosed(task.Exception?.InnerException);
((HttpConnection)state).OnConnectionClosed(task.Exception?.InnerException);
},
connection, TaskContinuationOptions.ExecuteSynchronously);

View File

@ -6,17 +6,17 @@ using System.Collections.Concurrent;
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure
{
public class FrameConnectionManager
public class HttpConnectionManager
{
private readonly ConcurrentDictionary<long, FrameConnectionReference> _connectionReferences = new ConcurrentDictionary<long, FrameConnectionReference>();
private readonly ConcurrentDictionary<long, HttpConnectionReference> _connectionReferences = new ConcurrentDictionary<long, HttpConnectionReference>();
private readonly IKestrelTrace _trace;
public FrameConnectionManager(IKestrelTrace trace, long? upgradedConnectionLimit)
public HttpConnectionManager(IKestrelTrace trace, long? upgradedConnectionLimit)
: this(trace, GetCounter(upgradedConnectionLimit))
{
}
public FrameConnectionManager(IKestrelTrace trace, ResourceCounter upgradedConnections)
public HttpConnectionManager(IKestrelTrace trace, ResourceCounter upgradedConnections)
{
UpgradedConnectionCount = upgradedConnections;
_trace = trace;
@ -27,9 +27,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure
/// </summary>
public ResourceCounter UpgradedConnectionCount { get; }
public void AddConnection(long id, FrameConnection connection)
public void AddConnection(long id, HttpConnection connection)
{
if (!_connectionReferences.TryAdd(id, new FrameConnectionReference(connection)))
if (!_connectionReferences.TryAdd(id, new HttpConnectionReference(connection)))
{
throw new ArgumentException(nameof(id));
}
@ -43,7 +43,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure
}
}
public void Walk(Action<FrameConnection> callback)
public void Walk(Action<HttpConnection> callback)
{
foreach (var kvp in _connectionReferences)
{

View File

@ -9,9 +9,9 @@ using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal;
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure
{
public static class FrameConnectionManagerShutdownExtensions
public static class HttpConnectionManagerShutdownExtensions
{
public static async Task<bool> CloseAllConnectionsAsync(this FrameConnectionManager connectionManager, CancellationToken token)
public static async Task<bool> CloseAllConnectionsAsync(this HttpConnectionManager connectionManager, CancellationToken token)
{
var closeTasks = new List<Task>();
@ -24,7 +24,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure
return await Task.WhenAny(allClosedTask, CancellationTokenAsTask(token)).ConfigureAwait(false) == allClosedTask;
}
public static async Task<bool> AbortAllConnectionsAsync(this FrameConnectionManager connectionManager)
public static async Task<bool> AbortAllConnectionsAsync(this HttpConnectionManager connectionManager)
{
var abortTasks = new List<Task>();
var canceledException = new ConnectionAbortedException();

View File

@ -5,19 +5,19 @@ using System;
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure
{
public class FrameConnectionReference
public class HttpConnectionReference
{
private readonly WeakReference<FrameConnection> _weakReference;
private readonly WeakReference<HttpConnection> _weakReference;
public FrameConnectionReference(FrameConnection connection)
public HttpConnectionReference(HttpConnection connection)
{
_weakReference = new WeakReference<FrameConnection>(connection);
_weakReference = new WeakReference<HttpConnection>(connection);
ConnectionId = connection.ConnectionId;
}
public string ConnectionId { get; }
public bool TryGetConnection(out FrameConnection connection)
public bool TryGetConnection(out HttpConnection connection)
{
return _weakReference.TryGetTarget(out connection);
}

View File

@ -5,13 +5,13 @@ using System;
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure
{
public class FrameHeartbeatManager : IHeartbeatHandler
public class HttpHeartbeatManager : IHeartbeatHandler
{
private readonly FrameConnectionManager _connectionManager;
private readonly Action<FrameConnection> _walkCallback;
private readonly HttpConnectionManager _connectionManager;
private readonly Action<HttpConnection> _walkCallback;
private DateTimeOffset _now;
public FrameHeartbeatManager(FrameConnectionManager connectionManager)
public HttpHeartbeatManager(HttpConnectionManager connectionManager)
{
_connectionManager = connectionManager;
_walkCallback = WalkCallback;
@ -23,7 +23,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure
_connectionManager.Walk(_walkCallback);
}
private void WalkCallback(FrameConnection connection)
private void WalkCallback(HttpConnection connection)
{
connection.Tick(_now);
}

View File

@ -13,6 +13,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure
{
public const string Http10Version = "HTTP/1.0";
public const string Http11Version = "HTTP/1.1";
public const string Http2Version = "HTTP/2";
public const string HttpUriScheme = "http://";
public const string HttpsUriScheme = "https://";

View File

@ -29,7 +29,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure
// - Avoid renaming methods or parameters marked with EventAttribute. EventSource uses these to form the event object.
[NonEvent]
public void ConnectionStart(FrameConnection connection)
public void ConnectionStart(HttpConnection connection)
{
// avoid allocating strings unless this event source is enabled
if (IsEnabled())
@ -56,7 +56,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure
}
[NonEvent]
public void ConnectionStop(FrameConnection connection)
public void ConnectionStop(HttpConnection connection)
{
if (IsEnabled())
{
@ -82,12 +82,12 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure
}
[NonEvent]
public void RequestStart(Frame frame)
public void RequestStart(HttpProtocol httpProtocol)
{
// avoid allocating the trace identifier unless logging is enabled
if (IsEnabled())
{
RequestStart(frame.ConnectionIdFeature, frame.TraceIdentifier);
RequestStart(httpProtocol.ConnectionIdFeature, httpProtocol.TraceIdentifier);
}
}
@ -99,12 +99,12 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure
}
[NonEvent]
public void RequestStop(Frame frame)
public void RequestStop(HttpProtocol httpProtocol)
{
// avoid allocating the trace identifier unless logging is enabled
if (IsEnabled())
{
RequestStop(frame.ConnectionIdFeature, frame.TraceIdentifier);
RequestStop(httpProtocol.ConnectionIdFeature, httpProtocol.TraceIdentifier);
}
}

View File

@ -8,23 +8,23 @@ using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http;
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure
{
internal class Streams
public class Streams
{
private static readonly ThrowingWriteOnlyStream _throwingResponseStream
= new ThrowingWriteOnlyStream(new InvalidOperationException(CoreStrings.ResponseStreamWasUpgraded));
private readonly FrameResponseStream _response;
private readonly FrameRequestStream _request;
private readonly HttpResponseStream _response;
private readonly HttpRequestStream _request;
private readonly WrappingStream _upgradeableResponse;
private readonly FrameRequestStream _emptyRequest;
private readonly HttpRequestStream _emptyRequest;
private readonly Stream _upgradeStream;
public Streams(IHttpBodyControlFeature bodyControl, IFrameControl frameControl)
public Streams(IHttpBodyControlFeature bodyControl, IHttpResponseControl httpResponseControl)
{
_request = new FrameRequestStream(bodyControl);
_emptyRequest = new FrameRequestStream(bodyControl);
_response = new FrameResponseStream(bodyControl, frameControl);
_request = new HttpRequestStream(bodyControl);
_emptyRequest = new HttpRequestStream(bodyControl);
_response = new HttpResponseStream(bodyControl, httpResponseControl);
_upgradeableResponse = new WrappingStream(_response);
_upgradeStream = new FrameDuplexStream(_request, _response);
_upgradeStream = new HttpUpgradeStream(_request, _response);
}
public Stream Upgrade()

View File

@ -13,13 +13,13 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
public IThreadPool ThreadPool { get; set; }
public Func<FrameAdapter, IHttpParser<FrameAdapter>> HttpParserFactory { get; set; }
public Func<Http1ParsingHandler, IHttpParser<Http1ParsingHandler>> HttpParserFactory { get; set; }
public ISystemClock SystemClock { get; set; }
public DateHeaderValueManager DateHeaderValueManager { get; set; }
public FrameConnectionManager ConnectionManager { get; set; }
public HttpConnectionManager ConnectionManager { get; set; }
public KestrelServerOptions ServerOptions { get; set; }
}

View File

@ -44,9 +44,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core
_transportFactory = transportFactory;
ServiceContext = serviceContext;
var frameHeartbeatManager = new FrameHeartbeatManager(serviceContext.ConnectionManager);
var httpHeartbeatManager = new HttpHeartbeatManager(serviceContext.ConnectionManager);
_heartbeat = new Heartbeat(
new IHeartbeatHandler[] { serviceContext.DateHeaderValueManager, frameHeartbeatManager },
new IHeartbeatHandler[] { serviceContext.DateHeaderValueManager, httpHeartbeatManager },
serviceContext.SystemClock, Trace);
Features = new FeatureCollection();
@ -68,7 +68,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core
var serverOptions = options.Value ?? new KestrelServerOptions();
var logger = loggerFactory.CreateLogger("Microsoft.AspNetCore.Server.Kestrel");
var trace = new KestrelTrace(logger);
var connectionManager = new FrameConnectionManager(
var connectionManager = new HttpConnectionManager(
trace,
serverOptions.Limits.MaxConcurrentUpgradedConnections);
@ -94,7 +94,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core
return new ServiceContext
{
Log = trace,
HttpParserFactory = frameParser => new HttpParser<FrameAdapter>(frameParser.Frame.ServiceContext.Log.IsEnabled(LogLevel.Information)),
HttpParserFactory = handler => new HttpParser<Http1ParsingHandler>(handler.Connection.ServiceContext.Log.IsEnabled(LogLevel.Information)),
ThreadPool = threadPool,
SystemClock = systemClock,
DateHeaderValueManager = dateHeaderValueManager,
@ -111,7 +111,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core
private IKestrelTrace Trace => ServiceContext.Log;
private FrameConnectionManager ConnectionManager => ServiceContext.ConnectionManager;
private HttpConnectionManager ConnectionManager => ServiceContext.ConnectionManager;
public async Task StartAsync<TContext>(IHttpApplication<TContext> application, CancellationToken cancellationToken)
{

View File

@ -147,11 +147,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Https.Internal
{
/* If the Extended Key Usage extension is included, then we check that the serverAuth usage is included. (http://oid-info.com/get/1.3.6.1.5.5.7.3.1)
* If the Extended Key Usage extension is not included, then we assume the certificate is allowed for all usages.
*
*
* See also https://blogs.msdn.microsoft.com/kaushal/2012/02/17/client-certificates-vs-server-certificates/
*
*
* From https://tools.ietf.org/html/rfc3280#section-4.2.1.13 "Certificate Extensions: Extended Key Usage"
*
*
* If the (Extended Key Usage) extension is present, then the certificate MUST only be used
* for one of the purposes indicated. If multiple purposes are
* indicated the application need not recognize all purposes indicated,

View File

@ -25,21 +25,21 @@ using Xunit;
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
{
public class FrameTests : IDisposable
public class Http1ConnectionTests : IDisposable
{
private readonly IPipeConnection _transport;
private readonly IPipeConnection _application;
private readonly TestFrame<object> _frame;
private readonly TestHttp1Connection<object> _http1Connection;
private readonly ServiceContext _serviceContext;
private readonly FrameContext _frameContext;
private readonly Http1ConnectionContext _http1ConnectionContext;
private readonly PipeFactory _pipelineFactory;
private ReadCursor _consumed;
private ReadCursor _examined;
private Mock<ITimeoutControl> _timeoutControl;
private class TestFrame<TContext> : Frame<TContext>
private class TestHttp1Connection<TContext> : Http1Connection<TContext>
{
public TestFrame(IHttpApplication<TContext> application, FrameContext context)
public TestHttp1Connection(IHttpApplication<TContext> application, Http1ConnectionContext context)
: base(application, context)
{
}
@ -50,7 +50,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
}
}
public FrameTests()
public Http1ConnectionTests()
{
_pipelineFactory = new PipeFactory();
var pair = _pipelineFactory.CreateConnectionPair();
@ -60,7 +60,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
_serviceContext = new TestServiceContext();
_timeoutControl = new Mock<ITimeoutControl>();
_frameContext = new FrameContext
_http1ConnectionContext = new Http1ConnectionContext
{
ServiceContext = _serviceContext,
ConnectionFeatures = new FeatureCollection(),
@ -70,8 +70,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
Transport = pair.Transport
};
_frame = new TestFrame<object>(application: null, context: _frameContext);
_frame.Reset();
_http1Connection = new TestHttp1Connection<object>(application: null, context: _http1ConnectionContext);
_http1Connection.Reset();
}
public void Dispose()
@ -90,12 +90,12 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
{
const string headerLine = "Header: value\r\n";
_serviceContext.ServerOptions.Limits.MaxRequestHeadersTotalSize = headerLine.Length - 1;
_frame.Reset();
_http1Connection.Reset();
await _application.Output.WriteAsync(Encoding.ASCII.GetBytes($"{headerLine}\r\n"));
var readableBuffer = (await _transport.Input.ReadAsync()).Buffer;
var exception = Assert.Throws<BadHttpRequestException>(() => _frame.TakeMessageHeaders(readableBuffer, out _consumed, out _examined));
var exception = Assert.Throws<BadHttpRequestException>(() => _http1Connection.TakeMessageHeaders(readableBuffer, out _consumed, out _examined));
_transport.Input.Advance(_consumed, _examined);
Assert.Equal(CoreStrings.BadRequest_HeadersExceedMaxTotalSize, exception.Message);
@ -111,7 +111,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await _application.Output.WriteAsync(Encoding.ASCII.GetBytes($"{headerLines}\r\n"));
var readableBuffer = (await _transport.Input.ReadAsync()).Buffer;
var exception = Assert.Throws<BadHttpRequestException>(() => _frame.TakeMessageHeaders(readableBuffer, out _consumed, out _examined));
var exception = Assert.Throws<BadHttpRequestException>(() => _http1Connection.TakeMessageHeaders(readableBuffer, out _consumed, out _examined));
_transport.Input.Advance(_consumed, _examined);
Assert.Equal(CoreStrings.BadRequest_TooManyHeaders, exception.Message);
@ -121,60 +121,60 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
[Fact]
public void ResetResetsScheme()
{
_frame.Scheme = "https";
_http1Connection.Scheme = "https";
// Act
_frame.Reset();
_http1Connection.Reset();
// Assert
Assert.Equal("http", ((IFeatureCollection)_frame).Get<IHttpRequestFeature>().Scheme);
Assert.Equal("http", ((IFeatureCollection)_http1Connection).Get<IHttpRequestFeature>().Scheme);
}
[Fact]
public void ResetResetsTraceIdentifier()
{
_frame.TraceIdentifier = "xyz";
_http1Connection.TraceIdentifier = "xyz";
_frame.Reset();
_http1Connection.Reset();
var nextId = ((IFeatureCollection)_frame).Get<IHttpRequestIdentifierFeature>().TraceIdentifier;
var nextId = ((IFeatureCollection)_http1Connection).Get<IHttpRequestIdentifierFeature>().TraceIdentifier;
Assert.NotEqual("xyz", nextId);
_frame.Reset();
var secondId = ((IFeatureCollection)_frame).Get<IHttpRequestIdentifierFeature>().TraceIdentifier;
_http1Connection.Reset();
var secondId = ((IFeatureCollection)_http1Connection).Get<IHttpRequestIdentifierFeature>().TraceIdentifier;
Assert.NotEqual(nextId, secondId);
}
[Fact]
public void ResetResetsMinRequestBodyDataRate()
{
_frame.MinRequestBodyDataRate = new MinDataRate(bytesPerSecond: 1, gracePeriod: TimeSpan.MaxValue);
_http1Connection.MinRequestBodyDataRate = new MinDataRate(bytesPerSecond: 1, gracePeriod: TimeSpan.MaxValue);
_frame.Reset();
_http1Connection.Reset();
Assert.Same(_serviceContext.ServerOptions.Limits.MinRequestBodyDataRate, _frame.MinRequestBodyDataRate);
Assert.Same(_serviceContext.ServerOptions.Limits.MinRequestBodyDataRate, _http1Connection.MinRequestBodyDataRate);
}
[Fact]
public void ResetResetsMinResponseDataRate()
{
_frame.MinResponseDataRate = new MinDataRate(bytesPerSecond: 1, gracePeriod: TimeSpan.MaxValue);
_http1Connection.MinResponseDataRate = new MinDataRate(bytesPerSecond: 1, gracePeriod: TimeSpan.MaxValue);
_frame.Reset();
_http1Connection.Reset();
Assert.Same(_serviceContext.ServerOptions.Limits.MinResponseDataRate, _frame.MinResponseDataRate);
Assert.Same(_serviceContext.ServerOptions.Limits.MinResponseDataRate, _http1Connection.MinResponseDataRate);
}
[Fact]
public void TraceIdentifierCountsRequestsPerFrame()
public void TraceIdentifierCountsRequestsPerHttp1Connection()
{
var connectionId = _frameContext.ConnectionId;
var feature = ((IFeatureCollection)_frame).Get<IHttpRequestIdentifierFeature>();
var connectionId = _http1ConnectionContext.ConnectionId;
var feature = ((IFeatureCollection)_http1Connection).Get<IHttpRequestIdentifierFeature>();
// Reset() is called once in the test ctor
var count = 1;
void Reset()
{
_frame.Reset();
_http1Connection.Reset();
count++;
}
@ -193,13 +193,13 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
[Fact]
public void TraceIdentifierGeneratesWhenNull()
{
_frame.TraceIdentifier = null;
var id = _frame.TraceIdentifier;
_http1Connection.TraceIdentifier = null;
var id = _http1Connection.TraceIdentifier;
Assert.NotNull(id);
Assert.Equal(id, _frame.TraceIdentifier);
Assert.Equal(id, _http1Connection.TraceIdentifier);
_frame.Reset();
Assert.NotEqual(id, _frame.TraceIdentifier);
_http1Connection.Reset();
Assert.NotEqual(id, _http1Connection.TraceIdentifier);
}
[Fact]
@ -216,127 +216,127 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await _application.Output.WriteAsync(Encoding.ASCII.GetBytes($"{headerLine1}\r\n"));
var readableBuffer = (await _transport.Input.ReadAsync()).Buffer;
var takeMessageHeaders = _frame.TakeMessageHeaders(readableBuffer, out _consumed, out _examined);
var takeMessageHeaders = _http1Connection.TakeMessageHeaders(readableBuffer, out _consumed, out _examined);
_transport.Input.Advance(_consumed, _examined);
Assert.True(takeMessageHeaders);
Assert.Equal(1, _frame.RequestHeaders.Count);
Assert.Equal("value1", _frame.RequestHeaders["Header-1"]);
Assert.Equal(1, _http1Connection.RequestHeaders.Count);
Assert.Equal("value1", _http1Connection.RequestHeaders["Header-1"]);
_frame.Reset();
_http1Connection.Reset();
await _application.Output.WriteAsync(Encoding.ASCII.GetBytes($"{headerLine2}\r\n"));
readableBuffer = (await _transport.Input.ReadAsync()).Buffer;
takeMessageHeaders = _frame.TakeMessageHeaders(readableBuffer, out _consumed, out _examined);
takeMessageHeaders = _http1Connection.TakeMessageHeaders(readableBuffer, out _consumed, out _examined);
_transport.Input.Advance(_consumed, _examined);
Assert.True(takeMessageHeaders);
Assert.Equal(1, _frame.RequestHeaders.Count);
Assert.Equal("value2", _frame.RequestHeaders["Header-2"]);
Assert.Equal(1, _http1Connection.RequestHeaders.Count);
Assert.Equal("value2", _http1Connection.RequestHeaders["Header-2"]);
}
[Fact]
public async Task ThrowsWhenStatusCodeIsSetAfterResponseStarted()
{
// Act
await _frame.WriteAsync(new ArraySegment<byte>(new byte[1]));
await _http1Connection.WriteAsync(new ArraySegment<byte>(new byte[1]));
// Assert
Assert.True(_frame.HasResponseStarted);
Assert.Throws<InvalidOperationException>(() => ((IHttpResponseFeature)_frame).StatusCode = StatusCodes.Status404NotFound);
Assert.True(_http1Connection.HasResponseStarted);
Assert.Throws<InvalidOperationException>(() => ((IHttpResponseFeature)_http1Connection).StatusCode = StatusCodes.Status404NotFound);
}
[Fact]
public async Task ThrowsWhenReasonPhraseIsSetAfterResponseStarted()
{
// Act
await _frame.WriteAsync(new ArraySegment<byte>(new byte[1]));
await _http1Connection.WriteAsync(new ArraySegment<byte>(new byte[1]));
// Assert
Assert.True(_frame.HasResponseStarted);
Assert.Throws<InvalidOperationException>(() => ((IHttpResponseFeature)_frame).ReasonPhrase = "Reason phrase");
Assert.True(_http1Connection.HasResponseStarted);
Assert.Throws<InvalidOperationException>(() => ((IHttpResponseFeature)_http1Connection).ReasonPhrase = "Reason phrase");
}
[Fact]
public async Task ThrowsWhenOnStartingIsSetAfterResponseStarted()
{
await _frame.WriteAsync(new ArraySegment<byte>(new byte[1]));
await _http1Connection.WriteAsync(new ArraySegment<byte>(new byte[1]));
// Act/Assert
Assert.True(_frame.HasResponseStarted);
Assert.Throws<InvalidOperationException>(() => ((IHttpResponseFeature)_frame).OnStarting(_ => Task.CompletedTask, null));
Assert.True(_http1Connection.HasResponseStarted);
Assert.Throws<InvalidOperationException>(() => ((IHttpResponseFeature)_http1Connection).OnStarting(_ => Task.CompletedTask, null));
}
[Theory]
[MemberData(nameof(MinDataRateData))]
public void ConfiguringIHttpMinRequestBodyDataRateFeatureSetsMinRequestBodyDataRate(MinDataRate minDataRate)
{
((IFeatureCollection)_frame).Get<IHttpMinRequestBodyDataRateFeature>().MinDataRate = minDataRate;
((IFeatureCollection)_http1Connection).Get<IHttpMinRequestBodyDataRateFeature>().MinDataRate = minDataRate;
Assert.Same(minDataRate, _frame.MinRequestBodyDataRate);
Assert.Same(minDataRate, _http1Connection.MinRequestBodyDataRate);
}
[Theory]
[MemberData(nameof(MinDataRateData))]
public void ConfiguringIHttpMinResponseDataRateFeatureSetsMinResponseDataRate(MinDataRate minDataRate)
{
((IFeatureCollection)_frame).Get<IHttpMinResponseDataRateFeature>().MinDataRate = minDataRate;
((IFeatureCollection)_http1Connection).Get<IHttpMinResponseDataRateFeature>().MinDataRate = minDataRate;
Assert.Same(minDataRate, _frame.MinResponseDataRate);
Assert.Same(minDataRate, _http1Connection.MinResponseDataRate);
}
[Fact]
public void ResetResetsRequestHeaders()
{
// Arrange
var originalRequestHeaders = _frame.RequestHeaders;
_frame.RequestHeaders = new FrameRequestHeaders();
var originalRequestHeaders = _http1Connection.RequestHeaders;
_http1Connection.RequestHeaders = new HttpRequestHeaders();
// Act
_frame.Reset();
_http1Connection.Reset();
// Assert
Assert.Same(originalRequestHeaders, _frame.RequestHeaders);
Assert.Same(originalRequestHeaders, _http1Connection.RequestHeaders);
}
[Fact]
public void ResetResetsResponseHeaders()
{
// Arrange
var originalResponseHeaders = _frame.ResponseHeaders;
_frame.ResponseHeaders = new FrameResponseHeaders();
var originalResponseHeaders = _http1Connection.ResponseHeaders;
_http1Connection.ResponseHeaders = new HttpResponseHeaders();
// Act
_frame.Reset();
_http1Connection.Reset();
// Assert
Assert.Same(originalResponseHeaders, _frame.ResponseHeaders);
Assert.Same(originalResponseHeaders, _http1Connection.ResponseHeaders);
}
[Fact]
public void InitializeStreamsResetsStreams()
{
// Arrange
var messageBody = MessageBody.For(Kestrel.Core.Internal.Http.HttpVersion.Http11, (FrameRequestHeaders)_frame.RequestHeaders, _frame);
_frame.InitializeStreams(messageBody);
var messageBody = Http1MessageBody.For(Kestrel.Core.Internal.Http.HttpVersion.Http11, (HttpRequestHeaders)_http1Connection.RequestHeaders, _http1Connection);
_http1Connection.InitializeStreams(messageBody);
var originalRequestBody = _frame.RequestBody;
var originalResponseBody = _frame.ResponseBody;
_frame.RequestBody = new MemoryStream();
_frame.ResponseBody = new MemoryStream();
var originalRequestBody = _http1Connection.RequestBody;
var originalResponseBody = _http1Connection.ResponseBody;
_http1Connection.RequestBody = new MemoryStream();
_http1Connection.ResponseBody = new MemoryStream();
// Act
_frame.InitializeStreams(messageBody);
_http1Connection.InitializeStreams(messageBody);
// Assert
Assert.Same(originalRequestBody, _frame.RequestBody);
Assert.Same(originalResponseBody, _frame.ResponseBody);
Assert.Same(originalRequestBody, _http1Connection.RequestBody);
Assert.Same(originalResponseBody, _http1Connection.ResponseBody);
}
[Theory]
[MemberData(nameof(RequestLineValidData))]
public async Task TakeStartLineSetsFrameProperties(
public async Task TakeStartLineSetsHttpProtocolProperties(
string requestLine,
string expectedMethod,
string expectedRawTarget,
@ -353,15 +353,15 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await _application.Output.WriteAsync(requestLineBytes);
var readableBuffer = (await _transport.Input.ReadAsync()).Buffer;
var returnValue = _frame.TakeStartLine(readableBuffer, out _consumed, out _examined);
var returnValue = _http1Connection.TakeStartLine(readableBuffer, out _consumed, out _examined);
_transport.Input.Advance(_consumed, _examined);
Assert.True(returnValue);
Assert.Equal(expectedMethod, _frame.Method);
Assert.Equal(expectedRawTarget, _frame.RawTarget);
Assert.Equal(expectedDecodedPath, _frame.Path);
Assert.Equal(expectedQueryString, _frame.QueryString);
Assert.Equal(expectedHttpVersion, _frame.HttpVersion);
Assert.Equal(expectedMethod, _http1Connection.Method);
Assert.Equal(expectedRawTarget, _http1Connection.RawTarget);
Assert.Equal(expectedDecodedPath, _http1Connection.Path);
Assert.Equal(expectedQueryString, _http1Connection.QueryString);
Assert.Equal(expectedHttpVersion, _http1Connection.HttpVersion);
}
[Theory]
@ -376,13 +376,13 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await _application.Output.WriteAsync(requestLineBytes);
var readableBuffer = (await _transport.Input.ReadAsync()).Buffer;
var returnValue = _frame.TakeStartLine(readableBuffer, out _consumed, out _examined);
var returnValue = _http1Connection.TakeStartLine(readableBuffer, out _consumed, out _examined);
_transport.Input.Advance(_consumed, _examined);
Assert.True(returnValue);
Assert.Equal(expectedRawTarget, _frame.RawTarget);
Assert.Equal(expectedDecodedPath, _frame.Path);
Assert.Equal(expectedQueryString, _frame.QueryString);
Assert.Equal(expectedRawTarget, _http1Connection.RawTarget);
Assert.Equal(expectedDecodedPath, _http1Connection.Path);
Assert.Equal(expectedQueryString, _http1Connection.QueryString);
}
[Fact]
@ -390,7 +390,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
{
await _application.Output.WriteAsync(Encoding.ASCII.GetBytes("G"));
_frame.ParseRequest((await _transport.Input.ReadAsync()).Buffer, out _consumed, out _examined);
_http1Connection.ParseRequest((await _transport.Input.ReadAsync()).Buffer, out _consumed, out _examined);
_transport.Input.Advance(_consumed, _examined);
var expectedRequestHeadersTimeout = _serviceContext.ServerOptions.Limits.RequestHeadersTimeout.Ticks;
@ -406,7 +406,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await _application.Output.WriteAsync(requestLineBytes);
var readableBuffer = (await _transport.Input.ReadAsync()).Buffer;
var exception = Assert.Throws<BadHttpRequestException>(() => _frame.TakeStartLine(readableBuffer, out _consumed, out _examined));
var exception = Assert.Throws<BadHttpRequestException>(() => _http1Connection.TakeStartLine(readableBuffer, out _consumed, out _examined));
_transport.Input.Advance(_consumed, _examined);
Assert.Equal(CoreStrings.BadRequest_RequestLineTooLong, exception.Message);
@ -421,7 +421,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
var readableBuffer = (await _transport.Input.ReadAsync()).Buffer;
var exception = Assert.Throws<BadHttpRequestException>(() =>
_frame.TakeStartLine(readableBuffer, out _consumed, out _examined));
_http1Connection.TakeStartLine(readableBuffer, out _consumed, out _examined));
_transport.Input.Advance(_consumed, _examined);
Assert.Equal(CoreStrings.FormatBadRequest_InvalidRequestTarget_Detail(target), exception.Message);
@ -435,7 +435,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
var readableBuffer = (await _transport.Input.ReadAsync()).Buffer;
var exception = Assert.Throws<BadHttpRequestException>(() =>
_frame.TakeStartLine(readableBuffer, out _consumed, out _examined));
_http1Connection.TakeStartLine(readableBuffer, out _consumed, out _examined));
_transport.Input.Advance(_consumed, _examined);
Assert.Equal(CoreStrings.FormatBadRequest_InvalidRequestTarget_Detail(target.EscapeNonPrintable()), exception.Message);
@ -451,7 +451,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
var readableBuffer = (await _transport.Input.ReadAsync()).Buffer;
var exception = Assert.Throws<BadHttpRequestException>(() =>
_frame.TakeStartLine(readableBuffer, out _consumed, out _examined));
_http1Connection.TakeStartLine(readableBuffer, out _consumed, out _examined));
_transport.Input.Advance(_consumed, _examined);
Assert.Equal(CoreStrings.FormatBadRequest_InvalidRequestLine_Detail(requestLine.EscapeNonPrintable()), exception.Message);
@ -467,7 +467,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
var readableBuffer = (await _transport.Input.ReadAsync()).Buffer;
var exception = Assert.Throws<BadHttpRequestException>(() =>
_frame.TakeStartLine(readableBuffer, out _consumed, out _examined));
_http1Connection.TakeStartLine(readableBuffer, out _consumed, out _examined));
_transport.Input.Advance(_consumed, _examined);
Assert.Equal(CoreStrings.FormatBadRequest_InvalidRequestTarget_Detail(target.EscapeNonPrintable()), exception.Message);
@ -483,7 +483,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
var readableBuffer = (await _transport.Input.ReadAsync()).Buffer;
var exception = Assert.Throws<BadHttpRequestException>(() =>
_frame.TakeStartLine(readableBuffer, out _consumed, out _examined));
_http1Connection.TakeStartLine(readableBuffer, out _consumed, out _examined));
_transport.Input.Advance(_consumed, _examined);
Assert.Equal(CoreStrings.FormatBadRequest_InvalidRequestTarget_Detail(target.EscapeNonPrintable()), exception.Message);
@ -497,7 +497,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
var readableBuffer = (await _transport.Input.ReadAsync()).Buffer;
var exception = Assert.Throws<BadHttpRequestException>(() =>
_frame.TakeStartLine(readableBuffer, out _consumed, out _examined));
_http1Connection.TakeStartLine(readableBuffer, out _consumed, out _examined));
_transport.Input.Advance(_consumed, _examined);
Assert.Equal(405, exception.StatusCode);
@ -508,12 +508,12 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
[Fact]
public void ProcessRequestsAsyncEnablesKeepAliveTimeout()
{
var requestProcessingTask = _frame.ProcessRequestsAsync();
var requestProcessingTask = _http1Connection.ProcessRequestsAsync();
var expectedKeepAliveTimeout = _serviceContext.ServerOptions.Limits.KeepAliveTimeout.Ticks;
_timeoutControl.Verify(cc => cc.SetTimeout(expectedKeepAliveTimeout, TimeoutAction.StopProcessingNextRequest));
_frame.StopProcessingNextRequest();
_http1Connection.StopProcessingNextRequest();
_application.Output.Complete();
requestProcessingTask.Wait();
@ -523,82 +523,82 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
public async Task WriteThrowsForNonBodyResponse()
{
// Arrange
((IHttpResponseFeature)_frame).StatusCode = StatusCodes.Status304NotModified;
((IHttpResponseFeature)_http1Connection).StatusCode = StatusCodes.Status304NotModified;
// Act/Assert
await Assert.ThrowsAsync<InvalidOperationException>(() => _frame.WriteAsync(new ArraySegment<byte>(new byte[1])));
await Assert.ThrowsAsync<InvalidOperationException>(() => _http1Connection.WriteAsync(new ArraySegment<byte>(new byte[1])));
}
[Fact]
public async Task WriteAsyncThrowsForNonBodyResponse()
{
// Arrange
_frame.HttpVersion = "HTTP/1.1";
((IHttpResponseFeature)_frame).StatusCode = StatusCodes.Status304NotModified;
_http1Connection.HttpVersion = "HTTP/1.1";
((IHttpResponseFeature)_http1Connection).StatusCode = StatusCodes.Status304NotModified;
// Act/Assert
await Assert.ThrowsAsync<InvalidOperationException>(() => _frame.WriteAsync(new ArraySegment<byte>(new byte[1]), default(CancellationToken)));
await Assert.ThrowsAsync<InvalidOperationException>(() => _http1Connection.WriteAsync(new ArraySegment<byte>(new byte[1]), default(CancellationToken)));
}
[Fact]
public async Task WriteDoesNotThrowForHeadResponse()
{
// Arrange
_frame.HttpVersion = "HTTP/1.1";
((IHttpRequestFeature)_frame).Method = "HEAD";
_http1Connection.HttpVersion = "HTTP/1.1";
((IHttpRequestFeature)_http1Connection).Method = "HEAD";
// Act/Assert
await _frame.WriteAsync(new ArraySegment<byte>(new byte[1]));
await _http1Connection.WriteAsync(new ArraySegment<byte>(new byte[1]));
}
[Fact]
public async Task WriteAsyncDoesNotThrowForHeadResponse()
{
// Arrange
_frame.HttpVersion = "HTTP/1.1";
((IHttpRequestFeature)_frame).Method = "HEAD";
_http1Connection.HttpVersion = "HTTP/1.1";
((IHttpRequestFeature)_http1Connection).Method = "HEAD";
// Act/Assert
await _frame.WriteAsync(new ArraySegment<byte>(new byte[1]), default(CancellationToken));
await _http1Connection.WriteAsync(new ArraySegment<byte>(new byte[1]), default(CancellationToken));
}
[Fact]
public async Task ManuallySettingTransferEncodingThrowsForHeadResponse()
{
// Arrange
_frame.HttpVersion = "HTTP/1.1";
((IHttpRequestFeature)_frame).Method = "HEAD";
_http1Connection.HttpVersion = "HTTP/1.1";
((IHttpRequestFeature)_http1Connection).Method = "HEAD";
// Act
_frame.ResponseHeaders.Add("Transfer-Encoding", "chunked");
_http1Connection.ResponseHeaders.Add("Transfer-Encoding", "chunked");
// Assert
await Assert.ThrowsAsync<InvalidOperationException>(() => _frame.FlushAsync());
await Assert.ThrowsAsync<InvalidOperationException>(() => _http1Connection.FlushAsync());
}
[Fact]
public async Task ManuallySettingTransferEncodingThrowsForNoBodyResponse()
{
// Arrange
_frame.HttpVersion = "HTTP/1.1";
((IHttpResponseFeature)_frame).StatusCode = StatusCodes.Status304NotModified;
_http1Connection.HttpVersion = "HTTP/1.1";
((IHttpResponseFeature)_http1Connection).StatusCode = StatusCodes.Status304NotModified;
// Act
_frame.ResponseHeaders.Add("Transfer-Encoding", "chunked");
_http1Connection.ResponseHeaders.Add("Transfer-Encoding", "chunked");
// Assert
await Assert.ThrowsAsync<InvalidOperationException>(() => _frame.FlushAsync());
await Assert.ThrowsAsync<InvalidOperationException>(() => _http1Connection.FlushAsync());
}
[Fact]
public async Task RequestProcessingTaskIsUnwrapped()
{
var requestProcessingTask = _frame.ProcessRequestsAsync();
var requestProcessingTask = _http1Connection.ProcessRequestsAsync();
var data = Encoding.ASCII.GetBytes("GET / HTTP/1.1\r\nHost:\r\n\r\n");
await _application.Output.WriteAsync(data);
_frame.StopProcessingNextRequest();
_http1Connection.StopProcessingNextRequest();
Assert.IsNotType<Task<Task>>(requestProcessingTask);
await requestProcessingTask.TimeoutAfter(TimeSpan.FromSeconds(10));
@ -608,74 +608,74 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
[Fact]
public async Task RequestAbortedTokenIsResetBeforeLastWriteWithContentLength()
{
_frame.ResponseHeaders["Content-Length"] = "12";
_http1Connection.ResponseHeaders["Content-Length"] = "12";
// Need to compare WaitHandle ref since CancellationToken is struct
var original = _frame.RequestAborted.WaitHandle;
var original = _http1Connection.RequestAborted.WaitHandle;
foreach (var ch in "hello, worl")
{
await _frame.WriteAsync(new ArraySegment<byte>(new[] { (byte)ch }));
Assert.Same(original, _frame.RequestAborted.WaitHandle);
await _http1Connection.WriteAsync(new ArraySegment<byte>(new[] { (byte)ch }));
Assert.Same(original, _http1Connection.RequestAborted.WaitHandle);
}
await _frame.WriteAsync(new ArraySegment<byte>(new[] { (byte)'d' }));
Assert.NotSame(original, _frame.RequestAborted.WaitHandle);
await _http1Connection.WriteAsync(new ArraySegment<byte>(new[] { (byte)'d' }));
Assert.NotSame(original, _http1Connection.RequestAborted.WaitHandle);
}
[Fact]
public async Task RequestAbortedTokenIsResetBeforeLastWriteAsyncWithContentLength()
{
_frame.ResponseHeaders["Content-Length"] = "12";
_http1Connection.ResponseHeaders["Content-Length"] = "12";
// Need to compare WaitHandle ref since CancellationToken is struct
var original = _frame.RequestAborted.WaitHandle;
var original = _http1Connection.RequestAborted.WaitHandle;
foreach (var ch in "hello, worl")
{
await _frame.WriteAsync(new ArraySegment<byte>(new[] { (byte)ch }), default(CancellationToken));
Assert.Same(original, _frame.RequestAborted.WaitHandle);
await _http1Connection.WriteAsync(new ArraySegment<byte>(new[] { (byte)ch }), default(CancellationToken));
Assert.Same(original, _http1Connection.RequestAborted.WaitHandle);
}
await _frame.WriteAsync(new ArraySegment<byte>(new[] { (byte)'d' }), default(CancellationToken));
Assert.NotSame(original, _frame.RequestAborted.WaitHandle);
await _http1Connection.WriteAsync(new ArraySegment<byte>(new[] { (byte)'d' }), default(CancellationToken));
Assert.NotSame(original, _http1Connection.RequestAborted.WaitHandle);
}
[Fact]
public async Task RequestAbortedTokenIsResetBeforeLastWriteAsyncAwaitedWithContentLength()
{
_frame.ResponseHeaders["Content-Length"] = "12";
_http1Connection.ResponseHeaders["Content-Length"] = "12";
// Need to compare WaitHandle ref since CancellationToken is struct
var original = _frame.RequestAborted.WaitHandle;
var original = _http1Connection.RequestAborted.WaitHandle;
// Only first write can be WriteAsyncAwaited
var startingTask = _frame.InitializeResponseAwaited(Task.CompletedTask, 1);
await _frame.WriteAsyncAwaited(startingTask, new ArraySegment<byte>(new[] { (byte)'h' }), default(CancellationToken));
Assert.Same(original, _frame.RequestAborted.WaitHandle);
var startingTask = _http1Connection.InitializeResponseAwaited(Task.CompletedTask, 1);
await _http1Connection.WriteAsyncAwaited(startingTask, new ArraySegment<byte>(new[] { (byte)'h' }), default(CancellationToken));
Assert.Same(original, _http1Connection.RequestAborted.WaitHandle);
foreach (var ch in "ello, worl")
{
await _frame.WriteAsync(new ArraySegment<byte>(new[] { (byte)ch }), default(CancellationToken));
Assert.Same(original, _frame.RequestAborted.WaitHandle);
await _http1Connection.WriteAsync(new ArraySegment<byte>(new[] { (byte)ch }), default(CancellationToken));
Assert.Same(original, _http1Connection.RequestAborted.WaitHandle);
}
await _frame.WriteAsync(new ArraySegment<byte>(new[] { (byte)'d' }), default(CancellationToken));
Assert.NotSame(original, _frame.RequestAborted.WaitHandle);
await _http1Connection.WriteAsync(new ArraySegment<byte>(new[] { (byte)'d' }), default(CancellationToken));
Assert.NotSame(original, _http1Connection.RequestAborted.WaitHandle);
}
[Fact]
public async Task RequestAbortedTokenIsResetBeforeLastWriteWithChunkedEncoding()
{
// Need to compare WaitHandle ref since CancellationToken is struct
var original = _frame.RequestAborted.WaitHandle;
var original = _http1Connection.RequestAborted.WaitHandle;
_frame.HttpVersion = "HTTP/1.1";
await _frame.WriteAsync(new ArraySegment<byte>(Encoding.ASCII.GetBytes("hello, world")), default(CancellationToken));
Assert.Same(original, _frame.RequestAborted.WaitHandle);
_http1Connection.HttpVersion = "HTTP/1.1";
await _http1Connection.WriteAsync(new ArraySegment<byte>(Encoding.ASCII.GetBytes("hello, world")), default(CancellationToken));
Assert.Same(original, _http1Connection.RequestAborted.WaitHandle);
await _frame.ProduceEndAsync();
Assert.NotSame(original, _frame.RequestAborted.WaitHandle);
await _http1Connection.ProduceEndAsync();
Assert.NotSame(original, _http1Connection.RequestAborted.WaitHandle);
}
[Fact]
@ -696,7 +696,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
var readableBuffer = (await _transport.Input.ReadAsync()).Buffer;
var exception = Assert.Throws<BadHttpRequestException>(() =>
_frame.TakeStartLine(readableBuffer, out _consumed, out _examined));
_http1Connection.TakeStartLine(readableBuffer, out _consumed, out _examined));
_transport.Input.Advance(_consumed, _examined);
Assert.Equal(CoreStrings.FormatBadRequest_InvalidRequestTarget_Detail(string.Empty), exception.Message);
@ -722,22 +722,22 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
var headers0 = MakeHeaders(header0Count);
var headers1 = MakeHeaders(header1Count, header0Count);
var requestProcessingTask = _frame.ProcessRequestsAsync();
var requestProcessingTask = _http1Connection.ProcessRequestsAsync();
await _application.Output.WriteAsync(Encoding.ASCII.GetBytes("GET / HTTP/1.0\r\n"));
await WaitForCondition(TimeSpan.FromSeconds(1), () => _frame.RequestHeaders != null);
Assert.Equal(0, _frame.RequestHeaders.Count);
await WaitForCondition(TimeSpan.FromSeconds(1), () => _http1Connection.RequestHeaders != null);
Assert.Equal(0, _http1Connection.RequestHeaders.Count);
await _application.Output.WriteAsync(Encoding.ASCII.GetBytes(headers0));
await WaitForCondition(TimeSpan.FromSeconds(1), () => _frame.RequestHeaders.Count >= header0Count);
Assert.Equal(header0Count, _frame.RequestHeaders.Count);
await WaitForCondition(TimeSpan.FromSeconds(1), () => _http1Connection.RequestHeaders.Count >= header0Count);
Assert.Equal(header0Count, _http1Connection.RequestHeaders.Count);
await _application.Output.WriteAsync(Encoding.ASCII.GetBytes(headers1));
await WaitForCondition(TimeSpan.FromSeconds(1), () => _frame.RequestHeaders.Count >= header0Count + header1Count);
Assert.Equal(header0Count + header1Count, _frame.RequestHeaders.Count);
await WaitForCondition(TimeSpan.FromSeconds(1), () => _http1Connection.RequestHeaders.Count >= header0Count + header1Count);
Assert.Equal(header0Count + header1Count, _http1Connection.RequestHeaders.Count);
await _application.Output.WriteAsync(Encoding.ASCII.GetBytes("\r\n"));
Assert.Equal(header0Count + header1Count, _frame.RequestHeaders.Count);
Assert.Equal(header0Count + header1Count, _http1Connection.RequestHeaders.Count);
await requestProcessingTask.TimeoutAfter(TimeSpan.FromSeconds(10));
}
@ -756,29 +756,29 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
var headers0 = MakeHeaders(header0Count);
var headers1 = MakeHeaders(header1Count, header0Count);
var requestProcessingTask = _frame.ProcessRequestsAsync();
var requestProcessingTask = _http1Connection.ProcessRequestsAsync();
await _application.Output.WriteAsync(Encoding.ASCII.GetBytes("GET / HTTP/1.0\r\n"));
await WaitForCondition(TimeSpan.FromSeconds(1), () => _frame.RequestHeaders != null);
Assert.Equal(0, _frame.RequestHeaders.Count);
await WaitForCondition(TimeSpan.FromSeconds(1), () => _http1Connection.RequestHeaders != null);
Assert.Equal(0, _http1Connection.RequestHeaders.Count);
var newRequestHeaders = new RequestHeadersWrapper(_frame.RequestHeaders);
_frame.RequestHeaders = newRequestHeaders;
Assert.Same(newRequestHeaders, _frame.RequestHeaders);
var newRequestHeaders = new RequestHeadersWrapper(_http1Connection.RequestHeaders);
_http1Connection.RequestHeaders = newRequestHeaders;
Assert.Same(newRequestHeaders, _http1Connection.RequestHeaders);
await _application.Output.WriteAsync(Encoding.ASCII.GetBytes(headers0));
await WaitForCondition(TimeSpan.FromSeconds(1), () => _frame.RequestHeaders.Count >= header0Count);
Assert.Same(newRequestHeaders, _frame.RequestHeaders);
Assert.Equal(header0Count, _frame.RequestHeaders.Count);
await WaitForCondition(TimeSpan.FromSeconds(1), () => _http1Connection.RequestHeaders.Count >= header0Count);
Assert.Same(newRequestHeaders, _http1Connection.RequestHeaders);
Assert.Equal(header0Count, _http1Connection.RequestHeaders.Count);
await _application.Output.WriteAsync(Encoding.ASCII.GetBytes(headers1));
await WaitForCondition(TimeSpan.FromSeconds(1), () => _frame.RequestHeaders.Count >= header0Count + header1Count);
Assert.Same(newRequestHeaders, _frame.RequestHeaders);
Assert.Equal(header0Count + header1Count, _frame.RequestHeaders.Count);
await WaitForCondition(TimeSpan.FromSeconds(1), () => _http1Connection.RequestHeaders.Count >= header0Count + header1Count);
Assert.Same(newRequestHeaders, _http1Connection.RequestHeaders);
Assert.Equal(header0Count + header1Count, _http1Connection.RequestHeaders.Count);
await _application.Output.WriteAsync(Encoding.ASCII.GetBytes("\r\n"));
Assert.Same(newRequestHeaders, _frame.RequestHeaders);
Assert.Equal(header0Count + header1Count, _frame.RequestHeaders.Count);
Assert.Same(newRequestHeaders, _http1Connection.RequestHeaders);
Assert.Equal(header0Count + header1Count, _http1Connection.RequestHeaders.Count);
await requestProcessingTask.TimeoutAfter(TimeSpan.FromSeconds(10));
}
@ -788,11 +788,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
{
// Act
// This would normally be set by the MessageBody during the first read.
_frame.HasStartedConsumingRequestBody = true;
_http1Connection.HasStartedConsumingRequestBody = true;
// Assert
Assert.True(((IHttpMaxRequestBodySizeFeature)_frame).IsReadOnly);
var ex = Assert.Throws<InvalidOperationException>(() => ((IHttpMaxRequestBodySizeFeature)_frame).MaxRequestBodySize = 1);
Assert.True(((IHttpMaxRequestBodySizeFeature)_http1Connection).IsReadOnly);
var ex = Assert.Throws<InvalidOperationException>(() => ((IHttpMaxRequestBodySizeFeature)_http1Connection).MaxRequestBodySize = 1);
Assert.Equal(CoreStrings.MaxRequestBodySizeCannotBeModifiedAfterRead, ex.Message);
}
@ -800,7 +800,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
public void ThrowsWhenMaxRequestBodySizeIsSetToANegativeValue()
{
// Assert
var ex = Assert.Throws<ArgumentOutOfRangeException>(() => ((IHttpMaxRequestBodySizeFeature)_frame).MaxRequestBodySize = -1);
var ex = Assert.Throws<ArgumentOutOfRangeException>(() => ((IHttpMaxRequestBodySizeFeature)_http1Connection).MaxRequestBodySize = -1);
Assert.StartsWith(CoreStrings.NonNegativeNumberOrNullRequired, ex.Message);
}

View File

@ -1011,7 +1011,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await StopConnectionAsync(expectedLastStreamId: 1, ignoreNonGoAwayFrames: false);
var responseHeaders = new FrameResponseHeaders();
var responseHeaders = new HttpResponseHeaders();
_hpackDecoder.Decode(headersFrame.HeadersPayload, responseHeaders);
_hpackDecoder.Decode(continuationFrame1.HeadersPayload, responseHeaders);
_hpackDecoder.Decode(continuationFrame2.HeadersPayload, responseHeaders);

View File

@ -11,23 +11,23 @@ using Xunit;
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
{
public class FrameConnectionManagerTests
public class HttpConnectionManagerTests
{
[Fact]
public void UnrootedConnectionsGetRemovedFromHeartbeat()
{
var connectionId = "0";
var trace = new Mock<IKestrelTrace>();
var frameConnectionManager = new FrameConnectionManager(trace.Object, ResourceCounter.Unlimited);
var httpConnectionManager = new HttpConnectionManager(trace.Object, ResourceCounter.Unlimited);
// Create FrameConnection in inner scope so it doesn't get rooted by the current frame.
UnrootedConnectionsGetRemovedFromHeartbeatInnerScope(connectionId, frameConnectionManager, trace);
// Create HttpConnection in inner scope so it doesn't get rooted by the current frame.
UnrootedConnectionsGetRemovedFromHeartbeatInnerScope(connectionId, httpConnectionManager, trace);
GC.Collect();
GC.WaitForPendingFinalizers();
var connectionCount = 0;
frameConnectionManager.Walk(_ => connectionCount++);
httpConnectionManager.Walk(_ => connectionCount++);
Assert.Equal(0, connectionCount);
trace.Verify(t => t.ApplicationNeverCompleted(connectionId), Times.Once());
@ -36,25 +36,25 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
[MethodImpl(MethodImplOptions.NoInlining)]
private void UnrootedConnectionsGetRemovedFromHeartbeatInnerScope(
string connectionId,
FrameConnectionManager frameConnectionManager,
HttpConnectionManager httpConnectionManager,
Mock<IKestrelTrace> trace)
{
var frameConnection = new FrameConnection(new FrameConnectionContext
var httpConnection = new HttpConnection(new HttpConnectionContext
{
ServiceContext = new TestServiceContext(),
ConnectionId = connectionId
});
frameConnectionManager.AddConnection(0, frameConnection);
httpConnectionManager.AddConnection(0, httpConnection);
var connectionCount = 0;
frameConnectionManager.Walk(_ => connectionCount++);
httpConnectionManager.Walk(_ => connectionCount++);
Assert.Equal(1, connectionCount);
trace.Verify(t => t.ApplicationNeverCompleted(connectionId), Times.Never());
// Ensure frameConnection doesn't get GC'd before this point.
GC.KeepAlive(frameConnection);
// Ensure httpConnection doesn't get GC'd before this point.
GC.KeepAlive(httpConnection);
}
}
}

View File

@ -15,24 +15,24 @@ using Xunit;
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
{
public class FrameConnectionTests : IDisposable
public class HttpConnectionTests : IDisposable
{
private readonly PipeFactory _pipeFactory;
private readonly FrameConnectionContext _frameConnectionContext;
private readonly FrameConnection _frameConnection;
private readonly HttpConnectionContext _httpConnectionContext;
private readonly HttpConnection _httpConnection;
public FrameConnectionTests()
public HttpConnectionTests()
{
_pipeFactory = new PipeFactory();
var pair = _pipeFactory.CreateConnectionPair();
_frameConnectionContext = new FrameConnectionContext
_httpConnectionContext = new HttpConnectionContext
{
ConnectionId = "0123456789",
ConnectionAdapters = new List<IConnectionAdapter>(),
ConnectionFeatures = new FeatureCollection(),
PipeFactory = _pipeFactory,
FrameConnectionId = long.MinValue,
HttpConnectionId = long.MinValue,
Application = pair.Application,
Transport = pair.Transport,
ServiceContext = new TestServiceContext
@ -41,7 +41,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
}
};
_frameConnection = new FrameConnection(_frameConnectionContext);
_httpConnection = new HttpConnection(_httpConnectionContext);
}
public void Dispose()
@ -54,15 +54,15 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
{
var mockDebugger = new Mock<IDebugger>();
mockDebugger.SetupGet(g => g.IsAttached).Returns(true);
_frameConnection.Debugger = mockDebugger.Object;
_frameConnection.CreateFrame(new DummyApplication(), _frameConnectionContext.Transport, _frameConnectionContext.Application);
_httpConnection.Debugger = mockDebugger.Object;
_httpConnection.CreateHttp1Connection(new DummyApplication(), _httpConnectionContext.Transport, _httpConnectionContext.Application);
var now = DateTimeOffset.Now;
_frameConnection.Tick(now);
_frameConnection.SetTimeout(1, TimeoutAction.SendTimeoutResponse);
_frameConnection.Tick(now.AddTicks(2).Add(Heartbeat.Interval));
_httpConnection.Tick(now);
_httpConnection.SetTimeout(1, TimeoutAction.SendTimeoutResponse);
_httpConnection.Tick(now.AddTicks(2).Add(Heartbeat.Interval));
Assert.False(_frameConnection.RequestTimedOut);
Assert.False(_httpConnection.RequestTimedOut);
}
[Fact]
@ -70,14 +70,14 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
{
var mockDebugger = new Mock<IDebugger>();
mockDebugger.SetupGet(g => g.IsAttached).Returns(true);
_frameConnection.Debugger = mockDebugger.Object;
_httpConnection.Debugger = mockDebugger.Object;
var bytesPerSecond = 100;
var mockLogger = new Mock<IKestrelTrace>();
mockLogger.Setup(l => l.RequestBodyMininumDataRateNotSatisfied(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<double>())).Throws(new InvalidOperationException("Should not log"));
TickBodyWithMinimumDataRate(mockLogger.Object, bytesPerSecond);
Assert.False(_frameConnection.RequestTimedOut);
Assert.False(_httpConnection.RequestTimedOut);
}
[Fact]
@ -88,7 +88,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
TickBodyWithMinimumDataRate(mockLogger.Object, bytesPerSecond);
// Timed out
Assert.True(_frameConnection.RequestTimedOut);
Assert.True(_httpConnection.RequestTimedOut);
mockLogger.Verify(logger =>
logger.RequestBodyMininumDataRateNotSatisfied(It.IsAny<string>(), It.IsAny<string>(), bytesPerSecond), Times.Once);
}
@ -97,24 +97,24 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
{
var gracePeriod = TimeSpan.FromSeconds(5);
_frameConnectionContext.ServiceContext.ServerOptions.Limits.MinRequestBodyDataRate =
_httpConnectionContext.ServiceContext.ServerOptions.Limits.MinRequestBodyDataRate =
new MinDataRate(bytesPerSecond: bytesPerSecond, gracePeriod: gracePeriod);
_frameConnectionContext.ServiceContext.Log = logger;
_httpConnectionContext.ServiceContext.Log = logger;
_frameConnection.CreateFrame(new DummyApplication(), _frameConnectionContext.Transport, _frameConnectionContext.Application);
_frameConnection.Frame.Reset();
_httpConnection.CreateHttp1Connection(new DummyApplication(), _httpConnectionContext.Transport, _httpConnectionContext.Application);
_httpConnection.Http1Connection.Reset();
// Initialize timestamp
var now = DateTimeOffset.UtcNow;
_frameConnection.Tick(now);
_httpConnection.Tick(now);
_frameConnection.StartTimingReads();
_httpConnection.StartTimingReads();
// Tick after grace period w/ low data rate
now += gracePeriod + TimeSpan.FromSeconds(1);
_frameConnection.BytesRead(1);
_frameConnection.Tick(now);
_httpConnection.BytesRead(1);
_httpConnection.Tick(now);
}
[Fact]
@ -123,38 +123,38 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
var bytesPerSecond = 100;
var gracePeriod = TimeSpan.FromSeconds(2);
_frameConnectionContext.ServiceContext.ServerOptions.Limits.MinRequestBodyDataRate =
_httpConnectionContext.ServiceContext.ServerOptions.Limits.MinRequestBodyDataRate =
new MinDataRate(bytesPerSecond: bytesPerSecond, gracePeriod: gracePeriod);
var mockLogger = new Mock<IKestrelTrace>();
_frameConnectionContext.ServiceContext.Log = mockLogger.Object;
_httpConnectionContext.ServiceContext.Log = mockLogger.Object;
_frameConnection.CreateFrame(new DummyApplication(), _frameConnectionContext.Transport, _frameConnectionContext.Application);
_frameConnection.Frame.Reset();
_httpConnection.CreateHttp1Connection(new DummyApplication(), _httpConnectionContext.Transport, _httpConnectionContext.Application);
_httpConnection.Http1Connection.Reset();
// Initialize timestamp
var now = DateTimeOffset.UtcNow;
_frameConnection.Tick(now);
_httpConnection.Tick(now);
_frameConnection.StartTimingReads();
_httpConnection.StartTimingReads();
// Tick during grace period w/ low data rate
now += TimeSpan.FromSeconds(1);
_frameConnection.BytesRead(10);
_frameConnection.Tick(now);
_httpConnection.BytesRead(10);
_httpConnection.Tick(now);
// Not timed out
Assert.False(_frameConnection.RequestTimedOut);
Assert.False(_httpConnection.RequestTimedOut);
mockLogger.Verify(logger =>
logger.RequestBodyMininumDataRateNotSatisfied(It.IsAny<string>(), It.IsAny<string>(), bytesPerSecond), Times.Never);
// Tick after grace period w/ low data rate
now += TimeSpan.FromSeconds(2);
_frameConnection.BytesRead(10);
_frameConnection.Tick(now);
_httpConnection.BytesRead(10);
_httpConnection.Tick(now);
// Timed out
Assert.True(_frameConnection.RequestTimedOut);
Assert.True(_httpConnection.RequestTimedOut);
mockLogger.Verify(logger =>
logger.RequestBodyMininumDataRateNotSatisfied(It.IsAny<string>(), It.IsAny<string>(), bytesPerSecond), Times.Once);
}
@ -165,73 +165,73 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
var bytesPerSecond = 100;
var gracePeriod = TimeSpan.FromSeconds(2);
_frameConnectionContext.ServiceContext.ServerOptions.Limits.MinRequestBodyDataRate =
_httpConnectionContext.ServiceContext.ServerOptions.Limits.MinRequestBodyDataRate =
new MinDataRate(bytesPerSecond: bytesPerSecond, gracePeriod: gracePeriod);
var mockLogger = new Mock<IKestrelTrace>();
_frameConnectionContext.ServiceContext.Log = mockLogger.Object;
_httpConnectionContext.ServiceContext.Log = mockLogger.Object;
_frameConnection.CreateFrame(new DummyApplication(), _frameConnectionContext.Transport, _frameConnectionContext.Application);
_frameConnection.Frame.Reset();
_httpConnection.CreateHttp1Connection(new DummyApplication(), _httpConnectionContext.Transport, _httpConnectionContext.Application);
_httpConnection.Http1Connection.Reset();
// Initialize timestamp
var now = DateTimeOffset.UtcNow;
_frameConnection.Tick(now);
_httpConnection.Tick(now);
_frameConnection.StartTimingReads();
_httpConnection.StartTimingReads();
// Set base data rate to 200 bytes/second
now += gracePeriod;
_frameConnection.BytesRead(400);
_frameConnection.Tick(now);
_httpConnection.BytesRead(400);
_httpConnection.Tick(now);
// Data rate: 200 bytes/second
now += TimeSpan.FromSeconds(1);
_frameConnection.BytesRead(200);
_frameConnection.Tick(now);
_httpConnection.BytesRead(200);
_httpConnection.Tick(now);
// Not timed out
Assert.False(_frameConnection.RequestTimedOut);
Assert.False(_httpConnection.RequestTimedOut);
mockLogger.Verify(logger =>
logger.RequestBodyMininumDataRateNotSatisfied(It.IsAny<string>(), It.IsAny<string>(), bytesPerSecond), Times.Never);
// Data rate: 150 bytes/second
now += TimeSpan.FromSeconds(1);
_frameConnection.BytesRead(0);
_frameConnection.Tick(now);
_httpConnection.BytesRead(0);
_httpConnection.Tick(now);
// Not timed out
Assert.False(_frameConnection.RequestTimedOut);
Assert.False(_httpConnection.RequestTimedOut);
mockLogger.Verify(logger =>
logger.RequestBodyMininumDataRateNotSatisfied(It.IsAny<string>(), It.IsAny<string>(), bytesPerSecond), Times.Never);
// Data rate: 120 bytes/second
now += TimeSpan.FromSeconds(1);
_frameConnection.BytesRead(0);
_frameConnection.Tick(now);
_httpConnection.BytesRead(0);
_httpConnection.Tick(now);
// Not timed out
Assert.False(_frameConnection.RequestTimedOut);
Assert.False(_httpConnection.RequestTimedOut);
mockLogger.Verify(logger =>
logger.RequestBodyMininumDataRateNotSatisfied(It.IsAny<string>(), It.IsAny<string>(), bytesPerSecond), Times.Never);
// Data rate: 100 bytes/second
now += TimeSpan.FromSeconds(1);
_frameConnection.BytesRead(0);
_frameConnection.Tick(now);
_httpConnection.BytesRead(0);
_httpConnection.Tick(now);
// Not timed out
Assert.False(_frameConnection.RequestTimedOut);
Assert.False(_httpConnection.RequestTimedOut);
mockLogger.Verify(logger =>
logger.RequestBodyMininumDataRateNotSatisfied(It.IsAny<string>(), It.IsAny<string>(), bytesPerSecond), Times.Never);
// Data rate: ~85 bytes/second
now += TimeSpan.FromSeconds(1);
_frameConnection.BytesRead(0);
_frameConnection.Tick(now);
_httpConnection.BytesRead(0);
_httpConnection.Tick(now);
// Timed out
Assert.True(_frameConnection.RequestTimedOut);
Assert.True(_httpConnection.RequestTimedOut);
mockLogger.Verify(logger =>
logger.RequestBodyMininumDataRateNotSatisfied(It.IsAny<string>(), It.IsAny<string>(), bytesPerSecond), Times.Once);
}
@ -241,64 +241,64 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
{
var systemClock = new MockSystemClock();
_frameConnectionContext.ServiceContext.ServerOptions.Limits.MinRequestBodyDataRate =
_httpConnectionContext.ServiceContext.ServerOptions.Limits.MinRequestBodyDataRate =
new MinDataRate(bytesPerSecond: 100, gracePeriod: TimeSpan.FromSeconds(2));
_frameConnectionContext.ServiceContext.SystemClock = systemClock;
_httpConnectionContext.ServiceContext.SystemClock = systemClock;
var mockLogger = new Mock<IKestrelTrace>();
_frameConnectionContext.ServiceContext.Log = mockLogger.Object;
_httpConnectionContext.ServiceContext.Log = mockLogger.Object;
_frameConnection.CreateFrame(new DummyApplication(), _frameConnectionContext.Transport, _frameConnectionContext.Application);
_frameConnection.Frame.Reset();
_httpConnection.CreateHttp1Connection(new DummyApplication(), _httpConnectionContext.Transport, _httpConnectionContext.Application);
_httpConnection.Http1Connection.Reset();
// Initialize timestamp
_frameConnection.Tick(systemClock.UtcNow);
_httpConnection.Tick(systemClock.UtcNow);
_frameConnection.StartTimingReads();
_httpConnection.StartTimingReads();
// Tick at 3s, expected counted time is 3s, expected data rate is 200 bytes/second
systemClock.UtcNow += TimeSpan.FromSeconds(3);
_frameConnection.BytesRead(600);
_frameConnection.Tick(systemClock.UtcNow);
_httpConnection.BytesRead(600);
_httpConnection.Tick(systemClock.UtcNow);
// Pause at 3.5s
systemClock.UtcNow += TimeSpan.FromSeconds(0.5);
_frameConnection.PauseTimingReads();
_httpConnection.PauseTimingReads();
// Tick at 4s, expected counted time is 4s (first tick after pause goes through), expected data rate is 150 bytes/second
systemClock.UtcNow += TimeSpan.FromSeconds(0.5);
_frameConnection.Tick(systemClock.UtcNow);
_httpConnection.Tick(systemClock.UtcNow);
// Tick at 6s, expected counted time is 4s, expected data rate is 150 bytes/second
systemClock.UtcNow += TimeSpan.FromSeconds(2);
_frameConnection.Tick(systemClock.UtcNow);
_httpConnection.Tick(systemClock.UtcNow);
// Not timed out
Assert.False(_frameConnection.RequestTimedOut);
Assert.False(_httpConnection.RequestTimedOut);
mockLogger.Verify(
logger => logger.RequestBodyMininumDataRateNotSatisfied(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<double>()),
Times.Never);
// Resume at 6.5s
systemClock.UtcNow += TimeSpan.FromSeconds(0.5);
_frameConnection.ResumeTimingReads();
_httpConnection.ResumeTimingReads();
// Tick at 9s, expected counted time is 6s, expected data rate is 100 bytes/second
systemClock.UtcNow += TimeSpan.FromSeconds(1.5);
_frameConnection.Tick(systemClock.UtcNow);
_httpConnection.Tick(systemClock.UtcNow);
// Not timed out
Assert.False(_frameConnection.RequestTimedOut);
Assert.False(_httpConnection.RequestTimedOut);
mockLogger.Verify(
logger => logger.RequestBodyMininumDataRateNotSatisfied(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<double>()),
Times.Never);
// Tick at 10s, expected counted time is 7s, expected data rate drops below 100 bytes/second
systemClock.UtcNow += TimeSpan.FromSeconds(1);
_frameConnection.Tick(systemClock.UtcNow);
_httpConnection.Tick(systemClock.UtcNow);
// Timed out
Assert.True(_frameConnection.RequestTimedOut);
Assert.True(_httpConnection.RequestTimedOut);
mockLogger.Verify(
logger => logger.RequestBodyMininumDataRateNotSatisfied(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<double>()),
Times.Once);
@ -309,57 +309,57 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
{
var systemClock = new MockSystemClock();
_frameConnectionContext.ServiceContext.ServerOptions.Limits.MinRequestBodyDataRate =
_httpConnectionContext.ServiceContext.ServerOptions.Limits.MinRequestBodyDataRate =
new MinDataRate(bytesPerSecond: 100, gracePeriod: TimeSpan.FromSeconds(2));
_frameConnectionContext.ServiceContext.SystemClock = systemClock;
_httpConnectionContext.ServiceContext.SystemClock = systemClock;
var mockLogger = new Mock<IKestrelTrace>();
_frameConnectionContext.ServiceContext.Log = mockLogger.Object;
_httpConnectionContext.ServiceContext.Log = mockLogger.Object;
_frameConnection.CreateFrame(new DummyApplication(), _frameConnectionContext.Transport, _frameConnectionContext.Application);
_frameConnection.Frame.Reset();
_httpConnection.CreateHttp1Connection(new DummyApplication(), _httpConnectionContext.Transport, _httpConnectionContext.Application);
_httpConnection.Http1Connection.Reset();
// Initialize timestamp
_frameConnection.Tick(systemClock.UtcNow);
_httpConnection.Tick(systemClock.UtcNow);
_frameConnection.StartTimingReads();
_httpConnection.StartTimingReads();
// Tick at 2s, expected counted time is 2s, expected data rate is 100 bytes/second
systemClock.UtcNow += TimeSpan.FromSeconds(2);
_frameConnection.BytesRead(200);
_frameConnection.Tick(systemClock.UtcNow);
_httpConnection.BytesRead(200);
_httpConnection.Tick(systemClock.UtcNow);
// Not timed out
Assert.False(_frameConnection.RequestTimedOut);
Assert.False(_httpConnection.RequestTimedOut);
mockLogger.Verify(
logger => logger.RequestBodyMininumDataRateNotSatisfied(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<double>()),
Times.Never);
// Pause at 2.25s
systemClock.UtcNow += TimeSpan.FromSeconds(0.25);
_frameConnection.PauseTimingReads();
_httpConnection.PauseTimingReads();
// Resume at 2.5s
systemClock.UtcNow += TimeSpan.FromSeconds(0.25);
_frameConnection.ResumeTimingReads();
_httpConnection.ResumeTimingReads();
// Tick at 3s, expected counted time is 3s, expected data rate is 100 bytes/second
systemClock.UtcNow += TimeSpan.FromSeconds(0.5);
_frameConnection.BytesRead(100);
_frameConnection.Tick(systemClock.UtcNow);
_httpConnection.BytesRead(100);
_httpConnection.Tick(systemClock.UtcNow);
// Not timed out
Assert.False(_frameConnection.RequestTimedOut);
Assert.False(_httpConnection.RequestTimedOut);
mockLogger.Verify(
logger => logger.RequestBodyMininumDataRateNotSatisfied(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<double>()),
Times.Never);
// Tick at 4s, expected counted time is 4s, expected data rate drops below 100 bytes/second
systemClock.UtcNow += TimeSpan.FromSeconds(1);
_frameConnection.Tick(systemClock.UtcNow);
_httpConnection.Tick(systemClock.UtcNow);
// Timed out
Assert.True(_frameConnection.RequestTimedOut);
Assert.True(_httpConnection.RequestTimedOut);
mockLogger.Verify(
logger => logger.RequestBodyMininumDataRateNotSatisfied(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<double>()),
Times.Once);
@ -371,39 +371,39 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
var systemClock = new MockSystemClock();
var timeout = TimeSpan.FromSeconds(5);
_frameConnectionContext.ServiceContext.ServerOptions.Limits.MinRequestBodyDataRate =
_httpConnectionContext.ServiceContext.ServerOptions.Limits.MinRequestBodyDataRate =
new MinDataRate(bytesPerSecond: 100, gracePeriod: TimeSpan.FromSeconds(2));
_frameConnectionContext.ServiceContext.SystemClock = systemClock;
_httpConnectionContext.ServiceContext.SystemClock = systemClock;
var mockLogger = new Mock<IKestrelTrace>();
_frameConnectionContext.ServiceContext.Log = mockLogger.Object;
_httpConnectionContext.ServiceContext.Log = mockLogger.Object;
_frameConnection.CreateFrame(new DummyApplication(), _frameConnectionContext.Transport, _frameConnectionContext.Application);
_frameConnection.Frame.Reset();
_httpConnection.CreateHttp1Connection(new DummyApplication(), _httpConnectionContext.Transport, _httpConnectionContext.Application);
_httpConnection.Http1Connection.Reset();
var startTime = systemClock.UtcNow;
// Initialize timestamp
_frameConnection.Tick(startTime);
_httpConnection.Tick(startTime);
_frameConnection.StartTimingReads();
_httpConnection.StartTimingReads();
_frameConnection.SetTimeout(timeout.Ticks, TimeoutAction.StopProcessingNextRequest);
_httpConnection.SetTimeout(timeout.Ticks, TimeoutAction.StopProcessingNextRequest);
// Tick beyond grace period with low data rate
systemClock.UtcNow += TimeSpan.FromSeconds(3);
_frameConnection.BytesRead(1);
_frameConnection.Tick(systemClock.UtcNow);
_httpConnection.BytesRead(1);
_httpConnection.Tick(systemClock.UtcNow);
// Not timed out
Assert.False(_frameConnection.RequestTimedOut);
Assert.False(_httpConnection.RequestTimedOut);
// Tick just past timeout period, adjusted by Heartbeat.Interval
systemClock.UtcNow = startTime + timeout + Heartbeat.Interval + TimeSpan.FromTicks(1);
_frameConnection.Tick(systemClock.UtcNow);
_httpConnection.Tick(systemClock.UtcNow);
// Timed out
Assert.True(_frameConnection.RequestTimedOut);
Assert.True(_httpConnection.RequestTimedOut);
}
[Fact]
@ -412,31 +412,31 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
var systemClock = new MockSystemClock();
var aborted = new ManualResetEventSlim();
_frameConnectionContext.ServiceContext.ServerOptions.Limits.MinResponseDataRate =
_httpConnectionContext.ServiceContext.ServerOptions.Limits.MinResponseDataRate =
new MinDataRate(bytesPerSecond: 100, gracePeriod: TimeSpan.FromSeconds(2));
_frameConnectionContext.ServiceContext.SystemClock = systemClock;
_httpConnectionContext.ServiceContext.SystemClock = systemClock;
var mockLogger = new Mock<IKestrelTrace>();
_frameConnectionContext.ServiceContext.Log = mockLogger.Object;
_httpConnectionContext.ServiceContext.Log = mockLogger.Object;
_frameConnection.CreateFrame(new DummyApplication(), _frameConnectionContext.Transport, _frameConnectionContext.Application);
_frameConnection.Frame.Reset();
_frameConnection.Frame.RequestAborted.Register(() =>
_httpConnection.CreateHttp1Connection(new DummyApplication(), _httpConnectionContext.Transport, _httpConnectionContext.Application);
_httpConnection.Http1Connection.Reset();
_httpConnection.Http1Connection.RequestAborted.Register(() =>
{
aborted.Set();
});
// Initialize timestamp
_frameConnection.Tick(systemClock.UtcNow);
_httpConnection.Tick(systemClock.UtcNow);
// Should complete within 4 seconds, but the timeout is adjusted by adding Heartbeat.Interval
_frameConnection.StartTimingWrite(400);
_httpConnection.StartTimingWrite(400);
// Tick just past 4s plus Heartbeat.Interval
systemClock.UtcNow += TimeSpan.FromSeconds(4) + Heartbeat.Interval + TimeSpan.FromTicks(1);
_frameConnection.Tick(systemClock.UtcNow);
_httpConnection.Tick(systemClock.UtcNow);
Assert.True(_frameConnection.RequestTimedOut);
Assert.True(_httpConnection.RequestTimedOut);
Assert.True(aborted.Wait(TimeSpan.FromSeconds(10)));
}
@ -447,38 +447,38 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
var minResponseDataRate = new MinDataRate(bytesPerSecond: 100, gracePeriod: TimeSpan.FromSeconds(5));
var aborted = new ManualResetEventSlim();
_frameConnectionContext.ServiceContext.ServerOptions.Limits.MinResponseDataRate = minResponseDataRate;
_frameConnectionContext.ServiceContext.SystemClock = systemClock;
_httpConnectionContext.ServiceContext.ServerOptions.Limits.MinResponseDataRate = minResponseDataRate;
_httpConnectionContext.ServiceContext.SystemClock = systemClock;
var mockLogger = new Mock<IKestrelTrace>();
_frameConnectionContext.ServiceContext.Log = mockLogger.Object;
_httpConnectionContext.ServiceContext.Log = mockLogger.Object;
_frameConnection.CreateFrame(new DummyApplication(), _frameConnectionContext.Transport, _frameConnectionContext.Application);
_frameConnection.Frame.Reset();
_frameConnection.Frame.RequestAborted.Register(() =>
_httpConnection.CreateHttp1Connection(new DummyApplication(), _httpConnectionContext.Transport, _httpConnectionContext.Application);
_httpConnection.Http1Connection.Reset();
_httpConnection.Http1Connection.RequestAborted.Register(() =>
{
aborted.Set();
});
// Initialize timestamp
var startTime = systemClock.UtcNow;
_frameConnection.Tick(startTime);
_httpConnection.Tick(startTime);
// Should complete within 1 second, but the timeout is adjusted by adding Heartbeat.Interval
_frameConnection.StartTimingWrite(100);
_httpConnection.StartTimingWrite(100);
// Tick just past 1s plus Heartbeat.Interval
systemClock.UtcNow += TimeSpan.FromSeconds(1) + Heartbeat.Interval + TimeSpan.FromTicks(1);
_frameConnection.Tick(systemClock.UtcNow);
_httpConnection.Tick(systemClock.UtcNow);
// Still within grace period, not timed out
Assert.False(_frameConnection.RequestTimedOut);
Assert.False(_httpConnection.RequestTimedOut);
// Tick just past grace period (adjusted by Heartbeat.Interval)
systemClock.UtcNow = startTime + minResponseDataRate.GracePeriod + Heartbeat.Interval + TimeSpan.FromTicks(1);
_frameConnection.Tick(systemClock.UtcNow);
_httpConnection.Tick(systemClock.UtcNow);
Assert.True(_frameConnection.RequestTimedOut);
Assert.True(_httpConnection.RequestTimedOut);
Assert.True(aborted.Wait(TimeSpan.FromSeconds(10)));
}
@ -488,44 +488,44 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
var systemClock = new MockSystemClock();
var aborted = new ManualResetEventSlim();
_frameConnectionContext.ServiceContext.ServerOptions.Limits.MinResponseDataRate =
_httpConnectionContext.ServiceContext.ServerOptions.Limits.MinResponseDataRate =
new MinDataRate(bytesPerSecond: 100, gracePeriod: TimeSpan.FromSeconds(2));
_frameConnectionContext.ServiceContext.SystemClock = systemClock;
_httpConnectionContext.ServiceContext.SystemClock = systemClock;
var mockLogger = new Mock<IKestrelTrace>();
_frameConnectionContext.ServiceContext.Log = mockLogger.Object;
_httpConnectionContext.ServiceContext.Log = mockLogger.Object;
_frameConnection.CreateFrame(new DummyApplication(), _frameConnectionContext.Transport, _frameConnectionContext.Application);
_frameConnection.Frame.Reset();
_frameConnection.Frame.RequestAborted.Register(() =>
_httpConnection.CreateHttp1Connection(new DummyApplication(), _httpConnectionContext.Transport, _httpConnectionContext.Application);
_httpConnection.Http1Connection.Reset();
_httpConnection.Http1Connection.RequestAborted.Register(() =>
{
aborted.Set();
});
// Initialize timestamp
_frameConnection.Tick(systemClock.UtcNow);
_httpConnection.Tick(systemClock.UtcNow);
// Should complete within 5 seconds, but the timeout is adjusted by adding Heartbeat.Interval
_frameConnection.StartTimingWrite(500);
_httpConnection.StartTimingWrite(500);
// Start a concurrent write after 3 seconds, which should complete within 3 seconds (adjusted by Heartbeat.Interval)
_frameConnection.StartTimingWrite(300);
_httpConnection.StartTimingWrite(300);
// Tick just past 5s plus Heartbeat.Interval, when the first write should have completed
systemClock.UtcNow += TimeSpan.FromSeconds(5) + Heartbeat.Interval + TimeSpan.FromTicks(1);
_frameConnection.Tick(systemClock.UtcNow);
_httpConnection.Tick(systemClock.UtcNow);
// Not timed out because the timeout was pushed by the second write
Assert.False(_frameConnection.RequestTimedOut);
Assert.False(_httpConnection.RequestTimedOut);
// Complete the first write, this should have no effect on the timeout
_frameConnection.StopTimingWrite();
_httpConnection.StopTimingWrite();
// Tick just past +3s, when the second write should have completed
systemClock.UtcNow += TimeSpan.FromSeconds(3) + TimeSpan.FromTicks(1);
_frameConnection.Tick(systemClock.UtcNow);
_httpConnection.Tick(systemClock.UtcNow);
Assert.True(_frameConnection.RequestTimedOut);
Assert.True(_httpConnection.RequestTimedOut);
Assert.True(aborted.Wait(TimeSpan.FromSeconds(10)));
}
}

View File

@ -10,7 +10,7 @@ using Xunit;
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
{
public class FrameHeadersTests
public class HttpHeadersTests
{
[Theory]
[InlineData("", ConnectionOptions.None)]
@ -128,7 +128,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
[InlineData("cl2se ,", ConnectionOptions.None)]
public void TestParseConnection(string connection, ConnectionOptions expectedConnectionOptions)
{
var connectionOptions = FrameHeaders.ParseConnection(connection);
var connectionOptions = HttpHeaders.ParseConnection(connection);
Assert.Equal(expectedConnectionOptions, connectionOptions);
}
@ -151,7 +151,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
public void TestParseConnectionMultipleValues(string value1, string value2, ConnectionOptions expectedConnectionOptions)
{
var connection = new StringValues(new[] { value1, value2 });
var connectionOptions = FrameHeaders.ParseConnection(connection);
var connectionOptions = HttpHeaders.ParseConnection(connection);
Assert.Equal(expectedConnectionOptions, connectionOptions);
}
@ -191,7 +191,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
[InlineData("gzip, chunked2", TransferCoding.Other)]
public void TestParseTransferEncoding(string transferEncoding, TransferCoding expectedTransferEncodingOptions)
{
var transferEncodingOptions = FrameHeaders.GetFinalTransferCoding(transferEncoding);
var transferEncodingOptions = HttpHeaders.GetFinalTransferCoding(transferEncoding);
Assert.Equal(expectedTransferEncodingOptions, transferEncodingOptions);
}
@ -210,68 +210,68 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
public void TestParseTransferEncodingMultipleValues(string value1, string value2, TransferCoding expectedTransferEncodingOptions)
{
var transferEncoding = new StringValues(new[] { value1, value2 });
var transferEncodingOptions = FrameHeaders.GetFinalTransferCoding(transferEncoding);
var transferEncodingOptions = HttpHeaders.GetFinalTransferCoding(transferEncoding);
Assert.Equal(expectedTransferEncodingOptions, transferEncodingOptions);
}
[Fact]
public void ValidContentLengthsAccepted()
{
ValidContentLengthsAcceptedImpl(new FrameRequestHeaders());
ValidContentLengthsAcceptedImpl(new FrameResponseHeaders());
ValidContentLengthsAcceptedImpl(new HttpRequestHeaders());
ValidContentLengthsAcceptedImpl(new HttpResponseHeaders());
}
private static void ValidContentLengthsAcceptedImpl(FrameHeaders frameHeaders)
private static void ValidContentLengthsAcceptedImpl(HttpHeaders httpHeaders)
{
IDictionary<string, StringValues> headers = frameHeaders;
IDictionary<string, StringValues> headers = httpHeaders;
StringValues value;
Assert.False(headers.TryGetValue("Content-Length", out value));
Assert.Null(frameHeaders.ContentLength);
Assert.False(frameHeaders.ContentLength.HasValue);
Assert.Null(httpHeaders.ContentLength);
Assert.False(httpHeaders.ContentLength.HasValue);
frameHeaders.ContentLength = 1;
httpHeaders.ContentLength = 1;
Assert.True(headers.TryGetValue("Content-Length", out value));
Assert.Equal("1", value[0]);
Assert.Equal(1, frameHeaders.ContentLength);
Assert.True(frameHeaders.ContentLength.HasValue);
Assert.Equal(1, httpHeaders.ContentLength);
Assert.True(httpHeaders.ContentLength.HasValue);
frameHeaders.ContentLength = long.MaxValue;
httpHeaders.ContentLength = long.MaxValue;
Assert.True(headers.TryGetValue("Content-Length", out value));
Assert.Equal(HeaderUtilities.FormatNonNegativeInt64(long.MaxValue), value[0]);
Assert.Equal(long.MaxValue, frameHeaders.ContentLength);
Assert.True(frameHeaders.ContentLength.HasValue);
Assert.Equal(long.MaxValue, httpHeaders.ContentLength);
Assert.True(httpHeaders.ContentLength.HasValue);
frameHeaders.ContentLength = null;
httpHeaders.ContentLength = null;
Assert.False(headers.TryGetValue("Content-Length", out value));
Assert.Null(frameHeaders.ContentLength);
Assert.False(frameHeaders.ContentLength.HasValue);
Assert.Null(httpHeaders.ContentLength);
Assert.False(httpHeaders.ContentLength.HasValue);
}
[Fact]
public void InvalidContentLengthsRejected()
{
InvalidContentLengthsRejectedImpl(new FrameRequestHeaders());
InvalidContentLengthsRejectedImpl(new FrameResponseHeaders());
InvalidContentLengthsRejectedImpl(new HttpRequestHeaders());
InvalidContentLengthsRejectedImpl(new HttpResponseHeaders());
}
private static void InvalidContentLengthsRejectedImpl(FrameHeaders frameHeaders)
private static void InvalidContentLengthsRejectedImpl(HttpHeaders httpHeaders)
{
IDictionary<string, StringValues> headers = frameHeaders;
IDictionary<string, StringValues> headers = httpHeaders;
StringValues value;
Assert.False(headers.TryGetValue("Content-Length", out value));
Assert.Null(frameHeaders.ContentLength);
Assert.False(frameHeaders.ContentLength.HasValue);
Assert.Null(httpHeaders.ContentLength);
Assert.False(httpHeaders.ContentLength.HasValue);
Assert.Throws<ArgumentOutOfRangeException>(() => frameHeaders.ContentLength = -1);
Assert.Throws<ArgumentOutOfRangeException>(() => frameHeaders.ContentLength = long.MinValue);
Assert.Throws<ArgumentOutOfRangeException>(() => httpHeaders.ContentLength = -1);
Assert.Throws<ArgumentOutOfRangeException>(() => httpHeaders.ContentLength = long.MinValue);
Assert.False(headers.TryGetValue("Content-Length", out value));
Assert.Null(frameHeaders.ContentLength);
Assert.False(frameHeaders.ContentLength.HasValue);
Assert.Null(httpHeaders.ContentLength);
Assert.False(httpHeaders.ContentLength.HasValue);
}
}
}

View File

@ -27,7 +27,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
string expectedRawTarget,
string expectedRawPath,
// This warns that theory methods should use all of their parameters,
// but this method is using a shared data collection with FrameTests.TakeStartLineSetsFrameProperties and others.
// but this method is using a shared data collection with Http1ConnectionTests.TakeStartLineSetsHttpProtocolProperties and others.
#pragma warning disable xUnit1026
string expectedDecodedPath,
string expectedQueryString,

View File

@ -12,12 +12,12 @@ using Xunit;
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
{
public class FrameRequestHeadersTests
public class HttpRequestHeadersTests
{
[Fact]
public void InitialDictionaryIsEmpty()
{
IDictionary<string, StringValues> headers = new FrameRequestHeaders();
IDictionary<string, StringValues> headers = new HttpRequestHeaders();
Assert.Equal(0, headers.Count);
Assert.False(headers.IsReadOnly);
@ -26,7 +26,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
[Fact]
public void SettingUnknownHeadersWorks()
{
IDictionary<string, StringValues> headers = new FrameRequestHeaders();
IDictionary<string, StringValues> headers = new HttpRequestHeaders();
headers["custom"] = new[] { "value" };
@ -37,7 +37,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
[Fact]
public void SettingKnownHeadersWorks()
{
IDictionary<string, StringValues> headers = new FrameRequestHeaders();
IDictionary<string, StringValues> headers = new HttpRequestHeaders();
headers["host"] = new[] { "value" };
headers["content-length"] = new[] { "0" };
@ -51,7 +51,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
[Fact]
public void KnownAndCustomHeaderCountAddedTogether()
{
IDictionary<string, StringValues> headers = new FrameRequestHeaders();
IDictionary<string, StringValues> headers = new HttpRequestHeaders();
headers["host"] = new[] { "value" };
headers["custom"] = new[] { "value" };
@ -63,7 +63,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
[Fact]
public void TryGetValueWorksForKnownAndUnknownHeaders()
{
IDictionary<string, StringValues> headers = new FrameRequestHeaders();
IDictionary<string, StringValues> headers = new HttpRequestHeaders();
StringValues value;
Assert.False(headers.TryGetValue("host", out value));
@ -89,7 +89,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
[Fact]
public void SameExceptionThrownForMissingKey()
{
IDictionary<string, StringValues> headers = new FrameRequestHeaders();
IDictionary<string, StringValues> headers = new HttpRequestHeaders();
Assert.Throws<KeyNotFoundException>(() => headers["custom"]);
Assert.Throws<KeyNotFoundException>(() => headers["host"]);
@ -99,7 +99,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
[Fact]
public void EntriesCanBeEnumerated()
{
IDictionary<string, StringValues> headers = new FrameRequestHeaders();
IDictionary<string, StringValues> headers = new HttpRequestHeaders();
var v1 = new[] { "localhost" };
var v2 = new[] { "0" };
var v3 = new[] { "value" };
@ -119,7 +119,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
[Fact]
public void KeysAndValuesCanBeEnumerated()
{
IDictionary<string, StringValues> headers = new FrameRequestHeaders();
IDictionary<string, StringValues> headers = new HttpRequestHeaders();
StringValues v1 = new[] { "localhost" };
StringValues v2 = new[] { "0" };
StringValues v3 = new[] { "value" };
@ -139,7 +139,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
[Fact]
public void ContainsAndContainsKeyWork()
{
IDictionary<string, StringValues> headers = new FrameRequestHeaders();
IDictionary<string, StringValues> headers = new HttpRequestHeaders();
var kv1 = new KeyValuePair<string, StringValues>("host", new[] { "localhost" });
var kv2 = new KeyValuePair<string, StringValues>("custom", new[] { "value" });
var kv3 = new KeyValuePair<string, StringValues>("Content-Length", new[] { "0" });
@ -191,7 +191,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
[Fact]
public void AddWorksLikeSetAndThrowsIfKeyExists()
{
IDictionary<string, StringValues> headers = new FrameRequestHeaders();
IDictionary<string, StringValues> headers = new HttpRequestHeaders();
StringValues value;
Assert.False(headers.TryGetValue("host", out value));
@ -216,7 +216,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
[Fact]
public void ClearRemovesAllHeaders()
{
IDictionary<string, StringValues> headers = new FrameRequestHeaders();
IDictionary<string, StringValues> headers = new HttpRequestHeaders();
headers.Add("host", new[] { "localhost" });
headers.Add("custom", new[] { "value" });
headers.Add("Content-Length", new[] { "0" });
@ -238,7 +238,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
[Fact]
public void RemoveTakesHeadersOutOfDictionary()
{
IDictionary<string, StringValues> headers = new FrameRequestHeaders();
IDictionary<string, StringValues> headers = new HttpRequestHeaders();
headers.Add("host", new[] { "localhost" });
headers.Add("custom", new[] { "value" });
headers.Add("Content-Length", new[] { "0" });
@ -276,7 +276,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
[Fact]
public void CopyToMovesDataIntoArray()
{
IDictionary<string, StringValues> headers = new FrameRequestHeaders();
IDictionary<string, StringValues> headers = new HttpRequestHeaders();
headers.Add("host", new[] { "localhost" });
headers.Add("Content-Length", new[] { "0" });
headers.Add("custom", new[] { "value" });
@ -303,7 +303,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
[Fact]
public void AppendThrowsWhenHeaderNameContainsNonASCIICharacters()
{
var headers = new FrameRequestHeaders();
var headers = new HttpRequestHeaders();
const string key = "\u00141\u00F3d\017c";
var encoding = Encoding.GetEncoding("iso-8859-1");

View File

@ -12,54 +12,54 @@ using Xunit;
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
{
public class FrameRequestStreamTests
public class HttpRequestStreamTests
{
[Fact]
public void CanReadReturnsTrue()
{
var stream = new FrameRequestStream(Mock.Of<IHttpBodyControlFeature>());
var stream = new HttpRequestStream(Mock.Of<IHttpBodyControlFeature>());
Assert.True(stream.CanRead);
}
[Fact]
public void CanSeekReturnsFalse()
{
var stream = new FrameRequestStream(Mock.Of<IHttpBodyControlFeature>());
var stream = new HttpRequestStream(Mock.Of<IHttpBodyControlFeature>());
Assert.False(stream.CanSeek);
}
[Fact]
public void CanWriteReturnsFalse()
{
var stream = new FrameRequestStream(Mock.Of<IHttpBodyControlFeature>());
var stream = new HttpRequestStream(Mock.Of<IHttpBodyControlFeature>());
Assert.False(stream.CanWrite);
}
[Fact]
public void SeekThrows()
{
var stream = new FrameRequestStream(Mock.Of<IHttpBodyControlFeature>());
var stream = new HttpRequestStream(Mock.Of<IHttpBodyControlFeature>());
Assert.Throws<NotSupportedException>(() => stream.Seek(0, SeekOrigin.Begin));
}
[Fact]
public void LengthThrows()
{
var stream = new FrameRequestStream(Mock.Of<IHttpBodyControlFeature>());
var stream = new HttpRequestStream(Mock.Of<IHttpBodyControlFeature>());
Assert.Throws<NotSupportedException>(() => stream.Length);
}
[Fact]
public void SetLengthThrows()
{
var stream = new FrameRequestStream(Mock.Of<IHttpBodyControlFeature>());
var stream = new HttpRequestStream(Mock.Of<IHttpBodyControlFeature>());
Assert.Throws<NotSupportedException>(() => stream.SetLength(0));
}
[Fact]
public void PositionThrows()
{
var stream = new FrameRequestStream(Mock.Of<IHttpBodyControlFeature>());
var stream = new HttpRequestStream(Mock.Of<IHttpBodyControlFeature>());
Assert.Throws<NotSupportedException>(() => stream.Position);
Assert.Throws<NotSupportedException>(() => stream.Position = 0);
}
@ -67,21 +67,21 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
[Fact]
public void WriteThrows()
{
var stream = new FrameRequestStream(Mock.Of<IHttpBodyControlFeature>());
var stream = new HttpRequestStream(Mock.Of<IHttpBodyControlFeature>());
Assert.Throws<NotSupportedException>(() => stream.Write(new byte[1], 0, 1));
}
[Fact]
public void WriteByteThrows()
{
var stream = new FrameRequestStream(Mock.Of<IHttpBodyControlFeature>());
var stream = new HttpRequestStream(Mock.Of<IHttpBodyControlFeature>());
Assert.Throws<NotSupportedException>(() => stream.WriteByte(0));
}
[Fact]
public async Task WriteAsyncThrows()
{
var stream = new FrameRequestStream(Mock.Of<IHttpBodyControlFeature>());
var stream = new HttpRequestStream(Mock.Of<IHttpBodyControlFeature>());
await Assert.ThrowsAsync<NotSupportedException>(() => stream.WriteAsync(new byte[1], 0, 1));
}
@ -89,7 +89,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
[Fact]
public void BeginWriteThrows()
{
var stream = new FrameRequestStream(Mock.Of<IHttpBodyControlFeature>());
var stream = new HttpRequestStream(Mock.Of<IHttpBodyControlFeature>());
Assert.Throws<NotSupportedException>(() => stream.BeginWrite(new byte[1], 0, 1, null, null));
}
#elif NETCOREAPP2_0
@ -100,14 +100,14 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
[Fact]
public void FlushThrows()
{
var stream = new FrameRequestStream(Mock.Of<IHttpBodyControlFeature>());
var stream = new HttpRequestStream(Mock.Of<IHttpBodyControlFeature>());
Assert.Throws<NotSupportedException>(() => stream.Flush());
}
[Fact]
public async Task FlushAsyncThrows()
{
var stream = new FrameRequestStream(Mock.Of<IHttpBodyControlFeature>());
var stream = new HttpRequestStream(Mock.Of<IHttpBodyControlFeature>());
await Assert.ThrowsAsync<NotSupportedException>(() => stream.FlushAsync());
}
@ -117,10 +117,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
var allowSynchronousIO = false;
var mockBodyControl = new Mock<IHttpBodyControlFeature>();
mockBodyControl.Setup(m => m.AllowSynchronousIO).Returns(() => allowSynchronousIO);
var mockMessageBody = new Mock<MessageBody>((Frame)null);
var mockMessageBody = new Mock<MessageBody>((HttpProtocol)null);
mockMessageBody.Setup(m => m.ReadAsync(It.IsAny<ArraySegment<byte>>(), CancellationToken.None)).ReturnsAsync(0);
var stream = new FrameRequestStream(mockBodyControl.Object);
var stream = new HttpRequestStream(mockBodyControl.Object);
stream.StartAcceptingReads(mockMessageBody.Object);
Assert.Equal(0, await stream.ReadAsync(new byte[1], 0, 1));
@ -138,7 +138,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
[Fact]
public void AbortCausesReadToCancel()
{
var stream = new FrameRequestStream(Mock.Of<IHttpBodyControlFeature>());
var stream = new HttpRequestStream(Mock.Of<IHttpBodyControlFeature>());
stream.StartAcceptingReads(null);
stream.Abort();
var task = stream.ReadAsync(new byte[1], 0, 1);
@ -148,7 +148,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
[Fact]
public void AbortWithErrorCausesReadToCancel()
{
var stream = new FrameRequestStream(Mock.Of<IHttpBodyControlFeature>());
var stream = new HttpRequestStream(Mock.Of<IHttpBodyControlFeature>());
stream.StartAcceptingReads(null);
var error = new Exception();
stream.Abort(error);
@ -160,7 +160,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
[Fact]
public void StopAcceptingReadsCausesReadToThrowObjectDisposedException()
{
var stream = new FrameRequestStream(Mock.Of<IHttpBodyControlFeature>());
var stream = new HttpRequestStream(Mock.Of<IHttpBodyControlFeature>());
stream.StartAcceptingReads(null);
stream.StopAcceptingReads();
Assert.Throws<ObjectDisposedException>(() => { stream.ReadAsync(new byte[1], 0, 1); });
@ -169,7 +169,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
[Fact]
public void AbortCausesCopyToAsyncToCancel()
{
var stream = new FrameRequestStream(Mock.Of<IHttpBodyControlFeature>());
var stream = new HttpRequestStream(Mock.Of<IHttpBodyControlFeature>());
stream.StartAcceptingReads(null);
stream.Abort();
var task = stream.CopyToAsync(Mock.Of<Stream>());
@ -179,7 +179,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
[Fact]
public void AbortWithErrorCausesCopyToAsyncToCancel()
{
var stream = new FrameRequestStream(Mock.Of<IHttpBodyControlFeature>());
var stream = new HttpRequestStream(Mock.Of<IHttpBodyControlFeature>());
stream.StartAcceptingReads(null);
var error = new Exception();
stream.Abort(error);
@ -191,7 +191,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
[Fact]
public void StopAcceptingReadsCausesCopyToAsyncToThrowObjectDisposedException()
{
var stream = new FrameRequestStream(Mock.Of<IHttpBodyControlFeature>());
var stream = new HttpRequestStream(Mock.Of<IHttpBodyControlFeature>());
stream.StartAcceptingReads(null);
stream.StopAcceptingReads();
Assert.Throws<ObjectDisposedException>(() => { stream.CopyToAsync(Mock.Of<Stream>()); });
@ -200,7 +200,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
[Fact]
public void NullDestinationCausesCopyToAsyncToThrowArgumentNullException()
{
var stream = new FrameRequestStream(Mock.Of<IHttpBodyControlFeature>());
var stream = new HttpRequestStream(Mock.Of<IHttpBodyControlFeature>());
stream.StartAcceptingReads(null);
Assert.Throws<ArgumentNullException>(() => { stream.CopyToAsync(null); });
}
@ -208,7 +208,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
[Fact]
public void ZeroBufferSizeCausesCopyToAsyncToThrowArgumentException()
{
var stream = new FrameRequestStream(Mock.Of<IHttpBodyControlFeature>());
var stream = new HttpRequestStream(Mock.Of<IHttpBodyControlFeature>());
stream.StartAcceptingReads(null);
Assert.Throws<ArgumentException>(() => { stream.CopyToAsync(Mock.Of<Stream>(), 0); });
}

View File

@ -14,14 +14,14 @@ using Xunit;
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
{
public class FrameResponseHeadersTests
public class HttpResponseHeadersTests
{
[Fact]
public void InitialDictionaryIsEmpty()
{
var factory = new PipeFactory();
var pair = factory.CreateConnectionPair();
var frameContext = new FrameContext
var http1ConnectionContext = new Http1ConnectionContext
{
ServiceContext = new TestServiceContext(),
ConnectionFeatures = new FeatureCollection(),
@ -31,11 +31,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
TimeoutControl = null
};
var frame = new Frame<object>(application: null, frameContext: frameContext);
var http1Connection = new Http1Connection<object>(application: null, context: http1ConnectionContext);
frame.Reset();
http1Connection.Reset();
IDictionary<string, StringValues> headers = frame.ResponseHeaders;
IDictionary<string, StringValues> headers = http1Connection.ResponseHeaders;
Assert.Equal(0, headers.Count);
Assert.False(headers.IsReadOnly);
@ -76,7 +76,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
[InlineData("Seršver", "Data")]
public void AddingControlOrNonAsciiCharactersToHeadersThrows(string key, string value)
{
var responseHeaders = new FrameResponseHeaders();
var responseHeaders = new HttpResponseHeaders();
Assert.Throws<InvalidOperationException>(() =>
{
@ -109,7 +109,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
[Fact]
public void ThrowsWhenAddingHeaderAfterReadOnlyIsSet()
{
var headers = new FrameResponseHeaders();
var headers = new HttpResponseHeaders();
headers.SetReadOnly();
Assert.Throws<InvalidOperationException>(() => ((IDictionary<string, StringValues>)headers).Add("my-header", new[] { "value" }));
@ -118,7 +118,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
[Fact]
public void ThrowsWhenChangingHeaderAfterReadOnlyIsSet()
{
var headers = new FrameResponseHeaders();
var headers = new HttpResponseHeaders();
var dictionary = (IDictionary<string, StringValues>)headers;
dictionary.Add("my-header", new[] { "value" });
headers.SetReadOnly();
@ -129,7 +129,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
[Fact]
public void ThrowsWhenRemovingHeaderAfterReadOnlyIsSet()
{
var headers = new FrameResponseHeaders();
var headers = new HttpResponseHeaders();
var dictionary = (IDictionary<string, StringValues>)headers;
dictionary.Add("my-header", new[] { "value" });
headers.SetReadOnly();
@ -140,7 +140,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
[Fact]
public void ThrowsWhenClearingHeadersAfterReadOnlyIsSet()
{
var headers = new FrameResponseHeaders();
var headers = new HttpResponseHeaders();
var dictionary = (IDictionary<string, StringValues>)headers;
dictionary.Add("my-header", new[] { "value" });
headers.SetReadOnly();
@ -152,7 +152,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
[MemberData(nameof(BadContentLengths))]
public void ThrowsWhenAddingContentLengthWithNonNumericValue(string contentLength)
{
var headers = new FrameResponseHeaders();
var headers = new HttpResponseHeaders();
var dictionary = (IDictionary<string, StringValues>)headers;
var exception = Assert.Throws<InvalidOperationException>(() => dictionary.Add("Content-Length", new[] { contentLength }));
@ -163,7 +163,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
[MemberData(nameof(BadContentLengths))]
public void ThrowsWhenSettingContentLengthToNonNumericValue(string contentLength)
{
var headers = new FrameResponseHeaders();
var headers = new HttpResponseHeaders();
var dictionary = (IDictionary<string, StringValues>)headers;
var exception = Assert.Throws<InvalidOperationException>(() => ((IHeaderDictionary)headers)["Content-Length"] = contentLength);
@ -174,7 +174,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
[MemberData(nameof(BadContentLengths))]
public void ThrowsWhenAssigningHeaderContentLengthToNonNumericValue(string contentLength)
{
var headers = new FrameResponseHeaders();
var headers = new HttpResponseHeaders();
var exception = Assert.Throws<InvalidOperationException>(() => headers.HeaderContentLength = contentLength);
Assert.Equal(CoreStrings.FormatInvalidContentLength_InvalidNumber(contentLength), exception.Message);
@ -184,7 +184,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
[MemberData(nameof(GoodContentLengths))]
public void ContentLengthValueCanBeReadAsLongAfterAddingHeader(string contentLength)
{
var headers = new FrameResponseHeaders();
var headers = new HttpResponseHeaders();
var dictionary = (IDictionary<string, StringValues>)headers;
dictionary.Add("Content-Length", contentLength);
@ -195,7 +195,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
[MemberData(nameof(GoodContentLengths))]
public void ContentLengthValueCanBeReadAsLongAfterSettingHeader(string contentLength)
{
var headers = new FrameResponseHeaders();
var headers = new HttpResponseHeaders();
var dictionary = (IDictionary<string, StringValues>)headers;
dictionary["Content-Length"] = contentLength;
@ -206,7 +206,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
[MemberData(nameof(GoodContentLengths))]
public void ContentLengthValueCanBeReadAsLongAfterAssigningHeader(string contentLength)
{
var headers = new FrameResponseHeaders();
var headers = new HttpResponseHeaders();
headers.HeaderContentLength = contentLength;
Assert.Equal(ParseLong(contentLength), headers.ContentLength);
@ -215,7 +215,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
[Fact]
public void ContentLengthValueClearedWhenHeaderIsRemoved()
{
var headers = new FrameResponseHeaders();
var headers = new HttpResponseHeaders();
headers.HeaderContentLength = "42";
var dictionary = (IDictionary<string, StringValues>)headers;
@ -227,7 +227,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
[Fact]
public void ContentLengthValueClearedWhenHeadersCleared()
{
var headers = new FrameResponseHeaders();
var headers = new HttpResponseHeaders();
headers.HeaderContentLength = "42";
var dictionary = (IDictionary<string, StringValues>)headers;

View File

@ -13,82 +13,82 @@ using Xunit;
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
{
public class FrameResponseStreamTests
public class HttpResponseStreamTests
{
[Fact]
public void CanReadReturnsFalse()
{
var stream = new FrameResponseStream(Mock.Of<IHttpBodyControlFeature>(), new MockFrameControl());
var stream = new HttpResponseStream(Mock.Of<IHttpBodyControlFeature>(), new MockHttpResponseControl());
Assert.False(stream.CanRead);
}
[Fact]
public void CanSeekReturnsFalse()
{
var stream = new FrameResponseStream(Mock.Of<IHttpBodyControlFeature>(), new MockFrameControl());
var stream = new HttpResponseStream(Mock.Of<IHttpBodyControlFeature>(), new MockHttpResponseControl());
Assert.False(stream.CanSeek);
}
[Fact]
public void CanWriteReturnsTrue()
{
var stream = new FrameResponseStream(Mock.Of<IHttpBodyControlFeature>(), new MockFrameControl());
var stream = new HttpResponseStream(Mock.Of<IHttpBodyControlFeature>(), new MockHttpResponseControl());
Assert.True(stream.CanWrite);
}
[Fact]
public void ReadThrows()
{
var stream = new FrameResponseStream(Mock.Of<IHttpBodyControlFeature>(), new MockFrameControl());
var stream = new HttpResponseStream(Mock.Of<IHttpBodyControlFeature>(), new MockHttpResponseControl());
Assert.Throws<NotSupportedException>(() => stream.Read(new byte[1], 0, 1));
}
[Fact]
public void ReadByteThrows()
{
var stream = new FrameResponseStream(Mock.Of<IHttpBodyControlFeature>(), new MockFrameControl());
var stream = new HttpResponseStream(Mock.Of<IHttpBodyControlFeature>(), new MockHttpResponseControl());
Assert.Throws<NotSupportedException>(() => stream.ReadByte());
}
[Fact]
public async Task ReadAsyncThrows()
{
var stream = new FrameResponseStream(Mock.Of<IHttpBodyControlFeature>(), new MockFrameControl());
var stream = new HttpResponseStream(Mock.Of<IHttpBodyControlFeature>(), new MockHttpResponseControl());
await Assert.ThrowsAsync<NotSupportedException>(() => stream.ReadAsync(new byte[1], 0, 1));
}
[Fact]
public void BeginReadThrows()
{
var stream = new FrameResponseStream(Mock.Of<IHttpBodyControlFeature>(), new MockFrameControl());
var stream = new HttpResponseStream(Mock.Of<IHttpBodyControlFeature>(), new MockHttpResponseControl());
Assert.Throws<NotSupportedException>(() => stream.BeginRead(new byte[1], 0, 1, null, null));
}
[Fact]
public void SeekThrows()
{
var stream = new FrameResponseStream(Mock.Of<IHttpBodyControlFeature>(), new MockFrameControl());
var stream = new HttpResponseStream(Mock.Of<IHttpBodyControlFeature>(), new MockHttpResponseControl());
Assert.Throws<NotSupportedException>(() => stream.Seek(0, SeekOrigin.Begin));
}
[Fact]
public void LengthThrows()
{
var stream = new FrameResponseStream(Mock.Of<IHttpBodyControlFeature>(), new MockFrameControl());
var stream = new HttpResponseStream(Mock.Of<IHttpBodyControlFeature>(), new MockHttpResponseControl());
Assert.Throws<NotSupportedException>(() => stream.Length);
}
[Fact]
public void SetLengthThrows()
{
var stream = new FrameResponseStream(Mock.Of<IHttpBodyControlFeature>(), new MockFrameControl());
var stream = new HttpResponseStream(Mock.Of<IHttpBodyControlFeature>(), new MockHttpResponseControl());
Assert.Throws<NotSupportedException>(() => stream.SetLength(0));
}
[Fact]
public void PositionThrows()
{
var stream = new FrameResponseStream(Mock.Of<IHttpBodyControlFeature>(), new MockFrameControl());
var stream = new HttpResponseStream(Mock.Of<IHttpBodyControlFeature>(), new MockHttpResponseControl());
Assert.Throws<NotSupportedException>(() => stream.Position);
Assert.Throws<NotSupportedException>(() => stream.Position = 0);
}
@ -96,7 +96,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
[Fact]
public void StopAcceptingWritesCausesWriteToThrowObjectDisposedException()
{
var stream = new FrameResponseStream(Mock.Of<IHttpBodyControlFeature>(), Mock.Of<IFrameControl>());
var stream = new HttpResponseStream(Mock.Of<IHttpBodyControlFeature>(), Mock.Of<IHttpResponseControl>());
stream.StartAcceptingWrites();
stream.StopAcceptingWrites();
Assert.Throws<ObjectDisposedException>(() => { stream.WriteAsync(new byte[1], 0, 1); });
@ -108,10 +108,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
var allowSynchronousIO = false;
var mockBodyControl = new Mock<IHttpBodyControlFeature>();
mockBodyControl.Setup(m => m.AllowSynchronousIO).Returns(() => allowSynchronousIO);
var mockFrameControl = new Mock<IFrameControl>();
mockFrameControl.Setup(m => m.WriteAsync(It.IsAny<ArraySegment<byte>>(), CancellationToken.None)).Returns(Task.CompletedTask);
var mockHttpResponseControl = new Mock<IHttpResponseControl>();
mockHttpResponseControl.Setup(m => m.WriteAsync(It.IsAny<ArraySegment<byte>>(), CancellationToken.None)).Returns(Task.CompletedTask);
var stream = new FrameResponseStream(mockBodyControl.Object, mockFrameControl.Object);
var stream = new HttpResponseStream(mockBodyControl.Object, mockHttpResponseControl.Object);
stream.StartAcceptingWrites();
// WriteAsync doesn't throw.

View File

@ -28,10 +28,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
{
using (var input = new TestInput())
{
var body = MessageBody.For(httpVersion, new FrameRequestHeaders { HeaderContentLength = "5" }, input.Frame);
var body = Http1MessageBody.For(httpVersion, new HttpRequestHeaders { HeaderContentLength = "5" }, input.Http1Connection);
var mockBodyControl = new Mock<IHttpBodyControlFeature>();
mockBodyControl.Setup(m => m.AllowSynchronousIO).Returns(true);
var stream = new FrameRequestStream(mockBodyControl.Object);
var stream = new HttpRequestStream(mockBodyControl.Object);
stream.StartAcceptingReads(body);
input.Add("Hello");
@ -56,8 +56,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
{
using (var input = new TestInput())
{
var body = MessageBody.For(httpVersion, new FrameRequestHeaders { HeaderContentLength = "5" }, input.Frame);
var stream = new FrameRequestStream(Mock.Of<IHttpBodyControlFeature>());
var body = Http1MessageBody.For(httpVersion, new HttpRequestHeaders { HeaderContentLength = "5" }, input.Http1Connection);
var stream = new HttpRequestStream(Mock.Of<IHttpBodyControlFeature>());
stream.StartAcceptingReads(body);
input.Add("Hello");
@ -80,10 +80,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
{
using (var input = new TestInput())
{
var body = MessageBody.For(HttpVersion.Http11, new FrameRequestHeaders { HeaderTransferEncoding = "chunked" }, input.Frame);
var body = Http1MessageBody.For(HttpVersion.Http11, new HttpRequestHeaders { HeaderTransferEncoding = "chunked" }, input.Http1Connection);
var mockBodyControl = new Mock<IHttpBodyControlFeature>();
mockBodyControl.Setup(m => m.AllowSynchronousIO).Returns(true);
var stream = new FrameRequestStream(mockBodyControl.Object);
var stream = new HttpRequestStream(mockBodyControl.Object);
stream.StartAcceptingReads(body);
input.Add("5\r\nHello\r\n");
@ -108,8 +108,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
{
using (var input = new TestInput())
{
var body = MessageBody.For(HttpVersion.Http11, new FrameRequestHeaders { HeaderTransferEncoding = "chunked" }, input.Frame);
var stream = new FrameRequestStream(Mock.Of<IHttpBodyControlFeature>());
var body = Http1MessageBody.For(HttpVersion.Http11, new HttpRequestHeaders { HeaderTransferEncoding = "chunked" }, input.Http1Connection);
var stream = new HttpRequestStream(Mock.Of<IHttpBodyControlFeature>());
stream.StartAcceptingReads(body);
input.Add("5\r\nHello\r\n");
@ -134,8 +134,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
{
using (var input = new TestInput())
{
var body = MessageBody.For(HttpVersion.Http11, new FrameRequestHeaders { HeaderTransferEncoding = "chunked" }, input.Frame);
var stream = new FrameRequestStream(Mock.Of<IHttpBodyControlFeature>());
var body = Http1MessageBody.For(HttpVersion.Http11, new HttpRequestHeaders { HeaderTransferEncoding = "chunked" }, input.Http1Connection);
var stream = new HttpRequestStream(Mock.Of<IHttpBodyControlFeature>());
stream.StartAcceptingReads(body);
input.Add("5;\r\0");
@ -159,8 +159,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
{
using (var input = new TestInput())
{
var body = MessageBody.For(HttpVersion.Http11, new FrameRequestHeaders { HeaderTransferEncoding = "chunked" }, input.Frame);
var stream = new FrameRequestStream(Mock.Of<IHttpBodyControlFeature>());
var body = Http1MessageBody.For(HttpVersion.Http11, new HttpRequestHeaders { HeaderTransferEncoding = "chunked" }, input.Http1Connection);
var stream = new HttpRequestStream(Mock.Of<IHttpBodyControlFeature>());
stream.StartAcceptingReads(body);
input.Add("80000000\r\n");
@ -180,8 +180,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
{
using (var input = new TestInput())
{
var body = MessageBody.For(HttpVersion.Http11, new FrameRequestHeaders { HeaderTransferEncoding = "chunked" }, input.Frame);
var stream = new FrameRequestStream(Mock.Of<IHttpBodyControlFeature>());
var body = Http1MessageBody.For(HttpVersion.Http11, new HttpRequestHeaders { HeaderTransferEncoding = "chunked" }, input.Http1Connection);
var stream = new HttpRequestStream(Mock.Of<IHttpBodyControlFeature>());
stream.StartAcceptingReads(body);
input.Add("012345678\r");
@ -203,10 +203,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
{
using (var input = new TestInput())
{
var body = MessageBody.For(httpVersion, new FrameRequestHeaders { HeaderConnection = "upgrade" }, input.Frame);
var body = Http1MessageBody.For(httpVersion, new HttpRequestHeaders { HeaderConnection = "upgrade" }, input.Http1Connection);
var mockBodyControl = new Mock<IHttpBodyControlFeature>();
mockBodyControl.Setup(m => m.AllowSynchronousIO).Returns(true);
var stream = new FrameRequestStream(mockBodyControl.Object);
var stream = new HttpRequestStream(mockBodyControl.Object);
stream.StartAcceptingReads(body);
input.Add("Hello");
@ -230,8 +230,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
{
using (var input = new TestInput())
{
var body = MessageBody.For(httpVersion, new FrameRequestHeaders { HeaderConnection = "upgrade" }, input.Frame);
var stream = new FrameRequestStream(Mock.Of<IHttpBodyControlFeature>());
var body = Http1MessageBody.For(httpVersion, new HttpRequestHeaders { HeaderConnection = "upgrade" }, input.Http1Connection);
var stream = new HttpRequestStream(Mock.Of<IHttpBodyControlFeature>());
stream.StartAcceptingReads(body);
input.Add("Hello");
@ -255,10 +255,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
{
using (var input = new TestInput())
{
var body = MessageBody.For(httpVersion, new FrameRequestHeaders(), input.Frame);
var body = Http1MessageBody.For(httpVersion, new HttpRequestHeaders(), input.Http1Connection);
var mockBodyControl = new Mock<IHttpBodyControlFeature>();
mockBodyControl.Setup(m => m.AllowSynchronousIO).Returns(true);
var stream = new FrameRequestStream(mockBodyControl.Object);
var stream = new HttpRequestStream(mockBodyControl.Object);
stream.StartAcceptingReads(body);
input.Add("Hello");
@ -277,8 +277,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
{
using (var input = new TestInput())
{
var body = MessageBody.For(httpVersion, new FrameRequestHeaders(), input.Frame);
var stream = new FrameRequestStream(Mock.Of<IHttpBodyControlFeature>());
var body = Http1MessageBody.For(httpVersion, new HttpRequestHeaders(), input.Http1Connection);
var stream = new HttpRequestStream(Mock.Of<IHttpBodyControlFeature>());
stream.StartAcceptingReads(body);
input.Add("Hello");
@ -295,8 +295,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
{
using (var input = new TestInput())
{
var body = MessageBody.For(HttpVersion.Http10, new FrameRequestHeaders { HeaderContentLength = "8197" }, input.Frame);
var stream = new FrameRequestStream(Mock.Of<IHttpBodyControlFeature>());
var body = Http1MessageBody.For(HttpVersion.Http10, new HttpRequestHeaders { HeaderContentLength = "8197" }, input.Http1Connection);
var stream = new HttpRequestStream(Mock.Of<IHttpBodyControlFeature>());
stream.StartAcceptingReads(body);
// Input needs to be greater than 4032 bytes to allocate a block not backed by a slab.
@ -324,7 +324,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
using (var input = new TestInput())
{
var ex = Assert.Throws<BadHttpRequestException>(() =>
MessageBody.For(HttpVersion.Http11, new FrameRequestHeaders { HeaderTransferEncoding = "chunked, not-chunked" }, input.Frame));
Http1MessageBody.For(HttpVersion.Http11, new HttpRequestHeaders { HeaderTransferEncoding = "chunked, not-chunked" }, input.Http1Connection));
Assert.Equal(StatusCodes.Status400BadRequest, ex.StatusCode);
Assert.Equal(CoreStrings.FormatBadRequest_FinalTransferCodingNotChunked("chunked, not-chunked"), ex.Message);
@ -338,9 +338,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
{
using (var input = new TestInput())
{
input.Frame.Method = method;
input.Http1Connection.Method = method;
var ex = Assert.Throws<BadHttpRequestException>(() =>
MessageBody.For(HttpVersion.Http11, new FrameRequestHeaders(), input.Frame));
Http1MessageBody.For(HttpVersion.Http11, new HttpRequestHeaders(), input.Http1Connection));
Assert.Equal(StatusCodes.Status411LengthRequired, ex.StatusCode);
Assert.Equal(CoreStrings.FormatBadRequest_LengthRequired(method), ex.Message);
@ -354,9 +354,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
{
using (var input = new TestInput())
{
input.Frame.Method = method;
input.Http1Connection.Method = method;
var ex = Assert.Throws<BadHttpRequestException>(() =>
MessageBody.For(HttpVersion.Http10, new FrameRequestHeaders(), input.Frame));
Http1MessageBody.For(HttpVersion.Http10, new HttpRequestHeaders(), input.Http1Connection));
Assert.Equal(StatusCodes.Status400BadRequest, ex.StatusCode);
Assert.Equal(CoreStrings.FormatBadRequest_LengthRequiredHttp10(method), ex.Message);
@ -368,7 +368,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
{
using (var input = new TestInput())
{
var body = MessageBody.For(HttpVersion.Http10, new FrameRequestHeaders { HeaderContentLength = "5" }, input.Frame);
var body = Http1MessageBody.For(HttpVersion.Http10, new HttpRequestHeaders { HeaderContentLength = "5" }, input.Http1Connection);
input.Add("Hello");
@ -388,7 +388,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
{
using (var input = new TestInput())
{
var body = MessageBody.For(HttpVersion.Http10, new FrameRequestHeaders { HeaderContentLength = "5" }, input.Frame);
var body = Http1MessageBody.For(HttpVersion.Http10, new HttpRequestHeaders { HeaderContentLength = "5" }, input.Http1Connection);
input.Add("Hello");
@ -409,9 +409,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
public static IEnumerable<object[]> RequestData => new[]
{
// Content-Length
new object[] { new FrameRequestHeaders { HeaderContentLength = "12" }, new[] { "Hello ", "World!" } },
new object[] { new HttpRequestHeaders { HeaderContentLength = "12" }, new[] { "Hello ", "World!" } },
// Chunked
new object[] { new FrameRequestHeaders { HeaderTransferEncoding = "chunked" }, new[] { "6\r\nHello \r\n", "6\r\nWorld!\r\n0\r\n\r\n" } },
new object[] { new HttpRequestHeaders { HeaderTransferEncoding = "chunked" }, new[] { "6\r\nHello \r\n", "6\r\nWorld!\r\n0\r\n\r\n" } },
};
public static IEnumerable<object[]> CombinedData =>
@ -421,7 +421,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
[Theory]
[MemberData(nameof(RequestData))]
public async Task CopyToAsyncDoesNotCopyBlocks(FrameRequestHeaders headers, string[] data)
public async Task CopyToAsyncDoesNotCopyBlocks(HttpRequestHeaders headers, string[] data)
{
var writeCount = 0;
var writeTcs = new TaskCompletionSource<byte[]>();
@ -438,7 +438,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
using (var input = new TestInput())
{
var body = MessageBody.For(HttpVersion.Http11, headers, input.Frame);
var body = Http1MessageBody.For(HttpVersion.Http11, headers, input.Http1Connection);
var copyToAsyncTask = body.CopyToAsync(mockDestination.Object);
@ -487,8 +487,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
{
using (var input = new TestInput())
{
var body = MessageBody.For(HttpVersion.Http11, new FrameRequestHeaders { HeaderConnection = headerConnection }, input.Frame);
var stream = new FrameRequestStream(Mock.Of<IHttpBodyControlFeature>());
var body = Http1MessageBody.For(HttpVersion.Http11, new HttpRequestHeaders { HeaderConnection = headerConnection }, input.Http1Connection);
var stream = new HttpRequestStream(Mock.Of<IHttpBodyControlFeature>());
stream.StartAcceptingReads(body);
input.Add("Hello");
@ -508,8 +508,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
{
using (var input = new TestInput())
{
var body = MessageBody.For(HttpVersion.Http11, new FrameRequestHeaders { HeaderContentLength = "2" }, input.Frame);
var stream = new FrameRequestStream(Mock.Of<IHttpBodyControlFeature>());
var body = Http1MessageBody.For(HttpVersion.Http11, new HttpRequestHeaders { HeaderContentLength = "2" }, input.Http1Connection);
var stream = new HttpRequestStream(Mock.Of<IHttpBodyControlFeature>());
stream.StartAcceptingReads(body);
// Add some input and consume it to ensure PumpAsync is running
@ -531,8 +531,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
{
using (var input = new TestInput())
{
var body = MessageBody.For(HttpVersion.Http11, new FrameRequestHeaders { HeaderContentLength = "2" }, input.Frame);
var stream = new FrameRequestStream(Mock.Of<IHttpBodyControlFeature>());
var body = Http1MessageBody.For(HttpVersion.Http11, new HttpRequestHeaders { HeaderContentLength = "2" }, input.Http1Connection);
var stream = new HttpRequestStream(Mock.Of<IHttpBodyControlFeature>());
stream.StartAcceptingReads(body);
// Add some input and consume it to ensure PumpAsync is running
@ -557,16 +557,16 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
{
var mockTimeoutControl = new Mock<ITimeoutControl>();
input.FrameContext.TimeoutControl = mockTimeoutControl.Object;
input.Http1ConnectionContext.TimeoutControl = mockTimeoutControl.Object;
var body = MessageBody.For(HttpVersion.Http11, new FrameRequestHeaders { HeaderContentLength = "5" }, input.Frame);
var body = Http1MessageBody.For(HttpVersion.Http11, new HttpRequestHeaders { HeaderContentLength = "5" }, input.Http1Connection);
// Add some input and read it to start PumpAsync
input.Add("a");
Assert.Equal(1, await body.ReadAsync(new ArraySegment<byte>(new byte[1])));
// Time out on the next read
input.Frame.SendTimeoutResponse();
input.Http1Connection.SendTimeoutResponse();
var exception = await Assert.ThrowsAsync<BadHttpRequestException>(() => body.ReadAsync(new ArraySegment<byte>(new byte[1])));
Assert.Equal(StatusCodes.Status408RequestTimeout, exception.StatusCode);
@ -582,16 +582,16 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
{
var mockTimeoutControl = new Mock<ITimeoutControl>();
input.FrameContext.TimeoutControl = mockTimeoutControl.Object;
input.Http1ConnectionContext.TimeoutControl = mockTimeoutControl.Object;
var body = MessageBody.For(HttpVersion.Http11, new FrameRequestHeaders { HeaderContentLength = "5" }, input.Frame);
var body = Http1MessageBody.For(HttpVersion.Http11, new HttpRequestHeaders { HeaderContentLength = "5" }, input.Http1Connection);
// Add some input and read it to start PumpAsync
input.Add("a");
Assert.Equal(1, await body.ReadAsync(new ArraySegment<byte>(new byte[1])));
// Time out on the next read
input.Frame.SendTimeoutResponse();
input.Http1Connection.SendTimeoutResponse();
var exception = await Assert.ThrowsAsync<BadHttpRequestException>(() => body.ConsumeAsync());
Assert.Equal(StatusCodes.Status408RequestTimeout, exception.StatusCode);
@ -607,16 +607,16 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
{
var mockTimeoutControl = new Mock<ITimeoutControl>();
input.FrameContext.TimeoutControl = mockTimeoutControl.Object;
input.Http1ConnectionContext.TimeoutControl = mockTimeoutControl.Object;
var body = MessageBody.For(HttpVersion.Http11, new FrameRequestHeaders { HeaderContentLength = "5" }, input.Frame);
var body = Http1MessageBody.For(HttpVersion.Http11, new HttpRequestHeaders { HeaderContentLength = "5" }, input.Http1Connection);
// Add some input and read it to start PumpAsync
input.Add("a");
Assert.Equal(1, await body.ReadAsync(new ArraySegment<byte>(new byte[1])));
// Time out on the next read
input.Frame.SendTimeoutResponse();
input.Http1Connection.SendTimeoutResponse();
using (var ms = new MemoryStream())
{
@ -634,12 +634,12 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
using (var input = new TestInput())
{
var mockLogger = new Mock<IKestrelTrace>();
input.Frame.ServiceContext.Log = mockLogger.Object;
input.Frame.ConnectionIdFeature = "ConnectionId";
input.Frame.TraceIdentifier = "RequestId";
input.Http1Connection.ServiceContext.Log = mockLogger.Object;
input.Http1Connection.ConnectionIdFeature = "ConnectionId";
input.Http1Connection.TraceIdentifier = "RequestId";
var body = MessageBody.For(HttpVersion.Http11, new FrameRequestHeaders { HeaderContentLength = "2" }, input.Frame);
var stream = new FrameRequestStream(Mock.Of<IHttpBodyControlFeature>());
var body = Http1MessageBody.For(HttpVersion.Http11, new HttpRequestHeaders { HeaderContentLength = "2" }, input.Http1Connection);
var stream = new HttpRequestStream(Mock.Of<IHttpBodyControlFeature>());
stream.StartAcceptingReads(body);
// Add some input and consume it to ensure PumpAsync is running
@ -664,12 +664,12 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
mockLogger
.Setup(logger => logger.RequestBodyDone("ConnectionId", "RequestId"))
.Callback(() => logEvent.Set());
input.Frame.ServiceContext.Log = mockLogger.Object;
input.Frame.ConnectionIdFeature = "ConnectionId";
input.Frame.TraceIdentifier = "RequestId";
input.Http1Connection.ServiceContext.Log = mockLogger.Object;
input.Http1Connection.ConnectionIdFeature = "ConnectionId";
input.Http1Connection.TraceIdentifier = "RequestId";
var body = MessageBody.For(HttpVersion.Http11, new FrameRequestHeaders { HeaderContentLength = "2" }, input.Frame);
var stream = new FrameRequestStream(Mock.Of<IHttpBodyControlFeature>());
var body = Http1MessageBody.For(HttpVersion.Http11, new HttpRequestHeaders { HeaderContentLength = "2" }, input.Http1Connection);
var stream = new HttpRequestStream(Mock.Of<IHttpBodyControlFeature>());
stream.StartAcceptingReads(body);
// Add some input and consume it to ensure PumpAsync is running
@ -690,9 +690,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
using (var input = new TestInput())
{
var mockTimeoutControl = new Mock<ITimeoutControl>();
input.FrameContext.TimeoutControl = mockTimeoutControl.Object;
input.Http1ConnectionContext.TimeoutControl = mockTimeoutControl.Object;
var body = MessageBody.For(HttpVersion.Http11, new FrameRequestHeaders { HeaderContentLength = "12" }, input.Frame);
var body = Http1MessageBody.For(HttpVersion.Http11, new HttpRequestHeaders { HeaderContentLength = "12" }, input.Http1Connection);
// Add some input and read it to start PumpAsync
input.Add("hello,");
@ -701,7 +701,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
input.Add(" world");
Assert.Equal(6, await body.ReadAsync(new ArraySegment<byte>(new byte[6])));
// Due to the limits set on Frame.RequestBodyPipe, backpressure should be triggered on every write to that pipe.
// Due to the limits set on HttpProtocol.RequestBodyPipe, backpressure should be triggered on every write to that pipe.
mockTimeoutControl.Verify(timeoutControl => timeoutControl.PauseTimingReads(), Times.Exactly(2));
mockTimeoutControl.Verify(timeoutControl => timeoutControl.ResumeTimingReads(), Times.Exactly(2));
}
@ -715,20 +715,20 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
var produceContinueCalled = false;
var startTimingReadsCalledAfterProduceContinue = false;
var mockFrameControl = new Mock<IFrameControl>();
mockFrameControl
.Setup(frameControl => frameControl.ProduceContinue())
var mockHttpResponseControl = new Mock<IHttpResponseControl>();
mockHttpResponseControl
.Setup(httpResponseControl => httpResponseControl.ProduceContinue())
.Callback(() => produceContinueCalled = true);
input.Frame.FrameControl = mockFrameControl.Object;
input.Http1Connection.HttpResponseControl = mockHttpResponseControl.Object;
var mockTimeoutControl = new Mock<ITimeoutControl>();
mockTimeoutControl
.Setup(timeoutControl => timeoutControl.StartTimingReads())
.Callback(() => startTimingReadsCalledAfterProduceContinue = produceContinueCalled);
input.FrameContext.TimeoutControl = mockTimeoutControl.Object;
input.Http1ConnectionContext.TimeoutControl = mockTimeoutControl.Object;
var body = MessageBody.For(HttpVersion.Http11, new FrameRequestHeaders { HeaderContentLength = "5" }, input.Frame);
var body = Http1MessageBody.For(HttpVersion.Http11, new HttpRequestHeaders { HeaderContentLength = "5" }, input.Http1Connection);
// Add some input and read it to start PumpAsync
var readTask = body.ReadAsync(new ArraySegment<byte>(new byte[1]));
@ -748,9 +748,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
using (var input = new TestInput())
{
var mockTimeoutControl = new Mock<ITimeoutControl>();
input.FrameContext.TimeoutControl = mockTimeoutControl.Object;
input.Http1ConnectionContext.TimeoutControl = mockTimeoutControl.Object;
var body = MessageBody.For(HttpVersion.Http11, new FrameRequestHeaders { HeaderConnection = "upgrade" }, input.Frame);
var body = Http1MessageBody.For(HttpVersion.Http11, new HttpRequestHeaders { HeaderConnection = "upgrade" }, input.Http1Connection);
// Add some input and read it to start PumpAsync
input.Add("a");
@ -763,7 +763,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
mockTimeoutControl.Verify(timeoutControl => timeoutControl.StartTimingReads(), Times.Never);
mockTimeoutControl.Verify(timeoutControl => timeoutControl.StopTimingReads(), Times.Never);
// Due to the limits set on Frame.RequestBodyPipe, backpressure should be triggered on every
// Due to the limits set on HttpProtocol.RequestBodyPipe, backpressure should be triggered on every
// write to that pipe. Verify that read timing pause and resume are not called on upgrade
// requests.
mockTimeoutControl.Verify(timeoutControl => timeoutControl.PauseTimingReads(), Times.Never);

View File

@ -50,11 +50,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
}
}
private OutputProducer CreateOutputProducer(PipeOptions pipeOptions)
private Http1OutputProducer CreateOutputProducer(PipeOptions pipeOptions)
{
var pipe = _pipeFactory.Create(pipeOptions);
var serviceContext = new TestServiceContext();
var socketOutput = new OutputProducer(
var socketOutput = new Http1OutputProducer(
pipe.Reader,
pipe.Writer,
"0",

View File

@ -59,7 +59,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
var serviceContext = new TestServiceContext();
serviceContext.ServerOptions.Limits.MaxRequestBufferSize = maxRequestBufferSize;
var connectionLifetime = new FrameConnection(new FrameConnectionContext
var connectionLifetime = new HttpConnection(new HttpConnectionContext
{
ServiceContext = serviceContext
});
@ -78,7 +78,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
var serviceContext = new TestServiceContext();
serviceContext.ServerOptions.Limits.MaxResponseBufferSize = maxRequestBufferSize;
var connectionLifetime = new FrameConnection(new FrameConnectionContext
var connectionLifetime = new HttpConnection(new HttpConnectionContext
{
ServiceContext = serviceContext
});

View File

@ -16,7 +16,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
[Fact]
public async Task StreamsThrowAfterAbort()
{
var streams = new Streams(Mock.Of<IHttpBodyControlFeature>(), Mock.Of<IFrameControl>());
var streams = new Streams(Mock.Of<IHttpBodyControlFeature>(), Mock.Of<IHttpResponseControl>());
var (request, response) = streams.Start(new MockMessageBody());
var ex = new Exception("My error");
@ -30,7 +30,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
[Fact]
public async Task StreamsThrowOnAbortAfterUpgrade()
{
var streams = new Streams(Mock.Of<IHttpBodyControlFeature>(), Mock.Of<IFrameControl>());
var streams = new Streams(Mock.Of<IHttpBodyControlFeature>(), Mock.Of<IHttpResponseControl>());
var (request, response) = streams.Start(new MockMessageBody(upgradeable: true));
var upgrade = streams.Upgrade();
@ -52,7 +52,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
[Fact]
public async Task StreamsThrowOnUpgradeAfterAbort()
{
var streams = new Streams(Mock.Of<IHttpBodyControlFeature>(), Mock.Of<IFrameControl>());
var streams = new Streams(Mock.Of<IHttpBodyControlFeature>(), Mock.Of<IHttpResponseControl>());
var (request, response) = streams.Start(new MockMessageBody(upgradeable: true));
var ex = new Exception("My error");
@ -72,7 +72,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await upgrade.WriteAsync(new byte[1], 0, 1);
}
private class MockMessageBody : MessageBody
private class MockMessageBody : Http1MessageBody
{
public MockMessageBody(bool upgradeable = false)
: base(null)

View File

@ -8,7 +8,7 @@ using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http;
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests.TestHelpers
{
public class MockFrameControl : IFrameControl
public class MockHttpResponseControl : IHttpResponseControl
{
public Task FlushAsync(CancellationToken cancellationToken)
{

View File

@ -25,7 +25,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
Transport = pair.Transport;
Application = pair.Application;
FrameContext = new FrameContext
Http1ConnectionContext = new Http1ConnectionContext
{
ServiceContext = new TestServiceContext(),
ConnectionFeatures = new FeatureCollection(),
@ -35,8 +35,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
TimeoutControl = Mock.Of<ITimeoutControl>()
};
Frame = new Frame<object>(null, FrameContext);
Frame.FrameControl = Mock.Of<IFrameControl>();
Http1Connection = new Http1Connection<object>(null, Http1ConnectionContext);
Http1Connection.HttpResponseControl = Mock.Of<IHttpResponseControl>();
}
public IPipeConnection Transport { get; }
@ -45,9 +45,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
public PipeFactory PipeFactory => _pipelineFactory;
public FrameContext FrameContext { get; }
public Http1ConnectionContext Http1ConnectionContext { get; }
public Frame Frame { get; set; }
public Http1Connection Http1Connection { get; set; }
public void Add(string text)
{

View File

@ -12,41 +12,35 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
[Fact]
public void GeneratedCodeIsUpToDate()
{
const string frameHeadersGeneratedPath = "../../../../../src/Kestrel.Core/Internal/Http/FrameHeaders.Generated.cs";
const string frameGeneratedPath = "../../../../../src/Kestrel.Core/Internal/Http/Frame.Generated.cs";
const string http2StreamGeneratedPath = "../../../../../src/Kestrel.Core/Internal/Http2/Http2Stream.Generated.cs";
const string httpHeadersGeneratedPath = "../../../../../src/Kestrel.Core/Internal/Http/HttpHeaders.Generated.cs";
const string httpProtocolGeneratedPath = "../../../../../src/Kestrel.Core/Internal/Http/HttpProtocol.Generated.cs";
const string httpUtilitiesGeneratedPath = "../../../../../src/Kestrel.Core/Internal/Infrastructure/HttpUtilities.Generated.cs";
var testFrameHeadersGeneratedPath = Path.GetTempFileName();
var testFrameGeneratedPath = Path.GetTempFileName();
var testHttp2StreamGeneratedPath = Path.GetTempFileName();
var testHttpHeadersGeneratedPath = Path.GetTempFileName();
var testHttpProtocolGeneratedPath = Path.GetTempFileName();
var testHttpUtilitiesGeneratedPath = Path.GetTempFileName();
try
{
var currentFrameHeadersGenerated = File.ReadAllText(frameHeadersGeneratedPath);
var currentFrameGenerated = File.ReadAllText(frameGeneratedPath);
var currentHttp2StreamGenerated = File.ReadAllText(http2StreamGeneratedPath);
var currentHttpHeadersGenerated = File.ReadAllText(httpHeadersGeneratedPath);
var currentHttpProtocolGenerated = File.ReadAllText(httpProtocolGeneratedPath);
var currentHttpUtilitiesGenerated = File.ReadAllText(httpUtilitiesGeneratedPath);
CodeGenerator.Program.Run(testFrameHeadersGeneratedPath, testFrameGeneratedPath, testHttp2StreamGeneratedPath, testHttpUtilitiesGeneratedPath);
CodeGenerator.Program.Run(testHttpHeadersGeneratedPath, testHttpProtocolGeneratedPath, testHttpUtilitiesGeneratedPath);
var testFrameHeadersGenerated = File.ReadAllText(testFrameHeadersGeneratedPath);
var testFrameGenerated = File.ReadAllText(testFrameGeneratedPath);
var testHttp2StreamGenerated = File.ReadAllText(testHttp2StreamGeneratedPath);
var testHttpHeadersGenerated = File.ReadAllText(testHttpHeadersGeneratedPath);
var testHttpProtocolGenerated = File.ReadAllText(testHttpProtocolGeneratedPath);
var testHttpUtilitiesGenerated = File.ReadAllText(testHttpUtilitiesGeneratedPath);
Assert.Equal(currentFrameHeadersGenerated, testFrameHeadersGenerated, ignoreLineEndingDifferences: true);
Assert.Equal(currentFrameGenerated, testFrameGenerated, ignoreLineEndingDifferences: true);
Assert.Equal(currentHttp2StreamGenerated, testHttp2StreamGenerated, ignoreLineEndingDifferences: true);
Assert.Equal(currentHttpHeadersGenerated, testHttpHeadersGenerated, ignoreLineEndingDifferences: true);
Assert.Equal(currentHttpProtocolGenerated, testHttpProtocolGenerated, ignoreLineEndingDifferences: true);
Assert.Equal(currentHttpUtilitiesGenerated, testHttpUtilitiesGenerated, ignoreLineEndingDifferences: true);
}
finally
{
File.Delete(testFrameHeadersGeneratedPath);
File.Delete(testFrameGeneratedPath);
File.Delete(testHttp2StreamGeneratedPath);
File.Delete(testHttpHeadersGeneratedPath);
File.Delete(testHttpProtocolGeneratedPath);
File.Delete(testHttpUtilitiesGeneratedPath);
}
}

View File

@ -14,7 +14,7 @@ using Xunit;
namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{
public class FrameConnectionManagerTests
public class HttpConnectionManagerTests
{
// This test causes MemoryPoolBlocks to be finalized which in turn causes an assert failure in debug builds.
#if !DEBUG
@ -24,7 +24,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{
////////////////////////////////////////////////////////////////////////////////////////
// WARNING: This test will fail under a debugger because Task.s_currentActiveTasks //
// roots FrameConnection. //
// roots HttpConnection. //
////////////////////////////////////////////////////////////////////////////////////////
var logWh = new SemaphoreSlim(0);

View File

@ -1111,7 +1111,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
if (firstRequest)
{
originalRequestHeaders = requestFeature.Headers;
requestFeature.Headers = new FrameRequestHeaders();
requestFeature.Headers = new HttpRequestHeaders();
firstRequest = false;
}
else

View File

@ -154,7 +154,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
context.Response.OnStarting(() => Task.Run(() => onStartingCalled = true));
context.Response.OnCompleted(() => Task.Run(() => onCompletedCalled = true));
// Prevent OnStarting call (see Frame<T>.ProcessRequestsAsync()).
// Prevent OnStarting call (see HttpProtocol.ProcessRequestsAsync()).
throw new Exception();
});
});
@ -501,7 +501,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
"");
// Wait for message to be logged before disposing the socket.
// Disposing the socket will abort the connection and Frame._requestAborted
// Disposing the socket will abort the connection and HttpProtocol._requestAborted
// might be 1 by the time ProduceEnd() gets called and the message is logged.
await logTcs.Task.TimeoutAfter(TimeSpan.FromSeconds(10));
}
@ -736,7 +736,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
httpContext.Response.ContentLength = 12;
await httpContext.Response.WriteAsync("hello,");
// Wait until the request is aborted so we know Frame will skip the response content length check.
// Wait until the request is aborted so we know HttpProtocol will skip the response content length check.
Assert.True(await requestAborted.WaitAsync(TimeSpan.FromSeconds(10)));
}, new TestServiceContext { Log = mockTrace.Object }))
{
@ -1526,12 +1526,12 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
"a",
"");
// This will be consumed by Frame when it attempts to
// This will be consumed by Http1Connection when it attempts to
// consume the request body and will cause an error.
await connection.Send(
"gg");
// If 100 Continue sets Frame.HasResponseStarted to true,
// If 100 Continue sets HttpProtocol.HasResponseStarted to true,
// a success response will be produced before the server sees the
// bad chunk header above, making this test fail.
await connection.ReceiveForcedEnd(
@ -2215,7 +2215,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
if (firstRequest)
{
originalResponseHeaders = responseFeature.Headers;
responseFeature.Headers = new FrameResponseHeaders();
responseFeature.Headers = new HttpResponseHeaders();
firstRequest = false;
}
else

View File

@ -260,7 +260,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
const int limit = 10;
var upgradeTcs = new TaskCompletionSource<object>();
var serviceContext = new TestServiceContext();
serviceContext.ConnectionManager = new FrameConnectionManager(serviceContext.Log, ResourceCounter.Quota(limit));
serviceContext.ConnectionManager = new HttpConnectionManager(serviceContext.Log, ResourceCounter.Quota(limit));
using (var server = new TestServer(async context =>
{

View File

@ -75,7 +75,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Tests
var buffer = new ArraySegment<byte>(new byte[bufferSize], 0, bufferSize);
// Act
var writeTask = outputProducer.WriteAsync(buffer);
var writeTask = outputProducer.WriteDataAsync(buffer);
// Assert
await writeTask.TimeoutAfter(TimeSpan.FromSeconds(5));
@ -110,7 +110,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Tests
var buffer = new ArraySegment<byte>(new byte[bufferSize], 0, bufferSize);
// Act
var writeTask = outputProducer.WriteAsync(buffer);
var writeTask = outputProducer.WriteDataAsync(buffer);
// Assert
await writeTask.TimeoutAfter(TimeSpan.FromSeconds(5));
@ -156,7 +156,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Tests
var buffer = new ArraySegment<byte>(new byte[bufferSize], 0, bufferSize);
// Act
var writeTask = outputProducer.WriteAsync(buffer);
var writeTask = outputProducer.WriteDataAsync(buffer);
// Assert
Assert.False(writeTask.IsCompleted);
@ -211,14 +211,14 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Tests
var buffer = new ArraySegment<byte>(new byte[bufferSize], 0, bufferSize);
// Act
var writeTask1 = outputProducer.WriteAsync(buffer);
var writeTask1 = outputProducer.WriteDataAsync(buffer);
// Assert
// The first write should pre-complete since it is <= _maxBytesPreCompleted.
Assert.Equal(TaskStatus.RanToCompletion, writeTask1.Status);
// Act
var writeTask2 = outputProducer.WriteAsync(buffer);
var writeTask2 = outputProducer.WriteDataAsync(buffer);
await _mockLibuv.OnPostTask;
// Assert
@ -275,7 +275,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Tests
var halfWriteBehindBuffer = new ArraySegment<byte>(data, 0, bufferSize);
// Act
var writeTask1 = outputProducer.WriteAsync(halfWriteBehindBuffer);
var writeTask1 = outputProducer.WriteDataAsync(halfWriteBehindBuffer);
// Assert
// The first write should pre-complete since it is <= _maxBytesPreCompleted.
@ -291,10 +291,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Tests
halfWriteBehindBuffer);
// Act
var writeTask2 = outputProducer.WriteAsync(halfWriteBehindBuffer);
var writeTask2 = outputProducer.WriteDataAsync(halfWriteBehindBuffer);
Assert.False(writeTask2.IsCompleted);
var writeTask3 = outputProducer.WriteAsync(halfWriteBehindBuffer);
var writeTask3 = outputProducer.WriteDataAsync(halfWriteBehindBuffer);
Assert.False(writeTask3.IsCompleted);
// Drain the write queue
@ -345,7 +345,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Tests
var fullBuffer = new ArraySegment<byte>(data, 0, bufferSize);
// Act
var task1Success = outputProducer.WriteAsync(fullBuffer, cancellationToken: abortedSource.Token);
var task1Success = outputProducer.WriteDataAsync(fullBuffer, cancellationToken: abortedSource.Token);
// task1 should complete successfully as < _maxBytesPreCompleted
// First task is completed and successful
@ -354,8 +354,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Tests
Assert.False(task1Success.IsFaulted);
// following tasks should wait.
var task2Success = outputProducer.WriteAsync(fullBuffer);
var task3Canceled = outputProducer.WriteAsync(fullBuffer, cancellationToken: abortedSource.Token);
var task2Success = outputProducer.WriteDataAsync(fullBuffer);
var task3Canceled = outputProducer.WriteDataAsync(fullBuffer, cancellationToken: abortedSource.Token);
// Give time for tasks to percolate
await _mockLibuv.OnPostTask;
@ -383,7 +383,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Tests
// A final write guarantees that the error is observed by OutputProducer,
// but doesn't return a canceled/faulted task.
var task4Success = outputProducer.WriteAsync(fullBuffer, cancellationToken: default(CancellationToken));
var task4Success = outputProducer.WriteDataAsync(fullBuffer, cancellationToken: default(CancellationToken));
Assert.True(task4Success.IsCompleted);
Assert.False(task4Success.IsCanceled);
Assert.False(task4Success.IsFaulted);
@ -437,7 +437,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Tests
var fullBuffer = new ArraySegment<byte>(data, 0, bufferSize);
// Act
var task1Success = outputProducer.WriteAsync(fullBuffer, cancellationToken: abortedSource.Token);
var task1Success = outputProducer.WriteDataAsync(fullBuffer, cancellationToken: abortedSource.Token);
// task1 should complete successfully as < _maxBytesPreCompleted
// First task is completed and successful
@ -446,7 +446,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Tests
Assert.False(task1Success.IsFaulted);
// following tasks should wait.
var task3Canceled = outputProducer.WriteAsync(fullBuffer, cancellationToken: abortedSource.Token);
var task3Canceled = outputProducer.WriteDataAsync(fullBuffer, cancellationToken: abortedSource.Token);
// Give time for tasks to percolate
await _mockLibuv.OnPostTask;
@ -466,7 +466,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Tests
// A final write guarantees that the error is observed by OutputProducer,
// but doesn't return a canceled/faulted task.
var task4Success = outputProducer.WriteAsync(fullBuffer);
var task4Success = outputProducer.WriteDataAsync(fullBuffer);
Assert.True(task4Success.IsCompleted);
Assert.False(task4Success.IsCanceled);
Assert.False(task4Success.IsFaulted);
@ -520,7 +520,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Tests
var fullBuffer = new ArraySegment<byte>(data, 0, bufferSize);
// Act
var task1Waits = outputProducer.WriteAsync(fullBuffer);
var task1Waits = outputProducer.WriteDataAsync(fullBuffer);
// First task is not completed
Assert.False(task1Waits.IsCompleted);
@ -528,7 +528,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Tests
Assert.False(task1Waits.IsFaulted);
// following tasks should wait.
var task3Canceled = outputProducer.WriteAsync(fullBuffer, cancellationToken: abortedSource.Token);
var task3Canceled = outputProducer.WriteDataAsync(fullBuffer, cancellationToken: abortedSource.Token);
// Give time for tasks to percolate
await _mockLibuv.OnPostTask;
@ -553,7 +553,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Tests
// A final write guarantees that the error is observed by OutputProducer,
// but doesn't return a canceled/faulted task.
var task4Success = outputProducer.WriteAsync(fullBuffer);
var task4Success = outputProducer.WriteDataAsync(fullBuffer);
Assert.True(task4Success.IsCompleted);
Assert.False(task4Success.IsCanceled);
Assert.False(task4Success.IsFaulted);
@ -599,7 +599,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Tests
var buffer = new ArraySegment<byte>(new byte[bufferSize], 0, bufferSize);
// Act (Pre-complete the maximum number of bytes in preparation for the rest of the test)
var writeTask1 = outputProducer.WriteAsync(buffer);
var writeTask1 = outputProducer.WriteDataAsync(buffer);
// Assert
// The first write should pre-complete since it is < _maxBytesPreCompleted.
@ -608,8 +608,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Tests
Assert.NotEmpty(completeQueue);
// Act
var writeTask2 = outputProducer.WriteAsync(buffer);
var writeTask3 = outputProducer.WriteAsync(buffer);
var writeTask2 = outputProducer.WriteDataAsync(buffer);
var writeTask3 = outputProducer.WriteDataAsync(buffer);
await _mockLibuv.OnPostTask;
@ -661,8 +661,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Tests
// Two calls to WriteAsync trigger uv_write once if both calls
// are made before write is scheduled
var ignore = outputProducer.WriteAsync(buffer);
ignore = outputProducer.WriteAsync(buffer);
var ignore = outputProducer.WriteDataAsync(buffer);
ignore = outputProducer.WriteDataAsync(buffer);
_mockLibuv.KestrelThreadBlocker.Set();
@ -682,7 +682,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Tests
}
}
private OutputProducer CreateOutputProducer(PipeOptions pipeOptions, CancellationTokenSource cts = null)
private Http1OutputProducer CreateOutputProducer(PipeOptions pipeOptions, CancellationTokenSource cts = null)
{
var pair = _pipeFactory.CreateConnectionPair(pipeOptions, pipeOptions);
@ -697,7 +697,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Tests
var socket = new MockSocket(_mockLibuv, _libuvThread.Loop.ThreadId, transportContext.Log);
var consumer = new LibuvOutputConsumer(pair.Application.Input, _libuvThread, socket, "0", transportContext.Log);
var frame = new Frame<object>(null, new FrameContext
var http1Connection = new Http1Connection<object>(null, new Http1ConnectionContext
{
ServiceContext = serviceContext,
ConnectionFeatures = new FeatureCollection(),
@ -709,15 +709,15 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Tests
if (cts != null)
{
frame.RequestAborted.Register(cts.Cancel);
http1Connection.RequestAborted.Register(cts.Cancel);
}
var ignore = WriteOutputAsync(consumer, pair.Application.Input, frame);
var ignore = WriteOutputAsync(consumer, pair.Application.Input, http1Connection);
return frame.Output;
return (Http1OutputProducer)http1Connection.Output;
}
private async Task WriteOutputAsync(LibuvOutputConsumer consumer, IPipeReader outputReader, Frame frame)
private async Task WriteOutputAsync(LibuvOutputConsumer consumer, IPipeReader outputReader, Http1Connection http1Connection)
{
// This WriteOutputAsync() calling code is equivalent to that in LibuvConnection.
try
@ -726,14 +726,14 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Tests
// Without ConfigureAwait(false), xunit will dispatch.
await consumer.WriteOutputAsync().ConfigureAwait(false);
frame.Abort(error: null);
http1Connection.Abort(error: null);
outputReader.Complete();
}
catch (UvException ex)
{
frame.Abort(ex);
http1Connection.Abort(ex);
outputReader.Complete(ex);
}
}
}
}
}

View File

@ -8,9 +8,9 @@ using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure;
namespace Microsoft.AspNetCore.Testing
{
public class TestFrame<TContext> : Frame<TContext>
public class TestHttp1Connection<TContext> : Http1Connection<TContext>
{
public TestFrame(IHttpApplication<TContext> application, FrameContext context)
public TestHttp1Connection(IHttpApplication<TContext> application, Http1ConnectionContext context)
: base(application, context)
{
}

View File

@ -38,8 +38,8 @@ namespace Microsoft.AspNetCore.Testing
ThreadPool = new LoggingThreadPool(Log);
SystemClock = new MockSystemClock();
DateHeaderValueManager = new DateHeaderValueManager(SystemClock);
ConnectionManager = new FrameConnectionManager(Log, ResourceCounter.Unlimited);
HttpParserFactory = frameAdapter => new HttpParser<FrameAdapter>(frameAdapter.Frame.ServiceContext.Log.IsEnabled(LogLevel.Information));
ConnectionManager = new HttpConnectionManager(Log, ResourceCounter.Unlimited);
HttpParserFactory = handler => new HttpParser<Http1ParsingHandler>(handler.Connection.ServiceContext.Log.IsEnabled(LogLevel.Information));
ServerOptions = new KestrelServerOptions
{
AddServerHeader = false

View File

@ -18,7 +18,7 @@
<PropertyGroup>
<StartWorkingDirectory>$(MSBuildThisFileDirectory)..\..\src\Kestrel.Core</StartWorkingDirectory>
<StartArguments>Internal/Http/FrameHeaders.Generated.cs Internal/Http/Frame.Generated.cs Internal/Http2/Http2Stream.Generated.cs Internal/Infrastructure/HttpUtilities.Generated.cs</StartArguments>
<StartArguments>Internal/Http/HttpHeaders.Generated.cs Internal/Http/HttpProtocol.Generated.cs Internal/Infrastructure/HttpUtilities.Generated.cs</StartArguments>
</PropertyGroup>
</Project>

View File

@ -12,17 +12,15 @@ namespace CodeGenerator
{
// This project can output the Class library as a NuGet Package.
// To enable this option, right-click on the project and select the Properties menu item. In the Build tab select "Produce outputs on build".
public class FrameFeatureCollection
public class HttpProtocolFeatureCollection
{
static string Each<T>(IEnumerable<T> values, Func<T, string> formatter)
{
return values.Select(formatter).Aggregate((a, b) => a + b);
}
public static string GeneratedFile(string className, string namespaceSuffix, IEnumerable<Type> additionalFeatures = null)
public static string GeneratedFile(string className)
{
additionalFeatures = additionalFeatures ?? new Type[] { };
var alwaysFeatures = new[]
{
typeof(IHttpRequestFeature),
@ -43,6 +41,7 @@ namespace CodeGenerator
var sometimesFeatures = new[]
{
typeof(IHttpUpgradeFeature),
typeof(IHttp2StreamIdFeature),
typeof(IResponseCookiesFeature),
typeof(IItemsFeature),
typeof(ITlsConnectionFeature),
@ -59,15 +58,14 @@ namespace CodeGenerator
typeof(IHttpSendFileFeature),
};
var allFeatures = alwaysFeatures.Concat(commonFeatures).Concat(sometimesFeatures).Concat(rareFeatures).Concat(additionalFeatures);
var allFeatures = alwaysFeatures.Concat(commonFeatures).Concat(sometimesFeatures).Concat(rareFeatures);
// NOTE: This list MUST always match the set of feature interfaces implemented by Frame.
// See also: src/Kestrel/Http/Frame.FeatureCollection.cs
// NOTE: This list MUST always match the set of feature interfaces implemented by HttpProtocol.
// See also: src/Kestrel/Http/HttpProtocol.FeatureCollection.cs
var implementedFeatures = new[]
{
typeof(IHttpRequestFeature),
typeof(IHttpResponseFeature),
typeof(IHttpUpgradeFeature),
typeof(IHttpRequestIdentifierFeature),
typeof(IHttpRequestLifetimeFeature),
typeof(IHttpConnectionFeature),
@ -75,7 +73,7 @@ namespace CodeGenerator
typeof(IHttpMinRequestBodyDataRateFeature),
typeof(IHttpMinResponseDataRateFeature),
typeof(IHttpBodyControlFeature),
}.Concat(additionalFeatures);
};
return $@"// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
@ -83,7 +81,7 @@ namespace CodeGenerator
using System;
using System.Collections.Generic;
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.{namespaceSuffix}
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
{{
public partial class {className}
{{{Each(allFeatures, feature => $@"
@ -107,7 +105,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.{namespaceSuffix}
return ExtraFeatureGet(key);
}}
internal void FastFeatureSet(Type key, object feature)
protected void FastFeatureSet(Type key, object feature)
{{
_featureRevision++;
{Each(allFeatures, feature => $@"

View File

@ -6,6 +6,7 @@ using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http;
namespace CodeGenerator
{
@ -265,14 +266,14 @@ namespace CodeGenerator
{
Headers = requestHeaders,
HeadersByLength = requestHeaders.GroupBy(x => x.Name.Length),
ClassName = "FrameRequestHeaders",
ClassName = nameof(HttpRequestHeaders),
Bytes = default(byte[])
},
new
{
Headers = responseHeaders,
HeadersByLength = responseHeaders.GroupBy(x => x.Name.Length),
ClassName = "FrameResponseHeaders",
ClassName = nameof(HttpResponseHeaders),
Bytes = responseHeaders.SelectMany(header => header.Bytes).ToArray()
}
};
@ -383,7 +384,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
}}
protected override void SetValueFast(string key, StringValues value)
{{{(loop.ClassName == "FrameResponseHeaders" ? @"
{{{(loop.ClassName == nameof(HttpResponseHeaders) ? @"
ValidateHeaderCharacters(value);" : "")}
switch (key.Length)
{{{Each(loop.HeadersByLength, byLength => $@"
@ -405,7 +406,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
}}
protected override bool AddValueFast(string key, StringValues value)
{{{(loop.ClassName == "FrameResponseHeaders" ? @"
{{{(loop.ClassName == nameof(HttpResponseHeaders) ? @"
ValidateHeaderCharacters(value);" : "")}
switch (key.Length)
{{{Each(loop.HeadersByLength, byLength => $@"
@ -431,7 +432,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
}}
break;")}
}}
{(loop.ClassName == "FrameResponseHeaders" ? @"
{(loop.ClassName == nameof(HttpResponseHeaders) ? @"
ValidateHeaderCharacters(key);" : "")}
Unknown.Add(key, value);
// Return true, above will throw and exit for false
@ -474,7 +475,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
_contentLength = null;
var tempBits = _bits;
_bits = 0;
if(FrameHeaders.BitCount(tempBits) > 12)
if(HttpHeaders.BitCount(tempBits) > 12)
{{
_headers = default(HeaderReferences);
return;
@ -521,7 +522,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
return true;
}}
{(loop.ClassName == "FrameResponseHeaders" ? $@"
{(loop.ClassName == nameof(HttpResponseHeaders) ? $@"
protected void CopyToFast(ref WritableBufferWriter output)
{{
var tempBits = _bits | (_contentLength.HasValue ? {1L << 63}L : 0);
@ -564,7 +565,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
tempBits &= ~{1L << 63}L;
}}" : "")}")}
}}" : "")}
{(loop.ClassName == "FrameRequestHeaders" ? $@"
{(loop.ClassName == nameof(HttpRequestHeaders) ? $@"
public unsafe void Append(byte* pKeyBytes, int keyLength, string value)
{{
var pUB = pKeyBytes;

View File

@ -5,7 +5,6 @@ using System;
using System.IO;
using Microsoft.AspNetCore.Server.Kestrel.Core.Features;
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http;
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2;
namespace CodeGenerator
{
@ -15,12 +14,12 @@ namespace CodeGenerator
{
if (args.Length < 1)
{
Console.Error.WriteLine("Missing path to FrameHeaders.Generated.cs");
Console.Error.WriteLine("Missing path to HttpHeaders.Generated.cs");
return 1;
}
else if (args.Length < 2)
{
Console.Error.WriteLine("Missing path to Frame.Generated.cs");
Console.Error.WriteLine("Missing path to HttpProtocol.Generated.cs");
return 1;
}
else if (args.Length < 3)
@ -29,16 +28,15 @@ namespace CodeGenerator
return 1;
}
Run(args[0], args[1], args[2], args[3]);
Run(args[0], args[1], args[2]);
return 0;
}
public static void Run(string knownHeadersPath, string frameFeatureCollectionPath, string http2StreamFeatureCollectionPath, string httpUtilitiesPath)
public static void Run(string knownHeadersPath, string httpProtocolFeatureCollectionPath, string httpUtilitiesPath)
{
var knownHeadersContent = KnownHeaders.GeneratedFile();
var frameFeatureCollectionContent = FrameFeatureCollection.GeneratedFile(nameof(Frame), "Http");
var http2StreamFeatureCollectionContent = FrameFeatureCollection.GeneratedFile(nameof(Http2Stream), "Http2", new[] { typeof(IHttp2StreamIdFeature) });
var httpProtocolFeatureCollectionContent = HttpProtocolFeatureCollection.GeneratedFile(nameof(HttpProtocol));
var httpUtilitiesContent = HttpUtilities.HttpUtilities.GeneratedFile();
var existingKnownHeaders = File.Exists(knownHeadersPath) ? File.ReadAllText(knownHeadersPath) : "";
@ -47,16 +45,10 @@ namespace CodeGenerator
File.WriteAllText(knownHeadersPath, knownHeadersContent);
}
var existingFrameFeatureCollection = File.Exists(frameFeatureCollectionPath) ? File.ReadAllText(frameFeatureCollectionPath) : "";
if (!string.Equals(frameFeatureCollectionContent, existingFrameFeatureCollection))
var existingHttpProtocolFeatureCollection = File.Exists(httpProtocolFeatureCollectionPath) ? File.ReadAllText(httpProtocolFeatureCollectionPath) : "";
if (!string.Equals(httpProtocolFeatureCollectionContent, existingHttpProtocolFeatureCollection))
{
File.WriteAllText(frameFeatureCollectionPath, frameFeatureCollectionContent);
}
var existingHttp2StreamFeatureCollection = File.Exists(http2StreamFeatureCollectionPath) ? File.ReadAllText(http2StreamFeatureCollectionPath) : "";
if (!string.Equals(http2StreamFeatureCollectionContent, existingHttp2StreamFeatureCollection))
{
File.WriteAllText(http2StreamFeatureCollectionPath, http2StreamFeatureCollectionContent);
File.WriteAllText(httpProtocolFeatureCollectionPath, httpProtocolFeatureCollectionContent);
}
var existingHttpUtilities = File.Exists(httpUtilitiesPath) ? File.ReadAllText(httpUtilitiesPath) : "";