commit c71b1c811a2593c2b84b1f74714ddef283a07630
parent ef78343baca4dddb99139d42e7e2bbfc061da77c
Author: Christian Grothoff <christian@grothoff.org>
Date: Sun, 26 Apr 2026 22:48:40 +0200
do not 502 on early response
Diffstat:
1 file changed, 52 insertions(+), 48 deletions(-)
diff --git a/src/backend/paivana-httpd_reverse.c b/src/backend/paivana-httpd_reverse.c
@@ -742,27 +742,28 @@ curl_download_cb (void *ptr,
if (REQUEST_STATE_PROXY_UPLOAD_STARTED == hr->state)
{
- /* Web server started with response before we finished
- the upload. In this case, current libcurl decides
- to NOT complete the upload, so we should jump in the
- state machine to process the download, dropping the
- rest of the upload. This should only really happen
- with uploads without "Expect: 100 Continue" and
- Web servers responding with an error (i.e. upload
- not allowed) */hr->state = REQUEST_STATE_PROXY_DOWNLOAD_STARTED;
- GNUNET_log
- (GNUNET_ERROR_TYPE_INFO,
- "Stopping %u byte upload: we are already downloading...\n",
- (unsigned int) hr->io_len);
- hr->io_len = 0;
- }
-
- if (REQUEST_STATE_PROXY_DOWNLOAD_STARTED != hr->state)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Download callback goes to sleep\n");
- hr->curl_paused = true;
- return CURL_WRITEFUNC_PAUSE;
+ if (0 == hr->io_len)
+ {
+ /* Either the request body was empty (CURLOPT_POSTFIELDSIZE = 0,
+ so libcurl never called our read callback) or the upload
+ already drained but we have not yet entered upload_cb to
+ flip the state — either way, the upload is complete and we
+ can move straight on to consuming the response. */
+ hr->state = REQUEST_STATE_PROXY_DOWNLOAD_STARTED;
+ }
+ else
+ {
+ /* HTTP/1.1 lets the upstream begin responding before we have
+ finished sending the request body. Pause the download
+ callback (libcurl will buffer the response bytes for us)
+ and let curl_upload_cb finish draining io_buf; it will
+ resume us via curl_easy_pause() once io_len hits zero. */
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Pausing download cb, %u upload bytes still to send\n",
+ (unsigned int) hr->io_len);
+ hr->curl_paused = true;
+ return CURL_WRITEFUNC_PAUSE;
+ }
}
GNUNET_assert (REQUEST_STATE_PROXY_DOWNLOAD_STARTED ==
hr->state);
@@ -813,42 +814,35 @@ curl_upload_cb (void *buf,
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Upload cb is working...\n");
- if ( (REQUEST_STATE_PROXY_DOWNLOAD_STARTED == hr->state) ||
- (REQUEST_STATE_PROXY_DOWNLOAD_DONE == hr->state) )
+ if (REQUEST_STATE_PROXY_DOWNLOAD_DONE == hr->state)
{
- GNUNET_log
- (GNUNET_ERROR_TYPE_INFO,
- "Upload cb aborts: we are already downloading...\n");
+ /* Should not happen: the curl task only declares the transfer
+ done after consuming POSTFIELDSIZE bytes from us. */
+ GNUNET_break (0);
return CURL_READFUNC_ABORT;
}
- if ( (0 == hr->io_len) &&
- (REQUEST_STATE_PROXY_UPLOAD_STARTED == hr->state) )
+ if (REQUEST_STATE_PROXY_DOWNLOAD_STARTED == hr->state)
{
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Pausing CURL UPLOAD, need more data\n");
- return CURL_READFUNC_PAUSE;
+ /* We have already drained io_buf and flipped the state in a
+ previous call. With CURLOPT_POSTFIELDSIZE set, libcurl
+ should not ask for more bytes — but if it does, signal a
+ clean end-of-upload rather than aborting the transfer. */
+ GNUNET_assert (0 == hr->io_len);
+ return 0;
}
- /**
- * We got rescheduled because the download callback was asleep.
- * FIXME: can this block be eliminated and the unpausing being
- * moved in the last block where we return zero as well?
- */
- if ( (0 == hr->io_len) &&
- (REQUEST_STATE_PROXY_DOWNLOAD_STARTED == hr->state) )
+ GNUNET_assert (REQUEST_STATE_PROXY_UPLOAD_STARTED == hr->state);
+ if (0 == hr->io_len)
{
- if (hr->curl_paused)
- {
- hr->curl_paused = false;
- curl_easy_pause (hr->curl,
- CURLPAUSE_CONT);
- }
- curl_download_prepare ();
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Completed CURL UPLOAD\n");
- return 0; /* upload finished, can now download */
+ /* Should not happen: start_curl_request runs only after the
+ client body is fully buffered, so io_len begins > 0 (or 0
+ for an empty body, which CURLOPT_POSTFIELDSIZE = 0 keeps
+ libcurl from asking about). */
+ GNUNET_break (0);
+ return CURL_READFUNC_PAUSE;
}
+
to_copy = GNUNET_MIN (hr->io_len,
len);
GNUNET_memcpy (buf,
@@ -864,6 +858,16 @@ curl_upload_cb (void *buf,
hr->state = REQUEST_STATE_PROXY_DOWNLOAD_STARTED;
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Completed CURL UPLOAD\n");
+ if (hr->curl_paused)
+ {
+ /* curl_download_cb pauses itself when an early upstream
+ response arrives mid-upload; resume it now that the
+ request body has been fully delivered. */
+ hr->curl_paused = false;
+ curl_easy_pause (hr->curl,
+ CURLPAUSE_CONT);
+ curl_download_prepare ();
+ }
}
return to_copy;
}