2 local coroutine = coroutine;
3 local tonumber = tonumber;
5 local deadroutine = coroutine.create(function() end);
6 coroutine.resume(deadroutine);
10 local function parser(data, success_cb)
11 local function readline()
12 if not data then coroutine.yield("Unexpected EOF"); end
13 local pos, line = (data:find("\r\n", nil, true));
15 local newdata = coroutine.yield();
16 if not newdata then data = nil; coroutine.yield("Unexpected EOF"); end
20 line, data = data:sub(1, pos-1), data:sub(pos+2);
23 local function readlength(n)
24 if not data then coroutine.yield("Unexpected EOF"); end
26 local newdata = coroutine.yield();
27 if not newdata then data = nil; coroutine.yield("Unexpected EOF"); end
30 local r = data:sub(1, n);
31 data = data:sub(n + 1);
37 local status_line = readline();
38 local method, path, httpversion = status_line:match("^(%S+)%s+(%S+)%s+HTTP/(%S+)$");
39 if not method then coroutine.yield("invalid-status-line"); end
42 local headers = {}; -- read headers
44 local line = readline();
45 if line == "" then break; end -- headers done
46 local key, val = line:match("^([^%s:]+): *(.*)$");
47 if not key then coroutine.yield("invalid-header-line"); end -- TODO handle multi-line and invalid headers
49 headers[key] = headers[key] and headers[key]..","..val or val;
53 local len = tonumber(headers["content-length"]);
54 len = len or 0; -- TODO check for invalid len
55 local body = readlength(len);
60 httpversion = httpversion;
67 function new(success_cb, error_cb)
68 local co = coroutine.create(parser);
70 feed = function(self, data)
71 local success, result = coroutine.resume(co, data, success_cb);