libhttppp ..
Loading...
Searching...
No Matches
http.h
1/*******************************************************************************
2Copyright (c) 2014, Jan Koester jan.koester@gmx.net
3All rights reserved.
4
5Redistribution and use in source and binary forms, with or without
6modification, are permitted provided that the following conditions are met:
7 * Redistributions of source code must retain the above copyright
8 notice, this list of conditions and the following disclaimer.
9 * Redistributions in binary form must reproduce the above copyright
10 notice, this list of conditions and the following disclaimer in the
11 documentation and/or other materials provided with the distribution.
12 * Neither the name of the <organization> nor the
13 names of its contributors may be used to endorse or promote products
14 derived from this software without specific prior written permission.
15
16THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
20DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26*******************************************************************************/
27
28#include <stddef.h>
29#include <sys/types.h>
30
31#include <vector>
32#include <string>
33#include <memory>
34#include <deque>
35#include <map>
36#include <unordered_map>
37#include <chrono>
38
39#include <netplus/socket.h>
40#include <netplus/connection.h>
41#include <netplus/eventapi.h>
42#include <netplus/crypto/tls.h>
43
44#include "config.h"
45
46#include "httpdefinitions.h"
47#include "hpack.h"
48
49#pragma once
50
51namespace libhttppp::qpack { struct HeaderField; }
52
53namespace libhttppp {
54 // Method token <-> *REQUEST constant (httpdefinitions.h). Unknown methods map to
55 // GETREQUEST; RequestMethodName returns "" for an unknown type.
56 int RequestTypeFromMethod(const std::string &method);
57 const char *RequestMethodName(int requestType);
58
59 class HttpRequest;
60
61 class HttpUrl {
62 public:
63 enum HttpProtocol{
64 HTTP=0,
65 HTTPS=1,
66 HTTP3=2
67 };
68
69 HttpUrl();
70 HttpUrl(const std::string &url,bool http3=false);
71 HttpUrl(const HttpUrl &src);
72 HttpUrl& operator=(const HttpUrl &src) = default;
73 ~HttpUrl();
74
75 bool operator==(const HttpUrl& other) const;
76 bool operator<(const HttpUrl& other) const;
77
78 int getProtocol() const;
79 // The scheme (HTTP or HTTPS) as it actually appeared in the URL string,
80 // unaffected by the http3 ctor flag -- getProtocol() returns HTTP3 once
81 // that flag is set, which loses the info needed to fall back correctly
82 // when HTTP/3 isn't available.
83 int getOriginalProtocol() const;
84 const std::string &getHost() const;
85 int getPort() const;
86 const std::string &getPath() const;
87
88 void clear();
89
90 std::string print() const;
91
92 private:
93 int _protocol;
94 int _origProtocol;
95 std::string _host;
96 int _port;
97 std::string _path;
98 };
99
100 class HttpResponse;
101
102 // HttpClient's documented opt-in default (see its ctor's trustPolicy parameter below) --
103 // a bare netplus::TlsTrustPolicy() is secure-by-default (verifyPeer=true) for its other
104 // callers, so this pins HttpClient's default to "verify nothing" independently of that.
105 inline netplus::TlsTrustPolicy noVerifyTrustPolicy() {
106 netplus::TlsTrustPolicy p;
107 p.verifyPeer = false;
108 return p;
109 }
110
112 public:
113 // timeoutSec bounds the constructor's own eager connection attempt
114 // (see resetConnection()) in addition to being the initial value
115 // setTimeout() would otherwise set afterward -- too late to affect
116 // that first connect.
117 // trustPolicy is opt-in (default = verify nothing, the pre-existing behavior every
118 // caller got before this parameter existed): pass one with verifyPeer=true to get real
119 // hostname/CA-chain/pinned-fingerprint verification of the upstream's TLS certificate
120 // (see netplus::TlsTrustPolicy in <netplus/crypto/cert_verify.h>). Note this can't just
121 // default to a bare netplus::TlsTrustPolicy() -- that struct is secure-by-default
122 // (verifyPeer=true) for its other callers (e.g. proxyplus's own TLS client), so HttpClient
123 // needs its own explicitly-relaxed default to keep its documented opt-in contract.
124 // If trustPolicy.expectedHostname is left empty, it defaults to desturl's host.
125 HttpClient( const HttpUrl &desturl, int vers = 2, int timeoutSec = 60,
126 const netplus::TlsTrustPolicy &trustPolicy = noVerifyTrustPolicy());
127 ~HttpClient()=default;
128 void reconnect();
129 void setTimeout(int timeout_sec);
130 const std::vector<char> Get(HttpRequest &nreq, size_t maxTries=0);
131 const std::vector<char> Post(HttpRequest &nreq,const std::vector<char> &post, size_t maxTries=0);
132 const std::vector<char> Put(HttpRequest &nreq,const std::vector<char> &put, size_t maxTries=0);
133 const std::vector<char> Delete(HttpRequest &nreq, size_t maxTries=0);
134 const std::vector<char> Options(HttpRequest &nreq, size_t maxTries=0);
135 const std::vector<char> Head(HttpRequest &nreq, size_t maxTries=0);
136 const std::vector<char> Patch(HttpRequest &nreq,const std::vector<char> &patch, size_t maxTries=0);
137 // Any method known to RequestTypeFromMethod (e.g. PROPFIND, MKCOL, MOVE for WebDAV);
138 // body may be nullptr. Never follows redirects.
139 const std::vector<char> Request(int requestType, HttpRequest &nreq,
140 const std::vector<char> *body = nullptr, size_t maxTries=0);
141
142 // Streaming API: send request, return parsed response headers only.
143 // After this call, use readBodyChunk() to read body data incrementally.
144 HttpResponse GetStream(HttpRequest &nreq);
145
146 // Same as GetStream, but POSTs a body first (e.g. an OpenAI-compatible
147 // "stream": true chat-completions request) before entering streaming-
148 // read mode. HTTP/1.1 upstream connections only -- throws HTTPException
149 // for HTTP/2 or HTTP/3 connections (no redirect handling either, unlike
150 // Post()); local LLM inference backends are plain HTTP/1.1 services, so
151 // this deliberately doesn't replicate Post()'s H2/H3/redirect handling.
152 HttpResponse PostStream(HttpRequest &nreq, const std::vector<char> &postBody);
153
154 // Read the next chunk of body data (up to bufsize bytes).
155 // Returns number of bytes written to buf, 0 when body is complete.
156 size_t readBodyChunk(char *buf, size_t bufsize);
157
158 // Non-blocking variant: returns 0 immediately if no data available yet.
159 // Returns (size_t)-1 when stream is complete (no more data will come).
160 size_t readBodyChunkNonBlocking(char *buf, size_t bufsize);
161
162 // True while a streaming read is in progress.
163 bool isStreaming() const;
164
165 // True once the peer has signaled it wants this connection closed
166 // (an HTTP/2 GOAWAY frame, or an HTTP/1.x response's own
167 // "Connection: close" header) -- a caller pooling HttpClient instances
168 // for reuse should check this (and !isStreaming()) before handing an
169 // instance back for a future request instead of closing it.
170 bool wantsClose() const { return _peerWantsClose; }
171
172 // Wait until upstream socket has data to read (or timeout expires).
173 // timeout_ms: -1 = infinite, 0 = return immediately, >0 = milliseconds
174 // Returns true if readable, false on timeout.
175 bool waitReadable(int timeout_ms);
176
177 // Override the maximum number of redirects to follow (default: 5).
178 // Set to 0 to disable automatic redirect following.
179 void setMaxRedirects(int max) { _maxRedirects = max; }
180
181 // Returns the HTTP status code from the last response.
182 int lastStatusCode() const { return _lastStatusCode; }
183
184 // Returns the Content-Type from the last response.
185 const std::string &lastContentType() const { return _lastContentType; }
186
187 // Returns the full parsed response from the last request.
188 const HttpResponse *lastResponse() const { return _lastResponse.get(); }
189
190 // Drop the current connection so the next request opens a fresh one.
191 void resetConnection();
192
193 // Shared TLS session cache — enables abbreviated TLS 1.2 handshakes
194 // across reconnects to the same host. One cache per process.
195 static netplus::TlsSessionCache& tlsSessionCache();
196 private:
197 netplus::TlsTrustPolicy _trustPolicy;
198
199 void _ensureConnected();
200 // Thin wrapper around netplus::tcp::connectTimeout() using
201 // _recvTimeoutSec, translating its NetException into HTTPException.
202 void _connectTcp(netplus::tcp &sock);
203 bool tryHttp3First();
204
205 // Non-blocking I/O helpers using poll() for efficient waiting
206 size_t _recvBlocking(netplus::buffer &b, int timeout_sec = 0);
207 // Returns 0 on EAGAIN (no data yet), otherwise bytes read
208 size_t _recvNonBlocking(netplus::buffer &b);
209 void _sendAll(const char *data, size_t len);
210 void _sendAll(const std::string &data);
211
212 // Shared HTTP/1.x response reader (avoids code duplication)
213 std::vector<char> _h1ReadResponse(const std::string &label);
214
215 // Shared blocking/non-blocking body readers for STREAM_CHUNKED and
216 // STREAM_EOF (used by both readBodyChunk() and
217 // readBodyChunkNonBlocking() -- a single implementation parameterized
218 // by `blocking` instead of two independently-maintained copies, the
219 // second of which used to not exist at all and silently fell back to
220 // the blocking one). Return value matches readBodyChunkNonBlocking's
221 // contract: 0 = no data yet (blocking=false only) or a benign empty
222 // read, (size_t)-1 = body complete, otherwise bytes written to buf.
223 bool _streamRecvMore(bool blocking, bool &eof);
224 size_t _streamReadChunked(char *buf, size_t bufsize, bool blocking);
225 size_t _streamReadEof(char *buf, size_t bufsize, bool blocking);
226
227 // Same idea for STREAM_H2: readBodyChunk() and
228 // readBodyChunkNonBlocking() previously carried two independently
229 // hand-copied H2 frame-dispatch switches, which had already drifted
230 // (the non-blocking copy had gained an explicit
231 // H2C_FRAME_WINDOW_UPDATE case the blocking one lacked). The frame
232 // dispatch itself is now written once; only how bytes are pulled off
233 // the wire (wait vs. one non-blocking attempt) differs by `blocking`.
234 size_t _streamReadH2(char *buf, size_t bufsize, bool blocking);
235
236 // Same idea for STREAM_H3: the QUIC varint frame-parsing loop (partial
237 // DATA-frame continuation handling included) was duplicated verbatim
238 // between readBodyChunk() and readBodyChunkNonBlocking(); now written
239 // once and shared, with only the "wait for more data vs. try once and
240 // return" difference kept per-mode.
241 size_t _streamReadH3(char *buf, size_t bufsize, bool blocking);
242
243 // Mode-dispatch body of readBodyChunkNonBlocking(), wrapped by the
244 // public method with an idle-stream deadline (see _streamIdleSince).
245 size_t _readBodyChunkNonBlockingRaw(char *buf, size_t bufsize);
246
247 // Shared Get/Post/Put/Delete implementation: builds+sends the request
248 // over whichever transport is active (H1/H2/H3), follows redirects
249 // per RFC 7231 §6.4 (303 always converts to a bodyless GET; 307/308
250 // preserve the original method) when followRedirects is set, and
251 // retries up to maxTries times on transport errors.
252 const std::vector<char> _doH1Request(const std::string &method, int requestType,
253 HttpRequest &nreq, const std::vector<char> *body,
254 size_t maxTries, bool followRedirects);
255
256 // HTTP/2 client helpers
257 bool _isH2 = false;
258 bool _isH3 = false; // cached in place of repeated dynamic_cast<quic*>
259 bool _h2PrefaceSent = false; // true after connection preface sent
260
261 // HTTP/3 client helpers
262 uint32_t _h2NextStreamId = 1; // next client-initiated stream ID (odd)
263 std::unique_ptr<hpack::Decoder> _h2Decoder; // persistent HPACK decoder for connection
264 const std::vector<char> _h2Request(const std::string &method,
265 HttpRequest &nreq,
266 const std::vector<char> *postBody = nullptr);
267 const std::vector<char> _h3Request(const std::string &method,
268 HttpRequest &nreq,
269 const std::vector<char> *postBody = nullptr);
270
271 // Streaming state
272 enum StreamMode { STREAM_NONE, STREAM_CONTENT_LENGTH, STREAM_CHUNKED, STREAM_EOF,
273 STREAM_H2, STREAM_H3 };
274 StreamMode _streamMode = STREAM_NONE;
275 size_t _streamRemaining = 0; // bytes left for content-length mode
276 std::vector<char> _streamBuf; // leftover data from header read
277 size_t _streamBufPos = 0;
278 // Chunked streaming sub-state
279 bool _streamChunkDone = false; // true after final 0-length chunk
280 size_t _streamChunkRemaining = 0; // bytes left in current chunk
281
282 // HTTP/2 streaming state
283 uint32_t _streamH2Sid = 0; // stream ID for active H2 stream
284 bool _streamH2EndStream = false;
285 std::vector<uint8_t> _streamH2Raw; // raw frame buffer
286
287 // HTTP/3 streaming state
288 uint64_t _streamH3Sid = 0; // stream ID for active H3 stream
289 bool _streamH3EndStream = false;
290 std::vector<uint8_t> _streamH3Raw; // raw frame buffer
291 std::vector<char> _streamH3Body; // decoded DATA frames not yet consumed
292 size_t _streamH3BodyPos = 0; // consumed offset into _streamH3Body
293 bool _streamH3InDataFrame = false; // true when inside a partial DATA frame
294 uint64_t _streamH3DataRemaining = 0; // bytes left in current DATA frame
295
296 // Set the moment readBodyChunkNonBlocking() first sees "no data yet"
297 // for the active stream; cleared on any progress or completion. Lets
298 // the wrapper tell "briefly nothing to read" apart from "peer has
299 // gone silent for _recvTimeoutSec and is never sending another byte".
300 std::chrono::steady_clock::time_point _streamIdleSince{};
301
302 private:
303 HttpUrl _url;
304 std::unique_ptr<netplus::socket> _cltsock;
305 netplus::socketwait _sw;
306 netplus::x509cert _cert;
307 // Reused across every recv() call in this object's lifetime instead of
308 // constructing a fresh netplus::buffer per read: the Linux buffer ctor
309 // value-initializes (zero-fills) the whole CHUNKSIZE (64KB) allocation,
310 // so a fresh one per network read cost an alloc+memset+free even when
311 // only a few KB actually arrived. Safe to share across all call sites
312 // since each one copies out exactly the bytes recvData() reports before
313 // doing anything else with the buffer (nothing relies on unwritten
314 // bytes being zero, and HttpClient is not used from multiple threads
315 // concurrently -- _cltsock/_streamMode etc. already assume that).
316 netplus::buffer _recvScratch{CHUNKSIZE};
317 int _recvTimeoutSec = 60;
318 int _sendTimeoutSec = 30;
319 int _vers = 2; // HTTP version preference (0=h1 only, 1=h1+h2, 2=h2 preferred, 3=h3, 4=internal h2-only probe)
320
321 // Set by _h1ReadResponse (Connection: close) and the H2 GOAWAY handlers
322 // in GetStream/_streamReadH2/_h2Request -- see wantsClose() above.
323 // Reset in resetConnection() since a fresh connection never wants closing.
324 bool _peerWantsClose = false;
325
326 // Response tracking (populated by _h1ReadResponse / _h2Request / _h3Request)
327 int _lastStatusCode = 0;
328 std::string _lastLocation;
329 std::string _lastContentType;
330 std::unique_ptr<HttpResponse> _lastResponse;
331 static constexpr int MAX_REDIRECTS = 5;
332 int _maxRedirects = MAX_REDIRECTS;
333 };
334
335
337 public:
339 public:
340 class Values{
341 public:
342 Values &operator=(const std::string &val);
343 Values &operator=(size_t val);
344 Values &operator=(int val);
345 Values& operator=(const Values &val);
346
347 Values &operator<<(const std::string &value);
348 Values &operator<<(size_t value);
349 Values &operator<<(int value);
350
351 const std::string &getvalue();
352 int getIntvalue();
353 size_t getSizetValue();
354
355 Values *nextvalue();
356 Values(const std::string& val);
357 Values(const Values& val);
358 Values()=default;
359 ~Values() = default;
360 private:
361 std::string _value;
362 std::unique_ptr<Values> _nextvalue=nullptr;
363 friend class HeaderData;
364 };
365
366 Values *getfirstValue();
367 Values &at(int pos);
368 Values &operator[](int pos);
369
370 Values &push_back(const Values &val);
371 Values &push_back(const std::string &val);
372 Values &push_back(const char* val);
373 Values &push_back(size_t val);
374 Values &push_back(int val);
375
376 bool empty();
377
378 void erase(int pos);
379
380 void clear();
381
382 const std ::string &getkey();
383
384 HeaderData *nextHeaderData();
385 HeaderData(const std ::string &key);
386 ~HeaderData() = default;
387 private:
388 std::string _Key;
389 std::unique_ptr<Values> _firstValue=nullptr;
390 Values *_lastValue=nullptr;
391 std::unique_ptr<HeaderData> _nextHeaderData=nullptr;
392 friend class HttpHeader;
393 };
394
395 HeaderData *getfirstHeaderData() const;
396 HeaderData *getHeaderData(const std ::string &key) const;
397 HeaderData *setHeaderData(const std ::string &key);
398
399 void deldata(const std ::string &key);
400 void deldata(HeaderData*pos);
401
402 size_t getElements();
403 size_t getHeaderSize();
404
405 void clear();
406 protected:
407 HttpHeader();
408 virtual ~HttpHeader()=default;
409 std::unique_ptr<HeaderData> _firstHeaderData;
410 HeaderData *_lastHeaderData;
411 private:
412 // O(1)-average lookup by key, keyed exactly like the old linear scan
413 // compared (raw key string, case-sensitive -- callers already always
414 // pass pre-lowercased keys; HeaderData's own ctor separately lowercases
415 // _Key for storage). The linked list above remains the source of truth
416 // for iteration order and node lifetime/address stability (HeaderData*
417 // handed out to callers, e.g. HttpResponse's cached _ContentLength etc.,
418 // must stay valid -- nodes are never moved, only the index pointing at
419 // them changes), this is purely a lookup accelerator kept in sync by
420 // setHeaderData()/deldata()/clear().
421 std::unordered_map<std::string, HeaderData*> _index;
422 };
423
424 class HttpResponse : public HttpHeader,public netplus::con {
425 public:
426 HttpResponse();
427 HttpResponse(const HttpResponse &src);
429
430 /*server methods*/
431 void setState(const std ::string &httpstate);
432 void setContentType(const std ::string &type);
433 void setContentLength(size_t len);
434 void setConnection(const std ::string &type);
435 void setTransferEncoding(const std ::string &enc);
436
437 /*client methods*/
438 const std ::string &getState() const;
439 int getStatusCode() const;
440 const std ::string &getContentType() const;
441 size_t getContentLength() const;
442 const std ::string &getConnection() const;
443 const std ::string &getVersion() const;
444 HttpHeader::HeaderData::Values *getTransferEncoding() const;
445
446 size_t printHeader(std::vector<char> &buffer);
447
448 /*server methods*/
449 void send(netplus::con &curconnection,const std::string &data,int datalen=0); //only use as server
450 void send(netplus::con &curconnection,const unsigned char *data,int datalen); //only use as server
451 void send(netplus::con &curconnection,const std::vector<char> &data,int datalen=0); //only use as server
452
453 // Outbound chunked-transfer-encoding streaming (HTTP/1.1 only -- caller
454 // is responsible for only using these on a connection confirmed to be
455 // HTTP/1.1; they don't go through _storeResponseInfo's H2/H3 path).
456 // Usage: sendChunkedHeaders() once, then sendChunk() any number of
457 // times as data becomes available, then endChunked() exactly once.
458 void sendChunkedHeaders(netplus::con &curconnection);
459 void sendChunk(netplus::con &curconnection, const char *data, size_t len);
460 void endChunked(netplus::con &curconnection);
461
462 /*client method*/
463 size_t parse(const char *in,size_t inlen);
464
465 // Ingest already-decoded HPACK/QPACK header fields directly (client
466 // side, HTTP/2 and HTTP/3). Avoids building a synthetic HTTP/1.1-style
467 // text block and reparsing it -- besides the redundant copy/reparse,
468 // that round-trip had no way to reject a header value containing an
469 // embedded "\r\n", letting a malicious/compromised peer inject extra
470 // header lines (including a premature blank line truncating the block).
471 // Returns the number of header fields ingested.
472 size_t parseH2(const std::vector<hpack::HeaderField> &headers);
473 size_t parseH3(const std::vector<qpack::HeaderField> &headers);
474
475 private:
476 bool _storeResponseInfo(netplus::con &curconnection, int datalen);
477
478 std::string _State=HTTP200;
479 std::string _Version;
480 int _StatusCode=200;
481 HeaderData *_TransferEncoding;
482 HeaderData *_Connection;
483 HeaderData *_ContentType;
484 HeaderData *_ContentLength;
485 mutable std::string _ContentTypeCache;
486 };
487
488
489 class HttpRequest : public HttpHeader, public netplus::con{
490 public:
491 HttpRequest();
492 HttpRequest(netplus::eventapi *evapi);
493 ~HttpRequest();
494
495 void clear();
496
497 /*server methods*/
498
499 size_t parse(); //only use as server
500
501 /*protocol-specific parse helpers (all store into _firstHeaderData)*/
502 size_t parseH2(const std::vector<hpack::HeaderField> &headers, uint32_t stream_id = 0);
503 size_t parseH3(const std::vector<qpack::HeaderField> &headers);
504
505 void printHeader(std::string &buffer);
506 int getRequestType();
507 // Original method token as received (":method"), e.g. "PROPFIND" -- unlike
508 // getRequestType() this survives methods unknown to RequestTypeFromMethod.
509 std::string getRequestMethod();
510 const std::string &getRequestURL();
511 const std::string &getRequest();
512 size_t getRequestLength();
513 const std::string &getRequestVersion();
514 const std::string &getHost();
515 size_t getContentLength();
516 size_t getMaxUploadSize();
517
518 /* HTTP/1.1 chunked request-body support (server side).
519 * Traefik and other reverse proxies downgrade HTTP/2 POSTs to HTTP/1.1
520 * using Transfer-Encoding: chunked (no Content-Length). These helpers let
521 * the server loop de-chunk the body before dispatching RequestEvent. */
522 bool isChunkedRequest();
523 int decodeChunkedBody();
524
525 /*mobilphone switch*/
526 bool isMobile();
527
528 /* Per-stream HTTP/2 and HTTP/3 requests are dispatched on a fresh,
529 * unconnected HttpRequest (see HttpEvent::_dispatchH2Stream/H3 in
530 * httpd.cpp) since one physical connection multiplexes many streams --
531 * that object's `slots` is always empty, so callers that need the real
532 * peer address (e.g. a reverse proxy building X-Forwarded-For) can't
533 * get it from `con::slots` the way an HTTP/1.1 request's connection
534 * object provides it. The dispatcher stashes the address it already
535 * has (from the real connection's socket) here before calling
536 * RequestEvent(); callers should prefer this over slots[0].csock when
537 * it's non-empty. */
538 void setPeerAddress(const std::string &addr);
539 const std::string &getPeerAddress() const;
540
541 /*Client methods*/
542 void setRequestType(int req);
543 void setRequestURL(const std::string &url);
544 void setRequestVersion(const std::string &version);
545 /*only for post Reuquesttype*/
546 void setRequestData(const std::string &data,size_t len);
547 void setMaxUploadSize(size_t upsize);
548
549 void send(const HttpUrl &dest,std::unique_ptr<netplus::socket> &sock);
550
551 private:
552 size_t parseH1(); // HTTP/1.x request parsing
553
554 /*
555 * Helper: extracts URL path from :path header (strips query string).
556 * Used by getRequestURL() and parse helpers.
557 */
558 static std::string extractPath(const std::string &target);
559
560 int _RequestType = PARSEREQUEST;
561 size_t _MaxUploadSize = DEFAULT_UPLOADSIZE;
562
563 // Cached strings derived from _firstHeaderData pseudo-headers.
564 // Populated by parseH1/parseH2/parseH3, read by getters.
565 mutable std::string _cachedRequestURL;
566 mutable std::string _cachedRequest;
567 mutable std::string _cachedRequestVersion;
568 mutable std::string _cachedHost;
569 std::string _peerAddress;
570
571 // HTTP/2 and HTTP/3 protocol state (managed by HttpEvent).
572 // All H2-specific mutable state lives in a heap-allocated struct so
573 // that inline-layout corruption of HttpRequest cannot trash the
574 // deque / map / decoder internals.
575 int _httpProtocol = 0; // 0=HTTP/1.x, 1=HTTP/2, 2=HTTP/3
576
577 struct H2PendingResponse {
578 uint32_t streamId;
579 std::string body; // remaining body data to send as DATA frames
580 size_t offset = 0; // how far into body we've sent
581 };
582
583 // Active streaming response state — lives on the connection's H2State
584 // so Http2RequestEvent can resume sending after WINDOW_UPDATE.
585 struct H2StreamingResponse {
586 uint32_t streamId = 0;
587 size_t contentLength = 0;
588 size_t totalSent = 0;
589 std::string pendingData; // buffered DATA not yet framed
590 size_t pendingOffset = 0;
591 std::unique_ptr<HttpRequest> tempreq; // per-stream request for ResponseEvent
592 int tid = 0;
593 ULONG_PTR args = 0;
594 size_t emptyCount = 0;
595 unsigned int backoffMs = 1;
596 bool finished = false;
597 // Set while totalSent < contentLength and the peer's flow-control
598 // window is the reason no DATA can go out (not an upstream stall).
599 // Reaped by _reapStalledH2Streams() if it stays true too long —
600 // guards against a peer that stops sending WINDOW_UPDATE entirely,
601 // which would otherwise leave the stream (and its tempreq) parked
602 // in activeStreams forever.
603 bool windowBlocked = false;
604 std::chrono::steady_clock::time_point blockedSince{};
605 };
606
607 struct H2PendingIncoming {
608 std::vector<hpack::HeaderField> headers;
609 std::string body;
610 std::vector<uint8_t> rawHpack; // accumulates HPACK across CONTINUATION frames
611 bool headersComplete = false; // true once END_HEADERS received
612 bool endStreamOnHeaders = false; // END_STREAM was on HEADERS frame
613 bool streaming = false; // body handled by onH2DataChunk callback
614 };
615
616 struct H2State {
617 uint32_t streamId = 0;
618 bool headersSent = false;
619 bool serverPrefaceSent = false;
620 size_t expectedContentLength = 0;
621 size_t bodyBytesSent = 0;
622 std::deque<H2PendingResponse> pendingResponses;
623 std::map<uint32_t, H2PendingIncoming> pendingIncoming;
624 hpack::Decoder hpackDecoder;
625 // Peer flow-control windows (RFC 7540 §6.9)
626 int32_t peerConnWindow = 65535; // connection-level
627 int32_t peerInitialStreamWindow = 65535; // from peer SETTINGS
628 size_t peerMaxFrameSize = 16384; // from peer SETTINGS_MAX_FRAME_SIZE (0x05)
629 std::map<uint32_t, int32_t> peerStreamWindows; // per-stream
630 // Active streaming responses (one per stream)
631 std::map<uint32_t, std::shared_ptr<H2StreamingResponse>> activeStreams;
632 };
633
634 // Lazily allocated when the connection is upgraded to HTTP/2.
635 std::unique_ptr<H2State> _h2;
636
637 // Allocate H2 state if not yet present; return reference.
638 H2State &h2state() {
639 if (!_h2) _h2 = std::make_unique<H2State>();
640 return *_h2;
641 }
642
643 friend class HttpForm;
644 friend class HttpResponse;
645 friend class HttpEvent;
646 };
647
648 class HttpForm {
649 public:
650 // ─── Multipart form-data (RFC 2046) ───────────────────
652 struct Header {
653 std::string key; // lowercased header name (e.g. "content-disposition")
654 std::string value; // full header value
655 };
656
657 struct Disposition {
658 std::string key; // e.g. "name", "filename"
659 std::string value; // e.g. "field1", "upload.txt"
660 };
661
662 std::vector<Header> headers;
663 std::vector<Disposition> dispositions;
664 std::vector<char> value; // raw body (binary-safe for file uploads)
665 };
666
667 // ─── URL-encoded form data ────────────────────────────
668 struct UrlEntry {
669 std::string key;
670 std::string value;
671 };
672
673 HttpForm() = default;
674 ~HttpForm() = default;
675
676 void parse(HttpRequest &request);
677
678 // Accessors
679 const std::string &getContentType() const { return _contentType; }
680 const std::string &getBoundary() const { return _boundary; }
681 const std::vector<MultipartEntry> &multipartData() const { return _multipartEntries; }
682 const std::vector<UrlEntry> &urlData() const { return _urlEntries; }
683
684 // URL encoding / decoding utilities
685 static void urlDecode(const std::string &in, std::string &out);
686 static void urlEncode(const std::string &in, std::string &out);
687
688 private:
689 void _parseMultipart(const char *data, size_t len);
690 void _parseMultiSection(const char *data, size_t len, size_t start, size_t end);
691 void _parseUrlDecode(const char *data, size_t len);
692
693 std::string _boundary;
694 std::string _contentType;
695 std::vector<MultipartEntry> _multipartEntries;
696 std::vector<UrlEntry> _urlEntries;
697 };
698
700 public:
702 public:
703 CookieData *nextCookieData() const;
704 const std::string &getKey() const;
705 const std::string &getValue() const;
706 CookieData()=default;
707 CookieData(const CookieData& src);
708 ~CookieData();
709 private:
710 std::string _Key;
711 std::string _Value;
712 std::unique_ptr <CookieData> _nextCookieData=nullptr;
713
714 friend class HttpCookie;
715 };
716 HttpCookie();
717 ~HttpCookie();
718 void parse(libhttppp::HttpRequest& curreq);
719 void setcookie(libhttppp::HttpResponse& curresp,
720 const std::string &key,const std::string &value,
721 const std::string &comment="",const std::string &domain="",
722 int maxage=-1,const std::string &path="",
723 bool secure=false,const std::string &version="1",const std::string &samesite="",bool httponly=false);
724 CookieData *getfirstCookieData();
725 CookieData *getlastCookieData();
726 CookieData *addCookieData();
727 private:
728 std::unique_ptr <CookieData> _firstCookieData;
729 CookieData *_lastCookieData;
730 };
731
732#define BASICAUTH 0
733#define DIGESTAUTH 1
734#define NTLMAUTH 2
735
736 class HttpAuth {
737 public:
738 HttpAuth();
739 ~HttpAuth();
740 void parse(libhttppp::HttpRequest &curreq);
741 void setAuth(libhttppp::HttpResponse &curresp);
742
743 void setAuthType(int authtype);
744 void setRealm(const std::string &realm);
745 void setUsername(const std::string &username);
746 void setPassword(const std::string &password);
747
748 const std::string &getUsername();
749 const std::string &getPassword();
750 int getAuthType();
751 const std::string &getAuthRequest();
752
753 private:
754 int _Authtype;
755 std::string _Username;
756 std::string _Password;
757 std::string _Realm;
758 std::string _Nonce;
759
760 };
761};
Definition https.h:38
Definition http.h:736
Definition http.h:111
Definition http.h:699
Definition httpd.h:51
Definition http.h:648
Definition http.h:336
Definition http.h:489
Definition http.h:424
Definition http.h:61
Definition hpack.h:68
Definition http.h:668