From a038da390d7fd3a75b5a1045afbb4d9782556c57 Mon Sep 17 00:00:00 2001 From: Jo-Philipp Wich Date: Sun, 22 Jun 2008 12:07:37 +0000 Subject: [PATCH] * luci/libs: add support for chunked transfer decoding in http.protocol --- libs/http/luasrc/http/protocol.lua | 24 ++++++--- libs/http/luasrc/http/protocol/filter.lua | 81 +++++++++++++++++++++++++++++++ 2 files changed, 99 insertions(+), 6 deletions(-) create mode 100644 libs/http/luasrc/http/protocol/filter.lua diff --git a/libs/http/luasrc/http/protocol.lua b/libs/http/luasrc/http/protocol.lua index 6e53d7ca1..08e8ba256 100644 --- a/libs/http/luasrc/http/protocol.lua +++ b/libs/http/luasrc/http/protocol.lua @@ -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 @@ -435,7 +436,7 @@ 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] ) @@ -701,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") @@ -713,7 +726,7 @@ function parse_message_body( source, msg, filecb ) 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 @@ -721,7 +734,6 @@ function parse_message_body( source, msg, filecb ) else local sink - local length = 0 -- If we have a file callback then feed it if type(filecb) == "function" then @@ -733,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 diff --git a/libs/http/luasrc/http/protocol/filter.lua b/libs/http/luasrc/http/protocol/filter.lua new file mode 100644 index 000000000..71263c351 --- /dev/null +++ b/libs/http/luasrc/http/protocol/filter.lua @@ -0,0 +1,81 @@ +--[[ + +HTTP protocol implementation for LuCI - filter implementation +(c) 2008 Freifunk Leipzig / Jo-Philipp Wich + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +$Id$ + +]]-- + +module("luci.http.protocol.filter", package.seeall) + +require("ltn12") + + +-- Factory that produces a filter which normalizes chunked transfer encoding +function decode_chunked() + + local length = 0 + local read = 0 + + return ltn12.filter.cycle( + function( chunk, ctx ) + + if chunk ~= nil then + + -- EOF + if ctx == nil then + if ( length - read ) > 0 then + return nil, "Unexpected EOF" + else + return "" + end + end + + chunk = ctx .. chunk + + local buf = "" + while true do + + if read == length then + + -- Find chunk length indicator + local spos, epos = chunk:find("^\r?\n?[a-fA-F0-9]+ *\r\n") + if spos and spos == 1 then + read = 0 + length = tonumber( + chunk:sub( 1, epos ):gsub( "[^a-fA-F0-9]", "" ), 16 + ) + + -- Check for end of chunk + if length > 0 then + chunk = chunk:sub( epos + 1, #chunk ) + else + return buf, "" + end + else + return "", nil + end + else + if ( read + #chunk ) <= length then + read = read + #chunk + return buf .. chunk, "" + else + local rest = length - read + read = read + rest + buf = buf .. chunk:sub( 1, rest ) + chunk = chunk:sub( rest + 1, #chunk ) + end + end + end + end + end, + "" + ) +end -- 2.11.0