From 8499b5bc97bff61e1c37902c9147ba362fff3e58 Mon Sep 17 00:00:00 2001 From: Emil Renner Berthing Date: Wed, 21 Aug 2013 10:31:14 +0200 Subject: http: move Request and Response objects out ..to http.server and http.client modules respectively, and don't tie them to the parser. --- lem/http.lua | 85 ++++------------------------------------------------- lem/http/client.lua | 78 +++++++++++++++++++++++++++++++++++++++++++----- lem/http/core.c | 52 ++++++++------------------------ lem/http/server.lua | 25 ++++++++++++++++ test/httptest.lua | 3 +- 5 files changed, 115 insertions(+), 128 deletions(-) diff --git a/lem/http.lua b/lem/http.lua index 1f972a9..c758430 100644 --- a/lem/http.lua +++ b/lem/http.lua @@ -16,86 +16,13 @@ -- License along with LEM. If not, see . -- -local io = require 'lem.io' -local http = require 'lem.http.core' +local http = require 'lem.http.core' +local parsers = require 'lem.parsers' -do - local parsers = require 'lem.parsers' - - parsers.lookup['HTTPRequest'] = http.HTTPRequest - http.HTTPRequest = nil - parsers.lookup['HTTPResponse'] = http.HTTPResponse - http.HTTPResponse = nil -end - -local tonumber = tonumber -local concat = table.concat - -function http.Request:body() - local len, body = self.headers['content-length'], '' - if not len then return body end - - len = tonumber(len) - if len <= 0 then return body end - - if self.headers['expect'] == '100-continue' then - local ok, err = self.client:write('HTTP/1.1 100 Continue\r\n\r\n') - if not ok then return nil, err end - end - - local err - body, err = self.client:read(len) - if not body then return nil, err end - - return body -end - -function http.Response:body_chunked() - local client = self.client - local t, n = {}, 0 - local line, err - while true do - line, err = client:read('*l') - if not line then return nil, err end - - local num = tonumber(line, 16) - if not num then return nil, 'expectation failed' end - if num == 0 then break end - - local data, err = client:read(num) - if not data then return nil, err end - - n = n + 1 - t[n] = data - - line, err = client:read('*l') - if not line then return nil, err end - end - - line, err = client:read('*l') - if not line then return nil, err end - - return concat(t) -end - -function http.Response:body() - if self.headers['transfer-encoding'] == 'chunked' then - return self:body_chunked() - end - - local num = self.headers['content-length'] - if not num then - if self.headers['connection'] == 'close' then - return self.client:read('*a') - end - return nil, 'no content length specified' - end - - num = tonumber(num) - if not num then return nil, 'invalid content length' end - - return self.client:read(num) -end +parsers.lookup['HTTPRequest'] = http.HTTPRequest +http.HTTPRequest = nil +parsers.lookup['HTTPResponse'] = http.HTTPResponse +http.HTTPResponse = nil return http diff --git a/lem/http/client.lua b/lem/http/client.lua index 04b9de6..0e9631e 100644 --- a/lem/http/client.lua +++ b/lem/http/client.lua @@ -16,16 +16,80 @@ -- License along with LEM. If not, see . -- +local setmetatable = setmetatable +local tonumber = tonumber +local concat = table.concat + local io = require 'lem.io' require 'lem.http' -local client = {} +local M = {} + +local Response = {} +Response.__index = Response +M.Response = Response + +function Response:body_chunked() + if self._body then return self._body end + + local conn = self.conn + local rope, i = {}, 0 + local line, err + while true do + line, err = conn:read('*l') + if not line then return nil, err end + + local len = tonumber(line, 16) + if not len then return nil, 'expectation failed' end + if len == 0 then break end + + local data, err = conn:read(len) + if not data then return nil, err end + + i = i + 1 + rope[i] = data + + line, err = conn:read('*l') + if not line then return nil, err end + end + + line, err = conn:read('*l') + if not line then return nil, err end + + rope = concat(rope) + self._body = rope + return rope +end + +function Response:body() + if self._body then return self._body end + if self.headers['transfer-encoding'] == 'chunked' then + return self:body_chunked() + end + + local len, body, err = self.headers['content-length'] + if len then + len = tonumber(len) + if not len then return nil, 'invalid content length' end + body, err = self.conn:read(len) + else + if self.headers['connection'] == 'close' then + body, err = self.client:read('*a') + else + return nil, 'no content length specified' + end + end + if not body then return nil, err end + + self._body = body + return body +end local Client = {} Client.__index = Client -client.Client = Client +M.Client = Client -function client.new() +function M.new() return setmetatable({ proto = false, domain = false, @@ -95,14 +159,12 @@ function Client:get(url) if not res then return fail(self, err) end end - local body - body, err = res:body() - if not body then return fail(self, err) end + res.conn = c + setmetatable(res, Response) self.proto = proto self.domain = domain self.conn = c - res.body = body return res end @@ -123,6 +185,6 @@ function Client:download(url, filename) return true end -return client +return M -- vim: set ts=2 sw=2 noet: diff --git a/lem/http/core.c b/lem/http/core.c index 5c14d13..9fdd954 100644 --- a/lem/http/core.c +++ b/lem/http/core.c @@ -18,11 +18,6 @@ #include -#if !(LUA_VERSION_NUM >= 502) -#define lua_getuservalue lua_getfenv -#define lua_setuservalue lua_setfenv -#endif - enum classes { C_CTL, /* control characters */ C_LF, /* \n */ @@ -164,8 +159,6 @@ parse_http_init(lua_State *T) /* create result table */ lua_settop(T, 2); lua_createtable(T, 0, 5); - lua_pushvalue(T, 1); - lua_setfield(T, -2, "client"); } static void @@ -303,10 +296,6 @@ parse_http_process(lua_State *T, struct lem_inputbuf *b) } lua_setfield(T, -2, "headers"); - /* set metatable */ - lua_getuservalue(T, 2); - lua_setmetatable(T, -2); - if (r == end) b->start = b->end = 0; else @@ -328,42 +317,25 @@ parse_http_process(lua_State *T, struct lem_inputbuf *b) return LEM_PMORE; } +static const struct lem_parser http_req_parser = { + .init = parse_http_req_init, + .process = parse_http_process, +}; + +static const struct lem_parser http_res_parser = { + .init = parse_http_res_init, + .process = parse_http_process, +}; + int luaopen_lem_http_core(lua_State *L) { - struct lem_parser *p; - /* create module table M */ lua_newtable(L); - /* create Request metatable */ - lua_newtable(L); - lua_pushvalue(L, -1); - lua_setfield(L, -2, "__index"); - /* insert Request metatable */ - lua_setfield(L, -2, "Request"); - - /* create Response metatable */ - lua_newtable(L); - lua_pushvalue(L, -1); - lua_setfield(L, -2, "__index"); - /* insert Request metatable */ - lua_setfield(L, -2, "Response"); - - p = lua_newuserdata(L, sizeof(struct lem_parser)); - p->init = parse_http_req_init; - p->process = parse_http_process; - p->destroy = NULL; - lua_getfield(L, -2, "Request"); - lua_setuservalue(L, -2); + lua_pushlightuserdata(L, (void *)&http_req_parser); lua_setfield(L, -2, "HTTPRequest"); - - p = lua_newuserdata(L, sizeof(struct lem_parser)); - p->init = parse_http_res_init; - p->process = parse_http_process; - p->destroy = NULL; - lua_getfield(L, -2, "Response"); - lua_setuservalue(L, -2); + lua_pushlightuserdata(L, (void *)&http_res_parser); lua_setfield(L, -2, "HTTPResponse"); return 1; diff --git a/lem/http/server.lua b/lem/http/server.lua index fcab33b..a3cb86b 100644 --- a/lem/http/server.lua +++ b/lem/http/server.lua @@ -35,6 +35,29 @@ local M = {} function M.debug() end +local Request = {} +Request.__index = Request +M.Request = Request + +function Request:body() + local len, body = self.headers['content-length'], '' + if not len then return body end + + len = tonumber(len) + if len <= 0 then return body end + + if self.headers['expect'] == '100-continue' then + local ok, err = self.client:write('HTTP/1.1 100 Continue\r\n\r\n') + if not ok then return nil, err end + end + + local err + body, err = self.client:read(len) + if not body then return nil, err end + + return body +end + do local gsub, char, tonumber = string.gsub, string.char, tonumber @@ -67,6 +90,8 @@ local function handleHTTP(self, client) if not req then self.debug('read', err) break end local method, uri, version = req.method, req.uri, req.version + setmetatable(req, Request) + req.client = client req.path = urldecode(uri:match('^([^?]*)')) local res = newresponse(req) diff --git a/test/httptest.lua b/test/httptest.lua index bce4cff..311a317 100755 --- a/test/httptest.lua +++ b/test/httptest.lua @@ -44,7 +44,8 @@ local function get(n, close) printf('%d: %s: %s\n', n, k, v) end - printf('\n%d: #body = %d\n', n, #res.body) + local body = assert(res:body()) + printf('\n%d: #body = %d\n', n, #body) assert(c:close()) running = running - 1 -- cgit v1.2.1