Compare commits

..

17 Commits

Author SHA1 Message Date
Jocelyn Fiat
ec6cc5f2b8 code cleaning, and prepare for internal review 2011-08-18 12:25:40 +02:00
Jocelyn Fiat
40018d36eb enhanced the ERROR_HANDLER 2011-08-04 15:11:51 +02:00
Jocelyn Fiat
8e18329063 minor improvements on response_as_result code 2011-08-02 14:53:37 +02:00
Jocelyn Fiat
c372494713 cosmetic in config file .ecf 2011-08-02 10:47:16 +02:00
Jocelyn Fiat
10f4a99ee1 add "write_headers_string" to RESPONSE_BUFFER 2011-08-02 10:46:53 +02:00
Jocelyn Fiat
e9085c614c sync wiki 2011-08-01 17:02:33 +02:00
Jocelyn Fiat
f7d3f519a7 moved ewsgi-full config file under tests (this is mainly for dev purpose, to be able to compile and edit all classes related to ewsgi) 2011-08-01 16:47:00 +02:00
Jocelyn Fiat
4eb22d0272 Tried to reduce gap between both EWSGI proposals
Re-adapt the Spec-compliant solution (instead of Lib-compliant solution).
  Thus no more 100% deferred interface.
Rename EWSGI_RESPONSE into EWSGI_RESPONSE_BUFFER
Added in extra/response-as-result/  an copy/paste from the implementation of Paul's proposal (not up to date with Paul's spec). But this is mainly for information and tests.
Removed part of the ewsgi/specification interfaces ... to be able to test EWSGI compliant library against the pure specification (experimental).
Renamed most of the GW_... into EWSGI_...
2011-08-01 16:41:16 +02:00
Jocelyn Fiat
bbcc9ef44b added http_accept feature to represent "Accept:" HTTP header 2011-07-29 15:13:34 +02:00
Jocelyn Fiat
801caa4e69 added hello_routed_world example
few changes on new `router' library (still in-progress)
2011-07-29 15:13:08 +02:00
Jocelyn Fiat
1b49445077 Added first draft for a URI and/or URI-template base request router. 2011-07-29 10:51:22 +02:00
Jocelyn Fiat
f005d8bb06 cosmetic 2011-07-29 10:50:31 +02:00
Jocelyn Fiat
a278537f7b Added "flush" to the EWSGI_RESPONSE_STREAM 2011-07-29 10:50:24 +02:00
Jocelyn Fiat
78b5b6f5fe Merge branch 'master' of github.com:jocelyn/Eiffel-Web-Framework 2011-07-29 08:52:57 +02:00
Jocelyn Fiat
a215c1e4d2 added missing non-void-safe .ecf 2011-07-29 08:52:36 +02:00
Jocelyn Fiat
fe3726677b added missing non-void-safe .ecf 2011-07-29 08:45:26 +02:00
Jocelyn Fiat
94d4909644 Fixed various issue with URI template, added corresponding tests 2011-07-28 18:45:25 +02:00
81 changed files with 3551 additions and 646 deletions

View File

@@ -0,0 +1,24 @@
<?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="hello_routed_world" uuid="7C9887BD-4AE4-47F2-A0AA-4BBB6736D433">
<target name="hello_routed_world">
<root class="HELLO_ROUTED_WORLD" feature="make"/>
<file_rule>
<exclude>/EIFGENs$</exclude>
<exclude>/\.git$</exclude>
<exclude>/\.svn$</exclude>
</file_rule>
<option warning="true" full_class_checking="true" is_attached_by_default="true" void_safety="all" syntax="provisional">
<assertions precondition="true" postcondition="true" invariant="true" supplier_precondition="true"/>
</option>
<setting name="concurrency" value="thread"/>
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
<library name="connector_nino" location="..\..\library\server\ewsgi\connectors\nino\nino-safe.ecf" readonly="false"/>
<library name="default_nino" location="..\..\library\server\ewsgi\default\ewsgi_nino-safe.ecf" readonly="false"/>
<library name="encoder" location="..\..\library\text\encoder\encoder-safe.ecf" readonly="false"/>
<library name="ewsgi" location="..\..\library\server\ewsgi\ewsgi-safe.ecf" readonly="false"/>
<library name="http" location="..\..\library\protocol\http\http-safe.ecf" readonly="false"/>
<library name="router" location="..\..\library\server\request\router\router-safe.ecf" readonly="false"/>
<library name="uri_template" location="..\..\library\protocol\uri_template\uri_template-safe.ecf" readonly="false"/>
<cluster name="src" location="src\" recursive="true"/>
</target>
</system>

View File

@@ -0,0 +1,10 @@
${NOTE_KEYWORD}
copyright: "2011-${YEAR}, 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
]"

View File

@@ -0,0 +1,145 @@
note
description: "Summary description for {ROUTED_APPLICATION_HELPER}."
author: ""
date: "$Date$"
revision: "$Revision$"
class
ROUTED_APPLICATION_HELPER
inherit
ANY
HTTP_FORMAT_CONSTANTS
export
{NONE} all
end
feature -- Helper
execute_content_type_not_allowed (req: EWSGI_REQUEST; res: EWSGI_RESPONSE_BUFFER; a_content_types: detachable ARRAY [STRING]; a_uri_formats: detachable ARRAY [STRING])
local
s, uri_s: detachable STRING
i, n: INTEGER
h: GW_HEADER
do
create h.make
h.put_status ({HTTP_STATUS_CODE}.unsupported_media_type)
h.put_content_type_text_plain
if a_content_types /= Void then
create s.make (10)
from
i := a_content_types.lower
n := a_content_types.upper
until
i > n
loop
s.append_string (a_content_types[i])
if i < n then
s.append_character (',')
s.append_character (' ')
end
i := i + 1
end
h.put_header_key_value ("Accept", s)
end
if a_uri_formats /= Void then
create uri_s.make (10)
from
i := a_uri_formats.lower
n := a_uri_formats.upper
until
i > n
loop
uri_s.append_string (a_uri_formats[i])
if i < n then
uri_s.append_character (',')
uri_s.append_character (' ')
end
i := i + 1
end
end
if s /= Void then
res.write_string ("Unsupported request content-type, Accept: " + s + "%N")
end
if uri_s /= Void then
res.write_string ("Unsupported request format from the URI: " + uri_s + "%N")
end
end
execute_method_not_allowed (req: EWSGI_REQUEST; res: EWSGI_RESPONSE_BUFFER; a_methods: ARRAY [STRING])
local
s: STRING
i, n: INTEGER
do
create s.make (10)
from
i := a_methods.lower
n := a_methods.upper
until
i > n
loop
s.append_string (a_methods[i])
if i < n then
s.append_character (',')
s.append_character (' ')
end
i := i + 1
end
res.write_header ({HTTP_STATUS_CODE}.method_not_allowed, <<
["Content-Type", {HTTP_CONSTANTS}.plain_text],
["Allow", s]
>>)
res.write_string ("Unsupported request method, Allow: " + s + "%N")
end
feature -- Context helper
request_format_id (ctx: REQUEST_HANDLER_CONTEXT; a_format_variable_name: detachable STRING; content_type_supported: detachable ARRAY [STRING]): INTEGER
-- Format id for the request based on {HTTP_FORMAT_CONSTANTS}
local
l_format: detachable STRING_8
do
if a_format_variable_name /= Void and then attached ctx.parameter (a_format_variable_name) as ctx_format then
l_format := ctx_format.as_string_8
else
l_format := content_type_to_request_format (ctx.request_content_type (content_type_supported))
end
if l_format /= Void then
Result := format_id (l_format)
else
Result := 0
end
end
content_type_to_request_format (a_content_type: detachable STRING): detachable STRING
-- `a_content_type' converted into a request format name
do
if a_content_type /= Void then
if a_content_type.same_string ({HTTP_CONSTANTS}.json_text) then
Result := {HTTP_FORMAT_CONSTANTS}.json_name
elseif a_content_type.same_string ({HTTP_CONSTANTS}.json_app) then
Result := {HTTP_FORMAT_CONSTANTS}.json_name
elseif a_content_type.same_string ({HTTP_CONSTANTS}.xml_text) then
Result := {HTTP_FORMAT_CONSTANTS}.xml_name
elseif a_content_type.same_string ({HTTP_CONSTANTS}.html_text) then
Result := {HTTP_FORMAT_CONSTANTS}.html_name
elseif a_content_type.same_string ({HTTP_CONSTANTS}.plain_text) then
Result := {HTTP_FORMAT_CONSTANTS}.text_name
end
end
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

@@ -0,0 +1,183 @@
note
description : "Objects that ..."
author : "$Author$"
date : "$Date$"
revision : "$Revision$"
class
HELLO_ROUTED_WORLD
inherit
ANY
ROUTED_APPLICATION
ROUTED_APPLICATION_HELPER
DEFAULT_EWSGI_APPLICATION
create
make
feature {NONE} -- Initialization
make
do
initialize_router
make_and_launch
end
create_router
do
-- create {REQUEST_URI_ROUTER} router.make (5)
create {REQUEST_URI_TEMPLATE_ROUTER} router.make (5)
end
setup_router
local
ra: REQUEST_AGENT_HANDLER
do
router.map_agent ("/home", agent execute_home)
create ra.make (agent handle_hello)
router.map ("/hello/{name}.{format}", ra)
router.map ("/hello.{format}/{name}", ra)
router.map ("/hello/{name}", ra)
create ra.make (agent handle_anonymous_hello)
router.map ("/hello", ra)
router.map ("/hello.{format}", ra)
end
feature -- Execution
execute_default (req: EWSGI_REQUEST; res: EWSGI_RESPONSE_BUFFER)
local
h: GW_HEADER
l_url: STRING
e: EXECUTION_ENVIRONMENT
n: INTEGER
i: INTEGER
do
create h.make
l_url := req.script_url ("/home")
n := 3
h.put_refresh (l_url, 5, 200)
res.set_status_code (200)
res.write_headers_string (h.string)
from
create e
until
n = 0
loop
if n > 1 then
res.write_string ("Redirected to " + l_url + " in " + n.out + " seconds :%N")
else
res.write_string ("Redirected to " + l_url + " in 1 second :%N")
end
res.flush
from
i := 1
until
i = 1001
loop
res.write_string (".")
if i \\ 100 = 0 then
res.write_string ("%N")
end
res.flush
e.sleep (1_000_000)
i := i + 1
end
res.write_string ("%N")
n := n - 1
end
res.write_string ("You are now being redirected...%N")
res.flush
end
execute_home (ctx: REQUEST_HANDLER_CONTEXT; req: EWSGI_REQUEST; res: EWSGI_RESPONSE_BUFFER)
do
res.write_header (200, <<["Content-Type", "text/html"]>>)
res.write_string ("<html><body>Hello World ?!%N")
res.write_string ("<h3>Please try the following links</h3><ul>%N")
res.write_string ("<li><a href=%""+ req.script_url ("/") + "%">default</a></li>%N")
res.write_string ("<li><a href=%""+ req.script_url ("/hello") + "%">/hello</a></li>%N")
res.write_string ("<li><a href=%""+ req.script_url ("/hello.html/Joce") + "%">/hello.html/Joce</a></li>%N")
res.write_string ("<li><a href=%""+ req.script_url ("/hello.json/Joce") + "%">/hello.json/Joce</a></li>%N")
res.write_string ("<li><a href=%""+ req.script_url ("/hello/Joce.html") + "%">/hello/Joce.html</a></li>%N")
res.write_string ("<li><a href=%""+ req.script_url ("/hello/Joce.xml") + "%">/hello/Joce.xml</a></li>%N")
res.write_string ("<li><a href=%""+ req.script_url ("/hello/Joce") + "%">/hello/Joce</a></li>%N")
res.write_string ("</ul>%N")
if attached req.environment_variable ("REQUEST_COUNT") as rqc then
res.write_string ("request #"+ rqc + "%N")
end
res.write_string ("</body></html>%N")
end
execute_hello (req: EWSGI_REQUEST; res: EWSGI_RESPONSE_BUFFER; a_name: detachable STRING_32; ctx: REQUEST_HANDLER_CONTEXT)
local
l_response_content_type: detachable STRING
msg: STRING
h: GW_HEADER
content_type_supported: ARRAY [STRING]
do
if a_name /= Void then
msg := "Hello %"" + a_name + "%" !%N"
else
msg := "Hello anonymous visitor !%N"
end
content_type_supported := <<{HTTP_CONSTANTS}.json_app, {HTTP_CONSTANTS}.html_text, {HTTP_CONSTANTS}.xml_text, {HTTP_CONSTANTS}.plain_text>>
inspect request_format_id (ctx, "format", content_type_supported)
when {HTTP_FORMAT_CONSTANTS}.json then
l_response_content_type := {HTTP_CONSTANTS}.json_app
msg := "{%N%"application%": %"/hello%",%N %"message%": %"" + msg + "%" %N}"
when {HTTP_FORMAT_CONSTANTS}.html then
l_response_content_type := {HTTP_CONSTANTS}.html_text
when {HTTP_FORMAT_CONSTANTS}.xml then
l_response_content_type := {HTTP_CONSTANTS}.xml_text
msg := "<response><application>/hello</application><message>" + msg + "</message></response>%N"
when {HTTP_FORMAT_CONSTANTS}.text then
l_response_content_type := {HTTP_CONSTANTS}.plain_text
else
execute_content_type_not_allowed (req, res, content_type_supported,
<<{HTTP_FORMAT_CONSTANTS}.json_name, {HTTP_FORMAT_CONSTANTS}.html_name, {HTTP_FORMAT_CONSTANTS}.xml_name, {HTTP_FORMAT_CONSTANTS}.text_name>>
)
end
if l_response_content_type /= Void then
create h.make
h.put_status (200)
h.put_content_type (l_response_content_type)
h.put_content_length (msg.count)
res.set_status_code (200)
res.write_string (h.string)
-- res.write_header (200, <<
-- ["Content-Type", l_response_content_type],
-- ["Content-Length", msg.count.out
-- >>)
res.write_string (msg)
end
end
handle_hello (ctx: REQUEST_HANDLER_CONTEXT; req: EWSGI_REQUEST; res: EWSGI_RESPONSE_BUFFER)
do
execute_hello (req, res, Void, ctx)
end
handle_anonymous_hello (ctx: REQUEST_HANDLER_CONTEXT; req: EWSGI_REQUEST; res: EWSGI_RESPONSE_BUFFER)
do
execute_hello (req, res, ctx.parameter ("name"), ctx)
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

@@ -8,6 +8,11 @@ note
class class
ERROR_HANDLER ERROR_HANDLER
inherit
ANY
DEBUG_OUTPUT
create create
make make
@@ -35,6 +40,18 @@ feature -- Status
errors: LIST [ERROR] errors: LIST [ERROR]
-- Errors container -- Errors container
feature -- Status report
debug_output: STRING
-- String that should be displayed in debugger to represent `Current'.
do
if has_error then
Result := count.out + " errors"
else
Result := "no error"
end
end
feature -- Basic operation feature -- Basic operation
add_error (a_error: ERROR) add_error (a_error: ERROR)
@@ -52,6 +69,12 @@ feature -- Basic operation
add_error (e) add_error (e)
end end
append (a_err_handler: ERROR_HANDLER)
-- Append errors from `a_err_handler'
do
errors.append (a_err_handler.errors)
end
feature -- Access feature -- Access
as_single_error: detachable ERROR as_single_error: detachable ERROR
@@ -61,6 +84,20 @@ feature -- Access
elseif count > 0 then elseif count > 0 then
Result := errors.first Result := errors.first
end end
ensure
has_error_implies_result_attached: has_error implies Result /= Void
end
as_string_representation: STRING
require
has_error
do
if attached as_single_error as e then
Result := e.string_representation
else
check has_error: False end
Result := "Error occured"
end
end end
feature -- Element changes feature -- Element changes

View File

@@ -153,6 +153,8 @@ feature -- Builder
feature -- Match feature -- Match
match (a_uri: STRING): detachable URI_TEMPLATE_MATCH_RESULT match (a_uri: STRING): detachable URI_TEMPLATE_MATCH_RESULT
require
is_valid: is_valid
local local
b: BOOLEAN b: BOOLEAN
tpl: like template tpl: like template
@@ -163,6 +165,8 @@ feature -- Match
vv: STRING vv: STRING
l_vars, l_path_vars, l_query_vars: HASH_TABLE [STRING, STRING] l_vars, l_path_vars, l_query_vars: HASH_TABLE [STRING, STRING]
l_uri_count: INTEGER l_uri_count: INTEGER
tpl_count: INTEGER
l_next_literal_separator: detachable STRING
do do
--| Extract expansion parts "\\{([^\\}]*)\\}" --| Extract expansion parts "\\{([^\\}]*)\\}"
analyze analyze
@@ -173,8 +177,10 @@ feature -- Match
b := True b := True
l_uri_count := a_uri.count l_uri_count := a_uri.count
tpl := template tpl := template
tpl_count := tpl.count
if l_expressions.is_empty then if l_expressions.is_empty then
b := a_uri.substring (1, tpl.count).same_string (tpl) -- b := a_uri.substring (1, tpl_count).same_string (tpl)
b := a_uri.same_string (tpl)
else else
from from
l_expressions.start l_expressions.start
@@ -200,6 +206,8 @@ feature -- Match
b := s.same_string (t) b := s.same_string (t)
p := exp.end_position p := exp.end_position
end end
l_expressions.forth --| we forth `l_expressions' so be careful
--| Check related variable --| Check related variable
if b and then not vn.is_empty then if b and then not vn.is_empty then
if exp.is_query then if exp.is_query then
@@ -211,10 +219,21 @@ feature -- Match
inspect vn[1] inspect vn[1]
when '?' then when '?' then
import_form_style_parameters_into (a_uri.substring (q + l_offset + 1, l_uri_count), l_vars) import_form_style_parameters_into (a_uri.substring (q + l_offset + 1, l_uri_count), l_vars)
p := tpl_count + 1
l_offset := l_offset + (l_uri_count - (q + l_offset + 1))
when ';' then when ';' then
import_path_style_parameters_into (a_uri.substring (q + l_offset, l_uri_count), l_vars) import_path_style_parameters_into (a_uri.substring (q + l_offset, l_uri_count), l_vars)
p := tpl_count + 1
else else
vv := next_path_variable_value (a_uri, q + l_offset) if not l_expressions.after then
exp := l_expressions.item --| We change `exp' here
l_next_literal_separator := tpl.substring (p, exp.position -1)
elseif p < tpl_count then
l_next_literal_separator := tpl.substring (p, tpl_count)
else
l_next_literal_separator := Void
end
vv := next_path_variable_value (a_uri, q + l_offset, l_next_literal_separator)
l_vars.force (vv, vn) l_vars.force (vv, vn)
l_offset := l_offset + vv.count - (vn.count + 2) l_offset := l_offset + vv.count - (vn.count + 2)
end end
@@ -222,7 +241,17 @@ feature -- Match
b := exp.is_query --| query are optional b := exp.is_query --| query are optional
end end
end end
l_expressions.forth if b and l_expressions.after then
if
(p < tpl_count) or
(p + l_offset < l_uri_count)
then
--| Remaining literal part
t := tpl.substring (p, tpl_count)
s := a_uri.substring (p + l_offset, l_uri_count)
b := s.same_string (t)
end
end
end end
end end
if b then if b then
@@ -231,8 +260,36 @@ feature -- Match
end end
end end
feature -- Basic operation
parse
-- Parse template
do
reset
analyze
end
feature -- Status report
is_valid: BOOLEAN
-- Is Current URI template valid?
do
analyze
Result := not has_syntax_error
end
feature {NONE} -- Internal Access feature {NONE} -- Internal Access
reset
do
expressions := Void
has_syntax_error := False
end
has_syntax_error: BOOLEAN
-- Has syntax error
--| Make sense only if `analyze' was processed before
expressions: detachable LIST [URI_TEMPLATE_EXPRESSION] expressions: detachable LIST [URI_TEMPLATE_EXPRESSION]
-- Expansion parts -- Expansion parts
@@ -248,6 +305,7 @@ feature {NONE} -- Implementation
in_query: BOOLEAN in_query: BOOLEAN
x: STRING x: STRING
exp: URI_TEMPLATE_EXPRESSION exp: URI_TEMPLATE_EXPRESSION
l_has_query_expression: BOOLEAN
do do
l_expressions := expressions l_expressions := expressions
if l_expressions = Void then if l_expressions = Void then
@@ -258,6 +316,7 @@ feature {NONE} -- Implementation
from from
i := 1 i := 1
n := tpl.count n := tpl.count
l_has_query_expression := False
create x.make_empty create x.make_empty
until until
i > n i > n
@@ -269,6 +328,10 @@ feature {NONE} -- Implementation
l_expressions.force (exp) l_expressions.force (exp)
x.wipe_out x.wipe_out
in_x := False in_x := False
if l_has_query_expression and then i < n then
--| Remaining text after {?exp}
has_syntax_error := True
end
else else
x.extend (c) x.extend (c)
end end
@@ -278,8 +341,11 @@ feature {NONE} -- Implementation
check x_is_empty: x.is_empty end check x_is_empty: x.is_empty end
p := i p := i
in_x := True in_x := True
if not l_has_query_expression then
l_has_query_expression := tpl.valid_index (i+1) and then tpl[i+1] = '?'
end
if not in_query then if not in_query then
in_query := tpl.valid_index (i+1) and then tpl[i+1] = '?' in_query := l_has_query_expression
end end
when '?' then when '?' then
in_query := True in_query := True
@@ -344,23 +410,39 @@ feature {NONE} -- Implementation
end end
end end
next_path_variable_value (a_uri: STRING; a_index: INTEGER): STRING next_path_variable_value (a_uri: STRING; a_index: INTEGER; a_end_token: detachable STRING): STRING
require require
valid_index: a_index <= a_uri.count valid_index: a_index <= a_uri.count
local local
c: CHARACTER
i,n,p: INTEGER i,n,p: INTEGER
l_end_token_first_char: CHARACTER
l_end_token_count: INTEGER
do do
from from
if a_end_token /= Void and then not a_end_token.is_empty then
l_end_token_first_char := a_end_token.item (1)
l_end_token_count := a_end_token.count
end
i := a_index i := a_index
n := a_uri.count n := a_uri.count
until until
i > n i > n
loop loop
inspect a_uri[i] c := a_uri[i]
inspect c
when '/', '?' then when '/', '?' then
i := n i := n
else else
p := i if
a_end_token /= Void and then
c = l_end_token_first_char and then
a_uri.substring (i, i + l_end_token_count - 1).same_string (a_end_token)
then
i := n
else
p := i
end
end end
i := i + 1 i := i + 1
end end

View File

@@ -11,7 +11,8 @@ inherit
ANY ANY
DEBUG_OUTPUT DEBUG_OUTPUT
export {NONE} all end
URI_TEMPLATE_CONSTANTS URI_TEMPLATE_CONSTANTS
export {NONE} all end export {NONE} all end

View File

@@ -24,22 +24,75 @@ feature {NONE} -- Initialization
make (create {like path_variables}.make (0), create {like query_variables}.make (0)) make (create {like path_variables}.make (0), create {like query_variables}.make (0))
end end
feature -- Access
variable (n: STRING): detachable STRING feature -- Access
-- Value related to variable name `n'
do
Result := query_variables.item (n)
if Result = Void then
Result := path_variables.item (n)
end
end
path_variables: HASH_TABLE [STRING, STRING] path_variables: HASH_TABLE [STRING, STRING]
-- Variables being part of the path segments -- Variables being part of the path segments
query_variables: HASH_TABLE [STRING, STRING] query_variables: HASH_TABLE [STRING, STRING]
-- Variables being part of the query segments (i.e: after the ? ) -- Variables being part of the query segments (i.e: after the ?)
feature -- Query
path_variable (n: STRING): detachable STRING
-- Value related to query variable name `n'
do
Result := path_variables.item (n)
end
query_variable (n: STRING): detachable STRING
-- Value related to path variable name `n'
do
Result := query_variables.item (n)
end
variable (n: STRING): detachable STRING
-- Value related to variable name `n'
do
Result := query_variable (n)
if Result = Void then
Result := path_variable (n)
end
end
feature -- Query: url-decoded
url_decoded_query_variable (n: STRING): detachable STRING_32
-- Unencoded value related to variable name `n'
do
if attached query_variable (n) as v then
Result := url_decoded_string (v)
end
end
url_decoded_path_variable (n: STRING): detachable STRING_32
-- Unencoded value related to variable name `n'
do
if attached path_variable (n) as v then
Result := url_decoded_string (v)
end
end
url_decoded_variable (n: STRING): detachable STRING_32
-- Unencoded value related to variable name `n'
do
if attached variable (n) as v then
Result := url_decoded_string (v)
end
end
feature {NONE} -- Implementation
url_decoded_string (s: READABLE_STRING_GENERAL): STRING_32
do
Result := url_encoder.decoded_string (s.as_string_8)
end
url_encoder: URL_ENCODER
once
create Result
end
;note ;note
copyright: "2011-2011, Eiffel Software and others" copyright: "2011-2011, Eiffel Software and others"

View File

@@ -21,6 +21,9 @@ feature -- Test routines
do do
uri_template_parse ("api/foo/{foo_id}/{?id,extra}", <<"foo_id">>, <<"id", "extra">>) uri_template_parse ("api/foo/{foo_id}/{?id,extra}", <<"foo_id">>, <<"id", "extra">>)
uri_template_parse ("weather/{state}/{city}?forecast={day}", <<"state", "city">>, <<"day">>) uri_template_parse ("weather/{state}/{city}?forecast={day}", <<"state", "city">>, <<"day">>)
uri_template_parse ("/hello/{name}.{format}", <<"name", "format">>, <<>>)
uri_template_parse ("/hello.{format}/{name}", <<"format", "name">>, <<>>)
uri_template_parse ("/hello/Joce.{format}/foo{?foobar};crazy=IDEA", <<"name">>, <<"foobar">>)
end end
test_uri_template_matcher test_uri_template_matcher
@@ -49,6 +52,32 @@ feature -- Test routines
create tpl.make ("weather/{state}/{city}?forecast={day}") create tpl.make ("weather/{state}/{city}?forecast={day}")
uri_template_match (tpl, "weather/California/Goleta?forecast=today", <<["state", "California"], ["city", "Goleta"]>>, <<["day", "today"]>>) uri_template_match (tpl, "weather/California/Goleta?forecast=today", <<["state", "California"], ["city", "Goleta"]>>, <<["day", "today"]>>)
create tpl.make ("/hello")
uri_template_match (tpl, "/hello", <<>>, <<>>)
uri_template_mismatch (tpl, "/hello/Foo2") -- longer
uri_template_mismatch (tpl, "/hell") -- shorter
create tpl.make ("/hello.{format}")
uri_template_match (tpl, "/hello.xml", <<["format", "xml"]>>, <<>>)
uri_template_mismatch (tpl, "/hello.xml/Bar")
create tpl.make ("/hello.{format}/{name}")
uri_template_match (tpl, "/hello.xml/Joce", <<["format", "xml"], ["name", "Joce"]>>, <<>>)
create tpl.make ("/hello/{name}.{format}")
uri_template_match (tpl, "/hello/Joce.json", <<["name", "Joce"], ["format", "json"]>>, <<>>)
create tpl.make ("/hello/{name}.{format}/foo")
uri_template_match (tpl, "/hello/Joce.xml/foo", <<["name", "Joce"], ["format", "xml"]>>, <<>>)
uri_template_mismatch (tpl, "/hello/Joce.xml/fooBAR")
create tpl.make ("/hello/{name}.{format}/foo{?foo};crazy={idea}")
-- uri_template_match (tpl, "/hello/Joce.xml/foo", <<["name", "Joce"], ["format", "xml"]>>, <<>>)
uri_template_match (tpl, "/hello/Joce.xml/foo?foo=FOO", <<["name", "Joce"], ["format", "xml"]>>, <<["foo", "FOO"]>>)
uri_template_match (tpl, "/hello/Joce.xml/foo;crazy=IDEA", <<["name", "Joce"], ["format", "xml"]>>, <<["idea", "IDEA"], ["crazy", "IDEA"]>>)
end end
uri_template_string_errors: detachable LIST [STRING] uri_template_string_errors: detachable LIST [STRING]
@@ -544,6 +573,8 @@ feature -- Test routines
i: INTEGER i: INTEGER
do do
create u.make (s) create u.make (s)
u.parse
assert ("Template %""+ s +"%" is valid", u.is_valid)
if attached u.path_variable_names as vars then if attached u.path_variable_names as vars then
matched := vars.count = path_vars.count matched := vars.count = path_vars.count
from from
@@ -559,7 +590,7 @@ feature -- Test routines
else else
matched := path_vars.is_empty matched := path_vars.is_empty
end end
assert ("path variables matched", matched) assert ("path variables matched for %""+ s +"%"", matched)
if attached u.query_variable_names as vars then if attached u.query_variable_names as vars then
matched := vars.count = query_vars.count matched := vars.count = query_vars.count
@@ -576,7 +607,7 @@ feature -- Test routines
else else
matched := query_vars.is_empty matched := query_vars.is_empty
end end
assert ("query variables matched", matched) assert ("query variables matched %""+ s +"%"", matched)
end end
uri_template_mismatch (a_uri_template: URI_TEMPLATE; a_uri: STRING) uri_template_mismatch (a_uri_template: URI_TEMPLATE; a_uri: STRING)

View File

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

View File

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

View File

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

View File

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

View File

@@ -10,7 +10,7 @@
<option warning="true" full_class_checking="true" is_attached_by_default="true" void_safety="all"> <option warning="true" full_class_checking="true" is_attached_by_default="true" void_safety="all">
</option> </option>
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/> <library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
<library name="ewsgi_spec" location="..\..\ewsgi_specification-safe.ecf" readonly="false"/> <library name="ewsgi" location="..\..\ewsgi-safe.ecf" readonly="false"/>
<library name="http" location="..\..\..\..\protocol\http\http-safe.ecf"/> <library name="http" location="..\..\..\..\protocol\http\http-safe.ecf"/>
<library name="nino" location="..\..\..\..\..\ext\server\nino\nino-safe.ecf" readonly="false"> <library name="nino" location="..\..\..\..\..\ext\server\nino\nino-safe.ecf" readonly="false">
<renaming old_name="HTTP_CONSTANTS" new_name="NINO_HTTP_CONSTANTS"/> <renaming old_name="HTTP_CONSTANTS" new_name="NINO_HTTP_CONSTANTS"/>

View File

@@ -10,7 +10,7 @@
<option warning="true" full_class_checking="true"> <option warning="true" full_class_checking="true">
</option> </option>
<library name="base" location="$ISE_LIBRARY\library\base\base.ecf"/> <library name="base" location="$ISE_LIBRARY\library\base\base.ecf"/>
<library name="ewsgi_spec" location="..\..\ewsgi_specification.ecf" readonly="false"/> <library name="ewsgi" location="..\..\ewsgi.ecf" readonly="false"/>
<library name="http" location="..\..\..\..\protocol\http\http.ecf"/> <library name="http" location="..\..\..\..\protocol\http\http.ecf"/>
<library name="nino" location="..\..\..\..\..\ext\server\nino\nino.ecf" readonly="false"> <library name="nino" location="..\..\..\..\..\ext\server\nino\nino.ecf" readonly="false">
<renaming old_name="HTTP_CONSTANTS" new_name="NINO_HTTP_CONSTANTS"/> <renaming old_name="HTTP_CONSTANTS" new_name="NINO_HTTP_CONSTANTS"/>

View File

@@ -7,7 +7,7 @@ deferred class
DEFAULT_EWSGI_APPLICATION DEFAULT_EWSGI_APPLICATION
inherit inherit
GW_APPLICATION_IMP EWSGI_APPLICATION
feature {NONE} -- Initialization feature {NONE} -- Initialization

View File

@@ -11,7 +11,6 @@
</option> </option>
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/> <library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
<library name="time" location="$ISE_LIBRARY\library\time\time-safe.ecf"/> <library name="time" location="$ISE_LIBRARY\library\time\time-safe.ecf"/>
<library name="ewsgi_spec" location="../ewsgi_specification-safe.ecf"/>
<library name="ewsgi" location="../ewsgi-safe.ecf"/> <library name="ewsgi" location="../ewsgi-safe.ecf"/>
<library name="connector_cgi" location="../connectors/cgi/cgi-safe.ecf"/> <library name="connector_cgi" location="../connectors/cgi/cgi-safe.ecf"/>
<library name="error" location="..\..\..\error\error-safe.ecf"/> <library name="error" location="..\..\..\error\error-safe.ecf"/>

View File

@@ -0,0 +1,21 @@
<?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="ewsgi_cgi" uuid="82D0E5BA-3EBD-4E0F-94D1-776375158DAA" library_target="ewsgi_cgi">
<target name="ewsgi_cgi">
<root all_classes="true"/>
<file_rule>
<exclude>/EIFGENs$</exclude>
<exclude>/\.git$</exclude>
<exclude>/\.svn$</exclude>
</file_rule>
<option warning="true" full_class_checking="true" syntax="provisional">
</option>
<library name="base" location="$ISE_LIBRARY\library\base\base.ecf"/>
<library name="time" location="$ISE_LIBRARY\library\time\time.ecf"/>
<library name="ewsgi" location="../ewsgi.ecf"/>
<library name="connector_cgi" location="../connectors/cgi/cgi.ecf"/>
<library name="error" location="..\..\..\error\error.ecf"/>
<library name="http" location="..\..\..\protocol\http\http.ecf"/>
<library name="encoder" location="..\..\..\text\encoder\encoder.ecf" readonly="false"/>
<cluster name="default_cgi" location="./cgi" recursive="true"/>
</target>
</system>

View File

@@ -11,7 +11,6 @@
</option> </option>
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/> <library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
<library name="time" location="$ISE_LIBRARY\library\time\time-safe.ecf"/> <library name="time" location="$ISE_LIBRARY\library\time\time-safe.ecf"/>
<library name="ewsgi_spec" location="../ewsgi_specification-safe.ecf"/>
<library name="ewsgi" location="../ewsgi-safe.ecf"/> <library name="ewsgi" location="../ewsgi-safe.ecf"/>
<library name="connector_nino" location="../connectors/nino/nino-safe.ecf"/> <library name="connector_nino" location="../connectors/nino/nino-safe.ecf"/>
<library name="nino" location="..\..\..\..\ext\server\nino\nino-safe.ecf" readonly="false"> <library name="nino" location="..\..\..\..\ext\server\nino\nino-safe.ecf" readonly="false">

View File

@@ -0,0 +1,25 @@
<?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="ewsgi_nino" uuid="11D70618-3D82-4C5B-9501-8833DD04A3D6" library_target="ewsgi_nino">
<target name="ewsgi_nino">
<root all_classes="true"/>
<file_rule>
<exclude>/EIFGENs$</exclude>
<exclude>/\.git$</exclude>
<exclude>/\.svn$</exclude>
</file_rule>
<option warning="true" full_class_checking="true" syntax="provisional">
</option>
<library name="base" location="$ISE_LIBRARY\library\base\base.ecf"/>
<library name="time" location="$ISE_LIBRARY\library\time\time.ecf"/>
<library name="ewsgi" location="../ewsgi.ecf"/>
<library name="connector_nino" location="../connectors/nino/nino.ecf"/>
<library name="nino" location="..\..\..\..\ext\server\nino\nino.ecf" readonly="false">
<renaming old_name="HTTP_CONSTANTS" new_name="NINO_HTTP_CONSTANTS"/>
</library>
<library name="error" location="..\..\..\error\error.ecf"/>
<library name="http" location="..\..\..\protocol\http\http.ecf"/>
<library name="encoder" location="..\..\..\text\encoder\encoder.ecf" readonly="false"/>
<cluster name="default_nino" location="./nino" recursive="true"/>
</target>
</system>

View File

@@ -7,18 +7,21 @@ deferred class
DEFAULT_EWSGI_APPLICATION DEFAULT_EWSGI_APPLICATION
inherit inherit
GW_APPLICATION_IMP EWSGI_APPLICATION
feature {NONE} -- Initialization feature {NONE} -- Initialization
make_and_launch make_and_launch
do do
print ("Example: start a Nino web server on port " + port_number.out + ", %Nand reply Hello World for any request such as http://localhost:8123/%N") port_number := 80
print ("Example: start a Nino web server on port " + port_number.out + ", %Nand reply Hello World for any request such as http://localhost:" + port_number.out + "/%N")
(create {NINO_APPLICATION}.make_custom (agent execute, "")).listen (port_number) (create {NINO_APPLICATION}.make_custom (agent execute, "")).listen (port_number)
end end
port_number: INTEGER = 80 port_number: INTEGER
invariant
port_number_valid: port_number > 0
note note
copyright: "2011-2011, Eiffel Software and others" copyright: "2011-2011, 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)"

View File

@@ -12,16 +12,16 @@ create
feature {NONE} -- Implementation feature {NONE} -- Implementation
make (a_callback: like {GW_AGENT_APPLICATION}.callback) make (a_callback: like {EWSGI_AGENT_APPLICATION}.callback)
-- Initialize `Current'. -- Initialize `Current'.
do do
make_custom (a_callback, Void) make_custom (a_callback, Void)
end end
make_custom (a_callback: like {GW_AGENT_APPLICATION}.callback; a_base_url: detachable STRING) make_custom (a_callback: like {EWSGI_AGENT_APPLICATION}.callback; a_base_url: detachable STRING)
-- Initialize `Current'. -- Initialize `Current'.
local local
app: GW_AGENT_APPLICATION app: EWSGI_AGENT_APPLICATION
do do
create app.make (a_callback) create app.make (a_callback)
create connector.make_with_base (app, a_base_url) create connector.make_with_base (app, a_base_url)

View File

@@ -1,25 +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="ewsgi-full" uuid="D924DBE1-1231-434A-80EF-234BA09D1E30" library_target="ewsgi-full">
<target name="ewsgi-full">
<root all_classes="true"/>
<file_rule>
<exclude>/EIFGENs$</exclude>
<exclude>/\.git$</exclude>
<exclude>/\.svn$</exclude>
</file_rule>
<option warning="true" full_class_checking="true" is_attached_by_default="false" void_safety="none" syntax="provisional">
</option>
<library name="base" location="$ISE_LIBRARY\library\base\base.ecf"/>
<library name="ewsgi_spec" location="ewsgi_specification.ecf"/>
<library name="error" location="..\..\error\error.ecf"/>
<library name="http" location="..\..\protocol\http\http.ecf"/>
<library name="encoder" location="..\..\text\encoder\encoder.ecf" readonly="false"/>
<library name="libfcgi" location="..\libfcgi\libfcgi.ecf"/>
<library name="nino" location="..\..\..\ext\server\nino\nino.ecf" readonly="false">
<renaming old_name="HTTP_CONSTANTS" new_name="NINO_HTTP_CONSTANTS"/>
</library>
<library name="time" location="$ISE_LIBRARY\library\time\time.ecf"/>
<cluster name="connectors" location="connectors\" recursive="true"/>
<cluster name="interface" location="src\" recursive="true"/>
</target>
</system>

View File

@@ -11,7 +11,6 @@
</option> </option>
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/> <library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
<library name="time" location="$ISE_LIBRARY\library\time\time-safe.ecf"/> <library name="time" location="$ISE_LIBRARY\library\time\time-safe.ecf"/>
<library name="ewsgi_spec" location="ewsgi_specification-safe.ecf"/>
<library name="error" location="..\..\error\error-safe.ecf"/> <library name="error" location="..\..\error\error-safe.ecf"/>
<library name="http" location="..\..\protocol\http\http-safe.ecf"/> <library name="http" location="..\..\protocol\http\http-safe.ecf"/>
<library name="encoder" location="..\..\text\encoder\encoder-safe.ecf" readonly="false"/> <library name="encoder" location="..\..\text\encoder\encoder-safe.ecf" readonly="false"/>

View File

@@ -11,7 +11,6 @@
</option> </option>
<library name="base" location="$ISE_LIBRARY\library\base\base.ecf"/> <library name="base" location="$ISE_LIBRARY\library\base\base.ecf"/>
<library name="time" location="$ISE_LIBRARY\library\time\time.ecf"/> <library name="time" location="$ISE_LIBRARY\library\time\time.ecf"/>
<library name="ewsgi_spec" location="ewsgi_specification.ecf"/>
<library name="error" location="..\..\error\error.ecf"/> <library name="error" location="..\..\error\error.ecf"/>
<library name="http" location="..\..\protocol\http\http.ecf"/> <library name="http" location="..\..\protocol\http\http.ecf"/>
<library name="encoder" location="..\..\text\encoder\encoder.ecf" readonly="false"/> <library name="encoder" location="..\..\text\encoder\encoder.ecf" readonly="false"/>

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="ISO-8859-1"?> <?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="ewsgi_spec" uuid="D6455232-E709-43B3-A2C7-D3E6F6A98288" library_target="ewsgi_spec"> <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="ewsgi_spec" uuid="AA193B9F-02FD-47B9-B60D-C42B9AB35E1C" library_target="ewsgi_spec">
<target name="ewsgi_spec"> <target name="ewsgi_spec">
<root all_classes="true"/> <root all_classes="true"/>
<file_rule> <file_rule>

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="ISO-8859-1"?> <?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="ewsgi_spec" uuid="D6455232-E709-43B3-A2C7-D3E6F6A98288" library_target="ewsgi_spec"> <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="ewsgi_spec" uuid="AA193B9F-02FD-47B9-B60D-C42B9AB35E1C" library_target="ewsgi_spec">
<target name="ewsgi_spec"> <target name="ewsgi_spec">
<root all_classes="true"/> <root all_classes="true"/>
<file_rule> <file_rule>

View File

@@ -13,7 +13,6 @@
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/> <library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
<library name="default_nino" location="..\..\default\ewsgi_nino-safe.ecf" readonly="false"/> <library name="default_nino" location="..\..\default\ewsgi_nino-safe.ecf" readonly="false"/>
<library name="connector_nino" location="..\..\connectors\nino\nino-safe.ecf" readonly="false"/> <library name="connector_nino" location="..\..\connectors\nino\nino-safe.ecf" readonly="false"/>
<library name="ewsgi_spec" location="..\..\ewsgi_specification-safe.ecf" readonly="false"/>
<library name="ewsgi" location="..\..\ewsgi-safe.ecf" readonly="false"/> <library name="ewsgi" location="..\..\ewsgi-safe.ecf" readonly="false"/>
<cluster name="src" location="src\" recursive="true"/> <cluster name="src" location="src\" recursive="true"/>
</target> </target>

View File

@@ -0,0 +1,19 @@
<?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="hello_world" uuid="734385F1-0D17-4B5F-9138-24DC8D4F06C6">
<target name="hello_world">
<root class="HELLO_WORLD" feature="make"/>
<file_rule>
<exclude>/EIFGENs$</exclude>
<exclude>/\.git$</exclude>
<exclude>/\.svn$</exclude>
</file_rule>
<option warning="true" full_class_checking="true" syntax="provisional">
</option>
<setting name="concurrency" value="thread"/>
<library name="base" location="$ISE_LIBRARY\library\base\base.ecf"/>
<library name="default_nino" location="..\..\default\ewsgi_nino.ecf" readonly="false"/>
<library name="connector_nino" location="..\..\connectors\nino\nino.ecf" readonly="false"/>
<library name="ewsgi" location="..\..\ewsgi.ecf" readonly="false"/>
<cluster name="src" location="src\" recursive="true"/>
</target>
</system>

View File

@@ -18,7 +18,7 @@ feature {NONE} -- Initialization
(create {NINO_APPLICATION}.make_custom (agent execute, "")).listen (port_number) (create {NINO_APPLICATION}.make_custom (agent execute, "")).listen (port_number)
end end
execute (req: EWSGI_REQUEST; res: EWSGI_RESPONSE_STREAM) execute (req: EWSGI_REQUEST; res: EWSGI_RESPONSE_BUFFER)
do do
res.write_header (200, <<["Content-Type", "text/plain"]>>) res.write_header (200, <<["Content-Type", "text/plain"]>>)
res.write_string ("Hello World!%N") res.write_string ("Hello World!%N")

View File

@@ -7,23 +7,22 @@
<exclude>/\.svn$</exclude> <exclude>/\.svn$</exclude>
</file_rule> </file_rule>
<option warning="true" full_class_checking="true" is_attached_by_default="true" void_safety="all" syntax="provisional"> <option warning="true" full_class_checking="true" is_attached_by_default="true" void_safety="all" syntax="provisional">
<assertions precondition="true" postcondition="true" invariant="true" supplier_precondition="true"/>
</option> </option>
<setting name="concurrency" value="thread"/> <setting name="concurrency" value="thread"/>
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/> <library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
<library name="ewsgi_spec" location="..\..\ewsgi_specification-safe.ecf" readonly="false"/>
<library name="ewsgi" location="..\..\ewsgi-safe.ecf" readonly="false"/> <library name="ewsgi" location="..\..\ewsgi-safe.ecf" readonly="false"/>
</target> </target>
<target name="hello_nino_world" extends="hello_world"> <target name="hello_nino_world" extends="hello_world">
<root class="HELLO_WORLD" feature="make_and_launch"/> <root class="HELLO_WORLD" feature="make_and_launch"/>
<library name="default_nino" location="..\..\default\ewsgi_nino-safe.ecf" readonly="false"/>
<library name="connector_nino" location="..\..\connectors\nino\nino-safe.ecf" readonly="false"/> <library name="connector_nino" location="..\..\connectors\nino\nino-safe.ecf" readonly="false"/>
<library name="default_nino" location="..\..\default\ewsgi_nino-safe.ecf" readonly="false"/>
<cluster name="src" location="src\" recursive="true"/> <cluster name="src" location="src\" recursive="true"/>
</target> </target>
<target name="hello_cgi_world" extends="hello_world"> <target name="hello_cgi_world" extends="hello_world">
<root class="HELLO_WORLD" feature="make_and_launch"/> <root class="HELLO_WORLD" feature="make_and_launch"/>
<library name="default_cgi" location="..\..\default\ewsgi_cgi-safe.ecf" readonly="false"/>
<library name="connector_cgi" location="..\..\connectors\cgi\cgi-safe.ecf" readonly="false"/> <library name="connector_cgi" location="..\..\connectors\cgi\cgi-safe.ecf" readonly="false"/>
<library name="default_cgi" location="..\..\default\ewsgi_cgi-safe.ecf" readonly="false"/>
<cluster name="src" location="src\" recursive="true"/> <cluster name="src" location="src\" recursive="true"/>
</target> </target>
</system> </system>

View File

@@ -19,7 +19,19 @@ feature -- Response
response (request: EWSGI_REQUEST): EWSGI_RESPONSE response (request: EWSGI_REQUEST): EWSGI_RESPONSE
do do
create {HELLO_WORLD_RESPONSE} Result.make if request.environment.path_info.starts_with ("/streaming/") then
Result := streaming_response (request)
else
create Result.make
Result.set_status (200)
Result.set_header ("Content-Type", "text/html; charset=utf-8")
Result.set_message_body ("<html><body>Hello World</body></html>")
end
end
streaming_response (request: EWSGI_REQUEST): EWSGI_RESPONSE
do
create {HELLO_WORLD_RESPONSE} Result.make
Result.set_status (200) Result.set_status (200)
Result.set_header ("Content-Type", "text/html; charset=utf-8") Result.set_header ("Content-Type", "text/html; charset=utf-8")
end end

View File

@@ -31,23 +31,26 @@ feature {NONE} -- Entity body
local local
i: INTEGER i: INTEGER
do do
if current_hello >= 100000 then if current_hello >= 10000 then
end_of_blocks := True end_of_blocks := True
else else
if current_hello = 0 then if current_hello = 0 then
current_block := "<html><body>%N" current_block := "<html><style>div#status {position: absolute; top: 30%%; left: 40%%; border: red solid 1px; padding: 10px; background-color: #ffcccc;}</style><body>%N"
current_block.append ("<a name=%"top%">Welcome</a><br/><div id=%"status%">In progress</div>")
end end
from from
i := 0 i := 0
until until
i = 10000 i = 1000
loop loop
current_block.append ("Hello World ("+ current_hello.out +","+ i.out +")<br/>%N") current_block.append ("Hello World ("+ current_hello.out +","+ i.out +")<br/>%N")
i := i + 1 i := i + 1
end end
current_hello := current_hello + i current_hello := current_hello + i
if current_hello = 100000 then current_block.append ("<div id=%"status%">In progress - "+ (100 * current_hello // 10000).out +"%%</div>")
current_block.append ("Bye bye..<br/></body></html>") if current_hello = 10000 then
current_block.append ("<a name=%"bottom%">Bye bye..</a><br/><div id=%"status%">Completed - GO TO <a href=%"#bottom%">BOTTOM</a></div></body></html>")
end_of_blocks := True
end end
end end
end end

View File

@@ -1,66 +0,0 @@
note
description: "Summary description for {EWSGI_AGENT_APPLICATION}."
author: ""
date: "$Date$"
revision: "$Revision$"
class
EWSGI_AGENT_APPLICATION
inherit
EWSGI_APPLICATION
create
make
feature {NONE} -- Implementation
make (a_callback: like callback; a_request_creator: like request_creator; a_response_creator: like response_creator)
-- Initialize `Current'.
do
callback := a_callback
request_creator := a_request_creator
response_creator := a_response_creator
end
feature {NONE} -- Implementation
request_creator: FUNCTION [ANY, TUPLE [env: EWSGI_ENVIRONMENT; input: EWSGI_INPUT_STREAM], EWSGI_REQUEST]
response_creator: FUNCTION [ANY, TUPLE [req: EWSGI_REQUEST; output: EWSGI_OUTPUT_STREAM], EWSGI_RESPONSE_STREAM]
callback: PROCEDURE [ANY, TUPLE [req: like new_request; res: like new_response]]
-- Procedure called on `execute'
execute (req: like new_request; res: like new_response)
-- Execute the request
do
callback.call ([req, res])
end
feature -- Factory
new_request (env: EWSGI_ENVIRONMENT; a_input: EWSGI_INPUT_STREAM): EWSGI_REQUEST
do
Result := request_creator.item ([env, a_input])
end
new_response (req: EWSGI_REQUEST; a_output: EWSGI_OUTPUT_STREAM): EWSGI_RESPONSE_STREAM
do
Result := response_creator.item ([req, a_output])
end
invariant
callback_attached: callback /= Void
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

@@ -8,73 +8,19 @@ note
deferred class deferred class
EWSGI_APPLICATION EWSGI_APPLICATION
feature -- Process request
process (env: EWSGI_ENVIRONMENT; a_input: EWSGI_INPUT_STREAM; a_output: EWSGI_OUTPUT_STREAM)
-- Process request with environment `env', and i/o streams `a_input' and `a_output'
local
rescued: BOOLEAN
req: detachable like new_request
res: detachable like new_response
do
if not rescued then
pre_execute (env)
req := new_request (env, a_input)
res := new_response (req, a_output)
execute (req, res)
post_execute (req, res)
else
rescue_execute (req, res, (create {EXCEPTION_MANAGER}).last_exception)
end
if res /= Void then
res.commit (a_output)
end
end
feature {NONE} -- Execution feature {NONE} -- Execution
execute (req: EWSGI_REQUEST; res: EWSGI_RESPONSE_STREAM) execute (req: EWSGI_REQUEST; res: EWSGI_RESPONSE_BUFFER)
-- Execute the request -- Execute the request
-- See `req.input' for input stream -- See `req.input' for input stream
-- `req.environment' for the Gateway environment -- `req.environment' for the Gateway environment
-- and `res.output' for output stream -- and `res' for the output buffer
deferred
end
pre_execute (env: EWSGI_ENVIRONMENT)
-- Operation processed before `execute'
require require
env_attached: env /= Void res_status_unset: not res.status_is_set
do
end
post_execute (req: detachable EWSGI_REQUEST; res: detachable EWSGI_RESPONSE_STREAM)
-- Operation processed after `execute', or after `rescue_execute'
do
end
rescue_execute (req: detachable EWSGI_REQUEST; res: detachable EWSGI_RESPONSE_STREAM; a_exception: detachable EXCEPTION)
-- Operation processed on rescue of `execute'
do
post_execute (req, res)
end
feature -- Factory
new_request (env: EWSGI_ENVIRONMENT; a_input: EWSGI_INPUT_STREAM): EWSGI_REQUEST
-- New Request context based on `env' and `a_input'
--| note: you can redefine this function to create your own
--| descendant of EWSGI_REQUEST , or even to reuse/recycle existing
--| instance of EWSGI_REQUEST
deferred
end
new_response (req: EWSGI_REQUEST; a_output: EWSGI_OUTPUT_STREAM): EWSGI_RESPONSE_STREAM
-- New Response based on `req' and `a_output'
--| note: you can redefine this function to create your own
--| descendant of EWSGI_RESPONSE_STREAM , or even to reuse/recycle existing
--| instance of EWSGI_RESPONSE_STREAM
deferred deferred
ensure
res_status_set: res.status_is_set
res_committed: res.message_committed
end end
note note

View File

@@ -4,85 +4,66 @@ note
Interface for a request environment Interface for a request environment
It includes CGI interface and a few extra values that are usually valuable It includes CGI interface and a few extra values that are usually valuable
See http://ken.coar.org/cgi/draft-coar-cgi-v11-03.txt See http://www.ietf.org/rfc/rfc3875
2.2. Basic Rules 2.2. Basic Rules
The following rules are used throughout this specification to The following rules are used throughout this specification to
describe basic parsing constructs. describe basic parsing constructs.
alpha = lowalpha | hialpha alpha = lowalpha | hialpha
alphanum = alpha | digit lowalpha = a | b | c | d | e | f | g | h |
lowalpha = a | b | c | d | e | f | g | h i | j | k | l | m | n | o | p |
| i | j | k | l | m | n | o | p q | r | s | t | u | v | w | x |
| q | r | s | t | u | v | w | x y | z
| y | z hialpha = A | B | C | D | E | F | G | H |
hialpha = A | B | C | D | E | F | G | H I | J | K | L | M | N | O | P |
| I | J | K | L | M | N | O | P Q | R | S | T | U | V | W | X |
| Q | R | S | T | U | V | W | X Y | Z
| Y | Z digit = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
digit = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 8 | 9
| 8 | 9 alphanum = alpha | digit
hex = digit | A | B | C | D | E | F | a OCTET = <any 8-bit byte>
| b | c | d | e | f CHAR = alpha | digit | separator | ! | # | $ |
escaped = % hex hex %% | & | ' | * | + | - | . | ` |
OCTET = <any 8-bit sequence of data> ^ | _ | { | | | } | ~ | CTL
CHAR = <any US-ASCII character (octets 0 - 127)> CTL = <any control character>
CTL = <any US-ASCII control character SP = <space character>
(octets 0 - 31) and DEL (127)> HT = <horizontal tab character>
CR = <US-ASCII CR, carriage return (13)> NL = <newline>
LF = <US-ASCII LF, linefeed (10)> LWSP = SP | HT | NL
SP = <US-ASCII SP, space (32)> separator = ( | ) | < | > | @ | , | ; | : |
HT = <US-ASCII HT, horizontal tab (9)> \ | " | / | [ | ] | ? | = | { |
NL = CR | LF } | SP | HT
LWSP = SP | HT | NL token = 1*<any CHAR except CTLs or separators>
tspecial = ( | ) | "@" | , | ; | : | \ | " quoted-string = " *qdtext "
| / | [ | ] | ? | < | > | { | } qdtext = <any CHAR except " and CTLs but including LWSP>
| SP | HT | NL TEXT = <any printable character>
token = 1*<any CHAR except CTLs or tspecials>
quoted-string = ( " *qdtext " ) | ( "<" *qatext ">") 2.3. URL Encoding
qdtext = <any CHAR except %" and CTLs but including LWSP>
qatext = <any CHAR except "<", ">" and CTLs but including LWSP> reserved = ; | / | ? | : | @ | & | = | + | $ |
mark = - | _ | . | ! | ~ | * | ' | ( | ) , | [ | ]
unreserved = alphanum | mark
reserved = ; | / | ? | : | @ | & | = | $ | , hex = digit | A | B | C | D | E | F | a | b
uric = reserved | unreserved | escaped | c | d | e | f
escaped = "%%" hex hex
unreserved = alpha | digit | mark
mark = - | _ | . | ! | ~ | * | ' | ( | )
Note that newline (NL) need not be a single character, but can Note that newline (NL) need not be a single character, but can
be a character sequence. be a character sequence.
3.2. The Script-URI 3.2. The Script-URI
The 'Script-URI' is defined as the URI of the resource script-URI = <scheme> "://" <server-name> ":" <server-port>
identified by the metavariables. Often, this URI will be the <script-path> <extra-path> "?" <query-string>
same as the URI requested by the client (the 'Client-URI');
however, it need not be. Instead, it could be a URI invented
by the server, and so it can only be used in the context of
the server and its CGI interface.
The Script-URI has the syntax of generic-RL as defined in
section 2.1 of RFC 1808 [7], with the exception that object
parameters and fragment identifiers are not permitted:
<scheme>://<host><port>/<path>?<query>
The various components of the Script-URI are defined by some where <scheme> is found from SERVER_PROTOCOL, <server-name>,
of the metavariables (see section 4 below); <server-port> and <query-string> are the values of the respective
meta-variables. The SCRIPT_NAME and PATH_INFO values, URL-encoded
script-uri = protocol "://" SERVER_NAME ":" SERVER_PORT enc-script with ";", "=" and "?" reserved, give <script-path> and <extra-path>.
enc-path-info "?" QUERY_STRING
where 'protocol' is obtained from SERVER_PROTOCOL,
'enc-script' is a URL-encoded version of SCRIPT_NAME and
'enc-path-info' is a URL-encoded version of PATH_INFO. See
section 4.6 for more information about the PATH_INFO
metavariable.
Note that the scheme and the protocol are not identical; for
instance, a resource accessed via an SSL mechanism may have a
Client-URI with a scheme of "https" rather than "http".
CGI/1.1 provides no means for the script to reconstruct this,
and therefore the Script-URI includes the base protocol used.
]" ]"
legal: "See notice at end of class." legal: "See notice at end of class."
status: "See notice at end of class." status: "See notice at end of class."
@@ -518,6 +499,12 @@ feature -- Common Gateway Interface - 1.1 8 January 1996
feature -- HTTP_* feature -- HTTP_*
http_accept: detachable STRING
-- Contents of the Accept: header from the current request, if there is one.
-- Example: 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8'
deferred
end
http_accept_charset: detachable STRING http_accept_charset: detachable STRING
-- Contents of the Accept-Charset: header from the current request, if there is one. -- Contents of the Accept-Charset: header from the current request, if there is one.
-- Example: 'iso-8859-1,*,utf-8'. -- Example: 'iso-8859-1,*,utf-8'.

View File

@@ -4,8 +4,6 @@ note
You can create your own descendant of this class to You can create your own descendant of this class to
add/remove specific value or processing add/remove specific value or processing
This object is created by {GW_APPLICATION}.new_request_context
]" ]"
legal: "See notice at end of class." legal: "See notice at end of class."
status: "See notice at end of class." status: "See notice at end of class."

View File

@@ -1,19 +1,38 @@
note note
description: "Summary description for {EWSGI_RESPONSE_STREAM}." description: "Summary description for {EWSGI_RESPONSE_BUFFER}."
author: "" author: ""
date: "$Date$" date: "$Date$"
revision: "$Revision$" revision: "$Revision$"
deferred class deferred class
EWSGI_RESPONSE_STREAM EWSGI_RESPONSE_BUFFER
feature {EWSGI_APPLICATION} -- Commit feature {EWSGI_APPLICATION} -- Commit
commit (a_output_stream: EWSGI_OUTPUT_STREAM) commit
-- Commit the current response -- Commit the current response
deferred deferred
ensure ensure
status_set: is_status_set status_is_set: status_is_set
header_committed: header_committed
message_committed: message_committed
end
feature -- Status report
header_committed: BOOLEAN
-- Header committed?
deferred
end
message_committed: BOOLEAN
-- Message committed?
deferred
end
message_writable: BOOLEAN
-- Can message be written?
deferred
end end
feature {NONE} -- Core output operation feature {NONE} -- Core output operation
@@ -26,7 +45,7 @@ feature {NONE} -- Core output operation
feature -- Status setting feature -- Status setting
is_status_set: BOOLEAN status_is_set: BOOLEAN
-- Is status set? -- Is status set?
deferred deferred
end end
@@ -35,11 +54,12 @@ feature -- Status setting
-- Set response status code -- Set response status code
-- Should be done before sending any data back to the client -- Should be done before sending any data back to the client
require require
status_not_set: not is_status_set status_not_set: not status_is_set
header_not_committed: not header_committed
deferred deferred
ensure ensure
status_code_set: status_code = a_code status_code_set: status_code = a_code
status_set: is_status_set status_set: status_is_set
end end
status_code: INTEGER status_code: INTEGER
@@ -47,33 +67,57 @@ feature -- Status setting
deferred deferred
end end
feature -- Header output operation
write_headers_string (a_headers: STRING)
require
status_set: status_is_set
header_not_committed: not header_committed
deferred
ensure
status_set: status_is_set
header_committed: header_committed
end
write_header (a_status_code: INTEGER; a_headers: detachable ARRAY [TUPLE [key: STRING; value: STRING]])
-- 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
end
feature -- Output operation feature -- Output operation
write_string (s: STRING) write_string (s: STRING)
-- Send the string `s' -- Send the string `s'
require require
status_set: is_status_set message_writable: message_writable
deferred
end
write_substring (s: STRING; a_begin_index, a_end_index: INTEGER)
-- Send the substring `s[a_begin_index:a_end_index]'
require
message_writable: message_writable
deferred deferred
end end
write_file_content (fn: STRING) write_file_content (fn: STRING)
-- Send the content of file `fn' -- Send the content of file `fn'
require require
status_set: is_status_set message_writable: message_writable
deferred deferred
end end
feature -- Header output operation flush
-- Flush if it makes sense
write_header (a_status_code: INTEGER; a_headers: detachable ARRAY [TUPLE [key: STRING; value: STRING]])
-- Send headers with status `a_status', and headers from `a_headers'
require
status_not_set: not is_status_set
deferred deferred
ensure
status_set: is_status_set
end end
note note
copyright: "2011-2011, Eiffel Software and others" copyright: "2011-2011, 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)"

View File

@@ -5,10 +5,10 @@ note
revision: "$Revision$" revision: "$Revision$"
class class
GW_AGENT_APPLICATION EWSGI_AGENT_APPLICATION
inherit inherit
GW_APPLICATION_IMP EWSGI_APPLICATION
create create
make make

View File

@@ -0,0 +1,109 @@
note
description: "[
EWSGI_APPLICATION
]"
specification: "EWSGI specification https://github.com/Eiffel-World/Eiffel-Web-Framework/wiki/EWSGI-specification"
legal: "See notice at end of class."
status: "See notice at end of class."
date: "$Date$"
revision: "$Revision$"
deferred class
EWSGI_APPLICATION
feature {NONE} -- Execution
execute (req: EWSGI_REQUEST; res: EWSGI_RESPONSE_BUFFER)
-- Execute the request
-- See `req.input' for input stream
-- `req.environment' for the Gateway environment
-- and `res' for output buffer
require
res_status_unset: not res.status_is_set
deferred
ensure
res_status_set: res.status_is_set
res_committed: res.message_committed
end
feature -- Process request
process (env: EWSGI_ENVIRONMENT; a_input: EWSGI_INPUT_STREAM; a_output: EWSGI_OUTPUT_STREAM)
-- Process request with environment `env', and i/o streams `a_input' and `a_output'
local
rescued: BOOLEAN
req: detachable like new_request
res: detachable like new_response
do
if not rescued then
request_count := request_count + 1
pre_execute (env)
req := new_request (env, a_input)
res := new_response (req, a_output)
execute (req, res)
post_execute (req, res)
else
rescue_execute (req, res, (create {EXCEPTION_MANAGER}).last_exception)
end
if res /= Void then
res.commit
end
end
feature -- Access
request_count: INTEGER
-- Request count
feature {NONE} -- Execution
pre_execute (env: EWSGI_ENVIRONMENT)
-- Operation processed before `execute'
require
env_attached: env /= Void
do
end
post_execute (req: detachable EWSGI_REQUEST; res: detachable EWSGI_RESPONSE_BUFFER)
-- Operation processed after `execute', or after `rescue_execute'
do
end
rescue_execute (req: detachable EWSGI_REQUEST; res: detachable EWSGI_RESPONSE_BUFFER; a_exception: detachable EXCEPTION)
-- Operation processed on rescue of `execute'
do
if
req /= Void and res /= Void
and a_exception /= Void and then attached a_exception.exception_trace as l_trace
then
res.write_header ({HTTP_STATUS_CODE}.internal_server_error, Void)
res.write_string ("<pre>" + l_trace + "</pre>")
end
post_execute (req, res)
end
feature {NONE} -- Factory
new_request (env: EWSGI_ENVIRONMENT; a_input: EWSGI_INPUT_STREAM): EWSGI_REQUEST
do
create {EWSGI_REQUEST} Result.make (env, a_input)
Result.environment.set_variable (request_count.out, "REQUEST_COUNT")
end
new_response (req: EWSGI_REQUEST; a_output: EWSGI_OUTPUT_STREAM): EWSGI_RESPONSE_BUFFER
do
create {EWSGI_RESPONSE_BUFFER} Result.make (a_output)
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,69 +0,0 @@
note
description: "Summary description for {GW_APPLICATION_IMP} "
legal: "See notice at end of class."
status: "See notice at end of class."
date: "$Date$"
revision: "$Revision$"
deferred class
GW_APPLICATION_IMP
inherit
EWSGI_APPLICATION
redefine
process,
rescue_execute
end
feature -- Access
request_count: INTEGER
-- Request count
feature -- Execution
process (env: EWSGI_ENVIRONMENT; a_input: EWSGI_INPUT_STREAM; a_output: EWSGI_OUTPUT_STREAM)
-- Process request with environment `env', and i/o streams `a_input' and `a_output'
do
request_count := request_count + 1
Precursor (env, a_input, a_output)
end
rescue_execute (req: detachable EWSGI_REQUEST; res: detachable EWSGI_RESPONSE_STREAM; a_exception: detachable EXCEPTION)
-- Operation processed on rescue of `execute'
do
if
req /= Void and res /= Void
and a_exception /= Void and then attached a_exception.exception_trace as l_trace
then
res.write_header ({HTTP_STATUS_CODE}.internal_server_error, Void)
res.write_string ("<pre>" + l_trace + "</pre>")
end
Precursor (req, res, a_exception)
end
feature -- Factory
new_request (env: EWSGI_ENVIRONMENT; a_input: EWSGI_INPUT_STREAM): EWSGI_REQUEST
do
create {GW_REQUEST_IMP} Result.make (env, a_input)
Result.execution_variables.set_variable (request_count.out, "REQUEST_COUNT")
end
new_response (req: EWSGI_REQUEST; a_output: EWSGI_OUTPUT_STREAM): EWSGI_RESPONSE_STREAM
do
create {GW_RESPONSE_STREAM_IMP} Result.make (a_output)
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

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

@@ -8,7 +8,14 @@ class
GW_BUFFERED_RESPONSE GW_BUFFERED_RESPONSE
inherit inherit
EWSGI_RESPONSE_STREAM EWSGI_RESPONSE_BUFFER
rename
make as buffer_make
redefine
write,
flush,
commit
end
create {EWSGI_APPLICATION} create {EWSGI_APPLICATION}
make make
@@ -17,13 +24,11 @@ feature {NONE} -- Initialization
make (a_output: like output; a_buffer_size: INTEGER) make (a_output: like output; a_buffer_size: INTEGER)
do do
output := a_output buffer_make (a_output)
buffer_capacity := a_buffer_size buffer_capacity := a_buffer_size
create buffer.make (a_buffer_size) create buffer.make (a_buffer_size)
end end
output: EWSGI_OUTPUT_STREAM
buffer: STRING_8 buffer: STRING_8
buffer_capacity: INTEGER buffer_capacity: INTEGER
@@ -60,77 +65,13 @@ feature {NONE} -- Core output operation
end end
end end
feature -- Status setting
is_status_set: BOOLEAN
do
Result := status_code /= 0
end
set_status_code (a_code: INTEGER)
-- Set response status code
-- Should be done before sending any data back to the client
do
status_code := a_code
output.put_status_line (status_code)
--| We could also just append it to the `buffer'
end
status_code: INTEGER
-- Response status
feature -- Output operation feature -- Output operation
write_string (s: STRING) flush
-- Send the string `s'
do do
write (s) flush_buffer
end end
write_file_content (fn: STRING)
-- Send the content of file `fn'
local
f: RAW_FILE
do
create f.make (fn)
if f.exists and then f.is_readable then
f.open_read
from
until
f.exhausted
loop
f.read_stream (buffer_capacity)
write (f.last_string)
end
f.close
end
end
feature -- Header output operation
write_header (a_status_code: INTEGER; a_headers: detachable ARRAY [TUPLE [key: STRING; value: STRING]])
-- Send headers with status `a_status', and headers from `a_headers'
local
h: GW_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 (h.string)
end
feature {NONE} -- Implementation feature {NONE} -- Implementation
flush_buffer flush_buffer
@@ -145,10 +86,10 @@ feature {NONE} -- Implementation
feature {EWSGI_APPLICATION} -- Commit feature {EWSGI_APPLICATION} -- Commit
commit (a_output: EWSGI_OUTPUT_STREAM) commit
do do
flush_buffer flush_buffer
a_output.flush Precursor
end end
;note ;note

View File

@@ -1,6 +1,5 @@
note note
description: "Summary description for {GW_IN_MEMORY_RESPONSE}." description: "Summary description for {GW_IN_MEMORY_RESPONSE}."
author: ""
date: "$Date$" date: "$Date$"
revision: "$Revision$" revision: "$Revision$"
@@ -8,9 +7,14 @@ class
GW_IN_MEMORY_RESPONSE GW_IN_MEMORY_RESPONSE
inherit inherit
EWSGI_RESPONSE_STREAM EWSGI_RESPONSE_BUFFER
redefine redefine
commit make,
commit,
write,
set_status_code,
write_header,
flush
end end
create {EWSGI_APPLICATION} create {EWSGI_APPLICATION}
@@ -18,8 +22,9 @@ create {EWSGI_APPLICATION}
feature {NONE} -- Initialization feature {NONE} -- Initialization
make make (a_output: EWSGI_OUTPUT_STREAM)
do do
Precursor (a_output)
create header.make create header.make
create body.make (100) create body.make (100)
end end
@@ -38,11 +43,6 @@ feature {NONE} -- Status output
feature -- Status setting feature -- Status setting
is_status_set: BOOLEAN
do
Result := status_code /= 0
end
set_status_code (a_code: INTEGER) set_status_code (a_code: INTEGER)
-- Set response status code -- Set response status code
-- Should be done before sending any data back to the client -- Should be done before sending any data back to the client
@@ -50,34 +50,11 @@ feature -- Status setting
status_code := a_code status_code := a_code
end end
status_code: INTEGER
-- Response status
feature -- Output operation feature -- Output operation
write_string (s: STRING) flush
-- Send the string `s'
do do
write (s) --| Do nothing ... this is in_memory response
end
write_file_content (fn: STRING)
-- Send the content of file `fn'
local
f: RAW_FILE
do
create f.make (fn)
if f.exists and then f.is_readable then
f.open_read
from
until
f.exhausted
loop
f.read_stream (1024)
write (f.last_string)
end
f.close
end
end end
feature -- Header output operation feature -- Header output operation
@@ -106,12 +83,17 @@ feature -- Header output operation
feature {EWSGI_APPLICATION} -- Commit feature {EWSGI_APPLICATION} -- Commit
commit (a_output: EWSGI_OUTPUT_STREAM) commit
local
o: like output
do do
a_output.put_status_line (status_code) o := output
a_output.put_string (header.string) o.put_status_line (status_code)
a_output.put_string (body) o.put_string (header.string)
a_output.flush header_committed := True
o.put_string (body)
o.flush
Precursor
end end
;note ;note

View File

@@ -10,34 +10,14 @@ deferred class
inherit inherit
EWSGI_APPLICATION EWSGI_APPLICATION
redefine redefine
process new_response
end
feature -- Access
request_count: INTEGER
-- Request count
feature -- Execution
process (env: EWSGI_ENVIRONMENT; a_input: EWSGI_INPUT_STREAM; a_output: EWSGI_OUTPUT_STREAM)
-- Process request with environment `env', and i/o streams `a_input' and `a_output'
do
request_count := request_count + 1
Precursor (env, a_input, a_output)
end end
feature -- Factory feature -- Factory
new_request (env: EWSGI_ENVIRONMENT; a_input: EWSGI_INPUT_STREAM): EWSGI_REQUEST
do
create {GW_REQUEST_IMP} Result.make (env, a_input)
Result.execution_variables.set_variable (request_count.out, "REQUEST_COUNT")
end
new_response (req: EWSGI_REQUEST; a_output: EWSGI_OUTPUT_STREAM): GW_IN_MEMORY_RESPONSE new_response (req: EWSGI_REQUEST; a_output: EWSGI_OUTPUT_STREAM): GW_IN_MEMORY_RESPONSE
do do
create {GW_IN_MEMORY_RESPONSE} Result.make create {GW_IN_MEMORY_RESPONSE} Result.make (a_output)
end end
note note

View File

@@ -28,10 +28,10 @@ feature {NONE} -- Initialization
feature {EWSGI_RESPONSE_APPLICATION} -- Response status feature {EWSGI_RESPONSE_APPLICATION} -- Response status
transmit_to (res: EWSGI_RESPONSE_STREAM) transmit_to (res: EWSGI_RESPONSE_BUFFER)
do do
res.set_status_code (status) res.set_status_code (status)
res.write_string (headers) res.write_headers_string (headers)
from from
read_block read_block
res.write_string (last_block) res.write_string (last_block)
@@ -131,7 +131,8 @@ feature {EWSGI_RESPONSE_APPLICATION} -- Message body
-- -- TBD! -- -- TBD!
end end
ensure ensure
not is_buffered implies last_block.count <= max_block_size --Commented, since it is far from obvious to ensure that:
-- not is_buffered implies last_block.count <= max_block_size
end end
last_block: STRING last_block: STRING

View File

@@ -10,14 +10,13 @@ deferred class
feature -- Execution feature -- Execution
execute (req: EWSGI_REQUEST; res: EWSGI_RESPONSE_STREAM) execute (req: EWSGI_REQUEST; res: EWSGI_RESPONSE_BUFFER)
-- Execute the request -- Execute the request
-- See `req.input' for input stream -- See `req.input' for input stream
-- `req.environment' for the Gateway environment -- `req.environment' for the Gateway environment
-- and `res.output' for output stream -- and `res.output' for output stream
local local
rs: EWSGI_RESPONSE rs: EWSGI_RESPONSE
s: STRING
do do
rs := response (req) rs := response (req)
if rs.ready_to_transmit then if rs.ready_to_transmit then

View File

@@ -0,0 +1,76 @@
note
description: "[
Contains all information of a rfc2109 cookie that was read from the request header
]"
legal: "See notice at end of class."
status: "See notice at end of class."
date: "$Date$"
revision: "$Revision$"
class
EWSGI_COOKIE
create
make
convert
value: {READABLE_STRING_8, STRING_8, READABLE_STRING_GENERAL, STRING_GENERAL}
feature {NONE} -- Initialization
make (a_name: STRING; a_value: STRING)
-- Creates current.
require
a_name_not_empty: a_name /= Void and then not a_name.is_empty
a_value_not_empty: a_value /= Void and then not a_value.is_empty
do
name := a_name
value := a_value
ensure
a_name_set: name = a_name
a_value_set: value = a_value
end
feature -- Access
name: STRING
-- Required. The name of the state information ("cookie") is NAME,
-- and its value is VALUE. NAMEs that begin with $ are reserved for
-- other uses and must not be used by applications.
value: STRING
-- The VALUE is opaque to the user agent and may be anything the
-- origin server chooses to send, possibly in a server-selected
-- printable ASCII encoding. "Opaque" implies that the content is of
-- interest and relevance only to the origin server. The content
-- may, in fact, be readable by anyone that examines the Set-Cookie
-- header.
feature -- Query
variables: detachable HASH_TABLE [STRING, STRING]
-- Potential variable contained in the encoded cookie's value.
feature -- Status report
value_is_string (s: READABLE_STRING_GENERAL): BOOLEAN
-- Is `value' same string as `s'
do
Result := s.same_string (value)
end
invariant
name_attached: name /= Void
value_attached: value /= Void
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

@@ -0,0 +1,617 @@
note
description: "[
Interface for a request environment
It includes CGI interface and a few extra values that are usually valuable
See http://www.ietf.org/rfc/rfc3875
2.2. Basic Rules
The following rules are used throughout this specification to
describe basic parsing constructs.
alpha = lowalpha | hialpha
lowalpha = a | b | c | d | e | f | g | h |
i | j | k | l | m | n | o | p |
q | r | s | t | u | v | w | x |
y | z
hialpha = A | B | C | D | E | F | G | H |
I | J | K | L | M | N | O | P |
Q | R | S | T | U | V | W | X |
Y | Z
digit = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9
alphanum = alpha | digit
OCTET = <any 8-bit byte>
CHAR = alpha | digit | separator | ! | # | $ |
%% | & | ' | * | + | - | . | ` |
^ | _ | { | | | } | ~ | CTL
CTL = <any control character>
SP = <space character>
HT = <horizontal tab character>
NL = <newline>
LWSP = SP | HT | NL
separator = ( | ) | < | > | @ | , | ; | : |
\ | " | / | [ | ] | ? | = | { |
} | SP | HT
token = 1*<any CHAR except CTLs or separators>
quoted-string = " *qdtext "
qdtext = <any CHAR except " and CTLs but including LWSP>
TEXT = <any printable character>
2.3. URL Encoding
reserved = ; | / | ? | : | @ | & | = | + | $ |
, | [ | ]
hex = digit | A | B | C | D | E | F | a | b
| c | d | e | f
escaped = "%%" hex hex
unreserved = alpha | digit | mark
mark = - | _ | . | ! | ~ | * | ' | ( | )
Note that newline (NL) need not be a single character, but can
be a character sequence.
3.2. The Script-URI
script-URI = <scheme> "://" <server-name> ":" <server-port>
<script-path> <extra-path> "?" <query-string>
where <scheme> is found from SERVER_PROTOCOL, <server-name>,
<server-port> and <query-string> are the values of the respective
meta-variables. The SCRIPT_NAME and PATH_INFO values, URL-encoded
with ";", "=" and "?" reserved, give <script-path> and <extra-path>.
]"
specification: "EWSGI specification https://github.com/Eiffel-World/Eiffel-Web-Framework/wiki/EWSGI-specification"
legal: "See notice at end of class."
status: "See notice at end of class."
date: "$Date$"
revision: "$Revision$"
deferred
class
EWSGI_ENVIRONMENT
inherit
EWSGI_VARIABLES [STRING_8]
ITERABLE [STRING_8]
feature -- Access
table: HASH_TABLE [STRING, STRING]
-- These variables are specific to requests made with HTTP.
-- Interpretation of these variables may depend on the value of
-- SERVER_PROTOCOL.
--
-- Environment variables with names beginning with "HTTP_" contain
-- header data read from the client, if the protocol used was HTTP.
-- The HTTP header name is converted to upper case, has all
-- occurrences of "-" replaced with "_" and has "HTTP_" prepended to
-- give the environment variable name. The header data may be
-- presented as sent by the client, or may be rewritten in ways which
-- do not change its semantics. If multiple headers with the same
-- field-name are received then they must be rewritten as a single
-- header having the same semantics. Similarly, a header that is
-- received on more than one line must be merged onto a single line.
-- The server must, if necessary, change the representation of the
-- data (for example, the character set) to be appropriate for a CGI
-- environment variable.
--
-- The server is not required to create environment variables for all
-- the headers that it receives. In particular, it may remove any
-- headers carrying authentication information, such as
-- "Authorization"; it may remove headers whose value is available to
-- the script via other variables, such as "Content-Length" and
-- "Content-Type".
--
-- For convenience it might also include the following CGI entries
deferred
end
feature -- Access: table
new_cursor: HASH_TABLE_ITERATION_CURSOR [STRING_8, STRING_8]
-- Fresh cursor associated with current structure
do
create Result.make (table)
end
feature -- Common Gateway Interface - 1.1 8 January 1996
auth_type: detachable STRING
-- This variable is specific to requests made via the "http"
-- scheme.
--
-- If the Script-URI required access authentication for external
-- access, then the server MUST set the value of this variable
-- from the 'auth-scheme' token in the request's "Authorization"
-- header field. Otherwise it is set to NULL.
--
-- AUTH_TYPE = "" | auth-scheme
-- auth-scheme = "Basic" | "Digest" | token
--
-- HTTP access authentication schemes are described in section 11
-- of the HTTP/1.1 specification [8]. The auth-scheme is not
-- case-sensitive.
--
-- Servers MUST provide this metavariable to scripts if the
-- request header included an "Authorization" field that was
-- authenticated.
deferred
end
content_length: detachable STRING
-- This metavariable is set to the size of the message-body
-- entity attached to the request, if any, in decimal number of
-- octets. If no data are attached, then this metavariable is
-- either NULL or not defined. The syntax is the same as for the
-- HTTP "Content-Length" header field (section 14.14, HTTP/1.1
-- specification [8]).
--
-- CONTENT_LENGTH = "" | 1*digit
--
-- Servers MUST provide this metavariable to scripts if the
-- request was accompanied by a message-body entity.
deferred
end
content_length_value: INTEGER
-- Integer value related to `content_length"
deferred
end
content_type: detachable STRING
-- If the request includes a message-body, CONTENT_TYPE is set to
-- the Internet Media Type [9] of the attached entity if the type
-- was provided via a "Content-type" field in the request header,
-- or if the server can determine it in the absence of a supplied
-- "Content-type" field. The syntax is the same as for the HTTP
-- "Content-Type" header field.
--
-- CONTENT_TYPE = "" | media-type
-- media-type = type "/" subtype *( ";" parameter)
-- type = token
-- subtype = token
-- parameter = attribute "=" value
-- attribute = token
-- value = token | quoted-string
--
-- The type, subtype, and parameter attribute names are not
-- case-sensitive. Parameter values MAY be case sensitive. Media
-- types and their use in HTTP are described in section 3.7 of
-- the HTTP/1.1 specification [8].
--
-- Example:
--
-- application/x-www-form-urlencoded
--
-- There is no default value for this variable. If and only if it
-- is unset, then the script MAY attempt to determine the media
-- type from the data received. If the type remains unknown, then
-- the script MAY choose to either assume a content-type of
-- application/octet-stream or reject the request with a 415
-- ("Unsupported Media Type") error. See section 7.2.1.3 for more
-- information about returning error status values.
--
-- Servers MUST provide this metavariable to scripts if a
-- "Content-Type" field was present in the original request
-- header. If the server receives a request with an attached
-- entity but no "Content-Type" header field, it MAY attempt to
-- determine the correct datatype, or it MAY omit this
-- metavariable when communicating the request information to the
-- script.
deferred
end
gateway_interface: STRING
-- This metavariable is set to the dialect of CGI being used by
-- the server to communicate with the script. Syntax:
--
-- GATEWAY_INTERFACE = "CGI" "/" major "." minor
-- major = 1*digit
-- minor = 1*digit
--
-- Note that the major and minor numbers are treated as separate
-- integers and hence each may be more than a single digit. Thus
-- CGI/2.4 is a lower version than CGI/2.13 which in turn is
-- lower than CGI/12.3. Leading zeros in either the major or the
-- minor number MUST be ignored by scripts and SHOULD NOT be
-- generated by servers.
--
-- This document defines the 1.1 version of the CGI interface
-- ("CGI/1.1").
--
-- Servers MUST provide this metavariable to scripts.
--
-- The version of the CGI specification to which this server
-- complies. Syntax:
--
-- GATEWAY_INTERFACE = "CGI" "/" 1*digit "." 1*digit
--
-- Note that the major and minor numbers are treated as separate
-- integers and that each may be incremented higher than a single
-- digit. Thus CGI/2.4 is a lower version than CGI/2.13 which in
-- turn is lower than CGI/12.3. Leading zeros must be ignored by
-- scripts and should never be generated by servers.
deferred
end
path_info: STRING assign update_path_info
-- The PATH_INFO metavariable specifies a path to be interpreted
-- by the CGI script. It identifies the resource or sub-resource
-- to be returned by the CGI script, and it is derived from the
-- portion of the URI path following the script name but
-- preceding any query data. The syntax and semantics are similar
-- to a decoded HTTP URL 'path' token (defined in RFC 2396 [4]),
-- with the exception that a PATH_INFO of "/" represents a single
-- void path segment.
--
-- PATH_INFO = "" | ( "/" path )
-- path = segment *( "/" segment )
-- segment = *pchar
-- pchar = <any CHAR except "/">
--
-- The PATH_INFO string is the trailing part of the <path>
-- component of the Script-URI (see section 3.2) that follows the
-- SCRIPT_NAME portion of the path.
--
-- Servers MAY impose their own restrictions and limitations on
-- what values they will accept for PATH_INFO, and MAY reject or
-- edit any values they consider objectionable before passing
-- them to the script.
--
-- Servers MUST make this URI component available to CGI scripts.
-- The PATH_INFO value is case-sensitive, and the server MUST
-- preserve the case of the PATH_INFO element of the URI when
-- making it available to scripts.
deferred
end
path_translated: detachable STRING
-- PATH_TRANSLATED is derived by taking any path-info component
-- of the request URI (see section 6.1.6), decoding it (see
-- section 3.1), parsing it as a URI in its own right, and
-- performing any virtual-to-physical translation appropriate to
-- map it onto the server's document repository structure. If the
-- request URI includes no path-info component, the
-- PATH_TRANSLATED metavariable SHOULD NOT be defined.
--
--
-- PATH_TRANSLATED = *CHAR
--
-- For a request such as the following:
--
-- http://somehost.com/cgi-bin/somescript/this%2eis%2epath%2einfo
--
-- the PATH_INFO component would be decoded, and the result
-- parsed as though it were a request for the following:
--
-- http://somehost.com/this.is.the.path.info
--
-- This would then be translated to a location in the server's
-- document repository, perhaps a filesystem path something like
-- this:
--
-- /usr/local/www/htdocs/this.is.the.path.info
--
-- The result of the translation is the value of PATH_TRANSLATED.
--
-- The value of PATH_TRANSLATED may or may not map to a valid
-- repository location. Servers MUST preserve the case of the
-- path-info segment if and only if the underlying repository
-- supports case-sensitive names. If the repository is only
-- case-aware, case-preserving, or case-blind with regard to
-- document names, servers are not required to preserve the case
-- of the original segment through the translation.
--
-- The translation algorithm the server uses to derive
-- PATH_TRANSLATED is implementation defined; CGI scripts which
-- use this variable may suffer limited portability.
--
-- Servers SHOULD provide this metavariable to scripts if and
-- only if the request URI includes a path-info component.
deferred
end
query_string: STRING
-- A URL-encoded string; the <query> part of the Script-URI. (See
-- section 3.2.)
--
-- QUERY_STRING = query-string
-- query-string = *uric
-- The URL syntax for a query string is described in section 3 of
-- RFC 2396 [4].
--
-- Servers MUST supply this value to scripts. The QUERY_STRING
-- value is case-sensitive. If the Script-URI does not include a
-- query component, the QUERY_STRING metavariable MUST be defined
-- as an empty string ("").
deferred
end
remote_addr: STRING
-- The IP address of the client sending the request to the
-- server. This is not necessarily that of the user agent (such
-- as if the request came through a proxy).
--
-- REMOTE_ADDR = hostnumber
-- hostnumber = ipv4-address | ipv6-address
-- The definitions of ipv4-address and ipv6-address are provided
-- in Appendix B of RFC 2373 [13].
--
-- Servers MUST supply this value to scripts.
deferred
end
remote_host: detachable STRING
-- The fully qualified domain name of the client sending the
-- request to the server, if available, otherwise NULL. (See
-- section 6.1.9.) Fully qualified domain names take the form as
-- described in section 3.5 of RFC 1034 [10] and section 2.1 of
-- RFC 1123 [5]. Domain names are not case sensitive.
--
-- Servers SHOULD provide this information to scripts.
deferred
end
remote_ident: detachable STRING
-- The identity information reported about the connection by a
-- RFC 1413 [11] request to the remote agent, if available.
-- Servers MAY choose not to support this feature, or not to
-- request the data for efficiency reasons.
--
-- REMOTE_IDENT = *CHAR
--
-- The data returned may be used for authentication purposes, but
-- the level of trust reposed in them should be minimal.
--
-- Servers MAY supply this information to scripts if the RFC1413
-- [11] lookup is performed.
deferred
end
remote_user: detachable STRING
-- If the request required authentication using the "Basic"
-- mechanism (i.e., the AUTH_TYPE metavariable is set to
-- "Basic"), then the value of the REMOTE_USER metavariable is
-- set to the user-ID supplied. In all other cases the value of
-- this metavariable is undefined.
--
-- REMOTE_USER = *OCTET
--
-- This variable is specific to requests made via the HTTP
-- protocol.
--
-- Servers SHOULD provide this metavariable to scripts.
deferred
end
request_method: STRING
-- The REQUEST_METHOD metavariable is set to the method with
-- which the request was made, as described in section 5.1.1 of
-- the HTTP/1.0 specification [3] and section 5.1.1 of the
-- HTTP/1.1 specification [8].
--
-- REQUEST_METHOD = http-method
-- http-method = "GET" | "HEAD" | "POST" | "PUT" | "DELETE"
-- | "OPTIONS" | "TRACE" | extension-method
-- extension-method = token
--
-- The method is case sensitive. CGI/1.1 servers MAY choose to
-- process some methods directly rather than passing them to
-- scripts.
--
-- This variable is specific to requests made with HTTP.
--
-- Servers MUST provide this metavariable to scripts.
deferred
end
script_name: STRING
-- The SCRIPT_NAME metavariable is set to a URL path that could
-- identify the CGI script (rather than the script's output). The
-- syntax and semantics are identical to a decoded HTTP URL
-- 'path' token (see RFC 2396 [4]).
--
-- SCRIPT_NAME = "" | ( "/" [ path ] )
--
-- The SCRIPT_NAME string is some leading part of the <path>
-- component of the Script-URI derived in some implementation
-- defined manner. No PATH_INFO or QUERY_STRING segments (see
-- sections 6.1.6 and 6.1.8) are included in the SCRIPT_NAME
-- value.
--
-- Servers MUST provide this metavariable to scripts.
deferred
end
server_name: STRING
-- The SERVER_NAME metavariable is set to the name of the server,
-- as derived from the <host> part of the Script-URI (see section
-- 3.2).
--
-- SERVER_NAME = hostname | hostnumber
--
-- Servers MUST provide this metavariable to scripts.
deferred
end
server_port: INTEGER
-- The SERVER_PORT metavariable is set to the port on which the
-- request was received, as used in the <port> part of the
-- Script-URI.
--
-- SERVER_PORT = 1*digit
--
-- If the <port> portion of the script-URI is blank, the actual
-- port number upon which the request was received MUST be
-- supplied.
--
-- Servers MUST provide this metavariable to scripts.
deferred
end
server_protocol: STRING
-- The SERVER_PROTOCOL metavariable is set to the name and
-- revision of the information protocol with which the request
-- arrived. This is not necessarily the same as the protocol
-- version used by the server in its response to the client.
--
-- SERVER_PROTOCOL = HTTP-Version | extension-version
-- | extension-token
-- HTTP-Version = "HTTP" "/" 1*digit "." 1*digit
-- extension-version = protocol "/" 1*digit "." 1*digit
-- protocol = 1*( alpha | digit | "+" | "-" | "." )
-- extension-token = token
--
-- 'protocol' is a version of the <scheme> part of the
-- Script-URI, but is not identical to it. For example, the
-- scheme of a request may be "https" while the protocol remains
-- "http". The protocol is not case sensitive, but by convention,
-- 'protocol' is in upper case.
--
-- A well-known extension token value is "INCLUDED", which
-- signals that the current document is being included as part of
-- a composite document, rather than being the direct target of
-- the client request.
--
-- Servers MUST provide this metavariable to scripts.
deferred
end
server_software: STRING
-- The SERVER_SOFTWARE metavariable is set to the name and
-- version of the information server software answering the
-- request (and running the gateway).
--
-- SERVER_SOFTWARE = 1*product
-- product = token [ "/" product-version ]
-- product-version = token
-- Servers MUST provide this metavariable to scripts.
deferred
end
feature -- HTTP_*
http_accept: detachable STRING
-- Contents of the Accept: header from the current request, if there is one.
-- Example: 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8'
deferred
end
http_accept_charset: detachable STRING
-- Contents of the Accept-Charset: header from the current request, if there is one.
-- Example: 'iso-8859-1,*,utf-8'.
deferred
end
http_accept_encoding: detachable STRING
-- Contents of the Accept-Encoding: header from the current request, if there is one.
-- Example: 'gzip'.
deferred
end
http_accept_language: detachable STRING
-- Contents of the Accept-Language: header from the current request, if there is one.
-- Example: 'en'.
deferred
end
http_connection: detachable STRING
-- Contents of the Connection: header from the current request, if there is one.
-- Example: 'Keep-Alive'.
deferred
end
http_host: detachable STRING
-- Contents of the Host: header from the current request, if there is one.
deferred
end
http_referer: detachable STRING
-- The address of the page (if any) which referred the user agent to the current page.
-- This is set by the user agent.
-- Not all user agents will set this, and some provide the ability to modify HTTP_REFERER as a feature.
-- In short, it cannot really be trusted.
deferred
end
http_user_agent: detachable STRING
-- Contents of the User-Agent: header from the current request, if there is one.
-- This is a string denoting the user agent being which is accessing the page.
-- A typical example is: Mozilla/4.5 [en] (X11; U; Linux 2.2.9 i586).
-- Among other things, you can use this value to tailor your page's
-- output to the capabilities of the user agent.
deferred
end
http_authorization: detachable STRING
-- Contents of the Authorization: header from the current request, if there is one.
deferred
end
feature -- Extra
request_uri: STRING
-- The URI which was given in order to access this page; for instance, '/index.html'.
deferred
end
orig_path_info: detachable STRING
-- Original version of `path_info' before processed by Current environment
deferred
end
feature {EWSGI_REQUEST} -- Element change
set_orig_path_info (s: STRING)
-- Set ORIG_PATH_INFO to `s'
require
s_attached: s /= Void
deferred
ensure
same_orig_path_info: orig_path_info ~ variable ({EWSGI_ENVIRONMENT_NAMES}.orig_path_info)
end
unset_orig_path_info
-- Unset ORIG_PATH_INFO
deferred
ensure
unset: not has_variable ({EWSGI_ENVIRONMENT_NAMES}.orig_path_info)
end
update_path_info (a_path_info: like path_info)
-- Updated PATH_INFO
deferred
ensure
same_path_info: path_info ~ variable ({EWSGI_ENVIRONMENT_NAMES}.path_info)
end
invariant
server_name_not_empty: not server_name.is_empty
server_port_set: server_port /= 0
request_method_attached: request_method /= Void
path_info_attached: path_info /= Void
query_string_attached: query_string /= Void
remote_addr_attached: remote_addr /= Void
path_info_identical: path_info ~ variable ({EWSGI_ENVIRONMENT_NAMES}.path_info)
;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

@@ -0,0 +1,89 @@
note
description: "Summary description for {EWSGI_ENVIRONMENT_NAMES}."
legal: "See notice at end of class."
status: "See notice at end of class."
date: "$Date$"
revision: "$Revision$"
class
EWSGI_ENVIRONMENT_NAMES
feature -- Access
request_uri: STRING = "REQUEST_URI"
request_method: STRING = "REQUEST_METHOD"
query_string: STRING = "QUERY_STRING"
content_type: STRING = "CONTENT_TYPE"
content_length: STRING = "CONTENT_LENGTH"
path_info: STRING = "PATH_INFO"
path_translated: STRING = "PATH_TRANSLATED"
http_user_agent: STRING = "HTTP_USER_AGENT"
http_authorization: STRING = "HTTP_AUTHORIZATION"
http_host: STRING = "HTTP_HOST"
http_cookie: STRING = "HTTP_COOKIE"
http_from: STRING = "HTTP_FROM"
http_accept: STRING = "HTTP_ACCEPT"
http_accept_charset: STRING = "HTTP_ACCEPT_CHARSET"
http_accept_encoding: STRING = "HTTP_ACCEPT_ENCODING"
http_accept_language: STRING = "HTTP_ACCEPT_LANGUAGE"
http_connection: STRING = "HTTP_CONNECTION"
http_referer: STRING = "HTTP_REFERER"
gateway_interface: STRING = "GATEWAY_INTERFACE"
auth_type: STRING = "AUTH_TYPE"
remote_host: STRING = "REMOTE_HOST"
remote_addr: STRING = "REMOTE_ADDR"
remote_ident: STRING = "REMOTE_IDENT"
remote_user: STRING = "REMOTE_USER"
script_name: STRING = "SCRIPT_NAME"
server_name: STRING = "SERVER_NAME"
server_port: STRING = "SERVER_PORT"
server_protocol: STRING = "SERVER_PROTOCOL"
server_software: STRING = "SERVER_SOFTWARE"
feature -- Extra names
request_time: STRING = "REQUEST_TIME"
self: STRING = "SELF"
orig_path_info: STRING = "ORIG_PATH_INFO"
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,21 +1,15 @@
note note
description: "[ description: "[
Server request context of the httpd request EWSGI interface to represent the Request
You can create your own descendant of this class to
add/remove specific value or processing
This object is created by {EWSGI_APPLICATION}.new_request
]" ]"
specification: "EWSGI specification https://github.com/Eiffel-World/Eiffel-Web-Framework/wiki/EWSGI-specification"
legal: "See notice at end of class." legal: "See notice at end of class."
status: "See notice at end of class." status: "See notice at end of class."
date: "$Date$" date: "$Date$"
revision: "$Revision$" revision: "$Revision$"
class class
GW_REQUEST_IMP
inherit
EWSGI_REQUEST EWSGI_REQUEST
create create
@@ -31,7 +25,6 @@ feature {NONE} -- Initialization
input := a_input input := a_input
environment := env environment := env
content_length := env.content_length_value content_length := env.content_length_value
create execution_variables.make (10)
create uploaded_files.make (0) create uploaded_files.make (0)
raw_post_data_recorded := True raw_post_data_recorded := True
@@ -100,6 +93,8 @@ feature -- Access: environment variables
environment_variable (a_name: STRING): detachable STRING environment_variable (a_name: STRING): detachable STRING
-- Environment variable related to `a_name' -- Environment variable related to `a_name'
require
a_name_valid: a_name /= Void and then not a_name.is_empty
do do
Result := environment.variable (a_name) Result := environment.variable (a_name)
end end
@@ -107,20 +102,10 @@ feature -- Access: environment variables
content_length: INTEGER content_length: INTEGER
-- Extracted Content-Length value -- Extracted Content-Length value
feature -- Access: execution variables
execution_variables: GW_EXECUTION_VARIABLES
-- Execution variables set by the application
execution_variable (a_name: STRING): detachable STRING_32
-- Execution variable related to `a_name'
do
Result := execution_variables.variable (a_name)
end
feature -- URL parameters feature -- URL parameters
parameters: GW_REQUEST_VARIABLES parameters: EWSGI_REQUEST_VARIABLES
-- Variables extracted from QUERY_STRING
local local
vars: like internal_parameters vars: like internal_parameters
p,e: INTEGER p,e: INTEGER
@@ -155,13 +140,16 @@ feature -- URL parameters
parameter (a_name: STRING): detachable STRING_32 parameter (a_name: STRING): detachable STRING_32
-- Parameter for name `n'. -- Parameter for name `n'.
require
a_name_valid: a_name /= Void and then not a_name.is_empty
do do
Result := parameters.variable (a_name) Result := parameters.variable (a_name)
end end
feature -- Form fields and related feature -- Form fields and related
form_fields: GW_REQUEST_VARIABLES form_fields: EWSGI_REQUEST_VARIABLES
-- Variables sent by POST request
local local
vars: like internal_form_fields vars: like internal_form_fields
s: STRING s: STRING
@@ -198,6 +186,8 @@ feature -- Form fields and related
form_field (a_name: STRING): detachable STRING_32 form_field (a_name: STRING): detachable STRING_32
-- Field for name `a_name'. -- Field for name `a_name'.
require
a_name_valid: a_name /= Void and then not a_name.is_empty
do do
Result := form_fields.variable (a_name) Result := form_fields.variable (a_name)
end end
@@ -244,6 +234,8 @@ feature -- Cookies
cookies_variable (a_name: STRING): detachable STRING cookies_variable (a_name: STRING): detachable STRING
-- Field for name `a_name'. -- Field for name `a_name'.
require
a_name_valid: a_name /= Void and then not a_name.is_empty
do do
Result := cookies_variables.item (a_name) Result := cookies_variables.item (a_name)
end end
@@ -302,16 +294,6 @@ feature -- Access: global variable
do do
create Result.make (100) create Result.make (100)
vars := execution_variables
from
vars.start
until
vars.after
loop
Result.put (vars.item_for_iteration, vars.key_for_iteration)
vars.forth
end
vars := environment.table vars := environment.table
from from
vars.start vars.start
@@ -360,16 +342,13 @@ feature -- Access: global variable
local local
s: detachable STRING_GENERAL s: detachable STRING_GENERAL
do do
s := execution_variable (a_name) s := environment_variable (a_name)
if s = Void then if s = Void then
s := environment_variable (a_name) s := parameter (a_name)
if s = Void then if s = Void then
s := parameter (a_name) s := form_field (a_name)
if s = Void then if s = Void then
s := form_field (a_name) s := cookies_variable (a_name)
if s = Void then
s := cookies_variable (a_name)
end
end end
end end
end end
@@ -378,7 +357,6 @@ feature -- Access: global variable
end end
end end
feature -- Access extra information feature -- Access extra information
request_time: detachable DATE_TIME request_time: detachable DATE_TIME
@@ -392,6 +370,28 @@ feature -- Access extra information
end end
end end
feature -- Uploaded File Handling
is_uploaded_file (a_filename: STRING): BOOLEAN
-- Is `a_filename' a file uploaded via HTTP Form
local
l_files: like uploaded_files
do
l_files := uploaded_files
if not l_files.is_empty then
from
l_files.start
until
l_files.after or Result
loop
if attached l_files.item_for_iteration.tmp_name as l_tmp_name and then l_tmp_name.same_string (a_filename) then
Result := True
end
l_files.forth
end
end
end
feature -- URL Utility feature -- URL Utility
absolute_script_url (a_path: STRING): STRING absolute_script_url (a_path: STRING): STRING
@@ -489,28 +489,6 @@ feature -- Element change
end end
end end
feature -- Uploaded File Handling
is_uploaded_file (a_filename: STRING): BOOLEAN
-- Is `a_filename' a file uploaded via HTTP Form
local
l_files: like uploaded_files
do
l_files := uploaded_files
if not l_files.is_empty then
from
l_files.start
until
l_files.after or Result
loop
if attached l_files.item_for_iteration.tmp_name as l_tmp_name and then l_tmp_name.same_string (a_filename) then
Result := True
end
l_files.forth
end
end
end
feature {NONE} -- Temporary File handling feature {NONE} -- Temporary File handling
delete_uploaded_file (uf: EWSGI_UPLOADED_FILE_DATA) delete_uploaded_file (uf: EWSGI_UPLOADED_FILE_DATA)

View File

@@ -8,7 +8,7 @@ note
revision: "$Revision$" revision: "$Revision$"
class class
GW_REQUEST_VARIABLES EWSGI_REQUEST_VARIABLES
inherit inherit
EWSGI_VARIABLES [STRING_32] EWSGI_VARIABLES [STRING_32]

View File

@@ -0,0 +1,103 @@
note
description: "Summary description for {GW_UPLOADED_FILE}."
author: ""
date: "$Date$"
revision: "$Revision$"
class
EWSGI_UPLOADED_FILE_DATA
create
make
feature {NONE} -- Initialization
make (n: like name; t: like content_type; s: like size)
do
name := n
content_type := t
size := s
end
feature -- Access
name: STRING
-- original filename
content_type: STRING
-- Content type
size: INTEGER
-- Size of uploaded file
tmp_name: detachable STRING
-- Filename of tmp file
tmp_basename: detachable STRING
-- Basename of tmp file
feature -- Basic operation
move_to (a_destination: STRING): BOOLEAN
-- Move current uploaded file to `a_destination'
require
has_no_error: not has_error
local
f: RAW_FILE
do
if attached tmp_name as n then
create f.make (n)
if f.exists then
f.change_name (a_destination)
Result := True
end
end
end
feature -- Status
has_error: BOOLEAN
-- Has error during uploading
do
Result := error /= 0
end
error: INTEGER
-- Eventual error code
--| no error => 0
feature -- Element change
set_error (e: like error)
-- Set `error' to `e'
do
error := e
end
set_tmp_name (n: like tmp_name)
-- Set `tmp_name' to `n'
do
tmp_name := n
end
set_tmp_basename (n: like tmp_basename)
-- Set `tmp_basename' to `n'
do
tmp_basename := n
end
invariant
valid_tmp_name: not has_error implies attached tmp_name as n and then not n.is_empty
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

@@ -0,0 +1,80 @@
note
description : "[
Interface to access the variable stored in a container
]"
legal: "See notice at end of class."
status: "See notice at end of class."
date: "$Date$"
revision: "$Revision$"
deferred class
EWSGI_VARIABLES [G -> STRING_GENERAL]
inherit
ITERABLE [G]
feature -- Status report
has_variable (a_name: STRING): BOOLEAN
-- Has variable associated with `a_name'
require
a_name_not_empty: a_name /= Void and then not a_name.is_empty
deferred
end
feature -- Access
variable (a_name: STRING): detachable G
-- Value for variable associated with `a_name'
-- If not found, return Void
require
a_name_not_empty: a_name /= Void and then not a_name.is_empty
deferred
end
variable_or_default (a_name: STRING; a_default: G; use_default_when_empty: BOOLEAN): G
-- Value for variable `a_name'
-- If not found, return `a_default'
require
a_name_not_empty: a_name /= Void and then not a_name.is_empty
do
if attached variable (a_name) as s then
if use_default_when_empty and then s.is_empty then
Result := a_default
else
Result := s
end
else
Result := a_default
end
end
feature {EWSGI_REQUEST, EWSGI_APPLICATION, EWSGI_CONNECTOR} -- Element change
set_variable (a_name: STRING; a_value: G)
require
a_name_not_empty: a_name /= Void and then not a_name.is_empty
deferred
ensure
variable_set: has_variable (a_name) and then variable (a_name) ~ a_value
end
unset_variable (a_name: STRING)
require
a_name_not_empty: a_name /= Void and then not a_name.is_empty
deferred
ensure
variable_unset: not has_variable (a_name)
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,56 +0,0 @@
note
description: "Summary description for {GW_EXECUTION_VARIABLES}."
legal: "See notice at end of class."
status: "See notice at end of class."
date: "$Date$"
revision: "$Revision$"
class
GW_EXECUTION_VARIABLES
inherit
EWSGI_VARIABLES [STRING_32]
undefine
copy, is_equal
end
HASH_TABLE [STRING_32, STRING]
create
make
feature -- Status report
variable (a_name: STRING): detachable STRING_32
do
Result := item (a_name)
end
has_variable (a_name: STRING): BOOLEAN
do
Result := has (a_name)
end
feature {EWSGI_REQUEST, EWSGI_APPLICATION, EWSGI_CONNECTOR} -- Element change
set_variable (a_name: STRING; a_value: STRING_32)
do
force (a_value, a_name)
end
unset_variable (a_name: STRING)
do
remove (a_name)
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,14 +1,14 @@
note note
description: "Summary description for {GW_RESPONSE_STREAM_IMP}." description: "[
author: "" Response buffer
]"
specification: "EWSGI specification https://github.com/Eiffel-World/Eiffel-Web-Framework/wiki/EWSGI-specification"
date: "$Date$" date: "$Date$"
revision: "$Revision$" revision: "$Revision$"
class class
GW_RESPONSE_STREAM_IMP EWSGI_RESPONSE_BUFFER
inherit
EWSGI_RESPONSE_STREAM
create {EWSGI_APPLICATION} create {EWSGI_APPLICATION}
make make
@@ -22,23 +22,44 @@ feature {NONE} -- Initialization
feature {EWSGI_APPLICATION} -- Commit feature {EWSGI_APPLICATION} -- Commit
commit (a_output_stream: EWSGI_OUTPUT_STREAM) commit
-- Commit the current response -- Commit the current response
do do
a_output_stream.flush output.flush
message_committed := True
ensure
status_is_set: status_is_set
header_committed: header_committed
message_committed: message_committed
end
feature -- Status report
header_committed: BOOLEAN
-- Header committed?
message_committed: BOOLEAN
-- Message committed?
message_writable: BOOLEAN
-- Can message be written?
do
Result := status_is_set and header_committed
end end
feature {NONE} -- Core output operation feature {NONE} -- Core output operation
write (s: STRING) write (s: STRING)
-- Send the content of `s' -- Send the content of `s'
-- this can be used for header and body
do do
output.put_string (s) output.put_string (s)
end end
feature -- Status setting feature -- Status setting
is_status_set: BOOLEAN status_is_set: BOOLEAN
-- Is status set?
do do
Result := status_code /= 0 Result := status_code /= 0
end end
@@ -46,32 +67,39 @@ feature -- Status setting
set_status_code (a_code: INTEGER) set_status_code (a_code: INTEGER)
-- Set response status code -- Set response status code
-- Should be done before sending any data back to the client -- Should be done before sending any data back to the client
require
status_not_set: not status_is_set
header_not_committed: not header_committed
do do
status_code := a_code status_code := a_code
output.put_status_line (a_code) output.put_status_line (a_code)
ensure
status_code_set: status_code = a_code
status_set: status_is_set
end end
status_code: INTEGER status_code: INTEGER
-- Response status -- Response status
feature -- Output operation
write_string (s: STRING)
-- Send the string `s'
do
write (s)
end
write_file_content (fn: STRING)
-- Send the content of file `fn'
do
output.put_file_content (fn)
end
feature -- Header output operation feature -- Header output operation
write_headers_string (a_headers: STRING)
require
status_set: status_is_set
header_not_committed: not header_committed
do
write (a_headers)
header_committed := True
ensure
status_set: status_is_set
header_committed: header_committed
end
write_header (a_status_code: INTEGER; a_headers: detachable ARRAY [TUPLE [key: STRING; value: STRING]]) write_header (a_status_code: INTEGER; a_headers: detachable ARRAY [TUPLE [key: STRING; value: STRING]])
-- Send headers with status `a_status', and headers from `a_headers' -- 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
local local
h: GW_HEADER h: GW_HEADER
i,n: INTEGER i,n: INTEGER
@@ -90,7 +118,42 @@ feature -- Header output operation
i := i + 1 i := i + 1
end end
end end
write (h.string) write_headers_string (h.string)
ensure
status_set: status_is_set
header_committed: header_committed
end
feature -- Output operation
write_string (s: STRING)
-- Send the string `s'
require
message_writable: message_writable
do
write (s)
end
write_substring (s: STRING; start_index, end_index: INTEGER)
-- Send the substring `start_index:end_index]'
--| Could be optimized according to the target output
require
message_writable: message_writable
do
output.put_substring (s, start_index, end_index)
end
write_file_content (fn: STRING)
-- Send the content of file `fn'
require
message_writable: message_writable
do
output.put_file_content (fn)
end
flush
do
output.flush
end end
feature {NONE} -- Implementation: Access feature {NONE} -- Implementation: Access

View File

@@ -0,0 +1,41 @@
note
description : "[
Objects that represents the input stream
]"
specification: "EWSGI/connector specification https://github.com/Eiffel-World/Eiffel-Web-Framework/wiki/EWSGI-specification"
legal: "See notice at end of class."
status: "See notice at end of class."
date: "$Date$"
revision: "$Revision$"
deferred class
EWSGI_INPUT_STREAM
feature -- Access
last_string: STRING_8
-- Last read string from stream
deferred
end
feature -- Basic operation
read_stream (n: INTEGER)
require
n_positive: n > 0
deferred
ensure
at_max_n: last_string.count <= n
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

@@ -0,0 +1,84 @@
note
description : "[
Objects that represents the output stream
]"
specification: "EWSGI/connector specification https://github.com/Eiffel-World/Eiffel-Web-Framework/wiki/EWSGI-specification"
legal: "See notice at end of class."
status: "See notice at end of class."
date: "$Date$"
revision: "$Revision$"
deferred class
EWSGI_OUTPUT_STREAM
feature -- Core operation
put_string (s: STRING_8)
-- Write `s' into the output stream
require
s_not_empty: s /= Void and then not s.is_empty
deferred
end
flush
-- Flush the output stream
do
end
feature -- Status writing
put_status_line (a_code: INTEGER)
-- Put status code line for `a_code'
--| Note this is a default implementation, and could be redefined
--| for instance in relation to NPH CGI script
deferred
end
feature -- Basic operation
put_substring (s: STRING; start_index, end_index: INTEGER)
-- Write `s[start_index:end_index]' into the output stream
--| Could be redefined for optimization
require
s_not_empty: s /= Void and then not s.is_empty
do
put_string (s.substring (start_index, end_index))
end
put_file_content (fn: STRING)
-- Send the content of file `fn'
local
f: RAW_FILE
do
create f.make (fn)
if f.exists and then f.is_readable then
f.open_read
from
until
f.exhausted
loop
f.read_stream (4096)
put_string (f.last_string)
end
f.close
end
end
put_header_line (s: STRING)
-- Send `s' to http client as header line
do
put_string (s)
put_string ("%R%N")
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

@@ -231,11 +231,13 @@ feature -- Others
feature -- Redirection feature -- Redirection
put_redirection (a_location: STRING; a_code: INTEGER) put_redirection (a_location: STRING; a_code: INTEGER)
-- Tell the client to redirect to page with `a_location' right away
do do
put_header_key_value ("Location", a_location) put_header_key_value ("Location", a_location)
end end
put_refresh (a_location: STRING; a_timeout: INTEGER; a_code: INTEGER) put_refresh (a_location: STRING; a_timeout: INTEGER; a_code: INTEGER)
-- Tell the client to refresh page with `a_location' after `a_timeout' in seconds
do do
put_header_key_value ("Refresh", a_timeout.out + "; url=" + a_location) put_header_key_value ("Refresh", a_timeout.out + "; url=" + a_location)
end end

View File

@@ -0,0 +1,24 @@
<?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="ewsgi-full" uuid="663191A1-53D8-4974-AB46-7359C7377AD2" library_target="ewsgi-full">
<target name="ewsgi-full">
<root all_classes="true"/>
<file_rule>
<exclude>/EIFGENs$</exclude>
<exclude>/\.git$</exclude>
<exclude>/\.svn$</exclude>
</file_rule>
<option warning="true" full_class_checking="true" is_attached_by_default="true" void_safety="all" syntax="provisional">
</option>
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
<library name="encoder" location="..\..\..\text\encoder\encoder-safe.ecf" readonly="false"/>
<library name="error" location="..\..\..\error\error-safe.ecf"/>
<library name="http" location="..\..\..\protocol\http\http-safe.ecf"/>
<library name="libfcgi" location="..\..\libfcgi\libfcgi-safe.ecf"/>
<library name="nino" location="..\..\..\..\ext\server\nino\nino-safe.ecf" readonly="false">
<renaming old_name="HTTP_CONSTANTS" new_name="NINO_HTTP_CONSTANTS"/>
</library>
<library name="time" location="$ISE_LIBRARY\library\time\time-safe.ecf"/>
<cluster name="connectors" location="..\connectors\" recursive="true"/>
<cluster name="interface" location="..\src\" recursive="true"/>
</target>
</system>

View File

@@ -0,0 +1,24 @@
<?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="ewsgi-full" uuid="663191A1-53D8-4974-AB46-7359C7377AD2" library_target="ewsgi-full">
<target name="ewsgi-full">
<root all_classes="true"/>
<file_rule>
<exclude>/EIFGENs$</exclude>
<exclude>/\.git$</exclude>
<exclude>/\.svn$</exclude>
</file_rule>
<option warning="true" full_class_checking="true" syntax="provisional">
</option>
<library name="base" location="$ISE_LIBRARY\library\base\base.ecf"/>
<library name="error" location="..\..\..\error\error.ecf"/>
<library name="http" location="..\..\..\protocol\http\http.ecf"/>
<library name="encoder" location="..\..\..\text\encoder\encoder.ecf" readonly="false"/>
<library name="libfcgi" location="..\..\libfcgi\libfcgi.ecf"/>
<library name="nino" location="..\..\..\..\ext\server\nino\nino.ecf" readonly="false">
<renaming old_name="HTTP_CONSTANTS" new_name="NINO_HTTP_CONSTANTS"/>
</library>
<library name="time" location="$ISE_LIBRARY\library\time\time.ecf"/>
<cluster name="connectors" location="..\connectors\" recursive="true"/>
<cluster name="interface" location="..\src\" recursive="true"/>
</target>
</system>

View File

@@ -0,0 +1,52 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-6-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-6-0 http://www.eiffel.com/developers/xml/configuration-1-6-0.xsd" name="fcgi" uuid="3F4BCF74-3503-4533-9D74-5A65EC4CA3C4" library_target="fcgi">
<target name="fcgi">
<root all_classes="true"/>
<file_rule>
<exclude>/\.svn$</exclude>
<exclude>/\.git$</exclude>
<exclude>/EIFGENs$</exclude>
</file_rule>
<option warning="true" full_class_checking="true" is_attached_by_default="true" void_safety="all" >
</option>
<external_include location="$ECF_CONFIG_PATH/spec/include/libfcgi">
<condition>
<platform excluded_value="windows"/>
</condition>
</external_include>
<external_include location="$ECF_CONFIG_PATH\spec\include\libfcgi">
<condition>
<platform value="windows"/>
</condition>
</external_include>
<external_library location="$ECF_CONFIG_PATH\spec\lib\$ISE_PLATFORM\$ISE_C_COMPILER\libfcgi.lib">
<condition>
<platform value="windows"/>
</condition>
</external_library>
<external_library location="/usr/local/lib/libfcgi.so">
<condition>
<platform excluded_value="windows"/>
</condition>
</external_library>
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
<cluster name="interface" location="interface" recursive="true"/>
<cluster name="implementation" location="implementation" recursive="true">
<file_rule>
<exclude>/linux$</exclude>
<exclude>/fake$</exclude>
<condition>
<platform value="windows"/>
</condition>
</file_rule>
<file_rule>
<exclude>/windows$</exclude>
<exclude>/fake$</exclude>
<condition>
<platform excluded_value="windows"/>
</condition>
</file_rule>
</cluster>
</target>
</system>

View File

@@ -8,7 +8,7 @@
<exclude>/EIFGENs$</exclude> <exclude>/EIFGENs$</exclude>
</file_rule> </file_rule>
<option warning="true" full_class_checking="true" is_attached_by_default="true" void_safety="all" > <option warning="true" full_class_checking="true">
</option> </option>
<external_include location="$ECF_CONFIG_PATH/spec/include/libfcgi"> <external_include location="$ECF_CONFIG_PATH/spec/include/libfcgi">
<condition> <condition>
@@ -30,7 +30,7 @@
<platform excluded_value="windows"/> <platform excluded_value="windows"/>
</condition> </condition>
</external_library> </external_library>
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/> <library name="base" location="$ISE_LIBRARY\library\base\base.ecf"/>
<cluster name="interface" location="interface" recursive="true"/> <cluster name="interface" location="interface" recursive="true"/>
<cluster name="implementation" location="implementation" recursive="true"> <cluster name="implementation" location="implementation" recursive="true">
<file_rule> <file_rule>

View File

@@ -0,0 +1,16 @@
<?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="eiffelweb" uuid="6A03BC65-BD03-4B68-B51E-2543F9471D42">
<target name="eiffelweb">
<root cluster="application" class="APPLICATION_ROOT" feature="make"/>
<file_rule>
<exclude>/EIFGENs$</exclude>
<exclude>/\.git$</exclude>
<exclude>/\.svn$</exclude>
</file_rule>
<setting name="console_application" value="true"/>
<setting name="concurrency" value="thread"/>
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
<library name="fcgi" location="..\fcgi-safe.ecf" readonly="false"/>
<cluster name="application" location=".\" recursive="true"/>
</target>
</system>

View File

@@ -9,7 +9,7 @@
</file_rule> </file_rule>
<setting name="console_application" value="true"/> <setting name="console_application" value="true"/>
<setting name="concurrency" value="thread"/> <setting name="concurrency" value="thread"/>
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/> <library name="base" location="$ISE_LIBRARY\library\base\base.ecf"/>
<library name="fcgi" location="..\fcgi.ecf" readonly="false"/> <library name="fcgi" location="..\fcgi.ecf" readonly="false"/>
<cluster name="application" location=".\" recursive="true"/> <cluster name="application" location=".\" recursive="true"/>
</target> </target>

View File

@@ -0,0 +1,14 @@
This library introduce the notion of router and handler
The Router manages the association between URI,URI template and Handler
The Router is in charge to route/dispatch a request to one of the Handler according to the URI and how the Handler is mapped in the Router.
Common usage
router: REQUEST_ROUTER
hello_handler: REQUEST_HANDLER
create {REQUEST_URI_TEMPLATE_ROUTER} router.make
create {REQUEST_AGENT_HANDLER} hello_handler.make (agent handle_hello)
router.map ("/hello/{name}", hello_handler)
router.map_agent ("/hello/{name}", agent handle_hello)

View File

@@ -0,0 +1,10 @@
${NOTE_KEYWORD}
copyright: "2011-${YEAR}, 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
]"

View File

@@ -0,0 +1,39 @@
<?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="router" uuid="7E530655-8578-4AF8-99CA-175A0025D843" library_target="router">
<target name="router">
<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">
<assertions precondition="true"/>
</option>
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
<library name="base_extension" location="$ISE_LIBRARY\library\base_extension\base_extension-safe.ecf"/>
<library name="ewsgi" location="..\..\ewsgi\ewsgi-safe.ecf" readonly="false"/>
<library name="http" location="..\..\..\protocol\http\http-safe.ecf"/>
<library name="uri_template" location="..\..\..\protocol\uri_template\uri_template-safe.ecf" readonly="false"/>
<library name="time" location="$ISE_LIBRARY\library\time\time-safe.ecf"/>
<cluster name="src" location="src\" recursive="true"/>
</target>
<target name="ewsgi_compliant_router">
<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">
<assertions precondition="true"/>
</option>
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
<library name="base_extension" location="$ISE_LIBRARY\library\base_extension\base_extension-safe.ecf"/>
<library name="ewsgi" location="..\..\ewsgi\ewsgi_spec-safe.ecf" readonly="false"/>
<library name="http" location="..\..\..\protocol\http\http-safe.ecf"/>
<library name="uri_template" location="..\..\..\protocol\uri_template\uri_template-safe.ecf" readonly="false"/>
<library name="time" location="$ISE_LIBRARY\library\time\time-safe.ecf"/>
<cluster name="src" location="src\" recursive="true"/>
</target>
</system>

View File

@@ -0,0 +1,39 @@
<?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="router" uuid="7E530655-8578-4AF8-99CA-175A0025D843" library_target="router">
<target name="router">
<root all_classes="true"/>
<file_rule>
<exclude>/.git$</exclude>
<exclude>/EIFGENs$</exclude>
<exclude>/.svn$</exclude>
</file_rule>
<option warning="true" full_class_checking="true">
<assertions precondition="true"/>
</option>
<library name="base" location="$ISE_LIBRARY\library\base\base.ecf"/>
<library name="base_extension" location="$ISE_LIBRARY\library\base_extension\base_extension.ecf"/>
<library name="ewsgi" location="..\..\ewsgi\ewsgi.ecf"/>
<library name="http" location="..\..\..\protocol\http\http.ecf"/>
<library name="uri_template" location="..\..\..\protocol\uri_template\uri_template.ecf"/>
<library name="time" location="$ISE_LIBRARY\library\time\time.ecf"/>
<cluster name="src" location="src\" recursive="true"/>
</target>
<target name="ewsgi_compliant_router">
<root all_classes="true"/>
<file_rule>
<exclude>/.git$</exclude>
<exclude>/EIFGENs$</exclude>
<exclude>/.svn$</exclude>
</file_rule>
<option warning="true" full_class_checking="true">
<assertions precondition="true"/>
</option>
<library name="base" location="$ISE_LIBRARY\library\base\base.ecf"/>
<library name="base_extension" location="$ISE_LIBRARY\library\base_extension\base_extension.ecf"/>
<library name="ewsgi" location="..\..\ewsgi\ewsgi.ecf"/>
<library name="http" location="..\..\..\protocol\http\http.ecf"/>
<library name="uri_template" location="..\..\..\protocol\uri_template\uri_template.ecf"/>
<library name="time" location="$ISE_LIBRARY\library\time\time.ecf"/>
<cluster name="src" location="src\" recursive="true"/>
</target>
</system>

View File

@@ -0,0 +1,61 @@
note
description: "Summary description for {ROUTED_APPLICATION}."
author: ""
date: "$Date$"
revision: "$Revision$"
deferred class
ROUTED_APPLICATION
feature -- Setup
initialize_router
-- Initialize `router'
do
create_router
setup_router
end
create_router
-- Create `router'
deferred
ensure
router_created: router /= Void
end
setup_router
-- Setup `router'
require
router_created: router /= Void
deferred
end
router: REQUEST_ROUTER
-- Request router
feature -- Execution
execute (req: EWSGI_REQUEST; res: EWSGI_RESPONSE_BUFFER)
do
if attached router.dispatch (req, res) as r then
--| done
else
execute_default (req, res)
end
end
execute_default (req: EWSGI_REQUEST; res: EWSGI_RESPONSE_BUFFER)
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

@@ -0,0 +1,95 @@
note
description: "Summary description for {REQUEST_HANDLER_CONTEXT}."
author: ""
date: "$Date$"
revision: "$Revision$"
deferred class
REQUEST_HANDLER_CONTEXT
inherit
ANY
REQUEST_FORMAT_UTILITY
export
{NONE} all
end
feature -- Access
request: EWSGI_REQUEST
-- Associated request
path: STRING
-- ???
request_content_type (content_type_supported: detachable ARRAY [STRING]): detachable STRING
local
s: detachable STRING
i,n: INTEGER
do
Result := request.environment.content_type
if Result = Void then
s := request.environment.http_accept
if s /= Void then
if attached accepted_content_types (request) as l_accept_lst then
from
l_accept_lst.start
until
l_accept_lst.after or Result /= Void
loop
s := l_accept_lst.item
if content_type_supported /= Void then
from
i := content_type_supported.lower
n := content_type_supported.upper
until
i > n or Result /= Void
loop
if content_type_supported[i].same_string (s) then
Result := s
end
i := i + 1
end
end
l_accept_lst.forth
end
end
end
end
end
feature -- Query
path_parameter (a_name: STRING): detachable STRING_32
-- Parameter value for path variable `a_name'
deferred
end
query_parameter (a_name: STRING): detachable STRING_32
-- Parameter value for query variable `a_name'
--| i.e after the ? character
deferred
end
parameter (a_name: STRING): detachable STRING_32
-- Any parameter value for variable `a_name'
-- URI template parameter and query parameters
do
Result := query_parameter (a_name)
if Result = Void then
Result := path_parameter (a_name)
end
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

@@ -0,0 +1,45 @@
note
description: "Summary description for {REQUEST_URI_HANDLER_CONTEXT}."
author: ""
date: "$Date$"
revision: "$Revision$"
class
REQUEST_URI_HANDLER_CONTEXT
inherit
REQUEST_HANDLER_CONTEXT
create
make
feature {NONE} -- Initialization
make (req: EWSGI_REQUEST; p: like path)
do
request := req
path := p
end
feature -- Query
path_parameter (a_name: STRING): detachable STRING_32
do
end
query_parameter (a_name: STRING): detachable STRING_32
do
Result := request.parameter (a_name)
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

@@ -0,0 +1,57 @@
note
description: "Summary description for {REQUEST_URI_TEMPLATE_HANDLER_CONTEXT}."
author: ""
date: "$Date$"
revision: "$Revision$"
class
REQUEST_URI_TEMPLATE_HANDLER_CONTEXT
inherit
REQUEST_HANDLER_CONTEXT
create
make
feature {NONE} -- Initialization
make (req: EWSGI_REQUEST; tpl: URI_TEMPLATE; tpl_res: URI_TEMPLATE_MATCH_RESULT; p: like path)
do
request := req
uri_template := tpl
uri_template_match := tpl_res
path := p
end
feature -- Access
uri_template: URI_TEMPLATE
uri_template_match: URI_TEMPLATE_MATCH_RESULT
feature -- Query
path_parameter (a_name: STRING): detachable STRING_32
do
Result := uri_template_match.url_decoded_path_variable (a_name)
end
query_parameter (a_name: STRING): detachable STRING_32
do
Result := uri_template_match.url_decoded_query_variable (a_name)
if Result = Void then
Result := request.parameter (a_name)
end
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

@@ -0,0 +1,45 @@
note
description: "Summary description for REQUEST_AGENT_HANDLER."
author: ""
date: "$Date$"
revision: "$Revision$"
class
REQUEST_AGENT_HANDLER
inherit
REQUEST_HANDLER
create
make
feature -- Initialization
make (act: like action)
do
action := act
initialize
end
feature -- Access
action: PROCEDURE [ANY, TUPLE [ctx: REQUEST_HANDLER_CONTEXT; req: EWSGI_REQUEST; res: EWSGI_RESPONSE_BUFFER]]
feature -- Execution
execute_application (ctx: REQUEST_HANDLER_CONTEXT; req: EWSGI_REQUEST; res: EWSGI_RESPONSE_BUFFER)
do
action.call ([ctx, req, res])
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

@@ -0,0 +1,310 @@
note
description: "Summary description for {REQUEST_HANDLER}."
author: ""
date: "$Date$"
revision: "$Revision$"
deferred class
REQUEST_HANDLER
feature {NONE} -- Initialization
initialize
-- Initialize various attributes
do
end
feature -- Access
description: detachable STRING
-- Optional descriptiong
feature -- Status report
is_valid_context (req: EWSGI_REQUEST): BOOLEAN
-- Is `req' valid context for current handler?
do
Result := request_method_name_supported (req.environment.request_method)
end
feature -- Execution
execute (a_hdl_context: REQUEST_HANDLER_CONTEXT; req: EWSGI_REQUEST; res: EWSGI_RESPONSE_BUFFER)
-- Execute request handler
require
is_valid_context: is_valid_context (req)
local
rescued: BOOLEAN
do
if not rescued then
if request_method_name_supported (req.environment.request_method) then
pre_execute (req)
execute_application (a_hdl_context, req, res)
post_execute (req, res)
else
execute_method_not_allowed (a_hdl_context, req, res)
end
else
rescue_execute (req, res)
end
rescue
rescued := True
retry
end
execute_method_not_allowed (a_hdl_context: REQUEST_HANDLER_CONTEXT; req: EWSGI_REQUEST; res: EWSGI_RESPONSE_BUFFER)
local
s: STRING
lst: LIST [STRING]
do
res.set_status_code ({HTTP_STATUS_CODE}.method_not_allowed)
create s.make (25)
from
lst := supported_request_method_names
lst.start
until
lst.after
loop
s.append_string (lst.item)
if not lst.islast then
s.append_character (',')
s.append_character (' ')
end
lst.forth
end
res.write_header ({HTTP_STATUS_CODE}.method_not_allowed, <<["Allow", s]>>)
end
execute_application (a_hdl_context: REQUEST_HANDLER_CONTEXT; req: EWSGI_REQUEST; res: EWSGI_RESPONSE_BUFFER)
-- Execute request handler
deferred
end
pre_execute (req: EWSGI_REQUEST)
-- Operation processed before `execute'
do
--| To be redefined if needed
end
post_execute (req: EWSGI_REQUEST; res: EWSGI_RESPONSE_BUFFER)
-- Operation processed after `execute'
do
--| To be redefined if needed
end
rescue_execute (req: EWSGI_REQUEST; res: EWSGI_RESPONSE_BUFFER)
-- Operation processed after a rescue
do
--| To be redefined if needed
post_execute (req, res)
end
feature -- Execution: report
-- execution_information (req: EWSGI_REQUEST): detachable REQUEST_HANDLER_CONTEXT
-- -- Execution information related to the request
-- do
-- if attached path_information (req, req.environment.path_info) as info then
-- create Result.make (req.environment.path_info)
-- end
-- end
-- path_information (req: EWSGI_REQUEST; a_rq_path: STRING): detachable TUPLE [format: detachable STRING; arguments: detachable STRING]
-- -- Information related to `a_path'
-- local
-- l_rq_path: STRING
-- i,p,n: INTEGER
-- l_format, l_args: detachable STRING
-- do
-- l_rq_path := a_rq_path
-- if l_rq_path.count > 0 and then l_rq_path[1] /= '/' then
-- l_rq_path := "/" + l_rq_path
-- end
-- n := l_rq_path.count
-- i := req.environment.path_info.count + 1
-- if format_located_before_parameters then
-- --| path = app-path{.format}/parameters
-- if l_rq_path.valid_index (i) and then l_rq_path[i] = '.' then
-- p := l_rq_path.index_of ('/', i + 1)
-- if p = 0 then
-- p := n + 1
-- else
-- l_args := l_rq_path.substring (p + 1, n)
-- end
-- l_format := l_rq_path.substring (i + 1, p - 1)
-- elseif n > i then
-- check l_rq_path[i] = '/' end
-- l_args := l_rq_path.substring (i + 1, n)
-- end
-- elseif format_located_after_parameters then
-- --| path = app-path/parameters{.format}
-- p := l_rq_path.last_index_of ('.', n)
-- if p > i then
-- l_format := l_rq_path.substring (p + 1, n)
-- l_args := l_rq_path.substring (i + 1, p - 1)
-- elseif n > i then
-- check l_rq_path[i] = '/' end
-- l_format := Void
-- l_args := l_rq_path.substring (i + 1, n)
-- end
-- end
-- if l_format /= Void or l_args /= Void then
-- Result := [l_format, l_args]
-- end
-- end
url (req: EWSGI_REQUEST; args: detachable STRING; abs: BOOLEAN): STRING
-- Associated url based on `path' and `args'
-- if `abs' then return absolute url
local
s: detachable STRING
do
s := args
if s /= Void and then s.count > 0 then
if s[1] /= '/' then
s := req.environment.request_uri + "/" + s
else
s := req.environment.request_uri + s
end
else
s := req.environment.request_uri
end
if abs then
Result := req.absolute_script_url (s)
else
Result := req.script_url (s)
end
ensure
result_attached: Result /= Void
end
feature -- Element change
set_description (s: like description)
-- Set `description' to `s'
do
description := s
end
feature {NONE} -- Implementation
supported_request_methods: INTEGER
-- Support request method such as GET, POST, ...
feature {NONE} -- Status report
request_method_id_supported (a_id: INTEGER): BOOLEAN
do
Result := (supported_request_methods & a_id) = a_id
end
request_method_name_supported (n: STRING): BOOLEAN
-- Is request method `n' supported?
do
Result := request_method_id_supported (request_method_constants.method_id (n))
end
request_method_constants: HTTP_REQUEST_METHOD_CONSTANTS
once
create Result
end
feature -- Status report
supported_request_method_names: LIST [STRING]
-- Support request method such as GET, POST, ...
do
create {LINKED_LIST [STRING]} Result.make
if method_get_supported then
Result.extend (request_method_constants.method_get_name)
end
if method_post_supported then
Result.extend (request_method_constants.method_post_name)
end
if method_put_supported then
Result.extend (request_method_constants.method_put_name)
end
if method_delete_supported then
Result.extend (request_method_constants.method_delete_name)
end
if method_head_supported then
Result.extend (request_method_constants.method_head_name)
end
end
method_get_supported: BOOLEAN
do
Result := request_method_id_supported ({HTTP_REQUEST_METHOD_CONSTANTS}.method_get)
end
method_post_supported: BOOLEAN
do
Result := request_method_id_supported ({HTTP_REQUEST_METHOD_CONSTANTS}.method_post)
end
method_put_supported: BOOLEAN
do
Result := request_method_id_supported ({HTTP_REQUEST_METHOD_CONSTANTS}.method_put)
end
method_delete_supported: BOOLEAN
do
Result := request_method_id_supported ({HTTP_REQUEST_METHOD_CONSTANTS}.method_delete)
end
method_head_supported: BOOLEAN
do
Result := request_method_id_supported ({HTTP_REQUEST_METHOD_CONSTANTS}.method_head)
end
feature -- Element change: request methods
reset_supported_request_methods
do
supported_request_methods := 0
end
enable_request_method_get
do
enable_request_method ({HTTP_REQUEST_METHOD_CONSTANTS}.method_get)
end
enable_request_method_post
do
enable_request_method ({HTTP_REQUEST_METHOD_CONSTANTS}.method_post)
end
enable_request_method_put
do
enable_request_method ({HTTP_REQUEST_METHOD_CONSTANTS}.method_put)
end
enable_request_method_delete
do
enable_request_method ({HTTP_REQUEST_METHOD_CONSTANTS}.method_delete)
end
enable_request_method_head
do
enable_request_method ({HTTP_REQUEST_METHOD_CONSTANTS}.method_head)
end
enable_request_method (m: INTEGER)
do
supported_request_methods := supported_request_methods | m
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

@@ -0,0 +1,87 @@
note
description: "Summary description for {REQUEST_ROUTER}."
author: ""
date: "$Date$"
revision: "$Revision$"
deferred class
REQUEST_ROUTER
feature -- Registration
map_default (r: like default_handler)
-- Map default handler
-- If no route/handler is found,
-- then use `default_handler' as default if not Void
do
default_handler := r
end
map (a_id: STRING; h: REQUEST_HANDLER)
-- Map handler `h' with `a_id'
deferred
end
map_agent (a_id: STRING; a_action: like {REQUEST_AGENT_HANDLER}.action)
local
h: REQUEST_AGENT_HANDLER
do
create h.make (a_action)
map (a_id, h)
end
feature -- Execution
dispatch (req: EWSGI_REQUEST; res: EWSGI_RESPONSE_BUFFER): detachable REQUEST_HANDLER
-- Dispatch `req, res' to the associated handler
-- And return this handler
-- If Result is Void, this means no handler was found.
local
d: like handler
ctx: detachable REQUEST_HANDLER_CONTEXT
do
d := handler (req)
if d /= Void then
Result := d.handler
ctx := d.context
else
Result := default_handler
end
if Result /= Void then
if ctx = Void then
check is_default_handler: Result = default_handler end
create {REQUEST_URI_HANDLER_CONTEXT} ctx.make (req, "/")
end
Result.execute (ctx, req, res)
end
ensure
result_void_implie_no_default: Result = Void implies default_handler = Void
end
feature {NONE} -- Access: Implementation
handler (req: EWSGI_REQUEST): detachable TUPLE [handler: REQUEST_HANDLER; context: REQUEST_HANDLER_CONTEXT]
-- Handler whose map matched with `req'
require
req_valid: req /= Void and then req.environment.path_info /= Void
deferred
ensure
req_path_info_unchanged: req.environment.path_info.same_string (old req.environment.path_info)
end
feature {NONE} -- Implementation
default_handler: detachable REQUEST_HANDLER
-- Default handler
;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

@@ -0,0 +1,169 @@
note
description: "Summary description for {REQUEST_URI_ROUTER}."
author: ""
date: "$Date$"
revision: "$Revision$"
class
REQUEST_URI_ROUTER
inherit
REQUEST_ROUTER
ITERABLE [REQUEST_HANDLER]
redefine
new_cursor
end
create
make
feature -- Initialization
make (n: INTEGER)
do
create handlers.make (n)
handlers.compare_objects
end
feature -- Registration
map (p: STRING; h: REQUEST_HANDLER)
do
handlers.force (h, p)
end
feature {NONE} -- Access: Implementation
handler (req: EWSGI_REQUEST): detachable TUPLE [handler: REQUEST_HANDLER; context: REQUEST_HANDLER_CONTEXT]
local
h: detachable REQUEST_HANDLER
ctx: detachable REQUEST_HANDLER_CONTEXT
do
h := handler_by_path (req.environment.path_info)
if h = Void then
if attached smart_handler_by_path (req.environment.path_info) as info then
h := info.handler
ctx := handler_context (info.path, req)
end
end
if h /= Void then
if h.is_valid_context (req) then
if ctx = Void then
ctx := handler_context (Void, req)
end
Result := [h, ctx]
else
Result := Void
end
end
end
smart_handler (req: EWSGI_REQUEST): detachable TUPLE [path: STRING; handler: REQUEST_HANDLER]
require
req_valid: req /= Void and then req.environment.path_info /= Void
do
Result := smart_handler_by_path (req.environment.path_info)
ensure
req_path_info_unchanged: req.environment.path_info.same_string (old req.environment.path_info)
end
handler_by_path (a_path: STRING): detachable REQUEST_HANDLER
require
a_path_valid: a_path /= Void
do
Result := handlers.item (context_path (a_path))
ensure
a_path_unchanged: a_path.same_string (old a_path)
end
smart_handler_by_path (a_path: STRING): detachable TUPLE [path: STRING; handler: REQUEST_HANDLER]
require
a_path_valid: a_path /= Void
local
p: INTEGER
l_context_path, l_path: STRING
h: detachable REQUEST_HANDLER
do
l_context_path := context_path (a_path)
from
p := l_context_path.count + 1
until
p <= 1 or Result /= Void
loop
l_path := l_context_path.substring (1, p - 1)
h := handler_by_path (l_path)
if h /= Void then
Result := [l_path, h]
else
p := l_context_path.last_index_of ('/', p - 1)
end
variant
p
end
ensure
a_path_unchanged: a_path.same_string (old a_path)
end
feature -- Context factory
handler_context (p: detachable STRING; req: EWSGI_REQUEST): REQUEST_URI_HANDLER_CONTEXT
do
if p /= Void then
create Result.make (req, p)
else
create Result.make (req, req.environment.path_info)
end
end
feature -- Access
new_cursor: HASH_TABLE_ITERATION_CURSOR [REQUEST_HANDLER, STRING]
-- Fresh cursor associated with current structure
do
Result := handlers.new_cursor
end
item (a_path: STRING): detachable REQUEST_HANDLER
do
Result := handler_by_path (a_path)
end
feature {NONE} -- Implementation
handlers: HASH_TABLE [REQUEST_HANDLER, STRING]
-- Handlers
context_path (a_path: STRING): STRING
-- Prepared path from context which match requirement
-- i.e: not empty, starting with '/'
local
p: INTEGER
do
Result := a_path
if Result.is_empty then
Result := "/"
else
if Result[1] /= '/' then
Result := "/" + Result
end
p := Result.index_of ('.', 1)
if p > 0 then
Result := Result.substring (1, p - 1)
end
end
ensure
result_not_empty: not Result.is_empty
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

@@ -0,0 +1,138 @@
note
description: "Summary description for {REQUEST_URI_TEMPLATE_ROUTER}."
author: ""
date: "$Date$"
revision: "$Revision$"
class
REQUEST_URI_TEMPLATE_ROUTER
inherit
REQUEST_ROUTER
ITERABLE [REQUEST_HANDLER]
redefine
new_cursor
end
create
make
feature -- Initialization
make (n: INTEGER)
do
create handlers.make (n)
create templates.make (n)
handlers.compare_objects
end
feature -- Registration
map_with_uri_template (uri: URI_TEMPLATE; h: REQUEST_HANDLER)
do
handlers.force (h, uri.template)
templates.force (uri, uri.template)
end
map (tpl: STRING; h: REQUEST_HANDLER)
local
uri: URI_TEMPLATE
do
create uri.make (tpl)
map_with_uri_template (uri, h)
end
feature {NONE} -- Access: Implementation
handler (req: EWSGI_REQUEST): detachable TUPLE [handler: REQUEST_HANDLER; context: REQUEST_HANDLER_CONTEXT]
local
ctx: detachable REQUEST_URI_TEMPLATE_HANDLER_CONTEXT
l_handlers: like handlers
t: STRING
p: STRING
do
p := req.environment.request_uri
from
l_handlers := handlers
l_handlers.start
until
l_handlers.after or Result /= Void
loop
t := l_handlers.key_for_iteration
if attached templates.item (t) as tpl and then
attached tpl.match (p) as res
then
ctx := handler_context (p, req, tpl, res)
Result := [l_handlers.item_for_iteration, ctx]
end
l_handlers.forth
end
end
feature -- Context factory
handler_context (p: detachable STRING; req: EWSGI_REQUEST; tpl: URI_TEMPLATE; tpl_res: URI_TEMPLATE_MATCH_RESULT): REQUEST_URI_TEMPLATE_HANDLER_CONTEXT
do
if p /= Void then
create Result.make (req, tpl, tpl_res, p)
else
create Result.make (req, tpl, tpl_res, req.environment.path_info)
end
end
feature -- Access
new_cursor: HASH_TABLE_ITERATION_CURSOR [REQUEST_HANDLER, STRING]
-- Fresh cursor associated with current structure
do
Result := handlers.new_cursor
end
item (a_path: STRING): detachable REQUEST_HANDLER
do
Result := handlers.item (a_path)
end
feature {NONE} -- Implementation
handlers: HASH_TABLE [REQUEST_HANDLER, STRING]
-- Handlers indexed by the template expression
-- see `templates'
templates: HASH_TABLE [URI_TEMPLATE, STRING]
-- URI Template indexed by the template expression
context_path (a_path: STRING): STRING
-- Prepared path from context which match requirement
-- i.e: not empty, starting with '/'
local
p: INTEGER
do
Result := a_path
if Result.is_empty then
Result := "/"
else
if Result[1] /= '/' then
Result := "/" + Result
end
p := Result.index_of ('.', 1)
if p > 0 then
Result := Result.substring (1, p - 1)
end
end
ensure
result_not_empty: not Result.is_empty
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

@@ -0,0 +1,89 @@
note
description: "Summary description for {REQUEST_FORMAT_UTILITY}."
author: ""
date: "$Date$"
revision: "$Revision$"
class
REQUEST_FORMAT_UTILITY
feature -- Access
accepted_content_types (req: EWSGI_REQUEST): detachable ARRAYED_LIST [STRING]
local
l_accept: detachable STRING
s,q: STRING
p: INTEGER
lst: LIST [STRING]
qs: QUICK_SORTER [STRING]
do
l_accept := req.environment.http_accept
--TEST l_accept := "text/html,application/xhtml+xml;q=0.6,application/xml;q=0.2,text/plain;q=0.5,*/*;q=0.8"
if l_accept /= Void then
lst := l_accept.split (',')
create Result.make (lst.count)
from
lst.start
until
lst.after
loop
s := lst.item
p := s.substring_index (";q=", 1)
if p > 0 then
q := s.substring (p + 3, s.count)
s := s.substring (1, p - 1)
else
q := "1.0"
end
Result.force (q + ":" + s)
lst.forth
end
create qs.make (create {COMPARABLE_COMPARATOR [STRING]})
qs.reverse_sort (Result)
from
Result.start
until
Result.after
loop
s := Result.item
p := s.index_of (':', 1)
if p > 0 then
s.remove_head (p)
else
check should_have_colon: False end
end
Result.forth
end
end
end
feature {NONE} -- Implementation
string_in_array (arr: ARRAY [STRING]; s: STRING): BOOLEAN
local
i,n: INTEGER
do
from
i := arr.lower
n := arr.upper
until
i > n or Result
loop
Result := s.same_string (arr[i])
i := i + 1
end
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