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 <chrono>
37
38#include <netplus/socket.h>
39#include <netplus/connection.h>
40#include <netplus/eventapi.h>
41#include <netplus/crypto/tls.h>
42
43#include "config.h"
44
45#include "httpdefinitions.h"
46#include "hpack.h"
47
48#pragma once
49
50namespace libhttppp::qpack { struct HeaderField; }
51
52namespace libhttppp {
53 class HttpRequest;
54
55 class HttpUrl {
56 public:
57 enum HttpProtocol{
58 HTTP=0,
59 HTTPS=1,
60 HTTP3=2
61 };
62
63 HttpUrl();
64 HttpUrl(const std::string &url,bool http3=false);
65 HttpUrl(const HttpUrl &src);
66 HttpUrl& operator=(const HttpUrl &src) = default;
67 ~HttpUrl();
68
69 bool operator==(const HttpUrl& other) const;
70 bool operator<(const HttpUrl& other) const;
71
72 int getProtocol() const;
73 // The scheme (HTTP or HTTPS) as it actually appeared in the URL string,
74 // unaffected by the http3 ctor flag -- getProtocol() returns HTTP3 once
75 // that flag is set, which loses the info needed to fall back correctly
76 // when HTTP/3 isn't available.
77 int getOriginalProtocol() const;
78 const std::string &getHost() const;
79 int getPort() const;
80 const std::string &getPath() const;
81
82 void clear();
83
84 std::string print() const;
85
86 private:
87 int _protocol;
88 int _origProtocol;
89 std::string _host;
90 int _port;
91 std::string _path;
92 };
93
94 class HttpResponse;
95
97 public:
98 // timeoutSec bounds the constructor's own eager connection attempt
99 // (see resetConnection()) in addition to being the initial value
100 // setTimeout() would otherwise set afterward -- too late to affect
101 // that first connect.
102 HttpClient( const HttpUrl &desturl, int vers = 2, int timeoutSec = 60);
103 ~HttpClient()=default;
104 void reconnect();
105 void setTimeout(int timeout_sec);
106 const std::vector<char> Get(HttpRequest &nreq, size_t maxTries=0);
107 const std::vector<char> Post(HttpRequest &nreq,const std::vector<char> &post, size_t maxTries=0);
108 const std::vector<char> Put(HttpRequest &nreq,const std::vector<char> &put, size_t maxTries=0);
109 const std::vector<char> Delete(HttpRequest &nreq, size_t maxTries=0);
110
111 // Streaming API: send request, return parsed response headers only.
112 // After this call, use readBodyChunk() to read body data incrementally.
113 HttpResponse GetStream(HttpRequest &nreq);
114
115 // Same as GetStream, but POSTs a body first (e.g. an OpenAI-compatible
116 // "stream": true chat-completions request) before entering streaming-
117 // read mode. HTTP/1.1 upstream connections only -- throws HTTPException
118 // for HTTP/2 or HTTP/3 connections (no redirect handling either, unlike
119 // Post()); local LLM inference backends are plain HTTP/1.1 services, so
120 // this deliberately doesn't replicate Post()'s H2/H3/redirect handling.
121 HttpResponse PostStream(HttpRequest &nreq, const std::vector<char> &postBody);
122
123 // Read the next chunk of body data (up to bufsize bytes).
124 // Returns number of bytes written to buf, 0 when body is complete.
125 size_t readBodyChunk(char *buf, size_t bufsize);
126
127 // Non-blocking variant: returns 0 immediately if no data available yet.
128 // Returns (size_t)-1 when stream is complete (no more data will come).
129 size_t readBodyChunkNonBlocking(char *buf, size_t bufsize);
130
131 // True while a streaming read is in progress.
132 bool isStreaming() const;
133
134 // Wait until upstream socket has data to read (or timeout expires).
135 // timeout_ms: -1 = infinite, 0 = return immediately, >0 = milliseconds
136 // Returns true if readable, false on timeout.
137 bool waitReadable(int timeout_ms);
138
139 // Override the maximum number of redirects to follow (default: 5).
140 // Set to 0 to disable automatic redirect following.
141 void setMaxRedirects(int max) { _maxRedirects = max; }
142
143 // Returns the HTTP status code from the last response.
144 int lastStatusCode() const { return _lastStatusCode; }
145
146 // Returns the Content-Type from the last response.
147 const std::string &lastContentType() const { return _lastContentType; }
148
149 // Returns the full parsed response from the last request.
150 const HttpResponse *lastResponse() const { return _lastResponse.get(); }
151
152 // Drop the current connection so the next request opens a fresh one.
153 void resetConnection();
154
155 // Shared TLS session cache — enables abbreviated TLS 1.2 handshakes
156 // across reconnects to the same host. One cache per process.
157 static netplus::TlsSessionCache& tlsSessionCache();
158 private:
159 void _ensureConnected();
160 // Thin wrapper around netplus::tcp::connectTimeout() using
161 // _recvTimeoutSec, translating its NetException into HTTPException.
162 void _connectTcp(netplus::tcp &sock);
163 bool tryHttp3First();
164 int readchunk(const char *data,int datasize,int &pos);
165
166 // Non-blocking I/O helpers using poll() for efficient waiting
167 size_t _recvBlocking(netplus::buffer &b, int timeout_sec = 0);
168 // Returns 0 on EAGAIN (no data yet), otherwise bytes read
169 size_t _recvNonBlocking(netplus::buffer &b);
170 void _sendAll(const char *data, size_t len);
171 void _sendAll(const std::string &data);
172
173 // Shared HTTP/1.x response reader (avoids code duplication)
174 std::vector<char> _h1ReadResponse(const std::string &label);
175
176 // HTTP/2 client helpers
177 bool _isH2 = false;
178 bool _h2PrefaceSent = false; // true after connection preface sent
179
180 // HTTP/3 client helpers
181 uint32_t _h2NextStreamId = 1; // next client-initiated stream ID (odd)
182 std::unique_ptr<hpack::Decoder> _h2Decoder; // persistent HPACK decoder for connection
183 const std::vector<char> _h2Request(const std::string &method,
184 HttpRequest &nreq,
185 const std::vector<char> *postBody = nullptr);
186 const std::vector<char> _h3Request(const std::string &method,
187 HttpRequest &nreq,
188 const std::vector<char> *postBody = nullptr);
189
190 // Streaming state
191 enum StreamMode { STREAM_NONE, STREAM_CONTENT_LENGTH, STREAM_CHUNKED, STREAM_EOF,
192 STREAM_H2, STREAM_H3 };
193 StreamMode _streamMode = STREAM_NONE;
194 size_t _streamRemaining = 0; // bytes left for content-length mode
195 std::vector<char> _streamBuf; // leftover data from header read
196 size_t _streamBufPos = 0;
197 // Chunked streaming sub-state
198 bool _streamChunkDone = false; // true after final 0-length chunk
199 size_t _streamChunkRemaining = 0; // bytes left in current chunk
200
201 // HTTP/2 streaming state
202 uint32_t _streamH2Sid = 0; // stream ID for active H2 stream
203 bool _streamH2EndStream = false;
204 std::vector<uint8_t> _streamH2Raw; // raw frame buffer
205
206 // HTTP/3 streaming state
207 uint64_t _streamH3Sid = 0; // stream ID for active H3 stream
208 bool _streamH3EndStream = false;
209 std::vector<uint8_t> _streamH3Raw; // raw frame buffer
210 std::vector<char> _streamH3Body; // decoded DATA frames not yet consumed
211 size_t _streamH3BodyPos = 0; // consumed offset into _streamH3Body
212 bool _streamH3InDataFrame = false; // true when inside a partial DATA frame
213 uint64_t _streamH3DataRemaining = 0; // bytes left in current DATA frame
214
215 private:
216 HttpUrl _url;
217 std::unique_ptr<netplus::socket> _cltsock;
218 netplus::socketwait _sw;
219 netplus::x509cert _cert;
220 int _recvTimeoutSec = 60;
221 int _sendTimeoutSec = 30;
222 int _vers = 2; // HTTP version preference (0=h1 only, 1=h1+h2, 2=h2 preferred, 3=h3, 4=internal h2-only probe)
223
224 // Response tracking (populated by _h1ReadResponse / _h2Request / _h3Request)
225 int _lastStatusCode = 0;
226 std::string _lastLocation;
227 std::string _lastContentType;
228 std::unique_ptr<HttpResponse> _lastResponse;
229 static constexpr int MAX_REDIRECTS = 5;
230 int _maxRedirects = MAX_REDIRECTS;
231 };
232
233
235 public:
237 public:
238 class Values{
239 public:
240 Values &operator=(const std::string &val);
241 Values &operator=(size_t val);
242 Values &operator=(int val);
243 Values& operator=(const Values &val);
244
245 Values &operator<<(const std::string &value);
246 Values &operator<<(size_t value);
247 Values &operator<<(int value);
248
249 const std::string &getvalue();
250 int getIntvalue();
251 size_t getSizetValue();
252
253 Values *nextvalue();
254 Values(const std::string& val);
255 Values(const Values& val);
256 Values()=default;
257 ~Values() = default;
258 private:
259 std::string _value;
260 std::unique_ptr<Values> _nextvalue=nullptr;
261 friend class HeaderData;
262 };
263
264 Values *getfirstValue();
265 Values &at(int pos);
266 Values &operator[](int pos);
267
268 void push_back(const Values &val);
269 void push_back(const std::string &val);
270 void push_back(const char* val);
271 void push_back(size_t val);
272 void push_back(int val);
273
274 bool empty();
275
276 void erase(int pos);
277
278 void clear();
279
280 const std ::string &getkey();
281
282 HeaderData *nextHeaderData();
283 HeaderData(const std ::string &key);
284 ~HeaderData() = default;
285 private:
286 std::string _Key;
287 std::unique_ptr<Values> _firstValue=nullptr;
288 Values *_lastValue=nullptr;
289 std::unique_ptr<HeaderData> _nextHeaderData=nullptr;
290 friend class HttpHeader;
291 };
292
293 HeaderData *getfirstHeaderData();
294 HeaderData *getHeaderData(const std ::string &key) const;
295 HeaderData *setHeaderData(const std ::string &key);
296
297 void deldata(const std ::string &key);
298 void deldata(HeaderData*pos);
299
300 size_t getElements();
301 size_t getHeaderSize();
302
303 void clear();
304 protected:
305 HttpHeader();
306 virtual ~HttpHeader()=default;
307 std::unique_ptr<HeaderData> _firstHeaderData;
308 HeaderData *_lastHeaderData;
309 };
310
311 class HttpResponse : public HttpHeader,public netplus::con {
312 public:
313 HttpResponse();
314 HttpResponse(const HttpResponse &src);
316
317 /*server methods*/
318 void setState(const std ::string &httpstate);
319 void setContentType(const std ::string &type);
320 void setContentLength(size_t len);
321 void setConnection(const std ::string &type);
322 void setTransferEncoding(const std ::string &enc);
323
324 /*client methods*/
325 const std ::string &getState() const;
326 int getStatusCode() const;
327 const std ::string &getContentType() const;
328 size_t getContentLength() const;
329 const std ::string &getConnection() const;
330 const std ::string &getVersion() const;
331 HttpHeader::HeaderData::Values *getTransferEncoding() const;
332
333 size_t printHeader(std::vector<char> &buffer);
334
335 /*server methods*/
336 void send(netplus::con &curconnection,const std::string &data,int datalen=0); //only use as server
337 void send(netplus::con &curconnection,const unsigned char *data,int datalen); //only use as server
338 void send(netplus::con &curconnection,const std::vector<char> &data,int datalen=0); //only use as server
339
340 // Outbound chunked-transfer-encoding streaming (HTTP/1.1 only -- caller
341 // is responsible for only using these on a connection confirmed to be
342 // HTTP/1.1; they don't go through _storeResponseInfo's H2/H3 path).
343 // Usage: sendChunkedHeaders() once, then sendChunk() any number of
344 // times as data becomes available, then endChunked() exactly once.
345 void sendChunkedHeaders(netplus::con &curconnection);
346 void sendChunk(netplus::con &curconnection, const char *data, size_t len);
347 void endChunked(netplus::con &curconnection);
348
349 /*client method*/
350 size_t parse(const char *in,size_t inlen);
351
352 private:
353 bool _storeResponseInfo(netplus::con &curconnection, int datalen);
354
355 std::string _State=HTTP200;
356 std::string _Version;
357 int _StatusCode=200;
358 HeaderData *_TransferEncoding;
359 HeaderData *_Connection;
360 HeaderData *_ContentType;
361 HeaderData *_ContentLength;
362 mutable std::string _ContentTypeCache;
363 };
364
365
366 class HttpRequest : public HttpHeader, public netplus::con{
367 public:
368 HttpRequest();
369 HttpRequest(netplus::eventapi *evapi);
370 ~HttpRequest();
371
372 void clear();
373
374 /*server methods*/
375
376 size_t parse(); //only use as server
377
378 /*protocol-specific parse helpers (all store into _firstHeaderData)*/
379 size_t parseH2(const std::vector<hpack::HeaderField> &headers, uint32_t stream_id = 0);
380 size_t parseH3(const std::vector<qpack::HeaderField> &headers);
381
382 void printHeader(std::string &buffer);
383 int getRequestType();
384 const std::string &getRequestURL();
385 const std::string &getRequest();
386 size_t getRequestLength();
387 const std::string &getRequestVersion();
388 const std::string &getHost();
389 size_t getContentLength();
390 size_t getMaxUploadSize();
391
392 /* HTTP/1.1 chunked request-body support (server side).
393 * Traefik and other reverse proxies downgrade HTTP/2 POSTs to HTTP/1.1
394 * using Transfer-Encoding: chunked (no Content-Length). These helpers let
395 * the server loop de-chunk the body before dispatching RequestEvent. */
396 bool isChunkedRequest();
397 int decodeChunkedBody();
398
399 /*mobilphone switch*/
400 bool isMobile();
401
402 /*Client methods*/
403 void setRequestType(int req);
404 void setRequestURL(const std::string &url);
405 void setRequestVersion(const std::string &version);
406 /*only for post Reuquesttype*/
407 void setRequestData(const std::string &data,size_t len);
408 void setMaxUploadSize(size_t upsize);
409
410 void send(const HttpUrl &dest,std::unique_ptr<netplus::socket> &sock);
411
412 private:
413 size_t parseH1(); // HTTP/1.x request parsing
414
415 /*
416 * Helper: extracts URL path from :path header (strips query string).
417 * Used by getRequestURL() and parse helpers.
418 */
419 static std::string extractPath(const std::string &target);
420
421 int _RequestType = PARSEREQUEST;
422 size_t _MaxUploadSize = DEFAULT_UPLOADSIZE;
423
424 // Cached strings derived from _firstHeaderData pseudo-headers.
425 // Populated by parseH1/parseH2/parseH3, read by getters.
426 mutable std::string _cachedRequestURL;
427 mutable std::string _cachedRequest;
428 mutable std::string _cachedRequestVersion;
429 mutable std::string _cachedHost;
430
431 // HTTP/2 and HTTP/3 protocol state (managed by HttpEvent).
432 // All H2-specific mutable state lives in a heap-allocated struct so
433 // that inline-layout corruption of HttpRequest cannot trash the
434 // deque / map / decoder internals.
435 int _httpProtocol = 0; // 0=HTTP/1.x, 1=HTTP/2, 2=HTTP/3
436
437 struct H2PendingResponse {
438 uint32_t streamId;
439 std::string body; // remaining body data to send as DATA frames
440 size_t offset = 0; // how far into body we've sent
441 };
442
443 // Active streaming response state — lives on the connection's H2State
444 // so Http2RequestEvent can resume sending after WINDOW_UPDATE.
445 struct H2StreamingResponse {
446 uint32_t streamId = 0;
447 size_t contentLength = 0;
448 size_t totalSent = 0;
449 std::string pendingData; // buffered DATA not yet framed
450 size_t pendingOffset = 0;
451 std::unique_ptr<HttpRequest> tempreq; // per-stream request for ResponseEvent
452 int tid = 0;
453 ULONG_PTR args = 0;
454 size_t emptyCount = 0;
455 unsigned int backoffMs = 1;
456 bool finished = false;
457 // Set while totalSent < contentLength and the peer's flow-control
458 // window is the reason no DATA can go out (not an upstream stall).
459 // Reaped by _reapStalledH2Streams() if it stays true too long —
460 // guards against a peer that stops sending WINDOW_UPDATE entirely,
461 // which would otherwise leave the stream (and its tempreq) parked
462 // in activeStreams forever.
463 bool windowBlocked = false;
464 std::chrono::steady_clock::time_point blockedSince{};
465 };
466
467 struct H2PendingIncoming {
468 std::vector<hpack::HeaderField> headers;
469 std::string body;
470 std::vector<uint8_t> rawHpack; // accumulates HPACK across CONTINUATION frames
471 bool headersComplete = false; // true once END_HEADERS received
472 bool endStreamOnHeaders = false; // END_STREAM was on HEADERS frame
473 bool streaming = false; // body handled by onH2DataChunk callback
474 };
475
476 struct H2State {
477 uint32_t streamId = 0;
478 bool headersSent = false;
479 bool serverPrefaceSent = false;
480 size_t expectedContentLength = 0;
481 size_t bodyBytesSent = 0;
482 std::deque<H2PendingResponse> pendingResponses;
483 std::map<uint32_t, H2PendingIncoming> pendingIncoming;
484 hpack::Decoder hpackDecoder;
485 // Peer flow-control windows (RFC 7540 §6.9)
486 int32_t peerConnWindow = 65535; // connection-level
487 int32_t peerInitialStreamWindow = 65535; // from peer SETTINGS
488 size_t peerMaxFrameSize = 16384; // from peer SETTINGS_MAX_FRAME_SIZE (0x05)
489 std::map<uint32_t, int32_t> peerStreamWindows; // per-stream
490 // Active streaming responses (one per stream)
491 std::map<uint32_t, std::shared_ptr<H2StreamingResponse>> activeStreams;
492 };
493
494 // Lazily allocated when the connection is upgraded to HTTP/2.
495 std::unique_ptr<H2State> _h2;
496
497 // Allocate H2 state if not yet present; return reference.
498 H2State &h2state() {
499 if (!_h2) _h2 = std::make_unique<H2State>();
500 return *_h2;
501 }
502
503 friend class HttpForm;
504 friend class HttpResponse;
505 friend class HttpEvent;
506 };
507
508 class HttpForm {
509 public:
510 // ─── Multipart form-data (RFC 2046) ───────────────────
512 struct Header {
513 std::string key; // lowercased header name (e.g. "content-disposition")
514 std::string value; // full header value
515 };
516
517 struct Disposition {
518 std::string key; // e.g. "name", "filename"
519 std::string value; // e.g. "field1", "upload.txt"
520 };
521
522 std::vector<Header> headers;
523 std::vector<Disposition> dispositions;
524 std::vector<char> value; // raw body (binary-safe for file uploads)
525 };
526
527 // ─── URL-encoded form data ────────────────────────────
528 struct UrlEntry {
529 std::string key;
530 std::string value;
531 };
532
533 HttpForm() = default;
534 ~HttpForm() = default;
535
536 void parse(HttpRequest &request);
537
538 // Accessors
539 const std::string &getContentType() const { return _contentType; }
540 const std::string &getBoundary() const { return _boundary; }
541 const std::vector<MultipartEntry> &multipartData() const { return _multipartEntries; }
542 const std::vector<UrlEntry> &urlData() const { return _urlEntries; }
543
544 // URL encoding / decoding utilities
545 static void urlDecode(const std::string &in, std::string &out);
546 static void urlEncode(const std::string &in, std::string &out);
547
548 private:
549 void _parseMultipart(const char *data, size_t len);
550 void _parseMultiSection(const char *data, size_t len, size_t start, size_t end);
551 void _parseUrlDecode(const char *data, size_t len);
552
553 std::string _boundary;
554 std::string _contentType;
555 std::vector<MultipartEntry> _multipartEntries;
556 std::vector<UrlEntry> _urlEntries;
557 };
558
560 public:
562 public:
563 CookieData *nextCookieData() const;
564 const std::string &getKey() const;
565 const std::string &getValue() const;
566 CookieData()=default;
567 CookieData(const CookieData& src);
568 ~CookieData();
569 private:
570 std::string _Key;
571 std::string _Value;
572 std::unique_ptr <CookieData> _nextCookieData=nullptr;
573
574 friend class HttpCookie;
575 };
576 HttpCookie();
577 ~HttpCookie();
578 void parse(libhttppp::HttpRequest& curreq);
579 void setcookie(libhttppp::HttpResponse& curresp,
580 const std::string &key,const std::string &value,
581 const std::string &comment="",const std::string &domain="",
582 int maxage=-1,const std::string &path="",
583 bool secure=false,const std::string &version="1",const std::string &samesite="",bool httponly=false);
584 CookieData *getfirstCookieData();
585 CookieData *getlastCookieData();
586 CookieData *addCookieData();
587 private:
588 std::unique_ptr <CookieData> _firstCookieData;
589 CookieData *_lastCookieData;
590 };
591
592#define BASICAUTH 0
593#define DIGESTAUTH 1
594#define NTLMAUTH 2
595
596 class HttpAuth {
597 public:
598 HttpAuth();
599 ~HttpAuth();
600 void parse(libhttppp::HttpRequest &curreq);
601 void setAuth(libhttppp::HttpResponse &curresp);
602
603 void setAuthType(int authtype);
604 void setRealm(const std::string &realm);
605 void setUsername(const std::string &username);
606 void setPassword(const std::string &password);
607
608 const std::string &getUsername();
609 const std::string &getPassword();
610 int getAuthType();
611 const std::string &getAuthRequest();
612
613 private:
614 int _Authtype;
615 std::string _Username;
616 std::string _Password;
617 std::string _Realm;
618 std::string _Nonce;
619
620 };
621};
Definition https.h:38
Definition http.h:596
Definition http.h:96
Definition http.h:559
Definition httpd.h:51
Definition http.h:508
Definition http.h:234
Definition http.h:366
Definition http.h:311
Definition http.h:55
Definition hpack.h:68
Definition http.h:528