Compare commits

...

13 Commits

Author SHA1 Message Date
40fb3893af Include wsf_proxy to the installation process. 2016-09-27 16:18:06 +02:00
21407f8dcf Fixed SSL support on the httpd component, and also on the EiffelWeb standalone connector.
- the standalone connector support for SSL, is using certicate files for now (no in-memory support).
  - to enable ssl support, set ecf variable `httpd_ssl_enabled=true`.
  - added the `simple_ssl` example to demonstrate how to have standalone ssl server.
    (be careful when using EiffelNet SSL and the http_client library, disable the libcurl
      via ecf variable `libcurl_http_client_disabled=true` )

Added support for recv timeout to the EiffelWeb standalone connector.
  - made EiffelWeb compilable with 16.05 and upcoming 16.11.
    Done via ecfs condition on version to accept EiffelNet with recv_timeout (from 16.11), and without (until 16.05).
  - adding recv timeout prevents server to hang for ever if a client wait too long to send data.

Updated various comments.
2016-09-27 16:11:47 +02:00
356eb143ea Fixed the non void-safe ecf for wsf_proxy. 2016-09-26 17:42:49 +02:00
df551d4a4f Use latest API from http_client using DEFAULT_HTTP_CLIENT,
that could use libcurl or EiffelNet depending on the configuration (.ecf).
2016-09-26 13:13:57 +02:00
f010da04e9 Merge branch 'reverse_proxy' 2016-09-19 22:19:21 +02:00
5029049ef0 Replaced host+port by uri (http://remotemachine:port/path).
Added support for SSL (https).
2016-08-08 12:30:28 +02:00
80254b2278 When possible keep ecf location relative within the same EiffelWeb directory structure. 2016-08-06 10:07:42 +02:00
210fae5000 First step towards SSL support. 2016-08-06 10:04:45 +02:00
9cc9b95190 Added a simple reverse proxy handler.
- For now, it does not support SSL connection on the target yet.
- No external config file support, this is all about coding.
2016-08-05 11:38:35 +02:00
8b172b5d33 Revisited WSF_REQUEST.read_input_data* functions:
- read_input_data_into_file now accepts a IO_MEDIUM argument instead of just FILE.
- cleaned the implementation, and make sure that eventual `raw_input_data` is containing only the raw input data.
2016-08-05 11:32:14 +02:00
cc2d7dbb1c Ignore empty header line. 2016-08-05 11:28:59 +02:00
c88394b9fd Added support for category in ATOM format (input and output). 2016-06-24 13:03:09 +02:00
4283662f43 Removed unwanted .ecf file. 2016-06-22 10:55:41 +02:00
54 changed files with 1418 additions and 156 deletions

View File

@@ -0,0 +1,29 @@
note
description: "Launcher for reverse proxy web application."
date: "$Date$"
revision: "$Revision$"
class
APPLICATION
inherit
WSF_DEFAULT_SERVICE [APPLICATION_EXECUTION]
redefine
initialize
end
create
make_and_launch
feature {NONE} -- Initialization
initialize
-- Initialize current service.
do
-- 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 ("server.ini"))
end
end

View File

@@ -0,0 +1,49 @@
note
description: "Reverse proxy example."
date: "$Date$"
revision: "$Revision$"
class
APPLICATION_EXECUTION
inherit
WSF_EXECUTION
WSF_URI_REWRITER
rename
uri as proxy_uri
end
create
make
feature -- Basic operations
execute
do
-- NOTE: please enter the target server uri here
-- replace "http://localhost:8080/foobar"
send_proxy_response ("http://localhost:8080/foobar", Current)
end
send_proxy_response (a_remote: READABLE_STRING_8; a_rewriter: detachable WSF_URI_REWRITER)
local
h: WSF_SIMPLE_REVERSE_PROXY_HANDLER
do
create h.make (a_remote)
h.set_uri_rewriter (a_rewriter)
h.set_uri_rewriter (create {WSF_AGENT_URI_REWRITER}.make (agent proxy_uri))
h.set_timeout (30) -- 30 seconds
h.set_connect_timeout (5_000) -- milliseconds = 5 seconds
h.execute (request, response)
end
feature -- Helpers
proxy_uri (a_request: WSF_REQUEST): STRING
-- Request uri rewriten as url.
do
Result := a_request.request_uri
end
end

28
examples/proxy/proxy.ecf Normal file
View File

@@ -0,0 +1,28 @@
<?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="proxy" uuid="B55F0D95-3793-4C90-BBAC-BF5F2DECD5E6" library_target="proxy">
<target name="common" abstract="true">
<file_rule>
<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">
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
</option>
<setting name="console_application" value="true"/>
<variable name="ssl_supported" value="false"/>
<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"/>
<library name="wsf_proxy" location="..\..\library\server\wsf_proxy\wsf_proxy-safe.ecf" readonly="false"/>
</target>
<target name="proxy" extends="common">
<root class="APPLICATION" feature="make_and_launch"/>
<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="scoop"/>
<library name="default_standalone" location="..\..\library\server\wsf\default\standalone-safe.ecf"/>
<cluster name="proxy" location=".\" recursive="true"/>
</target>
</system>

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

@@ -0,0 +1,29 @@
note
description : "simple application root class"
date : "$Date$"
revision : "$Revision$"
class
APPLICATION
inherit
WSF_DEFAULT_SERVICE [APPLICATION_EXECUTION]
redefine
initialize
end
create
make_and_launch
feature {NONE} -- Initialization
initialize
-- Initialize current service.
do
-- 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

@@ -0,0 +1,41 @@
note
description : "simple application execution"
date : "$Date$"
revision : "$Revision$"
class
APPLICATION_EXECUTION
inherit
WSF_EXECUTION
create
make
feature -- Basic operations
execute
local
s: STRING
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 + ").")
if request.is_https then
s.append ("<p>This is a secured connection! (https)</p>%N")
end
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
response.header.put_content_length (s.count)
if attached request.http_connection as l_connection and then l_connection.is_case_insensitive_equal_general ("keep-alive") then
response.header.put_header_key_value ("Connection", "keep-alive")
end
response.put_string (s)
end
end

View File

@@ -0,0 +1,15 @@
-----BEGIN CERTIFICATE-----
MIICWDCCAcGgAwIBAgIJAJnXGtV+PtiYMA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNV
BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX
aWRnaXRzIFB0eSBMdGQwHhcNMTUwNDAzMjIxNTA0WhcNMTYwNDAyMjIxNTA0WjBF
MQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50
ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKB
gQDFMK6ojzg+KlklhTossR13c51izMgGc3B0z9ttfHIcx2kxra3HtHcKIl5wSUvn
G8zmSyFAyQTs5LUv65q46FM9qU8tP+vTeFCfNXvjRcIEpouta3J53K0xuUlxz4d4
4D6qvdDWAez/0AkI4y5etW5zXtg7IQorJhsI9TmfGuruzwIDAQABo1AwTjAdBgNV
HQ4EFgQUbWpk2HoHa0YqpEwr7CGEatBFTMkwHwYDVR0jBBgwFoAUbWpk2HoHa0Yq
pEwr7CGEatBFTMkwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOBgQAi+h4/
IgEocWkdRZBKHEcTrRxz5WhEDJMoVo9LhnXvCfn1G/4p6Un6sYv7Xzpi9NuSY8uV
cjfJJXhtF3AtyZ70iTAxWaRWjGaZ03PYOjlledJ5rqJEt6CCn8m+JsfznduZvbxQ
zQ6jCLXfyD/tvemB+yYEI3NntvRKx5/zt6Q26Q==
-----END CERTIFICATE-----

View File

@@ -0,0 +1,28 @@
##########################################################
### EiffelWeb settings for related connector ###
### Mostly for EiffelWeb standalone connector ###
### See {WGI_STANDALONE_CONSTANTS} for default values. ###
##########################################################
### Connection settings
port=9090
#max_concurrent_connections=100
#max_tcp_clients=100
### Timeout settings
#socket_timeout=60
#socket_recv_timeout=5
### Persistent connection settings
#keep_alive_timeout=15
#max_keep_alive_requests=100
### SSL settings
# enable SSL, with file certificate.
ssl_enabled=true
ssl_ca_key=simple.key
ssl_ca_crt=simple.crt
### App settings
verbose=true
verbose_level=ALERT

View File

@@ -0,0 +1,15 @@
-----BEGIN RSA PRIVATE KEY-----
MIICXAIBAAKBgQDFMK6ojzg+KlklhTossR13c51izMgGc3B0z9ttfHIcx2kxra3H
tHcKIl5wSUvnG8zmSyFAyQTs5LUv65q46FM9qU8tP+vTeFCfNXvjRcIEpouta3J5
3K0xuUlxz4d44D6qvdDWAez/0AkI4y5etW5zXtg7IQorJhsI9TmfGuruzwIDAQAB
AoGAR5efMg+dieRyLU8rieJcImxVbfOPg9gRsjdtIVkXTR+RL7ow59q7hXBo/Td/
WU8cm1gXoJ/bK+71YYqWyB+BaLRIWvRWb7Gdw203tu4e136Ca5uuY+71qdbVTVcl
NQ7J+T+eAQFP+a+DdT3ZQxu9eze87SMbu6i5YSpIk2kusOECQQDunv/DQ+nc+NgR
DF+Td3sNYUVRT9a1CWi6abAG6reXwp8MS4NobWDf+Ps4JODhEEwlIdq5qL7qqYBZ
Gc1TJJ53AkEA0404Fn6vAzzegBcS4RLlYTK7nMr0m4pMmDMCI6YzAYdMmKHp1e6f
IwxSmQrmwyAgwcT01bc0+A8yipcC2BWQaQJBAJ01QZm635OGmos41KsKF5bsE8gL
SpBBH69Yu/ECqGwie7iU84FUNnO4zIHjwghlPVVlZX3Vz9o4S+fn2N9DC+cCQGyZ
QyCxGdC0r5fbwHJQS/ZQn+UGfvlVzqoXDVMVn3t6ZES6YZrT61eHnOM5qGqklIxE
Old3vDZXPt/MU8Zvk3kCQBOgUx2VxvTrHN37hk9/QIDiM62+RenBm1M3ah8xTosf
1mSeEb6d9Kwb3TgPBmA7YXzJuAQfRIvEPMPxT5SSr6Q=
-----END RSA PRIVATE KEY-----

View File

@@ -0,0 +1,27 @@
<?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="simple_ssl" uuid="C2FE296C-3C18-4609-A5AB-F604BDEE4410" library_target="simple_ssl">
<target name="simple_ssl">
<description>Simple EiffelWeb standalone server with SSL support (Concurrent connection supported thanks to SCOOP).</description>
<root class="APPLICATION" feature="make_and_launch"/>
<file_rule>
<exclude>/.svn$</exclude>
<exclude>/CVS$</exclude>
<exclude>/EIFGENs$</exclude>
</file_rule>
<option warning="true" is_attached_by_default="true" void_safety="all" syntax="transitional">
<assertions/>
</option>
<setting name="console_application" value="true"/>
<setting name="concurrency" value="scoop"/>
<variable name="httpd_ssl_enabled" value="true"/>
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
<library name="default_standalone" location="..\..\library\server\wsf\default\standalone-safe.ecf"/>
<library name="http" location="..\..\library\network\protocol\http\http-safe.ecf"/>
<library name="wsf" location="..\..\library\server\wsf\wsf-safe.ecf"/>
<cluster name="simple" location=".\" recursive="true"/>
</target>
<target name="simple_ssl_st" extends="simple_ssl">
<description>Simple EiffelWeb standalone server with SSL support (Single threaded, thus no concurrent connection.)</description>
<setting name="concurrency" value="none"/>
</target>
</system>

View File

@@ -180,7 +180,9 @@ feature -- Header: adding
if line [line.count] = '%R' then if line [line.count] = '%R' then
line.remove_tail (1) line.remove_tail (1)
end end
add_header (line) if not line.is_empty then
add_header (line)
end
end end
end end
end end

View File

@@ -508,9 +508,9 @@ feature -- Helper
new_session (a_uri: READABLE_STRING_8): HTTP_CLIENT_SESSION new_session (a_uri: READABLE_STRING_8): HTTP_CLIENT_SESSION
local local
cl: LIBCURL_HTTP_CLIENT cl: DEFAULT_HTTP_CLIENT
do do
create cl.make create cl
Result := cl.new_session (a_uri) Result := cl.new_session (a_uri)
Result.set_is_insecure (True) Result.set_is_insecure (True)
Result.set_max_redirects (5) Result.set_max_redirects (5)

View File

@@ -25,20 +25,27 @@ feature {CONCURRENT_POOL, HTTPD_CONNECTION_HANDLER_I} -- Basic operation
release release
-- <Precursor> -- <Precursor>
local local
d: STRING d: detachable STRING
do do
if attached internal_client_socket as l_socket then
d := l_socket.descriptor.out
else
d := "N/A"
end
debug ("dbglog") debug ("dbglog")
if
attached internal_client_socket as l_socket and then
l_socket.descriptor_available
then
d := l_socket.descriptor.out
else
d := "N/A"
end
dbglog (generator + ".release: ENTER {" + d + "}") dbglog (generator + ".release: ENTER {" + d + "}")
end end
Precursor {HTTPD_REQUEST_HANDLER_I} Precursor {HTTPD_REQUEST_HANDLER_I}
release_pool_item release_pool_item
debug ("dbglog") debug ("dbglog")
dbglog (generator + ".release: LEAVE {" + d + "}") if d /= Void then
dbglog (generator + ".release: LEAVE {" + d + "}")
else
dbglog (generator + ".release: LEAVE {N/A}")
end
end end
end end

View File

@@ -18,21 +18,28 @@ inherit
feature {HTTPD_CONNECTION_HANDLER_I} -- Basic operation feature {HTTPD_CONNECTION_HANDLER_I} -- Basic operation
release release
-- <Precursor>
local local
d: STRING d: detachable STRING
do do
-- FIXME: for log purpose
if attached internal_client_socket as l_socket then
d := l_socket.descriptor.out
else
d := "N/A"
end
debug ("dbglog") debug ("dbglog")
if
attached internal_client_socket as l_socket and then
l_socket.descriptor_available
then
d := l_socket.descriptor.out
else
d := "N/A"
end
dbglog (generator + ".release: ENTER {" + d + "}") dbglog (generator + ".release: ENTER {" + d + "}")
end end
Precursor {HTTPD_REQUEST_HANDLER_I} Precursor {HTTPD_REQUEST_HANDLER_I}
debug ("dbglog") debug ("dbglog")
dbglog (generator + ".release: LEAVE {" + d + "}") if d /= Void then
dbglog (generator + ".release: LEAVE {" + d + "}")
else
dbglog (generator + ".release: LEAVE {N/A}")
end
end end
end end

View File

@@ -6,6 +6,11 @@ note
deferred class deferred class
HTTPD_CONFIGURATION_I HTTPD_CONFIGURATION_I
inherit
ANY
HTTPD_CONSTANTS
feature {NONE} -- Initialization feature {NONE} -- Initialization
make make
@@ -14,6 +19,7 @@ feature {NONE} -- Initialization
max_concurrent_connections := default_max_concurrent_connections max_concurrent_connections := default_max_concurrent_connections
max_tcp_clients := default_max_tcp_clients max_tcp_clients := default_max_tcp_clients
socket_timeout := default_socket_timeout socket_timeout := default_socket_timeout
socket_recv_timeout := default_socket_recv_timeout
keep_alive_timeout := default_keep_alive_timeout keep_alive_timeout := default_keep_alive_timeout
max_keep_alive_requests := default_max_keep_alive_requests max_keep_alive_requests := default_max_keep_alive_requests
is_secure := False is_secure := False
@@ -21,15 +27,6 @@ feature {NONE} -- Initialization
create ca_key.make_empty create ca_key.make_empty
end 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 feature -- Access
Server_details: STRING_8 Server_details: STRING_8
@@ -45,7 +42,12 @@ feature -- Access
socket_timeout: INTEGER assign set_socket_timeout socket_timeout: INTEGER assign set_socket_timeout
-- Amount of seconds that the server waits for receipts and transmissions during communications. -- Amount of seconds that the server waits for receipts and transmissions during communications.
-- note: with timeout of 0, socket can wait for ever. -- note: with timeout of 0, socket can wait for ever.
-- By default: 300 seconds, which is appropriate for most situations. -- By default: 60 seconds, which is appropriate for most situations.
socket_recv_timeout: INTEGER assign set_socket_recv_timeout
-- Amount of seconds that the server waits for receiving data during communications.
-- note: with timeout of 0, socket can wait for ever.
-- By default: 5 seconds.
max_concurrent_connections: INTEGER assign set_max_concurrent_connections max_concurrent_connections: INTEGER assign set_max_concurrent_connections
-- Max number of concurrent connections. -- Max number of concurrent connections.
@@ -83,8 +85,10 @@ feature -- Access
Result.is_verbose := is_verbose Result.is_verbose := is_verbose
Result.verbose_level := verbose_level Result.verbose_level := verbose_level
Result.timeout := socket_timeout Result.timeout := socket_timeout
Result.socket_recv_timeout := socket_recv_timeout
Result.keep_alive_timeout := keep_alive_timeout Result.keep_alive_timeout := keep_alive_timeout
Result.max_keep_alive_requests := max_keep_alive_requests Result.max_keep_alive_requests := max_keep_alive_requests
Result.is_secure := is_secure
end end
feature -- Access: SSL feature -- Access: SSL
@@ -92,10 +96,10 @@ feature -- Access: SSL
is_secure: BOOLEAN is_secure: BOOLEAN
-- Is SSL/TLS session?. -- Is SSL/TLS session?.
ca_crt: IMMUTABLE_STRING_8 ca_crt: detachable IMMUTABLE_STRING_32
-- the signed certificate. -- the signed certificate.
ca_key: IMMUTABLE_STRING_8 ca_key: detachable IMMUTABLE_STRING_32
-- private key to the certificate. -- private key to the certificate.
ssl_protocol: NATURAL ssl_protocol: NATURAL
@@ -103,6 +107,22 @@ feature -- Access: SSL
feature -- Element change feature -- Element change
set_ssl_settings (v: detachable separate TUPLE [protocol: separate READABLE_STRING_GENERAL; ca_crt, ca_key: detachable separate READABLE_STRING_GENERAL])
local
prot: STRING_32
do
is_secure := False
ca_crt := Void
ca_key := Void
if v /= Void then
is_secure := True
create prot.make_from_separate (v.protocol)
set_ssl_protocol_from_string (prot)
set_ca_crt (v.ca_crt)
set_ca_key (v.ca_key)
end
end
set_http_server_name (v: detachable separate READABLE_STRING_8) set_http_server_name (v: detachable separate READABLE_STRING_8)
do do
if v = Void then if v = Void then
@@ -152,6 +172,14 @@ feature -- Element change
socket_timeout_set: socket_timeout = a_nb_seconds socket_timeout_set: socket_timeout = a_nb_seconds
end end
set_socket_recv_timeout (a_nb_seconds: like socket_recv_timeout)
-- Set `socket_recv_timeout' with `a_nb_seconds'
do
socket_recv_timeout := a_nb_seconds
ensure
socket_recv_timeout_set: socket_recv_timeout = a_nb_seconds
end
set_keep_alive_timeout (a_seconds: like keep_alive_timeout) set_keep_alive_timeout (a_seconds: like keep_alive_timeout)
-- Set `keep_alive_timeout' with `a_seconds' -- Set `keep_alive_timeout' with `a_seconds'
do do
@@ -198,17 +226,33 @@ feature -- Element change
verbose_level_set: verbose_level = lev verbose_level_set: verbose_level = lev
end end
mark_secure set_is_secure (b: BOOLEAN)
-- Set is_secure in True -- Set `is_secure' to `b'.
do do
if has_ssl_support then if b and has_ssl_support then
is_secure := True is_secure := True
if http_server_port = 80 then if
http_server_port = 80
then
set_http_server_port (443) set_http_server_port (443)
end end
else else
is_secure := False is_secure := False
if
http_server_port = 443
then
set_http_server_port (80)
end
end end
ensure
is_secure_set: has_ssl_support implies is_secure
is_not_secure: not has_ssl_support implies not is_secure
end
mark_secure
-- Set is_secure in True
do
set_is_secure (True)
ensure ensure
is_secure_set: has_ssl_support implies is_secure is_secure_set: has_ssl_support implies is_secure
-- http_server_port_set: has_ssl_support implies http_server_port = 443 -- http_server_port_set: has_ssl_support implies http_server_port = 443
@@ -218,16 +262,24 @@ feature -- Element change
feature -- Element change feature -- Element change
set_ca_crt (a_value: separate READABLE_STRING_8) set_ca_crt (a_value: detachable separate READABLE_STRING_GENERAL)
-- Set `ca_crt' from `a_value'. -- Set `ca_crt' from `a_value'.
do do
create ca_crt.make_from_separate (a_value) if a_value /= Void then
create ca_crt.make_from_separate (a_value)
else
ca_crt := Void
end
end end
set_ca_key (a_value: separate READABLE_STRING_8) set_ca_key (a_value: detachable separate READABLE_STRING_GENERAL)
-- Set `ca_key' with `a_value'. -- Set `ca_key' with `a_value'.
do do
create ca_key.make_from_separate (a_value) if a_value /= Void then
create ca_key.make_from_separate (a_value)
else
ca_key := Void
end
end end
set_ssl_protocol (a_version: NATURAL) set_ssl_protocol (a_version: NATURAL)
@@ -238,6 +290,24 @@ feature -- Element change
ssl_protocol_set: ssl_protocol = a_version ssl_protocol_set: ssl_protocol = a_version
end end
set_ssl_protocol_from_string (a_ssl_version: READABLE_STRING_GENERAL)
-- Set `ssl_protocol' with `a_ssl_version'
do
if a_ssl_version.is_case_insensitive_equal ("ssl_2_3") then
set_ssl_protocol_to_ssl_2_or_3
elseif a_ssl_version.is_case_insensitive_equal ("tls_1_0") then
set_ssl_protocol_to_tls_1_0
elseif a_ssl_version.is_case_insensitive_equal ("tls_1_1") then
set_ssl_protocol_to_tls_1_1
elseif a_ssl_version.is_case_insensitive_equal ("tls_1_2") then
set_ssl_protocol_to_tls_1_2
elseif a_ssl_version.is_case_insensitive_equal ("dtls_1_0") then
set_ssl_protocol_to_dtls_1_0
else -- Default
set_ssl_protocol_to_tls_1_2
end
end
feature -- SSL Helpers feature -- SSL Helpers
set_ssl_protocol_to_ssl_2_or_3 set_ssl_protocol_to_ssl_2_or_3

View File

@@ -0,0 +1,28 @@
note
description: "[
Various constant values used in httpd settings.
]"
author: "$Author$"
date: "$Date$"
revision: "$Revision$"
deferred class
HTTPD_CONSTANTS
feature -- Default connection settings
default_http_server_port: INTEGER = 80
default_max_concurrent_connections: INTEGER = 100
default_max_tcp_clients: INTEGER = 100
feature -- Default timeout settings
default_socket_timeout: INTEGER = 60 -- seconds
default_socket_recv_timeout: INTEGER = 5 -- seconds
feature -- Default persistent connection settings
default_keep_alive_timeout: INTEGER = 15 -- seconds
default_max_keep_alive_requests: INTEGER = 100
end

View File

@@ -17,9 +17,15 @@ feature -- Access
verbose_level: INTEGER assign set_verbose_level verbose_level: INTEGER assign set_verbose_level
-- Verbosity of output. -- Verbosity of output.
is_secure: BOOLEAN assign set_is_secure
-- Is using secure connection? i.e SSL?
timeout: INTEGER assign set_timeout timeout: INTEGER assign set_timeout
-- Amount of seconds that the server waits for receipts and transmissions during communications. -- Amount of seconds that the server waits for receipts and transmissions during communications.
socket_recv_timeout: INTEGER assign set_socket_recv_timeout
-- Amount of seconds that the server waits for receiving data on socket during communications.
keep_alive_timeout: INTEGER assign set_keep_alive_timeout keep_alive_timeout: INTEGER assign set_keep_alive_timeout
-- Keep-alive timeout, also known as persistent-connection 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. -- Number of seconds the server waits after a request has been served before it closes the connection.
@@ -42,12 +48,24 @@ feature -- Change
verbose_level := lev verbose_level := lev
end end
set_is_secure (b: BOOLEAN)
-- Set `is_secure' to `b'.
do
is_secure := b
end
set_timeout (a_timeout_in_seconds: INTEGER) set_timeout (a_timeout_in_seconds: INTEGER)
-- Set `timeout' to `a_timeout_in_seconds'. -- Set `timeout' to `a_timeout_in_seconds'.
do do
timeout := a_timeout_in_seconds timeout := a_timeout_in_seconds
end end
set_socket_recv_timeout (a_timeout_in_seconds: INTEGER)
-- Set `socket_recv_timeout' to `a_timeout_in_seconds'.
do
socket_recv_timeout := a_timeout_in_seconds
end
set_keep_alive_timeout (a_timeout_in_seconds: INTEGER) set_keep_alive_timeout (a_timeout_in_seconds: INTEGER)
-- Set `keep_alive_timeout' to `a_timeout_in_seconds'. -- Set `keep_alive_timeout' to `a_timeout_in_seconds'.
do do

View File

@@ -10,20 +10,29 @@
<option warning="true" full_class_checking="false" is_attached_by_default="true" void_safety="all" syntax="standard"> <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"/> <assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
</option> </option>
<setting name="concurrency" value="scoop"/>
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/> <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" location="$ISE_LIBRARY\library\net\net-safe.ecf" readonly="false"/>
<library name="net_ssl" location="$ISE_LIBRARY\unstable\library\network\socket\netssl\net_ssl-safe.ecf"> <library name="net_ssl" location="$ISE_LIBRARY\unstable\library\network\socket\netssl\net_ssl-safe.ecf">
<condition> <condition>
<custom name="net_ssl_enabled" value="true"/> <custom name="net_ssl_enabled" value="true"/>
</condition> </condition>
</library> </library>
<cluster name="network" location=".\network" recursive="false"> <cluster name="network" location=".\network\">
<cluster name="ssl_network" location="$|ssl" recursive="true"> <cluster name="ssl_network" location="$|ssl\" recursive="true">
<condition> <condition>
<custom name="net_ssl_enabled" value="true"/> <custom name="net_ssl_enabled" value="true"/>
</condition> </condition>
</cluster> </cluster>
<cluster name="network_until_16_05" location="$|until_16_05\">
<condition>
<version type="compiler" max="16.11.0.0"/>
</condition>
</cluster>
<cluster name="network_from_16_11" location="$|from_16_11\">
<condition>
<version type="compiler" min="16.11.0.0"/>
</condition>
</cluster>
</cluster> </cluster>
</target> </target>
</system> </system>

View File

@@ -24,6 +24,16 @@
<custom name="net_ssl_enabled" value="true"/> <custom name="net_ssl_enabled" value="true"/>
</condition> </condition>
</cluster> </cluster>
<cluster name="network_until_16_05" location="$|until_16_05\">
<condition>
<version type="compiler" max="16.11.0.0"/>
</condition>
</cluster>
<cluster name="network_from_16_11" location="$|from_16_11\">
<condition>
<version type="compiler" min="16.11.0.0"/>
</condition>
</cluster>
</cluster> </cluster>
</target> </target>
</system> </system>

View File

@@ -30,6 +30,16 @@
<custom name="httpd_ssl_enabled" value="true"/> <custom name="httpd_ssl_enabled" value="true"/>
</condition> </condition>
</cluster> </cluster>
<cluster name="network_until_16_05" location="$|until_16_05\">
<condition>
<version type="compiler" max="16.11.0.0"/>
</condition>
</cluster>
<cluster name="network_from_16_11" location="$|from_16_11\">
<condition>
<version type="compiler" min="16.11.0.0"/>
</condition>
</cluster>
</cluster> </cluster>
<cluster name="httpd_server" location=".\" recursive="true"> <cluster name="httpd_server" location=".\" recursive="true">
<file_rule> <file_rule>

View File

@@ -11,7 +11,6 @@
</option> </option>
<setting name="concurrency" value="thread"/> <setting name="concurrency" value="thread"/>
<library name="base" location="$ISE_LIBRARY\library\base\base.ecf"/> <library name="base" location="$ISE_LIBRARY\library\base\base.ecf"/>
<library name="encoder" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\text\encoder\encoder.ecf"/>
<library name="net" location="$ISE_LIBRARY\library\net\net.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"> <library name="net_ssl" location="$ISE_LIBRARY\unstable\library\network\socket\netssl\net_ssl.ecf">
<condition> <condition>
@@ -30,6 +29,16 @@
<custom name="httpd_ssl_enabled" value="true"/> <custom name="httpd_ssl_enabled" value="true"/>
</condition> </condition>
</cluster> </cluster>
<cluster name="network_until_16_05" location="$|until_16_05\">
<condition>
<version type="compiler" max="16.11.0.0"/>
</condition>
</cluster>
<cluster name="network_from_16_11" location="$|from_16_11\">
<condition>
<version type="compiler" min="16.11.0.0"/>
</condition>
</cluster>
</cluster> </cluster>
<cluster name="httpd_server" location=".\" recursive="true"> <cluster name="httpd_server" location=".\" recursive="true">
<file_rule> <file_rule>

View File

@@ -11,6 +11,8 @@ inherit
HTTPD_LOGGER_CONSTANTS HTTPD_LOGGER_CONSTANTS
HTTPD_SOCKET_FACTORY
feature {NONE} -- Initialization feature {NONE} -- Initialization
make (a_request_settings: HTTPD_REQUEST_SETTINGS) make (a_request_settings: HTTPD_REQUEST_SETTINGS)
@@ -18,11 +20,13 @@ feature {NONE} -- Initialization
reset reset
-- Import global request settings. -- Import global request settings.
timeout := a_request_settings.timeout -- seconds timeout := a_request_settings.timeout -- seconds
socket_recv_timeout := a_request_settings.socket_recv_timeout -- seconds
keep_alive_timeout := a_request_settings.keep_alive_timeout -- seconds keep_alive_timeout := a_request_settings.keep_alive_timeout -- seconds
max_keep_alive_requests := a_request_settings.max_keep_alive_requests max_keep_alive_requests := a_request_settings.max_keep_alive_requests
is_verbose := a_request_settings.is_verbose is_verbose := a_request_settings.is_verbose
verbose_level := a_request_settings.verbose_level verbose_level := a_request_settings.verbose_level
is_secure := a_request_settings.is_secure
end end
reset reset
@@ -68,7 +72,7 @@ feature -- Access
do do
s := internal_client_socket s := internal_client_socket
if s = Void then if s = Void then
create s.make_empty s := new_client_socket (is_secure)
internal_client_socket := s internal_client_socket := s
end end
Result := s Result := s
@@ -121,6 +125,10 @@ feature -- Settings
verbose_level: INTEGER verbose_level: INTEGER
-- Output verbosity. -- Output verbosity.
is_secure: BOOLEAN
-- Is secure socket?
-- i.e: SSL?
is_persistent_connection_supported: BOOLEAN is_persistent_connection_supported: BOOLEAN
-- Is persistent connection supported? -- Is persistent connection supported?
do do
@@ -134,6 +142,9 @@ feature -- Settings
timeout: INTEGER -- seconds timeout: INTEGER -- seconds
-- Amount of seconds that the server waits for receipts and transmissions during communications. -- Amount of seconds that the server waits for receipts and transmissions during communications.
socket_recv_timeout: INTEGER -- seconds
-- Amount of seconds that the server waits for receiving data on socket during communications.
max_keep_alive_requests: INTEGER max_keep_alive_requests: INTEGER
-- Maximum number of requests allowed per persistent connection. -- Maximum number of requests allowed per persistent connection.
@@ -187,6 +198,7 @@ feature -- Execution
n,m: INTEGER n,m: INTEGER
do do
l_socket := client_socket l_socket := client_socket
l_socket.set_recv_timeout (socket_recv_timeout)
check check
socket_attached: l_socket /= Void socket_attached: l_socket /= Void
socket_valid: l_socket.is_open_read and then l_socket.is_open_write socket_valid: l_socket.is_open_read and then l_socket.is_open_write
@@ -206,18 +218,24 @@ feature -- Execution
log ("Reuse connection (" + n.out + ")", information_level) log ("Reuse connection (" + n.out + ")", information_level)
end end
-- FIXME: it seems to be called one more time, mostly to see this is done. -- FIXME: it seems to be called one more time, mostly to see this is done.
execute_request execute_request (n > 1)
l_exit := not is_persistent_connection_supported l_exit := not is_persistent_connection_supported
or not is_next_persistent_connection_supported -- related to `max_keep_alive_requests' or not is_next_persistent_connection_supported -- related to `max_keep_alive_requests'
or not is_persistent_connection_requested or not is_persistent_connection_requested
or has_error or l_socket.is_closed or not l_socket.is_open_read or has_error or l_socket.is_closed or not l_socket.is_open_read
reset_request reset_request
end end
if l_exit and has_error and not l_socket.is_closed then
l_socket.close
end
end end
execute_request execute_request (a_is_reusing_connection: BOOLEAN)
-- Execute http request, and if `a_is_reusing_connection' is True
-- the execution is reusing the persistent connection.
require require
is_connected: is_connected is_connected: is_connected
reuse_connection_when_possible: a_is_reusing_connection implies is_persistent_connection_supported
local local
l_remote_info: detachable like remote_info l_remote_info: detachable like remote_info
l_socket: like client_socket l_socket: like client_socket
@@ -237,13 +255,16 @@ feature -- Execution
dbglog (generator + ".execute_request socket=" + l_socket.descriptor.out + " ENTER") dbglog (generator + ".execute_request socket=" + l_socket.descriptor.out + " ENTER")
end end
--| TODO: add configuration options for socket timeout. if a_is_reusing_connection then
--| set by default 5 seconds. --| set by default 5 seconds.
l_socket.set_timeout (keep_alive_timeout) -- 5 seconds! l_socket.set_recv_timeout (keep_alive_timeout) -- in seconds!
l_is_ready := l_socket.ready_for_reading l_is_ready := l_socket.ready_for_reading
else
l_is_ready := True
end
if l_is_ready then if l_is_ready then
l_socket.set_timeout (timeout) -- FIXME: return a 408 Request Timeout response .. l_socket.set_recv_timeout (socket_recv_timeout) -- FIXME: return a 408 Request Timeout response ..
create l_remote_info create l_remote_info
if attached l_socket.peer_address as l_addr then if attached l_socket.peer_address as l_addr then
l_remote_info.addr := l_addr.host_address.host_address l_remote_info.addr := l_addr.host_address.host_address
@@ -326,8 +347,7 @@ feature -- Parsing
has_error := True has_error := True
end end
l_is_verbose := is_verbose l_is_verbose := is_verbose
if not has_error or l_is_verbose then if not has_error then
-- if `is_verbose' we can try to print the request, even if it is a bad HTTP request
from from
line := next_line (a_socket) line := next_line (a_socket)
until until

View File

@@ -108,6 +108,7 @@ feature -- Execution
log (" - max_tcp_clients = " + configuration.max_tcp_clients.out) log (" - max_tcp_clients = " + configuration.max_tcp_clients.out)
log (" - max_concurrent_connections = " + configuration.max_concurrent_connections.out) log (" - max_concurrent_connections = " + configuration.max_concurrent_connections.out)
log (" - socket_timeout = " + configuration.socket_timeout.out + " seconds") log (" - socket_timeout = " + configuration.socket_timeout.out + " seconds")
log (" - socket_recv_timeout = " + configuration.socket_recv_timeout.out + " seconds")
log (" - keep_alive_timeout = " + configuration.keep_alive_timeout.out + " seconds") log (" - keep_alive_timeout = " + configuration.keep_alive_timeout.out + " seconds")
log (" - max_keep_alive_requests = " + configuration.max_keep_alive_requests.out) log (" - max_keep_alive_requests = " + configuration.max_keep_alive_requests.out)
if configuration.verbose_level > 0 then if configuration.verbose_level > 0 then

View File

@@ -0,0 +1,9 @@
note
description: "[
Since 16.11, the EiffelNet socket interface has recv_timeout and send_timeout.
]"
deferred class
TCP_STREAM_SOCKET_EXT
end

View File

@@ -62,6 +62,7 @@ feature {NONE} -- Initialization
feature -- Change feature -- Change
set_timeout (n: INTEGER) set_timeout (n: INTEGER)
-- Set timeout to `n' seconds.
do do
if attached {NETWORK_STREAM_SOCKET} socket as l_socket then if attached {NETWORK_STREAM_SOCKET} socket as l_socket then
l_socket.set_timeout (n) l_socket.set_timeout (n)
@@ -82,6 +83,22 @@ feature -- Change
end end
end end
set_recv_timeout (a_timeout_seconds: INTEGER)
-- Set the receive timeout in seconds on Current socket.
do
if attached {TCP_STREAM_SOCKET} socket as l_socket then
l_socket.set_recv_timeout (a_timeout_seconds)
end
end
set_send_timeout (a_timeout_seconds: INTEGER)
-- Set the send timeout in seconds on Current socket.
do
if attached {TCP_STREAM_SOCKET} socket as l_socket then
l_socket.set_send_timeout (a_timeout_seconds)
end
end
feature -- Access feature -- Access
last_string: STRING last_string: STRING

View File

@@ -17,58 +17,66 @@ inherit
ready_for_writing, ready_for_writing,
ready_for_reading, ready_for_reading,
try_ready_for_reading, try_ready_for_reading,
put_readable_string_8 put_readable_string_8,
make_empty
end end
create create
make_ssl_server_by_address_and_port, make_ssl_server_by_port, 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_ssl_client_by_address_and_port, make_ssl_client_by_port,
make_client_by_address_and_port, make_client_by_port make_client_by_address_and_port, make_client_by_port,
make_empty
create {HTTPD_STREAM_SOCKET} create {HTTPD_STREAM_SOCKET}
make make
feature {NONE} -- Initialization feature {NONE} -- Initialization
make_ssl_server_by_address_and_port (an_address: INET_ADDRESS; a_port: INTEGER; a_ssl_protocol: NATURAL; a_crt: STRING; a_key: STRING) make_ssl_server_by_address_and_port (an_address: INET_ADDRESS; a_port: INTEGER; a_ssl_protocol: NATURAL; a_crt_fn, a_key_fn: detachable READABLE_STRING_GENERAL)
local local
l_socket: SSL_TCP_STREAM_SOCKET l_socket: SSL_TCP_STREAM_SOCKET
do do
create l_socket.make_server_by_address_and_port (an_address, a_port) create l_socket.make_server_by_address_and_port (an_address, a_port)
l_socket.set_tls_protocol (a_ssl_protocol) l_socket.set_tls_protocol (a_ssl_protocol)
socket := l_socket socket := l_socket
set_certificates (a_crt, a_key) set_certificates (a_crt_fn, a_key_fn)
end end
make_ssl_server_by_port (a_port: INTEGER; a_ssl_protocol: NATURAL; a_crt: STRING; a_key: STRING) make_ssl_server_by_port (a_port: INTEGER; a_ssl_protocol: NATURAL; a_crt_fn, a_key_fn: detachable READABLE_STRING_GENERAL)
local local
l_socket: SSL_TCP_STREAM_SOCKET l_socket: SSL_TCP_STREAM_SOCKET
do do
create l_socket.make_server_by_port (a_port) create l_socket.make_server_by_port (a_port)
l_socket.set_tls_protocol (a_ssl_protocol) l_socket.set_tls_protocol (a_ssl_protocol)
socket := l_socket socket := l_socket
set_certificates (a_crt, a_key) set_certificates (a_crt_fn, a_key_fn)
end 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) make_ssl_client_by_address_and_port (an_address: INET_ADDRESS; a_port: INTEGER; a_ssl_protocol: NATURAL; a_crt_fn, a_key_fn: detachable READABLE_STRING_GENERAL)
local local
l_socket: SSL_TCP_STREAM_SOCKET l_socket: SSL_TCP_STREAM_SOCKET
do do
create l_socket.make_client_by_address_and_port (an_address, a_port) create l_socket.make_client_by_address_and_port (an_address, a_port)
l_socket.set_tls_protocol (a_ssl_protocol) l_socket.set_tls_protocol (a_ssl_protocol)
socket := l_socket socket := l_socket
set_certificates (a_crt, a_key) set_certificates (a_crt_fn, a_key_fn)
end end
make_ssl_client_by_port (a_peer_port: INTEGER; a_peer_host: STRING; a_ssl_protocol: NATURAL; a_crt: STRING; a_key: STRING) make_ssl_client_by_port (a_peer_port: INTEGER; a_peer_host: STRING; a_ssl_protocol: NATURAL; a_crt_fn, a_key_fn: detachable READABLE_STRING_GENERAL)
local local
l_socket: SSL_TCP_STREAM_SOCKET l_socket: SSL_TCP_STREAM_SOCKET
do do
create l_socket.make_client_by_port (a_peer_port, a_peer_host) create l_socket.make_client_by_port (a_peer_port, a_peer_host)
l_socket.set_tls_protocol (a_ssl_protocol) l_socket.set_tls_protocol (a_ssl_protocol)
socket := l_socket socket := l_socket
set_certificates (a_crt, a_key) set_certificates (a_crt_fn, a_key_fn)
end
make_empty
-- <Precursor>.
do
create {SSL_TCP_STREAM_SOCKET} socket.make_empty
end end
feature -- Output feature -- Output
@@ -136,15 +144,15 @@ feature -- Status Report
feature {HTTPD_STREAM_SOCKET} -- Implementation feature {HTTPD_STREAM_SOCKET} -- Implementation
set_certificates (a_crt: STRING; a_key: STRING) set_certificates (a_crt_filename, a_key_filename: detachable READABLE_STRING_GENERAL)
local
a_file_name: FILE_NAME
do do
if attached {SSL_NETWORK_STREAM_SOCKET} socket as l_socket then if attached {SSL_NETWORK_STREAM_SOCKET} socket as l_socket then
create a_file_name.make_from_string (a_crt) if a_crt_filename /= Void then
l_socket.set_certificate_file_name (a_file_name) l_socket.set_certificate_file_name (a_crt_filename)
create a_file_name.make_from_string (a_key) end
l_socket.set_key_file_name (a_file_name) if a_key_filename /= Void then
l_socket.set_key_file_name (a_key_filename)
end
end end
end end

View File

@@ -12,6 +12,8 @@ inherit
make make
end end
TCP_STREAM_SOCKET_EXT
create create
make_server_by_address_and_port, make_server_by_address_and_port,
make_server_by_port, make_server_by_port,

View File

@@ -0,0 +1,104 @@
note
description: "[
Until 16.05, the EiffelNet socket interface DOES NOT have recv_timeout and send_timeout.
]"
deferred class
TCP_STREAM_SOCKET_EXT
feature -- Access
descriptor: INTEGER
-- Socket descriptor of current socket
deferred
end
feature -- Socket Recv and Send timeout.
-- recv_timeout: INTEGER
-- -- Receive timeout in seconds on Current socket.
-- do
-- Result := c_get_sock_recv_timeout (descriptor, level_sol_socket)
-- ensure
-- result_not_negative: Result >= 0
-- end
--
-- send_timeout: INTEGER
-- -- Send timeout in seconds on Current socket.
-- do
-- Result := c_get_sock_send_timeout (descriptor, level_sol_socket)
-- ensure
-- result_not_negative: Result >= 0
-- end
set_recv_timeout (a_timeout_seconds: INTEGER)
-- Set the receive timeout in seconds on Current socket.
-- if `0' the related operations will never timeout.
require
positive_timeout: a_timeout_seconds >= 0
do
c_set_sock_recv_timeout (descriptor, level_sol_socket, a_timeout_seconds)
end
set_send_timeout (a_timeout_seconds: INTEGER)
-- Set the send timeout in milliseconds on Current socket.
-- if `0' the related operations will never timeout.
require
positive_timeout: a_timeout_seconds >= 0
do
c_set_sock_send_timeout (descriptor, level_sol_socket, a_timeout_seconds)
end
feature {NONE} -- Externals
level_sol_socket: INTEGER
-- SOL_SOCKET level of options
deferred
end
-- set_so_rcvtimeo (a_timeout_seconds: INTEGER)
-- -- Set the receive timeout in seconds on Current socket.
-- -- if `0' the related operations will never timeout.
-- require
-- positive_timeout: a_timeout_seconds >= 0
-- do
-- c_set_sock_recv_timeout (descriptor, level_sol_socket, a_timeout_seconds)
-- end
c_set_sock_recv_timeout (a_fd, a_level: INTEGER; a_timeout_seconds: INTEGER)
-- C routine to set socket option `SO_RCVTIMEO' with `a_timeout_seconds' seconds.
external
"C inline"
alias
"[
#ifdef EIF_WINDOWS
int arg = (int) 1000 * $a_timeout_seconds; /* Timeout in milliseconds */
setsockopt((SOCKET) $a_fd, (int) $a_level, (int) SO_RCVTIMEO, (char *) &arg, sizeof(arg));
#else
struct timeval tv;
tv.tv_sec = $a_timeout_seconds; /* Timeout in seconds */
setsockopt((int) $a_fd, (int) $a_level, (int) SO_RCVTIMEO, (struct timeval *)&tv, sizeof(struct timeval));
#endif
]"
end
c_set_sock_send_timeout (a_fd, a_level: INTEGER; a_timeout_seconds: INTEGER)
-- C routine to set socket option `SO_SNDTIMEO' with `a_timeout_seconds' seconds.
external
"C inline"
alias
"[
#ifdef EIF_WINDOWS
int arg = (int) 1000 * $a_timeout_seconds; /* Timeout in milliseconds */
setsockopt((SOCKET) $a_fd, (int) $a_level, (int) SO_SNDTIMEO, (char *) &arg, sizeof(arg));
#else
struct timeval tv;
tv.tv_sec = $a_timeout_seconds; /* Timeout in seconds */
setsockopt((int) $a_fd, (int) $a_level, (int) SO_SNDTIMEO, (struct timeval *)&tv, sizeof(struct timeval));
#endif
]"
end
end

View File

@@ -0,0 +1,17 @@
note
description: "Summary description for {HTTPD_SOCKET_FACTORY}."
date: "$Date$"
revision: "$Revision$"
deferred class
HTTPD_SOCKET_FACTORY
feature -- Access
new_client_socket (a_is_secure: BOOLEAN): HTTPD_STREAM_SOCKET
do
check not_secure: not a_is_secure end
create Result.make_empty
end
end

View File

@@ -0,0 +1,20 @@
note
description: "Summary description for {HTTPD_SOCKET_FACTORY}."
date: "$Date$"
revision: "$Revision$"
deferred class
HTTPD_SOCKET_FACTORY
feature -- Access
new_client_socket (a_is_secure: BOOLEAN): HTTPD_STREAM_SOCKET
do
if a_is_secure then
create {HTTPD_STREAM_SSL_SOCKET} Result.make_empty
else
create Result.make_empty
end
end
end

View File

@@ -230,6 +230,9 @@ feature -- Request processing
set_environment_variable (l_server_port, "SERVER_PORT", Result) set_environment_variable (l_server_port, "SERVER_PORT", Result)
set_environment_variable (version, "SERVER_PROTOCOL", Result) set_environment_variable (version, "SERVER_PROTOCOL", Result)
set_environment_variable ({HTTPD_CONFIGURATION}.Server_details, "SERVER_SOFTWARE", Result) set_environment_variable ({HTTPD_CONFIGURATION}.Server_details, "SERVER_SOFTWARE", Result)
if is_secure then
set_environment_variable ("on", "HTTPS", Result)
end
--| Apply `base' value --| Apply `base' value
l_base := base l_base := base

View File

@@ -2,8 +2,8 @@ note
description: "[ description: "[
Standalone Web Server connector. Standalone Web Server connector.
]" ]"
date: "$Date$" date: "$Date: 2016-08-06 13:34:52 +0200 (sam., 06 août 2016) $"
revision: "$Revision$" revision: "$Revision: 99106 $"
class class
WGI_STANDALONE_CONNECTOR [G -> WGI_EXECUTION create make end] WGI_STANDALONE_CONNECTOR [G -> WGI_EXECUTION create make end]
@@ -155,6 +155,12 @@ feature -- Element change
set_is_verbose_on_configuration (b, configuration) set_is_verbose_on_configuration (b, configuration)
end end
set_is_secure (b: BOOLEAN)
-- Set is_secure connection mode.
-- i.e: using SSL.
do
set_is_secure_on_configuration (b, configuration)
end
feature -- Server feature -- Server
@@ -242,6 +248,11 @@ feature {NONE} -- Implementation: element change
cfg.set_is_verbose (b) cfg.set_is_verbose (b)
end end
set_is_secure_on_configuration (b: BOOLEAN; cfg: like configuration)
do
cfg.set_is_secure (b)
end
note note
copyright: "2011-2016, Jocelyn Fiat, Javier Velilla, Eiffel Software and others" copyright: "2011-2016, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"

View File

@@ -0,0 +1,18 @@
note
description: "[
Constants value related to Standalone connector,
and indirectly to `httpd' component.
]"
author: "$Author$"
date: "$Date$"
revision: "$Revision$"
deferred class
WGI_STANDALONE_CONSTANTS
inherit
ANY
HTTPD_CONSTANTS
end

View File

@@ -1,24 +0,0 @@
<?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="connector_standalone_websocket" uuid="38E6F413-E001-4774-9B4C-E0C08753D9F7" library_target="connector_standalone_websocket">
<target name="connector_standalone_websocket">
<root all_classes="true"/>
<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="transitional">
<debug name="dbglog" enabled="true"/>
<assertions precondition="true"/>
</option>
<setting name="concurrency" value="scoop"/>
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
<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="lib\httpd\httpd-safe.ecf" readonly="false"/>
<cluster name="src" location=".\src\">
<cluster name="implementation" location="$|implementation\" hidden="true"/>
</cluster>
</target>
</system>

View File

@@ -2,14 +2,27 @@ note
description: "[ description: "[
Component to launch the service using the default connector Component to launch the service using the default connector
Eiffel Web httpd for this class EiffelWeb httpd for this class
The httpd default connector support options:
verbose: to display verbose output
port: numeric such as 8099 (or equivalent string as "8099")
base: base_url (very specific to standalone server)
max_concurrent_connections: set one, for single threaded behavior
max_tcp_clients: max number of open tcp connection
socket_timeout: connection timeout
socket_recv_timeout: read data timeout
keep_alive_timeout: amount of time the server will wait for subsequent
requests on a persistent connection,
max_keep_alive_requests: number of requests allowed on a persistent connection,
ssl_enabled: set to True for https support.
ssl_ca_crt: path to the certificat crt file (relevant when ssl_enabled is True)
ssl_ca_key: path to the certificat key file (relevant when ssl_enabled is True)
The httpd default connector support options:
port: numeric such as 8099 (or equivalent string as "8099")
base: base_url (very specific to standalone server)
verbose: to display verbose output, useful for standalone connector
force_single_threaded: use only one thread, useful for standalone connector
check WSF_SERVICE_LAUNCHER for more documentation check WSF_SERVICE_LAUNCHER for more documentation
]" ]"
@@ -41,12 +54,13 @@ feature {NONE} -- Initialization
create on_launched_actions create on_launched_actions
create on_stopped_actions create on_stopped_actions
port_number := 80 --| Default, but quite often, this port is already used ... port_number := {WGI_STANDALONE_CONSTANTS}.default_http_server_port --| Default, but quite often, this port is already used ...
max_concurrent_connections := 100 max_concurrent_connections := {WGI_STANDALONE_CONSTANTS}.default_max_concurrent_connections
max_tcp_clients := 100 max_tcp_clients := {WGI_STANDALONE_CONSTANTS}.default_max_tcp_clients
socket_timeout := 300 -- 300 seconds socket_timeout := {WGI_STANDALONE_CONSTANTS}.default_socket_timeout -- seconds
keep_alive_timeout := 15 -- 15 seconds. socket_recv_timeout := {WGI_STANDALONE_CONSTANTS}.default_socket_recv_timeout -- seconds
max_keep_alive_requests := 100 keep_alive_timeout := {WGI_STANDALONE_CONSTANTS}.default_keep_alive_timeout -- seconds.
max_keep_alive_requests := {WGI_STANDALONE_CONSTANTS}.default_max_keep_alive_requests
verbose := False verbose := False
verbose_level := notice_level verbose_level := notice_level
@@ -59,6 +73,7 @@ feature {NONE} -- Initialization
if attached {READABLE_STRING_GENERAL} opts.option ("base") as l_base_str then if attached {READABLE_STRING_GENERAL} opts.option ("base") as l_base_str then
base_url := l_base_str.as_string_8 base_url := l_base_str.as_string_8
end end
verbose := opts.option_boolean_value ("verbose", verbose) verbose := opts.option_boolean_value ("verbose", verbose)
-- See `{HTTPD_REQUEST_HANDLER_I}.*_verbose_level` -- See `{HTTPD_REQUEST_HANDLER_I}.*_verbose_level`
@@ -96,8 +111,16 @@ feature {NONE} -- Initialization
max_concurrent_connections := opts.option_integer_value ("max_concurrent_connections", max_concurrent_connections) 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) max_tcp_clients := opts.option_integer_value ("max_tcp_clients", max_tcp_clients)
socket_timeout := opts.option_integer_value ("socket_timeout", socket_timeout) socket_timeout := opts.option_integer_value ("socket_timeout", socket_timeout)
socket_recv_timeout := opts.option_integer_value ("socket_recv_timeout", socket_recv_timeout)
keep_alive_timeout := opts.option_integer_value ("keep_alive_timeout", keep_alive_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) max_keep_alive_requests := opts.option_integer_value ("max_keep_alive_requests", max_keep_alive_requests)
if
opts.option_boolean_value ("ssl_enabled", ssl_enabled) and then
attached opts.option_string_32_value ("ssl_protocol", "tls_1_2") as ssl_prot
then
ssl_settings := [ssl_prot, opts.option_string_32_value ("ssl_ca_crt", Void), opts.option_string_32_value ("ssl_ca_key", Void)]
end
end end
create conn.make create conn.make
@@ -120,11 +143,13 @@ feature -- Execution
do do
cfg.set_is_verbose (verbose) cfg.set_is_verbose (verbose)
cfg.set_verbose_level (verbose_level) cfg.set_verbose_level (verbose_level)
cfg.set_ssl_settings (ssl_settings)
cfg.set_http_server_name (server_name) cfg.set_http_server_name (server_name)
cfg.http_server_port := port_number cfg.http_server_port := port_number
cfg.set_max_concurrent_connections (max_concurrent_connections) cfg.set_max_concurrent_connections (max_concurrent_connections)
cfg.set_max_tcp_clients (max_tcp_clients) cfg.set_max_tcp_clients (max_tcp_clients)
cfg.set_socket_timeout (socket_timeout) cfg.set_socket_timeout (socket_timeout)
cfg.set_socket_recv_timeout (socket_recv_timeout)
cfg.set_keep_alive_timeout (keep_alive_timeout) cfg.set_keep_alive_timeout (keep_alive_timeout)
cfg.set_max_keep_alive_requests (max_keep_alive_requests) cfg.set_max_keep_alive_requests (max_keep_alive_requests)
end end
@@ -140,11 +165,17 @@ feature -- Execution
debug ("ew_standalone") debug ("ew_standalone")
if verbose then if verbose then
io.error.put_string ("Launching standalone web server on port " + port_number.out) io.error.put_string ("Launching standalone web server on port " + port_number.out)
if attached server_name as l_name then if ssl_enabled then
io.error.put_string ("%N http://" + l_name + ":" + port_number.out + "/" + base_url + "%N") io.error.put_string ("%N https://")
else else
io.error.put_string ("%N http://localhost:" + port_number.out + "/" + base_url + "%N") io.error.put_string ("%N http://")
end end
if attached server_name as l_name then
io.error.put_string (l_name)
else
io.error.put_string ("localhost")
end
io.error.put_string (":" + port_number.out + "/" + base_url + "%N")
end end
end end
update_configuration (conn.configuration) update_configuration (conn.configuration)
@@ -177,9 +208,18 @@ feature {NONE} -- Implementation
-- Help defining the verbosity. -- Help defining the verbosity.
-- The higher, the more output. -- The higher, the more output.
ssl_settings: detachable TUPLE [protocol: READABLE_STRING_GENERAL; ca_crt, ca_key: detachable READABLE_STRING_GENERAL]
ssl_enabled: BOOLEAN
-- Is secure server? i.e using SSL?
do
Result := attached ssl_settings as ssl and then attached ssl.protocol as prot and then not prot.is_whitespace
end
max_concurrent_connections: INTEGER max_concurrent_connections: INTEGER
max_tcp_clients: INTEGER max_tcp_clients: INTEGER
socket_timeout: INTEGER socket_timeout: INTEGER
socket_recv_timeout: INTEGER
keep_alive_timeout: INTEGER keep_alive_timeout: INTEGER
max_keep_alive_requests: INTEGER max_keep_alive_requests: INTEGER

View File

@@ -22,11 +22,11 @@ note
For instance, you can use For instance, you can use
create s.make_and_launch_and_options (agent execute, <<["port", 8099]>>) create s.make_and_launch_and_options (agent execute, <<["port", 8099]>>)
And if Nino is the default connector it will support: And if the connector is the Standalone connector,
check {WSF_STANDALONE_SERVICE_LAUNCHER} for options description, such as:
port: numeric such as 8099 (or equivalent string as "8099") port: numeric such as 8099 (or equivalent string as "8099")
base: base_url (very specific to standalone server) base: base_url (very specific to standalone server)
force_single_threaded: use only one thread, useful for Nino verbose: to display verbose output.
verbose: to display verbose output, useful for Nino
]" ]"
date: "$Date$" date: "$Date$"
revision: "$Revision$" revision: "$Revision$"

View File

@@ -8,8 +8,8 @@ note
force_single_threaded: use only one thread, useful for Nino force_single_threaded: use only one thread, useful for Nino
verbose: to display verbose output, useful for Nino verbose: to display verbose output, useful for Nino
]" ]"
date: "$Date$" date: "$Date: 2016-08-06 13:34:52 +0200 (sam., 06 août 2016) $"
revision: "$Revision$" revision: "$Revision: 99106 $"
class class
WSF_SERVICE_LAUNCHER_OPTIONS WSF_SERVICE_LAUNCHER_OPTIONS
@@ -85,6 +85,12 @@ feature -- Access
feature -- Helpers feature -- Helpers
has_option (a_opt_name: READABLE_STRING_GENERAL): BOOLEAN
-- Is there any value associated to option name `a_opt_name'?
do
Result := attached option (a_opt_name)
end
has_integer_option (a_opt_name: READABLE_STRING_GENERAL): BOOLEAN has_integer_option (a_opt_name: READABLE_STRING_GENERAL): BOOLEAN
-- Is there any INTEGER value associated to option name `a_opt_name'? -- Is there any INTEGER value associated to option name `a_opt_name'?
local local
@@ -100,6 +106,29 @@ feature -- Helpers
end end
end end
has_string_32_option (a_opt_name: READABLE_STRING_GENERAL): BOOLEAN
-- Is there any string 32 value associated to option name `a_opt_name'?
do
if attached option (a_opt_name) as opt then
Result := attached {READABLE_STRING_GENERAL} opt
end
end
option_string_32_value (a_opt_name: READABLE_STRING_GENERAL; a_default: detachable READABLE_STRING_GENERAL): detachable IMMUTABLE_STRING_32
-- Unicode String value associated to option name `a_opt_name', other return `a_default'.
do
if attached option (a_opt_name) as opt then
if attached {READABLE_STRING_32} opt as s32 then
create Result.make_from_string (s32)
elseif attached {READABLE_STRING_GENERAL} opt as s then
create Result.make_from_string_general (s)
end
end
if Result = Void and a_default /= Void then
create Result.make_from_string_general (a_default)
end
end
option_integer_value (a_opt_name: READABLE_STRING_GENERAL; a_default: INTEGER): INTEGER 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'. -- INTEGER value associated to option name `a_opt_name', other return `a_default'.
local local

View File

@@ -261,10 +261,12 @@ feature -- Access: Input
local local
l_input: WGI_INPUT_STREAM l_input: WGI_INPUT_STREAM
n: INTEGER n: INTEGER
buf_initial_size: INTEGER
do do
if raw_input_data_recorded and then attached raw_input_data as d then if raw_input_data_recorded and then attached raw_input_data as d then
buf.append (d) buf.append (d)
else else
buf_initial_size := buf.count
l_input := input l_input := input
if is_chunked_input then if is_chunked_input then
from from
@@ -286,73 +288,53 @@ feature -- Access: Input
end end
end end
if raw_input_data_recorded then if raw_input_data_recorded then
set_raw_input_data (buf) set_raw_input_data (buf.substring (buf_initial_size + 1, buf.count))
-- Only the input data! And differente reference.
end end
end end
end end
read_input_data_into_file (a_file: FILE) read_input_data_into_file (a_medium: IO_MEDIUM)
-- retrieve the content from the `input' stream into `s' -- retrieve the content from the `input' stream into `s'
-- warning: if the input data has already been retrieved -- warning: if the input data has already been retrieved
-- you might not get anything -- you might not get anything
require require
a_file_is_open_write: a_file.is_open_write a_medium_is_open_write: a_medium.is_open_write
local local
s: STRING s: STRING
l_input: WGI_INPUT_STREAM l_input: WGI_INPUT_STREAM
l_raw_data: detachable STRING_8 l_raw_data: detachable STRING_8
len: NATURAL_64
nb, l_step: INTEGER nb, l_step: INTEGER
l_size: NATURAL_64
do do
if raw_input_data_recorded and then attached raw_input_data as d then if raw_input_data_recorded and then attached raw_input_data as d then
a_file.put_string (d) a_medium.put_string (d)
else else
if raw_input_data_recorded then if raw_input_data_recorded then
create l_raw_data.make_empty create l_raw_data.make_empty
end end
l_input := input l_input := input
len := content_length_value
debug ("wsf")
io.error.put_string (generator + ".read_input_data_into_file (a_file) content_length=" + len.out + "%N")
end
from from
l_size := 0
l_step := 8_192 l_step := 8_192
create s.make (l_step) create s.make (l_step)
until until
l_step = 0 or l_input.end_of_input l_step = 0 or l_input.end_of_input
loop loop
if len < l_step.to_natural_64 then l_input.append_to_string (s, l_step)
l_step := len.to_integer_32 nb := l_input.last_appended_count
a_medium.put_string (s)
if l_raw_data /= Void then
l_raw_data.append (s)
end end
if l_step > 0 then s.wipe_out
l_input.append_to_string (s, l_step) if nb < l_step then
nb := l_input.last_appended_count l_step := 0
l_size := l_size + nb.to_natural_64
len := len - nb.to_natural_64
debug ("wsf")
io.error.put_string (" append (s, " + l_step.out + ") -> " + nb.out + " (" + l_size.out + " / "+ content_length_value.out + ")%N")
end
a_file.put_string (s)
if l_raw_data /= Void then
l_raw_data.append (s)
end
s.wipe_out
if nb < l_step then
l_step := 0
end
end end
end end
a_file.flush if attached {FILE} a_medium as f then
debug ("wsf") f.flush
io.error.put_string ("offset =" + len.out + "%N")
end end
check got_all_data: len = 0 end
if l_raw_data /= Void then if l_raw_data /= Void then
set_raw_input_data (l_raw_data) set_raw_input_data (l_raw_data)
end end

View File

@@ -0,0 +1,20 @@
note
description: "Summary description for {WSF_PROXY_SOCKET_FACTORY}."
author: ""
date: "$Date$"
revision: "$Revision$"
class
WSF_PROXY_SOCKET_FACTORY
inherit
WSF_PROXY_SOCKET_FACTORY_I
feature {NONE} -- Implementation
ssl_socket (a_host: READABLE_STRING_8; a_port: INTEGER): detachable NETWORK_STREAM_SOCKET
do
check supported: False end
end
end

View File

@@ -0,0 +1,30 @@
note
description: "Summary description for {WSF_PROXY_SOCKET_FACTORY}."
author: ""
date: "$Date$"
revision: "$Revision$"
class
WSF_PROXY_SOCKET_FACTORY
inherit
WSF_PROXY_SOCKET_FACTORY_I
redefine
is_ssl_supported
end
feature {NONE} -- Implementation
ssl_socket (a_host: READABLE_STRING_8; a_port: INTEGER): detachable SSL_NETWORK_STREAM_SOCKET
do
if attached create_from_name (a_host) as l_peer_address then
create Result.make_client_by_address_and_port (l_peer_address, a_port)
end
end
feature -- Status
is_ssl_supported: BOOLEAN = True
-- Is https:// supported?
end

View File

@@ -0,0 +1,67 @@
note
description: "Summary description for {WSF_PROXY_SOCKET_FACTORY_I}."
date: "$Date$"
revision: "$Revision$"
deferred class
WSF_PROXY_SOCKET_FACTORY_I
inherit
INET_ADDRESS_FACTORY
feature -- Access
socket_from_uri (a_uri: URI): like socket
local
l_port: INTEGER
do
if a_uri.is_valid and then attached a_uri.host as l_host then
l_port := a_uri.port
if a_uri.scheme.is_case_insensitive_equal_general ("https") then
if is_ssl_supported then
if l_port <= 0 then
l_port := 443
end
Result := ssl_socket (l_host, l_port)
end
elseif a_uri.scheme.is_case_insensitive_equal_general ("http") then
if l_port <= 0 then
l_port := 80
end
Result := socket (l_host, l_port)
end
end
end
feature -- Status
is_uri_supported (a_uri: URI): BOOLEAN
do
Result := a_uri.scheme.is_case_insensitive_equal_general ("http")
or else (
a_uri.scheme.is_case_insensitive_equal_general ("https")
and is_ssl_supported
)
end
is_ssl_supported: BOOLEAN
-- Is https:// supported?
do
end
feature {NONE} -- Implementation
socket (a_host: READABLE_STRING_8; a_port: INTEGER): detachable NETWORK_STREAM_SOCKET
do
if attached create_from_name (a_host) as l_peer_address then
create Result.make_client_by_address_and_port (l_peer_address, a_port)
end
end
ssl_socket (a_host: READABLE_STRING_8; a_port: INTEGER): detachable NETWORK_STREAM_SOCKET
require
is_ssl_supported: is_ssl_supported
deferred
end
end

View File

@@ -0,0 +1,303 @@
note
description: "Summary description for {WSF_SIMPLE_REVERSE_PROXY_HANDLER}."
date: "$Date$"
revision: "$Revision$"
class
WSF_SIMPLE_REVERSE_PROXY_HANDLER
create
make
feature {NONE} -- Initialization
make (a_remote_uri: READABLE_STRING_8)
do
create remote_uri.make_from_string (a_remote_uri)
timeout := 30 -- seconds. See {NETWORK_SOCKET}.default_timeout
connect_timeout := 5_000 -- 5 seconds.
is_via_header_supported := True
end
feature -- Access
remote_uri: URI
-- Url for the targetted service.
uri_rewriter: detachable WSF_URI_REWRITER assign set_uri_rewriter
-- URI rewriter component, to compute the URI on targetted service
-- based on current request.
feature -- Settings
connect_timeout: INTEGER assign set_connect_timeout
-- In milliseconds.
timeout: INTEGER assign set_timeout
-- In seconds.
is_via_header_supported: BOOLEAN
-- Via: header supported.
-- Default: True.
feature -- Change
set_uri_rewriter (a_rewriter: like uri_rewriter)
do
uri_rewriter := a_rewriter
end
set_timeout (a_timeout_in_seconds: INTEGER)
-- in seconds.
do
timeout := a_timeout_in_seconds
end
set_connect_timeout (a_timeout_in_milliseconds: INTEGER)
-- in milliseconds.
do
connect_timeout := a_timeout_in_milliseconds
end
set_is_via_header_supported (b: BOOLEAN)
-- Set `is_via_header_supported' to `b'.
do
is_via_header_supported := b
end
feature -- Execution
proxy_uri (request: WSF_REQUEST): STRING
-- URI to query on proxyfied host.
do
if attached uri_rewriter as r then
Result := r.uri (request)
else
Result := request.request_uri
end
end
execute (request: WSF_REQUEST; response: WSF_RESPONSE)
-- Execute reverse proxy request.
local
h: HTTP_HEADER
l_http_query: STRING
l_status_line: STRING
l_max_forward: INTEGER
l_via: detachable STRING
l_protocol: STRING
i: INTEGER
l_completed: BOOLEAN
l_remote_uri: like remote_uri
l_socket_factory: WSF_PROXY_SOCKET_FACTORY
do
l_remote_uri := remote_uri
create l_socket_factory
if not l_socket_factory.is_uri_supported (l_remote_uri) then
send_error (request, response, {HTTP_STATUS_CODE}.bad_gateway, l_remote_uri.scheme + " is not supported! [for remote " + l_remote_uri.string + "]")
elseif attached l_socket_factory.socket_from_uri (l_remote_uri) as l_socket then
l_socket.set_connect_timeout (connect_timeout) -- milliseconds
l_socket.set_timeout (timeout) -- seconds
l_socket.connect
if l_socket.is_connected then
create l_http_query.make_from_string (request.request_method)
l_http_query.append_character (' ')
l_http_query.append (l_remote_uri.path)
l_http_query.append (proxy_uri (request))
l_http_query.append_character (' ')
l_http_query.append (request.server_protocol)
if attached request.raw_header_data as l_raw_header then
i := l_raw_header.substring_index ("%R%N", 1)
if i > 0 then
-- Skip the first status line.
create h.make_from_raw_header_data (l_raw_header.substring (i + 2, l_raw_header.count))
else
create h.make_from_raw_header_data (l_raw_header)
end
if attached l_remote_uri.host as l_remote_host then
if l_remote_uri.port > 0 then
h.put_header_key_value ("Host", l_remote_host + ":" + l_remote_uri.port.out)
else
h.put_header_key_value ("Host", l_remote_host)
end
end
-- Via header
if is_via_header_supported then
if attached h.item ("Via") as v then
l_via := v
l_via.append (", ")
else
create l_via.make_empty
end
l_via.append (request.server_protocol + " " + request.server_name + " (PROXY-" + request.server_software + ")")
h.put_header_key_value ("Via", l_via)
end
-- Max-Forwards header handling
if attached h.item ("Max-Forwards") as h_max_forward then
-- Max-Forwards: 0 stop, otherwise decrement by one.
-- see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.31
if h_max_forward.is_integer then
l_max_forward := h_max_forward.to_integer - 1
if l_max_forward >= 0 then
h.put_header_key_value ("Max-Forwards", l_max_forward.out)
end
end
end
if l_max_forward < 0 then
-- i.e previous Max-Forwards was '0'
send_error (request, response, {HTTP_STATUS_CODE}.bad_gateway, "Reached maximum number of Forwards, not forwarded to " + l_remote_uri.string)
else
l_socket.put_string (l_http_query)
l_socket.put_string ("%R%N")
l_socket.put_string (h.string)
l_socket.put_string ("%R%N")
if request.content_length_value > 0 then
request.read_input_data_into_file (l_socket)
end
-- Get HTTP status
l_socket.read_line_thread_aware
create l_status_line.make_from_string (l_socket.last_string)
-- Get HTTP header block
if attached next_http_header_block (l_socket) as l_resp_header then
create h.make_from_raw_header_data (l_resp_header)
if attached status_line_info (l_status_line) as l_status_info then
l_protocol := l_status_info.protocol
if attached l_status_info.reason_phrase as l_phrase then
response.set_status_code_with_reason_phrase (l_status_info.status_code, l_phrase)
else
response.set_status_code (l_status_info.status_code)
end
else
check has_status_line: False end
l_protocol := "1.0" -- Default?
response.set_status_code (80)
end
if is_via_header_supported then
if attached h.item ("Via") as v then
l_via := v
l_via.append (", ")
else
create l_via.make_empty
end
l_via.append (l_protocol + " " + request.server_name + " (PROXY-" + request.server_software + ")")
h.put_header_key_value ("Via", l_via)
end
response.add_header_lines (h)
from
l_socket.read_stream (2_048)
until
l_socket.was_error
or not l_socket.is_connected
or l_socket.bytes_read <= 0
or l_completed
loop
response.put_string (l_socket.last_string)
if l_socket.bytes_read = 2_048 then
l_socket.read_stream (2_048)
else
l_completed := True
end
end
else
send_error (request, response, {HTTP_STATUS_CODE}.internal_server_error, "Invalid response header!")
end
end
else
send_error (request, response, {HTTP_STATUS_CODE}.internal_server_error, "Can not access request header!")
end
else
send_error (request, response, {HTTP_STATUS_CODE}.gateway_timeout, "Unable to connect " + l_remote_uri.string)
end
else
send_error (request, response, {HTTP_STATUS_CODE}.bad_gateway, "Unable to connect " + l_remote_uri.string)
end
end
feature {NONE} -- Implementation
status_line_info (a_line: READABLE_STRING_8): detachable TUPLE [protocol: READABLE_STRING_8; status_code: INTEGER; reason_phrase: detachable READABLE_STRING_8]
-- Info from status line
--| Such as "HTTP/1.1 200 OK" -> ["1.1", 200, "OK"]
local
i,j: INTEGER
p,s: detachable READABLE_STRING_8
c: INTEGER
do
i := a_line.index_of (' ', 1)
if i > 0 then
p := a_line.substring (1, i - 1)
if p.starts_with_general ("HTTP/") then
p := p.substring (6, p.count) -- We could also keep HTTP/
end
j := i + 1
i := a_line.index_of (' ', j)
if i > 0 then
s := a_line.substring (j, i - 1)
if s.is_integer then
c := s.to_integer
s := a_line.substring (i + 1, a_line.count)
if s.is_whitespace then
s := Void
elseif s[s.count].is_space then
s := s.substring (1, s.count - 1)
end
Result := [p, c, s]
end
end
end
end
next_http_header_block (a_socket: NETWORK_STREAM_SOCKET): detachable STRING
local
h: STRING
do
create h.make_empty
from
a_socket.read_line_thread_aware
until
Result /= Void
or a_socket.was_error
or (a_socket.bytes_read = 0 or a_socket.bytes_read = -1)
or not a_socket.is_connected
loop
if a_socket.last_string.same_string ("%R") then
-- End of header
Result := h
else
h.append (a_socket.last_string)
h.append ("%N")
a_socket.read_line_thread_aware
end
end
end
send_error (request: WSF_REQUEST; response: WSF_RESPONSE; a_status_code: INTEGER; a_message: READABLE_STRING_8)
local
s: STRING
do
-- To send a response we need to setup, the status code and
-- the response headers.
create s.make_from_string (a_message)
debug
s.append ("%N(UTC time is " + (create {HTTP_DATE}.make_now_utc).rfc850_string + ").%N")
end
response.put_header ({HTTP_STATUS_CODE}.ok, <<["Content-Type", "plain/text"], ["Content-Length", s.count.out]>>)
response.set_status_code (a_status_code)
response.header.put_content_type_text_html
response.header.put_content_length (s.count)
if
attached request.http_connection as l_connection and then
l_connection.is_case_insensitive_equal_general ("keep-alive")
then
response.header.put_header_key_value ("Connection", "keep-alive")
end
response.put_string (s)
end
end

View File

@@ -0,0 +1,38 @@
note
description: "Summary description for {WSF_AGENT_URI_REWRITER}."
author: ""
date: "$Date$"
revision: "$Revision$"
class
WSF_AGENT_URI_REWRITER
inherit
WSF_URI_REWRITER
create
make
--convert
-- make ({FUNCTION [TUPLE [WSF_REQUEST], STRING]})
feature {NONE} -- Initialization
make (a_rewriter_function: like rewriter_function)
do
rewriter_function := a_rewriter_function
end
feature -- Access
rewriter_function: FUNCTION [TUPLE [WSF_REQUEST], STRING]
feature -- Conversion
uri (a_request: WSF_REQUEST): STRING
-- <Precursor>.
do
Result := rewriter_function (a_request)
end
end

View File

@@ -0,0 +1,16 @@
note
description: "Summary description for {WSF_URI_REWRITER}."
date: "$Date$"
revision: "$Revision$"
deferred class
WSF_URI_REWRITER
feature -- Conversion
uri (a_request: WSF_REQUEST): STRING
-- Rewritten request uri based on `a_request'.
deferred
end
end

View File

@@ -0,0 +1,34 @@
<?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="wsf_proxy" uuid="A39CCC27-BF63-4959-B881-7D0713F4C84A" library_target="wsf_proxy">
<target name="wsf_proxy">
<root all_classes="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="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="ssl_supported" value="true"/>
</condition>
</library>
<library name="uri" location="$ISE_LIBRARY\library\text\uri\uri-safe.ecf"/>
<library name="wsf" location="..\wsf\wsf-safe.ecf"/>
<cluster name="network" location=".\network\" recursive="true">
<file_rule>
<exclude>no_ssl</exclude>
<exclude>ssl</exclude>
</file_rule>
<cluster name="network_ssl" location="$|ssl\">
<condition>
<custom name="ssl_supported" value="true"/>
</condition>
</cluster>
<cluster name="network_no_ssl" location="$|no_ssl\">
<condition>
<custom name="ssl_supported" excluded_value="true"/>
</condition>
</cluster>
</cluster>
<cluster name="reverse_proxy" location=".\reverse_proxy\" recursive="true"/>
<cluster name="rewriter" location=".\rewriter\" recursive="true"/>
</target>
</system>

View File

@@ -0,0 +1,36 @@
<?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="wsf_proxy" uuid="A39CCC27-BF63-4959-B881-7D0713F4C84A" library_target="wsf_proxy">
<target name="wsf_proxy">
<root all_classes="true"/>
<option void_safety="none">
</option>
<library name="base" location="$ISE_LIBRARY\library\base\base.ecf"/>
<library name="http" location="..\..\..\library\network\protocol\http\http.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="ssl_supported" value="true"/>
</condition>
</library>
<library name="uri" location="$ISE_LIBRARY\library\text\uri\uri.ecf"/>
<library name="wsf" location="..\wsf\wsf.ecf"/>
<cluster name="network" location=".\network\" recursive="true">
<file_rule>
<exclude>no_ssl</exclude>
<exclude>ssl</exclude>
</file_rule>
<cluster name="network_ssl" location="$|ssl\">
<condition>
<custom name="ssl_supported" value="true"/>
</condition>
</cluster>
<cluster name="network_no_ssl" location="$|no_ssl\">
<condition>
<custom name="ssl_supported" excluded_value="true"/>
</condition>
</cluster>
</cluster>
<cluster name="reverse_proxy" location=".\reverse_proxy\" recursive="true"/>
<cluster name="rewriter" location=".\rewriter\" recursive="true"/>
</target>
</system>

View File

@@ -5,7 +5,7 @@
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/> <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="base_extension" location="$ISE_LIBRARY\library\base_extension\base_extension-safe.ecf"/>
<library name="encoders" location="..\..\encoder\encoder-safe.ecf"/> <library name="encoders" location="..\..\encoder\encoder-safe.ecf"/>
<library name="http" location="$ISE_LIBRARY\contrib\library\network\protocol\http\http-safe.ecf"/> <library name="http" location="..\..\..\network\protocol\http\http-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="uuid" location="$ISE_LIBRARY\library\uuid\uuid-safe.ecf"/> <library name="uuid" location="$ISE_LIBRARY\library\uuid\uuid-safe.ecf"/>
<library name="xml_parser" location="$ISE_LIBRARY\library\text\parser\xml\parser\xml_parser-safe.ecf"/> <library name="xml_parser" location="$ISE_LIBRARY\library\text\parser\xml\parser\xml_parser-safe.ecf"/>

View File

@@ -7,7 +7,7 @@
<library name="base" location="$ISE_LIBRARY\library\base\base.ecf"/> <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="base_extension" location="$ISE_LIBRARY\library\base_extension\base_extension.ecf"/>
<library name="encoders" location="..\..\encoder\encoder.ecf"/> <library name="encoders" location="..\..\encoder\encoder.ecf"/>
<library name="http" location="$ISE_LIBRARY\contrib\library\network\protocol\http\http.ecf"/> <library name="http" location="..\..\..\network\protocol\http\http.ecf"/>
<library name="time" location="$ISE_LIBRARY\library\time\time.ecf"/> <library name="time" location="$ISE_LIBRARY\library\time\time.ecf"/>
<library name="uuid" location="$ISE_LIBRARY\library\uuid\uuid.ecf"/> <library name="uuid" location="$ISE_LIBRARY\library\uuid\uuid.ecf"/>
<library name="xml_parser" location="$ISE_LIBRARY\library\text\parser\xml\parser\xml_parser.ecf"/> <library name="xml_parser" location="$ISE_LIBRARY\library\text\parser\xml\parser\xml_parser.ecf"/>

View File

@@ -72,6 +72,13 @@ feature -- Visitor
if attached a_entry.date as dt then if attached a_entry.date as dt then
append_content_tag_to ("updated", Void, date_to_string (dt), buffer) append_content_tag_to ("updated", Void, date_to_string (dt), buffer)
end end
if attached a_entry.categories as cats then
across
cats as ic
loop
append_content_tag_to ("category", <<["term", ic.item]>>, Void, buffer)
end
end
append_content_tag_to ("summary", Void, a_entry.description, buffer) append_content_tag_to ("summary", Void, a_entry.description, buffer)
if attached a_entry.content as l_content then if attached a_entry.content as l_content then

View File

@@ -81,6 +81,7 @@ feature -- Access
if attached x_entry.element_by_name ("content") as x_content then if attached x_entry.element_by_name ("content") as x_content then
e.set_content (xml_element_code (x_content), xml_attribute_text (x_content, "type")) e.set_content (xml_element_code (x_content), xml_attribute_text (x_content, "type"))
end end
if attached x_entry.element_by_name ("author") as x_author then if attached x_entry.element_by_name ("author") as x_author then
if attached x_author.element_by_name ("name") as x_name and then if attached x_author.element_by_name ("name") as x_name and then
attached x_name.text as l_author_name attached x_name.text as l_author_name
@@ -92,6 +93,17 @@ feature -- Access
e.set_author (l_author) e.set_author (l_author)
end end
end end
-- Optional "category"
if attached x_entry.elements_by_name ("category") as x_categories then
across
x_categories as cats_ic
loop
if attached xml_attribute_text (cats_ic.item, "term") as l_term then
e.set_category (l_term)
end
end
end
Result.extend (e) Result.extend (e)
end end
end end

View File

@@ -66,7 +66,7 @@ feature {NONE} -- Helpers
end end
end end
if a_content = Void then if a_content = Void then
a_output.append ("/>") a_output.append ("/>%N")
else else
a_output.append (">") a_output.append (">")
a_output.append (escaped_unicode_xml (a_content.as_string_32)) a_output.append (escaped_unicode_xml (a_content.as_string_32))

View File

@@ -65,6 +65,7 @@ feature {NONE} -- Data
<name>John Doe</name> <name>John Doe</name>
<email>johndoe@example.com</email> <email>johndoe@example.com</email>
</author> </author>
<category term="foo"/><category term="bar"/>
</entry> </entry>
</feed> </feed>

View File

@@ -6,7 +6,7 @@
<setting name="concurrency" value="none"/> <setting name="concurrency" value="none"/>
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/> <library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
<library name="feed" location="..\feed-safe.ecf" readonly="false"/> <library name="feed" location="..\feed-safe.ecf" readonly="false"/>
<library name="http_client" location="$ISE_LIBRARY\contrib\library\network\http_client\http_client-safe.ecf"/> <library name="http_client" location="..\..\..\..\network\http_client\http_client-safe.ecf"/>
<library name="testing" location="$ISE_LIBRARY\library\testing\testing-safe.ecf"/> <library name="testing" location="$ISE_LIBRARY\library\testing\testing-safe.ecf"/>
<library name="xml_parser" location="$ISE_LIBRARY\library\text\parser\xml\parser\xml_parser-safe.ecf"/> <library name="xml_parser" location="$ISE_LIBRARY\library\text\parser\xml\parser\xml_parser-safe.ecf"/>
<tests name="src" location=".\" recursive="true"/> <tests name="src" location=".\" recursive="true"/>

View File

@@ -59,6 +59,8 @@ echo Install library: ewf/wsf
%COPYCMD% %TMP_DIR%\library\server\wsf %TMP_CONTRIB_DIR%\library\web\framework\ewf\wsf %COPYCMD% %TMP_DIR%\library\server\wsf %TMP_CONTRIB_DIR%\library\web\framework\ewf\wsf
echo Install library: ewf/wsf_extension echo Install library: ewf/wsf_extension
%COPYCMD% %TMP_DIR%\library\server\wsf_extension %TMP_CONTRIB_DIR%\library\web\framework\ewf\wsf_extension %COPYCMD% %TMP_DIR%\library\server\wsf_extension %TMP_CONTRIB_DIR%\library\web\framework\ewf\wsf_extension
echo Install library: ewf/wsf_proxy
%COPYCMD% %TMP_DIR%\library\server\wsf_proxy %TMP_CONTRIB_DIR%\library\web\framework\ewf\wsf_proxy
echo Install library: ewf/wsf_html echo Install library: ewf/wsf_html
%COPYCMD% %TMP_DIR%\library\server\wsf_html %TMP_CONTRIB_DIR%\library\web\framework\ewf\wsf_html %COPYCMD% %TMP_DIR%\library\server\wsf_html %TMP_CONTRIB_DIR%\library\web\framework\ewf\wsf_html
echo Install library: ewf/encoder echo Install library: ewf/encoder