| From ddab2835c583d45dec62680ca8d3cbde55e0bae6 Mon Sep 17 00:00:00 2001 |
| From: daurnimator <quae@daurnimator.com> |
| Date: Tue, 22 Aug 2023 23:30:20 +1000 |
| Subject: [PATCH] http/h1_stream: handle EOF when `body_read_type==length` |
| |
| If a client closes the connection before sending the expected number of bytes |
| then return `EPIPE`. |
| This fixes a potential infinite draining loop when trying to trying to |
| `:shutdown()` a stream. |
| |
| Upstream: https://github.com/daurnimator/lua-http/commit/ddab2835c583d45dec62680ca8d3cbde55e0bae6 |
| Signed-off-by: Francois Perrad <francois.perrad@gadz.org> |
| --- |
| http/h1_stream.lua | 2 ++ |
| spec/h1_stream_spec.lua | 27 +++++++++++++++++++++++++++ |
| 2 files changed, 29 insertions(+) |
| |
| diff --git a/lua-http-0.4/http/h1_stream.lua b/lua-http-0.4/http/h1_stream.lua |
| index b2469a1..b0ca821 100644 |
| --- a/lua-http-0.4/http/h1_stream.lua |
| +++ b/lua-http-0.4/http/h1_stream.lua |
| @@ -861,6 +861,8 @@ function stream_methods:read_next_chunk(timeout) |
| if chunk ~= nil then |
| self.body_read_left = length_n - #chunk |
| end_stream = (self.body_read_left == 0) |
| + elseif err == nil then |
| + return nil, ce.strerror(ce.EPIPE), ce.EPIPE |
| end |
| elseif length_n == 0 then |
| chunk = "" |
| diff --git a/lua-http-0.4/spec/h1_stream_spec.lua b/lua-http-0.4/spec/h1_stream_spec.lua |
| index f9cfea9..1303f94 100644 |
| --- a/lua-http-0.4/spec/h1_stream_spec.lua |
| +++ b/lua-http-0.4/spec/h1_stream_spec.lua |
| @@ -295,6 +295,33 @@ describe("http1 stream", function() |
| server:close() |
| client:close() |
| end) |
| + it("Doesn't hang when a content-length delimited stream is closed", function() |
| + local server, client = new_pair(1.1) |
| + local cq = cqueues.new() |
| + cq:wrap(function() |
| + local stream = client:new_stream() |
| + local headers = new_headers() |
| + headers:append(":method", "GET") |
| + headers:append(":scheme", "http") |
| + headers:append(":authority", "myauthority") |
| + headers:append(":path", "/a") |
| + assert(stream:write_headers(headers, true)) |
| + end) |
| + cq:wrap(function() |
| + local stream = server:get_next_incoming_stream() |
| + assert(stream:get_headers()) |
| + local res_headers = new_headers() |
| + res_headers:append(":status", "200") |
| + res_headers:append("content-length", "100") |
| + assert(stream:write_headers(res_headers, false)) |
| + assert(stream:write_chunk("foo", false)) |
| + assert(stream:shutdown()) |
| + end) |
| + assert_loop(cq, TEST_TIMEOUT) |
| + assert.truthy(cq:empty()) |
| + server:close() |
| + client:close() |
| + end) |
| it("allows pipelining", function() |
| local server, client = new_pair(1.1) |
| local cq = cqueues.new() |
| -- |
| 2.40.1 |
| |