diff options
Diffstat (limited to 'src/http.lua')
-rw-r--r-- | src/http.lua | 98 |
1 files changed, 55 insertions, 43 deletions
diff --git a/src/http.lua b/src/http.lua index 1a7b101..91c52da 100644 --- a/src/http.lua +++ b/src/http.lua | |||
@@ -28,9 +28,40 @@ PORT = 80 | |||
28 | USERAGENT = socket._VERSION | 28 | USERAGENT = socket._VERSION |
29 | 29 | ||
30 | ----------------------------------------------------------------------------- | 30 | ----------------------------------------------------------------------------- |
31 | -- Reads MIME headers from a connection, unfolding where needed | ||
32 | ----------------------------------------------------------------------------- | ||
33 | local function receiveheaders(sock, headers) | ||
34 | local line, name, value, err | ||
35 | headers = headers or {} | ||
36 | -- get first line | ||
37 | line, err = sock:receive() | ||
38 | if err then return nil, err end | ||
39 | -- headers go until a blank line is found | ||
40 | while line ~= "" do | ||
41 | -- get field-name and value | ||
42 | name, value = socket.skip(2, string.find(line, "^(.-):%s*(.*)")) | ||
43 | if not (name and value) then return nil, "malformed reponse headers" end | ||
44 | name = string.lower(name) | ||
45 | -- get next line (value might be folded) | ||
46 | line, err = sock:receive() | ||
47 | if err then return nil, err end | ||
48 | -- unfold any folded values | ||
49 | while string.find(line, "^%s") do | ||
50 | value = value .. line | ||
51 | line = sock:receive() | ||
52 | if err then return nil, err end | ||
53 | end | ||
54 | -- save pair in table | ||
55 | if headers[name] then headers[name] = headers[name] .. ", " .. value | ||
56 | else headers[name] = value end | ||
57 | end | ||
58 | return headers | ||
59 | end | ||
60 | |||
61 | ----------------------------------------------------------------------------- | ||
31 | -- Extra sources and sinks | 62 | -- Extra sources and sinks |
32 | ----------------------------------------------------------------------------- | 63 | ----------------------------------------------------------------------------- |
33 | socket.sourcet["http-chunked"] = function(sock) | 64 | socket.sourcet["http-chunked"] = function(sock, headers) |
34 | return base.setmetatable({ | 65 | return base.setmetatable({ |
35 | getfd = function() return sock:getfd() end, | 66 | getfd = function() return sock:getfd() end, |
36 | dirty = function() return sock:dirty() end | 67 | dirty = function() return sock:dirty() end |
@@ -42,18 +73,15 @@ socket.sourcet["http-chunked"] = function(sock) | |||
42 | local size = base.tonumber(string.gsub(line, ";.*", ""), 16) | 73 | local size = base.tonumber(string.gsub(line, ";.*", ""), 16) |
43 | if not size then return nil, "invalid chunk size" end | 74 | if not size then return nil, "invalid chunk size" end |
44 | -- was it the last chunk? | 75 | -- was it the last chunk? |
45 | if size <= 0 then | 76 | if size > 0 then |
46 | -- skip trailer headers, if any | 77 | -- if not, get chunk and skip terminating CRLF |
47 | local line, err = sock:receive() | ||
48 | while not err and line ~= "" do | ||
49 | line, err = sock:receive() | ||
50 | end | ||
51 | return nil, err | ||
52 | else | ||
53 | -- get chunk and skip terminating CRLF | ||
54 | local chunk, err, part = sock:receive(size) | 78 | local chunk, err, part = sock:receive(size) |
55 | if chunk then sock:receive() end | 79 | if chunk then sock:receive() end |
56 | return chunk, err | 80 | return chunk, err |
81 | else | ||
82 | -- if it was, read trailers into headers table | ||
83 | headers, err = receiveheaders(sock, headers) | ||
84 | if not headers then return nil, err end | ||
57 | end | 85 | end |
58 | end | 86 | end |
59 | }) | 87 | }) |
@@ -78,8 +106,8 @@ end | |||
78 | local metat = { __index = {} } | 106 | local metat = { __index = {} } |
79 | 107 | ||
80 | -- default connect function, respecting the timeout | 108 | -- default connect function, respecting the timeout |
81 | local function connect(host, port) | 109 | local function connect(host, port, create) |
82 | local c, e = socket.tcp() | 110 | local c, e = (create or socket.tcp)() |
83 | if not c then return nil, e end | 111 | if not c then return nil, e end |
84 | c:settimeout(TIMEOUT) | 112 | c:settimeout(TIMEOUT) |
85 | local r, e = c:connect(host, port or PORT) | 113 | local r, e = c:connect(host, port or PORT) |
@@ -90,9 +118,9 @@ local function connect(host, port) | |||
90 | return c | 118 | return c |
91 | end | 119 | end |
92 | 120 | ||
93 | function open(host, port, user) | 121 | function open(host, port, create) |
94 | -- create socket with user connect function, or with default | 122 | -- create socket with user connect function, or with default |
95 | local c = socket.try((user or connect)(host, port)) | 123 | local c = socket.try(connect(host, port, create)) |
96 | -- create our http request object, pointing to the socket | 124 | -- create our http request object, pointing to the socket |
97 | local h = base.setmetatable({ c = c }, metat) | 125 | local h = base.setmetatable({ c = c }, metat) |
98 | -- make sure the object close gets called on exception | 126 | -- make sure the object close gets called on exception |
@@ -130,37 +158,16 @@ function metat.__index:receivestatusline() | |||
130 | end | 158 | end |
131 | 159 | ||
132 | function metat.__index:receiveheaders() | 160 | function metat.__index:receiveheaders() |
133 | local line, name, value | 161 | return self.try(receiveheaders(self.c)) |
134 | local headers = {} | ||
135 | -- get first line | ||
136 | line = self.try(self.c:receive()) | ||
137 | -- headers go until a blank line is found | ||
138 | while line ~= "" do | ||
139 | -- get field-name and value | ||
140 | name, value = socket.skip(2, string.find(line, "^(.-):%s*(.*)")) | ||
141 | self.try(name and value, "malformed reponse headers") | ||
142 | name = string.lower(name) | ||
143 | -- get next line (value might be folded) | ||
144 | line = self.try(self.c:receive()) | ||
145 | -- unfold any folded values | ||
146 | while string.find(line, "^%s") do | ||
147 | value = value .. line | ||
148 | line = self.try(self.c:receive()) | ||
149 | end | ||
150 | -- save pair in table | ||
151 | if headers[name] then headers[name] = headers[name] .. ", " .. value | ||
152 | else headers[name] = value end | ||
153 | end | ||
154 | return headers | ||
155 | end | 162 | end |
156 | 163 | ||
157 | function metat.__index:receivebody(headers, sink, step) | 164 | function metat.__index:receivebody(headers, sink, step) |
158 | sink = sink or ltn12.sink.null() | 165 | sink = sink or ltn12.sink.null() |
159 | step = step or ltn12.pump.step | 166 | step = step or ltn12.pump.step |
160 | local length = base.tonumber(headers["content-length"]) | 167 | local length = base.tonumber(headers["content-length"]) |
161 | local TE = headers["transfer-encoding"] | 168 | local t = headers["transfer-encoding"] -- shortcut |
162 | local mode = "default" -- connection close | 169 | local mode = "default" -- connection close |
163 | if TE and TE ~= "identity" then mode = "http-chunked" | 170 | if t and t ~= "identity" then mode = "http-chunked" |
164 | elseif base.tonumber(headers["content-length"]) then mode = "by-length" end | 171 | elseif base.tonumber(headers["content-length"]) then mode = "by-length" end |
165 | return self.try(ltn12.pump.all(socket.source(mode, self.c, length), | 172 | return self.try(ltn12.pump.all(socket.source(mode, self.c, length), |
166 | sink, step)) | 173 | sink, step)) |
@@ -198,16 +205,21 @@ local function adjustproxy(reqt) | |||
198 | end | 205 | end |
199 | 206 | ||
200 | local function adjustheaders(headers, host) | 207 | local function adjustheaders(headers, host) |
201 | local lower = {} | 208 | -- default headers |
202 | -- override with user values | 209 | local lower = { |
210 | ["user-agent"] = USERAGENT, | ||
211 | ["host"] = host, | ||
212 | ["connection"] = "close, TE", | ||
213 | ["te"] = "trailers" | ||
214 | } | ||
215 | -- override with user headers | ||
203 | for i,v in pairs(headers or lower) do | 216 | for i,v in pairs(headers or lower) do |
204 | lower[string.lower(i)] = v | 217 | lower[string.lower(i)] = v |
205 | end | 218 | end |
206 | lower["user-agent"] = lower["user-agent"] or USERAGENT | ||
207 | lower["host"] = lower["host"] or host | ||
208 | return lower | 219 | return lower |
209 | end | 220 | end |
210 | 221 | ||
222 | -- default url parts | ||
211 | local default = { | 223 | local default = { |
212 | host = "", | 224 | host = "", |
213 | port = PORT, | 225 | port = PORT, |
@@ -280,7 +292,7 @@ end | |||
280 | 292 | ||
281 | function trequest(reqt) | 293 | function trequest(reqt) |
282 | reqt = adjustrequest(reqt) | 294 | reqt = adjustrequest(reqt) |
283 | local h = open(reqt.host, reqt.port, reqt.connect) | 295 | local h = open(reqt.host, reqt.port, reqt.create) |
284 | h:sendrequestline(reqt.method, reqt.uri) | 296 | h:sendrequestline(reqt.method, reqt.uri) |
285 | h:sendheaders(reqt.headers) | 297 | h:sendheaders(reqt.headers) |
286 | if reqt.source then h:sendbody(reqt.headers, reqt.source, reqt.step) end | 298 | if reqt.source then h:sendbody(reqt.headers, reqt.source, reqt.step) end |