Commit a9a4fad5 authored by jan.koester's avatar jan.koester
Browse files

test

parent f112a253
Loading
Loading
Loading
Loading
+47 −14
Original line number Diff line number Diff line
@@ -807,6 +807,7 @@ static constexpr uint8_t H2C_FRAME_SETTINGS = 0x04;
static constexpr uint8_t H2C_FRAME_PING         = 0x06;
static constexpr uint8_t H2C_FRAME_GOAWAY       = 0x07;
static constexpr uint8_t H2C_FRAME_WINDOW_UPDATE = 0x08;
static constexpr uint8_t H2C_FRAME_CONTINUATION = 0x09;

static constexpr uint8_t H2C_FLAG_END_STREAM  = 0x01;
static constexpr uint8_t H2C_FLAG_ACK         = 0x01;
@@ -2625,6 +2626,33 @@ const std::vector<char> libhttppp::HttpClient::_h2Request(
        if (!_h2Decoder) _h2Decoder = std::make_unique<hpack::Decoder>();
        hpack::Decoder &decoder = *_h2Decoder;

        // RFC 7540 §6.2/§6.10: a HEADERS frame whose END_HEADERS flag isn't
        // set is only the first fragment of the block -- the rest arrives in
        // one or more CONTINUATION frames. Accumulate raw bytes here and only
        // decode once END_HEADERS is finally seen; decoding a lone HEADERS
        // frame's payload as if it were the whole block (the previous
        // behavior) hands decodeString() a truncated buffer, which silently
        // returns "" for any header whose value got cut at the frame boundary
        // -- e.g. a long Content-Security-Policy pushing a response's header
        // block past one frame, corrupting Content-Type along with it.
        std::vector<uint8_t> pendingHpack;
        bool pendingEndStream = false;
        auto finishHeaders = [&]() {
            auto headers = decoder.decode(pendingHpack.data(), pendingHpack.size());
            pendingHpack.clear();
            got_headers = true;
            _lastResponse = std::make_unique<HttpResponse>();
            _lastResponse->parseH2(headers);
            _lastStatusCode = _lastResponse->getStatusCode();
            _lastContentType = _lastResponse->getContentType();
            _lastLocation.clear();
            try {
                auto *loc = _lastResponse->getHeaderData("location");
                if (loc && loc->getfirstValue())
                    _lastLocation = loc->getfirstValue()->getvalue();
            } catch (...) {}
        };

        // Read enough data into raw buffer
        auto ensure_bytes = [&](size_t need) {
            while (raw.size() < need) {
@@ -2681,20 +2709,25 @@ const std::vector<char> libhttppp::HttpClient::_h2Request(

                case H2C_FRAME_HEADERS: {
                    if (frame_stream == stream_id) {
                        auto headers = decoder.decode(payload, frame_len);
                        got_headers = true;
                        _lastResponse = std::make_unique<HttpResponse>();
                        _lastResponse->parseH2(headers);
                        _lastStatusCode = _lastResponse->getStatusCode();
                        _lastContentType = _lastResponse->getContentType();
                        _lastLocation.clear();
                        try {
                            auto *loc = _lastResponse->getHeaderData("location");
                            if (loc && loc->getfirstValue())
                                _lastLocation = loc->getfirstValue()->getvalue();
                        } catch (...) {}
                        if (frame_flags & H2C_FLAG_END_STREAM) {
                            got_end_stream = true;
                        pendingHpack.assign(payload, payload + frame_len);
                        pendingEndStream = (frame_flags & H2C_FLAG_END_STREAM) != 0;
                        if (frame_flags & H2C_FLAG_END_HEADERS) {
                            finishHeaders();
                            if (pendingEndStream) got_end_stream = true;
                        }
                        // else: block continues in CONTINUATION frame(s) below
                    }
                    break;
                }

                case H2C_FRAME_CONTINUATION: {
                    // RFC 7540 §6.10: more HPACK data for a HEADERS block
                    // whose END_HEADERS wasn't set yet.
                    if (frame_stream == stream_id) {
                        pendingHpack.insert(pendingHpack.end(), payload, payload + frame_len);
                        if (frame_flags & H2C_FLAG_END_HEADERS) {
                            finishHeaders();
                            if (pendingEndStream) got_end_stream = true;
                        }
                    }
                    break;