diff options
author | Diego Nehab <diego.nehab@gmail.com> | 2019-02-24 21:29:19 -0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-02-24 21:29:19 -0300 |
commit | 9b3f7a430454dc9568dfb063fbc324b29c5134d7 (patch) | |
tree | bd2e57f3df7a04cf02a64d575e8d6f25093e3fd2 | |
parent | 38865fad3ac949d833bab7a02e8727a2ad336450 (diff) | |
parent | 2a467001f6dbadf447a79dfbd036f32b6a82ce05 (diff) | |
download | luasocket-9b3f7a430454dc9568dfb063fbc324b29c5134d7.tar.gz luasocket-9b3f7a430454dc9568dfb063fbc324b29c5134d7.tar.bz2 luasocket-9b3f7a430454dc9568dfb063fbc324b29c5134d7.zip |
Merge pull request #268 from ewestbrook/prc-multischeme
Scheme-independent connection and redirection
-rw-r--r-- | src/http.lua | 60 |
1 files changed, 41 insertions, 19 deletions
diff --git a/src/http.lua b/src/http.lua index 19b4dd3..2fa5a26 100644 --- a/src/http.lua +++ b/src/http.lua | |||
@@ -26,15 +26,24 @@ _M.TIMEOUT = 60 | |||
26 | -- user agent field sent in request | 26 | -- user agent field sent in request |
27 | _M.USERAGENT = socket._VERSION | 27 | _M.USERAGENT = socket._VERSION |
28 | 28 | ||
29 | -- supported schemes | 29 | -- supported schemes and their particulars |
30 | local SCHEMES = { | 30 | local SCHEMES = { |
31 | http = { port = 80 } | 31 | http = { |
32 | , https = { port = 443 }} | 32 | port = 80 |
33 | , create = function(t) | ||
34 | return socket.tcp end } | ||
35 | , https = { | ||
36 | port = 443 | ||
37 | , create = function(t) | ||
38 | local https = assert( | ||
39 | require("ssl.https"), 'LuaSocket: LuaSec not found') | ||
40 | local tcp = assert( | ||
41 | https.tcp, 'LuaSocket: Function tcp() not available from LuaSec') | ||
42 | return tcp(t) end }} | ||
33 | 43 | ||
34 | -- default scheme and port for document retrieval | 44 | -- default scheme and port for document retrieval |
35 | local SCHEME = 'http' | 45 | local SCHEME = 'http' |
36 | local PORT = SCHEMES[SCHEME].port | 46 | local PORT = SCHEMES[SCHEME].port |
37 | |||
38 | ----------------------------------------------------------------------------- | 47 | ----------------------------------------------------------------------------- |
39 | -- Reads MIME headers from a connection, unfolding where needed | 48 | -- Reads MIME headers from a connection, unfolding where needed |
40 | ----------------------------------------------------------------------------- | 49 | ----------------------------------------------------------------------------- |
@@ -115,13 +124,13 @@ local metat = { __index = {} } | |||
115 | 124 | ||
116 | function _M.open(host, port, create) | 125 | function _M.open(host, port, create) |
117 | -- create socket with user connect function, or with default | 126 | -- create socket with user connect function, or with default |
118 | local c = socket.try((create or socket.tcp)()) | 127 | local c = socket.try(create()) |
119 | local h = base.setmetatable({ c = c }, metat) | 128 | local h = base.setmetatable({ c = c }, metat) |
120 | -- create finalized try | 129 | -- create finalized try |
121 | h.try = socket.newtry(function() h:close() end) | 130 | h.try = socket.newtry(function() h:close() end) |
122 | -- set timeout before connecting | 131 | -- set timeout before connecting |
123 | h.try(c:settimeout(_M.TIMEOUT)) | 132 | h.try(c:settimeout(_M.TIMEOUT)) |
124 | h.try(c:connect(host, port or PORT)) | 133 | h.try(c:connect(host, port)) |
125 | -- here everything worked | 134 | -- here everything worked |
126 | return h | 135 | return h |
127 | end | 136 | end |
@@ -221,14 +230,13 @@ end | |||
221 | 230 | ||
222 | local function adjustheaders(reqt) | 231 | local function adjustheaders(reqt) |
223 | -- default headers | 232 | -- default headers |
224 | local headhost = reqt.host | 233 | local host = reqt.host |
225 | local headport = tostring(reqt.port) | 234 | local port = tostring(reqt.port) |
226 | local schemeport = tostring(SCHEMES[reqt.scheme].port) | 235 | if port ~= tostring(SCHEMES[reqt.scheme].port) then |
227 | if headport ~= schemeport then | 236 | host = host .. ':' .. port end |
228 | headhost = headhost .. ':' .. headport end | ||
229 | local lower = { | 237 | local lower = { |
230 | ["user-agent"] = _M.USERAGENT, | 238 | ["user-agent"] = _M.USERAGENT, |
231 | ["host"] = headhost, | 239 | ["host"] = host, |
232 | ["connection"] = "close, TE", | 240 | ["connection"] = "close, TE", |
233 | ["te"] = "trailers" | 241 | ["te"] = "trailers" |
234 | } | 242 | } |
@@ -267,8 +275,13 @@ local function adjustrequest(reqt) | |||
267 | local nreqt = reqt.url and url.parse(reqt.url, default) or {} | 275 | local nreqt = reqt.url and url.parse(reqt.url, default) or {} |
268 | -- explicit components override url | 276 | -- explicit components override url |
269 | for i,v in base.pairs(reqt) do nreqt[i] = v end | 277 | for i,v in base.pairs(reqt) do nreqt[i] = v end |
270 | if nreqt.port == "" then nreqt.port = PORT end | 278 | -- default to scheme particulars |
271 | if not (nreqt.host and nreqt.host ~= "") then | 279 | local schemedefs, host, port, method |
280 | = SCHEMES[nreqt.scheme], nreqt.host, nreqt.port, nreqt.method | ||
281 | if not nreqt.create then nreqt.create = schemedefs.create(nreqt) end | ||
282 | if not (port and port ~= '') then nreqt.port = schemedefs.port end | ||
283 | if not (method and method ~= '') then nreqt.method = 'GET' end | ||
284 | if not (host and host ~= "") then | ||
272 | socket.try(nil, "invalid host '" .. base.tostring(nreqt.host) .. "'") | 285 | socket.try(nil, "invalid host '" .. base.tostring(nreqt.host) .. "'") |
273 | end | 286 | end |
274 | -- compute uri if user hasn't overriden | 287 | -- compute uri if user hasn't overriden |
@@ -285,8 +298,10 @@ local function shouldredirect(reqt, code, headers) | |||
285 | if not location then return false end | 298 | if not location then return false end |
286 | location = string.gsub(location, "%s", "") | 299 | location = string.gsub(location, "%s", "") |
287 | if location == "" then return false end | 300 | if location == "" then return false end |
288 | local scheme = string.match(location, "^([%w][%w%+%-%.]*)%:") | 301 | local scheme = url.parse(location).scheme |
289 | if scheme and not SCHEMES[scheme] then return false end | 302 | if scheme and (not SCHEMES[scheme]) then return false end |
303 | -- avoid https downgrades | ||
304 | if ('https' == reqt.scheme) and ('https' ~= scheme) then return false end | ||
290 | return (reqt.redirect ~= false) and | 305 | return (reqt.redirect ~= false) and |
291 | (code == 301 or code == 302 or code == 303 or code == 307) and | 306 | (code == 301 or code == 302 or code == 303 or code == 307) and |
292 | (not reqt.method or reqt.method == "GET" or reqt.method == "HEAD") | 307 | (not reqt.method or reqt.method == "GET" or reqt.method == "HEAD") |
@@ -306,10 +321,16 @@ end | |||
306 | local trequest, tredirect | 321 | local trequest, tredirect |
307 | 322 | ||
308 | --[[local]] function tredirect(reqt, location) | 323 | --[[local]] function tredirect(reqt, location) |
324 | -- the RFC says the redirect URL has to be absolute, but some | ||
325 | -- servers do not respect that | ||
326 | local newurl = url.absolute(reqt.url, location) | ||
327 | -- if switching schemes, reset port and create function | ||
328 | if url.parse(newurl).scheme ~= reqt.scheme then | ||
329 | reqt.port = nil | ||
330 | reqt.create = nil end | ||
331 | -- make new request | ||
309 | local result, code, headers, status = trequest { | 332 | local result, code, headers, status = trequest { |
310 | -- the RFC says the redirect URL has to be absolute, but some | 333 | url = newurl, |
311 | -- servers do not respect that | ||
312 | url = url.absolute(reqt.url, location), | ||
313 | source = reqt.source, | 334 | source = reqt.source, |
314 | sink = reqt.sink, | 335 | sink = reqt.sink, |
315 | headers = reqt.headers, | 336 | headers = reqt.headers, |
@@ -397,4 +418,5 @@ _M.request = socket.protect(function(reqt, body) | |||
397 | else return trequest(reqt) end | 418 | else return trequest(reqt) end |
398 | end) | 419 | end) |
399 | 420 | ||
421 | _M.schemes = SCHEMES | ||
400 | return _M | 422 | return _M |