aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDiego Nehab <diego@tecgraf.puc-rio.br>2004-03-26 06:05:20 +0000
committerDiego Nehab <diego@tecgraf.puc-rio.br>2004-03-26 06:05:20 +0000
commite5a090b01cd5b490f7331235b7c58c36c05bb7b6 (patch)
tree8f98ae6217650ac0b9d1c04f1ad1d3fb77af3d37
parente77f1792005088f55353c6c85fe9335e60772754 (diff)
downloadluasocket-e5a090b01cd5b490f7331235b7c58c36c05bb7b6.tar.gz
luasocket-e5a090b01cd5b490f7331235b7c58c36c05bb7b6.tar.bz2
luasocket-e5a090b01cd5b490f7331235b7c58c36c05bb7b6.zip
Using socket pumps in http.lua.
Adjusted socket.try.
-rw-r--r--TODO1
-rw-r--r--etc/b64.lua2
-rw-r--r--src/http.lua138
-rw-r--r--src/mime.lua22
-rw-r--r--test/httptest.lua2
5 files changed, 41 insertions, 124 deletions
diff --git a/TODO b/TODO
index 79a15af..1e30a78 100644
--- a/TODO
+++ b/TODO
@@ -27,6 +27,7 @@ falar sobre encodet/wrapt/decodet no manual sobre mime
27RECEIVE MUDOU!!! COLOCAR NO MANUAL. 27RECEIVE MUDOU!!! COLOCAR NO MANUAL.
28HTTP.lua mudou bastante também. 28HTTP.lua mudou bastante também.
29 29
30fazer com que a socket.source e socket.sink sejam "selectable".
30 31
31change mime.eol to output marker on detection of first candidate, instead 32change mime.eol to output marker on detection of first candidate, instead
32of on the second. that way it works in one pass for strings that end with 33of on the second. that way it works in one pass for strings that end with
diff --git a/etc/b64.lua b/etc/b64.lua
index ea157c4..3f861b4 100644
--- a/etc/b64.lua
+++ b/etc/b64.lua
@@ -9,4 +9,4 @@ else
9 convert = ltn12.filter.chain(base64, wrap) 9 convert = ltn12.filter.chain(base64, wrap)
10end 10end
11source = ltn12.source.chain(source, convert) 11source = ltn12.source.chain(source, convert)
12ltn12.pump(source, sink) 12repeat until not ltn12.pump(source, sink)
diff --git a/src/http.lua b/src/http.lua
index 8b06184..da18aaf 100644
--- a/src/http.lua
+++ b/src/http.lua
@@ -51,7 +51,7 @@ local function receive_headers(reqt, respt, tmp)
51 while line ~= "" do 51 while line ~= "" do
52 -- get field-name and value 52 -- get field-name and value
53 _, _, name, value = string.find(line, "^(.-):%s*(.*)") 53 _, _, name, value = string.find(line, "^(.-):%s*(.*)")
54 assert(name and value, "malformed reponse headers") 54 socket.try(name and value, "malformed reponse headers")
55 name = string.lower(name) 55 name = string.lower(name)
56 -- get next line (value might be folded) 56 -- get next line (value might be folded)
57 line = socket.try(sock:receive()) 57 line = socket.try(sock:receive())
@@ -66,119 +66,32 @@ local function receive_headers(reqt, respt, tmp)
66 end 66 end
67end 67end
68 68
69local function abort(cb, err)
70 local go, cb_err = cb(nil, err)
71 error(cb_err or err)
72end
73
74local function hand(cb, chunk)
75 local go, cb_err = cb(chunk)
76 assert(go, cb_err or "aborted by callback")
77end
78
79local function receive_body_bychunks(sock, sink)
80 while 1 do
81 -- get chunk size, skip extention
82 local line, err = sock:receive()
83 if err then abort(sink, err) end
84 local size = tonumber(string.gsub(line, ";.*", ""), 16)
85 if not size then abort(sink, "invalid chunk size") end
86 -- was it the last chunk?
87 if size <= 0 then break end
88 -- get chunk
89 local chunk, err = sock:receive(size)
90 if err then abort(sink, err) end
91 -- pass chunk to callback
92 hand(sink, chunk)
93 -- skip CRLF on end of chunk
94 err = second(sock:receive())
95 if err then abort(sink, err) end
96 end
97 -- let callback know we are done
98 hand(sink, nil)
99 -- servers shouldn't send trailer headers, but who trusts them?
100 local line = socket.try(sock:receive())
101 while line ~= "" do
102 line = socket.try(sock:receive())
103 end
104end
105
106local function receive_body_bylength(sock, length, sink)
107 while length > 0 do
108 local size = math.min(BLOCKSIZE, length)
109 local chunk, err = sock:receive(size)
110 if err then abort(sink, err) end
111 length = length - string.len(chunk)
112 -- see if there was an error
113 hand(sink, chunk)
114 end
115 -- let callback know we are done
116 hand(sink, nil)
117end
118
119local function receive_body_untilclosed(sock, sink)
120 while true do
121 local chunk, err, partial = sock:receive(BLOCKSIZE)
122 -- see if we are done
123 if err == "closed" then
124 hand(sink, partial)
125 break
126 end
127 hand(sink, chunk)
128 -- see if there was an error
129 if err then abort(sink, err) end
130 end
131 -- let callback know we are done
132 hand(sink, nil)
133end
134
135local function receive_body(reqt, respt, tmp) 69local function receive_body(reqt, respt, tmp)
136 local sink = reqt.sink or ltn12.sink.null() 70 local sink = reqt.sink or ltn12.sink.null()
137 local headers = respt.headers 71 local pump = reqt.pump or ltn12.pump
138 local sock = tmp.sock 72 local source
139 local te = headers["transfer-encoding"] 73 local te = respt.headers["transfer-encoding"]
140 if te and te ~= "identity" then 74 if te and te ~= "identity" then
141 -- get by chunked transfer-coding of message body 75 -- get by chunked transfer-coding of message body
142 receive_body_bychunks(sock, sink) 76 source = socket.source("http-chunked", tmp.sock)
143 elseif tonumber(headers["content-length"]) then 77 elseif tonumber(respt.headers["content-length"]) then
144 -- get by content-length 78 -- get by content-length
145 local length = tonumber(headers["content-length"]) 79 local length = tonumber(respt.headers["content-length"])
146 receive_body_bylength(sock, length, sink) 80 source = socket.source("by-length", tmp.sock, length)
147 else 81 else
148 -- get it all until connection closes 82 -- get it all until connection closes
149 receive_body_untilclosed(sock, sink) 83 source = socket.source("until-closed", tmp.sock)
150 end
151end
152
153local function send_body_bychunks(data, source)
154 while true do
155 local chunk, err = source()
156 assert(chunk or not err, err)
157 if not chunk then break end
158 socket.try(data:send(string.format("%X\r\n", string.len(chunk))))
159 socket.try(data:send(chunk, "\r\n"))
160 end
161 socket.try(data:send("0\r\n\r\n"))
162end
163
164local function send_body(data, source)
165 while true do
166 local chunk, err = source()
167 assert(chunk or not err, err)
168 if not chunk then break end
169 socket.try(data:send(chunk))
170 end 84 end
85 socket.try(pump(source, sink))
171end 86end
172 87
173local function send_headers(sock, headers) 88local function send_headers(sock, headers)
174 -- send request headers 89 -- send request headers
175 for i, v in pairs(headers) do 90 for i, v in pairs(headers) do
176 socket.try(sock:send(i .. ": " .. v .. "\r\n")) 91 socket.try(sock:send(i .. ": " .. v .. "\r\n"))
177--io.write(i .. ": " .. v .. "\r\n")
178 end 92 end
179 -- mark end of request headers 93 -- mark end of request headers
180 socket.try(sock:send("\r\n")) 94 socket.try(sock:send("\r\n"))
181--io.write("\r\n")
182end 95end
183 96
184local function should_receive_body(reqt, respt, tmp) 97local function should_receive_body(reqt, respt, tmp)
@@ -211,22 +124,21 @@ end
211 124
212local function send_request(reqt, respt, tmp) 125local function send_request(reqt, respt, tmp)
213 local uri = request_uri(reqt, respt, tmp) 126 local uri = request_uri(reqt, respt, tmp)
214 local sock = tmp.sock
215 local headers = tmp.headers 127 local headers = tmp.headers
128 local pump = reqt.pump or ltn12.pump
216 -- send request line 129 -- send request line
217 socket.try(sock:send((reqt.method or "GET") 130 socket.try(tmp.sock:send((reqt.method or "GET")
218 .. " " .. uri .. " HTTP/1.1\r\n")) 131 .. " " .. uri .. " HTTP/1.1\r\n"))
219--io.write((reqt.method or "GET")
220 --.. " " .. uri .. " HTTP/1.1\r\n")
221 -- send request headers headeres
222 if reqt.source and not headers["content-length"] then 132 if reqt.source and not headers["content-length"] then
223 headers["transfer-encoding"] = "chunked" 133 headers["transfer-encoding"] = "chunked"
224 end 134 end
225 send_headers(sock, headers) 135 send_headers(tmp.sock, headers)
226 -- send request message body, if any 136 -- send request message body, if any
227 if reqt.source then 137 if not reqt.source then return end
228 if headers["content-length"] then send_body(sock, reqt.source) 138 if headers["content-length"] then
229 else send_body_bychunks(sock, reqt.source) end 139 socket.try(pump(reqt.source, socket.sink(tmp.sock)))
140 else
141 socket.try(pump(reqt.source, socket.sink("http-chunked", tmp.sock)))
230 end 142 end
231end 143end
232 144
@@ -235,7 +147,7 @@ local function open(reqt, respt, tmp)
235 local host, port 147 local host, port
236 if proxy then 148 if proxy then
237 local pproxy = socket.url.parse(proxy) 149 local pproxy = socket.url.parse(proxy)
238 assert(pproxy.port and pproxy.host, "invalid proxy") 150 socket.try(pproxy.port and pproxy.host, "invalid proxy")
239 host, port = pproxy.host, pproxy.port 151 host, port = pproxy.host, pproxy.port
240 else 152 else
241 host, port = tmp.parsed.host, tmp.parsed.port 153 host, port = tmp.parsed.host, tmp.parsed.port
@@ -271,9 +183,8 @@ local function parse_url(reqt, respt, tmp)
271 scheme = "http" 183 scheme = "http"
272 }) 184 })
273 -- scheme has to be http 185 -- scheme has to be http
274 if parsed.scheme ~= "http" then 186 socket.try(parsed.scheme == "http",
275 error(string.format("unknown scheme '%s'", parsed.scheme)) 187 string.format("unknown scheme '%s'", parsed.scheme))
276 end
277 -- explicit authentication info overrides that given by the URL 188 -- explicit authentication info overrides that given by the URL
278 parsed.user = reqt.user or parsed.user 189 parsed.user = reqt.user or parsed.user
279 parsed.password = reqt.password or parsed.password 190 parsed.password = reqt.password or parsed.password
@@ -342,6 +253,12 @@ local function redirect(reqt, respt, tmp)
342 if respt.headers then respt.headers.location = redirt.url end 253 if respt.headers then respt.headers.location = redirt.url end
343end 254end
344 255
256local function skip_continue(reqt, respt, tmp)
257 if respt.code == 100 then
258 receive_status(reqt, respt, tmp)
259 end
260end
261
345-- execute a request of through an exception 262-- execute a request of through an exception
346function request_p(reqt, respt, tmp) 263function request_p(reqt, respt, tmp)
347 parse_url(reqt, respt, tmp) 264 parse_url(reqt, respt, tmp)
@@ -349,6 +266,7 @@ function request_p(reqt, respt, tmp)
349 open(reqt, respt, tmp) 266 open(reqt, respt, tmp)
350 send_request(reqt, respt, tmp) 267 send_request(reqt, respt, tmp)
351 receive_status(reqt, respt, tmp) 268 receive_status(reqt, respt, tmp)
269 skip_continue(reqt, respt, tmp)
352 receive_headers(reqt, respt, tmp) 270 receive_headers(reqt, respt, tmp)
353 if should_redirect(reqt, respt, tmp) then 271 if should_redirect(reqt, respt, tmp) then
354 tmp.sock:close() 272 tmp.sock:close()
diff --git a/src/mime.lua b/src/mime.lua
index 8c2a5c0..d263d48 100644
--- a/src/mime.lua
+++ b/src/mime.lua
@@ -11,11 +11,14 @@ decodet = {}
11wrapt = {} 11wrapt = {}
12 12
13-- creates a function that chooses a filter by name from a given table 13-- creates a function that chooses a filter by name from a given table
14local function choose(table) 14function choose(table)
15 return function(name, opt) 15 return function(name, opt1, opt2)
16 if type(name) ~= "string" then
17 name, opt1, opt2 = "default", name, opt1
18 end
16 local f = table[name or "nil"] 19 local f = table[name or "nil"]
17 if not f then error("unknown filter (" .. tostring(name) .. ")", 3) 20 if not f then error("unknown key (" .. tostring(name) .. ")", 3)
18 else return f(opt) end 21 else return f(opt1, opt2) end
19 end 22 end
20end 23end
21 24
@@ -44,6 +47,7 @@ wrapt['text'] = function(length)
44 return ltn12.filter.cycle(wrp, length, length) 47 return ltn12.filter.cycle(wrp, length, length)
45end 48end
46wrapt['base64'] = wrapt['text'] 49wrapt['base64'] = wrapt['text']
50wrapt['default'] = wrapt['text']
47 51
48wrapt['quoted-printable'] = function() 52wrapt['quoted-printable'] = function()
49 return ltn12.filter.cycle(qpwrp, 76, 76) 53 return ltn12.filter.cycle(qpwrp, 76, 76)
@@ -52,15 +56,7 @@ end
52-- function that choose the encoding, decoding or wrap algorithm 56-- function that choose the encoding, decoding or wrap algorithm
53encode = choose(encodet) 57encode = choose(encodet)
54decode = choose(decodet) 58decode = choose(decodet)
55-- it's different because there is a default wrap filter 59wrap = choose(wrapt)
56local cwt = choose(wrapt)
57function wrap(mode_or_length, length)
58 if type(mode_or_length) ~= "string" then
59 length = mode_or_length
60 mode_or_length = "text"
61 end
62 return cwt(mode_or_length, length)
63end
64 60
65-- define the end-of-line normalization filter 61-- define the end-of-line normalization filter
66function normalize(marker) 62function normalize(marker)
diff --git a/test/httptest.lua b/test/httptest.lua
index 4ec0cc1..c16ef2b 100644
--- a/test/httptest.lua
+++ b/test/httptest.lua
@@ -411,6 +411,7 @@ local body
411io.write("testing simple get function: ") 411io.write("testing simple get function: ")
412body = socket.http.get("http://" .. host .. prefix .. "/index.html") 412body = socket.http.get("http://" .. host .. prefix .. "/index.html")
413assert(body == index) 413assert(body == index)
414print("ok")
414 415
415------------------------------------------------------------------------ 416------------------------------------------------------------------------
416io.write("testing HEAD method: ") 417io.write("testing HEAD method: ")
@@ -420,6 +421,7 @@ response = socket.http.request {
420 url = "http://www.cs.princeton.edu/~diego/" 421 url = "http://www.cs.princeton.edu/~diego/"
421} 422}
422assert(response and response.headers) 423assert(response and response.headers)
424print("ok")
423 425
424------------------------------------------------------------------------ 426------------------------------------------------------------------------
425print("passed all tests") 427print("passed all tests")