diff options
author | Diego Nehab <diego@tecgraf.puc-rio.br> | 2004-01-16 07:06:31 +0000 |
---|---|---|
committer | Diego Nehab <diego@tecgraf.puc-rio.br> | 2004-01-16 07:06:31 +0000 |
commit | 89f3ecf7820857f91c4039536d2bbe3cf12d5f95 (patch) | |
tree | 76e6cc9ff403a8c5ee52448d7ae9311bdf499039 | |
parent | 3febb302ad28fd25de51cbc686739469b92d8921 (diff) | |
download | luasocket-89f3ecf7820857f91c4039536d2bbe3cf12d5f95.tar.gz luasocket-89f3ecf7820857f91c4039536d2bbe3cf12d5f95.tar.bz2 luasocket-89f3ecf7820857f91c4039536d2bbe3cf12d5f95.zip |
http.lua updated. still needs proxy support.
code.lua updated. looks neat.
-rw-r--r-- | TODO | 15 | ||||
-rw-r--r-- | etc/get.lua | 2 | ||||
-rw-r--r-- | src/http.lua | 526 | ||||
-rw-r--r-- | src/socket.h | 1 | ||||
-rw-r--r-- | src/tcp.c | 38 | ||||
-rw-r--r-- | src/usocket.c | 110 | ||||
-rw-r--r-- | src/wsocket.c | 8 | ||||
-rw-r--r-- | test/auth/.htaccess | 2 | ||||
-rw-r--r-- | test/httptest.lua | 89 | ||||
-rw-r--r-- | test/testclnt.lua | 6 |
10 files changed, 442 insertions, 355 deletions
@@ -1,7 +1,17 @@ | |||
1 | replace times by getrusage | 1 | change send/recv to avoid using select |
2 | 2 | ||
3 | make sure modules know if their dependencies are there. | 3 | add gethostname and use it in HTTP, SMTP etc, and add manual entry. |
4 | add local connect, and manual entry | ||
5 | add shutdown, and manual entry | ||
6 | |||
7 | only allocate in case of success | ||
8 | only call select if io fails... | ||
9 | Proxy support pro http | ||
4 | 10 | ||
11 | make REUSEADDR an option... | ||
12 | |||
13 | make sure modules know if their dependencies are there. | ||
14 | _ | ||
5 | one thing i noticed in usocket.c is that it doesn't check for EINTR | 15 | one thing i noticed in usocket.c is that it doesn't check for EINTR |
6 | after write(), sendto(), read(), recvfrom() etc. ? the usual trick is | 16 | after write(), sendto(), read(), recvfrom() etc. ? the usual trick is |
7 | to loop while you get EINTR: | 17 | to loop while you get EINTR: |
@@ -68,7 +78,6 @@ Ajeitar o protocolo da luaopen_socket()... sei lá qual é. | |||
68 | - proteger ou atomizar o conjunto (timedout, receive), (timedout, send) | 78 | - proteger ou atomizar o conjunto (timedout, receive), (timedout, send) |
69 | - inet_ntoa também é uma merda. | 79 | - inet_ntoa também é uma merda. |
70 | - SSL | 80 | - SSL |
71 | - Proxy support pro http | ||
72 | 81 | ||
73 | - checar operações em closed sockets | 82 | - checar operações em closed sockets |
74 | - checar teste de writable socket com select | 83 | - checar teste de writable socket com select |
diff --git a/etc/get.lua b/etc/get.lua index a093e24..2d804a0 100644 --- a/etc/get.lua +++ b/etc/get.lua | |||
@@ -99,7 +99,7 @@ end | |||
99 | function getbyhttp(url, file, size) | 99 | function getbyhttp(url, file, size) |
100 | local response = socket.http.request_cb( | 100 | local response = socket.http.request_cb( |
101 | {url = url}, | 101 | {url = url}, |
102 | {body_cb = receive2disk(file, size)} | 102 | {body_cb = receive2disk(file, size)} |
103 | ) | 103 | ) |
104 | print() | 104 | print() |
105 | if response.code ~= 200 then print(response.status or response.error) end | 105 | if response.code ~= 200 then print(response.status or response.error) end |
diff --git a/src/http.lua b/src/http.lua index 18a44b6..1925c68 100644 --- a/src/http.lua +++ b/src/http.lua | |||
@@ -5,22 +5,29 @@ | |||
5 | -- Conforming to: RFC 2616, LTN7 | 5 | -- Conforming to: RFC 2616, LTN7 |
6 | -- RCS ID: $Id$ | 6 | -- RCS ID: $Id$ |
7 | ----------------------------------------------------------------------------- | 7 | ----------------------------------------------------------------------------- |
8 | 8 | -- make sure LuaSocket is loaded | |
9 | local Public, Private = {}, {} | 9 | if not LUASOCKET_LIBNAME then error('module requires LuaSocket') end |
10 | local socket = _G[LUASOCKET_LIBNAME] -- get LuaSocket namespace | 10 | -- get LuaSocket namespace |
11 | socket.http = Public -- create http sub namespace | 11 | local socket = _G[LUASOCKET_LIBNAME] |
12 | if not socket then error('module requires LuaSocket') end | ||
13 | -- create smtp namespace inside LuaSocket namespace | ||
14 | local http = {} | ||
15 | socket.http = http | ||
16 | -- make all module globals fall into smtp namespace | ||
17 | setmetatable(http, { __index = _G }) | ||
18 | setfenv(1, http) | ||
12 | 19 | ||
13 | ----------------------------------------------------------------------------- | 20 | ----------------------------------------------------------------------------- |
14 | -- Program constants | 21 | -- Program constants |
15 | ----------------------------------------------------------------------------- | 22 | ----------------------------------------------------------------------------- |
16 | -- connection timeout in seconds | 23 | -- connection timeout in seconds |
17 | Public.TIMEOUT = 60 | 24 | TIMEOUT = 60 |
18 | -- default port for document retrieval | 25 | -- default port for document retrieval |
19 | Public.PORT = 80 | 26 | PORT = 80 |
20 | -- user agent field sent in request | 27 | -- user agent field sent in request |
21 | Public.USERAGENT = "LuaSocket 2.0" | 28 | USERAGENT = "LuaSocket 2.0" |
22 | -- block size used in transfers | 29 | -- block size used in transfers |
23 | Public.BLOCKSIZE = 8192 | 30 | BLOCKSIZE = 8192 |
24 | 31 | ||
25 | ----------------------------------------------------------------------------- | 32 | ----------------------------------------------------------------------------- |
26 | -- Tries to get a pattern from the server and closes socket on error | 33 | -- Tries to get a pattern from the server and closes socket on error |
@@ -30,7 +37,7 @@ Public.BLOCKSIZE = 8192 | |||
30 | -- received pattern on success | 37 | -- received pattern on success |
31 | -- nil followed by error message on error | 38 | -- nil followed by error message on error |
32 | ----------------------------------------------------------------------------- | 39 | ----------------------------------------------------------------------------- |
33 | function Private.try_receive(sock, pattern) | 40 | local function try_receiving(sock, pattern) |
34 | local data, err = sock:receive(pattern) | 41 | local data, err = sock:receive(pattern) |
35 | if not data then sock:close() end | 42 | if not data then sock:close() end |
36 | return data, err | 43 | return data, err |
@@ -43,26 +50,13 @@ end | |||
43 | -- Returns | 50 | -- Returns |
44 | -- err: error message if any, nil if successfull | 51 | -- err: error message if any, nil if successfull |
45 | ----------------------------------------------------------------------------- | 52 | ----------------------------------------------------------------------------- |
46 | function Private.try_send(sock, data) | 53 | local function try_sending(sock, ...) |
47 | local sent, err = sock:send(data) | 54 | local sent, err = sock:send(unpack(arg)) |
48 | if not sent then sock:close() end | 55 | if not sent then sock:close() end |
49 | return err | 56 | return err |
50 | end | 57 | end |
51 | 58 | ||
52 | ----------------------------------------------------------------------------- | 59 | ----------------------------------------------------------------------------- |
53 | -- Computes status code from HTTP status line | ||
54 | -- Input | ||
55 | -- line: HTTP status line | ||
56 | -- Returns | ||
57 | -- code: integer with status code, or nil if malformed line | ||
58 | ----------------------------------------------------------------------------- | ||
59 | function Private.get_statuscode(line) | ||
60 | local code, _ | ||
61 | _, _, code = string.find(line, "HTTP/%d*%.%d* (%d%d%d)") | ||
62 | return tonumber(code) | ||
63 | end | ||
64 | |||
65 | ----------------------------------------------------------------------------- | ||
66 | -- Receive server reply messages, parsing for status code | 60 | -- Receive server reply messages, parsing for status code |
67 | -- Input | 61 | -- Input |
68 | -- sock: socket connected to the server | 62 | -- sock: socket connected to the server |
@@ -71,10 +65,13 @@ end | |||
71 | -- line: full HTTP status line | 65 | -- line: full HTTP status line |
72 | -- err: error message if any | 66 | -- err: error message if any |
73 | ----------------------------------------------------------------------------- | 67 | ----------------------------------------------------------------------------- |
74 | function Private.receive_status(sock) | 68 | local function receive_status(sock) |
75 | local line, err | 69 | local line, err |
76 | line, err = Private.try_receive(sock) | 70 | line, err = try_receiving(sock) |
77 | if not err then return Private.get_statuscode(line), line | 71 | if not err then |
72 | local code, _ | ||
73 | _, _, code = string.find(line, "HTTP/%d*%.%d* (%d%d%d)") | ||
74 | return tonumber(code), line | ||
78 | else return nil, nil, err end | 75 | else return nil, nil, err end |
79 | end | 76 | end |
80 | 77 | ||
@@ -89,11 +86,12 @@ end | |||
89 | -- all name_i are lowercase | 86 | -- all name_i are lowercase |
90 | -- nil and error message in case of error | 87 | -- nil and error message in case of error |
91 | ----------------------------------------------------------------------------- | 88 | ----------------------------------------------------------------------------- |
92 | function Private.receive_headers(sock, headers) | 89 | local function receive_headers(sock, headers) |
93 | local line, err | 90 | local line, err |
94 | local name, value, _ | 91 | local name, value, _ |
92 | headers = headers or {} | ||
95 | -- get first line | 93 | -- get first line |
96 | line, err = Private.try_receive(sock) | 94 | line, err = try_receiving(sock) |
97 | if err then return nil, err end | 95 | if err then return nil, err end |
98 | -- headers go until a blank line is found | 96 | -- headers go until a blank line is found |
99 | while line ~= "" do | 97 | while line ~= "" do |
@@ -105,12 +103,12 @@ function Private.receive_headers(sock, headers) | |||
105 | end | 103 | end |
106 | name = string.lower(name) | 104 | name = string.lower(name) |
107 | -- get next line (value might be folded) | 105 | -- get next line (value might be folded) |
108 | line, err = Private.try_receive(sock) | 106 | line, err = try_receiving(sock) |
109 | if err then return nil, err end | 107 | if err then return nil, err end |
110 | -- unfold any folded values | 108 | -- unfold any folded values |
111 | while not err and string.find(line, "^%s") do | 109 | while not err and string.find(line, "^%s") do |
112 | value = value .. line | 110 | value = value .. line |
113 | line, err = Private.try_receive(sock) | 111 | line, err = try_receiving(sock) |
114 | if err then return nil, err end | 112 | if err then return nil, err end |
115 | end | 113 | end |
116 | -- save pair in table | 114 | -- save pair in table |
@@ -121,6 +119,19 @@ function Private.receive_headers(sock, headers) | |||
121 | end | 119 | end |
122 | 120 | ||
123 | ----------------------------------------------------------------------------- | 121 | ----------------------------------------------------------------------------- |
122 | -- Aborts a receive callback | ||
123 | -- Input | ||
124 | -- cb: callback function | ||
125 | -- err: error message to pass to callback | ||
126 | -- Returns | ||
127 | -- callback return or if nil err | ||
128 | ----------------------------------------------------------------------------- | ||
129 | local function abort(cb, err) | ||
130 | local go, err_or_f = cb(nil, err) | ||
131 | return err_or_f or err | ||
132 | end | ||
133 | |||
134 | ----------------------------------------------------------------------------- | ||
124 | -- Receives a chunked message body | 135 | -- Receives a chunked message body |
125 | -- Input | 136 | -- Input |
126 | -- sock: socket connected to the server | 137 | -- sock: socket connected to the server |
@@ -129,54 +140,37 @@ end | |||
129 | -- Returns | 140 | -- Returns |
130 | -- nil if successfull or an error message in case of error | 141 | -- nil if successfull or an error message in case of error |
131 | ----------------------------------------------------------------------------- | 142 | ----------------------------------------------------------------------------- |
132 | function Private.receivebody_bychunks(sock, headers, receive_cb) | 143 | local function receive_body_bychunks(sock, headers, receive_cb) |
133 | local chunk, size, line, err, go, uerr, _ | 144 | local chunk, size, line, err, go, err_or_f, _ |
134 | while 1 do | 145 | while 1 do |
135 | -- get chunk size, skip extention | 146 | -- get chunk size, skip extention |
136 | line, err = Private.try_receive(sock) | 147 | line, err = try_receiving(sock) |
137 | if err then | 148 | if err then return abort(receive_cb, err) end |
138 | local go, uerr = receive_cb(nil, err) | ||
139 | return uerr or err | ||
140 | end | ||
141 | size = tonumber(string.gsub(line, ";.*", ""), 16) | 149 | size = tonumber(string.gsub(line, ";.*", ""), 16) |
142 | if not size then | 150 | if not size then return abort(receive_cb, "invalid chunk size") end |
143 | err = "invalid chunk size" | ||
144 | sock:close() | ||
145 | go, uerr = receive_cb(nil, err) | ||
146 | return uerr or err | ||
147 | end | ||
148 | -- was it the last chunk? | 151 | -- was it the last chunk? |
149 | if size <= 0 then break end | 152 | if size <= 0 then break end |
150 | -- get chunk | 153 | -- get chunk |
151 | chunk, err = Private.try_receive(sock, size) | 154 | chunk, err = try_receiving(sock, size) |
152 | if err then | 155 | if err then return abort(receive_cb, err) end |
153 | go, uerr = receive_cb(nil, err) | ||
154 | return uerr or err | ||
155 | end | ||
156 | -- pass chunk to callback | 156 | -- pass chunk to callback |
157 | go, uerr = receive_cb(chunk) | 157 | go, err_or_f = receive_cb(chunk) |
158 | if not go then | 158 | -- see if callback needs to be replaced |
159 | sock:close() | 159 | receive_cb = err_or_f or receive_cb |
160 | return uerr or "aborted by callback" | 160 | -- see if callback aborted |
161 | end | 161 | if not go then return err_or_f or "aborted by callback" end |
162 | -- skip CRLF on end of chunk | 162 | -- skip CRLF on end of chunk |
163 | _, err = Private.try_receive(sock) | 163 | _, err = try_receiving(sock) |
164 | if err then | 164 | if err then return abort(receive_cb, err) end |
165 | go, uerr = receive_cb(nil, err) | ||
166 | return uerr or err | ||
167 | end | ||
168 | end | 165 | end |
169 | -- the server should not send trailer headers because we didn't send a | 166 | -- the server should not send trailer headers because we didn't send a |
170 | -- header informing it we know how to deal with them. we do not risk | 167 | -- header informing it we know how to deal with them. we do not risk |
171 | -- being caught unprepaired. | 168 | -- being caught unprepaired. |
172 | headers, err = Private.receive_headers(sock, headers) | 169 | _, err = receive_headers(sock, headers) |
173 | if err then | 170 | if err then return abort(receive_cb, err) end |
174 | go, uerr = receive_cb(nil, err) | ||
175 | return uerr or err | ||
176 | end | ||
177 | -- let callback know we are done | 171 | -- let callback know we are done |
178 | go, uerr = receive_cb("") | 172 | _, err_or_f = receive_cb("") |
179 | return uerr | 173 | return err_or_f |
180 | end | 174 | end |
181 | 175 | ||
182 | ----------------------------------------------------------------------------- | 176 | ----------------------------------------------------------------------------- |
@@ -188,25 +182,21 @@ end | |||
188 | -- Returns | 182 | -- Returns |
189 | -- nil if successfull or an error message in case of error | 183 | -- nil if successfull or an error message in case of error |
190 | ----------------------------------------------------------------------------- | 184 | ----------------------------------------------------------------------------- |
191 | function Private.receivebody_bylength(sock, length, receive_cb) | 185 | local function receive_body_bylength(sock, length, receive_cb) |
192 | local uerr, go | ||
193 | while length > 0 do | 186 | while length > 0 do |
194 | local size = math.min(Public.BLOCKSIZE, length) | 187 | local size = math.min(BLOCKSIZE, length) |
195 | local chunk, err = sock:receive(size) | 188 | local chunk, err = sock:receive(size) |
196 | -- if there was an error before we got all the data | 189 | local go, err_or_f = receive_cb(chunk) |
197 | if err and string.len(chunk) ~= length then | 190 | length = length - string.len(chunk) |
198 | go, uerr = receive_cb(nil, err) | 191 | -- see if callback aborted |
199 | return uerr or err | 192 | if not go then return err_or_f or "aborted by callback" end |
200 | end | 193 | -- see if callback needs to be replaced |
201 | go, uerr = receive_cb(chunk) | 194 | receive_cb = err_or_f or receive_cb |
202 | if not go then | 195 | -- see if there was an error |
203 | sock:close() | 196 | if err and length > 0 then return abort(receive_cb, err) end |
204 | return uerr or "aborted by callback" | ||
205 | end | ||
206 | length = length - size | ||
207 | end | 197 | end |
208 | go, uerr = receive_cb("") | 198 | local _, err_or_f = receive_cb("") |
209 | return uerr | 199 | return err_or_f |
210 | end | 200 | end |
211 | 201 | ||
212 | ----------------------------------------------------------------------------- | 202 | ----------------------------------------------------------------------------- |
@@ -217,24 +207,24 @@ end | |||
217 | -- Returns | 207 | -- Returns |
218 | -- nil if successfull or an error message in case of error | 208 | -- nil if successfull or an error message in case of error |
219 | ----------------------------------------------------------------------------- | 209 | ----------------------------------------------------------------------------- |
220 | function Private.receivebody_untilclosed(sock, receive_cb) | 210 | local function receive_body_untilclosed(sock, receive_cb) |
221 | local err, go, uerr | ||
222 | while 1 do | 211 | while 1 do |
223 | local chunk, err = sock:receive(Public.BLOCKSIZE) | 212 | local chunk, err = sock:receive(BLOCKSIZE) |
224 | if err == "closed" or not err then | 213 | local go, err_or_f = receive_cb(chunk) |
225 | go, uerr = receive_cb(chunk) | 214 | -- see if callback aborted |
226 | if not go then | 215 | if not go then return err_or_f or "aborted by callback" end |
227 | sock:close() | 216 | -- see if callback needs to be replaced |
228 | return uerr or "aborted by callback" | 217 | receive_cb = err_or_f or receive_cb |
218 | -- see if we are done | ||
219 | if err == "closed" then | ||
220 | if chunk ~= "" then | ||
221 | go, err_or_f = receive_cb("") | ||
222 | return err_or_f | ||
229 | end | 223 | end |
230 | if err == "closed" then break end | ||
231 | else | ||
232 | go, uerr = callback(nil, err) | ||
233 | return uerr or err | ||
234 | end | 224 | end |
225 | -- see if there was an error | ||
226 | if err then return abort(receive_cb, err) end | ||
235 | end | 227 | end |
236 | go, uerr = receive_cb("") | ||
237 | return uerr | ||
238 | end | 228 | end |
239 | 229 | ||
240 | ----------------------------------------------------------------------------- | 230 | ----------------------------------------------------------------------------- |
@@ -246,59 +236,68 @@ end | |||
246 | -- Returns | 236 | -- Returns |
247 | -- nil if successfull or an error message in case of error | 237 | -- nil if successfull or an error message in case of error |
248 | ----------------------------------------------------------------------------- | 238 | ----------------------------------------------------------------------------- |
249 | function Private.receive_body(sock, headers, receive_cb) | 239 | local function receive_body(sock, headers, receive_cb) |
250 | local te = headers["transfer-encoding"] | 240 | local te = headers["transfer-encoding"] |
251 | if te and te ~= "identity" then | 241 | if te and te ~= "identity" then |
252 | -- get by chunked transfer-coding of message body | 242 | -- get by chunked transfer-coding of message body |
253 | return Private.receivebody_bychunks(sock, headers, receive_cb) | 243 | return receive_body_bychunks(sock, headers, receive_cb) |
254 | elseif tonumber(headers["content-length"]) then | 244 | elseif tonumber(headers["content-length"]) then |
255 | -- get by content-length | 245 | -- get by content-length |
256 | local length = tonumber(headers["content-length"]) | 246 | local length = tonumber(headers["content-length"]) |
257 | return Private.receivebody_bylength(sock, length, receive_cb) | 247 | return receive_body_bylength(sock, length, receive_cb) |
258 | else | 248 | else |
259 | -- get it all until connection closes | 249 | -- get it all until connection closes |
260 | return Private.receivebody_untilclosed(sock, receive_cb) | 250 | return receive_body_untilclosed(sock, receive_cb) |
261 | end | 251 | end |
262 | end | 252 | end |
263 | 253 | ||
264 | ----------------------------------------------------------------------------- | 254 | ----------------------------------------------------------------------------- |
265 | -- Drop HTTP response body | 255 | -- Sends data comming from a callback |
266 | -- Input | 256 | -- Input |
267 | -- sock: socket connected to the server | 257 | -- data: data connection |
268 | -- headers: response header fields | 258 | -- send_cb: callback to produce file contents |
269 | -- Returns | 259 | -- Returns |
270 | -- nil if successfull or an error message in case of error | 260 | -- nil if successfull, or an error message in case of error |
271 | ----------------------------------------------------------------------------- | 261 | ----------------------------------------------------------------------------- |
272 | function Private.drop_body(sock, headers) | 262 | local function send_body_bychunks(data, send_cb) |
273 | return Private.receive_body(sock, headers, function (c, e) return 1 end) | 263 | while 1 do |
264 | local chunk, err_or_f = send_cb() | ||
265 | -- check if callback aborted | ||
266 | if not chunk then return err_or_f or "aborted by callback" end | ||
267 | -- check if callback should be replaced | ||
268 | send_cb = err_or_f or send_cb | ||
269 | -- if we are done, send last-chunk | ||
270 | if chunk == "" then return try_sending(data, "0\r\n\r\n") end | ||
271 | -- else send middle chunk | ||
272 | local err = try_sending(data, | ||
273 | string.format("%X\r\n", string.len(chunk)), | ||
274 | chunk, | ||
275 | "\r\n" | ||
276 | ) | ||
277 | if err then return err end | ||
278 | end | ||
274 | end | 279 | end |
275 | 280 | ||
276 | ----------------------------------------------------------------------------- | 281 | ----------------------------------------------------------------------------- |
277 | -- Sends data comming from a callback | 282 | -- Sends data comming from a callback |
278 | -- Input | 283 | -- Input |
279 | -- data: data connection | 284 | -- data: data connection |
280 | -- send_cb: callback to produce file contents | 285 | -- send_cb: callback to produce body contents |
281 | -- chunk, size: first callback return values | ||
282 | -- Returns | 286 | -- Returns |
283 | -- nil if successfull, or an error message in case of error | 287 | -- nil if successfull, or an error message in case of error |
284 | ----------------------------------------------------------------------------- | 288 | ----------------------------------------------------------------------------- |
285 | function Private.send_indirect(data, send_cb, chunk, size) | 289 | local function send_body_bylength(data, send_cb) |
286 | local total, sent, err | ||
287 | total = 0 | ||
288 | while 1 do | 290 | while 1 do |
289 | if type(chunk) ~= "string" or type(size) ~= "number" then | 291 | local chunk, err_or_f = send_cb() |
290 | data:close() | 292 | -- check if callback aborted |
291 | if not chunk and type(size) == "string" then return size | 293 | if not chunk then return err_or_f or "aborted by callback" end |
292 | else return "invalid callback return" end | 294 | -- check if callback should be replaced |
293 | end | 295 | send_cb = err_or_f or send_cb |
294 | sent, err = data:send(chunk) | 296 | -- check if callback is done |
295 | if err then | 297 | if chunk == "" then return end |
296 | data:close() | 298 | -- send data |
297 | return err | 299 | local err = try_sending(data, chunk) |
298 | end | 300 | if err then return err end |
299 | total = total + sent | ||
300 | if total >= size then break end | ||
301 | chunk, size = send_cb() | ||
302 | end | 301 | end |
303 | end | 302 | end |
304 | 303 | ||
@@ -310,16 +309,16 @@ end | |||
310 | -- Returns | 309 | -- Returns |
311 | -- err: error message if any | 310 | -- err: error message if any |
312 | ----------------------------------------------------------------------------- | 311 | ----------------------------------------------------------------------------- |
313 | function Private.send_headers(sock, headers) | 312 | local function send_headers(sock, headers) |
314 | local err | 313 | local err |
315 | headers = headers or {} | 314 | headers = headers or {} |
316 | -- send request headers | 315 | -- send request headers |
317 | for i, v in headers do | 316 | for i, v in headers do |
318 | err = Private.try_send(sock, i .. ": " .. v .. "\r\n") | 317 | err = try_sending(sock, i .. ": " .. v .. "\r\n") |
319 | if err then return err end | 318 | if err then return err end |
320 | end | 319 | end |
321 | -- mark end of request headers | 320 | -- mark end of request headers |
322 | return Private.try_send(sock, "\r\n") | 321 | return try_sending(sock, "\r\n") |
323 | end | 322 | end |
324 | 323 | ||
325 | ----------------------------------------------------------------------------- | 324 | ----------------------------------------------------------------------------- |
@@ -333,43 +332,39 @@ end | |||
333 | -- Returns | 332 | -- Returns |
334 | -- err: nil in case of success, error message otherwise | 333 | -- err: nil in case of success, error message otherwise |
335 | ----------------------------------------------------------------------------- | 334 | ----------------------------------------------------------------------------- |
336 | function Private.send_request(sock, method, uri, headers, body_cb) | 335 | local function send_request(sock, method, uri, headers, body_cb) |
337 | local chunk, size, done, err | 336 | local chunk, size, done, err |
338 | -- send request line | 337 | -- send request line |
339 | err = Private.try_send(sock, method .. " " .. uri .. " HTTP/1.1\r\n") | 338 | err = try_sending(sock, method .. " " .. uri .. " HTTP/1.1\r\n") |
340 | if err then return err end | 339 | if err then return err end |
341 | -- if there is a request message body, add content-length header | 340 | if body_cb and not headers["content-length"] then |
342 | chunk, size = body_cb() | 341 | headers["transfer-encoding"] = "chunked" |
343 | if type(chunk) == "string" and type(size) == "number" then | ||
344 | if size > 0 then | ||
345 | headers["content-length"] = tostring(size) | ||
346 | end | ||
347 | else | ||
348 | sock:close() | ||
349 | if not chunk and type(size) == "string" then return size | ||
350 | else return "invalid callback return" end | ||
351 | end | 342 | end |
352 | -- send request headers | 343 | -- send request headers |
353 | err = Private.send_headers(sock, headers) | 344 | err = send_headers(sock, headers) |
354 | if err then return err end | 345 | if err then return err end |
355 | -- send request message body, if any | 346 | -- send request message body, if any |
356 | if body_cb then | 347 | if body_cb then |
357 | return Private.send_indirect(sock, body_cb, chunk, size) | 348 | if not headers["content-length"] then |
349 | return send_body_bychunks(sock, body_cb) | ||
350 | else | ||
351 | return send_body_bylength(sock, body_cb) | ||
352 | end | ||
358 | end | 353 | end |
359 | end | 354 | end |
360 | 355 | ||
361 | ----------------------------------------------------------------------------- | 356 | ----------------------------------------------------------------------------- |
362 | -- Determines if we should read a message body from the server response | 357 | -- Determines if we should read a message body from the server response |
363 | -- Input | 358 | -- Input |
364 | -- request: a table with the original request information | 359 | -- reqt: a table with the original request information |
365 | -- response: a table with the server response information | 360 | -- respt: a table with the server response information |
366 | -- Returns | 361 | -- Returns |
367 | -- 1 if a message body should be processed, nil otherwise | 362 | -- 1 if a message body should be processed, nil otherwise |
368 | ----------------------------------------------------------------------------- | 363 | ----------------------------------------------------------------------------- |
369 | function Private.has_body(request, response) | 364 | local function should_receive_body(reqt, respt) |
370 | if request.method == "HEAD" then return nil end | 365 | if reqt.method == "HEAD" then return nil end |
371 | if response.code == 204 or response.code == 304 then return nil end | 366 | if respt.code == 204 or respt.code == 304 then return nil end |
372 | if response.code >= 100 and response.code < 200 then return nil end | 367 | if respt.code >= 100 and respt.code < 200 then return nil end |
373 | return 1 | 368 | return 1 |
374 | end | 369 | end |
375 | 370 | ||
@@ -381,11 +376,11 @@ end | |||
381 | -- Returns | 376 | -- Returns |
382 | -- lower: a table with the same headers, but with lowercase field names | 377 | -- lower: a table with the same headers, but with lowercase field names |
383 | ----------------------------------------------------------------------------- | 378 | ----------------------------------------------------------------------------- |
384 | function Private.fill_headers(headers, parsed) | 379 | local function fill_headers(headers, parsed) |
385 | local lower = {} | 380 | local lower = {} |
386 | headers = headers or {} | 381 | headers = headers or {} |
387 | -- set default headers | 382 | -- set default headers |
388 | lower["user-agent"] = Public.USERAGENT | 383 | lower["user-agent"] = USERAGENT |
389 | -- override with user values | 384 | -- override with user values |
390 | for i,v in headers do | 385 | for i,v in headers do |
391 | lower[string.lower(i)] = v | 386 | lower[string.lower(i)] = v |
@@ -399,15 +394,15 @@ end | |||
399 | ----------------------------------------------------------------------------- | 394 | ----------------------------------------------------------------------------- |
400 | -- Decides wether we should follow retry with authorization formation | 395 | -- Decides wether we should follow retry with authorization formation |
401 | -- Input | 396 | -- Input |
402 | -- request: a table with the original request information | 397 | -- reqt: a table with the original request information |
403 | -- parsed: parsed request URL | 398 | -- parsed: parsed request URL |
404 | -- response: a table with the server response information | 399 | -- respt: a table with the server response information |
405 | -- Returns | 400 | -- Returns |
406 | -- 1 if we should retry, nil otherwise | 401 | -- 1 if we should retry, nil otherwise |
407 | ----------------------------------------------------------------------------- | 402 | ----------------------------------------------------------------------------- |
408 | function Private.should_authorize(request, parsed, response) | 403 | local function should_authorize(reqt, parsed, respt) |
409 | -- if there has been an authorization attempt, it must have failed | 404 | -- if there has been an authorization attempt, it must have failed |
410 | if request.headers["authorization"] then return nil end | 405 | if reqt.headers["authorization"] then return nil end |
411 | -- if we don't have authorization information, we can't retry | 406 | -- if we don't have authorization information, we can't retry |
412 | if parsed.user and parsed.password then return 1 | 407 | if parsed.user and parsed.password then return 1 |
413 | else return nil end | 408 | else return nil end |
@@ -416,66 +411,66 @@ end | |||
416 | ----------------------------------------------------------------------------- | 411 | ----------------------------------------------------------------------------- |
417 | -- Returns the result of retrying a request with authorization information | 412 | -- Returns the result of retrying a request with authorization information |
418 | -- Input | 413 | -- Input |
419 | -- request: a table with the original request information | 414 | -- reqt: a table with the original request information |
420 | -- parsed: parsed request URL | 415 | -- parsed: parsed request URL |
421 | -- response: a table with the server response information | 416 | -- respt: a table with the server response information |
422 | -- Returns | 417 | -- Returns |
423 | -- response: result of target redirection | 418 | -- respt: result of target authorization |
424 | ----------------------------------------------------------------------------- | 419 | ----------------------------------------------------------------------------- |
425 | function Private.authorize(request, parsed, response) | 420 | local function authorize(reqt, parsed, respt) |
426 | request.headers["authorization"] = "Basic " .. | 421 | reqt.headers["authorization"] = "Basic " .. |
427 | socket.code.base64(parsed.user .. ":" .. parsed.password) | 422 | socket.code.base64.encode(parsed.user .. ":" .. parsed.password) |
428 | local authorize = { | 423 | local autht = { |
429 | redirects = request.redirects, | 424 | nredirects = reqt.nredirects, |
430 | method = request.method, | 425 | method = reqt.method, |
431 | url = request.url, | 426 | url = reqt.url, |
432 | body_cb = request.body_cb, | 427 | body_cb = reqt.body_cb, |
433 | headers = request.headers | 428 | headers = reqt.headers |
434 | } | 429 | } |
435 | return Public.request_cb(authorize, response) | 430 | return request_cb(autht, respt) |
436 | end | 431 | end |
437 | 432 | ||
438 | ----------------------------------------------------------------------------- | 433 | ----------------------------------------------------------------------------- |
439 | -- Decides wether we should follow a server redirect message | 434 | -- Decides wether we should follow a server redirect message |
440 | -- Input | 435 | -- Input |
441 | -- request: a table with the original request information | 436 | -- reqt: a table with the original request information |
442 | -- response: a table with the server response information | 437 | -- respt: a table with the server response information |
443 | -- Returns | 438 | -- Returns |
444 | -- 1 if we should redirect, nil otherwise | 439 | -- 1 if we should redirect, nil otherwise |
445 | ----------------------------------------------------------------------------- | 440 | ----------------------------------------------------------------------------- |
446 | function Private.should_redirect(request, response) | 441 | local function should_redirect(reqt, respt) |
447 | local follow = not request.stay | 442 | local follow = not reqt.stay |
448 | follow = follow and (response.code == 301 or response.code == 302) | 443 | follow = follow and (respt.code == 301 or respt.code == 302) |
449 | follow = follow and (request.method == "GET" or request.method == "HEAD") | 444 | follow = follow and (reqt.method == "GET" or reqt.method == "HEAD") |
450 | follow = follow and not (request.redirects and request.redirects >= 5) | 445 | follow = follow and not (reqt.nredirects and reqt.nredirects >= 5) |
451 | return follow | 446 | return follow |
452 | end | 447 | end |
453 | 448 | ||
454 | ----------------------------------------------------------------------------- | 449 | ----------------------------------------------------------------------------- |
455 | -- Returns the result of a request following a server redirect message. | 450 | -- Returns the result of a request following a server redirect message. |
456 | -- Input | 451 | -- Input |
457 | -- request: a table with the original request information | 452 | -- reqt: a table with the original request information |
458 | -- response: a table with the following fields: | 453 | -- respt: a table with the following fields: |
459 | -- body_cb: response method body receive-callback | 454 | -- body_cb: response method body receive-callback |
460 | -- Returns | 455 | -- Returns |
461 | -- response: result of target redirection | 456 | -- respt: result of target redirection |
462 | ----------------------------------------------------------------------------- | 457 | ----------------------------------------------------------------------------- |
463 | function Private.redirect(request, response) | 458 | local function redirect(reqt, respt) |
464 | local redirects = request.redirects or 0 | 459 | local nredirects = reqt.nredirects or 0 |
465 | redirects = redirects + 1 | 460 | nredirects = nredirects + 1 |
466 | local redirect = { | 461 | local redirt = { |
467 | redirects = redirects, | 462 | nredirects = nredirects, |
468 | method = request.method, | 463 | method = reqt.method, |
469 | -- the RFC says the redirect URL has to be absolute, but some | 464 | -- the RFC says the redirect URL has to be absolute, but some |
470 | -- servers do not respect that | 465 | -- servers do not respect that |
471 | url = socket.url.absolute(request.url, response.headers["location"]), | 466 | url = socket.url.absolute(reqt.url, respt.headers["location"]), |
472 | body_cb = request.body_cb, | 467 | body_cb = reqt.body_cb, |
473 | headers = request.headers | 468 | headers = reqt.headers |
474 | } | 469 | } |
475 | local response = Public.request_cb(redirect, response) | 470 | respt = request_cb(redirt, respt) |
476 | -- we pass the location header as a clue we tried to redirect | 471 | -- we pass the location header as a clue we tried to redirect |
477 | if response.headers then response.headers.location = redirect.url end | 472 | if respt.headers then respt.headers.location = redirt.url end |
478 | return response | 473 | return respt |
479 | end | 474 | end |
480 | 475 | ||
481 | ----------------------------------------------------------------------------- | 476 | ----------------------------------------------------------------------------- |
@@ -485,7 +480,7 @@ end | |||
485 | -- Returns | 480 | -- Returns |
486 | -- uri: request URI for parsed URL | 481 | -- uri: request URI for parsed URL |
487 | ----------------------------------------------------------------------------- | 482 | ----------------------------------------------------------------------------- |
488 | function Private.request_uri(parsed) | 483 | local function request_uri(parsed) |
489 | local uri = "" | 484 | local uri = "" |
490 | if parsed.path then uri = uri .. parsed.path end | 485 | if parsed.path then uri = uri .. parsed.path end |
491 | if parsed.params then uri = uri .. ";" .. parsed.params end | 486 | if parsed.params then uri = uri .. ";" .. parsed.params end |
@@ -502,105 +497,110 @@ end | |||
502 | -- user: account user name | 497 | -- user: account user name |
503 | -- password: account password) | 498 | -- password: account password) |
504 | -- Returns | 499 | -- Returns |
505 | -- request: request table | 500 | -- reqt: request table |
506 | ----------------------------------------------------------------------------- | 501 | ----------------------------------------------------------------------------- |
507 | function Private.build_request(data) | 502 | local function build_request(data) |
508 | local request = {} | 503 | local reqt = {} |
509 | if type(data) == "table" then | 504 | if type(data) == "table" then |
510 | for i, v in data | 505 | for i, v in data |
511 | do request[i] = v | 506 | do reqt[i] = v |
512 | end | 507 | end |
513 | else request.url = data end | 508 | else reqt.url = data end |
514 | return request | 509 | return reqt |
515 | end | 510 | end |
516 | 511 | ||
517 | ----------------------------------------------------------------------------- | 512 | ----------------------------------------------------------------------------- |
518 | -- Sends a HTTP request and retrieves the server reply using callbacks to | 513 | -- Sends a HTTP request and retrieves the server reply using callbacks to |
519 | -- send the request body and receive the response body | 514 | -- send the request body and receive the response body |
520 | -- Input | 515 | -- Input |
521 | -- request: a table with the following fields | 516 | -- reqt: a table with the following fields |
522 | -- method: "GET", "PUT", "POST" etc (defaults to "GET") | 517 | -- method: "GET", "PUT", "POST" etc (defaults to "GET") |
523 | -- url: target uniform resource locator | 518 | -- url: target uniform resource locator |
524 | -- user, password: authentication information | 519 | -- user, password: authentication information |
525 | -- headers: request headers to send, or nil if none | 520 | -- headers: request headers to send, or nil if none |
526 | -- body_cb: request message body send-callback, or nil if none | 521 | -- body_cb: request message body send-callback, or nil if none |
527 | -- stay: should we refrain from following a server redirect message? | 522 | -- stay: should we refrain from following a server redirect message? |
528 | -- response: a table with the following fields: | 523 | -- respt: a table with the following fields: |
529 | -- body_cb: response method body receive-callback | 524 | -- body_cb: response method body receive-callback |
530 | -- Returns | 525 | -- Returns |
531 | -- response: a table with the following fields: | 526 | -- respt: a table with the following fields: |
532 | -- headers: response header fields received, or nil if failed | 527 | -- headers: response header fields received, or nil if failed |
533 | -- status: server response status line, or nil if failed | 528 | -- status: server response status line, or nil if failed |
534 | -- code: server status code, or nil if failed | 529 | -- code: server status code, or nil if failed |
535 | -- error: error message, or nil if successfull | 530 | -- error: error message, or nil if successfull |
536 | ----------------------------------------------------------------------------- | 531 | ----------------------------------------------------------------------------- |
537 | function Public.request_cb(request, response) | 532 | function request_cb(reqt, respt) |
538 | local parsed = socket.url.parse(request.url, { | 533 | local parsed = socket.url.parse(reqt.url, { |
539 | host = "", | 534 | host = "", |
540 | port = Public.PORT, | 535 | port = PORT, |
541 | path ="/", | 536 | path ="/", |
542 | scheme = "http" | 537 | scheme = "http" |
543 | }) | 538 | }) |
544 | if parsed.scheme ~= "http" then | 539 | if parsed.scheme ~= "http" then |
545 | response.error = string.format("unknown scheme '%s'", parsed.scheme) | 540 | respt.error = string.format("unknown scheme '%s'", parsed.scheme) |
546 | return response | 541 | return respt |
547 | end | 542 | end |
548 | -- explicit authentication info overrides that given by the URL | 543 | -- explicit authentication info overrides that given by the URL |
549 | parsed.user = request.user or parsed.user | 544 | parsed.user = reqt.user or parsed.user |
550 | parsed.password = request.password or parsed.password | 545 | parsed.password = reqt.password or parsed.password |
551 | -- default method | 546 | -- default method |
552 | request.method = request.method or "GET" | 547 | reqt.method = reqt.method or "GET" |
553 | -- fill default headers | 548 | -- fill default headers |
554 | request.headers = Private.fill_headers(request.headers, parsed) | 549 | reqt.headers = fill_headers(reqt.headers, parsed) |
555 | -- try to connect to server | 550 | -- try to connect to server |
556 | local sock | 551 | local sock |
557 | sock, response.error = socket.connect(parsed.host, parsed.port) | 552 | sock, respt.error = socket.connect(parsed.host, parsed.port) |
558 | if not sock then return response end | 553 | if not sock then return respt end |
559 | -- set connection timeout so that we do not hang forever | 554 | -- set connection timeout so that we do not hang forever |
560 | sock:settimeout(Public.TIMEOUT) | 555 | sock:settimeout(TIMEOUT) |
561 | -- send request message | 556 | -- send request message |
562 | response.error = Private.send_request(sock, request.method, | 557 | respt.error = send_request(sock, reqt.method, |
563 | Private.request_uri(parsed), request.headers, request.body_cb) | 558 | request_uri(parsed), reqt.headers, reqt.body_cb) |
564 | if response.error then return response end | 559 | if respt.error then |
560 | sock:close() | ||
561 | return respt | ||
562 | end | ||
565 | -- get server response message | 563 | -- get server response message |
566 | response.code, response.status, response.error = | 564 | respt.code, respt.status, respt.error = receive_status(sock) |
567 | Private.receive_status(sock) | 565 | if respt.error then return respt end |
568 | if response.error then return response end | 566 | -- deal with continue 100 |
569 | -- deal with 1xx status | 567 | -- servers should not send them, but they might |
570 | if response.code == 100 then | 568 | if respt.code == 100 then |
571 | response.headers, response.error = Private.receive_headers(sock, {}) | 569 | respt.headers, respt.error = receive_headers(sock, {}) |
572 | if response.error then return response end | 570 | if respt.error then return respt end |
573 | response.code, response.status, response.error = | 571 | respt.code, respt.status, respt.error = receive_status(sock) |
574 | Private.receive_status(sock) | 572 | if respt.error then return respt end |
575 | if response.error then return response end | ||
576 | end | 573 | end |
577 | -- receive all headers | 574 | -- receive all headers |
578 | response.headers, response.error = Private.receive_headers(sock, {}) | 575 | respt.headers, respt.error = receive_headers(sock, {}) |
579 | if response.error then return response end | 576 | if respt.error then return respt end |
580 | -- decide what to do based on request and response parameters | 577 | -- decide what to do based on request and response parameters |
581 | if Private.should_redirect(request, response) then | 578 | if should_redirect(reqt, respt) then |
582 | Private.drop_body(sock, response.headers) | 579 | -- drop the body |
580 | receive_body(sock, respt.headers, function (c, e) return 1 end) | ||
581 | -- we are done with this connection | ||
583 | sock:close() | 582 | sock:close() |
584 | return Private.redirect(request, response) | 583 | return redirect(reqt, respt) |
585 | elseif Private.should_authorize(request, parsed, response) then | 584 | elseif should_authorize(reqt, parsed, respt) then |
586 | Private.drop_body(sock, response.headers) | 585 | -- drop the body |
586 | receive_body(sock, respt.headers, function (c, e) return 1 end) | ||
587 | -- we are done with this connection | ||
587 | sock:close() | 588 | sock:close() |
588 | return Private.authorize(request, parsed, response) | 589 | return authorize(reqt, parsed, respt) |
589 | elseif Private.has_body(request, response) then | 590 | elseif should_receive_body(reqt, respt) then |
590 | response.error = Private.receive_body(sock, response.headers, | 591 | respt.error = receive_body(sock, respt.headers, respt.body_cb) |
591 | response.body_cb) | 592 | if respt.error then return respt end |
592 | if response.error then return response end | ||
593 | sock:close() | 593 | sock:close() |
594 | return response | 594 | return respt |
595 | end | 595 | end |
596 | sock:close() | 596 | sock:close() |
597 | return response | 597 | return respt |
598 | end | 598 | end |
599 | 599 | ||
600 | ----------------------------------------------------------------------------- | 600 | ----------------------------------------------------------------------------- |
601 | -- Sends a HTTP request and retrieves the server reply | 601 | -- Sends a HTTP request and retrieves the server reply |
602 | -- Input | 602 | -- Input |
603 | -- request: a table with the following fields | 603 | -- reqt: a table with the following fields |
604 | -- method: "GET", "PUT", "POST" etc (defaults to "GET") | 604 | -- method: "GET", "PUT", "POST" etc (defaults to "GET") |
605 | -- url: request URL, i.e. the document to be retrieved | 605 | -- url: request URL, i.e. the document to be retrieved |
606 | -- user, password: authentication information | 606 | -- user, password: authentication information |
@@ -608,22 +608,22 @@ end | |||
608 | -- body: request message body as a string, or nil if none | 608 | -- body: request message body as a string, or nil if none |
609 | -- stay: should we refrain from following a server redirect message? | 609 | -- stay: should we refrain from following a server redirect message? |
610 | -- Returns | 610 | -- Returns |
611 | -- response: a table with the following fields: | 611 | -- respt: a table with the following fields: |
612 | -- body: response message body, or nil if failed | 612 | -- body: response message body, or nil if failed |
613 | -- headers: response header fields, or nil if failed | 613 | -- headers: response header fields, or nil if failed |
614 | -- status: server response status line, or nil if failed | 614 | -- status: server response status line, or nil if failed |
615 | -- code: server response status code, or nil if failed | 615 | -- code: server response status code, or nil if failed |
616 | -- error: error message if any | 616 | -- error: error message if any |
617 | ----------------------------------------------------------------------------- | 617 | ----------------------------------------------------------------------------- |
618 | function Public.request(request) | 618 | function request(reqt) |
619 | local response = {} | 619 | local respt = {} |
620 | request.body_cb = socket.callback.send_string(request.body) | 620 | reqt.body_cb = socket.callback.send_string(reqt.body) |
621 | local concat = socket.concat.create() | 621 | local concat = socket.concat.create() |
622 | response.body_cb = socket.callback.receive_concat(concat) | 622 | respt.body_cb = socket.callback.receive_concat(concat) |
623 | response = Public.request_cb(request, response) | 623 | respt = request_cb(reqt, respt) |
624 | response.body = concat:getresult() | 624 | respt.body = concat:getresult() |
625 | response.body_cb = nil | 625 | respt.body_cb = nil |
626 | return response | 626 | return respt |
627 | end | 627 | end |
628 | 628 | ||
629 | ----------------------------------------------------------------------------- | 629 | ----------------------------------------------------------------------------- |
@@ -639,12 +639,11 @@ end | |||
639 | -- code: server response status code, or nil if failed | 639 | -- code: server response status code, or nil if failed |
640 | -- error: error message if any | 640 | -- error: error message if any |
641 | ----------------------------------------------------------------------------- | 641 | ----------------------------------------------------------------------------- |
642 | function Public.get(url_or_request) | 642 | function get(url_or_request) |
643 | local request = Private.build_request(url_or_request) | 643 | local reqt = build_request(url_or_request) |
644 | request.method = "GET" | 644 | reqt.method = "GET" |
645 | local response = Public.request(request) | 645 | local respt = request(reqt) |
646 | return response.body, response.headers, | 646 | return respt.body, respt.headers, respt.code, respt.error |
647 | response.code, response.error | ||
648 | end | 647 | end |
649 | 648 | ||
650 | ----------------------------------------------------------------------------- | 649 | ----------------------------------------------------------------------------- |
@@ -662,11 +661,14 @@ end | |||
662 | -- code: server response status code, or nil if failed | 661 | -- code: server response status code, or nil if failed |
663 | -- error: error message, or nil if successfull | 662 | -- error: error message, or nil if successfull |
664 | ----------------------------------------------------------------------------- | 663 | ----------------------------------------------------------------------------- |
665 | function Public.post(url_or_request, body) | 664 | function post(url_or_request, body) |
666 | local request = Private.build_request(url_or_request) | 665 | local reqt = build_request(url_or_request) |
667 | request.method = "POST" | 666 | reqt.method = "POST" |
668 | request.body = request.body or body | 667 | reqt.body = reqt.body or body |
669 | local response = Public.request(request) | 668 | reqt.headers = reqt.headers or |
670 | return response.body, response.headers, | 669 | { ["content-length"] = string.len(reqt.body) } |
671 | response.code, response.error | 670 | local respt = request(reqt) |
671 | return respt.body, respt.headers, respt.code, respt.error | ||
672 | end | 672 | end |
673 | |||
674 | return http | ||
diff --git a/src/socket.h b/src/socket.h index c7db5f2..cea9e0d 100644 --- a/src/socket.h +++ b/src/socket.h | |||
@@ -37,6 +37,7 @@ int sock_accept(p_sock ps, p_sock pa, SA *addr, socklen_t *addr_len, | |||
37 | const char *sock_connect(p_sock ps, SA *addr, socklen_t addr_len); | 37 | const char *sock_connect(p_sock ps, SA *addr, socklen_t addr_len); |
38 | const char *sock_bind(p_sock ps, SA *addr, socklen_t addr_len); | 38 | const char *sock_bind(p_sock ps, SA *addr, socklen_t addr_len); |
39 | void sock_listen(p_sock ps, int backlog); | 39 | void sock_listen(p_sock ps, int backlog); |
40 | void sock_shutdown(p_sock ps, int how); | ||
40 | int sock_send(p_sock ps, const char *data, size_t count, | 41 | int sock_send(p_sock ps, const char *data, size_t count, |
41 | size_t *sent, int timeout); | 42 | size_t *sent, int timeout); |
42 | int sock_recv(p_sock ps, char *data, size_t count, | 43 | int sock_recv(p_sock ps, char *data, size_t count, |
@@ -25,6 +25,7 @@ static int meth_bind(lua_State *L); | |||
25 | static int meth_send(lua_State *L); | 25 | static int meth_send(lua_State *L); |
26 | static int meth_getsockname(lua_State *L); | 26 | static int meth_getsockname(lua_State *L); |
27 | static int meth_getpeername(lua_State *L); | 27 | static int meth_getpeername(lua_State *L); |
28 | static int meth_shutdown(lua_State *L); | ||
28 | static int meth_receive(lua_State *L); | 29 | static int meth_receive(lua_State *L); |
29 | static int meth_accept(lua_State *L); | 30 | static int meth_accept(lua_State *L); |
30 | static int meth_close(lua_State *L); | 31 | static int meth_close(lua_State *L); |
@@ -49,6 +50,7 @@ static luaL_reg tcp[] = { | |||
49 | {"getsockname", meth_getsockname}, | 50 | {"getsockname", meth_getsockname}, |
50 | {"settimeout", meth_settimeout}, | 51 | {"settimeout", meth_settimeout}, |
51 | {"close", meth_close}, | 52 | {"close", meth_close}, |
53 | {"shutdown", meth_shutdown}, | ||
52 | {"setoption", meth_setoption}, | 54 | {"setoption", meth_setoption}, |
53 | {"__gc", meth_close}, | 55 | {"__gc", meth_close}, |
54 | {"fd", meth_fd}, | 56 | {"fd", meth_fd}, |
@@ -201,12 +203,12 @@ static int meth_accept(lua_State *L) | |||
201 | int err = IO_ERROR; | 203 | int err = IO_ERROR; |
202 | p_tcp server = (p_tcp) aux_checkclass(L, "tcp{server}", 1); | 204 | p_tcp server = (p_tcp) aux_checkclass(L, "tcp{server}", 1); |
203 | p_tm tm = &server->tm; | 205 | p_tm tm = &server->tm; |
204 | p_tcp client = lua_newuserdata(L, sizeof(t_tcp)); | 206 | p_tcp client; |
205 | aux_setclass(L, "tcp{client}", -1); | 207 | t_sock sock; |
206 | tm_markstart(tm); | 208 | tm_markstart(tm); |
207 | /* loop until connection accepted or timeout happens */ | 209 | /* loop until connection accepted or timeout happens */ |
208 | while (err != IO_DONE) { | 210 | while (err != IO_DONE) { |
209 | err = sock_accept(&server->sock, &client->sock, | 211 | err = sock_accept(&server->sock, &sock, |
210 | (SA *) &addr, &addr_len, tm_getfailure(tm)); | 212 | (SA *) &addr, &addr_len, tm_getfailure(tm)); |
211 | if (err == IO_CLOSED || (err == IO_TIMEOUT && !tm_getfailure(tm))) { | 213 | if (err == IO_CLOSED || (err == IO_TIMEOUT && !tm_getfailure(tm))) { |
212 | lua_pushnil(L); | 214 | lua_pushnil(L); |
@@ -214,6 +216,9 @@ static int meth_accept(lua_State *L) | |||
214 | return 2; | 216 | return 2; |
215 | } | 217 | } |
216 | } | 218 | } |
219 | client = lua_newuserdata(L, sizeof(t_tcp)); | ||
220 | aux_setclass(L, "tcp{client}", -1); | ||
221 | client->sock = sock; | ||
217 | /* initialize remaining structure fields */ | 222 | /* initialize remaining structure fields */ |
218 | io_init(&client->io, (p_send) sock_send, (p_recv) sock_recv, &client->sock); | 223 | io_init(&client->io, (p_send) sock_send, (p_recv) sock_recv, &client->sock); |
219 | tm_init(&client->tm, -1, -1); | 224 | tm_init(&client->tm, -1, -1); |
@@ -273,6 +278,33 @@ static int meth_close(lua_State *L) | |||
273 | } | 278 | } |
274 | 279 | ||
275 | /*-------------------------------------------------------------------------*\ | 280 | /*-------------------------------------------------------------------------*\ |
281 | * Shuts the connection down | ||
282 | \*-------------------------------------------------------------------------*/ | ||
283 | static int meth_shutdown(lua_State *L) | ||
284 | { | ||
285 | p_tcp tcp = (p_tcp) aux_checkgroup(L, "tcp{any}", 1); | ||
286 | const char *how = luaL_optstring(L, 2, "both"); | ||
287 | switch (how[0]) { | ||
288 | case 'b': | ||
289 | if (strcmp(how, "both")) goto error; | ||
290 | sock_shutdown(&tcp->sock, 2); | ||
291 | break; | ||
292 | case 's': | ||
293 | if (strcmp(how, "send")) goto error; | ||
294 | sock_shutdown(&tcp->sock, 1); | ||
295 | break; | ||
296 | case 'r': | ||
297 | if (strcmp(how, "receive")) goto error; | ||
298 | sock_shutdown(&tcp->sock, 0); | ||
299 | break; | ||
300 | } | ||
301 | return 0; | ||
302 | error: | ||
303 | luaL_argerror(L, 2, "invalid shutdown method"); | ||
304 | return 0; | ||
305 | } | ||
306 | |||
307 | /*-------------------------------------------------------------------------*\ | ||
276 | * Just call inet methods | 308 | * Just call inet methods |
277 | \*-------------------------------------------------------------------------*/ | 309 | \*-------------------------------------------------------------------------*/ |
278 | static int meth_getpeername(lua_State *L) | 310 | static int meth_getpeername(lua_State *L) |
diff --git a/src/usocket.c b/src/usocket.c index b120d7b..f2d9f01 100644 --- a/src/usocket.c +++ b/src/usocket.c | |||
@@ -73,6 +73,14 @@ void sock_listen(p_sock ps, int backlog) | |||
73 | } | 73 | } |
74 | 74 | ||
75 | /*-------------------------------------------------------------------------*\ | 75 | /*-------------------------------------------------------------------------*\ |
76 | * | ||
77 | \*-------------------------------------------------------------------------*/ | ||
78 | void sock_shutdown(p_sock ps, int how) | ||
79 | { | ||
80 | shutdown(*ps, how); | ||
81 | } | ||
82 | |||
83 | /*-------------------------------------------------------------------------*\ | ||
76 | * Accept with timeout | 84 | * Accept with timeout |
77 | \*-------------------------------------------------------------------------*/ | 85 | \*-------------------------------------------------------------------------*/ |
78 | int sock_accept(p_sock ps, p_sock pa, SA *addr, socklen_t *addr_len, | 86 | int sock_accept(p_sock ps, p_sock pa, SA *addr, socklen_t *addr_len, |
@@ -100,39 +108,47 @@ int sock_accept(p_sock ps, p_sock pa, SA *addr, socklen_t *addr_len, | |||
100 | 108 | ||
101 | /*-------------------------------------------------------------------------*\ | 109 | /*-------------------------------------------------------------------------*\ |
102 | * Send with timeout | 110 | * Send with timeout |
111 | * Here we exchanged the order of the calls to write and select | ||
112 | * The idea is that the outer loop (whoever is calling sock_send) | ||
113 | * will call the function again if we didn't time out, so we can | ||
114 | * call write and then select only if it fails. | ||
115 | * Should speed things up! | ||
116 | * We are also treating EINTR and EPIPE errors. | ||
103 | \*-------------------------------------------------------------------------*/ | 117 | \*-------------------------------------------------------------------------*/ |
104 | int sock_send(p_sock ps, const char *data, size_t count, size_t *sent, | 118 | int sock_send(p_sock ps, const char *data, size_t count, size_t *sent, |
105 | int timeout) | 119 | int timeout) |
106 | { | 120 | { |
107 | t_sock sock = *ps; | 121 | t_sock sock = *ps; |
108 | struct timeval tv; | 122 | ssize_t put; |
109 | fd_set fds; | ||
110 | ssize_t put = 0; | ||
111 | int err; | ||
112 | int ret; | 123 | int ret; |
124 | /* avoid making system calls on closed sockets */ | ||
113 | if (sock == SOCK_INVALID) return IO_CLOSED; | 125 | if (sock == SOCK_INVALID) return IO_CLOSED; |
114 | tv.tv_sec = timeout / 1000; | 126 | /* make sure we repeat in case the call was interrupted */ |
115 | tv.tv_usec = (timeout % 1000) * 1000; | 127 | do put = write(sock, data, count); |
116 | FD_ZERO(&fds); | 128 | while (put <= 0 && errno == EINTR); |
117 | FD_SET(sock, &fds); | 129 | /* deal with failure */ |
118 | ret = select(sock+1, NULL, &fds, NULL, timeout >= 0 ? &tv : NULL); | 130 | if (put <= 0) { |
119 | if (ret > 0) { | 131 | /* in any case, nothing has been sent */ |
120 | put = write(sock, data, count); | ||
121 | if (put <= 0) { | ||
122 | err = IO_CLOSED; | ||
123 | #ifdef __CYGWIN__ | ||
124 | /* this is for CYGWIN, which is like Unix but has Win32 bugs */ | ||
125 | if (errno == EWOULDBLOCK) err = IO_DONE; | ||
126 | #endif | ||
127 | *sent = 0; | ||
128 | } else { | ||
129 | *sent = put; | ||
130 | err = IO_DONE; | ||
131 | } | ||
132 | return err; | ||
133 | } else { | ||
134 | *sent = 0; | 132 | *sent = 0; |
135 | return IO_TIMEOUT; | 133 | /* run select to avoid busy wait */ |
134 | if (errno != EPIPE) { | ||
135 | struct timeval tv; | ||
136 | fd_set fds; | ||
137 | tv.tv_sec = timeout / 1000; | ||
138 | tv.tv_usec = (timeout % 1000) * 1000; | ||
139 | FD_ZERO(&fds); | ||
140 | FD_SET(sock, &fds); | ||
141 | ret = select(sock+1, NULL, &fds, NULL, timeout >= 0 ? &tv : NULL); | ||
142 | /* tell the caller to call us again because there is more data */ | ||
143 | if (ret > 0) return IO_DONE; | ||
144 | /* tell the caller there was no data before timeout */ | ||
145 | else return IO_TIMEOUT; | ||
146 | /* here we know the connection has been closed */ | ||
147 | } else return IO_CLOSED; | ||
148 | /* here we sent successfully sent something */ | ||
149 | } else { | ||
150 | *sent = put; | ||
151 | return IO_DONE; | ||
136 | } | 152 | } |
137 | } | 153 | } |
138 | 154 | ||
@@ -176,32 +192,36 @@ int sock_sendto(p_sock ps, const char *data, size_t count, size_t *sent, | |||
176 | 192 | ||
177 | /*-------------------------------------------------------------------------*\ | 193 | /*-------------------------------------------------------------------------*\ |
178 | * Receive with timeout | 194 | * Receive with timeout |
195 | * Here we exchanged the order of the calls to write and select | ||
196 | * The idea is that the outer loop (whoever is calling sock_send) | ||
197 | * will call the function again if we didn't time out, so we can | ||
198 | * call write and then select only if it fails. | ||
199 | * Should speed things up! | ||
200 | * We are also treating EINTR errors. | ||
179 | \*-------------------------------------------------------------------------*/ | 201 | \*-------------------------------------------------------------------------*/ |
180 | int sock_recv(p_sock ps, char *data, size_t count, size_t *got, int timeout) | 202 | int sock_recv(p_sock ps, char *data, size_t count, size_t *got, int timeout) |
181 | { | 203 | { |
182 | t_sock sock = *ps; | 204 | t_sock sock = *ps; |
183 | struct timeval tv; | 205 | ssize_t taken; |
184 | fd_set fds; | ||
185 | int ret; | ||
186 | ssize_t taken = 0; | ||
187 | if (sock == SOCK_INVALID) return IO_CLOSED; | 206 | if (sock == SOCK_INVALID) return IO_CLOSED; |
188 | tv.tv_sec = timeout / 1000; | 207 | do taken = read(sock, data, count); |
189 | tv.tv_usec = (timeout % 1000) * 1000; | 208 | while (taken <= 0 && errno == EINTR); |
190 | FD_ZERO(&fds); | 209 | if (taken <= 0) { |
191 | FD_SET(sock, &fds); | 210 | struct timeval tv; |
192 | ret = select(sock+1, &fds, NULL, NULL, timeout >= 0 ? &tv : NULL); | 211 | fd_set fds; |
193 | if (ret > 0) { | 212 | int ret; |
194 | taken = read(sock, data, count); | ||
195 | if (taken <= 0) { | ||
196 | *got = 0; | ||
197 | return IO_CLOSED; | ||
198 | } else { | ||
199 | *got = taken; | ||
200 | return IO_DONE; | ||
201 | } | ||
202 | } else { | ||
203 | *got = 0; | 213 | *got = 0; |
204 | return IO_TIMEOUT; | 214 | if (taken == 0) return IO_CLOSED; |
215 | tv.tv_sec = timeout / 1000; | ||
216 | tv.tv_usec = (timeout % 1000) * 1000; | ||
217 | FD_ZERO(&fds); | ||
218 | FD_SET(sock, &fds); | ||
219 | ret = select(sock+1, &fds, NULL, NULL, timeout >= 0 ? &tv : NULL); | ||
220 | if (ret > 0) return IO_DONE; | ||
221 | else return IO_TIMEOUT; | ||
222 | } else { | ||
223 | *got = taken; | ||
224 | return IO_DONE; | ||
205 | } | 225 | } |
206 | } | 226 | } |
207 | 227 | ||
diff --git a/src/wsocket.c b/src/wsocket.c index 1ba28b6..59d88df 100644 --- a/src/wsocket.c +++ b/src/wsocket.c | |||
@@ -36,6 +36,14 @@ void sock_destroy(p_sock ps) | |||
36 | } | 36 | } |
37 | 37 | ||
38 | /*-------------------------------------------------------------------------*\ | 38 | /*-------------------------------------------------------------------------*\ |
39 | * | ||
40 | \*-------------------------------------------------------------------------*/ | ||
41 | void sock_shutdown(p_sock ps, int how) | ||
42 | { | ||
43 | shutdown(*ps, how); | ||
44 | } | ||
45 | |||
46 | /*-------------------------------------------------------------------------*\ | ||
39 | * Creates and sets up a socket | 47 | * Creates and sets up a socket |
40 | \*-------------------------------------------------------------------------*/ | 48 | \*-------------------------------------------------------------------------*/ |
41 | const char *sock_create(p_sock ps, int domain, int type, int protocol) | 49 | const char *sock_create(p_sock ps, int domain, int type, int protocol) |
diff --git a/test/auth/.htaccess b/test/auth/.htaccess index b9f100e..31e1123 100644 --- a/test/auth/.htaccess +++ b/test/auth/.htaccess | |||
@@ -1,4 +1,4 @@ | |||
1 | AuthName "Test Realm" | 1 | AuthName "Test Realm" |
2 | AuthType Basic | 2 | AuthType Basic |
3 | AuthUserFile /home/diego/tec/luasocket/test/auth/.htpasswd | 3 | AuthUserFile /Users/diego/tec/luasocket/test/auth/.htpasswd |
4 | require valid-user | 4 | require valid-user |
diff --git a/test/httptest.lua b/test/httptest.lua index 030974c..9d9fa25 100644 --- a/test/httptest.lua +++ b/test/httptest.lua | |||
@@ -1,8 +1,8 @@ | |||
1 | -- needs Alias from /home/c/diego/tec/luasocket/test to | 1 | -- needs Alias from /home/c/diego/tec/luasocket/test to |
2 | -- /luasocket-test | 2 | -- "/luasocket-test" and "/luasocket-test/" |
3 | -- needs ScriptAlias from /home/c/diego/tec/luasocket/test/cgi | 3 | -- needs ScriptAlias from /home/c/diego/tec/luasocket/test/cgi |
4 | -- to /luasocket-test-cgi | 4 | -- to "/luasocket-test-cgi" and "/luasocket-test-cgi/" |
5 | -- needs AllowOverride AuthConfig on /home/c/diego/tec/luasocket/test/auth | 5 | -- needs "AllowOverride AuthConfig" on /home/c/diego/tec/luasocket/test/auth |
6 | local similar = function(s1, s2) | 6 | local similar = function(s1, s2) |
7 | return string.lower(string.gsub(s1 or "", "%s", "")) == | 7 | return string.lower(string.gsub(s1 or "", "%s", "")) == |
8 | string.lower(string.gsub(s2 or "", "%s", "")) | 8 | string.lower(string.gsub(s2 or "", "%s", "")) |
@@ -31,12 +31,18 @@ local check_request = function(request, expect, ignore) | |||
31 | local response = socket.http.request(request) | 31 | local response = socket.http.request(request) |
32 | for i,v in response do | 32 | for i,v in response do |
33 | if not ignore[i] then | 33 | if not ignore[i] then |
34 | if v ~= expect[i] then fail(i .. " differs!") end | 34 | if v ~= expect[i] then |
35 | if string.len(v) < 80 then print(v) end | ||
36 | fail(i .. " differs!") | ||
37 | end | ||
35 | end | 38 | end |
36 | end | 39 | end |
37 | for i,v in expect do | 40 | for i,v in expect do |
38 | if not ignore[i] then | 41 | if not ignore[i] then |
39 | if v ~= response[i] then fail(i .. " differs!") end | 42 | if v ~= response[i] then |
43 | if string.len(v) < 80 then print(v) end | ||
44 | fail(i .. " differs!") | ||
45 | end | ||
40 | end | 46 | end |
41 | end | 47 | end |
42 | print("ok") | 48 | print("ok") |
@@ -47,15 +53,18 @@ local host, request, response, ignore, expect, index, prefix, cgiprefix | |||
47 | local t = socket.time() | 53 | local t = socket.time() |
48 | 54 | ||
49 | host = host or "localhost" | 55 | host = host or "localhost" |
50 | prefix = prefix or "/luasocket" | 56 | prefix = prefix or "/luasocket-test" |
51 | cgiprefix = cgiprefix or "/luasocket/cgi" | 57 | cgiprefix = cgiprefix or "/luasocket-test-cgi" |
52 | index = readfile("test/index.html") | 58 | index = readfile("test/index.html") |
53 | 59 | ||
54 | io.write("testing request uri correctness: ") | 60 | io.write("testing request uri correctness: ") |
55 | local forth = cgiprefix .. "/request-uri?" .. "this+is+the+query+string" | 61 | local forth = cgiprefix .. "/request-uri?" .. "this+is+the+query+string" |
56 | local back = socket.http.get("http://" .. host .. forth) | 62 | local back, h, c, e = socket.http.get("http://" .. host .. forth) |
57 | if similar(back, forth) then print("ok") | 63 | if similar(back, forth) then print("ok") |
58 | else fail("failed!") end | 64 | else |
65 | print(h, c, e) | ||
66 | fail() | ||
67 | end | ||
59 | 68 | ||
60 | io.write("testing query string correctness: ") | 69 | io.write("testing query string correctness: ") |
61 | forth = "this+is+the+query+string" | 70 | forth = "this+is+the+query+string" |
@@ -77,6 +86,38 @@ ignore = { | |||
77 | } | 86 | } |
78 | check_request(request, expect, ignore) | 87 | check_request(request, expect, ignore) |
79 | 88 | ||
89 | socket.http.get("http://" .. host .. prefix .. "/lixo.html") | ||
90 | |||
91 | io.write("testing post method: ") | ||
92 | -- wanted to test chunked post, but apache doesn't support it... | ||
93 | request = { | ||
94 | url = "http://" .. host .. cgiprefix .. "/cat", | ||
95 | method = "POST", | ||
96 | body = index, | ||
97 | -- remove content-length header to send chunked body | ||
98 | headers = { ["content-length"] = string.len(index) } | ||
99 | } | ||
100 | expect = { | ||
101 | body = index, | ||
102 | code = 200 | ||
103 | } | ||
104 | ignore = { | ||
105 | status = 1, | ||
106 | headers = 1 | ||
107 | } | ||
108 | check_request(request, expect, ignore) | ||
109 | |||
110 | io.write("testing simple post function: ") | ||
111 | body = socket.http.post("http://" .. host .. cgiprefix .. "/cat", index) | ||
112 | check(body == index) | ||
113 | |||
114 | io.write("testing simple post function with table args: ") | ||
115 | body = socket.http.post { | ||
116 | url = "http://" .. host .. cgiprefix .. "/cat", | ||
117 | body = index | ||
118 | } | ||
119 | check(body == index) | ||
120 | |||
80 | io.write("testing http redirection: ") | 121 | io.write("testing http redirection: ") |
81 | request = { | 122 | request = { |
82 | url = "http://" .. host .. prefix | 123 | url = "http://" .. host .. prefix |
@@ -175,7 +216,8 @@ io.write("testing manual basic auth: ") | |||
175 | request = { | 216 | request = { |
176 | url = "http://" .. host .. prefix .. "/auth/index.html", | 217 | url = "http://" .. host .. prefix .. "/auth/index.html", |
177 | headers = { | 218 | headers = { |
178 | authorization = "Basic " .. socket.code.base64("luasocket:password") | 219 | authorization = "Basic " .. |
220 | socket.code.base64.encode("luasocket:password") | ||
179 | } | 221 | } |
180 | } | 222 | } |
181 | expect = { | 223 | expect = { |
@@ -246,22 +288,6 @@ ignore = { | |||
246 | } | 288 | } |
247 | check_request(request, expect, ignore) | 289 | check_request(request, expect, ignore) |
248 | 290 | ||
249 | io.write("testing post method: ") | ||
250 | request = { | ||
251 | url = "http://" .. host .. cgiprefix .. "/cat", | ||
252 | method = "POST", | ||
253 | body = index | ||
254 | } | ||
255 | expect = { | ||
256 | body = index, | ||
257 | code = 200 | ||
258 | } | ||
259 | ignore = { | ||
260 | status = 1, | ||
261 | headers = 1 | ||
262 | } | ||
263 | check_request(request, expect, ignore) | ||
264 | |||
265 | io.write("testing wrong scheme: ") | 291 | io.write("testing wrong scheme: ") |
266 | request = { | 292 | request = { |
267 | url = "wrong://" .. host .. cgiprefix .. "/cat", | 293 | url = "wrong://" .. host .. cgiprefix .. "/cat", |
@@ -287,17 +313,6 @@ body = socket.http.get { | |||
287 | } | 313 | } |
288 | check(body == index) | 314 | check(body == index) |
289 | 315 | ||
290 | io.write("testing simple post function: ") | ||
291 | body = socket.http.post("http://" .. host .. cgiprefix .. "/cat", index) | ||
292 | check(body == index) | ||
293 | |||
294 | io.write("testing simple post function with table args: ") | ||
295 | body = socket.http.post { | ||
296 | url = "http://" .. host .. cgiprefix .. "/cat", | ||
297 | body = index | ||
298 | } | ||
299 | check(body == index) | ||
300 | |||
301 | io.write("testing HEAD method: ") | 316 | io.write("testing HEAD method: ") |
302 | response = socket.http.request { | 317 | response = socket.http.request { |
303 | method = "HEAD", | 318 | method = "HEAD", |
diff --git a/test/testclnt.lua b/test/testclnt.lua index 3f217bd..2420711 100644 --- a/test/testclnt.lua +++ b/test/testclnt.lua | |||
@@ -84,19 +84,19 @@ function reconnect() | |||
84 | remote [[ | 84 | remote [[ |
85 | if data then data:close() data = nil end | 85 | if data then data:close() data = nil end |
86 | data = server:accept() | 86 | data = server:accept() |
87 | -- data:setoption("nodelay", true) | 87 | data:setoption("tcp-nodelay", true) |
88 | ]] | 88 | ]] |
89 | data, err = socket.connect(host, port) | 89 | data, err = socket.connect(host, port) |
90 | if not data then fail(err) | 90 | if not data then fail(err) |
91 | else pass("connected!") end | 91 | else pass("connected!") end |
92 | -- data:setoption("nodelay", true) | 92 | data:setoption("tcp-nodelay", true) |
93 | end | 93 | end |
94 | 94 | ||
95 | pass("attempting control connection...") | 95 | pass("attempting control connection...") |
96 | control, err = socket.connect(host, port) | 96 | control, err = socket.connect(host, port) |
97 | if err then fail(err) | 97 | if err then fail(err) |
98 | else pass("connected!") end | 98 | else pass("connected!") end |
99 | -- control:setoption("nodelay", true) | 99 | control:setoption("tcp-nodelay", true) |
100 | 100 | ||
101 | ------------------------------------------------------------------------ | 101 | ------------------------------------------------------------------------ |
102 | test("method registration") | 102 | test("method registration") |