Commit 73819fb2 authored by jan.koester's avatar jan.koester
Browse files

non block httpclient added

parent c6b65690
Loading
Loading
Loading
Loading
+4 −4
Original line number Diff line number Diff line
@@ -418,13 +418,13 @@ std::string Encoder::encodeResponseHeaders(uint16_t status_code,
    std::string out;

    if (status_code == 200) {
        out.push_back(static_cast<char>(0x88));
        out.push_back(static_cast<char>(0x88));  // index 8
    } else if (status_code == 204) {
        out.push_back(static_cast<char>(0x89));
        out.push_back(static_cast<char>(0x89));  // index 9
    } else if (status_code == 206) {
        out.push_back(static_cast<char>(0x8b));
        out.push_back(static_cast<char>(0x8a));  // index 10
    } else if (status_code == 304) {
        out.push_back(static_cast<char>(0x8f));
        out.push_back(static_cast<char>(0x8b));  // index 11
    } else {
        std::string status = std::to_string(status_code);
        appendLiteralWithNameIndex(out, 0x08, status, false);
+73 −0
Original line number Diff line number Diff line
@@ -410,6 +410,18 @@ size_t libhttppp::HttpClient::_recvBlocking(netplus::buffer &b) {
    }
}

// Non-blocking recv: returns 0 immediately if no data available (EAGAIN)
size_t libhttppp::HttpClient::_recvNonBlocking(netplus::buffer &b) {
    try {
        return _cltsock->recvData(b, 0);
    } catch (netplus::NetException &e) {
        if (e.getErrorType() == netplus::NetException::Note) {
            return 0;   // EAGAIN — no data yet
        }
        throw;
    }
}

// Efficient send: uses socketwait::waitWrite() instead of busy-waiting
void libhttppp::HttpClient::_sendAll(const char *data, size_t len) {
    size_t off = 0;
@@ -1479,6 +1491,67 @@ size_t libhttppp::HttpClient::readBodyChunk(char *buf, size_t bufsize) {
    return 0;
}

size_t libhttppp::HttpClient::readBodyChunkNonBlocking(char *buf, size_t bufsize) {
    // Stream finished: signal completion with (size_t)-1
    if (_streamMode == STREAM_NONE || bufsize == 0)
        return static_cast<size_t>(-1);

    // Helper: drain from internal leftover buffer
    auto drainBuf = [&](size_t maxBytes) -> size_t {
        size_t have = _streamBuf.size() - _streamBufPos;
        if (have == 0) return 0;
        size_t take = (std::min)(have, maxBytes);
        std::memcpy(buf, _streamBuf.data() + _streamBufPos, take);
        _streamBufPos += take;
        if (_streamBufPos == _streamBuf.size()) {
            _streamBuf.clear();
            _streamBufPos = 0;
        }
        return take;
    };

    // ── Content-Length mode ──
    if (_streamMode == STREAM_CONTENT_LENGTH) {
        if (_streamRemaining == 0) {
            _streamMode = STREAM_NONE;
            return static_cast<size_t>(-1);
        }

        size_t want = (std::min)(bufsize, _streamRemaining);

        // First try leftover buffer
        size_t got = drainBuf(want);
        if (got > 0) {
            _streamRemaining -= got;
            if (_streamRemaining == 0)
                _streamMode = STREAM_NONE;
            return got;
        }

        // Non-blocking read from socket
        netplus::buffer nb(BLOCKSIZE);
        size_t n = _recvNonBlocking(nb);
        if (n == 0)
            return 0;  // No data yet — caller should retry later

        size_t take = (std::min)({n, want, bufsize});
        std::memcpy(buf, nb.data.buf, take);
        _streamRemaining -= take;

        if (take < n) {
            _streamBuf.assign(nb.data.buf + take, nb.data.buf + n);
            _streamBufPos = 0;
        }

        if (_streamRemaining == 0)
            _streamMode = STREAM_NONE;
        return take;
    }

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

static bool is_redirect_status(int code) {
  return code == 301 || code == 302 || code == 303 || code == 307 || code == 308;
}
+6 −0
Original line number Diff line number Diff line
@@ -102,6 +102,10 @@ namespace libhttppp {
      // 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;

@@ -116,6 +120,8 @@ namespace libhttppp {

      // Non-blocking I/O helpers using poll() for efficient waiting
      size_t _recvBlocking(netplus::buffer &b);
      // 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);

+10 −1
Original line number Diff line number Diff line
@@ -30,6 +30,8 @@
#include <iostream>
#include <cctype>
#include <string>
#include <thread>
#include <chrono>

#include <netplus/socket.h>
#include <netplus/exception.h>
@@ -417,6 +419,7 @@ void libhttppp::HttpEvent::_dispatchH2Stream(HttpRequest &cureq,
            tempreq.SendData.pos = 0;

            size_t max_iterations = content_length / BLOCKSIZE + 4096;
            size_t empty_count = 0;
            for (size_t i = 0;
                 i < max_iterations && body.size() < content_length;
                 ++i) {
@@ -425,8 +428,14 @@ void libhttppp::HttpEvent::_dispatchH2Stream(HttpRequest &cureq,
                if (sa > 0) {
                    body.append(tempreq.SendData.data(), sa);
                    tempreq.SendData.clear();
                    empty_count = 0;
                } else {
                    break; // No more data available
                    ++empty_count;
                    if (empty_count > 500) {
                        break; // Genuine stall — give up
                    }
                    // No data yet — yield briefly so backend can deliver
                    std::this_thread::sleep_for(std::chrono::milliseconds(1));
                }
            }
        }