Merge branch 'master' into void-safe

This commit is contained in:
2014-05-14 11:32:28 +02:00
19 changed files with 944 additions and 619 deletions

View File

@@ -1,8 +1,9 @@
note
description: "[
The class provides an easy way to build HTTP header.
The class represents a HTTP header, and it provides simple routine
to build it.
You will also find some helper feature to help coding most common usage
You will also find some helper features to help coding most common usages
Please, have a look at constants classes such as
HTTP_MIME_TYPES
@@ -24,6 +25,8 @@ class
inherit
ITERABLE [READABLE_STRING_8]
HTTP_HEADER_MODIFIER
create
make,
make_with_count,
@@ -116,6 +119,8 @@ feature -- Access
result_has_single_ending_cr_lf: Result.count >= 4 implies not Result.substring (Result.count - 3, Result.count).same_string ("%R%N%R%N")
end
feature -- Conversion
to_name_value_iterable: ITERABLE [TUPLE [name: READABLE_STRING_8; value: READABLE_STRING_8]]
-- Iterable representation of the header entries.
local
@@ -132,7 +137,7 @@ feature -- Access
Result := res
end
feature -- Conversion
feature --
append_string_to (a_result: STRING_8)
-- Append current as string representation to `a_result'
@@ -250,60 +255,6 @@ feature -- Header: merging
end
end
feature -- Status report
has, has_header_named (a_name: READABLE_STRING_8): BOOLEAN
-- Has header item for `n'?
do
Result := across headers as c some has_same_header_name (c.item, a_name) end
end
has_content_length: BOOLEAN
-- Has header "Content-Length"
do
Result := has_header_named ({HTTP_HEADER_NAMES}.header_content_length)
end
has_content_type: BOOLEAN
-- Has header "Content-Type"
do
Result := has_header_named ({HTTP_HEADER_NAMES}.header_content_type)
end
has_transfer_encoding_chunked: BOOLEAN
-- Has "Transfer-Encoding: chunked" header
do
if has_header_named ({HTTP_HEADER_NAMES}.header_transfer_encoding) then
Result := attached header_named_value ({HTTP_HEADER_NAMES}.header_transfer_encoding) as v and then v.same_string (str_chunked)
end
end
feature -- Access
header_named_value (a_name: READABLE_STRING_8): detachable STRING_8
-- First header item found for `a_name' if any
require
has_header: has_header_named (a_name)
local
n: INTEGER
l_line: READABLE_STRING_8
do
n := a_name.count
across
headers as ic
until
Result /= Void
loop
l_line := ic.item
if has_same_header_name (l_line, a_name) then
Result := l_line.substring (n + 2, l_line.count)
Result.left_adjust
Result.right_adjust
end
end
end
feature -- Removal
remove_header_named (a_name: READABLE_STRING_8)
@@ -334,375 +285,16 @@ feature -- Header change: general
-- Add header `h'
-- if it already exists, there will be multiple header with same name
-- which can also be valid
require
h_not_empty: not h.is_empty
do
headers.force (h)
end
put_header (h: READABLE_STRING_8)
-- Add header `h' or replace existing header of same header name
require
h_not_empty: not h.is_empty
do
force_header_by_name (header_name_colon (h), h)
end
add_header_key_value (k,v: READABLE_STRING_8)
-- Add header `k:v'.
-- If it already exists, there will be multiple header with same name
-- which can also be valid
local
s: STRING_8
do
create s.make (k.count + 2 + v.count)
s.append (k)
s.append (colon_space)
s.append (v)
add_header (s)
ensure
added: has_header_named (k)
end
put_header_key_value (k,v: READABLE_STRING_8)
-- Add header `k:v', or replace existing header of same header name/key
local
s: STRING_8
do
create s.make (k.count + 2 + v.count)
s.append (k)
s.append (colon_space)
s.append (v)
put_header (s)
ensure
added: has_header_named (k)
end
put_header_key_values (k: READABLE_STRING_8; a_values: ITERABLE [READABLE_STRING_8]; a_separator: detachable READABLE_STRING_8)
-- Add header `k: a_values', or replace existing header of same header values/key.
-- Use `comma_space' as default separator if `a_separator' is Void or empty.
local
s: STRING_8
l_separator: READABLE_STRING_8
do
if a_separator /= Void and then not a_separator.is_empty then
l_separator := a_separator
else
l_separator := comma_space
end
create s.make_empty
across
a_values as c
loop
if not s.is_empty then
s.append_string (l_separator)
end
s.append (c.item)
end
if not s.is_empty then
put_header_key_value (k, s)
end
ensure
added: has_header_named (k)
end
feature -- Content related header
put_content_type (t: READABLE_STRING_8)
do
put_header_key_value ({HTTP_HEADER_NAMES}.header_content_type, t)
end
add_content_type (t: READABLE_STRING_8)
-- same as `put_content_type', but allow multiple definition of "Content-Type"
do
add_header_key_value ({HTTP_HEADER_NAMES}.header_content_type, t)
end
put_content_type_with_parameters (t: READABLE_STRING_8; a_params: detachable ARRAY [TUPLE [name: READABLE_STRING_8; value: READABLE_STRING_8]])
local
s: STRING_8
do
if a_params /= Void and then not a_params.is_empty then
create s.make_from_string (t)
across
a_params as p
loop
if attached p.item as nv then
s.append_character (';')
s.append_character (' ')
s.append (nv.name)
s.append_character ('=')
s.append_character ('%"')
s.append (nv.value)
s.append_character ('%"')
end
end
put_header_key_value ({HTTP_HEADER_NAMES}.header_content_type, s)
else
put_content_type (t)
end
end
add_content_type_with_parameters (t: READABLE_STRING_8; a_params: detachable ARRAY [TUPLE [name: READABLE_STRING_8; value: READABLE_STRING_8]])
local
s: STRING_8
do
if a_params /= Void and then not a_params.is_empty then
create s.make_from_string (t)
across
a_params as p
loop
if attached p.item as nv then
s.append_character (';')
s.append_character (' ')
s.append (nv.name)
s.append_character ('=')
s.append_character ('%"')
s.append (nv.value)
s.append_character ('%"')
end
end
add_header_key_value ({HTTP_HEADER_NAMES}.header_content_type, s)
else
add_content_type (t)
end
end
put_content_type_with_charset (t: READABLE_STRING_8; c: READABLE_STRING_8)
do
put_content_type_with_parameters (t, <<["charset", c]>>)
end
add_content_type_with_charset (t: READABLE_STRING_8; c: READABLE_STRING_8)
-- same as `put_content_type_with_charset', but allow multiple definition of "Content-Type"
do
add_content_type_with_parameters (t, <<["charset", c]>>)
end
put_content_type_with_name (t: READABLE_STRING_8; n: READABLE_STRING_8)
do
put_content_type_with_parameters (t, <<["name", n]>>)
end
add_content_type_with_name (t: READABLE_STRING_8; n: READABLE_STRING_8)
-- same as `put_content_type_with_name', but allow multiple definition of "Content-Type"
do
add_content_type_with_parameters (t, <<["name", n]>>)
end
put_content_length (n: INTEGER)
do
put_header_key_value ({HTTP_HEADER_NAMES}.header_content_length, n.out)
end
put_content_transfer_encoding (a_mechanism: READABLE_STRING_8)
-- Put "Content-Transfer-Encoding" header with for instance "binary"
--| encoding := "Content-Transfer-Encoding" ":" mechanism
--|
--| mechanism := "7bit" ; case-insensitive
--| / "quoted-printable"
--| / "base64"
--| / "8bit"
--| / "binary"
--| / x-token
do
put_header_key_value ({HTTP_HEADER_NAMES}.header_content_transfer_encoding, a_mechanism)
end
put_content_language (a_lang: READABLE_STRING_8)
-- Put "Content-Language" header of value `a_lang'.
do
put_header_key_value ({HTTP_HEADER_NAMES}.header_content_language, a_lang)
end
put_content_encoding (a_enc: READABLE_STRING_8)
-- Put "Content-Encoding" header of value `a_enc'.
do
put_header_key_value ({HTTP_HEADER_NAMES}.header_content_encoding, a_enc)
end
put_transfer_encoding (a_enc: READABLE_STRING_8)
-- Put "Transfer-Encoding" header with for instance "chunked"
do
put_header_key_value ({HTTP_HEADER_NAMES}.header_transfer_encoding, a_enc)
end
put_transfer_encoding_binary
-- Put "Transfer-Encoding: binary" header
do
put_transfer_encoding (str_binary)
end
put_transfer_encoding_chunked
-- Put "Transfer-Encoding: chunked" header
do
put_transfer_encoding (str_chunked)
end
put_content_disposition (a_type: READABLE_STRING_8; a_params: detachable READABLE_STRING_8)
-- Put "Content-Disposition" header
--| See RFC2183
--| disposition := "Content-Disposition" ":"
--| disposition-type
--| *(";" disposition-parm)
--| disposition-type := "inline"
--| / "attachment"
--| / extension-token
--| ; values are not case-sensitive
--| disposition-parm := filename-parm
--| / creation-date-parm
--| / modification-date-parm
--| / read-date-parm
--| / size-parm
--| / parameter
--| filename-parm := "filename" "=" value
--| creation-date-parm := "creation-date" "=" quoted-date-time
--| modification-date-parm := "modification-date" "=" quoted-date-time
--| read-date-parm := "read-date" "=" quoted-date-time
--| size-parm := "size" "=" 1*DIGIT
--| quoted-date-time := quoted-string
--| ; contents MUST be an RFC 822 `date-time'
--| ; numeric timezones (+HHMM or -HHMM) MUST be used
do
if a_params /= Void then
put_header_key_value ({HTTP_HEADER_NAMES}.header_content_disposition, a_type + semi_colon_space + a_params)
else
put_header_key_value ({HTTP_HEADER_NAMES}.header_content_disposition, a_type)
end
end
feature -- Content-type helpers
put_content_type_text_css do put_content_type ({HTTP_MIME_TYPES}.text_css) end
put_content_type_text_csv do put_content_type ({HTTP_MIME_TYPES}.text_csv) end
put_content_type_text_html do put_content_type ({HTTP_MIME_TYPES}.text_html) end
put_content_type_text_javascript do put_content_type ({HTTP_MIME_TYPES}.text_javascript) end
put_content_type_text_json do put_content_type ({HTTP_MIME_TYPES}.text_json) end
put_content_type_text_plain do put_content_type ({HTTP_MIME_TYPES}.text_plain) end
put_content_type_text_xml do put_content_type ({HTTP_MIME_TYPES}.text_xml) end
put_content_type_application_json do put_content_type ({HTTP_MIME_TYPES}.application_json) end
put_content_type_application_javascript do put_content_type ({HTTP_MIME_TYPES}.application_javascript) end
put_content_type_application_zip do put_content_type ({HTTP_MIME_TYPES}.application_zip) end
put_content_type_application_pdf do put_content_type ({HTTP_MIME_TYPES}.application_pdf) end
put_content_type_image_gif do put_content_type ({HTTP_MIME_TYPES}.image_gif) end
put_content_type_image_png do put_content_type ({HTTP_MIME_TYPES}.image_png) end
put_content_type_image_jpg do put_content_type ({HTTP_MIME_TYPES}.image_jpg) end
put_content_type_image_svg_xml do put_content_type ({HTTP_MIME_TYPES}.image_svg_xml) end
put_content_type_message_http do put_content_type ({HTTP_MIME_TYPES}.message_http) end
put_content_type_multipart_mixed do put_content_type ({HTTP_MIME_TYPES}.multipart_mixed) end
put_content_type_multipart_alternative do put_content_type ({HTTP_MIME_TYPES}.multipart_alternative) end
put_content_type_multipart_related do put_content_type ({HTTP_MIME_TYPES}.multipart_related) end
put_content_type_multipart_form_data do put_content_type ({HTTP_MIME_TYPES}.multipart_form_data) end
put_content_type_multipart_signed do put_content_type ({HTTP_MIME_TYPES}.multipart_signed) end
put_content_type_multipart_encrypted do put_content_type ({HTTP_MIME_TYPES}.multipart_encrypted) end
put_content_type_application_x_www_form_encoded do put_content_type ({HTTP_MIME_TYPES}.application_x_www_form_encoded) end
feature -- Cross-Origin Resource Sharing
put_access_control_allow_origin (s: READABLE_STRING_8)
-- Put "Access-Control-Allow-Origin" header.
do
put_header_key_value ({HTTP_HEADER_NAMES}.header_access_control_allow_origin, s)
end
put_access_control_allow_all_origin
-- Put "Access-Control-Allow-Origin: *" header.
do
put_access_control_allow_origin ("*")
end
put_access_control_allow_methods (a_methods: ITERABLE [READABLE_STRING_8])
-- If `a_methods' is not empty, put `Access-Control-Allow-Methods' header with list `a_methods' of methods
do
put_header_key_values ({HTTP_HEADER_NAMES}.header_access_control_allow_methods, a_methods, Void)
end
put_access_control_allow_headers (s: READABLE_STRING_8)
-- Put "Access-Control-Allow-Headers" header.
do
put_header_key_value ({HTTP_HEADER_NAMES}.header_access_control_allow_headers, s)
end
feature -- Method related
put_allow (a_methods: ITERABLE [READABLE_STRING_8])
-- If `a_methods' is not empty, put `Allow' header with list `a_methods' of methods
do
put_header_key_values ({HTTP_HEADER_NAMES}.header_allow, a_methods, Void)
end
feature -- Date
put_date (s: READABLE_STRING_8)
-- Put "Date: " header
do
put_header_key_value ({HTTP_HEADER_NAMES}.header_date, s)
end
put_current_date
-- Put current date time with "Date" header
do
put_utc_date (create {DATE_TIME}.make_now_utc)
end
put_utc_date (a_utc_date: DATE_TIME)
-- Put UTC date time `dt' with "Date" header
do
put_date (date_to_rfc1123_http_date_format (a_utc_date))
end
put_last_modified (a_utc_date: DATE_TIME)
-- Put UTC date time `dt' with "Date" header
do
put_header_key_value ({HTTP_HEADER_NAMES}.header_last_modified, date_to_rfc1123_http_date_format (a_utc_date))
end
feature -- Authorization
put_authorization (s: READABLE_STRING_8)
-- Put authorization `s' with "Authorization" header
do
put_header_key_value ({HTTP_HEADER_NAMES}.header_authorization, s)
end
feature -- Others
put_expires (sec: INTEGER)
do
put_expires_string (sec.out)
end
put_expires_string (s: STRING)
do
put_header_key_value ("Expires", s)
end
put_expires_date (a_utc_date: DATE_TIME)
do
put_header_key_value ("Expires", date_to_rfc1123_http_date_format (a_utc_date))
end
put_cache_control (s: READABLE_STRING_8)
-- `s' could be for instance "no-cache, must-revalidate"
do
put_header_key_value ("Cache-Control", s)
end
put_pragma (s: READABLE_STRING_8)
do
put_header_key_value ("Pragma", s)
end
put_pragma_no_cache
do
put_pragma ("no-cache")
end
feature -- Redirection
remove_location
@@ -711,79 +303,7 @@ feature -- Redirection
remove_header_named ({HTTP_HEADER_NAMES}.header_location)
end
put_location (a_location: READABLE_STRING_8)
-- Tell the client the new location `a_location'
require
a_location_valid: not a_location.is_empty
do
put_header_key_value ({HTTP_HEADER_NAMES}.header_location, a_location)
end
put_refresh (a_location: READABLE_STRING_8; a_timeout_in_seconds: INTEGER)
-- Tell the client to refresh page with `a_location' after `a_timeout_in_seconds' in seconds
require
a_location_valid: not a_location.is_empty
do
put_header_key_value ({HTTP_HEADER_NAMES}.header_refresh, a_timeout_in_seconds.out + "; url=" + a_location)
end
feature -- Cookie
put_cookie (key, value: READABLE_STRING_8; expiration, path, domain: detachable READABLE_STRING_8; secure, http_only: BOOLEAN)
-- Set a cookie on the client's machine
-- with key 'key' and value 'value'.
-- Note: you should avoid using "localhost" as `domain' for local cookies
-- since they are not always handled by browser (for instance Chrome)
require
make_sense: (key /= Void and value /= Void) and then (not key.is_empty and not value.is_empty)
domain_without_port_info: domain /= Void implies domain.index_of (':', 1) = 0
local
s: STRING
do
s := {HTTP_HEADER_NAMES}.header_set_cookie + colon_space + key + "=" + value
if
domain /= Void and then not domain.same_string ("localhost")
then
s.append ("; Domain=")
s.append (domain)
end
if path /= Void then
s.append ("; Path=")
s.append (path)
end
if expiration /= Void then
s.append ("; Expires=")
s.append (expiration)
end
if secure then
s.append ("; Secure")
end
if http_only then
s.append ("; HttpOnly")
end
add_header (s)
end
put_cookie_with_expiration_date (key, value: READABLE_STRING_8; expiration: DATE_TIME; path, domain: detachable READABLE_STRING_8; secure, http_only: BOOLEAN)
-- Set a cookie on the client's machine
-- with key 'key' and value 'value'.
require
make_sense: (key /= Void and value /= Void) and then (not key.is_empty and not value.is_empty)
do
put_cookie (key, value, date_to_rfc1123_http_date_format (expiration), path, domain, secure, http_only)
end
feature {NONE} -- Implementation: Header
has_same_header_name (h: READABLE_STRING_8; a_name: READABLE_STRING_8): BOOLEAN
-- Header line `h' has same name as `a_name' ?
do
if h.starts_with (a_name) then
if h.valid_index (a_name.count + 1) then
Result := h[a_name.count + 1] = ':'
end
end
end
feature {NONE} -- Implementation: Header change
force_header_by_name (n: detachable READABLE_STRING_8; h: READABLE_STRING_8)
-- Add header `h' or replace existing header of same header name `n'
@@ -811,6 +331,27 @@ feature {NONE} -- Implementation: Header
end
end
feature {NONE} -- Implementation: Header conversion
append_line_to (a_line: READABLE_STRING_8; h: STRING_8)
-- Append header line `a_line' to string `h'.
--| this is used to build the header text
require
not_ending_with_new_line: not a_line.ends_with_general ("%N")
do
h.append_string (a_line)
append_end_of_line_to (h)
end
append_end_of_line_to (h: STRING_8)
-- Append the CRLN end of header line to string `h'.
do
h.append_character ('%R')
h.append_character ('%N')
end
feature {NONE} -- Implementation: Header queries
header_name_colon (h: READABLE_STRING_8): detachable STRING_8
-- If any, header's name with colon
--| ex: for "Foo-bar: something", this will return "Foo-bar:"
@@ -870,51 +411,6 @@ feature {NONE} -- Implementation: Header
end
end
feature {NONE} -- Implementation
append_line_to (s: READABLE_STRING_8; h: STRING_8)
do
h.append_string (s)
append_end_of_line_to (h)
end
append_end_of_line_to (h: STRING_8)
do
h.append_character ('%R')
h.append_character ('%N')
end
feature -- Access
date_to_rfc1123_http_date_format (dt: DATE_TIME): STRING_8
-- String representation of `dt' using the RFC 1123
local
d: HTTP_DATE
do
create d.make_from_date_time (dt)
Result := d.string
end
feature {NONE} -- Constants
str_binary: STRING = "binary"
str_chunked: STRING = "chunked"
colon_space: IMMUTABLE_STRING_8
once
create Result.make_from_string (": ")
end
semi_colon_space: IMMUTABLE_STRING_8
once
create Result.make_from_string ("; ")
end
comma_space: IMMUTABLE_STRING_8
once
create Result.make_from_string (", ")
end
note
copyright: "2011-2014, Jocelyn Fiat, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"

View File

@@ -0,0 +1,667 @@
note
description: "[
The class provides an easy way to build and modify HTTP header text
thanks to add_header (..) and put_header (..)
You will also find some helper features to help coding most common usages
Please, have a look at constants classes such as
HTTP_MIME_TYPES
HTTP_HEADER_NAMES
HTTP_STATUS_CODE
HTTP_REQUEST_METHODS
(or HTTP_CONSTANTS which groups them for convenience)
Note the return status code is not part of the HTTP header
]"
legal: "See notice at end of class."
status: "See notice at end of class."
date: "$Date$"
revision: "$Revision$"
deferred class
HTTP_HEADER_MODIFIER
inherit
ITERABLE [READABLE_STRING_8]
feature -- Access: deferred
new_cursor: INDEXABLE_ITERATION_CURSOR [READABLE_STRING_8]
-- Fresh cursor associated with current structure.
deferred
end
feature -- Header change: deferred
add_header (h: READABLE_STRING_8)
-- Add header `h'
-- if it already exists, there will be multiple header with same name
-- which can also be valid
require
h_not_empty: h /= Void and then not h.is_empty
deferred
end
put_header (h: READABLE_STRING_8)
-- Add header `h' or replace existing header of same header name
require
h_not_empty: h /= Void and then not h.is_empty
deferred
end
feature -- Status report
has, has_header_named (a_name: READABLE_STRING_8): BOOLEAN
-- Has header item for `n'?
local
ic: like new_cursor
do
from
ic := new_cursor
until
ic.after or Result
loop
Result := has_same_header_name (ic.item, a_name)
ic.forth
end
end
has_content_length: BOOLEAN
-- Has header "Content-Length"
do
Result := has_header_named ({HTTP_HEADER_NAMES}.header_content_length)
end
has_content_type: BOOLEAN
-- Has header "Content-Type"
do
Result := has_header_named ({HTTP_HEADER_NAMES}.header_content_type)
end
has_transfer_encoding_chunked: BOOLEAN
-- Has "Transfer-Encoding: chunked" header
do
if has_header_named ({HTTP_HEADER_NAMES}.header_transfer_encoding) then
Result := attached item ({HTTP_HEADER_NAMES}.header_transfer_encoding) as v and then v.same_string (str_chunked)
end
end
feature -- Access
item alias "[]" (a_header_name: READABLE_STRING_8): detachable READABLE_STRING_8 assign force
-- First header item found for `a_name' if any
local
res: STRING_8
n: INTEGER
l_line: READABLE_STRING_8
ic: like new_cursor
do
n := a_header_name.count
from
ic := new_cursor
until
ic.after or Result /= Void
loop
l_line := ic.item
if has_same_header_name (l_line, a_header_name) then
res := l_line.substring (n + 2, l_line.count)
res.left_adjust
res.right_adjust
Result := res
end
ic.forth
end
end
header_named_value (a_name: READABLE_STRING_8): like item
-- First header item found for `a_name' if any
obsolete
"Use `item' [2014-03]"
do
Result := item (a_name)
end
feature -- Header change: general
force (a_value: detachable READABLE_STRING_8; a_header_name: READABLE_STRING_8)
-- Put header `a_header_name:a_value' or replace existing header of name `a_header_name'.
--| this is used as assigner for `item'
do
if a_value = Void then
put_header_key_value (a_header_name, "")
else
put_header_key_value (a_header_name, a_value)
end
end
add_header_key_value (a_header_name, a_value: READABLE_STRING_8)
-- Add header `a_header_name:a_value'.
-- If it already exists, there will be multiple header with same name
-- which can also be valid
local
s: STRING_8
do
create s.make (a_header_name.count + 2 + a_value.count)
s.append (a_header_name)
s.append (colon_space)
s.append (a_value)
add_header (s)
ensure
added: has_header_named (a_header_name)
end
put_header_key_value (a_header_name, a_value: READABLE_STRING_8)
-- Add header `a_header_name:a_value', or replace existing header of same header name/key
local
s: STRING_8
do
create s.make (a_header_name.count + 2 + a_value.count)
s.append (a_header_name)
s.append (colon_space)
s.append (a_value)
put_header (s)
ensure
added: has_header_named (a_header_name)
end
put_header_key_values (a_header_name: READABLE_STRING_8; a_values: ITERABLE [READABLE_STRING_8]; a_separator: detachable READABLE_STRING_8)
-- Add header `a_header_name: a_values', or replace existing header of same header values/key.
-- Use `comma_space' as default separator if `a_separator' is Void or empty.
local
s: STRING_8
l_separator: READABLE_STRING_8
do
if a_separator /= Void and then not a_separator.is_empty then
l_separator := a_separator
else
l_separator := comma_space
end
create s.make_empty
across
a_values as c
loop
if not s.is_empty then
s.append_string (l_separator)
end
s.append (c.item)
end
if not s.is_empty then
put_header_key_value (a_header_name, s)
end
ensure
added: has_header_named (a_header_name)
end
feature -- Content related header
put_content_type (a_content_type: READABLE_STRING_8)
-- Put header line "Content-Type:" + type `a_content_type'
do
put_header_key_value ({HTTP_HEADER_NAMES}.header_content_type, a_content_type)
end
add_content_type (a_content_type: READABLE_STRING_8)
-- same as `put_content_type', but allow multiple definition of "Content-Type"
do
add_header_key_value ({HTTP_HEADER_NAMES}.header_content_type, a_content_type)
end
put_content_type_with_parameters (a_content_type: READABLE_STRING_8; a_params: detachable ARRAY [TUPLE [name: READABLE_STRING_8; value: READABLE_STRING_8]])
-- Put header line "Content-Type:" + type `a_content_type' and extra paramaters `a_params'
--| note: see `put_content_type_with_charset' for examples.
local
s: STRING_8
do
if a_params /= Void and then not a_params.is_empty then
create s.make_from_string (a_content_type)
across
a_params as p
loop
if attached p.item as nv then
s.append_character (';')
s.append_character (' ')
s.append (nv.name)
s.append_character ('=')
s.append_character ('%"')
s.append (nv.value)
s.append_character ('%"')
end
end
put_header_key_value ({HTTP_HEADER_NAMES}.header_content_type, s)
else
put_content_type (a_content_type)
end
end
add_content_type_with_parameters (a_content_type: READABLE_STRING_8; a_params: detachable ARRAY [TUPLE [name: READABLE_STRING_8; value: READABLE_STRING_8]])
-- Add header line "Content-Type:" + type `a_content_type' and extra paramaters `a_params'.
--| note: see `put_content_type_with_charset' for examples.
local
s: STRING_8
do
if a_params /= Void and then not a_params.is_empty then
create s.make_from_string (a_content_type)
across
a_params as p
loop
if attached p.item as nv then
s.append_character (';')
s.append_character (' ')
s.append (nv.name)
s.append_character ('=')
s.append_character ('%"')
s.append (nv.value)
s.append_character ('%"')
end
end
add_header_key_value ({HTTP_HEADER_NAMES}.header_content_type, s)
else
add_content_type (a_content_type)
end
end
put_content_type_with_charset (a_content_type: READABLE_STRING_8; a_charset: READABLE_STRING_8)
-- Put content type `a_content_type' with `a_charset' as "charset" parameter.
do
put_content_type_with_parameters (a_content_type, <<["charset", a_charset]>>)
end
add_content_type_with_charset (a_content_type: READABLE_STRING_8; a_charset: READABLE_STRING_8)
-- Same as `put_content_type_with_charset', but allow multiple definition of "Content-Type".
do
add_content_type_with_parameters (a_content_type, <<["charset", a_charset]>>)
end
put_content_type_with_name (a_content_type: READABLE_STRING_8; a_name: READABLE_STRING_8)
-- Put content type `a_content_type' with `a_name' as "name" parameter.
do
put_content_type_with_parameters (a_content_type, <<["name", a_name]>>)
end
add_content_type_with_name (a_content_type: READABLE_STRING_8; a_name: READABLE_STRING_8)
-- same as `put_content_type_with_name', but allow multiple definition of "Content-Type"
do
add_content_type_with_parameters (a_content_type, <<["name", a_name]>>)
end
put_content_length (a_length: INTEGER)
-- Put "Content-Length:" + length `a_length'.
do
put_header_key_value ({HTTP_HEADER_NAMES}.header_content_length, a_length.out)
end
put_content_transfer_encoding (a_mechanism: READABLE_STRING_8)
-- Put "Content-Transfer-Encoding" header with `a_mechanism'
--| encoding := "Content-Transfer-Encoding" ":" mechanism
--|
--| mechanism := "7bit" ; case-insensitive
--| / "quoted-printable"
--| / "base64"
--| / "8bit"
--| / "binary"
--| / x-token
do
put_header_key_value ({HTTP_HEADER_NAMES}.header_content_transfer_encoding, a_mechanism)
end
put_content_language (a_lang: READABLE_STRING_8)
-- Put "Content-Language" header of value `a_lang'.
do
put_header_key_value ({HTTP_HEADER_NAMES}.header_content_language, a_lang)
end
put_content_encoding (a_encoding: READABLE_STRING_8)
-- Put "Content-Encoding" header of value `a_encoding'.
do
put_header_key_value ({HTTP_HEADER_NAMES}.header_content_encoding, a_encoding)
end
put_transfer_encoding (a_encoding: READABLE_STRING_8)
-- Put "Transfer-Encoding" header with `a_encoding' value.
--| for instance "chunked"
do
put_header_key_value ({HTTP_HEADER_NAMES}.header_transfer_encoding, a_encoding)
end
put_transfer_encoding_binary
-- Put "Transfer-Encoding: binary" header
do
put_transfer_encoding (str_binary)
end
put_transfer_encoding_chunked
-- Put "Transfer-Encoding: chunked" header
do
put_transfer_encoding (str_chunked)
end
put_content_disposition (a_type: READABLE_STRING_8; a_params: detachable READABLE_STRING_8)
-- Put "Content-Disposition" header
--| See RFC2183
--| disposition := "Content-Disposition" ":"
--| disposition-type
--| *(";" disposition-parm)
--| disposition-type := "inline"
--| / "attachment"
--| / extension-token
--| ; values are not case-sensitive
--| disposition-parm := filename-parm
--| / creation-date-parm
--| / modification-date-parm
--| / read-date-parm
--| / size-parm
--| / parameter
--| filename-parm := "filename" "=" value
--| creation-date-parm := "creation-date" "=" quoted-date-time
--| modification-date-parm := "modification-date" "=" quoted-date-time
--| read-date-parm := "read-date" "=" quoted-date-time
--| size-parm := "size" "=" 1*DIGIT
--| quoted-date-time := quoted-string
--| ; contents MUST be an RFC 822 `date-time'
--| ; numeric timezones (+HHMM or -HHMM) MUST be used
do
if a_params /= Void then
put_header_key_value ({HTTP_HEADER_NAMES}.header_content_disposition, a_type + semi_colon_space + a_params)
else
put_header_key_value ({HTTP_HEADER_NAMES}.header_content_disposition, a_type)
end
end
feature -- Content-type helpers
put_content_type_text_css do put_content_type ({HTTP_MIME_TYPES}.text_css) end
put_content_type_text_csv do put_content_type ({HTTP_MIME_TYPES}.text_csv) end
put_content_type_text_html do put_content_type ({HTTP_MIME_TYPES}.text_html) end
put_content_type_text_javascript do put_content_type ({HTTP_MIME_TYPES}.text_javascript) end
put_content_type_text_json do put_content_type ({HTTP_MIME_TYPES}.text_json) end
put_content_type_text_plain do put_content_type ({HTTP_MIME_TYPES}.text_plain) end
put_content_type_text_xml do put_content_type ({HTTP_MIME_TYPES}.text_xml) end
put_content_type_application_json do put_content_type ({HTTP_MIME_TYPES}.application_json) end
put_content_type_application_javascript do put_content_type ({HTTP_MIME_TYPES}.application_javascript) end
put_content_type_application_zip do put_content_type ({HTTP_MIME_TYPES}.application_zip) end
put_content_type_application_pdf do put_content_type ({HTTP_MIME_TYPES}.application_pdf) end
put_content_type_image_gif do put_content_type ({HTTP_MIME_TYPES}.image_gif) end
put_content_type_image_png do put_content_type ({HTTP_MIME_TYPES}.image_png) end
put_content_type_image_jpg do put_content_type ({HTTP_MIME_TYPES}.image_jpg) end
put_content_type_image_svg_xml do put_content_type ({HTTP_MIME_TYPES}.image_svg_xml) end
put_content_type_message_http do put_content_type ({HTTP_MIME_TYPES}.message_http) end
put_content_type_multipart_mixed do put_content_type ({HTTP_MIME_TYPES}.multipart_mixed) end
put_content_type_multipart_alternative do put_content_type ({HTTP_MIME_TYPES}.multipart_alternative) end
put_content_type_multipart_related do put_content_type ({HTTP_MIME_TYPES}.multipart_related) end
put_content_type_multipart_form_data do put_content_type ({HTTP_MIME_TYPES}.multipart_form_data) end
put_content_type_multipart_signed do put_content_type ({HTTP_MIME_TYPES}.multipart_signed) end
put_content_type_multipart_encrypted do put_content_type ({HTTP_MIME_TYPES}.multipart_encrypted) end
put_content_type_application_x_www_form_encoded do put_content_type ({HTTP_MIME_TYPES}.application_x_www_form_encoded) end
put_content_type_utf_8_text_plain do put_content_type_with_charset ({HTTP_MIME_TYPES}.text_plain, "utf-8") end
feature -- Cross-Origin Resource Sharing
put_access_control_allow_origin (a_origin: READABLE_STRING_8)
-- Put "Access-Control-Allow-Origin: " + `a_origin' header.
-- `a_origin' specifies a URI that may access the resource
--| for instance "http://example.com"
do
put_header_key_value ({HTTP_HEADER_NAMES}.header_access_control_allow_origin, a_origin)
end
put_access_control_allow_all_origin
-- Put "Access-Control-Allow-Origin: *" header.
do
put_access_control_allow_origin ("*")
end
put_access_control_allow_credentials (b: BOOLEAN)
-- Indicates whether or not the response to the request can be exposed when the credentials flag is true.
-- When used as part of a response to a preflight request, this indicates whether or not the actual request can be made using credentials.
-- Note that simple GET requests are not preflighted, and so if a request is made for a resource with credentials,
-- if this header is not returned with the resource, the response is ignored by the browser and not returned to web content.
-- ex: Access-Control-Allow-Credentials: true | false
do
if b then
put_header_key_value ({HTTP_HEADER_NAMES}.header_access_control_allow_Credentials, "true")
else
put_header_key_value ({HTTP_HEADER_NAMES}.header_access_control_allow_Credentials, "false")
end
end
put_access_control_allow_methods (a_methods: ITERABLE [READABLE_STRING_8])
-- If `a_methods' is not empty, put `Access-Control-Allow-Methods' header with list `a_methods' of methods
-- `a_methods' specifies the method or methods allowed when accessing the resource.
-- This is used in response to a preflight request.
-- ex: Access-Control-Allow-Methods: <method>[, <method>]*
do
put_header_key_values ({HTTP_HEADER_NAMES}.header_access_control_allow_methods, a_methods, Void)
end
put_access_control_allow_headers (a_headers: READABLE_STRING_8)
-- Put "Access-Control-Allow-Headers" header. with value `a_headers'
-- Used in response to a preflight request to indicate which HTTP headers can be used when making the actual request.
-- ex: Access-Control-Allow-Headers: <field-name>[, <field-name>]*
do
put_header_key_value ({HTTP_HEADER_NAMES}.header_access_control_allow_headers, a_headers)
end
put_access_control_allow_iterable_headers (a_fields: ITERABLE [READABLE_STRING_8])
-- Put "Access-Control-Allow-Headers" header. with value `a_headers'
-- Used in response to a preflight request to indicate which HTTP headers can be used when making the actual request.
-- ex: Access-Control-Allow-Headers: <field-name>[, <field-name>]*
do
put_header_key_values ({HTTP_HEADER_NAMES}.header_access_control_allow_headers, a_fields, Void)
end
feature -- Method related
put_allow (a_methods: ITERABLE [READABLE_STRING_8])
-- If `a_methods' is not empty, put `Allow' header with list `a_methods' of methods
do
put_header_key_values ({HTTP_HEADER_NAMES}.header_allow, a_methods, Void)
end
feature -- Date
put_date (a_date: READABLE_STRING_8)
-- Put "Date: " header
do
put_header_key_value ({HTTP_HEADER_NAMES}.header_date, a_date)
end
put_current_date
-- Put current date time with "Date" header
do
put_utc_date (create {DATE_TIME}.make_now_utc)
end
put_utc_date (a_utc_date: DATE_TIME)
-- Put UTC date time `a_utc_date' with "Date" header
-- using RFC1123 date formating.
do
put_date (date_to_rfc1123_http_date_format (a_utc_date))
end
put_last_modified (a_utc_date: DATE_TIME)
-- Put UTC date time `dt' with "Last-Modified" header
do
put_header_key_value ({HTTP_HEADER_NAMES}.header_last_modified, date_to_rfc1123_http_date_format (a_utc_date))
end
feature -- Authorization
put_authorization (a_authorization: READABLE_STRING_8)
-- Put `a_authorization' with "Authorization" header
-- The Authorization header is constructed as follows:
-- 1. Username and password are combined into a string "username:password".
-- 2. The resulting string literal is then encoded using Base64.
-- 3. The authorization method and a space, i.e. "Basic " is then put before the encoded string.
-- ex: Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==
do
put_header_key_value ({HTTP_HEADER_NAMES}.header_authorization, a_authorization)
end
feature -- Others
put_expires (a_seconds: INTEGER)
-- Put "Expires" header to `a_seconds' seconds
do
put_expires_string (a_seconds.out)
end
put_expires_string (a_expires: STRING)
-- Put "Expires" header with `a_expires' string value
do
put_header_key_value ("Expires", a_expires)
end
put_expires_date (a_utc_date: DATE_TIME)
-- Put "Expires" header with UTC date time value
-- formatted following RFC1123 specification.
do
put_header_key_value ("Expires", date_to_rfc1123_http_date_format (a_utc_date))
end
put_cache_control (a_cache_control: READABLE_STRING_8)
-- Put "Cache-Control" header with value `a_cache_control'
--| note: ex "Cache-Control: no-cache, must-revalidate"
do
put_header_key_value ("Cache-Control", a_cache_control)
end
put_pragma (a_pragma: READABLE_STRING_8)
-- Put "Pragma" header with value `a_pragma'
do
put_header_key_value ("Pragma", a_pragma)
end
put_pragma_no_cache
-- Put "Pragma" header with "no-cache" a_pragma
do
put_pragma ("no-cache")
end
feature -- Redirection
put_location (a_uri: READABLE_STRING_8)
-- Tell the client the new location `a_uri'
-- using "Location" header.
require
a_uri_valid: not a_uri.is_empty
do
put_header_key_value ({HTTP_HEADER_NAMES}.header_location, a_uri)
end
put_refresh (a_uri: READABLE_STRING_8; a_timeout_in_seconds: INTEGER)
-- Tell the client to refresh page with `a_uri' after `a_timeout_in_seconds' in seconds
-- using "Refresh" header.
require
a_uri_valid: not a_uri.is_empty
do
put_header_key_value ({HTTP_HEADER_NAMES}.header_refresh, a_timeout_in_seconds.out + "; url=" + a_uri)
end
feature -- Cookie
put_cookie (key, value: READABLE_STRING_8; expiration, path, domain: detachable READABLE_STRING_8; secure, http_only: BOOLEAN)
-- Set a cookie on the client's machine
-- with key 'key' and value 'value'.
-- Note: you should avoid using "localhost" as `domain' for local cookies
-- since they are not always handled by browser (for instance Chrome)
require
make_sense: (key /= Void and value /= Void) and then (not key.is_empty and not value.is_empty)
domain_without_port_info: domain /= Void implies domain.index_of (':', 1) = 0
local
s: STRING
do
s := {HTTP_HEADER_NAMES}.header_set_cookie + colon_space + key + "=" + value
if
domain /= Void and then not domain.same_string ("localhost")
then
s.append ("; Domain=")
s.append (domain)
end
if path /= Void then
s.append ("; Path=")
s.append (path)
end
if expiration /= Void then
s.append ("; Expires=")
s.append (expiration)
end
if secure then
s.append ("; Secure")
end
if http_only then
s.append ("; HttpOnly")
end
add_header (s)
end
put_cookie_with_expiration_date (key, value: READABLE_STRING_8; expiration: DATE_TIME; path, domain: detachable READABLE_STRING_8; secure, http_only: BOOLEAN)
-- Set a cookie on the client's machine
-- with key 'key' and value 'value'.
require
make_sense: (key /= Void and value /= Void) and then (not key.is_empty and not value.is_empty)
do
put_cookie (key, value, date_to_rfc1123_http_date_format (expiration), path, domain, secure, http_only)
end
feature -- Access
date_to_rfc1123_http_date_format (dt: DATE_TIME): STRING_8
-- String representation of `dt' using the RFC 1123
local
d: HTTP_DATE
do
create d.make_from_date_time (dt)
Result := d.string
end
feature {NONE} -- Implementation
has_same_header_name (h: READABLE_STRING_8; a_name: READABLE_STRING_8): BOOLEAN
-- Header line `h' has same name as `a_name' ?
do
if h.starts_with (a_name) then
if h.valid_index (a_name.count + 1) then
Result := h[a_name.count + 1] = ':'
end
end
end
feature {NONE} -- Constants
str_binary: STRING = "binary"
str_chunked: STRING = "chunked"
colon_space: IMMUTABLE_STRING_8
once
create Result.make_from_string (": ")
end
semi_colon_space: IMMUTABLE_STRING_8
once
create Result.make_from_string ("; ")
end
comma_space: IMMUTABLE_STRING_8
once
create Result.make_from_string (", ")
end
note
copyright: "2011-2014, Jocelyn Fiat, 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

View File

@@ -199,17 +199,26 @@ feature -- Cross-Origin Resource Sharing
header_access_control_allow_origin: STRING = "Access-Control-Allow-Origin"
-- Indicates whether a resource can be shared based by returning
-- the value of the Origin request header in the response.
-- | Example: Access-Control-Allow-Origin: http://example.org
--| Example: Access-Control-Allow-Origin: http://example.org
header_access_control_allow_credentials: STRING = "Access-Control-Allow-Credentials"
-- Indicates whether or not the response to the request can be exposed when the credentials flag is true.
-- When used as part of a response to a preflight request, this indicates whether or not the actual request can be made using credentials.
-- Note that simple GET requests are not preflighted, and so if a request is made for a resource with credentials,
-- if this header is not returned with the resource, the response is ignored by the browser and not returned to web content.
--| Access-Control-Allow-Credentials: true | false
header_access_control_allow_methods: STRING = "Access-Control-Allow-Methods"
-- Indicates, as part of the response to a preflight request,
-- which methods can be used during the actual request.
-- | Example: Access-Control-Allow-Methods: PUT, DELETE
--| Access-Control-Allow-Methods: <method>[, <method>]*
--| Example: Access-Control-Allow-Methods: PUT, DELETE
header_access_control_allow_headers: STRING = "Access-Control-Allow-Headers"
-- Indicates, as part of the response to a preflight request,
-- which header field names can be used during the actual request.
-- | Example: Access-Control-Allow-Headers: Authorization
--| Access-Control-Allow-Headers: <field-name>[, <field-name>]*
--| Example: Access-Control-Allow-Headers: Authorization
feature -- Request or Response header name
@@ -265,7 +274,7 @@ feature -- MIME related
header_content_transfer_encoding: STRING = "Content-Transfer-Encoding"
note
copyright: "2011-2013, Jocelyn Fiat, Eiffel Software and others"
copyright: "2011-2014, Jocelyn Fiat, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[
Eiffel Software