aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDiego Nehab <diego@tecgraf.puc-rio.br>2005-06-20 04:51:55 +0000
committerDiego Nehab <diego@tecgraf.puc-rio.br>2005-06-20 04:51:55 +0000
commit37f7af4b9f1250e3c3439df03d43cf291a4d6f37 (patch)
tree33f7d8fbbc2860378059810c5372aadc140f1305
parentae4ba2aa98af4e6d035850fcbc5985cea94f25fa (diff)
downloadluasocket-37f7af4b9f1250e3c3439df03d43cf291a4d6f37.tar.gz
luasocket-37f7af4b9f1250e3c3439df03d43cf291a4d6f37.tar.bz2
luasocket-37f7af4b9f1250e3c3439df03d43cf291a4d6f37.zip
Added check-links-nb.lua that check links in a non-blocking way.
-rw-r--r--FIX1
-rw-r--r--config47
-rw-r--r--doc/index.html51
-rw-r--r--etc/check-links-nb.lua262
-rw-r--r--samples/forward.lua2
-rw-r--r--src/buffer.c3
-rw-r--r--test/httptest.lua2
7 files changed, 341 insertions, 27 deletions
diff --git a/FIX b/FIX
index 0ec76a1..3d0b3de 100644
--- a/FIX
+++ b/FIX
@@ -14,3 +14,4 @@ fixed a bug in select.c that prevented sockets with descriptor 0 from working (R
14fixed a "bug" that caused dns.toip to crash under uLinux 14fixed a "bug" that caused dns.toip to crash under uLinux
15fixed a "bug" that caused a crash in gethostbyname under VMS 15fixed a "bug" that caused a crash in gethostbyname under VMS
16DEBUG and VERSION became _DEBUG and _VERSION 16DEBUG and VERSION became _DEBUG and _VERSION
17send returns the right value if input is "". Alexander Marinov
diff --git a/config b/config
new file mode 100644
index 0000000..dcc3955
--- /dev/null
+++ b/config
@@ -0,0 +1,47 @@
1#------
2# LuaSocket makefile configuration
3#
4
5#------
6# Output file names
7#
8EXT=so
9SOCKET_V=2.0.0
10MIME_V=1.0.0
11SOCKET_SO=socket-core.$(EXT).$(SOCKET_V)
12MIME_SO=mime-core.$(EXT).$(MIME_V)
13UNIX_SO=unix.$(EXT)
14
15#------
16# Lua includes and libraries
17#
18LUAINC=
19LUALIB=
20
21#------
22# Compat-5.1 directory
23#
24COMPAT=compat-5.1r3
25
26#------
27# Top of your Lua installation
28# Relative paths will be inside src tree
29#
30INSTALL_TOP=/usr/local/share/lua/5.0
31
32INSTALL_DATA=cp
33INSTALL_EXEC=cp
34INSTALL_LINK=ln
35
36#------
37# Compiler and linker settings
38#
39CC=gcc
40DEF=-DLUASOCKET_DEBUG -DUNIX_HAS_SUN_LEN
41CFLAGS= $(LUAINC) -I$(COMPAT) $(DEF) -pedantic -Wall -O2
42LDFLAGS=-bundle -undefined dynamic_lookup
43LD=export MACOSX_DEPLOYMENT_TARGET="10.3"; gcc
44
45#------
46# End of makefile configuration
47#
diff --git a/doc/index.html b/doc/index.html
index ee97e02..933fa5f 100644
--- a/doc/index.html
+++ b/doc/index.html
@@ -165,32 +165,35 @@ support.
165</p> 165</p>
166 166
167<ul> 167<ul>
168<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; 168<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;
169<li> Improved: HTTP, SMTP, and FTP functions accept a new field 169<li> Improved: HTTP, SMTP, and FTP functions to accept a new field
170<tt>connect</tt> that can be used to replace the function invoked to 170<tt>create</tt> that overrides the function used to create socket objects;
171create and connect the sockets used internally; 171<li> Fixed: <tt>url.absolute()</tt> to work when <tt>base_url</tt> is in
172<li> Fixed: <tt>url.absolute()</tt> was not working when <tt>base_url</tt> was 172parsed form;
173already parsed; 173<li> Fixed: <tt>http.request()</tt> not to redirect when the location
174<li> Fixed: <tt>http.request()</tt> was redirecting even when the location 174header is empty (naughty servers...);
175header was empty (well, it shouldn't be empty); 175<li> Fixed: <tt>tcp{client}:shutdown()</tt> to check for class instead of
176<li> Fixed: <tt>tcp{client}:shutdown()</tt> was checking for group instead of class; 176group;
177<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; 177<li> Fixed: The manual to stop using <tt>socket.try()</tt> in place of
178<li> Improved: Get rid of <tt>require("base")</tt> kludge in <tt>package.loaded</tt>; 178<tt>assert()</tt>, since it can't;
179<li> Fixedd: Parts of the manual referred to <tt>require("http")</tt> instead of 179<li> Improved: Got rid of <tt>package.loaded.base = _G</tt> kludge;
180<li> Fixed: Parts of the manual referred to <tt>require("http")</tt> instead of
180<tt>require("socket.http")</tt>; 181<tt>require("socket.http")</tt>;
181<li> Improved: Changed 'l' prefix in C libraries to 'c' to avoid clash with LHF 182<li> Improved: Socket and MIME binaries are called 'core' each inside their
182libraries; 183directory (ex. "socket/core.dll"). The 'l' prefix was just a bad idea;
183<li> Improved: Using bundles in Mac OS X; 184<li> Improved: Using bundles in Mac OS X, instead of dylibs;
184<li> Fixed: <tt>luasocket.h</tt> was exporting <tt>luaopen_socket</tt> 185<li> Fixed: <tt>luasocket.h</tt> to export <tt>luaopen_socketcore</tt>;
185instead of <tt>luaopen_csocket</tt>; 186<li> Fixed: <tt>udp:setpeername()</tt> so you can "disconnect" an
186<li> Fixed: <tt>udp:setpeername()</tt> only worked for 187<tt>UDP</tt> socket;
187<tt>udp{unconnected}</tt>. Now you can "disconnect" an <tt>UDP</tt> socket; 188<li> Fixed: A weird bug in HTTP support that caused some requests to
188<li> Fixed: bug in http.lua that caused some requests to fail (Florian 189fail (Florian Berger);
189Berger); 190<li> Fixed: Bug in <tt>socket.select()</tt> that caused sockets
190<li> Fixed: bug in <tt>select.c</tt> that prevented sockets with descriptor 0 from working (Renato Maia); 191with descriptor 0 to be ignored (Renato Maia);
191<li> Fixed: "bug" that caused <tt>dns.toip</tt> to crash under uLinux; 192<li> Fixed: "Bug" that caused <tt>dns.toip()</tt> to crash under uLinux
192<li> Fixed: "bug" that caused a crash in <tt>gethostbyname</tt> under VMS 193(William Trenker);
194<li> Fixed: "Bug" that caused <tt>gethostbyname</tt> to crash under VMS
193(Renato Maia); 195(Renato Maia);
196<li> Fixed: <tt>tcp:send("")</tt> to return 0 bytes sent (Alexander Marinov);
194<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. 197<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.
195</ul> 198</ul>
196 199
diff --git a/etc/check-links-nb.lua b/etc/check-links-nb.lua
new file mode 100644
index 0000000..7e8df1b
--- /dev/null
+++ b/etc/check-links-nb.lua
@@ -0,0 +1,262 @@
1-----------------------------------------------------------------------------
2-- Little program that checks links in HTML files, using coroutines and
3-- non-blocking I/O. Thus, faster than simpler version of same program
4-- LuaSocket sample files
5-- Author: Diego Nehab
6-- RCS ID: $$
7-----------------------------------------------------------------------------
8local socket = require("socket")
9
10TIMEOUT = 10
11
12-- we need to yield across calls to protect, so we can't use pcall
13-- we borrow and simplify code from coxpcall to reimplement socket.protect
14-- before loading http
15function socket.protect(f)
16 return function(...)
17 local co = coroutine.create(f)
18 while true do
19 local results = {coroutine.resume(co, unpack(arg))}
20 local status = results[1]
21 table.remove(results, 1)
22 if not status then
23 return nil, results[1][1]
24 end
25 if coroutine.status(co) == "suspended" then
26 arg = {coroutine.yield(unpack(results))}
27 else
28 return unpack(results)
29 end
30 end
31 end
32end
33
34local http = require("socket.http")
35local url = require("socket.url")
36
37-- creates a new set data structure
38function newset()
39 local reverse = {}
40 local set = {}
41 return setmetatable(set, {__index = {
42 insert = function(set, value)
43 if not reverse[value] then
44 table.insert(set, value)
45 reverse[value] = table.getn(set)
46 end
47 end,
48 remove = function(set, value)
49 local index = reverse[value]
50 if index then
51 reverse[value] = nil
52 local top = table.remove(set)
53 if top ~= value then
54 reverse[top] = index
55 set[index] = top
56 end
57 end
58 end
59 }})
60end
61
62local context = {}
63local sending = newset()
64local receiving = newset()
65local nthreads = 0
66
67-- socket.tcp() replacement for non-blocking I/O
68-- implements enough functionality to be used with http.request
69-- in Lua 5.1, we have coroutine.running to simplify things...
70function newcreate(thread)
71 return function()
72 -- try to create underlying socket
73 local tcp, error = socket.tcp()
74 if not tcp then return nil, error end
75 -- put it in non-blocking mode right away
76 tcp:settimeout(0)
77 local trap = {
78 -- we ignore settimeout to preserve our 0 timeout
79 settimeout = function(self, mode, value)
80 return 1
81 end,
82 -- send in non-blocking mode and yield on timeout
83 send = function(self, data, first, last)
84 first = (first or 1) - 1
85 local result, error
86 while true do
87 result, error, first = tcp:send(data, first+1, last)
88 if error == "timeout" then
89 -- tell dispatcher we want to keep sending
90 sending:insert(tcp)
91 -- mark time we started waiting
92 context[tcp].last = socket.gettime()
93 -- return control to dispatcher
94 if coroutine.yield() == "timeout" then
95 return nil, "timeout"
96 end
97 else return result, error, first end
98 end
99 end,
100 -- receive in non-blocking mode and yield on timeout
101 receive = function(self, pattern)
102 local error, partial = "timeout", ""
103 local value
104 while true do
105 value, error, partial = tcp:receive(pattern, partial)
106 if error == "timeout" then
107 -- tell dispatcher we want to keep receiving
108 receiving:insert(tcp)
109 -- mark time we started waiting
110 context[tcp].last = socket.gettime()
111 -- return control to dispatcher
112 if coroutine.yield() == "timeout" then
113 return nil, "timeout"
114 end
115 else return value, error, partial end
116 end
117 end,
118 -- connect in non-blocking mode and yield on timeout
119 connect = function(self, host, port)
120 local result, error = tcp:connect(host, port)
121 if error == "timeout" then
122 -- tell dispatcher we will be able to write uppon connection
123 sending:insert(tcp)
124 -- mark time we started waiting
125 context[tcp].last = socket.gettime()
126 -- return control to dispatcher
127 if coroutine.yield() == "timeout" then
128 return nil, "timeout"
129 end
130 -- when we come back, check if connection was successful
131 result, error = tcp:connect(host, port)
132 if result or error == "already connected" then return 1
133 else return nil, "non-blocking connect failed" end
134 else return result, error end
135 end,
136 close = function(self)
137 context[tcp] = nil
138 return tcp:close()
139 end
140 }
141 -- add newly created socket to context
142 context[tcp] = {
143 thread = thread,
144 trap = trap
145 }
146 return trap
147 end
148end
149
150-- get the status of a URL, non-blocking
151function getstatus(from, link)
152 local parsed = url.parse(link, {scheme = "file"})
153 if parsed.scheme == "http" then
154 local thread = coroutine.create(function(thread, from, link)
155 local r, c, h, s = http.request{
156 method = "HEAD",
157 url = link,
158 create = newcreate(thread)
159 }
160 if c == 200 then io.write('\t', link, '\n')
161 else io.write('\t', link, ': ', c, '\n') end
162 nthreads = nthreads - 1
163 end)
164 nthreads = nthreads + 1
165 assert(coroutine.resume(thread, thread, from, link))
166 end
167end
168
169-- dispatch all threads until we are done
170function dispatch()
171 while nthreads > 0 do
172 -- check which sockets are interesting and act on them
173 local readable, writable = socket.select(receiving, sending, 1)
174 -- for all readable connections, resume their threads
175 for _, who in ipairs(readable) do
176 if context[who] then
177 receiving:remove(who)
178 assert(coroutine.resume(context[who].thread))
179 end
180 end
181 -- for all writable connections, do the same
182 for _, who in ipairs(writable) do
183 if context[who] then
184 sending:remove(who)
185 assert(coroutine.resume(context[who].thread))
186 end
187 end
188 -- politely ask replacement I/O functions in idle threads to
189 -- return reporting a timeout
190 local now = socket.gettime()
191 for who, data in pairs(context) do
192 if data.last and now - data.last > TIMEOUT then
193 assert(coroutine.resume(context[who].thread, "timeout"))
194 end
195 end
196 end
197end
198
199function readfile(path)
200 path = url.unescape(path)
201 local file, error = io.open(path, "r")
202 if file then
203 local body = file:read("*a")
204 file:close()
205 return body
206 else return nil, error end
207end
208
209function retrieve(u)
210 local parsed = url.parse(u, { scheme = "file" })
211 local body, headers, code, error
212 local base = u
213 if parsed.scheme == "http" then
214 body, code, headers = http.request(u)
215 if code == 200 then
216 base = base or headers.location
217 end
218 if not body then
219 error = code
220 end
221 elseif parsed.scheme == "file" then
222 body, error = readfile(parsed.path)
223 else error = string.format("unhandled scheme '%s'", parsed.scheme) end
224 return base, body, error
225end
226
227function getlinks(body, base)
228 -- get rid of comments
229 body = string.gsub(body, "%<%!%-%-.-%-%-%>", "")
230 local links = {}
231 -- extract links
232 body = string.gsub(body, '[Hh][Rr][Ee][Ff]%s*=%s*"([^"]*)"', function(href)
233 table.insert(links, url.absolute(base, href))
234 end)
235 body = string.gsub(body, "[Hh][Rr][Ee][Ff]%s*=%s*'([^']*)'", function(href)
236 table.insert(links, url.absolute(base, href))
237 end)
238 string.gsub(body, "[Hh][Rr][Ee][Ff]%s*=%s*(.-)>", function(href)
239 table.insert(links, url.absolute(base, href))
240 end)
241 return links
242end
243
244function checklinks(from)
245 local base, body, error = retrieve(from)
246 if not body then print(error) return end
247 local links = getlinks(body, base)
248 for _, link in ipairs(links) do
249 getstatus(from, link)
250 end
251end
252
253arg = arg or {}
254if table.getn(arg) < 1 then
255 print("Usage:\n luasocket check-links.lua {<url>}")
256 exit()
257end
258for _, a in ipairs(arg) do
259 print("Checking ", a)
260 checklinks(url.absolute("file:", a))
261end
262dispatch()
diff --git a/samples/forward.lua b/samples/forward.lua
index a53ab5d..548a753 100644
--- a/samples/forward.lua
+++ b/samples/forward.lua
@@ -2,7 +2,7 @@
2local socket = require"socket" 2local socket = require"socket"
3 3
4-- creates a new set data structure 4-- creates a new set data structure
5function newset(a) 5function newset()
6 local reverse = {} 6 local reverse = {}
7 local set = {} 7 local set = {}
8 return setmetatable(set, {__index = { 8 return setmetatable(set, {__index = {
diff --git a/src/buffer.c b/src/buffer.c
index 62211d8..1188fda 100644
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -122,7 +122,8 @@ int buf_meth_receive(lua_State *L, p_buf buf) {
122 if (p[0] == '*' && p[1] == 'l') err = recvline(buf, &b); 122 if (p[0] == '*' && p[1] == 'l') err = recvline(buf, &b);
123 else if (p[0] == '*' && p[1] == 'a') err = recvall(buf, &b); 123 else if (p[0] == '*' && p[1] == 'a') err = recvall(buf, &b);
124 else luaL_argcheck(L, 0, 2, "invalid receive pattern"); 124 else luaL_argcheck(L, 0, 2, "invalid receive pattern");
125 /* get a fixed number of bytes */ 125 /* get a fixed number of bytes (minus what was already partially
126 * received) */
126 } else err = recvraw(buf, (size_t) lua_tonumber(L, 2)-size, &b); 127 } else err = recvraw(buf, (size_t) lua_tonumber(L, 2)-size, &b);
127 /* check if there was an error */ 128 /* check if there was an error */
128 if (err != IO_DONE) { 129 if (err != IO_DONE) {
diff --git a/test/httptest.lua b/test/httptest.lua
index 86f14a4..3816b54 100644
--- a/test/httptest.lua
+++ b/test/httptest.lua
@@ -26,7 +26,7 @@ host = host or "localhost" -- "diego.student.princeton.edu"
26proxy = proxy or "http://localhost:3128" 26proxy = proxy or "http://localhost:3128"
27prefix = prefix or "/luasocket-test" 27prefix = prefix or "/luasocket-test"
28cgiprefix = cgiprefix or "/luasocket-test-cgi" 28cgiprefix = cgiprefix or "/luasocket-test-cgi"
29index_file = "test/index.html" 29index_file = "index.html"
30 30
31-- read index with CRLF convention 31-- read index with CRLF convention
32index = readfile(index_file) 32index = readfile(index_file)