Commit Jocelyn changes.
@@ -1,26 +0,0 @@
|
|||||||
note
|
|
||||||
description : "nino application root class"
|
|
||||||
date : "$Date$"
|
|
||||||
revision : "$Revision$"
|
|
||||||
|
|
||||||
class
|
|
||||||
APPLICATION
|
|
||||||
|
|
||||||
inherit
|
|
||||||
ARGUMENTS
|
|
||||||
|
|
||||||
create
|
|
||||||
make
|
|
||||||
|
|
||||||
feature {NONE} -- Initialization
|
|
||||||
|
|
||||||
make
|
|
||||||
-- Run application.
|
|
||||||
local
|
|
||||||
l_server : HTTP_SERVER
|
|
||||||
do
|
|
||||||
create l_server.make
|
|
||||||
l_server.setup
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
|
|
||||||
nino: system execution failed.
|
|
||||||
Following is the set of recorded exceptions:
|
|
||||||
|
|
||||||
******************************** Thread exception *****************************
|
|
||||||
In thread Child thread 0x88 (thread id)
|
|
||||||
*******************************************************************************
|
|
||||||
-------------------------------------------------------------------------------
|
|
||||||
Class / Object Routine Nature of exception Effect
|
|
||||||
-------------------------------------------------------------------------------
|
|
||||||
TCP_STREAM_SOCKET send @1 socket_exists:
|
|
||||||
<00000000024B1A48> (From SOCKET) Precondition violated. Fail
|
|
||||||
-------------------------------------------------------------------------------
|
|
||||||
GET_REQUEST_HANDLER send_message @4
|
|
||||||
<00000000024B4428> Routine failure. Fail
|
|
||||||
-------------------------------------------------------------------------------
|
|
||||||
GET_REQUEST_HANDLER execute @2
|
|
||||||
<00000000024B4428> Routine failure. Fail
|
|
||||||
-------------------------------------------------------------------------------
|
|
||||||
GET_REQUEST_HANDLER thr_main @4
|
|
||||||
<00000000024B4428> (From THREAD) Routine failure. Rescue
|
|
||||||
-------------------------------------------------------------------------------
|
|
||||||
APPLICATION root's creation
|
|
||||||
<00000000024B05A8> Routine failure. Exit
|
|
||||||
-------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
nino: system execution failed.
|
|
||||||
Following is the set of recorded exceptions:
|
|
||||||
|
|
||||||
******************************** Thread exception *****************************
|
|
||||||
In thread Child thread 0x88 (thread id)
|
|
||||||
*******************************************************************************
|
|
||||||
-------------------------------------------------------------------------------
|
|
||||||
Class / Object Routine Nature of exception Effect
|
|
||||||
-------------------------------------------------------------------------------
|
|
||||||
TCP_STREAM_SOCKET put_string @1 extendible:
|
|
||||||
<0000000002621A48> (From SOCKET) Precondition violated. Fail
|
|
||||||
-------------------------------------------------------------------------------
|
|
||||||
GET_REQUEST_HANDLER execute @2
|
|
||||||
<0000000002624408> Routine failure. Fail
|
|
||||||
-------------------------------------------------------------------------------
|
|
||||||
GET_REQUEST_HANDLER thr_main @4
|
|
||||||
<0000000002624408> (From THREAD) Routine failure. Rescue
|
|
||||||
-------------------------------------------------------------------------------
|
|
||||||
APPLICATION root's creation
|
|
||||||
<00000000026205A8> Routine failure. Exit
|
|
||||||
-------------------------------------------------------------------------------
|
|
||||||
@@ -1,274 +0,0 @@
|
|||||||
note
|
|
||||||
description: "Summary description for {HTTP_CONNECTION_HANDLER}."
|
|
||||||
author: ""
|
|
||||||
date: "$Date$"
|
|
||||||
revision: "$Revision$"
|
|
||||||
|
|
||||||
class
|
|
||||||
HTTP_CONNECTION_HANDLER
|
|
||||||
|
|
||||||
inherit
|
|
||||||
|
|
||||||
THREAD
|
|
||||||
HTTP_CONSTANTS
|
|
||||||
create
|
|
||||||
make
|
|
||||||
|
|
||||||
feature {NONE} -- Initialization
|
|
||||||
|
|
||||||
make (a_main_server: like main_server; a_name: STRING)
|
|
||||||
-- Creates a {HTTP_CONNECTION_HANDLER}, assigns the main_server and sets the current_request_message to empty.
|
|
||||||
--
|
|
||||||
-- `a_main_server': The main server object
|
|
||||||
-- `a_name': The name of this module
|
|
||||||
require
|
|
||||||
a_main_server_attached: a_main_server /= Void
|
|
||||||
a_name_attached: a_name /= Void
|
|
||||||
do
|
|
||||||
main_server := a_main_server
|
|
||||||
create current_request_message.make_empty
|
|
||||||
create method.make_empty
|
|
||||||
create uri.make_empty
|
|
||||||
create request_header_map.make (10)
|
|
||||||
is_stop_requested := False
|
|
||||||
ensure
|
|
||||||
main_server_set: a_main_server ~ main_server
|
|
||||||
current_request_message_attached: current_request_message /= Void
|
|
||||||
end
|
|
||||||
|
|
||||||
feature -- Inherited Features
|
|
||||||
|
|
||||||
execute
|
|
||||||
-- <Precursor>
|
|
||||||
-- Creates a socket and connects to the http server.
|
|
||||||
local
|
|
||||||
l_http_socket: detachable TCP_STREAM_SOCKET
|
|
||||||
do
|
|
||||||
is_stop_requested := False
|
|
||||||
create l_http_socket.make_server_by_port ({HTTP_CONSTANTS}.Http_server_port)
|
|
||||||
if not l_http_socket.is_bound then
|
|
||||||
print ("Socket could not be bound on port " + {HTTP_CONSTANTS}.Http_server_port.out )
|
|
||||||
else
|
|
||||||
from
|
|
||||||
l_http_socket.listen ({HTTP_CONSTANTS}.Max_tcp_clients)
|
|
||||||
print ("%NHTTP Connection Server ready on port " + {HTTP_CONSTANTS}.Http_server_port.out +"%N")
|
|
||||||
until
|
|
||||||
is_stop_requested
|
|
||||||
loop
|
|
||||||
l_http_socket.accept
|
|
||||||
if not is_stop_requested then
|
|
||||||
if attached l_http_socket.accepted as l_thread_http_socket then
|
|
||||||
receive_message_and_send_replay (l_thread_http_socket)
|
|
||||||
l_thread_http_socket.cleanup
|
|
||||||
check
|
|
||||||
socket_closed: l_thread_http_socket.is_closed
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
l_http_socket.cleanup
|
|
||||||
check
|
|
||||||
socket_is_closed: l_http_socket.is_closed
|
|
||||||
end
|
|
||||||
end
|
|
||||||
print ("HTTP Connection Server ends.")
|
|
||||||
rescue
|
|
||||||
print ("HTTP Connection Server shutdown due to exception. Please relaunch manually.")
|
|
||||||
|
|
||||||
if attached l_http_socket as ll_http_socket then
|
|
||||||
ll_http_socket.cleanup
|
|
||||||
check
|
|
||||||
socket_is_closed: ll_http_socket.is_closed
|
|
||||||
end
|
|
||||||
end
|
|
||||||
is_stop_requested := True
|
|
||||||
retry
|
|
||||||
end
|
|
||||||
|
|
||||||
feature -- Access
|
|
||||||
|
|
||||||
is_stop_requested: BOOLEAN
|
|
||||||
-- Set true to stop accept loop
|
|
||||||
|
|
||||||
|
|
||||||
feature {NONE} -- Access
|
|
||||||
|
|
||||||
request_header_map : HASH_TABLE [STRING,STRING]
|
|
||||||
-- Containts key value of the header
|
|
||||||
|
|
||||||
main_server: HTTP_SERVER
|
|
||||||
-- The main server object
|
|
||||||
|
|
||||||
current_request_message: STRING
|
|
||||||
-- Stores the current request message received from http server
|
|
||||||
|
|
||||||
Max_fragments: INTEGER = 1000
|
|
||||||
-- Defines the maximum number of fragments that can be received
|
|
||||||
|
|
||||||
method : STRING
|
|
||||||
-- http verb
|
|
||||||
|
|
||||||
uri : STRING
|
|
||||||
-- http endpoint
|
|
||||||
|
|
||||||
version : STRING
|
|
||||||
-- http_version
|
|
||||||
feature -- Status setting
|
|
||||||
|
|
||||||
shutdown
|
|
||||||
-- Stops the thread
|
|
||||||
do
|
|
||||||
is_stop_requested := True
|
|
||||||
end
|
|
||||||
|
|
||||||
feature {NONE} -- Implementation
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
read_string_from_socket (a_socket: TCP_STREAM_SOCKET; a_n: NATURAL): STRING
|
|
||||||
-- Reads characters from the socket and concatenates them to a string
|
|
||||||
--
|
|
||||||
-- `a_socket': The socket to read from
|
|
||||||
-- `a_n': The number of characters to read
|
|
||||||
-- `Result': The created string
|
|
||||||
require
|
|
||||||
socket_is_open: not a_socket.is_closed
|
|
||||||
local
|
|
||||||
l_read_size: INTEGER
|
|
||||||
l_buf: detachable STRING
|
|
||||||
do
|
|
||||||
create Result.make (a_n.as_integer_32)
|
|
||||||
from
|
|
||||||
l_read_size := 0
|
|
||||||
Result := ""
|
|
||||||
l_buf := ""
|
|
||||||
until
|
|
||||||
l_buf.is_equal ("%R")
|
|
||||||
loop
|
|
||||||
a_socket.read_line_thread_aware
|
|
||||||
l_buf := a_socket.last_string
|
|
||||||
if l_buf /= Void then
|
|
||||||
Result.append (l_buf)
|
|
||||||
end
|
|
||||||
if l_buf.is_equal ("%R") then
|
|
||||||
a_socket.set_nodelay
|
|
||||||
a_socket.put_string ("HTTP/1.1 100 Continue%/13/%/10/%/13/%/10/")
|
|
||||||
a_socket.close_socket
|
|
||||||
end
|
|
||||||
|
|
||||||
l_read_size := Result.count
|
|
||||||
end
|
|
||||||
ensure
|
|
||||||
Result_attached: Result /= Void
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
feature -- New implementation
|
|
||||||
|
|
||||||
|
|
||||||
parse_http_request_line (line: STRING)
|
|
||||||
require
|
|
||||||
line /= Void
|
|
||||||
local
|
|
||||||
pos, next_pos: INTEGER
|
|
||||||
do
|
|
||||||
print ("%N parse http request line:%N" + line)
|
|
||||||
-- parse (this should be done by a lexer)
|
|
||||||
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)
|
|
||||||
ensure
|
|
||||||
not_void_method: method /= Void
|
|
||||||
end
|
|
||||||
|
|
||||||
feature -- New Implementation
|
|
||||||
receive_message_and_send_replay (client_socket: TCP_STREAM_SOCKET)
|
|
||||||
require
|
|
||||||
socket_attached: client_socket /= Void
|
|
||||||
-- socket_valid: client_socket.is_open_read and then client_socket.is_open_write
|
|
||||||
a_http_socket:client_socket /= Void and then not client_socket.is_closed
|
|
||||||
local
|
|
||||||
message: detachable STRING
|
|
||||||
l_http_request : HTTP_REQUEST_HANDLER
|
|
||||||
do
|
|
||||||
parse_request_line (client_socket)
|
|
||||||
message := receive_message_internal (client_socket)
|
|
||||||
if method.is_equal (Get) then
|
|
||||||
create {GET_REQUEST_HANDLER} l_http_request
|
|
||||||
l_http_request.set_uri (uri)
|
|
||||||
l_http_request.process
|
|
||||||
-- client_socket.send_message (l_http_request.answer.reply_header + l_http_request.answer.reply_text)
|
|
||||||
client_socket.put_string (l_http_request.answer.reply_header + l_http_request.answer.reply_text)
|
|
||||||
elseif method.is_equal (Post) then
|
|
||||||
elseif method.is_equal (Put) then
|
|
||||||
elseif method.is_equal (Options) then
|
|
||||||
elseif method.is_equal (Head) then
|
|
||||||
elseif method.is_equal (Delete) then
|
|
||||||
elseif method.is_equal (Trace) then
|
|
||||||
elseif method.is_equal (Connect) then
|
|
||||||
else
|
|
||||||
debug
|
|
||||||
print ("Method not supported")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
parse_request_line (socket: NETWORK_STREAM_SOCKET)
|
|
||||||
require
|
|
||||||
socket: socket /= Void and then not socket.is_closed
|
|
||||||
do
|
|
||||||
socket.read_line
|
|
||||||
parse_request_line_internal (socket.last_string)
|
|
||||||
end
|
|
||||||
|
|
||||||
receive_message_internal (socket: TCP_STREAM_SOCKET) : STRING
|
|
||||||
require
|
|
||||||
socket: socket /= Void and then not socket.is_closed
|
|
||||||
local
|
|
||||||
end_of_stream : BOOLEAN
|
|
||||||
pos : INTEGER
|
|
||||||
line : STRING
|
|
||||||
do
|
|
||||||
from
|
|
||||||
socket.read_line_thread_aware
|
|
||||||
Result := ""
|
|
||||||
until
|
|
||||||
end_of_stream
|
|
||||||
loop
|
|
||||||
line := socket.last_string
|
|
||||||
print ("%N" +line+ "%N")
|
|
||||||
pos := line.index_of(':',1)
|
|
||||||
request_header_map.put (line.substring (pos + 1, line.count), line.substring (1,pos-1))
|
|
||||||
Result.append(socket.last_string)
|
|
||||||
if not socket.last_string.is_equal("%R") and socket.socket_ok then
|
|
||||||
socket.read_line_thread_aware
|
|
||||||
else
|
|
||||||
end_of_stream := True
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
parse_request_line_internal (line: STRING)
|
|
||||||
require
|
|
||||||
line /= Void
|
|
||||||
local
|
|
||||||
pos, next_pos: INTEGER
|
|
||||||
do
|
|
||||||
print ("%N parse request line:%N" + line)
|
|
||||||
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)
|
|
||||||
version := line.substring (next_pos + 1, line.count)
|
|
||||||
ensure
|
|
||||||
not_void_method: method /= Void
|
|
||||||
end
|
|
||||||
|
|
||||||
invariant
|
|
||||||
main_server_attached: main_server /= Void
|
|
||||||
current_request_message_attached: current_request_message /= Void
|
|
||||||
|
|
||||||
end
|
|
||||||
@@ -1,56 +0,0 @@
|
|||||||
note
|
|
||||||
description: "Summary description for {HTTP_SERVER}."
|
|
||||||
author: ""
|
|
||||||
date: "$Date$"
|
|
||||||
revision: "$Revision$"
|
|
||||||
|
|
||||||
class
|
|
||||||
HTTP_SERVER
|
|
||||||
inherit
|
|
||||||
SHARED_DOCUMENT_ROOT
|
|
||||||
create
|
|
||||||
make
|
|
||||||
feature -- Initialization
|
|
||||||
make
|
|
||||||
do
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
setup
|
|
||||||
local
|
|
||||||
l_http_handler : HTTP_CONNECTION_HANDLER
|
|
||||||
do
|
|
||||||
print("%N%N%N")
|
|
||||||
print ("Starting Web Application Server:%N")
|
|
||||||
stop := False
|
|
||||||
document_root_cell.put (document_root)
|
|
||||||
create l_http_handler.make (current,"HTTP_HANDLER")
|
|
||||||
l_http_handler.launch
|
|
||||||
run
|
|
||||||
end
|
|
||||||
|
|
||||||
shutdown_server
|
|
||||||
do
|
|
||||||
stop := True
|
|
||||||
end
|
|
||||||
|
|
||||||
feature -- Access
|
|
||||||
stop: BOOLEAN
|
|
||||||
-- Stops the server
|
|
||||||
|
|
||||||
document_root: STRING = "./webroot"
|
|
||||||
|
|
||||||
feature {NONE} -- implementation
|
|
||||||
|
|
||||||
run
|
|
||||||
-- Start the server
|
|
||||||
local
|
|
||||||
l_thread: EXECUTION_ENVIRONMENT
|
|
||||||
do
|
|
||||||
create l_thread
|
|
||||||
from until stop loop
|
|
||||||
l_thread.sleep (1000000)
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
end
|
|
||||||
28
nino.ecf
@@ -1,19 +1,25 @@
|
|||||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-5-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-5-0 http://www.eiffel.com/developers/xml/configuration-1-5-0.xsd" name="nino" uuid="32C1D67D-33DE-4F1E-864B-D45388F2E3E6">
|
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-8-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-8-0 http://www.eiffel.com/developers/xml/configuration-1-8-0.xsd" name="nino" uuid="32C1D67D-33DE-4F1E-864B-D45388F2E3E6" library_target="nino">
|
||||||
<target name="nino">
|
<target name="nino">
|
||||||
<root feature="make" class="APPLICATION"/>
|
<root class="APPLICATION" feature="make"/>
|
||||||
<option warning="true">
|
|
||||||
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
|
|
||||||
</option>
|
|
||||||
<setting name="multithreaded" value="true"/>
|
|
||||||
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
|
|
||||||
<library name="thread" location="$ISE_LIBRARY\library\thread\thread-safe.ecf"/>
|
|
||||||
<library name="net" location="$ISE_LIBRARY\library\net\net-safe.ecf"/>
|
|
||||||
<cluster name="nino" location=".\" recursive="true">
|
|
||||||
<file_rule>
|
<file_rule>
|
||||||
<exclude>/EIFGENs$</exclude>
|
<exclude>/EIFGENs$</exclude>
|
||||||
<exclude>/.svn$</exclude>
|
|
||||||
<exclude>/CVS$</exclude>
|
<exclude>/CVS$</exclude>
|
||||||
|
<exclude>/.svn$</exclude>
|
||||||
|
<exclude>/.git$</exclude>
|
||||||
|
</file_rule>
|
||||||
|
<option warning="true" is_attached_by_default="true" void_safety="all">
|
||||||
|
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
|
||||||
|
</option>
|
||||||
|
<setting name="concurrency" value="thread"/>
|
||||||
|
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
|
||||||
|
<library name="net" location="$ISE_LIBRARY\library\net\net-safe.ecf"/>
|
||||||
|
<library name="thread" location="$ISE_LIBRARY\library\thread\thread-safe.ecf"/>
|
||||||
|
<cluster name="nino" location=".\src" recursive="true">
|
||||||
|
<file_rule>
|
||||||
|
<exclude>head_request_handler.e</exclude>
|
||||||
|
<exclude>shared_http_request_handlers.e</exclude>
|
||||||
|
<exclude>http_protocol_handler.e</exclude>
|
||||||
</file_rule>
|
</file_rule>
|
||||||
</cluster>
|
</cluster>
|
||||||
</target>
|
</target>
|
||||||
|
|||||||
@@ -1,53 +0,0 @@
|
|||||||
deferred class HTTP_REQUEST_HANDLER
|
|
||||||
feature
|
|
||||||
|
|
||||||
set_uri (new_uri: STRING)
|
|
||||||
-- set new URI
|
|
||||||
require
|
|
||||||
valid_uri: new_uri /= Void
|
|
||||||
do
|
|
||||||
request_uri := new_uri
|
|
||||||
end
|
|
||||||
|
|
||||||
request_uri: STRING
|
|
||||||
-- requested url
|
|
||||||
|
|
||||||
set_data (new_data: STRING)
|
|
||||||
-- set new data
|
|
||||||
do
|
|
||||||
data := new_data
|
|
||||||
end
|
|
||||||
|
|
||||||
data: STRING
|
|
||||||
-- the entire request message
|
|
||||||
|
|
||||||
|
|
||||||
headers : HASH_TABLE [STRING, STRING]
|
|
||||||
-- Provides access to the request's HTTP headers, for example:
|
|
||||||
-- headers["Content-Type"] is "text/plain"
|
|
||||||
|
|
||||||
|
|
||||||
set_headers ( a_header : HASH_TABLE [STRING, STRING] )
|
|
||||||
do
|
|
||||||
headers := a_header
|
|
||||||
end
|
|
||||||
|
|
||||||
process
|
|
||||||
-- process the request and create an answer
|
|
||||||
require
|
|
||||||
valid_uri: request_uri /= Void
|
|
||||||
deferred
|
|
||||||
end
|
|
||||||
|
|
||||||
answer: HTTP_RESPONSE
|
|
||||||
-- reply to this request
|
|
||||||
|
|
||||||
reset
|
|
||||||
-- reinit the fields
|
|
||||||
do
|
|
||||||
request_uri := Void
|
|
||||||
data := Void
|
|
||||||
answer := Void
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
@@ -1,115 +0,0 @@
|
|||||||
note
|
|
||||||
description: "Summary description for {POST_REQUEST_HANDLER}."
|
|
||||||
author: ""
|
|
||||||
date: "$Date$"
|
|
||||||
revision: "$Revision$"
|
|
||||||
|
|
||||||
class
|
|
||||||
POST_REQUEST_HANDLER
|
|
||||||
|
|
||||||
inherit
|
|
||||||
|
|
||||||
SHARED_DOCUMENT_ROOT
|
|
||||||
|
|
||||||
SHARED_URI_CONTENTS_TYPES
|
|
||||||
|
|
||||||
HTTP_REQUEST_HANDLER
|
|
||||||
|
|
||||||
HTTP_CONSTANTS
|
|
||||||
|
|
||||||
feature
|
|
||||||
|
|
||||||
|
|
||||||
process is
|
|
||||||
-- process the request and create an answer
|
|
||||||
local
|
|
||||||
fname: STRING
|
|
||||||
f: RAW_FILE
|
|
||||||
ctype, extension: STRING
|
|
||||||
do
|
|
||||||
fname := document_root_cell.item.twin
|
|
||||||
fname.append (request_uri)
|
|
||||||
debug
|
|
||||||
print ("URI name: " + fname )
|
|
||||||
end
|
|
||||||
create f.make (fname)
|
|
||||||
create answer.make
|
|
||||||
if f.exists then
|
|
||||||
extension := ct_table.extension (request_uri)
|
|
||||||
ctype := ct_table.content_types.item (extension)
|
|
||||||
-- TODO: This code could be improved to avoid string
|
|
||||||
-- comparisons
|
|
||||||
if ctype = Void then
|
|
||||||
process_default
|
|
||||||
answer.set_content_type ("text/html")
|
|
||||||
else
|
|
||||||
if ctype.is_equal ("text/html") then
|
|
||||||
process_text_file (f)
|
|
||||||
else
|
|
||||||
process_raw_file (f)
|
|
||||||
end
|
|
||||||
answer.set_content_type (ctype)
|
|
||||||
end
|
|
||||||
else
|
|
||||||
answer.set_status_code (not_found)
|
|
||||||
answer.set_reason_phrase (not_found_message)
|
|
||||||
answer.set_reply_text ("Not found on this server%N%R")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
process_default is
|
|
||||||
--
|
|
||||||
local
|
|
||||||
html : STRING
|
|
||||||
do
|
|
||||||
answer.set_reply_text ("")
|
|
||||||
html := " <html> <head> <title> Micro HTTPD </title> " +
|
|
||||||
" </head> " +
|
|
||||||
" <body> " +
|
|
||||||
" <h1> Welcome to Micro HTTPD! </h1> "+
|
|
||||||
" <p> Default page " +
|
|
||||||
|
|
||||||
" </p> " +
|
|
||||||
" </body> " +
|
|
||||||
" </html> "
|
|
||||||
answer.append_reply_text (html)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
process_text_file (f: FILE) is
|
|
||||||
-- send a text file reply
|
|
||||||
require
|
|
||||||
valid_f: f /= Void
|
|
||||||
do
|
|
||||||
f.open_read
|
|
||||||
from
|
|
||||||
answer.set_reply_text ("")
|
|
||||||
f.read_line
|
|
||||||
until f.end_of_file
|
|
||||||
loop
|
|
||||||
answer.append_reply_text (f.last_string)
|
|
||||||
answer.append_reply_text (crlf)
|
|
||||||
f.read_line
|
|
||||||
end
|
|
||||||
f.close
|
|
||||||
end
|
|
||||||
|
|
||||||
process_raw_file (f: FILE) is
|
|
||||||
-- send a raw file reply
|
|
||||||
require
|
|
||||||
valid_f: f /= Void
|
|
||||||
do
|
|
||||||
-- this is not quite right....
|
|
||||||
f.open_read
|
|
||||||
from
|
|
||||||
answer.set_reply_text ("")
|
|
||||||
until f.end_of_file
|
|
||||||
loop
|
|
||||||
f.read_stream (1024)
|
|
||||||
answer.append_reply_text (f.last_string)
|
|
||||||
end
|
|
||||||
f.close
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
end
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
note
|
|
||||||
description: "Summary description for {SHARED_DOCUMENT_ROOT}."
|
|
||||||
author: ""
|
|
||||||
date: "$Date$"
|
|
||||||
revision: "$Revision$"
|
|
||||||
|
|
||||||
class
|
|
||||||
SHARED_DOCUMENT_ROOT
|
|
||||||
feature
|
|
||||||
|
|
||||||
document_root_cell: CELL [STRING]
|
|
||||||
once ("PROCESS")
|
|
||||||
create Result.put (Void)
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
5
src/README
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
Eiffel Web Nino is and HTTPD server. It's a work in progress, so maybe it will be refactored.
|
||||||
|
The goal of is to provide a simple web server for development (like Java, Python and Ruby provide)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
68
src/configuration/http_server_configuration.e
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
note
|
||||||
|
description: "Summary description for {HTTP_SERVER_CONFIGURATION}."
|
||||||
|
date: "$Date$"
|
||||||
|
revision: "$Revision$"
|
||||||
|
|
||||||
|
class
|
||||||
|
HTTP_SERVER_CONFIGURATION
|
||||||
|
|
||||||
|
create
|
||||||
|
make
|
||||||
|
|
||||||
|
feature {NONE} -- Initialization
|
||||||
|
|
||||||
|
make
|
||||||
|
do
|
||||||
|
http_server_port := 80
|
||||||
|
max_tcp_clients := 100
|
||||||
|
socket_accept_timeout := 1_000
|
||||||
|
socket_connect_timeout := 5_000
|
||||||
|
document_root := "htdocs"
|
||||||
|
force_single_threaded := False
|
||||||
|
end
|
||||||
|
|
||||||
|
feature -- Access
|
||||||
|
|
||||||
|
Server_details : STRING = "Server : NANO Eiffel Server"
|
||||||
|
|
||||||
|
document_root: STRING assign set_document_root
|
||||||
|
http_server_port: INTEGER assign set_http_server_port
|
||||||
|
max_tcp_clients: INTEGER assign set_max_tcp_clients
|
||||||
|
socket_accept_timeout: INTEGER assign set_socket_accept_timeout
|
||||||
|
socket_connect_timeout: INTEGER assign set_socket_connect_timeout
|
||||||
|
force_single_threaded: BOOLEAN assign set_force_single_threaded
|
||||||
|
|
||||||
|
feature -- Element change
|
||||||
|
|
||||||
|
set_http_server_port (v: like http_server_port)
|
||||||
|
do
|
||||||
|
http_server_port := v
|
||||||
|
end
|
||||||
|
|
||||||
|
set_document_root (v: like document_root)
|
||||||
|
do
|
||||||
|
document_root := v
|
||||||
|
end
|
||||||
|
|
||||||
|
set_max_tcp_clients (v: like max_tcp_clients)
|
||||||
|
do
|
||||||
|
max_tcp_clients := v
|
||||||
|
end
|
||||||
|
|
||||||
|
set_socket_accept_timeout (v: like socket_accept_timeout)
|
||||||
|
do
|
||||||
|
socket_accept_timeout := v
|
||||||
|
end
|
||||||
|
|
||||||
|
set_socket_connect_timeout (v: like socket_connect_timeout)
|
||||||
|
do
|
||||||
|
socket_connect_timeout := v
|
||||||
|
end
|
||||||
|
|
||||||
|
set_force_single_threaded (v: like force_single_threaded)
|
||||||
|
do
|
||||||
|
force_single_threaded := v
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
end
|
||||||
45
src/configuration/http_server_shared_configuration.e
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
note
|
||||||
|
description: "Summary description for {HTTP_SERVER_SHARED_CONFIGURATION}."
|
||||||
|
author: ""
|
||||||
|
date: "$Date$"
|
||||||
|
revision: "$Revision$"
|
||||||
|
|
||||||
|
class
|
||||||
|
HTTP_SERVER_SHARED_CONFIGURATION
|
||||||
|
|
||||||
|
feature -- Access
|
||||||
|
|
||||||
|
server_configuration: detachable HTTP_SERVER_CONFIGURATION
|
||||||
|
-- Shared configuration
|
||||||
|
do
|
||||||
|
if attached server_configuration_cell.item as l_cfg then
|
||||||
|
Result := l_cfg
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
document_root: STRING_8
|
||||||
|
-- Shared document root
|
||||||
|
do
|
||||||
|
if attached server_configuration as l_cfg then
|
||||||
|
Result := l_cfg.document_root
|
||||||
|
else
|
||||||
|
Result := ""
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
feature -- Element change
|
||||||
|
|
||||||
|
set_server_configuration (a_cfg: like server_configuration)
|
||||||
|
-- Set `server_configuration' to `a_cfg'.
|
||||||
|
do
|
||||||
|
server_configuration_cell.replace (a_cfg)
|
||||||
|
end
|
||||||
|
|
||||||
|
feature {NONE} -- Implementation
|
||||||
|
|
||||||
|
server_configuration_cell: CELL [detachable HTTP_SERVER_CONFIGURATION]
|
||||||
|
once ("PROCESS")
|
||||||
|
create Result.put (Void)
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
175
src/http_connection_handler.e
Normal file
@@ -0,0 +1,175 @@
|
|||||||
|
note
|
||||||
|
description: "Summary description for {HTTP_CONNECTION_HANDLER}."
|
||||||
|
author: ""
|
||||||
|
date: "$Date$"
|
||||||
|
revision: "$Revision$"
|
||||||
|
|
||||||
|
deferred class
|
||||||
|
HTTP_CONNECTION_HANDLER
|
||||||
|
|
||||||
|
inherit
|
||||||
|
HTTP_HANDLER
|
||||||
|
redefine
|
||||||
|
make
|
||||||
|
end
|
||||||
|
|
||||||
|
feature {NONE} -- Initialization
|
||||||
|
|
||||||
|
make (a_main_server: like main_server; a_name: STRING)
|
||||||
|
-- Creates a {HTTP_CONNECTION_HANDLER}, assigns the main_server and sets the current_request_message to empty.
|
||||||
|
--
|
||||||
|
-- `a_main_server': The main server object
|
||||||
|
-- `a_name': The name of this module
|
||||||
|
do
|
||||||
|
Precursor (a_main_server, a_name)
|
||||||
|
reset
|
||||||
|
end
|
||||||
|
|
||||||
|
reset
|
||||||
|
do
|
||||||
|
create method.make_empty
|
||||||
|
create uri.make_empty
|
||||||
|
create request_header.make_empty
|
||||||
|
create request_header_map.make (10)
|
||||||
|
remote_info := Void
|
||||||
|
end
|
||||||
|
|
||||||
|
feature -- Execution
|
||||||
|
|
||||||
|
receive_message_and_send_reply (client_socket: TCP_STREAM_SOCKET)
|
||||||
|
local
|
||||||
|
l_input: HTTP_INPUT_STREAM
|
||||||
|
l_output: HTTP_OUTPUT_STREAM
|
||||||
|
l_remote_info: detachable like remote_info
|
||||||
|
do
|
||||||
|
create l_input.make (client_socket)
|
||||||
|
create l_output.make (client_socket)
|
||||||
|
|
||||||
|
|
||||||
|
create l_remote_info
|
||||||
|
if attached client_socket.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
|
||||||
|
|
||||||
|
analyze_request_message (l_input)
|
||||||
|
process_request (Current, l_input, l_output)
|
||||||
|
reset
|
||||||
|
end
|
||||||
|
|
||||||
|
feature -- Request processing
|
||||||
|
|
||||||
|
process_request (a_handler: HTTP_CONNECTION_HANDLER; a_input: HTTP_INPUT_STREAM; a_output: HTTP_OUTPUT_STREAM)
|
||||||
|
-- Process request ...
|
||||||
|
require
|
||||||
|
a_handler_attached: a_handler /= Void
|
||||||
|
a_uri_attached: a_handler.uri /= Void
|
||||||
|
a_method_attached: a_handler.method /= Void
|
||||||
|
a_header_map_attached: a_handler.request_header_map /= Void
|
||||||
|
a_header_text_attached: a_handler.request_header /= Void
|
||||||
|
a_input_attached: a_input /= Void
|
||||||
|
a_output_attached: a_output /= Void
|
||||||
|
deferred
|
||||||
|
end
|
||||||
|
|
||||||
|
feature -- Access
|
||||||
|
|
||||||
|
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]
|
||||||
|
|
||||||
|
feature -- Parsing
|
||||||
|
|
||||||
|
parse_http_request_line (line: STRING)
|
||||||
|
require
|
||||||
|
line /= Void
|
||||||
|
local
|
||||||
|
pos, next_pos: INTEGER
|
||||||
|
do
|
||||||
|
print ("%N parse http request line:%N" + line)
|
||||||
|
-- parse (this should be done by a lexer)
|
||||||
|
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)
|
||||||
|
ensure
|
||||||
|
not_void_method: method /= Void
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
analyze_request_message (a_input: HTTP_INPUT_STREAM)
|
||||||
|
require
|
||||||
|
input_readable: a_input /= Void and then a_input.is_readable
|
||||||
|
local
|
||||||
|
end_of_stream : BOOLEAN
|
||||||
|
pos : INTEGER
|
||||||
|
line : STRING
|
||||||
|
val: STRING
|
||||||
|
txt: STRING
|
||||||
|
do
|
||||||
|
create txt.make (64)
|
||||||
|
a_input.read_line
|
||||||
|
line := a_input.last_string
|
||||||
|
analyze_request_line (line)
|
||||||
|
txt.append (line)
|
||||||
|
txt.append_character ('%N')
|
||||||
|
|
||||||
|
request_header := txt
|
||||||
|
from
|
||||||
|
a_input.read_line
|
||||||
|
until
|
||||||
|
end_of_stream
|
||||||
|
loop
|
||||||
|
line := a_input.last_string
|
||||||
|
print ("%N" +line+ "%N")
|
||||||
|
pos := line.index_of (':',1)
|
||||||
|
val := line.substring (pos + 1, line.count)
|
||||||
|
if val[val.count] = '%R' then
|
||||||
|
val.remove_tail (1)
|
||||||
|
end
|
||||||
|
request_header_map.put (val, line.substring (1,pos-1))
|
||||||
|
txt.append (line)
|
||||||
|
txt.append_character ('%N')
|
||||||
|
if line.is_empty or else line[1] = '%R' then
|
||||||
|
end_of_stream := True
|
||||||
|
else
|
||||||
|
a_input.read_line
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
analyze_request_line (line: STRING)
|
||||||
|
require
|
||||||
|
line /= Void
|
||||||
|
local
|
||||||
|
pos, next_pos: INTEGER
|
||||||
|
do
|
||||||
|
print ("%N parse request line:%N" + line)
|
||||||
|
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)
|
||||||
|
version := line.substring (next_pos + 1, line.count)
|
||||||
|
ensure
|
||||||
|
not_void_method: method /= Void
|
||||||
|
end
|
||||||
|
|
||||||
|
invariant
|
||||||
|
request_header_attached: request_header /= Void
|
||||||
|
|
||||||
|
end
|
||||||
@@ -81,13 +81,6 @@ feature -- content types
|
|||||||
|
|
||||||
text_html: STRING = "text/html"
|
text_html: STRING = "text/html"
|
||||||
|
|
||||||
feature -- Network
|
|
||||||
Server_datails : STRING = "Server : NANO Eiffel Server"
|
|
||||||
Http_server_port: INTEGER = 9000
|
|
||||||
Max_tcp_clients: INTEGER = 100
|
|
||||||
Socket_accept_timeout: INTEGER = 1000
|
|
||||||
Socket_connect_timeout: INTEGER = 5000
|
|
||||||
|
|
||||||
feature -- General Header Fields
|
feature -- General Header Fields
|
||||||
|
|
||||||
-- There are a few header fields which have general applicability for both request and response messages,
|
-- There are a few header fields which have general applicability for both request and response messages,
|
||||||
123
src/http_handler.e
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
note
|
||||||
|
description: "Summary description for {HTTP_CONNECTION_HANDLER}."
|
||||||
|
author: ""
|
||||||
|
date: "$Date$"
|
||||||
|
revision: "$Revision$"
|
||||||
|
|
||||||
|
deferred class
|
||||||
|
HTTP_HANDLER
|
||||||
|
|
||||||
|
inherit
|
||||||
|
THREAD
|
||||||
|
|
||||||
|
HTTP_CONSTANTS
|
||||||
|
|
||||||
|
feature {NONE} -- Initialization
|
||||||
|
|
||||||
|
make (a_main_server: like main_server; a_name: STRING)
|
||||||
|
-- Creates a {HTTP_HANDLER}, assigns the main_server and initialize various values
|
||||||
|
--
|
||||||
|
-- `a_main_server': The main server object
|
||||||
|
-- `a_name': The name of this module
|
||||||
|
require
|
||||||
|
a_main_server_attached: a_main_server /= Void
|
||||||
|
a_name_attached: a_name /= Void
|
||||||
|
do
|
||||||
|
main_server := a_main_server
|
||||||
|
is_stop_requested := False
|
||||||
|
ensure
|
||||||
|
main_server_set: a_main_server ~ main_server
|
||||||
|
end
|
||||||
|
|
||||||
|
feature -- Inherited Features
|
||||||
|
|
||||||
|
execute
|
||||||
|
-- <Precursor>
|
||||||
|
-- Creates a socket and connects to the http server.
|
||||||
|
local
|
||||||
|
l_http_socket: detachable TCP_STREAM_SOCKET
|
||||||
|
l_http_port: INTEGER
|
||||||
|
do
|
||||||
|
is_stop_requested := False
|
||||||
|
l_http_port := main_server_configuration.http_server_port
|
||||||
|
create l_http_socket.make_server_by_port (l_http_port)
|
||||||
|
if not l_http_socket.is_bound then
|
||||||
|
print ("Socket could not be bound on port " + l_http_port.out )
|
||||||
|
else
|
||||||
|
from
|
||||||
|
l_http_socket.listen (main_server_configuration.max_tcp_clients)
|
||||||
|
print ("%NHTTP Connection Server ready on port " + l_http_port.out +"%N")
|
||||||
|
until
|
||||||
|
is_stop_requested
|
||||||
|
loop
|
||||||
|
l_http_socket.accept
|
||||||
|
if not is_stop_requested then
|
||||||
|
if attached l_http_socket.accepted as l_thread_http_socket then
|
||||||
|
receive_message_and_send_reply (l_thread_http_socket)
|
||||||
|
l_thread_http_socket.cleanup
|
||||||
|
check
|
||||||
|
socket_closed: l_thread_http_socket.is_closed
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
l_http_socket.cleanup
|
||||||
|
check
|
||||||
|
socket_is_closed: l_http_socket.is_closed
|
||||||
|
end
|
||||||
|
end
|
||||||
|
print ("HTTP Connection Server ends.")
|
||||||
|
rescue
|
||||||
|
print ("HTTP Connection Server shutdown due to exception. Please relaunch manually.")
|
||||||
|
|
||||||
|
if attached l_http_socket as ll_http_socket then
|
||||||
|
ll_http_socket.cleanup
|
||||||
|
check
|
||||||
|
socket_is_closed: ll_http_socket.is_closed
|
||||||
|
end
|
||||||
|
end
|
||||||
|
is_stop_requested := True
|
||||||
|
retry
|
||||||
|
end
|
||||||
|
|
||||||
|
feature -- Access
|
||||||
|
|
||||||
|
is_stop_requested: BOOLEAN
|
||||||
|
-- Set true to stop accept loop
|
||||||
|
|
||||||
|
feature {NONE} -- Access
|
||||||
|
|
||||||
|
main_server: HTTP_SERVER
|
||||||
|
-- The main server object
|
||||||
|
|
||||||
|
main_server_configuration: HTTP_SERVER_CONFIGURATION
|
||||||
|
-- The main server's configuration
|
||||||
|
do
|
||||||
|
Result := main_server.configuration
|
||||||
|
end
|
||||||
|
|
||||||
|
Max_fragments: INTEGER = 1000
|
||||||
|
-- Defines the maximum number of fragments that can be received
|
||||||
|
|
||||||
|
feature -- Status setting
|
||||||
|
|
||||||
|
shutdown
|
||||||
|
-- Stops the thread
|
||||||
|
do
|
||||||
|
is_stop_requested := True
|
||||||
|
end
|
||||||
|
|
||||||
|
feature -- Execution
|
||||||
|
|
||||||
|
receive_message_and_send_reply (client_socket: TCP_STREAM_SOCKET)
|
||||||
|
require
|
||||||
|
socket_attached: client_socket /= Void
|
||||||
|
-- socket_valid: client_socket.is_open_read and then client_socket.is_open_write
|
||||||
|
a_http_socket:client_socket /= Void and then not client_socket.is_closed
|
||||||
|
deferred
|
||||||
|
end
|
||||||
|
|
||||||
|
invariant
|
||||||
|
main_server_attached: main_server /= Void
|
||||||
|
|
||||||
|
end
|
||||||
@@ -70,7 +70,7 @@ feature -- Implementation
|
|||||||
done
|
done
|
||||||
loop
|
loop
|
||||||
if socket.socket_ok then
|
if socket.socket_ok then
|
||||||
done := receive_message_and_send_replay (socket)
|
done := receive_message_and_send_reply (socket)
|
||||||
request_header_map.wipe_out
|
request_header_map.wipe_out
|
||||||
else
|
else
|
||||||
done := True
|
done := True
|
||||||
@@ -80,7 +80,7 @@ feature -- Implementation
|
|||||||
io.put_new_line
|
io.put_new_line
|
||||||
end
|
end
|
||||||
|
|
||||||
receive_message_and_send_replay (client_socket: NETWORK_STREAM_SOCKET): BOOLEAN
|
receive_message_and_send_reply (client_socket: NETWORK_STREAM_SOCKET): BOOLEAN
|
||||||
require
|
require
|
||||||
socket_attached: client_socket /= Void
|
socket_attached: client_socket /= Void
|
||||||
socket_valid: client_socket.is_open_read and then client_socket.is_open_write
|
socket_valid: client_socket.is_open_read and then client_socket.is_open_write
|
||||||
65
src/http_server.e
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
note
|
||||||
|
description: "Summary description for {HTTP_SERVER}."
|
||||||
|
author: ""
|
||||||
|
date: "$Date$"
|
||||||
|
revision: "$Revision$"
|
||||||
|
|
||||||
|
class
|
||||||
|
HTTP_SERVER
|
||||||
|
|
||||||
|
inherit
|
||||||
|
HTTP_SERVER_SHARED_CONFIGURATION
|
||||||
|
|
||||||
|
create
|
||||||
|
make
|
||||||
|
|
||||||
|
feature -- Initialization
|
||||||
|
|
||||||
|
make (cfg: like configuration)
|
||||||
|
do
|
||||||
|
configuration := cfg
|
||||||
|
end
|
||||||
|
|
||||||
|
setup (a_http_handler : HTTP_HANDLER)
|
||||||
|
require
|
||||||
|
a_http_handler_valid: a_http_handler /= Void
|
||||||
|
do
|
||||||
|
print("%N%N%N")
|
||||||
|
print ("Starting Web Application Server (port="+ configuration.http_server_port.out +"):%N")
|
||||||
|
stop_requested := False
|
||||||
|
set_server_configuration (configuration)
|
||||||
|
if configuration.force_single_threaded then
|
||||||
|
a_http_handler.execute
|
||||||
|
else
|
||||||
|
a_http_handler.launch
|
||||||
|
a_http_handler.join
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
shutdown_server
|
||||||
|
do
|
||||||
|
stop_requested := True
|
||||||
|
end
|
||||||
|
|
||||||
|
feature -- Access
|
||||||
|
|
||||||
|
configuration: HTTP_SERVER_CONFIGURATION
|
||||||
|
-- Configuration of the server
|
||||||
|
|
||||||
|
stop_requested: BOOLEAN
|
||||||
|
-- Stops the server
|
||||||
|
|
||||||
|
feature {NONE} -- implementation
|
||||||
|
|
||||||
|
run
|
||||||
|
-- Start the server
|
||||||
|
local
|
||||||
|
e: EXECUTION_ENVIRONMENT
|
||||||
|
do
|
||||||
|
create e
|
||||||
|
from until stop_requested loop
|
||||||
|
e.sleep (1_000_000)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
64
src/io/http_input_stream.e
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
note
|
||||||
|
description: "Summary description for {HTTP_INPUT_STREAM}."
|
||||||
|
author: ""
|
||||||
|
date: "$Date$"
|
||||||
|
revision: "$Revision$"
|
||||||
|
|
||||||
|
class
|
||||||
|
HTTP_INPUT_STREAM
|
||||||
|
|
||||||
|
create
|
||||||
|
make
|
||||||
|
|
||||||
|
feature {NONE} -- Initialization
|
||||||
|
|
||||||
|
make (a_socket: like source)
|
||||||
|
do
|
||||||
|
source := a_socket
|
||||||
|
create last_string.make_empty
|
||||||
|
end
|
||||||
|
|
||||||
|
source: TCP_STREAM_SOCKET
|
||||||
|
|
||||||
|
feature -- Status Report
|
||||||
|
|
||||||
|
is_readable: BOOLEAN
|
||||||
|
-- Is readable?
|
||||||
|
do
|
||||||
|
Result := source.is_open_read
|
||||||
|
end
|
||||||
|
|
||||||
|
feature -- Basic operation
|
||||||
|
|
||||||
|
read_line
|
||||||
|
require
|
||||||
|
is_readable: is_readable
|
||||||
|
do
|
||||||
|
last_string.wipe_out
|
||||||
|
if source.socket_ok then
|
||||||
|
source.read_line_thread_aware
|
||||||
|
last_string.append_string (source.last_string)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
read_stream (nb_char: INTEGER)
|
||||||
|
-- Read a string of at most `nb_char' bound characters
|
||||||
|
-- or until end of file.
|
||||||
|
-- Make result available in `last_string'.
|
||||||
|
require
|
||||||
|
nb_char_positive: nb_char > 0
|
||||||
|
is_readable: is_readable
|
||||||
|
do
|
||||||
|
last_string.wipe_out
|
||||||
|
if source.socket_ok then
|
||||||
|
source.read_stream_thread_aware (nb_char)
|
||||||
|
last_string.append_string (source.last_string)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
feature -- Access
|
||||||
|
|
||||||
|
last_string: STRING
|
||||||
|
-- Last string read
|
||||||
|
|
||||||
|
end
|
||||||
31
src/io/http_output_stream.e
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
note
|
||||||
|
description: "Summary description for {HTTP_OUTPUT_STREAM}."
|
||||||
|
author: ""
|
||||||
|
date: "$Date$"
|
||||||
|
revision: "$Revision$"
|
||||||
|
|
||||||
|
class
|
||||||
|
HTTP_OUTPUT_STREAM
|
||||||
|
|
||||||
|
create
|
||||||
|
make
|
||||||
|
|
||||||
|
feature {NONE} -- Initialization
|
||||||
|
|
||||||
|
make (a_socket: like target)
|
||||||
|
do
|
||||||
|
target := a_socket
|
||||||
|
end
|
||||||
|
|
||||||
|
target: TCP_STREAM_SOCKET
|
||||||
|
|
||||||
|
feature -- Basic operation
|
||||||
|
|
||||||
|
put_string (s: STRING)
|
||||||
|
-- Write string `s' to `target'
|
||||||
|
do
|
||||||
|
target.put_string (s)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
end
|
||||||
@@ -2,40 +2,63 @@
|
|||||||
GET_REQUEST_HANDLER
|
GET_REQUEST_HANDLER
|
||||||
|
|
||||||
inherit
|
inherit
|
||||||
SHARED_DOCUMENT_ROOT
|
|
||||||
|
|
||||||
SHARED_URI_CONTENTS_TYPES
|
|
||||||
|
|
||||||
HTTP_REQUEST_HANDLER
|
HTTP_REQUEST_HANDLER
|
||||||
|
|
||||||
|
HTTP_SERVER_SHARED_CONFIGURATION
|
||||||
|
undefine
|
||||||
|
default_create
|
||||||
|
end
|
||||||
|
|
||||||
|
SHARED_URI_CONTENTS_TYPES
|
||||||
|
undefine
|
||||||
|
default_create
|
||||||
|
end
|
||||||
|
|
||||||
HTTP_CONSTANTS
|
HTTP_CONSTANTS
|
||||||
|
undefine
|
||||||
|
default_create
|
||||||
|
end
|
||||||
|
|
||||||
create
|
create
|
||||||
default_create
|
make
|
||||||
|
|
||||||
feature
|
feature {NONE} -- Initialization
|
||||||
|
|
||||||
|
make (a_input: like input; a_output: like output)
|
||||||
|
do
|
||||||
|
default_create
|
||||||
|
input := a_input
|
||||||
|
output := a_output
|
||||||
|
end
|
||||||
|
|
||||||
|
feature -- Access
|
||||||
|
|
||||||
|
input: HTTP_INPUT_STREAM
|
||||||
|
|
||||||
|
output: HTTP_OUTPUT_STREAM
|
||||||
|
|
||||||
|
feature -- Execution
|
||||||
|
|
||||||
process
|
process
|
||||||
-- process the request and create an answer
|
-- process the request and create an answer
|
||||||
local
|
local
|
||||||
fname: STRING_8
|
fname: STRING_8
|
||||||
f: RAW_FILE
|
f: RAW_FILE
|
||||||
ctype, extension: STRING_8
|
ctype, extension: detachable STRING_8
|
||||||
do
|
do
|
||||||
create answer.make
|
answer.reset
|
||||||
if request_uri.is_equal ("/") then
|
if script_name.is_equal ("/") then
|
||||||
process_default
|
process_default
|
||||||
answer.set_content_type ("text/html")
|
answer.set_content_type ("text/html")
|
||||||
else
|
else
|
||||||
fname := Document_root_cell.item.twin
|
create fname.make_from_string (Document_root)
|
||||||
fname.append (request_uri)
|
fname.append (script_name)
|
||||||
debug
|
debug
|
||||||
print ("URI name: " + fname)
|
print ("URI filename: " + fname)
|
||||||
end
|
end
|
||||||
create f.make (fname)
|
create f.make (real_filename (fname))
|
||||||
create answer.make
|
|
||||||
if f.exists then
|
if f.exists then
|
||||||
extension := Ct_table.extension (request_uri)
|
extension := Ct_table.extension (script_name)
|
||||||
ctype := Ct_table.content_types.item (extension)
|
ctype := Ct_table.content_types.item (extension)
|
||||||
if f.is_directory then
|
if f.is_directory then
|
||||||
process_directory (f)
|
process_directory (f)
|
||||||
@@ -58,11 +81,18 @@ feature
|
|||||||
answer.set_reply_text ("Not found on this server")
|
answer.set_reply_text ("Not found on this server")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
answer.set_content_length (answer.reply_text.count.out)
|
if attached answer.reply_text as t then
|
||||||
|
answer.set_content_length (t.count)
|
||||||
|
else
|
||||||
|
answer.set_content_length (0)
|
||||||
|
end
|
||||||
|
|
||||||
|
--| Output the result
|
||||||
|
output.put_string (answer.reply_header + answer.reply_text)
|
||||||
end
|
end
|
||||||
|
|
||||||
process_default
|
process_default
|
||||||
-- Return a defaul response
|
-- Return a default response
|
||||||
local
|
local
|
||||||
html: STRING_8
|
html: STRING_8
|
||||||
do
|
do
|
||||||
@@ -118,14 +148,14 @@ feature
|
|||||||
html2: STRING_8
|
html2: STRING_8
|
||||||
htmldir: STRING_8
|
htmldir: STRING_8
|
||||||
path: STRING_8
|
path: STRING_8
|
||||||
index: INTEGER_32
|
|
||||||
do
|
do
|
||||||
answer.set_reply_text ("")
|
answer.set_reply_text ("")
|
||||||
html1 := " <html> <head> <title> NINO HTTPD </title> " + " </head> " + " <body> " + " <h1> Welcome to NINO HTTPD! </h1> " + " <p> Default page "
|
html1 := " <html> <head> <title> NINO HTTPD </title> " + " </head> " + " <body> " + " <h1> Welcome to NINO HTTPD! </h1> " + " <p> Default page "
|
||||||
html2 := " </p> " + " </body> " + " </html> "
|
html2 := " </p> " + " </body> " + " </html> "
|
||||||
path := f.name.twin
|
path := script_name
|
||||||
index := path.last_index_of ('/', path.count)
|
if path[path.count] = '/' then
|
||||||
path.remove_substring (1, index)
|
path.remove_tail (1)
|
||||||
|
end
|
||||||
create l_dir.make_open_read (f.name)
|
create l_dir.make_open_read (f.name)
|
||||||
files := l_dir.linear_representation
|
files := l_dir.linear_representation
|
||||||
from
|
from
|
||||||
@@ -134,7 +164,7 @@ feature
|
|||||||
until
|
until
|
||||||
files.after
|
files.after
|
||||||
loop
|
loop
|
||||||
htmldir := htmldir + "<li><a href=%"./" + path + "/" + files.item_for_iteration + "%">" + files.item_for_iteration + "</a> </li>%N"
|
htmldir := htmldir + "<li><a href=%"" + path + "/" + files.item_for_iteration + "%">" + files.item_for_iteration + "</a> </li>%N"
|
||||||
files.forth
|
files.forth
|
||||||
end
|
end
|
||||||
htmldir := htmldir + "</ul>"
|
htmldir := htmldir + "</ul>"
|
||||||
@@ -20,7 +20,7 @@ inherit
|
|||||||
feature
|
feature
|
||||||
|
|
||||||
|
|
||||||
process is
|
process
|
||||||
-- process the request and create an answer
|
-- process the request and create an answer
|
||||||
local
|
local
|
||||||
fname: STRING
|
fname: STRING
|
||||||
@@ -57,7 +57,7 @@ feature
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
process_default is
|
process_default
|
||||||
--
|
--
|
||||||
local
|
local
|
||||||
html : STRING
|
html : STRING
|
||||||
@@ -76,7 +76,7 @@ feature
|
|||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
process_text_file (f: FILE) is
|
process_text_file (f: FILE)
|
||||||
-- send a text file reply
|
-- send a text file reply
|
||||||
require
|
require
|
||||||
valid_f: f /= Void
|
valid_f: f /= Void
|
||||||
@@ -94,7 +94,7 @@ feature
|
|||||||
f.close
|
f.close
|
||||||
end
|
end
|
||||||
|
|
||||||
process_raw_file (f: FILE) is
|
process_raw_file (f: FILE)
|
||||||
-- send a raw file reply
|
-- send a raw file reply
|
||||||
require
|
require
|
||||||
valid_f: f /= Void
|
valid_f: f /= Void
|
||||||
115
src/request/http_request_handler.e
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
deferred class HTTP_REQUEST_HANDLER
|
||||||
|
|
||||||
|
inherit
|
||||||
|
ANY
|
||||||
|
redefine
|
||||||
|
default_create
|
||||||
|
end
|
||||||
|
|
||||||
|
feature {NONE} -- Initialization
|
||||||
|
|
||||||
|
default_create
|
||||||
|
do
|
||||||
|
Precursor
|
||||||
|
create request_uri.make_empty
|
||||||
|
create script_name.make_empty
|
||||||
|
create query_string.make_empty
|
||||||
|
create answer
|
||||||
|
create headers.make (0)
|
||||||
|
end
|
||||||
|
|
||||||
|
feature -- Access
|
||||||
|
|
||||||
|
request_uri: STRING
|
||||||
|
-- requested url
|
||||||
|
|
||||||
|
script_name: STRING
|
||||||
|
-- Script name
|
||||||
|
|
||||||
|
query_string: STRING
|
||||||
|
-- Query string
|
||||||
|
|
||||||
|
data: detachable STRING
|
||||||
|
-- the entire request message
|
||||||
|
|
||||||
|
headers : HASH_TABLE [STRING, STRING]
|
||||||
|
-- Provides access to the request's HTTP headers, for example:
|
||||||
|
-- headers["Content-Type"] is "text/plain"
|
||||||
|
|
||||||
|
answer: HTTP_RESPONSE
|
||||||
|
-- reply to this request
|
||||||
|
|
||||||
|
feature -- Execution
|
||||||
|
|
||||||
|
process
|
||||||
|
-- process the request and create an answer
|
||||||
|
require
|
||||||
|
valid_uri: request_uri /= Void
|
||||||
|
deferred
|
||||||
|
end
|
||||||
|
|
||||||
|
feature -- Recycle
|
||||||
|
|
||||||
|
reset
|
||||||
|
-- reinit the fields
|
||||||
|
do
|
||||||
|
request_uri.wipe_out
|
||||||
|
script_name.wipe_out
|
||||||
|
query_string.wipe_out
|
||||||
|
data := Void
|
||||||
|
answer.reset
|
||||||
|
end
|
||||||
|
|
||||||
|
feature -- Element change
|
||||||
|
|
||||||
|
set_uri (new_uri: STRING)
|
||||||
|
-- set new URI
|
||||||
|
require
|
||||||
|
valid_uri: new_uri /= Void
|
||||||
|
local
|
||||||
|
p: INTEGER
|
||||||
|
do
|
||||||
|
request_uri := new_uri
|
||||||
|
p := new_uri.index_of ('?', 1)
|
||||||
|
if p > 0 then
|
||||||
|
script_name := new_uri.substring (1, p - 1)
|
||||||
|
query_string := new_uri.substring (p + 1, new_uri.count)
|
||||||
|
else
|
||||||
|
script_name := new_uri.string
|
||||||
|
query_string := ""
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
set_data (new_data: STRING)
|
||||||
|
-- set new data
|
||||||
|
do
|
||||||
|
data := new_data
|
||||||
|
end
|
||||||
|
|
||||||
|
set_headers ( a_header : HASH_TABLE [STRING, STRING] )
|
||||||
|
do
|
||||||
|
headers := a_header
|
||||||
|
end
|
||||||
|
|
||||||
|
feature {NONE} -- Implementation
|
||||||
|
|
||||||
|
real_filename (fn: STRING): STRING
|
||||||
|
-- Real filename from url-path `fn'
|
||||||
|
--| Find a better design for this piece of code
|
||||||
|
--| Eventually in a spec/$ISE_PLATFORM/ specific cluster
|
||||||
|
do
|
||||||
|
if {PLATFORM}.is_windows then
|
||||||
|
create Result.make_from_string (fn)
|
||||||
|
Result.replace_substring_all ("/", "\")
|
||||||
|
if Result[Result.count] = '\' then
|
||||||
|
Result.remove_tail (1)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
Result := fn
|
||||||
|
if Result[Result.count] = '/' then
|
||||||
|
Result := Result.substring (1, Result.count - 1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
42
src/request/post_request_handler.e
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
note
|
||||||
|
description: "Summary description for {POST_REQUEST_HANDLER}."
|
||||||
|
author: ""
|
||||||
|
date: "$Date$"
|
||||||
|
revision: "$Revision$"
|
||||||
|
|
||||||
|
class
|
||||||
|
POST_REQUEST_HANDLER
|
||||||
|
|
||||||
|
inherit
|
||||||
|
GET_REQUEST_HANDLER
|
||||||
|
redefine
|
||||||
|
process
|
||||||
|
end
|
||||||
|
|
||||||
|
create
|
||||||
|
make
|
||||||
|
|
||||||
|
feature -- Execution
|
||||||
|
|
||||||
|
process
|
||||||
|
-- process the request and create an answer
|
||||||
|
local
|
||||||
|
l_data: STRING
|
||||||
|
s: STRING
|
||||||
|
n: INTEGER
|
||||||
|
do
|
||||||
|
from
|
||||||
|
n := 1_024
|
||||||
|
input.read_stream (n)
|
||||||
|
s := input.last_string
|
||||||
|
create l_data.make_empty
|
||||||
|
until
|
||||||
|
s.count < n
|
||||||
|
loop
|
||||||
|
l_data.append_string (s)
|
||||||
|
input.read_stream (n)
|
||||||
|
end
|
||||||
|
Precursor
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
@@ -2,12 +2,38 @@
|
|||||||
class HTTP_RESPONSE
|
class HTTP_RESPONSE
|
||||||
|
|
||||||
inherit
|
inherit
|
||||||
|
|
||||||
HTTP_CONSTANTS
|
HTTP_CONSTANTS
|
||||||
|
redefine
|
||||||
|
default_create
|
||||||
|
end
|
||||||
|
|
||||||
create
|
create
|
||||||
|
default_create
|
||||||
|
|
||||||
make
|
feature -- creation
|
||||||
|
|
||||||
|
default_create
|
||||||
|
do
|
||||||
|
Precursor
|
||||||
|
set_defaults
|
||||||
|
end
|
||||||
|
|
||||||
|
set_defaults
|
||||||
|
-- Set default values for the reply
|
||||||
|
do
|
||||||
|
status_code := ok
|
||||||
|
create content_length_data.make_empty
|
||||||
|
reason_phrase := ok_message
|
||||||
|
content_type_data := text_html
|
||||||
|
set_reply_text (Void)
|
||||||
|
end
|
||||||
|
|
||||||
|
feature -- Recycle
|
||||||
|
|
||||||
|
reset
|
||||||
|
do
|
||||||
|
set_defaults
|
||||||
|
end
|
||||||
|
|
||||||
feature -- response header fields
|
feature -- response header fields
|
||||||
|
|
||||||
@@ -17,11 +43,19 @@ feature -- response header fields
|
|||||||
content_length_data : STRING
|
content_length_data : STRING
|
||||||
-- length
|
-- length
|
||||||
|
|
||||||
set_content_length (new_content_length: STRING)
|
reason_phrase: STRING
|
||||||
|
-- message, if any
|
||||||
|
|
||||||
|
content_type_data: STRING
|
||||||
|
-- type of content in this reply (eg. text/html)
|
||||||
|
|
||||||
|
feature -- Element change
|
||||||
|
|
||||||
|
set_content_length (new_content_length: INTEGER)
|
||||||
require
|
require
|
||||||
not_void: new_content_length /= Void
|
positive_or_zero: new_content_length >= 0
|
||||||
do
|
do
|
||||||
content_length_data := new_content_length
|
content_length_data := new_content_length.out
|
||||||
end
|
end
|
||||||
|
|
||||||
set_status_code (new_status_code: STRING)
|
set_status_code (new_status_code: STRING)
|
||||||
@@ -31,9 +65,6 @@ feature -- response header fields
|
|||||||
status_code := new_status_code
|
status_code := new_status_code
|
||||||
end
|
end
|
||||||
|
|
||||||
reason_phrase: STRING
|
|
||||||
-- message, if any
|
|
||||||
|
|
||||||
set_reason_phrase (new_reason_phrase: STRING)
|
set_reason_phrase (new_reason_phrase: STRING)
|
||||||
require
|
require
|
||||||
not_void: new_reason_phrase /= Void
|
not_void: new_reason_phrase /= Void
|
||||||
@@ -41,9 +72,6 @@ feature -- response header fields
|
|||||||
reason_phrase := new_reason_phrase
|
reason_phrase := new_reason_phrase
|
||||||
end
|
end
|
||||||
|
|
||||||
content_type_data: STRING
|
|
||||||
-- type of content in this reply (eg. text/html)
|
|
||||||
|
|
||||||
set_content_type (new_content_type: STRING)
|
set_content_type (new_content_type: STRING)
|
||||||
require
|
require
|
||||||
not_void: new_content_type /= Void
|
not_void: new_content_type /= Void
|
||||||
@@ -51,18 +79,7 @@ feature -- response header fields
|
|||||||
content_type_data := new_content_type
|
content_type_data := new_content_type
|
||||||
end
|
end
|
||||||
|
|
||||||
feature -- creation
|
feature -- Access: send reply
|
||||||
|
|
||||||
make
|
|
||||||
do
|
|
||||||
-- set default values for the reply
|
|
||||||
status_code := ok
|
|
||||||
reason_phrase := ok_message
|
|
||||||
content_type_data := text_html
|
|
||||||
end
|
|
||||||
|
|
||||||
feature -- access these to send a reply
|
|
||||||
|
|
||||||
|
|
||||||
reply_header: STRING
|
reply_header: STRING
|
||||||
-- header
|
-- header
|
||||||
@@ -73,7 +90,7 @@ feature -- access these to send a reply
|
|||||||
Result.extend (' ')
|
Result.extend (' ')
|
||||||
Result.append (reason_phrase)
|
Result.append (reason_phrase)
|
||||||
Result.append (crlf)
|
Result.append (crlf)
|
||||||
Result.append (Server_datails)
|
Result.append ({HTTP_SERVER_CONFIGURATION}.Server_details)
|
||||||
Result.append (crlf)
|
Result.append (crlf)
|
||||||
Result.append (Content_type + ": ")
|
Result.append (Content_type + ": ")
|
||||||
Result.append (content_type_data)
|
Result.append (content_type_data)
|
||||||
@@ -100,15 +117,20 @@ feature -- access these to send a reply
|
|||||||
-- then keep the connection alive
|
-- then keep the connection alive
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
reply_text: STRING
|
reply_text: STRING
|
||||||
-- reply text
|
-- reply text
|
||||||
|
|
||||||
set_reply_text (new_text: STRING)
|
feature -- Change element: send reply
|
||||||
|
|
||||||
|
set_reply_text (new_text: detachable STRING)
|
||||||
-- text could be Void
|
-- text could be Void
|
||||||
do
|
do
|
||||||
|
if new_text = Void then
|
||||||
|
create reply_text.make_empty
|
||||||
|
else
|
||||||
reply_text := new_text
|
reply_text := new_text
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
append_reply_text (more_text: STRING)
|
append_reply_text (more_text: STRING)
|
||||||
-- add more text to the reply
|
-- add more text to the reply
|
||||||
@@ -2,7 +2,7 @@ class SHARED_HTTP_REQUEST_HANDLERS
|
|||||||
|
|
||||||
feature
|
feature
|
||||||
|
|
||||||
http_request_handlers: HASH_TABLE [HTTP_REQUEST_HANDLER, STRING] is
|
http_request_handlers: HASH_TABLE [HTTP_REQUEST_HANDLER, STRING]
|
||||||
local
|
local
|
||||||
a_handler: HTTP_REQUEST_HANDLER
|
a_handler: HTTP_REQUEST_HANDLER
|
||||||
once
|
once
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
note
|
note
|
||||||
description: "Summary description for {TCP_STREAM_SOCKET}."
|
description: "Summary description for {TCP_STREAM_SOCKET}."
|
||||||
author: ""
|
|
||||||
date: "$Date$"
|
date: "$Date$"
|
||||||
revision: "$Revision$"
|
revision: "$Revision$"
|
||||||
|
|
||||||
class
|
class
|
||||||
TCP_STREAM_SOCKET
|
TCP_STREAM_SOCKET
|
||||||
|
|
||||||
inherit
|
inherit
|
||||||
NETWORK_STREAM_SOCKET
|
NETWORK_STREAM_SOCKET
|
||||||
|
|
||||||
@@ -15,7 +15,8 @@ create
|
|||||||
create {NETWORK_STREAM_SOCKET}
|
create {NETWORK_STREAM_SOCKET}
|
||||||
make_from_descriptor_and_address
|
make_from_descriptor_and_address
|
||||||
|
|
||||||
feature
|
feature -- Basic operation
|
||||||
|
|
||||||
send_message (a_msg: STRING)
|
send_message (a_msg: STRING)
|
||||||
local
|
local
|
||||||
a_package : PACKET
|
a_package : PACKET
|
||||||
@@ -25,6 +26,7 @@ feature
|
|||||||
create c_string.make (a_msg)
|
create c_string.make (a_msg)
|
||||||
create a_data.make_from_pointer (c_string.item, a_msg.count + 1)
|
create a_data.make_from_pointer (c_string.item, a_msg.count + 1)
|
||||||
create a_package.make_from_managed_pointer (a_data)
|
create a_package.make_from_managed_pointer (a_data)
|
||||||
Current.send (a_package, 1)
|
send (a_package, 1)
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
37
web_server/application.e
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
note
|
||||||
|
description : "nino application root class"
|
||||||
|
date : "$Date$"
|
||||||
|
revision : "$Revision$"
|
||||||
|
|
||||||
|
class
|
||||||
|
APPLICATION
|
||||||
|
|
||||||
|
inherit
|
||||||
|
ARGUMENTS
|
||||||
|
|
||||||
|
create
|
||||||
|
make
|
||||||
|
|
||||||
|
feature {NONE} -- Initialization
|
||||||
|
|
||||||
|
make
|
||||||
|
-- Run application.
|
||||||
|
local
|
||||||
|
l_server : HTTP_SERVER
|
||||||
|
l_cfg: HTTP_SERVER_CONFIGURATION
|
||||||
|
l_http_handler : HTTP_HANDLER
|
||||||
|
do
|
||||||
|
create l_cfg.make
|
||||||
|
l_cfg.http_server_port := 9_000
|
||||||
|
l_cfg.document_root := default_document_root
|
||||||
|
|
||||||
|
create l_server.make (l_cfg)
|
||||||
|
create {APPLICATION_CONNECTION_HANDLER} l_http_handler.make (l_server, "HTTP_HANDLER")
|
||||||
|
l_server.setup (l_http_handler)
|
||||||
|
end
|
||||||
|
|
||||||
|
feature -- Access
|
||||||
|
|
||||||
|
default_document_root: STRING = "webroot"
|
||||||
|
|
||||||
|
end
|
||||||
61
web_server/application_handler.e
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
note
|
||||||
|
description: "Summary description for {HTTP_CONNECTION_HANDLER}."
|
||||||
|
author: ""
|
||||||
|
date: "$Date$"
|
||||||
|
revision: "$Revision$"
|
||||||
|
|
||||||
|
class
|
||||||
|
APPLICATION_CONNECTION_HANDLER
|
||||||
|
|
||||||
|
inherit
|
||||||
|
HTTP_CONNECTION_HANDLER
|
||||||
|
|
||||||
|
create
|
||||||
|
make
|
||||||
|
|
||||||
|
feature -- Request processing
|
||||||
|
|
||||||
|
process_request (a_handler: HTTP_CONNECTION_HANDLER; a_input: HTTP_INPUT_STREAM; a_output: HTTP_OUTPUT_STREAM)
|
||||||
|
-- Process request ...
|
||||||
|
local
|
||||||
|
a_method: STRING
|
||||||
|
do
|
||||||
|
a_method := a_handler.method
|
||||||
|
|
||||||
|
if a_method.is_equal (Get) then
|
||||||
|
execute_get_request (a_handler.uri, a_handler.request_header_map, a_handler.request_header, a_input, a_output)
|
||||||
|
elseif a_method.is_equal (Post) then
|
||||||
|
execute_post_request (a_handler.uri, a_handler.request_header_map, a_handler.request_header, a_input, a_output)
|
||||||
|
elseif a_method.is_equal (Put) then
|
||||||
|
elseif a_method.is_equal (Options) then
|
||||||
|
elseif a_method.is_equal (Head) then
|
||||||
|
elseif a_method.is_equal (Delete) then
|
||||||
|
elseif a_method.is_equal (Trace) then
|
||||||
|
elseif a_method.is_equal (Connect) then
|
||||||
|
else
|
||||||
|
debug
|
||||||
|
print ("Method [" + a_method + "] not supported")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
execute_get_request (a_uri: STRING; a_headers_map: HASH_TABLE [STRING, STRING]; a_headers_text: STRING; a_input: HTTP_INPUT_STREAM; a_output: HTTP_OUTPUT_STREAM)
|
||||||
|
local
|
||||||
|
l_http_request : HTTP_REQUEST_HANDLER
|
||||||
|
do
|
||||||
|
create {GET_REQUEST_HANDLER} l_http_request.make (a_input, a_output)
|
||||||
|
l_http_request.set_uri (a_uri)
|
||||||
|
l_http_request.process
|
||||||
|
end
|
||||||
|
|
||||||
|
execute_post_request (a_uri: STRING; a_headers_map: HASH_TABLE [STRING, STRING]; a_headers_text: STRING; a_input: HTTP_INPUT_STREAM; a_output: HTTP_OUTPUT_STREAM)
|
||||||
|
local
|
||||||
|
l_http_request : HTTP_REQUEST_HANDLER
|
||||||
|
do
|
||||||
|
check not_yet_implemented: False end
|
||||||
|
create {POST_REQUEST_HANDLER} l_http_request.make (a_input, a_output)
|
||||||
|
l_http_request.set_uri (a_uri)
|
||||||
|
l_http_request.process
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
21
web_server/web_server.ecf
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||||
|
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-8-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-8-0 http://www.eiffel.com/developers/xml/configuration-1-8-0.xsd" name="web_server" uuid="B1D3254D-A58E-4259-9796-8A2843A511A9">
|
||||||
|
<target name="web_server">
|
||||||
|
<root class="APPLICATION" feature="make"/>
|
||||||
|
<file_rule>
|
||||||
|
<exclude>/EIFGENs$</exclude>
|
||||||
|
<exclude>/CVS$</exclude>
|
||||||
|
<exclude>/.svn$</exclude>
|
||||||
|
<exclude>/.git$</exclude>
|
||||||
|
</file_rule>
|
||||||
|
<option warning="true" is_attached_by_default="true" void_safety="all">
|
||||||
|
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
|
||||||
|
</option>
|
||||||
|
<setting name="concurrency" value="thread"/>
|
||||||
|
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
|
||||||
|
<library name="net" location="$ISE_LIBRARY\library\net\net-safe.ecf"/>
|
||||||
|
<library name="thread" location="$ISE_LIBRARY\library\thread\thread-safe.ecf"/>
|
||||||
|
<library name="nino" location="../nino.ecf"/>
|
||||||
|
<cluster name="src" location=".\" recursive="true"/>
|
||||||
|
</target>
|
||||||
|
</system>
|
||||||
|
Before Width: | Height: | Size: 578 B After Width: | Height: | Size: 578 B |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 703 B After Width: | Height: | Size: 703 B |
|
Before Width: | Height: | Size: 513 B After Width: | Height: | Size: 513 B |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 1009 B After Width: | Height: | Size: 1009 B |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 921 B After Width: | Height: | Size: 921 B |
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.0 KiB |
|
Before Width: | Height: | Size: 888 B After Width: | Height: | Size: 888 B |
|
Before Width: | Height: | Size: 1004 B After Width: | Height: | Size: 1004 B |
|
Before Width: | Height: | Size: 1014 B After Width: | Height: | Size: 1014 B |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 884 B After Width: | Height: | Size: 884 B |
|
Before Width: | Height: | Size: 1002 B After Width: | Height: | Size: 1002 B |
|
Before Width: | Height: | Size: 872 B After Width: | Height: | Size: 872 B |
|
Before Width: | Height: | Size: 990 B After Width: | Height: | Size: 990 B |
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 315 B After Width: | Height: | Size: 315 B |
|
Before Width: | Height: | Size: 315 B After Width: | Height: | Size: 315 B |
|
Before Width: | Height: | Size: 315 B After Width: | Height: | Size: 315 B |
|
Before Width: | Height: | Size: 315 B After Width: | Height: | Size: 315 B |
|
Before Width: | Height: | Size: 315 B After Width: | Height: | Size: 315 B |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 63 KiB After Width: | Height: | Size: 63 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 733 B After Width: | Height: | Size: 733 B |
|
Before Width: | Height: | Size: 711 B After Width: | Height: | Size: 711 B |
|
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 8.8 KiB After Width: | Height: | Size: 8.8 KiB |
|
Before Width: | Height: | Size: 608 B After Width: | Height: | Size: 608 B |
|
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 2.4 KiB |
|
Before Width: | Height: | Size: 668 B After Width: | Height: | Size: 668 B |
|
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.2 KiB |
|
Before Width: | Height: | Size: 8.3 KiB After Width: | Height: | Size: 8.3 KiB |
|
Before Width: | Height: | Size: 3.6 KiB After Width: | Height: | Size: 3.6 KiB |
|
Before Width: | Height: | Size: 493 KiB After Width: | Height: | Size: 493 KiB |
|
Before Width: | Height: | Size: 1009 B After Width: | Height: | Size: 1009 B |
|
Before Width: | Height: | Size: 74 KiB After Width: | Height: | Size: 74 KiB |
19
web_server/webroot/post/index.html
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>POST example</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1> POST example </h1>
|
||||||
|
<form action="index.html" method="POST">
|
||||||
|
<input type="text" name="name" value="a simple name"/> <br/>
|
||||||
|
<input type="text" name="text" value="a tiny text"/>
|
||||||
|
<input type="submit" value="POST"/>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<form action="index.html" method="GET">
|
||||||
|
<input type="text" name="name" value="a simple name"/> <br/>
|
||||||
|
<input type="text" name="text" value="a tiny text"/>
|
||||||
|
<input type="submit" value="GET"/>
|
||||||
|
</form>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||