File: stream_common.lua

package info (click to toggle)
lua-http 0.4-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 1,100 kB
  • sloc: makefile: 60; sh: 16
file content (190 lines) | stat: -rw-r--r-- 4,479 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
--[[
This module provides common functions for HTTP streams
no matter the underlying protocol version.

This is usually an internal module, and should be used by adding the
`methods` exposed to your own HTTP stream objects.
]]

local cqueues = require "cqueues"
local monotime = cqueues.monotime
local new_headers = require "http.headers".new

local CHUNK_SIZE = 2^20 -- write in 1MB chunks

local stream_methods = {}

function stream_methods:checktls()
	return self.connection:checktls()
end

function stream_methods:localname()
	return self.connection:localname()
end

function stream_methods:peername()
	return self.connection:peername()
end

-- 100-Continue response
local continue_headers = new_headers()
continue_headers:append(":status", "100")
function stream_methods:write_continue(timeout)
	return self:write_headers(continue_headers, false, timeout)
end

-- need helper to discard 'last' argument
-- (which would otherwise end up going in 'timeout')
local function each_chunk_helper(self)
	return self:get_next_chunk()
end
function stream_methods:each_chunk()
	return each_chunk_helper, self
end

function stream_methods:get_body_as_string(timeout)
	local deadline = timeout and (monotime()+timeout)
	local body, i = {}, 0
	while true do
		local chunk, err, errno = self:get_next_chunk(timeout)
		if chunk == nil then
			if err == nil then
				break
			else
				return nil, err, errno
			end
		end
		i = i + 1
		body[i] = chunk
		timeout = deadline and (deadline-monotime())
	end
	return table.concat(body, "", 1, i)
end

function stream_methods:get_body_chars(n, timeout)
	local deadline = timeout and (monotime()+timeout)
	local body, i, len = {}, 0, 0
	while len < n do
		local chunk, err, errno = self:get_next_chunk(timeout)
		if chunk == nil then
			if err == nil then
				break
			else
				return nil, err, errno
			end
		end
		i = i + 1
		body[i] = chunk
		len = len + #chunk
		timeout = deadline and (deadline-monotime())
	end
	if i == 0 then
		return nil
	end
	local r = table.concat(body, "", 1, i)
	if n < len then
		self:unget(r:sub(n+1, -1))
		r = r:sub(1, n)
	end
	return r
end

function stream_methods:get_body_until(pattern, plain, include_pattern, timeout)
	local deadline = timeout and (monotime()+timeout)
	local body
	while true do
		local chunk, err, errno = self:get_next_chunk(timeout)
		if chunk == nil then
			if err == nil then
				return body, err
			else
				return nil, err, errno
			end
		end
		if body then
			body = body .. chunk
		else
			body = chunk
		end
		local s, e = body:find(pattern, 1, plain)
		if s then
			if e < #body then
				self:unget(body:sub(e+1, -1))
			end
			if include_pattern then
				return body:sub(1, e)
			else
				return body:sub(1, s-1)
			end
		end
		timeout = deadline and (deadline-monotime())
	end
end

function stream_methods:save_body_to_file(file, timeout)
	local deadline = timeout and (monotime()+timeout)
	while true do
		local chunk, err, errno = self:get_next_chunk(timeout)
		if chunk == nil then
			if err == nil then
				break
			else
				return nil, err, errno
			end
		end
		assert(file:write(chunk))
		timeout = deadline and (deadline-monotime())
	end
	return true
end

function stream_methods:get_body_as_file(timeout)
	local file = assert(io.tmpfile())
	local ok, err, errno = self:save_body_to_file(file, timeout)
	if not ok then
		return nil, err, errno
	end
	assert(file:seek("set"))
	return file
end

function stream_methods:write_body_from_string(str, timeout)
	return self:write_chunk(str, true, timeout)
end

function stream_methods:write_body_from_file(options, timeout)
	local deadline = timeout and (monotime()+timeout)
	local file, count
	if io.type(options) then -- lua-http <= 0.2 took a file handle
		file = options
	else
		file = options.file
		count = options.count
	end
	if count == nil then
		count = math.huge
	elseif type(count) ~= "number" or count < 0 or count % 1 ~= 0 then
		error("invalid .count parameter (expected positive integer)")
	end
	while count > 0 do
		local chunk, err = file:read(math.min(CHUNK_SIZE, count))
		if chunk == nil then
			if err then
				error(err)
			elseif count ~= math.huge and count > 0 then
				error("unexpected EOF")
			end
			break
		end
		local ok, err2, errno2 = self:write_chunk(chunk, false, deadline and (deadline-monotime()))
		if not ok then
			return nil, err2, errno2
		end
		count = count - #chunk
	end
	return self:write_chunk("", true, deadline and (deadline-monotime()))
end

return {
	methods = stream_methods;
}