Implemented chunked Transfer-Encoding in net_http_client.
Implemented support for buffer_size and chunk_size for net_http_client.
This commit is contained in:
@@ -164,11 +164,24 @@ feature -- Settings
|
|||||||
Result := session.ignore_content_length
|
Result := session.ignore_content_length
|
||||||
end
|
end
|
||||||
|
|
||||||
buffer_size: NATURAL
|
buffer_size: INTEGER
|
||||||
-- Set the buffer size for request. This option will
|
-- Buffer size for request,
|
||||||
-- only be set if buffer_size is positive
|
-- initialized from the session buffer_size value, or default 2_048.
|
||||||
do
|
do
|
||||||
Result := session.buffer_size
|
Result := session.buffer_size.to_integer_32
|
||||||
|
if Result <= 0 then
|
||||||
|
Result := 2_048
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
chunk_size: INTEGER
|
||||||
|
-- Chunk size for request, when "Transfer-Encoding: chunked"
|
||||||
|
-- initialized from the session buffer_size value, or default 2_048.
|
||||||
|
do
|
||||||
|
Result := session.chunk_size.to_integer_32
|
||||||
|
if Result <= 0 then
|
||||||
|
Result := 2_048
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
default_response_charset: detachable READABLE_STRING_8
|
default_response_charset: detachable READABLE_STRING_8
|
||||||
@@ -249,7 +262,7 @@ feature {NONE} -- Utilities: encoding
|
|||||||
end
|
end
|
||||||
|
|
||||||
note
|
note
|
||||||
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
copyright: "2011-2016, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||||
source: "[
|
source: "[
|
||||||
Eiffel Software
|
Eiffel Software
|
||||||
|
|||||||
@@ -243,9 +243,13 @@ feature -- Settings
|
|||||||
ignore_content_length: BOOLEAN
|
ignore_content_length: BOOLEAN
|
||||||
-- Does this session ignore Content-Size headers?
|
-- Does this session ignore Content-Size headers?
|
||||||
|
|
||||||
buffer_size: NATURAL
|
buffer_size: NATURAL assign set_buffer_size
|
||||||
-- Set the buffer size for request. This option will
|
-- Set the buffer size for request. This option will
|
||||||
-- only be set if buffer_size is positive
|
-- only be set if buffer_size > 0.
|
||||||
|
|
||||||
|
chunk_size: NATURAL assign set_chunk_size
|
||||||
|
-- Set the chunk size for request, when "Transfer-Encoding: chunked".
|
||||||
|
-- This option will only be set if chunk_size > 0.
|
||||||
|
|
||||||
default_response_charset: detachable READABLE_STRING_8
|
default_response_charset: detachable READABLE_STRING_8
|
||||||
-- Default encoding of responses. Used if no charset is provided by the host.
|
-- Default encoding of responses. Used if no charset is provided by the host.
|
||||||
@@ -405,8 +409,18 @@ feature -- Element change
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
set_buffer_size (a_size: like buffer_size)
|
||||||
|
do
|
||||||
|
buffer_size := a_size
|
||||||
|
end
|
||||||
|
|
||||||
|
set_chunk_size (a_size: like chunk_size)
|
||||||
|
do
|
||||||
|
chunk_size := a_size
|
||||||
|
end
|
||||||
|
|
||||||
note
|
note
|
||||||
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
copyright: "2011-2016, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||||
source: "[
|
source: "[
|
||||||
Eiffel Software
|
Eiffel Software
|
||||||
|
|||||||
@@ -100,6 +100,7 @@ feature -- Access
|
|||||||
l_boundary: READABLE_STRING_8
|
l_boundary: READABLE_STRING_8
|
||||||
l_is_http_1_0_request: BOOLEAN
|
l_is_http_1_0_request: BOOLEAN
|
||||||
l_is_keep_alive: BOOLEAN
|
l_is_keep_alive: BOOLEAN
|
||||||
|
l_is_chunked_transfer_encoding: BOOLEAN
|
||||||
retried: BOOLEAN
|
retried: BOOLEAN
|
||||||
do
|
do
|
||||||
if not retried then
|
if not retried then
|
||||||
@@ -179,6 +180,8 @@ feature -- Access
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- handle sending data
|
-- handle sending data
|
||||||
|
l_is_chunked_transfer_encoding := attached headers.item ("Transfer-Encoding") as l_transfer_encoding and then l_transfer_encoding.same_string ("chunked")
|
||||||
|
|
||||||
if ctx /= Void then
|
if ctx /= Void then
|
||||||
if ctx.has_upload_filename then
|
if ctx.has_upload_filename then
|
||||||
l_upload_filename := ctx.upload_filename
|
l_upload_filename := ctx.upload_filename
|
||||||
@@ -195,14 +198,21 @@ feature -- Access
|
|||||||
headers.extend ("application/x-www-form-urlencoded", "Content-Type")
|
headers.extend ("application/x-www-form-urlencoded", "Content-Type")
|
||||||
l_upload_data := ctx.form_parameters_to_url_encoded_string
|
l_upload_data := ctx.form_parameters_to_url_encoded_string
|
||||||
headers.force (l_upload_data.count.out, "Content-Length")
|
headers.force (l_upload_data.count.out, "Content-Length")
|
||||||
|
if l_is_chunked_transfer_encoding then
|
||||||
else
|
-- Discard chunked transfer encoding
|
||||||
|
headers.remove ("Transfer-Encoding")
|
||||||
|
l_is_chunked_transfer_encoding := False
|
||||||
|
end
|
||||||
|
elseif l_form_data /= Void then
|
||||||
-- create form using multipart/form-data encoding
|
-- create form using multipart/form-data encoding
|
||||||
l_boundary := new_mime_boundary
|
l_boundary := new_mime_boundary (l_form_data)
|
||||||
headers.extend ("multipart/form-data; boundary=" + l_boundary, "Content-Type")
|
headers.extend ("multipart/form-data; boundary=" + l_boundary, "Content-Type")
|
||||||
if l_form_data /= Void then
|
l_upload_data := form_date_and_uploaded_files_to_mime_string (l_form_data, l_upload_filename, l_boundary)
|
||||||
l_upload_data := form_date_and_uploaded_files_to_mime_string (l_form_data, l_upload_filename, l_boundary)
|
headers.extend (l_upload_data.count.out, "Content-Length")
|
||||||
headers.extend (l_upload_data.count.out, "Content-Length")
|
if l_is_chunked_transfer_encoding then
|
||||||
|
-- Discard chunked transfer encoding
|
||||||
|
headers.remove ("Transfer-Encoding")
|
||||||
|
l_is_chunked_transfer_encoding := False
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
elseif l_upload_data /= Void then
|
elseif l_upload_data /= Void then
|
||||||
@@ -210,12 +220,16 @@ feature -- Access
|
|||||||
if not headers.has ("Content-Type") then
|
if not headers.has ("Content-Type") then
|
||||||
headers.extend ("application/x-www-form-urlencoded", "Content-Type")
|
headers.extend ("application/x-www-form-urlencoded", "Content-Type")
|
||||||
end
|
end
|
||||||
headers.extend (l_upload_data.count.out, "Content-Length")
|
if not l_is_chunked_transfer_encoding then
|
||||||
|
headers.extend (l_upload_data.count.out, "Content-Length")
|
||||||
|
end
|
||||||
elseif l_upload_filename /= Void then
|
elseif l_upload_filename /= Void then
|
||||||
check ctx.has_upload_filename end
|
check ctx.has_upload_filename end
|
||||||
create l_upload_file.make_with_name (l_upload_filename)
|
create l_upload_file.make_with_name (l_upload_filename)
|
||||||
if l_upload_file.exists and then l_upload_file.readable then
|
if l_upload_file.exists and then l_upload_file.readable then
|
||||||
headers.extend (l_upload_file.count.out, "Content-Length")
|
if not l_is_chunked_transfer_encoding then
|
||||||
|
headers.extend (l_upload_file.count.out, "Content-Length")
|
||||||
|
end
|
||||||
end
|
end
|
||||||
check l_upload_file /= Void end
|
check l_upload_file /= Void end
|
||||||
end
|
end
|
||||||
@@ -289,12 +303,7 @@ feature -- Access
|
|||||||
--| End of client header.
|
--| End of client header.
|
||||||
s.append (Http_end_of_header_line)
|
s.append (Http_end_of_header_line)
|
||||||
|
|
||||||
if l_upload_data /= Void then
|
--| Note that any remaining data or file to upload will be done directly via the socket
|
||||||
s.append (l_upload_data)
|
|
||||||
s.append (http_end_of_header_line)
|
|
||||||
end
|
|
||||||
|
|
||||||
--| Note that any remaining file to upload will be done directly via the socket
|
|
||||||
--| to optimize memory usage
|
--| to optimize memory usage
|
||||||
|
|
||||||
|
|
||||||
@@ -315,9 +324,20 @@ feature -- Access
|
|||||||
end
|
end
|
||||||
l_socket.put_string (s)
|
l_socket.put_string (s)
|
||||||
--| Send remaining payload data, if needed.
|
--| Send remaining payload data, if needed.
|
||||||
|
if l_upload_data /= Void then
|
||||||
|
if l_is_chunked_transfer_encoding then
|
||||||
|
put_string_using_chunked_transfer_encoding (l_upload_data, chunk_size, l_socket)
|
||||||
|
else
|
||||||
|
l_socket.put_string (l_upload_data)
|
||||||
|
end
|
||||||
|
end
|
||||||
if l_upload_file /= Void then
|
if l_upload_file /= Void then
|
||||||
-- i.e: not yet processed
|
if l_is_chunked_transfer_encoding then
|
||||||
append_file_content_to_socket (l_upload_file, l_upload_file.count, l_socket)
|
-- i.e: not yet processed
|
||||||
|
append_file_content_to_socket_using_chunked_transfer_encoding (l_upload_file, l_upload_file.count, chunk_size, l_socket)
|
||||||
|
else
|
||||||
|
append_file_content_to_socket (l_upload_file, l_upload_file.count, l_socket)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
--|-------------------------|--
|
--|-------------------------|--
|
||||||
@@ -498,8 +518,85 @@ feature {NONE} -- Helpers
|
|||||||
Result := utf.utf_32_string_to_utf_8_string_8 (s)
|
Result := utf.utf_32_string_to_utf_8_string_8 (s)
|
||||||
end
|
end
|
||||||
|
|
||||||
append_file_content_to_socket (a_file: FILE; a_len: INTEGER; a_output: NETWORK_STREAM_SOCKET)
|
put_string_using_chunked_transfer_encoding (a_string: READABLE_STRING_8; a_chunk_size: INTEGER; a_output: NETWORK_STREAM_SOCKET)
|
||||||
-- Append `a_file' content to `a_output'.
|
local
|
||||||
|
i,n: INTEGER
|
||||||
|
do
|
||||||
|
from
|
||||||
|
i := 1
|
||||||
|
n := a_string.count
|
||||||
|
until
|
||||||
|
i > n
|
||||||
|
loop
|
||||||
|
put_chunk (a_string.substring (i, i + a_chunk_size), Void, a_output)
|
||||||
|
i := i + a_chunk_size
|
||||||
|
end
|
||||||
|
put_chunk_end (Void, Void, a_output)
|
||||||
|
end
|
||||||
|
|
||||||
|
put_chunk (a_content: READABLE_STRING_8; a_ext: detachable READABLE_STRING_8; a_output: NETWORK_STREAM_SOCKET)
|
||||||
|
-- Write chunk non empty `a_content' to `a_output'
|
||||||
|
-- with optional extension `a_ext': chunk-extension= *( ";" chunk-ext-name [ "=" chunk-ext-val ] )
|
||||||
|
-- Note: that header "Transfer-Encoding: chunked" is required.
|
||||||
|
require
|
||||||
|
a_content_not_empty: a_content /= Void and then not a_content.is_empty
|
||||||
|
valid_chunk_extension: (a_ext /= Void and then not a_ext.is_empty) implies
|
||||||
|
( a_ext.starts_with (";") and not a_ext.has ('%N') and not not a_ext.has ('%R') )
|
||||||
|
local
|
||||||
|
l_chunk_size_line: STRING_8
|
||||||
|
i: INTEGER
|
||||||
|
do
|
||||||
|
--| Remove all left '0'
|
||||||
|
l_chunk_size_line := a_content.count.to_hex_string
|
||||||
|
from
|
||||||
|
i := 1
|
||||||
|
until
|
||||||
|
l_chunk_size_line[i] /= '0'
|
||||||
|
loop
|
||||||
|
i := i + 1
|
||||||
|
end
|
||||||
|
if i > 1 then
|
||||||
|
l_chunk_size_line := l_chunk_size_line.substring (i, l_chunk_size_line.count)
|
||||||
|
end
|
||||||
|
|
||||||
|
if a_ext /= Void then
|
||||||
|
l_chunk_size_line.append (a_ext)
|
||||||
|
end
|
||||||
|
l_chunk_size_line.append (crlf)
|
||||||
|
|
||||||
|
a_output.put_string (l_chunk_size_line)
|
||||||
|
a_output.put_string (a_content)
|
||||||
|
a_output.put_string (crlf)
|
||||||
|
end
|
||||||
|
|
||||||
|
put_chunk_end (a_ext: detachable READABLE_STRING_8; a_trailer: detachable READABLE_STRING_8; a_output: NETWORK_STREAM_SOCKET)
|
||||||
|
-- Put end of chunked content,
|
||||||
|
-- with optional extension `a_ext': chunk-extension= *( ";" chunk-ext-name [ "=" chunk-ext-val ] )
|
||||||
|
-- and with optional trailer `a_trailer' : trailer= *(entity-header CRLF)
|
||||||
|
local
|
||||||
|
l_chunk_size_line: STRING_8
|
||||||
|
do
|
||||||
|
-- Chunk end
|
||||||
|
create l_chunk_size_line.make (1)
|
||||||
|
l_chunk_size_line.append_integer (0)
|
||||||
|
|
||||||
|
if a_ext /= Void then
|
||||||
|
l_chunk_size_line.append (a_ext)
|
||||||
|
end
|
||||||
|
l_chunk_size_line.append (crlf)
|
||||||
|
a_output.put_string (l_chunk_size_line)
|
||||||
|
|
||||||
|
-- Optional trailer
|
||||||
|
if a_trailer /= Void and then not a_trailer.is_empty then
|
||||||
|
a_output.put_string (a_trailer)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Final CRLF
|
||||||
|
a_output.put_string (crlf)
|
||||||
|
end
|
||||||
|
|
||||||
|
append_file_content_to_socket_using_chunked_transfer_encoding (a_file: FILE; a_len: INTEGER; a_chunk_size: INTEGER; a_output: NETWORK_STREAM_SOCKET)
|
||||||
|
-- Append `a_file' content as chunks of `a_chunk_size' length to `a_output'.
|
||||||
-- If `a_len' >= 0 then read only `a_len' characters.
|
-- If `a_len' >= 0 then read only `a_len' characters.
|
||||||
require
|
require
|
||||||
a_file_readable: a_file.exists and then a_file.is_access_readable
|
a_file_readable: a_file.exists and then a_file.is_access_readable
|
||||||
@@ -523,7 +620,44 @@ feature {NONE} -- Helpers
|
|||||||
until
|
until
|
||||||
l_count = 0 or a_file.exhausted
|
l_count = 0 or a_file.exhausted
|
||||||
loop
|
loop
|
||||||
a_file.read_stream_thread_aware (l_count.min (2_048))
|
a_file.read_stream_thread_aware (l_count.min (a_chunk_size))
|
||||||
|
put_chunk (a_file.last_string, Void, a_output)
|
||||||
|
l_count := l_count - a_file.bytes_read
|
||||||
|
end
|
||||||
|
if not l_was_open then
|
||||||
|
a_file.close
|
||||||
|
end
|
||||||
|
put_chunk_end (Void, Void, a_output)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
append_file_content_to_socket (a_file: FILE; a_len: INTEGER; a_output: NETWORK_STREAM_SOCKET)
|
||||||
|
-- Append `a_file' content to `a_output'.
|
||||||
|
-- If `a_len' >= 0 then read only `a_len' characters.
|
||||||
|
require
|
||||||
|
a_file_readable: a_file.exists and then a_file.is_access_readable
|
||||||
|
local
|
||||||
|
l_was_open: BOOLEAN
|
||||||
|
l_count, l_buffer_size: INTEGER
|
||||||
|
do
|
||||||
|
if a_len >= 0 then
|
||||||
|
l_count := a_len
|
||||||
|
else
|
||||||
|
l_count := a_file.count
|
||||||
|
end
|
||||||
|
if l_count > 0 then
|
||||||
|
l_was_open := a_file.is_open_read
|
||||||
|
if a_file.is_open_read then
|
||||||
|
l_was_open := True
|
||||||
|
else
|
||||||
|
a_file.open_read
|
||||||
|
end
|
||||||
|
from
|
||||||
|
l_buffer_size := buffer_size
|
||||||
|
until
|
||||||
|
l_count = 0 or a_file.exhausted
|
||||||
|
loop
|
||||||
|
a_file.read_stream_thread_aware (l_count.min (l_buffer_size))
|
||||||
a_output.put_string (a_file.last_string)
|
a_output.put_string (a_file.last_string)
|
||||||
l_count := l_count - a_file.bytes_read
|
l_count := l_count - a_file.bytes_read
|
||||||
end
|
end
|
||||||
@@ -541,6 +675,7 @@ feature {NONE} -- Helpers
|
|||||||
local
|
local
|
||||||
l_was_open: BOOLEAN
|
l_was_open: BOOLEAN
|
||||||
l_count: INTEGER
|
l_count: INTEGER
|
||||||
|
l_buffer_size: INTEGER
|
||||||
do
|
do
|
||||||
if a_len >= 0 then
|
if a_len >= 0 then
|
||||||
l_count := a_len
|
l_count := a_len
|
||||||
@@ -555,11 +690,11 @@ feature {NONE} -- Helpers
|
|||||||
a_file.open_read
|
a_file.open_read
|
||||||
end
|
end
|
||||||
from
|
from
|
||||||
|
l_buffer_size := buffer_size
|
||||||
until
|
until
|
||||||
l_count = 0 or a_file.exhausted
|
l_count = 0 or a_file.exhausted
|
||||||
loop
|
loop
|
||||||
a_file.read_stream_thread_aware (l_count.min (2_048))
|
a_file.read_stream_thread_aware (l_count.min (l_buffer_size))
|
||||||
a_output.append (a_file.last_string)
|
a_output.append (a_file.last_string)
|
||||||
l_count := l_count - a_file.bytes_read
|
l_count := l_count - a_file.bytes_read
|
||||||
end
|
end
|
||||||
@@ -632,7 +767,7 @@ feature {NONE} -- Helpers
|
|||||||
-- FIXME: check solution!
|
-- FIXME: check solution!
|
||||||
from
|
from
|
||||||
l_count := 0
|
l_count := 0
|
||||||
l_chunk_size := 1_024
|
l_chunk_size := buffer_size
|
||||||
n := l_chunk_size --| value to satisfy until condition on first loop.
|
n := l_chunk_size --| value to satisfy until condition on first loop.
|
||||||
until
|
until
|
||||||
n < l_chunk_size or not a_socket.readable
|
n < l_chunk_size or not a_socket.readable
|
||||||
@@ -718,16 +853,42 @@ feature {NONE} -- Helpers
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
new_mime_boundary: STRING
|
new_mime_boundary (a_data: HASH_TABLE [READABLE_STRING_32, READABLE_STRING_32]): STRING
|
||||||
-- New MIME boundary.
|
-- New MIME boundary.
|
||||||
|
local
|
||||||
|
s: STRING
|
||||||
|
ran: RANDOM
|
||||||
|
n: INTEGER
|
||||||
|
i,j: INTEGER
|
||||||
do
|
do
|
||||||
-- FIXME: better boundary creation
|
across
|
||||||
Result := "----------------------------5eadfcf3bb3e"
|
a_data as ic
|
||||||
|
loop
|
||||||
|
i := i + ic.item.count + ic.key.count
|
||||||
|
end
|
||||||
|
create ran.set_seed (i) -- FIXME: use a real random seed.
|
||||||
|
ran.start
|
||||||
|
ran.forth
|
||||||
|
n := (20 * ran.real_item).truncated_to_integer
|
||||||
|
create Result.make_filled ('-', 3 + n)
|
||||||
|
s := "_1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||||
|
from
|
||||||
|
until
|
||||||
|
Result.count >= 40
|
||||||
|
loop
|
||||||
|
ran.forth
|
||||||
|
j := (ran.real_item * s.count).truncated_to_integer.max (1)
|
||||||
|
Result.append_character (s[j])
|
||||||
|
end
|
||||||
|
check Result.count = 40 and Result.starts_with ("---") end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
crlf: STRING = "%R%N"
|
||||||
|
-- CR and NL sequence.
|
||||||
|
|
||||||
invariant
|
invariant
|
||||||
note
|
note
|
||||||
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
copyright: "2011-2016, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||||
source: "[
|
source: "[
|
||||||
Eiffel Software
|
Eiffel Software
|
||||||
|
|||||||
@@ -64,4 +64,9 @@ feature -- Tests
|
|||||||
test_get_with_authentication
|
test_get_with_authentication
|
||||||
end
|
end
|
||||||
|
|
||||||
|
net_test_post_with_file_using_chunked_transfer_encoding
|
||||||
|
do
|
||||||
|
test_post_with_file_using_chunked_transfer_encoding
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -253,6 +253,38 @@ feature -- Factory
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test_post_with_file_using_chunked_transfer_encoding
|
||||||
|
local
|
||||||
|
sess: HTTP_CLIENT_SESSION
|
||||||
|
h: STRING_8
|
||||||
|
l_ctx: HTTP_CLIENT_REQUEST_CONTEXT
|
||||||
|
do
|
||||||
|
if attached global_requestbin_path as requestbin_path then
|
||||||
|
|
||||||
|
-- POST REQUEST WITH A FILE AND FORM DATA
|
||||||
|
-- check requestbin to ensure the file and form parameters are correctly received
|
||||||
|
-- set filename to a local file
|
||||||
|
sess := new_session ("http://requestb.in")
|
||||||
|
create l_ctx.make
|
||||||
|
l_ctx.add_header ("Transfer-Encoding", "chunked")
|
||||||
|
l_ctx.set_upload_filename ("logo.jpg")
|
||||||
|
create h.make_empty
|
||||||
|
if
|
||||||
|
attached sess.post (requestbin_path, l_ctx, Void) as res and then
|
||||||
|
attached res.headers as hds
|
||||||
|
then
|
||||||
|
across
|
||||||
|
hds as c
|
||||||
|
loop
|
||||||
|
h.append (c.item.name + ": " + c.item.value + "%R%N")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
print (h)
|
||||||
|
else
|
||||||
|
assert ("Has requestbin path", False)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
test_get_with_redirection
|
test_get_with_redirection
|
||||||
local
|
local
|
||||||
sess: HTTP_CLIENT_SESSION
|
sess: HTTP_CLIENT_SESSION
|
||||||
|
|||||||
Reference in New Issue
Block a user