Compare commits

...

8 Commits

10 changed files with 230 additions and 46 deletions

View File

@@ -101,7 +101,6 @@ Other connectors:
**WSF_STANDALONE_SERVICE_LAUNCHER** **WSF_STANDALONE_SERVICE_LAUNCHER**
**WSF_CGI_SERVICE_LAUNCHER** **WSF_CGI_SERVICE_LAUNCHER**
**WSF_NINO_SERVICE_LAUNCHER**
**WSF_LIBFCGI_SERVICE_LAUNCHER** **WSF_LIBFCGI_SERVICE_LAUNCHER**
A basic EWF service inherits from **WSF_DEFAULT_SERVICE**, which has a formal generic that should conform to **WSF_EXECUTION** class with a `make' creation procedure, in our case the class **APPLICATION_EXECUTION**. A basic EWF service inherits from **WSF_DEFAULT_SERVICE**, which has a formal generic that should conform to **WSF_EXECUTION** class with a `make' creation procedure, in our case the class **APPLICATION_EXECUTION**.

View File

@@ -13,7 +13,7 @@ EWF Deployment
4. Deploying EWF FCGI 4. Deploying EWF FCGI
5. FCGI overview 5. FCGI overview
1. Build EWF application 1. Build EWF application
2. Copy the generated exe file and the www content.htaccess CGI 2. Copy the generated exe file and the www content.htaccess CGI
@@ -25,10 +25,14 @@ EWF Deployment
>Apache Version: Apache 2.4.4 >Apache Version: Apache 2.4.4
>Windows: http://www.apachelounge.com/download/ >Windows: http://www.apachelounge.com/download/
note: on linux (debian), use
> sudo apt-get install apache2
#### Deploying EWF CGI #### Deploying EWF CGI
#### CGI overview #### CGI overview
>A new process is started for each HTTP request. So if there are N requests to the same >CGI program, the code of the CGI program is loaded into memory N times. >A new process is started for each HTTP request. So if there are N requests to the same
>CGI program, the code of the CGI program is loaded into memory N times.
>When a CGI program finishes handling a request, the program terminates. >When a CGI program finishes handling a request, the program terminates.
* Build EWF application * Build EWF application
@@ -95,6 +99,9 @@ Check that you have the following modules enabled
>To deploy FCGI you will need to download the mod_fcgi module. >To deploy FCGI you will need to download the mod_fcgi module.
>You can get it from here http://www.apachelounge.com/download/ >You can get it from here http://www.apachelounge.com/download/
note: on linux (debian), use
> sudo apt-get install libapache2-mod-fastcgi
#### FCGI overview #### FCGI overview
>FastCGI allows a single, long-running process to handle more than one user request while keeping close to the CGI programming model, retaining the simplicity while eliminating the overhead of creating a new process for each request. Unlike converting an application to a web server plug-in, FastCGI applications remain independent of the web server. >FastCGI allows a single, long-running process to handle more than one user request while keeping close to the CGI programming model, retaining the simplicity while eliminating the overhead of creating a new process for each request. Unlike converting an application to a web server plug-in, FastCGI applications remain independent of the web server.
@@ -128,6 +135,22 @@ Copy the app.exe and the folder "www" into a folder served by apache2, for exam
>NOTE: By default Apache does not come with fcgid module, so you will need to download it, and put the module under Apache2/modules >NOTE: By default Apache does not come with fcgid module, so you will need to download it, and put the module under Apache2/modules
It is also possible to set various parameters in the apache site configuration file such as:
```
<IfModule mod_fcgid.c>
# FcgidIdleTimeout 600
# FcgidBusyScanInterval 120
# FcgidProcessLifeTime 3600
# FcgidMaxProcesses 5
# FcgidMaxProcessesPerClass 100
# FcgidMinProcessesPerClass 100
# FcgidConnectTimeout 8
# FcgidIOTimeout 60
# FcgidBusyTimeout 1200
</IfModule>
```
See https://httpd.apache.org/mod_fcgid/mod/mod_fcgid.html for more information.
# .htaccess FCGI # .htaccess FCGI
``` ```

View File

@@ -24,6 +24,8 @@ feature -- Basic operations
s := "Hello World!" s := "Hello World!"
create dt.make_now_utc create dt.make_now_utc
s.append (" (UTC time is " + dt.rfc850_string + ").") s.append (" (UTC time is " + dt.rfc850_string + ").")
s.append ("%N")
s.append ("Your request: " + request.request_uri + " %N")
response.put_header ({HTTP_STATUS_CODE}.ok, <<["Content-Type", "text/html"], ["Content-Length", s.count.out]>>) 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.set_status_code ({HTTP_STATUS_CODE}.ok)
response.header.put_content_type_text_html response.header.put_content_type_text_html

View File

@@ -33,7 +33,7 @@ feature {NONE} -- Initialization
do do
reset_request reset_request
has_error := False reset_error
if attached internal_client_socket as l_sock then if attached internal_client_socket as l_sock then
l_sock.cleanup l_sock.cleanup
end end
@@ -156,6 +156,20 @@ feature -- Status report
has_error: BOOLEAN has_error: BOOLEAN
-- Error occurred during `analyze_request_message' -- Error occurred during `analyze_request_message'
feature -- Status change
report_error (m: detachable READABLE_STRING_GENERAL)
-- Report error occurred, with optional message `m'.
do
has_error := True
end
reset_error
-- Reset previous error for current request handler.
do
has_error := False
end
feature -- Change feature -- Change
set_is_verbose (b: BOOLEAN) set_is_verbose (b: BOOLEAN)
@@ -236,11 +250,18 @@ feature -- Execution
require require
is_connected: is_connected is_connected: is_connected
reuse_connection_when_possible: a_is_reusing_connection implies is_persistent_connection_supported reuse_connection_when_possible: a_is_reusing_connection implies is_persistent_connection_supported
no_error: not has_error
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
l_is_ready: BOOLEAN l_is_ready: BOOLEAN
do do
debug ("dbglog")
if a_is_reusing_connection then
dbglog ("execute_request: wait on persistent connection.")
end
end
reset_error
l_socket := client_socket l_socket := client_socket
check check
socket_attached: l_socket /= Void socket_attached: l_socket /= Void
@@ -248,17 +269,17 @@ feature -- Execution
end end
if l_socket.is_closed then if l_socket.is_closed then
debug ("dbglog") debug ("dbglog")
dbglog (generator + ".execute_request {socket is Closed!}") dbglog ("execute_request {socket is Closed!}")
end end
else else
debug ("dbglog") debug ("dbglog")
dbglog (generator + ".execute_request socket=" + l_socket.descriptor.out + " ENTER") dbglog ("execute_request socket=" + l_socket.descriptor.out + " ENTER")
end end
if a_is_reusing_connection then if a_is_reusing_connection then
--| set by default 5 seconds. --| set by default 5 seconds.
l_socket.set_recv_timeout (keep_alive_timeout) -- in seconds! l_socket.set_recv_timeout (keep_alive_timeout) -- in seconds!
l_is_ready := l_socket.ready_for_reading l_is_ready := socket_has_incoming_data (l_socket)
else else
l_is_ready := True l_is_ready := True
end end
@@ -273,28 +294,31 @@ feature -- Execution
remote_info := l_remote_info remote_info := l_remote_info
end end
analyze_request_message (l_socket) analyze_request_message (l_socket)
else
has_error := True
debug ("dbglog")
dbglog (generator + ".execute_request socket=" + l_socket.descriptor.out + "} timeout!")
end
end
if has_error then if has_error then
if l_is_ready then
-- check catch_bad_incoming_connection: False end -- check catch_bad_incoming_connection: False end
if is_verbose then if is_verbose then
log (request_header + "%NWARNING: invalid HTTP incoming request", warning_level) log (request_header + "%NWARNING: invalid HTTP incoming request", warning_level)
end end
end process_bad_request (l_socket)
else is_persistent_connection_requested := False
if is_verbose then else
log (request_header, information_level) if is_verbose then
end log (request_header, information_level)
process_request (l_socket) end
end process_request (l_socket)
end
else
check is_reusing_connection: a_is_reusing_connection end
-- Close persistent connection, since no new connection occurred in the delay `keep_alive_timeout'.
is_persistent_connection_requested := False
debug ("dbglog")
dbglog ("execute_request socket=" + l_socket.descriptor.out + "} close persistent connection.")
end
end
debug ("dbglog") debug ("dbglog")
dbglog (generator + ".execute_request {" + l_socket.descriptor.out + "} LEAVE") dbglog ("execute_request {" + l_socket.descriptor.out + "} LEAVE")
end end
end end
end end
@@ -307,7 +331,7 @@ feature -- Execution
feature -- Request processing feature -- Request processing
process_request (a_socket: HTTPD_STREAM_SOCKET) process_request (a_socket: HTTPD_STREAM_SOCKET)
-- Process request ... -- Process request on socket `a_socket'.
require require
no_error: not has_error no_error: not has_error
a_uri_attached: uri /= Void a_uri_attached: uri /= Void
@@ -318,6 +342,39 @@ feature -- Request processing
deferred deferred
end end
process_bad_request (a_socket: HTTPD_STREAM_SOCKET)
-- Process bad request catched on `a_socket'.
require
has_error: has_error
a_socket_attached: a_socket /= Void
local
-- h: STRING
-- s: STRING
do
-- NOTE: this is experiment code, and not ready yet.
-- if a_socket.ready_for_writing then
-- s := "{
--<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
--<html><head>
--<title>400 Bad Request</title>
--</head><body>
--<h1>Bad Request</h1>
--</body></html>
-- }"
-- create h.make (1_024)
-- h.append ("HTTP/1.1 400 Bad Request%R%N")
-- h.append ("Content-Length: " + s.count.out + "%R%N")
-- h.append ("Connection: close%R%N")
-- h.append ("Content-Type: text/html; charset=iso-8859-1%R%N")
-- h.append ("%R%N")
-- a_socket.put_string (h)
-- if a_socket.ready_for_writing then
-- a_socket.put_string (s)
-- end
-- end
end
feature -- Parsing feature -- Parsing
analyze_request_message (a_socket: HTTPD_STREAM_SOCKET) analyze_request_message (a_socket: HTTPD_STREAM_SOCKET)
@@ -336,15 +393,20 @@ feature -- Parsing
request_header := txt request_header := txt
if if
not has_error and then not has_error and then
a_socket.is_readable and then a_socket.readable
attached next_line (a_socket) as l_request_line and then
not l_request_line.is_empty
then then
txt.append (l_request_line) if
txt.append_character ('%N') attached next_line (a_socket) as l_request_line and then
analyze_request_line (l_request_line) not l_request_line.is_empty
then
txt.append (l_request_line)
txt.append_character ('%N')
analyze_request_line (l_request_line)
else
report_error ("Bad header line (empty)")
end
else else
has_error := True report_error ("Socket is not readable")
end end
l_is_verbose := is_verbose l_is_verbose := is_verbose
if not has_error then if not has_error then
@@ -419,7 +481,9 @@ feature -- Parsing
n := n - 1 n := n - 1
end end
version := line.substring (next_pos + 1, n) version := line.substring (next_pos + 1, n)
has_error := method.is_empty if method.is_empty then
report_error ("Missing request method data")
end
end end
next_line (a_socket: HTTPD_STREAM_SOCKET): detachable STRING next_line (a_socket: HTTPD_STREAM_SOCKET): detachable STRING
@@ -431,22 +495,26 @@ feature -- Parsing
retried: BOOLEAN retried: BOOLEAN
do do
if retried then if retried then
has_error := True report_error ("Rescue in next_line")
Result := Void Result := Void
elseif a_socket.readable then elseif
a_socket.readable and then
socket_has_incoming_data (a_socket)
then
a_socket.read_line_thread_aware a_socket.read_line_thread_aware
Result := a_socket.last_string Result := a_socket.last_string
-- Do no check `socket_ok' before socket operation, -- Do no check `socket_ok' before socket operation,
-- otherwise it may be False, due to error during other socket operation in same thread. -- otherwise it may be False, due to error during other socket operation in same thread.
if not a_socket.socket_ok then if not a_socket.socket_ok then
has_error := True report_error ("Socket error")
if is_verbose then if is_verbose then
log (request_header +"%N" + Result + "%N## socket_ok=False! ##", debug_level) log (request_header +"%N" + Result + "%N## socket_ok=False! ##", debug_level)
end end
end end
else else
-- Error with socket... -- Error with socket...
has_error := True report_error ("Socket error: not readable")
if is_verbose then if is_verbose then
log (request_header + "%N## Socket is not readable! ##", debug_level) log (request_header + "%N## Socket is not readable! ##", debug_level)
end end
@@ -482,6 +550,17 @@ feature -- Output
end end
end end
feature {NONE} -- Helpers
socket_has_incoming_data (a_socket: HTTPD_STREAM_SOCKET): BOOLEAN
-- Is there any data to read on `a_socket' ?
require
a_socket.readable
do
-- FIXME: check if both are really needed.
Result := a_socket.ready_for_reading and then a_socket.has_incoming_data
end
invariant invariant
request_header_attached: request_header /= Void request_header_attached: request_header /= Void

View File

@@ -111,6 +111,15 @@ feature -- Execution
log (" - socket_recv_timeout = " + configuration.socket_recv_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.has_ssl_support then
if configuration.is_secure then
log (" - SSL = enabled")
else
log (" - SSL = disabled")
end
else
log (" - SSL = not supported")
end
if configuration.verbose_level > 0 then if configuration.verbose_level > 0 then
log (" - verbose_level = " + configuration.verbose_level.out) log (" - verbose_level = " + configuration.verbose_level.out)
end end

View File

@@ -141,6 +141,15 @@ feature -- Input
socket.read_character socket.read_character
end end
peek_stream (nb_char: INTEGER)
require
nb_char_positive: nb_char > 0
do
if attached {TCP_STREAM_SOCKET} socket as l_socket then
l_socket.peek_stream (nb_char)
end
end
bytes_read: INTEGER bytes_read: INTEGER
do do
Result := socket.bytes_read Result := socket.bytes_read
@@ -308,6 +317,15 @@ feature -- Status Report
Result := socket.readable Result := socket.readable
end end
has_incoming_data: BOOLEAN
-- Check if Current has available data to be read.
-- note: no data will not be removed from the queue.
do
if attached {TCP_STREAM_SOCKET} socket as l_socket then
Result := l_socket.has_incoming_data
end
end
ready_for_reading: BOOLEAN ready_for_reading: BOOLEAN
do do
if attached {TCP_STREAM_SOCKET} socket as l_socket then if attached {TCP_STREAM_SOCKET} socket as l_socket then

View File

@@ -55,6 +55,38 @@ feature {NONE} -- Initialization
feature -- Basic operation feature -- Basic operation
peek_stream (nb_char: INTEGER)
-- Read a string of at most `nb_char' characters without removing the data from the queue.
-- Make result available in last_string.
require
readable: readable
socket_exists: exists
local
ext: C_STRING
retval: INTEGER
l: like last_string
do
create ext.make_empty (nb_char + 1)
retval := clib_recv (descriptor, ext.item, nb_char, c_peekmsg)
if retval = 0 then
last_string.wipe_out
socket_error := Void
elseif retval > 0 then
ext.set_count (retval)
l := last_string
l.wipe_out
l.grow (retval)
l.set_count (retval)
ext.read_substring_into (l, 1, retval)
socket_error := Void
else
last_string.wipe_out
socket_error := "Socket error (MSG_PEEK)"
end
ensure
last_string_not_void: last_string /= Void
end
send_message (a_msg: STRING) send_message (a_msg: STRING)
do do
put_string (a_msg) put_string (a_msg)
@@ -73,6 +105,16 @@ feature -- Output
feature -- Status report feature -- Status report
has_incoming_data: BOOLEAN
-- Check if Current has available data to be read.
-- note: no data will not be removed from the queue.
require
socket_exists: exists
do
peek_stream (1)
Result := last_string.count = 1
end
try_ready_for_reading: BOOLEAN try_ready_for_reading: BOOLEAN
-- Is data available for reading from the socket right now? -- Is data available for reading from the socket right now?
require require
@@ -84,6 +126,19 @@ feature -- Status report
Result := (retval > 0) Result := (retval > 0)
end end
feature {NONE} -- C implementation
clib_recv (a_fd: INTEGER; buf: POINTER; len: INTEGER; flags: INTEGER): INTEGER
-- External routine to receive at most `len' number of
-- bytes into buffer `buf' from socket `fd' with `flags' options.
external
"C inline"
alias
"[
return (EIF_INTEGER) recv ((int) $a_fd, (char *) $buf, (int) $len, (int) $flags);
]"
end
note note
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others" copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"

View File

@@ -56,15 +56,6 @@ feature {NONE} -- Externals
deferred deferred
end 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_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. -- C routine to set socket option `SO_RCVTIMEO' with `a_timeout_seconds' seconds.
external external

View File

@@ -97,7 +97,9 @@ feature -- Request processing
end end
end end
rescue rescue
has_error := l_output = Void or else not l_output.is_available if l_output = Void or else not l_output.is_available then
report_error ("Missing WGI output")
end
if not retried then if not retried then
retried := True retried := True
retry retry

View File

@@ -26,4 +26,10 @@
</target> </target>
<target name="test_connector_standalone" extends="test_standalone_scoop"> <target name="test_connector_standalone" extends="test_standalone_scoop">
</target> </target>
<target name="test_standalone_scoop_ssl" extends="test_standalone_scoop">
<variable name="httpd_ssl_enabled" value="true"/>
<variable name="libcurl_http_client_disabled" value="true"/>
<variable name="net_http_client_disabled" value="false"/>
<variable name="netssl_http_client_enabled" value="true"/>
</target>
</system> </system>