Skip to content

Commit 13800ed

Browse files
committed
http/stream_common: Add .count option to write_body_from_file
1 parent deee7ce commit 13800ed

File tree

3 files changed

+82
-4
lines changed

3 files changed

+82
-4
lines changed

doc/interfaces/stream.md

+2
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,8 @@ Writes the string `str` to the stream and ends the stream. On error, returns `ni
9393

9494
- `options` is a table containing:
9595
- `.file` (file)
96+
- `.count` (positive integer): number of bytes of `file` to write
97+
defaults to infinity (the whole file will be written)
9698

9799
Writes the contents of file `file` to the stream and ends the stream. `file` will not be automatically seeked, so ensure it is at the correct offset before calling. On error, returns `nil`, an error message and an error number.
98100

http/stream_common.lua

+12-4
Original file line numberDiff line numberDiff line change
@@ -154,25 +154,33 @@ end
154154

155155
function stream_methods:write_body_from_file(options, timeout)
156156
local deadline = timeout and (monotime()+timeout)
157-
local file
157+
local file, count
158158
if io.type(options) then -- lua-http <= 0.2 took a file handle
159159
file = options
160160
else
161161
file = options.file
162+
count = options.count
162163
end
163-
-- Can't use :lines here as in Lua 5.1 it doesn't take a parameter
164-
while true do
165-
local chunk, err = file:read(CHUNK_SIZE)
164+
if count == nil then
165+
count = math.huge
166+
elseif type(count) ~= "number" or count < 0 or count % 1 ~= 0 then
167+
error("invalid .count parameter (expected positive integer)")
168+
end
169+
while count > 0 do
170+
local chunk, err = file:read(math.min(CHUNK_SIZE, count))
166171
if chunk == nil then
167172
if err then
168173
error(err)
174+
elseif count ~= math.huge and count > 0 then
175+
error("unexpected EOF")
169176
end
170177
break
171178
end
172179
local ok, err2, errno2 = self:write_chunk(chunk, false, deadline and (deadline-monotime()))
173180
if not ok then
174181
return nil, err2, errno2
175182
end
183+
count = count - #chunk
176184
end
177185
return self:write_chunk("", true, deadline and (deadline-monotime()))
178186
end

spec/stream_common_spec.lua

+68
Original file line numberDiff line numberDiff line change
@@ -127,5 +127,73 @@ describe("http.stream_common", function()
127127
client:close()
128128
server:close()
129129
end)
130+
it("validates .count option", function()
131+
local server, client = new_pair(1.1)
132+
local cq = cqueues.new()
133+
cq:wrap(function()
134+
local stream = client:new_stream()
135+
assert(stream:write_headers(new_request_headers(), false))
136+
assert.has_error(function()
137+
stream:write_body_from_file({
138+
file = io.tmpfile();
139+
count = "invalid count field";
140+
})
141+
end)
142+
end)
143+
cq:wrap(function()
144+
assert(server:get_next_incoming_stream())
145+
end)
146+
assert_loop(cq, TEST_TIMEOUT)
147+
assert.truthy(cq:empty())
148+
client:close()
149+
server:close()
150+
end)
151+
it("limits number of bytes when using .count option", function()
152+
local server, client = new_pair(1.1)
153+
local cq = cqueues.new()
154+
cq:wrap(function()
155+
local file = io.tmpfile()
156+
assert(file:write("hello world!"))
157+
assert(file:seek("set"))
158+
local stream = client:new_stream()
159+
assert(stream:write_headers(new_request_headers(), false))
160+
assert(stream:write_body_from_file({
161+
file = file;
162+
count = 5;
163+
}))
164+
end)
165+
cq:wrap(function()
166+
local stream = assert(server:get_next_incoming_stream())
167+
assert.same("hello", assert(stream:get_body_as_string()))
168+
end)
169+
assert_loop(cq, TEST_TIMEOUT)
170+
assert.truthy(cq:empty())
171+
client:close()
172+
server:close()
173+
end)
174+
it("reports an error on early EOF", function()
175+
local server, client = new_pair(1.1)
176+
local cq = cqueues.new()
177+
cq:wrap(function()
178+
local file = io.tmpfile()
179+
assert(file:write("hello world!"))
180+
assert(file:seek("set"))
181+
local stream = client:new_stream()
182+
assert(stream:write_headers(new_request_headers(), false))
183+
assert.has_error(function()
184+
assert(stream:write_body_from_file({
185+
file = file;
186+
count = 50; -- longer than the file
187+
}))
188+
end)
189+
end)
190+
cq:wrap(function()
191+
assert(server:get_next_incoming_stream())
192+
end)
193+
assert_loop(cq, TEST_TIMEOUT)
194+
assert.truthy(cq:empty())
195+
client:close()
196+
server:close()
197+
end)
130198
end)
131199
end)

0 commit comments

Comments
 (0)