Saving before big changes to support IPv6.

This commit is contained in:
Diego Nehab 2011-05-25 20:57:22 +00:00
parent bce60be30f
commit 3a8ba90dfb
30 changed files with 948 additions and 850 deletions

13
FIX
View File

@ -1,15 +1,14 @@
http was preserving old host header during redirects http was preserving old host header during redirects
fix smtp.send hang on source error fix smtp.send hang on source error
add create field to FTP and SMTP and fix HTTP ugliness add create field to FTP and SMTP and fix HTTP ugliness
clean timeout argument to open functions in SMTP, HTTP and FTP clean timeout argument to open functions in SMTP, HTTP and FTP
eliminate globals from namespaces created by module(). eliminate globals from namespaces created by module().
url.absolute was not working when base_url was already parsed url.absolute was not working when base_url was already parsed
http.request was redirecting even when the location header was empty http.request was redirecting even when the location header was empty
tcp{client}:shutdown() was checking for group instead of class. tcp{client}:shutdown() was checking for group instead of class.

46
NEW
View File

@ -2,19 +2,37 @@ What's New
This is just a bug-fix/update release. This is just a bug-fix/update release.
* Fixed: manual links to home.html changed to index.html (Robert Hahn) * Fixed: manual sample of HTTP authentication now uses correct
* Fixed: mime.unb64() returns empty string on results that start "authorization" header (Alexandre Ittner);
with a null character (Robert Raschke) * Fixed: failure on bind() was destroying the socket (Sam Roberts);
* Fixed: HTTP now automatically redirecting on 303 and 307 (Jonathan Gray) * Fixed: receive() returns immediatelly if prefix can satisfy
* Fixed: sleep(-1) could sleep forever wasting CPU. Now it bytes requested (M Joonas Pihlaja);
returns immediately (MPB); * Fixed: multicast didn't work on Windows, or anywhere
else for that matter (Herbert Leuwer, Adrian Sietsma);
* Fixed: select() now reports an error when called with more
sockets than FD_SETSIZE (Lorenzo Leonini);
* Fixed: manual links to home.html changed to index.html (Robert Hahn);
* Fixed: mime.unb64() would return an empty string on results that started
with a null character (Robert Raschke);
* Fixed: HTTP now automatically redirects on 303 and 307 (Jonathan Gray);
* Fixed: calling sleep() with negative numbers could
block forever, wasting CPU. Now it returns immediately (MPB);
* Improved: FTP commands are now sent in upper case to
help buggy servers (Anders Eurenius);
* Improved: known headers now sent in canonic
capitalization to help buggy servers (Joseph Stewart);
* Improved: Clarified tcp:receive() in the manual (MPB);
* Improved: Decent makefiles (LHF).
* Fixed: RFC links in documentation now point to IETF (Cosmin Apreutesei).
* Improved: FTP commands are now sent in upper case to
help buggy servers (Anders Eurenius)
* Improved: known headers now sent in canonic
capitalization to help buggy servers (Joseph Stewart);
* Improved: Clarified tcp:receive() in the manual (MPB);
* Fixed: multicast didn't work on Windows (Herbert Leuwer, Adrian Sietsma) Yuri's bug?
* Fixed: select() reports an error when called with more Dahlberg
sockets than FD_SETSIZE (Lorenzo Leonini) Sam Roberts
Thomas Harning Jr.
Sebastien Perin
remove getn in all files
ltn12.pump.all(
ltn12.source.file(io.open("original.png")),
ltn12.sink.file(io.open("copy.png", "wb"))
)

View File

@ -42,7 +42,7 @@
FTP (File Transfer Protocol) is a protocol used to transfer files FTP (File Transfer Protocol) is a protocol used to transfer files
between hosts. The <tt>ftp</tt> namespace offers thorough support between hosts. The <tt>ftp</tt> namespace offers thorough support
to FTP, under a simple interface. The implementation conforms to to FTP, under a simple interface. The implementation conforms to
<a href="http://www.cs.princeton.edu/~diego/rfc/rfc0959.txt">RFC 959</a>. <a href="http://www.ietf.org/rfc/rfc959.txt">RFC 959</a>.
</p> </p>
<p> <p>
@ -70,8 +70,8 @@ local ftp = require("socket.ftp")
<p> <p>
URLs MUST conform to URLs MUST conform to
<a href="http://www.cs.princeton.edu/~diego/rfc/rfc1738.txt">RFC <a href="http://www.ietf.org/rfc/rfc1738.txt">RFC 1738</a>,
1738</a>, that is, an URL is a string in the form: that is, an URL is a string in the form:
</p> </p>
<blockquote> <blockquote>

View File

@ -45,8 +45,7 @@ namespace offers full support for the client side of the HTTP
protocol (i.e., protocol (i.e.,
the facilities that would be used by a web-browser implementation). The the facilities that would be used by a web-browser implementation). The
implementation conforms to the HTTP/1.1 standard, implementation conforms to the HTTP/1.1 standard,
<a href="http://www.cs.princeton.edu/~diego/rfc/rfc2616.txt">RFC <a href="http://www.ietf.org/rfc/rfc2616.txt">RFC 2616</a>.
2616</a>.
</p> </p>
<p> <p>
@ -67,8 +66,7 @@ local http = require("socket.http")
<p> <p>
URLs must conform to URLs must conform to
<a href="http://www.cs.princeton.edu/~diego/rfc/rfc1738.txt">RFC <a href="http://www.ietf.org/rfc/rfc1738.txt">RFC 1738</a>,
1738</a>,
that is, an URL is a string in the form: that is, an URL is a string in the form:
</p> </p>
@ -199,8 +197,7 @@ it usually returns a message body (a web page informing the
URL was not found or some other useless page). To make sure the URL was not found or some other useless page). To make sure the
operation was successful, check the returned status <tt>code</tt>. For operation was successful, check the returned status <tt>code</tt>. For
a list of the possible values and their meanings, refer to <a a list of the possible values and their meanings, refer to <a
href="http://www.cs.princeton.edu/~diego/rfc/rfc2616.txt">RFC href="http://www.ietf.org/rfc/rfc2616.txt">RFC 2616</a>.
2616</a>.
</p> </p>
<p class=description> <p class=description>
@ -278,7 +275,7 @@ download and return status "401&nbsp;Authentication Required".
The HTTP/1.1 standard defines two authentication methods: the Basic The HTTP/1.1 standard defines two authentication methods: the Basic
Authentication Scheme and the Digest Authentication Scheme, both Authentication Scheme and the Digest Authentication Scheme, both
explained in detail in explained in detail in
<a href="http://www.cs.princeton.edu/~diego/rfc/rfc2068.txt">RFC 2068</a>. <a href="http://www.ietf.org/rfc/rfc2068.txt">RFC 2068</a>.
</p> </p>
<p class=note>The Basic Authentication Scheme sends <p class=note>The Basic Authentication Scheme sends
@ -304,7 +301,7 @@ b, c, h = http.request("http://fulano:silva@www.example.com/private/index.html")
-- the request directly. -- the request directly.
r, c = http.request { r, c = http.request {
url = "http://www.example.com/private/index.html", url = "http://www.example.com/private/index.html",
headers = { authentication = "Basic " .. (mime.b64("fulano:silva")) } headers = { authorization = "Basic " .. (mime.b64("fulano:silva")) }
} }
</pre> </pre>

View File

@ -138,18 +138,22 @@ all!
</p> </p>
<ul> <ul>
<li> Fixed: manual sample of HTTP authentication now uses correct
"authorization" header (Alexandre Ittner);
<li> Fixed: receive() returns immediatelly if prefix can satisfy
bytes requested (M Joonas Pihlaja);
<li> Fixed: multicast didn't work on Windows, or anywhere <li> Fixed: multicast didn't work on Windows, or anywhere
else for that matter (Herbert Leuwer, Adrian Sietsma) else for that matter (Herbert Leuwer, Adrian Sietsma);
<li> Fixed: select() now reports an error when called with more <li> Fixed: select() now reports an error when called with more
sockets than FD_SETSIZE (Lorenzo Leonini) sockets than FD_SETSIZE (Lorenzo Leonini);
<li> Fixed: manual links to home.html changed to index.html (Robert Hahn) <li> Fixed: manual links to home.html changed to index.html (Robert Hahn);
<li> Fixed: mime.unb64() would return an empty string on results that started <li> Fixed: mime.unb64() would return an empty string on results that started
with a null character (Robert Raschke) with a null character (Robert Raschke);
<li> Fixed: HTTP now automatically redirects on 303 and 307 (Jonathan Gray) <li> Fixed: HTTP now automatically redirects on 303 and 307 (Jonathan Gray);
<li> Fixed: calling sleep() with negative numbers could <li> Fixed: calling sleep() with negative numbers could
block forever, wasting CPU. Now it returns immediately (MPB); block forever, wasting CPU. Now it returns immediately (MPB);
<li> Improved: FTP commands are now sent in upper case to <li> Improved: FTP commands are now sent in upper case to
help buggy servers (Anders Eurenius) help buggy servers (Anders Eurenius);
<li> Improved: known headers now sent in canonic <li> Improved: known headers now sent in canonic
capitalization to help buggy servers (Joseph Stewart); capitalization to help buggy servers (Joseph Stewart);
<li> Improved: Clarified tcp:receive() in the manual (MPB); <li> Improved: Clarified tcp:receive() in the manual (MPB);

View File

@ -44,11 +44,11 @@ content transfer encodings, such as Base64 and Quoted-Printable.
It also provides functions to break text into lines and change It also provides functions to break text into lines and change
the end-of-line convention. the end-of-line convention.
MIME is described mainly in MIME is described mainly in
<a href="http://www.cs.princeton.edu/~diego/rfc/rfc2045.txt">RFC 2045</a>, <a href="http://www.ietf.org/rfc/rfc2045.txt">RFC 2045</a>,
<a href="http://www.cs.princeton.edu/~diego/rfc/rfc2046.txt">2046</a>, <a href="http://www.ietf.org/rfc/rfc2046.txt">2046</a>,
<a href="http://www.cs.princeton.edu/~diego/rfc/rfc2047.txt">2047</a>, <a href="http://www.ietf.org/rfc/rfc2047.txt">2047</a>,
<a href="http://www.cs.princeton.edu/~diego/rfc/rfc2047.txt">2048</a>, and <a href="http://www.ietf.org/rfc/rfc2047.txt">2048</a>, and
<a href="http://www.cs.princeton.edu/~diego/rfc/rfc2048.txt">2049</a>. <a href="http://www.ietf.org/rfc/rfc2048.txt">2049</a>.
</p> </p>
<p> <p>

View File

@ -48,14 +48,13 @@ control (if you bother to read the code).
</p> </p>
<p>The implementation conforms to the Simple Mail Transfer Protocol, <p>The implementation conforms to the Simple Mail Transfer Protocol,
<a href="http://www.cs.princeton.edu/~diego/rfc/rfc2821.txt">RFC 2821</a>. <a href="http://www.ietf.org/rfc/rfc2821.txt">RFC 2821</a>.
Another RFC of interest is <a Another RFC of interest is <a
href="http://www.cs.princeton.edu/~diego/rfc/rfc2822.txt">RFC 2822</a>, href="http://www.ietf.org/rfc/rfc2822.txt">RFC 2822</a>,
which governs the Internet Message Format. which governs the Internet Message Format.
Multipart messages (those that contain attachments) are part Multipart messages (those that contain attachments) are part
of the MIME standard, but described mainly of the MIME standard, but described mainly
in <a href="http://www.cs.princeton.edu/~diego/rfc/rfc2046.txt">RFC in <a href="http://www.ietf.org/rfc/rfc2046.txt">RFC 2046</a>
2046</a>
<p> In the description below, good understanding of <a <p> In the description below, good understanding of <a
href="http://lua-users.org/wiki/FiltersSourcesAndSinks"> LTN012, Filters href="http://lua-users.org/wiki/FiltersSourcesAndSinks"> LTN012, Filters
@ -196,7 +195,7 @@ part of the message and will not be sent to anyone.
</p> </p>
<p class=note> <p class=note>
<a href="http://www.cs.princeton.edu/~diego/rfc/rfc2822.txt">RFC 2822</a> <a href="http://www.ietf.org/rfc/rfc2822.txt">RFC 2822</a>
has two <em>important and short</em> sections, "3.6.3. Destination address has two <em>important and short</em> sections, "3.6.3. Destination address
fields" and "5. Security considerations", explaining the proper fields" and "5. Security considerations", explaining the proper
use of these headers. Here is a summary of what it says: use of these headers. Here is a summary of what it says:
@ -236,9 +235,9 @@ exactly what you <em>don't</em> want to happen!
<p class=note> <p class=note>
I hope this clarifies the issue. Otherwise, please refer to I hope this clarifies the issue. Otherwise, please refer to
<a href="http://www.cs.princeton.edu/~diego/rfc/rfc2821.txt">RFC 2821</a> <a href="http://www.ietf.org/rfc/rfc2821.txt">RFC 2821</a>
and and
<a href="http://www.cs.princeton.edu/~diego/rfc/rfc2822.txt">RFC 2822</a>. <a href="http://www.ietf.org/rfc/rfc2822.txt">RFC 2822</a>.
</p> </p>
<pre class=example> <pre class=example>

View File

@ -42,8 +42,7 @@
The <tt>url</tt> namespace provides functions to parse, protect, The <tt>url</tt> namespace provides functions to parse, protect,
and build URLs, as well as functions to compose absolute URLs and build URLs, as well as functions to compose absolute URLs
from base and relative URLs, according to from base and relative URLs, according to
<a href="http://www.cs.princeton.edu/~diego/rfc/rfc2396.txt">RFC <a href="http://www.ietf.org/rfc/rfc2396.txt">RFC 2396</a>.
2396</a>.
</p> </p>
<p> <p>
@ -91,7 +90,7 @@ The function returns a string with the absolute URL.
<p class=note> <p class=note>
Note: The rules that Note: The rules that
govern the composition are fairly complex, and are described in detail in govern the composition are fairly complex, and are described in detail in
<a href="http://www.cs.princeton.edu/~diego/rfc/rfc2396.txt">RFC 2396</a>. <a href="http://www.ietf.org/rfc/rfc2396.txt">RFC 2396</a>.
The example bellow should give an idea of what the rules are. The example bellow should give an idea of what the rules are.
</p> </p>

View File

@ -44,48 +44,48 @@ function metat.__index:check(ok)
end end
function metat.__index:getdef() function metat.__index:getdef()
local line = socket.try(self.tp:receive()) local line = socket.try(self.tp:receive())
local def = {} local def = {}
while line ~= "." do while line ~= "." do
table.insert(def, line) table.insert(def, line)
line = socket.try(self.tp:receive()) line = socket.try(self.tp:receive())
end end
return table.concat(def, "\n") return table.concat(def, "\n")
end end
function metat.__index:define(database, word) function metat.__index:define(database, word)
database = database or "!" database = database or "!"
socket.try(self.tp:command("DEFINE", database .. " " .. word)) socket.try(self.tp:command("DEFINE", database .. " " .. word))
local code, count = self:check(150) local code, count = self:check(150)
local defs = {} local defs = {}
for i = 1, count do for i = 1, count do
self:check(151) self:check(151)
table.insert(defs, self:getdef()) table.insert(defs, self:getdef())
end end
self:check(250) self:check(250)
return defs return defs
end end
function metat.__index:match(database, strat, word) function metat.__index:match(database, strat, word)
database = database or "!" database = database or "!"
strat = strat or "." strat = strat or "."
socket.try(self.tp:command("MATCH", database .." ".. strat .." ".. word)) socket.try(self.tp:command("MATCH", database .." ".. strat .." ".. word))
self:check(152) self:check(152)
local mat = {} local mat = {}
local line = socket.try(self.tp:receive()) local line = socket.try(self.tp:receive())
while line ~= '.' do while line ~= '.' do
database, word = socket.skip(2, string.find(line, "(%S+) (.*)")) database, word = socket.skip(2, string.find(line, "(%S+) (.*)"))
if not mat[database] then mat[database] = {} end if not mat[database] then mat[database] = {} end
table.insert(mat[database], word) table.insert(mat[database], word)
line = socket.try(self.tp:receive()) line = socket.try(self.tp:receive())
end end
self:check(250) self:check(250)
return mat return mat
end end
function metat.__index:quit() function metat.__index:quit()
self.tp:command("QUIT") self.tp:command("QUIT")
return self:check(221) return self:check(221)
end end
function metat.__index:close() function metat.__index:close()

View File

@ -12,53 +12,53 @@ local ltn12 = require("ltn12")
-- formats a number of seconds into human readable form -- formats a number of seconds into human readable form
function nicetime(s) function nicetime(s)
local l = "s" local l = "s"
if s > 60 then if s > 60 then
s = s / 60 s = s / 60
l = "m" l = "m"
if s > 60 then if s > 60 then
s = s / 60 s = s / 60
l = "h" l = "h"
if s > 24 then if s > 24 then
s = s / 24 s = s / 24
l = "d" -- hmmm l = "d" -- hmmm
end end
end end
end end
if l == "s" then return string.format("%5.0f%s", s, l) if l == "s" then return string.format("%5.0f%s", s, l)
else return string.format("%5.2f%s", s, l) end else return string.format("%5.2f%s", s, l) end
end end
-- formats a number of bytes into human readable form -- formats a number of bytes into human readable form
function nicesize(b) function nicesize(b)
local l = "B" local l = "B"
if b > 1024 then if b > 1024 then
b = b / 1024 b = b / 1024
l = "KB" l = "KB"
if b > 1024 then if b > 1024 then
b = b / 1024 b = b / 1024
l = "MB" l = "MB"
if b > 1024 then if b > 1024 then
b = b / 1024 b = b / 1024
l = "GB" -- hmmm l = "GB" -- hmmm
end end
end end
end end
return string.format("%7.2f%2s", b, l) return string.format("%7.2f%2s", b, l)
end end
-- returns a string with the current state of the download -- returns a string with the current state of the download
local remaining_s = "%s received, %s/s throughput, %2.0f%% done, %s remaining" local remaining_s = "%s received, %s/s throughput, %2.0f%% done, %s remaining"
local elapsed_s = "%s received, %s/s throughput, %s elapsed " local elapsed_s = "%s received, %s/s throughput, %s elapsed "
function gauge(got, delta, size) function gauge(got, delta, size)
local rate = got / delta local rate = got / delta
if size and size >= 1 then if size and size >= 1 then
return string.format(remaining_s, nicesize(got), nicesize(rate), return string.format(remaining_s, nicesize(got), nicesize(rate),
100*got/size, nicetime((size-got)/rate)) 100*got/size, nicetime((size-got)/rate))
else else
return string.format(elapsed_s, nicesize(got), return string.format(elapsed_s, nicesize(got),
nicesize(rate), nicetime(delta)) nicesize(rate), nicetime(delta))
end end
end end
-- creates a new instance of a receive_cb that saves to disk -- creates a new instance of a receive_cb that saves to disk
@ -89,10 +89,10 @@ end
-- determines the size of a http file -- determines the size of a http file
function gethttpsize(u) function gethttpsize(u)
local r, c, h = http.request {method = "HEAD", url = u} local r, c, h = http.request {method = "HEAD", url = u}
if c == 200 then if c == 200 then
return tonumber(h["content-length"]) return tonumber(h["content-length"])
end end
end end
-- downloads a file using the http protocol -- downloads a file using the http protocol
@ -101,7 +101,7 @@ function getbyhttp(u, file)
-- only print feedback if output is not stdout -- only print feedback if output is not stdout
if file then save = ltn12.sink.chain(stats(gethttpsize(u)), save) end if file then save = ltn12.sink.chain(stats(gethttpsize(u)), save) end
local r, c, h, s = http.request {url = u, sink = save } local r, c, h, s = http.request {url = u, sink = save }
if c ~= 200 then io.stderr:write(s or c, "\n") end if c ~= 200 then io.stderr:write(s or c, "\n") end
end end
-- downloads a file using the ftp protocol -- downloads a file using the ftp protocol
@ -114,29 +114,29 @@ function getbyftp(u, file)
gett.sink = save gett.sink = save
gett.type = "i" gett.type = "i"
local ret, err = ftp.get(gett) local ret, err = ftp.get(gett)
if err then print(err) end if err then print(err) end
end end
-- determines the scheme -- determines the scheme
function getscheme(u) function getscheme(u)
-- this is an heuristic to solve a common invalid url poblem -- this is an heuristic to solve a common invalid url poblem
if not string.find(u, "//") then u = "//" .. u end if not string.find(u, "//") then u = "//" .. u end
local parsed = url.parse(u, {scheme = "http"}) local parsed = url.parse(u, {scheme = "http"})
return parsed.scheme return parsed.scheme
end end
-- gets a file either by http or ftp, saving as <name> -- gets a file either by http or ftp, saving as <name>
function get(u, name) function get(u, name)
local fout = name and io.open(name, "wb") local fout = name and io.open(name, "wb")
local scheme = getscheme(u) local scheme = getscheme(u)
if scheme == "ftp" then getbyftp(u, fout) if scheme == "ftp" then getbyftp(u, fout)
elseif scheme == "http" then getbyhttp(u, fout) elseif scheme == "http" then getbyhttp(u, fout)
else print("unknown scheme" .. scheme) end else print("unknown scheme" .. scheme) end
end end
-- main program -- main program
arg = arg or {} arg = arg or {}
if table.getn(arg) < 1 then if table.getn(arg) < 1 then
io.write("Usage:\n lua get.lua <remote-url> [<local-file>]\n") io.write("Usage:\n lua get.lua <remote-url> [<local-file>]\n")
os.exit(1) os.exit(1)
else get(arg[1], arg[2]) end else get(arg[1], arg[2]) end

View File

@ -268,11 +268,11 @@ send = socket.protect(function(option)
local class = string.sub(option.class or localip or localhost,1,31) local class = string.sub(option.class or localip or localhost,1,31)
local _,_,ctlfn = string.find(file,".*[%/%\\](.*)") local _,_,ctlfn = string.find(file,".*[%/%\\](.*)")
ctlfn = string.sub(ctlfn or file,1,131) ctlfn = string.sub(ctlfn or file,1,131)
local cfile = local cfile =
string.format("H%-s\nC%-s\nJ%-s\nP%-s\n%.1s%-s\nU%-s\nN%-s\n", string.format("H%-s\nC%-s\nJ%-s\nP%-s\n%.1s%-s\nU%-s\nN%-s\n",
localhost, localhost,
class, class,
option.job or "LuaSocket", option.job or "LuaSocket",
user, user,
fmt, lpfile, fmt, lpfile,
lpfile, lpfile,

View File

@ -35,18 +35,18 @@ local OP_INV = {"RRQ", "WRQ", "DATA", "ACK", "ERROR"}
-- Packet creation functions -- Packet creation functions
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
local function RRQ(source, mode) local function RRQ(source, mode)
return char(0, OP_RRQ) .. source .. char(0) .. mode .. char(0) return char(0, OP_RRQ) .. source .. char(0) .. mode .. char(0)
end end
local function WRQ(source, mode) local function WRQ(source, mode)
return char(0, OP_RRQ) .. source .. char(0) .. mode .. char(0) return char(0, OP_RRQ) .. source .. char(0) .. mode .. char(0)
end end
local function ACK(block) local function ACK(block)
local low, high local low, high
low = math.mod(block, 256) low = math.mod(block, 256)
high = (block - low)/256 high = (block - low)/256
return char(0, OP_ACK, high, low) return char(0, OP_ACK, high, low)
end end
local function get_OP(dgram) local function get_OP(dgram)
@ -58,16 +58,16 @@ end
-- Packet analysis functions -- Packet analysis functions
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
local function split_DATA(dgram) local function split_DATA(dgram)
local block = byte(dgram, 3)*256 + byte(dgram, 4) local block = byte(dgram, 3)*256 + byte(dgram, 4)
local data = string.sub(dgram, 5) local data = string.sub(dgram, 5)
return block, data return block, data
end end
local function get_ERROR(dgram) local function get_ERROR(dgram)
local code = byte(dgram, 3)*256 + byte(dgram, 4) local code = byte(dgram, 3)*256 + byte(dgram, 4)
local msg local msg
_,_, msg = string.find(dgram, "(.*)\000", 5) _,_, msg = string.find(dgram, "(.*)\000", 5)
return string.format("error code %d: %s", code, msg) return string.format("error code %d: %s", code, msg)
end end
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
@ -77,40 +77,40 @@ local function tget(gett)
local retries, dgram, sent, datahost, dataport, code local retries, dgram, sent, datahost, dataport, code
local last = 0 local last = 0
socket.try(gett.host, "missing host") socket.try(gett.host, "missing host")
local con = socket.try(socket.udp()) local con = socket.try(socket.udp())
local try = socket.newtry(function() con:close() end) local try = socket.newtry(function() con:close() end)
-- convert from name to ip if needed -- convert from name to ip if needed
gett.host = try(socket.dns.toip(gett.host)) gett.host = try(socket.dns.toip(gett.host))
con:settimeout(1) con:settimeout(1)
-- first packet gives data host/port to be used for data transfers -- first packet gives data host/port to be used for data transfers
local path = string.gsub(gett.path or "", "^/", "") local path = string.gsub(gett.path or "", "^/", "")
path = url.unescape(path) path = url.unescape(path)
retries = 0 retries = 0
repeat repeat
sent = try(con:sendto(RRQ(path, "octet"), gett.host, gett.port)) sent = try(con:sendto(RRQ(path, "octet"), gett.host, gett.port))
dgram, datahost, dataport = con:receivefrom() dgram, datahost, dataport = con:receivefrom()
retries = retries + 1 retries = retries + 1
until dgram or datahost ~= "timeout" or retries > 5 until dgram or datahost ~= "timeout" or retries > 5
try(dgram, datahost) try(dgram, datahost)
-- associate socket with data host/port -- associate socket with data host/port
try(con:setpeername(datahost, dataport)) try(con:setpeername(datahost, dataport))
-- default sink -- default sink
local sink = gett.sink or ltn12.sink.null() local sink = gett.sink or ltn12.sink.null()
-- process all data packets -- process all data packets
while 1 do while 1 do
-- decode packet -- decode packet
code = get_OP(dgram) code = get_OP(dgram)
try(code ~= OP_ERROR, get_ERROR(dgram)) try(code ~= OP_ERROR, get_ERROR(dgram))
try(code == OP_DATA, "unhandled opcode " .. code) try(code == OP_DATA, "unhandled opcode " .. code)
-- get data packet parts -- get data packet parts
local block, data = split_DATA(dgram) local block, data = split_DATA(dgram)
-- if not repeated, write -- if not repeated, write
if block == last+1 then if block == last+1 then
try(sink(data)) try(sink(data))
last = block last = block
end end
-- last packet brings less than 512 bytes of data -- last packet brings less than 512 bytes of data
if string.len(data) < 512 then if string.len(data) < 512 then
try(con:send(ACK(block))) try(con:send(ACK(block)))
try(con:close()) try(con:close())
try(sink(nil)) try(sink(nil))
@ -118,13 +118,13 @@ local function tget(gett)
end end
-- get the next packet -- get the next packet
retries = 0 retries = 0
repeat repeat
sent = try(con:send(ACK(last))) sent = try(con:send(ACK(last)))
dgram, err = con:receive() dgram, err = con:receive()
retries = retries + 1 retries = retries + 1
until dgram or err ~= "timeout" or retries > 5 until dgram or err ~= "timeout" or retries > 5
try(dgram, err) try(dgram, err)
end end
end end
local default = { local default = {

View File

@ -1,21 +1,13 @@
PLAT= none PLAT?= macosx
PLATS= macosx linux PLATS= macosx linux win32
#------ #------
# Hopefully no need to change anything below this line # Hopefully no need to change anything below this line
# #
all: $(PLAT) all: $(PLAT)
none: $(PLATS) none install local clean:
@echo "Please run" @cd src; $(MAKE) $@
@echo " make PLATFORM"
@echo "where PLATFORM is one of these:"
@echo " $(PLATS)"
$(PLATS) install local clean:
cd src; $(MAKE) $@
dummy:
test: dummy test: dummy
lua test/hello.lua lua test/hello.lua

View File

@ -42,7 +42,7 @@ int buffer_open(lua_State *L) {
* Initializes C structure * Initializes C structure
\*-------------------------------------------------------------------------*/ \*-------------------------------------------------------------------------*/
void buffer_init(p_buffer buf, p_io io, p_timeout tm) { void buffer_init(p_buffer buf, p_io io, p_timeout tm) {
buf->first = buf->last = 0; buf->first = buf->last = 0;
buf->io = io; buf->io = io;
buf->tm = tm; buf->tm = tm;
buf->received = buf->sent = 0; buf->received = buf->sent = 0;
@ -122,9 +122,15 @@ int buffer_meth_receive(lua_State *L, p_buffer buf) {
if (p[0] == '*' && p[1] == 'l') err = recvline(buf, &b); if (p[0] == '*' && p[1] == 'l') err = recvline(buf, &b);
else if (p[0] == '*' && p[1] == 'a') err = recvall(buf, &b); else if (p[0] == '*' && p[1] == 'a') err = recvall(buf, &b);
else luaL_argcheck(L, 0, 2, "invalid receive pattern"); else luaL_argcheck(L, 0, 2, "invalid receive pattern");
/* get a fixed number of bytes (minus what was already partially /* get a fixed number of bytes (minus what was already partially
* received) */ * received) */
} else err = recvraw(buf, (size_t) lua_tonumber(L, 2)-size, &b); } else {
double n = lua_tonumber(L, 2);
size_t wanted = (size_t) n;
luaL_argcheck(L, n >= 0, 2, "invalid receive pattern");
if (size == 0 || wanted > size)
err = recvraw(buf, wanted-size, &b);
}
/* check if there was an error */ /* check if there was an error */
if (err != IO_DONE) { if (err != IO_DONE) {
/* we can't push anyting in the stack before pushing the /* we can't push anyting in the stack before pushing the

View File

@ -29,8 +29,8 @@ typedef struct t_buffer_ {
size_t sent, received; /* bytes sent, and bytes received */ size_t sent, received; /* bytes sent, and bytes received */
p_io io; /* IO driver used for this buffer */ p_io io; /* IO driver used for this buffer */
p_timeout tm; /* timeout management for this buffer */ p_timeout tm; /* timeout management for this buffer */
size_t first, last; /* index of first and last bytes of stored data */ size_t first, last; /* index of first and last bytes of stored data */
char data[BUF_SIZE]; /* storage space for buffer data */ char data[BUF_SIZE]; /* storage space for buffer data */
} t_buffer; } t_buffer;
typedef t_buffer *p_buffer; typedef t_buffer *p_buffer;

View File

@ -212,8 +212,8 @@ local function tput(putt)
end end
local default = { local default = {
path = "/", path = "/",
scheme = "ftp" scheme = "ftp"
} }
local function parse(u) local function parse(u)

View File

@ -213,7 +213,7 @@ const char *inet_tryconnect(p_socket ps, const char *address,
memset(&remote, 0, sizeof(remote)); memset(&remote, 0, sizeof(remote));
remote.sin_family = AF_INET; remote.sin_family = AF_INET;
remote.sin_port = htons(port); remote.sin_port = htons(port);
if (strcmp(address, "*")) { if (strcmp(address, "*")) {
if (!inet_aton(address, &remote.sin_addr)) { if (!inet_aton(address, &remote.sin_addr)) {
struct hostent *hp = NULL; struct hostent *hp = NULL;
struct in_addr **addr; struct in_addr **addr;
@ -248,7 +248,6 @@ const char *inet_trybind(p_socket ps, const char *address, unsigned short port)
memcpy(&local.sin_addr, *addr, sizeof(struct in_addr)); memcpy(&local.sin_addr, *addr, sizeof(struct in_addr));
} }
err = socket_bind(ps, (SA *) &local, sizeof(local)); err = socket_bind(ps, (SA *) &local, sizeof(local));
if (err != IO_DONE) socket_destroy(ps);
return socket_strerror(err); return socket_strerror(err);
} }

View File

@ -1,8 +1,13 @@
PLAT = none PLAT?=macosx
INSTALL_DATA=cp INSTALL_DATA=cp
INSTALL_EXEC=cp INSTALL_EXEC=cp
INSTALL_TOP= /opt/local INSTALL_TOP=/opt/local
LUAINC= $(LUAINC_$(PLAT))
LUAINC_macosx=/opt/local/include
LUAINC_linux=/usr/include/lua5.1
LUAINC_win32="../../lua-5.1.3/src"
LUALIB_win32="../../lua-5.1.3"
#------ #------
# Install directories # Install directories
@ -15,40 +20,76 @@ INSTALL_MIME_SHARE=$(INSTALL_TOP_SHARE)/mime
INSTALL_MIME_LIB=$(INSTALL_TOP_LIB)/mime INSTALL_MIME_LIB=$(INSTALL_TOP_LIB)/mime
#------ #------
# Output file names # Supported platforms
# #
EXT=so PLATS= macosx linux win32
SOCKET_V=2.0.3
MIME_V=1.0.3
SOCKET_SO=socket.$(EXT).$(SOCKET_V)
MIME_SO=mime.$(EXT).$(MIME_V)
UNIX_SO=unix.$(EXT)
#------ #------
# Compiler and linker settings # Compiler and linker settings
# for Mac OS X # for Mac OS X
LUAINC_macosx= -I/opt/local/include SO_macosx=so
O_macosx=o
CC_macosx=gcc CC_macosx=gcc
DEF_macosx= -DLUASOCKET_DEBUG -DUNIX_HAS_SUN_LEN \ DEF_macosx= -DLUASOCKET_DEBUG -DUNIX_HAS_SUN_LEN \
-DLUASOCKET_API='__attribute__((visibility("default")))' \ -DLUASOCKET_API='__attribute__((visibility("default")))' \
-DMIME_API='__attribute__((visibility("default")))' -DMIME_API='__attribute__((visibility("default")))'
CFLAGS_macosx= $(LUAINC) $(COMPAT) $(DEF) -pedantic -Wall -O2 -fno-common \ CFLAGS_macosx= -I$(LUAINC) $(DEF) -pedantic -Wall -O2 -fno-common \
-fvisibility=hidden -fvisibility=hidden
LDFLAGS_macosx= -bundle -undefined dynamic_lookup LDFLAGS_macosx= -bundle -undefined dynamic_lookup -o
LD_macosx= export MACOSX_DEPLOYMENT_TARGET="10.3"; gcc LD_macosx= export MACOSX_DEPLOYMENT_TARGET="10.3"; gcc
SOCKET_macosx=usocket.o
#------ #------
# Compiler and linker settings # Compiler and linker settings
# for Linux # for Linux
LUAINC_linux= -I/usr/local/include/lua5.1 SO_linux=so
O_linux=o
CC_linux=gcc CC_linux=gcc
DEF_linux=-DLUASOCKET_DEBUG \ DEF_linux=-DLUASOCKET_DEBUG \
-DLUASOCKET_API='__attribute__((visibility("default")))' \ -DLUASOCKET_API='__attribute__((visibility("default")))' \
-DMIME_API='__attribute__((visibility("default")))' -DMIME_API='__attribute__((visibility("default")))'
CFLAGS_linux= $(LUAINC) $(DEF) -pedantic -Wall -O2 -fpic \ CFLAGS_linux= -I$(LUAINC) $(DEF) -pedantic -Wall -O2 -fpic \
-fvisibility=hidden -fvisibility=hidden
LDFLAGS_linux=-O -shared -fpic LDFLAGS_linux=-O -shared -fpic -o
LD_linux= gcc LD_linux=gcc
SOCKET_linux=usocket.o
#------
# Compiler and linker settings
# for Win32
SO_win32=dll
O_win32=obj
CC_win32=cl
DEF_win32= /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_USRDLL" \
/D "LUASOCKET_API=__declspec(dllexport)" /D "LUASOCKET_DEBUG" \
/D "_CRT_SECURE_NO_WARNINGS" /D "_WINDLL"
CFLAGS_win32=/I$(LUAINC) $(DEF) /O2 /Ot /MD /W3 /nologo
LDFLAGS_win32= /nologo /link /NOLOGO /DLL /INCREMENTAL:NO \
/LIBPATH:$(LUALIB) \
/MANIFEST \
/MANIFESTFILE:"intermediate.manifest" \
/MANIFESTUAC:"level='asInvoker' uiAccess='false'" \
/SUBSYSTEM:WINDOWS /OPT:REF /OPT:ICF /DYNAMICBASE:NO \
/MACHINE:X86 ws2_32.lib lua5.1.lib /OUT:
LD_win32=cl
SOCKET_win32=wsocket.obj
.SUFFIXES: .obj
.c.obj:
$(CC) $(CFLAGS) /Fo"$@" /c $<
#------
# Output file names
#
SO=$(SO_$(PLAT))
O=$(O_$(PLAT))
SOCKET_V=2.0.3
MIME_V=1.0.3
SOCKET_SO=socket.$(SO).$(SOCKET_V)
MIME_SO=mime.$(SO).$(MIME_V)
UNIX_SO=unix.$(SO)
SOCKET=$(SOCKET_$(PLAT))
#------ #------
# Settings selected for platform # Settings selected for platform
@ -58,46 +99,48 @@ DEF=$(DEF_$(PLAT))
CFLAGS=$(CFLAGS_$(PLAT)) CFLAGS=$(CFLAGS_$(PLAT))
LDFLAGS=$(LDFLAGS_$(PLAT)) LDFLAGS=$(LDFLAGS_$(PLAT))
LD=$(LD_$(PLAT)) LD=$(LD_$(PLAT))
LUAINC= $(LUAINC_$(PLAT))
LUALIB= $(LUALIB_$(PLAT))
#------ #------
# Modules belonging to socket-core # Modules belonging to socket-core
# #
SOCKET_OBJS= \ SOCKET_OBJS= \
luasocket.o \ luasocket.$(O) \
timeout.o \ timeout.$(O) \
buffer.o \ buffer.$(O) \
io.o \ io.$(O) \
auxiliar.o \ auxiliar.$(O) \
options.o \ options.$(O) \
inet.o \ inet.$(O) \
usocket.o \ $(SOCKET) \
except.o \ except.$(O) \
select.o \ select.$(O) \
tcp.o \ tcp.$(O) \
udp.o udp.$(O)
#------ #------
# Modules belonging mime-core # Modules belonging mime-core
# #
MIME_OBJS= \ MIME_OBJS= \
mime.o mime.$(O)
#------ #------
# Modules belonging unix (local domain sockets) # Modules belonging unix (local domain sockets)
# #
UNIX_OBJS:=\ UNIX_OBJS=\
buffer.o \ buffer.$(O) \
auxiliar.o \ auxiliar.$(O) \
options.o \ options.$(O) \
timeout.o \ timeout.$(O) \
io.o \ io.$(O) \
usocket.o \ usocket.$(O) \
unix.o unix.$(O)
#------ #------
# Files to install # Files to install
# #
TO_SOCKET_SHARE:= \ TO_SOCKET_SHARE= \
http.lua \ http.lua \
url.lua \ url.lua \
tp.lua \ tp.lua \
@ -105,33 +148,41 @@ TO_SOCKET_SHARE:= \
headers.lua \ headers.lua \
smtp.lua smtp.lua
TO_TOP_SHARE:= \ TO_TOP_SHARE= \
ltn12.lua \ ltn12.lua \
socket.lua \ socket.lua \
mime.lua mime.lua
#------
# Targets
#
default: $(PLAT) default: $(PLAT)
macosx: macosx:
$(MAKE) all PLAT=macosx $(MAKE) all PLAT=macosx
win32:
$(MAKE) all PLAT=win32
linux: linux:
$(MAKE) all PLAT=linux $(MAKE) all PLAT=linux
none: none:
@echo "Please choose a platform:" @echo "Please run"
@echo " make PLATFORM"
@echo "where PLATFORM is one of these:"
@echo " $(PLATS)" @echo " $(PLATS)"
all: $(SOCKET_SO) $(MIME_SO) all: $(SOCKET_SO) $(MIME_SO)
$(SOCKET_SO): $(SOCKET_OBJS) $(SOCKET_SO): $(SOCKET_OBJS)
$(LD) $(LDFLAGS) -o $@ $(SOCKET_OBJS) $(LD) $(SOCKET_OBJS) $(LDFLAGS)$@
$(MIME_SO): $(MIME_OBJS) $(MIME_SO): $(MIME_OBJS)
$(LD) $(LDFLAGS) -o $@ $(MIME_OBJS) $(LD) $(MIME_OBJS) $(LDFLAGS)$@
$(UNIX_SO): $(UNIX_OBJS) $(UNIX_SO): $(UNIX_OBJS)
$(LD) $(LDFLAGS) -o $@ $(UNIX_OBJS) $(LD) $(UNIX_OBJS) $(LDFLAGS)$@
install: install:
mkdir -p $(INSTALL_TOP_SHARE) mkdir -p $(INSTALL_TOP_SHARE)
@ -139,9 +190,9 @@ install:
mkdir -p $(INSTALL_SOCKET_SHARE) mkdir -p $(INSTALL_SOCKET_SHARE)
$(INSTALL_DATA) $(TO_SOCKET_SHARE) $(INSTALL_SOCKET_SHARE) $(INSTALL_DATA) $(TO_SOCKET_SHARE) $(INSTALL_SOCKET_SHARE)
mkdir -p $(INSTALL_SOCKET_LIB) mkdir -p $(INSTALL_SOCKET_LIB)
$(INSTALL_EXEC) $(SOCKET_SO) $(INSTALL_SOCKET_LIB)/core.$(EXT) $(INSTALL_EXEC) $(SOCKET_SO) $(INSTALL_SOCKET_LIB)/core.$(SO)
mkdir -p $(INSTALL_MIME_LIB) mkdir -p $(INSTALL_MIME_LIB)
$(INSTALL_EXEC) $(MIME_SO) $(INSTALL_MIME_LIB)/core.$(EXT) $(INSTALL_EXEC) $(MIME_SO) $(INSTALL_MIME_LIB)/core.$(SO)
local: local:
$(MAKE) install INSTALL_TOP_LIB=.. INSTALL_TOP_SHARE=.. $(MAKE) install INSTALL_TOP_LIB=.. INSTALL_TOP_SHARE=..
@ -155,24 +206,24 @@ clean:
#------ #------
# List of dependencies # List of dependencies
# #
auxiliar.o: auxiliar.c auxiliar.h auxiliar.$(O): auxiliar.c auxiliar.h
buffer.o: buffer.c buffer.h io.h timeout.h buffer.$(O): buffer.c buffer.h io.h timeout.h
except.o: except.c except.h except.$(O): except.c except.h
inet.o: inet.c inet.h socket.h io.h timeout.h usocket.h inet.$(O): inet.c inet.h socket.h io.h timeout.h usocket.h
io.o: io.c io.h timeout.h io.$(O): io.c io.h timeout.h
luasocket.o: luasocket.c luasocket.h auxiliar.h except.h \ luasocket.$(O): luasocket.c luasocket.h auxiliar.h except.h \
timeout.h buffer.h io.h inet.h socket.h usocket.h tcp.h \ timeout.h buffer.h io.h inet.h socket.h usocket.h tcp.h \
udp.h select.h udp.h select.h
mime.o: mime.c mime.h mime.$(O): mime.c mime.h
options.o: options.c auxiliar.h options.h socket.h io.h \ options.$(O): options.c auxiliar.h options.h socket.h io.h \
timeout.h usocket.h inet.h timeout.h usocket.h inet.h
select.o: select.c socket.h io.h timeout.h usocket.h select.h select.$(O): select.c socket.h io.h timeout.h usocket.h select.h
tcp.o: tcp.c auxiliar.h socket.h io.h timeout.h usocket.h \ tcp.$(O): tcp.c auxiliar.h socket.h io.h timeout.h usocket.h \
inet.h options.h tcp.h buffer.h inet.h options.h tcp.h buffer.h
timeout.o: timeout.c auxiliar.h timeout.h timeout.$(O): timeout.c auxiliar.h timeout.h
udp.o: udp.c auxiliar.h socket.h io.h timeout.h usocket.h \ udp.$(O): udp.c auxiliar.h socket.h io.h timeout.h usocket.h \
inet.h options.h udp.h inet.h options.h udp.h
unix.o: unix.c auxiliar.h socket.h io.h timeout.h usocket.h \ unix.$(O): unix.c auxiliar.h socket.h io.h timeout.h usocket.h \
options.h unix.h buffer.h options.h unix.h buffer.h
usocket.o: usocket.c socket.h io.h timeout.h usocket.h usocket.$(O): usocket.c socket.h io.h timeout.h usocket.h
wsocket.o: wsocket.c socket.h io.h timeout.h usocket.h wsocket.$(O): wsocket.c socket.h io.h timeout.h usocket.h

View File

@ -5,10 +5,10 @@ mbox = Public
function Public.split_message(message_s) function Public.split_message(message_s)
local message = {} local message = {}
message_s = string.gsub(message_s, "\r\n", "\n") message_s = string.gsub(message_s, "\r\n", "\n")
string.gsub(message_s, "^(.-\n)\n", function (h) message.headers = h end) string.gsub(message_s, "^(.-\n)\n", function (h) message.headers = h end)
string.gsub(message_s, "^.-\n\n(.*)", function (b) message.body = b end) string.gsub(message_s, "^.-\n\n(.*)", function (b) message.body = b end)
if not message.body then if not message.body then
string.gsub(message_s, "^\n(.*)", function (b) message.body = b end) string.gsub(message_s, "^\n(.*)", function (b) message.body = b end)
end end
if not message.headers and not message.body then if not message.headers and not message.body then
message.headers = message_s message.headers = message_s
@ -54,30 +54,30 @@ function Public.parse_from(from)
name = name or "" name = name or ""
address = address or "" address = address or ""
if name == "" then name = address end if name == "" then name = address end
name = string.gsub(name, '"', "") name = string.gsub(name, '"', "")
return name, address return name, address
end end
function Public.split_mbox(mbox_s) function Public.split_mbox(mbox_s)
mbox = {} mbox = {}
mbox_s = string.gsub(mbox_s, "\r\n", "\n") .."\n\nFrom \n" mbox_s = string.gsub(mbox_s, "\r\n", "\n") .."\n\nFrom \n"
local nj, i, j = 1, 1, 1 local nj, i, j = 1, 1, 1
while 1 do while 1 do
i, nj = string.find(mbox_s, "\n\nFrom .-\n", j) i, nj = string.find(mbox_s, "\n\nFrom .-\n", j)
if not i then break end if not i then break end
local message = string.sub(mbox_s, j, i-1) local message = string.sub(mbox_s, j, i-1)
table.insert(mbox, message) table.insert(mbox, message)
j = nj+1 j = nj+1
end end
return mbox return mbox
end end
function Public.parse(mbox_s) function Public.parse(mbox_s)
local mbox = Public.split_mbox(mbox_s) local mbox = Public.split_mbox(mbox_s)
for i = 1, table.getn(mbox) do for i = 1, table.getn(mbox) do
mbox[i] = Public.parse_message(mbox[i]) mbox[i] = Public.parse_message(mbox[i])
end end
return mbox return mbox
end end
function Public.parse_message(message_s) function Public.parse_message(message_s)

View File

@ -106,7 +106,7 @@ end
-- closes the underlying c -- closes the underlying c
function metat.__index:close() function metat.__index:close()
self.c:close() self.c:close()
return 1 return 1
end end
-- connect with server and return c object -- connect with server and return c object

View File

@ -40,25 +40,25 @@ end
-- escaped representation of string binary -- escaped representation of string binary
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
local function make_set(t) local function make_set(t)
local s = {} local s = {}
for i,v in base.ipairs(t) do for i,v in base.ipairs(t) do
s[t[i]] = 1 s[t[i]] = 1
end end
return s return s
end end
-- these are allowed withing a path segment, along with alphanum -- these are allowed withing a path segment, along with alphanum
-- other characters must be escaped -- other characters must be escaped
local segment_set = make_set { local segment_set = make_set {
"-", "_", ".", "!", "~", "*", "'", "(", "-", "_", ".", "!", "~", "*", "'", "(",
")", ":", "@", "&", "=", "+", "$", ",", ")", ":", "@", "&", "=", "+", "$", ",",
} }
local function protect_segment(s) local function protect_segment(s)
return string.gsub(s, "([^A-Za-z0-9_])", function (c) return string.gsub(s, "([^A-Za-z0-9_])", function (c)
if segment_set[c] then return c if segment_set[c] then return c
else return string.format("%%%02x", string.byte(c)) end else return string.format("%%%02x", string.byte(c)) end
end) end)
end end
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
@ -182,20 +182,26 @@ function build(parsed)
local url = build_path(ppath) local url = build_path(ppath)
if parsed.params then url = url .. ";" .. parsed.params end if parsed.params then url = url .. ";" .. parsed.params end
if parsed.query then url = url .. "?" .. parsed.query end if parsed.query then url = url .. "?" .. parsed.query end
local authority = parsed.authority local authority = parsed.authority
if parsed.host then if parsed.host then
authority = parsed.host authority = parsed.host
if parsed.port then authority = authority .. ":" .. parsed.port end if parsed.port then authority = authority .. ":" .. parsed.port end
local userinfo = parsed.userinfo local userinfo = parsed.userinfo
if parsed.user then if parsed.user then
userinfo = parsed.user userinfo = parsed.user
if parsed.password then if parsed.password then
userinfo = userinfo .. ":" .. parsed.password userinfo = userinfo .. ":" .. parsed.password
end end
end end
if userinfo then authority = userinfo .. "@" .. authority end if userinfo then authority = userinfo .. "@" .. authority end
end end
if authority then url = "//" .. authority .. url end if authority then
if string.sub(url, 1, 1) == "/" then
url = "//" .. authority .. url
else
url = "//" .. authority .. "/" .. url
end
end
if parsed.scheme then url = parsed.scheme .. ":" .. url end if parsed.scheme then url = parsed.scheme .. ":" .. url end
if parsed.fragment then url = url .. "#" .. parsed.fragment end if parsed.fragment then url = url .. "#" .. parsed.fragment end
-- url = string.gsub(url, "%s", "") -- url = string.gsub(url, "%s", "")
@ -211,8 +217,8 @@ end
-- corresponding absolute url -- corresponding absolute url
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
function absolute(base_url, relative_url) function absolute(base_url, relative_url)
local base_parsed = base_url
if base.type(base_url) == "table" then if base.type(base_url) == "table" then
base_parsed = base_url
base_url = build(base_parsed) base_url = build(base_parsed)
else else
base_parsed = parse(base_url) base_parsed = parse(base_url)
@ -250,16 +256,16 @@ end
-- segment: a table with one entry per segment -- segment: a table with one entry per segment
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
function parse_path(path) function parse_path(path)
local parsed = {} local parsed = {}
path = path or "" path = path or ""
--path = string.gsub(path, "%s", "") --path = string.gsub(path, "%s", "")
string.gsub(path, "([^/]+)", function (s) table.insert(parsed, s) end) string.gsub(path, "([^/]+)", function (s) table.insert(parsed, s) end)
for i = 1, table.getn(parsed) do for i = 1, table.getn(parsed) do
parsed[i] = unescape(parsed[i]) parsed[i] = unescape(parsed[i])
end end
if string.sub(path, 1, 1) == "/" then parsed.is_absolute = 1 end if string.sub(path, 1, 1) == "/" then parsed.is_absolute = 1 end
if string.sub(path, -1, -1) == "/" then parsed.is_directory = 1 end if string.sub(path, -1, -1) == "/" then parsed.is_directory = 1 end
return parsed return parsed
end end
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
@ -271,27 +277,27 @@ end
-- path: corresponding path stringing -- path: corresponding path stringing
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
function build_path(parsed, unsafe) function build_path(parsed, unsafe)
local path = "" local path = ""
local n = table.getn(parsed) local n = table.getn(parsed)
if unsafe then if unsafe then
for i = 1, n-1 do for i = 1, n-1 do
path = path .. parsed[i] path = path .. parsed[i]
path = path .. "/" path = path .. "/"
end end
if n > 0 then if n > 0 then
path = path .. parsed[n] path = path .. parsed[n]
if parsed.is_directory then path = path .. "/" end if parsed.is_directory then path = path .. "/" end
end end
else else
for i = 1, n-1 do for i = 1, n-1 do
path = path .. protect_segment(parsed[i]) path = path .. protect_segment(parsed[i])
path = path .. "/" path = path .. "/"
end end
if n > 0 then if n > 0 then
path = path .. protect_segment(parsed[n]) path = path .. protect_segment(parsed[n])
if parsed.is_directory then path = path .. "/" end if parsed.is_directory then path = path .. "/" end
end end
end end
if parsed.is_absolute then path = "/" .. path end if parsed.is_absolute then path = "/" .. path end
return path return path
end end

View File

@ -30,9 +30,9 @@ int socket_waitfd(p_socket ps, int sw, p_timeout tm) {
pfd.revents = 0; pfd.revents = 0;
if (timeout_iszero(tm)) return IO_TIMEOUT; /* optimize timeout == 0 case */ if (timeout_iszero(tm)) return IO_TIMEOUT; /* optimize timeout == 0 case */
do { do {
int t = (int)(timeout_getretry(tm)*1e3); int t = (int)(timeout_getretry(tm)*1e3);
ret = poll(&pfd, 1, t >= 0? t: -1); ret = poll(&pfd, 1, t >= 0? t: -1);
} while (ret == -1 && errno == EINTR); } while (ret == -1 && errno == EINTR);
if (ret == -1) return errno; if (ret == -1) return errno;
if (ret == 0) return IO_TIMEOUT; if (ret == 0) return IO_TIMEOUT;
if (sw == WAITFD_C && (pfd.revents & (POLLIN|POLLERR))) return IO_CLOSED; if (sw == WAITFD_C && (pfd.revents & (POLLIN|POLLERR))) return IO_CLOSED;

View File

@ -54,7 +54,7 @@ int socket_waitfd(p_socket ps, int sw, p_timeout tm) {
if (timeout_iszero(tm)) return IO_TIMEOUT; /* optimize timeout == 0 case */ if (timeout_iszero(tm)) return IO_TIMEOUT; /* optimize timeout == 0 case */
if (sw & WAITFD_R) { if (sw & WAITFD_R) {
FD_ZERO(&rfds); FD_ZERO(&rfds);
FD_SET(*ps, &rfds); FD_SET(*ps, &rfds);
rp = &rfds; rp = &rfds;
} }
if (sw & WAITFD_W) { FD_ZERO(&wfds); FD_SET(*ps, &wfds); wp = &wfds; } if (sw & WAITFD_W) { FD_ZERO(&wfds); FD_SET(*ps, &wfds); wp = &wfds; }
@ -207,7 +207,7 @@ int socket_send(p_socket ps, const char *data, size_t count,
/* loop until we send something or we give up on error */ /* loop until we send something or we give up on error */
for ( ;; ) { for ( ;; ) {
/* try to send something */ /* try to send something */
int put = send(*ps, data, (int) count, 0); int put = send(*ps, data, (int) count, 0);
/* if we sent something, we are done */ /* if we sent something, we are done */
if (put > 0) { if (put > 0) {
*sent = put; *sent = put;
@ -346,8 +346,8 @@ const char *socket_strerror(int err) {
} }
const char *socket_ioerror(p_socket ps, int err) { const char *socket_ioerror(p_socket ps, int err) {
(void) ps; (void) ps;
return socket_strerror(err); return socket_strerror(err);
} }
static const char *wstrerror(int err) { static const char *wstrerror(int err) {

View File

@ -82,10 +82,10 @@ print("ok")
io.write("testing parameter overriding: ") io.write("testing parameter overriding: ")
local back = {} local back = {}
ret, err = ftp.get{ ret, err = ftp.get{
url = "//stupid:mistake@" .. host .. "/index.html", url = "//stupid:mistake@" .. host .. "/index.html",
user = "luasocket", user = "luasocket",
password = "pedrovian", password = "pedrovian",
type = "i", type = "i",
sink = ltn12.sink.table(back) sink = ltn12.sink.table(back)
} }
assert(ret and not err and table.concat(back) == index, err) assert(ret and not err and table.concat(back) == index, err)

View File

@ -34,28 +34,28 @@ index_file = "index.html"
index = readfile(index_file) index = readfile(index_file)
local check_result = function(response, expect, ignore) local check_result = function(response, expect, ignore)
for i,v in pairs(response) do for i,v in pairs(response) do
if not ignore[i] then if not ignore[i] then
if v ~= expect[i] then if v ~= expect[i] then
local f = io.open("err", "w") local f = io.open("err", "w")
f:write(tostring(v), "\n\n versus\n\n", tostring(expect[i])) f:write(tostring(v), "\n\n versus\n\n", tostring(expect[i]))
f:close() f:close()
fail(i .. " differs!") fail(i .. " differs!")
end end
end end
end end
for i,v in pairs(expect) do for i,v in pairs(expect) do
if not ignore[i] then if not ignore[i] then
if v ~= response[i] then if v ~= response[i] then
local f = io.open("err", "w") local f = io.open("err", "w")
f:write(tostring(response[i]), "\n\n versus\n\n", tostring(v)) f:write(tostring(response[i]), "\n\n versus\n\n", tostring(v))
v = string.sub(type(v) == "string" and v or "", 1, 70) v = string.sub(type(v) == "string" and v or "", 1, 70)
f:close() f:close()
fail(i .. " differs!") fail(i .. " differs!")
end end
end end
end end
print("ok") print("ok")
end end
local check_request = function(request, expect, ignore) local check_request = function(request, expect, ignore)
@ -63,7 +63,7 @@ local check_request = function(request, expect, ignore)
if not request.sink then request.sink, t = ltn12.sink.table() end if not request.sink then request.sink, t = ltn12.sink.table() end
request.source = request.source or request.source = request.source or
(request.body and ltn12.source.string(request.body)) (request.body and ltn12.source.string(request.body))
local response = {} local response = {}
response.code, response.headers, response.status = response.code, response.headers, response.status =
socket.skip(1, http.request(request)) socket.skip(1, http.request(request))
if t and table.getn(t) > 0 then response.body = table.concat(t) end if t and table.getn(t) > 0 then response.body = table.concat(t) end
@ -90,15 +90,15 @@ else fail("failed!") end
------------------------------------------------------------------------ ------------------------------------------------------------------------
io.write("testing document retrieval: ") io.write("testing document retrieval: ")
request = { request = {
url = "http://" .. host .. prefix .. "/index.html" url = "http://" .. host .. prefix .. "/index.html"
} }
expect = { expect = {
body = index, body = index,
code = 200 code = 200
} }
ignore = { ignore = {
status = 1, status = 1,
headers = 1 headers = 1
} }
check_request(request, expect, ignore) check_request(request, expect, ignore)
@ -111,9 +111,9 @@ expect = {
code = 302 code = 302
} }
ignore = { ignore = {
status = 1, status = 1,
headers = 1, headers = 1,
body = 1 body = 1
} }
check_request(request, expect, ignore) check_request(request, expect, ignore)
@ -144,19 +144,19 @@ check_request(request, expect, ignore)
io.write("testing post method: ") io.write("testing post method: ")
-- wanted to test chunked post, but apache doesn't support it... -- wanted to test chunked post, but apache doesn't support it...
request = { request = {
url = "http://" .. host .. cgiprefix .. "/cat", url = "http://" .. host .. cgiprefix .. "/cat",
method = "POST", method = "POST",
body = index, body = index,
-- remove content-length header to send chunked body -- remove content-length header to send chunked body
headers = { ["content-length"] = string.len(index) } headers = { ["content-length"] = string.len(index) }
} }
expect = { expect = {
body = index, body = index,
code = 200 code = 200
} }
ignore = { ignore = {
status = 1, status = 1,
headers = 1 headers = 1
} }
check_request(request, expect, ignore) check_request(request, expect, ignore)
@ -164,19 +164,19 @@ check_request(request, expect, ignore)
--[[ --[[
io.write("testing proxy with post method: ") io.write("testing proxy with post method: ")
request = { request = {
url = "http://" .. host .. cgiprefix .. "/cat", url = "http://" .. host .. cgiprefix .. "/cat",
method = "POST", method = "POST",
body = index, body = index,
headers = { ["content-length"] = string.len(index) }, headers = { ["content-length"] = string.len(index) },
proxy= proxy proxy= proxy
} }
expect = { expect = {
body = index, body = index,
code = 200 code = 200
} }
ignore = { ignore = {
status = 1, status = 1,
headers = 1 headers = 1
} }
check_request(request, expect, ignore) check_request(request, expect, ignore)
]] ]]
@ -190,18 +190,18 @@ print("ok")
------------------------------------------------------------------------ ------------------------------------------------------------------------
io.write("testing ltn12.(sink|source).file: ") io.write("testing ltn12.(sink|source).file: ")
request = { request = {
url = "http://" .. host .. cgiprefix .. "/cat", url = "http://" .. host .. cgiprefix .. "/cat",
method = "POST", method = "POST",
source = ltn12.source.file(io.open(index_file, "rb")), source = ltn12.source.file(io.open(index_file, "rb")),
sink = ltn12.sink.file(io.open(index_file .. "-back", "wb")), sink = ltn12.sink.file(io.open(index_file .. "-back", "wb")),
headers = { ["content-length"] = string.len(index) } headers = { ["content-length"] = string.len(index) }
} }
expect = { expect = {
code = 200 code = 200
} }
ignore = { ignore = {
status = 1, status = 1,
headers = 1 headers = 1
} }
check_request(request, expect, ignore) check_request(request, expect, ignore)
back = readfile(index_file .. "-back") back = readfile(index_file .. "-back")
@ -231,19 +231,19 @@ local sink = ltn12.sink.chain(
) )
request = { request = {
url = "http://" .. host .. cgiprefix .. "/cat", url = "http://" .. host .. cgiprefix .. "/cat",
method = "POST", method = "POST",
source = source, source = source,
sink = sink, sink = sink,
headers = { ["content-length"] = b64length(string.len(index)) } headers = { ["content-length"] = b64length(string.len(index)) }
} }
expect = { expect = {
code = 200 code = 200
} }
ignore = { ignore = {
body_cb = 1, body_cb = 1,
status = 1, status = 1,
headers = 1 headers = 1
} }
check_request(request, expect, ignore) check_request(request, expect, ignore)
back = readfile(index_file .. "-back") back = readfile(index_file .. "-back")
@ -253,15 +253,15 @@ os.remove(index_file .. "-back")
------------------------------------------------------------------------ ------------------------------------------------------------------------
io.write("testing http redirection: ") io.write("testing http redirection: ")
request = { request = {
url = "http://" .. host .. prefix url = "http://" .. host .. prefix
} }
expect = { expect = {
body = index, body = index,
code = 200 code = 200
} }
ignore = { ignore = {
status = 1, status = 1,
headers = 1 headers = 1
} }
check_request(request, expect, ignore) check_request(request, expect, ignore)
@ -269,16 +269,16 @@ check_request(request, expect, ignore)
--[[ --[[
io.write("testing proxy with redirection: ") io.write("testing proxy with redirection: ")
request = { request = {
url = "http://" .. host .. prefix, url = "http://" .. host .. prefix,
proxy = proxy proxy = proxy
} }
expect = { expect = {
body = index, body = index,
code = 200 code = 200
} }
ignore = { ignore = {
status = 1, status = 1,
headers = 1 headers = 1
} }
check_request(request, expect, ignore) check_request(request, expect, ignore)
]] ]]
@ -293,104 +293,104 @@ expect = {
} }
ignore = { ignore = {
body = 1, body = 1,
status = 1, status = 1,
headers = 1 headers = 1
} }
check_request(request, expect, ignore) check_request(request, expect, ignore)
------------------------------------------------------------------------ ------------------------------------------------------------------------
io.write("testing http redirection failure: ") io.write("testing http redirection failure: ")
request = { request = {
url = "http://" .. host .. prefix, url = "http://" .. host .. prefix,
redirect = false redirect = false
} }
expect = { expect = {
code = 301 code = 301
} }
ignore = { ignore = {
body = 1, body = 1,
status = 1, status = 1,
headers = 1 headers = 1
} }
check_request(request, expect, ignore) check_request(request, expect, ignore)
------------------------------------------------------------------------ ------------------------------------------------------------------------
io.write("testing document not found: ") io.write("testing document not found: ")
request = { request = {
url = "http://" .. host .. "/wrongdocument.html" url = "http://" .. host .. "/wrongdocument.html"
} }
expect = { expect = {
code = 404 code = 404
} }
ignore = { ignore = {
body = 1, body = 1,
status = 1, status = 1,
headers = 1 headers = 1
} }
check_request(request, expect, ignore) check_request(request, expect, ignore)
------------------------------------------------------------------------ ------------------------------------------------------------------------
io.write("testing auth failure: ") io.write("testing auth failure: ")
request = { request = {
url = "http://" .. host .. prefix .. "/auth/index.html" url = "http://" .. host .. prefix .. "/auth/index.html"
} }
expect = { expect = {
code = 401 code = 401
} }
ignore = { ignore = {
body = 1, body = 1,
status = 1, status = 1,
headers = 1 headers = 1
} }
check_request(request, expect, ignore) check_request(request, expect, ignore)
------------------------------------------------------------------------ ------------------------------------------------------------------------
io.write("testing manual basic auth: ") io.write("testing manual basic auth: ")
request = { request = {
url = "http://" .. host .. prefix .. "/auth/index.html", url = "http://" .. host .. prefix .. "/auth/index.html",
headers = { headers = {
authorization = "Basic " .. (mime.b64("luasocket:password")) authorization = "Basic " .. (mime.b64("luasocket:password"))
} }
} }
expect = { expect = {
code = 200, code = 200,
body = index body = index
} }
ignore = { ignore = {
status = 1, status = 1,
headers = 1 headers = 1
} }
check_request(request, expect, ignore) check_request(request, expect, ignore)
------------------------------------------------------------------------ ------------------------------------------------------------------------
io.write("testing automatic basic auth: ") io.write("testing automatic basic auth: ")
request = { request = {
url = "http://luasocket:password@" .. host .. prefix .. "/auth/index.html" url = "http://luasocket:password@" .. host .. prefix .. "/auth/index.html"
} }
expect = { expect = {
code = 200, code = 200,
body = index body = index
} }
ignore = { ignore = {
status = 1, status = 1,
headers = 1 headers = 1
} }
check_request(request, expect, ignore) check_request(request, expect, ignore)
------------------------------------------------------------------------ ------------------------------------------------------------------------
io.write("testing auth info overriding: ") io.write("testing auth info overriding: ")
request = { request = {
url = "http://really:wrong@" .. host .. prefix .. "/auth/index.html", url = "http://really:wrong@" .. host .. prefix .. "/auth/index.html",
user = "luasocket", user = "luasocket",
password = "password" password = "password"
} }
expect = { expect = {
code = 200, code = 200,
body = index body = index
} }
ignore = { ignore = {
status = 1, status = 1,
headers = 1 headers = 1
} }
check_request(request, expect, ignore) check_request(request, expect, ignore)
@ -400,12 +400,12 @@ request = {
url = "http://" .. host .. cgiprefix .. "/cat-index-html" url = "http://" .. host .. cgiprefix .. "/cat-index-html"
} }
expect = { expect = {
body = index, body = index,
code = 200 code = 200
} }
ignore = { ignore = {
status = 1, status = 1,
headers = 1 headers = 1
} }
check_request(request, expect, ignore) check_request(request, expect, ignore)

View File

@ -19,11 +19,11 @@ local parse = mbox.parse
dofile("testsupport.lua") dofile("testsupport.lua")
local total = function() local total = function()
local t = 0 local t = 0
for i = 1, table.getn(sent) do for i = 1, table.getn(sent) do
t = t + sent[i].count t = t + sent[i].count
end end
return t return t
end end
local similar = function(s1, s2) local similar = function(s1, s2)
@ -39,14 +39,14 @@ local fail = function(s)
end end
local readfile = function(name) local readfile = function(name)
local f = io.open(name, "r") local f = io.open(name, "r")
if not f then if not f then
fail("unable to open file!") fail("unable to open file!")
return nil return nil
end end
local s = f:read("*a") local s = f:read("*a")
f:close() f:close()
return s return s
end end
local empty = function() local empty = function()
@ -62,7 +62,7 @@ end
local get = function() local get = function()
local s = "" local s = ""
for i,v in ipairs(files) do for i,v in ipairs(files) do
s = s .. "\n" .. readfile(v) s = s .. "\n" .. readfile(v)
end end
return s return s
end end
@ -82,40 +82,40 @@ local check_body = function(sent, got)
end end
local check = function(sent, m) local check = function(sent, m)
io.write("checking ", m.headers.title, ": ") io.write("checking ", m.headers.title, ": ")
for i = 1, table.getn(sent) do for i = 1, table.getn(sent) do
local s = sent[i] local s = sent[i]
if s.title == m.headers.title and s.count > 0 then if s.title == m.headers.title and s.count > 0 then
check_headers(s.headers, m.headers) check_headers(s.headers, m.headers)
check_body(s.body, m.body) check_body(s.body, m.body)
s.count = s.count - 1 s.count = s.count - 1
print("ok") print("ok")
return return
end end
end end
fail("not found") fail("not found")
end end
local insert = function(sent, message) local insert = function(sent, message)
if type(message.rcpt) == "table" then if type(message.rcpt) == "table" then
message.count = table.getn(message.rcpt) message.count = table.getn(message.rcpt)
else message.count = 1 end else message.count = 1 end
message.headers = message.headers or {} message.headers = message.headers or {}
message.headers.title = message.title message.headers.title = message.title
table.insert(sent, message) table.insert(sent, message)
end end
local mark = function() local mark = function()
local time = socket.time() local time = socket.time()
return { time = time } return { time = time }
end end
local wait = function(sentinel, n) local wait = function(sentinel, n)
local to local to
io.write("waiting for ", n, " messages: ") io.write("waiting for ", n, " messages: ")
while 1 do while 1 do
local mbox = parse(get()) local mbox = parse(get())
if n == table.getn(mbox) then break end if n == table.getn(mbox) then break end
if socket.time() - sentinel.time > 50 then if socket.time() - sentinel.time > 50 then
to = 1 to = 1
break break
@ -124,8 +124,8 @@ local wait = function(sentinel, n)
io.write(".") io.write(".")
io.stdout:flush() io.stdout:flush()
end end
if to then fail("timeout") if to then fail("timeout")
else print("ok") end else print("ok") end
end end
local stuffed_body = [[ local stuffed_body = [[
@ -144,21 +144,21 @@ a lot of trouble.
insert(sent, { insert(sent, {
from = from, from = from,
rcpt = { rcpt = {
"luasocket@localhost", "luasocket@localhost",
"luasock3@dell-diego.cs.princeton.edu", "luasock3@dell-diego.cs.princeton.edu",
"luasock1@dell-diego.cs.princeton.edu" "luasock1@dell-diego.cs.princeton.edu"
}, },
body = "multiple rcpt body", body = "multiple rcpt body",
title = "multiple rcpt", title = "multiple rcpt",
}) })
insert(sent, { insert(sent, {
from = from, from = from,
rcpt = { rcpt = {
"luasock2@localhost", "luasock2@localhost",
"luasock3", "luasock3",
"luasock1" "luasock1"
}, },
headers = { headers = {
header1 = "header 1", header1 = "header 1",
header2 = "header 2", header2 = "header 2",
@ -210,24 +210,24 @@ insert(sent, {
io.write("testing host not found: ") io.write("testing host not found: ")
local c, e = socket.connect("wrong.host", 25) local c, e = socket.connect("wrong.host", 25)
local ret, err = socket.smtp.mail{ local ret, err = socket.smtp.mail{
from = from, from = from,
rcpt = rcpt, rcpt = rcpt,
server = "wrong.host" server = "wrong.host"
} }
if ret or e ~= err then fail("wrong error message") if ret or e ~= err then fail("wrong error message")
else print("ok") end else print("ok") end
io.write("testing invalid from: ") io.write("testing invalid from: ")
local ret, err = socket.smtp.mail{ local ret, err = socket.smtp.mail{
from = ' " " (( _ * ', from = ' " " (( _ * ',
rcpt = rcpt, rcpt = rcpt,
} }
if ret or not err then fail("wrong error message") if ret or not err then fail("wrong error message")
else print(err) end else print(err) end
io.write("testing no rcpt: ") io.write("testing no rcpt: ")
local ret, err = socket.smtp.mail{ local ret, err = socket.smtp.mail{
from = from, from = from,
} }
if ret or not err then fail("wrong error message") if ret or not err then fail("wrong error message")
else print(err) end else print(err) end
@ -252,7 +252,7 @@ local mbox = parse(get())
print(table.getn(mbox) .. " messages found!") print(table.getn(mbox) .. " messages found!")
for i = 1, table.getn(mbox) do for i = 1, table.getn(mbox) do
check(sent, mbox[i]) check(sent, mbox[i])
end end
print("passed all tests") print("passed all tests")

View File

@ -3,15 +3,20 @@ local socket = require"socket"
host = host or "localhost" host = host or "localhost"
port = port or "8383" port = port or "8383"
function pass(...) function printf(...)
local s = string.format(unpack(arg)) local s = string.format(unpack(arg))
io.stderr:write(s, "\n") io.stderr:write(s)
end
function pass(...)
printf(...)
io.stderr:write("\n")
end end
function fail(...) function fail(...)
local s = string.format(unpack(arg)) io.stderr:write("ERROR: ")
io.stderr:write("ERROR: ", s, "!\n") printf(...)
socket.sleep(3) io.stderr:write("!\n")
os.exit() os.exit()
end end
@ -80,7 +85,6 @@ io.stderr:write("----------------------------------------------\n",
start = socket.gettime() start = socket.gettime()
function reconnect() function reconnect()
io.stderr:write("attempting data connection... ")
if data then data:close() end if data then data:close() end
remote [[ remote [[
if data then data:close() data = nil end if data then data:close() data = nil end
@ -88,12 +92,11 @@ function reconnect()
data:setoption("tcp-nodelay", true) data:setoption("tcp-nodelay", true)
]] ]]
data, err = socket.connect(host, port) data, err = socket.connect(host, port)
if not data then fail(err) if not data then fail(err) end
else pass("connected!") end
data:setoption("tcp-nodelay", true) data:setoption("tcp-nodelay", true)
end end
pass("attempting control connection...") printf("attempting control connection...")
control, err = socket.connect(host, port) control, err = socket.connect(host, port)
if err then fail(err) if err then fail(err)
else pass("connected!") end else pass("connected!") end
@ -112,6 +115,7 @@ end
------------------------------------------------------------------------ ------------------------------------------------------------------------
function test_mixed(len) function test_mixed(len)
reconnect() reconnect()
io.stderr:write("length " .. len .. ": ")
local inter = math.ceil(len/4) local inter = math.ceil(len/4)
local p1 = "unix " .. string.rep("x", inter) .. "line\n" local p1 = "unix " .. string.rep("x", inter) .. "line\n"
local p2 = "dos " .. string.rep("y", inter) .. "line\r\n" local p2 = "dos " .. string.rep("y", inter) .. "line\r\n"
@ -139,6 +143,7 @@ end
------------------------------------------------------------------------ ------------------------------------------------------------------------
function test_asciiline(len) function test_asciiline(len)
reconnect() reconnect()
io.stderr:write("length " .. len .. ": ")
local str, str10, back, err local str, str10, back, err
str = string.rep("x", math.mod(len, 10)) str = string.rep("x", math.mod(len, 10))
str10 = string.rep("aZb.c#dAe?", math.floor(len/10)) str10 = string.rep("aZb.c#dAe?", math.floor(len/10))
@ -156,6 +161,7 @@ end
------------------------------------------------------------------------ ------------------------------------------------------------------------
function test_rawline(len) function test_rawline(len)
reconnect() reconnect()
io.stderr:write("length " .. len .. ": ")
local str, str10, back, err local str, str10, back, err
str = string.rep(string.char(47), math.mod(len, 10)) 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),
@ -174,6 +180,7 @@ end
------------------------------------------------------------------------ ------------------------------------------------------------------------
function test_raw(len) function test_raw(len)
reconnect() reconnect()
io.stderr:write("length " .. len .. ": ")
local half = math.floor(len/2) local half = math.floor(len/2)
local s1, s2, back, err local s1, s2, back, err
s1 = string.rep("x", half) s1 = string.rep("x", half)
@ -194,7 +201,7 @@ end
function test_totaltimeoutreceive(len, tm, sl) function test_totaltimeoutreceive(len, tm, sl)
reconnect() reconnect()
local str, err, partial local str, err, partial
pass("%d bytes, %ds total timeout, %ds pause", len, tm, sl) printf("%d bytes, %ds total timeout, %ds pause: ", len, tm, sl)
remote (string.format ([[ remote (string.format ([[
data:settimeout(%d) data:settimeout(%d)
str = string.rep('a', %d) str = string.rep('a', %d)
@ -215,7 +222,7 @@ end
function test_totaltimeoutsend(len, tm, sl) function test_totaltimeoutsend(len, tm, sl)
reconnect() reconnect()
local str, err, total local str, err, total
pass("%d bytes, %ds total timeout, %ds pause", len, tm, sl) printf("%d bytes, %ds total timeout, %ds pause: ", len, tm, sl)
remote (string.format ([[ remote (string.format ([[
data:settimeout(%d) data:settimeout(%d)
str = data:receive(%d) str = data:receive(%d)
@ -235,7 +242,7 @@ end
function test_blockingtimeoutreceive(len, tm, sl) function test_blockingtimeoutreceive(len, tm, sl)
reconnect() reconnect()
local str, err, partial local str, err, partial
pass("%d bytes, %ds blocking timeout, %ds pause", len, tm, sl) printf("%d bytes, %ds blocking timeout, %ds pause: ", len, tm, sl)
remote (string.format ([[ remote (string.format ([[
data:settimeout(%d) data:settimeout(%d)
str = string.rep('a', %d) str = string.rep('a', %d)
@ -255,7 +262,7 @@ end
function test_blockingtimeoutsend(len, tm, sl) function test_blockingtimeoutsend(len, tm, sl)
reconnect() reconnect()
local str, err, total local str, err, total
pass("%d bytes, %ds blocking timeout, %ds pause", len, tm, sl) printf("%d bytes, %ds blocking timeout, %ds pause: ", len, tm, sl)
remote (string.format ([[ remote (string.format ([[
data:settimeout(%d) data:settimeout(%d)
str = data:receive(%d) str = data:receive(%d)
@ -273,6 +280,7 @@ end
------------------------------------------------------------------------ ------------------------------------------------------------------------
function empty_connect() function empty_connect()
printf("empty connect: ")
reconnect() reconnect()
if data then data:close() data = nil end if data then data:close() data = nil end
remote [[ remote [[
@ -284,7 +292,7 @@ function empty_connect()
pass("ok") pass("ok")
data = socket.connect(host, port) data = socket.connect(host, port)
else else
pass("gethostbyname returns localhost on empty string...") pass("gethostbyname returns localhost on empty string...")
end end
end end
@ -311,7 +319,7 @@ function test_closed()
local back, partial, err local back, partial, err
local str = 'little string' local str = 'little string'
reconnect() reconnect()
pass("trying read detection") printf("trying read detection: ")
remote (string.format ([[ remote (string.format ([[
data:send('%s') data:send('%s')
data:close() data:close()
@ -324,7 +332,7 @@ function test_closed()
elseif str ~= partial then fail("didn't receive partial result.") elseif str ~= partial then fail("didn't receive partial result.")
else pass("graceful 'closed' received") end else pass("graceful 'closed' received") end
reconnect() reconnect()
pass("trying write detection") printf("trying write detection: ")
remote [[ remote [[
data:close() data:close()
data = nil data = nil
@ -342,7 +350,6 @@ end
------------------------------------------------------------------------ ------------------------------------------------------------------------
function test_selectbugs() function test_selectbugs()
local r, s, e = socket.select(nil, nil, 0.1) local r, s, e = socket.select(nil, nil, 0.1)
print(r, s, e)
assert(type(r) == "table" and type(s) == "table" and assert(type(r) == "table" and type(s) == "table" and
(e == "timeout" or e == "error")) (e == "timeout" or e == "error"))
pass("both nil: ok") pass("both nil: ok")
@ -374,7 +381,7 @@ end
------------------------------------------------------------------------ ------------------------------------------------------------------------
function accept_timeout() function accept_timeout()
io.stderr:write("accept with timeout (if it hangs, it failed): ") printf("accept with timeout (if it hangs, it failed): ")
local s, e = socket.bind("*", 0, 0) local s, e = socket.bind("*", 0, 0)
assert(s, e) assert(s, e)
local t = socket.gettime() local t = socket.gettime()
@ -390,23 +397,22 @@ end
------------------------------------------------------------------------ ------------------------------------------------------------------------
function connect_timeout() function connect_timeout()
io.stderr:write("connect with timeout (if it hangs, it failed!): ") printf("connect with timeout (if it hangs, it failed!): ")
local t = socket.gettime() local t = socket.gettime()
local c, e = socket.tcp() local c, e = socket.tcp()
assert(c, e) assert(c, e)
c:settimeout(0.1) c:settimeout(0.1)
local t = socket.gettime() local t = socket.gettime()
local r, e = c:connect("10.0.0.1", 81) local r, e = c:connect("10.0.0.1", 81)
print(r, e)
assert(not r, "should not connect") 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() c:close()
print("ok") pass("ok")
end end
------------------------------------------------------------------------ ------------------------------------------------------------------------
function accept_errors() function accept_errors()
io.stderr:write("not listening: ") printf("not listening: ")
local d, e = socket.bind("*", 0) local d, e = socket.bind("*", 0)
assert(d, e); assert(d, e);
local c, e = socket.tcp(); local c, e = socket.tcp();
@ -415,26 +421,26 @@ function accept_errors()
d:settimeout(2) d:settimeout(2)
local r, e = d:accept() local r, e = d:accept()
assert(not r and e) assert(not r and e)
print("ok: ", e) pass("ok")
io.stderr:write("not supported: ") printf("not supported: ")
local c, e = socket.udp() local c, e = socket.udp()
assert(c, e); assert(c, e);
d:setfd(c:getfd()) d:setfd(c:getfd())
local r, e = d:accept() local r, e = d:accept()
assert(not r and e) assert(not r and e)
print("ok: ", e) pass("ok")
end end
------------------------------------------------------------------------ ------------------------------------------------------------------------
function connect_errors() function connect_errors()
io.stderr:write("connection refused: ") printf("connection refused: ")
local c, e = socket.connect("localhost", 1); local c, e = socket.connect("localhost", 1);
assert(not c and e) assert(not c and e)
print("ok: ", e) pass("ok")
io.stderr:write("host not found: ") printf("host not found: ")
local c, e = socket.connect("host.is.invalid", 1); local c, e = socket.connect("host.is.invalid", 1);
assert(not c and e, e) assert(not c and e, e)
print("ok: ", e) pass("ok")
end end
------------------------------------------------------------------------ ------------------------------------------------------------------------
@ -447,7 +453,7 @@ function rebind_test()
r, e = s:bind("localhost", p) r, e = s:bind("localhost", p)
assert(not r, "managed to rebind!") assert(not r, "managed to rebind!")
assert(e) assert(e)
print("ok: ", e) pass("ok")
end end
------------------------------------------------------------------------ ------------------------------------------------------------------------
@ -469,14 +475,14 @@ function getstats_test()
assert(s == t, "sent count failed" .. tostring(s) assert(s == t, "sent count failed" .. tostring(s)
.. "/" .. tostring(t)) .. "/" .. tostring(t))
end end
print("ok") pass("ok")
end end
------------------------------------------------------------------------ ------------------------------------------------------------------------
function test_nonblocking(size) function test_nonblocking(size)
reconnect() reconnect()
print("Testing " .. 2*size .. " bytes") printf("testing " .. 2*size .. " bytes: ")
remote(string.format([[ remote(string.format([[
data:send(string.rep("a", %d)) data:send(string.rep("a", %d))
socket.sleep(0.5) socket.sleep(0.5)
@ -508,7 +514,7 @@ remote(string.format([[
data:settimeout(-1) data:settimeout(-1)
local back = data:receive(2*size) local back = data:receive(2*size)
assert(back == str, "'" .. back .. "' vs '" .. str .. "'") assert(back == str, "'" .. back .. "' vs '" .. str .. "'")
print("ok") pass("ok")
end end
------------------------------------------------------------------------ ------------------------------------------------------------------------
@ -516,7 +522,7 @@ function test_readafterclose()
local back, partial, err local back, partial, err
local str = 'little string' local str = 'little string'
reconnect() reconnect()
pass("trying repeated '*a' pattern") printf("trying repeated '*a' pattern")
remote (string.format ([[ remote (string.format ([[
data:send('%s') data:send('%s')
data:close() data:close()
@ -526,9 +532,9 @@ function test_readafterclose()
assert(back == str, "unexpected data read") assert(back == str, "unexpected data read")
back, err, partial = data:receive("*a") back, err, partial = data:receive("*a")
assert(back == nil and err == "closed", "should have returned 'closed'") assert(back == nil and err == "closed", "should have returned 'closed'")
print("ok") pass("ok")
reconnect() reconnect()
pass("trying active close before '*a'") printf("trying active close before '*a'")
remote (string.format ([[ remote (string.format ([[
data:close() data:close()
data = nil data = nil
@ -536,9 +542,9 @@ function test_readafterclose()
data:close() data:close()
back, err, partial = data:receive("*a") back, err, partial = data:receive("*a")
assert(back == nil and err == "closed", "should have returned 'closed'") assert(back == nil and err == "closed", "should have returned 'closed'")
print("ok") pass("ok")
reconnect() reconnect()
pass("trying active close before '*l'") printf("trying active close before '*l'")
remote (string.format ([[ remote (string.format ([[
data:close() data:close()
data = nil data = nil
@ -546,9 +552,9 @@ function test_readafterclose()
data:close() data:close()
back, err, partial = data:receive() back, err, partial = data:receive()
assert(back == nil and err == "closed", "should have returned 'closed'") assert(back == nil and err == "closed", "should have returned 'closed'")
print("ok") pass("ok")
reconnect() reconnect()
pass("trying active close before raw 1") printf("trying active close before raw 1")
remote (string.format ([[ remote (string.format ([[
data:close() data:close()
data = nil data = nil
@ -556,9 +562,9 @@ function test_readafterclose()
data:close() data:close()
back, err, partial = data:receive(1) back, err, partial = data:receive(1)
assert(back == nil and err == "closed", "should have returned 'closed'") assert(back == nil and err == "closed", "should have returned 'closed'")
print("ok") pass("ok")
reconnect() reconnect()
pass("trying active close before raw 0") printf("trying active close before raw 0")
remote (string.format ([[ remote (string.format ([[
data:close() data:close()
data = nil data = nil
@ -566,7 +572,7 @@ function test_readafterclose()
data:close() data:close()
back, err, partial = data:receive(0) back, err, partial = data:receive(0)
assert(back == nil and err == "closed", "should have returned 'closed'") assert(back == nil and err == "closed", "should have returned 'closed'")
print("ok") pass("ok")
end end
------------------------------------------------------------------------ ------------------------------------------------------------------------
@ -581,13 +587,29 @@ function test_writeafterclose()
while not err do while not err do
sent, err, errsent, time = data:send(str) sent, err, errsent, time = data:send(str)
end end
print(sent, err, errsent, time) assert(err == "closed", "should have returned 'closed'")
print("ok") pass("ok")
end end
------------------------------------------------------------------------ ------------------------------------------------------------------------
--test_writeafterclose()
function test_partialrecv()
local str = 'little string'
reconnect()
remote([[
data:send("7890")
]])
data:settimeout(1)
back, err = data:receive(10, "123456")
assert(back == "1234567890", "failed on exact mixed length")
back, err = data:receive(8, "87654321")
assert(back == "87654321", "failed on exact length")
back, err = data:receive(4, "87654321")
assert(back == "87654321", "failed on smaller length")
pass("ok")
end
------------------------------------------------------------------------
test("method registration") test("method registration")
test_methods(socket.tcp(), { test_methods(socket.tcp(), {
"accept", "accept",
@ -629,12 +651,18 @@ test_methods(socket.udp(), {
"settimeout" "settimeout"
}) })
test("partial receive")
test_partialrecv()
test("select function") test("select function")
test_selectbugs() test_selectbugs()
test("testing read after close") test("read after close")
test_readafterclose() test_readafterclose()
test("write after close")
test_writeafterclose()
test("connect function") test("connect function")
connect_timeout() connect_timeout()
empty_connect() empty_connect()

View File

@ -1,13 +1,13 @@
function readfile(name) function readfile(name)
local f = io.open(name, "rb") local f = io.open(name, "rb")
if not f then return nil end if not f then return nil end
local s = f:read("*a") local s = f:read("*a")
f:close() f:close()
return s return s
end end
function similar(s1, s2) 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", "")) string.lower(string.gsub(s2 or "", "%s", ""))
end end
@ -28,7 +28,7 @@ local set = rawset
local warn = print local warn = print
local setglobal = function(table, key, value) local setglobal = function(table, key, value)
warn("changed " .. key) warn("changed " .. key)
set(table, key, value) set(table, key, value)
end end

View File

@ -3,439 +3,439 @@ socket.url = require("socket.url")
dofile("testsupport.lua") dofile("testsupport.lua")
local check_build_url = function(parsed) local check_build_url = function(parsed)
local built = socket.url.build(parsed) local built = socket.url.build(parsed)
if built ~= parsed.url then if built ~= parsed.url then
print("built is different from expected") print("built is different from expected")
print(built) print(built)
print(expected) print(expected)
exit() exit()
end end
end end
local check_protect = function(parsed, path, unsafe) local check_protect = function(parsed, path, unsafe)
local built = socket.url.build_path(parsed, unsafe) local built = socket.url.build_path(parsed, unsafe)
if built ~= path then if built ~= path then
print(built, path) print(built, path)
print("path composition failed.") print("path composition failed.")
exit() exit()
end end
end end
local check_invert = function(url) local check_invert = function(url)
local parsed = socket.url.parse(url) local parsed = socket.url.parse(url)
parsed.path = socket.url.build_path(socket.url.parse_path(parsed.path)) parsed.path = socket.url.build_path(socket.url.parse_path(parsed.path))
local rebuilt = socket.url.build(parsed) local rebuilt = socket.url.build(parsed)
if rebuilt ~= url then if rebuilt ~= url then
print(url, rebuilt) print(url, rebuilt)
print("original and rebuilt are different") print("original and rebuilt are different")
exit() exit()
end end
end end
local check_parse_path = function(path, expect) local check_parse_path = function(path, expect)
local parsed = socket.url.parse_path(path) local parsed = socket.url.parse_path(path)
for i = 1, math.max(table.getn(parsed), table.getn(expect)) do for i = 1, math.max(table.getn(parsed), table.getn(expect)) do
if parsed[i] ~= expect[i] then if parsed[i] ~= expect[i] then
print(path) print(path)
exit() exit()
end end
end end
if expect.is_directory ~= parsed.is_directory then if expect.is_directory ~= parsed.is_directory then
print(path) print(path)
print("is_directory mismatch") print("is_directory mismatch")
exit() exit()
end end
if expect.is_absolute ~= parsed.is_absolute then if expect.is_absolute ~= parsed.is_absolute then
print(path) print(path)
print("is_absolute mismatch") print("is_absolute mismatch")
exit() exit()
end end
local built = socket.url.build_path(expect) local built = socket.url.build_path(expect)
if built ~= path then if built ~= path then
print(built, path) print(built, path)
print("path composition failed.") print("path composition failed.")
exit() exit()
end end
end end
local check_absolute_url = function(base, relative, absolute) local check_absolute_url = function(base, relative, absolute)
local res = socket.url.absolute(base, relative) local res = socket.url.absolute(base, relative)
if res ~= absolute then if res ~= absolute then
io.write("absolute: In test for '", relative, "' expected '", io.write("absolute: In test for '", relative, "' expected '",
absolute, "' but got '", res, "'\n") absolute, "' but got '", res, "'\n")
exit() exit()
end end
end end
local check_parse_url = function(gaba) local check_parse_url = function(gaba)
local url = gaba.url local url = gaba.url
gaba.url = nil gaba.url = nil
local parsed = socket.url.parse(url) local parsed = socket.url.parse(url)
for i, v in pairs(gaba) do for i, v in pairs(gaba) do
if v ~= parsed[i] then 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") v, "' but got '", tostring(parsed[i]), "'\n")
for i,v in pairs(parsed) do print(i,v) end for i,v in pairs(parsed) do print(i,v) end
exit() exit()
end end
end end
for i, v in pairs(parsed) do for i, v in pairs(parsed) do
if v ~= gaba[i] then 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") tostring(gaba[i]), "' but got '", v, "'\n")
for i,v in pairs(parsed) do print(i,v) end for i,v in pairs(parsed) do print(i,v) end
exit() exit()
end end
end end
end end
print("testing URL parsing") print("testing URL parsing")
check_parse_url{ check_parse_url{
url = "scheme://userinfo@host:port/path;params?query#fragment", url = "scheme://userinfo@host:port/path;params?query#fragment",
scheme = "scheme", scheme = "scheme",
authority = "userinfo@host:port", authority = "userinfo@host:port",
host = "host", host = "host",
port = "port", port = "port",
userinfo = "userinfo", userinfo = "userinfo",
user = "userinfo", user = "userinfo",
path = "/path", path = "/path",
params = "params", params = "params",
query = "query", query = "query",
fragment = "fragment" fragment = "fragment"
} }
check_parse_url{ check_parse_url{
url = "scheme://user:password@host:port/path;params?query#fragment", url = "scheme://user:password@host:port/path;params?query#fragment",
scheme = "scheme", scheme = "scheme",
authority = "user:password@host:port", authority = "user:password@host:port",
host = "host", host = "host",
port = "port", port = "port",
userinfo = "user:password", userinfo = "user:password",
user = "user", user = "user",
password = "password", password = "password",
path = "/path", path = "/path",
params = "params", params = "params",
query = "query", query = "query",
fragment = "fragment", fragment = "fragment",
} }
check_parse_url{ check_parse_url{
url = "scheme://userinfo@host:port/path;params?query#", url = "scheme://userinfo@host:port/path;params?query#",
scheme = "scheme", scheme = "scheme",
authority = "userinfo@host:port", authority = "userinfo@host:port",
host = "host", host = "host",
port = "port", port = "port",
userinfo = "userinfo", userinfo = "userinfo",
user = "userinfo", user = "userinfo",
path = "/path", path = "/path",
params = "params", params = "params",
query = "query", query = "query",
fragment = "" fragment = ""
} }
check_parse_url{ check_parse_url{
url = "scheme://userinfo@host:port/path;params?#fragment", url = "scheme://userinfo@host:port/path;params?#fragment",
scheme = "scheme", scheme = "scheme",
authority = "userinfo@host:port", authority = "userinfo@host:port",
host = "host", host = "host",
port = "port", port = "port",
userinfo = "userinfo", userinfo = "userinfo",
user = "userinfo", user = "userinfo",
path = "/path", path = "/path",
params = "params", params = "params",
query = "", query = "",
fragment = "fragment" fragment = "fragment"
} }
check_parse_url{ check_parse_url{
url = "scheme://userinfo@host:port/path;params#fragment", url = "scheme://userinfo@host:port/path;params#fragment",
scheme = "scheme", scheme = "scheme",
authority = "userinfo@host:port", authority = "userinfo@host:port",
host = "host", host = "host",
port = "port", port = "port",
userinfo = "userinfo", userinfo = "userinfo",
user = "userinfo", user = "userinfo",
path = "/path", path = "/path",
params = "params", params = "params",
fragment = "fragment" fragment = "fragment"
} }
check_parse_url{ check_parse_url{
url = "scheme://userinfo@host:port/path;?query#fragment", url = "scheme://userinfo@host:port/path;?query#fragment",
scheme = "scheme", scheme = "scheme",
authority = "userinfo@host:port", authority = "userinfo@host:port",
host = "host", host = "host",
port = "port", port = "port",
userinfo = "userinfo", userinfo = "userinfo",
user = "userinfo", user = "userinfo",
path = "/path", path = "/path",
params = "", params = "",
query = "query", query = "query",
fragment = "fragment" fragment = "fragment"
} }
check_parse_url{ check_parse_url{
url = "scheme://userinfo@host:port/path?query#fragment", url = "scheme://userinfo@host:port/path?query#fragment",
scheme = "scheme", scheme = "scheme",
authority = "userinfo@host:port", authority = "userinfo@host:port",
host = "host", host = "host",
port = "port", port = "port",
userinfo = "userinfo", userinfo = "userinfo",
user = "userinfo", user = "userinfo",
path = "/path", path = "/path",
query = "query", query = "query",
fragment = "fragment" fragment = "fragment"
} }
check_parse_url{ check_parse_url{
url = "scheme://userinfo@host:port/;params?query#fragment", url = "scheme://userinfo@host:port/;params?query#fragment",
scheme = "scheme", scheme = "scheme",
authority = "userinfo@host:port", authority = "userinfo@host:port",
host = "host", host = "host",
port = "port", port = "port",
userinfo = "userinfo", userinfo = "userinfo",
user = "userinfo", user = "userinfo",
path = "/", path = "/",
params = "params", params = "params",
query = "query", query = "query",
fragment = "fragment" fragment = "fragment"
} }
check_parse_url{ check_parse_url{
url = "scheme://userinfo@host:port", url = "scheme://userinfo@host:port",
scheme = "scheme", scheme = "scheme",
authority = "userinfo@host:port", authority = "userinfo@host:port",
host = "host", host = "host",
port = "port", port = "port",
userinfo = "userinfo", userinfo = "userinfo",
user = "userinfo", user = "userinfo",
} }
check_parse_url{ check_parse_url{
url = "//userinfo@host:port/path;params?query#fragment", url = "//userinfo@host:port/path;params?query#fragment",
authority = "userinfo@host:port", authority = "userinfo@host:port",
host = "host", host = "host",
port = "port", port = "port",
userinfo = "userinfo", userinfo = "userinfo",
user = "userinfo", user = "userinfo",
path = "/path", path = "/path",
params = "params", params = "params",
query = "query", query = "query",
fragment = "fragment" fragment = "fragment"
} }
check_parse_url{ check_parse_url{
url = "//userinfo@host:port/path", url = "//userinfo@host:port/path",
authority = "userinfo@host:port", authority = "userinfo@host:port",
host = "host", host = "host",
port = "port", port = "port",
userinfo = "userinfo", userinfo = "userinfo",
user = "userinfo", user = "userinfo",
path = "/path", path = "/path",
} }
check_parse_url{ check_parse_url{
url = "//userinfo@host/path", url = "//userinfo@host/path",
authority = "userinfo@host", authority = "userinfo@host",
host = "host", host = "host",
userinfo = "userinfo", userinfo = "userinfo",
user = "userinfo", user = "userinfo",
path = "/path", path = "/path",
} }
check_parse_url{ check_parse_url{
url = "//user:password@host/path", url = "//user:password@host/path",
authority = "user:password@host", authority = "user:password@host",
host = "host", host = "host",
userinfo = "user:password", userinfo = "user:password",
password = "password", password = "password",
user = "user", user = "user",
path = "/path", path = "/path",
} }
check_parse_url{ check_parse_url{
url = "//user:@host/path", url = "//user:@host/path",
authority = "user:@host", authority = "user:@host",
host = "host", host = "host",
userinfo = "user:", userinfo = "user:",
password = "", password = "",
user = "user", user = "user",
path = "/path", path = "/path",
} }
check_parse_url{ check_parse_url{
url = "//user@host:port/path", url = "//user@host:port/path",
authority = "user@host:port", authority = "user@host:port",
host = "host", host = "host",
userinfo = "user", userinfo = "user",
user = "user", user = "user",
port = "port", port = "port",
path = "/path", path = "/path",
} }
check_parse_url{ check_parse_url{
url = "//host:port/path", url = "//host:port/path",
authority = "host:port", authority = "host:port",
port = "port", port = "port",
host = "host", host = "host",
path = "/path", path = "/path",
} }
check_parse_url{ check_parse_url{
url = "//host/path", url = "//host/path",
authority = "host", authority = "host",
host = "host", host = "host",
path = "/path", path = "/path",
} }
check_parse_url{ check_parse_url{
url = "//host", url = "//host",
authority = "host", authority = "host",
host = "host", host = "host",
} }
check_parse_url{ check_parse_url{
url = "/path", url = "/path",
path = "/path", path = "/path",
} }
check_parse_url{ check_parse_url{
url = "path", url = "path",
path = "path", path = "path",
} }
print("testing URL building") print("testing URL building")
check_build_url { check_build_url {
url = "scheme://user:password@host:port/path;params?query#fragment", url = "scheme://user:password@host:port/path;params?query#fragment",
scheme = "scheme", scheme = "scheme",
host = "host", host = "host",
port = "port", port = "port",
user = "user", user = "user",
password = "password", password = "password",
path = "/path", path = "/path",
params = "params", params = "params",
query = "query", query = "query",
fragment = "fragment" fragment = "fragment"
} }
check_build_url { check_build_url {
url = "scheme://user:password@host/path;params?query#fragment", url = "scheme://user:password@host/path;params?query#fragment",
scheme = "scheme", scheme = "scheme",
host = "host", host = "host",
user = "user", user = "user",
password = "password", password = "password",
path = "/path", path = "/path",
params = "params", params = "params",
query = "query", query = "query",
fragment = "fragment" fragment = "fragment"
} }
check_build_url { check_build_url {
url = "scheme://user@host/path;params?query#fragment", url = "scheme://user@host/path;params?query#fragment",
scheme = "scheme", scheme = "scheme",
host = "host", host = "host",
user = "user", user = "user",
path = "/path", path = "/path",
params = "params", params = "params",
query = "query", query = "query",
fragment = "fragment" fragment = "fragment"
} }
check_build_url { check_build_url {
url = "scheme://host/path;params?query#fragment", url = "scheme://host/path;params?query#fragment",
scheme = "scheme", scheme = "scheme",
host = "host", host = "host",
path = "/path", path = "/path",
params = "params", params = "params",
query = "query", query = "query",
fragment = "fragment" fragment = "fragment"
} }
check_build_url { check_build_url {
url = "scheme://host/path;params#fragment", url = "scheme://host/path;params#fragment",
scheme = "scheme", scheme = "scheme",
host = "host", host = "host",
path = "/path", path = "/path",
params = "params", params = "params",
fragment = "fragment" fragment = "fragment"
} }
check_build_url { check_build_url {
url = "scheme://host/path#fragment", url = "scheme://host/path#fragment",
scheme = "scheme", scheme = "scheme",
host = "host", host = "host",
path = "/path", path = "/path",
fragment = "fragment" fragment = "fragment"
} }
check_build_url { check_build_url {
url = "scheme://host/path", url = "scheme://host/path",
scheme = "scheme", scheme = "scheme",
host = "host", host = "host",
path = "/path", path = "/path",
} }
check_build_url { check_build_url {
url = "//host/path", url = "//host/path",
host = "host", host = "host",
path = "/path", path = "/path",
} }
check_build_url { check_build_url {
url = "/path", url = "/path",
path = "/path", path = "/path",
} }
check_build_url { check_build_url {
url = "scheme://user:password@host:port/path;params?query#fragment", url = "scheme://user:password@host:port/path;params?query#fragment",
scheme = "scheme", scheme = "scheme",
host = "host", host = "host",
port = "port", port = "port",
user = "user", user = "user",
userinfo = "not used", userinfo = "not used",
password = "password", password = "password",
path = "/path", path = "/path",
params = "params", params = "params",
query = "query", query = "query",
fragment = "fragment" fragment = "fragment"
} }
check_build_url { check_build_url {
url = "scheme://user:password@host:port/path;params?query#fragment", url = "scheme://user:password@host:port/path;params?query#fragment",
scheme = "scheme", scheme = "scheme",
host = "host", host = "host",
port = "port", port = "port",
user = "user", user = "user",
userinfo = "not used", userinfo = "not used",
authority = "not used", authority = "not used",
password = "password", password = "password",
path = "/path", path = "/path",
params = "params", params = "params",
query = "query", query = "query",
fragment = "fragment" fragment = "fragment"
} }
check_build_url { check_build_url {
url = "scheme://user:password@host:port/path;params?query#fragment", url = "scheme://user:password@host:port/path;params?query#fragment",
scheme = "scheme", scheme = "scheme",
host = "host", host = "host",
port = "port", port = "port",
userinfo = "user:password", userinfo = "user:password",
authority = "not used", authority = "not used",
path = "/path", path = "/path",
params = "params", params = "params",
query = "query", query = "query",
fragment = "fragment" fragment = "fragment"
} }
check_build_url { check_build_url {
url = "scheme://user:password@host:port/path;params?query#fragment", url = "scheme://user:password@host:port/path;params?query#fragment",
scheme = "scheme", scheme = "scheme",
authority = "user:password@host:port", authority = "user:password@host:port",
path = "/path", path = "/path",
params = "params", params = "params",
query = "query", query = "query",
fragment = "fragment" fragment = "fragment"
} }
-- standard RFC tests -- standard RFC tests
@ -488,11 +488,11 @@ print("testing path parsing and composition")
check_parse_path("/eu/tu/ele", { "eu", "tu", "ele"; is_absolute = 1 }) 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/", { "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}) { "eu", "tu", "ele", "nos", "vos", "eles"; is_directory = 1})
check_parse_path("/", { is_absolute = 1, is_directory = 1}) check_parse_path("/", { is_absolute = 1, is_directory = 1})
check_parse_path("", { }) 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}) { "eu\1", "\2tu", "e\3l\4e", "nos", "vos\5", "e\18les"; is_directory = 1})
check_parse_path("eu/tu", { "eu", "tu" }) check_parse_path("eu/tu", { "eu", "tu" })
print("testing path protection") print("testing path protection")

View File

@ -298,7 +298,7 @@ function empty_connect()
pass("ok") pass("ok")
data = socket.connect(host, port) data = socket.connect(host, port)
else else
pass("gethostbyname returns localhost on empty string...") pass("gethostbyname returns localhost on empty string...")
end end
end end