Simplified EWSGI interfaces

Renamed WGI_RESPONSE_BUFFER as WGI_RESPONSE to avoid confusion
Removed EWF_HEADER and removed related caller from WGI implementation,
   now this is only part of WSF library
Added wgi_version, wgi_implementation and wgi_connector to the WGI_REQUEST interface
   to give more information to the user
Added back WGI_CONNECTOR to WGI specification, mainly because of `{WGI_REQUEST}.wgi_connector'
   simplified WGI_CONNECTOR to contain for now only `name' and `version'
   if the implementation of connector inherit from WGI_CONNECTOR (recommended)
   this might gives more access to the user using a reverse assignment for specific needs
   (but this usage is not recommended due to portability issue on other connector)
Removed useless connector.ecf since now EWF/WGI library provides the helper classes
This commit is contained in:
Jocelyn Fiat
2011-11-25 14:39:48 +01:00
parent 3032b91ff7
commit fd0912904c
24 changed files with 553 additions and 627 deletions

View File

@@ -10,7 +10,6 @@
<option warning="true" full_class_checking="true" is_attached_by_default="true" void_safety="all">
</option>
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
<library name="connector" location="..\connector-safe.ecf" readonly="false"/>
<library name="ewsgi" location="..\..\ewsgi-safe.ecf" readonly="false"/>
<library name="http" location="..\..\..\..\protocol\http\http-safe.ecf"/>
<cluster name="src" location=".\src\" recursive="true"/>

View File

@@ -10,7 +10,6 @@
<option warning="true" full_class_checking="true">
</option>
<library name="base" location="$ISE_LIBRARY\library\base\base.ecf"/>
<library name="connector" location="..\connector.ecf" readonly="false"/>
<library name="ewsgi" location="..\..\ewsgi.ecf" readonly="false"/>
<library name="http" location="..\..\..\..\protocol\http\http.ecf"/>
<cluster name="src" location=".\src\" recursive="true"/>

View File

@@ -1,6 +1,5 @@
note
description: "Summary description for {WGI_CGI_CONNECTOR}."
author: ""
date: "$Date$"
revision: "$Revision$"
@@ -13,23 +12,43 @@ inherit
create
make
feature {NONE} -- Initialization
make (a_app: like application)
do
application := a_app
end
feature -- Access
Name: STRING_8 = "CGI"
-- Name of Current connector
Version: STRING_8 = "0.1"
-- Version of Current connector
feature {NONE} -- Access
application: WGI_SERVICE
-- Gateway Service
feature -- Execution
launch
local
req: WGI_REQUEST_FROM_TABLE
res: detachable WGI_RESPONSE_STREAM_BUFFER
res: detachable WGI_RESPONSE_STREAM
rescued: BOOLEAN
do
if not rescued then
create req.make ((create {EXECUTION_ENVIRONMENT}).starting_environment_variables, create {WGI_CGI_INPUT_STREAM}.make)
create req.make ((create {EXECUTION_ENVIRONMENT}).starting_environment_variables, create {WGI_CGI_INPUT_STREAM}.make, Current)
create res.make (create {WGI_CGI_OUTPUT_STREAM}.make)
application.execute (req, res)
else
if attached (create {EXCEPTION_MANAGER}).last_exception as e and then attached e.exception_trace as l_trace then
if res /= Void then
if not res.status_is_set then
res.write_header ({HTTP_STATUS_CODE}.internal_server_error, Void)
res.set_status_code ({HTTP_STATUS_CODE}.internal_server_error)
end
if res.message_writable then
res.write_string ("<pre>" + l_trace + "</pre>")
@@ -41,7 +60,7 @@ feature -- Execution
rescued := True
retry
end
note
copyright: "2011-2011, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"

View File

@@ -1,44 +0,0 @@
note
description: "Summary description for {WGI_CONNECTOR}."
specification: "EWSGI/connector specification https://github.com/Eiffel-World/Eiffel-Web-Framework/wiki/EWSGI-specification"
date: "$Date$"
revision: "$Revision$"
deferred class
WGI_CONNECTOR
feature {NONE} -- Initialization
make (a_app: like application)
do
application := a_app
initialize
end
initialize
-- Initialize connector
do
end
feature {NONE} -- Access
application: WGI_SERVICE
-- Gateway Service
feature -- Server
launch
deferred
end
note
copyright: "2011-2011, 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

@@ -1,16 +0,0 @@
<?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="connector" uuid="61FBBC8E-558A-4079-920E-204946E54EFB" library_target="connector">
<target name="connector">
<root all_classes="true"/>
<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="standard">
</option>
<library name="base" location="$ISE_LIBRARY/library/base/base-safe.ecf"/>
<library name="ewsgi" location="../ewsgi-safe.ecf"/>
<cluster name="common" location="./common" recursive="true"/>
</target>
</system>

View File

@@ -1,16 +0,0 @@
<?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="connector" uuid="61FBBC8E-558A-4079-920E-204946E54EFB" library_target="connector">
<target name="connector">
<root all_classes="true"/>
<file_rule>
<exclude>/.git$</exclude>
<exclude>/EIFGENs$</exclude>
<exclude>/.svn$</exclude>
</file_rule>
<option warning="true" full_class_checking="true" void_safety="none" syntax="standard">
</option>
<library name="base" location="$ISE_LIBRARY/library/base/base.ecf"/>
<library name="ewsgi" location="../ewsgi.ecf"/>
<cluster name="common" location="./common" recursive="true"/>
</target>
</system>

View File

@@ -10,7 +10,6 @@
<option warning="true" full_class_checking="true" is_attached_by_default="true" void_safety="all">
</option>
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
<library name="connector" location="..\connector-safe.ecf"/>
<library name="ewsgi" location="..\..\ewsgi-safe.ecf"/>
<library name="libfcgi" location="..\..\..\libfcgi\libfcgi-safe.ecf"/>
<library name="http" location="..\..\..\..\protocol\http\http-safe.ecf"/>

View File

@@ -10,7 +10,6 @@
<option warning="true" full_class_checking="true">
</option>
<library name="base" location="$ISE_LIBRARY\library\base\base.ecf"/>
<library name="connector" location="..\connector.ecf" readonly="false"/>
<library name="ewsgi" location="..\..\ewsgi.ecf" readonly="false"/>
<library name="libfcgi" location="..\..\..\libfcgi\libfcgi.ecf" />
<library name="http" location="..\..\..\..\protocol\http\http.ecf"/>

View File

@@ -10,22 +10,33 @@ class
inherit
WGI_CONNECTOR
redefine
initialize
end
create
make
feature {NONE} -- Initialization
initialize
make (a_app: like application)
do
application := a_app
create fcgi.make
create {WGI_LIBFCGI_INPUT_STREAM} input.make (fcgi)
create {WGI_LIBFCGI_OUTPUT_STREAM} output.make (fcgi)
end
feature -- Access
Name: STRING_8 = "libFCGI"
-- Name of Current connector
Version: STRING_8 = "0.1"
-- Version of Current connector
feature {NONE} -- Access
application: WGI_SERVICE
-- Gateway Service
feature -- Server
launch
@@ -47,18 +58,18 @@ feature -- Execution
process_fcgi_request (vars: HASH_TABLE [STRING, STRING]; a_input: like input; a_output: like output)
local
req: WGI_REQUEST_FROM_TABLE
res: detachable WGI_RESPONSE_STREAM_BUFFER
res: detachable WGI_RESPONSE_STREAM
rescued: BOOLEAN
do
if not rescued then
create req.make (vars, a_input)
create req.make (vars, a_input, Current)
create res.make (a_output)
application.execute (req, res)
else
if attached (create {EXCEPTION_MANAGER}).last_exception as e and then attached e.exception_trace as l_trace then
if res /= Void then
if not res.status_is_set then
res.write_header ({HTTP_STATUS_CODE}.internal_server_error, Void)
res.set_status_code ({HTTP_STATUS_CODE}.internal_server_error)
end
if res.message_writable then
res.write_string ("<pre>" + l_trace + "</pre>")

View File

@@ -10,7 +10,6 @@
<option warning="true" full_class_checking="true" is_attached_by_default="true" void_safety="all">
</option>
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
<library name="connector" location="..\connector-safe.ecf" readonly="false"/>
<library name="ewsgi" location="..\..\ewsgi-safe.ecf" readonly="false"/>
<library name="http" location="..\..\..\..\protocol\http\http-safe.ecf"/>
<library name="nino" location="..\..\..\..\..\contrib\library\server\nino\nino-safe.ecf" readonly="false">

View File

@@ -10,7 +10,6 @@
<option warning="true" full_class_checking="true">
</option>
<library name="base" location="$ISE_LIBRARY\library\base\base.ecf"/>
<library name="connector" location="..\connector.ecf" readonly="false"/>
<library name="ewsgi" location="..\..\ewsgi.ecf" readonly="false"/>
<library name="http" location="..\..\..\..\protocol\http\http.ecf"/>
<library name="nino" location="..\..\..\..\..\contrib\library\server\nino\nino.ecf" readonly="false">

View File

@@ -9,9 +9,6 @@ class
inherit
WGI_CONNECTOR
redefine
initialize
end
create
make,
@@ -19,6 +16,16 @@ create
feature {NONE} -- Initialization
make (a_app: like application)
local
cfg: HTTP_SERVER_CONFIGURATION
do
application := a_app
create cfg.make
create server.make (cfg)
end
make_with_base (a_app: like application; a_base: like base)
require
a_base_starts_with_slash: (a_base /= Void and then not a_base.is_empty) implies a_base.starts_with ("/")
@@ -27,15 +34,18 @@ feature {NONE} -- Initialization
set_base (a_base)
end
feature {NONE} -- Initialization
feature -- Access
initialize
local
cfg: HTTP_SERVER_CONFIGURATION
do
create cfg.make
create server.make (cfg)
end
name: STRING_8 = "Nino"
-- Name of Current connector
version: STRING_8 = "0.1"
-- Version of Current connector
feature {NONE} -- Access
application: WGI_SERVICE
-- Gateway Service
feature -- Access
@@ -107,11 +117,11 @@ feature -- Server
process_request (env: HASH_TABLE [STRING, STRING]; a_headers_text: STRING; a_socket: TCP_STREAM_SOCKET)
local
req: WGI_REQUEST_FROM_TABLE
res: detachable WGI_RESPONSE_STREAM_BUFFER
res: detachable WGI_RESPONSE_STREAM
rescued: BOOLEAN
do
if not rescued then
create req.make (env, create {WGI_NINO_INPUT_STREAM}.make (a_socket))
create req.make (env, create {WGI_NINO_INPUT_STREAM}.make (a_socket), Current)
create res.make (create {WGI_NINO_OUTPUT_STREAM}.make (a_socket))
req.set_meta_string_variable ("RAW_HEADER_DATA", a_headers_text)
application.execute (req, res)
@@ -119,7 +129,7 @@ feature -- Server
if attached (create {EXCEPTION_MANAGER}).last_exception as e and then attached e.exception_trace as l_trace then
if res /= Void then
if not res.status_is_set then
res.write_header ({HTTP_STATUS_CODE}.internal_server_error, Void)
res.set_status_code ({HTTP_STATUS_CODE}.internal_server_error)
end
if res.message_writable then
res.write_string ("<pre>" + l_trace + "</pre>")

View File

@@ -17,5 +17,6 @@
<cluster name="interface" location="src\" recursive="true"/>
<cluster name="specification_request" location="specification\request" recursive="true"/>
<cluster name="specification_response" location="specification\response" recursive="true"/>
<cluster name="specification_connector" location="specification\connector" recursive="true"/>
</target>
</system>

View File

@@ -17,5 +17,6 @@
<cluster name="interface" location="src\" recursive="true" />
<cluster name="specification_request" location="specification\request" recursive="true"/>
<cluster name="specification_response" location="specification\response" recursive="true"/>
<cluster name="specification_connector" location="specification\connector" recursive="true"/>
</target>
</system>

View File

@@ -129,6 +129,25 @@ feature -- Access: CGI meta variables
deferred
end
feature -- EWSGI access
wgi_version: READABLE_STRING_8
-- Eiffel WGI version
--| example: "1.0"
deferred
end
wgi_implementation: READABLE_STRING_8
-- Information about Eiffel WGI implementation
--| example: "Eiffel Web Framework 1.0"
deferred
end
wgi_connector: WGI_CONNECTOR
-- Associated Eiffel WGI connector
deferred
end
feature -- Common Gateway Interface - 1.1 8 January 1996
auth_type: detachable READABLE_STRING_8

View File

@@ -1,11 +1,10 @@
note
description: "Summary description for {WGI_RESPONSE_BUFFER}."
author: ""
description: "Summary description for {WGI_RESPONSE}."
date: "$Date$"
revision: "$Revision$"
deferred class
WGI_RESPONSE_BUFFER
WGI_RESPONSE
feature {WGI_SERVICE} -- Commit
@@ -69,7 +68,7 @@ feature -- Status setting
feature -- Header output operation
write_headers_string (a_headers: READABLE_STRING_8)
write_headers (a_headers: READABLE_STRING_8)
require
status_set: status_is_set
header_not_committed: not header_committed
@@ -80,18 +79,6 @@ feature -- Header output operation
message_writable: message_writable
end
write_header (a_status_code: INTEGER; a_headers: detachable ARRAY [TUPLE [key: READABLE_STRING_8; value: READABLE_STRING_8]])
-- Send headers with status `a_status', and headers from `a_headers'
require
status_not_set: not status_is_set
header_not_committed: not header_committed
deferred
ensure
header_committed: header_committed
status_set: status_is_set
message_writable: message_writable
end
feature -- Output operation
write_string (s: READABLE_STRING_8)

View File

@@ -19,10 +19,11 @@ create
feature {NONE} -- Initialization
make (a_vars: HASH_TABLE [READABLE_STRING_8, READABLE_STRING_8]; a_input: like input)
make (a_vars: HASH_TABLE [READABLE_STRING_8, READABLE_STRING_8]; a_input: like input; a_wgi_connector: like wgi_connector)
require
vars_attached: a_vars /= Void
do
wgi_connector := a_wgi_connector
input := a_input
set_meta_variables (a_vars)
@@ -34,6 +35,14 @@ feature -- Access: Input
input: WGI_INPUT_STREAM
-- Server input channel
feature -- EWSGI access
wgi_version: STRING = "0.1"
wgi_implementation: STRING = "Eiffel Web Framework 0.1"
wgi_connector: WGI_CONNECTOR
feature -- Access: CGI meta parameters
meta_variables: HASH_TABLE [READABLE_STRING_8, READABLE_STRING_8]

View File

@@ -1,17 +1,16 @@
note
description: "[
Response buffer
WGI Response implemented using stream buffer
]"
specification: "EWSGI specification https://github.com/Eiffel-World/Eiffel-Web-Framework/wiki/EWSGI-specification"
date: "$Date$"
revision: "$Revision$"
class
WGI_RESPONSE_STREAM_BUFFER
WGI_RESPONSE_STREAM
inherit
WGI_RESPONSE_BUFFER
WGI_RESPONSE
create
make
@@ -76,34 +75,12 @@ feature -- Status setting
feature -- Header output operation
write_headers_string (a_headers: READABLE_STRING_8)
write_headers (a_headers: READABLE_STRING_8)
do
write (a_headers)
header_committed := True
end
write_header (a_status_code: INTEGER; a_headers: detachable ARRAY [TUPLE [key: READABLE_STRING_8; value: READABLE_STRING_8]])
-- Send headers with status `a_status', and headers from `a_headers'
local
h: EWF_HEADER
i,n: INTEGER
do
set_status_code (a_status_code)
create h.make
if a_headers /= Void then
from
i := a_headers.lower
n := a_headers.upper
until
i > n
loop
h.put_header_key_value (a_headers[i].key, a_headers[i].value)
i := i + 1
end
end
write_headers_string (h.string)
end
feature -- Output operation
write_string (s: READABLE_STRING_8)

View File

@@ -1,465 +0,0 @@
note
description: "[
The class provides an easy way to build HTTP header.
You will also find some helper feature to help coding most common usage
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
However you can set the "Status: " header line if you want
]"
legal: "See notice at end of class."
status: "See notice at end of class."
date: "$Date$"
revision: "$Revision$"
class
EWF_HEADER
inherit
ANY
HTTP_STATUS_CODE_MESSAGES --| useful for `put_status'
export
{NONE} all
end
create
make,
make_with_count
feature {NONE} -- Initialization
make
-- Initialize current
do
make_with_count (3)
end
make_with_count (n: INTEGER)
-- Make with a capacity of `n' header entries
do
create {ARRAYED_LIST [READABLE_STRING_8]} headers.make (n)
end
feature -- Recycle
recycle
-- Recycle current object
do
headers.wipe_out
end
feature -- Access
headers: LIST [READABLE_STRING_8]
-- Header's lines
string: STRING
-- String representation of the headers
local
l_headers: like headers
do
create Result.make (32)
l_headers := headers
if l_headers.is_empty then
put_content_type_text_html -- See if this make sense to define a default content-type
else
from
l_headers.start
until
l_headers.after
loop
append_line_to (l_headers.item, Result)
l_headers.forth
end
end
append_end_of_line_to (Result)
end
feature -- Header change: general
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: 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 (h), h)
end
add_header_key_value (k,v: READABLE_STRING_8)
-- Add header `k:v', or replace existing header of same header name/key
do
add_header (k + colon_space + v)
end
put_header_key_value (k,v: READABLE_STRING_8)
-- Add header `k:v', or replace existing header of same header name/key
do
put_header (k + colon_space + v)
end
feature -- Status related
put_status (c: INTEGER)
-- Put "Status: " header
-- Rarely used
local
s: STRING
do
create s.make_from_string (c.out)
if attached http_status_code_message (c) as msg then
s.append_character (' ')
s.append (msg)
end
put_header_key_value ("Status", s)
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_charset (t: READABLE_STRING_8; c: READABLE_STRING_8)
do
put_header_key_value ({HTTP_HEADER_NAMES}.header_content_type, 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_header_key_value ({HTTP_HEADER_NAMES}.header_content_type, t + "; charset=" + c + "")
end
put_content_type_with_name (t: READABLE_STRING_8; n: READABLE_STRING_8)
do
put_header_key_value ({HTTP_HEADER_NAMES}.header_content_type, 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_header_key_value ({HTTP_HEADER_NAMES}.header_content_type, 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_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_chunked
-- Put "Transfer-Encoding: chunked" header
do
put_transfer_encoding ("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_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
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 (dt: DATE_TIME)
-- Put UTC date time `dt' with "Date" header
do
put_date (dt.formatted_out ("ddd,[0]dd mmm yyyy [0]hh:[0]mi:[0]ss.ff2") + " GMT")
end
feature -- Others
put_expires (n: INTEGER)
do
put_header_key_value ("Expires", n.out)
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
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, secure: detachable READABLE_STRING_8)
-- 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)
local
s: STRING
do
s := {HTTP_HEADER_NAMES}.header_set_cookie + colon_space + key + "=" + value
if expiration /= Void then
s.append ("; expires=" + expiration)
end
if path /= Void then
s.append ("; path=" + path)
end
if domain /= Void then
s.append ("; domain=" + domain)
end
if secure /= Void then
s.append ("; secure=" + secure)
end
add_header (s)
end
feature -- Status report
has_header_named (a_name: READABLE_STRING_8): BOOLEAN
-- Has header item for `n'?
local
c: like headers.new_cursor
n: INTEGER
l_line: READABLE_STRING_8
do
from
n := a_name.count
c := headers.new_cursor
until
c.after or Result
loop
l_line := c.item
if l_line.starts_with (a_name) then
if l_line.valid_index (n + 1) then
Result := l_line [n + 1] = ':'
end
end
c.forth
end
end
has_content_length: BOOLEAN
-- Has header "content_length"
do
Result := has_header_named ({HTTP_HEADER_NAMES}.header_content_length)
end
feature {NONE} -- Implementation: Header
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'
require
h_has_name_n: (n /= Void and attached header_name (h) as hn) implies n.same_string (hn)
local
l_headers: like headers
do
if n /= Void then
from
l_headers := headers
l_headers.start
until
l_headers.after or l_headers.item.starts_with (n)
loop
l_headers.forth
end
if not l_headers.after then
l_headers.replace (h)
else
add_header (h)
end
else
add_header (h)
end
end
header_name (h: READABLE_STRING_8): detachable READABLE_STRING_8
-- If any, header's name with colon
--| ex: for "Foo-bar: something", this will return "Foo-bar:"
local
s: detachable STRING_8
i,n: INTEGER
c: CHARACTER
do
from
i := 1
n := h.count
create s.make (10)
until
i > n or c = ':' or s = Void
loop
c := h[i]
inspect c
when ':' then
s.extend (c)
when '-', 'a' .. 'z', 'A' .. 'Z' then
s.extend (c)
else
s := Void
end
i := i + 1
end
Result := s
end
feature {NONE} -- Implementation
append_line_to (s: READABLE_STRING_8; h: like string)
do
h.append_string (s)
append_end_of_line_to (h)
end
append_end_of_line_to (h: like string)
do
h.append_character ('%R')
h.append_character ('%N')
end
feature {NONE} -- Constants
colon_space: STRING = ": "
semi_colon_space: STRING = "; "
note
copyright: "2011-2011, 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

@@ -23,10 +23,10 @@ feature {NONE} -- Implementation
feature {NONE} -- Implementation
callback: PROCEDURE [ANY, TUPLE [req: WGI_REQUEST; res: WGI_RESPONSE_BUFFER]]
callback: PROCEDURE [ANY, TUPLE [req: WGI_REQUEST; res: WGI_RESPONSE]]
-- Procedure called on `execute'
execute (req: WGI_REQUEST; res: WGI_RESPONSE_BUFFER)
execute (req: WGI_REQUEST; res: WGI_RESPONSE)
-- Execute the request
do
callback.call ([req, res])

View File

@@ -13,7 +13,7 @@ deferred class
feature -- Execution
execute (req: WGI_REQUEST; res: WGI_RESPONSE_BUFFER)
execute (req: WGI_REQUEST; res: WGI_RESPONSE)
-- Execute the request
-- See `req.input' for input stream
-- `req.meta_variables' for the CGI meta variable

View File

@@ -23,12 +23,434 @@ class
WSF_HEADER
inherit
EWF_HEADER
ANY
HTTP_STATUS_CODE_MESSAGES --| useful for `put_status'
export
{NONE} all
end
create
make,
make_with_count
feature {NONE} -- Initialization
make
-- Initialize current
do
make_with_count (3)
end
make_with_count (n: INTEGER)
-- Make with a capacity of `n' header entries
do
create {ARRAYED_LIST [READABLE_STRING_8]} headers.make (n)
end
feature -- Recycle
recycle
-- Recycle current object
do
headers.wipe_out
end
feature -- Access
headers: LIST [READABLE_STRING_8]
-- Header's lines
string: STRING
-- String representation of the headers
local
l_headers: like headers
do
create Result.make (32)
l_headers := headers
if l_headers.is_empty then
put_content_type_text_html -- See if this make sense to define a default content-type
else
from
l_headers.start
until
l_headers.after
loop
append_line_to (l_headers.item, Result)
l_headers.forth
end
end
append_end_of_line_to (Result)
end
feature -- Header change: general
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: 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 (h), h)
end
add_header_key_value (k,v: READABLE_STRING_8)
-- Add header `k:v', or replace existing header of same header name/key
do
add_header (k + colon_space + v)
end
put_header_key_value (k,v: READABLE_STRING_8)
-- Add header `k:v', or replace existing header of same header name/key
do
put_header (k + colon_space + v)
end
feature -- Status related
put_status (c: INTEGER)
-- Put "Status: " header
-- Rarely used
local
s: STRING
do
create s.make_from_string (c.out)
if attached http_status_code_message (c) as msg then
s.append_character (' ')
s.append (msg)
end
put_header_key_value ("Status", s)
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_charset (t: READABLE_STRING_8; c: READABLE_STRING_8)
do
put_header_key_value ({HTTP_HEADER_NAMES}.header_content_type, 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_header_key_value ({HTTP_HEADER_NAMES}.header_content_type, t + "; charset=" + c + "")
end
put_content_type_with_name (t: READABLE_STRING_8; n: READABLE_STRING_8)
do
put_header_key_value ({HTTP_HEADER_NAMES}.header_content_type, 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_header_key_value ({HTTP_HEADER_NAMES}.header_content_type, 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_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_chunked
-- Put "Transfer-Encoding: chunked" header
do
put_transfer_encoding ("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_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
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 (dt: DATE_TIME)
-- Put UTC date time `dt' with "Date" header
do
put_date (dt.formatted_out ("ddd,[0]dd mmm yyyy [0]hh:[0]mi:[0]ss.ff2") + " GMT")
end
feature -- Others
put_expires (n: INTEGER)
do
put_header_key_value ("Expires", n.out)
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
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, secure: detachable READABLE_STRING_8)
-- 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)
local
s: STRING
do
s := {HTTP_HEADER_NAMES}.header_set_cookie + colon_space + key + "=" + value
if expiration /= Void then
s.append ("; expires=" + expiration)
end
if path /= Void then
s.append ("; path=" + path)
end
if domain /= Void then
s.append ("; domain=" + domain)
end
if secure /= Void then
s.append ("; secure=" + secure)
end
add_header (s)
end
feature -- Status report
has_header_named (a_name: READABLE_STRING_8): BOOLEAN
-- Has header item for `n'?
local
c: like headers.new_cursor
n: INTEGER
l_line: READABLE_STRING_8
do
from
n := a_name.count
c := headers.new_cursor
until
c.after or Result
loop
l_line := c.item
if l_line.starts_with (a_name) then
if l_line.valid_index (n + 1) then
Result := l_line [n + 1] = ':'
end
end
c.forth
end
end
has_content_length: BOOLEAN
-- Has header "content_length"
do
Result := has_header_named ({HTTP_HEADER_NAMES}.header_content_length)
end
feature {NONE} -- Implementation: Header
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'
require
h_has_name_n: (n /= Void and attached header_name (h) as hn) implies n.same_string (hn)
local
l_headers: like headers
do
if n /= Void then
from
l_headers := headers
l_headers.start
until
l_headers.after or l_headers.item.starts_with (n)
loop
l_headers.forth
end
if not l_headers.after then
l_headers.replace (h)
else
add_header (h)
end
else
add_header (h)
end
end
header_name (h: READABLE_STRING_8): detachable READABLE_STRING_8
-- If any, header's name with colon
--| ex: for "Foo-bar: something", this will return "Foo-bar:"
local
s: detachable STRING_8
i,n: INTEGER
c: CHARACTER
do
from
i := 1
n := h.count
create s.make (10)
until
i > n or c = ':' or s = Void
loop
c := h[i]
inspect c
when ':' then
s.extend (c)
when '-', 'a' .. 'z', 'A' .. 'Z' then
s.extend (c)
else
s := Void
end
i := i + 1
end
Result := s
end
feature {NONE} -- Implementation
append_line_to (s: READABLE_STRING_8; h: like string)
do
h.append_string (s)
append_end_of_line_to (h)
end
append_end_of_line_to (h: like string)
do
h.append_character ('%R')
h.append_character ('%N')
end
feature {NONE} -- Constants
colon_space: STRING = ": "
semi_colon_space: STRING = "; "
note
copyright: "2011-2011, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"

View File

@@ -12,16 +12,17 @@ create {WSF_SERVICE}
make_from_wgi
convert
make_from_wgi ({WGI_RESPONSE_BUFFER})
make_from_wgi ({WGI_RESPONSE})
feature {NONE} -- Initialization
make_from_wgi (r: WGI_RESPONSE_BUFFER)
make_from_wgi (r: WGI_RESPONSE)
do
wgi_response := r
end
wgi_response: WGI_RESPONSE_BUFFER
wgi_response: WGI_RESPONSE
-- Associated WGI_RESPONSE
feature -- Status report
@@ -77,7 +78,7 @@ feature -- Header output operation
status_set: status_is_set
header_not_committed: not header_committed
do
wgi_response.write_headers_string (a_headers)
wgi_response.write_headers (a_headers)
ensure
status_set: status_is_set
header_committed: header_committed
@@ -89,8 +90,24 @@ feature -- Header output operation
require
status_not_set: not status_is_set
header_not_committed: not header_committed
local
h: WSF_HEADER
i,n: INTEGER
do
wgi_response.write_header (a_status_code, a_headers)
set_status_code (a_status_code)
create h.make
if a_headers /= Void then
from
i := a_headers.lower
n := a_headers.upper
until
i > n
loop
h.put_header_key_value (a_headers[i].key, a_headers[i].value)
i := i + 1
end
end
wgi_response.write_headers (h.string)
ensure
header_committed: header_committed
status_set: status_is_set

View File

@@ -28,7 +28,7 @@ feature -- Execution
feature -- WGI Execution
wgi_execute (req: WGI_REQUEST; res: WGI_RESPONSE_BUFFER)
wgi_execute (req: WGI_REQUEST; res: WGI_RESPONSE)
do
execute (create {WSF_REQUEST}.make_from_wgi (req), create {WSF_RESPONSE}.make_from_wgi (res))
end