-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathHttpMessageSender.cs
More file actions
133 lines (122 loc) · 5.97 KB
/
Copy pathHttpMessageSender.cs
File metadata and controls
133 lines (122 loc) · 5.97 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
using System.IO.Pipelines;
using System.Net.Http.Headers;
using System.Security.Authentication;
using System.Text;
using CHttp.Data;
using CHttp.Writers;
namespace CHttp.Http;
internal sealed class HttpMessageSender
{
private readonly IWriter _writer;
private readonly HttpClient _client;
private readonly bool _toUtf8;
public HttpMessageSender(IWriter writer, ICookieContainer cookieContainer, BaseSocketsHandlerProvider socketsProvider, HttpBehavior behavior)
{
_writer = writer ?? throw new ArgumentNullException(nameof(writer));
ArgumentNullException.ThrowIfNull(behavior, nameof(behavior));
_toUtf8 = behavior.ToUtf8;
SocketsHttpHandler messageHandler = socketsProvider.GetMessageHandler(cookieContainer, behavior.SocketsBehavior);
_client = new HttpClient(messageHandler);
_client.Timeout = TimeSpan.FromSeconds(behavior.Timeout);
}
public HttpMessageSender(IWriter writer, HttpClient client)
{
_writer = writer ?? throw new ArgumentNullException(nameof(writer));
_client = client ?? throw new ArgumentNullException(nameof(client));
_toUtf8 = false;
}
public async Task SendRequestAsync(HttpRequestDetails requestData, CancellationToken token = default)
{
var request = new HttpRequestMessage(requestData.Method, requestData.Uri);
request.Version = requestData.Version;
request.VersionPolicy = HttpVersionPolicy.RequestVersionExact;
if (requestData.Content is MemoryArrayContent source)
request.Content = new MemoryArrayContent(source);
else
request.Content = requestData.Content;
SetHeaders(requestData, request);
await SendRequestAsync(_client, request, token);
}
public async Task SendRequestAsync(HttpRequestMessage request) => await SendRequestAsync(_client, request);
private async Task SendRequestAsync(HttpClient client, HttpRequestMessage request, CancellationToken token = default)
{
Summary summary = new(request.RequestUri?.ToString() ?? string.Empty);
HttpResponseHeaders? trailers = null;
{
try
{
var response = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, token);
var charSet = response.Content.Headers.ContentType?.CharSet;
var encoding = charSet is { } ? Encoding.GetEncoding(charSet) : Encoding.UTF8;
await _writer.InitializeResponseAsync(new HttpResponseInitials(response.StatusCode, response.Headers, response.Content.Headers, response.Version, encoding));
await ProcessResponseAsync(response, encoding, token);
summary.RequestCompleted(response.StatusCode);
trailers = response.TrailingHeaders;
}
catch (HttpRequestException requestException)
{
summary = summary with { Error = HandleRequestException(requestException), ErrorCode = ErrorType.HttpRequestException };
}
catch (HttpProtocolException protocolException)
{
summary = summary with { Error = $"Protocol Error {protocolException.ErrorCode}", ErrorCode = ErrorType.HttpProtocolException };
}
catch (TaskCanceledException)
{
summary = summary with { Error = "Request Timed Out or Canceled", ErrorCode = ErrorType.Timeout };
}
catch (OperationCanceledException)
{
summary = summary with { Error = "Request Timed Out or Canceled", ErrorCode = ErrorType.Timeout };
}
catch (Exception ex)
{
summary = summary with { Error = $"Generic Error {ex}", ErrorCode = ErrorType.Other };
}
}
await _writer.WriteSummaryAsync(trailers, summary);
}
private async Task ProcessResponseAsync(HttpResponseMessage response, Encoding encoding, CancellationToken token = default)
{
var contentStream = await response.Content.ReadAsStreamAsync(token);
var transcodingStream = _toUtf8 ? Encoding.CreateTranscodingStream(contentStream, encoding, Encoding.UTF8) : contentStream;
await transcodingStream.CopyToAsync(_writer.Buffer, token);
await _writer.Buffer.CompleteAsync();
}
private void SetHeaders(HttpRequestDetails requestData, HttpRequestMessage request)
{
foreach (var header in requestData.Headers)
{
var headerKey = header.GetKey();
var headerValue = header.GetValue();
if (!request.Headers.TryAddWithoutValidation(headerKey, headerValue) && request.Content is { })
{
if (string.Equals(headerKey, "Content-Type", StringComparison.OrdinalIgnoreCase))
{
request.Content.Headers.ContentType = MediaTypeHeaderValue.Parse(headerValue);
}
else
{
// Removing the header when overriden by the user.
request.Content.Headers.Remove(headerKey);
request.Content.Headers.TryAddWithoutValidation(headerKey, headerValue);
}
}
}
}
private string HandleRequestException(HttpRequestException requestException)
{
if (requestException.InnerException is AuthenticationException authException)
{
if (authException.Message.StartsWith("The remote certificate is invalid"))
return $"Enable flag --no-certificate-validation true'. SSL error: {authException.Message}";
if (authException.Message.StartsWith("Cannot determine the frame size or a corrupted frame was received"))
return $"Invalid http(s) schema: {authException.Message}";
}
if (requestException.InnerException is HttpIOException ioException)
{
return $"Invalid http(s) schema: {ioException.Message}";
}
return $"Request Error {requestException}";
}
}