Skip to content

Commit

Permalink
Enhance MiniZipHttpException to include request details
Browse files Browse the repository at this point in the history
  • Loading branch information
joelverhagen committed Mar 29, 2023
1 parent 69c8e59 commit 74163ed
Show file tree
Hide file tree
Showing 5 changed files with 154 additions and 28 deletions.
12 changes: 6 additions & 6 deletions MiniZip.Test/MiniZip.Test.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,12 @@
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.StaticFiles" Version="2.2.0" />
<PackageReference Include="Microsoft.AspNetCore.TestHost" Version="2.2.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.1.0" />
<PackageReference Include="Moq" Version="4.17.2" />
<PackageReference Include="NuGet.Protocol" Version="6.1.0" />
<PackageReference Include="SharpZipLib" Version="1.3.3" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.5.0" />
<PackageReference Include="Moq" Version="4.18.4" />
<PackageReference Include="NuGet.Protocol" Version="6.5.0" />
<PackageReference Include="SharpZipLib" Version="1.4.2" />
<PackageReference Include="xunit" Version="2.4.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
Expand Down
41 changes: 41 additions & 0 deletions MiniZip.Test/Support/HttpMessageExtensionTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
using System;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using Xunit;

namespace Knapcode.MiniZip
{
public class HttpMessageExtensionTest
{
public class TheToHttpExceptionAsyncMethod
{
[Fact]
public async Task PutsRequestAndResponseInExceptionMessage()
{
// Arrange
var request = new HttpRequestMessage(HttpMethod.Put, "https://www.example.com/v3/index.json");
request.Headers.TryAddWithoutValidation("User-Agent", "my fake UA");
request.Content = new StringContent("my test content\nand the second line too.");
request.Content.Headers.TryAddWithoutValidation("X-Content-Header", "I'm here too!");

var response = new HttpResponseMessage(HttpStatusCode.BadRequest);
response.ReasonPhrase = "Not good request";
response.Headers.Age = TimeSpan.FromDays(9001);
response.Content = new StringContent("This is the super cool response.");
response.RequestMessage = request;

// Act
var exception = await response.ToHttpExceptionAsync("The request failed I guess?");

// Assert
Assert.Equal(HttpStatusCode.BadRequest, exception.StatusCode);
Assert.Equal("Not good request", exception.ReasonPhrase);
Assert.Null(exception.InnerException);
Assert.Equal(
"The request failed I guess?\r\n\r\n=== Request ===\r\nPUT https://www.example.com/v3/index.json HTTP/1.1\r\nUser-Agent: my\r\nUser-Agent: fake\r\nUser-Agent: UA\r\nContent-Type: text/plain; charset=utf-8\r\nX-Content-Header: I'm here too!\r\n\r\nmy test content\nand the second line too.\r\n\r\n=== Response ===\r\nHTTP/1.1 400 Not good request\r\nAge: 777686400\r\nContent-Type: text/plain; charset=utf-8\r\n\r\nThis is the super cool response.\r\n\r\n",
exception.Message);
}
}
}
}
4 changes: 2 additions & 2 deletions MiniZip/MiniZip.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@
<AssemblyOriginatorKeyFile>..\MiniZip.snk</AssemblyOriginatorKeyFile>
<DelaySign>false</DelaySign>

<Version>0.19.0</Version>
<Version>0.20.0</Version>

<Authors>Joel Verhagen</Authors>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<PackageProjectUrl>https://github.com/joelverhagen/MiniZip</PackageProjectUrl>
<PackageReleaseNotes>Fix bug where 404s were causing HTTP retries</PackageReleaseNotes>
<PackageReleaseNotes>Enhance MiniZipHttpException to include request details</PackageReleaseNotes>
<PackageReadmeFile>README.md</PackageReadmeFile>
<PackageTags>zip;http;partial;range;mzip;minizip;fetch;directory</PackageTags>
<Description>Read the file listing of a .zip archive without downloading the whole thing.</Description>
Expand Down
58 changes: 51 additions & 7 deletions MiniZip/MiniZipHttpException.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Net;
using System.Text;

namespace Knapcode.MiniZip
{
Expand Down Expand Up @@ -28,13 +29,33 @@ public MiniZipHttpException(string message, HttpStatusCode statusCode, string re
/// <param name="debugResponse">A string containing debug information about the response.</param>
/// <param name="innerException">The inner exception.</param>
public MiniZipHttpException(string message, HttpStatusCode statusCode, string reasonPhrase, string debugResponse, Exception innerException)
: this(message, debugRequest: null, statusCode, reasonPhrase, debugResponse, innerException)
{
}

/// <summary>
/// Initialize the exception.
/// </summary>
/// <param name="message">The exception message.</param>
/// <param name="debugRequest">A string containing debug information about the request.</param>
/// <param name="statusCode">The HTTP status code that caused this exception.</param>
/// <param name="reasonPhrase">The reason phrase returned with the <paramref name="statusCode"/>.</param>
/// <param name="debugResponse">A string containing debug information about the response.</param>
/// <param name="innerException">The inner exception.</param>
public MiniZipHttpException(string message, string debugRequest, HttpStatusCode statusCode, string reasonPhrase, string debugResponse, Exception innerException)
: base(message, innerException)
{
DebugRequest = debugRequest;
StatusCode = statusCode;
ReasonPhrase = reasonPhrase;
DebugResponse = debugResponse;
}

/// <summary>
/// A string containing debug information about the request.
/// </summary>
public string DebugRequest { get; }

/// <summary>
/// The HTTP status code of the response.
/// </summary>
Expand All @@ -53,12 +74,35 @@ public MiniZipHttpException(string message, HttpStatusCode statusCode, string re
/// <summary>
/// Gets a message that describes the current exception.
/// </summary>
public override string Message =>
base.Message +
Environment.NewLine +
Environment.NewLine +
"Debug response:" +
Environment.NewLine +
DebugResponse;
public override string Message
{
get
{
if (DebugRequest == null && DebugResponse == null)
{
return base.Message;
}

var sb = new StringBuilder();
sb.AppendLine(base.Message);
sb.AppendLine();

if (DebugRequest != null)
{
sb.AppendLine("=== Request ===");
sb.AppendLine(DebugRequest);
sb.AppendLine();
}

if (DebugResponse != null)
{
sb.AppendLine("=== Response ===");
sb.AppendLine(DebugResponse);
sb.AppendLine();
}

return sb.ToString();
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Http;
Expand All @@ -7,44 +8,86 @@

namespace Knapcode.MiniZip
{
internal static class HttpResponseMessageExtensions
internal static class HttpMessageExtensions
{
public static async Task<MiniZipHttpException> ToHttpExceptionAsync(this HttpResponseMessage response, string message)
{
string debugResponse = null;
Exception innerException = null;

string debugRequest = null;
if (response.RequestMessage != null)
{
try
{
debugRequest = await response.RequestMessage.GetDebugStringAsync();
}
catch (Exception ex)
{
debugRequest = "<error>";
innerException = ex;
}
}

string debugResponse;
try
{
debugResponse = await response.GetDebugStringAsync();
}
catch (Exception ex)
{
debugResponse = "<error>";
innerException = ex;
if (innerException == null)
{
innerException = ex;
}
else
{
innerException = new AggregateException(innerException, ex);
}
}

return new MiniZipHttpException(
message,
debugRequest,
response.StatusCode,
response.ReasonPhrase,
debugResponse,
innerException);
}

public static async Task<string> GetDebugStringAsync(this HttpRequestMessage request)
{
var builder = new StringBuilder();

builder.AppendFormat("{0} {1} HTTP/{2}\r\n", request.Method, request.RequestUri.AbsoluteUri, request.Version);

await AppendHeadersAndBody(builder, request.Headers, request.Content);

return builder.ToString();
}

public static async Task<string> GetDebugStringAsync(this HttpResponseMessage response)
{
var builder = new StringBuilder();

// Write the opening line.
builder.AppendFormat("HTTP/{0} {1} {2}\r\n", response.Version, (int)response.StatusCode, response.ReasonPhrase);

// Write the headers.
var headers = response.Headers.AsEnumerable();
if (response.Content != null)
await AppendHeadersAndBody(builder, response.Headers, response.Content);

return builder.ToString();
}

private static async Task AppendHeadersAndBody(
StringBuilder builder,
IEnumerable<KeyValuePair<string, IEnumerable<string>>> headers,
HttpContent content)
{
if (content != null)
{
headers = headers.Concat(response.Content.Headers);
headers = headers.Concat(content.Headers);
}

// Write the headers.
foreach (var header in headers)
{
foreach (var value in header.Value)
Expand All @@ -55,10 +98,10 @@ public static async Task<string> GetDebugStringAsync(this HttpResponseMessage re

builder.Append("\r\n");

// Write the response body.
if (response.Content != null)
// Write the request or response body.
if (content != null)
{
using (var stream = await response.Content.ReadAsStreamAsync())
using (var stream = await content.ReadAsStreamAsync())
{
var buffer = new byte[1024 * 32 + 1];
var totalRead = 0;
Expand Down Expand Up @@ -95,8 +138,6 @@ public static async Task<string> GetDebugStringAsync(this HttpResponseMessage re
}
}
}

return builder.ToString();
}
}
}

0 comments on commit 74163ed

Please sign in to comment.