X-Git-Url: https://git.archive.openwrt.org/?p=project%2Fluci.git;a=blobdiff_plain;f=libs%2Fweb%2Fluasrc%2Fhttp%2Fprotocol.lua;h=d557f78e5e2bb1546ab0194a40e8be54d6c1183a;hp=524a4c329aebf649fb61cf68672164df320007a4;hb=72aa111b2200ec24a6c6c0f895cdbb24ded1132b;hpb=6571e9ba6f945c1b454e7f08361acc3a6fb70280 diff --git a/libs/web/luasrc/http/protocol.lua b/libs/web/luasrc/http/protocol.lua index 524a4c329..d557f78e5 100644 --- a/libs/web/luasrc/http/protocol.lua +++ b/libs/web/luasrc/http/protocol.lua @@ -16,20 +16,19 @@ $Id$ module("luci.http.protocol", package.seeall) require("luci.util") -require("luci.bits") HTTP_MAX_CONTENT = 1048576 -- 1 MB HTTP_DEFAULT_CTYPE = "text/html" -- default content type HTTP_DEFAULT_VERSION = "1.0" -- HTTP default version - +HTTP_DEFAULT_LINEBUF = 1024 * 4 -- Read buffer size -- Decode an urlencoded string. -- Returns the decoded value. function urldecode( str ) local function __chrdec( hex ) - return string.char( luci.bits.Hex2Dec( hex ) ) + return string.char( tonumber( hex, 16 ) ) end if type(str) == "string" then @@ -117,7 +116,7 @@ function mimedecode( data, boundary, filecb ) local params = { } -- create a line reader - local reader = _linereader( data ) + local reader = _linereader( data, HTTP_DEFAULT_LINEBUF ) -- state variables local in_part = false @@ -130,9 +129,8 @@ function mimedecode( data, boundary, filecb ) local field local clen = 0 - -- try to read all mime parts - for line in reader do + for line, eol in reader do -- update content length clen = clen + line:len() @@ -328,8 +326,22 @@ end -- Parse a http message function parse_message( data, filecb ) + local reader = _linereader( data, HTTP_DEFAULT_LINEBUF ) + local message = parse_message_header( reader ) + + if message then + parse_message_body( reader, message, filecb ) + end + + return message +end + + +-- Parse a http message header +function parse_message_header( data ) + -- Create a line reader - local reader = _linereader( data ) + local reader = _linereader( data, HTTP_DEFAULT_LINEBUF ) local message = { } -- Try to extract magic @@ -339,7 +351,8 @@ function parse_message( data, filecb ) if method then message.request_method = method - message.status = arg2 and arg1 or 0 + message.status_code = arg2 and arg1 or 200 + message.status_message = arg2 or nil message.request_uri = arg2 and nil or arg1 if method == "response" then @@ -356,77 +369,13 @@ function parse_message( data, filecb ) message.headers = hdrs - -- Get content - local clen = ( hdrs['Content-Length'] or HTTP_MAX_CONTENT ) + 0 - -- Process get parameters - if method == "get" or method == "post" then + if ( method == "get" or method == "post" ) and + message.request_uri:match("?") + then message.params = urldecode_params( message.request_uri ) - end - - -- Process post method - if method == "post" and hdrs['Content-Type'] then - - -- Is it multipart/form-data ? - if hdrs['Content-Type']:match("^multipart/form%-data") then - for k, v in pairs( mimedecode( - reader, - hdrs['Content-Type']:match("boundary=(.+)"), - filecb - ) ) do - message.params[k] = v - end - - -- Is it x-www-urlencoded? - elseif hdrs['Content-Type'] == 'application/x-www-urlencoded' then - - -- XXX: readline isn't the best solution here - for chunk in reader do - for k, v in pairs( urldecode_params( chunk ) ) do - message.params[k] = v - end - - -- XXX: unreliable (undefined line length) - if clen + chunk:len() >= HTTP_MAX_CONTENT then - break - end - - clen = clen + chunk:len() - end - - -- Unhandled encoding - -- If a file callback is given then feed it line by line, else - -- store whole buffer in message.content - else - - for chunk in reader do - - -- We have a callback, feed it. - if type(filecb) == "function" then - - filecb( "_post", nil, chunk, false ) - - -- Append to .content buffer. - else - message.content = - type(message.content) == "string" - and message.content .. chunk - or chunk - end - - -- XXX: unreliable - if clen + chunk:len() >= HTTP_MAX_CONTENT then - break - end - - clen = clen + chunk:len() - end - - -- Send eof to callback - if type(filecb) == "function" then - filecb( "_post", nil, "", true ) - end - end + else + message.params = { } end -- Populate common environment variables @@ -463,42 +412,146 @@ function parse_message( data, filecb ) end end -function _linereader( obj ) + +-- Parse a http message body +function parse_message_body( reader, message, filecb ) + + if type(message) == "table" then + local env = message.env + + local clen = ( env.CONTENT_LENGTH or HTTP_MAX_CONTENT ) + 0 + + -- Process post method + if env.REQUEST_METHOD:lower() == "post" and env.CONTENT_TYPE then + -- Is it multipart/form-data ? + if env.CONTENT_TYPE:match("^multipart/form%-data") then + for k, v in pairs( mimedecode( + reader, + env.CONTENT_TYPE:match("boundary=(.+)"), + filecb + ) ) do + message.params[k] = v + end + + -- Is it x-www-form-urlencoded? + elseif env.CONTENT_TYPE:match('^application/x%-www%-form%-urlencoded') then + -- XXX: readline isn't the best solution here + for chunk in reader do + for k, v in pairs( urldecode_params( chunk ) ) do + message.params[k] = v + end + + -- XXX: unreliable (undefined line length) + if clen + chunk:len() >= HTTP_MAX_CONTENT then + break + end + + clen = clen + chunk:len() + end + + -- Unhandled encoding + -- If a file callback is given then feed it line by line, else + -- store whole buffer in message.content + else + for chunk in reader do + + -- We have a callback, feed it. + if type(filecb) == "function" then + + filecb( "_post", nil, chunk, false ) + + -- Append to .content buffer. + else + message.content = + type(message.content) == "string" + and message.content .. chunk + or chunk + end + + -- XXX: unreliable + if clen + chunk:len() >= HTTP_MAX_CONTENT then + break + end + + clen = clen + chunk:len() + end + + -- Send eof to callback + if type(filecb) == "function" then + filecb( "_post", nil, "", true ) + end + end + end + end +end + + +function _linereader( obj, bufsz ) + + bufsz = ( bufsz and bufsz >= 256 ) and bufsz or 256 + + local __read = function() return nil end + local __eof = function(x) return type(x) ~= "string" or #x == 0 end + + local _pos = 1 + local _buf = "" + local _eof = nil -- object is string if type(obj) == "string" then - return obj:gmatch( "[^\r\n]*\r?\n" ) + __read = function() return obj:sub( _pos, _pos + bufsz - #_buf - 1 ) end + + -- object implements a receive() or read() function + elseif type(obj) == "userdata" and ( type(obj.receive) == "function" or type(obj.read) == "function" ) then + + if type(obj.read) == "function" then + __read = function() return obj:read( bufsz ) end + else + __read = function() return obj:receive( bufsz ) end + end -- object is a function elseif type(obj) == "function" then return obj - -- object is a table and implements a readline() function - elseif type(obj) == "table" and type(obj.readline) == "function" then + -- no usable data type + else + + -- dummy iterator + return __read + end - return obj.readline - -- object is a table and has a lines property - elseif type(obj) == "table" and obj.lines then + -- generic block to line algorithm + return function() + if not _eof then + local buffer = __read() - -- decide wheather to use "lines" as function or table - local _lns = ( type(obj.lines) == "function" ) and obj.lines() or obj.lines - local _pos = 1 - - return function() - if _pos <= #_lns then - _pos = _pos + 1 - return _lns[_pos] + if __eof( buffer ) then + buffer = "" end - end - -- no usable data type - else + _pos = _pos + #buffer + buffer = _buf .. buffer - -- dummy iterator - return function() + local crlf, endpos = buffer:find("\r?\n") + + + if crlf then + _buf = buffer:sub( endpos + 1, #buffer ) + return buffer:sub( 1, endpos ), true + else + -- check for eof + _eof = __eof( buffer ) + + -- clear overflow buffer + _buf = "" + + return buffer, false + end + else return nil end end