• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Aldaviva / Unfucked / 41314065342

01 Nov 2026 02:00PM UTC coverage: 44.713% (-1.4%) from 46.093%
41314065342

push

github

Aldaviva
Added JSON MessageBodyReader for Corvus.JsonSchema's ParsedJsonDocument, which is how you map UTF-8 JSON streams to C# classes autogenerated from a JSON Schema. Removed all .NET 6 targets because it's now the second most recent out of support LTS release behind 8, and it causes some NuGet compatibility warnings with newer libraries. Fixed HTTP exceptions themselves throwing exceptions with deflated HTTP response bodies. Converted GenericBodyReader to cache MethodInfo instead of delegates, because the delegates are actually slightly slower to call in benchmarks. Made JSON enum converter KernighanRitchieCEnumToLowerCamelCaseNamingPolicy public and configurable to serialize and deserialize JSON strings with any naming policy, not just lowerCamelCase (which is still the default).

800 of 2339 branches covered (34.2%)

41 of 50 new or added lines in 5 files covered. (82.0%)

42 existing lines in 3 files now uncovered.

1404 of 3140 relevant lines covered (44.71%)

154.98 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/HTTP/Exceptions/HttpException.cs
1
using System.Net;
2
using System.Net.Http.Headers;
3
using System.Text;
4

5
namespace Unfucked.HTTP.Exceptions;
6

7
#pragma warning disable CS9113 // Parameter is unread. - the superclass constructor overload that reads it only exists in .NET 6, not .NET Standard 2.0
8
/// <summary>
9
/// Base exception class for all network, filtering, deserialization, and HTTP status code errors
10
/// </summary>
11
public abstract class HttpException(HttpStatusCode? status, string message, Exception? cause, HttpExceptionParams exceptionParams): HttpRequestException(MessageChain(message, cause), cause
×
NEW
12
#if NET5_0_OR_GREATER
×
13
    , status
×
14
#endif
×
15
) {
×
16

17
#pragma warning restore CS9113 // Parameter is unread.
18

19
    protected readonly HttpExceptionParams HttpExceptionParams = exceptionParams;
×
20

21
    public Uri? RequestUrl => HttpExceptionParams.RequestUrl;
×
22
    public HttpMethod Verb => HttpExceptionParams.Verb;
×
23
    public IDictionary<string, object?>? RequestProperties => HttpExceptionParams.RequestProperties;
×
24
#if NET5_0_OR_GREATER
25
    public HttpRequestOptions? RequestOptions => HttpExceptionParams.RequestOptions;
26
#endif
27

28
    private static string MessageChain(string outerMessage, Exception? cause) {
29
        StringBuilder chain = new(outerMessage);
×
30
        while (cause != null) {
×
31
            chain.Append(": ").Append(cause.Message);
×
32
            cause = cause.InnerException is var inner && cause != inner ? inner : null;
×
33
        }
34
        return chain.ToString();
×
35
    }
36

37
}
38

39
#pragma warning disable CS0618 // Type or member is obsolete - it's not obsolete in .NET Standard 2.0, which this library targets
40
public sealed record HttpExceptionParams(
×
41
    HttpMethod Verb,
×
42
    Uri? RequestUrl,
×
43
    HttpResponseHeaders? ResponseHeaders = null,
×
44
    ReadOnlyMemory<byte>? ResponseBody = null,
×
45
    IDictionary<string, object?>? RequestProperties = null
×
46
#if NET5_0_OR_GREATER
×
47
    ,
×
48
    HttpRequestOptions? RequestOptions = null
×
49
#endif
×
50
) {
×
51

52
    public static HttpExceptionParams FromRequest(HttpRequestMessage request) => new(request.Method, request.RequestUri, null, null, request.Properties
×
53
#if NET5_0_OR_GREATER
×
54
        , request.Options
×
55
#endif
×
56
    );
×
57

58
    public static async Task<HttpExceptionParams> FromResponse(HttpResponseMessage response, CancellationToken cancellationToken = default) {
59
        HttpRequestMessage? request = response.RequestMessage;
×
60

61
        ReadOnlyMemory<byte>? responseBody = null;
×
62
        try {
63
            await response.Content.LoadIntoBufferAsync().ConfigureAwait(false);
×
64
            Task<byte[]> readAsByteArrayAsync =
×
65
#if NET5_0_OR_GREATER
×
66
                response.Content.ReadAsByteArrayAsync(cancellationToken);
×
67
#else
68
                response.Content.ReadAsByteArrayAsync();
69
#endif
70
            responseBody = (await readAsByteArrayAsync.ConfigureAwait(false)).AsMemory();
×
NEW
71
        } catch (InvalidOperationException) {} catch (InvalidDataException) {
×
72
            // leave responseBody null
73
        }
×
74

75
        return new HttpExceptionParams(request?.Method ?? HttpMethod.Get, request?.RequestUri, response.Headers, responseBody, request?.Properties
×
76
#if NET5_0_OR_GREATER
×
77
            , request?.Options
×
78
#endif
×
79
        );
×
80
    }
×
81

82
}
83
#pragma warning restore CS0618 // Type or member is obsolete
84

85
/// <summary>
86
/// Unsuccessful HTTP status code
87
/// </summary>
88
public class WebApplicationException(HttpStatusCode status, string reasonPhrase, HttpExceptionParams exceptionParams)
89
    : HttpException(status, $"{(int) status} {(reasonPhrase.HasText ? reasonPhrase : status.ToString())} from {exceptionParams?.RequestUrl}", null, exceptionParams!) {
×
90

91
    public string ReasonPhrase => reasonPhrase;
×
92
    public HttpResponseHeaders ResponseHeaders => HttpExceptionParams.ResponseHeaders!;
×
93
    public ReadOnlyMemory<byte>? ResponseBody => HttpExceptionParams.ResponseBody;
×
94

95
    public
96
#if NET5_0_OR_GREATER
97
        new
98
#endif
99
        HttpStatusCode StatusCode => status;
×
100

101
}
102

103
/// <summary>400–499</summary>
104
public class ClientErrorException(HttpStatusCode status, string reasonPhrase, HttpExceptionParams exceptionParams): WebApplicationException(status, reasonPhrase, exceptionParams);
×
105

106
/// <summary>400</summary>
107
public sealed class BadRequestException(string? reasonPhrase, HttpExceptionParams exceptionParams): ClientErrorException(HttpStatusCode.BadRequest, reasonPhrase ?? "Bad Request", exceptionParams);
×
108

109
/// <summary>401</summary>
110
public sealed class NotAuthorizedException(string? reasonPhrase, HttpExceptionParams exceptionParams)
111
    : ClientErrorException(HttpStatusCode.Unauthorized, reasonPhrase ?? "Unauthorized", exceptionParams);
×
112

113
/// <summary>403</summary>
114
public sealed class ForbiddenException(string? reasonPhrase, HttpExceptionParams exceptionParams): ClientErrorException(HttpStatusCode.Forbidden, reasonPhrase ?? "Forbidden", exceptionParams);
×
115

116
/// <summary>404</summary>
117
public sealed class NotFoundException(string? reasonPhrase, HttpExceptionParams exceptionParams): ClientErrorException(HttpStatusCode.NotFound, reasonPhrase ?? "Not Found", exceptionParams);
×
118

119
/// <summary>405</summary>
120
public sealed class NotAllowedException(string? reasonPhrase, HttpExceptionParams exceptionParams)
121
    : ClientErrorException(HttpStatusCode.MethodNotAllowed, reasonPhrase ?? "Method Not Allowed", exceptionParams);
×
122

123
/// <summary>406</summary>
124
public sealed class NotAcceptableException(string? reasonPhrase, HttpExceptionParams exceptionParams)
125
    : ClientErrorException(HttpStatusCode.NotAcceptable, reasonPhrase ?? "Not Acceptable", exceptionParams);
×
126

127
/// <summary>415</summary>
128
public sealed class NotSupportedException(string? reasonPhrase, HttpExceptionParams exceptionParams)
129
    : ClientErrorException(HttpStatusCode.UnsupportedMediaType, reasonPhrase ?? "Unsupported Media Type", exceptionParams);
×
130

131
/// <summary>500–599</summary>
132
public class ServerErrorException(HttpStatusCode statusCode, string reasonPhrase, HttpExceptionParams exceptionParams): WebApplicationException(statusCode, reasonPhrase, exceptionParams);
×
133

134
/// <summary>500</summary>
135
public sealed class InternalServerErrorException(string? reasonPhrase, HttpExceptionParams exceptionParams)
136
    : ServerErrorException(HttpStatusCode.InternalServerError, reasonPhrase ?? "Internal Server Error", exceptionParams);
×
137

138
/// <summary>503</summary>
139
public sealed class ServiceUnavailableException(string? reasonPhrase, HttpExceptionParams exceptionParams)
140
    : ServerErrorException(HttpStatusCode.ServiceUnavailable, reasonPhrase ?? "Service Unavailable", exceptionParams);
×
141

142
/// <summary>300–399</summary>
143
public sealed class RedirectionException(HttpStatusCode statusCode, Uri? destination, string reasonPhrase, HttpExceptionParams exceptionParams)
144
    : WebApplicationException(statusCode, reasonPhrase, exceptionParams) {
×
145

146
    public Uri? Destination { get; } = destination;
×
147

148
}
149

150
/// <summary>Network, filter, or deserialization error</summary>
151
public sealed class ProcessingException(Exception cause, HttpExceptionParams exceptionParams): HttpException(null,
×
152
    $"Network, filter, or deserialization error during HTTP {exceptionParams.Verb} request to {exceptionParams.RequestUrl?.AbsoluteUri}", cause, exceptionParams) {
×
153

154
#if NET5_0_OR_GREATER
155
    // ReSharper disable once MemberCanBeMadeStatic.Global - unhides parent property, defeating the purpose of hiding it
156
    [Obsolete(
157
        $"{nameof(ProcessingException)}s never have status codes, so this property always returns null. They represent failures in network I/O or response deserialization (such as timeouts, refused connections, or malformed JSON), rather than non-200-class status codes, which are instead represented by {nameof(WebApplicationException)}.")]
158
    public new HttpStatusCode? StatusCode => null;
159
#endif
160

161
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc