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