Compare commits
8 Commits
es_rev9919
...
es_rev9926
| Author | SHA1 | Date | |
|---|---|---|---|
| b4fd04ad9f | |||
| 71a98f3c28 | |||
| ed22be2551 | |||
| 77085364ee | |||
| 0217c6d3f4 | |||
| 55fec2423c | |||
| 1f7a81a2d6 | |||
| 612ff243c1 |
@@ -101,7 +101,6 @@ Other connectors:
|
||||
|
||||
**WSF_STANDALONE_SERVICE_LAUNCHER**
|
||||
**WSF_CGI_SERVICE_LAUNCHER**
|
||||
**WSF_NINO_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**.
|
||||
|
||||
@@ -13,7 +13,7 @@ EWF Deployment
|
||||
4. Deploying EWF FCGI
|
||||
5. FCGI overview
|
||||
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
|
||||
>Windows: http://www.apachelounge.com/download/
|
||||
|
||||
note: on linux (debian), use
|
||||
> sudo apt-get install apache2
|
||||
|
||||
#### Deploying EWF CGI
|
||||
|
||||
#### 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.
|
||||
|
||||
* 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.
|
||||
>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
|
||||
>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
|
||||
|
||||
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
|
||||
|
||||
```
|
||||
|
||||
@@ -24,6 +24,8 @@ feature -- Basic operations
|
||||
s := "Hello World!"
|
||||
create dt.make_now_utc
|
||||
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.set_status_code ({HTTP_STATUS_CODE}.ok)
|
||||
response.header.put_content_type_text_html
|
||||
|
||||
@@ -33,7 +33,7 @@ feature {NONE} -- Initialization
|
||||
do
|
||||
reset_request
|
||||
|
||||
has_error := False
|
||||
reset_error
|
||||
if attached internal_client_socket as l_sock then
|
||||
l_sock.cleanup
|
||||
end
|
||||
@@ -156,6 +156,20 @@ feature -- Status report
|
||||
has_error: BOOLEAN
|
||||
-- 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
|
||||
|
||||
set_is_verbose (b: BOOLEAN)
|
||||
@@ -236,11 +250,18 @@ feature -- Execution
|
||||
require
|
||||
is_connected: is_connected
|
||||
reuse_connection_when_possible: a_is_reusing_connection implies is_persistent_connection_supported
|
||||
no_error: not has_error
|
||||
local
|
||||
l_remote_info: detachable like remote_info
|
||||
l_socket: like client_socket
|
||||
l_is_ready: BOOLEAN
|
||||
do
|
||||
debug ("dbglog")
|
||||
if a_is_reusing_connection then
|
||||
dbglog ("execute_request: wait on persistent connection.")
|
||||
end
|
||||
end
|
||||
reset_error
|
||||
l_socket := client_socket
|
||||
check
|
||||
socket_attached: l_socket /= Void
|
||||
@@ -248,17 +269,17 @@ feature -- Execution
|
||||
end
|
||||
if l_socket.is_closed then
|
||||
debug ("dbglog")
|
||||
dbglog (generator + ".execute_request {socket is Closed!}")
|
||||
dbglog ("execute_request {socket is Closed!}")
|
||||
end
|
||||
else
|
||||
debug ("dbglog")
|
||||
dbglog (generator + ".execute_request socket=" + l_socket.descriptor.out + " ENTER")
|
||||
dbglog ("execute_request socket=" + l_socket.descriptor.out + " ENTER")
|
||||
end
|
||||
|
||||
if a_is_reusing_connection then
|
||||
--| set by default 5 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
|
||||
l_is_ready := True
|
||||
end
|
||||
@@ -273,28 +294,31 @@ feature -- Execution
|
||||
remote_info := l_remote_info
|
||||
end
|
||||
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 l_is_ready then
|
||||
if has_error then
|
||||
-- check catch_bad_incoming_connection: False end
|
||||
if is_verbose then
|
||||
log (request_header + "%NWARNING: invalid HTTP incoming request", warning_level)
|
||||
end
|
||||
end
|
||||
else
|
||||
if is_verbose then
|
||||
log (request_header, information_level)
|
||||
end
|
||||
process_request (l_socket)
|
||||
end
|
||||
process_bad_request (l_socket)
|
||||
is_persistent_connection_requested := False
|
||||
else
|
||||
if is_verbose then
|
||||
log (request_header, information_level)
|
||||
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")
|
||||
dbglog (generator + ".execute_request {" + l_socket.descriptor.out + "} LEAVE")
|
||||
dbglog ("execute_request {" + l_socket.descriptor.out + "} LEAVE")
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -307,7 +331,7 @@ feature -- Execution
|
||||
feature -- Request processing
|
||||
|
||||
process_request (a_socket: HTTPD_STREAM_SOCKET)
|
||||
-- Process request ...
|
||||
-- Process request on socket `a_socket'.
|
||||
require
|
||||
no_error: not has_error
|
||||
a_uri_attached: uri /= Void
|
||||
@@ -318,6 +342,39 @@ feature -- Request processing
|
||||
deferred
|
||||
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
|
||||
|
||||
analyze_request_message (a_socket: HTTPD_STREAM_SOCKET)
|
||||
@@ -336,15 +393,20 @@ feature -- Parsing
|
||||
request_header := txt
|
||||
if
|
||||
not has_error and then
|
||||
a_socket.is_readable and then
|
||||
attached next_line (a_socket) as l_request_line and then
|
||||
not l_request_line.is_empty
|
||||
a_socket.readable
|
||||
then
|
||||
txt.append (l_request_line)
|
||||
txt.append_character ('%N')
|
||||
analyze_request_line (l_request_line)
|
||||
if
|
||||
attached next_line (a_socket) as l_request_line and then
|
||||
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
|
||||
has_error := True
|
||||
report_error ("Socket is not readable")
|
||||
end
|
||||
l_is_verbose := is_verbose
|
||||
if not has_error then
|
||||
@@ -419,7 +481,9 @@ feature -- Parsing
|
||||
n := n - 1
|
||||
end
|
||||
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
|
||||
|
||||
next_line (a_socket: HTTPD_STREAM_SOCKET): detachable STRING
|
||||
@@ -431,22 +495,26 @@ feature -- Parsing
|
||||
retried: BOOLEAN
|
||||
do
|
||||
if retried then
|
||||
has_error := True
|
||||
report_error ("Rescue in next_line")
|
||||
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
|
||||
Result := a_socket.last_string
|
||||
-- Do no check `socket_ok' before socket operation,
|
||||
-- otherwise it may be False, due to error during other socket operation in same thread.
|
||||
if not a_socket.socket_ok then
|
||||
has_error := True
|
||||
report_error ("Socket error")
|
||||
if is_verbose then
|
||||
log (request_header +"%N" + Result + "%N## socket_ok=False! ##", debug_level)
|
||||
end
|
||||
end
|
||||
else
|
||||
-- Error with socket...
|
||||
has_error := True
|
||||
report_error ("Socket error: not readable")
|
||||
if is_verbose then
|
||||
log (request_header + "%N## Socket is not readable! ##", debug_level)
|
||||
end
|
||||
@@ -482,6 +550,17 @@ feature -- Output
|
||||
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
|
||||
request_header_attached: request_header /= Void
|
||||
|
||||
|
||||
@@ -111,6 +111,15 @@ feature -- Execution
|
||||
log (" - socket_recv_timeout = " + configuration.socket_recv_timeout.out + " seconds")
|
||||
log (" - keep_alive_timeout = " + configuration.keep_alive_timeout.out + " seconds")
|
||||
log (" - max_keep_alive_requests = " + configuration.max_keep_alive_requests.out)
|
||||
if configuration.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
|
||||
log (" - verbose_level = " + configuration.verbose_level.out)
|
||||
end
|
||||
|
||||
@@ -141,6 +141,15 @@ feature -- Input
|
||||
socket.read_character
|
||||
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
|
||||
do
|
||||
Result := socket.bytes_read
|
||||
@@ -308,6 +317,15 @@ feature -- Status Report
|
||||
Result := socket.readable
|
||||
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
|
||||
do
|
||||
if attached {TCP_STREAM_SOCKET} socket as l_socket then
|
||||
|
||||
@@ -55,6 +55,38 @@ feature {NONE} -- Initialization
|
||||
|
||||
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)
|
||||
do
|
||||
put_string (a_msg)
|
||||
@@ -73,6 +105,16 @@ feature -- Output
|
||||
|
||||
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
|
||||
-- Is data available for reading from the socket right now?
|
||||
require
|
||||
@@ -84,6 +126,19 @@ feature -- Status report
|
||||
Result := (retval > 0)
|
||||
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
|
||||
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
|
||||
@@ -56,15 +56,6 @@ feature {NONE} -- Externals
|
||||
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
|
||||
|
||||
@@ -97,7 +97,9 @@ feature -- Request processing
|
||||
end
|
||||
end
|
||||
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
|
||||
retried := True
|
||||
retry
|
||||
|
||||
@@ -26,4 +26,10 @@
|
||||
</target>
|
||||
<target name="test_connector_standalone" extends="test_standalone_scoop">
|
||||
</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>
|
||||
|
||||
Reference in New Issue
Block a user