Added support for chunked input data (see Transfer-Encoding: chunked)

This commit is contained in:
Jocelyn Fiat
2012-02-07 15:47:55 +01:00
parent cf8d25c4e5
commit 69bc4d568c
16 changed files with 378 additions and 102 deletions

View File

@@ -26,15 +26,24 @@ feature {NONE} -- Initialization
wgi_connector := a_wgi_connector
input := a_input
set_meta_variables (a_vars)
update_path_info
if attached http_transfer_encoding as l_transfer_encoding and then l_transfer_encoding.same_string ("chunked") then
is_chunked_input := True
create chunked_input.make (a_input)
end
end
feature -- Access: Input
is_chunked_input: BOOLEAN
-- Is request using chunked transfer-encoding?
input: WGI_INPUT_STREAM
-- Server input channel
chunked_input: detachable WGI_CHUNKED_INPUT_STREAM
-- Chunked server input channel
feature -- EWSGI access
wgi_version: STRING = "0.1"
@@ -211,6 +220,13 @@ feature -- Access: HTTP_* CGI meta parameters - 1.1
Result := meta_string_variable ({WGI_META_NAMES}.http_authorization)
end
http_transfer_encoding: detachable READABLE_STRING_8
-- Transfer-Encoding
-- for instance chunked
do
Result := meta_string_variable ({WGI_META_NAMES}.http_transfer_encoding)
end
feature -- Access: Extension to CGI meta parameters - 1.1
request_uri: READABLE_STRING_8
@@ -340,20 +356,6 @@ feature {NONE} -- Element change: CGI meta parameter related to PATH_INFO
end
end
feature {NONE} -- I/O: implementation
read_input (nb: INTEGER)
-- Read `nb' bytes from `input'
do
input.read_string (nb)
end
last_input_string: STRING
-- Last string read from `input'
do
Result := input.last_string
end
feature {NONE} -- Implementation: utilities
single_slash_starting_string (s: READABLE_STRING_8): STRING_8

View File

@@ -6,118 +6,167 @@ note
class
WGI_CHUNKED_INPUT_STREAM
create
make
feature {NONE} -- Implementation
make ( an_input : like input)
make (an_input: like input)
do
create last_chunked.make_empty
create last_chunk_size.make_empty
create tmp_hex_chunk_size.make_empty
input := an_input
end
feature --Input
read
-- Read all the data in a chunked stream.
-- Make the result available in `last_chunked'.
-- Chunked-Body = *chunk
-- last-chunk
-- trailer
-- CRLF
-- chunk = chunk-size [ chunk-extension ] CRLF
-- chunk-data CRLF
-- chunk-size = 1*HEX
-- last-chunk = 1*("0") [ chunk-extension ] CRLF
-- chunk-extension= *( ";" chunk-ext-name [ "=" chunk-ext-val ] )
-- chunk-ext-name = token
-- chunk-ext-val = token | quoted-string
-- chunk-data = chunk-size(OCTET)
-- trailer = *(entity-header CRLF)
feature -- Input
data: READABLE_STRING_8
local
d: like internal_data
do
d := internal_data
if d = Void then
d := fetched_data
internal_data := d
end
Result := d
end
feature {NONE} -- Parser
internal_data: detachable READABLE_STRING_8
tmp_hex_chunk_size: STRING_8
last_chunk_size: INTEGER
last_chunk: detachable STRING_8
fetched_data: READABLE_STRING_8
-- Read all the data in a chunked stream.
-- Make the result available in `last_chunked'.
-- Chunked-Body = *chunk
-- last-chunk
-- trailer
-- CRLF
-- chunk = chunk-size [ chunk-extension ] CRLF
-- chunk-data CRLF
-- chunk-size = 1*HEX
-- last-chunk = 1*("0") [ chunk-extension ] CRLF
-- chunk-extension= *( ";" chunk-ext-name [ "=" chunk-ext-val ] )
-- chunk-ext-name = token
-- chunk-ext-val = token | quoted-string
-- chunk-data = chunk-size(OCTET)
-- trailer = *(entity-header CRLF)
local
eoc : BOOLEAN
s: STRING_8
do
from
read_chunk
last_chunked.append (input.last_string)
if last_chunk_size.is_equal ("0") then
eoc := true
end
create s.make (1024)
until
eoc
loop
read_chunk
last_chunked.append (input.last_string)
if last_chunk_size.is_equal ("0") then
if attached last_chunk as l_last_chunk then
s.append (l_last_chunk)
else
eoc := true
end
if last_chunk_size = 0 then
eoc := True
end
end
read_trailer
Result := s
end
feature {NONE} -- Parser
last_chunk_size : STRING
reset_chunk
do
last_chunk := Void
last_chunk_size := 0
end
read_chunk
do
-- clear last results
last_chunk_size.wipe_out
-- new read
reset_chunk
read_chunk_size
read_chunk_data
if last_chunk_size > 0 then
read_chunk_data
end
end
read_chunk_data
local
hex : HEXADECIMAL_STRING_TO_INTEGER_CONVERTER
require
last_chunk_size > 0
do
create hex.make
hex.parse_string_with_type (last_chunk_size, hex.type_integer)
if hex.parse_successful then
input.read_string (hex.parsed_integer)
end
input.read_string (last_chunk_size)
last_chunk := input.last_string
-- read CRLF
input.read_character
if input.last_character.is_equal ('%R') then
if input.last_character = '%R' then
input.read_character
end
ensure
last_chunk_attached: attached last_chunk as el_last_chunk
last_chunk_size_ok: el_last_chunk.count = last_chunk_size
end
read_chunk_size
require
tmp_hex_chunk_size_is_empty: tmp_hex_chunk_size.is_empty
local
eol : BOOLEAN
c: CHARACTER
hex : HEXADECIMAL_STRING_TO_INTEGER_CONVERTER
do
from
input.read_character
until
eol
loop
if input.last_character.is_equal ('%R') then
c := input.last_character
inspect c
when '%R' then
-- We are in the end of the line, we need to read the next character to start the next line.
eol := True
input.read_character
eol := true
elseif input.last_character.is_equal (';') then
when ';' then
-- We are in an extension chunk data
read_extension_chunk
else
last_chunk_size.append_character (input.last_character)
tmp_hex_chunk_size.append_character (c)
input.read_character
end
end
if tmp_hex_chunk_size.same_string ("0") then
last_chunk_size := 0
else
create hex.make
hex.parse_string_with_type (tmp_hex_chunk_size, hex.type_integer)
if hex.parse_successful then
last_chunk_size := hex.parsed_integer
else
last_chunk_size := 0 -- ERROR ...
end
end
tmp_hex_chunk_size.wipe_out
end
read_extension_chunk
do
print (" Reading extension chunk ")
debug
print (" Reading extension chunk ")
end
from
input.read_character
until
input.last_character.is_equal ('%R')
input.last_character = '%R'
loop
print (input.last_character)
debug
print (input.last_character)
end
input.read_character
end
end
@@ -125,29 +174,31 @@ feature {NONE} -- Parser
read_trailer
do
if not input.end_of_input then
print (" Reading trailer ")
debug
print (" Reading trailer ")
end
from
input.read_character
until
input.last_character.is_equal ('%R')
input.last_character = '%R'
loop
print (input.last_character)
debug
print (input.last_character)
end
input.read_character
end
-- read the LF
input.read_character
end
end
feature {NONE} -- Access
feature {NONE} -- Implementation
input: WGI_INPUT_STREAM
-- Input Stream
feature -- Access
last_chunked: STRING_8
;note
copyright: "2011-2011, Eiffel Software and others"
copyright: "2011-2012, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[
Eiffel Software