Skip to content

Commit 113b851

Browse files
committed
Add options to read no or partial request body
1 parent 6fbc068 commit 113b851

2 files changed

Lines changed: 91 additions & 20 deletions

File tree

tools/http-fault-injector/Azure.Sdk.Tools.HttpFaultInjector/FaultInjectingMiddleware.cs

Lines changed: 77 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
1-
using System.Threading.Tasks;
2-
using System.Threading;
3-
using Microsoft.Extensions.Logging;
41
using System;
5-
using Microsoft.AspNetCore.Connections.Features;
6-
using Microsoft.AspNetCore.Http;
7-
using Microsoft.Extensions.Primitives;
2+
using System.Buffers;
83
using System.Collections.Generic;
4+
using System.Diagnostics;
5+
using System.IO;
6+
using System.Linq;
97
using System.Net.Http;
108
using System.Net.Sockets;
119
using System.Reflection;
12-
using System.Linq;
13-
using System.IO;
14-
using System.Buffers;
15-
using System.Diagnostics;
10+
using System.Threading;
11+
using System.Threading.Tasks;
12+
using Microsoft.AspNetCore.Connections.Features;
13+
using Microsoft.AspNetCore.Http;
14+
using Microsoft.Extensions.Logging;
15+
using Microsoft.Extensions.Primitives;
1616

1717
namespace Azure.Sdk.Tools.HttpFaultInjector
1818
{
@@ -122,7 +122,42 @@ private async Task<MemoryStream> BufferContentAsync(HttpContent content, Cancell
122122

123123
private async Task ProxyResponse(HttpContext context, string upstreamUri, string fault, CancellationToken cancellationToken)
124124
{
125+
switch (fault)
126+
{
127+
case "nq":
128+
// No request body, then wait indefinitely
129+
await Task.Delay(Timeout.InfiniteTimeSpan, cancellationToken);
130+
return;
131+
case "nqc":
132+
// No request body, then close (TCP FIN)
133+
Close(context);
134+
return;
135+
case "nqa":
136+
// No request body, then abort (TCP RST)
137+
Abort(context);
138+
return;
139+
case "pq":
140+
// Partial request (50% of body), then wait indefinitely
141+
await ReadPartialRequest(context.Request, cancellationToken);
142+
await Task.Delay(Timeout.InfiniteTimeSpan, cancellationToken);
143+
return;
144+
case "pqc":
145+
// Partial request (50% of body), then close (TCP FIN)
146+
await ReadPartialRequest(context.Request, cancellationToken);
147+
Close(context);
148+
return;
149+
case "pqa":
150+
// Partial request (50% of body), then abort (TCP RST)
151+
await ReadPartialRequest(context.Request, cancellationToken);
152+
Abort(context);
153+
return;
154+
default:
155+
// Fall through and read full request body
156+
break;
157+
}
158+
125159
UpstreamResponse upstreamResponse = await SendUpstreamRequest(context.Request, upstreamUri, cancellationToken);
160+
126161
switch (fault)
127162
{
128163
case "f":
@@ -136,12 +171,12 @@ private async Task ProxyResponse(HttpContext context, string upstreamUri, string
136171
return;
137172
case "pc":
138173
// Partial Response (full headers, 50% of body), then close (TCP FIN)
139-
await SendDownstreamResponse(context.Response,upstreamResponse, upstreamResponse.ContentLength / 2, cancellationToken);
174+
await SendDownstreamResponse(context.Response, upstreamResponse, upstreamResponse.ContentLength / 2, cancellationToken);
140175
Close(context);
141176
return;
142177
case "pa":
143178
// Partial Response (full headers, 50% of body), then abort (TCP RST)
144-
await SendDownstreamResponse(context.Response,upstreamResponse, upstreamResponse.ContentLength / 2, cancellationToken);
179+
await SendDownstreamResponse(context.Response, upstreamResponse, upstreamResponse.ContentLength / 2, cancellationToken);
145180
Abort(context);
146181
return;
147182
case "pn":
@@ -166,6 +201,36 @@ private async Task ProxyResponse(HttpContext context, string upstreamUri, string
166201
}
167202
}
168203

204+
private static async Task ReadPartialRequest(HttpRequest request, CancellationToken cancellationToken)
205+
{
206+
var contentLength = request.ContentLength
207+
?? throw new InvalidOperationException("Partial request options require content-length request headers");
208+
var bytesToRead = contentLength / 2;
209+
long totalBytesRead = 0;
210+
var buffer = ArrayPool<byte>.Shared.Rent(81920);
211+
try
212+
{
213+
while (true)
214+
{
215+
var bytesRead = await request.Body.ReadAsync(
216+
buffer,
217+
0,
218+
(int)Math.Min(buffer.Length, bytesToRead - totalBytesRead),
219+
cancellationToken
220+
);
221+
totalBytesRead += bytesRead;
222+
if (totalBytesRead >= bytesToRead || bytesRead == 0)
223+
{
224+
break;
225+
}
226+
}
227+
}
228+
finally
229+
{
230+
ArrayPool<byte>.Shared.Return(buffer);
231+
}
232+
}
233+
169234
private async Task SendDownstreamResponse(HttpResponse response, UpstreamResponse upstreamResponse, long contentBytes, CancellationToken cancellationToken)
170235
{
171236
response.StatusCode = upstreamResponse.StatusCode;

tools/http-fault-injector/Azure.Sdk.Tools.HttpFaultInjector/Utils.cs

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,20 @@ public static class Utils
77
{
88
public static readonly IDictionary<string, string> FaultModes = new Dictionary<string, string>()
99
{
10-
{ "f", "Full response" },
11-
{ "p", "Partial Response (full headers, 50% of body), then wait indefinitely" },
12-
{"pc", "Partial Response (full headers, 50% of body), then close (TCP FIN)" },
13-
{"pa", "Partial Response (full headers, 50% of body), then abort (TCP RST)" },
14-
{"pn", "Partial Response (full headers, 50% of body), then finish normally" },
15-
{"n", "No response, then wait indefinitely"},
16-
{"nc", "No response, then close (TCP FIN)" },
17-
{"na", "No response, then abort (TCP RST)" }
10+
{ "f", "Full response" },
11+
{ "p", "Partial Response (full headers, 50% of body), then wait indefinitely" },
12+
{ "pc", "Partial Response (full headers, 50% of body), then close (TCP FIN)" },
13+
{ "pa", "Partial Response (full headers, 50% of body), then abort (TCP RST)" },
14+
{ "pn", "Partial Response (full headers, 50% of body), then finish normally" },
15+
{ "n", "No response, then wait indefinitely"},
16+
{ "nc", "No response, then close (TCP FIN)" },
17+
{ "na", "No response, then abort (TCP RST)" },
18+
{ "pq", "Partial request (50% of body), then wait indefinitely"},
19+
{"pqc", "Partial request (50% of body), then close (TCP FIN)"},
20+
{"pqa", "Partial request (50% of body), then abort (TCP RST)"},
21+
{ "nq", "No request body, then wait indefinitely"},
22+
{"nqc", "No request body, then close (TCP FIN)"},
23+
{"nqa", "No request body, then abort (TCP RST)"},
1824
};
1925

2026
public static readonly string[] ExcludedRequestHeaders = new string[] {

0 commit comments

Comments
 (0)