Merge branch 'release/2.2'

This commit is contained in:
Chris Ross (ASP.NET) 2018-08-08 12:25:01 -07:00
commit aab0d1bdca
9 changed files with 127 additions and 9 deletions

View File

@ -52,5 +52,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Performance
public void Http2StreamResetAbort(string traceIdentifier, Http2ErrorCode error, ConnectionAbortedException abortReason) { } public void Http2StreamResetAbort(string traceIdentifier, Http2ErrorCode error, ConnectionAbortedException abortReason) { }
public void Http2ConnectionClosing(string connectionId) { } public void Http2ConnectionClosing(string connectionId) { }
public void Http2ConnectionClosed(string connectionId, int highestOpenedStreamId) { } public void Http2ConnectionClosed(string connectionId, int highestOpenedStreamId) { }
public void Http2FrameReceived(string connectionId, Http2Frame frame) { }
public void Http2FrameSending(string connectionId, Http2Frame frame) { }
} }
} }

View File

@ -36,7 +36,6 @@ namespace Http2SampleApp
options.Listen(IPAddress.Any, basePort, listenOptions => options.Listen(IPAddress.Any, basePort, listenOptions =>
{ {
listenOptions.Protocols = HttpProtocols.Http1; listenOptions.Protocols = HttpProtocols.Http1;
listenOptions.UseConnectionLogging();
}); });
// TLS Http/1.1 or HTTP/2 endpoint negotiated via ALPN // TLS Http/1.1 or HTTP/2 endpoint negotiated via ALPN
@ -44,7 +43,6 @@ namespace Http2SampleApp
{ {
listenOptions.Protocols = HttpProtocols.Http1AndHttp2; listenOptions.Protocols = HttpProtocols.Http1AndHttp2;
listenOptions.UseHttps("testCert.pfx", "testPassword"); listenOptions.UseHttps("testCert.pfx", "testPassword");
listenOptions.UseConnectionLogging();
listenOptions.ConnectionAdapters.Add(new TlsFilterAdapter()); listenOptions.ConnectionAdapters.Add(new TlsFilterAdapter());
}); });
@ -53,7 +51,6 @@ namespace Http2SampleApp
options.Listen(IPAddress.Any, basePort + 5, listenOptions => options.Listen(IPAddress.Any, basePort + 5, listenOptions =>
{ {
listenOptions.Protocols = HttpProtocols.Http2; listenOptions.Protocols = HttpProtocols.Http2;
listenOptions.UseConnectionLogging();
}); });
}) })
.UseContentRoot(Directory.GetCurrentDirectory()) .UseContentRoot(Directory.GetCurrentDirectory())

View File

@ -4,6 +4,7 @@
using System; using System;
using System.Buffers; using System.Buffers;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.IO;
using System.IO.Pipelines; using System.IO.Pipelines;
using System.Security.Authentication; using System.Security.Authentication;
using System.Text; using System.Text;
@ -14,8 +15,8 @@ using Microsoft.AspNetCore.Connections.Features;
using Microsoft.AspNetCore.Hosting.Server; using Microsoft.AspNetCore.Hosting.Server;
using Microsoft.AspNetCore.Http.Features; using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http;
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2.HPack;
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2.FlowControl; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2.FlowControl;
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2.HPack;
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Net.Http.Headers; using Microsoft.Net.Http.Headers;
@ -84,7 +85,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
public Http2Connection(Http2ConnectionContext context) public Http2Connection(Http2ConnectionContext context)
{ {
_context = context; _context = context;
_frameWriter = new Http2FrameWriter(context.Transport.Output, context.Application.Input, _outputFlowControl, this); _frameWriter = new Http2FrameWriter(context.Transport.Output, context.Application.Input, _outputFlowControl, this, context.ConnectionId, context.ServiceContext.Log);
_hpackDecoder = new HPackDecoder((int)_serverSettings.HeaderTableSize); _hpackDecoder = new HPackDecoder((int)_serverSettings.HeaderTableSize);
} }
@ -186,7 +187,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
{ {
if (Http2FrameReader.ReadFrame(readableBuffer, _incomingFrame, _serverSettings.MaxFrameSize, out consumed, out examined)) if (Http2FrameReader.ReadFrame(readableBuffer, _incomingFrame, _serverSettings.MaxFrameSize, out consumed, out examined))
{ {
Log.LogTrace($"Connection id {ConnectionId} received {_incomingFrame.Type} frame with flags 0x{_incomingFrame.Flags:x} and length {_incomingFrame.Length} for stream ID {_incomingFrame.StreamId}"); Log.Http2FrameReceived(ConnectionId, _incomingFrame);
await ProcessFrameAsync(application); await ProcessFrameAsync(application);
} }
} }
@ -218,6 +219,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
error = ex; error = ex;
} }
catch (IOException ex)
{
Log.RequestProcessingError(ConnectionId, ex);
error = ex;
}
catch (Http2ConnectionErrorException ex) catch (Http2ConnectionErrorException ex)
{ {
Log.Http2ConnectionError(ConnectionId, ex); Log.Http2ConnectionError(ConnectionId, ex);
@ -232,9 +238,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
} }
catch (Exception ex) catch (Exception ex)
{ {
Log.LogWarning(0, ex, CoreStrings.RequestProcessingEndError);
error = ex; error = ex;
errorCode = Http2ErrorCode.INTERNAL_ERROR; errorCode = Http2ErrorCode.INTERNAL_ERROR;
throw;
} }
finally finally
{ {
@ -608,6 +614,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
_clientSettings.ParseFrame(_incomingFrame); _clientSettings.ParseFrame(_incomingFrame);
var ackTask = _frameWriter.WriteSettingsAckAsync(); // Ack before we update the windows, they could send data immediately.
// This difference can be negative. // This difference can be negative.
var windowSizeDifference = (int)_clientSettings.InitialWindowSize - previousInitialWindowSize; var windowSizeDifference = (int)_clientSettings.InitialWindowSize - previousInitialWindowSize;
@ -625,7 +633,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
} }
} }
return _frameWriter.WriteSettingsAckAsync(); return ackTask;
} }
catch (Http2SettingsParameterOutOfRangeException ex) catch (Http2SettingsParameterOutOfRangeException ex)
{ {

View File

@ -46,5 +46,33 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
{ {
logger.LogTrace("'DATA' Frame. Flags = {DataFlags}, PadLength = {PadLength}, PayloadLength = {PayloadLength}", DataFlags, DataPadLength, DataPayload.Count); logger.LogTrace("'DATA' Frame. Flags = {DataFlags}, PadLength = {PadLength}, PayloadLength = {PayloadLength}", DataFlags, DataPadLength, DataPayload.Count);
} }
internal object ShowFlags()
{
switch (Type)
{
case Http2FrameType.CONTINUATION:
return ContinuationFlags;
case Http2FrameType.DATA:
return DataFlags;
case Http2FrameType.HEADERS:
return HeadersFlags;
case Http2FrameType.SETTINGS:
return SettingsFlags;
case Http2FrameType.PING:
return PingFlags;
// Not Implemented
case Http2FrameType.PUSH_PROMISE:
// No flags defined
case Http2FrameType.PRIORITY:
case Http2FrameType.RST_STREAM:
case Http2FrameType.GOAWAY:
case Http2FrameType.WINDOW_UPDATE:
default:
return $"0x{Flags:x}";
}
}
} }
} }

View File

@ -13,6 +13,7 @@ using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http;
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2.FlowControl; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2.FlowControl;
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2.HPack; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2.HPack;
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure;
using Microsoft.Extensions.Logging;
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2 namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
{ {
@ -27,6 +28,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
private readonly PipeWriter _outputWriter; private readonly PipeWriter _outputWriter;
private readonly PipeReader _outputReader; private readonly PipeReader _outputReader;
private readonly OutputFlowControl _connectionOutputFlowControl; private readonly OutputFlowControl _connectionOutputFlowControl;
private readonly string _connectionId;
private readonly IKestrelTrace _log;
private readonly StreamSafePipeFlusher _flusher; private readonly StreamSafePipeFlusher _flusher;
private bool _completed; private bool _completed;
@ -35,12 +38,16 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
PipeWriter outputPipeWriter, PipeWriter outputPipeWriter,
PipeReader outputPipeReader, PipeReader outputPipeReader,
OutputFlowControl connectionOutputFlowControl, OutputFlowControl connectionOutputFlowControl,
ITimeoutControl timeoutControl) ITimeoutControl timeoutControl,
string connectionId,
IKestrelTrace log)
{ {
_outputWriter = outputPipeWriter; _outputWriter = outputPipeWriter;
_outputReader = outputPipeReader; _outputReader = outputPipeReader;
_connectionOutputFlowControl = connectionOutputFlowControl; _connectionOutputFlowControl = connectionOutputFlowControl;
_connectionId = connectionId;
_log = log;
_flusher = new StreamSafePipeFlusher(_outputWriter, timeoutControl); _flusher = new StreamSafePipeFlusher(_outputWriter, timeoutControl);
} }
@ -110,6 +117,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
_outgoingFrame.HeadersFlags = Http2HeadersFrameFlags.END_HEADERS; _outgoingFrame.HeadersFlags = Http2HeadersFrameFlags.END_HEADERS;
} }
_log.Http2FrameSending(_connectionId, _outgoingFrame);
_outputWriter.Write(_outgoingFrame.Raw); _outputWriter.Write(_outgoingFrame.Raw);
while (!done) while (!done)
@ -124,6 +132,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
_outgoingFrame.ContinuationFlags = Http2ContinuationFrameFlags.END_HEADERS; _outgoingFrame.ContinuationFlags = Http2ContinuationFrameFlags.END_HEADERS;
} }
_log.Http2FrameSending(_connectionId, _outgoingFrame);
_outputWriter.Write(_outgoingFrame.Raw); _outputWriter.Write(_outgoingFrame.Raw);
} }
} }
@ -170,6 +179,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
current.Span.Slice(0, payload.Length).CopyTo(payload); current.Span.Slice(0, payload.Length).CopyTo(payload);
current = current.Slice(payload.Length); current = current.Slice(payload.Length);
_log.Http2FrameSending(_connectionId, _outgoingFrame);
_outputWriter.Write(_outgoingFrame.Raw); _outputWriter.Write(_outgoingFrame.Raw);
payload = _outgoingFrame.Payload; payload = _outgoingFrame.Payload;
unwrittenPayloadLength = 0; unwrittenPayloadLength = 0;
@ -189,6 +199,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
} }
_outgoingFrame.Length = unwrittenPayloadLength; _outgoingFrame.Length = unwrittenPayloadLength;
_log.Http2FrameSending(_connectionId, _outgoingFrame);
_outputWriter.Write(_outgoingFrame.Raw); _outputWriter.Write(_outgoingFrame.Raw);
return _flusher.FlushAsync(); return _flusher.FlushAsync();
@ -303,6 +315,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
return Task.CompletedTask; return Task.CompletedTask;
} }
_log.Http2FrameSending(_connectionId, _outgoingFrame);
_outputWriter.Write(_outgoingFrame.Raw); _outputWriter.Write(_outgoingFrame.Raw);
return _flusher.FlushAsync(); return _flusher.FlushAsync();
} }

View File

@ -66,5 +66,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure
void Http2StreamResetAbort(string traceIdentifier, Http2ErrorCode error, ConnectionAbortedException abortReason); void Http2StreamResetAbort(string traceIdentifier, Http2ErrorCode error, ConnectionAbortedException abortReason);
void HPackDecodingError(string connectionId, int streamId, HPackDecodingException ex); void HPackDecodingError(string connectionId, int streamId, HPackDecodingException ex);
void Http2FrameReceived(string connectionId, Http2Frame frame);
void Http2FrameSending(string connectionId, Http2Frame frame);
} }
} }

View File

@ -99,6 +99,14 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
LoggerMessage.Define<string, int>(LogLevel.Debug, new EventId(36, nameof(Http2ConnectionClosed)), LoggerMessage.Define<string, int>(LogLevel.Debug, new EventId(36, nameof(Http2ConnectionClosed)),
@"Connection id ""{ConnectionId}"" is closed. The last processed stream ID was {HighestOpenedStreamId}."); @"Connection id ""{ConnectionId}"" is closed. The last processed stream ID was {HighestOpenedStreamId}.");
private static readonly Action<ILogger, string, Http2FrameType, int, int, object, Exception> _http2FrameReceived =
LoggerMessage.Define<string, Http2FrameType, int, int, object>(LogLevel.Trace, new EventId(37, nameof(Http2FrameReceived)),
@"Connection id ""{ConnectionId}"" received {type} frame for stream ID {id} with length {length} and flags {flags}");
private static readonly Action<ILogger, string, Http2FrameType, int, int, object, Exception> _http2FrameSending =
LoggerMessage.Define<string, Http2FrameType, int, int, object>(LogLevel.Trace, new EventId(37, nameof(Http2FrameReceived)),
@"Connection id ""{ConnectionId}"" sending {type} frame for stream ID {id} with length {length} and flags {flags}");
protected readonly ILogger _logger; protected readonly ILogger _logger;
public KestrelTrace(ILogger logger) public KestrelTrace(ILogger logger)
@ -246,6 +254,16 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
_hpackDecodingError(_logger, connectionId, streamId, ex); _hpackDecodingError(_logger, connectionId, streamId, ex);
} }
public void Http2FrameReceived(string connectionId, Http2Frame frame)
{
_http2FrameReceived(_logger, connectionId, frame.Type, frame.StreamId, frame.Length, frame.ShowFlags(), null);
}
public void Http2FrameSending(string connectionId, Http2Frame frame)
{
_http2FrameSending(_logger, connectionId, frame.Type, frame.StreamId, frame.Length, frame.ShowFlags(), null);
}
public virtual void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter) public virtual void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
=> _logger.Log(logLevel, eventId, state, exception, formatter); => _logger.Log(logLevel, eventId, state, exception, formatter);

View File

@ -21,6 +21,7 @@ using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2.HPack;
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure;
using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal; using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal;
using Microsoft.AspNetCore.Testing; using Microsoft.AspNetCore.Testing;
using Microsoft.Extensions.Logging;
using Microsoft.Net.Http.Headers; using Microsoft.Net.Http.Headers;
using Moq; using Moq;
using Xunit; using Xunit;
@ -2328,6 +2329,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
_clientSettings.InitialWindowSize += 1; _clientSettings.InitialWindowSize += 1;
await SendSettingsAsync(); await SendSettingsAsync();
await ExpectAsync(Http2FrameType.SETTINGS,
withLength: 0,
withFlags: (byte)Http2SettingsFrameFlags.ACK,
withStreamId: 0);
await WaitForConnectionErrorAsync<Http2ConnectionErrorException>( await WaitForConnectionErrorAsync<Http2ConnectionErrorException>(
ignoreNonGoAwayFrames: false, ignoreNonGoAwayFrames: false,
expectedLastStreamId: 1, expectedLastStreamId: 1,
@ -3420,6 +3426,36 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await WaitForConnectionStopAsync(expectedLastStreamId: 1, ignoreNonGoAwayFrames: false); await WaitForConnectionStopAsync(expectedLastStreamId: 1, ignoreNonGoAwayFrames: false);
} }
[Fact]
public void IOExceptionDuringFrameProcessingLoggedAsInfo()
{
var ioException = new IOException();
_pair.Application.Output.Complete(ioException);
Assert.Equal(TaskStatus.RanToCompletion, _connection.ProcessRequestsAsync(new DummyApplication(_noopApplication)).Status);
var logMessage = _logger.Messages.Single();
Assert.Equal(LogLevel.Information, logMessage.LogLevel);
Assert.Equal("Connection id \"(null)\" request processing ended abnormally.", logMessage.Message);
Assert.Same(ioException, logMessage.Exception);
}
[Fact]
public void UnexpectedExceptionDuringFrameProcessingLoggedAWarning()
{
var exception = new Exception();
_pair.Application.Output.Complete(exception);
Assert.Equal(TaskStatus.RanToCompletion, _connection.ProcessRequestsAsync(new DummyApplication(_noopApplication)).Status);
var logMessage = _logger.Messages.Single();
Assert.Equal(LogLevel.Warning, logMessage.LogLevel);
Assert.Equal(CoreStrings.RequestProcessingEndError, logMessage.Message);
Assert.Same(exception, logMessage.Exception);
}
private async Task InitializeConnectionAsync(RequestDelegate application) private async Task InitializeConnectionAsync(RequestDelegate application)
{ {
_connectionTask = _connection.ProcessRequestsAsync(new DummyApplication(application)); _connectionTask = _connection.ProcessRequestsAsync(new DummyApplication(application));

View File

@ -206,5 +206,17 @@ namespace Microsoft.AspNetCore.Testing
_trace1.Http2ConnectionClosed(connectionId, highestOpenedStreamId); _trace1.Http2ConnectionClosed(connectionId, highestOpenedStreamId);
_trace2.Http2ConnectionClosed(connectionId, highestOpenedStreamId); _trace2.Http2ConnectionClosed(connectionId, highestOpenedStreamId);
} }
public void Http2FrameReceived(string connectionId, Http2Frame frame)
{
_trace1.Http2FrameReceived(connectionId, frame);
_trace2.Http2FrameReceived(connectionId, frame);
}
public void Http2FrameSending(string connectionId, Http2Frame frame)
{
_trace1.Http2FrameSending(connectionId, frame);
_trace2.Http2FrameSending(connectionId, frame);
}
} }
} }