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:
+
+ - If ok is falsy, the second value is used as an error
+ message: the connection is closed and request returns
+ nil followed by that message, just like any other request
+ failure.
+ - If ok is truthy, the request continues normally. If the
+ second value is also provided, it replaces sink for reading the
+ response body; otherwise the original sink is used.
+
+ 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