Comparison

net/http/parser.lua @ 5467:9b7c919bf238

Merge 0.9->trunk
author Matthew Wild <mwild1@gmail.com>
date Fri, 12 Apr 2013 00:32:10 +0100
parent 5463:111953bfe767
child 5475:c2c9f07c5d6a
comparison
equal deleted inserted replaced
5457:953888c31071 5467:9b7c919bf238
1
2 local tonumber = tonumber; 1 local tonumber = tonumber;
3 local assert = assert; 2 local assert = assert;
4 local url_parse = require "socket.url".parse; 3 local url_parse = require "socket.url".parse;
5 local urldecode = require "net.http".urldecode; 4 local urldecode = require "util.http".urldecode;
6 5
7 local function preprocess_path(path) 6 local function preprocess_path(path)
8 path = urldecode((path:gsub("//+", "/"))); 7 path = urldecode((path:gsub("//+", "/")));
9 if path:sub(1,1) ~= "/" then 8 if path:sub(1,1) ~= "/" then
10 path = "/"..path; 9 path = "/"..path;
27 26
28 function httpstream.new(success_cb, error_cb, parser_type, options_cb) 27 function httpstream.new(success_cb, error_cb, parser_type, options_cb)
29 local client = true; 28 local client = true;
30 if not parser_type or parser_type == "server" then client = false; else assert(parser_type == "client", "Invalid parser type"); end 29 if not parser_type or parser_type == "server" then client = false; else assert(parser_type == "client", "Invalid parser type"); end
31 local buf = ""; 30 local buf = "";
32 local chunked; 31 local chunked, chunk_size, chunk_start;
33 local state = nil; 32 local state = nil;
34 local packet; 33 local packet;
35 local len; 34 local len;
36 local have_body; 35 local have_body;
37 local error; 36 local error;
63 headers[key] = headers[key] and headers[key]..","..val or val; 62 headers[key] = headers[key] and headers[key]..","..val or val;
64 else 63 else
65 first_line = line; 64 first_line = line;
66 if client then 65 if client then
67 httpversion, status_code, reason_phrase = line:match("^HTTP/(1%.[01]) (%d%d%d) (.*)$"); 66 httpversion, status_code, reason_phrase = line:match("^HTTP/(1%.[01]) (%d%d%d) (.*)$");
67 status_code = tonumber(status_code);
68 if not status_code then error = true; return error_cb("invalid-status-line"); end 68 if not status_code then error = true; return error_cb("invalid-status-line"); end
69 have_body = not 69 have_body = not
70 ( (options_cb and options_cb().method == "HEAD") 70 ( (options_cb and options_cb().method == "HEAD")
71 or (status_code == 204 or status_code == 304 or status_code == 301) 71 or (status_code == 204 or status_code == 304 or status_code == 301)
72 or (status_code >= 100 and status_code < 200) ); 72 or (status_code >= 100 and status_code < 200) );
73 chunked = have_body and headers["transfer-encoding"] == "chunked";
74 else 73 else
75 method, path, httpversion = line:match("^(%w+) (%S+) HTTP/(1%.[01])$"); 74 method, path, httpversion = line:match("^(%w+) (%S+) HTTP/(1%.[01])$");
76 if not method then error = true; return error_cb("invalid-status-line"); end 75 if not method then error = true; return error_cb("invalid-status-line"); end
77 end 76 end
78 end 77 end
79 end 78 end
80 if not first_line then error = true; return error_cb("invalid-status-line"); end 79 if not first_line then error = true; return error_cb("invalid-status-line"); end
80 chunked = have_body and headers["transfer-encoding"] == "chunked";
81 len = tonumber(headers["content-length"]); -- TODO check for invalid len 81 len = tonumber(headers["content-length"]); -- TODO check for invalid len
82 if client then 82 if client then
83 -- FIXME handle '100 Continue' response (by skipping it) 83 -- FIXME handle '100 Continue' response (by skipping it)
84 if not have_body then len = 0; end 84 if not have_body then len = 0; end
85 packet = { 85 packet = {
118 state = true; 118 state = true;
119 end 119 end
120 if state then -- read body 120 if state then -- read body
121 if client then 121 if client then
122 if chunked then 122 if chunked then
123 local index = buf:find("\r\n", nil, true); 123 if not buf:find("\r\n", nil, true) then
124 if not index then return; end -- not enough data 124 return;
125 local chunk_size = buf:match("^%x+"); 125 end -- not enough data
126 if not chunk_size then error = true; return error_cb("invalid-chunk-size"); end 126 if not chunk_size then
127 chunk_size = tonumber(chunk_size, 16); 127 chunk_size, chunk_start = buf:match("^(%x+)[^\r\n]*\r\n()");
128 index = index + 2; 128 chunk_size = chunk_size and tonumber(chunk_size, 16);
129 if chunk_size == 0 then 129 if not chunk_size then error = true; return error_cb("invalid-chunk-size"); end
130 state = nil; success_cb(packet);
131 elseif #buf - index + 1 >= chunk_size then -- we have a chunk
132 packet.body = packet.body..buf:sub(index, index + chunk_size - 1);
133 buf = buf:sub(index + chunk_size);
134 end 130 end
135 error("trailers"); -- FIXME MUST read trailers 131 if chunk_size == 0 and buf:find("\r\n\r\n", chunk_start-2, true) then
132 state, chunk_size = nil, nil;
133 buf = buf:gsub("^.-\r\n\r\n", ""); -- This ensure extensions and trailers are stripped
134 success_cb(packet);
135 elseif #buf - chunk_start + 2 >= chunk_size then -- we have a chunk
136 packet.body = packet.body..buf:sub(chunk_start, chunk_start + chunk_size);
137 buf = buf:sub(chunk_start + chunk_size + 2);
138 chunk_size, chunk_start = nil, nil;
139 else -- Partial chunk remaining
140 break;
141 end
136 elseif len and #buf >= len then 142 elseif len and #buf >= len then
137 packet.body, buf = buf:sub(1, len), buf:sub(len + 1); 143 packet.body, buf = buf:sub(1, len), buf:sub(len + 1);
138 state = nil; success_cb(packet); 144 state = nil; success_cb(packet);
145 else
146 break;
139 end 147 end
140 elseif #buf >= len then 148 elseif #buf >= len then
141 packet.body, buf = buf:sub(1, len), buf:sub(len + 1); 149 packet.body, buf = buf:sub(1, len), buf:sub(len + 1);
142 state = nil; success_cb(packet); 150 state = nil; success_cb(packet);
143 else 151 else