Software / code / prosody
Comparison
net/http/parser.lua @ 4631:fc5d3b053454
net.http.{server|codes|parser}: Initial commit.
| author | Waqas Hussain <waqas20@gmail.com> |
|---|---|
| date | Sun, 08 Apr 2012 04:09:33 +0500 |
| child | 4712:4fc99f1b7570 |
comparison
equal
deleted
inserted
replaced
| 4630:9502c0224caf | 4631:fc5d3b053454 |
|---|---|
| 1 | |
| 2 local tonumber = tonumber; | |
| 3 local assert = assert; | |
| 4 | |
| 5 local httpstream = {}; | |
| 6 | |
| 7 function httpstream.new(success_cb, error_cb, parser_type, options_cb) | |
| 8 local client = true; | |
| 9 if not parser_type or parser_type == "server" then client = false; else assert(parser_type == "client", "Invalid parser type"); end | |
| 10 local buf = ""; | |
| 11 local chunked; | |
| 12 local state = nil; | |
| 13 local packet; | |
| 14 local len; | |
| 15 local have_body; | |
| 16 local error; | |
| 17 return { | |
| 18 feed = function(self, data) | |
| 19 if error then return nil, "parse has failed"; end | |
| 20 if not data then -- EOF | |
| 21 if state and client and not len then -- reading client body until EOF | |
| 22 packet.body = buf; | |
| 23 success_cb(packet); | |
| 24 elseif buf ~= "" then -- unexpected EOF | |
| 25 error = true; return error_cb(); | |
| 26 end | |
| 27 return; | |
| 28 end | |
| 29 buf = buf..data; | |
| 30 while #buf > 0 do | |
| 31 if state == nil then -- read request | |
| 32 local index = buf:find("\r\n\r\n", nil, true); | |
| 33 if not index then return; end -- not enough data | |
| 34 local method, path, httpversion, status_code, reason_phrase; | |
| 35 local first_line; | |
| 36 local headers = {}; | |
| 37 for line in buf:sub(1,index+1):gmatch("([^\r\n]+)\r\n") do -- parse request | |
| 38 if first_line then | |
| 39 local key, val = line:match("^([^%s:]+): *(.*)$"); | |
| 40 if not key then error = true; return error_cb("invalid-header-line"); end -- TODO handle multi-line and invalid headers | |
| 41 key = key:lower(); | |
| 42 headers[key] = headers[key] and headers[key]..","..val or val; | |
| 43 else | |
| 44 first_line = line; | |
| 45 if client then | |
| 46 httpversion, status_code, reason_phrase = line:match("^HTTP/(1%.[01]) (%d%d%d) (.*)$"); | |
| 47 if not status_code then error = true; return error_cb("invalid-status-line"); end | |
| 48 have_body = not | |
| 49 ( (options_cb and options_cb().method == "HEAD") | |
| 50 or (status_code == 204 or status_code == 304 or status_code == 301) | |
| 51 or (status_code >= 100 and status_code < 200) ); | |
| 52 chunked = have_body and headers["transfer-encoding"] == "chunked"; | |
| 53 else | |
| 54 method, path, httpversion = line:match("^(%w+) (%S+) HTTP/(1%.[01])$"); | |
| 55 if not method then error = true; return error_cb("invalid-status-line"); end | |
| 56 path = path:gsub("^//+", "/"); -- TODO parse url more | |
| 57 end | |
| 58 end | |
| 59 end | |
| 60 len = tonumber(headers["content-length"]); -- TODO check for invalid len | |
| 61 if client then | |
| 62 -- FIXME handle '100 Continue' response (by skipping it) | |
| 63 if not have_body then len = 0; end | |
| 64 packet = { | |
| 65 code = status_code; | |
| 66 httpversion = httpversion; | |
| 67 headers = headers; | |
| 68 body = have_body and "" or nil; | |
| 69 -- COMPAT the properties below are deprecated | |
| 70 responseversion = httpversion; | |
| 71 responseheaders = headers; | |
| 72 }; | |
| 73 else | |
| 74 len = len or 0; | |
| 75 packet = { | |
| 76 method = method; | |
| 77 path = path; | |
| 78 httpversion = httpversion; | |
| 79 headers = headers; | |
| 80 body = nil; | |
| 81 }; | |
| 82 end | |
| 83 buf = buf:sub(index + 4); | |
| 84 state = true; | |
| 85 end | |
| 86 if state then -- read body | |
| 87 if client then | |
| 88 if chunked then | |
| 89 local index = buf:find("\r\n", nil, true); | |
| 90 if not index then return; end -- not enough data | |
| 91 local chunk_size = buf:match("^%x+"); | |
| 92 if not chunk_size then error = true; return error_cb("invalid-chunk-size"); end | |
| 93 chunk_size = tonumber(chunk_size, 16); | |
| 94 index = index + 2; | |
| 95 if chunk_size == 0 then | |
| 96 state = nil; success_cb(packet); | |
| 97 elseif #buf - index + 1 >= chunk_size then -- we have a chunk | |
| 98 packet.body = packet.body..buf:sub(index, index + chunk_size - 1); | |
| 99 buf = buf:sub(index + chunk_size); | |
| 100 end | |
| 101 error("trailers"); -- FIXME MUST read trailers | |
| 102 elseif len and #buf >= len then | |
| 103 packet.body, buf = buf:sub(1, len), buf:sub(len + 1); | |
| 104 state = nil; success_cb(packet); | |
| 105 end | |
| 106 elseif #buf >= len then | |
| 107 packet.body, buf = buf:sub(1, len), buf:sub(len + 1); | |
| 108 state = nil; success_cb(packet); | |
| 109 end | |
| 110 end | |
| 111 end | |
| 112 end; | |
| 113 }; | |
| 114 end | |
| 115 | |
| 116 return httpstream; |