Commit 6a933a6c authored by Jan Koester's avatar Jan Koester
Browse files

timeout added

parent 53e1cef2
Loading
Loading
Loading
Loading
+34 −1
Changes for src/http.cpp: 34 added lines, 1 removed line.
Original line number Diff line number Diff line
@@ -2198,7 +2198,7 @@ size_t libhttppp::HttpClient::readBodyChunk(char *buf, size_t bufsize) {
    return 0;
}

size_t libhttppp::HttpClient::readBodyChunkNonBlocking(char *buf, size_t bufsize) {
size_t libhttppp::HttpClient::_readBodyChunkNonBlockingRaw(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);
@@ -2285,6 +2285,39 @@ size_t libhttppp::HttpClient::readBodyChunkNonBlocking(char *buf, size_t bufsize
    return static_cast<size_t>(-1);
}

// Wraps _readBodyChunkNonBlockingRaw() with an idle-stream deadline: a caller
// polling this method (typically an HTTP event loop driven off ResponsePending,
// e.g. blogi's media proxy) has no way of knowing whether "0 bytes yet" means
// "more is coming shortly" or "the peer went silent mid-response and will
// never send another byte" -- the TCP connection stays ESTABLISHED either
// way. Left unbounded, that turns into a permanently stuck stream (and, for
// callers that hold a pooled connection alive until the stream finishes, a
// leaked connection). Reuses _recvTimeoutSec (the same timeout setTimeout()/
// the constructor already configure) rather than inventing a second knob.
size_t libhttppp::HttpClient::readBodyChunkNonBlocking(char *buf, size_t bufsize) {
    if (_streamMode == STREAM_NONE) {
        _streamIdleSince = std::chrono::steady_clock::time_point{};
        return static_cast<size_t>(-1);
    }

    if (_streamIdleSince != std::chrono::steady_clock::time_point{} &&
        std::chrono::steady_clock::now() - _streamIdleSince > std::chrono::seconds(_recvTimeoutSec)) {
        _streamMode = STREAM_NONE;
        _streamIdleSince = std::chrono::steady_clock::time_point{};
        return static_cast<size_t>(-1);
    }

    size_t got = _readBodyChunkNonBlockingRaw(buf, bufsize);

    if (got == 0 && _streamMode != STREAM_NONE) {
        if (_streamIdleSince == std::chrono::steady_clock::time_point{})
            _streamIdleSince = std::chrono::steady_clock::now();
    } else {
        _streamIdleSince = std::chrono::steady_clock::time_point{};
    }
    return got;
}

  // --------------- HTTP/3 client frame helpers ---------------
  static size_t h3EncodeVarInt(uint64_t value, uint8_t *out) {
    if (value < 64) {
+10 −0
Changes for src/http.h: 10 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -231,6 +231,10 @@ namespace libhttppp {
      // return" difference kept per-mode.
      size_t _streamReadH3(char *buf, size_t bufsize, bool blocking);

      // Mode-dispatch body of readBodyChunkNonBlocking(), wrapped by the
      // public method with an idle-stream deadline (see _streamIdleSince).
      size_t _readBodyChunkNonBlockingRaw(char *buf, size_t bufsize);

      // Shared Get/Post/Put/Delete implementation: builds+sends the request
      // over whichever transport is active (H1/H2/H3), follows redirects
      // per RFC 7231 §6.4 (303 always converts to a bodyless GET; 307/308
@@ -280,6 +284,12 @@ namespace libhttppp {
      bool     _streamH3InDataFrame = false; // true when inside a partial DATA frame
      uint64_t _streamH3DataRemaining = 0;  // bytes left in current DATA frame

      // Set the moment readBodyChunkNonBlocking() first sees "no data yet"
      // for the active stream; cleared on any progress or completion. Lets
      // the wrapper tell "briefly nothing to read" apart from "peer has
      // gone silent for _recvTimeoutSec and is never sending another byte".
      std::chrono::steady_clock::time_point _streamIdleSince{};

  private:
      HttpUrl _url;
      std::unique_ptr<netplus::socket> _cltsock;