From 5b4b9158799293eeaf8439d40a7845b5b0a5e125 Mon Sep 17 00:00:00 2001 From: Diego Nehab Date: Fri, 4 Mar 2016 16:16:41 -0300 Subject: Remove global PORT. Fix https redirect. --- src/http.lua | 42 +++++++++++++++++++++++++----------------- 1 file changed, 25 insertions(+), 17 deletions(-) (limited to 'src/http.lua') diff --git a/src/http.lua b/src/http.lua index 45ffa15..d6bcc91 100644 --- a/src/http.lua +++ b/src/http.lua @@ -23,11 +23,14 @@ local _M = socket.http ----------------------------------------------------------------------------- -- connection timeout in seconds _M.TIMEOUT = 60 --- default port for document retrieval -_M.PORT = 80 -- user agent field sent in request _M.USERAGENT = socket._VERSION +-- supported schemes +local SCHEMES = { ["http"] = true } +-- default port for document retrieval +local PORT = 80 + ----------------------------------------------------------------------------- -- Reads MIME headers from a connection, unfolding where needed ----------------------------------------------------------------------------- @@ -114,7 +117,7 @@ function _M.open(host, port, create) h.try = socket.newtry(function() h:close() end) -- set timeout before connecting h.try(c:settimeout(_M.TIMEOUT)) - h.try(c:connect(host, port or _M.PORT)) + h.try(c:connect(host, port or PORT)) -- here everything worked return h end @@ -218,7 +221,7 @@ local function adjustheaders(reqt) } -- if we have authentication information, pass it along if reqt.user and reqt.password then - lower["authorization"] = + lower["authorization"] = "Basic " .. (mime.b64(reqt.user .. ":" .. reqt.password)) end -- if we have proxy authentication information, pass it along @@ -226,7 +229,7 @@ local function adjustheaders(reqt) if proxy then proxy = url.parse(proxy) if proxy.user and proxy.password then - lower["proxy-authorization"] = + lower["proxy-authorization"] = "Basic " .. (mime.b64(proxy.user .. ":" .. proxy.password)) end end @@ -240,7 +243,7 @@ end -- default url parts local default = { host = "", - port = _M.PORT, + port = PORT, path ="/", scheme = "http" } @@ -250,9 +253,10 @@ local function adjustrequest(reqt) local nreqt = reqt.url and url.parse(reqt.url, default) or {} -- explicit components override url for i,v in base.pairs(reqt) do nreqt[i] = v end - if nreqt.port == "" then nreqt.port = 80 end - socket.try(nreqt.host and nreqt.host ~= "", - "invalid host '" .. base.tostring(nreqt.host) .. "'") + if nreqt.port == "" then nreqt.port = PORT end + if not (nreqt.host and nreqt.host ~= "") then + socket.try(nil, "invalid host '" .. base.tostring(nreqt.host) .. "'") + end -- compute uri if user hasn't overriden nreqt.uri = reqt.uri or adjusturi(nreqt) -- adjust headers in request @@ -263,9 +267,13 @@ local function adjustrequest(reqt) end local function shouldredirect(reqt, code, headers) - return headers.location and - string.gsub(headers.location, "%s", "") ~= "" and - (reqt.redirect ~= false) and + local location = headers.location + if not location then return false end + location = string.gsub(location, "%s", "") + if location == "" then return false end + local scheme = string.match(location, "^([%w][%w%+%-%.]*)%:") + if scheme and not SCHEMES[scheme] then return false end + return (reqt.redirect ~= false) and (code == 301 or code == 302 or code == 303 or code == 307) and (not reqt.method or reqt.method == "GET" or reqt.method == "HEAD") and (not reqt.nredirects or reqt.nredirects < 5) @@ -289,10 +297,10 @@ local trequest, tredirect source = reqt.source, sink = reqt.sink, headers = reqt.headers, - proxy = reqt.proxy, + proxy = reqt.proxy, nredirects = (reqt.nredirects or 0) + 1, create = reqt.create - } + } -- pass location header back as a hint we redirected headers = headers or {} headers.location = headers.location or location @@ -309,7 +317,7 @@ end h:sendheaders(nreqt.headers) -- if there is a body, send it if nreqt.source then - h:sendbody(nreqt.headers, nreqt.source, nreqt.step) + h:sendbody(nreqt.headers, nreqt.source, nreqt.step) end local code, status = h:receivestatusline() -- if it is an HTTP/0.9 server, simply get the body and we are done @@ -319,13 +327,13 @@ end end local headers -- ignore any 100-continue messages - while code == 100 do + while code == 100 do headers = h:receiveheaders() code, status = h:receivestatusline() end headers = h:receiveheaders() -- at this point we should have a honest reply from the server - -- we can't redirect if we already used the source, so we report the error + -- we can't redirect if we already used the source, so we report the error if shouldredirect(nreqt, code, headers) and not nreqt.source then h:close() return tredirect(reqt, headers.location) -- cgit v1.2.3-55-g6feb From 916b548240b1513410b13f964d80f329aec9c13a Mon Sep 17 00:00:00 2001 From: Diego Nehab Date: Mon, 7 Mar 2016 01:30:30 -0300 Subject: Family agostic FTP and expose HTTP/FTP url parsing --- src/ftp.lua | 67 ++++++++++++++++++++++++++++++++++++++++++-------------- src/http.lua | 17 ++++++++++---- src/tp.lua | 10 ++++++++- test/ftptest.lua | 34 ++++++++++++++++++---------- 4 files changed, 96 insertions(+), 32 deletions(-) (limited to 'src/http.lua') diff --git a/src/ftp.lua b/src/ftp.lua index e0c3cae..11798ad 100644 --- a/src/ftp.lua +++ b/src/ftp.lua @@ -51,7 +51,7 @@ end function metat.__index:pasvconnect() self.data = self.try(socket.tcp()) self.try(self.data:settimeout(_M.TIMEOUT)) - self.try(self.data:connect(self.pasvt.ip, self.pasvt.port)) + self.try(self.data:connect(self.pasvt.address, self.pasvt.port)) end function metat.__index:login(user, password) @@ -71,32 +71,65 @@ function metat.__index:pasv() local a, b, c, d, p1, p2 = socket.skip(2, string.find(reply, pattern)) self.try(a and b and c and d and p1 and p2, reply) self.pasvt = { - ip = string.format("%d.%d.%d.%d", a, b, c, d), + address = string.format("%d.%d.%d.%d", a, b, c, d), port = p1*256 + p2 } if self.server then self.server:close() self.server = nil end - return self.pasvt.ip, self.pasvt.port + return self.pasvt.address, self.pasvt.port end -function metat.__index:port(ip, port) +function metat.__index:epsv() + self.try(self.tp:command("epsv")) + local code, reply = self.try(self.tp:check("229")) + local pattern = "%((.)(.-)%1(.-)%1(.-)%1%)" + local d, prt, address, port = string.match(reply, pattern) + self.try(port, "invalid epsv response") + self.pasvt = { + address = self.tp:getpeername(), + port = port + } + if self.server then + self.server:close() + self.server = nil + end + return self.pasvt.address, self.pasvt.port +end + + +function metat.__index:port(address, port) self.pasvt = nil - if not ip then - ip, port = self.try(self.tp:getcontrol():getsockname()) - self.server = self.try(socket.bind(ip, 0)) - ip, port = self.try(self.server:getsockname()) + if not address then + address, port = self.try(self.tp:getsockname()) + self.server = self.try(socket.bind(address, 0)) + address, port = self.try(self.server:getsockname()) self.try(self.server:settimeout(_M.TIMEOUT)) end local pl = math.mod(port, 256) local ph = (port - pl)/256 - local arg = string.gsub(string.format("%s,%d,%d", ip, ph, pl), "%.", ",") + local arg = string.gsub(string.format("%s,%d,%d", address, ph, pl), "%.", ",") self.try(self.tp:command("port", arg)) self.try(self.tp:check("2..")) return 1 end +function metat.__index:eprt(family, address, port) + self.pasvt = nil + if not address then + address, port = self.try(self.tp:getsockname()) + self.server = self.try(socket.bind(address, 0)) + address, port = self.try(self.server:getsockname()) + self.try(self.server:settimeout(_M.TIMEOUT)) + end + local arg = string.format("|%s|%s|%d|", family, address, port) + self.try(self.tp:command("eprt", arg)) + self.try(self.tp:check("2..")) + return 1 +end + + function metat.__index:send(sendt) self.try(self.pasvt or self.server, "need port or pasv first") -- if there is a pasvt table, we already sent a PASV command @@ -110,12 +143,12 @@ function metat.__index:send(sendt) -- send the transfer command and check the reply self.try(self.tp:command(command, argument)) local code, reply = self.try(self.tp:check{"2..", "1.."}) - -- if there is not a a pasvt table, then there is a server + -- if there is not a pasvt table, then there is a server -- and we already sent a PORT command if not self.pasvt then self:portconnect() end -- get the sink, source and step for the transfer local step = sendt.step or ltn12.pump.step - local readt = {self.tp.c} + local readt = { self.tp } local checkstep = function(src, snk) -- check status in control connection while downloading local readyt = socket.select(readt, nil, 0) @@ -207,7 +240,7 @@ local function tput(putt) f:greet() f:login(putt.user, putt.password) if putt.type then f:type(putt.type) end - f:pasv() + f:epsv() local sent = f:send(putt) f:quit() f:close() @@ -219,7 +252,7 @@ local default = { scheme = "ftp" } -local function parse(u) +local function genericform(u) local t = socket.try(url.parse(u, default)) socket.try(t.scheme == "ftp", "wrong scheme '" .. t.scheme .. "'") socket.try(t.host, "missing hostname") @@ -232,8 +265,10 @@ local function parse(u) return t end +_M.genericform = genericform + local function sput(u, body) - local putt = parse(u) + local putt = genericform(u) putt.source = ltn12.source.string(body) return tput(putt) end @@ -250,14 +285,14 @@ local function tget(gett) f:greet() f:login(gett.user, gett.password) if gett.type then f:type(gett.type) end - f:pasv() + f:epsv() f:receive(gett) f:quit() return f:close() end local function sget(u) - local gett = parse(u) + local gett = genericform(u) local t = {} gett.sink = ltn12.sink.table(t) tget(gett) diff --git a/src/http.lua b/src/http.lua index d6bcc91..f2fff01 100644 --- a/src/http.lua +++ b/src/http.lua @@ -346,11 +346,13 @@ end return 1, code, headers, status end -local function srequest(u, b) +-- turns an url and a body into a generic request +local function genericform(u, b) local t = {} local reqt = { url = u, - sink = ltn12.sink.table(t) + sink = ltn12.sink.table(t), + target = t } if b then reqt.source = ltn12.source.string(b) @@ -360,8 +362,15 @@ local function srequest(u, b) } reqt.method = "POST" end - local code, headers, status = socket.skip(1, trequest(reqt)) - return table.concat(t), code, headers, status + return reqt +end + +_M.genericform = genericform + +local function srequest(u, b) + local reqt = genericform(u, b) + local _, code, headers, status = trequest(reqt) + return table.concat(reqt.target), code, headers, status end _M.request = socket.protect(function(reqt, body) diff --git a/src/tp.lua b/src/tp.lua index 328cbab..b8ebc56 100644 --- a/src/tp.lua +++ b/src/tp.lua @@ -46,6 +46,14 @@ end -- metatable for sock object local metat = { __index = {} } +function metat.__index:getpeername() + return self.c:getpeername() +end + +function metat.__index:getsockname() + return self.c:getpeername() +end + function metat.__index:check(ok) local code, reply = get_reply(self.c) if not code then return nil, reply end @@ -123,4 +131,4 @@ function _M.connect(host, port, timeout, create) return base.setmetatable({c = c}, metat) end -return _M \ No newline at end of file +return _M diff --git a/test/ftptest.lua b/test/ftptest.lua index fb13326..3ea0d39 100644 --- a/test/ftptest.lua +++ b/test/ftptest.lua @@ -3,19 +3,31 @@ local ftp = require("socket.ftp") local url = require("socket.url") local ltn12 = require("ltn12") +-- use dscl to create user "luasocket" with password "password" +-- with home in /Users/diego/luasocket/test/ftp +-- with group com.apple.access_ftp +-- with shell set to /sbin/nologin +-- set /etc/ftpchroot to chroot luasocket +-- must set group com.apple.access_ftp on user _ftp (for anonymous access) +-- copy index.html to /var/empty/pub (home of user ftp) +-- start ftp server with +-- sudo -s launchctl load -w /System/Library/LaunchDaemons/ftp.plist +-- copy index.html to /Users/diego/luasocket/test/ftp +-- stop with +-- sudo -s launchctl unload -w /System/Library/LaunchDaemons/ftp.plist + -- override protection to make sure we see all errors --socket.protect = function(s) return s end dofile("testsupport.lua") -local host, port, index_file, index, back, err, ret +local host = host or "localhost" +local port, index_file, index, back, err, ret local t = socket.gettime() -host = host or "localhost" index_file = "index.html" - -- a function that returns a directory listing local function nlst(u) local t = {} @@ -55,27 +67,27 @@ assert(not err and back == index, err) print("ok") io.write("erasing before upload: ") -ret, err = dele("ftp://luasocket:pedrovian@" .. host .. "/index.up.html") -if not ret then print(err) +ret, err = dele("ftp://luasocket:password@" .. host .. "/index.up.html") +if not ret then print(err) else print("ok") end io.write("testing upload: ") -ret, err = ftp.put("ftp://luasocket:pedrovian@" .. host .. "/index.up.html;type=i", index) +ret, err = ftp.put("ftp://luasocket:password@" .. host .. "/index.up.html;type=i", index) assert(ret and not err, err) print("ok") io.write("downloading uploaded file: ") -back, err = ftp.get("ftp://luasocket:pedrovian@" .. host .. "/index.up.html;type=i") +back, err = ftp.get("ftp://luasocket:password@" .. host .. "/index.up.html;type=i") assert(ret and not err and index == back, err) print("ok") io.write("erasing after upload/download: ") -ret, err = dele("ftp://luasocket:pedrovian@" .. host .. "/index.up.html") -assert(ret and not err, err) +ret, err = dele("ftp://luasocket:password@" .. host .. "/index.up.html") +assert(ret and not err, err) print("ok") io.write("testing weird-character translation: ") -back, err = ftp.get("ftp://luasocket:pedrovian@" .. host .. "/%23%3f;type=i") +back, err = ftp.get("ftp://luasocket:password@" .. host .. "/%23%3f;type=i") assert(not err and back == index, err) print("ok") @@ -84,7 +96,7 @@ local back = {} ret, err = ftp.get{ url = "//stupid:mistake@" .. host .. "/index.html", user = "luasocket", - password = "pedrovian", + password = "password", type = "i", sink = ltn12.sink.table(back) } -- cgit v1.2.3-55-g6feb From b9f6fd215a8f522733caedcb4d6d6b0c6e55103e Mon Sep 17 00:00:00 2001 From: Okash Khawaja Date: Tue, 12 Apr 2016 00:04:21 +0100 Subject: URL-decode user password before adding to authorization header. --- src/http.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/http.lua') diff --git a/src/http.lua b/src/http.lua index f2fff01..a386165 100644 --- a/src/http.lua +++ b/src/http.lua @@ -222,7 +222,8 @@ local function adjustheaders(reqt) -- if we have authentication information, pass it along if reqt.user and reqt.password then lower["authorization"] = - "Basic " .. (mime.b64(reqt.user .. ":" .. reqt.password)) + "Basic " .. (mime.b64(reqt.user .. ":" .. + url.unescape(reqt.password))) end -- if we have proxy authentication information, pass it along local proxy = reqt.proxy or _M.PROXY -- cgit v1.2.3-55-g6feb From 4a3504612cda28f25ab777db94bfeab55e081e16 Mon Sep 17 00:00:00 2001 From: xyida <522918670@qq.com> Date: Thu, 26 Apr 2018 16:39:29 +0800 Subject: Fixed an issue that was mistaken for HTTP 0.9 when timeout --- src/http.lua | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'src/http.lua') diff --git a/src/http.lua b/src/http.lua index a386165..79a931c 100644 --- a/src/http.lua +++ b/src/http.lua @@ -147,10 +147,15 @@ function metat.__index:sendbody(headers, source, step) end function metat.__index:receivestatusline() - local status = self.try(self.c:receive(5)) + local status,ec = self.try(self.c:receive(5)) -- identify HTTP/0.9 responses, which do not contain a status line -- this is just a heuristic, but is what the RFC recommends - if status ~= "HTTP/" then return nil, status end + if status ~= "HTTP/" then + if ec == "timeout" then + return 408 + end + return nil, status + end -- otherwise proceed reading a status line status = self.try(self.c:receive("*l", status)) local code = socket.skip(2, string.find(status, "HTTP/%d*%.%d* (%d%d%d)")) @@ -325,6 +330,8 @@ end if not code then h:receive09body(status, nreqt.sink, nreqt.step) return 1, 200 + elseif code == 408 then + return 1, code end local headers -- ignore any 100-continue messages -- cgit v1.2.3-55-g6feb From 686f2ce8222077e13ecdf9cf09f43f0b1878a737 Mon Sep 17 00:00:00 2001 From: "E. Westbrook" Date: Thu, 12 Jul 2018 11:07:20 -0600 Subject: http.lua: if default for scheme, omit port number in "Host:" header --- src/http.lua | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) (limited to 'src/http.lua') diff --git a/src/http.lua b/src/http.lua index a386165..6b26a0a 100644 --- a/src/http.lua +++ b/src/http.lua @@ -27,9 +27,13 @@ _M.TIMEOUT = 60 _M.USERAGENT = socket._VERSION -- supported schemes -local SCHEMES = { ["http"] = true } --- default port for document retrieval -local PORT = 80 +local SCHEMES = { + http = { port = 80 } + , https = { port = 443 }} + +-- default scheme and port for document retrieval +local SCHEME = 'http' +local PORT = SCHEMES[SCHEME].port ----------------------------------------------------------------------------- -- Reads MIME headers from a connection, unfolding where needed @@ -212,10 +216,14 @@ end local function adjustheaders(reqt) -- default headers - local host = string.gsub(reqt.authority, "^.-@", "") + local headhost = reqt.host + local headport = tostring(reqt.port) + local schemeport = tostring(SCHEMES[reqt.scheme].port) + if headport ~= schemeport then + headhost = headhost .. ':' .. headport end local lower = { ["user-agent"] = _M.USERAGENT, - ["host"] = host, + ["host"] = headhost, ["connection"] = "close, TE", ["te"] = "trailers" } @@ -246,7 +254,7 @@ local default = { host = "", port = PORT, path ="/", - scheme = "http" + scheme = SCHEME } local function adjustrequest(reqt) -- cgit v1.2.3-55-g6feb From 09ff9b650c3567c91eed62f4dd571729df2dedd4 Mon Sep 17 00:00:00 2001 From: "E. Westbrook" Date: Fri, 13 Jul 2018 12:52:26 -0600 Subject: http.lua: allow override of hard-coded 5 max redirects --- doc/http.html | 8 +++++--- src/http.lua | 5 ++++- 2 files changed, 9 insertions(+), 4 deletions(-) (limited to 'src/http.lua') diff --git a/doc/http.html b/doc/http.html index 3b7a8b1..78f785a 100644 --- a/doc/http.html +++ b/doc/http.html @@ -135,7 +135,8 @@ http.request{
  [step = LTN12 pump step,]
  [proxy = string,]
  [redirect = boolean,]
-  [create = function]
+  [create = function,]
+  [maxredirects = number]
}

@@ -185,6 +186,7 @@ Defaults to the LTN12 pump.step function. function from automatically following 301 or 302 server redirect messages;
  • create: An optional function to be used instead of socket.tcp when the communications socket is created. +
  • maxredirects: An optional number specifying the maximum number of redirects to follow. Defaults to 5 if not specified. A boolean false value means no maximum (unlimited).

    @@ -324,8 +326,8 @@ r, c = http.request {

    -Last modified by Diego Nehab on
    -Thu Apr 20 00:25:26 EDT 2006 +Last modified by Eric Westbrook on
    +Sat Feb 23 19:09:42 UTC 2019

    diff --git a/src/http.lua b/src/http.lua index a386165..8bda0d8 100644 --- a/src/http.lua +++ b/src/http.lua @@ -277,7 +277,9 @@ local function shouldredirect(reqt, code, headers) return (reqt.redirect ~= false) and (code == 301 or code == 302 or code == 303 or code == 307) and (not reqt.method or reqt.method == "GET" or reqt.method == "HEAD") - and (not reqt.nredirects or reqt.nredirects < 5) + and ((false == reqt.maxredirects) + or ((reqt.nredirects or 0) + < (reqt.maxredirects or 5))) end local function shouldreceivebody(reqt, code) @@ -299,6 +301,7 @@ local trequest, tredirect sink = reqt.sink, headers = reqt.headers, proxy = reqt.proxy, + maxredirects = reqt.maxredirects, nredirects = (reqt.nredirects or 0) + 1, create = reqt.create } -- cgit v1.2.3-55-g6feb From e587800164d5d6fb75fe34166978438c949db215 Mon Sep 17 00:00:00 2001 From: "E. Westbrook" Date: Thu, 12 Jul 2018 01:10:11 -0600 Subject: socket.http.request(): simultaneous support for http and https URL schemes, with caller-adjustable scheme-to-transport mappings (default "socket.http" and "ssl.https") --- src/http.lua | 56 +++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 37 insertions(+), 19 deletions(-) (limited to 'src/http.lua') 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 -- user agent field sent in request _M.USERAGENT = socket._VERSION --- supported schemes +-- supported schemes and their particulars local SCHEMES = { - http = { port = 80 } - , https = { port = 443 }} + http = { + port = 80 + , create = function(t) + return socket.tcp end } + , https = { + port = 443 + , create = function(t) + return require("ssl.https").tcp(t) end }} -- default scheme and port for document retrieval local SCHEME = 'http' local PORT = SCHEMES[SCHEME].port - ----------------------------------------------------------------------------- -- Reads MIME headers from a connection, unfolding where needed ----------------------------------------------------------------------------- @@ -115,13 +120,13 @@ local metat = { __index = {} } function _M.open(host, port, create) -- create socket with user connect function, or with default - local c = socket.try((create or socket.tcp)()) + local c = socket.try(create()) local h = base.setmetatable({ c = c }, metat) -- create finalized try h.try = socket.newtry(function() h:close() end) -- set timeout before connecting h.try(c:settimeout(_M.TIMEOUT)) - h.try(c:connect(host, port or PORT)) + h.try(c:connect(host, port)) -- here everything worked return h end @@ -221,14 +226,13 @@ end local function adjustheaders(reqt) -- default headers - local headhost = reqt.host - local headport = tostring(reqt.port) - local schemeport = tostring(SCHEMES[reqt.scheme].port) - if headport ~= schemeport then - headhost = headhost .. ':' .. headport end + local host = reqt.host + local port = tostring(reqt.port) + if port ~= tostring(SCHEMES[reqt.scheme].port) then + host = host .. ':' .. port end local lower = { ["user-agent"] = _M.USERAGENT, - ["host"] = headhost, + ["host"] = host, ["connection"] = "close, TE", ["te"] = "trailers" } @@ -267,8 +271,13 @@ local function adjustrequest(reqt) local nreqt = reqt.url and url.parse(reqt.url, default) or {} -- explicit components override url for i,v in base.pairs(reqt) do nreqt[i] = v end - if nreqt.port == "" then nreqt.port = PORT end - if not (nreqt.host and nreqt.host ~= "") then + -- default to scheme particulars + local schemedefs, host, port, method + = SCHEMES[nreqt.scheme], nreqt.host, nreqt.port, nreqt.method + if not nreqt.create then nreqt.create = schemedefs.create(nreqt) end + if not (port and port ~= '') then nreqt.port = schemedefs.port end + if not (method and method ~= '') then nreqt.method = 'GET' end + if not (host and host ~= "") then socket.try(nil, "invalid host '" .. base.tostring(nreqt.host) .. "'") end -- compute uri if user hasn't overriden @@ -285,8 +294,10 @@ local function shouldredirect(reqt, code, headers) if not location then return false end location = string.gsub(location, "%s", "") if location == "" then return false end - local scheme = string.match(location, "^([%w][%w%+%-%.]*)%:") - if scheme and not SCHEMES[scheme] then return false end + local scheme = url.parse(location).scheme + if scheme and (not SCHEMES[scheme]) then return false end + -- avoid https downgrades + if ('https' == reqt.scheme) and ('https' ~= scheme) then return false end return (reqt.redirect ~= false) and (code == 301 or code == 302 or code == 303 or code == 307) and (not reqt.method or reqt.method == "GET" or reqt.method == "HEAD") @@ -306,10 +317,16 @@ end local trequest, tredirect --[[local]] function tredirect(reqt, location) + -- the RFC says the redirect URL has to be absolute, but some + -- servers do not respect that + local newurl = url.absolute(reqt.url, location) + -- if switching schemes, reset port and create function + if url.parse(newurl).scheme ~= reqt.scheme then + reqt.port = nil + reqt.create = nil end + -- make new request local result, code, headers, status = trequest { - -- the RFC says the redirect URL has to be absolute, but some - -- servers do not respect that - url = url.absolute(reqt.url, location), + url = newurl, source = reqt.source, sink = reqt.sink, headers = reqt.headers, @@ -397,4 +414,5 @@ _M.request = socket.protect(function(reqt, body) else return trequest(reqt) end end) +_M.schemes = SCHEMES return _M -- cgit v1.2.3-55-g6feb From 2a467001f6dbadf447a79dfbd036f32b6a82ce05 Mon Sep 17 00:00:00 2001 From: "E. Westbrook" Date: Sun, 24 Feb 2019 16:24:42 -0700 Subject: http.lua: Error informatively if insufficient LuaSec support --- src/http.lua | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'src/http.lua') diff --git a/src/http.lua b/src/http.lua index 25eb25d..2fa5a26 100644 --- a/src/http.lua +++ b/src/http.lua @@ -35,7 +35,11 @@ local SCHEMES = { , https = { port = 443 , create = function(t) - return require("ssl.https").tcp(t) end }} + local https = assert( + require("ssl.https"), 'LuaSocket: LuaSec not found') + local tcp = assert( + https.tcp, 'LuaSocket: Function tcp() not available from LuaSec') + return tcp(t) end }} -- default scheme and port for document retrieval local SCHEME = 'http' -- cgit v1.2.3-55-g6feb From 297f9d0277ca4c93a5bd4306e0d31a4a98940089 Mon Sep 17 00:00:00 2001 From: "E. Westbrook" Date: Thu, 28 Feb 2019 18:40:30 -0700 Subject: bugfix: http.lua multischeme change that got dropped during PR conflict resolution --- src/http.lua | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'src/http.lua') diff --git a/src/http.lua b/src/http.lua index 2fa5a26..6a3416e 100644 --- a/src/http.lua +++ b/src/http.lua @@ -264,10 +264,8 @@ end -- default url parts local default = { - host = "", - port = PORT, - path ="/", - scheme = SCHEME + path ="/" + , scheme = "http" } local function adjustrequest(reqt) -- cgit v1.2.3-55-g6feb From 601ad8d59f11d7180015d0ecfb9d0a8d67f6f5c1 Mon Sep 17 00:00:00 2001 From: Thijs Schreijer Date: Fri, 18 Mar 2022 12:12:39 +0100 Subject: refactor: Address issues raised by linter --- etc/cookie.lua | 22 ++++----- etc/get.lua | 2 +- gem/ex11.lua | 4 +- gem/ex3.lua | 2 +- gem/ex4.lua | 2 +- samples/cddb.lua | 2 +- src/ftp.lua | 14 +++--- src/http.lua | 11 ++--- src/ltn12.lua | 3 +- src/mbox.lua | 13 +++--- src/mime.lua | 12 +---- src/url.lua | 6 +-- test/ltn12test.lua | 24 +++++----- test/mimetest.lua | 46 +++++++++--------- test/smtptest.lua | 20 ++++---- test/test_socket_error.lua | 2 +- test/testmesg.lua | 14 +++--- test/testsupport.lua | 2 +- test/urltest.lua | 114 ++++++++++++++++++++++----------------------- test/utestclnt.lua | 68 +++++++++++++-------------- test/utestsrvr.lua | 2 +- 21 files changed, 187 insertions(+), 198 deletions(-) (limited to 'src/http.lua') diff --git a/etc/cookie.lua b/etc/cookie.lua index 4adb403..fec10a1 100644 --- a/etc/cookie.lua +++ b/etc/cookie.lua @@ -5,7 +5,7 @@ local ltn12 = require"ltn12" local token_class = '[^%c%s%(%)%<%>%@%,%;%:%\\%"%/%[%]%?%=%{%}]' -local function unquote(t, quoted) +local function unquote(t, quoted) local n = string.match(t, "%$(%d+)$") if n then n = tonumber(n) end if quoted[n] then return quoted[n] @@ -14,19 +14,19 @@ end local function parse_set_cookie(c, quoted, cookie_table) c = c .. ";$last=last;" - local _, __, n, v, i = string.find(c, "(" .. token_class .. + local _, _, n, v, i = string.find(c, "(" .. token_class .. "+)%s*=%s*(.-)%s*;%s*()") local cookie = { - name = n, - value = unquote(v, quoted), + name = n, + value = unquote(v, quoted), attributes = {} } while 1 do - _, __, n, v, i = string.find(c, "(" .. token_class .. + _, _, n, v, i = string.find(c, "(" .. token_class .. "+)%s*=?%s*(.-)%s*;%s*()", i) if not n or n == "$last" then break end cookie.attributes[#cookie.attributes+1] = { - name = n, + name = n, value = unquote(v, quoted) } end @@ -46,8 +46,8 @@ local function split_set_cookie(s, cookie_table) -- split into individual cookies i = 1 while 1 do - local _, __, cookie, next_token - _, __, cookie, i, next_token = string.find(s, "(.-)%s*%,%s*()(" .. + local _, _, cookie, next_token + _, _, cookie, i, next_token = string.find(s, "(.-)%s*%,%s*()(" .. token_class .. "+)%s*=", i) if not next_token then break end parse_set_cookie(cookie, quoted, cookie_table) @@ -62,12 +62,12 @@ local function quote(s) end local _empty = {} -local function build_cookies(cookies) +local function build_cookies(cookies) s = "" for i,v in ipairs(cookies or _empty) do if v.name then s = s .. v.name - if v.value and v.value ~= "" then + if v.value and v.value ~= "" then s = s .. '=' .. quote(v.value) end end @@ -83,6 +83,6 @@ local function build_cookies(cookies) end if i < #cookies then s = s .. ", " end end - return s + return s end diff --git a/etc/get.lua b/etc/get.lua index 9edc235..d53c465 100644 --- a/etc/get.lua +++ b/etc/get.lua @@ -71,7 +71,7 @@ function stats(size) local current = socket.gettime() if chunk then -- total bytes received - got = got + string.len(chunk) + got = got + string.len(chunk) -- not enough time for estimate if current - last > 1 then io.stderr:write("\r", gauge(got, current - start, size)) diff --git a/gem/ex11.lua b/gem/ex11.lua index 1cbf01f..79c99af 100644 --- a/gem/ex11.lua +++ b/gem/ex11.lua @@ -1,7 +1,7 @@ local input = source.chain( - source.file(io.open("input.bin", "rb")), + source.file(io.open("input.bin", "rb")), encode("base64")) local output = sink.chain( - wrap(76), + wrap(76), sink.file(io.open("output.b64", "w"))) pump.all(input, output) diff --git a/gem/ex3.lua b/gem/ex3.lua index a43fefa..60b4423 100644 --- a/gem/ex3.lua +++ b/gem/ex3.lua @@ -7,7 +7,7 @@ local function chainpair(f1, f2) end function filter.chain(...) - local f = select(1, ...) + local f = select(1, ...) for i = 2, select('#', ...) do f = chainpair(f, select(i, ...)) end diff --git a/gem/ex4.lua b/gem/ex4.lua index c670e0e..c48b77e 100644 --- a/gem/ex4.lua +++ b/gem/ex4.lua @@ -1,4 +1,4 @@ -local qp = filter.chain(normalize(CRLF), encode("quoted-printable"), +local qp = filter.chain(normalize(CRLF), encode("quoted-printable"), wrap("quoted-printable")) local input = source.chain(source.file(io.stdin), qp) local output = sink.file(io.stdout) diff --git a/samples/cddb.lua b/samples/cddb.lua index 49a1871..59d5a44 100644 --- a/samples/cddb.lua +++ b/samples/cddb.lua @@ -26,7 +26,7 @@ function parse(body) data[key] = value end end - return data, code, message + return data, code, message end local host = socket.dns.gethostname() diff --git a/src/ftp.lua b/src/ftp.lua index bd528ca..0ebc508 100644 --- a/src/ftp.lua +++ b/src/ftp.lua @@ -56,7 +56,7 @@ end function metat.__index:login(user, password) self.try(self.tp:command("user", user or _M.USER)) - local code, reply = self.try(self.tp:check{"2..", 331}) + local code, _ = self.try(self.tp:check{"2..", 331}) if code == 331 then self.try(self.tp:command("pass", password or _M.PASSWORD)) self.try(self.tp:check("2..")) @@ -66,7 +66,7 @@ end function metat.__index:pasv() self.try(self.tp:command("pasv")) - local code, reply = self.try(self.tp:check("2..")) + local _, reply = self.try(self.tp:check("2..")) local pattern = "(%d+)%D(%d+)%D(%d+)%D(%d+)%D(%d+)%D(%d+)" local a, b, c, d, p1, p2 = socket.skip(2, string.find(reply, pattern)) self.try(a and b and c and d and p1 and p2, reply) @@ -83,9 +83,9 @@ end function metat.__index:epsv() self.try(self.tp:command("epsv")) - local code, reply = self.try(self.tp:check("229")) + local _, reply = self.try(self.tp:check("229")) local pattern = "%((.)(.-)%1(.-)%1(.-)%1%)" - local d, prt, address, port = string.match(reply, pattern) + local _, _, _, port = string.match(reply, pattern) self.try(port, "invalid epsv response") self.pasvt = { address = self.tp:getpeername(), @@ -102,7 +102,7 @@ end function metat.__index:port(address, port) self.pasvt = nil if not address then - address, port = self.try(self.tp:getsockname()) + address = self.try(self.tp:getsockname()) self.server = self.try(socket.bind(address, 0)) address, port = self.try(self.server:getsockname()) self.try(self.server:settimeout(_M.TIMEOUT)) @@ -118,7 +118,7 @@ end function metat.__index:eprt(family, address, port) self.pasvt = nil if not address then - address, port = self.try(self.tp:getsockname()) + address = self.try(self.tp:getsockname()) self.server = self.try(socket.bind(address, 0)) address, port = self.try(self.server:getsockname()) self.try(self.server:settimeout(_M.TIMEOUT)) @@ -142,7 +142,7 @@ function metat.__index:send(sendt) local command = sendt.command or "stor" -- send the transfer command and check the reply self.try(self.tp:command(command, argument)) - local code, reply = self.try(self.tp:check{"2..", "1.."}) + local code, _ = self.try(self.tp:check{"2..", "1.."}) -- if there is not a pasvt table, then there is a server -- and we already sent a PORT command if not self.pasvt then self:portconnect() end diff --git a/src/http.lua b/src/http.lua index 6a3416e..e3a1742 100644 --- a/src/http.lua +++ b/src/http.lua @@ -41,9 +41,6 @@ local SCHEMES = { https.tcp, 'LuaSocket: Function tcp() not available from LuaSec') return tcp(t) end }} --- default scheme and port for document retrieval -local SCHEME = 'http' -local PORT = SCHEMES[SCHEME].port ----------------------------------------------------------------------------- -- Reads MIME headers from a connection, unfolding where needed ----------------------------------------------------------------------------- @@ -92,7 +89,7 @@ socket.sourcet["http-chunked"] = function(sock, headers) -- was it the last chunk? if size > 0 then -- if not, get chunk and skip terminating CRLF - local chunk, err, part = sock:receive(size) + local chunk, err, _ = sock:receive(size) if chunk then sock:receive() end return chunk, err else @@ -166,8 +163,8 @@ function metat.__index:receivestatusline() if status ~= "HTTP/" then if ec == "timeout" then return 408 - end - return nil, status + end + return nil, status end -- otherwise proceed reading a status line status = self.try(self.c:receive("*l", status)) @@ -366,7 +363,7 @@ end local headers -- ignore any 100-continue messages while code == 100 do - headers = h:receiveheaders() + h:receiveheaders() code, status = h:receivestatusline() end headers = h:receiveheaders() diff --git a/src/ltn12.lua b/src/ltn12.lua index afa735d..f1e05e1 100644 --- a/src/ltn12.lua +++ b/src/ltn12.lua @@ -13,7 +13,7 @@ local unpack = unpack or table.unpack local base = _G local _M = {} if module then -- heuristic for exporting a global package table - ltn12 = _M + ltn12 = _M -- luacheck: ignore end local filter,source,sink,pump = {},{},{},{} @@ -23,7 +23,6 @@ _M.sink = sink _M.pump = pump local unpack = unpack or table.unpack -local select = base.select -- 2048 seems to be better in windows... _M.BLOCKSIZE = 2048 diff --git a/src/mbox.lua b/src/mbox.lua index ed9e781..12823b0 100644 --- a/src/mbox.lua +++ b/src/mbox.lua @@ -1,8 +1,8 @@ local _M = {} if module then - mbox = _M -end + mbox = _M -- luacheck: ignore +end function _M.split_message(message_s) local message = {} @@ -29,7 +29,7 @@ end function _M.parse_header(header_s) header_s = string.gsub(header_s, "\n[ ]+", " ") header_s = string.gsub(header_s, "\n+", "") - local _, __, name, value = string.find(header_s, "([^%s:]-):%s*(.*)") + local _, _, name, value = string.find(header_s, "([^%s:]-):%s*(.*)") return name, value end @@ -49,9 +49,9 @@ function _M.parse_headers(headers_s) end function _M.parse_from(from) - local _, __, name, address = string.find(from, "^%s*(.-)%s*%<(.-)%>") + local _, _, name, address = string.find(from, "^%s*(.-)%s*%<(.-)%>") if not address then - _, __, address = string.find(from, "%s*(.+)%s*") + _, _, address = string.find(from, "%s*(.+)%s*") end name = name or "" address = address or "" @@ -63,7 +63,8 @@ end function _M.split_mbox(mbox_s) local mbox = {} mbox_s = string.gsub(mbox_s, "\r\n", "\n") .."\n\nFrom \n" - local nj, i, j = 1, 1, 1 + local nj, i + local j = 1 while 1 do i, nj = string.find(mbox_s, "\n\nFrom .-\n", j) if not i then break end diff --git a/src/mime.lua b/src/mime.lua index d3abac5..93539de 100644 --- a/src/mime.lua +++ b/src/mime.lua @@ -10,7 +10,6 @@ local base = _G local ltn12 = require("ltn12") local mime = require("mime.core") -local string = require("string") local _M = mime -- encode, decode and wrap algorithm tables @@ -18,7 +17,7 @@ local encodet, decodet, wrapt = {},{},{} _M.encodet = encodet _M.decodet = decodet -_M.wrapt = wrapt +_M.wrapt = wrapt -- creates a function that chooses a filter by name from a given table local function choose(table) @@ -27,7 +26,7 @@ local function choose(table) name, opt1, opt2 = "default", name, opt1 end local f = table[name or "nil"] - if not f then + if not f then base.error("unknown key (" .. base.tostring(name) .. ")", 3) else return f(opt1, opt2) end end @@ -52,13 +51,6 @@ decodet['quoted-printable'] = function() return ltn12.filter.cycle(_M.unqp, "") end -local function format(chunk) - if chunk then - if chunk == "" then return "''" - else return string.len(chunk) end - else return "nil" end -end - -- define the line-wrap filters wrapt['text'] = function(length) length = length or 76 diff --git a/src/url.lua b/src/url.lua index 0a3a80a..8e0dc5c 100644 --- a/src/url.lua +++ b/src/url.lua @@ -179,9 +179,9 @@ function _M.parse(url, default) function(u) parsed.userinfo = u; return "" end) authority = string.gsub(authority, ":([^:%]]*)$", function(p) parsed.port = p; return "" end) - if authority ~= "" then + if authority ~= "" then -- IPv6? - parsed.host = string.match(authority, "^%[(.+)%]$") or authority + parsed.host = string.match(authority, "^%[(.+)%]$") or authority end local userinfo = parsed.userinfo if not userinfo then return parsed end @@ -264,7 +264,7 @@ function _M.absolute(base_url, relative_url) relative_parsed.query = base_parsed.query end end - else + else relative_parsed.path = absolute_path(base_parsed.path or "", relative_parsed.path) end diff --git a/test/ltn12test.lua b/test/ltn12test.lua index e7d368d..0cafbc9 100644 --- a/test/ltn12test.lua +++ b/test/ltn12test.lua @@ -38,7 +38,7 @@ local function named(f, name) end -------------------------------- -local function split(size) +local function split(size) local buffer = "" local last_out = "" local last_in = "" @@ -50,12 +50,12 @@ local function split(size) return last_out end return function(chunk, done) - if done then - return not last_in and not last_out + if done then + return not last_in and not last_out end -- check if argument is consistent with state if not chunk then - if last_in and last_in ~= "" and last_out ~= "" then + if last_in and last_in ~= "" and last_out ~= "" then error("nil chunk following data chunk", 2) end if not last_out then error("extra nil chunk", 2) end @@ -67,8 +67,8 @@ local function split(size) return output(chunk) else if not last_in then error("data chunk following nil chunk", 2) end - if last_in ~= "" and last_out ~= "" then - error("data chunk following data chunk", 2) + if last_in ~= "" and last_out ~= "" then + error("data chunk following data chunk", 2) end buffer = chunk return output(chunk) @@ -85,7 +85,7 @@ local function format(chunk) end -------------------------------- -local function merge(size) +local function merge(size) local buffer = "" local last_out = "" local last_in = "" @@ -102,12 +102,12 @@ local function merge(size) return last_out end return function(chunk, done) - if done then - return not last_in and not last_out + if done then + return not last_in and not last_out end -- check if argument is consistent with state if not chunk then - if last_in and last_in ~= "" and last_out ~= "" then + if last_in and last_in ~= "" and last_out ~= "" then error("nil chunk following data chunk", 2) end if not last_out then error("extra nil chunk", 2) end @@ -119,8 +119,8 @@ local function merge(size) return output(chunk) else if not last_in then error("data chunk following nil chunk", 2) end - if last_in ~= "" and last_out ~= "" then - error("data chunk following data chunk", 2) + if last_in ~= "" and last_out ~= "" then + error("data chunk following data chunk", 2) end buffer = buffer .. chunk return output(chunk) diff --git a/test/mimetest.lua b/test/mimetest.lua index f5b3747..a3c89ac 100644 --- a/test/mimetest.lua +++ b/test/mimetest.lua @@ -15,27 +15,27 @@ local eb64test = "b64test.bin2" local db64test = "b64test.bin3" --- from Machado de Assis, "A Mão e a Rosa" +-- from Machado de Assis, "A M�o e a Rosa" local mao = [[ - Cursavam estes dois moços a academia de S. Paulo, estando - Luís Alves no quarto ano e Estêvão no terceiro. - Conheceram-se na academia, e ficaram amigos íntimos, tanto - quanto podiam sê-lo dois espíritos diferentes, ou talvez por - isso mesmo que o eram. Estêvão, dotado de extrema - sensibilidade, e não menor fraqueza de ânimo, afetuoso e - bom, não daquela bondade varonil, que é apanágio de uma alma - forte, mas dessa outra bondade mole e de cera, que vai à - mercê de todas as circunstâncias, tinha, além de tudo isso, - o infortúnio de trazer ainda sobre o nariz os óculos - cor-de-rosa de suas virginais ilusões. Luís Alves via bem - com os olhos da cara. Não era mau rapaz, mas tinha o seu - grão de egoísmo, e se não era incapaz de afeições, sabia - regê-las, moderá-las, e sobretudo guiá-las ao seu próprio + Cursavam estes dois mo�os a academia de S. Paulo, estando + Lu�s Alves no quarto ano e Est�v�o no terceiro. + Conheceram-se na academia, e ficaram amigos �ntimos, tanto + quanto podiam s�-lo dois esp�ritos diferentes, ou talvez por + isso mesmo que o eram. Est�v�o, dotado de extrema + sensibilidade, e n�o menor fraqueza de �nimo, afetuoso e + bom, n�o daquela bondade varonil, que � apan�gio de uma alma + forte, mas dessa outra bondade mole e de cera, que vai � + merc� de todas as circunst�ncias, tinha, al�m de tudo isso, + o infort�nio de trazer ainda sobre o nariz os �culos + cor-de-rosa de suas virginais ilus�es. Lu�s Alves via bem + com os olhos da cara. N�o era mau rapaz, mas tinha o seu + gr�o de ego�smo, e se n�o era incapaz de afei��es, sabia + reg�-las, moder�-las, e sobretudo gui�-las ao seu pr�prio interesse. Entre estes dois homens travara-se amizade - íntima, nascida para um na simpatia, para outro no costume. + �ntima, nascida para um na simpatia, para outro no costume. Eram eles os naturais confidentes um do outro, com a - diferença que Luís Alves dava menos do que recebia, e, ainda - assim, nem tudo o que dava exprimia grande confiança. + diferen�a que Lu�s Alves dava menos do que recebia, e, ainda + assim, nem tudo o que dava exprimia grande confian�a. ]] local function random(handle, io_err) @@ -44,8 +44,8 @@ local function random(handle, io_err) if not handle then error("source is empty!", 2) end local len = math.random(0, 1024) local chunk = handle:read(len) - if not chunk then - handle:close() + if not chunk then + handle:close() handle = nil end return chunk @@ -62,7 +62,7 @@ local what = nil local function transform(input, output, filter) local source = random(io.open(input, "rb")) local sink = ltn12.sink.file(io.open(output, "wb")) - if what then + if what then sink = ltn12.sink.chain(filter, sink) else source = ltn12.source.chain(source, filter) @@ -147,7 +147,7 @@ local function create_qptest() f:write(' ',string.char(32)) end f:write("\r\n") - + f:close() end @@ -157,7 +157,7 @@ local function cleanup_qptest() os.remove(dqptest) end --- create test file +-- create test file local function create_b64test() local f = assert(io.open(b64test, "wb")) local t = {} diff --git a/test/smtptest.lua b/test/smtptest.lua index b5380ff..9d06054 100644 --- a/test/smtptest.lua +++ b/test/smtptest.lua @@ -27,8 +27,8 @@ local total = function() end local similar = function(s1, s2) - return - string.lower(string.gsub(s1, "%s", "")) == + return + string.lower(string.gsub(s1, "%s", "")) == string.lower(string.gsub(s2, "%s", "")) end @@ -40,9 +40,9 @@ end local readfile = function(name) local f = io.open(name, "r") - if not f then + if not f then fail("unable to open file!") - return nil + return nil end local s = f:read("*a") f:close() @@ -52,7 +52,7 @@ end local empty = function() for i,v in ipairs(files) do local f = io.open(v, "w") - if not f then + if not f then fail("unable to open file!") end f:close() @@ -116,8 +116,8 @@ local wait = function(sentinel, n) while 1 do local mbox = parse(get()) if n == #mbox then break end - if socket.time() - sentinel.time > 50 then - to = 1 + if socket.time() - sentinel.time > 50 then + to = 1 break end socket.sleep(1) @@ -132,7 +132,7 @@ local stuffed_body = [[ This message body needs to be stuffed because it has a dot . -by itself on a line. +by itself on a line. Otherwise the mailer would think that the dot . @@ -219,7 +219,7 @@ else print("ok") end io.write("testing invalid from: ") local ret, err = socket.smtp.mail{ - from = ' " " (( _ * ', + from = ' " " (( _ * ', rcpt = rcpt, } if ret or not err then fail("wrong error message") @@ -227,7 +227,7 @@ else print(err) end io.write("testing no rcpt: ") local ret, err = socket.smtp.mail{ - from = from, + from = from, } if ret or not err then fail("wrong error message") else print(err) end diff --git a/test/test_socket_error.lua b/test/test_socket_error.lua index bda6408..1b4b601 100644 --- a/test/test_socket_error.lua +++ b/test/test_socket_error.lua @@ -19,7 +19,7 @@ for i = 1, 10 do assert(ss == sock) else assert('timeout' == err, 'unexpected error :' .. tostring(err)) - end + end err = sock:getoption("error") -- i get 'connection refused' on WinXP if err then print("Passed! Error is '" .. err .. "'.") diff --git a/test/testmesg.lua b/test/testmesg.lua index 135a008..8c086d5 100644 --- a/test/testmesg.lua +++ b/test/testmesg.lua @@ -34,11 +34,11 @@ r, e = smtp.send{ print(r, e) --- creates a source to send a message with two parts. The first part is +-- creates a source to send a message with two parts. The first part is -- plain text, the second part is a PNG image, encoded as base64. source = smtp.message{ headers = { - -- Remember that headers are *ignored* by smtp.send. + -- Remember that headers are *ignored* by smtp.send. from = "Sicrano ", to = "Fulano ", subject = "Here is a message with attachments" @@ -49,18 +49,18 @@ source = smtp.message{ "Preamble might show up even in a MIME enabled client.", -- first part: No headers means plain text, us-ascii. -- The mime.eol low-level filter normalizes end-of-line markers. - [1] = { + [1] = { body = mime.eol(0, [[ - Lines in a message body should always end with CRLF. + Lines in a message body should always end with CRLF. The smtp module will *NOT* perform translation. It will perform necessary stuffing, though. ]]) }, - -- second part: Headers describe content the to be an image, + -- second part: Headers describe content the to be an image, -- sent under the base64 transfer content encoding. - -- Notice that nothing happens until the message is sent. Small + -- Notice that nothing happens until the message is sent. Small -- chunks are loaded into memory and translation happens on the fly. - [2] = { + [2] = { headers = { ["ConTenT-tYpE"] = 'image/png; name="luasocket.png"', ["content-disposition"] = 'attachment; filename="luasocket.png"', diff --git a/test/testsupport.lua b/test/testsupport.lua index b986088..4360b6b 100644 --- a/test/testsupport.lua +++ b/test/testsupport.lua @@ -7,7 +7,7 @@ function readfile(name) end function similar(s1, s2) - return string.lower(string.gsub(s1 or "", "%s", "")) == + return string.lower(string.gsub(s1 or "", "%s", "")) == string.lower(string.gsub(s2 or "", "%s", "")) end diff --git a/test/urltest.lua b/test/urltest.lua index ae8ba75..9a3c470 100644 --- a/test/urltest.lua +++ b/test/urltest.lua @@ -60,8 +60,8 @@ end local check_absolute_url = function(base, relative, absolute) local res = socket.url.absolute(base, relative) - if res ~= absolute then - io.write("absolute: In test for base='", base, "', rel='", relative, "' expected '", + if res ~= absolute then + io.write("absolute: In test for base='", base, "', rel='", relative, "' expected '", absolute, "' but got '", res, "'\n") os.exit() end @@ -73,7 +73,7 @@ local check_parse_url = function(gaba) local parsed = socket.url.parse(url) for i, v in pairs(gaba) do if v ~= parsed[i] then - io.write("parse: In test for '", url, "' expected ", i, " = '", + io.write("parse: In test for '", url, "' expected ", i, " = '", v, "' but got '", tostring(parsed[i]), "'\n") for i,v in pairs(parsed) do print(i,v) end os.exit() @@ -81,7 +81,7 @@ local check_parse_url = function(gaba) end for i, v in pairs(parsed) do if v ~= gaba[i] then - io.write("parse: In test for '", url, "' expected ", i, " = '", + io.write("parse: In test for '", url, "' expected ", i, " = '", tostring(gaba[i]), "' but got '", v, "'\n") for i,v in pairs(parsed) do print(i,v) end os.exit() @@ -92,8 +92,8 @@ end print("testing URL parsing") check_parse_url{ url = "scheme://user:pass$%?#wd@host:port/path;params?query#fragment", - scheme = "scheme", - authority = "user:pass$%?#wd@host:port", + scheme = "scheme", + authority = "user:pass$%?#wd@host:port", host = "host", port = "port", userinfo = "user:pass$%?#wd", @@ -106,8 +106,8 @@ check_parse_url{ } check_parse_url{ url = "scheme://user:pass?#wd@host:port/path;params?query#fragment", - scheme = "scheme", - authority = "user:pass?#wd@host:port", + scheme = "scheme", + authority = "user:pass?#wd@host:port", host = "host", port = "port", userinfo = "user:pass?#wd", @@ -120,8 +120,8 @@ check_parse_url{ } check_parse_url{ url = "scheme://user:pass-wd@host:port/path;params?query#fragment", - scheme = "scheme", - authority = "user:pass-wd@host:port", + scheme = "scheme", + authority = "user:pass-wd@host:port", host = "host", port = "port", userinfo = "user:pass-wd", @@ -134,8 +134,8 @@ check_parse_url{ } check_parse_url{ url = "scheme://user:pass#wd@host:port/path;params?query#fragment", - scheme = "scheme", - authority = "user:pass#wd@host:port", + scheme = "scheme", + authority = "user:pass#wd@host:port", host = "host", port = "port", userinfo = "user:pass#wd", @@ -148,8 +148,8 @@ check_parse_url{ } check_parse_url{ url = "scheme://user:pass#wd@host:port/path;params?query", - scheme = "scheme", - authority = "user:pass#wd@host:port", + scheme = "scheme", + authority = "user:pass#wd@host:port", host = "host", port = "port", userinfo = "user:pass#wd", @@ -161,8 +161,8 @@ check_parse_url{ } check_parse_url{ url = "scheme://userinfo@host:port/path;params?query#fragment", - scheme = "scheme", - authority = "userinfo@host:port", + scheme = "scheme", + authority = "userinfo@host:port", host = "host", port = "port", userinfo = "userinfo", @@ -175,8 +175,8 @@ check_parse_url{ check_parse_url{ url = "scheme://user:password@host:port/path;params?query#fragment", - scheme = "scheme", - authority = "user:password@host:port", + scheme = "scheme", + authority = "user:password@host:port", host = "host", port = "port", userinfo = "user:password", @@ -190,8 +190,8 @@ check_parse_url{ check_parse_url{ url = "scheme://userinfo@host:port/path;params?query#", - scheme = "scheme", - authority = "userinfo@host:port", + scheme = "scheme", + authority = "userinfo@host:port", host = "host", port = "port", userinfo = "userinfo", @@ -204,8 +204,8 @@ check_parse_url{ check_parse_url{ url = "scheme://userinfo@host:port/path;params?#fragment", - scheme = "scheme", - authority = "userinfo@host:port", + scheme = "scheme", + authority = "userinfo@host:port", host = "host", port = "port", userinfo = "userinfo", @@ -218,8 +218,8 @@ check_parse_url{ check_parse_url{ url = "scheme://userinfo@host:port/path;params#fragment", - scheme = "scheme", - authority = "userinfo@host:port", + scheme = "scheme", + authority = "userinfo@host:port", host = "host", port = "port", userinfo = "userinfo", @@ -231,8 +231,8 @@ check_parse_url{ check_parse_url{ url = "scheme://userinfo@host:port/path;?query#fragment", - scheme = "scheme", - authority = "userinfo@host:port", + scheme = "scheme", + authority = "userinfo@host:port", host = "host", port = "port", userinfo = "userinfo", @@ -245,8 +245,8 @@ check_parse_url{ check_parse_url{ url = "scheme://userinfo@host:port/path?query#fragment", - scheme = "scheme", - authority = "userinfo@host:port", + scheme = "scheme", + authority = "userinfo@host:port", host = "host", port = "port", userinfo = "userinfo", @@ -258,8 +258,8 @@ check_parse_url{ check_parse_url{ url = "scheme://userinfo@host:port/;params?query#fragment", - scheme = "scheme", - authority = "userinfo@host:port", + scheme = "scheme", + authority = "userinfo@host:port", host = "host", port = "port", userinfo = "userinfo", @@ -272,8 +272,8 @@ check_parse_url{ check_parse_url{ url = "scheme://userinfo@host:port", - scheme = "scheme", - authority = "userinfo@host:port", + scheme = "scheme", + authority = "userinfo@host:port", host = "host", port = "port", userinfo = "userinfo", @@ -282,7 +282,7 @@ check_parse_url{ check_parse_url{ url = "//userinfo@host:port/path;params?query#fragment", - authority = "userinfo@host:port", + authority = "userinfo@host:port", host = "host", port = "port", userinfo = "userinfo", @@ -295,7 +295,7 @@ check_parse_url{ check_parse_url{ url = "//userinfo@host:port/path", - authority = "userinfo@host:port", + authority = "userinfo@host:port", host = "host", port = "port", userinfo = "userinfo", @@ -305,7 +305,7 @@ check_parse_url{ check_parse_url{ url = "//userinfo@host/path", - authority = "userinfo@host", + authority = "userinfo@host", host = "host", userinfo = "userinfo", user = "userinfo", @@ -314,7 +314,7 @@ check_parse_url{ check_parse_url{ url = "//user:password@host/path", - authority = "user:password@host", + authority = "user:password@host", host = "host", userinfo = "user:password", password = "password", @@ -324,7 +324,7 @@ check_parse_url{ check_parse_url{ url = "//user:@host/path", - authority = "user:@host", + authority = "user:@host", host = "host", userinfo = "user:", password = "", @@ -334,7 +334,7 @@ check_parse_url{ check_parse_url{ url = "//user@host:port/path", - authority = "user@host:port", + authority = "user@host:port", host = "host", userinfo = "user", user = "user", @@ -344,7 +344,7 @@ check_parse_url{ check_parse_url{ url = "//host:port/path", - authority = "host:port", + authority = "host:port", port = "port", host = "host", path = "/path", @@ -352,14 +352,14 @@ check_parse_url{ check_parse_url{ url = "//host/path", - authority = "host", + authority = "host", host = "host", path = "/path", } check_parse_url{ url = "//host", - authority = "host", + authority = "host", host = "host", } @@ -433,7 +433,7 @@ check_parse_url{ check_parse_url{ url = "//userinfo@[::FFFF:129.144.52.38]:port/path;params?query#fragment", - authority = "userinfo@[::FFFF:129.144.52.38]:port", + authority = "userinfo@[::FFFF:129.144.52.38]:port", host = "::FFFF:129.144.52.38", port = "port", userinfo = "userinfo", @@ -447,7 +447,7 @@ check_parse_url{ check_parse_url{ url = "scheme://user:password@[::192.9.5.5]:port/path;params?query#fragment", scheme = "scheme", - authority = "user:password@[::192.9.5.5]:port", + authority = "user:password@[::192.9.5.5]:port", host = "::192.9.5.5", port = "port", userinfo = "user:password", @@ -462,7 +462,7 @@ check_parse_url{ print("testing URL building") check_build_url { url = "scheme://user:password@host:port/path;params?query#fragment", - scheme = "scheme", + scheme = "scheme", host = "host", port = "port", user = "user", @@ -499,7 +499,7 @@ check_build_url{ check_build_url { url = "scheme://user:password@host/path;params?query#fragment", - scheme = "scheme", + scheme = "scheme", host = "host", user = "user", password = "password", @@ -511,7 +511,7 @@ check_build_url { check_build_url { url = "scheme://user@host/path;params?query#fragment", - scheme = "scheme", + scheme = "scheme", host = "host", user = "user", path = "/path", @@ -522,7 +522,7 @@ check_build_url { check_build_url { url = "scheme://host/path;params?query#fragment", - scheme = "scheme", + scheme = "scheme", host = "host", path = "/path", params = "params", @@ -532,7 +532,7 @@ check_build_url { check_build_url { url = "scheme://host/path;params#fragment", - scheme = "scheme", + scheme = "scheme", host = "host", path = "/path", params = "params", @@ -541,7 +541,7 @@ check_build_url { check_build_url { url = "scheme://host/path#fragment", - scheme = "scheme", + scheme = "scheme", host = "host", path = "/path", fragment = "fragment" @@ -549,7 +549,7 @@ check_build_url { check_build_url { url = "scheme://host/path", - scheme = "scheme", + scheme = "scheme", host = "host", path = "/path", } @@ -567,7 +567,7 @@ check_build_url { check_build_url { url = "scheme://user:password@host:port/path;params?query#fragment", - scheme = "scheme", + scheme = "scheme", host = "host", port = "port", user = "user", @@ -581,7 +581,7 @@ check_build_url { check_build_url { url = "scheme://user:password@host:port/path;params?query#fragment", - scheme = "scheme", + scheme = "scheme", host = "host", port = "port", user = "user", @@ -596,7 +596,7 @@ check_build_url { check_build_url { url = "scheme://user:password@host:port/path;params?query#fragment", - scheme = "scheme", + scheme = "scheme", host = "host", port = "port", userinfo = "user:password", @@ -609,7 +609,7 @@ check_build_url { check_build_url { url = "scheme://user:password@host:port/path;params?query#fragment", - scheme = "scheme", + scheme = "scheme", authority = "user:password@host:port", path = "/path", params = "params", @@ -683,7 +683,7 @@ check_absolute_url("//a/b/c/d;p?q#f", "d/e/f", "//a/b/c/d/e/f") check_absolute_url("/a/b/c/d;p?q#f", "d/e/f", "/a/b/c/d/e/f") check_absolute_url("a/b/c/d", "d/e/f", "a/b/c/d/e/f") check_absolute_url("a/b/c/d/../", "d/e/f", "a/b/c/d/e/f") -check_absolute_url("http://velox.telemar.com.br", "/dashboard/index.html", +check_absolute_url("http://velox.telemar.com.br", "/dashboard/index.html", "http://velox.telemar.com.br/dashboard/index.html") check_absolute_url("http://example.com/", "../.badhost.com/", "http://example.com/.badhost.com/") check_absolute_url("http://example.com/", "...badhost.com/", "http://example.com/...badhost.com/") @@ -700,11 +700,11 @@ check_absolute_url("http://example.com/a/b/c/d/", "../x/a/../y/z/../../../../q", print("testing path parsing and composition") check_parse_path("/eu/tu/ele", { "eu", "tu", "ele"; is_absolute = 1 }) check_parse_path("/eu/", { "eu"; is_absolute = 1, is_directory = 1 }) -check_parse_path("eu/tu/ele/nos/vos/eles/", +check_parse_path("eu/tu/ele/nos/vos/eles/", { "eu", "tu", "ele", "nos", "vos", "eles"; is_directory = 1}) check_parse_path("/", { is_absolute = 1, is_directory = 1}) check_parse_path("", { }) -check_parse_path("eu%01/%02tu/e%03l%04e/nos/vos%05/e%12les/", +check_parse_path("eu%01/%02tu/e%03l%04e/nos/vos%05/e%12les/", { "eu\1", "\2tu", "e\3l\4e", "nos", "vos\5", "e\18les"; is_directory = 1}) check_parse_path("eu/tu", { "eu", "tu" }) diff --git a/test/utestclnt.lua b/test/utestclnt.lua index 34a0718..7f10643 100644 --- a/test/utestclnt.lua +++ b/test/utestclnt.lua @@ -54,30 +54,30 @@ function check_timeout(tm, sl, elapsed, err, opp, mode, alldone) if not err then warn("must be buffered") elseif err == "timeout" then pass("proper timeout") else fail("unexpected error '%s'", err) end - else - if err ~= "timeout" then fail("should have timed out") + else + if err ~= "timeout" then fail("should have timed out") else pass("proper timeout") end end else if mode == "total" then - if elapsed > tm then + if elapsed > tm then if err ~= "timeout" then fail("should have timed out") else pass("proper timeout") end elseif elapsed < tm then - if err then fail(err) + if err then fail(err) else pass("ok") end - else - if alldone then - if err then fail("unexpected error '%s'", err) + else + if alldone then + if err then fail("unexpected error '%s'", err) else pass("ok") end else - if err ~= "timeout" then fail(err) + if err ~= "timeout" then fail(err) else pass("proper timeoutk") end end end - else - if err then fail(err) - else pass("ok") end + else + if err then fail(err) + else pass("ok") end end end end @@ -104,7 +104,7 @@ function reconnect() print("done " .. i) ]] data, err = uconnect(host, port) - if not data then fail(err) + if not data then fail(err) else pass("connected!") end end @@ -116,8 +116,8 @@ else pass("connected!") end ------------------------------------------------------------------------ function test_methods(sock, methods) for _, v in pairs(methods) do - if type(sock[v]) ~= "function" then - fail(sock.class .. " method '" .. v .. "' not registered") + if type(sock[v]) ~= "function" then + fail(sock.class .. " method '" .. v .. "' not registered") end end pass(sock.class .. " methods are ok") @@ -132,7 +132,7 @@ function test_mixed(len) local p3 = "raw " .. string.rep("z", inter) .. "bytes" local p4 = "end" .. string.rep("w", inter) .. "bytes" local bp1, bp2, bp3, bp4 -remote (string.format("str = data:receive(%d)", +remote (string.format("str = data:receive(%d)", string.len(p1)+string.len(p2)+string.len(p3)+string.len(p4))) sent, err = data:send(p1..p2..p3..p4) if err then fail(err) end @@ -172,7 +172,7 @@ function test_rawline(len) reconnect() local str, str10, back, err str = string.rep(string.char(47), math.mod(len, 10)) - str10 = string.rep(string.char(120,21,77,4,5,0,7,36,44,100), + str10 = string.rep(string.char(120,21,77,4,5,0,7,36,44,100), math.floor(len/10)) str = str .. str10 remote "str = data:receive()" @@ -221,7 +221,7 @@ function test_totaltimeoutreceive(len, tm, sl) data:settimeout(tm, "total") local t = socket.gettime() str, err, partial, elapsed = data:receive(2*len) - check_timeout(tm, sl, elapsed, err, "receive", "total", + check_timeout(tm, sl, elapsed, err, "receive", "total", string.len(str or partial) == 2*len) end @@ -241,7 +241,7 @@ function test_totaltimeoutsend(len, tm, sl) data:settimeout(tm, "total") str = string.rep("a", 2*len) total, err, partial, elapsed = data:send(str) - check_timeout(tm, sl, elapsed, err, "send", "total", + check_timeout(tm, sl, elapsed, err, "send", "total", total == 2*len) end @@ -261,7 +261,7 @@ function test_blockingtimeoutreceive(len, tm, sl) ]], 2*tm, len, sl, sl)) data:settimeout(tm) str, err, partial, elapsed = data:receive(2*len) - check_timeout(tm, sl, elapsed, err, "receive", "blocking", + check_timeout(tm, sl, elapsed, err, "receive", "blocking", string.len(str or partial) == 2*len) end @@ -294,10 +294,10 @@ function empty_connect() data = server:accept() ]] data, err = socket.connect("", port) - if not data then + if not data then pass("ok") data = socket.connect(host, port) - else + else pass("gethostbyname returns localhost on empty string...") end end @@ -331,7 +331,7 @@ function test_closed() data:close() data = nil ]], str)) - -- try to get a line + -- try to get a line back, err, partial = data:receive() if not err then fail("should have gotten 'closed'.") elseif err ~= "closed" then fail("got '"..err.."' instead of 'closed'.") @@ -344,25 +344,25 @@ function test_closed() data = nil ]] total, err, partial = data:send(string.rep("ugauga", 100000)) - if not err then + if not err then pass("failed: output buffer is at least %d bytes long!", total) - elseif err ~= "closed" then + elseif err ~= "closed" then fail("got '"..err.."' instead of 'closed'.") - else - pass("graceful 'closed' received after %d bytes were sent", partial) + else + pass("graceful 'closed' received after %d bytes were sent", partial) end end ------------------------------------------------------------------------ function test_selectbugs() local r, s, e = socket.select(nil, nil, 0.1) - assert(type(r) == "table" and type(s) == "table" and + assert(type(r) == "table" and type(s) == "table" and (e == "timeout" or e == "error")) pass("both nil: ok") local udp = socket.udp() udp:close() r, s, e = socket.select({ udp }, { udp }, 0.1) - assert(type(r) == "table" and type(s) == "table" and + assert(type(r) == "table" and type(s) == "table" and (e == "timeout" or e == "error")) pass("closed sockets: ok") e = pcall(socket.select, "wrong", 1, 0.1) @@ -380,7 +380,7 @@ function accept_timeout() local t = socket.gettime() s:settimeout(1) local c, e = s:accept() - assert(not c, "should not accept") + assert(not c, "should not accept") assert(e == "timeout", string.format("wrong error message (%s)", e)) t = socket.gettime() - t assert(t < 2, string.format("took to long to give up (%gs)", t)) @@ -398,9 +398,9 @@ function connect_timeout() local t = socket.gettime() local r, e = c:connect("127.0.0.2", 80) assert(not r, "should not connect") - assert(socket.gettime() - t < 2, "took too long to give up.") + assert(socket.gettime() - t < 2, "took too long to give up.") c:close() - print("ok") + print("ok") end ------------------------------------------------------------------------ @@ -463,9 +463,9 @@ function getstats_test() data:receive(c) t = t + c local r, s, a = data:getstats() - assert(r == t, "received count failed" .. tostring(r) + assert(r == t, "received count failed" .. tostring(r) .. "/" .. tostring(t)) - assert(s == t, "sent count failed" .. tostring(s) + assert(s == t, "sent count failed" .. tostring(s) .. "/" .. tostring(t)) end print("ok") @@ -473,7 +473,7 @@ end ------------------------------------------------------------------------ -function test_nonblocking(size) +function test_nonblocking(size) reconnect() print("Testing " .. 2*size .. " bytes") remote(string.format([[ diff --git a/test/utestsrvr.lua b/test/utestsrvr.lua index a96b570..b6e4246 100644 --- a/test/utestsrvr.lua +++ b/test/utestsrvr.lua @@ -9,7 +9,7 @@ ack = "\n"; while 1 do print("server: waiting for client connection..."); control = assert(server:accept()); - while 1 do + while 1 do command = assert(control:receive()); assert(control:send(ack)); ((loadstring or load)(command))(); -- cgit v1.2.3-55-g6feb From 2a76cb906cb955a83ed76b8e47cc76c77ce8e15f Mon Sep 17 00:00:00 2001 From: Julian Squires Date: Fri, 16 Oct 2020 12:18:46 -0230 Subject: http.lua: set transfer-encoding if source and no content-length If a source is specified without a content-length header, LuaSocket sends the data in the chunked transfer coding; however, it doesn't set the transfer-encoding header. While I recognize that the user can set this manually, this is a gotcha that has caught me multiple times. RFC7230, section 3.3.3 (https://tools.ietf.org/html/rfc7230#section-3.3.3) is clear about this; if neither content-length nor transfer-encoding chunked are specified, the request message body length is zero. While some servers may ignore this, I have encountered several that follow the RFC in this regard, most recently golang's net/http. --- src/http.lua | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'src/http.lua') diff --git a/src/http.lua b/src/http.lua index e3a1742..1330355 100644 --- a/src/http.lua +++ b/src/http.lua @@ -283,6 +283,13 @@ local function adjustrequest(reqt) nreqt.uri = reqt.uri or adjusturi(nreqt) -- adjust headers in request nreqt.headers = adjustheaders(nreqt) + if nreqt.source + and not nreqt.headers["content-length"] + and not nreqt.headers["transfer-encoding"] + then + nreqt.headers["transfer-encoding"] = "chunked" + end + -- ajust host and port if there is a proxy nreqt.host, nreqt.port = adjustproxy(nreqt) return nreqt -- cgit v1.2.3-55-g6feb From 8c2ff7217e2a205eb107a6f48b04ff1b2b3090a1 Mon Sep 17 00:00:00 2001 From: Henri D Date: Sat, 8 Oct 2022 08:42:36 +0200 Subject: fix(http): Allow relative redirect on https (#395) Location header can now be relative: https://httpwg.org/specs/rfc9110.html#field.location --- src/http.lua | 5 +++-- test/httptest.lua | 31 +++++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 2 deletions(-) (limited to 'src/http.lua') diff --git a/src/http.lua b/src/http.lua index 1330355..fbd5ff6 100644 --- a/src/http.lua +++ b/src/http.lua @@ -300,6 +300,8 @@ local function shouldredirect(reqt, code, headers) if not location then return false end location = string.gsub(location, "%s", "") if location == "" then return false end + -- the RFC says the redirect URL may be relative + location = url.absolute(reqt.url, location) local scheme = url.parse(location).scheme if scheme and (not SCHEMES[scheme]) then return false end -- avoid https downgrades @@ -323,8 +325,7 @@ end local trequest, tredirect --[[local]] function tredirect(reqt, location) - -- the RFC says the redirect URL has to be absolute, but some - -- servers do not respect that + -- the RFC says the redirect URL may be relative local newurl = url.absolute(reqt.url, location) -- if switching schemes, reset port and create function if url.parse(newurl).scheme ~= reqt.scheme then diff --git a/test/httptest.lua b/test/httptest.lua index 63ff921..3457b07 100644 --- a/test/httptest.lua +++ b/test/httptest.lua @@ -265,6 +265,37 @@ ignore = { } check_request(request, expect, ignore) +-- Use https://httpbin.org/#/Dynamic_data/get_base64__value_ for testing +----------------------------------------------------- +io.write("testing absolute https redirection: ") +request = { + url = "https://httpbin.org/redirect-to?url=https://httpbin.org/base64/THVhIFNvY2tldA==" +} +expect = { + code = 200, + body = "Lua Socket" +} +ignore = { + status = 1, + headers = 1 +} +check_request(request, expect, ignore) + +----------------------------------------------------- +io.write("testing relative https redirection: ") +request = { + url = "https://httpbin.org/redirect-to?url=/base64/THVhIFNvY2tldA==" +} +expect = { + code = 200, + body = "Lua Socket" +} +ignore = { + status = 1, + headers = 1 +} +check_request(request, expect, ignore) + ------------------------------------------------------------------------ --[[ io.write("testing proxy with redirection: ") -- cgit v1.2.3-55-g6feb