aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDiego Nehab <diego.nehab@gmail.com>2019-02-24 21:29:19 -0300
committerGitHub <noreply@github.com>2019-02-24 21:29:19 -0300
commit9b3f7a430454dc9568dfb063fbc324b29c5134d7 (patch)
treebd2e57f3df7a04cf02a64d575e8d6f25093e3fd2
parent38865fad3ac949d833bab7a02e8727a2ad336450 (diff)
parent2a467001f6dbadf447a79dfbd036f32b6a82ce05 (diff)
downloadluasocket-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.lua60
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
30local SCHEMES = { 30local 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
35local SCHEME = 'http' 45local SCHEME = 'http'
36local PORT = SCHEMES[SCHEME].port 46local 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
116function _M.open(host, port, create) 125function _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
127end 136end
@@ -221,14 +230,13 @@ end
221 230
222local function adjustheaders(reqt) 231local 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
306local trequest, tredirect 321local 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
398end) 419end)
399 420
421_M.schemes = SCHEMES
400return _M 422return _M