diff options
author | E. Westbrook <github@westbrook.io> | 2018-07-12 01:10:11 -0600 |
---|---|---|
committer | E. Westbrook <github@westbrook.io> | 2019-02-24 15:23:16 -0700 |
commit | e587800164d5d6fb75fe34166978438c949db215 (patch) | |
tree | 626f20dad172a224b9dc96c45a08fc245d6d5f31 | |
parent | 38865fad3ac949d833bab7a02e8727a2ad336450 (diff) | |
download | luasocket-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.lua | 56 |
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 |
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 | 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 |
35 | local SCHEME = 'http' | 41 | local SCHEME = 'http' |
36 | local PORT = SCHEMES[SCHEME].port | 42 | local 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 | ||
116 | function _M.open(host, port, create) | 121 | function _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 |
127 | end | 132 | end |
@@ -221,14 +226,13 @@ end | |||
221 | 226 | ||
222 | local function adjustheaders(reqt) | 227 | local 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 | |||
306 | local trequest, tredirect | 317 | local 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 |
398 | end) | 415 | end) |
399 | 416 | ||
417 | _M.schemes = SCHEMES | ||
400 | return _M | 418 | return _M |