util.httpstream: Fixed a nil variable access introduced in the last commit.
[prosody.git] / util / httpstream.lua
1
2 local coroutine = coroutine;
3 local tonumber = tonumber;
4
5 local deadroutine = coroutine.create(function() end);
6 coroutine.resume(deadroutine);
7
8 module("httpstream")
9
10 local function parser(data, success_cb)
11         local function readline()
12                 local pos = data:find("\r\n", nil, true);
13                 while not pos do
14                         data = data..coroutine.yield();
15                         pos = data:find("\r\n", nil, true);
16                 end
17                 local r = data:sub(1, pos-1);
18                 data = data:sub(pos+2);
19                 return r;
20         end
21         local function readlength(n)
22                 while #data < n do
23                         data = data..coroutine.yield();
24                 end
25                 local r = data:sub(1, n);
26                 data = data:sub(n + 1);
27                 return r;
28         end
29         local function readheaders()
30                 local headers = {}; -- read headers
31                 while true do
32                         local line = readline();
33                         if line == "" then break; end -- headers done
34                         local key, val = line:match("^([^%s:]+): *(.*)$");
35                         if not key then coroutine.yield("invalid-header-line"); end -- TODO handle multi-line and invalid headers
36                         key = key:lower();
37                         headers[key] = headers[key] and headers[key]..","..val or val;
38                 end
39                 return headers;
40         end
41         
42         while true do
43                 -- read status line
44                 local status_line = readline();
45                 local method, path, httpversion = status_line:match("^(%S+)%s+(%S+)%s+HTTP/(%S+)$");
46                 if not method then coroutine.yield("invalid-status-line"); end
47                 -- TODO parse url
48                 local headers = readheaders();
49                 
50                 -- read body
51                 local len = tonumber(headers["content-length"]);
52                 len = len or 0; -- TODO check for invalid len
53                 local body = readlength(len);
54                 
55                 success_cb({
56                         method = method;
57                         path = path;
58                         httpversion = httpversion;
59                         headers = headers;
60                         body = body;
61                 });
62         end
63 end
64
65 function new(success_cb, error_cb)
66         local co = coroutine.create(parser);
67         return {
68                 feed = function(self, data)
69                         if not data then
70                                 co = deadroutine;
71                                 return error_cb();
72                         end
73                         local success, result = coroutine.resume(co, data, success_cb);
74                         if result then
75                                 co = deadroutine;
76                                 return error_cb(result);
77                         end
78                 end;
79         };
80 end
81
82 return _M;