Software /
code /
prosody
Comparison
util/httpstream.lua @ 3494:0f185563a4e4
util.httpstream: Initial commit of the new HTTP parser.
author | Waqas Hussain <waqas20@gmail.com> |
---|---|
date | Sat, 04 Sep 2010 17:44:13 +0500 |
child | 3495:bd7699a6d536 |
comparison
equal
deleted
inserted
replaced
3493:f2b6115b531b | 3494:0f185563a4e4 |
---|---|
1 | |
2 local setmetatable = setmetatable; | |
3 local coroutine = coroutine; | |
4 local tonumber = tonumber; | |
5 | |
6 local print = print; | |
7 local error = error; | |
8 local ser = require "util.serialization".serialize; | |
9 | |
10 local deadroutine = coroutine.create(function() end); | |
11 coroutine.resume(deadroutine); | |
12 | |
13 module("httpstream") | |
14 | |
15 local function parser(data, success_cb) | |
16 local function readline() | |
17 if not data then coroutine.yield("Unexpected EOF"); end | |
18 local pos, line = (data:find("\r\n", nil, true)); | |
19 if not pos then | |
20 local newdata = coroutine.yield(); | |
21 if not newdata then data = nil; coroutine.yield("Unexpected EOF"); end | |
22 data = data..newdata; | |
23 return readline(); | |
24 end | |
25 line, data = data:sub(1, pos-1), data:sub(pos+2); | |
26 return line; | |
27 end | |
28 local function readlength(n) | |
29 if not data then coroutine.yield("Unexpected EOF"); end | |
30 while #data < n do | |
31 local newdata = coroutine.yield(); | |
32 if not newdata then data = nil; coroutine.yield("Unexpected EOF"); end | |
33 data = data..newdata; | |
34 end | |
35 local r = data:sub(1, n); | |
36 data = data:sub(n + 1); | |
37 return r; | |
38 end | |
39 | |
40 while true do | |
41 -- read status line | |
42 local status_line = readline(); | |
43 local method, path, httpversion = status_line:match("^(%S+)%s+(%S+)%s+HTTP/(%S+)$"); | |
44 if not method then coroutine.yield("invalid-status-line"); end | |
45 -- TODO parse url | |
46 | |
47 local headers = {}; -- read headers | |
48 while true do | |
49 local line = readline(); | |
50 if line == "" then break; end -- headers done | |
51 local key, val = line:match("^([^%s:]+): *(.*)$"); | |
52 if not key then coroutine.yield("invalid-header-line"); end -- TODO handle multi-line and invalid headers | |
53 key = key:lower(); | |
54 headers[key] = headers[key] and headers[key]..","..val or val; | |
55 end | |
56 | |
57 -- read body | |
58 local len = tonumber(headers["content-length"]); | |
59 len = len or 0; -- TODO check for invalid len | |
60 local body = readlength(len); | |
61 | |
62 success_cb({ | |
63 method = method; | |
64 path = path; | |
65 httpversion = httpversion; | |
66 headers = headers; | |
67 body = body; | |
68 }); | |
69 end | |
70 end | |
71 | |
72 function new(success_cb, error_cb) | |
73 local co = coroutine.create(parser); | |
74 return { | |
75 feed = function(self, data) | |
76 local success, result = coroutine.resume(co, data, success_cb); | |
77 if result then | |
78 if result.method then | |
79 success_cb(result); | |
80 else -- error | |
81 error_cb(result); | |
82 co = deadroutine; | |
83 end | |
84 end | |
85 end; | |
86 }; | |
87 end | |
88 | |
89 return _M; |