Create Frame in FrameConnection.

This commit is contained in:
Cesar Blum Silveira 2017-05-10 16:48:37 -07:00 committed by GitHub
parent b9518e3684
commit 37f15bdd85
16 changed files with 108 additions and 91 deletions

View File

@ -33,15 +33,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
var connectionId = CorrelationIdGenerator.GetNextId();
var frameConnectionId = Interlocked.Increment(ref _lastFrameConnectionId);
var frameContext = new FrameContext
{
ConnectionId = connectionId,
ConnectionInformation = connectionInfo,
ServiceContext = _serviceContext
};
var frame = new Frame<TContext>(_application, frameContext);
var connection = new FrameConnection(new FrameConnectionContext
{
ConnectionId = connectionId,
@ -49,7 +40,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
ServiceContext = _serviceContext,
ConnectionInformation = connectionInfo,
ConnectionAdapters = _listenOptions.ConnectionAdapters,
Frame = frame,
Input = inputPipe,
Output = outputPipe,
});
@ -57,7 +47,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
// Since data cannot be added to the inputPipe by the transport until OnConnection returns,
// Frame.ProcessRequestsAsync is guaranteed to unblock the transport thread before calling
// application code.
connection.StartRequestProcessing();
connection.StartRequestProcessing<TContext>(_application);
return connection;
}

View File

@ -8,6 +8,7 @@ using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Hosting.Server;
using Microsoft.AspNetCore.Server.Kestrel.Core.Adapter.Internal;
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http;
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure;
@ -20,9 +21,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
public class FrameConnection : IConnectionContext, ITimeoutControl
{
private readonly FrameConnectionContext _context;
private readonly Frame _frame;
private List<IAdaptedConnection> _adaptedConnections;
private readonly TaskCompletionSource<object> _socketClosedTcs = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
private Frame _frame;
private long _lastTimestamp;
private long _timeoutTimestamp = long.MaxValue;
@ -33,7 +34,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
public FrameConnection(FrameConnectionContext context)
{
_context = context;
_frame = context.Frame;
}
public string ConnectionId => _context.ConnectionId;
@ -61,12 +61,12 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
private IKestrelTrace Log => _context.ServiceContext.Log;
public void StartRequestProcessing()
public void StartRequestProcessing<TContext>(IHttpApplication<TContext> application)
{
_lifetimeTask = ProcessRequestsAsync();
_lifetimeTask = ProcessRequestsAsync<TContext>(application);
}
private async Task ProcessRequestsAsync()
private async Task ProcessRequestsAsync<TContext>(IHttpApplication<TContext> application)
{
try
{
@ -90,11 +90,19 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
output = adaptedPipeline.Output;
}
// Set these before the first await, this is to make sure that we don't yield control
// to the transport until we've added the connection to the connection manager
_frame.TimeoutControl = this;
_frame.Input = input;
_frame.Output = new OutputProducer(output, ConnectionId, Log);
// _frame must be initialized before adding the connection to the connection manager
_frame = new Frame<TContext>(application, new FrameContext
{
ConnectionId = _context.ConnectionId,
ConnectionInformation = _context.ConnectionInformation,
ServiceContext = _context.ServiceContext,
TimeoutControl = this,
Input = input,
Output = output
});
// 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);
_lastTimestamp = _context.ServiceContext.SystemClock.UtcNow.Ticks;
@ -125,6 +133,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
public void OnConnectionClosed(Exception ex)
{
Debug.Assert(_frame != null, $"nameof({_frame}) is null");
// Abort the connection (if not already aborted)
_frame.Abort(ex);
@ -133,6 +143,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
public Task StopAsync()
{
Debug.Assert(_frame != null, $"nameof({_frame}) is null");
_frame.Stop();
return _lifetimeTask;
@ -140,12 +152,16 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
public void Abort(Exception ex)
{
Debug.Assert(_frame != null, $"nameof({_frame}) is null");
// Abort the connection (if not already aborted)
_frame.Abort(ex);
}
public Task AbortAsync(Exception ex)
{
Debug.Assert(_frame != null, $"nameof({_frame}) is null");
// Abort the connection (if not already aborted)
_frame.Abort(ex);
@ -154,11 +170,15 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
public void Timeout()
{
Debug.Assert(_frame != null, $"nameof({_frame}) is null");
_frame.SetBadRequestState(RequestRejectionReason.RequestTimeout);
}
private async Task<Stream> ApplyConnectionAdaptersAsync()
{
Debug.Assert(_frame != null, $"nameof({_frame}) is null");
var features = new FeatureCollection();
var connectionAdapters = _context.ConnectionAdapters;
var stream = new RawStream(_context.Input.Reader, _context.Output.Writer);
@ -202,6 +222,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
public void Tick(DateTimeOffset now)
{
Debug.Assert(_frame != null, $"nameof({_frame}) is null");
var timestamp = now.Ticks;
// TODO: Use PlatformApis.VolatileRead equivalent again

View File

@ -16,7 +16,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
public ServiceContext ServiceContext { get; set; }
public List<IConnectionAdapter> ConnectionAdapters { get; set; }
public IConnectionInformation ConnectionInformation { get; set; }
public Frame Frame { get; set; }
public IPipe Input { get; set; }
public IPipe Output { get; set; }

View File

@ -96,15 +96,17 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
FrameControl = this;
_keepAliveTicks = ServerOptions.Limits.KeepAliveTimeout.Ticks;
_requestHeadersTimeoutTicks = ServerOptions.Limits.RequestHeadersTimeout.Ticks;
Output = new OutputProducer(frameContext.Output, frameContext.ConnectionId, frameContext.ServiceContext.Log);
}
public ServiceContext ServiceContext => _frameContext.ServiceContext;
public IConnectionInformation ConnectionInformation => _frameContext.ConnectionInformation;
public IPipeReader Input { get; set; }
public OutputProducer Output { get; set; }
public IFeatureCollection ConnectionFeatures { get; set; }
public ITimeoutControl TimeoutControl { get; set; }
public IPipeReader Input => _frameContext.Input;
public OutputProducer Output { get; }
public ITimeoutControl TimeoutControl => _frameContext.TimeoutControl;
protected IKestrelTrace Log => ServiceContext.Log;
private DateHeaderValueManager DateHeaderValueManager => ServiceContext.DateHeaderValueManager;

View File

@ -1,6 +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 Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure;
using Microsoft.AspNetCore.Server.Kestrel.Internal.System.IO.Pipelines;
using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions;
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
@ -10,5 +12,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
public string ConnectionId { get; set; }
public ServiceContext ServiceContext { get; set; }
public IConnectionInformation ConnectionInformation { get; set; }
public ITimeoutControl TimeoutControl { get; set; }
public IPipeReader Input { get; set; }
public IPipe Output { get; set; }
}
}

View File

@ -6,6 +6,7 @@ using System.Collections.Generic;
using System.Globalization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http;
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure;
using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions;
using Microsoft.AspNetCore.Testing;
using Microsoft.Extensions.Primitives;
@ -22,7 +23,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
var frameContext = new FrameContext
{
ServiceContext = new TestServiceContext(),
ConnectionInformation = Mock.Of<IConnectionInformation>()
ConnectionInformation = Mock.Of<IConnectionInformation>(),
TimeoutControl = null
};
var frame = new Frame<object>(application: null, frameContext: frameContext);
@ -61,14 +63,14 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
[InlineData("Ser\u0080ver", "Data")]
[InlineData("Server", "Da\u0080ta")]
[InlineData("Unknown\u0080-Header", "Data")]
[InlineData("Ser™ver", "Data")]
[InlineData("Server", "Da™ta")]
[InlineData("Unknown™-Header", "Data")]
[InlineData("Ser™ver", "Data")]
[InlineData("šerver", "Data")]
[InlineData("Server", "Dašta")]
[InlineData("Unknownš-Header", "Data")]
[InlineData("Seršver", "Data")]
[InlineData("Ser™ver", "Data")]
[InlineData("Server", "Da™ta")]
[InlineData("Unknown™-Header", "Data")]
[InlineData("Ser™ver", "Data")]
[InlineData("šerver", "Data")]
[InlineData("Server", "Dašta")]
[InlineData("Unknownš-Header", "Data")]
[InlineData("Seršver", "Data")]
public void AddingControlOrNonAsciiCharactersToHeadersThrows(string key, string value)
{
var responseHeaders = new FrameResponseHeaders();
@ -259,4 +261,4 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
"42.000",
};
}
}
}

View File

@ -36,11 +36,12 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
private readonly PipeFactory _pipelineFactory;
private ReadCursor _consumed;
private ReadCursor _examined;
private Mock<ITimeoutControl> _timeoutControl;
private class TestFrame<TContext> : Frame<TContext>
{
public TestFrame(IHttpApplication<TContext> application, FrameContext context)
: base(application, context)
: base(application, context)
{
}
@ -57,21 +58,17 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
var output = _pipelineFactory.Create();
_serviceContext = new TestServiceContext();
_timeoutControl = new Mock<ITimeoutControl>();
_frameContext = new FrameContext
{
ServiceContext = _serviceContext,
ConnectionInformation = Mock.Of<IConnectionInformation>()
};
_frame = new TestFrame<object>(application: null, context: _frameContext)
{
ConnectionInformation = Mock.Of<IConnectionInformation>(),
TimeoutControl = _timeoutControl.Object,
Input = _input.Reader,
TimeoutControl = Mock.Of<ITimeoutControl>()
Output = output
};
_frame.Output = new OutputProducer(output, "", Mock.Of<IKestrelTrace>());
_frame = new TestFrame<object>(application: null, context: _frameContext);
_frame.Reset();
}
@ -343,16 +340,13 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
[Fact]
public async Task ParseRequestStartsRequestHeadersTimeoutOnFirstByteAvailable()
{
var connectionControl = new Mock<ITimeoutControl>();
_frame.TimeoutControl = connectionControl.Object;
await _input.Writer.WriteAsync(Encoding.ASCII.GetBytes("G"));
_frame.ParseRequest((await _input.Reader.ReadAsync()).Buffer, out _consumed, out _examined);
_input.Reader.Advance(_consumed, _examined);
var expectedRequestHeadersTimeout = _serviceContext.ServerOptions.Limits.RequestHeadersTimeout.Ticks;
connectionControl.Verify(cc => cc.ResetTimeout(expectedRequestHeadersTimeout, TimeoutAction.SendTimeoutResponse));
_timeoutControl.Verify(cc => cc.ResetTimeout(expectedRequestHeadersTimeout, TimeoutAction.SendTimeoutResponse));
}
[Fact]
@ -466,13 +460,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
[Fact]
public void ProcessRequestsAsyncEnablesKeepAliveTimeout()
{
var connectionControl = new Mock<ITimeoutControl>();
_frame.TimeoutControl = connectionControl.Object;
var requestProcessingTask = _frame.ProcessRequestsAsync();
var expectedKeepAliveTimeout = _serviceContext.ServerOptions.Limits.KeepAliveTimeout.Ticks;
connectionControl.Verify(cc => cc.SetTimeout(expectedKeepAliveTimeout, TimeoutAction.CloseConnection));
_timeoutControl.Verify(cc => cc.SetTimeout(expectedKeepAliveTimeout, TimeoutAction.CloseConnection));
_frame.Stop();
_input.Writer.Complete();

View File

@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http;
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure;
using Microsoft.AspNetCore.Server.Kestrel.Internal.System.IO.Pipelines;
using Microsoft.AspNetCore.Testing;
using Moq;

View File

@ -7,10 +7,12 @@ using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http;
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure;
using Microsoft.AspNetCore.Server.Kestrel.Internal.System.IO.Pipelines;
using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions;
using Microsoft.AspNetCore.Testing;
using Microsoft.Extensions.Internal;
using Moq;
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
{
@ -21,15 +23,17 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
public TestInput()
{
var innerContext = new FrameContext { ServiceContext = new TestServiceContext() };
FrameContext = new Frame<object>(null, innerContext);
FrameContext.FrameControl = this;
_memoryPool = new MemoryPool();
_pipelineFactory = new PipeFactory();
Pipe = _pipelineFactory.Create();
FrameContext.Input = Pipe.Reader;
FrameContext = new Frame<object>(null, new FrameContext
{
ServiceContext = new TestServiceContext(),
Input = Pipe.Reader
});
FrameContext.FrameControl = this;
}
public IPipe Pipe { get; }

View File

@ -6,6 +6,7 @@ using Microsoft.AspNetCore.Server.Kestrel.Core;
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal;
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http;
using Microsoft.AspNetCore.Server.Kestrel.Internal.System.IO.Pipelines;
using Microsoft.AspNetCore.Server.Kestrel.Performance.Mocks;
namespace Microsoft.AspNetCore.Server.Kestrel.Performance
{
@ -28,7 +29,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Performance
var frameContext = new FrameContext
{
ServiceContext = serviceContext,
ConnectionInformation = new MockConnectionInformation()
ConnectionInformation = new MockConnectionInformation(),
TimeoutControl = new MockTimeoutControl()
};
_frame = new Frame<object>(application: null, frameContext: frameContext);

View File

@ -9,6 +9,7 @@ using Microsoft.AspNetCore.Server.Kestrel.Core;
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal;
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http;
using Microsoft.AspNetCore.Server.Kestrel.Internal.System.IO.Pipelines;
using Microsoft.AspNetCore.Server.Kestrel.Performance.Mocks;
using Microsoft.AspNetCore.Testing;
namespace Microsoft.AspNetCore.Server.Kestrel.Performance
@ -98,18 +99,14 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Performance
Log = new MockTrace(),
HttpParserFactory = f => new HttpParser<FrameAdapter>()
};
var frameContext = new FrameContext
var frame = new TestFrame<object>(application: null, context: new FrameContext
{
ServiceContext = serviceContext,
ConnectionInformation = new MockConnectionInformation()
};
var frame = new TestFrame<object>(application: null, context: frameContext)
{
ConnectionInformation = new MockConnectionInformation(),
Input = input.Reader,
};
frame.Output = new OutputProducer(output, "", null);
Output = output
});
frame.Reset();

View File

@ -30,11 +30,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Performance
var frameContext = new FrameContext
{
ServiceContext = serviceContext,
ConnectionInformation = new MockConnectionInformation()
ConnectionInformation = new MockConnectionInformation(),
TimeoutControl = new MockTimeoutControl()
};
Frame = new Frame<object>(application: null, frameContext: frameContext);
Frame.TimeoutControl = new MockTimeoutControl();
PipelineFactory = new PipeFactory();
Pipe = PipelineFactory.Create();
}

View File

@ -32,10 +32,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Performance
}
[Params(
BenchmarkTypes.ContentLengthNumeric,
BenchmarkTypes.ContentLengthString,
BenchmarkTypes.Plaintext,
BenchmarkTypes.Common,
BenchmarkTypes.ContentLengthNumeric,
BenchmarkTypes.ContentLengthString,
BenchmarkTypes.Plaintext,
BenchmarkTypes.Common,
BenchmarkTypes.Unknown
)]
public BenchmarkTypes Type { get; set; }

View File

@ -11,6 +11,7 @@ using Microsoft.AspNetCore.Server.Kestrel.Core;
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal;
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http;
using Microsoft.AspNetCore.Server.Kestrel.Internal.System.IO.Pipelines;
using Microsoft.AspNetCore.Server.Kestrel.Performance.Mocks;
using Microsoft.AspNetCore.Testing;
namespace Microsoft.AspNetCore.Server.Kestrel.Performance
@ -121,17 +122,14 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Performance
HttpParserFactory = f => new HttpParser<FrameAdapter>()
};
var frameContext = new FrameContext
var frame = new TestFrame<object>(application: null, context: new FrameContext
{
ServiceContext = serviceContext,
ConnectionInformation = new MockConnectionInformation()
};
var frame = new TestFrame<object>(application: null, context: frameContext)
{
ConnectionInformation = new MockConnectionInformation(),
TimeoutControl = new MockTimeoutControl(),
Input = input.Reader,
};
frame.Output = new OutputProducer(output, "", null);
Output = output
});
frame.Reset();

View File

@ -693,12 +693,15 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Tests
};
var transportContext = new TestLibuvTransportContext { Log = new LibuvTrace(logger) };
var frame = new Frame<object>(null, new FrameContext { ServiceContext = serviceContext });
var socket = new MockSocket(_mockLibuv, _libuvThread.Loop.ThreadId, transportContext.Log);
var outputProducer = new OutputProducer(pipe, "0", serviceContext.Log);
var consumer = new LibuvOutputConsumer(pipe.Reader, _libuvThread, socket, "0", transportContext.Log);
frame.Output = outputProducer;
var frame = new Frame<object>(null, new FrameContext
{
ServiceContext = serviceContext,
TimeoutControl = Mock.Of<ITimeoutControl>(),
Output = pipe
});
if (cts != null)
{
@ -707,7 +710,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Tests
var ignore = WriteOutputAsync(consumer, pipe.Reader, frame);
return outputProducer;
return frame.Output;
}
private async Task WriteOutputAsync(LibuvOutputConsumer consumer, IPipeReader outputReader, Frame frame)
@ -729,4 +732,4 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Tests
}
}
}
}
}

View File

@ -4,13 +4,14 @@
using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting.Server;
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http;
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure;
namespace Microsoft.AspNetCore.Testing
{
public class TestFrame<TContext> : Frame<TContext>
{
public TestFrame(IHttpApplication<TContext> application, FrameContext context)
: base(application, context)
: base(application, context)
{
}
@ -31,4 +32,4 @@ namespace Microsoft.AspNetCore.Testing
return ProduceEnd();
}
}
}
}