X-Git-Url: https://git.archive.openwrt.org/?p=project%2Fluci.git;a=blobdiff_plain;f=libs%2Fweb%2Fluasrc%2Fhttp%2Fprotocol.lua;h=6901291b9ad69b5feefe4a91a6cee2a37d13e0eb;hp=d9259f660419df777f6123914e1411d336650c97;hb=dfe85d7e518cc35a55e372b0ac31345788a486a8;hpb=eae0e447989a1a37ce6cb8df9f1f353738aab4f6 diff --git a/libs/web/luasrc/http/protocol.lua b/libs/web/luasrc/http/protocol.lua index d9259f660..6901291b9 100644 --- a/libs/web/luasrc/http/protocol.lua +++ b/libs/web/luasrc/http/protocol.lua @@ -18,7 +18,9 @@ module("luci.http.protocol", package.seeall) require("luci.util") -HTTP_MAX_CONTENT = 1048576 -- 1 MB +HTTP_MAX_CONTENT = 1024^2 -- 1 MB maximum content size +HTTP_MAX_READBUF = 1024 -- 1 kB read buffer size + HTTP_DEFAULT_CTYPE = "text/html" -- default content type HTTP_DEFAULT_VERSION = "1.0" -- HTTP default version @@ -116,7 +118,7 @@ function mimedecode( data, boundary, filecb ) local params = { } -- create a line reader - local reader = _linereader( data ) + local reader = _linereader( data, HTTP_MAX_READBUF ) -- state variables local in_part = false @@ -129,9 +131,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() @@ -327,7 +328,7 @@ end -- Parse a http message function parse_message( data, filecb ) - local reader = _linereader( data ) + local reader = _linereader( data, HTTP_MAX_READBUF ) local message = parse_message_header( reader ) if message then @@ -342,7 +343,7 @@ end function parse_message_header( data ) -- Create a line reader - local reader = _linereader( data ) + local reader = _linereader( data, HTTP_MAX_READBUF ) local message = { } -- Try to extract magic @@ -424,8 +425,11 @@ function parse_message_body( reader, message, filecb ) -- 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 + + -- Read multipart/mime data for k, v in pairs( mimedecode( reader, env.CONTENT_TYPE:match("boundary=(.+)"), @@ -436,26 +440,36 @@ function parse_message_body( reader, message, filecb ) -- 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 + -- Read post data + local post_data = "" + + for chunk, eol in reader do + + post_data = post_data .. chunk + + -- Abort on eol or if maximum allowed size or content length is reached + if eol or #post_data >= HTTP_MAX_CONTENT or #post_data > clen then break end + end - clen = clen + chunk:len() + -- Parse params + for k, v in pairs( urldecode_params( post_data ) ) do + message.params[k] = v end -- Unhandled encoding -- If a file callback is given then feed it line by line, else -- store whole buffer in message.content else + + local len = 0 + for chunk in reader do + len = len + #chunk + -- We have a callback, feed it. if type(filecb) == "function" then @@ -469,12 +483,10 @@ function parse_message_body( reader, message, filecb ) or chunk end - -- XXX: unreliable - if clen + chunk:len() >= HTTP_MAX_CONTENT then + -- Abort if maximum allowed size or content length is reached + if len >= HTTP_MAX_CONTENT or len >= clen then break end - - clen = clen + chunk:len() end -- Send eof to callback @@ -487,42 +499,73 @@ function parse_message_body( reader, message, filecb ) end -function _linereader( obj ) +-- Wrap given object into a line read iterator +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" or type(obj) == "table") and ( type(obj.receive) == "function" or type(obj.read) == "function" ) then + + if type(obj.read) == "function" then + __read = function() return obj:read( bufsz - #_buf ) end + else + __read = function() return obj:receive( bufsz - #_buf ) 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