Commit 2fb81acb authored by jan.koester's avatar jan.koester
Browse files

test

parent 50b3d69b
Loading
Loading
Loading
Loading
+11 −1
Original line number Diff line number Diff line
@@ -212,9 +212,12 @@ uint32_t Decoder::decodeInt(const uint8_t *data, size_t len, uint8_t prefix_bits
    uint32_t m = 0;
    while (bytes_read < len) {
        uint8_t b = data[bytes_read++];
        if (m < 32) {
            value += static_cast<uint32_t>(b & 0x7f) << m;
        }
        m += 7;
        if (!(b & 0x80)) return value;
        if (m >= 32) return value; // prevent overflow / runaway
    }
    return value;
}
@@ -302,6 +305,7 @@ std::vector<HeaderField> Decoder::decode(const uint8_t *data, size_t len) {
    size_t off = 0;

    while (off < len) {
        size_t prev_off = off;   // progress guard
        uint8_t byte = data[off];

        if (byte & 0x80) {
@@ -360,6 +364,12 @@ std::vector<HeaderField> Decoder::decode(const uint8_t *data, size_t len) {
        } else {
            ++off; // skip unknown
        }

        // Safety: if no byte was consumed this iteration, advance by one
        // to avoid an infinite loop on malformed input.
        if (off == prev_off) {
            ++off;
        }
    }
    return headers;
}
+10 −0
Original line number Diff line number Diff line
@@ -62,6 +62,16 @@ private:
class Decoder {
public:
    Decoder() = default;
    ~Decoder() = default;

    // Non-copyable / non-movable — the dynamic table must not be
    // accidentally shared or left in a moved-from state.
    Decoder(const Decoder &) = delete;
    Decoder &operator=(const Decoder &) = delete;
    Decoder(Decoder &&) = delete;
    Decoder &operator=(Decoder &&) = delete;

    void reset() { _dynTable.clear(); _dynTableSize = 0; }

    // Instance method: decode using (and updating) the dynamic table
    std::vector<HeaderField> decode(const uint8_t *data, size_t len);
+9 −8
Original line number Diff line number Diff line
@@ -1537,6 +1537,8 @@ libhttppp::HttpRequest::HttpRequest() : HttpHeader() {
  _MaxUploadSize=DEFAULT_UPLOADSIZE;
};

libhttppp::HttpRequest::~HttpRequest() = default;

libhttppp::HttpRequest::HttpRequest(netplus::eventapi *evapi) : HttpHeader(), con(evapi){
  _RequestType=PARSEREQUEST;
  _MaxUploadSize=DEFAULT_UPLOADSIZE;
@@ -1549,11 +1551,9 @@ void libhttppp::HttpRequest::clear(){
  _cachedRequest.clear();
  _cachedRequestVersion.clear();
  _httpProtocol = 0;
  _h2StreamId = 0;
  _h2HeadersSent = false;
  _h2ExpectedContentLength = 0;
  _h2BodyBytesSent = 0;
  _h2PendingResponses.clear();
  // Reset H2 state: release the heap block entirely so that
  // any corrupted deque/map internals are freed as a unit.
  _h2.reset();
}

size_t libhttppp::HttpRequest::parse() {
@@ -1599,9 +1599,10 @@ size_t libhttppp::HttpRequest::parseH2(const std::vector<hpack::HeaderField> &he
                                       uint32_t stream_id) {
  clear();
  _httpProtocol = 1;
  _h2StreamId = stream_id;
  _h2HeadersSent = false;
  _h2BodyBytesSent = 0;
  auto &h2 = h2state();
  h2.streamId = stream_id;
  h2.headersSent = false;
  h2.bodyBytesSent = 0;
  SendData.pos = 0;

  // Store ALL headers (including pseudo-headers) in _firstHeaderData.
+23 −12
Original line number Diff line number Diff line
@@ -223,7 +223,7 @@ namespace libhttppp {
  public:
    HttpRequest();
    HttpRequest(netplus::eventapi *evapi);
    ~HttpRequest()=default;
    ~HttpRequest();

    void clear();

@@ -275,30 +275,41 @@ namespace libhttppp {
    mutable std::string _cachedRequest;
    mutable std::string _cachedRequestVersion;

    // HTTP/2 and HTTP/3 protocol state (managed by HttpEvent)
    // 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
    uint32_t          _h2StreamId = 0;
    bool              _h2HeadersSent = false;
    size_t            _h2ExpectedContentLength = 0;
    size_t            _h2BodyBytesSent = 0;

    // Queue of pending H2 stream responses (body data waiting to be framed)
    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
    };
    std::deque<H2PendingResponse> _h2PendingResponses;

    // Per-connection buffer for incomplete incoming H2 streams (awaiting DATA)
    struct H2PendingIncoming {
      std::vector<hpack::HeaderField> headers;
      std::string body;
    };
    std::map<uint32_t, H2PendingIncoming> _h2PendingIncoming;

    // Per-connection HPACK decoder (maintains dynamic table across H2 frames)
    hpack::Decoder _h2HpackDecoder;
    struct H2State {
      uint32_t          streamId = 0;
      bool              headersSent = false;
      size_t            expectedContentLength = 0;
      size_t            bodyBytesSent = 0;
      std::deque<H2PendingResponse>            pendingResponses;
      std::map<uint32_t, H2PendingIncoming>    pendingIncoming;
      hpack::Decoder                           hpackDecoder;
    };

    // 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;
+10 −10
Original line number Diff line number Diff line
@@ -346,7 +346,7 @@ void libhttppp::HttpEvent::_dispatchH2Stream(HttpRequest &cureq,

        if (is_streaming) {
            // Streaming response: collect all data via ResponseEvent
            tempreq._h2ExpectedContentLength = content_length;
            tempreq.h2state().expectedContentLength = content_length;
            tempreq.SendData.pos = 0;

            size_t max_iterations = content_length / 64 + 4096;
@@ -418,7 +418,7 @@ void libhttppp::HttpEvent::_dispatchH2Stream(HttpRequest &cureq,
            pending.streamId = sid;
            pending.body = std::move(body);
            pending.offset = H2_MAX_FRAME_SIZE;
            cureq._h2PendingResponses.push_back(std::move(pending));
            cureq.h2state().pendingResponses.push_back(std::move(pending));
        }
    }
}
@@ -486,21 +486,21 @@ bool libhttppp::HttpEvent::Http2RequestEvent(netplus::con &curcon,

            // Decode HPACK headers using the connection's decoder
            // (maintains dynamic table state across frames)
            auto decoded = cureq._h2HpackDecoder.decode(hpack_data, hpack_len);
            auto decoded = cureq.h2state().hpackDecoder.decode(hpack_data, hpack_len);

            if (fflags & H2_FLAG_END_STREAM) {
                // No body expected (GET, HEAD, etc.) — dispatch immediately
                _dispatchH2Stream(cureq, out, sid, decoded, "", tid, args);
            } else {
                // Body expected via DATA frames (POST, PUT, etc.)
                cureq._h2PendingIncoming[sid] = {std::move(decoded), ""};
                cureq.h2state().pendingIncoming[sid] = {std::move(decoded), ""};
            }
            break;
        }

        case H2_FRAME_DATA: {
            auto it = cureq._h2PendingIncoming.find(sid);
            if (it != cureq._h2PendingIncoming.end()) {
            auto it = cureq.h2state().pendingIncoming.find(sid);
            if (it != cureq.h2state().pendingIncoming.end()) {
                it->second.body.append(data + off, flen);
                if (fflags & H2_FLAG_END_STREAM) {
                    // All body data received — dispatch the request
@@ -508,7 +508,7 @@ bool libhttppp::HttpEvent::Http2RequestEvent(netplus::con &curcon,
                                      it->second.headers,
                                      it->second.body,
                                      tid, args);
                    cureq._h2PendingIncoming.erase(it);
                    cureq.h2state().pendingIncoming.erase(it);
                }
            }
            break;
@@ -754,8 +754,8 @@ void libhttppp::HttpEvent::ResponseEvent(netplus::con &curcon,const int tid,ULON
        // HTTP/2: connection is persistent and multiplexed.
        // Incrementally send pending H2 stream DATA frames.
        if (cureq._httpProtocol == 1) {
            if (!cureq._h2PendingResponses.empty()) {
                auto &pending = cureq._h2PendingResponses.front();
            if (cureq._h2 && !cureq._h2->pendingResponses.empty()) {
                auto &pending = cureq._h2->pendingResponses.front();
                size_t remaining = pending.body.size() - pending.offset;
                if (remaining > 0) {
                    size_t chunk = std::min(remaining, H2_MAX_FRAME_SIZE);
@@ -768,7 +768,7 @@ void libhttppp::HttpEvent::ResponseEvent(netplus::con &curcon,const int tid,ULON
                    pending.offset += chunk;
                }
                if (pending.offset >= pending.body.size()) {
                    cureq._h2PendingResponses.pop_front();
                    cureq._h2->pendingResponses.pop_front();
                }
            }
            return;