|
3 | 3 |
|
4 | 4 | #nullable enable
|
5 | 5 |
|
| 6 | +using System.Buffers; |
6 | 7 | using System.Globalization;
|
7 | 8 | using System.IO.Pipelines;
|
8 | 9 | using System.Linq.Expressions;
|
@@ -1376,6 +1377,132 @@ public async Task RequestDelegatePopulatesFromBodyParameter(Delegate action)
|
1376 | 1377 | Assert.Equal(originalTodo.Name, ((ITodo)deserializedRequestBody!).Name);
|
1377 | 1378 | }
|
1378 | 1379 |
|
| 1380 | + public static object[][] RawFromBodyActions |
| 1381 | + { |
| 1382 | + get |
| 1383 | + { |
| 1384 | + void TestStream(HttpContext httpContext, Stream stream) |
| 1385 | + { |
| 1386 | + var ms = new MemoryStream(); |
| 1387 | + stream.CopyTo(ms); |
| 1388 | + httpContext.Items.Add("body", ms.ToArray()); |
| 1389 | + } |
| 1390 | + |
| 1391 | + async Task TestPipeReader(HttpContext httpContext, PipeReader reader) |
| 1392 | + { |
| 1393 | + var ms = new MemoryStream(); |
| 1394 | + await reader.CopyToAsync(ms); |
| 1395 | + httpContext.Items.Add("body", ms.ToArray()); |
| 1396 | + } |
| 1397 | + |
| 1398 | + return new[] |
| 1399 | + { |
| 1400 | + new object[] { (Action<HttpContext, Stream>)TestStream }, |
| 1401 | + new object[] { (Func<HttpContext, PipeReader, Task>)TestPipeReader } |
| 1402 | + }; |
| 1403 | + } |
| 1404 | + } |
| 1405 | + |
| 1406 | + [Theory] |
| 1407 | + [MemberData(nameof(RawFromBodyActions))] |
| 1408 | + public async Task RequestDelegatePopulatesFromRawBodyParameter(Delegate action) |
| 1409 | + { |
| 1410 | + var httpContext = CreateHttpContext(); |
| 1411 | + |
| 1412 | + var requestBodyBytes = JsonSerializer.SerializeToUtf8Bytes(new |
| 1413 | + { |
| 1414 | + Name = "Write more tests!" |
| 1415 | + }); |
| 1416 | + |
| 1417 | + var stream = new MemoryStream(requestBodyBytes); |
| 1418 | + httpContext.Request.Body = stream; |
| 1419 | + |
| 1420 | + httpContext.Request.Headers["Content-Length"] = stream.Length.ToString(CultureInfo.InvariantCulture); |
| 1421 | + httpContext.Features.Set<IHttpRequestBodyDetectionFeature>(new RequestBodyDetectionFeature(true)); |
| 1422 | + |
| 1423 | + var mock = new Mock<IServiceProvider>(); |
| 1424 | + httpContext.RequestServices = mock.Object; |
| 1425 | + |
| 1426 | + var factoryResult = RequestDelegateFactory.Create(action); |
| 1427 | + |
| 1428 | + var requestDelegate = factoryResult.RequestDelegate; |
| 1429 | + |
| 1430 | + await requestDelegate(httpContext); |
| 1431 | + |
| 1432 | + Assert.Same(httpContext.Request.Body, stream); |
| 1433 | + |
| 1434 | + // Assert that we can read the body from both the pipe reader and Stream after executing |
| 1435 | + httpContext.Request.Body.Position = 0; |
| 1436 | + byte[] data = new byte[requestBodyBytes.Length]; |
| 1437 | + int read = await httpContext.Request.Body.ReadAsync(data.AsMemory()); |
| 1438 | + Assert.Equal(read, data.Length); |
| 1439 | + Assert.Equal(requestBodyBytes, data); |
| 1440 | + |
| 1441 | + httpContext.Request.Body.Position = 0; |
| 1442 | + var result = await httpContext.Request.BodyReader.ReadAsync(); |
| 1443 | + Assert.Equal(requestBodyBytes.Length, result.Buffer.Length); |
| 1444 | + Assert.Equal(requestBodyBytes, result.Buffer.ToArray()); |
| 1445 | + httpContext.Request.BodyReader.AdvanceTo(result.Buffer.End); |
| 1446 | + |
| 1447 | + var rawRequestBody = httpContext.Items["body"]; |
| 1448 | + Assert.NotNull(rawRequestBody); |
| 1449 | + Assert.Equal(requestBodyBytes, (byte[])rawRequestBody!); |
| 1450 | + } |
| 1451 | + |
| 1452 | + [Theory] |
| 1453 | + [MemberData(nameof(RawFromBodyActions))] |
| 1454 | + public async Task RequestDelegatePopulatesFromRawBodyParameterPipeReader(Delegate action) |
| 1455 | + { |
| 1456 | + var httpContext = CreateHttpContext(); |
| 1457 | + |
| 1458 | + var requestBodyBytes = JsonSerializer.SerializeToUtf8Bytes(new |
| 1459 | + { |
| 1460 | + Name = "Write more tests!" |
| 1461 | + }); |
| 1462 | + |
| 1463 | + var pipeReader = PipeReader.Create(new MemoryStream(requestBodyBytes)); |
| 1464 | + var stream = pipeReader.AsStream(); |
| 1465 | + httpContext.Features.Set<IRequestBodyPipeFeature>(new PipeRequestBodyFeature(pipeReader)); |
| 1466 | + httpContext.Request.Body = stream; |
| 1467 | + |
| 1468 | + httpContext.Request.Headers["Content-Length"] = requestBodyBytes.Length.ToString(CultureInfo.InvariantCulture); |
| 1469 | + httpContext.Features.Set<IHttpRequestBodyDetectionFeature>(new RequestBodyDetectionFeature(true)); |
| 1470 | + |
| 1471 | + var mock = new Mock<IServiceProvider>(); |
| 1472 | + httpContext.RequestServices = mock.Object; |
| 1473 | + |
| 1474 | + var factoryResult = RequestDelegateFactory.Create(action); |
| 1475 | + |
| 1476 | + var requestDelegate = factoryResult.RequestDelegate; |
| 1477 | + |
| 1478 | + await requestDelegate(httpContext); |
| 1479 | + |
| 1480 | + Assert.Same(httpContext.Request.Body, stream); |
| 1481 | + Assert.Same(httpContext.Request.BodyReader, pipeReader); |
| 1482 | + |
| 1483 | + // Assert that we can read the body from both the pipe reader and Stream after executing and verify that they are empty (the pipe reader isn't seekable here) |
| 1484 | + int read = await httpContext.Request.Body.ReadAsync(new byte[requestBodyBytes.Length].AsMemory()); |
| 1485 | + Assert.Equal(0, read); |
| 1486 | + |
| 1487 | + var result = await httpContext.Request.BodyReader.ReadAsync(); |
| 1488 | + Assert.Equal(0, result.Buffer.Length); |
| 1489 | + Assert.True(result.IsCompleted); |
| 1490 | + httpContext.Request.BodyReader.AdvanceTo(result.Buffer.End); |
| 1491 | + |
| 1492 | + var rawRequestBody = httpContext.Items["body"]; |
| 1493 | + Assert.NotNull(rawRequestBody); |
| 1494 | + Assert.Equal(requestBodyBytes, (byte[])rawRequestBody!); |
| 1495 | + } |
| 1496 | + |
| 1497 | + class PipeRequestBodyFeature : IRequestBodyPipeFeature |
| 1498 | + { |
| 1499 | + public PipeRequestBodyFeature(PipeReader pipeReader) |
| 1500 | + { |
| 1501 | + Reader = pipeReader; |
| 1502 | + } |
| 1503 | + public PipeReader Reader { get; set; } |
| 1504 | + } |
| 1505 | + |
1379 | 1506 | [Theory]
|
1380 | 1507 | [MemberData(nameof(ExplicitFromBodyActions))]
|
1381 | 1508 | public async Task RequestDelegateRejectsEmptyBodyGivenExplicitFromBodyParameter(Delegate action)
|
|
0 commit comments