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

@@ -9,6 +9,20 @@ deferred class
feature {NONE} -- Implementation
full_input_data (req: WSF_REQUEST): READABLE_STRING_8
do
if req.is_chunked_input then
if attached req.chunked_input as l_chunked_input then
Result := l_chunked_input.data
else
check has_chunked_input: False end
Result := ""
end
else
Result := read_input_data (req.input, req.content_length_value)
end
end
read_input_data (a_input: WGI_INPUT_STREAM; nb: NATURAL_64): READABLE_STRING_8
-- All data from input form
local

View File

@@ -21,7 +21,7 @@ feature -- Status report
feature -- Execution
handle (a_content_type: READABLE_STRING_8; a_content_length: NATURAL_64; req: WSF_REQUEST;
handle (a_content_type: READABLE_STRING_8; req: WSF_REQUEST;
a_vars: HASH_TABLE [WSF_VALUE, READABLE_STRING_32]; a_raw_data: detachable CELL [detachable STRING_8])
local
l_content: READABLE_STRING_8
@@ -29,12 +29,12 @@ feature -- Execution
s: READABLE_STRING_8
l_name, l_value: READABLE_STRING_8
do
l_content := read_input_data (req.input, a_content_length)
l_content := full_input_data (req)
if a_raw_data /= Void then
a_raw_data.replace (l_content)
end
check content_count_same_as_content_length_if_not_chunked: (not req.is_chunked_input) implies (l_content.count = req.content_length_value.to_integer_32) end --| FIXME: truncated value
n := l_content.count
check n_same_as_content_length: n = a_content_length.to_integer_32 end --| FIXME: truncated value
if n > 0 then
from
p := 1

View File

@@ -15,7 +15,7 @@ feature -- Status report
feature -- Execution
handle (a_content_type: READABLE_STRING_8; a_content_length: NATURAL_64; req: WSF_REQUEST;
handle (a_content_type: READABLE_STRING_8; req: WSF_REQUEST;
a_vars: TABLE [WSF_VALUE, READABLE_STRING_32]; a_raw_data: detachable CELL [detachable STRING_8])
-- Handle MIME content from request `req', eventually fill the `a_vars' (not yet available from `req')
-- and if `a_raw_data' is attached, store any read data inside `a_raw_data'

View File

@@ -43,12 +43,12 @@ feature -- Status report
feature -- Execution
handle (a_content_type: READABLE_STRING_8; a_content_length: NATURAL_64; req: WSF_REQUEST;
handle (a_content_type: READABLE_STRING_8; req: WSF_REQUEST;
a_vars: HASH_TABLE [WSF_VALUE, READABLE_STRING_32]; a_raw_data: detachable CELL [detachable STRING_8])
local
s: READABLE_STRING_8
do
s := read_input_data (req.input, a_content_length)
s := full_input_data (req)
if a_raw_data /= Void then
a_raw_data.replace (s)
end

View File

@@ -124,10 +124,26 @@ feature -- Access: Input
input: WGI_INPUT_STREAM
-- Server input channel
require
is_not_chunked_input: not is_chunked_input
do
Result := wgi_request.input
end
is_chunked_input: BOOLEAN
-- Is request using chunked transfer-encoding?
do
Result := wgi_request.is_chunked_input
end
chunked_input: detachable WGI_CHUNKED_INPUT_STREAM
-- Server input channel
require
is_chunked_input: is_chunked_input
do
Result := wgi_request.chunked_input
end
feature -- Helper
is_request_method (m: READABLE_STRING_8): BOOLEAN
@@ -768,6 +784,13 @@ feature -- HTTP_*
Result := wgi_request.http_authorization
end
http_transfer_encoding: detachable READABLE_STRING_8
-- Transfer-Encoding
-- for instance chunked
do
Result := wgi_request.http_transfer_encoding
end
feature -- Extra CGI environment variables
request_uri: READABLE_STRING_8
@@ -1133,13 +1156,11 @@ feature {NONE} -- Form fields and related
local
vars: like internal_form_data_parameters_table
l_raw_data_cell: detachable CELL [detachable STRING_8]
n: NATURAL_64
l_type: like content_type
do
vars := internal_form_data_parameters_table
if vars = Void then
n := content_length_value
if n = 0 then
if not is_chunked_input and content_length_value = 0 then
create vars.make (0)
vars.compare_objects
else
@@ -1151,9 +1172,10 @@ feature {NONE} -- Form fields and related
l_type := content_type
if l_type /= Void and then attached mime_handler (l_type) as hdl then
hdl.handle (l_type, n, Current, vars, l_raw_data_cell)
hdl.handle (l_type, Current, vars, l_raw_data_cell)
end
if l_raw_data_cell /= Void and then attached l_raw_data_cell.item as l_raw_data then
-- What if no mime handler is associated to `l_type' ?
set_meta_string_variable ("RAW_POST_DATA", l_raw_data)
end
end

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-9-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-9-0 http://www.eiffel.com/developers/xml/configuration-1-9-0.xsd" name="echo_server" uuid="D3EE5B55-6F2E-4247-8C94-E7CFC4C5B648">
<target name="echo_server">
<root class="ECHO_SERVER" feature="make"/>
<file_rule>
<exclude>/.git$</exclude>
<exclude>/EIFGENs$</exclude>
<exclude>/.svn$</exclude>
</file_rule>
<option warning="true" full_class_checking="true" is_attached_by_default="true" void_safety="all" syntax="provisional">
</option>
<setting name="concurrency" value="thread"/>
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
<library name="connector_nino" location="..\..\..\ewsgi\connectors\nino\nino-safe.ecf" readonly="false"/>
<library name="connector_null" location="..\..\..\ewsgi\connectors\null\null-safe.ecf" readonly="false"/>
<library name="dft_nino" location="..\..\default\nino-safe.ecf"/>
<library name="ewsgi" location="..\..\..\ewsgi\ewsgi-safe.ecf" readonly="false"/>
<library name="thread" location="$ISE_LIBRARY\library\thread\thread-safe.ecf"/>
<library name="wsf" location="..\..\wsf-safe.ecf" readonly="false"/>
<tests name="src" location=".\src\" recursive="true"/>
</target>
</system>

View File

@@ -0,0 +1,75 @@
note
description : "Objects that ..."
author : "$Author$"
date : "$Date$"
revision : "$Revision$"
class
ECHO_SERVER
inherit
WSF_SERVICE
create
make
feature {NONE} -- Initialization
make
-- Initialize `Current'.
local
launcher: DEFAULT_SERVICE_LAUNCHER
do
create launcher.make_and_launch_with_options (agent execute, <<["port", 9091]>>)
end
feature -- Execution
execute (req: WSF_REQUEST; res: WSF_RESPONSE)
local
page: WSF_PAGE_RESPONSE
l_body: STRING_8
do
create l_body.make (1024)
create page.make_with_body (l_body)
page.header.put_content_type_text_plain
l_body.append ("REQUEST_METHOD=" + req.request_method + "%N")
l_body.append ("REQUEST_URI=" + req.request_uri + "%N")
l_body.append ("PATH_INFO=" + req.path_info + "%N")
l_body.append ("QUERY_STRING=" + req.query_string + "%N")
l_body.append ("Query parameters:%N")
across
req.query_parameters as q
loop
l_body.append ("%T"+ q.item.name + "=" + q.item.string_representation +"%N")
end
l_body.append ("Form parameters:%N")
across
req.form_parameters as q
loop
l_body.append ("%T"+ q.item.name + "=" + q.item.string_representation +"%N")
end
l_body.append ("Meta variables:%N")
across
req.meta_variables as q
loop
l_body.append ("%T"+ q.item.name + "=" + q.item.string_representation +"%N")
end
res.put_response (page)
end
feature -- Access
feature -- Change
feature {NONE} -- Implementation
invariant
-- invariant_clause: True
end

View File

@@ -53,11 +53,14 @@ feature {NONE} -- Events
execute (req: WSF_REQUEST; res: WSF_RESPONSE)
local
q: detachable STRING_32
page: WSF_PAGE_RESPONSE
do
create page.make
if attached req.request_uri as l_uri then
if l_uri.starts_with (test_url ("get/01")) then
res.write_header (200, <<["Content-Type", "text/plain"]>>)
res.write_string ("get-01")
page.set_status_code (200)
page.header.put_content_type_text_plain
page.put_string ("get-01")
create q.make_empty
across
@@ -69,11 +72,11 @@ feature {NONE} -- Events
q.append (qcur.item.name.as_string_32 + "=" + qcur.item.as_string)
end
if not q.is_empty then
res.write_string ("(" + q + ")")
page.put_string ("(" + q + ")")
end
elseif l_uri.starts_with (test_url ("post/01")) then
res.write_header (200, <<["Content-Type", "text/plain"]>>)
res.write_string ("post-01")
page.put_header (200, <<["Content-Type", "text/plain"]>>)
page.put_string ("post-01")
create q.make_empty
across
@@ -86,7 +89,7 @@ feature {NONE} -- Events
end
if not q.is_empty then
res.write_string ("(" + q + ")")
page.put_string ("(" + q + ")")
end
create q.make_empty
@@ -102,16 +105,18 @@ feature {NONE} -- Events
end
if not q.is_empty then
res.write_string (" : " + q )
page.put_string (" : " + q )
end
else
res.write_header (200, <<["Content-Type", "text/plain"]>>)
res.write_string ("Hello")
page.put_header (200, <<["Content-Type", "text/plain"]>>)
page.put_string ("Hello")
end
else
res.write_header (200, <<["Content-Type", "text/plain"]>>)
res.write_string ("Bye")
page.put_header (200, <<["Content-Type", "text/plain"]>>)
page.put_string ("Bye")
end
page.send_to (res)
end
test_url (a_query_url: READABLE_STRING_8): READABLE_STRING_8

View File

@@ -0,0 +1,64 @@
note
description: "[
Eiffel tests that can be executed by testing tool.
]"
author: "EiffelStudio test wizard"
date: "$Date$"
revision: "$Revision$"
testing: "type/manual"
class
TEST_WSF_REQUEST_CHUNKED_INPUT
inherit
TEST_WSF_REQUEST
redefine
test_get_request_01,
test_post_request_01
end
feature -- Test routines
test_get_request_01
-- New test routine
local
ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT
do
get_http_session
if attached http_session as sess then
-- create ctx.make
-- ctx.set_proxy ("127.0.0.1", 8888) --| debugging with http://www.fiddler2.com/
test_get_request ("get/01", ctx, "get-01")
test_get_request ("get/01/?foo=bar", ctx, "get-01(foo=bar)")
test_get_request ("get/01/?foo=bar&abc=def", ctx, "get-01(foo=bar&abc=def)")
test_get_request ("get/01/?lst=a&lst=b", ctx, "get-01(lst=[a,b])")
else
assert ("not_implemented", False)
end
end
test_post_request_01
-- New test routine
local
ctx: HTTP_CLIENT_REQUEST_CONTEXT
do
get_http_session
if attached http_session as sess then
create ctx.make
ctx.add_form_parameter ("id", "123")
ctx.headers.extend ("chunked", "Transfer-Encoding")
-- ctx.set_proxy ("127.0.0.1", 8888) --| debugging with http://www.fiddler2.com/
test_post_request ("post/01", ctx, "post-01 : id=123")
test_post_request ("post/01/?foo=bar", ctx, "post-01(foo=bar) : id=123")
test_post_request ("post/01/?foo=bar&abc=def", ctx, "post-01(foo=bar&abc=def) : id=123")
test_post_request ("post/01/?lst=a&lst=b", ctx, "post-01(lst=[a,b]) : id=123")
else
assert ("not_implemented", False)
end
end
end

View File

@@ -80,7 +80,6 @@ feature {NONE} -- Implementation
new_request (a_meta: ARRAY [TUPLE [name: READABLE_STRING_8; value: READABLE_STRING_8]]): WSF_REQUEST
local
wgi_req: WGI_REQUEST
req: WSF_REQUEST
do
create {WGI_REQUEST_NULL} wgi_req.make (Current, a_meta)
create Result.make_from_wgi (wgi_req)

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-8-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-8-0 http://www.eiffel.com/developers/xml/configuration-1-8-0.xsd" name="wsf_tests" uuid="CA72F5B3-E608-4FA5-8F05-A812441DB961">
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-9-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-9-0 http://www.eiffel.com/developers/xml/configuration-1-9-0.xsd" name="wsf_tests" uuid="C4FF9CDA-B4E4-4841-97E0-7F799B85B657">
<target name="tests">
<root class="ANY" feature="default_create"/>
<file_rule>
@@ -14,11 +14,11 @@
<library name="connector_nino" location="..\..\ewsgi\connectors\nino\nino-safe.ecf" readonly="false"/>
<library name="connector_null" location="..\..\ewsgi\connectors\null\null-safe.ecf" readonly="false"/>
<library name="dft_nino" location="..\default\nino-safe.ecf"/>
<library name="ewsgi" location="..\..\ewsgi\ewsgi-safe.ecf"/>
<library name="http_client" location="..\..\..\client\http_client\http_client-safe.ecf"/>
<library name="ewsgi" location="..\..\ewsgi\ewsgi-safe.ecf" readonly="false"/>
<library name="http_client" location="..\..\..\client\http_client\http_client-safe.ecf" readonly="false"/>
<library name="testing" location="$ISE_LIBRARY\library\testing\testing-safe.ecf"/>
<library name="thread" location="$ISE_LIBRARY\library\thread\thread-safe.ecf"/>
<library name="wsf" location="..\wsf-safe.ecf" readonly="false"/>
<tests name="src" location=".\src" recursive="true"/>
<tests name="src" location=".\src\" recursive="true"/>
</target>
</system>

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-9-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-9-0 http://www.eiffel.com/developers/xml/configuration-1-9-0.xsd" name="wsf_tests" uuid="CA72F5B3-E608-4FA5-8F05-A812441DB961">
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-9-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-9-0 http://www.eiffel.com/developers/xml/configuration-1-9-0.xsd" name="wsf_tests" uuid="C4FF9CDA-B4E4-4841-97E0-7F799B85B657">
<target name="tests">
<root class="ANY" feature="default_create"/>
<file_rule>