* luci/libs/http: use aliased ltn12. instead of luci.ltn12.
[project/luci.git] / libs / http / luasrc / http / protocol.lua
index 95712c9..acf47d2 100644 (file)
@@ -17,10 +17,7 @@ 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
-TSRC_BLOCKSIZE        = 2048           -- target block size for throttling sources
-
+HTTP_MAX_CONTENT      = 1024*8         -- 8 kB maximum content size
 
 -- Decode an urlencoded string.
 -- Returns the decoded value.
@@ -229,157 +226,6 @@ process_states['headers'] = function( msg, chunk )
 end
 
 
--- Init urldecoding stream
-process_states['urldecode-init'] = function( msg, chunk, filecb )
-
-       if chunk ~= nil then
-
-               -- Check for Content-Length
-               if msg.env.CONTENT_LENGTH then
-                       msg.content_length = tonumber(msg.env.CONTENT_LENGTH)
-
-                       if msg.content_length <= HTTP_MAX_CONTENT then
-                               -- Initialize buffer
-                               msg._urldecbuffer = chunk
-                               msg._urldeclength = 0
-
-                               -- Switch to urldecode-key state
-                               return true, function(chunk)
-                                       return process_states['urldecode-key']( msg, chunk, filecb )
-                               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
-
-               -- Prevent oversized requests
-               if msg._urldeclength >= msg.content_length then
-                       return nil, "Request exceeds maximum allowed size"
-               end
-
-               -- 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
-                                       __initval( msg.params, key )
-
-                                       msg._urldeccallback = function( chunk, eof )
-                                               __appendval( msg.params, key, chunk )
-
-                                               -- FIXME: Use a filter
-                                               if eof then
-                                                       __finishval( msg.params, key, urldecode )
-                                               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
-
-
--- Process urldecoding stream, read parameter value
-process_states['urldecode-value'] = function( msg, chunk, filecb )
-
-       if chunk ~= nil then
-
-               -- Combine look-behind buffer with current chunk
-               local buffer = msg._urldecbuffer .. chunk
-
-               -- 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
-
-                               -- We won't accept data anymore
-                               return false
-                       else
-                               return nil, "Content-Length mismatch"
-                       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
-       end
-end
-
-
 -- Creates a header source from a given socket
 function header_source( sock )
        return ltn12.source.simplify( function()
@@ -420,6 +266,12 @@ function mimedecode_message_body( src, msg, filecb )
        end
 
 
+       local tlen   = 0
+       local inhdr  = false
+       local field  = nil
+       local store  = nil
+       local lchunk = nil
+
        local function parse_headers( chunk, field )
 
                local stat
@@ -448,24 +300,32 @@ function mimedecode_message_body( src, msg, filecb )
                                field.headers["Content-Type"] = "text/plain"
                        end
 
+                       if field.name and field.file and filecb then
+                               __initval( msg.params, field.name )
+                               __appendval( msg.params, field.name, field.file )
+
+                               store = filecb
+                       elseif field.name then
+                               __initval( msg.params, field.name )
+
+                               store = function( hdr, buf, eof )
+                                       __appendval( msg.params, field.name, buf )
+                               end
+                       else
+                               store = nil
+                       end
+
                        return chunk, true
                end
 
                return chunk, false
        end
 
-
-       local tlen   = 0
-       local inhdr  = false
-       local field  = nil
-       local store  = nil
-       local lchunk = nil
-
        local function snk( chunk )
 
                tlen = tlen + ( chunk and #chunk or 0 )
 
-               if msg.env.CONTENT_LENGTH and tlen > msg.env.CONTENT_LENGTH then
+               if msg.env.CONTENT_LENGTH and tlen > tonumber(msg.env.CONTENT_LENGTH) + 2 then
                        return nil, "Message body size exceeds Content-Length"
                end
 
@@ -492,9 +352,7 @@ function mimedecode_message_body( src, msg, filecb )
 
                                                if not eof then
                                                        return nil, "Invalid MIME section header"
-                                               end
-
-                                               if not field.name then
+                                               elseif not field.name then
                                                        return nil, "Invalid Content-Disposition header"
                                                end
                                        end
@@ -509,30 +367,16 @@ function mimedecode_message_body( src, msg, filecb )
 
                                        data, eof = parse_headers( data:sub( epos + 1, #data ), field )
                                        inhdr = not eof
-
-                                       if eof then
-                                               if field.file and filecb then
-                                                       msg.params[field.name] = field.file
-                                                       store = filecb
-                                               else
-                                                       __initval( msg.params, field.name )
-
-                                                       store = function( hdr, buf, eof )
-                                                               __appendval( msg.params, field.name, buf )
-                                                       end
-                                               end
-                                       end
                                end
                        until not spos
 
-
                        if found then
                                if #data > 78 then
                                        lchunk = data:sub( #data - 78 + 1, #data )
                                        data   = data:sub( 1, #data - 78 )
 
-                                       if store and field and field.name then
-                                               store( field.headers, data )
+                                       if store then
+                                               store( field.headers, data, false )
                                        else
                                                return nil, "Invalid MIME section header"
                                        end
@@ -544,7 +388,7 @@ function mimedecode_message_body( src, msg, filecb )
                                        lchunk, eof = parse_headers( data, field )
                                        inhdr = not eof
                                else
-                                       store( field.headers, lchunk )
+                                       store( field.headers, lchunk, false )
                                        lchunk, chunk = chunk, nil
                                end
                        end
@@ -553,45 +397,58 @@ function mimedecode_message_body( src, msg, filecb )
                return true
        end
 
-       return luci.ltn12.pump.all( src, snk )
+       return ltn12.pump.all( src, snk )
 end
 
 
 -- Decode urlencoded data.
-function urldecode_message_body( source, msg )
+function urldecode_message_body( src, msg )
 
-       -- Create an initial LTN12 sink
-       -- Return the initial state.
-       local sink = ltn12.sink.simplify(
-               function( chunk )
-                       return process_states['urldecode-init']( msg, chunk )
-               end
-       )
+       local tlen   = 0
+       local lchunk = nil
 
-       -- Create a throttling LTN12 source
-       -- See explaination in mimedecode_message_body().
-       local tsrc = function()
-               if msg._urldecbuffer ~= nil and #msg._urldecbuffer > 0 then
-                       return ""
-               else
-                       return source()
+       local function snk( chunk )
+
+               tlen = tlen + ( chunk and #chunk or 0 )
+
+               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
 
-       -- Pump input data...
-       while true do
-               -- get data
-               local ok, err = ltn12.pump.step( tsrc, sink )
+               if not lchunk and chunk then
+                       lchunk = chunk
 
-               -- step
-               if not ok and err then
-                       return nil, err
+               elseif lchunk then
+                       local data = lchunk .. ( chunk or "&" )
+                       local spos, epos
 
-               -- eof
-               elseif not ok then
-                       return true
+                       repeat
+                               spos, epos = data:find("^.-[;&]")
+
+                               if spos then
+                                       local pair = data:sub( spos, epos - 1 )
+                                       local key  = pair:match("^(.-)=")
+                                       local val  = pair:match("=(.*)$")
+
+                                       if key and #key > 0 then
+                                               __initval( msg.params, key )
+                                               __appendval( msg.params, key, val )
+                                               __finishval( msg.params, key, urldecode )
+                                       end
+
+                                       data = data:sub( epos + 1, #data )
+                               end
+                       until not spos
+
+                       lchunk = data
                end
+
+               return true
        end
+
+       return ltn12.pump.all( src, snk )
 end
 
 
@@ -631,7 +488,7 @@ function parse_message_header( source )
 
                        -- Populate common environment variables
                        msg.env = {
-                               CONTENT_LENGTH    = tonumber(msg.headers['Content-Length']);
+                               CONTENT_LENGTH    = msg.headers['Content-Length'];
                                CONTENT_TYPE      = msg.headers['Content-Type'];
                                REQUEST_METHOD    = msg.request_method:upper();
                                REQUEST_URI       = msg.request_uri;