Commit 7117d188 authored by jan.koester's avatar jan.koester
Browse files

bugfix h1 stream

parent 656d22ff
Loading
Loading
Loading
Loading
+27 −0
Changes for src/http.cpp: 27 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -3750,6 +3750,27 @@ bool libhttppp::HttpResponse::_storeResponseInfo(netplus::con &curconnection, in
  return true;
}

// A negative datalen is this codebase's established "streaming, more to come"
// signal (see _storeResponseInfo()/_finishH2Dispatch's identical is_streaming
// check for H2/H3). H2/H3 already have their own resumption path for that --
// WINDOW_UPDATE-driven self-calls into ResponseEvent via _resumeH2Streams --
// but plain HTTP/1.x has no equivalent: a headers-only send() here appends no
// body, so nothing is left in SendData to flush, and nothing ever re-arms
// polling for this connection again. Marking ResponsePending here, uniformly
// regardless of protocol, gives _dispatchH1Request (httpd.cpp) a signal to
// keep calling ResponseEvent until the app stops asking for another turn --
// harmless for H2/H3 (and for an H1 caller that fully drains synchronously
// inside RequestEvent() itself, e.g. proxyplus's own Range handling): an
// extra ResponseEvent call on a connection with nothing left to resume is a
// no-op for every existing ResponseEvent implementation in this codebase.
static void markDeferredIfStreaming(netplus::con &curconnection, int datalen) {
  if (datalen >= 0) return;
  if (auto *req = dynamic_cast<libhttppp::HttpRequest*>(&curconnection)) {
    if (!req->slots.empty())
      req->slots[0].ResponsePending.store(true, std::memory_order_release);
  }
}

void libhttppp::HttpResponse::send(netplus::con &curconnection,const std::string &data, int datalen){
  if(datalen==0)
    datalen=data.size();
@@ -3773,6 +3794,8 @@ void libhttppp::HttpResponse::send(netplus::con &curconnection,const std::string
    return;
  }

  markDeferredIfStreaming(curconnection, datalen);

  std::vector<char> header;

  printHeader(header);
@@ -3802,6 +3825,8 @@ void libhttppp::HttpResponse::send(netplus::con &curconnection,const unsigned c
    return;
  }

  markDeferredIfStreaming(curconnection, datalen);

  std::vector<char> header;

  printHeader(header);
@@ -3836,6 +3861,8 @@ void libhttppp::HttpResponse::send(netplus::con &curconnection,const std::vector
    return;
  }

  markDeferredIfStreaming(curconnection, datalen);

  std::vector<char> header;

  printHeader(header);
+23 −0
Changes for src/httpd.cpp: 23 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -714,6 +714,29 @@ void libhttppp::HttpEvent::_dispatchH1Request(HttpRequest &cureq, size_t consume
                            cureq2.slots[0].csock->close();
                            return;
                        }

                        // A deferred/streaming response (RequestEvent called send() with a
                        // negative datalen -- see http.cpp's markDeferredIfStreaming()) needs
                        // ResponseEvent called again to actually produce and flush its body.
                        // H2/H3 self-drive this via _resumeH2Streams' WINDOW_UPDATE handling;
                        // H1 has no such mechanism, so without this loop the connection would
                        // reattach with its response permanently stuck at headers-only -- the
                        // app's own STREAM_IDLE_TIMEOUT-style reaper (if it has one) would be
                        // the only thing to ever notice, and only after that timeout elapses,
                        // having produced zero bytes the whole time. Bounded by the same
                        // kH1OffloadSocketTimeoutMs already set on this socket (each flush
                        // below re-applies it) plus whatever stall detection the app's own
                        // ResponseEvent has for its upstream source -- no separate iteration
                        // cap needed on top of those.
                        while (cureq2.slots[0].ResponsePending.exchange(
                                   false, std::memory_order_acq_rel)) {
                            ResponseEvent(cureq2, tid, args);
                            if (!cureq2.slots[0].csock) return; // see the identical check above
                            if (!cureq2.flushSendData()) {
                                cureq2.slots[0].csock->close();
                                return;
                            }
                        }
                    } catch (const std::exception &) {
                        // Either RequestEvent or the flush hit a hard error (peer reset,
                        // socket-level failure) -- nothing to reattach. csock can't have gone