* luci/libs: add support for chunked transfer decoding in http.protocol
[project/luci.git] / libs / http / luasrc / http / protocol.lua
index 01d3128..08e8ba2 100644 (file)
@@ -17,6 +17,7 @@ module("luci.http.protocol", package.seeall)
 
 require("ltn12")
 require("luci.util")
+require("luci.http.protocol.filter")
 
 HTTP_MAX_CONTENT      = 1024*4         -- 4 kB maximum content size
 HTTP_URLENC_MAXKEYLEN = 1024           -- maximum allowd size of urlencoded parameter names
@@ -127,7 +128,7 @@ process_states['magic'] = function( msg, chunk )
                        msg.type           = "request"
                        msg.request_method = method:lower()
                        msg.request_uri    = uri
-                       msg.http_version   = http_ver
+                       msg.http_version   = tonumber( http_ver )
                        msg.headers        = { }
 
                        -- We're done, next state is header parsing
@@ -146,7 +147,7 @@ process_states['magic'] = function( msg, chunk )
                                msg.type           = "response"
                                msg.status_code    = code
                                msg.status_message = message
-                               msg.http_version   = http_ver
+                               msg.http_version   = tonumber( http_ver )
                                msg.headers        = { }
 
                                -- We're done, next state is header parsing
@@ -378,8 +379,8 @@ process_states['urldecode-init'] = function( msg, chunk, filecb )
        if chunk ~= nil then
 
                -- Check for Content-Length
-               if msg.headers['Content-Length'] then
-                       msg.content_length = tonumber(msg.headers['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
@@ -404,7 +405,6 @@ end
 
 -- Process urldecoding stream, read and validate parameter key
 process_states['urldecode-key'] = function( msg, chunk, filecb )
-
        if chunk ~= nil then
 
                -- Prevent oversized requests
@@ -436,6 +436,11 @@ process_states['urldecode-key'] = function( msg, chunk, filecb )
                                else
                                        msg._urldeccallback = function( chunk, eof )
                                                msg.params[key] = msg.params[key] .. chunk
+
+                                               -- FIXME: Use a filter
+                                               if eof then
+                                                       msg.params[key] = urldecode( msg.params[key] )
+                                               end
                                        end
                                end
 
@@ -520,9 +525,9 @@ end
 function mimedecode_message_body( source, msg, filecb )
 
        -- Find mime boundary
-       if msg and msg.headers['Content-Type'] then
+       if msg and msg.env.CONTENT_TYPE then
 
-               local bound = msg.headers['Content-Type']:match("^multipart/form%-data; boundary=(.+)")
+               local bound = msg.env.CONTENT_TYPE:match("^multipart/form%-data; boundary=(.+)")
 
                if bound then
                        msg.mime_boundary = bound
@@ -666,7 +671,8 @@ function parse_message_header( source )
                                REQUEST_METHOD    = msg.request_method:upper();
                                REQUEST_URI       = msg.request_uri;
                                SCRIPT_NAME       = msg.request_uri:gsub("?.+$","");
-                               SCRIPT_FILENAME   = ""          -- XXX implement me
+                               SCRIPT_FILENAME   = "";         -- XXX implement me
+                               SERVER_PROTOCOL   = "HTTP/" .. msg.http_version
                        }
 
                        -- Populate HTTP_* environment variables
@@ -696,6 +702,18 @@ end
 -- Parse a http message body
 function parse_message_body( source, msg, filecb )
 
+       -- Install an additional filter if we're operating on chunked transfer
+       -- coding and client is HTTP/1.1 capable
+       if msg.http_version == 1.1 and
+          msg.headers['Transfer-Encoding'] and
+          msg.headers['Transfer-Encoding']:find("chunked")
+       then
+               source = ltn12.source.chain(
+                       source, luci.http.protocol.filter.decode_chunked
+               )
+       end
+
+
        -- Is it multipart/mime ?
        if msg.env.REQUEST_METHOD == "POST" and msg.env.CONTENT_TYPE and
           msg.env.CONTENT_TYPE:match("^multipart/form%-data")
@@ -707,16 +725,15 @@ function parse_message_body( source, msg, filecb )
        elseif msg.env.REQUEST_METHOD == "POST" and msg.env.CONTENT_TYPE and
               msg.env.CONTENT_TYPE == "application/x-www-form-urlencoded"
        then
-
                return urldecode_message_body( source, msg, filecb )
 
+
        -- Unhandled encoding
        -- If a file callback is given then feed it line by line, else
        -- store whole buffer in message.content
        else
 
                local sink
-               local length = 0
 
                -- If we have a file callback then feed it
                if type(filecb) == "function" then
@@ -728,7 +745,7 @@ function parse_message_body( source, msg, filecb )
                        msg.content_length = 0
 
                        sink = function( chunk )
-                               if ( msg.content_length ) + #chunk <= HTTP_MAX_CONTENT then
+                               if ( msg.content_length + #chunk ) <= HTTP_MAX_CONTENT then
 
                                        msg.content        = msg.content        .. chunk
                                        msg.content_length = msg.content_length + #chunk