mirror of
https://github.com/lunarmodules/luasocket.git
synced 2024-12-25 12:08:21 +01:00
Added check-links-nb.lua that check links in a non-blocking way.
This commit is contained in:
parent
ae4ba2aa98
commit
37f7af4b9f
1
FIX
1
FIX
@ -14,3 +14,4 @@ fixed a bug in select.c that prevented sockets with descriptor 0 from working (R
|
||||
fixed a "bug" that caused dns.toip to crash under uLinux
|
||||
fixed a "bug" that caused a crash in gethostbyname under VMS
|
||||
DEBUG and VERSION became _DEBUG and _VERSION
|
||||
send returns the right value if input is "". Alexander Marinov
|
||||
|
47
config
Normal file
47
config
Normal file
@ -0,0 +1,47 @@
|
||||
#------
|
||||
# LuaSocket makefile configuration
|
||||
#
|
||||
|
||||
#------
|
||||
# Output file names
|
||||
#
|
||||
EXT=so
|
||||
SOCKET_V=2.0.0
|
||||
MIME_V=1.0.0
|
||||
SOCKET_SO=socket-core.$(EXT).$(SOCKET_V)
|
||||
MIME_SO=mime-core.$(EXT).$(MIME_V)
|
||||
UNIX_SO=unix.$(EXT)
|
||||
|
||||
#------
|
||||
# Lua includes and libraries
|
||||
#
|
||||
LUAINC=
|
||||
LUALIB=
|
||||
|
||||
#------
|
||||
# Compat-5.1 directory
|
||||
#
|
||||
COMPAT=compat-5.1r3
|
||||
|
||||
#------
|
||||
# Top of your Lua installation
|
||||
# Relative paths will be inside src tree
|
||||
#
|
||||
INSTALL_TOP=/usr/local/share/lua/5.0
|
||||
|
||||
INSTALL_DATA=cp
|
||||
INSTALL_EXEC=cp
|
||||
INSTALL_LINK=ln
|
||||
|
||||
#------
|
||||
# Compiler and linker settings
|
||||
#
|
||||
CC=gcc
|
||||
DEF=-DLUASOCKET_DEBUG -DUNIX_HAS_SUN_LEN
|
||||
CFLAGS= $(LUAINC) -I$(COMPAT) $(DEF) -pedantic -Wall -O2
|
||||
LDFLAGS=-bundle -undefined dynamic_lookup
|
||||
LD=export MACOSX_DEPLOYMENT_TARGET="10.3"; gcc
|
||||
|
||||
#------
|
||||
# End of makefile configuration
|
||||
#
|
@ -165,32 +165,35 @@ support.
|
||||
</p>
|
||||
|
||||
<ul>
|
||||
<li> Improved: <tt>tcp{client}:send(data, i)</tt> now returns <tt>(i+sent-1)</tt>. This is great for non-blocking I/O, but might break some code;
|
||||
<li> Improved: HTTP, SMTP, and FTP functions accept a new field
|
||||
<tt>connect</tt> that can be used to replace the function invoked to
|
||||
create and connect the sockets used internally;
|
||||
<li> Fixed: <tt>url.absolute()</tt> was not working when <tt>base_url</tt> was
|
||||
already parsed;
|
||||
<li> Fixed: <tt>http.request()</tt> was redirecting even when the location
|
||||
header was empty (well, it shouldn't be empty);
|
||||
<li> Fixed: <tt>tcp{client}:shutdown()</tt> was checking for group instead of class;
|
||||
<li> Fixed: <tt>socket.try()</tt> can't be used in place of <tt>assert()</tt>. The manual and examples don't do it anymore;
|
||||
<li> Improved: Get rid of <tt>require("base")</tt> kludge in <tt>package.loaded</tt>;
|
||||
<li> Fixedd: Parts of the manual referred to <tt>require("http")</tt> instead of
|
||||
<li> Improved: <tt>tcp:send(data, i, j)</tt> to return <tt>(i+sent-1)</tt>. This is great for non-blocking I/O, but might break some code;
|
||||
<li> Improved: HTTP, SMTP, and FTP functions to accept a new field
|
||||
<tt>create</tt> that overrides the function used to create socket objects;
|
||||
<li> Fixed: <tt>url.absolute()</tt> to work when <tt>base_url</tt> is in
|
||||
parsed form;
|
||||
<li> Fixed: <tt>http.request()</tt> not to redirect when the location
|
||||
header is empty (naughty servers...);
|
||||
<li> Fixed: <tt>tcp{client}:shutdown()</tt> to check for class instead of
|
||||
group;
|
||||
<li> Fixed: The manual to stop using <tt>socket.try()</tt> in place of
|
||||
<tt>assert()</tt>, since it can't;
|
||||
<li> Improved: Got rid of <tt>package.loaded.base = _G</tt> kludge;
|
||||
<li> Fixed: Parts of the manual referred to <tt>require("http")</tt> instead of
|
||||
<tt>require("socket.http")</tt>;
|
||||
<li> Improved: Changed 'l' prefix in C libraries to 'c' to avoid clash with LHF
|
||||
libraries;
|
||||
<li> Improved: Using bundles in Mac OS X;
|
||||
<li> Fixed: <tt>luasocket.h</tt> was exporting <tt>luaopen_socket</tt>
|
||||
instead of <tt>luaopen_csocket</tt>;
|
||||
<li> Fixed: <tt>udp:setpeername()</tt> only worked for
|
||||
<tt>udp{unconnected}</tt>. Now you can "disconnect" an <tt>UDP</tt> socket;
|
||||
<li> Fixed: bug in http.lua that caused some requests to fail (Florian
|
||||
Berger);
|
||||
<li> Fixed: bug in <tt>select.c</tt> that prevented sockets with descriptor 0 from working (Renato Maia);
|
||||
<li> Fixed: "bug" that caused <tt>dns.toip</tt> to crash under uLinux;
|
||||
<li> Fixed: "bug" that caused a crash in <tt>gethostbyname</tt> under VMS
|
||||
<li> Improved: Socket and MIME binaries are called 'core' each inside their
|
||||
directory (ex. "socket/core.dll"). The 'l' prefix was just a bad idea;
|
||||
<li> Improved: Using bundles in Mac OS X, instead of dylibs;
|
||||
<li> Fixed: <tt>luasocket.h</tt> to export <tt>luaopen_socketcore</tt>;
|
||||
<li> Fixed: <tt>udp:setpeername()</tt> so you can "disconnect" an
|
||||
<tt>UDP</tt> socket;
|
||||
<li> Fixed: A weird bug in HTTP support that caused some requests to
|
||||
fail (Florian Berger);
|
||||
<li> Fixed: Bug in <tt>socket.select()</tt> that caused sockets
|
||||
with descriptor 0 to be ignored (Renato Maia);
|
||||
<li> Fixed: "Bug" that caused <tt>dns.toip()</tt> to crash under uLinux
|
||||
(William Trenker);
|
||||
<li> Fixed: "Bug" that caused <tt>gethostbyname</tt> to crash under VMS
|
||||
(Renato Maia);
|
||||
<li> Fixed: <tt>tcp:send("")</tt> to return 0 bytes sent (Alexander Marinov);
|
||||
<li> Improved: <tt>socket.DEBUG</tt> and <tt>socket.VERSION</tt> became <tt>socket._DEBUGs</tt> and <tt>socket._VERSION</tt> for uniformity with other libraries.
|
||||
</ul>
|
||||
|
||||
|
262
etc/check-links-nb.lua
Normal file
262
etc/check-links-nb.lua
Normal file
@ -0,0 +1,262 @@
|
||||
-----------------------------------------------------------------------------
|
||||
-- Little program that checks links in HTML files, using coroutines and
|
||||
-- non-blocking I/O. Thus, faster than simpler version of same program
|
||||
-- LuaSocket sample files
|
||||
-- Author: Diego Nehab
|
||||
-- RCS ID: $$
|
||||
-----------------------------------------------------------------------------
|
||||
local socket = require("socket")
|
||||
|
||||
TIMEOUT = 10
|
||||
|
||||
-- we need to yield across calls to protect, so we can't use pcall
|
||||
-- we borrow and simplify code from coxpcall to reimplement socket.protect
|
||||
-- before loading http
|
||||
function socket.protect(f)
|
||||
return function(...)
|
||||
local co = coroutine.create(f)
|
||||
while true do
|
||||
local results = {coroutine.resume(co, unpack(arg))}
|
||||
local status = results[1]
|
||||
table.remove(results, 1)
|
||||
if not status then
|
||||
return nil, results[1][1]
|
||||
end
|
||||
if coroutine.status(co) == "suspended" then
|
||||
arg = {coroutine.yield(unpack(results))}
|
||||
else
|
||||
return unpack(results)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local http = require("socket.http")
|
||||
local url = require("socket.url")
|
||||
|
||||
-- creates a new set data structure
|
||||
function newset()
|
||||
local reverse = {}
|
||||
local set = {}
|
||||
return setmetatable(set, {__index = {
|
||||
insert = function(set, value)
|
||||
if not reverse[value] then
|
||||
table.insert(set, value)
|
||||
reverse[value] = table.getn(set)
|
||||
end
|
||||
end,
|
||||
remove = function(set, value)
|
||||
local index = reverse[value]
|
||||
if index then
|
||||
reverse[value] = nil
|
||||
local top = table.remove(set)
|
||||
if top ~= value then
|
||||
reverse[top] = index
|
||||
set[index] = top
|
||||
end
|
||||
end
|
||||
end
|
||||
}})
|
||||
end
|
||||
|
||||
local context = {}
|
||||
local sending = newset()
|
||||
local receiving = newset()
|
||||
local nthreads = 0
|
||||
|
||||
-- socket.tcp() replacement for non-blocking I/O
|
||||
-- implements enough functionality to be used with http.request
|
||||
-- in Lua 5.1, we have coroutine.running to simplify things...
|
||||
function newcreate(thread)
|
||||
return function()
|
||||
-- try to create underlying socket
|
||||
local tcp, error = socket.tcp()
|
||||
if not tcp then return nil, error end
|
||||
-- put it in non-blocking mode right away
|
||||
tcp:settimeout(0)
|
||||
local trap = {
|
||||
-- we ignore settimeout to preserve our 0 timeout
|
||||
settimeout = function(self, mode, value)
|
||||
return 1
|
||||
end,
|
||||
-- send in non-blocking mode and yield on timeout
|
||||
send = function(self, data, first, last)
|
||||
first = (first or 1) - 1
|
||||
local result, error
|
||||
while true do
|
||||
result, error, first = tcp:send(data, first+1, last)
|
||||
if error == "timeout" then
|
||||
-- tell dispatcher we want to keep sending
|
||||
sending:insert(tcp)
|
||||
-- mark time we started waiting
|
||||
context[tcp].last = socket.gettime()
|
||||
-- return control to dispatcher
|
||||
if coroutine.yield() == "timeout" then
|
||||
return nil, "timeout"
|
||||
end
|
||||
else return result, error, first end
|
||||
end
|
||||
end,
|
||||
-- receive in non-blocking mode and yield on timeout
|
||||
receive = function(self, pattern)
|
||||
local error, partial = "timeout", ""
|
||||
local value
|
||||
while true do
|
||||
value, error, partial = tcp:receive(pattern, partial)
|
||||
if error == "timeout" then
|
||||
-- tell dispatcher we want to keep receiving
|
||||
receiving:insert(tcp)
|
||||
-- mark time we started waiting
|
||||
context[tcp].last = socket.gettime()
|
||||
-- return control to dispatcher
|
||||
if coroutine.yield() == "timeout" then
|
||||
return nil, "timeout"
|
||||
end
|
||||
else return value, error, partial end
|
||||
end
|
||||
end,
|
||||
-- connect in non-blocking mode and yield on timeout
|
||||
connect = function(self, host, port)
|
||||
local result, error = tcp:connect(host, port)
|
||||
if error == "timeout" then
|
||||
-- tell dispatcher we will be able to write uppon connection
|
||||
sending:insert(tcp)
|
||||
-- mark time we started waiting
|
||||
context[tcp].last = socket.gettime()
|
||||
-- return control to dispatcher
|
||||
if coroutine.yield() == "timeout" then
|
||||
return nil, "timeout"
|
||||
end
|
||||
-- when we come back, check if connection was successful
|
||||
result, error = tcp:connect(host, port)
|
||||
if result or error == "already connected" then return 1
|
||||
else return nil, "non-blocking connect failed" end
|
||||
else return result, error end
|
||||
end,
|
||||
close = function(self)
|
||||
context[tcp] = nil
|
||||
return tcp:close()
|
||||
end
|
||||
}
|
||||
-- add newly created socket to context
|
||||
context[tcp] = {
|
||||
thread = thread,
|
||||
trap = trap
|
||||
}
|
||||
return trap
|
||||
end
|
||||
end
|
||||
|
||||
-- get the status of a URL, non-blocking
|
||||
function getstatus(from, link)
|
||||
local parsed = url.parse(link, {scheme = "file"})
|
||||
if parsed.scheme == "http" then
|
||||
local thread = coroutine.create(function(thread, from, link)
|
||||
local r, c, h, s = http.request{
|
||||
method = "HEAD",
|
||||
url = link,
|
||||
create = newcreate(thread)
|
||||
}
|
||||
if c == 200 then io.write('\t', link, '\n')
|
||||
else io.write('\t', link, ': ', c, '\n') end
|
||||
nthreads = nthreads - 1
|
||||
end)
|
||||
nthreads = nthreads + 1
|
||||
assert(coroutine.resume(thread, thread, from, link))
|
||||
end
|
||||
end
|
||||
|
||||
-- dispatch all threads until we are done
|
||||
function dispatch()
|
||||
while nthreads > 0 do
|
||||
-- check which sockets are interesting and act on them
|
||||
local readable, writable = socket.select(receiving, sending, 1)
|
||||
-- for all readable connections, resume their threads
|
||||
for _, who in ipairs(readable) do
|
||||
if context[who] then
|
||||
receiving:remove(who)
|
||||
assert(coroutine.resume(context[who].thread))
|
||||
end
|
||||
end
|
||||
-- for all writable connections, do the same
|
||||
for _, who in ipairs(writable) do
|
||||
if context[who] then
|
||||
sending:remove(who)
|
||||
assert(coroutine.resume(context[who].thread))
|
||||
end
|
||||
end
|
||||
-- politely ask replacement I/O functions in idle threads to
|
||||
-- return reporting a timeout
|
||||
local now = socket.gettime()
|
||||
for who, data in pairs(context) do
|
||||
if data.last and now - data.last > TIMEOUT then
|
||||
assert(coroutine.resume(context[who].thread, "timeout"))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function readfile(path)
|
||||
path = url.unescape(path)
|
||||
local file, error = io.open(path, "r")
|
||||
if file then
|
||||
local body = file:read("*a")
|
||||
file:close()
|
||||
return body
|
||||
else return nil, error end
|
||||
end
|
||||
|
||||
function retrieve(u)
|
||||
local parsed = url.parse(u, { scheme = "file" })
|
||||
local body, headers, code, error
|
||||
local base = u
|
||||
if parsed.scheme == "http" then
|
||||
body, code, headers = http.request(u)
|
||||
if code == 200 then
|
||||
base = base or headers.location
|
||||
end
|
||||
if not body then
|
||||
error = code
|
||||
end
|
||||
elseif parsed.scheme == "file" then
|
||||
body, error = readfile(parsed.path)
|
||||
else error = string.format("unhandled scheme '%s'", parsed.scheme) end
|
||||
return base, body, error
|
||||
end
|
||||
|
||||
function getlinks(body, base)
|
||||
-- get rid of comments
|
||||
body = string.gsub(body, "%<%!%-%-.-%-%-%>", "")
|
||||
local links = {}
|
||||
-- extract links
|
||||
body = string.gsub(body, '[Hh][Rr][Ee][Ff]%s*=%s*"([^"]*)"', function(href)
|
||||
table.insert(links, url.absolute(base, href))
|
||||
end)
|
||||
body = string.gsub(body, "[Hh][Rr][Ee][Ff]%s*=%s*'([^']*)'", function(href)
|
||||
table.insert(links, url.absolute(base, href))
|
||||
end)
|
||||
string.gsub(body, "[Hh][Rr][Ee][Ff]%s*=%s*(.-)>", function(href)
|
||||
table.insert(links, url.absolute(base, href))
|
||||
end)
|
||||
return links
|
||||
end
|
||||
|
||||
function checklinks(from)
|
||||
local base, body, error = retrieve(from)
|
||||
if not body then print(error) return end
|
||||
local links = getlinks(body, base)
|
||||
for _, link in ipairs(links) do
|
||||
getstatus(from, link)
|
||||
end
|
||||
end
|
||||
|
||||
arg = arg or {}
|
||||
if table.getn(arg) < 1 then
|
||||
print("Usage:\n luasocket check-links.lua {<url>}")
|
||||
exit()
|
||||
end
|
||||
for _, a in ipairs(arg) do
|
||||
print("Checking ", a)
|
||||
checklinks(url.absolute("file:", a))
|
||||
end
|
||||
dispatch()
|
@ -2,7 +2,7 @@
|
||||
local socket = require"socket"
|
||||
|
||||
-- creates a new set data structure
|
||||
function newset(a)
|
||||
function newset()
|
||||
local reverse = {}
|
||||
local set = {}
|
||||
return setmetatable(set, {__index = {
|
||||
|
@ -122,7 +122,8 @@ int buf_meth_receive(lua_State *L, p_buf buf) {
|
||||
if (p[0] == '*' && p[1] == 'l') err = recvline(buf, &b);
|
||||
else if (p[0] == '*' && p[1] == 'a') err = recvall(buf, &b);
|
||||
else luaL_argcheck(L, 0, 2, "invalid receive pattern");
|
||||
/* get a fixed number of bytes */
|
||||
/* get a fixed number of bytes (minus what was already partially
|
||||
* received) */
|
||||
} else err = recvraw(buf, (size_t) lua_tonumber(L, 2)-size, &b);
|
||||
/* check if there was an error */
|
||||
if (err != IO_DONE) {
|
||||
|
@ -26,7 +26,7 @@ host = host or "localhost" -- "diego.student.princeton.edu"
|
||||
proxy = proxy or "http://localhost:3128"
|
||||
prefix = prefix or "/luasocket-test"
|
||||
cgiprefix = cgiprefix or "/luasocket-test-cgi"
|
||||
index_file = "test/index.html"
|
||||
index_file = "index.html"
|
||||
|
||||
-- read index with CRLF convention
|
||||
index = readfile(index_file)
|
||||
|
Loading…
Reference in New Issue
Block a user