From 0a4fa559e44ed3e77f723092feab977539b8aab0 Mon Sep 17 00:00:00 2001 From: Thijs Schreijer Date: Tue, 1 Sep 2026 07:54:16 +0200 Subject: Address PR review: pass raw socket to headers callback, use ok/err return convention Rename response_headers to headers_callback, pass the raw connection as a 4th argument (for use cases like WebSocket upgrade handoff), and switch its return contract to the idiomatic ok/err shape used elsewhere in this file: truthy ok continues (optionally swapping in a new sink), falsy ok closes the connection and propagates the callback's error via socket.protect instead of faking a success return. Claude-Session: https://claude.ai/code/session_01S4imKCU4hxDg96DXj9hxSB --- docs/http.html | 36 +++++++++++++++++++++++++++++++++++- src/http.lua | 10 +++------- 2 files changed, 38 insertions(+), 8 deletions(-) diff --git a/docs/http.html b/docs/http.html index c6423ba..15f9c26 100644 --- a/docs/http.html +++ b/docs/http.html @@ -136,7 +136,8 @@ http.request{
  [proxy = string,]
  [redirect = boolean,]
  [create = function,]
-  [maxredirects = number]
+  [maxredirects = number,]
+  [headers_callback = function]
}

@@ -189,6 +190,39 @@ function from automatically following 301 or 302 server redirect messages;
  • maxredirects: An optional number specifying the maximum number of redirects to follow. Defaults to 5 if not specified. A boolean false value means no maximum (unlimited).
  • +
  • headers_callback: An optional function, called once per request + right after the headers have been received (this + is after any redirect has already been followed, so it only fires for the + final response) and before the response body would be read. It is called + as: ok, new_sink = headers_callback(code, headers, status, sock), where + sock is the raw socket used for the request (the + socket.tcp-like object returned + by create, or the default one). It is not called for an HTTP/0.9 + reply (no headers at all), for a 408 response, or for a response + that is going to be redirected. +
    + The callback should return two values, ok and a second value whose + meaning depends on ok: + + Note that a swapped-in sink is only ever read from when the response + actually has a body to receive: for a HEAD request or a + 204/304 response there is nothing to read regardless of + which sink is set. +
    + This makes it possible to inspect headers before committing to read a + body -- for example to reject a response early based on + content-type or content-length, to pick a different + sink depending on the headers, or -- combined with the raw + sock -- to hand the socket off for a protocol upgrade + such as WebSockets (101 Switching Protocols).
  • diff --git a/src/http.lua b/src/http.lua index 6cdc90a..3e2c789 100644 --- a/src/http.lua +++ b/src/http.lua @@ -360,7 +360,7 @@ local trequest, tredirect headers = reqt.headers, proxy = reqt.proxy, maxredirects = reqt.maxredirects, - response_headers = reqt.response_headers, + headers_callback = reqt.headers_callback, nredirects = (reqt.nredirects or 0) + 1, create = reqt.create } @@ -405,12 +405,8 @@ end end -- here we are finally done -- provide an opportunity to abort or replace the sink based on the response headers - if nreqt.response_headers then - local abort, sink = nreqt.response_headers(code, headers, status) - if abort then - h:close() - return 1, code, headers, status - end + if nreqt.headers_callback then + local _, sink = h.try(nreqt.headers_callback(code, headers, status, h.c)) if sink then nreqt.sink = sink end -- cgit v1.2.3-55-g6feb