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