Commit 704541bd authored by jan.koester's avatar jan.koester
Browse files

optimize

parent a87a372d
Loading
Loading
Loading
Loading
+104 −70
Original line number Diff line number Diff line
@@ -471,7 +471,7 @@ size_t libhttppp::HttpClient::_recvBlocking(netplus::buffer &b, int timeout_sec)
                    ee[HTTPException::Error] << "recv timeout: connection may be dead";
                    throw ee;
                }
                _sw.waitRead(*_cltsock, 5000);
                _sw.waitRead(*_cltsock, 500);
                continue;
            }
            throw;
@@ -506,7 +506,7 @@ void libhttppp::HttpClient::_sendAll(const char *data, size_t len) {
                    ee[HTTPException::Error] << "send timeout: connection may be dead";
                    throw ee;
                }
                _sw.waitWrite(*_cltsock, 5000);
                _sw.waitWrite(*_cltsock, 500);
                continue;
            }
            off += n;
@@ -517,7 +517,7 @@ void libhttppp::HttpClient::_sendAll(const char *data, size_t len) {
                    ee[HTTPException::Error] << "send timeout: connection may be dead";
                    throw ee;
                }
                _sw.waitWrite(*_cltsock, 5000);
                _sw.waitWrite(*_cltsock, 500);
                continue;
            }
            throw;
@@ -539,10 +539,12 @@ std::vector<char> libhttppp::HttpClient::_h1ReadResponse(const std::string &labe
    raw.reserve(16384);

    size_t header_end = std::string::npos;
    size_t search_from = 0;   // avoid re-scanning from 0 on every recv (O(n²) → O(n))

    for (;;) {
        if (raw.size() >= 4) {
            for (size_t i = 0; i + 3 < raw.size(); ++i) {
            size_t start = (search_from >= 3) ? search_from - 3 : 0;
            for (size_t i = start; i + 3 < raw.size(); ++i) {
                if (raw[i] == '\r' && raw[i+1] == '\n' &&
                    raw[i+2] == '\r' && raw[i+3] == '\n')
                {
@@ -550,6 +552,7 @@ std::vector<char> libhttppp::HttpClient::_h1ReadResponse(const std::string &labe
                    break;
                }
            }
            search_from = raw.size();
        }
        if (header_end != std::string::npos)
            break;
@@ -1001,7 +1004,7 @@ libhttppp::HttpResponse libhttppp::HttpClient::GetStream(libhttppp::HttpRequest
            _streamH2EndStream = false;

            auto h2_ensure_bytes = [&](size_t need) {
                while (_streamH2Raw.size() < need) {
                while (_streamH2Raw.size() - h2_off < need) {
                    netplus::buffer buf(CHUNKSIZE);
                    size_t n = _recvBlocking(buf, 15);
                    if (n == 0) {
@@ -1015,22 +1018,24 @@ libhttppp::HttpResponse libhttppp::HttpClient::GetStream(libhttppp::HttpRequest
                }
            };

            size_t h2_off = 0;

            while (!got_headers) {
                h2_ensure_bytes(H2C_FRAME_HEADER_LEN);

                uint32_t frame_len = (static_cast<uint32_t>(_streamH2Raw[0]) << 16)
                                   | (static_cast<uint32_t>(_streamH2Raw[1]) << 8)
                                   | static_cast<uint32_t>(_streamH2Raw[2]);
                uint8_t frame_type = _streamH2Raw[3];
                uint8_t frame_flags = _streamH2Raw[4];
                uint32_t frame_stream = ((static_cast<uint32_t>(_streamH2Raw[5]) & 0x7f) << 24)
                                      | (static_cast<uint32_t>(_streamH2Raw[6]) << 16)
                                      | (static_cast<uint32_t>(_streamH2Raw[7]) << 8)
                                      | static_cast<uint32_t>(_streamH2Raw[8]);
                uint32_t frame_len = (static_cast<uint32_t>(_streamH2Raw[h2_off]) << 16)
                                   | (static_cast<uint32_t>(_streamH2Raw[h2_off+1]) << 8)
                                   | static_cast<uint32_t>(_streamH2Raw[h2_off+2]);
                uint8_t frame_type = _streamH2Raw[h2_off+3];
                uint8_t frame_flags = _streamH2Raw[h2_off+4];
                uint32_t frame_stream = ((static_cast<uint32_t>(_streamH2Raw[h2_off+5]) & 0x7f) << 24)
                                      | (static_cast<uint32_t>(_streamH2Raw[h2_off+6]) << 16)
                                      | (static_cast<uint32_t>(_streamH2Raw[h2_off+7]) << 8)
                                      | static_cast<uint32_t>(_streamH2Raw[h2_off+8]);

                h2_ensure_bytes(H2C_FRAME_HEADER_LEN + frame_len);

                const uint8_t *payload = _streamH2Raw.data() + H2C_FRAME_HEADER_LEN;
                const uint8_t *payload = _streamH2Raw.data() + h2_off + H2C_FRAME_HEADER_LEN;

                switch (frame_type) {
                    case H2C_FRAME_SETTINGS: {
@@ -1068,8 +1073,9 @@ libhttppp::HttpResponse libhttppp::HttpClient::GetStream(libhttppp::HttpRequest
                            // Body data arrived with headers — save for readBodyChunk
                            _streamBuf.insert(_streamBuf.end(), payload, payload + frame_len);
                            if (frame_len > 0) {
                                _sendAll(h2cBuildWindowUpdate(0, frame_len));
                                _sendAll(h2cBuildWindowUpdate(stream_id, frame_len));
                                std::string wu = h2cBuildWindowUpdate(0, frame_len)
                                               + h2cBuildWindowUpdate(stream_id, frame_len);
                                _sendAll(wu);
                            }
                            if (frame_flags & H2C_FLAG_END_STREAM)
                                _streamH2EndStream = true;
@@ -1091,8 +1097,13 @@ libhttppp::HttpResponse libhttppp::HttpClient::GetStream(libhttppp::HttpRequest
                        break;
                }

                h2_off += H2C_FRAME_HEADER_LEN + frame_len;
            }

            // Single compact: remove all consumed bytes at once
            if (h2_off > 0) {
                _streamH2Raw.erase(_streamH2Raw.begin(),
                    _streamH2Raw.begin() + H2C_FRAME_HEADER_LEN + frame_len);
                    _streamH2Raw.begin() + static_cast<ptrdiff_t>(h2_off));
            }

            _streamMode = STREAM_H2;
@@ -1117,10 +1128,12 @@ libhttppp::HttpResponse libhttppp::HttpClient::GetStream(libhttppp::HttpRequest
        std::vector<char> raw;
        raw.reserve(16384);
        size_t header_end = std::string::npos;
        size_t search_from = 0;

        for (;;) {
            if (raw.size() >= 4) {
                for (size_t i = 0; i + 3 < raw.size(); ++i) {
                size_t start = (search_from >= 3) ? search_from - 3 : 0;
                for (size_t i = start; i + 3 < raw.size(); ++i) {
                    if (raw[i] == '\r' && raw[i+1] == '\n' &&
                        raw[i+2] == '\r' && raw[i+3] == '\n')
                    {
@@ -1128,6 +1141,7 @@ libhttppp::HttpResponse libhttppp::HttpClient::GetStream(libhttppp::HttpRequest
                        break;
                    }
                }
                search_from = raw.size();
            }
            if (header_end != std::string::npos)
                break;
@@ -1234,7 +1248,9 @@ size_t libhttppp::HttpClient::readBodyChunk(char *buf, size_t bufsize) {
        netplus::buffer nb(CHUNKSIZE);
        size_t n = _recvBlocking(nb);
        if (n > 0) {
            if (_streamBufPos > 0 && _streamBufPos < _streamBuf.size()) {
            // Compact only when consumed portion exceeds half the buffer
            // to amortize the O(n) memmove cost
            if (_streamBufPos > 0 && _streamBufPos >= _streamBuf.size() / 2) {
                _streamBuf.erase(_streamBuf.begin(),
                                 _streamBuf.begin() + (ptrdiff_t)_streamBufPos);
                _streamBufPos = 0;
@@ -1430,8 +1446,10 @@ size_t libhttppp::HttpClient::readBodyChunk(char *buf, size_t bufsize) {
        }

        // Read more H2 frames until we get DATA for our stream
        size_t h2_off = 0;   // track consumed position instead of erasing per frame

        auto h2_ensure_bytes = [&](size_t need) {
            while (_streamH2Raw.size() < need) {
            while (_streamH2Raw.size() - h2_off < need) {
                netplus::buffer fbuf(CHUNKSIZE);
                size_t n = _recvBlocking(fbuf);
                if (n == 0) {
@@ -1448,20 +1466,20 @@ size_t libhttppp::HttpClient::readBodyChunk(char *buf, size_t bufsize) {
            h2_ensure_bytes(H2C_FRAME_HEADER_LEN);
            if (_streamH2EndStream) break;

            uint32_t frame_len = (static_cast<uint32_t>(_streamH2Raw[0]) << 16)
                               | (static_cast<uint32_t>(_streamH2Raw[1]) << 8)
                               | static_cast<uint32_t>(_streamH2Raw[2]);
            uint8_t frame_type = _streamH2Raw[3];
            uint8_t frame_flags = _streamH2Raw[4];
            uint32_t frame_stream = ((static_cast<uint32_t>(_streamH2Raw[5]) & 0x7f) << 24)
                                  | (static_cast<uint32_t>(_streamH2Raw[6]) << 16)
                                  | (static_cast<uint32_t>(_streamH2Raw[7]) << 8)
                                  | static_cast<uint32_t>(_streamH2Raw[8]);
            uint32_t frame_len = (static_cast<uint32_t>(_streamH2Raw[h2_off]) << 16)
                               | (static_cast<uint32_t>(_streamH2Raw[h2_off+1]) << 8)
                               | static_cast<uint32_t>(_streamH2Raw[h2_off+2]);
            uint8_t frame_type = _streamH2Raw[h2_off+3];
            uint8_t frame_flags = _streamH2Raw[h2_off+4];
            uint32_t frame_stream = ((static_cast<uint32_t>(_streamH2Raw[h2_off+5]) & 0x7f) << 24)
                                  | (static_cast<uint32_t>(_streamH2Raw[h2_off+6]) << 16)
                                  | (static_cast<uint32_t>(_streamH2Raw[h2_off+7]) << 8)
                                  | static_cast<uint32_t>(_streamH2Raw[h2_off+8]);

            h2_ensure_bytes(H2C_FRAME_HEADER_LEN + frame_len);
            if (_streamH2EndStream) break;

            const uint8_t *payload = _streamH2Raw.data() + H2C_FRAME_HEADER_LEN;
            const uint8_t *payload = _streamH2Raw.data() + h2_off + H2C_FRAME_HEADER_LEN;

            switch (frame_type) {
                case H2C_FRAME_SETTINGS: {
@@ -1479,8 +1497,10 @@ size_t libhttppp::HttpClient::readBodyChunk(char *buf, size_t bufsize) {
                        }
                        _streamBuf.insert(_streamBuf.end(), payload, payload + frame_len);
                        if (frame_len > 0) {
                            _sendAll(h2cBuildWindowUpdate(0, frame_len));
                            _sendAll(h2cBuildWindowUpdate(_streamH2Sid, frame_len));
                            // Batch both WINDOW_UPDATE frames into a single send
                            std::string wu = h2cBuildWindowUpdate(0, frame_len)
                                           + h2cBuildWindowUpdate(_streamH2Sid, frame_len);
                            _sendAll(wu);
                        }
                        if (frame_flags & H2C_FLAG_END_STREAM)
                            _streamH2EndStream = true;
@@ -1507,8 +1527,13 @@ size_t libhttppp::HttpClient::readBodyChunk(char *buf, size_t bufsize) {
                    break;
            }

            h2_off += H2C_FRAME_HEADER_LEN + frame_len;
        }

        // Single compact: remove all consumed bytes at once
        if (h2_off > 0) {
            _streamH2Raw.erase(_streamH2Raw.begin(),
                _streamH2Raw.begin() + H2C_FRAME_HEADER_LEN + frame_len);
                _streamH2Raw.begin() + static_cast<ptrdiff_t>(h2_off));
        }

        // Drain any newly received body data
@@ -1878,21 +1903,22 @@ size_t libhttppp::HttpClient::readBodyChunkNonBlocking(char *buf, size_t bufsize
        }

        // Process all complete H2 frames in _streamH2Raw
        while (_streamH2Raw.size() >= H2C_FRAME_HEADER_LEN) {
            uint32_t frame_len = (static_cast<uint32_t>(_streamH2Raw[0]) << 16)
                               | (static_cast<uint32_t>(_streamH2Raw[1]) << 8)
                               | static_cast<uint32_t>(_streamH2Raw[2]);
            uint8_t frame_type = _streamH2Raw[3];
            uint8_t frame_flags = _streamH2Raw[4];
            uint32_t frame_stream = ((static_cast<uint32_t>(_streamH2Raw[5]) & 0x7f) << 24)
                                  | (static_cast<uint32_t>(_streamH2Raw[6]) << 16)
                                  | (static_cast<uint32_t>(_streamH2Raw[7]) << 8)
                                  | static_cast<uint32_t>(_streamH2Raw[8]);

            if (_streamH2Raw.size() < H2C_FRAME_HEADER_LEN + frame_len)
        size_t h2_off = 0;
        while (_streamH2Raw.size() - h2_off >= H2C_FRAME_HEADER_LEN) {
            uint32_t frame_len = (static_cast<uint32_t>(_streamH2Raw[h2_off]) << 16)
                               | (static_cast<uint32_t>(_streamH2Raw[h2_off+1]) << 8)
                               | static_cast<uint32_t>(_streamH2Raw[h2_off+2]);
            uint8_t frame_type = _streamH2Raw[h2_off+3];
            uint8_t frame_flags = _streamH2Raw[h2_off+4];
            uint32_t frame_stream = ((static_cast<uint32_t>(_streamH2Raw[h2_off+5]) & 0x7f) << 24)
                                  | (static_cast<uint32_t>(_streamH2Raw[h2_off+6]) << 16)
                                  | (static_cast<uint32_t>(_streamH2Raw[h2_off+7]) << 8)
                                  | static_cast<uint32_t>(_streamH2Raw[h2_off+8]);

            if (_streamH2Raw.size() - h2_off < H2C_FRAME_HEADER_LEN + frame_len)
                break;  // incomplete frame, wait for more data

            const uint8_t *payload = _streamH2Raw.data() + H2C_FRAME_HEADER_LEN;
            const uint8_t *payload = _streamH2Raw.data() + h2_off + H2C_FRAME_HEADER_LEN;

            switch (frame_type) {
                case H2C_FRAME_SETTINGS: {
@@ -1909,8 +1935,9 @@ size_t libhttppp::HttpClient::readBodyChunkNonBlocking(char *buf, size_t bufsize
                        }
                        _streamBuf.insert(_streamBuf.end(), payload, payload + frame_len);
                        if (frame_len > 0) {
                            _sendAll(h2cBuildWindowUpdate(0, frame_len));
                            _sendAll(h2cBuildWindowUpdate(_streamH2Sid, frame_len));
                            std::string wu = h2cBuildWindowUpdate(0, frame_len)
                                           + h2cBuildWindowUpdate(_streamH2Sid, frame_len);
                            _sendAll(wu);
                        }
                        if (frame_flags & H2C_FLAG_END_STREAM)
                            _streamH2EndStream = true;
@@ -1939,8 +1966,13 @@ size_t libhttppp::HttpClient::readBodyChunkNonBlocking(char *buf, size_t bufsize
                    break;
            }

            h2_off += H2C_FRAME_HEADER_LEN + frame_len;
        }

        // Single compact: remove all consumed bytes at once
        if (h2_off > 0) {
            _streamH2Raw.erase(_streamH2Raw.begin(),
                _streamH2Raw.begin() + H2C_FRAME_HEADER_LEN + frame_len);
                _streamH2Raw.begin() + static_cast<ptrdiff_t>(h2_off));
        }

        // Drain newly received body data
@@ -2152,7 +2184,7 @@ const std::vector<char> libhttppp::HttpClient::_h2Request(
                            ee[HTTPException::Error] << "H2 send timeout: connection may be dead";
                            throw ee;
                        }
                        _sw.waitWrite(*_cltsock, 5000);
                        _sw.waitWrite(*_cltsock, 500);
                        continue;
                    }
                    off += n;
@@ -2163,7 +2195,7 @@ const std::vector<char> libhttppp::HttpClient::_h2Request(
                            ee[HTTPException::Error] << "H2 send timeout: connection may be dead";
                            throw ee;
                        }
                        _sw.waitWrite(*_cltsock, 5000);
                        _sw.waitWrite(*_cltsock, 500);
                        continue;
                    }
                    throw;
@@ -2361,6 +2393,8 @@ const std::vector<char> libhttppp::HttpClient::_h2Request(
        // malicious server sends endless non-terminal frames (e.g. PINGs).
        auto h2_response_deadline = std::chrono::steady_clock::now() + std::chrono::seconds(120);

        size_t raw_off = 0;   // track consumed position to avoid per-frame erase

        while (!got_end_stream) {
            if (std::chrono::steady_clock::now() >= h2_response_deadline) {
                HTTPException ee;
@@ -2368,22 +2402,22 @@ const std::vector<char> libhttppp::HttpClient::_h2Request(
                throw ee;
            }
            // Read frame header (9 bytes)
            ensure_bytes(H2C_FRAME_HEADER_LEN);

            uint32_t frame_len = (static_cast<uint32_t>(raw[0]) << 16)
                               | (static_cast<uint32_t>(raw[1]) << 8)
                               | static_cast<uint32_t>(raw[2]);
            uint8_t frame_type = raw[3];
            uint8_t frame_flags = raw[4];
            uint32_t frame_stream = ((static_cast<uint32_t>(raw[5]) & 0x7f) << 24)
                                  | (static_cast<uint32_t>(raw[6]) << 16)
                                  | (static_cast<uint32_t>(raw[7]) << 8)
                                  | static_cast<uint32_t>(raw[8]);
            ensure_bytes(raw_off + H2C_FRAME_HEADER_LEN);

            uint32_t frame_len = (static_cast<uint32_t>(raw[raw_off]) << 16)
                               | (static_cast<uint32_t>(raw[raw_off+1]) << 8)
                               | static_cast<uint32_t>(raw[raw_off+2]);
            uint8_t frame_type = raw[raw_off+3];
            uint8_t frame_flags = raw[raw_off+4];
            uint32_t frame_stream = ((static_cast<uint32_t>(raw[raw_off+5]) & 0x7f) << 24)
                                  | (static_cast<uint32_t>(raw[raw_off+6]) << 16)
                                  | (static_cast<uint32_t>(raw[raw_off+7]) << 8)
                                  | static_cast<uint32_t>(raw[raw_off+8]);

            // Read frame payload
            ensure_bytes(H2C_FRAME_HEADER_LEN + frame_len);
            ensure_bytes(raw_off + H2C_FRAME_HEADER_LEN + frame_len);

            const uint8_t *payload = raw.data() + H2C_FRAME_HEADER_LEN;
            const uint8_t *payload = raw.data() + raw_off + H2C_FRAME_HEADER_LEN;

            switch (frame_type) {
                case H2C_FRAME_SETTINGS: {
@@ -2413,10 +2447,11 @@ const std::vector<char> libhttppp::HttpClient::_h2Request(
                        if (frame_flags & H2C_FLAG_END_STREAM) {
                            got_end_stream = true;
                        }
                        // Send WINDOW_UPDATE for connection and stream
                        // Send WINDOW_UPDATE for connection and stream (batched)
                        if (frame_len > 0) {
                            send_all(h2cBuildWindowUpdate(0, frame_len));
                            send_all(h2cBuildWindowUpdate(stream_id, frame_len));
                            std::string wu = h2cBuildWindowUpdate(0, frame_len)
                                           + h2cBuildWindowUpdate(stream_id, frame_len);
                            send_all(wu);
                        }
                    }
                    break;
@@ -2442,8 +2477,7 @@ const std::vector<char> libhttppp::HttpClient::_h2Request(
                    break;
            }

            // Consume processed frame from buffer
            raw.erase(raw.begin(), raw.begin() + H2C_FRAME_HEADER_LEN + frame_len);
            raw_off += H2C_FRAME_HEADER_LEN + frame_len;
        }

        return ret;