Files
EWF/library/network/http_client/src/http_client_response.e
Jocelyn Fiat a547cbaeb1 Fixed HTTP_CLIENT_RESPONSE when dealing with redirection
before it was storing some header in the body.
   now we added redirections: ..  which is a list of redirection informations:
     - status line
     - header
     - and eventual redirection body (but at least by default, libcurl does not cache body)

Enhanced the http_client library to be able to write directly the downloaded data into a file (or as convenient thanks to agent).
2013-04-11 15:53:46 +02:00

321 lines
8.2 KiB
Plaintext

note
description : "[
Response retrieved by the client
]"
date : "$Date$"
revision : "$Revision$"
class
HTTP_CLIENT_RESPONSE
create
make
feature {NONE} -- Initialization
make (a_url: like url)
-- Initialize `Current'.
do
--| Default values
status := 200
url := a_url
create {STRING_8} raw_header.make_empty
end
feature -- Status
error_occurred: BOOLEAN
-- Error occurred during request
error_message: detachable READABLE_STRING_8
feature {HTTP_CLIENT_REQUEST} -- Status setting
set_error_occurred (b: BOOLEAN)
-- Set `error_occurred' to `b'
do
error_occurred := b
end
set_error_message (m: READABLE_STRING_8)
-- Set `error_message' to `m'
do
set_error_occurred (True)
error_message := m
end
feature -- Access
url: STRING_8
-- URL associated with Current response
status: INTEGER assign set_status
-- Status code of the response.
status_line: detachable READABLE_STRING_8
raw_header: READABLE_STRING_8
-- Raw http header of the response.
redirections: detachable ARRAYED_LIST [TUPLE [status_line: detachable READABLE_STRING_8; raw_header: READABLE_STRING_8; body: detachable READABLE_STRING_8]]
-- Header of previous redirection if any.
header (a_name: READABLE_STRING_8): detachable READABLE_STRING_8
-- Header entry value related to `a_name'
-- if multiple entries, just concatenate them using comma character
--| See: http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html
--| Multiple message-header fields with the same field-name MAY be present in a message
--| if and only if the entire field-value for that header field is defined as a comma-separated list [i.e., #(values)].
--| It MUST be possible to combine the multiple header fields into one "field-name: field-value" pair,
--| without changing the semantics of the message, by appending each subsequent field-value to the first, each separated by a comma.
--| The order in which header fields with the same field-name are received is therefore significant
--| to the interpretation of the combined field value, and thus a proxy MUST NOT change the order of
--| these field values when a message is forwarded.
local
s: detachable STRING_8
k,v: READABLE_STRING_8
do
across
headers as hds
loop
k := hds.item.name
if k.same_string (a_name) then
v := hds.item.value
if s = Void then
create s.make_from_string (v)
else
s.append_character (',')
s.append (v)
end
end
end
Result := s
end
headers: LIST [TUPLE [name: READABLE_STRING_8; value: READABLE_STRING_8]]
-- Computed table of http headers of the response.
--| We use a LIST since one might have multiple message-header fields with the same field-name
--| Then the user can handle those case using default or custom concatenation
--| (note: `header' is concatenating using comma)
local
tb: like internal_headers
pos, l_start, l_end, n, c: INTEGER
h: like raw_header
k: STRING_8
do
tb := internal_headers
if tb = Void then
create tb.make (3)
h := raw_header
from
pos := 1
n := h.count
until
pos = 0 or pos > n
loop
l_start := pos
--| Left justify
from until not h[l_start].is_space loop
l_start := l_start + 1
end
pos := h.index_of ('%N', l_start)
if pos > 0 then
l_end := pos - 1
elseif l_start < n then
l_end := n + 1
else
-- Empty line
l_end := 0
end
if l_end > 0 then
--| Right justify
from until not h[l_end].is_space loop
l_end := l_end - 1
end
c := h.index_of (':', l_start)
if c > 0 then
k := h.substring (l_start, c - 1)
k.right_adjust
c := c + 1
from until c <= n and not h[c].is_space loop
c := c + 1
end
tb.force ([k, h.substring (c, l_end)])
else
check header_has_colon: c > 0 end
end
end
pos := pos + 1
end
internal_headers := tb
end
Result := tb
end
body: detachable READABLE_STRING_8 assign set_body
-- Content of the response
response_message_source (a_include_redirection: BOOLEAN): STRING_8
-- Full message source including redirection if any
do
create Result.make (1_024)
if
a_include_redirection and then
attached redirections as lst
then
across
lst as c
loop
if attached c.item.status_line as s then
Result.append (s)
Result.append ("%R%N")
end
Result.append (c.item.raw_header)
Result.append ("%R%N")
if attached c.item.body as l_body then
Result.append (l_body)
end
end
end
if attached status_line as s then
Result.append (s)
Result.append ("%R%N")
end
Result.append (raw_header)
Result.append ("%R%N")
if attached body as l_body then
Result.append (l_body)
end
end
feature -- Change
set_status (s: INTEGER)
-- Set response `status' code to `s'
do
status := s
end
set_response_message (a_source: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT)
-- Parse `a_source' response message
-- and set `header' and `body'.
--| ctx is the context associated with the request
--| it might be useful to deal with redirection customization...
local
i, j, pos: INTEGER
l_has_location: BOOLEAN
l_content_length: INTEGER
s: READABLE_STRING_8
l_status_line,h: detachable STRING_8
do
from
i := 1
j := 1
pos := 1
i := a_source.substring_index ("%R%N", i)
until
i = 0 or i > a_source.count
loop
s := a_source.substring (j, i - 1)
if s.starts_with ("HTTP/") then
--| Skip first line which is the status line
--| ex: HTTP/1.1 200 OK%R%N
j := i + 2
l_status_line := s
pos := j
elseif s.is_empty then
-- End of header %R%N%R%N
if attached raw_header as l_raw_header and then not l_raw_header.is_empty then
add_redirection (status_line, l_raw_header, body)
end
h := a_source.substring (pos, i - 1)
j := i + 2
pos := j
status_line := l_status_line
set_raw_header (h)
-- libcURL does not cache redirection content.
-- FIXME: check if this is customizable
-- if l_has_location then
-- if l_content_length > 0 then
-- j := pos + l_content_length - 1
-- l_body := a_source.substring (pos, j)
-- pos := j
-- else
-- l_body := Void
-- end
-- set_body (l_body)
-- end
if not l_has_location then
i := 0 -- exit loop
end
l_content_length := 0
l_status_line := Void
l_has_location := False
else
if s.starts_with ("Location:") then
l_has_location := True
elseif s.starts_with ("Content-Length:") then
s := s.substring (16, s.count)
if s.is_integer then
l_content_length := s.to_integer
end
end
j := i + 2
end
if i > 0 then
i := a_source.substring_index ("%R%N", j)
end
end
set_body (a_source.substring (pos, a_source.count))
ensure
parsed: response_message_source (True).count = a_source.count
end
set_raw_header (h: READABLE_STRING_8)
-- Set http header `raw_header' to `h'
do
raw_header := h
--| Reset internal headers
internal_headers := Void
end
add_redirection (s: detachable READABLE_STRING_8; h: READABLE_STRING_8; a_body: detachable READABLE_STRING_8)
-- Add redirection with status line `s' and raw header `h' and body `a_body' if any
local
lst: like redirections
do
lst := redirections
if lst = Void then
create lst.make (1)
redirections := lst
end
lst.force ([s,h, a_body])
end
set_body (s: like body)
-- Set `body' message to `s'
do
body := s
end
feature {NONE} -- Implementation
internal_headers: detachable ARRAYED_LIST [TUPLE [key: READABLE_STRING_8; value: READABLE_STRING_8]]
-- Internal cached value for the headers
;note
copyright: "2011-2012, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[
Eiffel Software
5949 Hollister Ave., Goleta, CA 93117 USA
Telephone 805-685-1006, Fax 805-685-6869
Website http://www.eiffel.com
Customer support http://support.eiffel.com
]"
end