Handle tokens in Transfer-Encoding header (#1181).
This commit is contained in:
parent
cc05e36dc6
commit
29408956f9
|
|
@ -99,6 +99,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel
|
|||
case RequestRejectionReason.UnrecognizedHTTPVersion:
|
||||
ex = new BadHttpRequestException($"Unrecognized HTTP version: {value}", 505);
|
||||
break;
|
||||
case RequestRejectionReason.FinalTransferCodingNotChunked:
|
||||
ex = new BadHttpRequestException($"Final transfer coding is not \"chunked\": \"{value}\"", 400);
|
||||
break;
|
||||
default:
|
||||
ex = new BadHttpRequestException("Bad request.", 400);
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -811,7 +811,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
|||
{
|
||||
var responseHeaders = FrameResponseHeaders;
|
||||
var hasConnection = responseHeaders.HasConnection;
|
||||
var connectionOptions = hasConnection ? FrameHeaders.ParseConnection(responseHeaders.HeaderConnection) : ConnectionOptions.None;
|
||||
var connectionOptions = FrameHeaders.ParseConnection(responseHeaders.HeaderConnection);
|
||||
var hasTransferEncoding = responseHeaders.HasTransferEncoding;
|
||||
var transferCoding = FrameHeaders.GetFinalTransferCoding(responseHeaders.HeaderTransferEncoding);
|
||||
|
||||
var end = SocketOutput.ProducingStart();
|
||||
|
||||
|
|
@ -820,6 +822,16 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
|||
_keepAlive = (connectionOptions & ConnectionOptions.KeepAlive) == ConnectionOptions.KeepAlive;
|
||||
}
|
||||
|
||||
// https://tools.ietf.org/html/rfc7230#section-3.3.1
|
||||
// If any transfer coding other than
|
||||
// chunked is applied to a response payload body, the sender MUST either
|
||||
// apply chunked as the final transfer coding or terminate the message
|
||||
// by closing the connection.
|
||||
if (hasTransferEncoding && transferCoding != TransferCoding.Chunked)
|
||||
{
|
||||
_keepAlive = false;
|
||||
}
|
||||
|
||||
// Set whether response can have body
|
||||
_canHaveBody = StatusCanHaveBody(StatusCode) && Method != "HEAD";
|
||||
|
||||
|
|
@ -827,7 +839,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
|||
// automatically for HEAD requests or 204, 205, 304 responses.
|
||||
if (_canHaveBody)
|
||||
{
|
||||
if (!responseHeaders.HasTransferEncoding && !responseHeaders.HasContentLength)
|
||||
if (!hasTransferEncoding && !responseHeaders.HasContentLength)
|
||||
{
|
||||
if (appCompleted && StatusCode != 101)
|
||||
{
|
||||
|
|
@ -856,12 +868,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
|||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
else if (hasTransferEncoding)
|
||||
{
|
||||
if (responseHeaders.HasTransferEncoding)
|
||||
{
|
||||
RejectNonBodyTransferEncodingResponse(appCompleted);
|
||||
}
|
||||
RejectNonBodyTransferEncodingResponse(appCompleted);
|
||||
}
|
||||
|
||||
responseHeaders.SetReadOnly();
|
||||
|
|
|
|||
|
|
@ -308,7 +308,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
|||
ch++;
|
||||
}
|
||||
|
||||
if (ch == tokenEnd || *ch == ',')
|
||||
if (ch == tokenEnd)
|
||||
{
|
||||
connectionOptions |= ConnectionOptions.KeepAlive;
|
||||
}
|
||||
|
|
@ -329,7 +329,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
|||
ch++;
|
||||
}
|
||||
|
||||
if (ch == tokenEnd || *ch == ',')
|
||||
if (ch == tokenEnd)
|
||||
{
|
||||
connectionOptions |= ConnectionOptions.Upgrade;
|
||||
}
|
||||
|
|
@ -348,7 +348,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
|||
ch++;
|
||||
}
|
||||
|
||||
if (ch == tokenEnd || *ch == ',')
|
||||
if (ch == tokenEnd)
|
||||
{
|
||||
connectionOptions |= ConnectionOptions.Close;
|
||||
}
|
||||
|
|
@ -364,6 +364,68 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
|||
return connectionOptions;
|
||||
}
|
||||
|
||||
public static unsafe TransferCoding GetFinalTransferCoding(StringValues transferEncoding)
|
||||
{
|
||||
var transferEncodingOptions = TransferCoding.None;
|
||||
|
||||
foreach (var value in transferEncoding)
|
||||
{
|
||||
fixed (char* ptr = value)
|
||||
{
|
||||
var ch = ptr;
|
||||
var tokenEnd = ch;
|
||||
var end = ch + value.Length;
|
||||
|
||||
while (ch < end)
|
||||
{
|
||||
while (tokenEnd < end && *tokenEnd != ',')
|
||||
{
|
||||
tokenEnd++;
|
||||
}
|
||||
|
||||
while (ch < tokenEnd && *ch == ' ')
|
||||
{
|
||||
ch++;
|
||||
}
|
||||
|
||||
var tokenLength = tokenEnd - ch;
|
||||
|
||||
if (tokenLength >= 7 && (*ch | 0x20) == 'c')
|
||||
{
|
||||
if ((*++ch | 0x20) == 'h' &&
|
||||
(*++ch | 0x20) == 'u' &&
|
||||
(*++ch | 0x20) == 'n' &&
|
||||
(*++ch | 0x20) == 'k' &&
|
||||
(*++ch | 0x20) == 'e' &&
|
||||
(*++ch | 0x20) == 'd')
|
||||
{
|
||||
ch++;
|
||||
while (ch < tokenEnd && *ch == ' ')
|
||||
{
|
||||
ch++;
|
||||
}
|
||||
|
||||
if (ch == tokenEnd)
|
||||
{
|
||||
transferEncodingOptions = TransferCoding.Chunked;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (tokenLength > 0 && ch != tokenEnd)
|
||||
{
|
||||
transferEncodingOptions = TransferCoding.Other;
|
||||
}
|
||||
|
||||
tokenEnd++;
|
||||
ch = tokenEnd;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return transferEncodingOptions;
|
||||
}
|
||||
|
||||
private static void ThrowInvalidContentLengthException(string value)
|
||||
{
|
||||
throw new InvalidOperationException($"Invalid Content-Length: \"{value}\". Value must be a positive integral number.");
|
||||
|
|
|
|||
|
|
@ -247,9 +247,22 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
|||
keepAlive = (connectionOptions & ConnectionOptions.KeepAlive) == ConnectionOptions.KeepAlive;
|
||||
}
|
||||
|
||||
var transferEncoding = headers.HeaderTransferEncoding.ToString();
|
||||
if (transferEncoding.Length > 0)
|
||||
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.RejectRequest(RequestRejectionReason.FinalTransferCodingNotChunked, transferEncoding.ToString());
|
||||
}
|
||||
|
||||
return new ForChunkedEncoding(keepAlive, headers, context);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -27,5 +27,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
|||
MissingCRInHeaderLine,
|
||||
TooManyHeaders,
|
||||
RequestTimeout,
|
||||
FinalTransferCodingNotChunked
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
using System;
|
||||
|
||||
namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
||||
{
|
||||
[Flags]
|
||||
public enum TransferCoding
|
||||
{
|
||||
None,
|
||||
Chunked,
|
||||
Other
|
||||
}
|
||||
}
|
||||
|
|
@ -848,6 +848,140 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
|
|||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("gzip")]
|
||||
[InlineData("chunked, gzip")]
|
||||
[InlineData("gzip")]
|
||||
[InlineData("chunked, gzip")]
|
||||
public async Task ConnectionClosedWhenChunkedIsNotFinalTransferCoding(string responseTransferEncoding)
|
||||
{
|
||||
using (var server = new TestServer(async httpContext =>
|
||||
{
|
||||
httpContext.Response.Headers["Transfer-Encoding"] = responseTransferEncoding;
|
||||
await httpContext.Response.WriteAsync("hello, world");
|
||||
}, new TestServiceContext()))
|
||||
{
|
||||
using (var connection = server.CreateConnection())
|
||||
{
|
||||
await connection.Send(
|
||||
"GET / HTTP/1.1",
|
||||
"",
|
||||
"");
|
||||
await connection.ReceiveEnd(
|
||||
"HTTP/1.1 200 OK",
|
||||
"Connection: close",
|
||||
$"Date: {server.Context.DateHeaderValue}",
|
||||
$"Transfer-Encoding: {responseTransferEncoding}",
|
||||
"",
|
||||
"hello, world");
|
||||
}
|
||||
|
||||
using (var connection = server.CreateConnection())
|
||||
{
|
||||
await connection.Send(
|
||||
"GET / HTTP/1.0",
|
||||
"Connection: keep-alive",
|
||||
"",
|
||||
"");
|
||||
await connection.ReceiveEnd(
|
||||
"HTTP/1.1 200 OK",
|
||||
"Connection: close",
|
||||
$"Date: {server.Context.DateHeaderValue}",
|
||||
$"Transfer-Encoding: {responseTransferEncoding}",
|
||||
"",
|
||||
"hello, world");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("gzip")]
|
||||
[InlineData("chunked, gzip")]
|
||||
[InlineData("gzip")]
|
||||
[InlineData("chunked, gzip")]
|
||||
public async Task ConnectionClosedWhenChunkedIsNotFinalTransferCodingEvenIfConnectionKeepAliveSetInResponse(string responseTransferEncoding)
|
||||
{
|
||||
using (var server = new TestServer(async httpContext =>
|
||||
{
|
||||
httpContext.Response.Headers["Connection"] = "keep-alive";
|
||||
httpContext.Response.Headers["Transfer-Encoding"] = responseTransferEncoding;
|
||||
await httpContext.Response.WriteAsync("hello, world");
|
||||
}, new TestServiceContext()))
|
||||
{
|
||||
using (var connection = server.CreateConnection())
|
||||
{
|
||||
await connection.Send(
|
||||
"GET / HTTP/1.1",
|
||||
"",
|
||||
"");
|
||||
await connection.ReceiveEnd(
|
||||
"HTTP/1.1 200 OK",
|
||||
"Connection: keep-alive",
|
||||
$"Date: {server.Context.DateHeaderValue}",
|
||||
$"Transfer-Encoding: {responseTransferEncoding}",
|
||||
"",
|
||||
"hello, world");
|
||||
}
|
||||
|
||||
using (var connection = server.CreateConnection())
|
||||
{
|
||||
await connection.Send(
|
||||
"GET / HTTP/1.0",
|
||||
"Connection: keep-alive",
|
||||
"",
|
||||
"");
|
||||
await connection.ReceiveEnd(
|
||||
"HTTP/1.1 200 OK",
|
||||
"Connection: keep-alive",
|
||||
$"Date: {server.Context.DateHeaderValue}",
|
||||
$"Transfer-Encoding: {responseTransferEncoding}",
|
||||
"",
|
||||
"hello, world");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("chunked")]
|
||||
[InlineData("gzip, chunked")]
|
||||
public async Task ConnectionKeptAliveWhenChunkedIsFinalTransferCoding(string responseTransferEncoding)
|
||||
{
|
||||
using (var server = new TestServer(async httpContext =>
|
||||
{
|
||||
httpContext.Response.Headers["Transfer-Encoding"] = responseTransferEncoding;
|
||||
|
||||
// App would have to chunk manually, but here we don't care
|
||||
await httpContext.Response.WriteAsync("hello, world");
|
||||
}, new TestServiceContext()))
|
||||
{
|
||||
using (var connection = server.CreateConnection())
|
||||
{
|
||||
await connection.Send(
|
||||
"GET / HTTP/1.1",
|
||||
"",
|
||||
"");
|
||||
await connection.Receive(
|
||||
"HTTP/1.1 200 OK",
|
||||
$"Date: {server.Context.DateHeaderValue}",
|
||||
$"Transfer-Encoding: {responseTransferEncoding}",
|
||||
"",
|
||||
"hello, world");
|
||||
|
||||
// Make sure connection was kept open
|
||||
await connection.SendEnd(
|
||||
"GET / HTTP/1.1",
|
||||
"",
|
||||
"");
|
||||
await connection.ReceiveEnd(
|
||||
"HTTP/1.1 200 OK",
|
||||
$"Date: {server.Context.DateHeaderValue}",
|
||||
$"Transfer-Encoding: {responseTransferEncoding}",
|
||||
"",
|
||||
"hello, world");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static TheoryData<string, StringValues, string> NullHeaderData
|
||||
{
|
||||
get
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ using System.Text;
|
|||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Testing;
|
||||
using Microsoft.Extensions.Internal;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Server.KestrelTests
|
||||
|
|
@ -512,6 +513,106 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(ConnectionFilterData))]
|
||||
public async Task ChunkedNotFinalTransferCodingResultsIn400(TestServiceContext testContext)
|
||||
{
|
||||
using (var server = new TestServer(httpContext =>
|
||||
{
|
||||
return TaskCache.CompletedTask;
|
||||
}, testContext))
|
||||
{
|
||||
using (var connection = server.CreateConnection())
|
||||
{
|
||||
await connection.SendAll(
|
||||
"POST / HTTP/1.1",
|
||||
"Transfer-Encoding: not-chunked",
|
||||
"",
|
||||
"C",
|
||||
"hello, world",
|
||||
"0",
|
||||
"",
|
||||
"");
|
||||
|
||||
await connection.ReceiveForcedEnd(
|
||||
"HTTP/1.1 400 Bad Request",
|
||||
"Connection: close",
|
||||
$"Date: {testContext.DateHeaderValue}",
|
||||
"Content-Length: 0",
|
||||
"",
|
||||
"");
|
||||
}
|
||||
|
||||
// Content-Length should not affect this
|
||||
using (var connection = server.CreateConnection())
|
||||
{
|
||||
await connection.SendAll(
|
||||
"POST / HTTP/1.1",
|
||||
"Transfer-Encoding: not-chunked",
|
||||
"Content-Length: 22",
|
||||
"",
|
||||
"C",
|
||||
"hello, world",
|
||||
"0",
|
||||
"",
|
||||
"");
|
||||
|
||||
await connection.ReceiveForcedEnd(
|
||||
"HTTP/1.1 400 Bad Request",
|
||||
"Connection: close",
|
||||
$"Date: {testContext.DateHeaderValue}",
|
||||
"Content-Length: 0",
|
||||
"",
|
||||
"");
|
||||
}
|
||||
|
||||
using (var connection = server.CreateConnection())
|
||||
{
|
||||
await connection.SendAll(
|
||||
"POST / HTTP/1.1",
|
||||
"Transfer-Encoding: chunked, not-chunked",
|
||||
"",
|
||||
"C",
|
||||
"hello, world",
|
||||
"0",
|
||||
"",
|
||||
"");
|
||||
|
||||
await connection.ReceiveForcedEnd(
|
||||
"HTTP/1.1 400 Bad Request",
|
||||
"Connection: close",
|
||||
$"Date: {testContext.DateHeaderValue}",
|
||||
"Content-Length: 0",
|
||||
"",
|
||||
"");
|
||||
}
|
||||
|
||||
// Content-Length should not affect this
|
||||
using (var connection = server.CreateConnection())
|
||||
{
|
||||
await connection.SendAll(
|
||||
"POST / HTTP/1.1",
|
||||
"Transfer-Encoding: chunked, not-chunked",
|
||||
"Content-Length: 22",
|
||||
"",
|
||||
"C",
|
||||
"hello, world",
|
||||
"0",
|
||||
"",
|
||||
"");
|
||||
|
||||
await connection.ReceiveForcedEnd(
|
||||
"HTTP/1.1 400 Bad Request",
|
||||
"Connection: close",
|
||||
$"Date: {testContext.DateHeaderValue}",
|
||||
"Content-Length: 0",
|
||||
"",
|
||||
"");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -10,6 +10,16 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
|||
public class FrameHeadersTests
|
||||
{
|
||||
[Theory]
|
||||
[InlineData("", ConnectionOptions.None)]
|
||||
[InlineData(",", ConnectionOptions.None)]
|
||||
[InlineData(" ,", ConnectionOptions.None)]
|
||||
[InlineData(" , ", ConnectionOptions.None)]
|
||||
[InlineData(",,", ConnectionOptions.None)]
|
||||
[InlineData(" ,,", ConnectionOptions.None)]
|
||||
[InlineData(",, ", ConnectionOptions.None)]
|
||||
[InlineData(" , ,", ConnectionOptions.None)]
|
||||
[InlineData(" , ,", ConnectionOptions.None)]
|
||||
[InlineData(" , , ", ConnectionOptions.None)]
|
||||
[InlineData("keep-alive", ConnectionOptions.KeepAlive)]
|
||||
[InlineData("keep-alive, upgrade", ConnectionOptions.KeepAlive | ConnectionOptions.Upgrade)]
|
||||
[InlineData("keep-alive,upgrade", ConnectionOptions.KeepAlive | ConnectionOptions.Upgrade)]
|
||||
|
|
@ -123,10 +133,10 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
|||
[InlineData("up2rade ,", ConnectionOptions.None)]
|
||||
[InlineData("c2ose ,", ConnectionOptions.None)]
|
||||
[InlineData("cl2se ,", ConnectionOptions.None)]
|
||||
public void TestParseConnection(string connection, ConnectionOptions expectedConnectionOptionss)
|
||||
public void TestParseConnection(string connection, ConnectionOptions expectedConnectionOptions)
|
||||
{
|
||||
var connectionOptions = FrameHeaders.ParseConnection(connection);
|
||||
Assert.Equal(expectedConnectionOptionss, connectionOptions);
|
||||
Assert.Equal(expectedConnectionOptions, connectionOptions);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
|
|
@ -145,11 +155,73 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
|||
[InlineData("", "close", ConnectionOptions.Close)]
|
||||
[InlineData("close", "upgrade", ConnectionOptions.Close | ConnectionOptions.Upgrade)]
|
||||
[InlineData("upgrade", "close", ConnectionOptions.Close | ConnectionOptions.Upgrade)]
|
||||
public void TestParseConnectionMultipleValues(string value1, string value2, ConnectionOptions expectedConnectionOptionss)
|
||||
public void TestParseConnectionMultipleValues(string value1, string value2, ConnectionOptions expectedConnectionOptions)
|
||||
{
|
||||
var connection = new StringValues(new[] { value1, value2 });
|
||||
var connectionOptions = FrameHeaders.ParseConnection(connection);
|
||||
Assert.Equal(expectedConnectionOptionss, connectionOptions);
|
||||
Assert.Equal(expectedConnectionOptions, connectionOptions);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("", TransferCoding.None)]
|
||||
[InlineData(",,", TransferCoding.None)]
|
||||
[InlineData(" ,,", TransferCoding.None)]
|
||||
[InlineData(",, ", TransferCoding.None)]
|
||||
[InlineData(" , ,", TransferCoding.None)]
|
||||
[InlineData(" , ,", TransferCoding.None)]
|
||||
[InlineData(" , , ", TransferCoding.None)]
|
||||
[InlineData("chunked,", TransferCoding.Chunked)]
|
||||
[InlineData("chunked,,", TransferCoding.Chunked)]
|
||||
[InlineData(",chunked", TransferCoding.Chunked)]
|
||||
[InlineData(",,chunked", TransferCoding.Chunked)]
|
||||
[InlineData("chunked, ", TransferCoding.Chunked)]
|
||||
[InlineData("chunked, ,", TransferCoding.Chunked)]
|
||||
[InlineData("chunked, , ", TransferCoding.Chunked)]
|
||||
[InlineData("chunked ,", TransferCoding.Chunked)]
|
||||
[InlineData(",chunked", TransferCoding.Chunked)]
|
||||
[InlineData(", chunked", TransferCoding.Chunked)]
|
||||
[InlineData(",,chunked", TransferCoding.Chunked)]
|
||||
[InlineData(", ,chunked", TransferCoding.Chunked)]
|
||||
[InlineData(",, chunked", TransferCoding.Chunked)]
|
||||
[InlineData(", , chunked", TransferCoding.Chunked)]
|
||||
[InlineData("chunked, gzip", TransferCoding.Other)]
|
||||
[InlineData("chunked,compress", TransferCoding.Other)]
|
||||
[InlineData("deflate, chunked", TransferCoding.Chunked)]
|
||||
[InlineData("gzip,chunked", TransferCoding.Chunked)]
|
||||
[InlineData("compress,,chunked", TransferCoding.Chunked)]
|
||||
[InlineData("chunkedchunked", TransferCoding.Other)]
|
||||
[InlineData("chunked2", TransferCoding.Other)]
|
||||
[InlineData("chunked 2", TransferCoding.Other)]
|
||||
[InlineData("2chunked", TransferCoding.Other)]
|
||||
[InlineData("c2unked", TransferCoding.Other)]
|
||||
[InlineData("ch2nked", TransferCoding.Other)]
|
||||
[InlineData("chunked 2, gzip", TransferCoding.Other)]
|
||||
[InlineData("chunked2, gzip", TransferCoding.Other)]
|
||||
[InlineData("gzip, chunked 2", TransferCoding.Other)]
|
||||
[InlineData("gzip, chunked2", TransferCoding.Other)]
|
||||
public void TestParseTransferEncoding(string transferEncoding, TransferCoding expectedTransferEncodingOptions)
|
||||
{
|
||||
var transferEncodingOptions = FrameHeaders.GetFinalTransferCoding(transferEncoding);
|
||||
Assert.Equal(expectedTransferEncodingOptions, transferEncodingOptions);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("chunked", "gzip", TransferCoding.Other)]
|
||||
[InlineData("compress", "chunked", TransferCoding.Chunked)]
|
||||
[InlineData("chunked", "", TransferCoding.Chunked)]
|
||||
[InlineData("", "chunked", TransferCoding.Chunked)]
|
||||
[InlineData("chunked, deflate", "", TransferCoding.Other)]
|
||||
[InlineData("gzip, chunked", "", TransferCoding.Chunked)]
|
||||
[InlineData("", "chunked, compress", TransferCoding.Other)]
|
||||
[InlineData("", "compress, chunked", TransferCoding.Chunked)]
|
||||
[InlineData("", "", TransferCoding.None)]
|
||||
[InlineData("deflate", "", TransferCoding.Other)]
|
||||
[InlineData("", "gzip", TransferCoding.Other)]
|
||||
public void TestParseTransferEncodingMultipleValues(string value1, string value2, TransferCoding expectedTransferEncodingOptions)
|
||||
{
|
||||
var transferEncoding = new StringValues(new[] { value1, value2 });
|
||||
var transferEncodingOptions = FrameHeaders.GetFinalTransferCoding(transferEncoding);
|
||||
Assert.Equal(expectedTransferEncodingOptions, transferEncodingOptions);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ using System.Linq;
|
|||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Server.Kestrel;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Internal.Http;
|
||||
using Microsoft.AspNetCore.Server.KestrelTests.TestHelpers;
|
||||
using Microsoft.Extensions.Internal;
|
||||
|
|
@ -189,6 +190,19 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
|||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ForThrowsWhenFinalTransferCodingIsNotChunked()
|
||||
{
|
||||
using (var input = new TestInput())
|
||||
{
|
||||
var ex = Assert.Throws<BadHttpRequestException>(() =>
|
||||
MessageBody.For(HttpVersion.Http10, new FrameRequestHeaders { HeaderTransferEncoding = "chunked, not-chunked" }, input.FrameContext));
|
||||
|
||||
Assert.Equal(400, ex.StatusCode);
|
||||
Assert.Equal("Final transfer coding is not \"chunked\": \"chunked, not-chunked\"", ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<object[]> StreamData => new[]
|
||||
{
|
||||
new object[] { new ThrowOnWriteSynchronousStream() },
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
|||
var context = new Frame<object>(null, connectionContext);
|
||||
FrameContext = context;
|
||||
FrameContext.FrameControl = this;
|
||||
FrameContext.ConnectionContext.ListenerContext.ServiceContext.Log = trace;
|
||||
|
||||
_memoryPool = new MemoryPool();
|
||||
FrameContext.SocketInput = new SocketInput(_memoryPool, ltp);
|
||||
|
|
|
|||
|
|
@ -115,7 +115,7 @@ namespace Microsoft.AspNetCore.Testing
|
|||
{
|
||||
await Receive(lines);
|
||||
var ch = new char[128];
|
||||
var count = await _reader.ReadAsync(ch, 0, 128);
|
||||
var count = await _reader.ReadAsync(ch, 0, 128).TimeoutAfter(TimeSpan.FromMinutes(1));
|
||||
var text = new string(ch, 0, count);
|
||||
Assert.Equal("", text);
|
||||
}
|
||||
|
|
@ -127,7 +127,7 @@ namespace Microsoft.AspNetCore.Testing
|
|||
try
|
||||
{
|
||||
var ch = new char[128];
|
||||
var count = await _reader.ReadAsync(ch, 0, 128);
|
||||
var count = await _reader.ReadAsync(ch, 0, 128).TimeoutAfter(TimeSpan.FromMinutes(1));
|
||||
var text = new string(ch, 0, count);
|
||||
Assert.Equal("", text);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue