/*******************************************************************************
Copyright (c) 2014, Jan Koester jan.koester@gmx.net
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in the
      documentation and/or other materials provided with the distribution.
    * Neither the name of the <organization> nor the
      names of its contributors may be used to endorse or promote products
      derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*******************************************************************************/

#include <stddef.h>
#include <sys/types.h>

#include <vector>
#include <string>
#include <memory>
#include <deque>
#include <map>
#include <unordered_map>
#include <chrono>

#include <netplus/socket.h>
#include <netplus/connection.h>
#include <netplus/eventapi.h>
#include <netplus/crypto/tls.h>

#include "config.h"

#include "httpdefinitions.h"
#include "hpack.h"

#pragma once

namespace libhttppp::qpack { struct HeaderField; }

namespace libhttppp {
  class HttpRequest;

  class HttpUrl {
  public:
    enum HttpProtocol{
        HTTP=0,
        HTTPS=1,
        HTTP3=2
    };

     HttpUrl();
     HttpUrl(const std::string &url,bool http3=false);
     HttpUrl(const HttpUrl &src);
     HttpUrl& operator=(const HttpUrl &src) = default;
     ~HttpUrl();

     bool operator==(const HttpUrl& other) const;
     bool operator<(const HttpUrl& other) const;

     int getProtocol() const;
     // The scheme (HTTP or HTTPS) as it actually appeared in the URL string,
     // unaffected by the http3 ctor flag -- getProtocol() returns HTTP3 once
     // that flag is set, which loses the info needed to fall back correctly
     // when HTTP/3 isn't available.
     int getOriginalProtocol() const;
     const std::string &getHost() const;
     int getPort() const;
     const std::string &getPath() const;

     void clear();

     std::string print() const;

  private:
      int            _protocol;
      int            _origProtocol;
      std::string _host;
      int             _port;
      std::string _path;
  };

  class HttpResponse;

  class HttpClient{
  public:
      // timeoutSec bounds the constructor's own eager connection attempt
      // (see resetConnection()) in addition to being the initial value
      // setTimeout() would otherwise set afterward -- too late to affect
      // that first connect.
      HttpClient( const HttpUrl &desturl, int vers = 2, int timeoutSec = 60);
      ~HttpClient()=default;
      void reconnect();
      void setTimeout(int timeout_sec);
      const std::vector<char> Get(HttpRequest &nreq, size_t maxTries=0);
      const std::vector<char> Post(HttpRequest &nreq,const std::vector<char> &post, size_t maxTries=0);
      const std::vector<char> Put(HttpRequest &nreq,const std::vector<char> &put, size_t maxTries=0);
      const std::vector<char> Delete(HttpRequest &nreq, size_t maxTries=0);

      // Streaming API: send request, return parsed response headers only.
      // After this call, use readBodyChunk() to read body data incrementally.
      HttpResponse GetStream(HttpRequest &nreq);

      // Same as GetStream, but POSTs a body first (e.g. an OpenAI-compatible
      // "stream": true chat-completions request) before entering streaming-
      // read mode. HTTP/1.1 upstream connections only -- throws HTTPException
      // for HTTP/2 or HTTP/3 connections (no redirect handling either, unlike
      // Post()); local LLM inference backends are plain HTTP/1.1 services, so
      // this deliberately doesn't replicate Post()'s H2/H3/redirect handling.
      HttpResponse PostStream(HttpRequest &nreq, const std::vector<char> &postBody);

      // Read the next chunk of body data (up to bufsize bytes).
      // Returns number of bytes written to buf, 0 when body is complete.
      size_t readBodyChunk(char *buf, size_t bufsize);

      // Non-blocking variant: returns 0 immediately if no data available yet.
      // Returns (size_t)-1 when stream is complete (no more data will come).
      size_t readBodyChunkNonBlocking(char *buf, size_t bufsize);

      // True while a streaming read is in progress.
      bool isStreaming() const;

      // Wait until upstream socket has data to read (or timeout expires).
      // timeout_ms: -1 = infinite, 0 = return immediately, >0 = milliseconds
      // Returns true if readable, false on timeout.
      bool waitReadable(int timeout_ms);

      // Override the maximum number of redirects to follow (default: 5).
      // Set to 0 to disable automatic redirect following.
      void setMaxRedirects(int max) { _maxRedirects = max; }

      // Returns the HTTP status code from the last response.
      int lastStatusCode() const { return _lastStatusCode; }

      // Returns the Content-Type from the last response.
      const std::string &lastContentType() const { return _lastContentType; }

      // Returns the full parsed response from the last request.
      const HttpResponse *lastResponse() const { return _lastResponse.get(); }

      // Drop the current connection so the next request opens a fresh one.
      void resetConnection();

      // Shared TLS session cache — enables abbreviated TLS 1.2 handshakes
      // across reconnects to the same host.  One cache per process.
      static netplus::TlsSessionCache& tlsSessionCache();
  private:
      void _ensureConnected();
      // Thin wrapper around netplus::tcp::connectTimeout() using
      // _recvTimeoutSec, translating its NetException into HTTPException.
      void _connectTcp(netplus::tcp &sock);
      bool tryHttp3First();

      // Non-blocking I/O helpers using poll() for efficient waiting
      size_t _recvBlocking(netplus::buffer &b, int timeout_sec = 0);
      // Returns 0 on EAGAIN (no data yet), otherwise bytes read
      size_t _recvNonBlocking(netplus::buffer &b);
      void _sendAll(const char *data, size_t len);
      void _sendAll(const std::string &data);

      // Shared HTTP/1.x response reader (avoids code duplication)
      std::vector<char> _h1ReadResponse(const std::string &label);

      // Shared blocking/non-blocking body readers for STREAM_CHUNKED and
      // STREAM_EOF (used by both readBodyChunk() and
      // readBodyChunkNonBlocking() -- a single implementation parameterized
      // by `blocking` instead of two independently-maintained copies, the
      // second of which used to not exist at all and silently fell back to
      // the blocking one). Return value matches readBodyChunkNonBlocking's
      // contract: 0 = no data yet (blocking=false only) or a benign empty
      // read, (size_t)-1 = body complete, otherwise bytes written to buf.
      bool _streamRecvMore(bool blocking, bool &eof);
      size_t _streamReadChunked(char *buf, size_t bufsize, bool blocking);
      size_t _streamReadEof(char *buf, size_t bufsize, bool blocking);

      // Same idea for STREAM_H2: readBodyChunk() and
      // readBodyChunkNonBlocking() previously carried two independently
      // hand-copied H2 frame-dispatch switches, which had already drifted
      // (the non-blocking copy had gained an explicit
      // H2C_FRAME_WINDOW_UPDATE case the blocking one lacked). The frame
      // dispatch itself is now written once; only how bytes are pulled off
      // the wire (wait vs. one non-blocking attempt) differs by `blocking`.
      size_t _streamReadH2(char *buf, size_t bufsize, bool blocking);

      // Same idea for STREAM_H3: the QUIC varint frame-parsing loop (partial
      // DATA-frame continuation handling included) was duplicated verbatim
      // between readBodyChunk() and readBodyChunkNonBlocking(); now written
      // once and shared, with only the "wait for more data vs. try once and
      // return" difference kept per-mode.
      size_t _streamReadH3(char *buf, size_t bufsize, bool blocking);

      // Shared Get/Post/Put/Delete implementation: builds+sends the request
      // over whichever transport is active (H1/H2/H3), follows redirects
      // per RFC 7231 §6.4 (303 always converts to a bodyless GET; 307/308
      // preserve the original method) when followRedirects is set, and
      // retries up to maxTries times on transport errors.
      const std::vector<char> _doH1Request(const std::string &method, int requestType,
                                            HttpRequest &nreq, const std::vector<char> *body,
                                            size_t maxTries, bool followRedirects);

      // HTTP/2 client helpers
      bool _isH2 = false;
      bool _isH3 = false;                // cached in place of repeated dynamic_cast<quic*>
      bool _h2PrefaceSent = false;       // true after connection preface sent

      // HTTP/3 client helpers
      uint32_t _h2NextStreamId = 1;     // next client-initiated stream ID (odd)
      std::unique_ptr<hpack::Decoder> _h2Decoder; // persistent HPACK decoder for connection
      const std::vector<char> _h2Request(const std::string &method,
                                          HttpRequest &nreq,
                                          const std::vector<char> *postBody = nullptr);
      const std::vector<char> _h3Request(const std::string &method,
                  HttpRequest &nreq,
                  const std::vector<char> *postBody = nullptr);

      // Streaming state
      enum StreamMode { STREAM_NONE, STREAM_CONTENT_LENGTH, STREAM_CHUNKED, STREAM_EOF,
                        STREAM_H2, STREAM_H3 };
      StreamMode _streamMode = STREAM_NONE;
      size_t _streamRemaining = 0;        // bytes left for content-length mode
      std::vector<char> _streamBuf;       // leftover data from header read
      size_t _streamBufPos = 0;
      // Chunked streaming sub-state
      bool _streamChunkDone = false;      // true after final 0-length chunk
      size_t _streamChunkRemaining = 0;   // bytes left in current chunk

      // HTTP/2 streaming state
      uint32_t _streamH2Sid = 0;          // stream ID for active H2 stream
      bool     _streamH2EndStream = false;
      std::vector<uint8_t> _streamH2Raw; // raw frame buffer

      // HTTP/3 streaming state
      uint64_t _streamH3Sid = 0;          // stream ID for active H3 stream
      bool     _streamH3EndStream = false;
      std::vector<uint8_t> _streamH3Raw; // raw frame buffer
      std::vector<char> _streamH3Body;   // decoded DATA frames not yet consumed
      size_t   _streamH3BodyPos = 0;        // consumed offset into _streamH3Body
      bool     _streamH3InDataFrame = false; // true when inside a partial DATA frame
      uint64_t _streamH3DataRemaining = 0;  // bytes left in current DATA frame

  private:
      HttpUrl _url;
      std::unique_ptr<netplus::socket> _cltsock;
      netplus::socketwait _sw;
      netplus::x509cert _cert;
      // Reused across every recv() call in this object's lifetime instead of
      // constructing a fresh netplus::buffer per read: the Linux buffer ctor
      // value-initializes (zero-fills) the whole CHUNKSIZE (64KB) allocation,
      // so a fresh one per network read cost an alloc+memset+free even when
      // only a few KB actually arrived. Safe to share across all call sites
      // since each one copies out exactly the bytes recvData() reports before
      // doing anything else with the buffer (nothing relies on unwritten
      // bytes being zero, and HttpClient is not used from multiple threads
      // concurrently -- _cltsock/_streamMode etc. already assume that).
      netplus::buffer _recvScratch{CHUNKSIZE};
      int _recvTimeoutSec = 60;
      int _sendTimeoutSec = 30;
      int _vers = 2;  // HTTP version preference (0=h1 only, 1=h1+h2, 2=h2 preferred, 3=h3, 4=internal h2-only probe)

      // Response tracking (populated by _h1ReadResponse / _h2Request / _h3Request)
      int _lastStatusCode = 0;
      std::string _lastLocation;
      std::string _lastContentType;
      std::unique_ptr<HttpResponse> _lastResponse;
      static constexpr int MAX_REDIRECTS = 5;
      int _maxRedirects = MAX_REDIRECTS;
  };


  class HttpHeader {
  public:
    class HeaderData{
    public:
    class Values{
      public:
        Values &operator=(const std::string &val);
        Values &operator=(size_t val);
        Values &operator=(int val);
        Values& operator=(const Values &val);

        Values &operator<<(const std::string &value);
        Values &operator<<(size_t value);
        Values &operator<<(int value);

        const std::string &getvalue();
        int          getIntvalue();
        size_t       getSizetValue();

        Values      *nextvalue();
        Values(const std::string& val);
        Values(const Values& val);
        Values()=default;
        ~Values() = default;
      private:
        std::string  _value;
        std::unique_ptr<Values> _nextvalue=nullptr;
        friend class HeaderData;
      };

      Values *getfirstValue();
      Values &at(int pos);
      Values &operator[](int pos);

      void push_back(const Values &val);
      void push_back(const std::string &val);
      void push_back(const char* val);
      void push_back(size_t val);
      void push_back(int val);

      bool empty();

      void erase(int pos);

      void clear();

      const std ::string &getkey();

      HeaderData *nextHeaderData();
      HeaderData(const std ::string &key);
      ~HeaderData() = default;
    private:
      std::string  _Key;
      std::unique_ptr<Values> _firstValue=nullptr;
      Values      *_lastValue=nullptr;
      std::unique_ptr<HeaderData>  _nextHeaderData=nullptr;
      friend class HttpHeader;
    };
    
    HeaderData         *getfirstHeaderData();
    HeaderData         *getHeaderData(const std ::string &key) const;
    HeaderData         *setHeaderData(const std ::string &key);

    void             deldata(const std ::string &key);
    void             deldata(HeaderData*pos);
    
    size_t      getElements();
    size_t      getHeaderSize();

    void        clear();
  protected:
    HttpHeader();
    virtual ~HttpHeader()=default;
    std::unique_ptr<HeaderData> _firstHeaderData;
    HeaderData *_lastHeaderData;
  private:
    // O(1)-average lookup by key, keyed exactly like the old linear scan
    // compared (raw key string, case-sensitive -- callers already always
    // pass pre-lowercased keys; HeaderData's own ctor separately lowercases
    // _Key for storage). The linked list above remains the source of truth
    // for iteration order and node lifetime/address stability (HeaderData*
    // handed out to callers, e.g. HttpResponse's cached _ContentLength etc.,
    // must stay valid -- nodes are never moved, only the index pointing at
    // them changes), this is purely a lookup accelerator kept in sync by
    // setHeaderData()/deldata()/clear().
    std::unordered_map<std::string, HeaderData*> _index;
  };
  
  class HttpResponse : public HttpHeader,public netplus::con {
  public:
    HttpResponse();
    HttpResponse(const HttpResponse &src);
    ~HttpResponse();

    /*server methods*/
    void   setState(const std ::string &httpstate);
    void   setContentType(const std ::string &type);
    void   setContentLength(size_t len);
    void   setConnection(const std ::string &type);
    void   setTransferEncoding(const std ::string &enc);

    /*client methods*/
    const std ::string &getState() const;
    int                getStatusCode() const;
    const std ::string &getContentType() const;
    size_t              getContentLength() const;
    const std ::string &getConnection() const;
    const std ::string &getVersion() const;
    HttpHeader::HeaderData::Values *getTransferEncoding() const;

    size_t printHeader(std::vector<char> &buffer);

    /*server methods*/
    void send(netplus::con &curconnection,const std::string &data,int datalen=0); //only use as server
    void send(netplus::con &curconnection,const unsigned  char *data,int datalen); //only use as server
    void send(netplus::con &curconnection,const std::vector<char> &data,int datalen=0); //only use as server

    // Outbound chunked-transfer-encoding streaming (HTTP/1.1 only -- caller
    // is responsible for only using these on a connection confirmed to be
    // HTTP/1.1; they don't go through _storeResponseInfo's H2/H3 path).
    // Usage: sendChunkedHeaders() once, then sendChunk() any number of
    // times as data becomes available, then endChunked() exactly once.
    void sendChunkedHeaders(netplus::con &curconnection);
    void sendChunk(netplus::con &curconnection, const char *data, size_t len);
    void endChunked(netplus::con &curconnection);

    /*client method*/
    size_t   parse(const char *in,size_t inlen);

    // Ingest already-decoded HPACK/QPACK header fields directly (client
    // side, HTTP/2 and HTTP/3). Avoids building a synthetic HTTP/1.1-style
    // text block and reparsing it -- besides the redundant copy/reparse,
    // that round-trip had no way to reject a header value containing an
    // embedded "\r\n", letting a malicious/compromised peer inject extra
    // header lines (including a premature blank line truncating the block).
    // Returns the number of header fields ingested.
    size_t parseH2(const std::vector<hpack::HeaderField> &headers);
    size_t parseH3(const std::vector<qpack::HeaderField> &headers);

  private:
    bool _storeResponseInfo(netplus::con &curconnection, int datalen);

    std::string      _State=HTTP200;
    std::string      _Version;
    int              _StatusCode=200;
    HeaderData      *_TransferEncoding;
    HeaderData      *_Connection;
    HeaderData      *_ContentType;
    HeaderData      *_ContentLength;
    mutable std::string _ContentTypeCache;
  };
  
 
  class HttpRequest : public HttpHeader, public netplus::con{
  public:
    HttpRequest();
    HttpRequest(netplus::eventapi *evapi);
    ~HttpRequest();

    void clear();

    /*server methods*/

    size_t                    parse(); //only use as server

    /*protocol-specific parse helpers (all store into _firstHeaderData)*/
    size_t parseH2(const std::vector<hpack::HeaderField> &headers, uint32_t stream_id = 0);
    size_t parseH3(const std::vector<qpack::HeaderField> &headers);

    void                      printHeader(std::string &buffer);
    int                         getRequestType();
    const std::string &getRequestURL();
    const std::string &getRequest();
    size_t                    getRequestLength();
    const std::string &getRequestVersion();
    const std::string &getHost();
    size_t                    getContentLength();
    size_t                    getMaxUploadSize();

    /* HTTP/1.1 chunked request-body support (server side).
     * Traefik and other reverse proxies downgrade HTTP/2 POSTs to HTTP/1.1
     * using Transfer-Encoding: chunked (no Content-Length). These helpers let
     * the server loop de-chunk the body before dispatching RequestEvent. */
    bool                      isChunkedRequest();
    int                       decodeChunkedBody();

    /*mobilphone switch*/
    bool isMobile();

    /*Client methods*/
    void              setRequestType(int req);
    void              setRequestURL(const std::string &url);
    void              setRequestVersion(const std::string &version);
    /*only for post Reuquesttype*/
    void              setRequestData(const std::string &data,size_t len);
    void              setMaxUploadSize(size_t upsize);

    void              send(const HttpUrl &dest,std::unique_ptr<netplus::socket> &sock);

  private:
    size_t            parseH1(); // HTTP/1.x request parsing

    /*
     * Helper: extracts URL path from :path header (strips query string).
     * Used by getRequestURL() and parse helpers.
     */
    static std::string extractPath(const std::string &target);

    int               _RequestType = PARSEREQUEST;
    size_t            _MaxUploadSize = DEFAULT_UPLOADSIZE;

    // Cached strings derived from _firstHeaderData pseudo-headers.
    // Populated by parseH1/parseH2/parseH3, read by getters.
    mutable std::string _cachedRequestURL;
    mutable std::string _cachedRequest;
    mutable std::string _cachedRequestVersion;
    mutable std::string _cachedHost;

    // HTTP/2 and HTTP/3 protocol state (managed by HttpEvent).
    // All H2-specific mutable state lives in a heap-allocated struct so
    // that inline-layout corruption of HttpRequest cannot trash the
    // deque / map / decoder internals.
    int               _httpProtocol = 0;  // 0=HTTP/1.x, 1=HTTP/2, 2=HTTP/3

    struct H2PendingResponse {
      uint32_t    streamId;
      std::string body;       // remaining body data to send as DATA frames
      size_t      offset = 0; // how far into body we've sent
    };

    // Active streaming response state — lives on the connection's H2State
    // so Http2RequestEvent can resume sending after WINDOW_UPDATE.
    struct H2StreamingResponse {
      uint32_t    streamId = 0;
      size_t      contentLength = 0;
      size_t      totalSent = 0;
      std::string pendingData;     // buffered DATA not yet framed
      size_t      pendingOffset = 0;
      std::unique_ptr<HttpRequest> tempreq; // per-stream request for ResponseEvent
      int         tid = 0;
      ULONG_PTR   args = 0;
      size_t      emptyCount = 0;
      unsigned int backoffMs = 1;
      bool        finished = false;
      // Set while totalSent < contentLength and the peer's flow-control
      // window is the reason no DATA can go out (not an upstream stall).
      // Reaped by _reapStalledH2Streams() if it stays true too long —
      // guards against a peer that stops sending WINDOW_UPDATE entirely,
      // which would otherwise leave the stream (and its tempreq) parked
      // in activeStreams forever.
      bool        windowBlocked = false;
      std::chrono::steady_clock::time_point blockedSince{};
    };

    struct H2PendingIncoming {
      std::vector<hpack::HeaderField> headers;
      std::string body;
      std::vector<uint8_t> rawHpack;      // accumulates HPACK across CONTINUATION frames
      bool headersComplete = false;       // true once END_HEADERS received
      bool endStreamOnHeaders = false;    // END_STREAM was on HEADERS frame
      bool streaming = false;             // body handled by onH2DataChunk callback
    };

    struct H2State {
      uint32_t          streamId = 0;
      bool              headersSent = false;
      bool              serverPrefaceSent = false;
      size_t            expectedContentLength = 0;
      size_t            bodyBytesSent = 0;
      std::deque<H2PendingResponse>            pendingResponses;
      std::map<uint32_t, H2PendingIncoming>    pendingIncoming;
      hpack::Decoder                           hpackDecoder;
      // Peer flow-control windows (RFC 7540 §6.9)
      int32_t peerConnWindow = 65535;     // connection-level
      int32_t peerInitialStreamWindow = 65535; // from peer SETTINGS
      size_t  peerMaxFrameSize = 16384;        // from peer SETTINGS_MAX_FRAME_SIZE (0x05)
      std::map<uint32_t, int32_t> peerStreamWindows; // per-stream
      // Active streaming responses (one per stream)
      std::map<uint32_t, std::shared_ptr<H2StreamingResponse>> activeStreams;
    };

    // Lazily allocated when the connection is upgraded to HTTP/2.
    std::unique_ptr<H2State> _h2;

    // Allocate H2 state if not yet present; return reference.
    H2State &h2state() {
      if (!_h2) _h2 = std::make_unique<H2State>();
      return *_h2;
    }

    friend class HttpForm;
    friend class HttpResponse;
    friend class HttpEvent;
  };
  
  class HttpForm {
  public:
    // ─── Multipart form-data (RFC 2046) ───────────────────
    struct MultipartEntry {
      struct Header {
        std::string key;    // lowercased header name (e.g. "content-disposition")
        std::string value;  // full header value
      };

      struct Disposition {
        std::string key;    // e.g. "name", "filename"
        std::string value;  // e.g. "field1", "upload.txt"
      };

      std::vector<Header>      headers;
      std::vector<Disposition> dispositions;
      std::vector<char>        value;  // raw body (binary-safe for file uploads)
    };

    // ─── URL-encoded form data ────────────────────────────
    struct UrlEntry {
      std::string key;
      std::string value;
    };

    HttpForm() = default;
    ~HttpForm() = default;

    void parse(HttpRequest &request);

    // Accessors
    const std::string                  &getContentType() const { return _contentType; }
    const std::string                  &getBoundary()    const { return _boundary; }
    const std::vector<MultipartEntry>  &multipartData()  const { return _multipartEntries; }
    const std::vector<UrlEntry>        &urlData()        const { return _urlEntries; }

    // URL encoding / decoding utilities
    static void urlDecode(const std::string &in, std::string &out);
    static void urlEncode(const std::string &in, std::string &out);

  private:
    void _parseMultipart(const char *data, size_t len);
    void _parseMultiSection(const char *data, size_t len, size_t start, size_t end);
    void _parseUrlDecode(const char *data, size_t len);

    std::string                  _boundary;
    std::string                  _contentType;
    std::vector<MultipartEntry>  _multipartEntries;
    std::vector<UrlEntry>        _urlEntries;
  };

  class HttpCookie {
  public:
    class CookieData {
    public:
      CookieData *nextCookieData() const;
      const std::string &getKey() const;
      const std::string &getValue() const;
      CookieData()=default;
	  CookieData(const CookieData& src);
      ~CookieData();
    private:
      std::string _Key;
      std::string _Value;
      std::unique_ptr <CookieData> _nextCookieData=nullptr;

      friend class HttpCookie;
    };
    HttpCookie();
    ~HttpCookie();
    void parse(libhttppp::HttpRequest& curreq);
    void setcookie(libhttppp::HttpResponse& curresp,
                   const std::string &key,const std::string &value,
                   const std::string &comment="",const std::string &domain="",
                   int maxage=-1,const std::string &path="",
                   bool secure=false,const std::string &version="1",const std::string &samesite="",bool httponly=false);
    CookieData    *getfirstCookieData();
    CookieData    *getlastCookieData();
    CookieData    *addCookieData();
  private:
    std::unique_ptr <CookieData> _firstCookieData;
    CookieData     *_lastCookieData;
  };

#define BASICAUTH  0
#define DIGESTAUTH 1
#define NTLMAUTH   2
  
  class HttpAuth {
  public:
    HttpAuth();
    ~HttpAuth();
    void        parse(libhttppp::HttpRequest &curreq);
    void        setAuth(libhttppp::HttpResponse &curresp);
    
    void        setAuthType(int authtype);
    void        setRealm(const std::string &realm);
    void        setUsername(const std::string &username);
    void        setPassword(const std::string &password);
    
    const std::string &getUsername();
    const std::string &getPassword();
    int         getAuthType();
    const std::string &getAuthRequest();
    
  private:
    int             _Authtype;
    std::string _Username;
    std::string _Password;
    std::string _Realm;
    std::string _Nonce;
    
  };
};
