diff options
Diffstat (limited to 'src/http.lua')
| -rw-r--r-- | src/http.lua | 219 |
1 files changed, 104 insertions, 115 deletions
diff --git a/src/http.lua b/src/http.lua index 74c29ba..629bf65 100644 --- a/src/http.lua +++ b/src/http.lua | |||
| @@ -10,12 +10,11 @@ if not LUASOCKET_LIBNAME then error('module requires LuaSocket') end | |||
| 10 | -- get LuaSocket namespace | 10 | -- get LuaSocket namespace |
| 11 | local socket = _G[LUASOCKET_LIBNAME] | 11 | local socket = _G[LUASOCKET_LIBNAME] |
| 12 | if not socket then error('module requires LuaSocket') end | 12 | if not socket then error('module requires LuaSocket') end |
| 13 | -- create smtp namespace inside LuaSocket namespace | 13 | -- create namespace inside LuaSocket namespace |
| 14 | local http = socket.http or {} | 14 | socket.http = socket.http or {} |
| 15 | socket.http = http | 15 | -- make all module globals fall into namespace |
| 16 | -- make all module globals fall into smtp namespace | 16 | setmetatable(socket.http, { __index = _G }) |
| 17 | setmetatable(http, { __index = _G }) | 17 | setfenv(1, socket.http) |
| 18 | setfenv(1, http) | ||
| 19 | 18 | ||
| 20 | ----------------------------------------------------------------------------- | 19 | ----------------------------------------------------------------------------- |
| 21 | -- Program constants | 20 | -- Program constants |
| @@ -27,7 +26,18 @@ PORT = 80 | |||
| 27 | -- user agent field sent in request | 26 | -- user agent field sent in request |
| 28 | USERAGENT = socket.version | 27 | USERAGENT = socket.version |
| 29 | -- block size used in transfers | 28 | -- block size used in transfers |
| 30 | BLOCKSIZE = 8192 | 29 | BLOCKSIZE = 2048 |
| 30 | |||
| 31 | ----------------------------------------------------------------------------- | ||
| 32 | -- Function return value selectors | ||
| 33 | ----------------------------------------------------------------------------- | ||
| 34 | local function second(a, b) | ||
| 35 | return b | ||
| 36 | end | ||
| 37 | |||
| 38 | local function third(a, b, c) | ||
| 39 | return c | ||
| 40 | end | ||
| 31 | 41 | ||
| 32 | ----------------------------------------------------------------------------- | 42 | ----------------------------------------------------------------------------- |
| 33 | -- Tries to get a pattern from the server and closes socket on error | 43 | -- Tries to get a pattern from the server and closes socket on error |
| @@ -47,7 +57,7 @@ end | |||
| 47 | ----------------------------------------------------------------------------- | 57 | ----------------------------------------------------------------------------- |
| 48 | -- Tries to send data to the server and closes socket on error | 58 | -- Tries to send data to the server and closes socket on error |
| 49 | -- sock: socket connected to the server | 59 | -- sock: socket connected to the server |
| 50 | -- data: data to send | 60 | -- ...: data to send |
| 51 | -- Returns | 61 | -- Returns |
| 52 | -- err: error message if any, nil if successfull | 62 | -- err: error message if any, nil if successfull |
| 53 | ----------------------------------------------------------------------------- | 63 | ----------------------------------------------------------------------------- |
| @@ -68,11 +78,9 @@ end | |||
| 68 | -- err: error message if any | 78 | -- err: error message if any |
| 69 | ----------------------------------------------------------------------------- | 79 | ----------------------------------------------------------------------------- |
| 70 | local function receive_status(sock) | 80 | local function receive_status(sock) |
| 71 | local line, err | 81 | local line, err = try_receiving(sock) |
| 72 | line, err = try_receiving(sock) | ||
| 73 | if not err then | 82 | if not err then |
| 74 | local code, _ | 83 | local code = third(string.find(line, "HTTP/%d*%.%d* (%d%d%d)")) |
| 75 | _, _, code = string.find(line, "HTTP/%d*%.%d* (%d%d%d)") | ||
| 76 | return tonumber(code), line | 84 | return tonumber(code), line |
| 77 | else return nil, nil, err end | 85 | else return nil, nil, err end |
| 78 | end | 86 | end |
| @@ -121,7 +129,7 @@ local function receive_headers(sock, headers) | |||
| 121 | end | 129 | end |
| 122 | 130 | ||
| 123 | ----------------------------------------------------------------------------- | 131 | ----------------------------------------------------------------------------- |
| 124 | -- Aborts a receive callback | 132 | -- Aborts a sink with an error message |
| 125 | -- Input | 133 | -- Input |
| 126 | -- cb: callback function | 134 | -- cb: callback function |
| 127 | -- err: error message to pass to callback | 135 | -- err: error message to pass to callback |
| @@ -129,8 +137,8 @@ end | |||
| 129 | -- callback return or if nil err | 137 | -- callback return or if nil err |
| 130 | ----------------------------------------------------------------------------- | 138 | ----------------------------------------------------------------------------- |
| 131 | local function abort(cb, err) | 139 | local function abort(cb, err) |
| 132 | local go, err_or_f = cb(nil, err) | 140 | local go, cb_err = cb(nil, err) |
| 133 | return err_or_f or err | 141 | return cb_err or err |
| 134 | end | 142 | end |
| 135 | 143 | ||
| 136 | ----------------------------------------------------------------------------- | 144 | ----------------------------------------------------------------------------- |
| @@ -138,41 +146,36 @@ end | |||
| 138 | -- Input | 146 | -- Input |
| 139 | -- sock: socket connected to the server | 147 | -- sock: socket connected to the server |
| 140 | -- headers: header set in which to include trailer headers | 148 | -- headers: header set in which to include trailer headers |
| 141 | -- receive_cb: function to receive chunks | 149 | -- sink: response message body sink |
| 142 | -- Returns | 150 | -- Returns |
| 143 | -- nil if successfull or an error message in case of error | 151 | -- nil if successfull or an error message in case of error |
| 144 | ----------------------------------------------------------------------------- | 152 | ----------------------------------------------------------------------------- |
| 145 | local function receive_body_bychunks(sock, headers, receive_cb) | 153 | local function receive_body_bychunks(sock, headers, sink) |
| 146 | local chunk, size, line, err, go, err_or_f, _ | 154 | local chunk, size, line, err, go |
| 147 | while 1 do | 155 | while 1 do |
| 148 | -- get chunk size, skip extention | 156 | -- get chunk size, skip extention |
| 149 | line, err = try_receiving(sock) | 157 | line, err = try_receiving(sock) |
| 150 | if err then return abort(receive_cb, err) end | 158 | if err then return abort(sink, err) end |
| 151 | size = tonumber(string.gsub(line, ";.*", ""), 16) | 159 | size = tonumber(string.gsub(line, ";.*", ""), 16) |
| 152 | if not size then return abort(receive_cb, "invalid chunk size") end | 160 | if not size then return abort(sink, "invalid chunk size") end |
| 153 | -- was it the last chunk? | 161 | -- was it the last chunk? |
| 154 | if size <= 0 then break end | 162 | if size <= 0 then break end |
| 155 | -- get chunk | 163 | -- get chunk |
| 156 | chunk, err = try_receiving(sock, size) | 164 | chunk, err = try_receiving(sock, size) |
| 157 | if err then return abort(receive_cb, err) end | 165 | if err then return abort(sink, err) end |
| 158 | -- pass chunk to callback | 166 | -- pass chunk to callback |
| 159 | go, err_or_f = receive_cb(chunk) | 167 | go, err = sink(chunk) |
| 160 | -- see if callback needs to be replaced | ||
| 161 | receive_cb = err_or_f or receive_cb | ||
| 162 | -- see if callback aborted | 168 | -- see if callback aborted |
| 163 | if not go then return err_or_f or "aborted by callback" end | 169 | if not go then return err or "aborted by callback" end |
| 164 | -- skip CRLF on end of chunk | 170 | -- skip CRLF on end of chunk |
| 165 | _, err = try_receiving(sock) | 171 | err = second(try_receiving(sock)) |
| 166 | if err then return abort(receive_cb, err) end | 172 | if err then return abort(sink, err) end |
| 167 | end | 173 | end |
| 168 | -- the server should not send trailer headers because we didn't send a | 174 | -- servers shouldn't send trailer headers, but who trusts them? |
| 169 | -- header informing it we know how to deal with them. we do not risk | 175 | err = second(receive_headers(sock, headers)) |
| 170 | -- being caught unprepaired. | 176 | if err then return abort(sink, err) end |
| 171 | _, err = receive_headers(sock, headers) | ||
| 172 | if err then return abort(receive_cb, err) end | ||
| 173 | -- let callback know we are done | 177 | -- let callback know we are done |
| 174 | _, err_or_f = receive_cb("") | 178 | return second(sink(nil)) |
| 175 | return err_or_f | ||
| 176 | end | 179 | end |
| 177 | 180 | ||
| 178 | ----------------------------------------------------------------------------- | 181 | ----------------------------------------------------------------------------- |
| @@ -180,94 +183,84 @@ end | |||
| 180 | -- Input | 183 | -- Input |
| 181 | -- sock: socket connected to the server | 184 | -- sock: socket connected to the server |
| 182 | -- length: message body length | 185 | -- length: message body length |
| 183 | -- receive_cb: function to receive chunks | 186 | -- sink: response message body sink |
| 184 | -- Returns | 187 | -- Returns |
| 185 | -- nil if successfull or an error message in case of error | 188 | -- nil if successfull or an error message in case of error |
| 186 | ----------------------------------------------------------------------------- | 189 | ----------------------------------------------------------------------------- |
| 187 | local function receive_body_bylength(sock, length, receive_cb) | 190 | local function receive_body_bylength(sock, length, sink) |
| 188 | while length > 0 do | 191 | while length > 0 do |
| 189 | local size = math.min(BLOCKSIZE, length) | 192 | local size = math.min(BLOCKSIZE, length) |
| 190 | local chunk, err = sock:receive(size) | 193 | local chunk, err = sock:receive(size) |
| 191 | local go, err_or_f = receive_cb(chunk) | 194 | local go, cb_err = sink(chunk) |
| 192 | length = length - string.len(chunk) | 195 | length = length - string.len(chunk) |
| 193 | -- see if callback aborted | 196 | -- see if callback aborted |
| 194 | if not go then return err_or_f or "aborted by callback" end | 197 | if not go then return cb_err or "aborted by callback" end |
| 195 | -- see if callback needs to be replaced | ||
| 196 | receive_cb = err_or_f or receive_cb | ||
| 197 | -- see if there was an error | 198 | -- see if there was an error |
| 198 | if err and length > 0 then return abort(receive_cb, err) end | 199 | if err and length > 0 then return abort(sink, err) end |
| 199 | end | 200 | end |
| 200 | local _, err_or_f = receive_cb("") | 201 | return second(sink(nil)) |
| 201 | return err_or_f | ||
| 202 | end | 202 | end |
| 203 | 203 | ||
| 204 | ----------------------------------------------------------------------------- | 204 | ----------------------------------------------------------------------------- |
| 205 | -- Receives a message body by content-length | 205 | -- Receives a message body until the conection is closed |
| 206 | -- Input | 206 | -- Input |
| 207 | -- sock: socket connected to the server | 207 | -- sock: socket connected to the server |
| 208 | -- receive_cb: function to receive chunks | 208 | -- sink: response message body sink |
| 209 | -- Returns | 209 | -- Returns |
| 210 | -- nil if successfull or an error message in case of error | 210 | -- nil if successfull or an error message in case of error |
| 211 | ----------------------------------------------------------------------------- | 211 | ----------------------------------------------------------------------------- |
| 212 | local function receive_body_untilclosed(sock, receive_cb) | 212 | local function receive_body_untilclosed(sock, sink) |
| 213 | while 1 do | 213 | while 1 do |
| 214 | local chunk, err = sock:receive(BLOCKSIZE) | 214 | local chunk, err = sock:receive(BLOCKSIZE) |
| 215 | local go, err_or_f = receive_cb(chunk) | 215 | local go, cb_err = sink(chunk) |
| 216 | -- see if callback aborted | 216 | -- see if callback aborted |
| 217 | if not go then return err_or_f or "aborted by callback" end | 217 | if not go then return cb_err or "aborted by callback" end |
| 218 | -- see if callback needs to be replaced | ||
| 219 | receive_cb = err_or_f or receive_cb | ||
| 220 | -- see if we are done | 218 | -- see if we are done |
| 221 | if err == "closed" then | 219 | if err == "closed" then return chunk and second(sink(nil)) end |
| 222 | if chunk ~= "" then | ||
| 223 | go, err_or_f = receive_cb("") | ||
| 224 | return err_or_f | ||
| 225 | end | ||
| 226 | end | ||
| 227 | -- see if there was an error | 220 | -- see if there was an error |
| 228 | if err then return abort(receive_cb, err) end | 221 | if err then return abort(sink, err) end |
| 229 | end | 222 | end |
| 230 | end | 223 | end |
| 231 | 224 | ||
| 232 | ----------------------------------------------------------------------------- | 225 | ----------------------------------------------------------------------------- |
| 233 | -- Receives HTTP response body | 226 | -- Receives the HTTP response body |
| 234 | -- Input | 227 | -- Input |
| 235 | -- sock: socket connected to the server | 228 | -- sock: socket connected to the server |
| 236 | -- headers: response header fields | 229 | -- headers: response header fields |
| 237 | -- receive_cb: function to receive chunks | 230 | -- sink: response message body sink |
| 238 | -- Returns | 231 | -- Returns |
| 239 | -- nil if successfull or an error message in case of error | 232 | -- nil if successfull or an error message in case of error |
| 240 | ----------------------------------------------------------------------------- | 233 | ----------------------------------------------------------------------------- |
| 241 | local function receive_body(sock, headers, receive_cb) | 234 | local function receive_body(sock, headers, sink) |
| 235 | -- make sure sink is not fancy | ||
| 236 | sink = ltn12.sink.simplify(sink) | ||
| 242 | local te = headers["transfer-encoding"] | 237 | local te = headers["transfer-encoding"] |
| 243 | if te and te ~= "identity" then | 238 | if te and te ~= "identity" then |
| 244 | -- get by chunked transfer-coding of message body | 239 | -- get by chunked transfer-coding of message body |
| 245 | return receive_body_bychunks(sock, headers, receive_cb) | 240 | return receive_body_bychunks(sock, headers, sink) |
| 246 | elseif tonumber(headers["content-length"]) then | 241 | elseif tonumber(headers["content-length"]) then |
| 247 | -- get by content-length | 242 | -- get by content-length |
| 248 | local length = tonumber(headers["content-length"]) | 243 | local length = tonumber(headers["content-length"]) |
| 249 | return receive_body_bylength(sock, length, receive_cb) | 244 | return receive_body_bylength(sock, length, sink) |
| 250 | else | 245 | else |
| 251 | -- get it all until connection closes | 246 | -- get it all until connection closes |
| 252 | return receive_body_untilclosed(sock, receive_cb) | 247 | return receive_body_untilclosed(sock, sink) |
| 253 | end | 248 | end |
| 254 | end | 249 | end |
| 255 | 250 | ||
| 256 | ----------------------------------------------------------------------------- | 251 | ----------------------------------------------------------------------------- |
| 257 | -- Sends data comming from a callback | 252 | -- Sends the HTTP request message body in chunks |
| 258 | -- Input | 253 | -- Input |
| 259 | -- data: data connection | 254 | -- data: data connection |
| 260 | -- send_cb: callback to produce file contents | 255 | -- source: request message body source |
| 261 | -- Returns | 256 | -- Returns |
| 262 | -- nil if successfull, or an error message in case of error | 257 | -- nil if successfull, or an error message in case of error |
| 263 | ----------------------------------------------------------------------------- | 258 | ----------------------------------------------------------------------------- |
| 264 | local function send_body_bychunks(data, send_cb) | 259 | local function send_body_bychunks(data, source) |
| 265 | while 1 do | 260 | while 1 do |
| 266 | local chunk, err_or_f = send_cb() | 261 | local chunk, cb_err = source() |
| 267 | -- check if callback aborted | 262 | -- check if callback aborted |
| 268 | if not chunk then return err_or_f or "aborted by callback" end | 263 | if not chunk then return cb_err or "aborted by callback" end |
| 269 | -- check if callback should be replaced | ||
| 270 | send_cb = err_or_f or send_cb | ||
| 271 | -- if we are done, send last-chunk | 264 | -- if we are done, send last-chunk |
| 272 | if chunk == "" then return try_sending(data, "0\r\n\r\n") end | 265 | if chunk == "" then return try_sending(data, "0\r\n\r\n") end |
| 273 | -- else send middle chunk | 266 | -- else send middle chunk |
| @@ -281,22 +274,18 @@ local function send_body_bychunks(data, send_cb) | |||
| 281 | end | 274 | end |
| 282 | 275 | ||
| 283 | ----------------------------------------------------------------------------- | 276 | ----------------------------------------------------------------------------- |
| 284 | -- Sends data comming from a callback | 277 | -- Sends the HTTP request message body |
| 285 | -- Input | 278 | -- Input |
| 286 | -- data: data connection | 279 | -- data: data connection |
| 287 | -- send_cb: callback to produce body contents | 280 | -- source: request message body source |
| 288 | -- Returns | 281 | -- Returns |
| 289 | -- nil if successfull, or an error message in case of error | 282 | -- nil if successfull, or an error message in case of error |
| 290 | ----------------------------------------------------------------------------- | 283 | ----------------------------------------------------------------------------- |
| 291 | local function send_body_bylength(data, send_cb) | 284 | local function send_body(data, source) |
| 292 | while 1 do | 285 | while 1 do |
| 293 | local chunk, err_or_f = send_cb() | 286 | local chunk, cb_err = source() |
| 294 | -- check if callback aborted | ||
| 295 | if not chunk then return err_or_f or "aborted by callback" end | ||
| 296 | -- check if callback should be replaced | ||
| 297 | send_cb = err_or_f or send_cb | ||
| 298 | -- check if callback is done | 287 | -- check if callback is done |
| 299 | if chunk == "" then return end | 288 | if not chunk then return cb_err end |
| 300 | -- send data | 289 | -- send data |
| 301 | local err = try_sending(data, chunk) | 290 | local err = try_sending(data, chunk) |
| 302 | if err then return err end | 291 | if err then return err end |
| @@ -304,10 +293,10 @@ local function send_body_bylength(data, send_cb) | |||
| 304 | end | 293 | end |
| 305 | 294 | ||
| 306 | ----------------------------------------------------------------------------- | 295 | ----------------------------------------------------------------------------- |
| 307 | -- Sends mime headers | 296 | -- Sends request headers |
| 308 | -- Input | 297 | -- Input |
| 309 | -- sock: server socket | 298 | -- sock: server socket |
| 310 | -- headers: table with mime headers to be sent | 299 | -- headers: table with headers to be sent |
| 311 | -- Returns | 300 | -- Returns |
| 312 | -- err: error message if any | 301 | -- err: error message if any |
| 313 | ----------------------------------------------------------------------------- | 302 | ----------------------------------------------------------------------------- |
| @@ -330,27 +319,29 @@ end | |||
| 330 | -- method: request method to be used | 319 | -- method: request method to be used |
| 331 | -- uri: request uri | 320 | -- uri: request uri |
| 332 | -- headers: request headers to be sent | 321 | -- headers: request headers to be sent |
| 333 | -- body_cb: callback to send request message body | 322 | -- source: request message body source |
| 334 | -- Returns | 323 | -- Returns |
| 335 | -- err: nil in case of success, error message otherwise | 324 | -- err: nil in case of success, error message otherwise |
| 336 | ----------------------------------------------------------------------------- | 325 | ----------------------------------------------------------------------------- |
| 337 | local function send_request(sock, method, uri, headers, body_cb) | 326 | local function send_request(sock, method, uri, headers, source) |
| 338 | local chunk, size, done, err | 327 | local chunk, size, done, err |
| 339 | -- send request line | 328 | -- send request line |
| 340 | err = try_sending(sock, method .. " " .. uri .. " HTTP/1.1\r\n") | 329 | err = try_sending(sock, method .. " " .. uri .. " HTTP/1.1\r\n") |
| 341 | if err then return err end | 330 | if err then return err end |
| 342 | if body_cb and not headers["content-length"] then | 331 | if source and not headers["content-length"] then |
| 343 | headers["transfer-encoding"] = "chunked" | 332 | headers["transfer-encoding"] = "chunked" |
| 344 | end | 333 | end |
| 345 | -- send request headers | 334 | -- send request headers |
| 346 | err = send_headers(sock, headers) | 335 | err = send_headers(sock, headers) |
| 347 | if err then return err end | 336 | if err then return err end |
| 348 | -- send request message body, if any | 337 | -- send request message body, if any |
| 349 | if body_cb then | 338 | if source then |
| 350 | if not headers["content-length"] then | 339 | -- make sure source is not fancy |
| 351 | return send_body_bychunks(sock, body_cb) | 340 | source = ltn12.source.simplify(source) |
| 341 | if headers["content-length"] then | ||
| 342 | return send_body(sock, source) | ||
| 352 | else | 343 | else |
| 353 | return send_body_bylength(sock, body_cb) | 344 | return send_body_bychunks(sock, source) |
| 354 | end | 345 | end |
| 355 | end | 346 | end |
| 356 | end | 347 | end |
| @@ -415,23 +406,23 @@ end | |||
| 415 | -- Input | 406 | -- Input |
| 416 | -- reqt: a table with the original request information | 407 | -- reqt: a table with the original request information |
| 417 | -- parsed: parsed request URL | 408 | -- parsed: parsed request URL |
| 418 | -- respt: a table with the server response information | ||
| 419 | -- Returns | 409 | -- Returns |
| 420 | -- respt: result of target authorization | 410 | -- respt: result of target authorization |
| 421 | ----------------------------------------------------------------------------- | 411 | ----------------------------------------------------------------------------- |
| 422 | local function authorize(reqt, parsed, respt) | 412 | local function authorize(reqt, parsed) |
| 423 | reqt.headers["authorization"] = "Basic " .. | 413 | reqt.headers["authorization"] = "Basic " .. |
| 424 | (socket.mime.b64(parsed.user .. ":" .. parsed.password)) | 414 | (mime.b64(parsed.user .. ":" .. parsed.password)) |
| 425 | local autht = { | 415 | local autht = { |
| 426 | nredirects = reqt.nredirects, | 416 | nredirects = reqt.nredirects, |
| 427 | method = reqt.method, | 417 | method = reqt.method, |
| 428 | url = reqt.url, | 418 | url = reqt.url, |
| 429 | body_cb = reqt.body_cb, | 419 | source = reqt.source, |
| 420 | sink = reqt.sink, | ||
| 430 | headers = reqt.headers, | 421 | headers = reqt.headers, |
| 431 | timeout = reqt.timeout, | 422 | timeout = reqt.timeout, |
| 432 | proxy = reqt.proxy, | 423 | proxy = reqt.proxy, |
| 433 | } | 424 | } |
| 434 | return request_cb(autht, respt) | 425 | return request_cb(autht) |
| 435 | end | 426 | end |
| 436 | 427 | ||
| 437 | ----------------------------------------------------------------------------- | 428 | ----------------------------------------------------------------------------- |
| @@ -443,8 +434,8 @@ end | |||
| 443 | -- 1 if we should redirect, nil otherwise | 434 | -- 1 if we should redirect, nil otherwise |
| 444 | ----------------------------------------------------------------------------- | 435 | ----------------------------------------------------------------------------- |
| 445 | local function should_redirect(reqt, respt) | 436 | local function should_redirect(reqt, respt) |
| 446 | return (reqt.redirect ~= false) and | 437 | return (reqt.redirect ~= false) and |
| 447 | (respt.code == 301 or respt.code == 302) and | 438 | (respt.code == 301 or respt.code == 302) and |
| 448 | (reqt.method == "GET" or reqt.method == "HEAD") and | 439 | (reqt.method == "GET" or reqt.method == "HEAD") and |
| 449 | not (reqt.nredirects and reqt.nredirects >= 5) | 440 | not (reqt.nredirects and reqt.nredirects >= 5) |
| 450 | end | 441 | end |
| @@ -453,8 +444,7 @@ end | |||
| 453 | -- Returns the result of a request following a server redirect message. | 444 | -- Returns the result of a request following a server redirect message. |
| 454 | -- Input | 445 | -- Input |
| 455 | -- reqt: a table with the original request information | 446 | -- reqt: a table with the original request information |
| 456 | -- respt: a table with the following fields: | 447 | -- respt: response table of previous attempt |
| 457 | -- body_cb: response method body receive-callback | ||
| 458 | -- Returns | 448 | -- Returns |
| 459 | -- respt: result of target redirection | 449 | -- respt: result of target redirection |
| 460 | ----------------------------------------------------------------------------- | 450 | ----------------------------------------------------------------------------- |
| @@ -467,12 +457,13 @@ local function redirect(reqt, respt) | |||
| 467 | -- the RFC says the redirect URL has to be absolute, but some | 457 | -- the RFC says the redirect URL has to be absolute, but some |
| 468 | -- servers do not respect that | 458 | -- servers do not respect that |
| 469 | url = socket.url.absolute(reqt.url, respt.headers["location"]), | 459 | url = socket.url.absolute(reqt.url, respt.headers["location"]), |
| 470 | body_cb = reqt.body_cb, | 460 | source = reqt.source, |
| 461 | sink = reqt.sink, | ||
| 471 | headers = reqt.headers, | 462 | headers = reqt.headers, |
| 472 | timeout = reqt.timeout, | 463 | timeout = reqt.timeout, |
| 473 | proxy = reqt.proxy | 464 | proxy = reqt.proxy |
| 474 | } | 465 | } |
| 475 | respt = request_cb(redirt, respt) | 466 | respt = request_cb(redirt) |
| 476 | -- we pass the location header as a clue we tried to redirect | 467 | -- we pass the location header as a clue we tried to redirect |
| 477 | if respt.headers then respt.headers.location = redirt.url end | 468 | if respt.headers then respt.headers.location = redirt.url end |
| 478 | return respt | 469 | return respt |
| @@ -562,10 +553,9 @@ end | |||
| 562 | -- url: target uniform resource locator | 553 | -- url: target uniform resource locator |
| 563 | -- user, password: authentication information | 554 | -- user, password: authentication information |
| 564 | -- headers: request headers to send, or nil if none | 555 | -- headers: request headers to send, or nil if none |
| 565 | -- body_cb: request message body send-callback, or nil if none | 556 | -- source: request message body source, or nil if none |
| 557 | -- sink: response message body sink | ||
| 566 | -- redirect: should we refrain from following a server redirect message? | 558 | -- redirect: should we refrain from following a server redirect message? |
| 567 | -- respt: a table with the following fields: | ||
| 568 | -- body_cb: response method body receive-callback | ||
| 569 | -- Returns | 559 | -- Returns |
| 570 | -- respt: a table with the following fields: | 560 | -- respt: a table with the following fields: |
| 571 | -- headers: response header fields received, or nil if failed | 561 | -- headers: response header fields received, or nil if failed |
| @@ -573,7 +563,7 @@ end | |||
| 573 | -- code: server status code, or nil if failed | 563 | -- code: server status code, or nil if failed |
| 574 | -- error: error message, or nil if successfull | 564 | -- error: error message, or nil if successfull |
| 575 | ----------------------------------------------------------------------------- | 565 | ----------------------------------------------------------------------------- |
| 576 | function request_cb(reqt, respt) | 566 | function request_cb(reqt) |
| 577 | local sock, ret | 567 | local sock, ret |
| 578 | local parsed = socket.url.parse(reqt.url, { | 568 | local parsed = socket.url.parse(reqt.url, { |
| 579 | host = "", | 569 | host = "", |
| @@ -581,6 +571,7 @@ function request_cb(reqt, respt) | |||
| 581 | path ="/", | 571 | path ="/", |
| 582 | scheme = "http" | 572 | scheme = "http" |
| 583 | }) | 573 | }) |
| 574 | local respt = {} | ||
| 584 | if parsed.scheme ~= "http" then | 575 | if parsed.scheme ~= "http" then |
| 585 | respt.error = string.format("unknown scheme '%s'", parsed.scheme) | 576 | respt.error = string.format("unknown scheme '%s'", parsed.scheme) |
| 586 | return respt | 577 | return respt |
| @@ -597,7 +588,7 @@ function request_cb(reqt, respt) | |||
| 597 | if not sock then return respt end | 588 | if not sock then return respt end |
| 598 | -- send request message | 589 | -- send request message |
| 599 | respt.error = send_request(sock, reqt.method, | 590 | respt.error = send_request(sock, reqt.method, |
| 600 | request_uri(reqt, parsed), reqt.headers, reqt.body_cb) | 591 | request_uri(reqt, parsed), reqt.headers, reqt.source) |
| 601 | if respt.error then | 592 | if respt.error then |
| 602 | sock:close() | 593 | sock:close() |
| 603 | return respt | 594 | return respt |
| @@ -619,18 +610,18 @@ function request_cb(reqt, respt) | |||
| 619 | -- decide what to do based on request and response parameters | 610 | -- decide what to do based on request and response parameters |
| 620 | if should_redirect(reqt, respt) then | 611 | if should_redirect(reqt, respt) then |
| 621 | -- drop the body | 612 | -- drop the body |
| 622 | receive_body(sock, respt.headers, function (c, e) return 1 end) | 613 | receive_body(sock, respt.headers, ltn12.sink.null()) |
| 623 | -- we are done with this connection | 614 | -- we are done with this connection |
| 624 | sock:close() | 615 | sock:close() |
| 625 | return redirect(reqt, respt) | 616 | return redirect(reqt, respt) |
| 626 | elseif should_authorize(reqt, parsed, respt) then | 617 | elseif should_authorize(reqt, parsed, respt) then |
| 627 | -- drop the body | 618 | -- drop the body |
| 628 | receive_body(sock, respt.headers, function (c, e) return 1 end) | 619 | receive_body(sock, respt.headers, ltn12.sink.null()) |
| 629 | -- we are done with this connection | 620 | -- we are done with this connection |
| 630 | sock:close() | 621 | sock:close() |
| 631 | return authorize(reqt, parsed, respt) | 622 | return authorize(reqt, parsed, respt) |
| 632 | elseif should_receive_body(reqt, respt) then | 623 | elseif should_receive_body(reqt, respt) then |
| 633 | respt.error = receive_body(sock, respt.headers, respt.body_cb) | 624 | respt.error = receive_body(sock, respt.headers, reqt.sink) |
| 634 | if respt.error then return respt end | 625 | if respt.error then return respt end |
| 635 | sock:close() | 626 | sock:close() |
| 636 | return respt | 627 | return respt |
| @@ -658,13 +649,11 @@ end | |||
| 658 | -- error: error message if any | 649 | -- error: error message if any |
| 659 | ----------------------------------------------------------------------------- | 650 | ----------------------------------------------------------------------------- |
| 660 | function request(reqt) | 651 | function request(reqt) |
| 661 | local respt = {} | 652 | reqt.source = reqt.body and ltn12.source.string(reqt.body) |
| 662 | reqt.body_cb = socket.callback.send.string(reqt.body) | 653 | local t = {} |
| 663 | local concat = socket.concat.create() | 654 | reqt.sink = ltn12.sink.table(t) |
| 664 | respt.body_cb = socket.callback.receive.concat(concat) | 655 | local respt = request_cb(reqt) |
| 665 | respt = request_cb(reqt, respt) | 656 | if table.getn(t) > 0 then respt.body = table.concat(t) end |
| 666 | respt.body = concat:getresult() | ||
| 667 | respt.body_cb = nil | ||
| 668 | return respt | 657 | return respt |
| 669 | end | 658 | end |
| 670 | 659 | ||
| @@ -713,4 +702,4 @@ function post(url_or_request, body) | |||
| 713 | return respt.body, respt.headers, respt.code, respt.error | 702 | return respt.body, respt.headers, respt.code, respt.error |
| 714 | end | 703 | end |
| 715 | 704 | ||
| 716 | return http | 705 | return socket.http |
