Commit 59e24a4c authored by jan.koester's avatar jan.koester
Browse files

bugfix http3

parent 6043035f
Loading
Loading
Loading
Loading
+89 −1
Original line number Diff line number Diff line
@@ -1561,7 +1561,95 @@ size_t libhttppp::HttpClient::readBodyChunkNonBlocking(char *buf, size_t bufsize
        return take;
    }

    // For chunked/EOF/H2/H3 modes, fall back to blocking readBodyChunk
    // ── HTTP/3 non-blocking streaming ──
    if (_streamMode == STREAM_H3) {
        // Drain already-decoded body data first
        if (!_streamH3Body.empty()) {
            size_t have = _streamH3Body.size();
            size_t take = (std::min)(have, bufsize);
            std::memcpy(buf, _streamH3Body.data(), take);
            _streamH3Body.erase(_streamH3Body.begin(), _streamH3Body.begin() + (ptrdiff_t)take);
            return take;
        }

        if (_streamH3EndStream) {
            _streamMode = STREAM_NONE;
            return static_cast<size_t>(-1);
        }

        auto *q = dynamic_cast<netplus::quic*>(_cltsock.get());
        if (!q) {
            _streamMode = STREAM_NONE;
            return static_cast<size_t>(-1);
        }

        // Non-blocking pump
        for (int pump_i = 0; pump_i < 256; ++pump_i) {
            try {
                q->pumpNetwork(MSG_DONTWAIT);
            } catch (netplus::NetException &e) {
                if (e.getErrorType() != netplus::NetException::Note)
                    throw;
                break;
            }
        }

        uint8_t rbuf[4096];
        size_t n = q->recvStreamData(_streamH3Sid, rbuf, sizeof(rbuf));
        if (n == 0) {
            if (q->isStreamComplete(_streamH3Sid) && _streamH3Raw.empty()) {
                _streamH3EndStream = true;
                _streamMode = STREAM_NONE;
                return static_cast<size_t>(-1);
            }
            return 0;  // No data yet — caller should retry later
        }
        _streamH3Raw.insert(_streamH3Raw.end(), rbuf, rbuf + n);

        // Parse H3 frames
        size_t pos = 0;
        while (pos < _streamH3Raw.size()) {
            size_t frame_start = pos;
            size_t bytes = 0;
            uint64_t frame_type = h3DecodeVarInt(_streamH3Raw.data() + pos, _streamH3Raw.size() - pos, bytes);
            if (bytes == 0) break;
            pos += bytes;
            if (pos >= _streamH3Raw.size()) { pos = frame_start; break; }

            uint64_t frame_len = h3DecodeVarInt(_streamH3Raw.data() + pos, _streamH3Raw.size() - pos, bytes);
            if (bytes == 0) { pos = frame_start; break; }
            pos += bytes;
            if (pos + frame_len > _streamH3Raw.size()) { pos = frame_start; break; }

            if (frame_type == 0x00) { // DATA
                _streamH3Body.insert(_streamH3Body.end(),
                    _streamH3Raw.begin() + static_cast<ptrdiff_t>(pos),
                    _streamH3Raw.begin() + static_cast<ptrdiff_t>(pos + frame_len));
            }
            pos += frame_len;
        }
        if (pos > 0)
            _streamH3Raw.erase(_streamH3Raw.begin(), _streamH3Raw.begin() + static_cast<ptrdiff_t>(pos));

        if (q->isStreamComplete(_streamH3Sid) && _streamH3Raw.empty() && _streamH3Body.empty())
            _streamH3EndStream = true;

        if (!_streamH3Body.empty()) {
            size_t have = _streamH3Body.size();
            size_t take = (std::min)(have, bufsize);
            std::memcpy(buf, _streamH3Body.data(), take);
            _streamH3Body.erase(_streamH3Body.begin(), _streamH3Body.begin() + (ptrdiff_t)take);
            return take;
        }

        if (_streamH3EndStream) {
            _streamMode = STREAM_NONE;
            return static_cast<size_t>(-1);
        }
        return 0;
    }

    // For chunked/EOF/H2 modes, fall back to blocking readBodyChunk
    return readBodyChunk(buf, bufsize);
}