Skip to content
This repository was archived by the owner on Dec 18, 2018. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 0 additions & 10 deletions src/Kestrel.Core/Internal/Http/Http1MessageBody.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ public abstract class Http1MessageBody : MessageBody
{
private readonly Http1Connection _context;

private bool _send100Continue = true;
private volatile bool _canceled;
private Task _pumpTask;

Expand Down Expand Up @@ -150,15 +149,6 @@ protected override async Task OnConsumeAsync()
}
}

private void TryProduceContinue()
{
if (_send100Continue)
{
_context.HttpResponseControl.ProduceContinue();
_send100Continue = false;
}
}

protected void Copy(ReadableBuffer readableBuffer, WritableBuffer writableBuffer)
{
_context.TimeoutControl.BytesRead(readableBuffer.Length);
Expand Down
12 changes: 11 additions & 1 deletion src/Kestrel.Core/Internal/Http/MessageBody.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
using System.IO.Pipelines;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure;

namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
Expand All @@ -18,6 +17,8 @@ public abstract class MessageBody

private readonly HttpProtocol _context;

private bool _send100Continue = true;

protected MessageBody(HttpProtocol context)
{
_context = context;
Expand Down Expand Up @@ -111,6 +112,15 @@ public virtual Task ConsumeAsync()

public abstract Task StopAsync();

protected void TryProduceContinue()
{
if (_send100Continue)
{
_context.HttpResponseControl.ProduceContinue();
_send100Continue = false;
}
}

private void TryInit()
{
if (!_context.HasStartedConsumingRequestBody)
Expand Down
12 changes: 11 additions & 1 deletion src/Kestrel.Core/Internal/Http2/Http2FrameWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
{
public class Http2FrameWriter : IHttp2FrameWriter
{
// Literal Header Field without Indexing - Indexed Name (Index 8 - :status)
private static readonly byte[] _continueBytes = new byte[] { 0x08, 0x03, (byte)'1', (byte)'0', (byte)'0' };

private readonly Http2Frame _outgoingFrame = new Http2Frame();
private readonly object _writeLock = new object();
private readonly HPackEncoder _hpackEncoder = new HPackEncoder();
Expand Down Expand Up @@ -50,7 +53,14 @@ public Task FlushAsync(CancellationToken cancellationToken)

public Task Write100ContinueAsync(int streamId)
{
return Task.CompletedTask;
lock (_writeLock)
{
_outgoingFrame.PrepareHeaders(Http2HeadersFrameFlags.END_HEADERS, streamId);
_outgoingFrame.Length = _continueBytes.Length;
_continueBytes.CopyTo(_outgoingFrame.HeadersPayload);

return WriteAsync(_outgoingFrame.Raw);
}
}

public void WriteResponseHeaders(int streamId, int statusCode, IHeaderDictionary headers)
Expand Down
9 changes: 9 additions & 0 deletions src/Kestrel.Core/Internal/Http2/Http2MessageBody.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,15 @@ protected Http2MessageBody(Http2Stream context)
_context = context;
}

protected override void OnReadStarted()
{
// Produce 100-continue if no request body data for the stream has arrived yet.
if (!_context.RequestBodyStarted)
{
TryProduceContinue();
}
}

protected override Task OnConsumeAsync() => Task.CompletedTask;

public override Task StopAsync()
Expand Down
2 changes: 2 additions & 0 deletions src/Kestrel.Core/Internal/Http2/Http2Stream.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ public Http2Stream(Http2StreamContext context)

public int StreamId => _context.StreamId;

public bool RequestBodyStarted { get; private set; }
public bool EndStreamReceived { get; private set; }

protected IHttp2StreamLifetimeHandler StreamLifetimeHandler => _context.StreamLifetimeHandler;
Expand Down Expand Up @@ -84,6 +85,7 @@ public async Task OnDataAsync(ArraySegment<byte> data, bool endStream)
writableBuffer.Commit();
}

RequestBodyStarted = true;
await writableBuffer.FlushAsync();
}

Expand Down
41 changes: 41 additions & 0 deletions test/Kestrel.Core.Tests/Http2ConnectionTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,15 @@ public class Http2ConnectionTests : IDisposable, IHttpHeadersHandler
new KeyValuePair<string, string>(":scheme", "http"),
};

private static readonly IEnumerable<KeyValuePair<string, string>> _expectContinueRequestHeaders = new[]
{
new KeyValuePair<string, string>(":method", "POST"),
new KeyValuePair<string, string>(":path", "/"),
new KeyValuePair<string, string>(":authority", "127.0.0.1"),
new KeyValuePair<string, string>(":scheme", "https"),
new KeyValuePair<string, string>("expect", "100-continue"),
};

private static readonly IEnumerable<KeyValuePair<string, string>> _browserRequestHeaders = new[]
{
new KeyValuePair<string, string>(":method", "GET"),
Expand Down Expand Up @@ -755,6 +764,38 @@ await ExpectAsync(Http2FrameType.DATA,
await StopConnectionAsync(expectedLastStreamId: 3, ignoreNonGoAwayFrames: false);
}

[Fact]
public async Task HEADERS_Received_ContainsExpect100Continue_100ContinueSent()
{
await InitializeConnectionAsync(_echoApplication);

await StartStreamAsync(1, _expectContinueRequestHeaders, false);

var frame = await ExpectAsync(Http2FrameType.HEADERS,
withLength: 5,
withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS,
withStreamId: 1);

await SendDataAsync(1, _helloBytes, endStream: true);

await ExpectAsync(Http2FrameType.HEADERS,
withLength: 37,
withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS,
withStreamId: 1);
await ExpectAsync(Http2FrameType.DATA,
withLength: 5,
withFlags: (byte)Http2DataFrameFlags.NONE,
withStreamId: 1);
await ExpectAsync(Http2FrameType.DATA,
withLength: 0,
withFlags: (byte)Http2DataFrameFlags.END_STREAM,
withStreamId: 1);

Assert.Equal(new byte[] { 0x08, 0x03, (byte)'1', (byte)'0', (byte)'0' }, frame.HeadersPayload.ToArray());

await StopConnectionAsync(expectedLastStreamId: 1, ignoreNonGoAwayFrames: false);
}

[Fact]
public async Task HEADERS_Received_StreamIdZero_ConnectionError()
{
Expand Down