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; |