Comparison

util/httpstream.lua @ 3564:90f4e6dc1c11

util.httpstream: Added support for HTTP response parsing.
author Waqas Hussain <waqas20@gmail.com>
date Fri, 05 Nov 2010 03:07:36 +0500
parent 3563:544d9d2e3046
child 3565:e2cc09e83a3e
comparison
equal deleted inserted replaced
3563:544d9d2e3046 3564:90f4e6dc1c11
5 local deadroutine = coroutine.create(function() end); 5 local deadroutine = coroutine.create(function() end);
6 coroutine.resume(deadroutine); 6 coroutine.resume(deadroutine);
7 7
8 module("httpstream") 8 module("httpstream")
9 9
10 local function parser(data, success_cb) 10 local function parser(data, success_cb, parser_type)
11 local function readline() 11 local function readline()
12 local pos = data:find("\r\n", nil, true); 12 local pos = data:find("\r\n", nil, true);
13 while not pos do 13 while not pos do
14 data = data..coroutine.yield(); 14 data = data..coroutine.yield();
15 pos = data:find("\r\n", nil, true); 15 pos = data:find("\r\n", nil, true);
37 headers[key] = headers[key] and headers[key]..","..val or val; 37 headers[key] = headers[key] and headers[key]..","..val or val;
38 end 38 end
39 return headers; 39 return headers;
40 end 40 end
41 41
42 while true do 42 if not parser_type or parser_type == "server" then
43 -- read status line 43 while true do
44 local status_line = readline(); 44 -- read status line
45 local method, path, httpversion = status_line:match("^(%S+)%s+(%S+)%s+HTTP/(%S+)$"); 45 local status_line = readline();
46 if not method then coroutine.yield("invalid-status-line"); end 46 local method, path, httpversion = status_line:match("^(%S+)%s+(%S+)%s+HTTP/(%S+)$");
47 -- TODO parse url 47 if not method then coroutine.yield("invalid-status-line"); end
48 local headers = readheaders(); 48 -- TODO parse url
49 49 local headers = readheaders();
50 -- read body 50
51 local len = tonumber(headers["content-length"]); 51 -- read body
52 len = len or 0; -- TODO check for invalid len 52 local len = tonumber(headers["content-length"]);
53 local body = readlength(len); 53 len = len or 0; -- TODO check for invalid len
54 54 local body = readlength(len);
55 success_cb({ 55
56 method = method; 56 success_cb({
57 path = path; 57 method = method;
58 httpversion = httpversion; 58 path = path;
59 headers = headers; 59 httpversion = httpversion;
60 body = body; 60 headers = headers;
61 }); 61 body = body;
62 end 62 });
63 end
64 elseif parser_type == "client" then
65 while true do
66 -- read status line
67 local status_line = readline();
68 local httpversion, status_code, reason_phrase = status_line:match("^HTTP/(%S+)%s+(%d%d%d)%s+(.*)$");
69 if not httpversion then coroutine.yield("invalid-status-line"); end
70 local headers = readheaders();
71
72 -- read body
73 local body;
74 local len = tonumber(headers["content-length"]);
75 if len then -- TODO check for invalid len
76 body = readlength(len);
77 else -- read to end
78 repeat
79 local newdata = coroutine.yield();
80 data = data..newdata;
81 until newdata == "";
82 body, data = data, "";
83 end
84
85 success_cb({
86 code = status_code;
87 responseversion = httpversion;
88 responseheaders = headers;
89 body = body;
90 });
91 end
92 else coroutine.yield("unknown-parser-type"); end
63 end 93 end
64 94
65 function new(success_cb, error_cb) 95 function new(success_cb, error_cb, parser_type)
66 local co = coroutine.create(parser); 96 local co = coroutine.create(parser);
67 return { 97 return {
68 feed = function(self, data) 98 feed = function(self, data)
69 if not data then 99 if not data then
100 if parser_type == "client" then coroutine.resume(co, "", success_cb, parser_type); end
70 co = deadroutine; 101 co = deadroutine;
71 return error_cb(); 102 return error_cb();
72 end 103 end
73 local success, result = coroutine.resume(co, data, success_cb); 104 local success, result = coroutine.resume(co, data, success_cb, parser_type);
74 if result then 105 if result then
75 co = deadroutine; 106 co = deadroutine;
76 return error_cb(result); 107 return error_cb(result);
77 end 108 end
78 end; 109 end;