Skip to content

Commit bf3b3fe

Browse files
committed
Implements the ability to stop automatic redirects.
1 parent 25d40ce commit bf3b3fe

File tree

6 files changed

+114
-23
lines changed

6 files changed

+114
-23
lines changed

src/https/Program.cs

Lines changed: 41 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -261,20 +261,8 @@ public async Task<int> RunAsync(string[] args)
261261
var stdoutWriter = new StreamWriter(stdout) { AutoFlush = true };
262262
{
263263
var renderer = new Renderer(stdoutWriter, stderrWriter);
264-
265-
var http = options.IgnoreCertificate
266-
? new HttpClient(
267-
new HttpClientHandler
268-
{
269-
ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator
270-
}
271-
)
272-
: new HttpClient();
273264

274-
if (options.Timeout.HasValue)
275-
{
276-
http.Timeout = options.Timeout.Value;
277-
}
265+
var http = CreateHttpClient(options);
278266

279267
var request = new HttpRequestMessage(
280268
command.Method ?? HttpMethod.Get,
@@ -341,6 +329,35 @@ public async Task<int> RunAsync(string[] args)
341329

342330
return 0;
343331
}
332+
333+
static HttpClient CreateHttpClient(Options options)
334+
{
335+
var http = default(HttpClient);
336+
if (options.RequiresHandler)
337+
{
338+
var handler = new HttpClientHandler();
339+
if (options.IgnoreCertificate)
340+
{
341+
handler.ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator;
342+
}
343+
if (options.StopAutoRedirects)
344+
{
345+
handler.AllowAutoRedirect = false;
346+
}
347+
http = new HttpClient(handler);
348+
}
349+
else
350+
{
351+
http = new HttpClient();
352+
}
353+
354+
if (options.Timeout.HasValue)
355+
{
356+
http.Timeout = options.Timeout.Value;
357+
}
358+
359+
return http;
360+
}
344361
}
345362

346363
enum ContentType
@@ -358,16 +375,19 @@ class Options
358375
public TimeSpan? Timeout { get; }
359376
public bool Version { get; }
360377
public bool Help { get; }
378+
public bool StopAutoRedirects { get; }
361379

380+
public bool RequiresHandler => IgnoreCertificate || StopAutoRedirects;
362381

363-
public Options(ContentType requestContentType, string xmlRootName, bool ignoreCertificate, TimeSpan? timeout, bool version, bool help)
382+
public Options(ContentType requestContentType, string xmlRootName, bool ignoreCertificate, TimeSpan? timeout, bool version, bool help, bool stopAutoRedirects)
364383
{
365384
RequestContentType = requestContentType;
366385
XmlRootName = xmlRootName;
367386
IgnoreCertificate = ignoreCertificate;
368387
Timeout = timeout;
369388
Version = version;
370389
Help = help;
390+
StopAutoRedirects = stopAutoRedirects;
371391
}
372392

373393
public static IEnumerable<string> GetOptionHelp()
@@ -379,6 +399,7 @@ public static IEnumerable<string> GetOptionHelp()
379399
yield return "--timeout=<VALUE> Sets the timeout of the request using System.TimeSpan.TryParse (https://docs.microsoft.com/en-us/dotnet/api/system.timespan.parse)";
380400
yield return "--version Displays the application verison.";
381401
yield return "--xml=<ROOT_NAME> Renders the content arguments as application/xml using the optional xml root name.";
402+
yield return "--stop-auto-redirects Prevents redirects from automatically being processed.";
382403
}
383404

384405
static int GetArgValueIndex(string arg)
@@ -400,6 +421,7 @@ public static Options Parse(IEnumerable<string> args)
400421
var timeout = default(TimeSpan?);
401422
var help = false;
402423
var version = false;
424+
var stopAutoRedirects = false;
403425
foreach (var arg in args)
404426
{
405427
if (arg.StartsWith("--json"))
@@ -451,8 +473,12 @@ public static Options Parse(IEnumerable<string> args)
451473
{
452474
help = true;
453475
}
476+
else if (arg.StartsWith("--stop-auto-redirects"))
477+
{
478+
stopAutoRedirects = true;
479+
}
454480
}
455-
return new Options(requestContentType, xmlRootName, ignoreCertificate, timeout, version, help);
481+
return new Options(requestContentType, xmlRootName, ignoreCertificate, timeout, version, help, stopAutoRedirects);
456482
}
457483
}
458484

tests/https.Tests/HttpsResult.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ public HttpsResult(int exitCode, MemoryStream stdout, MemoryStream stderr)
3333
if (pos > -1)
3434
{
3535
var key = line.Substring(0, pos);
36-
var value = line.Substring(pos + 1);
36+
var value = line.Substring(pos + 2);
3737
headers[key] = value;
3838
}
3939
}

tests/https.Tests/IntegrationTests.cs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,27 @@ public async Task MirrorTests()
1515
{
1616
var args = new[]
1717
{
18-
"post", _fixture.Url, "--json", "foo=bar", "lorem=ipsum"
18+
"post", $"{_fixture.Url}/Mirror", "--json", "foo=bar", "lorem=ipsum"
1919
};
2020

2121
var result = await Https.ExecuteAsync(args);
2222

2323
var json = new StreamReader(result.StdOut).ReadToEnd();
2424
Assert.Equal("{\"foo\":\"bar\",\"lorem\":\"ipsum\"}", json);
2525
}
26+
27+
[Fact]
28+
public async Task RedirectTest_ShouldShow3XXResponse_GivenStopAutoRedirects()
29+
{
30+
var args = new[]
31+
{
32+
"get", "http://localhost:5000/Redirect", "--stop-auto-redirects"
33+
};
34+
35+
var result = await Https.ExecuteAsync(args);
36+
37+
Assert.Equal("HTTP/1.1 301 Moved Permanently", result.Status);
38+
Assert.Equal("http://localhost:5000/Mirror", result.Headers["Location"]);
39+
}
2640
}
2741
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
using Microsoft.AspNetCore.Http;
2+
using System.Threading.Tasks;
3+
4+
namespace Https.Tests
5+
{
6+
class MirrorMiddleware
7+
{
8+
readonly RequestDelegate _next;
9+
public MirrorMiddleware(RequestDelegate next) =>
10+
_next = next;
11+
public async Task InvokeAsync(HttpContext context)
12+
{
13+
if (context.Request.Path.StartsWithSegments("/Mirror"))
14+
{
15+
if (!string.IsNullOrEmpty(context.Request.ContentType))
16+
{
17+
context.Response.ContentType = context.Request.ContentType;
18+
}
19+
20+
await context.Request.Body.CopyToAsync(context.Response.Body);
21+
}
22+
else
23+
{
24+
await _next(context);
25+
}
26+
}
27+
}
28+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
using Microsoft.AspNetCore.Http;
2+
using System.Threading.Tasks;
3+
4+
namespace Https.Tests
5+
{
6+
class RedirectMiddleware
7+
{
8+
readonly RequestDelegate _next;
9+
public RedirectMiddleware(RequestDelegate next) =>
10+
_next = next;
11+
public async Task InvokeAsync(HttpContext context)
12+
{
13+
if (context.Request.Path.StartsWithSegments("/Redirect"))
14+
{
15+
context.Response.StatusCode = StatusCodes.Status301MovedPermanently;
16+
context.Response.Headers.Add("Location", "http://localhost:5000/Mirror");
17+
}
18+
else
19+
{
20+
await _next(context);
21+
}
22+
}
23+
}
24+
}

tests/https.Tests/Startup.cs

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,19 @@
11
using Microsoft.AspNetCore.Builder;
22
using Microsoft.AspNetCore.Hosting;
3+
using Microsoft.AspNetCore.Http;
34

45
namespace Https.Tests
56
{
67
public class Startup
78
{
89
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
910
{
11+
app.UseMiddleware<RedirectMiddleware>();
12+
app.UseMiddleware<MirrorMiddleware>();
13+
1014
app.Run(async (context) =>
1115
{
12-
if (!string.IsNullOrEmpty(context.Request.ContentType))
13-
{
14-
context.Response.ContentType = context.Request.ContentType;
15-
}
16-
17-
await context.Request.Body.CopyToAsync(context.Response.Body);
16+
await context.Response.WriteAsync("Hello!");
1817
});
1918
}
2019
}

0 commit comments

Comments
 (0)