Sane C++ Libraries
C++ Platform Abstraction Libraries
Loading...
Searching...
No Matches
HttpAsyncClient.h
1// Copyright (c) Stefano Cristiano
2// SPDX-License-Identifier: MIT
3#pragma once
4#include "HttpConnection.h"
5#include "HttpExport.h"
6#include "HttpURLParser.h"
7
8namespace SC
9{
12
13struct HttpWebSocketTransportView;
14
19struct SC_HTTP_EXPORT HttpAsyncClientTransportSetup
20{
21 HttpConnectionBase* connection = nullptr;
22 AsyncEventLoop* eventLoop = nullptr;
23
24 const HttpURLParser* url = nullptr;
25
26 Function<void(Result)> complete;
27};
28
29template <int ReadQueue, int WriteQueue, int HeaderBytes, int StreamBytes>
30struct SC_HTTP_EXPORT HttpAsyncClientConnection
31 : public HttpStaticConnection<ReadQueue, WriteQueue, HeaderBytes, StreamBytes, 8, HttpConnectionBase>
32{
33 static constexpr int ExtraBuffers = 8;
34
36 {
37 this->readableSocketStream.setAutoDestroy(false);
38 this->writableSocketStream.setAutoDestroy(false);
39 }
40};
41
46struct SC_HTTP_EXPORT HttpAsyncClientTlsOptions
47{
48 bool verifyPeer = true;
49
50 Span<const char> caCertificates;
51 StringSpan caCertificatesPath;
52};
53
74struct SC_HTTP_EXPORT HttpAsyncClient
75{
76 struct Header
77 {
78 StringSpan name;
79 StringSpan value;
80 };
81
83 {
84 enum class BodyMode : uint8_t
85 {
86 None,
87 Span,
88 Stream,
89 Multipart,
90 };
91
92 HttpParser::Method method = HttpParser::Method::HttpGET;
93 StringSpan url;
94
95 Span<const Header> headers;
96
97 Span<const char> body;
98 AsyncReadableStream* bodyStream = nullptr;
99 HttpMultipartWriter* multipartWriter = nullptr;
100
101 uint64_t bodyLength = 0;
102
103 BodyMode bodyMode = BodyMode::None;
104 bool keepAlive = false;
105
106 RequestOptions& setRequest(HttpParser::Method newMethod, StringSpan newURL, bool newKeepAlive = false)
107 {
108 method = newMethod;
109 url = newURL;
110 keepAlive = newKeepAlive;
111 return *this;
112 }
113
114 RequestOptions& setHeaders(Span<const Header> newHeaders)
115 {
116 headers = newHeaders;
117 return *this;
118 }
119
120 RequestOptions& setKeepAlive(bool newKeepAlive = true)
121 {
122 keepAlive = newKeepAlive;
123 return *this;
124 }
125
126 RequestOptions& clearBody()
127 {
128 body = {};
129 bodyStream = nullptr;
130 multipartWriter = nullptr;
131 bodyLength = 0;
132 bodyMode = BodyMode::None;
133 return *this;
134 }
135
136 RequestOptions& setBody(Span<const char> newBody)
137 {
138 clearBody();
139 body = newBody;
140 bodyLength = newBody.sizeInBytes();
141 bodyMode = BodyMode::Span;
142 return *this;
143 }
144
145 RequestOptions& setBody(StringSpan newBody) { return setBody(newBody.toCharSpan()); }
146
147 RequestOptions& setBody(const char* newBody)
148 {
149 return setBody(StringSpan::fromNullTerminated(newBody, StringEncoding::Ascii));
150 }
151
152 RequestOptions& setBody(AsyncReadableStream& newBodyStream, uint64_t newBodyLength)
153 {
154 clearBody();
155 bodyStream = &newBodyStream;
156 bodyLength = newBodyLength;
157 bodyMode = BodyMode::Stream;
158 return *this;
159 }
160
161 RequestOptions& setMultipart(HttpMultipartWriter& newMultipartWriter)
162 {
163 clearBody();
164 multipartWriter = &newMultipartWriter;
165 bodyMode = BodyMode::Multipart;
166 return *this;
167 }
168 };
169
172 Result init(HttpConnectionBase& storage);
173
175 Result close();
176
182 void setResponseDecompression(SyncZLibTransformStream& decoder) { responseDecoder = &decoder; }
183
185 void clearResponseDecompression() { responseDecoder = nullptr; }
186
188 void setTlsOptions(const HttpAsyncClientTlsOptions& options) { tlsOptions = options; }
189
191 void clearTlsOptions() { tlsOptions = {}; }
192
193 [[nodiscard]] const HttpAsyncClientTlsOptions& getTlsOptions() const { return tlsOptions; }
194
199 void setTransportSetup(Function<Result(HttpAsyncClientTransportSetup&)>&& setup) { transportSetup = move(setup); }
200
202 void clearTransportSetup() { transportSetup = {}; }
203
206
210 Result start(AsyncEventLoop& loop, HttpParser::Method method, StringSpan url, bool keepAlive = false);
211
214 Result sendRequest(AsyncEventLoop& loop, const RequestOptions& options);
215
217 Result get(AsyncEventLoop& loop, StringSpan url, bool keepAlive = false);
218
220 Result head(AsyncEventLoop& loop, StringSpan url, bool keepAlive = false);
221
223 Result options(AsyncEventLoop& loop, StringSpan url, bool keepAlive = false);
224
226 Result deleteRequest(AsyncEventLoop& loop, StringSpan url, bool keepAlive = false);
227
229 Result put(AsyncEventLoop& loop, StringSpan url, Span<const char> body, bool keepAlive = false);
230 Result put(AsyncEventLoop& loop, StringSpan url, StringSpan body, bool keepAlive = false)
231 {
232 return put(loop, url, body.toCharSpan(), keepAlive);
233 }
234
236 Result post(AsyncEventLoop& loop, StringSpan url, Span<const char> body, bool keepAlive = false);
237 Result post(AsyncEventLoop& loop, StringSpan url, StringSpan body, bool keepAlive = false)
238 {
239 return post(loop, url, body.toCharSpan(), keepAlive);
240 }
241
243 Result patch(AsyncEventLoop& loop, StringSpan url, Span<const char> body, bool keepAlive = false);
244 Result patch(AsyncEventLoop& loop, StringSpan url, StringSpan body, bool keepAlive = false)
245 {
246 return patch(loop, url, body.toCharSpan(), keepAlive);
247 }
248
250 Result postMultipart(AsyncEventLoop& loop, StringSpan url, HttpMultipartWriter& writer, bool keepAlive = false);
251
252 [[nodiscard]] HttpAsyncClientResponse& getResponse() { return response; }
253 [[nodiscard]] const HttpAsyncClientResponse& getResponse() const { return response; }
254
257
260
262 Function<void(Result)> onError;
263
264 private:
265 struct RequestPreset
266 {
267 enum class BodyMode : uint8_t
268 {
269 None,
270 Span,
271 Stream,
272 Multipart,
273 };
274
275 HttpParser::Method method = HttpParser::Method::HttpGET;
276
277 StringSpan url;
278
279 bool keepAlive = false;
280 bool autoSend = false;
281
282 Span<const char> bodySpan;
283
284 BodyMode bodyMode = BodyMode::None;
285 uint64_t contentLength = 0;
286
287 Span<const Header> headers;
288
289 AsyncReadableStream* bodyStream = nullptr;
290 HttpMultipartWriter* multipartWriter = nullptr;
291 };
292
293 enum class State : uint8_t
294 {
295 Idle,
296 Connecting,
297 Sending,
298 WaitingResponse,
299 StreamingResponse,
300 };
301
302 Result startRequest(AsyncEventLoop& loop, const RequestPreset& preset);
303 Result prepareRequest(const RequestPreset& preset);
304 Result startPreparedRequest(const RequestPreset& preset);
305 Result ensureConnected();
306 Result beginSocketConnection();
307 Result beginResponseRead();
308 Result beginRequestSend();
309 Result onResponseBodyStreamRead();
310 Result validateActiveRequest() const;
311
312 void completeTransportSetup(Result result);
313 [[nodiscard]] Result rememberConnectedOrigin();
314
315 void finalizeResponse(bool finishBodyStream);
316 void closeConnection();
317 void finishResponse();
318 void fail(Result error);
319
320 void onConnected(AsyncSocketConnect::Result& result);
321 void onReadableError(Result result);
322 void onWritableError(Result result);
323 void onPipelineError(Result result);
324 void onReadableEnd();
325 void onHeadersBufferWritten(AsyncBufferView::ID bufferID);
326 void onResponseData(AsyncBufferView::ID bufferID);
327 void onResponseBodyData(AsyncBufferView::ID bufferID);
328 void onCompressedResponseBodyData(AsyncBufferView::ID bufferID);
329 void onCompressedResponseBodyWritten(AsyncBufferView::ID bufferID);
330 void onCompressedResponseBodyEnd();
331 void onCompressedResponseError(Result result);
332
333 [[nodiscard]] bool canReuseConnectionFor(StringSpan protocol, StringSpan host, uint16_t port) const;
334 [[nodiscard]] bool responseMustNotHaveBody() const;
335 [[nodiscard]] bool responseHasKnownLength() const;
336 [[nodiscard]] Result prepareResponseDecompression();
337 [[nodiscard]] Result startResponseStreams();
338 void detachResponseDecompression();
339
340 HttpConnectionBase* connection = nullptr;
341
342 AsyncEventLoop* eventLoop = nullptr;
343 HttpAsyncClientRequest* currentRequest = nullptr;
344 HttpAsyncClientRequest request;
345 HttpAsyncClientResponse response;
346 AsyncSocketConnect connectAsync;
347 RequestPreset currentPreset;
348 SyncZLibTransformStream* responseDecoder = nullptr;
349 bool responseDecoderActive = false;
350 HttpAsyncClientTlsOptions tlsOptions;
351 Function<Result(HttpAsyncClientTransportSetup&)> transportSetup;
352
353 State state = State::Idle;
354
355 StringSpan currentProtocol;
356 char currentProtocolStorage[16] = {0};
357
358 StringSpan currentHost;
359 char currentHostStorage[256] = {0};
360 uint16_t currentPort = 0;
361
362 HttpURLParser currentURL;
363 uint32_t requestCount = 0;
364
365 bool hasOpenConnection = false;
366 bool responseDelivered = false;
367 bool responseFinalized = false;
368 bool webSocketUpgraded = false;
369};
370
372} // namespace SC
Asynchronous I/O (files, sockets, timers, processes, fs events, threads wake-up) (see Async) AsyncEve...
Definition Async.h:1447
Async source abstraction emitting data events in caller provided byte buffers.
Definition AsyncStreams.h:228
Definition HttpAsyncClient.h:32
Outgoing HTTP request sent by the client.
Definition HttpConnection.h:428
Incoming HTTP response received by the client.
Definition HttpConnection.h:259
TLS policy for HttpAsyncClient HTTPS requests.
Definition HttpAsyncClient.h:47
Mutable transport setup view used by HttpAsyncClient after the TCP socket connects.
Definition HttpAsyncClient.h:20
Definition HttpAsyncClient.h:77
Definition HttpAsyncClient.h:83
Asynchronous HTTP/1.1 client using caller-provided fixed storage.
Definition HttpAsyncClient.h:75
Result deleteRequest(AsyncEventLoop &loop, StringSpan url, bool keepAlive=false)
Convenience wrapper for a DELETE request without a request body.
Result options(AsyncEventLoop &loop, StringSpan url, bool keepAlive=false)
Convenience wrapper for an OPTIONS request without a request body.
void clearResponseDecompression()
Disables response decompression for future requests.
Definition HttpAsyncClient.h:185
Function< void(HttpAsyncClientResponse &)> onResponse
Called after the response headers have been parsed.
Definition HttpAsyncClient.h:259
Result patch(AsyncEventLoop &loop, StringSpan url, Span< const char > body, bool keepAlive=false)
Convenience wrapper for a PATCH request with a fixed in-memory body.
Result detachWebSocketTransport(HttpWebSocketTransportView &transport)
Hands the connected socket streams to a WebSocket owner after a validated 101 response.
Result head(AsyncEventLoop &loop, StringSpan url, bool keepAlive=false)
Convenience wrapper for a HEAD request without a request body.
Result start(AsyncEventLoop &loop, HttpParser::Method method, StringSpan url, bool keepAlive=false)
Starts a request that must be configured inside onPrepareRequest onPrepareRequest must send the heade...
Function< void(Result)> onError
Called on connection, protocol or streaming errors.
Definition HttpAsyncClient.h:262
Result postMultipart(AsyncEventLoop &loop, StringSpan url, HttpMultipartWriter &writer, bool keepAlive=false)
Convenience wrapper for a multipart/form-data POST request.
Result close()
Closes any active connection and releases references to the initialized storage.
void clearTransportSetup()
Clears the optional transport setup hook and restores default socket transport setup.
Definition HttpAsyncClient.h:202
Result get(AsyncEventLoop &loop, StringSpan url, bool keepAlive=false)
Convenience wrapper for a GET request without a request body.
Result sendRequest(AsyncEventLoop &loop, const RequestOptions &options)
Starts an auto-sent request described by caller-owned request options.
void setTransportSetup(Function< Result(HttpAsyncClientTransportSetup &)> &&setup)
Sets an optional transport setup hook invoked after TCP connect and before HTTP request bytes are sen...
Definition HttpAsyncClient.h:199
Result init(HttpConnectionBase &storage)
Initializes the client with caller-provided connection storage The storage must outlive the client an...
Result post(AsyncEventLoop &loop, StringSpan url, Span< const char > body, bool keepAlive=false)
Convenience wrapper for a POST request with a fixed in-memory body.
void setResponseDecompression(SyncZLibTransformStream &decoder)
Enables opt-in gzip/deflate response decompression.
Definition HttpAsyncClient.h:182
Function< void(HttpAsyncClientRequest &)> onPrepareRequest
Called after the request has been created and can still be customized.
Definition HttpAsyncClient.h:256
void clearTlsOptions()
Restores default TLS verification options.
Definition HttpAsyncClient.h:191
Result put(AsyncEventLoop &loop, StringSpan url, Span< const char > body, bool keepAlive=false)
Convenience wrapper for a PUT request with a fixed in-memory body.
void setTlsOptions(const HttpAsyncClientTlsOptions &options)
Sets TLS verification options used by future HTTPS transport integration.
Definition HttpAsyncClient.h:188
Shared async transport storage for HTTP client and server endpoints.
Definition HttpConnection.h:73
Definition HttpConnection.h:40
Method
Method of the current request / response.
Definition HttpParser.h:19
Adds compile-time configurable read and write queues to any class subclassing HttpConnectionBase.
Definition HttpConnection.h:654
Parse an URL splitting it into its base components.
Definition HttpURLParser.h:71
Minimal transport handoff shape for later HTTP upgrade integration.
Definition HttpWebSocket.h:58
Definition ZLibTransformStreams.h:10