aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorE. Westbrook <github@westbrook.io>2018-07-12 01:10:11 -0600
committerE. Westbrook <github@westbrook.io>2019-02-24 15:23:16 -0700
commite587800164d5d6fb75fe34166978438c949db215 (patch)
tree626f20dad172a224b9dc96c45a08fc245d6d5f31
parent38865fad3ac949d833bab7a02e8727a2ad336450 (diff)
downloadluasocket-e587800164d5d6fb75fe34166978438c949db215.tar.gz
luasocket-e587800164d5d6fb75fe34166978438c949db215.tar.bz2
luasocket-e587800164d5d6fb75fe34166978438c949db215.zip
socket.http.request(): simultaneous support for http and https URL schemes, with caller-adjustable scheme-to-transport mappings (default "socket.http" and "ssl.https")
-rw-r--r--src/http.lua56
1 files changed, 37 insertions, 19 deletions
diff --git a/src/http.lua b/src/http.lua
index 19b4dd3..25eb25d 100644
--- a/src/http.lua
+++ b/src/http.lua
@@ -26,15 +26,20 @@ _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 return require("ssl.https").tcp(t) end }}
33 39
34-- default scheme and port for document retrieval 40-- default scheme and port for document retrieval
35local SCHEME = 'http' 41local SCHEME = 'http'
36local PORT = SCHEMES[SCHEME].port 42local PORT = SCHEMES[SCHEME].port
37
38----------------------------------------------------------------------------- 43-----------------------------------------------------------------------------
39-- Reads MIME headers from a connection, unfolding where needed 44-- Reads MIME headers from a connection, unfolding where needed
40----------------------------------------------------------------------------- 45-----------------------------------------------------------------------------
@@ -115,13 +120,13 @@ local metat = { __index = {} }
115 120
116function _M.open(host, port, create) 121function _M.open(host, port, create)
117 -- create socket with user connect function, or with default 122 -- create socket with user connect function, or with default
118 local c = socket.try((create or socket.tcp)()) 123 local c = socket.try(create())
119 local h = base.setmetatable({ c = c }, metat) 124 local h = base.setmetatable({ c = c }, metat)
120 -- create finalized try 125 -- create finalized try
121 h.try = socket.newtry(function() h:close() end) 126 h.try = socket.newtry(function() h:close() end)
122 -- set timeout before connecting 127 -- set timeout before connecting
123 h.try(c:settimeout(_M.TIMEOUT)) 128 h.try(c:settimeout(_M.TIMEOUT))
124 h.try(c:connect(host, port or PORT)) 129 h.try(c:connect(host, port))
125 -- here everything worked 130 -- here everything worked
126 return h 131 return h
127end 132end
@@ -221,14 +226,13 @@ end
221 226
222local function adjustheaders(reqt) 227local function adjustheaders(reqt)
223 -- default headers 228 -- default headers
224 local headhost = reqt.host 229 local host = reqt.host
225 local headport = tostring(reqt.port) 230 local port = tostring(reqt.port)
226 local schemeport = tostring(SCHEMES[reqt.scheme].port) 231 if port ~= tostring(SCHEMES[reqt.scheme].port) then
227 if headport ~= schemeport then 232 host = host .. ':' .. port end
228 headhost = headhost .. ':' .. headport end
229 local lower = { 233 local lower = {
230 ["user-agent"] = _M.USERAGENT, 234 ["user-agent"] = _M.USERAGENT,
231 ["host"] = headhost, 235 ["host"] = host,
232 ["connection"] = "close, TE", 236 ["connection"] = "close, TE",
233 ["te"] = "trailers" 237 ["te"] = "trailers"
234 } 238 }
@@ -267,8 +271,13 @@ local function adjustrequest(reqt)
267 local nreqt = reqt.url and url.parse(reqt.url, default) or {} 271 local nreqt = reqt.url and url.parse(reqt.url, default) or {}
268 -- explicit components override url 272 -- explicit components override url
269 for i,v in base.pairs(reqt) do nreqt[i] = v end 273 for i,v in base.pairs(reqt) do nreqt[i] = v end
270 if nreqt.port == "" then nreqt.port = PORT end 274 -- default to scheme particulars
271 if not (nreqt.host and nreqt.host ~= "") then 275 local schemedefs, host, port, method
276 = SCHEMES[nreqt.scheme], nreqt.host, nreqt.port, nreqt.method
277 if not nreqt.create then nreqt.create = schemedefs.create(nreqt) end
278 if not (port and port ~= '') then nreqt.port = schemedefs.port end
279 if not (method and method ~= '') then nreqt.method = 'GET' end
280 if not (host and host ~= "") then
272 socket.try(nil, "invalid host '" .. base.tostring(nreqt.host) .. "'") 281 socket.try(nil, "invalid host '" .. base.tostring(nreqt.host) .. "'")
273 end 282 end
274 -- compute uri if user hasn't overriden 283 -- compute uri if user hasn't overriden
@@ -285,8 +294,10 @@ local function shouldredirect(reqt, code, headers)
285 if not location then return false end 294 if not location then return false end
286 location = string.gsub(location, "%s", "") 295 location = string.gsub(location, "%s", "")
287 if location == "" then return false end 296 if location == "" then return false end
288 local scheme = string.match(location, "^([%w][%w%+%-%.]*)%:") 297 local scheme = url.parse(location).scheme
289 if scheme and not SCHEMES[scheme] then return false end 298 if scheme and (not SCHEMES[scheme]) then return false end
299 -- avoid https downgrades
300 if ('https' == reqt.scheme) and ('https' ~= scheme) then return false end
290 return (reqt.redirect ~= false) and 301 return (reqt.redirect ~= false) and
291 (code == 301 or code == 302 or code == 303 or code == 307) and 302 (code == 301 or code == 302 or code == 303 or code == 307) and
292 (not reqt.method or reqt.method == "GET" or reqt.method == "HEAD") 303 (not reqt.method or reqt.method == "GET" or reqt.method == "HEAD")
@@ -306,10 +317,16 @@ end
306local trequest, tredirect 317local trequest, tredirect
307 318
308--[[local]] function tredirect(reqt, location) 319--[[local]] function tredirect(reqt, location)
320 -- the RFC says the redirect URL has to be absolute, but some
321 -- servers do not respect that
322 local newurl = url.absolute(reqt.url, location)
323 -- if switching schemes, reset port and create function
324 if url.parse(newurl).scheme ~= reqt.scheme then
325 reqt.port = nil
326 reqt.create = nil end
327 -- make new request
309 local result, code, headers, status = trequest { 328 local result, code, headers, status = trequest {
310 -- the RFC says the redirect URL has to be absolute, but some 329 url = newurl,
311 -- servers do not respect that
312 url = url.absolute(reqt.url, location),
313 source = reqt.source, 330 source = reqt.source,
314 sink = reqt.sink, 331 sink = reqt.sink,
315 headers = reqt.headers, 332 headers = reqt.headers,
@@ -397,4 +414,5 @@ _M.request = socket.protect(function(reqt, body)
397 else return trequest(reqt) end 414 else return trequest(reqt) end
398end) 415end)
399 416
417_M.schemes = SCHEMES
400return _M 418return _M