2
2
-- HTTP/1.1 client support for the Lua language.
3
3
-- LuaSocket toolkit.
4
4
-- Author: Diego Nehab
5
- -- RCS ID: $Id: http.lua,v 1.67 2006/04/03 03:10:56 diego Exp $
5
+ -- RCS ID: $Id: http.lua,v 1.70 2007/03/12 04:08:40 diego Exp $
6
6
---- -------------------------------------------------------------------------
7
7
8
8
---- -------------------------------------------------------------------------
@@ -15,7 +15,6 @@ local mime = require("mime")
15
15
local string = require (" string" )
16
16
local base = _G
17
17
local table = require (" table" )
18
- local print = print
19
18
module (" socket.http" )
20
19
21
20
---- -------------------------------------------------------------------------
@@ -108,7 +107,7 @@ local metat = { __index = {} }
108
107
109
108
function open (host , port , create )
110
109
-- create socket with user connect function, or with default
111
- local c = socket .try (create or socket .tcp )()
110
+ local c = socket .try (( create or socket .tcp )() )
112
111
local h = base .setmetatable ({ c = c }, metat )
113
112
-- create finalized try
114
113
h .try = socket .newtry (function () h :close () end )
@@ -143,7 +142,12 @@ function metat.__index:sendbody(headers, source, step)
143
142
end
144
143
145
144
function metat .__index :receivestatusline ()
146
- local status = self .try (self .c :receive ())
145
+ local status = self .try (self .c :receive (5 ))
146
+ -- identify HTTP/0.9 responses, which do not contain a status line
147
+ -- this is just a heuristic, but is what the RFC recommends
148
+ if status ~= " HTTP/" then return nil , status end
149
+ -- otherwise proceed reading a status line
150
+ status = self .try (self .c :receive (" *l" , status ))
147
151
local code = socket .skip (2 , string.find (status , " HTTP/%d*%.%d* (%d%d%d)" ))
148
152
return self .try (base .tonumber (code ), status )
149
153
end
@@ -164,6 +168,12 @@ function metat.__index:receivebody(headers, sink, step)
164
168
sink , step ))
165
169
end
166
170
171
+ function metat .__index :receive09body (status , sink , step )
172
+ local source = ltn12 .source .rewind (socket .source (" until-closed" , self .c ))
173
+ source (status )
174
+ return self .try (ltn12 .pump .all (source , sink , step ))
175
+ end
176
+
167
177
function metat .__index :close ()
168
178
return self .c :close ()
169
179
end
@@ -229,7 +239,8 @@ local function adjustrequest(reqt)
229
239
-- explicit components override url
230
240
for i ,v in base .pairs (reqt ) do nreqt [i ] = v end
231
241
if nreqt .port == " " then nreqt .port = 80 end
232
- socket .try (nreqt .host , " invalid host '" .. base .tostring (nreqt .host ) .. " '" )
242
+ socket .try (nreqt .host and nreqt .host ~= " " ,
243
+ " invalid host '" .. base .tostring (nreqt .host ) .. " '" )
233
244
-- compute uri if user hasn't overriden
234
245
nreqt .uri = reqt .uri or adjusturi (nreqt )
235
246
-- ajust host and port if there is a proxy
@@ -262,7 +273,7 @@ function tredirect(reqt, location)
262
273
local result , code , headers , status = trequest {
263
274
-- the RFC says the redirect URL has to be absolute, but some
264
275
-- servers do not respect that
265
- url = url .absolute (reqt , location ),
276
+ url = url .absolute (reqt . url , location ),
266
277
source = reqt .source ,
267
278
sink = reqt .sink ,
268
279
headers = reqt .headers ,
@@ -271,38 +282,45 @@ function tredirect(reqt, location)
271
282
create = reqt .create
272
283
}
273
284
-- pass location header back as a hint we redirected
285
+ headers = headers or {}
274
286
headers .location = headers .location or location
275
287
return result , code , headers , status
276
288
end
277
289
278
290
function trequest (reqt )
279
291
-- we loop until we get what we want, or
280
292
-- until we are sure there is no way to get it
281
- reqt = adjustrequest (reqt )
282
- local h = open (reqt .host , reqt .port , reqt .create )
293
+ local nreqt = adjustrequest (reqt )
294
+ local h = open (nreqt .host , nreqt .port , nreqt .create )
283
295
-- send request line and headers
284
- h :sendrequestline (reqt .method , reqt .uri )
285
- h :sendheaders (reqt .headers )
286
- local code = 100
287
- local headers , status
288
- -- if there is a body, check for server status
289
- if reqt .source then
290
- h :sendbody (reqt .headers , reqt .source , reqt .step )
296
+ h :sendrequestline (nreqt .method , nreqt .uri )
297
+ h :sendheaders (nreqt .headers )
298
+ -- if there is a body, send it
299
+ if nreqt .source then
300
+ h :sendbody (nreqt .headers , nreqt .source , nreqt .step )
301
+ end
302
+ local code , status = h :receivestatusline ()
303
+ -- if it is an HTTP/0.9 server, simply get the body and we are done
304
+ if not code then
305
+ h :receive09body (status , nreqt .sink , nreqt .step )
306
+ return 1 , 200
291
307
end
308
+ local headers
292
309
-- ignore any 100-continue messages
293
310
while code == 100 do
294
- code , status = h :receivestatusline ()
295
311
headers = h :receiveheaders ()
312
+ code , status = h :receivestatusline ()
296
313
end
314
+ headers = h :receiveheaders ()
297
315
-- at this point we should have a honest reply from the server
298
316
-- we can't redirect if we already used the source, so we report the error
299
- if shouldredirect (reqt , code , headers ) and not reqt .source then
317
+ if shouldredirect (nreqt , code , headers ) and not nreqt .source then
300
318
h :close ()
301
319
return tredirect (reqt , headers .location )
302
320
end
303
321
-- here we are finally done
304
- if shouldreceivebody (reqt , code ) then
305
- h :receivebody (headers , reqt .sink , reqt .step )
322
+ if shouldreceivebody (nreqt , code ) then
323
+ h :receivebody (headers , nreqt .sink , nreqt .step )
306
324
end
307
325
h :close ()
308
326
return 1 , code , headers , status
@@ -330,4 +348,3 @@ request = socket.protect(function(reqt, body)
330
348
if base .type (reqt ) == " string" then return srequest (reqt , body )
331
349
else return trequest (reqt ) end
332
350
end )
333
-
0 commit comments