Sane C++ Libraries
C++ Platform Abstraction Libraries
Loading...
Searching...
No Matches
HttpConnection.h
1// Copyright (c) Stefano Cristiano
2// SPDX-License-Identifier: MIT
3#pragma once
4#include "HttpParser.h"
5
6#include "../Async/Async.h"
7#include "../AsyncStreams/AsyncRequestStreams.h"
8#include "../Foundation/StringSpan.h"
9#include "Internal/HttpFixedBufferWriter.h"
10#include "Internal/HttpParsedHeaders.h"
11
12namespace SC
13{
16
17struct SC_COMPILER_EXPORT HttpMultipartWriter
18{
19 struct Part
20 {
21 StringSpan partName;
22 StringSpan fileName;
23 StringSpan contentType;
24
26 };
27
28 static constexpr size_t MaxParts = 8;
29
30 void reset();
31
32 Result setBoundary(StringSpan boundaryValue);
33 Result addField(StringSpan fieldName, StringSpan value);
34 Result addFile(StringSpan fieldName, StringSpan fileName, Span<const char> body,
35 StringSpan contentType = "application/octet-stream");
36
37 [[nodiscard]] StringSpan getBoundary() const { return boundary; }
38 [[nodiscard]] size_t getNumParts() const { return numParts; }
39 [[nodiscard]] const Part& getPart(size_t idx) const { return parts[idx]; }
40 [[nodiscard]] size_t getContentLength() const;
41
42 private:
43 Part parts[MaxParts];
44 size_t numParts = 0;
45 char boundaryStorage[71] = {0};
46 StringSpan boundary;
47};
48
50struct SC_COMPILER_EXPORT HttpConnectionBase
51{
54
55 ReadableSocketStream readableSocketStream;
56 WritableSocketStream writableSocketStream;
57 AsyncBuffersPool buffersPool;
58 AsyncPipeline pipeline;
59 SocketDescriptor socket;
60
61 void setHeaderMemory(Span<char> memory) { headerMemory = memory; }
62
63 [[nodiscard]] Span<char> getHeaderMemory() const { return headerMemory; }
64
65 void reset();
66
67 protected:
68 Span<char> headerMemory;
69};
70
72struct SC_COMPILER_EXPORT HttpIncomingMessage
73{
75 const HttpParser& getParser() const { return parsedHeaders.parser; }
76
78 [[nodiscard]] bool getKeepAlive() const { return parsedHeaders.parser.connectionKeepAlive; }
79
81 [[nodiscard]] uint64_t getBodyBytesRemaining() const { return bodyBytesRemaining; }
82
84 [[nodiscard]] bool isMultipart() const;
85
87 [[nodiscard]] StringSpan getBoundary() const;
88
93 [[nodiscard]] bool getHeader(StringSpan headerName, StringSpan& value) const;
94
97
100
103
104 protected:
105 void resetIncoming(HttpParser::Type type, Span<char> memory);
106
107 [[nodiscard]] bool hasReceivedHeaders() const { return parsedHeaders.headersEndReceived; }
108 [[nodiscard]] Span<char> getUnusedHeaderMemory() const { return parsedHeaders.availableHeader; }
109
110 void attachReadableStream(AsyncReadableStream& stream) { readableStream = &stream; }
111 void setBodyBytesRemaining(uint64_t value) { bodyBytesRemaining = value; }
112
117 [[nodiscard]] bool findParserToken(HttpParser::Token token, StringSpan& res) const;
118
121 Result writeHeaders(const uint32_t maxHeaderSize, Span<const char> readData, AsyncReadableStream& stream,
122 AsyncBufferView::ID bufferID, const char* outOfSpaceError, const char* sizeExceededError,
123 bool stopAtHeadersEnd);
124
126 [[nodiscard]] size_t getHeadersLength() const;
127
128 HttpParsedHeaders parsedHeaders;
129 Span<char> headerMemory;
130 AsyncReadableStream* readableStream = nullptr;
131 uint64_t bodyBytesRemaining = 0;
132};
133
135struct SC_COMPILER_EXPORT HttpRequest : public HttpIncomingMessage
136{
138 StringSpan getURL() const { return url; }
139
141 void reset();
142
143 private:
144 friend struct HttpConnectionsPool;
145 friend struct HttpResponse;
146 friend struct HttpAsyncServer;
147
148 void setHeaderMemory(Span<char> memory);
149
151 Result writeHeaders(const uint32_t maxHeaderSize, Span<const char> readData, AsyncReadableStream& stream,
152 AsyncBufferView::ID bufferID);
153
154 StringSpan url;
155};
156
158struct SC_COMPILER_EXPORT HttpAsyncClientResponse : public HttpIncomingMessage
159{
160 private:
161 friend struct HttpAsyncClient;
162
163 void reset(Span<char> memory);
164
165 Result writeHeaders(uint32_t maxHeaderSize, Span<const char> readData, AsyncReadableStream& stream,
166 AsyncBufferView::ID bufferID);
167};
168
170struct SC_COMPILER_EXPORT HttpOutgoingMessage
171{
175 Result addHeader(StringSpan headerName, StringSpan headerValue);
176
178 Result sendHeaders(Function<void(AsyncBufferView::ID)> callback = {});
179
181 void reset();
182
185
187 AsyncWritableStream& getWritableStream() { return *writableStream; }
188
192 void setKeepAlive(bool keepAlive);
193
196 [[nodiscard]] bool getKeepAlive() const { return forceDisableKeepAlive ? false : keepAlive; }
197
198 protected:
199 enum class KnownHeader : uint8_t
200 {
201 Connection,
202 Host,
203 UserAgent,
204 ContentLength,
205 ContentType,
206 TransferEncoding,
207 };
208
209 void setHeaderMemory(Span<char> memory);
210 void setWritableStream(AsyncWritableStream& stream) { writableStream = &stream; }
211
212 [[nodiscard]] bool hasHeader(KnownHeader header) const;
213 [[nodiscard]] bool hasSentHeaders() const { return headersSent; }
214 [[nodiscard]] bool hasEnded() const { return endCalled; }
215
216 HttpFixedBufferWriter responseHeaders;
217 Span<char> headerMemory;
218
219 bool headersSent = false;
220 bool endCalled = false;
221
222 bool forceDisableKeepAlive = false;
223 bool keepAlive = true;
224 bool connectionHeaderAdded = false;
225 bool hostHeaderAdded = false;
226 bool userAgentHeaderAdded = false;
227 bool contentLengthAdded = false;
228 bool contentTypeAdded = false;
229 bool transferEncodingAdded = false;
230
231 AsyncWritableStream* writableStream = nullptr;
232};
233
235struct SC_COMPILER_EXPORT HttpResponse : public HttpOutgoingMessage
236{
238 Result startResponse(int httpCode);
239
240 private:
241 friend struct HttpConnectionsPool;
242 friend struct HttpAsyncServer;
243
245 void grabUnusedHeaderMemory(HttpRequest& request);
246};
247
249struct SC_COMPILER_EXPORT HttpAsyncClientRequest : public HttpOutgoingMessage
250{
251 enum class BodyType : uint8_t
252 {
253 None,
254 Manual,
255 Span,
256 Stream,
257 Multipart,
258 };
259
260 void reset();
261
262 Result startRequest(HttpParser::Method method, StringSpan url);
263 Result setExpectedBodyLength(uint64_t value);
264 Result setBody(Span<const char> value);
265 Result setBody(StringSpan value) { return setBody(value.toCharSpan()); }
266 void setBody(AsyncReadableStream& stream, uint64_t contentLengthValue);
267 void setMultipart(HttpMultipartWriter& value);
268 Result sendHeaders(Function<void(AsyncBufferView::ID)> callback = {});
269
270 [[nodiscard]] HttpParser::Method getMethod() const { return method; }
271
272 [[nodiscard]] StringSpan getURL() const { return url; }
273 [[nodiscard]] BodyType getBodyType() const { return bodyType; }
274 [[nodiscard]] uint64_t getContentLength() const { return contentLength; }
275
276 private:
277 friend struct HttpAsyncClient;
278
279 void setDefaultHost(StringSpan value) { defaultHost = value; }
280
281 [[nodiscard]] bool hasTransferEncodingHeader() const { return hasHeader(KnownHeader::TransferEncoding); }
282
283 [[nodiscard]] AsyncReadableStream* getBodyStream() const { return bodyStream; }
284 [[nodiscard]] const HttpMultipartWriter* getMultipartWriter() const { return multipartWriter; }
285 [[nodiscard]] Span<const char> getBodySpan() const { return bodySpan; }
286
287 HttpParser::Method method = HttpParser::Method::HttpGET;
288 StringSpan url;
289 AsyncReadableStream* bodyStream = nullptr;
290 BodyType bodyType = BodyType::None;
291 Span<const char> bodySpan;
292 uint64_t contentLength = 0;
293 HttpMultipartWriter* multipartWriter = nullptr;
294 StringSpan defaultHost;
295
296 Function<void(AsyncBufferView::ID)> userHeadersSentCallback;
297 Function<void(AsyncBufferView::ID)> internalHeadersSentCallback;
298};
299
301struct SC_COMPILER_EXPORT HttpConnection : public HttpConnectionBase
302{
304
305 struct SC_COMPILER_EXPORT ID
306 {
307 size_t getIndex() const { return index; }
308
309 private:
310 friend struct HttpConnectionsPool;
311 size_t index = 0;
312 };
313
315 void reset();
316
318 ID getConnectionID() const { return connectionID; }
319
320 HttpRequest request;
321 HttpResponse response;
322
323 uint32_t requestCount = 0;
324
325 protected:
326 enum class State
327 {
328 Inactive,
329 Active
330 };
331 friend struct HttpConnectionsPool;
332 friend struct HttpAsyncServer;
333
334 State state = State::Inactive;
335 ID connectionID;
336};
337
339template <typename Type>
340struct SC_COMPILER_EXPORT SpanWithStride
341{
343 constexpr SpanWithStride() : data(nullptr), sizeElements(0), strideInBytes(0) {}
344
349 constexpr SpanWithStride(void* data, size_t sizeInElements, size_t strideInBytes)
350 : data(data), sizeElements(sizeInElements), strideInBytes(strideInBytes)
351 {}
352
353 template <typename U>
354 constexpr SpanWithStride(Span<U> span)
355 : data(span.data()), sizeElements(span.sizeInElements()), strideInBytes(sizeof(U))
356 {}
357
358 [[nodiscard]] constexpr size_t sizeInElements() const { return sizeElements; }
359 [[nodiscard]] constexpr bool empty() const { return sizeElements == 0; }
360
361 template <typename U>
362 SpanWithStride<U> castTo()
363 {
364 return {static_cast<U*>(reinterpret_cast<Type*>(data)), sizeElements, strideInBytes};
365 }
366
367 [[nodiscard]] Type& operator[](size_t idx)
368 {
369 return *reinterpret_cast<Type*>(reinterpret_cast<char*>(data) + idx * strideInBytes);
370 }
371
372 [[nodiscard]] const Type& operator[](size_t idx) const
373 {
374 return *reinterpret_cast<const Type*>(reinterpret_cast<const char*>(data) + idx * strideInBytes);
375 }
376
377 private:
378 void* data;
379 size_t sizeElements;
380 size_t strideInBytes;
381};
382
384struct SC_COMPILER_EXPORT HttpConnectionsPool
385{
387 {
388 size_t readQueueSize = 3;
389 size_t writeQueueSize = 3;
390 size_t buffersQueueSize = 6;
391 size_t headerBytesLength = 8 * 1024;
392 size_t streamBytesLength = 512 * 1024;
393 };
394
395 struct SC_COMPILER_EXPORT Memory
396 {
399
400 Span<AsyncBufferView> allBuffers;
401
402 Span<char> allHeaders;
403 Span<char> allStreams;
404
406 };
407
410
413
415 [[nodiscard]] size_t getNumActiveConnections() const { return numConnections; }
416
418 [[nodiscard]] size_t getNumTotalConnections() const { return connections.sizeInElements(); }
419
421 [[nodiscard]] size_t getHighestActiveConnection() const { return highestActiveConnection; }
422
425 {
426 return connections[connectionID.index];
427 }
428
430 [[nodiscard]] HttpConnection& getConnectionAt(size_t idx) { return connections[idx]; }
431
433 [[nodiscard]] bool activateNew(HttpConnection::ID& connectionID);
434
436 [[nodiscard]] bool deactivate(HttpConnection::ID connectionID);
437
438 private:
440
441 size_t numConnections = 0;
442
443 // Optimization for HttpAsyncServer::resize to avoid having to scan all connections
444 size_t highestActiveConnection = 0;
445};
446
448template <int ReadQueue, int WriteQueue, int HeaderBytes, int StreamBytes, int ExtraBuffers, typename BaseClass>
449struct SC_COMPILER_EXPORT HttpStaticConnection : public BaseClass
450{
451 AsyncReadableStream::Request readQueue[ReadQueue];
452 AsyncWritableStream::Request writeQueue[WriteQueue];
453
454 AsyncBufferView buffers[ReadQueue + WriteQueue + ExtraBuffers];
455
456 char headerStorage[HeaderBytes];
457 char streamStorage[StreamBytes];
458
459 constexpr HttpStaticConnection()
460 {
461 constexpr const size_t NumSlices = ReadQueue;
462 constexpr const size_t SliceLength = StreamBytes / NumSlices;
463
464 Span<char> memory = streamStorage;
465 for (size_t idx = 0; idx < NumSlices; ++idx)
466 {
467 Span<char> slice;
468 (void)memory.sliceStartLength(idx * SliceLength, SliceLength, slice);
469 buffers[idx] = slice;
470 buffers[idx].setReusable(true);
471 }
472 this->setHeaderMemory(headerStorage);
473 this->buffersPool.setBuffers(buffers);
474 this->readableSocketStream.setReadQueue(readQueue);
475 this->writableSocketStream.setWriteQueue(writeQueue);
476 }
477};
478
480} // namespace SC
unsigned char uint8_t
Platform independent (1) byte unsigned int.
Definition PrimitiveTypes.h:36
unsigned long long uint64_t
Platform independent (8) bytes unsigned int.
Definition PrimitiveTypes.h:42
unsigned int uint32_t
Platform independent (4) bytes unsigned int.
Definition PrimitiveTypes.h:38
struct SC_COMPILER_EXPORT Function
Wraps function pointers, member functions and lambdas without ever allocating.
Definition Function.h:19
Definition AsyncStreams.h:52
A Span of bytes memory to be read or written by async streams.
Definition AsyncStreams.h:50
void setReusable(bool reusable)
Tags this AsyncBufferView as reusable after its refCount goes to zero.
Definition AsyncStreams.h:91
Holds a Span of AsyncBufferView (allocated by user) holding available memory for the streams.
Definition AsyncStreams.h:163
Pipes read data from SC::AsyncReadableStream, forwarding them to SC::AsyncWritableStream.
Definition AsyncStreams.h:524
Definition AsyncStreams.h:219
Async source abstraction emitting data events in caller provided byte buffers.
Definition AsyncStreams.h:214
Definition AsyncStreams.h:348
Async destination abstraction where bytes can be written to.
Definition AsyncStreams.h:341
Outgoing HTTP request sent by the client.
Definition HttpConnection.h:250
Incoming HTTP response received by the client.
Definition HttpConnection.h:159
Async Http Server.
Definition HttpAsyncServer.h:39
Shared async transport storage for HTTP client and server endpoints.
Definition HttpConnection.h:51
Definition HttpConnection.h:306
Http connection abstraction holding both the incoming and outgoing messages in an HTTP transaction.
Definition HttpConnection.h:302
void reset()
Prepare this client for re-use, marking it as Inactive.
ID getConnectionID() const
The ID used to find this client in HttpConnectionsPool.
Definition HttpConnection.h:318
Definition HttpConnection.h:387
Definition HttpConnection.h:396
A pool of HttpConnection that can be active or inactive.
Definition HttpConnection.h:385
size_t getNumActiveConnections() const
Returns only the number of active connections.
Definition HttpConnection.h:415
size_t getHighestActiveConnection() const
Returns only the number of active connections.
Definition HttpConnection.h:421
HttpConnection & getConnectionAt(size_t idx)
Returns a connection in the [0, getNumTotalConnections] range.
Definition HttpConnection.h:430
Result init(SpanWithStride< HttpConnection > connectionsStorage)
Initializes the server with memory buffers for connections and headers.
bool activateNew(HttpConnection::ID &connectionID)
Finds an available connection (if any), activates it and returns its ID to use with getConnection(id)
HttpConnection & getConnection(HttpConnection::ID connectionID)
Returns a connection by ID.
Definition HttpConnection.h:424
size_t getNumTotalConnections() const
Returns the total number of connections (active + inactive)
Definition HttpConnection.h:418
Result close()
Closes the server, removing references to the memory buffers passed during init.
bool deactivate(HttpConnection::ID connectionID)
De-activates a connection previously returned by activateNew.
Incoming message from the perspective of the participants of an HTTP transaction.
Definition HttpConnection.h:73
bool getKeepAlive() const
Gets whether the other party requested the connection to stay alive.
Definition HttpConnection.h:78
const AsyncReadableStream & getReadableStream() const
Obtains the readable stream for the message body.
AsyncReadableStream & getReadableStream()
Obtains the readable stream for the message body.
bool isMultipart() const
Checks if the request is a multipart/form-data request.
const HttpParser & getParser() const
Gets the associated HttpParser.
Definition HttpConnection.h:75
bool getHeader(StringSpan headerName, StringSpan &value) const
Gets the value of a specific header (case-insensitive name matching)
size_t getHeadersLength() const
Gets the length of the headers in bytes.
StringSpan getBoundary() const
Gets the multipart boundary string (if isMultipart() returns true)
bool findParserToken(HttpParser::Token token, StringSpan &res) const
Finds a specific HttpParser::Result in the list of parsed header.
Result consumeBodyBytes(size_t bytes)
Decrements the remaining body bytes after consuming data.
uint64_t getBodyBytesRemaining() const
Returns how many body bytes are still expected for this message.
Definition HttpConnection.h:81
Result writeHeaders(const uint32_t maxHeaderSize, Span< const char > readData, AsyncReadableStream &stream, AsyncBufferView::ID bufferID, const char *outOfSpaceError, const char *sizeExceededError, bool stopAtHeadersEnd)
Parses an incoming slice of data eventually copying it to the availableHeader.
Definition HttpConnection.h:20
Definition HttpConnection.h:18
Outgoing message from the perspective of the participants of an HTTP transaction.
Definition HttpConnection.h:171
bool getKeepAlive() const
Gets whether the connection should be kept alive after this response.
Definition HttpConnection.h:196
Result end()
Finalizes the writable stream after sending all in progress writes.
void reset()
Resets this object for it to be re-usable.
Result sendHeaders(Function< void(AsyncBufferView::ID)> callback={})
Start sending response headers, before sending any data.
Result addHeader(StringSpan headerName, StringSpan headerValue)
Writes an http header to this response.
void setKeepAlive(bool keepAlive)
Sets whether to keep the connection alive after this response.
AsyncWritableStream & getWritableStream()
Obtain writable stream for sending content back to connected client.
Definition HttpConnection.h:187
Incremental HTTP request or response parser.
Definition HttpParser.h:15
Token
One possible Token reported by the parser.
Definition HttpParser.h:41
Method
Method of the current request / response.
Definition HttpParser.h:18
Type
Type of the stream to be parsed (Request or Response)
Definition HttpParser.h:58
Incoming HTTP request received by the server.
Definition HttpConnection.h:136
StringSpan getURL() const
Gets the request URL.
Definition HttpConnection.h:138
void reset()
Resets this object for it to be re-usable.
Outgoing HTTP response sent by the server.
Definition HttpConnection.h:236
Result startResponse(int httpCode)
Starts the response with a http standard code (200 OK, 404 NOT FOUND etc.)
Adds compile-time configurable read and write queues to any class subclassing HttpConnectionBase.
Definition HttpConnection.h:450
An ascii string used as boolean result. SC_TRY macro forwards errors to caller.
Definition Result.h:13
Low-level OS socket handle.
Definition Socket.h:153
View over a contiguous sequence of items with a custom stride between elements.
Definition HttpConnection.h:341
constexpr SpanWithStride(void *data, size_t sizeInElements, size_t strideInBytes)
Builds a SpanWithStride from data, size, and stride.
Definition HttpConnection.h:349
constexpr SpanWithStride()
Builds an empty SpanWithStride.
Definition HttpConnection.h:343
View over a contiguous sequence of items (pointer + size in elements).
Definition Span.h:29
constexpr bool sliceStartLength(SizeType offsetInElements, SizeType lengthInElements, Span &destination) const
Creates another Span, starting at an offset in elements from current Span of specified length.
Definition Span.h:121
An read-only view over a string (to avoid including Strings library when parsing is not needed).
Definition StringSpan.h:37
Span< const char > toCharSpan() const
Obtain a const char Span from this StringView.
Definition StringSpan.h:82