Moved httpd library from ewsgi/connectors/standalone/lib/httpd to httpd.

Reused the http_network library as well inside httpd library.
This commit is contained in:
2016-10-12 22:54:21 +02:00
parent d28f794828
commit c132d7734b
48 changed files with 86 additions and 73 deletions

View File

@@ -0,0 +1,86 @@
note
description: "[
Implementation of HTTPD_CONNECTION_HANDLER_I for concurrency mode: none
]"
date: "$Date$"
revision: "$Revision$"
class
HTTPD_CONNECTION_HANDLER
inherit
HTTPD_CONNECTION_HANDLER_I
create
make
feature {NONE} -- Initialization
initialize
do
end
feature -- Access
is_shutdown_requested: BOOLEAN
-- <Precursor>
shutdown_requested (a_server: like server): BOOLEAN
do
-- FIXME: we should probably remove this possibility, check with EWF if this is needed.
Result := a_server.controller.shutdown_requested
end
feature {HTTPD_SERVER_I} -- Execution
accept_incoming_connection (a_listening_socket: HTTPD_STREAM_SOCKET)
local
cl: HTTPD_STREAM_SOCKET
do
is_shutdown_requested := is_shutdown_requested or shutdown_requested (server)
if is_shutdown_requested then
-- Cancel
elseif attached factory.new_handler as h then
cl := h.client_socket
a_listening_socket.accept_to (cl)
if h.is_connected then
h.safe_execute
end
else
check is_not_full: False end
end
update_is_shutdown_requested
end
update_is_shutdown_requested
do
is_shutdown_requested := shutdown_requested (server)
end
shutdown
do
if not is_shutdown_requested then
is_shutdown_requested := True
server.controller.shutdown
end
end
feature {HTTPD_SERVER_I} -- Status report
wait_for_completion
-- Wait until Current is ready for shutdown
do
-- no concurrency, then current task should be done.
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)"
source: "[
Eiffel Software
5949 Hollister Ave., Goleta, CA 93117 USA
Telephone 805-685-1006, Fax 805-685-6869
Website http://www.eiffel.com
Customer support http://support.eiffel.com
]"
end

View File

@@ -0,0 +1,32 @@
note
description : "Concurrent specific feature to implement HTTPD_REQUEST_HANDLER"
date : "$Date$"
revision : "$Revision$"
deferred class
HTTPD_REQUEST_HANDLER
inherit
HTTPD_REQUEST_HANDLER_I
redefine
is_persistent_connection_supported
end
feature -- Status report
is_persistent_connection_supported: BOOLEAN = False
-- <Precursor>
-- When there is no concurrency support, do not try to support
-- persistent connection!
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)"
source: "[
Eiffel Software
5949 Hollister Ave., Goleta, CA 93117 USA
Telephone 805-685-1006, Fax 805-685-6869
Website http://www.eiffel.com
Customer support http://support.eiffel.com
]"
end

View File

@@ -0,0 +1,15 @@
note
description: "Implementation of request handler factory for concurrency mode: none"
date: "$Date$"
revision: "$Revision$"
deferred class
HTTPD_REQUEST_HANDLER_FACTORY
inherit
HTTPD_REQUEST_HANDLER_FACTORY_I
note
copyright: "2011-2013, Javier Velilla, Jocelyn Fiat and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
end

View File

@@ -0,0 +1,146 @@
note
description: "[
Implementation of HTTPD_CONNECTION_HANDLER_I for concurrency mode: SCOOP
]"
date: "$Date$"
revision: "$Revision$"
class
HTTPD_CONNECTION_HANDLER
inherit
HTTPD_CONNECTION_HANDLER_I
redefine
initialize
end
create
make
feature {NONE} -- Initialization
initialize
local
n: INTEGER
p: like pool
do
n := max_concurrent_connections (server).max (1) -- At least one processor!
create p.make (n)
initialize_pool (p, n)
pool := p
end
initialize_pool (p: like pool; n: INTEGER)
-- Initialize Concurrent pool of `n' potential separate connection handlers.
do
p.set_count (n)
end
feature -- Access
is_shutdown_requested: BOOLEAN
-- <Precursor>
max_concurrent_connections (a_server: like server): INTEGER
-- Max concurrent connection settings from server `a_server'.
do
Result := a_server.configuration.max_concurrent_connections
end
feature {HTTPD_SERVER_I} -- Execution
shutdown
-- <Precursor>
do
if not is_shutdown_requested then
is_shutdown_requested := True
pool_gracefull_stop (pool)
end
end
pool_gracefull_stop (p: like pool)
-- Graceful stop concurrent pool of separate connection handlers.
do
p.gracefull_stop
end
accept_incoming_connection (a_listening_socket: HTTPD_STREAM_SOCKET)
-- <Precursor>
do
accept_connection_on_pool (pool, a_listening_socket) -- Wait on not pool.is_full or is_stop_requested
end
accept_connection_on_pool (a_pool: like pool; a_listening_socket: HTTPD_STREAM_SOCKET)
-- Process accept connection
-- note that the precondition matters for scoop synchronization.
require
concurrency: not a_pool.is_full or is_shutdown_requested or a_pool.stop_requested
local
cl: separate HTTPD_STREAM_SOCKET
do
debug ("dbglog")
dbglog (generator + ".ENTER accept_connection_on_pool")
end
if is_shutdown_requested then
-- Cancel
elseif attached a_pool.separate_item (factory) as h then
cl := separate_client_socket (h)
a_listening_socket.accept_to (cl)
process_handler (h)
else
check is_not_full: False end
end
debug ("dbglog")
dbglog (generator + ".LEAVE accept_connection_on_pool")
end
end
process_handler (hdl: separate HTTPD_REQUEST_HANDLER)
-- Process request handler `hdl' as soon as `hdl' is connected to accepted socket.
require
hdl.is_connected
do
hdl.safe_execute
end
feature {HTTPD_SERVER_I} -- Status report
wait_for_completion
-- Wait until Current is ready for shutdown.
do
wait_for_pool_completion (pool)
end
wait_for_pool_completion (p: like pool)
-- Wait until concurrent pool is empty and terminated.
require
p.is_empty -- SCOOP wait condition.
do
p.terminate
end
feature {NONE} -- Implementation
separate_client_socket (hdl: separate HTTPD_REQUEST_HANDLER): separate HTTPD_STREAM_SOCKET
-- Client socket for request handler `hdl'.
do
Result := hdl.client_socket
end
pool: separate CONCURRENT_POOL [HTTPD_REQUEST_HANDLER]
-- Pool of separate connection handlers.
invariant
pool_attached: pool /= Void
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)"
source: "[
Eiffel Software
5949 Hollister Ave., Goleta, CA 93117 USA
Telephone 805-685-1006, Fax 805-685-6869
Website http://www.eiffel.com
Customer support http://support.eiffel.com
]"
end

View File

@@ -0,0 +1,62 @@
note
description: "[
Instance of HTTPD_REQUEST_HANDLER will process the incoming connection
and extract information on the request and the server
]"
date: "$Date$"
revision: "$Revision$"
deferred class
HTTPD_REQUEST_HANDLER
inherit
HTTPD_REQUEST_HANDLER_I
redefine
release
end
CONCURRENT_POOL_ITEM
rename
release as release_pool_item
end
feature {CONCURRENT_POOL, HTTPD_CONNECTION_HANDLER_I} -- Basic operation
release
-- <Precursor>
local
d: detachable STRING
do
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 + "}")
end
Precursor {HTTPD_REQUEST_HANDLER_I}
release_pool_item
debug ("dbglog")
if d /= Void then
dbglog (generator + ".release: LEAVE {" + d + "}")
else
dbglog (generator + ".release: LEAVE {N/A}")
end
end
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)"
source: "[
Eiffel Software
5949 Hollister Ave., Goleta, CA 93117 USA
Telephone 805-685-1006, Fax 805-685-6869
Website http://www.eiffel.com
Customer support http://support.eiffel.com
]"
end

View File

@@ -0,0 +1,27 @@
note
description: "Implementation of request handler factory for concurrency mode: SCOOP"
date: "$Date$"
revision: "$Revision$"
deferred class
HTTPD_REQUEST_HANDLER_FACTORY
inherit
HTTPD_REQUEST_HANDLER_FACTORY_I
CONCURRENT_POOL_FACTORY [HTTPD_REQUEST_HANDLER]
rename
new_separate_item as new_handler
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)"
source: "[
Eiffel Software
5949 Hollister Ave., Goleta, CA 93117 USA
Telephone 805-685-1006, Fax 805-685-6869
Website http://www.eiffel.com
Customer support http://support.eiffel.com
]"
end

View File

@@ -0,0 +1,190 @@
note
description: "Concurrent pool for SCOOP concurrency mode."
date: "$Date$"
revision: "$Revision$"
class
CONCURRENT_POOL [G -> CONCURRENT_POOL_ITEM]
inherit
HTTPD_DEBUG_FACILITIES
create
make
feature {NONE} -- Initialization
make (n: INTEGER)
do
capacity := n
create items.make_empty (n)
create busy_items.make_empty (n)
end
feature -- Access
count: INTEGER
-- Number of concurrent items managed by Current pool.
capacity: INTEGER
-- Maximum number of concurrent items managed by Current pool.
feature -- Status report
is_full: BOOLEAN
-- Pool is full?
do
Result := count >= capacity
end
is_empty: BOOLEAN
-- No concurrent item waiting in current pool.
do
Result := count = 0
end
stop_requested: BOOLEAN
-- Current pool received a request to terminate.
feature -- Access
separate_item (a_factory: separate CONCURRENT_POOL_FACTORY [G]): detachable separate G
-- Reused, or new separate item of type {G} created by `a_factory'.
require
is_not_full: not is_full
local
i,n,pos: INTEGER
lst: like busy_items
l_item: detachable separate G
do
if not stop_requested then
from
lst := busy_items
pos := -1
i := 0
n := lst.count - 1
until
i > n or l_item /= Void or pos >= 0
loop
if not lst [i] then -- is free (i.e not busy)
pos := i
if items.valid_index (pos) then
l_item := items [pos]
if l_item /= Void then
busy_items [pos] := True
end
end
if l_item = Void then
-- Empty, then let's create one.
l_item := a_factory.new_separate_item
register_item (l_item)
items [pos] := l_item
end
end
i := i + 1
end
if l_item = Void then
-- Pool is FULL ...
check overcapacity: False end
else
debug ("pool", "dbglog")
dbglog ("Lock pool item #" + pos.out + " (free:"+ (capacity - count).out +"))")
end
count := count + 1
busy_items [pos] := True
Result := l_item
a_factory.update_item (l_item)
end
end
end
feature -- Basic operation
gracefull_stop
-- Request the Current pool to terminate.
do
stop_requested := True
end
feature {NONE} -- Internal
items: SPECIAL [detachable separate G]
-- List of concurrent items.
busy_items: SPECIAL [BOOLEAN]
-- Map of items being proceed.
feature {CONCURRENT_POOL_ITEM} -- Change
release_item (a_item: separate G)
-- Unregister `a_item' from Current pool.
require
count > 0
local
i,n,pos: INTEGER
lst: like items
do
-- release handler for reuse
from
lst := items
i := 0
n := lst.count - 1
until
i > n or lst [i] = a_item
loop
i := i + 1
end
if i <= n then
pos := i
busy_items [pos] := False
count := count - 1
--reuse items [pos] := Void
debug ("pool", "dbglog")
dbglog ("Released pool item #" + i.out + " (free:"+ (capacity - count).out +"))")
end
else
check known_item: False end
end
end
feature -- Change
set_count (n: INTEGER)
-- Set capacity of Current pool to `n'.
local
g: detachable separate G
do
capacity := n
items.fill_with (g, 0, n - 1)
busy_items.fill_with (False, 0, n - 1)
end
terminate
-- Terminate current pool.
local
l_items: like items
do
l_items := items
l_items.wipe_out
end
feature {NONE} -- Implementation
register_item (a_item: separate G)
-- Adopt `a_item' in current pool.
do
a_item.set_pool (Current)
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)"
source: "[
Eiffel Software
5949 Hollister Ave., Goleta, CA 93117 USA
Telephone 805-685-1006, Fax 805-685-6869
Website http://www.eiffel.com
Customer support http://support.eiffel.com
]"
end

View File

@@ -0,0 +1,31 @@
note
description: "Factory in charge of creating new concurrent pool item."
date: "$Date$"
revision: "$Revision$"
deferred class
CONCURRENT_POOL_FACTORY [G -> CONCURRENT_POOL_ITEM]
feature -- Access
update_item (a_item: separate G)
-- Update `a_item' for optionally purpose.
do
end
new_separate_item: separate G
-- New separated object of type {G}.
deferred
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)"
source: "[
Eiffel Software
5949 Hollister Ave., Goleta, CA 93117 USA
Telephone 805-685-1006, Fax 805-685-6869
Website http://www.eiffel.com
Customer support http://support.eiffel.com
]"
end

View File

@@ -0,0 +1,52 @@
note
description: "[
Item create by the CONCURRENT_POOL_FACTORY, and managed by the CONCURRENT_POOL
for SCOOP concurrency mode.
]"
date: "$Date$"
revision: "$Revision$"
deferred class
CONCURRENT_POOL_ITEM
feature {NONE} -- Access
pool: detachable separate CONCURRENT_POOL [CONCURRENT_POOL_ITEM]
-- Associated concurrent pool component.
feature {CONCURRENT_POOL} -- Change
set_pool (p: like pool)
-- Set associated `pool' to `p'.
do
pool := p
end
feature {CONCURRENT_POOL, HTTPD_CONNECTION_HANDLER_I} -- Basic operation
release
-- Release Current pool item from associated pool.
do
if attached pool as p then
pool_release (p)
end
end
feature {NONE} -- Implementation
pool_release (p: separate CONCURRENT_POOL [CONCURRENT_POOL_ITEM])
do
p.release_item (Current)
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)"
source: "[
Eiffel Software
5949 Hollister Ave., Goleta, CA 93117 USA
Telephone 805-685-1006, Fax 805-685-6869
Website http://www.eiffel.com
Customer support http://support.eiffel.com
]"
end

View File

@@ -0,0 +1,103 @@
note
description: "[
Implementation of HTTPD_CONNECTION_HANDLER_I for concurrency mode: Thread
]"
date: "$Date$"
revision: "$Revision$"
class
HTTPD_CONNECTION_HANDLER
inherit
HTTPD_CONNECTION_HANDLER_I
redefine
initialize
end
create
make
feature {NONE} -- Initialization
initialize
local
n: INTEGER
do
n := max_concurrent_connections (server).max (1) -- At least one thread!
create pool.make (n.to_natural_32)
end
feature -- Access
is_shutdown_requested: BOOLEAN
max_concurrent_connections (a_server: like server): INTEGER
do
Result := a_server.configuration.max_concurrent_connections
end
feature {HTTPD_SERVER_I} -- Execution
shutdown
do
if not is_shutdown_requested then
is_shutdown_requested := True
pool_gracefull_stop (pool)
end
end
pool_gracefull_stop (p: like pool)
do
p.terminate
end
accept_incoming_connection (a_listening_socket: HTTPD_STREAM_SOCKET)
local
cl: separate HTTPD_STREAM_SOCKET
do
debug ("dbglog")
dbglog (generator + ".ENTER accept_connection {"+ a_listening_socket.descriptor.out +"}")
end
if is_shutdown_requested then
-- cancel
elseif attached factory.new_handler as h then
cl := h.client_socket
a_listening_socket.accept_to (cl)
if h.is_connected then
pool.add_work (agent h.safe_execute)
end
end
debug ("dbglog")
dbglog (generator + ".LEAVE accept_incoming_connection {"+ a_listening_socket.descriptor.out +"}")
end
end
feature {HTTPD_SERVER_I} -- Status report
wait_for_completion
-- Wait until Current is ready for shutdown
do
pool.wait_for_completion
end
feature {NONE} -- Access
pool: THREAD_POOL [HTTPD_REQUEST_HANDLER] --ANY] --POOLED_THREAD [HTTP_REQUEST_HANDLER]]
-- Pool of concurrent connection handlers.
invariant
pool_attached: pool /= Void
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)"
source: "[
Eiffel Software
5949 Hollister Ave., Goleta, CA 93117 USA
Telephone 805-685-1006, Fax 805-685-6869
Website http://www.eiffel.com
Customer support http://support.eiffel.com
]"
end

View File

@@ -0,0 +1,56 @@
note
description: "[
Instance of HTTPD_REQUEST_HANDLER will process the incoming connection
and extract information on the request and the server
]"
date: "$Date$"
revision: "$Revision$"
deferred class
HTTPD_REQUEST_HANDLER
inherit
HTTPD_REQUEST_HANDLER_I
redefine
release
end
feature {HTTPD_CONNECTION_HANDLER_I} -- Basic operation
release
-- <Precursor>
local
d: detachable STRING
do
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 + "}")
end
Precursor {HTTPD_REQUEST_HANDLER_I}
debug ("dbglog")
if d /= Void then
dbglog (generator + ".release: LEAVE {" + d + "}")
else
dbglog (generator + ".release: LEAVE {N/A}")
end
end
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)"
source: "[
Eiffel Software
5949 Hollister Ave., Goleta, CA 93117 USA
Telephone 805-685-1006, Fax 805-685-6869
Website http://www.eiffel.com
Customer support http://support.eiffel.com
]"
end

View File

@@ -0,0 +1,15 @@
note
description: "Implementation of request handler factory for concurrency mode: Thread"
date: "$Date$"
revision: "$Revision$"
deferred class
HTTPD_REQUEST_HANDLER_FACTORY
inherit
HTTPD_REQUEST_HANDLER_FACTORY_I
note
copyright: "2011-2013, Javier Velilla, Jocelyn Fiat and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
end

View File

@@ -0,0 +1,121 @@
note
description: "{POOLED_THREAD} is used in combination with {THREAD_POOL} to allow for pooled threads."
legal: "See notice at end of class."
status: "Community Preview 1.0"
date: "$Date: 2009-09-01 19:15:37 -0300 (mar 01 de sep de 2009) $"
revision: "$Revision: 80577 $"
class
POOLED_THREAD [G]
inherit
THREAD
rename
make as thread_make
end
create {THREAD_POOL}
make
feature {NONE} -- Initialization
make (a_thread_pool: THREAD_POOL [G]; a_semaphore: SEMAPHORE)
-- `a_thread_pool', the pool in which this thread is managed
-- `a_semaphore' is used for execution suspending
do
thread_make
thread_pool := a_thread_pool
semaphore := a_semaphore
end
feature {NONE} -- Access
thread_pool: THREAD_POOL [G]
-- Pool manager in which this thread is pooled
target: detachable G
-- Target on which the `thread_procedure' should be applied
-- Depending on which launch is used, target is not used
thread_procedure: detachable PROCEDURE
-- Work that should be executed by the thread
semaphore: SEMAPHORE
-- Semaphore share with all threads in a thread pool
-- to suspend execution until more work is available
feature -- Access
set_target (a_target: G)
-- Sets the target on which the work should be executed
do
target := a_target
end
feature {NONE} -- Implementation
execute
-- <Precursor>
local
done: BOOLEAN
do
from
semaphore.wait
thread_procedure := thread_pool.get_work (Current)
until
done
loop
if attached thread_procedure as l_work then
if attached target as t then
l_work.call ([t])
else
l_work.call (Void)
end
end
if thread_pool.over then
done := True
else
thread_procedure := thread_pool.get_work (Current)
if thread_procedure = Void then
semaphore.wait
thread_procedure := thread_pool.get_work (Current)
end
end
end
thread_pool.thread_terminated (Current)
end
note
copyright: "2011-2012, Javier Velilla and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
licensing_options: "http://www.eiffel.com/licensing"
copying: "[
This file is part of Eiffel Software's Eiffel Development Environment.
Eiffel Software's Eiffel Development Environment is free
software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published
by the Free Software Foundation, version 2 of the License
(available at the URL listed under "license" above).
Eiffel Software's Eiffel Development Environment is
distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty
of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public
License along with Eiffel Software's Eiffel Development
Environment; if not, write to the Free Software Foundation,
Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
]"
source: "[
Eiffel Software
5949 Hollister Ave., Goleta, CA 93117 USA
Telephone 805-685-1006, Fax 805-685-6869
Website http://www.eiffel.com
Customer support http://support.eiffel.com
]"
end

View File

@@ -0,0 +1,228 @@
note
description: "[
A thread pool manager. Manages threads up to `capacity' and queue after that,
till threads get available again.
]"
legal: "See notice at end of class."
status: "Community Preview 1.0"
date: "$Date: 2009-09-01 19:15:37 -0300 (mar 01 de sep de 2009) $"
revision: "$Revision: 80577 $"
class
THREAD_POOL [G]
inherit
EXECUTION_ENVIRONMENT
create
make
feature {NONE} -- Initialization
make (n: like capacity)
-- Initialize current pool with capacity `n'.
require
n_positive: n > 0
n_not_too_large: n < {INTEGER_32}.max_value.as_natural_32
local
i: NATURAL
do
capacity := n
create work_queue.make (n.to_integer_32)
create work_queue_mutex.make
create over_mutex.make
create termination_mutex.make
create work_semaphore.make (capacity.as_integer_32)
from
i := 1
until
i > capacity
loop
work_semaphore.wait
i := i + 1
end
initialize_threads
terminated_count := capacity
is_over := False
ensure
capacity_set: capacity = n
work_queue_set: work_queue.is_empty
end
initialize_threads
-- Launches all threads
local
i: NATURAL
thread: POOLED_THREAD [G]
do
from
i := 1
until
i > capacity
loop
create thread.make (Current, work_semaphore)
thread.launch
i := i + 1
end
end
feature -- Access
capacity: NATURAL
-- Maximal number of threads allowed (queuing otherwise)
queue_count: NATURAL
-- Number of items in queue
do
work_queue_mutex.lock
Result := work_queue.count.as_natural_32
work_queue_mutex.unlock
end
feature -- Status report
valid_action (a_action: PROCEDURE): BOOLEAN
-- Is `a_action' a valid action for the current pool.
do
-- There should be no open operands.
Result := a_action.valid_operands (Void)
end
feature -- Basic operations
add_work (work: PROCEDURE)
-- Launches a thread with the specified argument `arg'. Reuse of thread if possible.
require
valid_action: valid_action (work)
do
work_queue_mutex.lock
work_queue.extend (work)
if work_queue.count <= capacity.as_integer_32 then
-- Let one thread wake up and do the work
work_semaphore.post
end
work_queue_mutex.unlock
end
over: BOOLEAN
-- Is the thread pool being terminated?
do
over_mutex.lock
Result := is_over
over_mutex.unlock
end
thread_terminated (a_thread: POOLED_THREAD [G])
-- Notifies the thread pool that a thread has terminated its execution.
do
termination_mutex.lock
terminated_count := terminated_count - 1
termination_mutex.unlock
end
get_work (requester: POOLED_THREAD [G]): detachable PROCEDURE
-- If there is work to do, it is returned
-- Yields Void otherwise
do
if not over then
work_queue_mutex.lock
if not work_queue.is_empty then
Result := work_queue.item
work_queue.remove
end
work_queue_mutex.unlock
end
end
wait_for_completion
-- Wait until there is no more work to be completed
local
done: BOOLEAN
do
from
until
done
loop
work_queue_mutex.lock
done := work_queue.is_empty
work_queue_mutex.unlock
if not done then
sleep (1)
end
end
end
terminate
-- Terminates all the threads after their execution
do
over_mutex.lock
is_over := True
over_mutex.unlock
from
termination_mutex.lock
until
terminated_count = 0
loop
work_semaphore.post
termination_mutex.unlock
termination_mutex.lock
end
termination_mutex.unlock
end
feature {NONE} -- Implementation: Access
work_queue: ARRAYED_QUEUE [PROCEDURE]
-- Queue that holds unprocessed requests as agents
-- Thread-safe access when accessor holds `queue_mutex'
work_queue_mutex: MUTEX
-- Mutex for the queue
work_semaphore: SEMAPHORE
-- Semaphore which hols the number of work to be done.
-- Needed to wake up worker threads
terminated_count: NATURAL
--
is_over: BOOLEAN
-- Is the thread pool being terminated?
over_mutex: MUTEX
-- Mutex for the `is_over' variable
termination_mutex: MUTEX
;note
copyright: "2011-2012, Javier Velilla, Jocelyn Fiat and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
licensing_options: "http://www.eiffel.com/licensing"
copying: "[
This file is part of Eiffel Software's Eiffel Development Environment.
Eiffel Software's Eiffel Development Environment is free
software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published
by the Free Software Foundation, version 2 of the License
(available at the URL listed under "license" above).
Eiffel Software's Eiffel Development Environment is
distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty
of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public
License along with Eiffel Software's Eiffel Development
Environment; if not, write to the Free Software Foundation,
Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
]"
source: "[
Eiffel Software
5949 Hollister Ave., Goleta, CA 93117 USA
Telephone 805-685-1006, Fax 805-685-6869
Website http://www.eiffel.com
Customer support http://support.eiffel.com
]"
end

View File

@@ -0,0 +1,348 @@
note
description: "Configuration for the standalone HTTPd server."
date: "$Date$"
revision: "$Revision$"
deferred class
HTTPD_CONFIGURATION_I
inherit
ANY
HTTPD_CONSTANTS
feature {NONE} -- Initialization
make
do
http_server_port := default_http_server_port
max_concurrent_connections := default_max_concurrent_connections
max_tcp_clients := default_max_tcp_clients
socket_timeout := default_socket_timeout
socket_recv_timeout := default_socket_recv_timeout
keep_alive_timeout := default_keep_alive_timeout
max_keep_alive_requests := default_max_keep_alive_requests
is_secure := False
create ca_crt.make_empty
create ca_key.make_empty
end
feature -- Access
Server_details: STRING_8
-- Detail of the server.
deferred
end
http_server_name: detachable READABLE_STRING_8 assign set_http_server_name
http_server_port: INTEGER assign set_http_server_port
max_tcp_clients: INTEGER assign set_max_tcp_clients
-- Listen on socket for at most `queue' connections.
socket_timeout: INTEGER assign set_socket_timeout
-- Amount of seconds that the server waits for receipts and transmissions during communications.
-- note: with timeout of 0, socket can wait for ever.
-- By default: 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 number of concurrent connections.
force_single_threaded: BOOLEAN assign set_force_single_threaded
do
Result := max_concurrent_connections = 0
end
is_verbose: BOOLEAN assign set_is_verbose
-- Display verbose message to the output?
verbose_level: INTEGER assign set_verbose_level
-- Verbosity of output.
keep_alive_timeout: INTEGER assign set_keep_alive_timeout
-- Persistent connection timeout.
-- Number of seconds the server waits after a request has been served before it closes the connection.
-- Timeout unit in Seconds.
-- By default: 5 seconds.
max_keep_alive_requests: INTEGER assign set_max_keep_alive_requests
-- Maximum number of requests allowed per persistent connection.
-- Recommended a high setting.
-- To disable KeepAlive, set `max_keep_alive_requests' to 0.
-- By default: 100 .
has_ssl_support: BOOLEAN
-- Has SSL support?
deferred
end
request_settings: HTTPD_REQUEST_SETTINGS
do
Result.is_verbose := is_verbose
Result.verbose_level := verbose_level
Result.timeout := socket_timeout
Result.socket_recv_timeout := socket_recv_timeout
Result.keep_alive_timeout := keep_alive_timeout
Result.max_keep_alive_requests := max_keep_alive_requests
Result.is_secure := is_secure
end
feature -- Access: SSL
is_secure: BOOLEAN
-- Is SSL/TLS session?.
ca_crt: detachable IMMUTABLE_STRING_32
-- the signed certificate.
ca_key: detachable IMMUTABLE_STRING_32
-- private key to the certificate.
ssl_protocol: NATURAL
-- By default protocol is tls 1.2.
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)
do
if v = Void then
unset_http_server_name
else
create {IMMUTABLE_STRING_8} http_server_name.make_from_separate (v)
end
end
unset_http_server_name
-- Unset `http_server_name' value.
do
http_server_name := Void
ensure
unset_http_server_name: http_server_name = Void
end
set_http_server_port (v: like http_server_port)
-- Set `http_server_port' with `v'.
do
http_server_port := v
ensure
http_server_port_set: http_server_port = v
end
set_max_tcp_clients (v: like max_tcp_clients)
-- Set `max_tcp_clients' with `v'.
do
max_tcp_clients := v
ensure
max_tcp_clients_set: max_tcp_clients = v
end
set_max_concurrent_connections (v: like max_concurrent_connections)
-- Set `max_concurrent_connections' with `v'.
do
max_concurrent_connections := v
ensure
max_concurrent_connections_set : max_concurrent_connections = v
end
set_socket_timeout (a_nb_seconds: like socket_timeout)
-- Set `socket_timeout' with `a_nb_seconds'
do
socket_timeout := a_nb_seconds
ensure
socket_timeout_set: socket_timeout = a_nb_seconds
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' with `a_seconds'
do
keep_alive_timeout := a_seconds
ensure
keep_alive_timeout_set: keep_alive_timeout = a_seconds
end
set_max_keep_alive_requests (nb: like max_keep_alive_requests)
-- Set `max_keep_alive_requests' with `nb'
do
max_keep_alive_requests := nb
ensure
max_keep_alive_requests_set: max_keep_alive_requests = nb
end
set_force_single_threaded (v: like force_single_threaded)
-- Force server to handle incoming request in a single thread.
-- i.e set max_concurrent_connections to 0!
obsolete
"Use set_max_concurrent_connections (0) [June/2016]"
do
if v then
set_max_concurrent_connections (0)
end
--|Missing postcondition
--| force_single_thread_set: v implies max_concurrent_connections = 0
--| not_single_thread: not v implies max_concurrent_connections > 0
end
set_is_verbose (b: BOOLEAN)
-- Set `is_verbose' to `b'
do
is_verbose := b
ensure
is_verbose_set: is_verbose = b
end
set_verbose_level (lev: INTEGER)
-- Set `verbose_level' to `lev'.
do
verbose_level := lev
ensure
verbose_level_set: verbose_level = lev
end
set_is_secure (b: BOOLEAN)
-- Set `is_secure' to `b'.
do
if b and has_ssl_support then
is_secure := True
if
http_server_port = 80
then
set_http_server_port (443)
end
else
is_secure := False
if
http_server_port = 443
then
set_http_server_port (80)
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
is_secure_set: has_ssl_support implies is_secure
-- http_server_port_set: has_ssl_support implies http_server_port = 443
is_not_secure: not has_ssl_support implies not is_secure
-- default_port: not has_ssl_support implies http_server_port = 80
end
feature -- Element change
set_ca_crt (a_value: detachable separate READABLE_STRING_GENERAL)
-- Set `ca_crt' from `a_value'.
do
if a_value /= Void then
create ca_crt.make_from_separate (a_value)
else
ca_crt := Void
end
end
set_ca_key (a_value: detachable separate READABLE_STRING_GENERAL)
-- Set `ca_key' with `a_value'.
do
if a_value /= Void then
create ca_key.make_from_separate (a_value)
else
ca_key := Void
end
end
set_ssl_protocol (a_version: NATURAL)
-- Set `ssl_protocol' with `a_version'
do
ssl_protocol := a_version
ensure
ssl_protocol_set: ssl_protocol = a_version
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
set_ssl_protocol_to_ssl_2_or_3
-- Set `ssl_protocol' with `Ssl_23'.
deferred
end
set_ssl_protocol_to_tls_1_0
-- Set `ssl_protocol' with `Tls_1_0'.
deferred
end
set_ssl_protocol_to_tls_1_1
-- Set `ssl_protocol' with `Tls_1_1'.
deferred
end
set_ssl_protocol_to_tls_1_2
-- Set `ssl_protocol' with `Tls_1_2'.
deferred
end
set_ssl_protocol_to_dtls_1_0
-- Set `ssl_protocol' with `Dtls_1_0'.
deferred
end
note
copyright: "2011-2014, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[
Eiffel Software
5949 Hollister Ave., Goleta, CA 93117 USA
Telephone 805-685-1006, Fax 805-685-6869
Website http://www.eiffel.com
Customer support http://support.eiffel.com
]"
end

View File

@@ -0,0 +1,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

@@ -0,0 +1,82 @@
note
description: "[
Request settings for the standalone HTTPd server.
]"
author: "$Author$"
date: "$Date$"
revision: "$Revision$"
expanded class
HTTPD_REQUEST_SETTINGS
feature -- Access
is_verbose: BOOLEAN assign set_is_verbose
-- Is verbose?
verbose_level: INTEGER assign set_verbose_level
-- Verbosity of output.
is_secure: BOOLEAN assign set_is_secure
-- Is using secure connection? i.e SSL?
timeout: INTEGER assign set_timeout
-- 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, also known as persistent-connection timeout.
-- Number of seconds the server waits after a request has been served before it closes the connection.
-- Unit in Seconds.
max_keep_alive_requests: INTEGER assign set_max_keep_alive_requests
-- Maximum number of requests allowed per persistent connection.
feature -- Change
set_is_verbose (b: BOOLEAN)
-- Set `is_verbose' to `b'.
do
is_verbose := b
end
set_verbose_level (lev: INTEGER)
-- Set `verbose_level' to `lev'.
do
verbose_level := lev
end
set_is_secure (b: BOOLEAN)
-- Set `is_secure' to `b'.
do
is_secure := b
end
set_timeout (a_timeout_in_seconds: INTEGER)
-- Set `timeout' to `a_timeout_in_seconds'.
do
timeout := a_timeout_in_seconds
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' to `a_timeout_in_seconds'.
do
keep_alive_timeout := a_timeout_in_seconds
end
set_max_keep_alive_requests (nb: like max_keep_alive_requests)
-- Set `max_keep_alive_requests' with `nb'
do
max_keep_alive_requests := nb
end
end

View File

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

View File

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

View File

@@ -0,0 +1,79 @@
<?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="httpd" uuid="50FE258D-CC94-4748-9223-55F1129E5FB3" library_target="httpd">
<target name="httpd">
<root all_classes="true"/>
<file_rule>
<exclude>/.git$</exclude>
<exclude>/.svn$</exclude>
<exclude>/EIFGENs$</exclude>
</file_rule>
<option warning="true" full_class_checking="false" is_attached_by_default="true" void_safety="all" syntax="standard">
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
</option>
<setting name="concurrency" value="scoop"/>
<external_include location="$ECF_CONFIG_PATH/spec/include">
<condition>
<version type="compiler" min="16.11.0.0"/>
</condition>
</external_include>
<external_include location="$ECF_CONFIG_PATH/spec/include_until_16_05">
<condition>
<version type="compiler" max="16.11.0.0"/>
</condition>
</external_include>
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
<library name="http_network" location="http_network-safe.ecf" readonly="false"/>
<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_enabled" value="true"/>
</condition>
<condition>
<custom name="httpd_ssl_enabled" value="true"/>
</condition>
</library>
<library name="thread" location="$ISE_LIBRARY\library\thread\thread-safe.ecf">
<condition>
<concurrency excluded_value="none"/>
</condition>
</library>
<library name="time" location="$ISE_LIBRARY\library\time\time-safe.ecf"/>
<cluster name="httpd_server" location=".\" recursive="true">
<file_rule>
<exclude>/concurrency$</exclude>
<exclude>/network$</exclude>
<exclude>/no_ssl$</exclude>
<exclude>/ssl$</exclude>
</file_rule>
<cluster name="no_ssl" location="$|no_ssl\" recursive="true">
<condition>
<custom name="ssl_enabled" excluded_value="true"/>
<custom name="httpd_ssl_enabled" excluded_value="true"/>
</condition>
</cluster>
<cluster name="ssl" location="$|ssl\" recursive="true">
<condition>
<custom name="ssl_enabled" value="true"/>
</condition>
<condition>
<custom name="httpd_ssl_enabled" value="true"/>
</condition>
</cluster>
<cluster name="concurrency_none" location="$|concurrency\none\" recursive="true">
<condition>
<concurrency value="none"/>
</condition>
</cluster>
<cluster name="concurrency_scoop" location="$|concurrency\scoop\" recursive="true">
<condition>
<concurrency value="scoop"/>
</condition>
</cluster>
<cluster name="concurrency_thread" location="$|concurrency\thread\" recursive="true">
<condition>
<concurrency value="thread"/>
</condition>
</cluster>
</cluster>
</target>
</system>

View File

@@ -0,0 +1,68 @@
<?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="httpd" uuid="50FE258D-CC94-4748-9223-55F1129E5FB3" library_target="httpd">
<target name="httpd">
<root all_classes="true"/>
<file_rule>
<exclude>/.git$</exclude>
<exclude>/.svn$</exclude>
<exclude>/EIFGENs$</exclude>
</file_rule>
<option warning="true" full_class_checking="true" void_safety="none" syntax="standard">
</option>
<setting name="concurrency" value="thread"/>
<library name="base" location="$ISE_LIBRARY\library\base\base.ecf"/>
<library name="http_network" location="http_network.ecf" readonly="false"/>
<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_enabled" value="true"/>
</condition>
<condition>
<custom name="httpd_ssl_enabled" value="true"/>
</condition>
</library>
<library name="thread" location="$ISE_LIBRARY\library\thread\thread.ecf">
<condition>
<concurrency excluded_value="none"/>
</condition>
</library>
<library name="time" location="$ISE_LIBRARY\library\time\time.ecf"/>
<cluster name="httpd_server" location=".\" recursive="true">
<file_rule>
<exclude>/concurrency$</exclude>
<exclude>/no_ssl$</exclude>
<exclude>/ssl$</exclude>
<exclude>/network$</exclude>
</file_rule>
<cluster name="no_ssl" location="$|no_ssl\" recursive="true">
<condition>
<custom name="ssl_enabled" excluded_value="true"/>
<custom name="httpd_ssl_enabled" excluded_value="true"/>
</condition>
</cluster>
<cluster name="ssl" location="$|ssl\" recursive="true">
<condition>
<custom name="ssl_enabled" value="true"/>
</condition>
<condition>
<custom name="httpd_ssl_enabled" value="true"/>
</condition>
</cluster>
<cluster name="concurrency_none" location="$|concurrency\none\" recursive="true">
<condition>
<concurrency value="none"/>
</condition>
</cluster>
<cluster name="concurrency_scoop" location="$|concurrency\scoop\" recursive="true">
<condition>
<concurrency value="scoop"/>
</condition>
</cluster>
<cluster name="concurrency_thread" location="$|concurrency\thread\" recursive="true">
<condition>
<concurrency value="thread"/>
</condition>
</cluster>
</cluster>
</target>
</system>

View File

@@ -0,0 +1,97 @@
note
description: "[
Interface for the incoming connection handler.
Each incoming socket connection is processed by
an implementation of HTTPD_CONNECTION_HANDLER_I.
Note there are 3 implementations, one for each concurrent mode: none, thread, scoop.
]"
date: "$Date$"
revision: "$Revision$"
deferred class
HTTPD_CONNECTION_HANDLER_I
inherit
HTTPD_DEBUG_FACILITIES
feature {NONE} -- Initialization
frozen make (a_server: like server)
do
server := a_server
factory := separate_factory (a_server)
initialize
end
initialize
deferred
end
feature {NONE} -- Access
factory: separate HTTPD_REQUEST_HANDLER_FACTORY
-- Request handler factory.
server: separate HTTPD_SERVER_I
-- Associated server.
feature {HTTPD_SERVER_I} -- Execution
accept_incoming_connection (a_listening_socket: HTTPD_STREAM_SOCKET)
-- Accept incoming connection from `a_listening_socket'.
deferred
end
shutdown
-- Shutdown server.
deferred
end
wait_for_completion
-- Wait until Current completed any pending task.
--| Used for SCOOP synchronisation.
deferred
end
feature {HTTPD_SERVER} -- Status report
is_shutdown_requested: BOOLEAN
-- Any request to shutdown the server?
deferred
end
feature {NONE} -- Implementation
log (a_message: separate READABLE_STRING_8)
-- Log `a_message'
do
-- FIXME: Concurrency issue on `server'
separate_server_log (server, a_message)
end
separate_factory (a_server: like server): like factory
-- Separate factory from `a_server'.
--| required by SCOOP design.
do
Result := a_server.factory
end
separate_server_log (a_server: like server; a_message: separate READABLE_STRING_8)
-- Concurrent call to `a_server.log (a_message)'.
do
a_server.log (a_message)
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)"
source: "[
Eiffel Software
5949 Hollister Ave., Goleta, CA 93117 USA
Telephone 805-685-1006, Fax 805-685-6869
Website http://www.eiffel.com
Customer support http://support.eiffel.com
]"
end

View File

@@ -0,0 +1,28 @@
note
description: "[
Object used to control (i.e shutdown) the server.
Mostly needed in SCOOP concurrency mode.
]"
date: "$Date$"
revision: "$Revision$"
class
HTTPD_CONTROLLER
feature -- Operation
shutdown
-- Request the associated server to be shutdown.
do
shutdown_requested := True
end
feature -- Status report.
shutdown_requested: BOOLEAN
-- Shutdown requested.
;note
copyright: "2011-2013, Javier Velilla, Jocelyn Fiat and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
end

View File

@@ -0,0 +1,50 @@
note
description: " Routines used for debug logging."
date: "$Date$"
revision: "$Revision$"
deferred class
HTTPD_DEBUG_FACILITIES
feature {NONE} -- Output
dbglog (m: READABLE_STRING_8)
require
not m.ends_with_general ("%N")
local
s: STRING
do
debug ("dbglog")
create s.make (24)
s.append ("[EWF/DBG] <#")
s.append_integer (processor_id_from_object (Current))
s.append ("> ")
s.append (generator)
s.append (create {STRING}.make_filled (' ', (46 - s.count).max (0)))
s.append (" | ")
s.append (m)
s.append ("%N")
print (s)
end
end
feature -- runtime
frozen processor_id_from_object (a_object: separate ANY): INTEGER_32
external
"C inline use %"eif_scoop.h%""
alias
"RTS_PID(eif_access($a_object))"
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)"
source: "[
Eiffel Software
5949 Hollister Ave., Goleta, CA 93117 USA
Telephone 805-685-1006, Fax 805-685-6869
Website http://www.eiffel.com
Customer support http://support.eiffel.com
]"
end

View File

@@ -0,0 +1,26 @@
note
description: "Logging facilities component"
date: "$Date$"
revision: "$Revision$"
deferred class
HTTPD_LOGGER
feature -- Logs
log (a_message: separate READABLE_STRING_8)
-- Log `a_message'
deferred
end
note
copyright: "2011-2014, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[
Eiffel Software
5949 Hollister Ave., Goleta, CA 93117 USA
Telephone 805-685-1006, Fax 805-685-6869
Website http://www.eiffel.com
Customer support http://support.eiffel.com
]"
end

View File

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

View File

@@ -0,0 +1,26 @@
note
description: "Summary description for {HTTPD_REQUEST_HANDLER_FACTORY_I}."
author: ""
date: "$Date$"
revision: "$Revision$"
deferred class
HTTPD_REQUEST_HANDLER_FACTORY_I
feature -- Factory
new_handler: separate HTTPD_REQUEST_HANDLER
deferred
end
note
copyright: "2011-2014, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[
Eiffel Software
5949 Hollister Ave., Goleta, CA 93117 USA
Telephone 805-685-1006, Fax 805-685-6869
Website http://www.eiffel.com
Customer support http://support.eiffel.com
]"
end

View File

@@ -0,0 +1,597 @@
note
description: "HTTPD handler interface processing request."
date: "$Date$"
revision: "$Revision$"
deferred class
HTTPD_REQUEST_HANDLER_I
inherit
HTTPD_DEBUG_FACILITIES
HTTPD_LOGGER_CONSTANTS
HTTPD_SOCKET_FACTORY
feature {NONE} -- Initialization
make (a_request_settings: HTTPD_REQUEST_SETTINGS)
do
reset
-- Import global request settings.
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
max_keep_alive_requests := a_request_settings.max_keep_alive_requests
is_verbose := a_request_settings.is_verbose
verbose_level := a_request_settings.verbose_level
is_secure := a_request_settings.is_secure
end
reset
do
reset_request (False)
reset_error
if attached internal_client_socket as l_sock then
l_sock.cleanup
end
internal_client_socket := Void
end
reset_request (a_is_reusing_connection: BOOLEAN)
-- Reset the request, and `a_is_reusing_connection' says if the peristent connection is
-- still alive.
do
if a_is_reusing_connection then
-- Keep `remote_info' as it stays the same for the successive
-- persistent connections.
else
remote_info := Void
end
version := Void
-- FIXME: optimize to just wipe_out if needed
create method.make_empty
create uri.make_empty
create request_header.make_empty
create request_header_map.make (10)
is_persistent_connection_requested := False
end
feature -- Status report
is_connected: BOOLEAN
-- Is handler connected to incoming request via `client_socket'?
do
Result := client_socket.descriptor_available
end
feature -- Access
internal_client_socket: detachable HTTPD_STREAM_SOCKET
client_socket: HTTPD_STREAM_SOCKET
local
s: like internal_client_socket
do
s := internal_client_socket
if s = Void then
s := new_client_socket (is_secure)
internal_client_socket := s
end
Result := s
end
request_header: STRING
-- Header' source
request_header_map: HASH_TABLE [STRING, STRING]
-- Contains key:value of the header
method: STRING
-- http verb
uri: STRING
-- http endpoint
version: detachable STRING
-- http_version
--| unused for now
remote_info: detachable TUPLE [addr: STRING; hostname: STRING; port: INTEGER]
-- Information related to remote client
is_persistent_connection_requested: BOOLEAN
-- Persistent connection requested?
-- either has "Connection: keep-alive" header,
-- or is HTTP/1.1 and no header "Connection: close".
is_http_version_1_0: BOOLEAN
do
Result := not attached version as v or else v.same_string ("HTTP/1.0")
end
is_http_version_1_1: BOOLEAN
do
Result := not attached version as v or else v.same_string ("HTTP/1.1")
end
is_http_version_2: BOOLEAN
do
Result := not attached version as v or else v.same_string ("HTTP/2.0")
end
feature -- Settings
is_verbose: BOOLEAN
-- Output messages?
verbose_level: INTEGER
-- Output verbosity.
is_secure: BOOLEAN
-- Is secure socket?
-- i.e: SSL?
is_persistent_connection_supported: BOOLEAN
-- Is persistent connection supported?
do
Result := {HTTPD_SERVER}.is_persistent_connection_supported and then max_keep_alive_requests > 0
end
is_next_persistent_connection_supported: BOOLEAN
-- Is next persistent connection supported?
-- note: it is relevant only if `is_persistent_connection_supported' is True.
timeout: INTEGER -- seconds
-- Amount of seconds that the server waits for receipts and transmissions during communications.
socket_recv_timeout: INTEGER -- seconds
-- Amount of seconds that the server waits for receiving data on socket during communications.
max_keep_alive_requests: INTEGER
-- Maximum number of requests allowed per persistent connection.
keep_alive_timeout: INTEGER -- seconds
-- Number of seconds for persistent connection timeout.
feature -- Status report
has_error: BOOLEAN
-- Error occurred during `get_request_header'
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)
-- Set `is_verbose' with `b'.
do
is_verbose := b
ensure
is_verbose_set: is_verbose = b
end
feature -- Execution
safe_execute
-- Execute accepted incoming connection as request.
local
retried: BOOLEAN
do
if retried then
release
else
if
not has_error and then
is_connected
then
execute
end
release
end
rescue
retried := True
retry
end
execute
require
is_connected: is_connected
local
l_socket: like client_socket
l_remote_info: detachable like remote_info
l_exit: BOOLEAN
n,m: INTEGER
do
l_socket := client_socket
-- Compute remote info once for the persistent connection.
create l_remote_info
if attached l_socket.peer_address as l_addr then
l_remote_info.addr := l_addr.host_address.host_address
l_remote_info.hostname := l_addr.host_address.host_name
l_remote_info.port := l_addr.port
end
remote_info := l_remote_info
check
socket_attached: l_socket /= Void
socket_valid: l_socket.is_open_read and then l_socket.is_open_write
end
from
-- Process persistent connection as long the socket is not closed.
n := 0
m := max_keep_alive_requests
is_next_persistent_connection_supported := True
until
l_exit
loop
n := n + 1
if n >= m then
is_next_persistent_connection_supported := False
elseif n > 1 and is_verbose then
log ("Reuse connection (" + n.out + ")", information_level)
end
-- FIXME: it seems to be called one more time, mostly to see this is done.
execute_request (n > 1)
l_exit := not is_persistent_connection_supported
or not is_next_persistent_connection_supported -- related to `max_keep_alive_requests'
or not is_persistent_connection_requested
or has_error or l_socket.is_closed or not l_socket.is_open_read
reset_request (not l_exit)
end
if l_exit and has_error and not l_socket.is_closed then
l_socket.close
end
end
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
is_connected: is_connected
reuse_connection_when_possible: a_is_reusing_connection implies is_persistent_connection_supported
no_error: not has_error
remote_info_set: remote_info /= Void
local
l_socket: like client_socket
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
socket_valid: l_socket.is_open_read and then l_socket.is_open_write
end
if l_socket.is_closed then
debug ("dbglog")
dbglog ("execute_request {socket is Closed!}")
end
else
debug ("dbglog")
dbglog ("execute_request socket=" + l_socket.descriptor.out + " ENTER")
end
-- Try to get request header.
-- If the request is reusing persistent connection, use `keep_alive_timeout',
-- otherwise `socket_recv_timeout'.
get_request_header (l_socket, a_is_reusing_connection)
if has_error then
if a_is_reusing_connection and then request_header.is_empty then
-- Close persistent connection, since no new connection occurred in the delay `keep_alive_timeout'.
debug ("dbglog")
dbglog ("execute_request socket=" + l_socket.descriptor.out + "} close persistent connection.")
end
else
if is_verbose then
log (request_header + "%NWARNING: invalid HTTP incoming request", warning_level)
end
process_bad_request (l_socket)
end
is_persistent_connection_requested := False
else
if is_verbose then
log (request_header, information_level)
end
process_request (l_socket)
end
debug ("dbglog")
dbglog ("execute_request {" + l_socket.descriptor.out + "} LEAVE")
end
end
end
release
do
reset
end
feature -- Request processing
process_request (a_socket: HTTPD_STREAM_SOCKET)
-- Process request on socket `a_socket'.
require
no_error: not has_error
a_uri_attached: uri /= Void
a_method_attached: method /= Void
a_header_map_attached: request_header_map /= Void
a_header_text_attached: request_header /= Void
a_socket_attached: a_socket /= Void
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
get_request_header (a_socket: HTTPD_STREAM_SOCKET; a_is_reusing_connection: BOOLEAN)
-- Analyze message extracted from `a_socket' as HTTP request.
-- If `a_is_reusing_connection' is True, then first use
-- Note: it reads from socket.
-- Note: it updates `request_header' and `request_header_map', and eventually `is_persistent_connection_requested'.
require
input_readable: a_socket /= Void and then a_socket.is_open_read
local
end_of_stream: BOOLEAN
pos, n: INTEGER
line: detachable STRING
k, val: STRING
txt: STRING
l_is_verbose: BOOLEAN
do
create txt.make (64)
request_header := txt
l_is_verbose := is_verbose
if
not has_error and then
a_socket.readable
then
if a_is_reusing_connection then
a_socket.set_recv_timeout (keep_alive_timeout) -- in seconds!
else
a_socket.set_recv_timeout (socket_recv_timeout) -- FIXME: return a 408 Request Timeout response ..
end
if
attached next_line (a_socket) as l_request_line and then
not l_request_line.is_empty and then
not has_error
then
txt.append (l_request_line)
txt.append_character ('%N')
analyze_request_line (l_request_line)
if not has_error then
if a_is_reusing_connection then
-- Restore normal recv timeout!
a_socket.set_recv_timeout (socket_recv_timeout) -- FIXME: return a 408 Request Timeout response ..
end
from
line := next_line (a_socket)
until
line = Void or end_of_stream or has_error
loop
n := line.count
debug ("ew_standalone")
if l_is_verbose then
log (line, debug_level)
end
end
pos := line.index_of (':', 1)
if pos > 0 then
k := line.substring (1, pos - 1)
if line [pos + 1].is_space then
pos := pos + 1
end
if line [n] = '%R' then
n := n - 1
end
val := line.substring (pos + 1, n)
request_header_map.put (val, k)
end
txt.append (line)
txt.append_character ('%N')
if line.is_empty or else line [1] = '%R' then
end_of_stream := True
else
line := next_line (a_socket)
end
end
-- Except for HTTP/1.0, persistent connection is the default.
is_persistent_connection_requested := True
if is_http_version_1_0 then
is_persistent_connection_requested := attached request_header_map.item ("Connection") as l_connection and then
l_connection.is_case_insensitive_equal_general ("keep-alive")
else
-- By default HTTP:1/1 support persistent connection.
if attached request_header_map.item ("Connection") as l_connection then
if l_connection.is_case_insensitive_equal_general ("close") then
is_persistent_connection_requested := False
end
else
is_persistent_connection_requested := True
end
end
end
else
report_error ("Bad header line (empty)")
end
else
report_error ("Socket is not readable")
end
end
analyze_request_line (line: STRING)
-- Analyze `line' as a HTTP request line.
-- note: may update `has_error'.
require
valid_line: line /= Void and then not line.is_empty
local
n, pos, next_pos: INTEGER
do
debug ("ew_standalone")
if is_verbose then
log ("%N## Parse HTTP request line ##", debug_level)
log (line, debug_level)
end
end
pos := line.index_of (' ', 1)
method := line.substring (1, pos - 1)
next_pos := line.index_of (' ', pos + 1)
uri := line.substring (pos + 1, next_pos - 1)
n := line.count
if line[n] = '%R' then
n := n - 1
end
version := line.substring (next_pos + 1, n)
if method.is_empty then
report_error ("Missing request method data")
end
end
next_line (a_socket: HTTPD_STREAM_SOCKET): detachable STRING
-- Next line fetched from `a_socket' is available.
-- note: may update `has_error'.
require
not_has_error: not has_error
is_readable: a_socket.is_open_read
local
retried: BOOLEAN
do
if retried then
report_error ("Rescue in next_line")
a_socket.close
Result := Void
elseif a_socket.readable then
a_socket.read_line_noexception
Result := a_socket.last_string
-- Do no check `was_error' before socket operation,
-- otherwise it may be False, due to error during other socket operation in same thread.
if a_socket.was_error then
report_error ("Socket error")
if is_verbose then
log (request_header +"%N" + Result + "%N## was_error=False! ##", debug_level)
end
end
else
-- Error with socket...
report_error ("Socket error: not readable")
if is_verbose then
log (request_header + "%N## Socket is not readable! ##", debug_level)
end
end
rescue
-- In case of network error exception (as EiffelNet reports error raising exception)
retried := True
retry
end
feature -- Output
logger: detachable HTTPD_LOGGER
set_logger (a_logger: like logger)
-- Set `logger' with `a_logger'.
do
logger := a_logger
ensure
logger_set: logger = a_logger
end
log (m: STRING; a_level: INTEGER)
-- Log message `m'.
require
is_verbose: is_verbose
do
if is_verbose and (verbose_level & a_level) = a_level then
if attached logger as l_logger then
l_logger.log (m)
else
io.put_string (m + "%N")
end
end
end
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
-- Result := a_socket.has_incoming_data
Result := True
end
invariant
request_header_attached: request_header /= Void
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)"
source: "[
Eiffel Software
5949 Hollister Ave., Goleta, CA 93117 USA
Telephone 805-685-1006, Fax 805-685-6869
Website http://www.eiffel.com
Customer support http://support.eiffel.com
]"
end

View File

@@ -0,0 +1,377 @@
note
description: "HTTPD server interface"
date: "$Date$"
revision: "$Revision$"
deferred class
HTTPD_SERVER_I
inherit
HTTPD_DEBUG_FACILITIES
HTTPD_LOGGER
feature {NONE} -- Initialization
make (a_factory: like factory)
-- Create current httpd server with `a_factory' of connection handlers.
-- `a_factory': connection handler builder
require
a_factory_is_separated: {PLATFORM}.is_scoop_capable implies not attached {HTTPD_REQUEST_HANDLER_FACTORY} a_factory
do
make_configured (create {like configuration}.make, a_factory)
end
make_configured (a_cfg: like configuration; a_factory: like factory)
-- `a_cfg': server configuration
-- `a_factory': connection handler builder
do
configuration := a_cfg
factory := a_factory
build_controller
initialize
end
build_controller
-- Build `controller'.
do
create <NONE> controller
end
initialize
-- Initialize Current server.
do
is_shutdown_requested := False
end
feature -- Access
is_verbose: BOOLEAN
-- Is verbose for output messages.
verbose_level: INTEGER
-- Verbosity of output.
configuration: HTTPD_CONFIGURATION
-- Associated server configuration.
controller: separate HTTPD_CONTROLLER
factory: separate HTTPD_REQUEST_HANDLER_FACTORY
is_persistent_connection_supported: BOOLEAN = True
-- Is persistent connection supported?
feature -- Callbacks
observer: detachable separate HTTPD_SERVER_OBSERVER
set_observer (obs: like observer)
-- Set `observer' to `obs'.
do
observer := obs
end
feature -- Access: listening
port: INTEGER
-- Effective listening port.
--| If 0 then it is not launched successfully!
feature -- Status: listening
is_launched: BOOLEAN
-- Server launched and listening on `port'
is_terminated: BOOLEAN
-- Is terminated?
is_shutdown_requested: BOOLEAN
-- Set true to stop accept loop
feature {NONE} -- Access: server
request_counter: INTEGER
-- request counter, incremented for each new incoming connection.
feature -- Execution
launch
do
apply_configuration
is_terminated := False
if is_verbose then
log ("%N%NStarting Web Application Server ...")
log (" - port = " + configuration.http_server_port.out)
log (" - max_tcp_clients = " + configuration.max_tcp_clients.out)
log (" - max_concurrent_connections = " + configuration.max_concurrent_connections.out)
log (" - socket_timeout = " + configuration.socket_timeout.out + " seconds")
log (" - 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
end
is_shutdown_requested := False
listen
is_terminated := True
on_terminated
end
shutdown_server
do
debug ("dbglog")
dbglog ("Shutdown requested")
end
is_shutdown_requested := True
controller_shutdown (controller)
end
controller_shutdown (ctl: attached like controller)
do
ctl.shutdown
end
feature -- Listening
listen
-- <Precursor>
-- Creates a socket and connects to the http server.
-- `a_server': The main server object
local
l_listening_socket: detachable HTTPD_STREAM_SOCKET
l_http_port: INTEGER
l_connection_handler: HTTPD_CONNECTION_HANDLER
do
is_terminated := False
is_launched := False
port := 0
is_shutdown_requested := False
l_http_port := configuration.http_server_port
if
attached configuration.http_server_name as l_servername and then
attached (create {INET_ADDRESS_FACTORY}).create_from_name (l_servername) as l_addr
then
l_listening_socket := new_listening_socket (l_addr, l_http_port)
else
l_listening_socket := new_listening_socket (Void, l_http_port)
end
if not l_listening_socket.is_bound then
if is_verbose then
log ("Socket could not be bound on port " + l_http_port.out + " !")
end
else
l_http_port := l_listening_socket.port
create l_connection_handler.make (Current)
from
l_listening_socket.listen (configuration.max_tcp_clients)
if is_verbose then
if configuration.is_secure then
log ("%NListening on port " + l_http_port.out +" : https://localhost:" + l_http_port.out + "/")
else
log ("%NListening on port " + l_http_port.out +" : http://localhost:" + l_http_port.out + "/")
end
end
on_launched (l_http_port)
until
is_shutdown_requested
loop
request_counter := request_counter + 1
if is_verbose then
log ("#" + request_counter.out + "# Waiting connection...(listening socket:" + l_listening_socket.descriptor.out + ")")
end
debug ("dbglog")
dbglog (generator + ".before process_waiting_incoming_connection")
end
l_connection_handler.accept_incoming_connection (l_listening_socket)
debug ("dbglog")
dbglog (generator + ".after process_waiting_incoming_connection")
end
update_is_shutdown_requested (l_connection_handler)
end
wait_for_connection_handler_completion (l_connection_handler)
l_listening_socket.cleanup
check
socket_is_closed: l_listening_socket.is_closed
end
end
if is_launched then
on_stopped
end
if is_verbose then
log ("HTTP Connection Server ends.")
end
rescue
log ("HTTP Connection Server shutdown due to exception. Please relaunch manually.")
if l_listening_socket /= Void then
l_listening_socket.cleanup
check
listening_socket_is_closed: l_listening_socket.is_closed
end
end
if is_launched then
on_stopped
end
is_shutdown_requested := True
retry
end
feature {NONE} -- Factory
new_listening_socket (a_addr: detachable INET_ADDRESS; a_http_port: INTEGER): HTTPD_STREAM_SOCKET
do
if a_addr /= Void then
create Result.make_server_by_address_and_port (a_addr, a_http_port)
else
create Result.make_server_by_port (a_http_port)
end
end
feature {NONE} -- Helpers
wait_for_connection_handler_completion (h: HTTPD_CONNECTION_HANDLER)
do
h.wait_for_completion
debug ("dbglog")
dbglog ("Shutdown ready from connection_handler point of view")
end
end
update_is_shutdown_requested (a_connection_handler: HTTPD_CONNECTION_HANDLER)
do
is_shutdown_requested := is_shutdown_requested or shutdown_requested (controller)
if is_shutdown_requested then
a_connection_handler.shutdown
end
end
shutdown_requested (a_controller: separate HTTPD_CONTROLLER): BOOLEAN
-- Shutdown requested on concurrent `a_controller'?
do
Result := a_controller.shutdown_requested
end
feature -- Event
on_launched (a_port: INTEGER)
-- Server launched using port `a_port'
require
not_launched: not is_launched
do
is_launched := True
port := a_port
if attached observer as obs then
observer_on_launched (obs, a_port)
end
ensure
is_launched: is_launched
end
on_stopped
-- Server stopped
require
is_launched: is_launched
do
if attached observer as obs then
observer_on_stopped (obs)
end
end
on_terminated
-- Server terminated
require
is_terminated
do
if is_terminated and is_verbose then
log ("%N%NTerminating Web Application Server (port="+ port.out +"):%N")
end
if attached output as o then
o.flush
o.close
end
if attached observer as obs then
observer_on_terminated (obs)
end
end
feature {NONE} -- Separate event
observer_on_launched (obs: attached like observer; a_port: INTEGER)
do
obs.on_launched (a_port)
end
observer_on_stopped (obs: attached like observer)
do
obs.on_stopped
end
observer_on_terminated (obs: attached like observer)
do
obs.on_terminated
end
feature -- Configuration change
apply_configuration
require
is_not_launched: not is_launched
do
is_verbose := configuration.is_verbose
verbose_level := configuration.verbose_level
end
feature -- Output
output: detachable FILE
set_log_output (f: FILE)
-- Set `output' to `f'.
do
output := f
ensure
output_set: output = f
end
log (a_message: separate READABLE_STRING_8)
-- Log `a_message'.
local
m: STRING
do
create m.make_from_separate (a_message)
if attached output as o then
o.put_string (m)
o.put_new_line
else
io.error.put_string (m)
io.error.put_new_line
end
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)"
source: "[
Eiffel Software
5949 Hollister Ave., Goleta, CA 93117 USA
Telephone 805-685-1006, Fax 805-685-6869
Website http://www.eiffel.com
Customer support http://support.eiffel.com
]"
end

View File

@@ -0,0 +1,27 @@
note
description: "Summary description for {HTTPD_SERVER_OBSERVER}."
date: "$Date$"
revision: "$Revision$"
deferred class
HTTPD_SERVER_OBSERVER
feature -- Event
on_launched (a_port: INTEGER)
-- Associated server launched listening on port `a_port'.
deferred
end
on_stopped
-- Associated server stopped.
--| the server may restart itself after being rescued.
deferred
end
on_terminated
-- Associated server terminated.
deferred
end
end

View File

@@ -0,0 +1,243 @@
note
description: "Summary description for {HTTPD_STREAM_SOCKET}."
date: "$Date$"
revision: "$Revision$"
class
HTTPD_STREAM_SOCKET
inherit
NETWORK_STREAM_SOCKET
HTTPD_STREAM_SOCKET_EXT
create
make, make_empty,
make_client_by_port, make_client_by_address_and_port,
make_server_by_port, make_server_by_address_and_port, make_loopback_server_by_port
create {NETWORK_STREAM_SOCKET}
make_from_descriptor_and_address
feature -- Input
read_character_noexception
-- Read a new character.
-- Make result available in `last_character'.
-- No exception raised!
do
read_to_managed_pointer_noexception (socket_buffer, 0, character_8_bytes)
if bytes_read /= character_8_bytes then
socket_error := "Peer closed connection"
else
last_character := socket_buffer.read_character (0)
socket_error := Void
end
end
read_stream_noexception (nb_char: INTEGER)
-- Read a string of at most `nb_char' characters.
-- Make result available in `last_string'.
local
ext: C_STRING
return_val: INTEGER
do
create ext.make_empty (nb_char + 1)
return_val := c_read_stream_noexception (descriptor, nb_char, ext.item)
bytes_read := return_val
if return_val >= 0 then
ext.set_count (return_val)
last_string := ext.substring (1, return_val)
else
socket_error := "Peer error [0x" + return_val.to_hex_string + "]"
last_string.wipe_out
end
end
read_to_managed_pointer_noexception (p: MANAGED_POINTER; start_pos, nb_bytes: INTEGER)
-- Read at most `nb_bytes' bound bytes and make result
-- available in `p' at position `start_pos'.
-- No exception raised!
do
read_into_pointer_noexception (p.item, start_pos, nb_bytes)
end
read_line_noexception
-- Read a line of characters (ended by a new_line).
-- No exception raised!
local
l_last_string: like last_string
do
create l_last_string.make (512)
read_character_noexception
from
until
last_character = '%N' or else was_error
loop
l_last_string.extend (last_character)
read_character_noexception
end
last_string := l_last_string
end
peek_stream_noexception (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.
-- No exception raised!
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 := c_recv_noexception (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
feature {NONE} -- Input
read_into_pointer_noexception (p: POINTER; start_pos, nb_bytes: INTEGER_32)
-- Read at most `nb_bytes' bound bytes and make result
-- available in `p' at position `start_pos'.
-- No exception raised!
require
p_not_void: p /= default_pointer
nb_bytes_non_negative: nb_bytes >= 0
is_readable: readable
local
l_read: INTEGER_32
l_last_read: INTEGER_32
do
from
l_last_read := 1
until
l_read = nb_bytes or l_last_read <= 0
loop
l_last_read := c_read_stream_noexception (descriptor, nb_bytes - l_read, p + start_pos + l_read)
if l_last_read >= 0 then
l_read := l_read + l_last_read
end
end
bytes_read := l_read
ensure
bytes_read_updated: 0 <= bytes_read and bytes_read <= nb_bytes
end
feature -- Output
bytes_sent: INTEGER
-- Last number of bytes sent by `put_managed_pointer_noexception' (i.e `put_pointer_content_noexception').
put_managed_pointer_noexception (p: MANAGED_POINTER; start_pos, nb_bytes: INTEGER_32)
-- Put data of length `nb_bytes' pointed by `start_pos' index in `p' at
-- current position.
-- Update `bytes_sent'.
-- No exception raised!
require
p_not_void: p /= Void
p_large_enough: p.count >= nb_bytes + start_pos
nb_bytes_non_negative: nb_bytes >= 0
extendible: extendible
do
put_pointer_content_noexception (p.item, start_pos, nb_bytes)
end
put_pointer_content_noexception (a_pointer: POINTER; a_offset, a_byte_count: INTEGER)
-- Write `a_byte_count' bytes to the socket.
-- The data is taken from the memory area pointed to by `a_pointer', at offset `a_offset'.
-- Update `bytes_sent'.
-- No exception raised!
require
pointer_not_void: a_pointer /= default_pointer
byte_count_non_negative: a_byte_count >= 0
extendible: extendible
local
l_sent: INTEGER_32
l_last_sent: INTEGER_32
do
from
l_last_sent := 1
until
l_sent = a_byte_count or l_last_sent <= 0
loop
l_last_sent := c_put_stream_noexception (descriptor, a_pointer + a_offset + l_sent, a_byte_count - l_sent)
if l_last_sent >= 0 then
l_sent := l_sent + l_last_sent
elseif l_sent < a_byte_count then
socket_error := "No all bytes sent!"
end
end
bytes_sent := l_sent
ensure
bytes_sent_updated: not was_error implies (0 <= bytes_sent and bytes_sent <= a_byte_count)
end
put_character_noexception (c: CHARACTER)
-- Write character `c' to socket.
do
socket_buffer.put_character (c, 0)
put_managed_pointer_noexception (socket_buffer, 0, character_8_bytes)
end
put_readable_string_8_noexception (s: READABLE_STRING_8)
-- Write readable string `s' to socket.
-- No exception raised!
local
ext: C_STRING
do
create ext.make (s)
put_managed_pointer_noexception (ext.managed_data, 0, s.count)
end
put_readable_string_8 (s: READABLE_STRING_8)
-- Write readable string `s' to socket.
local
ext: C_STRING
do
create ext.make (s)
put_managed_pointer (ext.managed_data, 0, s.count)
end
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_noexception (1)
Result := last_string.count = 1
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)"
source: "[
Eiffel Software
5949 Hollister Ave., Goleta, CA 93117 USA
Telephone 805-685-1006, Fax 805-685-6869
Website http://www.eiffel.com
Customer support http://support.eiffel.com
]"
end

View File

@@ -0,0 +1,13 @@
note
description: "[
Extension to HTTPD_STREAM_SOCKET to support backward compatibility.
TO BE REMOVED IN THE FUTURE, WHEN 16.05 IS OLD.
]"
deferred class
HTTPD_STREAM_SOCKET_EXT
feature {NONE} -- No-Exception network operation
end

View File

@@ -0,0 +1,149 @@
note
description: "SSL tcp stream socket."
date: "$Date$"
revision: "$Revision$"
class
HTTPD_STREAM_SSL_SOCKET
inherit
HTTPD_STREAM_SOCKET
undefine
make_empty, make_from_descriptor_and_address,
error_number,
readstream, read_stream,
read_into_pointer,
read_to_managed_pointer,
put_pointer_content,
write, send,
close_socket,
connect, shutdown,
do_accept
redefine
put_managed_pointer,
read_stream_noexception,
read_into_pointer_noexception,
put_pointer_content_noexception
end
SSL_NETWORK_STREAM_SOCKET
redefine
put_managed_pointer -- Redefine to allow support of compiler before 16.11.
end
HTTPD_STREAM_SSL_SOCKET_EXT
create
make, make_empty,
make_client_by_port, make_client_by_address_and_port,
make_server_by_port, make_server_by_address_and_port, make_loopback_server_by_port
create {SSL_NETWORK_STREAM_SOCKET}
make_from_descriptor_and_address
feature -- Input
read_stream_noexception (nb_char: INTEGER)
-- Read a string of at most `nb_char' characters.
-- Make result available in `last_string'.
local
ext: C_STRING
return_val: INTEGER
do
if
attached context as l_context and then
attached l_context.last_ssl as l_ssl
then
create ext.make_empty (nb_char + 1)
return_val := l_ssl.read (ext.item , nb_char)
bytes_read := return_val
if return_val >= 0 then
ext.set_count (return_val)
last_string := ext.substring (1, return_val)
else
socket_error := "Peer error [0x" + return_val.to_hex_string + "]"
last_string.wipe_out
end
else
check has_context: False end
end
end
feature {NONE} -- Input
read_into_pointer_noexception (p: POINTER; start_pos, nb_bytes: INTEGER_32)
-- Read at most `nb_bytes' bound bytes and make result
-- available in `p' at position `start_pos'.
-- No exception raised!
local
l_read: INTEGER
l_last_read: INTEGER
do
if
attached context as l_context and then
attached l_context.last_ssl as l_ssl
then
from
l_last_read := 1
until
l_read = nb_bytes or l_last_read <= 0
loop
l_last_read := l_ssl.read (p + start_pos + l_read, nb_bytes - l_read)
if l_last_read >= 0 then
l_read := l_read + l_last_read
end
end
bytes_read := l_read
else
check has_context: False end
end
end
feature -- Output
put_managed_pointer (p: MANAGED_POINTER; start_pos, nb_bytes: INTEGER)
-- Put data of length `nb_bytes' pointed by `start_pos' index in `p' at
-- current position.
do
Precursor {HTTPD_STREAM_SOCKET} (p, start_pos, nb_bytes)
end
put_pointer_content_noexception (a_pointer: POINTER; a_offset, a_byte_count: INTEGER)
-- Write `a_byte_count' bytes to the socket.
-- The data is taken from the memory area pointed to by `a_pointer', at offset `a_offset'.
-- Update `bytes_sent'.
-- No exception raised!
local
l_bytes_sent: INTEGER
do
if
attached context as l_context and then
attached l_context.last_ssl as l_ssl
then
l_bytes_sent := ssl_write (l_ssl, a_pointer + a_offset, a_byte_count)
if l_bytes_sent < a_byte_count then
socket_error := "No all bytes sent!"
end
bytes_sent := l_bytes_sent
else
check has_last_ssl: False end
end
end
feature -- Element change
set_certificate_filenames (a_crt_filename, a_key_filename: detachable READABLE_STRING_GENERAL)
do
if a_crt_filename /= Void then
set_certificate_file_name (a_crt_filename)
end
if a_key_filename /= Void then
set_key_file_name (a_key_filename)
end
end
note
copyright: "2011-2013, Javier Velilla, Jocelyn Fiat and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
end

View File

@@ -0,0 +1,23 @@
note
description: "[
Extension to HTTPD_STREAM_SOCKET to support backward compatibility.
TO BE REMOVED IN THE FUTURE, WHEN 16.05 IS OLD.
]"
deferred class
HTTPD_STREAM_SSL_SOCKET_EXT
feature {NONE} -- SSL bridge
ssl_write (a_ssl: SSL; a_pointer: POINTER; a_byte_count: INTEGER): INTEGER
do
Result := a_ssl.write (a_pointer, a_byte_count)
if a_ssl.was_error then
if Result >= 0 then
Result := -1
end
end
end
end

View File

@@ -0,0 +1,160 @@
note
description: "[
Until 16.05, the EiffelNet socket interface DOES NOT have
- make_server_by_address_and_port
- recv_timeout
- send_timeout.
TO BE REMOVED IN THE FUTURE, WHEN 16.05 IS OLD.
]"
deferred class
HTTPD_STREAM_SOCKET_EXT
inherit
PLATFORM
feature -- Initialization
make
deferred
end
make_server_by_address_and_port (a_address: INET_ADDRESS; a_port: INTEGER)
-- Create server socket on `a_address' and `a_port'.
require
valid_port: a_port >= 0
do
make
set_address (create {like address_type}.make_from_address_and_port (a_address, a_port))
bind
end
feature -- Basic operation
bind
deferred
end
feature -- Access
set_address (addr: detachable like address_type)
deferred
end
address_type: NETWORK_SOCKET_ADDRESS
deferred
end
descriptor: INTEGER
-- Socket descriptor of current socket
deferred
end
feature -- Socket Recv and Send timeout.
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
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 use %"ew_httpd_net.h%""
alias
"[
#ifdef SO_RCVTIMEO
int flag = SO_RCVTIMEO;
#else
int flag = 0x1006;
#endif
#ifdef EIF_WINDOWS
int arg = (int) 1000 * $a_timeout_seconds; /* Timeout in milliseconds */
setsockopt((int) $a_fd, (int) $a_level, flag, (char *) &arg, sizeof(arg));
#else
struct timeval tv;
tv.tv_sec = $a_timeout_seconds; /* Timeout in seconds */
tv.tv_usec = 0;
setsockopt((int) $a_fd, (int) $a_level, flag, (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 use %"ew_httpd_net.h%""
alias
"[
#ifdef SO_RCVTIMEO
int flag = SO_SNDTIMEO;
#else
int flag = 0x1005;
#endif
#ifdef EIF_WINDOWS
int arg = (int) 1000 * $a_timeout_seconds; /* Timeout in milliseconds */
setsockopt((int) $a_fd, (int) $a_level, flag, (char *) &arg, sizeof(arg));
#else
struct timeval tv;
tv.tv_sec = $a_timeout_seconds; /* Timeout in seconds */
tv.tv_usec = 0;
setsockopt((int) $a_fd, (int) $a_level, flag, (struct timeval *)&tv, sizeof(struct timeval));
#endif
]"
end
feature {NONE} -- No-Exception network operation
c_recv_noexception (a_fd: INTEGER; buf: POINTER; len: INTEGER; flags: INTEGER): INTEGER
-- External routine to read a `len' number of characters
-- into buffer `buf' from socket `a_fd' with options `flags'.
external
"C inline use %"ew_httpd_net.h%""
alias
"[
recv((int) $a_fd, (char *) $buf, (int) $len, (int) $flags)
]"
end
c_read_stream_noexception (a_fd: INTEGER; len: INTEGER; buf: POINTER): INTEGER
-- External routine to read a `len' number of characters
-- into buffer `buf' from socket `a_fd'.
do
Result := c_recv_noexception (a_fd, buf, len, 0)
end
c_put_stream_noexception (a_fd: INTEGER; buf: POINTER; len: INTEGER): INTEGER
-- External routine to write stream pointed by `s' of
-- length `length' to socket `fd'.
-- Note: does not raise exception on error, but return error value as Result.
external
"C inline use %"ew_httpd_net.h%""
alias
"[
send((int) $a_fd, (char *) $buf, (int) $len, (int) 0)
]"
end
end

View File

@@ -0,0 +1,41 @@
note
description: "[
Extension to HTTPD_STREAM_SOCKET to support backward compatibility.
TO BE REMOVED IN THE FUTURE, WHEN 16.05 IS OLD.
]"
deferred class
HTTPD_STREAM_SSL_SOCKET_EXT
feature {NONE} -- SSL bridge
ssl_write (a_ssl: SSL; a_pointer: POINTER; a_byte_count: INTEGER): INTEGER
do
-- In delivery until 16.05
-- SSL.write does not return any value!
-- So let's use `c_ssl_write' from Current class
-- instead of:
-- a_ssl.write (a_pointer, a_byte_count)
Result := c_ssl_write (a_ssl.ptr, a_pointer, a_byte_count)
if a_ssl.was_error then
-- Until 16.05, there is no error check for `SSL.write'
-- so nothing can be done here.
if Result >= 0 then
Result := -1
end
end
end
c_ssl_write (an_ssl_ptr: POINTER; buffer: POINTER; nb_bytes: INTEGER_32): INTEGER_32
-- External call to SSL_write
-- (export status {NONE})
external
"C use %"eif_openssl.h%""
alias
"SSL_write"
end
end

View File

@@ -0,0 +1,66 @@
note
description: "Standalone server configuration (ssl NOT supported)."
date: "$Date$"
revision: "$Revision$"
class
HTTPD_CONFIGURATION
inherit
HTTPD_CONFIGURATION_I
create
make
feature -- Status
Server_details: STRING_8 = "Server: Standalone Eiffel Server"
has_ssl_support: BOOLEAN = False
-- Precursor
feature -- SSL Helpers
set_ssl_protocol_to_ssl_2_or_3
-- Set `ssl_protocol' with `Ssl_23'.
do
-- Ignored
end
set_ssl_protocol_to_tls_1_0
-- Set `ssl_protocol' with `Tls_1_0'.
do
-- Ignored
end
set_ssl_protocol_to_tls_1_1
-- Set `ssl_protocol' with `Tls_1_1'.
do
-- Ignored
end
set_ssl_protocol_to_tls_1_2
-- Set `ssl_protocol' with `Tls_1_2'.
do
-- Ignored
end
set_ssl_protocol_to_dtls_1_0
-- Set `ssl_protocol' with `Dtls_1_0'.
do
-- Ignored
end
note
copyright: "2011-2014, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[
Eiffel Software
5949 Hollister Ave., Goleta, CA 93117 USA
Telephone 805-685-1006, Fax 805-685-6869
Website http://www.eiffel.com
Customer support http://support.eiffel.com
]"
end

View File

@@ -0,0 +1,27 @@
note
description: "[
httpd server
]"
date: "$Date$"
revision: "$Revision$"
class
HTTPD_SERVER
inherit
HTTPD_SERVER_I
create
make
note
copyright: "2011-2014, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[
Eiffel Software
5949 Hollister Ave., Goleta, CA 93117 USA
Telephone 805-685-1006, Fax 805-685-6869
Website http://www.eiffel.com
Customer support http://support.eiffel.com
]"
end

View File

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

View File

@@ -0,0 +1,52 @@
/*
indexing
description: "Functions used by the EiffelWeb httpd networking classes. "
copyright: "Copyright (c) 2011-2016, Jocelyn Fiat, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[
Eiffel Software
5949 Hollister Ave., Goleta, CA 93117 USA
Telephone 805-685-1006, Fax 805-685-6869
Website http://www.eiffel.com
Customer support http://support.eiffel.com
]"
*/
#ifndef _ew_httpd_net_h_
#define _ew_httpd_net_h_
#include "eif_config.h"
#ifdef EIF_WINDOWS
# ifndef _WINSOCKAPI_
# define FD_SETSIZE 256
# include <winsock2.h>
# include <Ws2tcpip.h>
# include <stdio.h>
# endif
#else /* unix-specific */
# include <sys/socket.h>
# include <unistd.h>
#endif
#ifdef __cplusplus
extern "C" {
#endif
/* extern declarations ... */
#ifdef EIF_WINDOWS
extern int setsockopt(int, int, int, char*, int);
extern int recv(int, char *, int, int);
extern int send(int, char *, int, int);
#else
extern int setsockopt(int, int, int, const void*, socklen_t);
extern ssize_t recv(int, void *, size_t, int);
extern ssize_t send(int, const void *, size_t, int);
#endif
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -0,0 +1,79 @@
note
description: "Standalone server configuration (ssl supported)."
date: "$Date$"
revision: "$Revision$"
class
HTTPD_CONFIGURATION
inherit
HTTPD_CONFIGURATION_I
redefine
make
end
create
make
feature {NONE} -- Initialization
make
-- Create a new instance and set ssl protocol to tls_1_2.
do
Precursor
set_ssl_protocol_to_tls_1_2
ensure then
ssl_protocol_set: ssl_protocol = {SSL_PROTOCOL}.tls_1_2
end
feature -- Access
Server_details: STRING_8 = "Server: Standalone Eiffel Server (https)"
has_ssl_support: BOOLEAN = True
-- Precursor
feature -- SSL Helpers
set_ssl_protocol_to_ssl_2_or_3
-- Set `ssl_protocol' with `Ssl_23'.
do
set_ssl_protocol ({SSL_PROTOCOL}.Ssl_23)
end
set_ssl_protocol_to_tls_1_0
-- Set `ssl_protocol' with `Tls_1_0'.
do
set_ssl_protocol ({SSL_PROTOCOL}.Tls_1_0)
end
set_ssl_protocol_to_tls_1_1
-- Set `ssl_protocol' with `Tls_1_1'.
do
set_ssl_protocol ({SSL_PROTOCOL}.Tls_1_1)
end
set_ssl_protocol_to_tls_1_2
-- Set `ssl_protocol' with `Tls_1_2'.
do
set_ssl_protocol ({SSL_PROTOCOL}.Tls_1_2)
end
set_ssl_protocol_to_dtls_1_0
-- Set `ssl_protocol' with `Dtls_1_0'.
do
set_ssl_protocol ({SSL_PROTOCOL}.Dtls_1_0)
end
note
copyright: "2011-2014, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[
Eiffel Software
5949 Hollister Ave., Goleta, CA 93117 USA
Telephone 805-685-1006, Fax 805-685-6869
Website http://www.eiffel.com
Customer support http://support.eiffel.com
]"
end

View File

@@ -0,0 +1,41 @@
note
description: "[
SSL enabled server
]"
date: "$Date$"
revision: "$Revision$"
class
HTTPD_SERVER
inherit
HTTPD_SERVER_I
redefine
new_listening_socket
end
create
make
feature {NONE} -- Factory
new_listening_socket (a_addr: detachable INET_ADDRESS; a_http_port: INTEGER): HTTPD_STREAM_SOCKET
local
s_ssl: HTTPD_STREAM_SSL_SOCKET
do
if configuration.is_secure then
if a_addr /= Void then
create s_ssl.make_server_by_address_and_port (a_addr, a_http_port)
Result := s_ssl
else
create s_ssl.make_server_by_port (a_http_port)
end
s_ssl.set_tls_protocol (configuration.ssl_protocol)
s_ssl.set_certificate_filenames (configuration.ca_crt, configuration.ca_key)
Result := s_ssl
else
Result := Precursor (a_addr, a_http_port)
end
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