X-Git-Url: http://git.archive.openwrt.org/?p=project%2Fluci.git;a=blobdiff_plain;f=libs%2Fhttp%2Fluasrc%2Fhttp%2Fprotocol.lua;h=cd482a94fc680eea892de21ffc13f6d4eea32c9b;hp=cb5f78609f57eb301df257f598cec28d049951cc;hb=0969279c41a31733a4c1b6c9a9fa769a6bbc7ee9;hpb=1245a9eaa940f6ae3a9110c3169d41132a4f0ceb diff --git a/libs/http/luasrc/http/protocol.lua b/libs/http/luasrc/http/protocol.lua index cb5f78609..cd482a94f 100644 --- a/libs/http/luasrc/http/protocol.lua +++ b/libs/http/luasrc/http/protocol.lua @@ -13,16 +13,21 @@ $Id$ ]]-- +--- LuCI http protocol class. +-- This class contains several functions useful for http message- and content +-- decoding and to retrive form data from raw http messages. module("luci.http.protocol", package.seeall) local ltn12 = require("luci.ltn12") -HTTP_MAX_CONTENT = 1024*4 -- 4 kB maximum content size -HTTP_URLENC_MAXKEYLEN = 1024 -- maximum allowd size of urlencoded parameter names +HTTP_MAX_CONTENT = 1024*8 -- 8 kB maximum content size - --- Decode an urlencoded string. --- Returns the decoded value. +--- Decode an urlencoded string - optionally without decoding +-- the "+" sign to " " - and return the decoded string. +-- @param str Input string in x-www-urlencoded format +-- @param no_plus Don't decode "+" signs to spaces +-- @return The decoded string +-- @see urlencode function urldecode( str, no_plus ) local function __chrdec( hex ) @@ -40,9 +45,15 @@ function urldecode( str, no_plus ) return str end - --- Extract and split urlencoded data pairs, separated bei either "&" or ";" from given url. --- Returns a table value with urldecoded values. +--- Extract and split urlencoded data pairs, separated bei either "&" or ";" +-- from given url or string. Returns a table with urldecoded values. +-- Simple parameters are stored as string values associated with the parameter +-- name within the table. Parameters with multiple values are stored as array +-- containing the corresponding values. +-- @param url The url or string which contains x-www-urlencoded form data +-- @param tbl Use the given table for storing values (optional) +-- @return Table containing the urldecoded parameters +-- @see urlencode_params function urldecode_params( url, tbl ) local params = tbl or { } @@ -74,9 +85,10 @@ function urldecode_params( url, tbl ) return params end - --- Encode given string in urlencoded format. --- Returns the encoded string. +--- Encode given string to x-www-urlencoded format. +-- @param str String to encode +-- @return String containing the encoded data +-- @see urldecode function urlencode( str ) local function __chrenc( chr ) @@ -95,50 +107,75 @@ function urlencode( str ) return str end - --- Encode given table to urlencoded string. --- Returns the encoded string. +--- Encode each key-value-pair in given table to x-www-urlencoded format, +-- separated by "&". Tables are encoded as parameters with multiple values by +-- repeating the parameter name with each value. +-- @param tbl Table with the values +-- @return String containing encoded values +-- @see urldecode_params function urlencode_params( tbl ) local enc = "" for k, v in pairs(tbl) do - enc = enc .. ( enc and "&" or "" ) .. - urlencode(k) .. "=" .. - urlencode(v) + if type(v) == "table" then + for i, v2 in ipairs(v) do + enc = enc .. ( #enc > 0 and "&" or "" ) .. + urlencode(k) .. "=" .. urlencode(v2) + end + else + enc = enc .. ( #enc > 0 and "&" or "" ) .. + urlencode(k) .. "=" .. urlencode(v) + end end return enc end - --- Parameter helper +-- (Internal function) +-- Initialize given parameter and coerce string into table when the parameter +-- already exists. +-- @param tbl Table where parameter should be created +-- @param key Parameter name +-- @return Always nil local function __initval( tbl, key ) - local multival = ( key:sub( #key - 1, #key ) == "[]" ) - - if multival then - if type(tbl[key]) == "table" then - table.insert( tbl[key], "" ) - else - tbl[key] = { "" } - end - else + if tbl[key] == nil then tbl[key] = "" + elseif type(tbl[key]) == "string" then + tbl[key] = { tbl[key], "" } + else + table.insert( tbl[key], "" ) end - - return multival end -local function __appendval( tbl, key, multival, chunk ) - if multival then +-- (Internal function) +-- Append given data to given parameter, either by extending the string value +-- or by appending it to the last string in the parameter's value table. +-- @param tbl Table containing the previously initialized parameter value +-- @param key Parameter name +-- @param chunk String containing the data to append +-- @return Always nil +-- @see __initval +local function __appendval( tbl, key, chunk ) + if type(tbl[key]) == "table" then tbl[key][#tbl[key]] = tbl[key][#tbl[key]] .. chunk else tbl[key] = tbl[key] .. chunk end end -local function __finishval( tbl, key, multival, handler ) +-- (Internal function) +-- Finish the value of given parameter, either by transforming the string value +-- or - in the case of multi value parameters - the last element in the +-- associated values table. +-- @param tbl Table containing the previously initialized parameter value +-- @param key Parameter name +-- @param handler Function which transforms the parameter value +-- @return Always nil +-- @see __initval +-- @see __appendval +local function __finishval( tbl, key, handler ) if handler then - if multival then + if type(tbl[key]) == "table" then tbl[key][#tbl[key]] = handler( tbl[key][#tbl[key]] ) else tbl[key] = handler( tbl[key] ) @@ -211,7 +248,7 @@ process_states['headers'] = function( msg, chunk ) if chunk ~= nil then -- Look for a valid header format - local hdr, val = chunk:match( "^([A-Z][A-Za-z0-9%-_]+): +(.+)$" ) + local hdr, val = chunk:match( "^([A-Za-z][A-Za-z0-9%-_]+): +(.+)$" ) if type(hdr) == "string" and hdr:len() > 0 and type(val) == "string" and val:len() > 0 @@ -234,462 +271,263 @@ process_states['headers'] = function( msg, chunk ) end --- Find first MIME boundary -process_states['mime-init'] = function( msg, chunk, filecb ) - - if chunk ~= nil then - if #chunk >= #msg.mime_boundary + 2 then - local boundary = chunk:sub( 1, #msg.mime_boundary + 4 ) - - if boundary == "--" .. msg.mime_boundary .. "\r\n" then +--- Creates a ltn12 source from the given socket. The source will return it's +-- data line by line with the trailing \r\n stripped of. +-- @param sock Readable network socket +-- @return Ltn12 source function +function header_source( sock ) + return ltn12.source.simplify( function() - -- Store remaining data in buffer - msg._mimebuffer = chunk:sub( #msg.mime_boundary + 5, #chunk ) + local chunk, err, part = sock:receive("*l") - -- Switch to header processing state - return true, function( chunk ) - return process_states['mime-headers']( msg, chunk, filecb ) - end + -- Line too long + if chunk == nil then + if err ~= "timeout" then + return nil, part + and "Line exceeds maximum allowed length" + or "Unexpected EOF" else - return nil, "Invalid MIME boundary" + return nil, err end - else - return true + + -- Line ok + elseif chunk ~= nil then + + -- Strip trailing CR + chunk = chunk:gsub("\r$","") + + return chunk, nil end - else - return nil, "Unexpected EOF" - end + end ) end +--- Decode a mime encoded http message body with multipart/form-data +-- Content-Type. Stores all extracted data associated with its parameter name +-- in the params table withing the given message object. Multiple parameter +-- values are stored as tables, ordinary ones as strings. +-- If an optional file callback function is given then it is feeded with the +-- file contents chunk by chunk and only the extracted file name is stored +-- within the params table. The callback function will be called subsequently +-- with three arguments: +-- o Table containing decoded (name, file) and raw (headers) mime header data +-- o String value containing a chunk of the file data +-- o Boolean which indicates wheather the current chunk is the last one (eof) +-- @param src Ltn12 source function +-- @param msg HTTP message object +-- @param filecb File callback function (optional) +-- @return Value indicating successful operation (not nil means "ok") +-- @return String containing the error if unsuccessful +-- @see parse_message_header +function mimedecode_message_body( src, msg, filecb ) --- Read MIME part headers -process_states['mime-headers'] = function( msg, chunk, filecb ) - - if chunk ~= nil then + if msg and msg.env.CONTENT_TYPE then + msg.mime_boundary = msg.env.CONTENT_TYPE:match("^multipart/form%-data; boundary=(.+)$") + end - -- Combine look-behind buffer with current chunk - chunk = msg._mimebuffer .. chunk + if not msg.mime_boundary then + return nil, "Invalid Content-Type found" + end - if not msg._mimeheaders then - msg._mimeheaders = { } - end - local function __storehdr( k, v ) - msg._mimeheaders[k] = v - return "" - end + local tlen = 0 + local inhdr = false + local field = nil + local store = nil + local lchunk = nil - -- Read all header lines - local ok, count = 1, 0 - while ok > 0 do - chunk, ok = chunk:gsub( "^([A-Z][A-Za-z0-9%-_]+): +([^\r\n]+)\r\n", __storehdr ) - count = count + ok - end + local function parse_headers( chunk, field ) - -- Headers processed, check for empty line - chunk, ok = chunk:gsub( "^\r\n", "" ) + local stat + repeat + chunk, stat = chunk:gsub( + "^([A-Z][A-Za-z0-9%-_]+): +([^\r\n]+)\r\n", + function(k,v) + field.headers[k] = v + return "" + end + ) + until stat == 0 - -- Store remaining buffer contents - msg._mimebuffer = chunk + chunk, stat = chunk:gsub("^\r\n","") -- End of headers - if ok > 0 then + if stat > 0 then + if field.headers["Content-Disposition"] then + if field.headers["Content-Disposition"]:match("^form%-data; ") then + field.name = field.headers["Content-Disposition"]:match('name="(.-)"') + field.file = field.headers["Content-Disposition"]:match('filename="(.+)"$') + end + end - -- When no Content-Type header is given assume text/plain - if not msg._mimeheaders['Content-Type'] then - msg._mimeheaders['Content-Type'] = 'text/plain' + if not field.headers["Content-Type"] then + field.headers["Content-Type"] = "text/plain" end - -- Check Content-Disposition - if msg._mimeheaders['Content-Disposition'] then - -- Check for "form-data" token - if msg._mimeheaders['Content-Disposition']:match("^form%-data; ") then - -- Check for field name, filename - local field = msg._mimeheaders['Content-Disposition']:match('name="(.-)"') - local file = msg._mimeheaders['Content-Disposition']:match('filename="(.+)"$') - - -- Is a file field and we have a callback - if file and filecb then - msg.params[field] = file - msg._mimecallback = function(chunk,eof) - filecb( { - name = field; - file = file; - headers = msg._mimeheaders - }, chunk, eof ) - end + if field.name and field.file and filecb then + __initval( msg.params, field.name ) + __appendval( msg.params, field.name, field.file ) - -- Treat as form field - else - local mv = __initval( msg.params, field ) - msg._mimecallback = function(chunk,eof) - __appendval( msg.params, field, mv, chunk ) - end - end + store = filecb + elseif field.name then + __initval( msg.params, field.name ) - -- Header was valid, continue with mime-data - return true, function( chunk ) - return process_states['mime-data']( msg, chunk, filecb ) - end - else - -- Unknown Content-Disposition, abort - return nil, "Unexpected Content-Disposition MIME section header" + store = function( hdr, buf, eof ) + __appendval( msg.params, field.name, buf ) end else - -- Content-Disposition is required, abort without - return nil, "Missing Content-Disposition MIME section header" + store = nil end - -- We parsed no headers yet and buffer is almost empty - elseif count > 0 or #chunk < 128 then - -- Keep feeding me with chunks - return true, nil + return chunk, true end - -- Buffer looks like garbage - return nil, "Malformed MIME section header" - else - return nil, "Unexpected EOF" + return chunk, false end -end - - --- Read MIME part data -process_states['mime-data'] = function( msg, chunk, filecb ) - - if chunk ~= nil then - - -- Combine look-behind buffer with current chunk - local buffer = msg._mimebuffer .. chunk - - -- Look for MIME boundary - local spos, epos = buffer:find( "\r\n--" .. msg.mime_boundary .. "\r\n", 1, true ) - - if spos then - -- Content data - msg._mimecallback( buffer:sub( 1, spos - 1 ), true ) - - -- Store remainder - msg._mimebuffer = buffer:sub( epos + 1, #buffer ) - - -- Next state is mime-header processing - return true, function( chunk ) - return process_states['mime-headers']( msg, chunk, filecb ) - end - else - -- Look for EOF? - local spos, epos = buffer:find( "\r\n--" .. msg.mime_boundary .. "--\r\n", 1, true ) - - if spos then - -- Content data - msg._mimecallback( buffer:sub( 1, spos - 1 ), true ) - - -- We processed the final MIME boundary, cleanup - msg._mimebuffer = nil - msg._mimeheaders = nil - msg._mimecallback = nil - - -- We won't accept data anymore - return false - else - -- We're somewhere within a data section and our buffer is full - if #buffer > #chunk then - -- Flush buffered data - msg._mimecallback( buffer:sub( 1, #buffer - #chunk ), false ) - -- Store new data - msg._mimebuffer = buffer:sub( #buffer - #chunk + 1, #buffer ) + local function snk( chunk ) - -- Buffer is not full yet, append new data - else - msg._mimebuffer = buffer - end + tlen = tlen + ( chunk and #chunk or 0 ) - -- Keep feeding me - return true - end + if msg.env.CONTENT_LENGTH and tlen > tonumber(msg.env.CONTENT_LENGTH) + 2 then + return nil, "Message body size exceeds Content-Length" end - else - return nil, "Unexpected EOF" - end -end - --- Init urldecoding stream -process_states['urldecode-init'] = function( msg, chunk, filecb ) - - if chunk ~= nil then + if chunk and not lchunk then + lchunk = "\r\n" .. chunk - -- Check for Content-Length - if msg.env.CONTENT_LENGTH then - msg.content_length = tonumber(msg.env.CONTENT_LENGTH) + elseif lchunk then + local data = lchunk .. ( chunk or "" ) + local spos, epos, found - if msg.content_length <= HTTP_MAX_CONTENT then - -- Initialize buffer - msg._urldecbuffer = chunk - msg._urldeclength = 0 + repeat + spos, epos = data:find( "\r\n--" .. msg.mime_boundary .. "\r\n", 1, true ) - -- Switch to urldecode-key state - return true, function(chunk) - return process_states['urldecode-key']( msg, chunk, filecb ) + if not spos then + spos, epos = data:find( "\r\n--" .. msg.mime_boundary .. "--\r\n", 1, true ) end - else - return nil, "Request exceeds maximum allowed size" - end - else - return nil, "Missing Content-Length header" - end - else - return nil, "Unexpected EOF" - end -end --- Process urldecoding stream, read and validate parameter key -process_states['urldecode-key'] = function( msg, chunk, filecb ) - if chunk ~= nil then + if spos then + local predata = data:sub( 1, spos - 1 ) - -- Prevent oversized requests - if msg._urldeclength >= msg.content_length then - return nil, "Request exceeds maximum allowed size" - end + if inhdr then + predata, eof = parse_headers( predata, field ) - -- Combine look-behind buffer with current chunk - local buffer = msg._urldecbuffer .. chunk - local spos, epos = buffer:find("=") - - -- Found param - if spos then - - -- Check that key doesn't exceed maximum allowed key length - if ( spos - 1 ) <= HTTP_URLENC_MAXKEYLEN then - local key = urldecode( buffer:sub( 1, spos - 1 ) ) - - -- Prepare buffers - msg._urldeclength = msg._urldeclength + epos - msg._urldecbuffer = buffer:sub( epos + 1, #buffer ) - - -- Use file callback or store values inside msg.params - if filecb then - msg._urldeccallback = function( chunk, eof ) - filecb( field, chunk, eof ) - end - else - local mv = __initval( msg.params, key ) - msg._urldeccallback = function( chunk, eof ) - __appendval( msg.params, key, mv, chunk ) - - -- FIXME: Use a filter - if eof then - __finishval( msg.params, key, mv, urldecode ) + if not eof then + return nil, "Invalid MIME section header" + elseif not field.name then + return nil, "Invalid Content-Disposition header" end end - end - -- Proceed with urldecode-value state - return true, function( chunk ) - return process_states['urldecode-value']( msg, chunk, filecb ) - end - else - return nil, "POST parameter exceeds maximum allowed length" - end - else - return nil, "POST data exceeds maximum allowed length" - end - else - return nil, "Unexpected EOF" - end -end + if store then + store( field, predata, true ) + end --- Process urldecoding stream, read parameter value -process_states['urldecode-value'] = function( msg, chunk, filecb ) + field = { headers = { } } + found = found or true - if chunk ~= nil then - - -- Combine look-behind buffer with current chunk - local buffer = msg._urldecbuffer .. chunk + data, eof = parse_headers( data:sub( epos + 1, #data ), field ) + inhdr = not eof + end + until not spos - -- Check for EOF - if #buffer == 0 then - -- Compare processed length - if msg._urldeclength == msg.content_length then - -- Cleanup - msg._urldeclength = nil - msg._urldecbuffer = nil - msg._urldeccallback = nil + if found then + if #data > 78 then + lchunk = data:sub( #data - 78 + 1, #data ) + data = data:sub( 1, #data - 78 ) - -- We won't accept data anymore - return false + if store then + store( field, data, false ) + else + return nil, "Invalid MIME section header" + end + else + lchunk, data = data, nil + end else - return nil, "Content-Length mismatch" + if inhdr then + lchunk, eof = parse_headers( data, field ) + inhdr = not eof + else + store( field, lchunk, false ) + lchunk, chunk = chunk, nil + end end end - -- Check for end of value - local spos, epos = buffer:find("[&;]") - if spos then - - -- Flush buffer, send eof - msg._urldeccallback( buffer:sub( 1, spos - 1 ), true ) - msg._urldecbuffer = buffer:sub( epos + 1, #buffer ) - msg._urldeclength = msg._urldeclength + epos - - -- Back to urldecode-key state - return true, function( chunk ) - return process_states['urldecode-key']( msg, chunk, filecb ) - end - else - -- We're somewhere within a data section and our buffer is full - if #buffer > #chunk then - -- Flush buffered data - msg._urldeccallback( buffer:sub( 1, #buffer - #chunk ), false ) - - -- Store new data - msg._urldeclength = msg._urldeclength + #buffer - #chunk - msg._urldecbuffer = buffer:sub( #buffer - #chunk + 1, #buffer ) - - -- Buffer is not full yet, append new data - else - msg._urldecbuffer = buffer - end - - -- Keep feeding me - return true - end - else - -- Send EOF - msg._urldeccallback( "", true ) - return false + return true end -end - - --- Creates a header source from a given socket -function header_source( sock ) - return ltn12.source.simplify( function() - - local chunk, err, part = sock:receive("*l") - - -- Line too long - if chunk == nil then - if err ~= "timeout" then - return nil, part - and "Line exceeds maximum allowed length" - or "Unexpected EOF" - else - return nil, err - end - - -- Line ok - elseif chunk ~= nil then - -- Strip trailing CR - chunk = chunk:gsub("\r$","") - - return chunk, nil - end - end ) + return ltn12.pump.all( src, snk ) end +--- Decode an urlencoded http message body with application/x-www-urlencoded +-- Content-Type. Stores all extracted data associated with its parameter name +-- in the params table withing the given message object. Multiple parameter +-- values are stored as tables, ordinary ones as strings. +-- @param src Ltn12 source function +-- @param msg HTTP message object +-- @return Value indicating successful operation (not nil means "ok") +-- @return String containing the error if unsuccessful +-- @see parse_message_header +function urldecode_message_body( src, msg ) --- Decode MIME encoded data. -function mimedecode_message_body( source, msg, filecb ) + local tlen = 0 + local lchunk = nil - -- Find mime boundary - if msg and msg.env.CONTENT_TYPE then + local function snk( chunk ) - local bound = msg.env.CONTENT_TYPE:match("^multipart/form%-data; boundary=(.+)") + tlen = tlen + ( chunk and #chunk or 0 ) - if bound then - msg.mime_boundary = bound - else - return nil, "No MIME boundary found or invalid content type given" + if msg.env.CONTENT_LENGTH and tlen > tonumber(msg.env.CONTENT_LENGTH) + 2 then + return nil, "Message body size exceeds Content-Length" + elseif tlen > HTTP_MAX_CONTENT then + return nil, "Message body size exceeds maximum allowed length" end - end - -- Create an initial LTN12 sink - -- The whole MIME parsing process is implemented as fancy sink, sinks replace themself - -- depending on current processing state (init, header, data). Return the initial state. - local sink = ltn12.sink.simplify( - function( chunk ) - return process_states['mime-init']( msg, chunk, filecb ) - end - ) + if not lchunk and chunk then + lchunk = chunk - -- Create a throttling LTN12 source - -- Frequent state switching in the mime parsing process leads to unwanted buffer aggregation. - -- This source checks wheather there's still data in our internal read buffer and returns an - -- empty string if there's already enough data in the processing queue. If the internal buffer - -- runs empty we're calling the original source to get the next chunk of data. - local tsrc = function() - - -- XXX: we schould propably keep the maximum buffer size in sync with - -- the blocksize of our original source... but doesn't really matter - if msg._mimebuffer ~= null and #msg._mimebuffer > 256 then - return "" - else - return source() - end - end + elseif lchunk then + local data = lchunk .. ( chunk or "&" ) + local spos, epos - -- Pump input data... - while true do - -- get data - local ok, err = ltn12.pump.step( tsrc, sink ) + repeat + spos, epos = data:find("^.-[;&]") - -- error - if not ok and err then - return nil, err - - -- eof - elseif not ok then - return true - end - end -end + if spos then + local pair = data:sub( spos, epos - 1 ) + local key = pair:match("^(.-)=") + local val = pair:match("=([^%s]*)%s*$") + if key and #key > 0 then + __initval( msg.params, key ) + __appendval( msg.params, key, val ) + __finishval( msg.params, key, urldecode ) + end --- Decode urlencoded data. -function urldecode_message_body( source, msg ) + data = data:sub( epos + 1, #data ) + end + until not spos - -- Create an initial LTN12 sink - -- Return the initial state. - local sink = ltn12.sink.simplify( - function( chunk ) - return process_states['urldecode-init']( msg, chunk ) + lchunk = data end - ) - -- Create a throttling LTN12 source - -- See explaination in mimedecode_message_body(). - local tsrc = function() - if msg._urldecbuffer ~= null and #msg._urldecbuffer > 0 then - return "" - else - return source() - end + return true end - -- Pump input data... - while true do - -- get data - local ok, err = ltn12.pump.step( tsrc, sink ) - - -- step - if not ok and err then - return nil, err - - -- eof - elseif not ok then - return true - end - end + return ltn12.pump.all( src, snk ) end - --- Parse a http message header -function parse_message_header( source ) +--- Try to extract an http message header including information like protocol +-- version, message headers and resulting CGI environment variables from the +-- given ltn12 source. +-- @param src Ltn12 source function +-- @return HTTP message object +-- @see parse_message_body +function parse_message_header( src ) local ok = true local msg = { } @@ -704,7 +542,7 @@ function parse_message_header( source ) while ok do -- get data - ok, err = ltn12.pump.step( source, sink ) + ok, err = ltn12.pump.step( src, sink ) -- error if not ok and err then @@ -725,12 +563,14 @@ function parse_message_header( source ) -- Populate common environment variables msg.env = { CONTENT_LENGTH = msg.headers['Content-Length']; - CONTENT_TYPE = msg.headers['Content-Type']; + CONTENT_TYPE = msg.headers['Content-Type'] or msg.headers['Content-type']; REQUEST_METHOD = msg.request_method:upper(); REQUEST_URI = msg.request_uri; SCRIPT_NAME = msg.request_uri:gsub("?.+$",""); SCRIPT_FILENAME = ""; -- XXX implement me - SERVER_PROTOCOL = "HTTP/" .. string.format("%.1f", msg.http_version) + SERVER_PROTOCOL = "HTTP/" .. string.format("%.1f", msg.http_version); + QUERY_STRING = msg.request_uri:match("?") + and msg.request_uri:gsub("^.+?","") or "" } -- Populate HTTP_* environment variables @@ -756,21 +596,32 @@ function parse_message_header( source ) return msg end - --- Parse a http message body -function parse_message_body( source, msg, filecb ) +--- Try to extract and decode a http message body from the given ltn12 source. +-- This function will examine the Content-Type within the given message object +-- to select the appropriate content decoder. +-- Currently the application/x-www-urlencoded and application/form-data +-- mime types are supported. If the encountered content encoding can't be +-- handled then the whole message body will be stored unaltered as "content" +-- property within the given message object. +-- @param src Ltn12 source function +-- @param msg HTTP message object +-- @param filecb File data callback (optional, see mimedecode_message_body()) +-- @return Value indicating successful operation (not nil means "ok") +-- @return String containing the error if unsuccessful +-- @see parse_message_header +function parse_message_body( src, msg, filecb ) -- Is it multipart/mime ? if msg.env.REQUEST_METHOD == "POST" and msg.env.CONTENT_TYPE and msg.env.CONTENT_TYPE:match("^multipart/form%-data") then - return mimedecode_message_body( source, msg, filecb ) + return mimedecode_message_body( src, msg, filecb ) -- Is it application/x-www-form-urlencoded ? elseif msg.env.REQUEST_METHOD == "POST" and msg.env.CONTENT_TYPE and - msg.env.CONTENT_TYPE == "application/x-www-form-urlencoded" + msg.env.CONTENT_TYPE:match("^application/x%-www%-form%-urlencoded") then - return urldecode_message_body( source, msg, filecb ) + return urldecode_message_body( src, msg, filecb ) -- Unhandled encoding @@ -789,22 +640,23 @@ function parse_message_body( source, msg, filecb ) msg.content = "" msg.content_length = 0 - sink = function( chunk ) - if ( msg.content_length + #chunk ) <= HTTP_MAX_CONTENT then - - msg.content = msg.content .. chunk - msg.content_length = msg.content_length + #chunk - - return true - else - return nil, "POST data exceeds maximum allowed length" + sink = function( chunk, err ) + if chunk then + if ( msg.content_length + #chunk ) <= HTTP_MAX_CONTENT then + msg.content = msg.content .. chunk + msg.content_length = msg.content_length + #chunk + return true + else + return nil, "POST data exceeds maximum allowed length" + end end + return true end end -- Pump data... while true do - local ok, err = ltn12.pump.step( source, sink ) + local ok, err = ltn12.pump.step( src, sink ) if not ok and err then return nil, err @@ -812,13 +664,17 @@ function parse_message_body( source, msg, filecb ) return true end end + + return true end end --- Status codes +--- Table containing human readable messages for several http status codes. +-- @class table statusmsg = { [200] = "OK", [301] = "Moved Permanently", + [302] = "Found", [304] = "Not Modified", [400] = "Bad Request", [403] = "Forbidden",