Compare commits

...

6 Commits

Author SHA1 Message Date
0cecb9594c Fixed signature of {HTTPD_CONFIGURATION_I}.set_ca_key . 2016-06-16 10:37:26 +02:00
e384a6d6ed Make it easier to reuse the http network classes.
This is to make it easier for websocket solution to reuse httpd implementation.
2016-06-16 10:23:30 +02:00
71a5c086a5 Moved httpd from src to lib, under standalone connector. 2016-06-15 18:04:00 +02:00
dfa60bf8f5 Prepared httpd_stream to be useable for client too.
Fixed obsolete tests/dev compilation (mainly to avoid wrong failure reports).
added package.iron files.
2016-06-15 17:56:22 +02:00
113aa69efc Added advanced settings for standalone connector
- max_concurrent_connections=100
- keep_alive_timeout=15
- max_tcp_clients=100
- socket_timeout=300
- max_keep_alive_requests=300
And then can be set via the options as well, and via .ini file.
Also improved the verbose console output system.
2016-06-15 09:19:23 +02:00
af5fc75743 Using passive regions.
Improve connector options mainly for standalone connector.
Updated "simple" example to return a timestamp.
2016-06-14 16:01:37 +02:00
57 changed files with 885 additions and 399 deletions

View File

@@ -20,9 +20,10 @@ feature {NONE} -- Initialization
initialize
-- Initialize current service.
do
set_service_option ("port", 9090)
-- Specific to `standalone' connector (the EiffelWeb server).
-- See `{WSF_STANDALONE_SERVICE_LAUNCHER}.initialize'
set_service_option ("port", 9090)
import_service_options (create {WSF_SERVICE_LAUNCHER_OPTIONS_FROM_INI}.make_from_file ("simple.ini"))
end
end

View File

@@ -17,10 +17,13 @@ feature -- Basic operations
execute
local
s: STRING
do
-- To send a response we need to setup, the status code and
-- the response headers.
s := "Hello World!"
dt: HTTP_DATE
do
-- To send a response we need to setup, the status code and
-- the response headers.
s := "Hello World!"
create dt.make_now_utc
s.append (" (UTC time is " + dt.rfc850_string + ").")
response.put_header ({HTTP_STATUS_CODE}.ok, <<["Content-Type", "text/html"], ["Content-Length", s.count.out]>>)
response.set_status_code ({HTTP_STATUS_CODE}.ok)
response.header.put_content_type_text_html

View File

@@ -1,35 +1,28 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-13-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-13-0 http://www.eiffel.com/developers/xml/configuration-1-13-0.xsd" name="simple" uuid="C28C4F53-9963-46C0-A080-8F13E94E7486" library_target="simple">
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-15-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-15-0 http://www.eiffel.com/developers/xml/configuration-1-15-0.xsd" name="simple" uuid="C28C4F53-9963-46C0-A080-8F13E94E7486" library_target="simple">
<target name="common" abstract="true">
<file_rule>
<exclude>/EIFGENs$</exclude>
<exclude>/CVS$</exclude>
<exclude>/.svn$</exclude>
<exclude>/CVS$</exclude>
<exclude>/EIFGENs$</exclude>
</file_rule>
<option warning="true" full_class_checking="false" is_attached_by_default="true" void_safety="transitional" syntax="transitional">
<option warning="true" full_class_checking="false" is_attached_by_default="true" is_obsolete_routine_type="true" void_safety="transitional" syntax="transitional">
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
</option>
<setting name="console_application" value="true"/>
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
<library name="http" location="..\..\library\network\protocol\http\http-safe.ecf"/>
<library name="wsf" location="..\..\library\server\wsf\wsf-safe.ecf"/>
</target>
<target name="simple_standalone" extends="common">
<root class="APPLICATION" feature="make_and_launch"/>
<option warning="true" is_attached_by_default="true" void_safety="transitional" syntax="transitional">
<option warning="true" is_attached_by_default="true" void_safety="all" syntax="transitional">
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
</option>
<setting name="concurrency" value="thread"/>
<setting name="concurrency" value="scoop"/>
<library name="default_standalone" location="..\..\library\server\wsf\default\standalone-safe.ecf"/>
<cluster name="simple" location=".\" recursive="true"/>
</target>
<target name="simple_nino" extends="common">
<root class="APPLICATION" feature="make_and_launch"/>
<option warning="true" is_attached_by_default="true" void_safety="transitional" syntax="transitional">
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
</option>
<library name="default_nino" location="..\..\library\server\wsf\default\nino-safe.ecf"/>
<cluster name="simple" location=".\" recursive="true"/>
</target>
<target name="simple_cgi" extends="common">
<root class="APPLICATION" feature="make_and_launch"/>
<option warning="true" is_attached_by_default="true" void_safety="transitional" syntax="transitional">

View File

@@ -0,0 +1,8 @@
verbose=true
verbose_level=ALERT
port=9090
#max_concurrent_connections=100
#keep_alive_timeout=15
#max_tcp_clients=100
#socket_timeout=300
#max_keep_alive_requests=300

View File

@@ -23,8 +23,8 @@ feature {NONE} -- Initialization
local
n: INTEGER
p: like pool
do
n := max_concurrent_connections (server)
do
n := max_concurrent_connections (server).max (1) -- At least one processor!
create p.make (n)
initialize_pool (p, n)
pool := p

View File

@@ -23,7 +23,7 @@ feature {NONE} -- Initialization
local
n: INTEGER
do
n := max_concurrent_connections (server)
n := max_concurrent_connections (server).max (1) -- At least one thread!
create pool.make (n.to_natural_32)
end

View File

@@ -10,17 +10,26 @@ feature {NONE} -- Initialization
make
do
http_server_port := 80
max_concurrent_connections := 100
max_tcp_clients := 100
socket_accept_timeout := 1_000
socket_connect_timeout := 5_000
keep_alive_timeout := 5
http_server_port := default_http_server_port
max_concurrent_connections := default_max_concurrent_connections
max_tcp_clients := default_max_tcp_clients
socket_timeout := default_socket_timeout
keep_alive_timeout := default_keep_alive_timeout
max_keep_alive_requests := default_max_keep_alive_requests
is_secure := False
create ca_crt.make_empty
create ca_key.make_empty
end
feature -- Defaults
default_http_server_port: INTEGER = 80
default_max_concurrent_connections: INTEGER = 100
default_max_tcp_clients: INTEGER = 100
default_socket_timeout: INTEGER = 300 -- seconds
default_keep_alive_timeout: INTEGER = 15 -- seconds
default_max_keep_alive_requests: INTEGER = 100
feature -- Access
Server_details: STRING_8
@@ -31,35 +40,62 @@ feature -- Access
http_server_name: detachable READABLE_STRING_8 assign set_http_server_name
http_server_port: INTEGER assign set_http_server_port
max_tcp_clients: INTEGER assign set_max_tcp_clients
-- Listen on socket for at most `queue' connections.
socket_timeout: INTEGER assign set_socket_timeout
-- Amount of seconds that the server waits for receipts and transmissions during communications.
-- note: with timeout of 0, socket can wait for ever.
-- By default: 300 seconds, which is appropriate for most situations.
max_concurrent_connections: INTEGER assign set_max_concurrent_connections
socket_accept_timeout: INTEGER assign set_socket_accept_timeout
socket_connect_timeout: INTEGER assign set_socket_connect_timeout
-- Max number of concurrent connections.
force_single_threaded: BOOLEAN assign set_force_single_threaded
do
Result := (max_concurrent_connections = 0)
Result := max_concurrent_connections = 0
end
is_verbose: BOOLEAN assign set_is_verbose
-- Display verbose message to the output?
verbose_level: INTEGER assign set_verbose_level
-- Verbosity of output.
keep_alive_timeout: INTEGER assign set_keep_alive_timeout
-- Persistent connection timeout
-- Persistent connection timeout.
-- Number of seconds the server waits after a request has been served before it closes the connection.
-- Timeout unit in Seconds.
-- By default: 5 seconds.
max_keep_alive_requests: INTEGER assign set_max_keep_alive_requests
-- Maximum number of requests allowed per persistent connection.
-- Recommended a high setting.
-- To disable KeepAlive, set `max_keep_alive_requests' to 0.
-- By default: 100 .
has_ssl_support: BOOLEAN
-- Has SSL support?
deferred
end
request_settings: HTTPD_REQUEST_SETTINGS
do
Result.is_verbose := is_verbose
Result.verbose_level := verbose_level
Result.timeout := socket_timeout
Result.keep_alive_timeout := keep_alive_timeout
Result.max_keep_alive_requests := max_keep_alive_requests
end
feature -- Access: SSL
is_secure: BOOLEAN
-- Is SSL/TLS session?.
ca_crt: STRING
ca_crt: IMMUTABLE_STRING_8
-- the signed certificate.
ca_key: STRING
ca_key: IMMUTABLE_STRING_8
-- private key to the certificate.
ssl_protocol: NATURAL
@@ -74,7 +110,6 @@ feature -- Element change
else
create {IMMUTABLE_STRING_8} http_server_name.make_from_separate (v)
end
--| Missing postcondition.
end
unset_http_server_name
@@ -109,23 +144,35 @@ feature -- Element change
max_concurrent_connections_set : max_concurrent_connections = v
end
set_socket_accept_timeout (v: like socket_accept_timeout)
-- Set `socket_accept_timeout' with `v'
set_socket_timeout (a_nb_seconds: like socket_timeout)
-- Set `socket_timeout' with `a_nb_seconds'
do
socket_accept_timeout := v
socket_timeout := a_nb_seconds
ensure
socket_accept_timeout_set: socket_accept_timeout = v
socket_timeout_set: socket_timeout = a_nb_seconds
end
set_socket_connect_timeout (v: like socket_connect_timeout)
-- Set `socket_connect_timeout' with `v'
set_keep_alive_timeout (a_seconds: like keep_alive_timeout)
-- Set `keep_alive_timeout' with `a_seconds'
do
socket_connect_timeout := v
keep_alive_timeout := a_seconds
ensure
socket_connect_timeout_set: socket_connect_timeout = v
keep_alive_timeout_set: keep_alive_timeout = a_seconds
end
set_max_keep_alive_requests (nb: like max_keep_alive_requests)
-- Set `max_keep_alive_requests' with `nb'
do
max_keep_alive_requests := nb
ensure
max_keep_alive_requests_set: max_keep_alive_requests = nb
end
set_force_single_threaded (v: like force_single_threaded)
-- Force server to handle incoming request in a single thread.
-- i.e set max_concurrent_connections to 0!
obsolete
"Use set_max_concurrent_connections (0) [June/2016]"
do
if v then
set_max_concurrent_connections (0)
@@ -143,12 +190,12 @@ feature -- Element change
is_verbose_set: is_verbose = b
end
set_keep_alive_timeout (a_seconds: like keep_alive_timeout)
-- Set `keep_alive_timeout' with `a_seconds'
set_verbose_level (lev: INTEGER)
-- Set `verbose_level' to `lev'.
do
keep_alive_timeout := a_seconds
verbose_level := lev
ensure
keep_alive_timeout_set: keep_alive_timeout = a_seconds
verbose_level_set: verbose_level = lev
end
mark_secure
@@ -171,20 +218,16 @@ feature -- Element change
feature -- Element change
set_ca_crt (a_value: STRING)
-- Set `ca_crt' with `a_value'
set_ca_crt (a_value: separate READABLE_STRING_8)
-- Set `ca_crt' from `a_value'.
do
ca_crt := a_value
ensure
ca_crt_set: ca_crt = a_value
create ca_crt.make_from_separate (a_value)
end
set_ca_key (a_value: STRING)
-- Set `ca_key' with `a_value'
set_ca_key (a_value: separate READABLE_STRING_8)
-- Set `ca_key' with `a_value'.
do
ca_key := a_value
ensure
ca_key_set: ca_key = a_value
create ca_key.make_from_separate (a_value)
end
set_ssl_protocol (a_version: NATURAL)

View File

@@ -0,0 +1,64 @@
note
description: "[
Request settings for the standalone HTTPd server.
]"
author: "$Author$"
date: "$Date$"
revision: "$Revision$"
expanded class
HTTPD_REQUEST_SETTINGS
feature -- Access
is_verbose: BOOLEAN assign set_is_verbose
-- Is verbose?
verbose_level: INTEGER assign set_verbose_level
-- Verbosity of output.
timeout: INTEGER assign set_timeout
-- Amount of seconds that the server waits for receipts and transmissions during communications.
keep_alive_timeout: INTEGER assign set_keep_alive_timeout
-- Keep-alive timeout, also known as persistent-connection timeout.
-- Number of seconds the server waits after a request has been served before it closes the connection.
-- Unit in Seconds.
max_keep_alive_requests: INTEGER assign set_max_keep_alive_requests
-- Maximum number of requests allowed per persistent connection.
feature -- Change
set_is_verbose (b: BOOLEAN)
-- Set `is_verbose' to `b'.
do
is_verbose := b
end
set_verbose_level (lev: INTEGER)
-- Set `verbose_level' to `lev'.
do
verbose_level := lev
end
set_timeout (a_timeout_in_seconds: INTEGER)
-- Set `timeout' to `a_timeout_in_seconds'.
do
timeout := a_timeout_in_seconds
end
set_keep_alive_timeout (a_timeout_in_seconds: INTEGER)
-- Set `keep_alive_timeout' to `a_timeout_in_seconds'.
do
keep_alive_timeout := a_timeout_in_seconds
end
set_max_keep_alive_requests (nb: like max_keep_alive_requests)
-- Set `max_keep_alive_requests' with `nb'
do
max_keep_alive_requests := nb
end
end

View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-15-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-15-0 http://www.eiffel.com/developers/xml/configuration-1-15-0.xsd" name="http_network" uuid="56DAA1CE-0A2E-451A-BFC9-7821578E79F0" library_target="http_network">
<target name="http_network">
<root all_classes="true"/>
<file_rule>
<exclude>/.git$</exclude>
<exclude>/.svn$</exclude>
<exclude>/EIFGENs$</exclude>
</file_rule>
<option warning="true" full_class_checking="false" is_attached_by_default="true" void_safety="all" syntax="standard">
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
</option>
<setting name="concurrency" value="scoop"/>
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
<library name="net" location="$ISE_LIBRARY\library\net\net-safe.ecf"/>
<library name="net_ssl" location="$ISE_LIBRARY\unstable\library\network\socket\netssl\net_ssl-safe.ecf">
<condition>
<custom name="net_ssl_enabled" value="true"/>
</condition>
</library>
<cluster name="network" location=".\network" recursive="false">
<cluster name="ssl_network" location="$|ssl" recursive="true">
<condition>
<custom name="net_ssl_enabled" value="true"/>
</condition>
</cluster>
</cluster>
</target>
</system>

View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-15-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-15-0 http://www.eiffel.com/developers/xml/configuration-1-15-0.xsd" name="http_network" uuid="56DAA1CE-0A2E-451A-BFC9-7821578E79F0" library_target="http_network">
<target name="http_network">
<root all_classes="true"/>
<file_rule>
<exclude>/.git$</exclude>
<exclude>/.svn$</exclude>
<exclude>/EIFGENs$</exclude>
</file_rule>
<option warning="true" full_class_checking="false" void_safety="none" syntax="standard">
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
</option>
<setting name="concurrency" value="scoop"/>
<library name="base" location="$ISE_LIBRARY\library\base\base.ecf"/>
<library name="net" location="$ISE_LIBRARY\library\net\net.ecf"/>
<library name="net_ssl" location="$ISE_LIBRARY\unstable\library\network\socket\netssl\net_ssl.ecf">
<condition>
<custom name="net_ssl_enabled" value="true"/>
</condition>
</library>
<cluster name="network" location=".\network\">
<cluster name="ssl_network" location="$|ssl\" recursive="true">
<condition>
<custom name="net_ssl_enabled" value="true"/>
</condition>
</cluster>
</cluster>
</target>
</system>

View File

@@ -24,11 +24,19 @@
</condition>
</library>
<library name="time" location="$ISE_LIBRARY\library\time\time-safe.ecf"/>
<cluster name="network" location=".\network" recursive="false">
<cluster name="ssl_network" location="$|ssl" recursive="true">
<condition>
<custom name="httpd_ssl_enabled" value="true"/>
</condition>
</cluster>
</cluster>
<cluster name="httpd_server" location=".\" recursive="true">
<file_rule>
<exclude>/concurrency$</exclude>
<exclude>/no_ssl$</exclude>
<exclude>/ssl$</exclude>
<exclude>/network$</exclude>
</file_rule>
<cluster name="no_ssl" location="$|no_ssl\" recursive="true">
<condition>

View File

@@ -24,12 +24,19 @@
</condition>
</library>
<library name="time" location="$ISE_LIBRARY\library\time\time.ecf"/>
<cluster name="network" location=".\network" recursive="false">
<cluster name="ssl_network" location="$|ssl" recursive="true">
<condition>
<custom name="httpd_ssl_enabled" value="true"/>
</condition>
</cluster>
</cluster>
<cluster name="httpd_server" location=".\" recursive="true">
<file_rule>
<exclude>/EIFGENs$</exclude>
<exclude>/concurrency$</exclude>
<exclude>/no_ssl$</exclude>
<exclude>/ssl$</exclude>
<exclude>/network$</exclude>
</file_rule>
<cluster name="no_ssl" location="$|no_ssl\" recursive="true">
<condition>

View File

@@ -0,0 +1,22 @@
note
description: "[
Constant value to define the logging level.
]"
date: "$Date$"
revision: "$Revision$"
deferred class
HTTPD_LOGGER_CONSTANTS
feature -- Access
alert_level: INTEGER = 1 -- 0000 0001
critical_level: INTEGER = 2 -- 0000 0010
error_level: INTEGER = 4 -- 0000 0100
warning_level: INTEGER = 8 -- 0000 1000
notice_level: INTEGER = 16 -- 0001 0000
information_level: INTEGER = 32 -- 0010 0000
debug_level: INTEGER = 64 -- 0100 0000
end

View File

@@ -9,11 +9,20 @@ deferred class
inherit
HTTPD_DEBUG_FACILITIES
HTTPD_LOGGER_CONSTANTS
feature {NONE} -- Initialization
make
make (a_request_settings: HTTPD_REQUEST_SETTINGS)
do
reset
-- Import global request settings.
timeout := a_request_settings.timeout -- seconds
keep_alive_timeout := a_request_settings.keep_alive_timeout -- seconds
max_keep_alive_requests := a_request_settings.max_keep_alive_requests
is_verbose := a_request_settings.is_verbose
verbose_level := a_request_settings.verbose_level
end
reset
@@ -107,16 +116,29 @@ feature -- Access
feature -- Settings
is_verbose: BOOLEAN
-- Output messages?
verbose_level: INTEGER
-- Output verbosity.
is_persistent_connection_supported: BOOLEAN
-- Is persistent connection supported?
do
Result := {HTTPD_SERVER}.is_persistent_connection_supported
Result := {HTTPD_SERVER}.is_persistent_connection_supported and then max_keep_alive_requests > 0
end
persistent_connection_timeout: INTEGER = 5 -- seconds
is_next_persistent_connection_supported: BOOLEAN
-- Is next persistent connection supported?
-- note: it is relevant only if `is_persistent_connection_supported' is True.
timeout: INTEGER -- seconds
-- Amount of seconds that the server waits for receipts and transmissions during communications.
max_keep_alive_requests: INTEGER
-- Maximum number of requests allowed per persistent connection.
keep_alive_timeout: INTEGER -- seconds
-- Number of seconds for persistent connection timeout.
-- Default: 5 sec.
feature -- Status report
@@ -162,7 +184,7 @@ feature -- Execution
local
l_socket: like client_socket
l_exit: BOOLEAN
n: INTEGER
n,m: INTEGER
do
l_socket := client_socket
check
@@ -172,15 +194,21 @@ feature -- Execution
from
-- Process persistent connection as long the socket is not closed.
n := 0
m := max_keep_alive_requests
is_next_persistent_connection_supported := True
until
l_exit
loop
n := n + 1
if n >= m then
is_next_persistent_connection_supported := False
end
-- FIXME: it seems to be called one more time, mostly to see this is done.
execute_request
l_exit := not is_persistent_connection_supported
or has_error or l_socket.is_closed or not l_socket.is_open_read
or not is_next_persistent_connection_supported -- related to `max_keep_alive_requests'
or not is_persistent_connection_requested
or has_error or l_socket.is_closed or not l_socket.is_open_read
reset_request
end
end
@@ -192,7 +220,6 @@ feature -- Execution
l_remote_info: detachable like remote_info
l_socket: like client_socket
l_is_ready: BOOLEAN
i: INTEGER
do
l_socket := client_socket
check
@@ -210,19 +237,11 @@ feature -- Execution
--| TODO: add configuration options for socket timeout.
--| set by default 5 seconds.
-- l_socket.set_timeout (persistent_connection_timeout) -- 5 seconds!
l_socket.set_timeout (1) -- 1 second!
from
i := persistent_connection_timeout -- * 1 sec
until
l_is_ready or i <= 0 or has_error
loop
l_is_ready := l_socket.ready_for_reading
check not l_socket.is_closed end
i := i - 1
end
l_socket.set_timeout (keep_alive_timeout) -- 5 seconds!
l_is_ready := l_socket.ready_for_reading
if l_is_ready then
l_socket.set_timeout (timeout) -- FIXME: return a 408 Request Timeout response ..
create l_remote_info
if attached l_socket.peer_address as l_addr then
l_remote_info.addr := l_addr.host_address.host_address
@@ -242,10 +261,13 @@ feature -- Execution
if l_is_ready then
-- check catch_bad_incoming_connection: False end
if is_verbose then
log ("ERROR: invalid HTTP incoming request")
log (request_header + "%NWARNING: invalid HTTP incoming request", warning_level)
end
end
else
if is_verbose then
log (request_header, information_level)
end
process_request (l_socket)
end
debug ("dbglog")
@@ -310,8 +332,10 @@ feature -- Parsing
line = Void or end_of_stream or has_error
loop
n := line.count
if l_is_verbose then
log (line)
debug ("ew_standalone")
if l_is_verbose then
log (line, debug_level)
end
end
pos := line.index_of (':', 1)
if pos > 0 then
@@ -358,9 +382,11 @@ feature -- Parsing
local
n, pos, next_pos: INTEGER
do
if is_verbose then
log ("%N## Parse HTTP request line ##")
log (line)
debug ("ew_standalone")
if is_verbose then
log ("%N## Parse HTTP request line ##", debug_level)
log (line, debug_level)
end
end
pos := line.index_of (' ', 1)
method := line.substring (1, pos - 1)
@@ -377,7 +403,7 @@ feature -- Parsing
next_line (a_socket: HTTPD_STREAM_SOCKET): detachable STRING
-- Next line fetched from `a_socket' is available.
require
not_has_error: not has_error
not_has_error: not has_error or is_verbose
is_readable: a_socket.is_open_read
local
retried: BOOLEAN
@@ -393,14 +419,14 @@ feature -- Parsing
if not a_socket.socket_ok then
has_error := True
if is_verbose then
log ("%N## Socket is not ok! ##")
log (request_header +"%N" + Result + "%N## socket_ok=False! ##", debug_level)
end
end
else
-- Error with socket...
has_error := True
if is_verbose then
log ("%N## Socket is not readable! ##")
log (request_header + "%N## Socket is not readable! ##", debug_level)
end
end
rescue
@@ -420,13 +446,17 @@ feature -- Output
logger_set: logger = a_logger
end
log (m: STRING)
log (m: STRING; a_level: INTEGER)
-- Log message `m'.
require
is_verbose: is_verbose
do
if attached logger as l_logger then
l_logger.log (m)
else
io.put_string (m + "%N")
if is_verbose and (verbose_level & a_level) = a_level then
if attached logger as l_logger then
l_logger.log (m)
else
io.put_string (m + "%N")
end
end
end

View File

@@ -37,7 +37,7 @@ feature {NONE} -- Initialization
build_controller
-- Build `controller'.
do
create controller
create <NONE> controller
end
initialize
@@ -51,6 +51,9 @@ feature -- Access
is_verbose: BOOLEAN
-- Is verbose for output messages.
verbose_level: INTEGER
-- Verbosity of output.
configuration: HTTPD_CONFIGURATION
-- Associated server configuration.
@@ -100,7 +103,16 @@ feature -- Execution
apply_configuration
is_terminated := False
if is_verbose then
log ("%N%NStarting Web Application Server (port=" + configuration.http_server_port.out + "):%N")
log ("%N%NStarting Web Application Server ...")
log (" - port = " + configuration.http_server_port.out)
log (" - max_tcp_clients = " + configuration.max_tcp_clients.out)
log (" - max_concurrent_connections = " + configuration.max_concurrent_connections.out)
log (" - socket_timeout = " + configuration.socket_timeout.out + " seconds")
log (" - keep_alive_timeout = " + configuration.keep_alive_timeout.out + " seconds")
log (" - max_keep_alive_requests = " + configuration.max_keep_alive_requests.out)
if configuration.verbose_level > 0 then
log (" - verbose_level = " + configuration.verbose_level.out)
end
end
is_shutdown_requested := False
listen
@@ -150,7 +162,7 @@ feature -- Listening
if not l_listening_socket.is_bound then
if is_verbose then
log ("Socket could not be bound on port " + l_http_port.out)
log ("Socket could not be bound on port " + l_http_port.out + " !")
end
else
l_http_port := l_listening_socket.port
@@ -159,9 +171,9 @@ feature -- Listening
l_listening_socket.listen (configuration.max_tcp_clients)
if is_verbose then
if configuration.is_secure then
log ("%NHTTP Connection Server ready on port " + l_http_port.out +" : https://localhost:" + l_http_port.out + "/")
log ("%NListening on port " + l_http_port.out +" : https://localhost:" + l_http_port.out + "/")
else
log ("%NHTTP Connection Server ready on port " + l_http_port.out +" : http://localhost:" + l_http_port.out + "/")
log ("%NListening on port " + l_http_port.out +" : http://localhost:" + l_http_port.out + "/")
end
end
on_launched (l_http_port)
@@ -312,6 +324,7 @@ feature -- Configuration change
is_not_launched: not is_launched
do
is_verbose := configuration.is_verbose
verbose_level := configuration.verbose_level
end
feature -- Output

View File

@@ -12,6 +12,8 @@ class
create
make_server_by_address_and_port,
make_server_by_port,
make_client_by_address_and_port,
make_client_by_port,
make_from_separate,
make_empty
@@ -30,6 +32,16 @@ feature {NONE} -- Initialization
create {TCP_STREAM_SOCKET} socket.make_server_by_port (a_port)
end
make_client_by_address_and_port (an_address: INET_ADDRESS; a_port: INTEGER)
do
create {TCP_STREAM_SOCKET} socket.make_client_by_address_and_port (an_address, a_port)
end
make_client_by_port (a_peer_port: INTEGER; a_peer_host: STRING)
do
create {TCP_STREAM_SOCKET} socket.make_client_by_port (a_peer_port, a_peer_host)
end
make_from_separate (s: separate HTTPD_STREAM_SOCKET)
require
descriptor_available: s.descriptor_available
@@ -164,6 +176,11 @@ feature -- Status Report
end
end
exists: BOOLEAN
do
Result := socket.exists
end
is_blocking: BOOLEAN
do
Result := socket.is_blocking
@@ -176,6 +193,13 @@ feature -- Status Report
end
end
is_connected: BOOLEAN
do
if attached {TCP_STREAM_SOCKET} socket as l_socket then
Result := l_socket.is_connected
end
end
is_created: BOOLEAN
do
if attached {NETWORK_SOCKET} socket as l_socket then
@@ -220,6 +244,16 @@ feature -- Status Report
end
end
connect
do
socket.connect
end
close
do
socket.close
end
listen (a_queue: INTEGER)
do
socket.listen (a_queue)

View File

@@ -22,7 +22,9 @@ inherit
create
make_ssl_server_by_address_and_port, make_ssl_server_by_port,
make_server_by_address_and_port, make_server_by_port
make_server_by_address_and_port, make_server_by_port,
make_ssl_client_by_address_and_port, make_ssl_client_by_port,
make_client_by_address_and_port, make_client_by_port
create {HTTPD_STREAM_SOCKET}
make
@@ -49,6 +51,26 @@ feature {NONE} -- Initialization
set_certificates (a_crt, a_key)
end
make_ssl_client_by_address_and_port (an_address: INET_ADDRESS; a_port: INTEGER; a_ssl_protocol: NATURAL; a_crt: STRING; a_key: STRING)
local
l_socket: SSL_TCP_STREAM_SOCKET
do
create l_socket.make_client_by_address_and_port (an_address, a_port)
l_socket.set_tls_protocol (a_ssl_protocol)
socket := l_socket
set_certificates (a_crt, a_key)
end
make_ssl_client_by_port (a_peer_port: INTEGER; a_peer_host: STRING; a_ssl_protocol: NATURAL; a_crt: STRING; a_key: STRING)
local
l_socket: SSL_TCP_STREAM_SOCKET
do
create l_socket.make_client_by_port (a_peer_port, a_peer_host)
l_socket.set_tls_protocol (a_ssl_protocol)
socket := l_socket
set_certificates (a_crt, a_key)
end
feature -- Output
put_readable_string_8 (s: READABLE_STRING_8)

View File

@@ -12,6 +12,7 @@ inherit
create
make_server_by_address_and_port, make_server_by_port,
make_client_by_address_and_port, make_client_by_port,
make_empty
create {SSL_NETWORK_STREAM_SOCKET}

View File

@@ -15,6 +15,8 @@ inherit
create
make_server_by_address_and_port,
make_server_by_port,
make_client_by_address_and_port,
make_client_by_port,
make_from_separate,
make_empty

View File

@@ -0,0 +1,20 @@
package httpd
project
httpd = "httpd-safe.ecf"
httpd = "httpd.ecf"
note
title: HTTP server
description: "[
Simple HTTP listener and handler, that can be extended easily.
]"
tags: http,httpd,server,web
collection: EWF
copyright: 2011-2016, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Colin Adams, Eiffel Software and others
license: Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)
link[license]: http://www.eiffel.com/licensing/forum.txt
link[source]: "Github" https://github.com/EiffelWebFramework/EWF/library/server/httpd
link[doc]: "Documentation" http://eiffelwebframework.github.io/EWF/
end

View File

@@ -24,13 +24,10 @@ create
feature {NONE} -- Initialization
make_with_connector (conn: like connector)
make_with_connector (a_request_settings: HTTPD_REQUEST_SETTINGS; conn: like connector)
do
make
make (a_request_settings)
connector := conn
-- if conn /= Void then
-- set_is_verbose (is_connector_verbose (conn))
-- end
end
feature -- Access
@@ -56,11 +53,6 @@ feature -- SCOOP helpers
Result := conn.base
end
is_connector_verbose (conn: separate WGI_STANDALONE_CONNECTOR [G]): BOOLEAN
do
Result := conn.is_verbose
end
feature -- Request processing
process_request (a_socket: HTTPD_STREAM_SOCKET)
@@ -87,7 +79,7 @@ feature -- Request processing
else
l_output.set_http_version (version)
end
res.set_is_persistent_connection_supported ({HTTPD_SERVER}.is_persistent_connection_supported)
res.set_is_persistent_connection_supported (is_persistent_connection_supported and is_next_persistent_connection_supported)
res.set_is_persistent_connection_requested (is_persistent_connection_requested)
req.set_meta_string_variable ("RAW_HEADER_DATA", request_header)

View File

@@ -1,5 +1,5 @@
note
description: "Implementation of WGI request handler factory for WGI_STANDALOE_CONNECTOR."
description: "Implementation of WGI request handler factory for WGI_STANDALONE_CONNECTOR."
date: "$Date$"
revision: "$Revision$"
@@ -14,19 +14,23 @@ feature -- Access
connector: detachable separate WGI_STANDALONE_CONNECTOR [G]
-- httpd solution.
request_settings: HTTPD_REQUEST_SETTINGS
-- Settings specific to request handling.
feature -- Element change
set_connector (conn: like connector)
update_with (conn: like connector; a_conf: separate HTTPD_CONFIGURATION)
-- Set `connector' with `conn'.
do
connector := conn
request_settings := a_conf.request_settings
end
feature -- Factory
new_handler: separate WGI_HTTPD_REQUEST_HANDLER [G]
do
create Result.make_with_connector (connector)
create Result.make_with_connector (request_settings, connector)
end
note

View File

@@ -26,12 +26,12 @@ feature {NONE} -- Initialization
create on_launched_actions
-- Server
create fac
create <NONE> fac
request_handler_factory := fac
create server.make (fac)
create observer
create <NONE> observer
configuration := server_configuration (server)
controller := server_controller (server)
set_factory_connector (Current, fac)
initialize_server (server)
end
@@ -51,9 +51,9 @@ feature {NONE} -- Separate helper
a_server.set_observer (observer)
end
set_factory_connector (conn: detachable separate WGI_STANDALONE_CONNECTOR [G]; fac: separate WGI_HTTPD_REQUEST_HANDLER_FACTORY [G])
update_factory (conn: detachable separate WGI_STANDALONE_CONNECTOR [G]; fac: separate WGI_HTTPD_REQUEST_HANDLER_FACTORY [G]; a_conf: separate HTTPD_CONFIGURATION)
do
fac.set_connector (conn)
fac.update_with (conn, a_conf)
end
server_configuration (a_server: like server): like configuration
@@ -74,6 +74,9 @@ feature -- Access
server: separate HTTPD_SERVER
-- HTTPd server object.
request_handler_factory: separate WGI_HTTPD_REQUEST_HANDLER_FACTORY [G]
-- Factory for request handlers.
controller: separate HTTPD_CONTROLLER
-- Controller used to shutdown server.
@@ -97,9 +100,6 @@ feature -- Status report
-- Listening port.
--| 0: not launched
is_verbose: BOOLEAN
-- Is verbose?
feature -- Callbacks
on_launched_actions: ACTION_SEQUENCE [TUPLE [WGI_STANDALONE_CONNECTOR [WGI_EXECUTION]]]
@@ -190,18 +190,22 @@ feature {NONE} -- Implementation
Result := a_server.controller
end
configure_server (a_configuration: like configuration)
apply_configuration (a_configuration: like configuration)
local
v: BOOLEAN
do
if a_configuration.is_verbose then
if attached base as l_base then
v := a_configuration.is_verbose
if v then
if attached base as l_base and then not l_base.is_whitespace then
io.error.put_string ("Base=" + l_base + "%N")
end
end
update_factory (Current, request_handler_factory, a_configuration)
end
launch_server (a_server: like server)
do
configure_server (a_server.configuration)
apply_configuration (a_server.configuration)
a_server.launch
end
@@ -229,7 +233,6 @@ feature {NONE} -- Implementation: element change
set_is_verbose_on_configuration (b: BOOLEAN; cfg: like configuration)
do
is_verbose := b
cfg.set_is_verbose (b)
end

View File

@@ -0,0 +1,10 @@
note
description: "Export HTTPD_LOGGER_CONSTANTS to Standlone connector interfaces."
class
WGI_STANDALONE_HTTPD_LOGGER_CONSTANTS
inherit
HTTPD_LOGGER_CONSTANTS
end

View File

@@ -108,7 +108,6 @@ feature -- Header output operation
l_connection := s.substring (i + 12, j - 1)
l_connection.adjust
if
not is_http_version_1_0 and
not l_connection.is_case_insensitive_equal_general ("close")
then
s.replace_substring ("Connection: close", i + 1, j - 1)

View File

@@ -16,7 +16,7 @@
<library name="encoder" location="..\..\..\..\text\encoder\encoder-safe.ecf"/>
<library name="ewsgi" location="..\..\ewsgi-safe.ecf" readonly="false"/>
<library name="http" location="..\..\..\..\network\protocol\http\http-safe.ecf"/>
<library name="httpd" location="src\httpd\httpd-safe.ecf" readonly="false"/>
<library name="httpd" location="lib\httpd\httpd-safe.ecf" readonly="false"/>
<cluster name="src" location=".\src\">
<cluster name="implementation" location="$|implementation\" hidden="true"/>
</cluster>

View File

@@ -15,7 +15,7 @@
<library name="encoder" location="..\..\..\..\text\encoder\encoder.ecf"/>
<library name="ewsgi" location="..\..\ewsgi.ecf" readonly="false"/>
<library name="http" location="..\..\..\..\network\protocol\http\http.ecf"/>
<library name="httpd" location="src\httpd\httpd.ecf" readonly="false"/>
<library name="httpd" location="lib\httpd\httpd.ecf" readonly="false"/>
<cluster name="src" location=".\src\">
<cluster name="implementation" location="$|implementation\" hidden="true"/>
</cluster>

View File

@@ -25,6 +25,8 @@ inherit
launchable
end
WGI_STANDALONE_HTTPD_LOGGER_CONSTANTS
create
make,
make_and_launch
@@ -34,38 +36,68 @@ feature {NONE} -- Initialization
initialize
local
conn: like connector
s: READABLE_STRING_GENERAL
do
create on_launched_actions
create on_stopped_actions
port_number := 80 --| Default, but quite often, this port is already used ...
max_concurrent_connections := 100
max_tcp_clients := 100
socket_timeout := 300 -- 300 seconds
keep_alive_timeout := 15 -- 15 seconds.
max_keep_alive_requests := 100
verbose := False
verbose_level := notice_level
base_url := ""
if attached options as opts then
if attached {READABLE_STRING_GENERAL} opts.option ("server_name") as l_server_name then
server_name := l_server_name.to_string_8
end
if attached {INTEGER} opts.option ("port") as l_port then
port_number := l_port
elseif
attached {READABLE_STRING_GENERAL} opts.option ("port") as l_port_str and then
l_port_str.is_integer
then
port_number := l_port_str.as_string_8.to_integer
end
if attached {READABLE_STRING_GENERAL} opts.option ("base") as l_base_str then
base_url := l_base_str.as_string_8
end
if attached {BOOLEAN} opts.option ("force_single_threaded") as l_single_threaded then
single_threaded := l_single_threaded
elseif attached {READABLE_STRING_GENERAL} opts.option ("force_single_threaded") as l_single_threaded_str then
single_threaded := l_single_threaded_str.as_lower.same_string ("true")
verbose := opts.option_boolean_value ("verbose", verbose)
-- See `{HTTPD_REQUEST_HANDLER_I}.*_verbose_level`
if opts.has_integer_option ("verbose_level") then
verbose_level := opts.option_integer_value ("verbose_level", verbose_level)
elseif attached {READABLE_STRING_GENERAL} opts.option ("verbose_level") as s_verbose_level then
verbose_level := 0 -- Reset
across
s_verbose_level.split ('+') as ic
loop
s := ic.item
if s.is_case_insensitive_equal ("alert") then
verbose_level := verbose_level | alert_level
elseif s.is_case_insensitive_equal ("critical") then
verbose_level := verbose_level | critical_level
elseif s.is_case_insensitive_equal ("error") then
verbose_level := verbose_level | error_level
elseif s.is_case_insensitive_equal ("warning") then
verbose_level := verbose_level | warning_level
elseif s.is_case_insensitive_equal ("notice") then
verbose_level := verbose_level | notice_level
elseif s.is_case_insensitive_equal ("information") then
verbose_level := verbose_level | information_level
elseif s.is_case_insensitive_equal ("debug") then
verbose_level := verbose_level | debug_level
else
end
end
end
if attached {BOOLEAN} opts.option ("verbose") as l_verbose then
verbose := l_verbose
elseif attached {READABLE_STRING_GENERAL} opts.option ("verbose") as l_verbose_str then
verbose := l_verbose_str.as_lower.same_string ("true")
port_number := opts.option_integer_value ("port", port_number)
if opts.option_boolean_value ("force_single_threaded", False) then
force_single_threaded
end
max_concurrent_connections := opts.option_integer_value ("max_concurrent_connections", max_concurrent_connections)
max_tcp_clients := opts.option_integer_value ("max_tcp_clients", max_tcp_clients)
socket_timeout := opts.option_integer_value ("socket_timeout", socket_timeout)
keep_alive_timeout := opts.option_integer_value ("keep_alive_timeout", keep_alive_timeout)
max_keep_alive_requests := opts.option_integer_value ("max_keep_alive_requests", max_keep_alive_requests)
end
create conn.make
@@ -76,18 +108,25 @@ feature {NONE} -- Initialization
update_configuration (conn.configuration)
end
force_single_threaded
-- Set `single_threaded' to True.
do
max_concurrent_connections := 1
end
feature -- Execution
update_configuration (cfg: like connector.configuration)
do
if single_threaded then
cfg.set_force_single_threaded (True)
end
cfg.set_is_verbose (verbose)
if attached server_name as l_server_name then
cfg.set_http_server_name (l_server_name)
end
cfg.set_verbose_level (verbose_level)
cfg.set_http_server_name (server_name)
cfg.http_server_port := port_number
cfg.set_max_concurrent_connections (max_concurrent_connections)
cfg.set_max_tcp_clients (max_tcp_clients)
cfg.set_socket_timeout (socket_timeout)
cfg.set_keep_alive_timeout (keep_alive_timeout)
cfg.set_max_keep_alive_requests (max_keep_alive_requests)
end
launch
@@ -98,7 +137,7 @@ feature -- Execution
do
conn := connector
conn.set_base (base_url)
debug ("nino")
debug ("ew_standalone")
if verbose then
io.error.put_string ("Launching standalone web server on port " + port_number.out)
if attached server_name as l_name then
@@ -134,8 +173,20 @@ feature {NONE} -- Implementation
base_url: READABLE_STRING_8
verbose: BOOLEAN
verbose_level: INTEGER
-- Help defining the verbosity.
-- The higher, the more output.
max_concurrent_connections: INTEGER
max_tcp_clients: INTEGER
socket_timeout: INTEGER
keep_alive_timeout: INTEGER
max_keep_alive_requests: INTEGER
single_threaded: BOOLEAN
do
Result := max_concurrent_connections = 0
end
feature -- Status report

View File

@@ -1,13 +1,3 @@
note
title: Web Server Foundation
description: Core of the Eiffel Web Framework, used to build web server application.
tags: ewf,server,httpd,request,connector
license: Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)
link[license]: https://github.com/EiffelWebFramework/EWF/blob/master/LICENSE
link[source]: "Github" https://github.com/EiffelWebFramework/EWF
link[doc]: "Documentation" http://eiffelwebframework.github.io/EWF/
end
package wsf
@@ -37,6 +27,7 @@ project
default_standalone = "default/standalone-safe.ecf"
default_standalone = "default/standalone.ecf"
note
title: Web Server Foundation
description: "[

View File

@@ -83,6 +83,57 @@ feature -- Access
Result := options.item (a_name)
end
feature -- Helpers
has_integer_option (a_opt_name: READABLE_STRING_GENERAL): BOOLEAN
-- Is there any INTEGER value associated to option name `a_opt_name'?
local
s: READABLE_STRING_GENERAL
do
if attached option (a_opt_name) as opt then
if attached {INTEGER} opt as i then
Result := True
else
s := opt.out
Result := s.is_integer
end
end
end
option_integer_value (a_opt_name: READABLE_STRING_GENERAL; a_default: INTEGER): INTEGER
-- INTEGER value associated to option name `a_opt_name', other return `a_default'.
local
s: READABLE_STRING_GENERAL
do
Result := a_default
if attached option (a_opt_name) as opt then
if attached {INTEGER} opt as i then
Result := i
else
s := opt.out
if s.is_integer then
Result := s.to_integer
end
end
end
end
option_boolean_value (a_opt_name: READABLE_STRING_GENERAL; a_default: BOOLEAN): BOOLEAN
-- BOOLEAN value associated to option name `a_opt_name', other return `a_default'.
local
s: READABLE_STRING_GENERAL
do
Result := a_default
if attached option (a_opt_name) as opt then
if attached {BOOLEAN} opt as b then
Result := b
else
s := opt.out
Result := s.is_case_insensitive_equal ("true")
end
end
end
feature -- Access
new_cursor: TABLE_ITERATION_CURSOR [detachable ANY, READABLE_STRING_GENERAL]

Binary file not shown.

View File

@@ -17,6 +17,7 @@
<library name="connector_libfcgi" location="..\library\server\ewsgi\connectors\libfcgi\libfcgi-safe.ecf" readonly="false"/>
<library name="connector_nino" location="..\library\server\ewsgi\connectors\nino\nino-safe.ecf" readonly="false"/>
<library name="connector_null" location="..\library\server\ewsgi\connectors\null\null-safe.ecf" readonly="false"/>
<library name="connector_standalone" location="..\library\server\ewsgi\connectors\standalone\standalone-safe.ecf" readonly="false"/>
<library name="conneg" location="..\library\network\protocol\content_negotiation\conneg-safe.ecf" readonly="false"/>
<library name="default_cgi" location="..\library\server\wsf\default\cgi-safe.ecf" readonly="false"/>
<library name="default_libfcgi" location="..\library\server\wsf\default\libfcgi-safe.ecf" readonly="false"/>
@@ -30,6 +31,7 @@
<library name="filter" location="..\examples\filter\filter-safe.ecf" readonly="false"/>
<library name="hello_world" location="..\library\server\ewsgi\examples\hello_world\hello-safe.ecf" readonly="false"/>
<library name="http" location="..\library\network\protocol\http\http-safe.ecf" readonly="false"/>
<library name="httpd" location="..\library\server\ewsgi\connectors\standalone\lib\httpd\httpd-safe.ecf" readonly="false"/>
<library name="http_authorization" location="..\library\server\authentication\http_authorization\http_authorization-safe.ecf" readonly="false"/>
<library name="http_client" location="..\library\network\http_client\http_client-safe.ecf" readonly="false"/>
<library name="libfcgi" location="..\library\server\libfcgi\libfcgi-safe.ecf" readonly="false"/>

View File

@@ -1,14 +1,15 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-9-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-9-0 http://www.eiffel.com/developers/xml/configuration-1-9-0.xsd" name="hello_dev" uuid="7C9887BD-4AE4-47F2-A0AA-4BBB6736D433">
<target name="hello_dev" abstract="true">
<root class="HELLO_ROUTED_WORLD" feature="make"/>
<root class="HELLO_ROUTED_WORLD" feature="make_and_launch"/>
<file_rule>
<exclude>/EIFGENs$</exclude>
<exclude>/\.git$</exclude>
<exclude>/\.svn$</exclude>
</file_rule>
<option debug="true" warning="true" full_class_checking="true" is_attached_by_default="true" void_safety="all" syntax="provisional">
<debug name="nino" enabled="true"/>
<debug name="standalone" enabled="true"/>
<debug name="ew_standalone" enabled="true"/>
<assertions precondition="true" postcondition="true" check="true" invariant="true" supplier_precondition="true"/>
</option>
<setting name="concurrency" value="thread"/>
@@ -16,20 +17,20 @@
<library name="http" location="../../library/network/protocol/http/http-safe.ecf" readonly="false"/>
<library name="wsf" location="..\..\library\server\wsf\wsf-safe.ecf" readonly="false"/>
</target>
<target name="hello_nino" extends="hello_connector">
<target name="hello_standalone" extends="hello_dev">
<option debug="true" warning="true" full_class_checking="true" is_attached_by_default="true" void_safety="all" syntax="provisional">
<debug name="nino" enabled="true"/>
<debug name="standalone" enabled="true"/>
<assertions precondition="true" postcondition="true" check="true" invariant="true" supplier_precondition="true"/>
</option>
<library name="default_nino" location="..\..\library\server\wsf\default\nino-safe.ecf" readonly="false" use_application_options="true"/>
<library name="default_standalone" location="..\..\library\server\wsf\default\standalone-safe.ecf" readonly="false" use_application_options="true"/>
<cluster name="src" location="src\" recursive="true"/>
<override name="override" location="override\" recursive="true"/>
</target>
<target name="hello_cgi" extends="hello_connector">
<target name="hello_cgi" extends="hello_dev">
<library name="default_cgi" location="..\..\library\server\wsf\default\cgi-safe.ecf"/>
<cluster name="src" location="src\" recursive="true"/>
</target>
<target name="hello_libfcgi" extends="hello_connector">
<target name="hello_libfcgi" extends="hello_dev">
<library name="default_libfcgi" location="..\..\library\server\wsf\default\libfcgi-safe.ecf"/>
<cluster name="src" location="src\" recursive="true"/>
</target>

View File

@@ -8,243 +8,24 @@ class
HELLO_ROUTED_WORLD
inherit
WSF_URI_TEMPLATE_ROUTED_SERVICE
WSF_HANDLER_HELPER
WSF_DEFAULT_SERVICE
WSF_DEFAULT_SERVICE [HELLO_ROUTED_WORLD_EXECUTION]
redefine
initialize
end
create
make
make_and_launch
feature {NONE} -- Initialization
make
initialize
do
initialize_router
Precursor
set_service_option ("port", 8099)
make_and_launch
end
create_router
do
create router.make (5)
end
setup_router
local
ra: WSF_AGENT_HANDLER [WSF_URI_TEMPLATE_HANDLER_CONTEXT]
hello: WSF_URI_TEMPLATE_ROUTING_HANDLER
www: WSF_FILE_SYSTEM_HANDLER [WSF_URI_TEMPLATE_HANDLER_CONTEXT]
do
router.map_agent ("/refresh", agent execute_refresh)
router.map_agent ("/home", agent execute_home)
create www.make (document_root)
www.set_directory_index (<<"index.html">>)
router.map ("/www{/path}{?query}", www)
--| Map all "/hello*" using a ROUTING_HANDLER
create hello.make (3)
router.map ("/hello", hello)
create ra.make (agent handle_hello)
hello.map ("/hello/{name}.{format}", ra)
hello.map ("/hello.{format}/{name}", ra)
hello.map ("/hello/{name}", ra)
create ra.make (agent handle_anonymous_hello)
hello.map ("/hello", ra)
hello.map ("/hello.{format}", ra)
--| Various various route, directly on the "router"
router.map_agent_with_request_methods ("/method/any", agent handle_method_any, Void)
router.map_agent_with_request_methods ("/method/guess", agent handle_method_get_or_post, <<"GET", "POST">>)
router.map_agent_with_request_methods ("/method/custom", agent handle_method_get, <<"GET">>)
router.map_agent_with_request_methods ("/method/custom", agent handle_method_post, <<"POST">>)
end
document_root: READABLE_STRING_8
local
e: EXECUTION_ENVIRONMENT
dn: DIRECTORY_NAME
once
create e
create dn.make_from_string (e.current_working_directory)
dn.extend ("htdocs")
Result := dn.string
if Result[Result.count] = Operating_environment.directory_separator then
Result := Result.substring (1, Result.count - 1)
end
end
feature -- Execution
execute_default (req: WSF_REQUEST; res: WSF_RESPONSE)
local
l_url: STRING
do
l_url := req.absolute_script_url ("/home")
res.redirect_now_with_content (l_url, "You are now being redirected to " + l_url, {HTTP_MIME_TYPES}.text_html)
end
execute_refresh (ctx: WSF_URI_TEMPLATE_HANDLER_CONTEXT; req: WSF_REQUEST; res: WSF_RESPONSE)
local
h: HTTP_HEADER
l_url: STRING
e: EXECUTION_ENVIRONMENT
n: INTEGER
i: INTEGER
s: STRING_8
do
l_url := req.absolute_script_url ("/home")
n := 3
create h.make
h.put_refresh (l_url, 5)
h.put_location (l_url)
h.put_content_type_text_plain
h.put_transfer_encoding_chunked
-- h.put_content_length (0)
-- res.set_status_code ({HTTP_STATUS_CODE}.moved_permanently)
res.set_status_code ({HTTP_STATUS_CODE}.ok)
res.put_header_text (h.string)
from
create e
create s.make (255)
until
n = 0
loop
if n > 1 then
s.append ("%NRedirected to " + l_url + " in " + n.out + " seconds :%N")
else
s.append ("%NRedirected to " + l_url + " in 1 second :%N")
end
res.put_chunk (s, Void); s.wipe_out
from
i := 1
until
i = 1001
loop
s.append_character ('.')
if i \\ 100 = 0 then
s.append_character ('%N')
end
res.put_chunk (s, Void); s.wipe_out
e.sleep (1_000_000)
i := i + 1
end
n := n - 1
end
s.append ("%NYou are now being redirected...%N")
res.put_chunk (s, Void); s.wipe_out
res.put_chunk_end
end
execute_home (ctx: WSF_URI_TEMPLATE_HANDLER_CONTEXT; req: WSF_REQUEST; res: WSF_RESPONSE)
local
l_body: STRING_8
do
create l_body.make (255)
l_body.append ("<html><body>Hello World ?!%N")
l_body.append ("<h3>Please try the following links</h3><ul>%N")
l_body.append ("<li><a href=%""+ req.script_url ("/") + "%">default</a></li>%N")
l_body.append ("<li><a href=%""+ req.script_url ("/refresh") + "%">redirect using refresh and chunked encoding</a></li>%N")
l_body.append ("<li><a href=%""+ req.script_url ("/hello") + "%">/hello</a></li>%N")
l_body.append ("<li><a href=%""+ req.script_url ("/hello.html/Joce") + "%">/hello.html/Joce</a></li>%N")
l_body.append ("<li><a href=%""+ req.script_url ("/hello.json/Joce") + "%">/hello.json/Joce</a></li>%N")
l_body.append ("<li><a href=%""+ req.script_url ("/hello/Joce.html") + "%">/hello/Joce.html</a></li>%N")
l_body.append ("<li><a href=%""+ req.script_url ("/hello/Joce.xml") + "%">/hello/Joce.xml</a></li>%N")
l_body.append ("<li><a href=%""+ req.script_url ("/hello/Joce") + "%">/hello/Joce</a></li>%N")
l_body.append ("</ul>%N")
if attached req.item ("REQUEST_COUNT") as rqc then
l_body.append ("request #"+ rqc.as_string.string + "%N")
end
l_body.append ("</body></html>%N")
res.put_header ({HTTP_STATUS_CODE}.ok, <<["Content-Type", "text/html"], ["Content-Length", l_body.count.out]>>)
res.put_string (l_body)
end
execute_hello (req: WSF_REQUEST; res: WSF_RESPONSE; a_name: detachable READABLE_STRING_32; ctx: WSF_HANDLER_CONTEXT)
local
l_response_content_type: detachable STRING
h: HTTP_HEADER
content_type_supported: ARRAY [STRING]
l_body: STRING_8
do
if a_name /= Void then
l_body := "Hello %"" + a_name + "%" !%N"
else
l_body := "Hello anonymous visitor !%N"
end
content_type_supported := <<{HTTP_MIME_TYPES}.application_json, {HTTP_MIME_TYPES}.text_html, {HTTP_MIME_TYPES}.text_xml, {HTTP_MIME_TYPES}.text_plain>>
inspect ctx.request_format_id ("format", content_type_supported)
when {HTTP_FORMAT_CONSTANTS}.json then
l_response_content_type := {HTTP_MIME_TYPES}.application_json
l_body := "{%N%"application%": %"/hello%",%N %"message%": %"" + l_body + "%" %N}"
when {HTTP_FORMAT_CONSTANTS}.html then
l_response_content_type := {HTTP_MIME_TYPES}.text_html
when {HTTP_FORMAT_CONSTANTS}.xml then
l_response_content_type := {HTTP_MIME_TYPES}.text_xml
l_body := "<response><application>/hello</application><message>" + l_body + "</message></response>%N"
when {HTTP_FORMAT_CONSTANTS}.text then
l_response_content_type := {HTTP_MIME_TYPES}.text_plain
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_content_type (l_response_content_type)
h.put_content_length (l_body.count)
res.set_status_code ({HTTP_STATUS_CODE}.ok)
res.put_header_text (h.string)
res.put_string (l_body)
end
end
handle_hello (ctx: WSF_URI_TEMPLATE_HANDLER_CONTEXT; req: WSF_REQUEST; res: WSF_RESPONSE)
do
execute_hello (req, res, ctx.string_parameter ("name"), ctx)
end
handle_anonymous_hello (ctx: WSF_URI_TEMPLATE_HANDLER_CONTEXT; req: WSF_REQUEST; res: WSF_RESPONSE)
do
execute_hello (req, res, ctx.string_parameter ("name"), ctx)
end
handle_method_any (ctx: WSF_URI_TEMPLATE_HANDLER_CONTEXT; req: WSF_REQUEST; res: WSF_RESPONSE)
do
execute_hello (req, res, req.request_method, ctx)
end
handle_method_get (ctx: WSF_URI_TEMPLATE_HANDLER_CONTEXT; req: WSF_REQUEST; res: WSF_RESPONSE)
do
execute_hello (req, res, "GET", ctx)
end
handle_method_post (ctx: WSF_URI_TEMPLATE_HANDLER_CONTEXT; req: WSF_REQUEST; res: WSF_RESPONSE)
do
execute_hello (req, res, "POST", ctx)
end
handle_method_get_or_post (ctx: WSF_URI_TEMPLATE_HANDLER_CONTEXT; req: WSF_REQUEST; res: WSF_RESPONSE)
do
execute_hello (req, res, "GET or POST", ctx)
end
note
copyright: "2011-2011, Eiffel Software and others"
copyright: "2011-2016, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[
Eiffel Software

View File

@@ -0,0 +1,272 @@
note
description : "Objects that ..."
author : "$Author$"
date : "$Date$"
revision : "$Revision$"
class
HELLO_ROUTED_WORLD_EXECUTION
inherit
WSF_ROUTED_EXECUTION
redefine
execute_default
end
WSF_URI_TEMPLATE_HELPER_FOR_ROUTED_EXECUTION
create
make
feature {NONE} -- Initialization
setup_router
local
ra: WSF_URI_TEMPLATE_AGENT_HANDLER
hello: WSF_URI_TEMPLATE_ROUTING_HANDLER
www: WSF_FILE_SYSTEM_HANDLER
do
map_uri_template_agent ("/refresh", agent execute_refresh, Void)
map_uri_template_agent ("/home", agent execute_home, Void)
create www.make (document_root)
www.set_directory_index (<<"index.html">>)
router.handle ("/www{/path}{?query}", www, Void)
--| Map all "/hello*" using a ROUTING_HANDLER
create hello.make (3)
router.handle ("/hello", hello, Void)
create ra.make (agent handle_hello)
hello.router.handle ("/hello/{name}.{format}", ra, Void)
hello.router.handle ("/hello.{format}/{name}", ra, Void)
hello.router.handle ("/hello/{name}", ra, Void)
create ra.make (agent handle_anonymous_hello)
hello.router.handle ("/hello", ra, Void)
hello.router.handle ("/hello.{format}", ra, Void)
--| Various various route, directly on the "router"
map_uri_template_agent ("/method/any", agent handle_method_any, Void)
map_uri_template_agent ("/method/guess", agent handle_method_get_or_post, <<"GET", "POST">>)
map_uri_template_agent ("/method/custom", agent handle_method_get, <<"GET">>)
map_uri_template_agent ("/method/custom", agent handle_method_post, <<"POST">>)
end
document_root: READABLE_STRING_8
local
e: EXECUTION_ENVIRONMENT
dn: DIRECTORY_NAME
once
create e
create dn.make_from_string (e.current_working_directory)
dn.extend ("htdocs")
Result := dn.string
if Result[Result.count] = Operating_environment.directory_separator then
Result := Result.substring (1, Result.count - 1)
end
end
feature -- Execution
execute_default (req: WSF_REQUEST; res: WSF_RESPONSE)
local
l_url: STRING
do
l_url := req.absolute_script_url ("/home")
res.redirect_now_with_content (l_url, "You are now being redirected to " + l_url, {HTTP_MIME_TYPES}.text_html)
end
execute_refresh (req: WSF_REQUEST; res: WSF_RESPONSE) --ctx: WSF_URI_TEMPLATE_HANDLER_CONTEXT;
local
h: HTTP_HEADER
l_url: STRING
e: EXECUTION_ENVIRONMENT
n: INTEGER
i: INTEGER
s: STRING_8
do
l_url := req.absolute_script_url ("/home")
n := 3
create h.make
h.put_refresh (l_url, 5)
h.put_location (l_url)
h.put_content_type_text_plain
h.put_transfer_encoding_chunked
-- h.put_content_length (0)
-- res.set_status_code ({HTTP_STATUS_CODE}.moved_permanently)
res.set_status_code ({HTTP_STATUS_CODE}.ok)
res.put_header_text (h.string)
from
create e
create s.make (255)
until
n = 0
loop
if n > 1 then
s.append ("%NRedirected to " + l_url + " in " + n.out + " seconds :%N")
else
s.append ("%NRedirected to " + l_url + " in 1 second :%N")
end
res.put_chunk (s, Void); s.wipe_out
from
i := 1
until
i = 1001
loop
s.append_character ('.')
if i \\ 100 = 0 then
s.append_character ('%N')
end
res.put_chunk (s, Void); s.wipe_out
e.sleep (1_000_000)
i := i + 1
end
n := n - 1
end
s.append ("%NYou are now being redirected...%N")
res.put_chunk (s, Void); s.wipe_out
res.put_chunk_end
end
execute_home (req: WSF_REQUEST; res: WSF_RESPONSE) -- ctx: WSF_URI_TEMPLATE_HANDLER_CONTEXT;
local
l_body: STRING_8
do
create l_body.make (255)
l_body.append ("<html><body>Hello World ?!%N")
l_body.append ("<h3>Please try the following links</h3><ul>%N")
l_body.append ("<li><a href=%""+ req.script_url ("/") + "%">default</a></li>%N")
l_body.append ("<li><a href=%""+ req.script_url ("/refresh") + "%">redirect using refresh and chunked encoding</a></li>%N")
l_body.append ("<li><a href=%""+ req.script_url ("/hello") + "%">/hello</a></li>%N")
l_body.append ("<li><a href=%""+ req.script_url ("/hello.html/Joce") + "%">/hello.html/Joce</a></li>%N")
l_body.append ("<li><a href=%""+ req.script_url ("/hello.json/Joce") + "%">/hello.json/Joce</a></li>%N")
l_body.append ("<li><a href=%""+ req.script_url ("/hello/Joce.html") + "%">/hello/Joce.html</a></li>%N")
l_body.append ("<li><a href=%""+ req.script_url ("/hello/Joce.xml") + "%">/hello/Joce.xml</a></li>%N")
l_body.append ("<li><a href=%""+ req.script_url ("/hello/Joce") + "%">/hello/Joce</a></li>%N")
l_body.append ("</ul>%N")
if attached req.item ("REQUEST_COUNT") as rqc then
l_body.append ("request #"+ rqc.as_string.url_encoded_value + "%N")
end
l_body.append ("</body></html>%N")
res.put_header ({HTTP_STATUS_CODE}.ok, <<["Content-Type", "text/html"], ["Content-Length", l_body.count.out]>>)
res.put_string (l_body)
end
execute_hello (req: WSF_REQUEST; res: WSF_RESPONSE; a_name: detachable READABLE_STRING_32)
local
l_response_content_type: detachable STRING
h: HTTP_HEADER
content_type_supported: ARRAY [STRING]
l_body: STRING_8
l_format: detachable READABLE_STRING_GENERAL
l_http_format_constants: HTTP_FORMAT_CONSTANTS
do
if a_name /= Void then
l_body := "Hello %"" + a_name + "%" !%N"
else
l_body := "Hello anonymous visitor !%N"
end
content_type_supported := <<{HTTP_MIME_TYPES}.application_json, {HTTP_MIME_TYPES}.text_html, {HTTP_MIME_TYPES}.text_xml, {HTTP_MIME_TYPES}.text_plain>>
if attached {WSF_STRING} req.path_parameter ("format") as s_format then
l_format := s_format.value
end
if l_format = Void then
across
content_type_supported as ic
until
l_format /= Void
loop
if req.is_content_type_accepted (ic.item) then
l_format := ic.item
end
end
end
if l_format /= Void then
create l_http_format_constants
inspect
l_http_format_constants.format_id (l_format)
when {HTTP_FORMAT_CONSTANTS}.json then
l_response_content_type := {HTTP_MIME_TYPES}.application_json
l_body := "{%N%"application%": %"/hello%",%N %"message%": %"" + l_body + "%" %N}"
when {HTTP_FORMAT_CONSTANTS}.html then
l_response_content_type := {HTTP_MIME_TYPES}.text_html
when {HTTP_FORMAT_CONSTANTS}.xml then
l_response_content_type := {HTTP_MIME_TYPES}.text_xml
l_body := "<response><application>/hello</application><message>" + l_body + "</message></response>%N"
when {HTTP_FORMAT_CONSTANTS}.text then
l_response_content_type := {HTTP_MIME_TYPES}.text_plain
else
l_response_content_type := Void
end
end
if l_response_content_type /= Void then
create h.make
h.put_content_type (l_response_content_type)
h.put_content_length (l_body.count)
res.set_status_code ({HTTP_STATUS_CODE}.ok)
res.put_header_text (h.string)
res.put_string (l_body)
else
res.send (create {WSF_PRECONDITION_FAILED_MESSAGE}.make (req)) -- FIXME: better error message!
end
end
string_path_parameter (req: WSF_REQUEST; a_name: READABLE_STRING_GENERAL): detachable STRING_32
do
if attached {WSF_STRING} req.path_parameter (a_name) as s then
Result := s.value
end
end
handle_hello (req: WSF_REQUEST; res: WSF_RESPONSE)
do
execute_hello (req, res, string_path_parameter (req, "name"))
end
handle_anonymous_hello (req: WSF_REQUEST; res: WSF_RESPONSE)
do
execute_hello (req, res, string_path_parameter (req, "name"))
end
handle_method_any (req: WSF_REQUEST; res: WSF_RESPONSE)
do
execute_hello (req, res, req.request_method)
end
handle_method_get (req: WSF_REQUEST; res: WSF_RESPONSE)
do
execute_hello (req, res, "GET")
end
handle_method_post (req: WSF_REQUEST; res: WSF_RESPONSE)
do
execute_hello (req, res, "POST")
end
handle_method_get_or_post (req: WSF_REQUEST; res: WSF_RESPONSE)
do
execute_hello (req, res, "GET or POST")
end
note
copyright: "2011-2016, 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