http.lua updated. still needs proxy support.

code.lua updated. looks neat.
This commit is contained in:
Diego Nehab 2004-01-16 07:06:31 +00:00
parent 3febb302ad
commit 89f3ecf782
10 changed files with 445 additions and 358 deletions

15
TODO
View File

@ -1,7 +1,17 @@
replace times by getrusage change send/recv to avoid using select
add gethostname and use it in HTTP, SMTP etc, and add manual entry.
add local connect, and manual entry
add shutdown, and manual entry
only allocate in case of success
only call select if io fails...
Proxy support pro http
make REUSEADDR an option...
make sure modules know if their dependencies are there. make sure modules know if their dependencies are there.
_
one thing i noticed in usocket.c is that it doesn't check for EINTR one thing i noticed in usocket.c is that it doesn't check for EINTR
after write(), sendto(), read(), recvfrom() etc. ? the usual trick is after write(), sendto(), read(), recvfrom() etc. ? the usual trick is
to loop while you get EINTR: to loop while you get EINTR:
@ -68,7 +78,6 @@ Ajeitar o protocolo da luaopen_socket()... sei l
- proteger ou atomizar o conjunto (timedout, receive), (timedout, send) - proteger ou atomizar o conjunto (timedout, receive), (timedout, send)
- inet_ntoa também é uma merda. - inet_ntoa também é uma merda.
- SSL - SSL
- Proxy support pro http
- checar operações em closed sockets - checar operações em closed sockets
- checar teste de writable socket com select - checar teste de writable socket com select

View File

@ -99,7 +99,7 @@ end
function getbyhttp(url, file, size) function getbyhttp(url, file, size)
local response = socket.http.request_cb( local response = socket.http.request_cb(
{url = url}, {url = url},
{body_cb = receive2disk(file, size)} {body_cb = receive2disk(file, size)}
) )
print() print()
if response.code ~= 200 then print(response.status or response.error) end if response.code ~= 200 then print(response.status or response.error) end

View File

@ -5,22 +5,29 @@
-- Conforming to: RFC 2616, LTN7 -- Conforming to: RFC 2616, LTN7
-- RCS ID: $Id$ -- RCS ID: $Id$
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
-- make sure LuaSocket is loaded
local Public, Private = {}, {} if not LUASOCKET_LIBNAME then error('module requires LuaSocket') end
local socket = _G[LUASOCKET_LIBNAME] -- get LuaSocket namespace -- get LuaSocket namespace
socket.http = Public -- create http sub namespace local socket = _G[LUASOCKET_LIBNAME]
if not socket then error('module requires LuaSocket') end
-- create smtp namespace inside LuaSocket namespace
local http = {}
socket.http = http
-- make all module globals fall into smtp namespace
setmetatable(http, { __index = _G })
setfenv(1, http)
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
-- Program constants -- Program constants
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
-- connection timeout in seconds -- connection timeout in seconds
Public.TIMEOUT = 60 TIMEOUT = 60
-- default port for document retrieval -- default port for document retrieval
Public.PORT = 80 PORT = 80
-- user agent field sent in request -- user agent field sent in request
Public.USERAGENT = "LuaSocket 2.0" USERAGENT = "LuaSocket 2.0"
-- block size used in transfers -- block size used in transfers
Public.BLOCKSIZE = 8192 BLOCKSIZE = 8192
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
-- Tries to get a pattern from the server and closes socket on error -- Tries to get a pattern from the server and closes socket on error
@ -30,7 +37,7 @@ Public.BLOCKSIZE = 8192
-- received pattern on success -- received pattern on success
-- nil followed by error message on error -- nil followed by error message on error
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
function Private.try_receive(sock, pattern) local function try_receiving(sock, pattern)
local data, err = sock:receive(pattern) local data, err = sock:receive(pattern)
if not data then sock:close() end if not data then sock:close() end
return data, err return data, err
@ -43,25 +50,12 @@ end
-- Returns -- Returns
-- err: error message if any, nil if successfull -- err: error message if any, nil if successfull
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
function Private.try_send(sock, data) local function try_sending(sock, ...)
local sent, err = sock:send(data) local sent, err = sock:send(unpack(arg))
if not sent then sock:close() end if not sent then sock:close() end
return err return err
end end
-----------------------------------------------------------------------------
-- Computes status code from HTTP status line
-- Input
-- line: HTTP status line
-- Returns
-- code: integer with status code, or nil if malformed line
-----------------------------------------------------------------------------
function Private.get_statuscode(line)
local code, _
_, _, code = string.find(line, "HTTP/%d*%.%d* (%d%d%d)")
return tonumber(code)
end
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
-- Receive server reply messages, parsing for status code -- Receive server reply messages, parsing for status code
-- Input -- Input
@ -71,10 +65,13 @@ end
-- line: full HTTP status line -- line: full HTTP status line
-- err: error message if any -- err: error message if any
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
function Private.receive_status(sock) local function receive_status(sock)
local line, err local line, err
line, err = Private.try_receive(sock) line, err = try_receiving(sock)
if not err then return Private.get_statuscode(line), line if not err then
local code, _
_, _, code = string.find(line, "HTTP/%d*%.%d* (%d%d%d)")
return tonumber(code), line
else return nil, nil, err end else return nil, nil, err end
end end
@ -89,11 +86,12 @@ end
-- all name_i are lowercase -- all name_i are lowercase
-- nil and error message in case of error -- nil and error message in case of error
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
function Private.receive_headers(sock, headers) local function receive_headers(sock, headers)
local line, err local line, err
local name, value, _ local name, value, _
headers = headers or {}
-- get first line -- get first line
line, err = Private.try_receive(sock) line, err = try_receiving(sock)
if err then return nil, err end if err then return nil, err end
-- headers go until a blank line is found -- headers go until a blank line is found
while line ~= "" do while line ~= "" do
@ -105,12 +103,12 @@ function Private.receive_headers(sock, headers)
end end
name = string.lower(name) name = string.lower(name)
-- get next line (value might be folded) -- get next line (value might be folded)
line, err = Private.try_receive(sock) line, err = try_receiving(sock)
if err then return nil, err end if err then return nil, err end
-- unfold any folded values -- unfold any folded values
while not err and string.find(line, "^%s") do while not err and string.find(line, "^%s") do
value = value .. line value = value .. line
line, err = Private.try_receive(sock) line, err = try_receiving(sock)
if err then return nil, err end if err then return nil, err end
end end
-- save pair in table -- save pair in table
@ -120,6 +118,19 @@ function Private.receive_headers(sock, headers)
return headers return headers
end end
-----------------------------------------------------------------------------
-- Aborts a receive callback
-- Input
-- cb: callback function
-- err: error message to pass to callback
-- Returns
-- callback return or if nil err
-----------------------------------------------------------------------------
local function abort(cb, err)
local go, err_or_f = cb(nil, err)
return err_or_f or err
end
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
-- Receives a chunked message body -- Receives a chunked message body
-- Input -- Input
@ -129,54 +140,37 @@ end
-- Returns -- Returns
-- nil if successfull or an error message in case of error -- nil if successfull or an error message in case of error
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
function Private.receivebody_bychunks(sock, headers, receive_cb) local function receive_body_bychunks(sock, headers, receive_cb)
local chunk, size, line, err, go, uerr, _ local chunk, size, line, err, go, err_or_f, _
while 1 do while 1 do
-- get chunk size, skip extention -- get chunk size, skip extention
line, err = Private.try_receive(sock) line, err = try_receiving(sock)
if err then if err then return abort(receive_cb, err) end
local go, uerr = receive_cb(nil, err)
return uerr or err
end
size = tonumber(string.gsub(line, ";.*", ""), 16) size = tonumber(string.gsub(line, ";.*", ""), 16)
if not size then if not size then return abort(receive_cb, "invalid chunk size") end
err = "invalid chunk size"
sock:close()
go, uerr = receive_cb(nil, err)
return uerr or err
end
-- was it the last chunk? -- was it the last chunk?
if size <= 0 then break end if size <= 0 then break end
-- get chunk -- get chunk
chunk, err = Private.try_receive(sock, size) chunk, err = try_receiving(sock, size)
if err then if err then return abort(receive_cb, err) end
go, uerr = receive_cb(nil, err)
return uerr or err
end
-- pass chunk to callback -- pass chunk to callback
go, uerr = receive_cb(chunk) go, err_or_f = receive_cb(chunk)
if not go then -- see if callback needs to be replaced
sock:close() receive_cb = err_or_f or receive_cb
return uerr or "aborted by callback" -- see if callback aborted
end if not go then return err_or_f or "aborted by callback" end
-- skip CRLF on end of chunk -- skip CRLF on end of chunk
_, err = Private.try_receive(sock) _, err = try_receiving(sock)
if err then if err then return abort(receive_cb, err) end
go, uerr = receive_cb(nil, err)
return uerr or err
end
end end
-- the server should not send trailer headers because we didn't send a -- the server should not send trailer headers because we didn't send a
-- header informing it we know how to deal with them. we do not risk -- header informing it we know how to deal with them. we do not risk
-- being caught unprepaired. -- being caught unprepaired.
headers, err = Private.receive_headers(sock, headers) _, err = receive_headers(sock, headers)
if err then if err then return abort(receive_cb, err) end
go, uerr = receive_cb(nil, err)
return uerr or err
end
-- let callback know we are done -- let callback know we are done
go, uerr = receive_cb("") _, err_or_f = receive_cb("")
return uerr return err_or_f
end end
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
@ -188,25 +182,21 @@ end
-- Returns -- Returns
-- nil if successfull or an error message in case of error -- nil if successfull or an error message in case of error
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
function Private.receivebody_bylength(sock, length, receive_cb) local function receive_body_bylength(sock, length, receive_cb)
local uerr, go
while length > 0 do while length > 0 do
local size = math.min(Public.BLOCKSIZE, length) local size = math.min(BLOCKSIZE, length)
local chunk, err = sock:receive(size) local chunk, err = sock:receive(size)
-- if there was an error before we got all the data local go, err_or_f = receive_cb(chunk)
if err and string.len(chunk) ~= length then length = length - string.len(chunk)
go, uerr = receive_cb(nil, err) -- see if callback aborted
return uerr or err if not go then return err_or_f or "aborted by callback" end
end -- see if callback needs to be replaced
go, uerr = receive_cb(chunk) receive_cb = err_or_f or receive_cb
if not go then -- see if there was an error
sock:close() if err and length > 0 then return abort(receive_cb, err) end
return uerr or "aborted by callback"
end
length = length - size
end end
go, uerr = receive_cb("") local _, err_or_f = receive_cb("")
return uerr return err_or_f
end end
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
@ -217,24 +207,24 @@ end
-- Returns -- Returns
-- nil if successfull or an error message in case of error -- nil if successfull or an error message in case of error
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
function Private.receivebody_untilclosed(sock, receive_cb) local function receive_body_untilclosed(sock, receive_cb)
local err, go, uerr
while 1 do while 1 do
local chunk, err = sock:receive(Public.BLOCKSIZE) local chunk, err = sock:receive(BLOCKSIZE)
if err == "closed" or not err then local go, err_or_f = receive_cb(chunk)
go, uerr = receive_cb(chunk) -- see if callback aborted
if not go then if not go then return err_or_f or "aborted by callback" end
sock:close() -- see if callback needs to be replaced
return uerr or "aborted by callback" receive_cb = err_or_f or receive_cb
-- see if we are done
if err == "closed" then
if chunk ~= "" then
go, err_or_f = receive_cb("")
return err_or_f
end end
if err == "closed" then break end
else
go, uerr = callback(nil, err)
return uerr or err
end end
-- see if there was an error
if err then return abort(receive_cb, err) end
end end
go, uerr = receive_cb("")
return uerr
end end
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
@ -246,59 +236,68 @@ end
-- Returns -- Returns
-- nil if successfull or an error message in case of error -- nil if successfull or an error message in case of error
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
function Private.receive_body(sock, headers, receive_cb) local function receive_body(sock, headers, receive_cb)
local te = headers["transfer-encoding"] local te = headers["transfer-encoding"]
if te and te ~= "identity" then if te and te ~= "identity" then
-- get by chunked transfer-coding of message body -- get by chunked transfer-coding of message body
return Private.receivebody_bychunks(sock, headers, receive_cb) return receive_body_bychunks(sock, headers, receive_cb)
elseif tonumber(headers["content-length"]) then elseif tonumber(headers["content-length"]) then
-- get by content-length -- get by content-length
local length = tonumber(headers["content-length"]) local length = tonumber(headers["content-length"])
return Private.receivebody_bylength(sock, length, receive_cb) return receive_body_bylength(sock, length, receive_cb)
else else
-- get it all until connection closes -- get it all until connection closes
return Private.receivebody_untilclosed(sock, receive_cb) return receive_body_untilclosed(sock, receive_cb)
end end
end end
-----------------------------------------------------------------------------
-- Drop HTTP response body
-- Input
-- sock: socket connected to the server
-- headers: response header fields
-- Returns
-- nil if successfull or an error message in case of error
-----------------------------------------------------------------------------
function Private.drop_body(sock, headers)
return Private.receive_body(sock, headers, function (c, e) return 1 end)
end
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
-- Sends data comming from a callback -- Sends data comming from a callback
-- Input -- Input
-- data: data connection -- data: data connection
-- send_cb: callback to produce file contents -- send_cb: callback to produce file contents
-- chunk, size: first callback return values
-- Returns -- Returns
-- nil if successfull, or an error message in case of error -- nil if successfull, or an error message in case of error
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
function Private.send_indirect(data, send_cb, chunk, size) local function send_body_bychunks(data, send_cb)
local total, sent, err
total = 0
while 1 do while 1 do
if type(chunk) ~= "string" or type(size) ~= "number" then local chunk, err_or_f = send_cb()
data:close() -- check if callback aborted
if not chunk and type(size) == "string" then return size if not chunk then return err_or_f or "aborted by callback" end
else return "invalid callback return" end -- check if callback should be replaced
end send_cb = err_or_f or send_cb
sent, err = data:send(chunk) -- if we are done, send last-chunk
if err then if chunk == "" then return try_sending(data, "0\r\n\r\n") end
data:close() -- else send middle chunk
return err local err = try_sending(data,
end string.format("%X\r\n", string.len(chunk)),
total = total + sent chunk,
if total >= size then break end "\r\n"
chunk, size = send_cb() )
if err then return err end
end
end
-----------------------------------------------------------------------------
-- Sends data comming from a callback
-- Input
-- data: data connection
-- send_cb: callback to produce body contents
-- Returns
-- nil if successfull, or an error message in case of error
-----------------------------------------------------------------------------
local function send_body_bylength(data, send_cb)
while 1 do
local chunk, err_or_f = send_cb()
-- check if callback aborted
if not chunk then return err_or_f or "aborted by callback" end
-- check if callback should be replaced
send_cb = err_or_f or send_cb
-- check if callback is done
if chunk == "" then return end
-- send data
local err = try_sending(data, chunk)
if err then return err end
end end
end end
@ -310,16 +309,16 @@ end
-- Returns -- Returns
-- err: error message if any -- err: error message if any
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
function Private.send_headers(sock, headers) local function send_headers(sock, headers)
local err local err
headers = headers or {} headers = headers or {}
-- send request headers -- send request headers
for i, v in headers do for i, v in headers do
err = Private.try_send(sock, i .. ": " .. v .. "\r\n") err = try_sending(sock, i .. ": " .. v .. "\r\n")
if err then return err end if err then return err end
end end
-- mark end of request headers -- mark end of request headers
return Private.try_send(sock, "\r\n") return try_sending(sock, "\r\n")
end end
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
@ -333,43 +332,39 @@ end
-- Returns -- Returns
-- err: nil in case of success, error message otherwise -- err: nil in case of success, error message otherwise
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
function Private.send_request(sock, method, uri, headers, body_cb) local function send_request(sock, method, uri, headers, body_cb)
local chunk, size, done, err local chunk, size, done, err
-- send request line -- send request line
err = Private.try_send(sock, method .. " " .. uri .. " HTTP/1.1\r\n") err = try_sending(sock, method .. " " .. uri .. " HTTP/1.1\r\n")
if err then return err end if err then return err end
-- if there is a request message body, add content-length header if body_cb and not headers["content-length"] then
chunk, size = body_cb() headers["transfer-encoding"] = "chunked"
if type(chunk) == "string" and type(size) == "number" then
if size > 0 then
headers["content-length"] = tostring(size)
end
else
sock:close()
if not chunk and type(size) == "string" then return size
else return "invalid callback return" end
end end
-- send request headers -- send request headers
err = Private.send_headers(sock, headers) err = send_headers(sock, headers)
if err then return err end if err then return err end
-- send request message body, if any -- send request message body, if any
if body_cb then if body_cb then
return Private.send_indirect(sock, body_cb, chunk, size) if not headers["content-length"] then
return send_body_bychunks(sock, body_cb)
else
return send_body_bylength(sock, body_cb)
end
end end
end end
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
-- Determines if we should read a message body from the server response -- Determines if we should read a message body from the server response
-- Input -- Input
-- request: a table with the original request information -- reqt: a table with the original request information
-- response: a table with the server response information -- respt: a table with the server response information
-- Returns -- Returns
-- 1 if a message body should be processed, nil otherwise -- 1 if a message body should be processed, nil otherwise
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
function Private.has_body(request, response) local function should_receive_body(reqt, respt)
if request.method == "HEAD" then return nil end if reqt.method == "HEAD" then return nil end
if response.code == 204 or response.code == 304 then return nil end if respt.code == 204 or respt.code == 304 then return nil end
if response.code >= 100 and response.code < 200 then return nil end if respt.code >= 100 and respt.code < 200 then return nil end
return 1 return 1
end end
@ -381,11 +376,11 @@ end
-- Returns -- Returns
-- lower: a table with the same headers, but with lowercase field names -- lower: a table with the same headers, but with lowercase field names
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
function Private.fill_headers(headers, parsed) local function fill_headers(headers, parsed)
local lower = {} local lower = {}
headers = headers or {} headers = headers or {}
-- set default headers -- set default headers
lower["user-agent"] = Public.USERAGENT lower["user-agent"] = USERAGENT
-- override with user values -- override with user values
for i,v in headers do for i,v in headers do
lower[string.lower(i)] = v lower[string.lower(i)] = v
@ -399,15 +394,15 @@ end
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
-- Decides wether we should follow retry with authorization formation -- Decides wether we should follow retry with authorization formation
-- Input -- Input
-- request: a table with the original request information -- reqt: a table with the original request information
-- parsed: parsed request URL -- parsed: parsed request URL
-- response: a table with the server response information -- respt: a table with the server response information
-- Returns -- Returns
-- 1 if we should retry, nil otherwise -- 1 if we should retry, nil otherwise
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
function Private.should_authorize(request, parsed, response) local function should_authorize(reqt, parsed, respt)
-- if there has been an authorization attempt, it must have failed -- if there has been an authorization attempt, it must have failed
if request.headers["authorization"] then return nil end if reqt.headers["authorization"] then return nil end
-- if we don't have authorization information, we can't retry -- if we don't have authorization information, we can't retry
if parsed.user and parsed.password then return 1 if parsed.user and parsed.password then return 1
else return nil end else return nil end
@ -416,66 +411,66 @@ end
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
-- Returns the result of retrying a request with authorization information -- Returns the result of retrying a request with authorization information
-- Input -- Input
-- request: a table with the original request information -- reqt: a table with the original request information
-- parsed: parsed request URL -- parsed: parsed request URL
-- response: a table with the server response information -- respt: a table with the server response information
-- Returns -- Returns
-- response: result of target redirection -- respt: result of target authorization
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
function Private.authorize(request, parsed, response) local function authorize(reqt, parsed, respt)
request.headers["authorization"] = "Basic " .. reqt.headers["authorization"] = "Basic " ..
socket.code.base64(parsed.user .. ":" .. parsed.password) socket.code.base64.encode(parsed.user .. ":" .. parsed.password)
local authorize = { local autht = {
redirects = request.redirects, nredirects = reqt.nredirects,
method = request.method, method = reqt.method,
url = request.url, url = reqt.url,
body_cb = request.body_cb, body_cb = reqt.body_cb,
headers = request.headers headers = reqt.headers
} }
return Public.request_cb(authorize, response) return request_cb(autht, respt)
end end
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
-- Decides wether we should follow a server redirect message -- Decides wether we should follow a server redirect message
-- Input -- Input
-- request: a table with the original request information -- reqt: a table with the original request information
-- response: a table with the server response information -- respt: a table with the server response information
-- Returns -- Returns
-- 1 if we should redirect, nil otherwise -- 1 if we should redirect, nil otherwise
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
function Private.should_redirect(request, response) local function should_redirect(reqt, respt)
local follow = not request.stay local follow = not reqt.stay
follow = follow and (response.code == 301 or response.code == 302) follow = follow and (respt.code == 301 or respt.code == 302)
follow = follow and (request.method == "GET" or request.method == "HEAD") follow = follow and (reqt.method == "GET" or reqt.method == "HEAD")
follow = follow and not (request.redirects and request.redirects >= 5) follow = follow and not (reqt.nredirects and reqt.nredirects >= 5)
return follow return follow
end end
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
-- Returns the result of a request following a server redirect message. -- Returns the result of a request following a server redirect message.
-- Input -- Input
-- request: a table with the original request information -- reqt: a table with the original request information
-- response: a table with the following fields: -- respt: a table with the following fields:
-- body_cb: response method body receive-callback -- body_cb: response method body receive-callback
-- Returns -- Returns
-- response: result of target redirection -- respt: result of target redirection
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
function Private.redirect(request, response) local function redirect(reqt, respt)
local redirects = request.redirects or 0 local nredirects = reqt.nredirects or 0
redirects = redirects + 1 nredirects = nredirects + 1
local redirect = { local redirt = {
redirects = redirects, nredirects = nredirects,
method = request.method, method = reqt.method,
-- the RFC says the redirect URL has to be absolute, but some -- the RFC says the redirect URL has to be absolute, but some
-- servers do not respect that -- servers do not respect that
url = socket.url.absolute(request.url, response.headers["location"]), url = socket.url.absolute(reqt.url, respt.headers["location"]),
body_cb = request.body_cb, body_cb = reqt.body_cb,
headers = request.headers headers = reqt.headers
} }
local response = Public.request_cb(redirect, response) respt = request_cb(redirt, respt)
-- we pass the location header as a clue we tried to redirect -- we pass the location header as a clue we tried to redirect
if response.headers then response.headers.location = redirect.url end if respt.headers then respt.headers.location = redirt.url end
return response return respt
end end
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
@ -485,7 +480,7 @@ end
-- Returns -- Returns
-- uri: request URI for parsed URL -- uri: request URI for parsed URL
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
function Private.request_uri(parsed) local function request_uri(parsed)
local uri = "" local uri = ""
if parsed.path then uri = uri .. parsed.path end if parsed.path then uri = uri .. parsed.path end
if parsed.params then uri = uri .. ";" .. parsed.params end if parsed.params then uri = uri .. ";" .. parsed.params end
@ -502,105 +497,110 @@ end
-- user: account user name -- user: account user name
-- password: account password) -- password: account password)
-- Returns -- Returns
-- request: request table -- reqt: request table
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
function Private.build_request(data) local function build_request(data)
local request = {} local reqt = {}
if type(data) == "table" then if type(data) == "table" then
for i, v in data for i, v in data
do request[i] = v do reqt[i] = v
end end
else request.url = data end else reqt.url = data end
return request return reqt
end end
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
-- Sends a HTTP request and retrieves the server reply using callbacks to -- Sends a HTTP request and retrieves the server reply using callbacks to
-- send the request body and receive the response body -- send the request body and receive the response body
-- Input -- Input
-- request: a table with the following fields -- reqt: a table with the following fields
-- method: "GET", "PUT", "POST" etc (defaults to "GET") -- method: "GET", "PUT", "POST" etc (defaults to "GET")
-- url: target uniform resource locator -- url: target uniform resource locator
-- user, password: authentication information -- user, password: authentication information
-- headers: request headers to send, or nil if none -- headers: request headers to send, or nil if none
-- body_cb: request message body send-callback, or nil if none -- body_cb: request message body send-callback, or nil if none
-- stay: should we refrain from following a server redirect message? -- stay: should we refrain from following a server redirect message?
-- response: a table with the following fields: -- respt: a table with the following fields:
-- body_cb: response method body receive-callback -- body_cb: response method body receive-callback
-- Returns -- Returns
-- response: a table with the following fields: -- respt: a table with the following fields:
-- headers: response header fields received, or nil if failed -- headers: response header fields received, or nil if failed
-- status: server response status line, or nil if failed -- status: server response status line, or nil if failed
-- code: server status code, or nil if failed -- code: server status code, or nil if failed
-- error: error message, or nil if successfull -- error: error message, or nil if successfull
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
function Public.request_cb(request, response) function request_cb(reqt, respt)
local parsed = socket.url.parse(request.url, { local parsed = socket.url.parse(reqt.url, {
host = "", host = "",
port = Public.PORT, port = PORT,
path ="/", path ="/",
scheme = "http" scheme = "http"
}) })
if parsed.scheme ~= "http" then if parsed.scheme ~= "http" then
response.error = string.format("unknown scheme '%s'", parsed.scheme) respt.error = string.format("unknown scheme '%s'", parsed.scheme)
return response return respt
end end
-- explicit authentication info overrides that given by the URL -- explicit authentication info overrides that given by the URL
parsed.user = request.user or parsed.user parsed.user = reqt.user or parsed.user
parsed.password = request.password or parsed.password parsed.password = reqt.password or parsed.password
-- default method -- default method
request.method = request.method or "GET" reqt.method = reqt.method or "GET"
-- fill default headers -- fill default headers
request.headers = Private.fill_headers(request.headers, parsed) reqt.headers = fill_headers(reqt.headers, parsed)
-- try to connect to server -- try to connect to server
local sock local sock
sock, response.error = socket.connect(parsed.host, parsed.port) sock, respt.error = socket.connect(parsed.host, parsed.port)
if not sock then return response end if not sock then return respt end
-- set connection timeout so that we do not hang forever -- set connection timeout so that we do not hang forever
sock:settimeout(Public.TIMEOUT) sock:settimeout(TIMEOUT)
-- send request message -- send request message
response.error = Private.send_request(sock, request.method, respt.error = send_request(sock, reqt.method,
Private.request_uri(parsed), request.headers, request.body_cb) request_uri(parsed), reqt.headers, reqt.body_cb)
if response.error then return response end if respt.error then
sock:close()
return respt
end
-- get server response message -- get server response message
response.code, response.status, response.error = respt.code, respt.status, respt.error = receive_status(sock)
Private.receive_status(sock) if respt.error then return respt end
if response.error then return response end -- deal with continue 100
-- deal with 1xx status -- servers should not send them, but they might
if response.code == 100 then if respt.code == 100 then
response.headers, response.error = Private.receive_headers(sock, {}) respt.headers, respt.error = receive_headers(sock, {})
if response.error then return response end if respt.error then return respt end
response.code, response.status, response.error = respt.code, respt.status, respt.error = receive_status(sock)
Private.receive_status(sock) if respt.error then return respt end
if response.error then return response end
end end
-- receive all headers -- receive all headers
response.headers, response.error = Private.receive_headers(sock, {}) respt.headers, respt.error = receive_headers(sock, {})
if response.error then return response end if respt.error then return respt end
-- decide what to do based on request and response parameters -- decide what to do based on request and response parameters
if Private.should_redirect(request, response) then if should_redirect(reqt, respt) then
Private.drop_body(sock, response.headers) -- drop the body
receive_body(sock, respt.headers, function (c, e) return 1 end)
-- we are done with this connection
sock:close() sock:close()
return Private.redirect(request, response) return redirect(reqt, respt)
elseif Private.should_authorize(request, parsed, response) then elseif should_authorize(reqt, parsed, respt) then
Private.drop_body(sock, response.headers) -- drop the body
receive_body(sock, respt.headers, function (c, e) return 1 end)
-- we are done with this connection
sock:close() sock:close()
return Private.authorize(request, parsed, response) return authorize(reqt, parsed, respt)
elseif Private.has_body(request, response) then elseif should_receive_body(reqt, respt) then
response.error = Private.receive_body(sock, response.headers, respt.error = receive_body(sock, respt.headers, respt.body_cb)
response.body_cb) if respt.error then return respt end
if response.error then return response end
sock:close() sock:close()
return response return respt
end end
sock:close() sock:close()
return response return respt
end end
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
-- Sends a HTTP request and retrieves the server reply -- Sends a HTTP request and retrieves the server reply
-- Input -- Input
-- request: a table with the following fields -- reqt: a table with the following fields
-- method: "GET", "PUT", "POST" etc (defaults to "GET") -- method: "GET", "PUT", "POST" etc (defaults to "GET")
-- url: request URL, i.e. the document to be retrieved -- url: request URL, i.e. the document to be retrieved
-- user, password: authentication information -- user, password: authentication information
@ -608,22 +608,22 @@ end
-- body: request message body as a string, or nil if none -- body: request message body as a string, or nil if none
-- stay: should we refrain from following a server redirect message? -- stay: should we refrain from following a server redirect message?
-- Returns -- Returns
-- response: a table with the following fields: -- respt: a table with the following fields:
-- body: response message body, or nil if failed -- body: response message body, or nil if failed
-- headers: response header fields, or nil if failed -- headers: response header fields, or nil if failed
-- status: server response status line, or nil if failed -- status: server response status line, or nil if failed
-- code: server response status code, or nil if failed -- code: server response status code, or nil if failed
-- error: error message if any -- error: error message if any
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
function Public.request(request) function request(reqt)
local response = {} local respt = {}
request.body_cb = socket.callback.send_string(request.body) reqt.body_cb = socket.callback.send_string(reqt.body)
local concat = socket.concat.create() local concat = socket.concat.create()
response.body_cb = socket.callback.receive_concat(concat) respt.body_cb = socket.callback.receive_concat(concat)
response = Public.request_cb(request, response) respt = request_cb(reqt, respt)
response.body = concat:getresult() respt.body = concat:getresult()
response.body_cb = nil respt.body_cb = nil
return response return respt
end end
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
@ -639,12 +639,11 @@ end
-- code: server response status code, or nil if failed -- code: server response status code, or nil if failed
-- error: error message if any -- error: error message if any
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
function Public.get(url_or_request) function get(url_or_request)
local request = Private.build_request(url_or_request) local reqt = build_request(url_or_request)
request.method = "GET" reqt.method = "GET"
local response = Public.request(request) local respt = request(reqt)
return response.body, response.headers, return respt.body, respt.headers, respt.code, respt.error
response.code, response.error
end end
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
@ -662,11 +661,14 @@ end
-- code: server response status code, or nil if failed -- code: server response status code, or nil if failed
-- error: error message, or nil if successfull -- error: error message, or nil if successfull
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
function Public.post(url_or_request, body) function post(url_or_request, body)
local request = Private.build_request(url_or_request) local reqt = build_request(url_or_request)
request.method = "POST" reqt.method = "POST"
request.body = request.body or body reqt.body = reqt.body or body
local response = Public.request(request) reqt.headers = reqt.headers or
return response.body, response.headers, { ["content-length"] = string.len(reqt.body) }
response.code, response.error local respt = request(reqt)
return respt.body, respt.headers, respt.code, respt.error
end end
return http

View File

@ -37,6 +37,7 @@ int sock_accept(p_sock ps, p_sock pa, SA *addr, socklen_t *addr_len,
const char *sock_connect(p_sock ps, SA *addr, socklen_t addr_len); const char *sock_connect(p_sock ps, SA *addr, socklen_t addr_len);
const char *sock_bind(p_sock ps, SA *addr, socklen_t addr_len); const char *sock_bind(p_sock ps, SA *addr, socklen_t addr_len);
void sock_listen(p_sock ps, int backlog); void sock_listen(p_sock ps, int backlog);
void sock_shutdown(p_sock ps, int how);
int sock_send(p_sock ps, const char *data, size_t count, int sock_send(p_sock ps, const char *data, size_t count,
size_t *sent, int timeout); size_t *sent, int timeout);
int sock_recv(p_sock ps, char *data, size_t count, int sock_recv(p_sock ps, char *data, size_t count,

View File

@ -25,6 +25,7 @@ static int meth_bind(lua_State *L);
static int meth_send(lua_State *L); static int meth_send(lua_State *L);
static int meth_getsockname(lua_State *L); static int meth_getsockname(lua_State *L);
static int meth_getpeername(lua_State *L); static int meth_getpeername(lua_State *L);
static int meth_shutdown(lua_State *L);
static int meth_receive(lua_State *L); static int meth_receive(lua_State *L);
static int meth_accept(lua_State *L); static int meth_accept(lua_State *L);
static int meth_close(lua_State *L); static int meth_close(lua_State *L);
@ -49,6 +50,7 @@ static luaL_reg tcp[] = {
{"getsockname", meth_getsockname}, {"getsockname", meth_getsockname},
{"settimeout", meth_settimeout}, {"settimeout", meth_settimeout},
{"close", meth_close}, {"close", meth_close},
{"shutdown", meth_shutdown},
{"setoption", meth_setoption}, {"setoption", meth_setoption},
{"__gc", meth_close}, {"__gc", meth_close},
{"fd", meth_fd}, {"fd", meth_fd},
@ -201,12 +203,12 @@ static int meth_accept(lua_State *L)
int err = IO_ERROR; int err = IO_ERROR;
p_tcp server = (p_tcp) aux_checkclass(L, "tcp{server}", 1); p_tcp server = (p_tcp) aux_checkclass(L, "tcp{server}", 1);
p_tm tm = &server->tm; p_tm tm = &server->tm;
p_tcp client = lua_newuserdata(L, sizeof(t_tcp)); p_tcp client;
aux_setclass(L, "tcp{client}", -1); t_sock sock;
tm_markstart(tm); tm_markstart(tm);
/* loop until connection accepted or timeout happens */ /* loop until connection accepted or timeout happens */
while (err != IO_DONE) { while (err != IO_DONE) {
err = sock_accept(&server->sock, &client->sock, err = sock_accept(&server->sock, &sock,
(SA *) &addr, &addr_len, tm_getfailure(tm)); (SA *) &addr, &addr_len, tm_getfailure(tm));
if (err == IO_CLOSED || (err == IO_TIMEOUT && !tm_getfailure(tm))) { if (err == IO_CLOSED || (err == IO_TIMEOUT && !tm_getfailure(tm))) {
lua_pushnil(L); lua_pushnil(L);
@ -214,6 +216,9 @@ static int meth_accept(lua_State *L)
return 2; return 2;
} }
} }
client = lua_newuserdata(L, sizeof(t_tcp));
aux_setclass(L, "tcp{client}", -1);
client->sock = sock;
/* initialize remaining structure fields */ /* initialize remaining structure fields */
io_init(&client->io, (p_send) sock_send, (p_recv) sock_recv, &client->sock); io_init(&client->io, (p_send) sock_send, (p_recv) sock_recv, &client->sock);
tm_init(&client->tm, -1, -1); tm_init(&client->tm, -1, -1);
@ -272,6 +277,33 @@ static int meth_close(lua_State *L)
return 0; return 0;
} }
/*-------------------------------------------------------------------------*\
* Shuts the connection down
\*-------------------------------------------------------------------------*/
static int meth_shutdown(lua_State *L)
{
p_tcp tcp = (p_tcp) aux_checkgroup(L, "tcp{any}", 1);
const char *how = luaL_optstring(L, 2, "both");
switch (how[0]) {
case 'b':
if (strcmp(how, "both")) goto error;
sock_shutdown(&tcp->sock, 2);
break;
case 's':
if (strcmp(how, "send")) goto error;
sock_shutdown(&tcp->sock, 1);
break;
case 'r':
if (strcmp(how, "receive")) goto error;
sock_shutdown(&tcp->sock, 0);
break;
}
return 0;
error:
luaL_argerror(L, 2, "invalid shutdown method");
return 0;
}
/*-------------------------------------------------------------------------*\ /*-------------------------------------------------------------------------*\
* Just call inet methods * Just call inet methods
\*-------------------------------------------------------------------------*/ \*-------------------------------------------------------------------------*/

View File

@ -72,6 +72,14 @@ void sock_listen(p_sock ps, int backlog)
listen(*ps, backlog); listen(*ps, backlog);
} }
/*-------------------------------------------------------------------------*\
*
\*-------------------------------------------------------------------------*/
void sock_shutdown(p_sock ps, int how)
{
shutdown(*ps, how);
}
/*-------------------------------------------------------------------------*\ /*-------------------------------------------------------------------------*\
* Accept with timeout * Accept with timeout
\*-------------------------------------------------------------------------*/ \*-------------------------------------------------------------------------*/
@ -100,39 +108,47 @@ int sock_accept(p_sock ps, p_sock pa, SA *addr, socklen_t *addr_len,
/*-------------------------------------------------------------------------*\ /*-------------------------------------------------------------------------*\
* Send with timeout * Send with timeout
* Here we exchanged the order of the calls to write and select
* The idea is that the outer loop (whoever is calling sock_send)
* will call the function again if we didn't time out, so we can
* call write and then select only if it fails.
* Should speed things up!
* We are also treating EINTR and EPIPE errors.
\*-------------------------------------------------------------------------*/ \*-------------------------------------------------------------------------*/
int sock_send(p_sock ps, const char *data, size_t count, size_t *sent, int sock_send(p_sock ps, const char *data, size_t count, size_t *sent,
int timeout) int timeout)
{ {
t_sock sock = *ps; t_sock sock = *ps;
struct timeval tv; ssize_t put;
fd_set fds;
ssize_t put = 0;
int err;
int ret; int ret;
/* avoid making system calls on closed sockets */
if (sock == SOCK_INVALID) return IO_CLOSED; if (sock == SOCK_INVALID) return IO_CLOSED;
tv.tv_sec = timeout / 1000; /* make sure we repeat in case the call was interrupted */
tv.tv_usec = (timeout % 1000) * 1000; do put = write(sock, data, count);
FD_ZERO(&fds); while (put <= 0 && errno == EINTR);
FD_SET(sock, &fds); /* deal with failure */
ret = select(sock+1, NULL, &fds, NULL, timeout >= 0 ? &tv : NULL); if (put <= 0) {
if (ret > 0) { /* in any case, nothing has been sent */
put = write(sock, data, count);
if (put <= 0) {
err = IO_CLOSED;
#ifdef __CYGWIN__
/* this is for CYGWIN, which is like Unix but has Win32 bugs */
if (errno == EWOULDBLOCK) err = IO_DONE;
#endif
*sent = 0;
} else {
*sent = put;
err = IO_DONE;
}
return err;
} else {
*sent = 0; *sent = 0;
return IO_TIMEOUT; /* run select to avoid busy wait */
if (errno != EPIPE) {
struct timeval tv;
fd_set fds;
tv.tv_sec = timeout / 1000;
tv.tv_usec = (timeout % 1000) * 1000;
FD_ZERO(&fds);
FD_SET(sock, &fds);
ret = select(sock+1, NULL, &fds, NULL, timeout >= 0 ? &tv : NULL);
/* tell the caller to call us again because there is more data */
if (ret > 0) return IO_DONE;
/* tell the caller there was no data before timeout */
else return IO_TIMEOUT;
/* here we know the connection has been closed */
} else return IO_CLOSED;
/* here we sent successfully sent something */
} else {
*sent = put;
return IO_DONE;
} }
} }
@ -176,32 +192,36 @@ int sock_sendto(p_sock ps, const char *data, size_t count, size_t *sent,
/*-------------------------------------------------------------------------*\ /*-------------------------------------------------------------------------*\
* Receive with timeout * Receive with timeout
* Here we exchanged the order of the calls to write and select
* The idea is that the outer loop (whoever is calling sock_send)
* will call the function again if we didn't time out, so we can
* call write and then select only if it fails.
* Should speed things up!
* We are also treating EINTR errors.
\*-------------------------------------------------------------------------*/ \*-------------------------------------------------------------------------*/
int sock_recv(p_sock ps, char *data, size_t count, size_t *got, int timeout) int sock_recv(p_sock ps, char *data, size_t count, size_t *got, int timeout)
{ {
t_sock sock = *ps; t_sock sock = *ps;
struct timeval tv; ssize_t taken;
fd_set fds;
int ret;
ssize_t taken = 0;
if (sock == SOCK_INVALID) return IO_CLOSED; if (sock == SOCK_INVALID) return IO_CLOSED;
tv.tv_sec = timeout / 1000; do taken = read(sock, data, count);
tv.tv_usec = (timeout % 1000) * 1000; while (taken <= 0 && errno == EINTR);
FD_ZERO(&fds); if (taken <= 0) {
FD_SET(sock, &fds); struct timeval tv;
ret = select(sock+1, &fds, NULL, NULL, timeout >= 0 ? &tv : NULL); fd_set fds;
if (ret > 0) { int ret;
taken = read(sock, data, count);
if (taken <= 0) {
*got = 0;
return IO_CLOSED;
} else {
*got = taken;
return IO_DONE;
}
} else {
*got = 0; *got = 0;
return IO_TIMEOUT; if (taken == 0) return IO_CLOSED;
tv.tv_sec = timeout / 1000;
tv.tv_usec = (timeout % 1000) * 1000;
FD_ZERO(&fds);
FD_SET(sock, &fds);
ret = select(sock+1, &fds, NULL, NULL, timeout >= 0 ? &tv : NULL);
if (ret > 0) return IO_DONE;
else return IO_TIMEOUT;
} else {
*got = taken;
return IO_DONE;
} }
} }

View File

@ -35,6 +35,14 @@ void sock_destroy(p_sock ps)
} }
} }
/*-------------------------------------------------------------------------*\
*
\*-------------------------------------------------------------------------*/
void sock_shutdown(p_sock ps, int how)
{
shutdown(*ps, how);
}
/*-------------------------------------------------------------------------*\ /*-------------------------------------------------------------------------*\
* Creates and sets up a socket * Creates and sets up a socket
\*-------------------------------------------------------------------------*/ \*-------------------------------------------------------------------------*/

View File

@ -1,4 +1,4 @@
AuthName "Test Realm" AuthName "Test Realm"
AuthType Basic AuthType Basic
AuthUserFile /home/diego/tec/luasocket/test/auth/.htpasswd AuthUserFile /Users/diego/tec/luasocket/test/auth/.htpasswd
require valid-user require valid-user

View File

@ -1,8 +1,8 @@
-- needs Alias from /home/c/diego/tec/luasocket/test to -- needs Alias from /home/c/diego/tec/luasocket/test to
-- /luasocket-test -- "/luasocket-test" and "/luasocket-test/"
-- needs ScriptAlias from /home/c/diego/tec/luasocket/test/cgi -- needs ScriptAlias from /home/c/diego/tec/luasocket/test/cgi
-- to /luasocket-test-cgi -- to "/luasocket-test-cgi" and "/luasocket-test-cgi/"
-- needs AllowOverride AuthConfig on /home/c/diego/tec/luasocket/test/auth -- needs "AllowOverride AuthConfig" on /home/c/diego/tec/luasocket/test/auth
local similar = function(s1, s2) local similar = function(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", ""))
@ -31,12 +31,18 @@ local check_request = function(request, expect, ignore)
local response = socket.http.request(request) local response = socket.http.request(request)
for i,v in response do for i,v in response do
if not ignore[i] then if not ignore[i] then
if v ~= expect[i] then fail(i .. " differs!") end if v ~= expect[i] then
if string.len(v) < 80 then print(v) end
fail(i .. " differs!")
end
end end
end end
for i,v in expect do for i,v in expect do
if not ignore[i] then if not ignore[i] then
if v ~= response[i] then fail(i .. " differs!") end if v ~= response[i] then
if string.len(v) < 80 then print(v) end
fail(i .. " differs!")
end
end end
end end
print("ok") print("ok")
@ -47,15 +53,18 @@ local host, request, response, ignore, expect, index, prefix, cgiprefix
local t = socket.time() local t = socket.time()
host = host or "localhost" host = host or "localhost"
prefix = prefix or "/luasocket" prefix = prefix or "/luasocket-test"
cgiprefix = cgiprefix or "/luasocket/cgi" cgiprefix = cgiprefix or "/luasocket-test-cgi"
index = readfile("test/index.html") index = readfile("test/index.html")
io.write("testing request uri correctness: ") io.write("testing request uri correctness: ")
local forth = cgiprefix .. "/request-uri?" .. "this+is+the+query+string" local forth = cgiprefix .. "/request-uri?" .. "this+is+the+query+string"
local back = socket.http.get("http://" .. host .. forth) local back, h, c, e = socket.http.get("http://" .. host .. forth)
if similar(back, forth) then print("ok") if similar(back, forth) then print("ok")
else fail("failed!") end else
print(h, c, e)
fail()
end
io.write("testing query string correctness: ") io.write("testing query string correctness: ")
forth = "this+is+the+query+string" forth = "this+is+the+query+string"
@ -77,6 +86,38 @@ ignore = {
} }
check_request(request, expect, ignore) check_request(request, expect, ignore)
socket.http.get("http://" .. host .. prefix .. "/lixo.html")
io.write("testing post method: ")
-- wanted to test chunked post, but apache doesn't support it...
request = {
url = "http://" .. host .. cgiprefix .. "/cat",
method = "POST",
body = index,
-- remove content-length header to send chunked body
headers = { ["content-length"] = string.len(index) }
}
expect = {
body = index,
code = 200
}
ignore = {
status = 1,
headers = 1
}
check_request(request, expect, ignore)
io.write("testing simple post function: ")
body = socket.http.post("http://" .. host .. cgiprefix .. "/cat", index)
check(body == index)
io.write("testing simple post function with table args: ")
body = socket.http.post {
url = "http://" .. host .. cgiprefix .. "/cat",
body = index
}
check(body == index)
io.write("testing http redirection: ") io.write("testing http redirection: ")
request = { request = {
url = "http://" .. host .. prefix url = "http://" .. host .. prefix
@ -175,7 +216,8 @@ 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 " .. socket.code.base64("luasocket:password") authorization = "Basic " ..
socket.code.base64.encode("luasocket:password")
} }
} }
expect = { expect = {
@ -246,22 +288,6 @@ ignore = {
} }
check_request(request, expect, ignore) check_request(request, expect, ignore)
io.write("testing post method: ")
request = {
url = "http://" .. host .. cgiprefix .. "/cat",
method = "POST",
body = index
}
expect = {
body = index,
code = 200
}
ignore = {
status = 1,
headers = 1
}
check_request(request, expect, ignore)
io.write("testing wrong scheme: ") io.write("testing wrong scheme: ")
request = { request = {
url = "wrong://" .. host .. cgiprefix .. "/cat", url = "wrong://" .. host .. cgiprefix .. "/cat",
@ -287,17 +313,6 @@ body = socket.http.get {
} }
check(body == index) check(body == index)
io.write("testing simple post function: ")
body = socket.http.post("http://" .. host .. cgiprefix .. "/cat", index)
check(body == index)
io.write("testing simple post function with table args: ")
body = socket.http.post {
url = "http://" .. host .. cgiprefix .. "/cat",
body = index
}
check(body == index)
io.write("testing HEAD method: ") io.write("testing HEAD method: ")
response = socket.http.request { response = socket.http.request {
method = "HEAD", method = "HEAD",

View File

@ -84,19 +84,19 @@ function reconnect()
remote [[ remote [[
if data then data:close() data = nil end if data then data:close() data = nil end
data = server:accept() data = server:accept()
-- data:setoption("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)
else pass("connected!") end else pass("connected!") end
-- data:setoption("nodelay", true) data:setoption("tcp-nodelay", true)
end end
pass("attempting control connection...") pass("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
-- control:setoption("nodelay", true) control:setoption("tcp-nodelay", true)
------------------------------------------------------------------------ ------------------------------------------------------------------------
test("method registration") test("method registration")