Compare commits

..

47 Commits

Author SHA1 Message Date
91fbb3866f Experimentation of WSF_EXECUTOR.execute (req, res) 2015-06-18 14:49:29 +02:00
903f925a79 Changed the way SSL is supported with standalone connector (httpd lib).
Now by default, SSL is not supported,
  and if an application wants the SSL support,
  the related .ecf has to set custom variable "httpd_ssl_enabled" to "true"
2015-06-17 17:22:59 +02:00
80709578d6 Updated workbook Eiffel code to follow new EWF concurrent design. 2015-06-16 20:34:57 +02:00
c0d5b7c968 Added make_from_execution procedure to ease implementing various use cases. 2015-06-16 15:02:17 +02:00
7bea163f46 Updated ecf from obsolete v0 folder to include the "_v0" suffix in the library names. 2015-06-14 21:34:41 +02:00
8992dbc515 Added wsf_html in the obsolete v0 folder.
mostly because it is also dependent on "wsf", so it has to be using the obsolete v0 ecf.
2015-06-12 19:01:35 +02:00
c2d3ea6138 Simplified file names, and harmonized with estudio wizards. 2015-06-12 12:56:46 +02:00
9e336deb49 Updated EWF estudio wizard. 2015-06-12 12:43:54 +02:00
0160ce05dd Updated wizard template ecf to take into account current limitation, or known issue related to libcurl and ssl. 2015-06-12 12:00:54 +02:00
7d089a88c2 Made compilable without SSL enabled
(i.e when variable named "httpd_ssl_disabled" is set to "true")
2015-06-11 10:08:44 +02:00
ab0bc7b314 Marked most of the *_with_request_methods procedure obsolete by the same feature name without the "_with_request_methods".
Added argument passing request methods to feature without the _with_request_methods.
Prefer "thread" concurrency for now in examples.
2015-06-10 17:52:26 +02:00
0e3e97a7fd Added a few example based on the obsolete libraries (v0).
Updated the tutorial example.
Added WSF_MESSAGE_EXECUTION.
2015-06-10 16:49:23 +02:00
b790c7fd21 cosmetic, cleaning. 2015-06-10 10:59:24 +02:00
9424b1e369 Merge branch 'master' into v1 2015-06-08 10:10:51 +02:00
64463df552 Fixed various compilation error or warning. 2015-06-05 12:17:53 +02:00
dd5c89e31c Fixed compilation of SSL_TCP_STREAM_SOCKET with recent do_accept changes. 2015-05-07 10:44:38 +02:00
fffa763d05 Updated a few comments. 2015-05-06 22:37:55 +02:00
d015c065f6 Updated readme. 2015-05-06 22:18:27 +02:00
8ea443c115 Added abstraction WSF_ROUTED, and WSF_FILTERED.
Added under library/server/obsolete/v0 the previous non concurrent friendly version of EWF/WSF, for backward compatiblity.
Removed WSF_CALLBACK_SERVICE and WSF_TO_WGI_SERVICE which are not need with new EWF.
2015-05-06 22:17:37 +02:00
019393fdb1 Fixed typo. 2015-05-06 22:17:35 +02:00
da8028f8b3 Fixed a typo. 2015-05-06 22:17:33 +02:00
20ed000879 Added a few descriptions and comments. 2015-05-06 22:16:57 +02:00
jvelilla
24620b228c Added feature comments.
Added missing postconditions.
2015-05-06 22:16:10 +02:00
jvelilla
9c7e29b836 Added descriptions and comments 2015-05-06 22:16:08 +02:00
jvelilla
a0e9a41e21 Added descriptions and feature comments. 2015-05-06 22:16:07 +02:00
jvelilla
dd9aff03d3 Added features comments. 2015-05-06 22:16:05 +02:00
jvelilla
dc35925eb0 Added Missing Class and feature descriptions.
Removed author entry.
2015-05-06 22:16:03 +02:00
a1a620a9c3 Export request and response from WGI_EXECUTION to itself.
Added WSF_FILTERED_ROUTED_SKELETON_EXECUTION
2015-05-06 22:16:00 +02:00
d8ea9ba63c renamed keep_alive_requested as is_persistent_connection_requested. 2015-05-06 22:15:59 +02:00
c42af5b2de Following the spec, use "keep-alive" and "close" in lowercase for Connection header. 2015-05-06 22:15:57 +02:00
d9cbc72058 Better support for HTTP/1.0 and also related to persistent connection. 2015-05-06 22:15:56 +02:00
7e057b20b1 Improved support for HTTP/1.0 persistent connection. 2015-05-06 22:15:54 +02:00
3165c1e5c6 Enable support for persistent connections.
(test: works fine with curl -k , but weird behavior with ab -k ...)
2015-05-06 22:15:53 +02:00
89e26519e4 First step to improve a bit error handling related to socket disconnection.
Mainly in standalone connector for now.
2015-05-06 22:15:51 +02:00
9d20e85c03 Improved the simple_file example with image, and not found message.
Use standalone connector in SCOOP concurrency mode.
2015-05-06 22:15:50 +02:00
48cb99498c Cleaned simple example, and made the standalone target with SCOOP concurrency. 2015-05-06 22:15:48 +02:00
8246bc1444 Updated various indexing notes.
Removed a few obsolete classes.
Cosmetics
2015-05-06 22:15:46 +02:00
9e1083eba8 Added migration note. 2015-05-06 22:15:44 +02:00
4907bc3085 Migrated most of the example and library to new design. 2015-05-06 22:15:43 +02:00
7d2ce8a77f Implemented support for base url in httpd connector. 2015-05-06 22:15:38 +02:00
b4a9c92ffc Migrated simple, simple_file and upload_image example.
Adapted EWF accordingly.
2015-05-06 22:14:48 +02:00
bf0eb9a02d Added SCOOP support for WSF.
WSF_SERVICE is deeply changed, and addition of WSF_EXECUTION.
Todo: code cleaning, removing useless things.
2015-05-06 22:13:22 +02:00
ddf73077b3 Support for concurrencies: none, thread and SCOOP 2015-05-06 22:13:19 +02:00
3da80fce0d Finally SCOOP supported. 2015-05-06 22:13:17 +02:00
0970de5dc6 Experiment to avoid pseudo sequential execution 2015-05-06 22:13:15 +02:00
557b11f4e6 First attempt to use `{NETWORK_STREAM_SOCKET}.accept_to' 2015-05-06 22:13:13 +02:00
7f27a6c797 First steps to provide a concurrent compliant EWF connector. 2015-05-06 22:13:11 +02:00
380 changed files with 18489 additions and 4725 deletions

View File

@@ -1,38 +1,7 @@
History for Eiffel-Web-Framework
[2015-06-10]
* Updated EWF design to better support concurrency, including SCOOP via
the new standalone connector.
[2011-09-23] Jocelyn
* library "ewsgi":
- NEW simple autotest cases using Nino web server
-fixed issue with RAW_POST_DATA being added in form_data_parameters
instead of meta_variables ...
- Implemented WGI_VALUE for parameter's type (query_parameter,
form_data_parameter, item ...)
* Nino connector: added feature to shutdown the server from the WGI application
* NEW library "http_client": a new library to perform simple http requests
such as get, head, post, put, ... (currently implemented with Eiffel cURL)
* NEW library "http_authorization": added simple library to support
HTTP_AUTHORIZATION. For now only "Basic" auth type is supported ..
[2011-09-22] Javier
* NEW Example: added partial Restbuck example
[2011-09-21] Jocelyn
* Nino connector: fixed an issue with missing value for Content-Type and Content-Length
[2011-09-13] Jocelyn
* library "router": now using a generic design to allow customization of
request handler context class.
* NEW library "server/request/rest": first attempt to provide a library to
help building RESTful application (the interfaces are likely to change
soon) EXPERIMENTAL
[2011-09-09] Jocelyn
* library "uri-template": better support for {/vars} and {?vars}
[2011-09-07] Jocelyn
* library "router": now routing depends on uri (or uri template) and request methods
* Nino connector: Fixed issue where HTTP_ prefix were missing for header meta variable.
[2011-09-07] Jocelyn
* changelog: starting to write down changelogs file
[Previous ] Many significant changes in v0

3138
ChangeLog

File diff suppressed because it is too large Load Diff

121
MIGRATION.md Normal file
View File

@@ -0,0 +1,121 @@
Date: 2015-june
# Goal:
=======
- support safe concurrency with EWF
- provide a concurrent standalone connector
# Status:
=========
- The version v0 of EWF has mainly 3 connectors: CGI, libFCGI, and nino.
- CGI and libFCGI connectors does not need any concurrency support.
- But the nino connector had a pseudo concurrency support with Thread, however one could do write code that result in hasardeous concurrency execution.
So, it was decided to provide an improved Eiffel web nino connector, and update EWF design to make it concurrency compliant.
# Decisions:
============
- instead of updating current nino library, we now have a new "standalone" connector which is inspired by nino, but have support for the 3 concurrency modes: none, thread and SCOOP.
# Overview
==========
Adding support for SCOOP concurrency mode add constraints to the design, but also helps ensuring the concurrency design of EWF is correct.
As a consequence, we had to introduce a new interface WSF_EXECUTION which is instantiated for each incoming request. See its simplified interface :
<code lang="eiffel">
deferred class WSF_EXECUTION
feature -- Initialization
make (req: WGI_REQUEST; res: WGI_RESPONSE)
do
...
īnitialize
end
initialize
-- Initialize Current object.
--| To be redefined if needed.
do
end
feature -- Access
request: WSF_REQUEST
-- Access to request data.
-- Header, Query, Post, Input data..
response: WSF_RESPONSE
-- Access to output stream, back to the client.
feature -- Execution
execute
-- Execute Current `request',
-- getting data from `request'
-- and response to client via `response'.
deferred
ensure
is_valid_end_of_execution: is_valid_end_of_execution
end
end
</code>
And the related request execution routines are extracted from WSF_SERVICE which becomes almost useless. The "service" part is not mostly responsible of launching the expected connector and set optional options, and declare the type of "execution" interface.
As a result, the well known WSF_DEFAULT_SERVICE has now a formal generic that should conform to WSF_EXECUTION with a `make' creation procedure. See update code:
<code lang="eiffel">
class
APPLICATION
inherit
WSF_DEFAULT_SERVICE [APPLICATION_EXECUTION]
redefine
initialize
end
create
make_and_launch
feature {NONE} -- Initialization
initialize
-- Initialize current service.
do
set_service_option ("port", 9090)
end
end
</code>
Where APPLICATION_EXECUTION is an implementation of the WSF_EXECUTION interface (with the `make' creation procedure).
In addition to add better and safer concurrency support, there are other advantages:
- we now have a clear separation between the service launcher, and the request execution itself.
- the WSF_EXECUTION is created per request, with two main attributes <code>request: WSF_REQUEST</code> and <code>response: WSF_RESPONSE</code>.
# How to migrate to new design
- you can check the various example from the EWF repository, there should all be migrated to new design and comparing previous and new code, this will show you how the migration was done.
- a frequent process:
- identify the root class of your service, (the class implementing the WSF_SERVICE), let us name it APPLICATION_SERVICE
- copy the APPLICATION_SERVICE file to APPLICATION_EXECUTION file.
- change the class name to be APPLICATION_EXECUTION, and replace _SERVICE occurences by _EXECUTION (note the new WSF_ROUTED_EXECUTION and so on, which are mainly migration from previous WSF_ROUTED_SERVICE .., and also WSF_FILTERED_ROUTED_EXECUTION which is new.
- replace "make_and_launch" by "make", remove the initialize redefinition if any.
- in the APPLICATION_SERVICE class, remove most of the ROUTED, FILTERED ... inheritance, and keep WSF_DEFAULT_SERVICE, with a new formal generic i.e WSF_DEFAULT_SERVICE [APPLICATION_EXECUTION].
- in the eventual redefined initialize, remove code related to routers, filters, ...
- remove all the execution related code.
- And you should be done.
- To be short, this is mostly creating a new _EXECUTION class, and move the execution related code into this class from the _SERVICE class.
- Then, you can replace the usage of nino connector by using the new "Standalone" connector, and switch to SCOOP concurrency mode, to ensure you are not messing up with concurrency. Your own code/libraris may not be SCOOP compliant, we recommend to migrate to SCOOP, but as an intermediate solutioņ, you can use the other concurrency mode (none or thread).
Note: the new design impacts the _SERVICE classes, connectors, but WSF_REQUEST, WSF_RESPONSE , WSF_ROUTER are compatible, so the migration is really easy.
We may take the opportunity to update the design deeper according to user feedback, and eventually "wsf" library will be renamed "wsf2".
This is work in progress, all comments , feedback, suggestions, bug report are welcome.
Hopefully before the final version of the new design is out.

View File

@@ -1,13 +1,11 @@
note
description : "Basic Service that Generates Plain Text"
date : "$Date$"
revision : "$Revision$"
description: "Basic Service launcher"
class
APPLICATION
inherit
WSF_DEFAULT_SERVICE
WSF_DEFAULT_SERVICE [APPLICATION_EXECUTION]
redefine
initialize
end
@@ -23,15 +21,4 @@ feature {NONE} -- Initialization
set_service_option ("port", 9090)
end
feature -- Basic operations
execute (req: WSF_REQUEST; res: WSF_RESPONSE)
-- Execute the incomming request
do
-- To send a response we need to setup, the status code and
-- the response headers.
res.put_header ({HTTP_STATUS_CODE}.ok, <<["Content-Type", "text/plain"], ["Content-Length", "11"]>>)
res.put_string ("Hello World")
end
end

View File

@@ -0,0 +1,26 @@
note
description : "Basic Service that Generates Plain Text"
date : "$Date$"
revision : "$Revision$"
class
APPLICATION_EXECUTION
inherit
WSF_EXECUTION
create
make
feature -- Basic operations
execute
-- Execute the incomming request
do
-- To send a response we need to setup, the status code and
-- the response headers.
response.put_header ({HTTP_STATUS_CODE}.ok, <<["Content-Type", "text/plain"], ["Content-Length", "11"]>>)
response.put_string ("Hello World")
end
end

View File

@@ -1,13 +1,11 @@
note
description : "Basic Service that Generate HTML"
date : "$Date$"
revision : "$Revision$"
description: "Basic Service launcher"
class
APPLICATION
inherit
WSF_DEFAULT_SERVICE
WSF_DEFAULT_SERVICE [APPLICATION_EXECUTION]
redefine
initialize
end
@@ -23,55 +21,4 @@ feature {NONE} -- Initialization
set_service_option ("port", 9090)
end
feature -- Basic operations
execute (req: WSF_REQUEST; res: WSF_RESPONSE)
-- Execute the incomming request
do
-- To send a response we need to setup, the status code and
-- the response headers.
res.put_header ({HTTP_STATUS_CODE}.ok, <<["Content-Type", "text/html"], ["Content-Length", web_page.count.out]>>)
res.put_string (web_page)
end
web_page: STRING = "[
<!DOCTYPE html>
<html>
<head>
<title>Resume</title>
</head>
<body>
<div id="header">
<p id="name">Your Name Here</p>
<a href="mailto:you@yourdomain.com"><p id="email">you@yourdomain.com</p></a>
</div>
<div class="left"></div>
<div class="right">
<h4>Objective</h4>
<p>To take a position as a software engineer.</p>
<h4>Experience</h4>
<p>Junior Developer, Software Company (2010 - Present)</p>
<ul>
<li>Designed and implemented end-user features for Flagship Product</li>
<li>Wrote third-party JavaScript and Eiffel libraries</li>
</ul>
<h4>Skills</h4>
<p>Languages: C#, JavaScript, Python, Ruby, Eiffel</p>
<p>Frameworks: .NET, Node.js, Django, Ruby on Rails, EWF</p>
<h4>Education</h4>
<p>BS, Economics, My University</p>
<ul>
<li>Award for best senior thesis</li>
<li>GPA: 3.8</li>
</ul>
</div>
<div id="footer">
<p>123 Your Street, Anytown, State 12345-6789 | Tel: (555) 555-5555</p>
</div>
</body>
</html>
]"
end

View File

@@ -0,0 +1,66 @@
note
description : "Basic Service that Generate HTML"
date : "$Date$"
revision : "$Revision$"
class
APPLICATION_EXECUTION
inherit
WSF_EXECUTION
create
make
feature -- Basic operations
execute
-- Execute the incomming request
do
-- To send a response we need to setup, the status code and
-- the response headers.
response.put_header ({HTTP_STATUS_CODE}.ok, <<["Content-Type", "text/html"], ["Content-Length", web_page.count.out]>>)
response.put_string (web_page)
end
web_page: STRING = "[
<!DOCTYPE html>
<html>
<head>
<title>Resume</title>
</head>
<body>
<div id="header">
<p id="name">Your Name Here</p>
<a href="mailto:you@yourdomain.com"><p id="email">you@yourdomain.com</p></a>
</div>
<div class="left"></div>
<div class="right">
<h4>Objective</h4>
<p>To take a position as a software engineer.</p>
<h4>Experience</h4>
<p>Junior Developer, Software Company (2010 - Present)</p>
<ul>
<li>Designed and implemented end-user features for Flagship Product</li>
<li>Wrote third-party JavaScript and Eiffel libraries</li>
</ul>
<h4>Skills</h4>
<p>Languages: C#, JavaScript, Python, Ruby, Eiffel</p>
<p>Frameworks: .NET, Node.js, Django, Ruby on Rails, EWF</p>
<h4>Education</h4>
<p>BS, Economics, My University</p>
<ul>
<li>Award for best senior thesis</li>
<li>GPA: 3.8</li>
</ul>
</div>
<div id="footer">
<p>123 Your Street, Anytown, State 12345-6789 | Tel: (555) 555-5555</p>
</div>
</body>
</html>
]"
end

View File

@@ -1,13 +1,11 @@
note
description : "Basic Service that show how to use common Status Code"
date : "$Date$"
revision : "$Revision$"
description: "Basic Service launcher"
class
APPLICATION
inherit
WSF_DEFAULT_SERVICE
WSF_DEFAULT_SERVICE [APPLICATION_EXECUTION]
redefine
initialize
end
@@ -23,25 +21,4 @@ feature {NONE} -- Initialization
set_service_option ("port", 9090)
end
feature -- Basic operations
execute (req: WSF_REQUEST; res: WSF_RESPONSE)
-- Execute the incomming request
do
-- To send a response we need to setup, the status code and
-- the response headers.
res.put_header ({HTTP_STATUS_CODE}.ok, <<["Content-Type", "text/csv"],["Content-Disposition","attachment;filename=Report.xls"],["Content-Length", sheet.count.out]>>)
res.put_string (sheet)
end
-- ,["Content-Disposition","attachment;filename=Report.xls"]
sheet: STRING ="[
Q1 Q2 Q3 Q4 Total
Cherries 78 87 92 29 =SUM(B2:E2)
Grapes 77 86 93 30 =SUM(B3:E3)
]"
end

View File

@@ -0,0 +1,36 @@
note
description : "Basic Service that show how to use common Status Code"
date : "$Date$"
revision : "$Revision$"
class
APPLICATION_EXECUTION
inherit
WSF_EXECUTION
create
make
feature -- Basic operations
execute
-- Execute the incomming request
do
-- To send a response we need to setup, the status code and
-- the response headers.
response.put_header ({HTTP_STATUS_CODE}.ok, <<["Content-Type", "text/csv"],["Content-Disposition","attachment;filename=Report.xls"],["Content-Length", sheet.count.out]>>)
response.put_string (sheet)
end
-- ,["Content-Disposition","attachment;filename=Report.xls"]
sheet: STRING ="[
Q1 Q2 Q3 Q4 Total
Cherries 78 87 92 29 =SUM(B2:E2)
Grapes 77 86 93 30 =SUM(B3:E3)
]"
end

View File

@@ -1,13 +1,11 @@
note
description : "Basic Service that build a generic front end for the most used search engines."
date : "$Date$"
revision : "$Revision$"
description: "Basic Service launcher"
class
APPLICATION
inherit
WSF_DEFAULT_SERVICE
WSF_DEFAULT_SERVICE [APPLICATION_EXECUTION]
redefine
initialize
end
@@ -23,46 +21,4 @@ feature {NONE} -- Initialization
set_service_option ("port", 9090)
end
feature -- Basic operations
execute (req: WSF_REQUEST; res: WSF_RESPONSE)
-- Execute the incomming request
local
l_message: STRING
do
-- (1) To send a response we need to setup, the status code and the response headers.
-- res.put_header ({HTTP_STATUS_CODE}.ok, <<["Content-Type", "text/html"], ["Content-Length", web_page.count.out]>>)
-- res.put_string (web_page)
-- (2) Using put_header_line
-- res.set_status_code ({HTTP_STATUS_CODE}.ok)
-- res.put_header_line ("Content-Type:text/html")
res.put_header_line ("Content-Length:"+ web_page.count.out)
res.put_header_line ("Content-Type:text/plain")
res.put_string (web_page)
end
feature -- Home Page
web_page: STRING = "[
<!DOCTYPE html>
<html>
<head>
<title>EWF Headers Responses</title>
</head>
<body>
<div class="right">
<h2>Example Header Response</h2>
<p>Response headers</p>
</div>
<div id="footer">
<p>EWF Response Header</p>
</div>
</body>
</html>
]"
end

View File

@@ -0,0 +1,57 @@
note
description : "Basic Service that build a generic front end for the most used search engines."
date : "$Date$"
revision : "$Revision$"
class
APPLICATION_EXECUTION
inherit
WSF_EXECUTION
create
make
feature -- Basic operations
execute
-- Execute the incomming request
local
l_message: STRING
do
-- (1) To send a response we need to setup, the status code and the response headers.
-- response.put_header ({HTTP_STATUS_CODE}.ok, <<["Content-Type", "text/html"], ["Content-Length", web_page.count.out]>>)
-- response.put_string (web_page)
-- (2) Using put_header_line
-- response.set_status_code ({HTTP_STATUS_CODE}.ok)
-- response.put_header_line ("Content-Type:text/html")
response.put_header_line ("Content-Length:"+ web_page.count.out)
response.put_header_line ("Content-Type:text/plain")
response.put_string (web_page)
end
feature -- Home Page
web_page: STRING = "[
<!DOCTYPE html>
<html>
<head>
<title>EWF Headers Responses</title>
</head>
<body>
<div class="right">
<h2>Example Header Response</h2>
<p>Response headers</p>
</div>
<div id="footer">
<p>EWF Response Header</p>
</div>
</body>
</html>
]"
end

View File

@@ -1,13 +1,11 @@
note
description : "Basic Service that build a generic front end for the most used search engines."
date : "$Date$"
revision : "$Revision$"
description: "Basic Service launcher"
class
APPLICATION
inherit
WSF_DEFAULT_SERVICE
WSF_DEFAULT_SERVICE [APPLICATION_EXECUTION]
redefine
initialize
end
@@ -23,161 +21,4 @@ feature {NONE} -- Initialization
set_service_option ("port", 9090)
end
feature -- Basic operations
execute (req: WSF_REQUEST; res: WSF_RESPONSE)
-- Execute the incomming request
local
l_message: STRING
do
-- To send a response we need to setup, the status code and
-- the response headers.
if req.is_get_request_method then
if req.path_info.same_string ("/") then
res.put_header ({HTTP_STATUS_CODE}.ok, <<["Content-Type", "text/html"], ["Content-Length", web_page.count.out]>>)
res.put_string (web_page)
else
send_resouce_not_found (req, res)
end
elseif req.is_post_request_method then
if req.path_info.same_string ("/search") then
if attached {WSF_STRING} req.form_parameter ("query") as l_query then
if attached {WSF_STRING} req.form_parameter ("engine") as l_engine then
if attached {STRING} map.at (l_engine.value) as l_engine_url then
l_engine_url.append (l_query.value)
send_redirect (req, res, l_engine_url)
-- res.redirect_now (l_engine_url)
else
send_bad_request (req, res, " <strong>search engine: " + l_engine.value + "</strong> not supported,<br> try with Google or Bing")
end
else
send_bad_request (req, res, " <strong>search engine</strong> not selected")
end
else
send_bad_request (req, res, " form_parameter <strong>query</strong> is not present")
end
else
send_resouce_not_found (req, res)
end
else
create l_message.make_from_string (message_template)
l_message.replace_substring_all ("$title", "Method Not Allowed")
l_message.replace_substring_all ("$status", "Method Not Allowed 405")
-- Method not allowed
res.put_header ({HTTP_STATUS_CODE}.method_not_allowed, <<["Content-Type", "text/html"], ["Content-Length", l_message.count.out]>>)
res.put_string (l_message)
end
end
feature -- Engine Map
map : STRING_TABLE[STRING]
do
create Result.make (2)
Result.put ("http://www.google.com/search?q=", "Google")
Result.put ("http://www.bing.com/search?q=", "Bing")
end
feature -- Redirect
send_redirect (req: WSF_REQUEST; res: WSF_RESPONSE; a_location: READABLE_STRING_32)
-- Redirect to `a_location'
local
h: HTTP_HEADER
do
create h.make
h.put_content_type_text_html
h.put_current_date
h.put_location (a_location)
res.set_status_code ({HTTP_STATUS_CODE}.see_other)
res.put_header_text (h.string)
end
feature -- Bad Request
send_bad_request (req: WSF_REQUEST; res: WSF_RESPONSE; description: STRING)
local
l_message: STRING
do
create l_message.make_from_string (message_template)
l_message.replace_substring_all ("$title", "Bad Request")
l_message.replace_substring_all ("$status", "Bad Request" + description)
res.put_header ({HTTP_STATUS_CODE}.bad_request, <<["Content-Type", "text/html"], ["Content-Length", l_message.count.out]>>)
res.put_string (l_message)
end
feature -- Resource not found
send_resouce_not_found (req: WSF_REQUEST; res: WSF_RESPONSE)
local
l_message: STRING
do
create l_message.make_from_string (message_template)
l_message.replace_substring_all ("$title", "Resource not found")
l_message.replace_substring_all ("$status", "Resource " + req.request_uri + " not found 404")
res.put_header ({HTTP_STATUS_CODE}.not_found, <<["Content-Type", "text/html"], ["Content-Length", l_message.count.out]>>)
res.put_string (l_message)
end
feature -- Home Page
web_page: STRING = "[
<!DOCTYPE html>
<html>
<head>
<title>Generic Search Engine</title>
</head>
<body>
<div class="right">
<h2>Generic Search Engine</h2>
<form method="POST" action="/search" target="_blank">
<fieldset>
Search: <input type="search" name="query" placeholder="EWF framework"><br>
<div>
<input type="radio" name="engine" value="Google" checked><img src="http://ebizmba.ebizmbainc.netdna-cdn.com/images/logos/google.gif" height="24" width="42">
</div>
<div>
<input type="radio" name="engine" value="Bing"><img src="http://ebizmba.ebizmbainc.netdna-cdn.com/images/logos/bing.gif" height="24" width="42">
</div><br>
</fieldset>
<input type="submit">
</form>
</div>
<div id="footer">
<p><a href="http://www.ebizmba.com/articles/search-engines">Top 15 Most Popular Search Engines | March 2015</a></p>
</div>
</body>
</html>
]"
feature -- Generic Message
message_template: STRING="[
<!DOCTYPE html>
<html>
<head>
<title>$title</title>
</head>
<body>
<div id="header">
<p id="name">Use a tool to see the request and header details, for example (Developers tools in Chrome or Firebugs in Firefox)</p>
</div>
<div class="left"></div>
<div class="right">
<h4>This page is an example of $status</h4>
<div id="footer">
<p><a href="/">Back Home</a></p>
</div>
</body>
</html>
]"
end

View File

@@ -0,0 +1,172 @@
note
description : "Basic Service that build a generic front end for the most used search engines."
date : "$Date$"
revision : "$Revision$"
class
APPLICATION_EXECUTION
inherit
WSF_EXECUTION
create
make
feature -- Basic operations
execute
-- Execute the incomming request
local
l_message: STRING
do
-- To send a response we need to setup, the status code and
-- the response headers.
if request.is_get_request_method then
if request.path_info.same_string ("/") then
response.put_header ({HTTP_STATUS_CODE}.ok, <<["Content-Type", "text/html"], ["Content-Length", web_page.count.out]>>)
response.put_string (web_page)
else
send_resouce_not_found (request, response)
end
elseif request.is_post_request_method then
if request.path_info.same_string ("/search") then
if attached {WSF_STRING} request.form_parameter ("query") as l_query then
if attached {WSF_STRING} request.form_parameter ("engine") as l_engine then
if attached {STRING} map.at (l_engine.value) as l_engine_url then
l_engine_url.append (l_query.value)
send_redirect (request, response, l_engine_url)
-- response.redirect_now (l_engine_url)
else
send_bad_request (request, response, " <strong>search engine: " + l_engine.value + "</strong> not supported,<br> try with Google or Bing")
end
else
send_bad_request (request, response, " <strong>search engine</strong> not selected")
end
else
send_bad_request (request, response, " form_parameter <strong>query</strong> is not present")
end
else
send_resouce_not_found (request, response)
end
else
create l_message.make_from_string (message_template)
l_message.replace_substring_all ("$title", "Method Not Allowed")
l_message.replace_substring_all ("$status", "Method Not Allowed 405")
-- Method not allowed
response.put_header ({HTTP_STATUS_CODE}.method_not_allowed, <<["Content-Type", "text/html"], ["Content-Length", l_message.count.out]>>)
response.put_string (l_message)
end
end
feature -- Engine Map
map : STRING_TABLE[STRING]
do
create Result.make (2)
Result.put ("http://www.google.com/search?q=", "Google")
Result.put ("http://www.bing.com/search?q=", "Bing")
end
feature -- Redirect
send_redirect (req: WSF_REQUEST; res: WSF_RESPONSE; a_location: READABLE_STRING_32)
-- Redirect to `a_location'
local
h: HTTP_HEADER
do
create h.make
h.put_content_type_text_html
h.put_current_date
h.put_location (a_location)
res.set_status_code ({HTTP_STATUS_CODE}.see_other)
res.put_header_text (h.string)
end
feature -- Bad Request
send_bad_request (req: WSF_REQUEST; res: WSF_RESPONSE; description: STRING)
local
l_message: STRING
do
create l_message.make_from_string (message_template)
l_message.replace_substring_all ("$title", "Bad Request")
l_message.replace_substring_all ("$status", "Bad Request" + description)
res.put_header ({HTTP_STATUS_CODE}.bad_request, <<["Content-Type", "text/html"], ["Content-Length", l_message.count.out]>>)
res.put_string (l_message)
end
feature -- Resource not found
send_resouce_not_found (req: WSF_REQUEST; res: WSF_RESPONSE)
local
l_message: STRING
do
create l_message.make_from_string (message_template)
l_message.replace_substring_all ("$title", "Resource not found")
l_message.replace_substring_all ("$status", "Resource " + req.request_uri + " not found 404")
res.put_header ({HTTP_STATUS_CODE}.not_found, <<["Content-Type", "text/html"], ["Content-Length", l_message.count.out]>>)
res.put_string (l_message)
end
feature -- Home Page
web_page: STRING = "[
<!DOCTYPE html>
<html>
<head>
<title>Generic Search Engine</title>
</head>
<body>
<div class="right">
<h2>Generic Search Engine</h2>
<form method="POST" action="/search" target="_blank">
<fieldset>
Search: <input type="search" name="query" placeholder="EWF framework"><br>
<div>
<input type="radio" name="engine" value="Google" checked><img src="http://ebizmba.ebizmbainc.netdna-cdn.com/images/logos/google.gif" height="24" width="42">
</div>
<div>
<input type="radio" name="engine" value="Bing"><img src="http://ebizmba.ebizmbainc.netdna-cdn.com/images/logos/bing.gif" height="24" width="42">
</div><br>
</fieldset>
<input type="submit">
</form>
</div>
<div id="footer">
<p><a href="http://www.ebizmba.com/articles/search-engines">Top 15 Most Popular Search Engines | March 2015</a></p>
</div>
</body>
</html>
]"
feature -- Generic Message
message_template: STRING="[
<!DOCTYPE html>
<html>
<head>
<title>$title</title>
</head>
<body>
<div id="header">
<p id="name">Use a tool to see the request and header details, for example (Developers tools in Chrome or Firebugs in Firefox)</p>
</div>
<div class="left"></div>
<div class="right">
<h4>This page is an example of $status</h4>
<div id="footer">
<p><a href="/">Back Home</a></p>
</div>
</body>
</html>
]"
end

View File

@@ -1,13 +1,11 @@
note
description : "Basic Service that a simple web page to show the most common status codes"
date : "$Date$"
revision : "$Revision$"
description: "Basic Service launcher"
class
APPLICATION
inherit
WSF_DEFAULT_SERVICE
WSF_DEFAULT_SERVICE [APPLICATION_EXECUTION]
redefine
initialize
end
@@ -23,127 +21,4 @@ feature {NONE} -- Initialization
set_service_option ("port", 9090)
end
feature -- Basic operations
execute (req: WSF_REQUEST; res: WSF_RESPONSE)
-- Execute the incomming request
local
l_message: STRING
do
-- To send a response we need to setup, the status code and
-- the response headers.
if req.is_get_request_method then
if req.path_info.same_string ("/") then
res.put_header ({HTTP_STATUS_CODE}.ok, <<["Content-Type", "text/html"], ["Content-Length", web_page.count.out]>>)
res.put_string (web_page)
elseif req.path_info.same_string ("/redirect") then
send_redirect (req, res, "https://httpwg.github.io/")
elseif req.path_info.same_string ("/bad_request") then
-- Here you can do some logic for example log, send emails to register the error, before to send the response.
create l_message.make_from_string (message_template)
l_message.replace_substring_all ("$title", "Bad Request")
l_message.replace_substring_all ("$status", "Bad Request 400")
res.put_header ({HTTP_STATUS_CODE}.bad_request, <<["Content-Type", "text/html"], ["Content-Length", l_message.count.out]>>)
res.put_string (l_message)
elseif req.path_info.same_string ("/internal_error") then
-- Here you can do some logic for example log, send emails to register the error, before to send the response.
create l_message.make_from_string (message_template)
l_message.replace_substring_all ("$title", "Internal Server Error")
l_message.replace_substring_all ("$status", "Internal Server Error 500")
res.put_header ({HTTP_STATUS_CODE}.internal_server_error, <<["Content-Type", "text/html"], ["Content-Length", l_message.count.out]>>)
res.put_string (l_message)
else
create l_message.make_from_string (message_template)
l_message.replace_substring_all ("$title", "Resource not found")
l_message.replace_substring_all ("$status", "Resource not found 400")
res.put_header ({HTTP_STATUS_CODE}.not_found, <<["Content-Type", "text/html"], ["Content-Length", l_message.count.out]>>)
res.put_string (l_message)
end
else
create l_message.make_from_string (message_template)
l_message.replace_substring_all ("$title", "Method Not Allowed")
l_message.replace_substring_all ("$status", "Method Not Allowed 405")
-- Method not allowed
res.put_header ({HTTP_STATUS_CODE}.method_not_allowed, <<["Content-Type", "text/html"], ["Content-Length", l_message.count.out]>>)
res.put_string (l_message)
end
end
feature -- Home Page
send_redirect (req: WSF_REQUEST; res: WSF_RESPONSE; a_location: READABLE_STRING_32)
-- Redirect to `a_location'
local
h: HTTP_HEADER
do
create h.make
h.put_content_type_text_html
h.put_current_date
h.put_location (a_location)
res.set_status_code ({HTTP_STATUS_CODE}.see_other)
res.put_header_text (h.string)
end
web_page: STRING = "[
<!DOCTYPE html>
<html>
<head>
<title>Example showing common status codes</title>
</head>
<body>
<div id="header">
<p id="name">Use a tool to see the request and header details, for example (Developers tools in Chrome or Firebugs in Firefox)</p>
</div>
<div class="left"></div>
<div class="right">
<h4>This page is an example of Status Code 200</h4>
<h4> Redirect Example </h4>
<p> Click on the following link will redirect you to the HTTP Specifcation, we can do the redirect from the HTML directly but
here we want to show you an exmaple, where you can do something before to send a redirect <a href="/redirect">Redirect</a></p>
<h4> Bad Request </h4>
<p> Click on the following link, the server will answer with a 400 error, check the status code <a href="/bad_request">Bad Request</a></p>
<h4> Internal Server Error </h4>
<p> Click on the following link, the server will answer with a 500 error, check the status code <a href="/internal_error">Internal Error</a></p>
<h4> Resource not found </h4>
<p> Click on the following link or add to the end of the url something like /1030303 the server will answer with a 404 error, check the status code <a href="/not_foundd">Not found</a></p>
</div>
<div id="footer">
<p>Useful links for status codes <a href="httpstat.us">httpstat.us</a> and <a href="httpbing.org">httpbin.org</a></p>
</div>
</body>
</html>
]"
feature -- Generic Message
message_template: STRING="[
<!DOCTYPE html>
<html>
<head>
<title>$title</title>
</head>
<body>
<div id="header">
<p id="name">Use a tool to see the request and header details, for example (Developers tools in Chrome or Firebugs in Firefox)</p>
</div>
<div class="left"></div>
<div class="right">
<h4>This page is an example of $status</h4>
<div id="footer">
<p><a href="/">Back Home</a></p>
</div>
</body>
</html>
]"
end

View File

@@ -0,0 +1,138 @@
note
description : "Basic Service that a simple web page to show the most common status codes"
date : "$Date$"
revision : "$Revision$"
class
APPLICATION_EXECUTION
inherit
WSF_EXECUTION
create
make
feature -- Basic operations
execute
-- Execute the incomming request
local
l_message: STRING
do
-- To send a response we need to setup, the status code and
-- the response headers.
if request.is_get_request_method then
if request.path_info.same_string ("/") then
response.put_header ({HTTP_STATUS_CODE}.ok, <<["Content-Type", "text/html"], ["Content-Length", web_page.count.out]>>)
response.put_string (web_page)
elseif request.path_info.same_string ("/redirect") then
send_redirect (request, response, "https://httpwg.github.io/")
elseif request.path_info.same_string ("/bad_request") then
-- Here you can do some logic for example log, send emails to register the error, before to send the response.
create l_message.make_from_string (message_template)
l_message.replace_substring_all ("$title", "Bad Request")
l_message.replace_substring_all ("$status", "Bad Request 400")
response.put_header ({HTTP_STATUS_CODE}.bad_request, <<["Content-Type", "text/html"], ["Content-Length", l_message.count.out]>>)
response.put_string (l_message)
elseif request.path_info.same_string ("/internal_error") then
-- Here you can do some logic for example log, send emails to register the error, before to send the response.
create l_message.make_from_string (message_template)
l_message.replace_substring_all ("$title", "Internal Server Error")
l_message.replace_substring_all ("$status", "Internal Server Error 500")
response.put_header ({HTTP_STATUS_CODE}.internal_server_error, <<["Content-Type", "text/html"], ["Content-Length", l_message.count.out]>>)
response.put_string (l_message)
else
create l_message.make_from_string (message_template)
l_message.replace_substring_all ("$title", "Resource not found")
l_message.replace_substring_all ("$status", "Resource not found 400")
response.put_header ({HTTP_STATUS_CODE}.not_found, <<["Content-Type", "text/html"], ["Content-Length", l_message.count.out]>>)
response.put_string (l_message)
end
else
create l_message.make_from_string (message_template)
l_message.replace_substring_all ("$title", "Method Not Allowed")
l_message.replace_substring_all ("$status", "Method Not Allowed 405")
-- Method not allowed
response.put_header ({HTTP_STATUS_CODE}.method_not_allowed, <<["Content-Type", "text/html"], ["Content-Length", l_message.count.out]>>)
response.put_string (l_message)
end
end
feature -- Home Page
send_redirect (req: WSF_REQUEST; res: WSF_RESPONSE; a_location: READABLE_STRING_32)
-- Redirect to `a_location'
local
h: HTTP_HEADER
do
create h.make
h.put_content_type_text_html
h.put_current_date
h.put_location (a_location)
res.set_status_code ({HTTP_STATUS_CODE}.see_other)
res.put_header_text (h.string)
end
web_page: STRING = "[
<!DOCTYPE html>
<html>
<head>
<title>Example showing common status codes</title>
</head>
<body>
<div id="header">
<p id="name">Use a tool to see the request and header details, for example (Developers tools in Chrome or Firebugs in Firefox)</p>
</div>
<div class="left"></div>
<div class="right">
<h4>This page is an example of Status Code 200</h4>
<h4> Redirect Example </h4>
<p> Click on the following link will redirect you to the HTTP Specifcation, we can do the redirect from the HTML directly but
here we want to show you an exmaple, where you can do something before to send a redirect <a href="/redirect">Redirect</a></p>
<h4> Bad Request </h4>
<p> Click on the following link, the server will answer with a 400 error, check the status code <a href="/bad_request">Bad Request</a></p>
<h4> Internal Server Error </h4>
<p> Click on the following link, the server will answer with a 500 error, check the status code <a href="/internal_error">Internal Error</a></p>
<h4> Resource not found </h4>
<p> Click on the following link or add to the end of the url something like /1030303 the server will answer with a 404 error, check the status code <a href="/not_foundd">Not found</a></p>
</div>
<div id="footer">
<p>Useful links for status codes <a href="httpstat.us">httpstat.us</a> and <a href="httpbing.org">httpbin.org</a></p>
</div>
</body>
</html>
]"
feature -- Generic Message
message_template: STRING="[
<!DOCTYPE html>
<html>
<head>
<title>$title</title>
</head>
<body>
<div id="header">
<p id="name">Use a tool to see the request and header details, for example (Developers tools in Chrome or Firebugs in Firefox)</p>
</div>
<div class="left"></div>
<div class="right">
<h4>This page is an example of $status</h4>
<div id="footer">
<p><a href="/">Back Home</a></p>
</div>
</body>
</html>
]"
end

View File

@@ -1,13 +1,11 @@
note
description : "Basic Service that build a generic front to demonstrate the use of Cookies"
date : "$Date$"
revision : "$Revision$"
description: "Basic Service launcher"
class
APPLICATION
inherit
WSF_DEFAULT_SERVICE
WSF_DEFAULT_SERVICE [APPLICATION_EXECUTION]
redefine
initialize
end
@@ -21,133 +19,6 @@ feature {NONE} -- Initialization
-- Initialize current service.
do
set_service_option ("port", 9090)
set_service_option ("verbose",True)
end
feature -- Basic operations
execute (req: WSF_REQUEST; res: WSF_RESPONSE)
-- Execute the incomming request
local
l_message: STRING
l_header: HTTP_HEADER
l_time: HTTP_DATE
l_cookies: STRING
l_answer: STRING
do
-- all the cookies
create l_cookies.make_empty
across req.cookies as ic loop
l_cookies.append (ic.item.name)
l_cookies.append("<br>")
end
if req.path_info.same_string ("/") then
create l_header.make
create l_answer.make_from_string (web_page)
if req.cookie ("_EWF_Cookie") = Void then
-- First access the the home page, find a cookie with specific name `_EWF_Cookie'
l_answer.replace_substring_all ("$header_title", "Hey, thanks for access our cool site, this is your first acess")
l_answer.replace_substring_all ("$cookies", l_cookies)
create l_time.make_now_utc
l_time.date_time.day_add (40)
l_header.put_cookie_with_expiration_date ("_EWF_Cookie", "EXAMPLE",l_time.date_time, "", Void, False, True)
else
-- No a new access
l_answer.replace_substring_all ("$header_title", "Welcome back, please check all the new things we have!!!")
l_answer.replace_substring_all ("$cookies", l_cookies)
end
l_header.put_content_type_text_html
l_header.put_content_length (l_answer.count)
res.put_header_text (l_header.string)
res.put_string (l_answer)
elseif req.path_info.same_string ("/visitors") then
create l_header.make
create l_answer.make_from_string (visit_page)
if req.cookie ("_visits") = Void then
-- First access the the visit page, find a cookie with specific name `_visits'
l_answer.replace_substring_all ("$visit", "1")
l_answer.replace_substring_all ("$cookies", l_cookies)
create l_time.make_now_utc
l_time.date_time.day_add (40)
l_header.put_cookie_with_expiration_date ("_visits", "1",l_time.date_time, "/visitors", Void, False, True)
else
if attached {WSF_STRING} req.cookie ("_visits") as l_visit then
create l_time.make_now_utc
l_time.date_time.day_add (40)
l_answer.replace_substring_all ("$visit", (l_visit.value.to_integer + 1).out )
l_answer.replace_substring_all ("$cookies", l_cookies)
l_header.put_cookie_with_expiration_date ("_visits", (l_visit.value.to_integer + 1).out,l_time.date_time, "/visitors", Void, False, True)
end
end
create l_time.make_now_utc
l_time.date_time.second_add (120)
l_header.put_content_type_text_html
-- This cookie expires in 120 seconds, its valid for 120 seconds
l_header.put_cookie_with_expiration_date ("_Framework", "EWF",l_time.date_time, "/", Void, False, True)
-- This is a session cookie, valid only to the current browsing session.
l_header.put_cookie ("Session", "Cookie",Void, "/", Void, False, True)
l_header.put_content_length (l_answer.count)
res.add_header_text (l_header.string)
res.put_string (l_answer)
end
end
feature -- Home Page
web_page: STRING = "[
<!DOCTYPE html>
<html>
<head>
<title>EWF Handling Cookies</title>
</head>
<body>
<div class="right">
<h2>$header_title</h2>
</div>
<div class="right">
<a href="/visitors">Visitors</a>
</div>
<div>
<h3>Cookies for the home page</h3>
$cookies
</div>
</body>
</html>
]"
visit_page: STRING = "[
<!DOCTYPE html>
<html>
<head>
<title>EWF Handling Visit Page</title>
</head>
<body>
<div class="right">
<h2>The number of visits is $visit</h2>
</div>
<div>
<h3>Cookies for the Visit page</h3>
$cookies
</div>
</br>
<div>
Back to <a href="/"> Home </a>
</div>
<div id="footer">
<p>EWF Example Cookies</p>
</div>
</body>
</html>
]"
end

View File

@@ -0,0 +1,141 @@
note
description : "Basic Service that build a generic front to demonstrate the use of Cookies"
date : "$Date$"
revision : "$Revision$"
class
APPLICATION_EXECUTION
inherit
WSF_EXECUTION
create
make
feature -- Basic operations
execute
-- Execute the incomming request
local
l_message: STRING
l_header: HTTP_HEADER
l_time: HTTP_DATE
l_cookies: STRING
l_answer: STRING
do
-- all the cookies
create l_cookies.make_empty
across request.cookies as ic loop
l_cookies.append (ic.item.name)
l_cookies.append("<br>")
end
if request.path_info.same_string ("/") then
create l_header.make
create l_answer.make_from_string (web_page)
if request.cookie ("_EWF_Cookie") = Void then
-- First access the the home page, find a cookie with specific name `_EWF_Cookie'
l_answer.replace_substring_all ("$header_title", "Hey, thanks for access our cool site, this is your first acess")
l_answer.replace_substring_all ("$cookies", l_cookies)
create l_time.make_now_utc
l_time.date_time.day_add (40)
l_header.put_cookie_with_expiration_date ("_EWF_Cookie", "EXAMPLE",l_time.date_time, "", Void, False, True)
else
-- No a new access
l_answer.replace_substring_all ("$header_title", "Welcome back, please check all the new things we have!!!")
l_answer.replace_substring_all ("$cookies", l_cookies)
end
l_header.put_content_type_text_html
l_header.put_content_length (l_answer.count)
response.put_header_text (l_header.string)
response.put_string (l_answer)
elseif request.path_info.same_string ("/visitors") then
create l_header.make
create l_answer.make_from_string (visit_page)
if request.cookie ("_visits") = Void then
-- First access the the visit page, find a cookie with specific name `_visits'
l_answer.replace_substring_all ("$visit", "1")
l_answer.replace_substring_all ("$cookies", l_cookies)
create l_time.make_now_utc
l_time.date_time.day_add (40)
l_header.put_cookie_with_expiration_date ("_visits", "1",l_time.date_time, "/visitors", Void, False, True)
else
if attached {WSF_STRING} request.cookie ("_visits") as l_visit then
create l_time.make_now_utc
l_time.date_time.day_add (40)
l_answer.replace_substring_all ("$visit", (l_visit.value.to_integer + 1).out )
l_answer.replace_substring_all ("$cookies", l_cookies)
l_header.put_cookie_with_expiration_date ("_visits", (l_visit.value.to_integer + 1).out,l_time.date_time, "/visitors", Void, False, True)
end
end
create l_time.make_now_utc
l_time.date_time.second_add (120)
l_header.put_content_type_text_html
-- This cookie expires in 120 seconds, its valid for 120 seconds
l_header.put_cookie_with_expiration_date ("_Framework", "EWF",l_time.date_time, "/", Void, False, True)
-- This is a session cookie, valid only to the current browsing session.
l_header.put_cookie ("Session", "Cookie",Void, "/", Void, False, True)
l_header.put_content_length (l_answer.count)
response.add_header_text (l_header.string)
response.put_string (l_answer)
end
end
feature -- Home Page
web_page: STRING = "[
<!DOCTYPE html>
<html>
<head>
<title>EWF Handling Cookies</title>
</head>
<body>
<div class="right">
<h2>$header_title</h2>
</div>
<div class="right">
<a href="/visitors">Visitors</a>
</div>
<div>
<h3>Cookies for the home page</h3>
$cookies
</div>
</body>
</html>
]"
visit_page: STRING = "[
<!DOCTYPE html>
<html>
<head>
<title>EWF Handling Visit Page</title>
</head>
<body>
<div class="right">
<h2>The number of visits is $visit</h2>
</div>
<div>
<h3>Cookies for the Visit page</h3>
$cookies
</div>
</br>
<div>
Back to <a href="/"> Home </a>
</div>
<div id="footer">
<p>EWF Example Cookies</p>
</div>
</body>
</html>
]"
end

View File

@@ -1,13 +1,11 @@
note
description : "Basic Service that show how to handle a GET request"
date : "$Date$"
revision : "$Revision$"
description: "Basic Service launcher"
class
APPLICATION
inherit
WSF_DEFAULT_SERVICE
WSF_DEFAULT_SERVICE [APPLICATION_EXECUTION]
redefine
initialize
end
@@ -23,90 +21,4 @@ feature {NONE} -- Initialization
set_service_option ("port", 9090)
end
feature -- Basic operations
execute (req: WSF_REQUEST; res: WSF_RESPONSE)
-- Execute the incomming request
local
file: WSF_FILE_RESPONSE
l_parameter_names: STRING
l_answer: STRING
idioms: LIST[STRING]
l_raw_data: STRING
do
if req.is_get_request_method then
if req.path_info.same_string ("/") then
create file.make_html ("form.html")
res.send (file)
elseif req.path_info.same_string ("/search") then
-- (1) the parameter is case sensitive
if not (attached req.query_parameter ("GIVEN-NAME")) then
-- Wrong `GIVEN-NAME' need to be in lower case.
end
-- (2) Multiple values
if attached {WSF_MULTIPLE_STRING} req.query_parameter ("languages") as l_languages then
-- Get all the associated values
create {ARRAYED_LIST[STRING]} idioms.make (2)
across l_languages as ic loop idioms.force (ic.item.value) end
elseif attached {WSF_STRING} req.query_parameter ("languages") as l_language then
-- Single value
print (l_language.value)
else
-- Value Missing
end
-- Read the all parameters names and his values.
create l_parameter_names.make_from_string ("<h2>Parameters Names</h2>")
l_parameter_names.append ("<br>")
create l_answer.make_from_string ("<h2>Parameter Names and Values</h2>")
l_answer.append ("<br>")
across req.query_parameters as ic loop
l_parameter_names.append (ic.item.key)
l_parameter_names.append ("<br>")
l_answer.append (ic.item.key)
l_answer.append_character ('=')
if attached {WSF_STRING} req.query_parameter (ic.item.key) as l_value then
l_answer.append_string (l_value.value)
end
l_answer.append ("<br>")
end
l_parameter_names.append ("<br>")
l_parameter_names.append_string (l_answer)
res.put_header ({HTTP_STATUS_CODE}.ok, <<["Content-Type", "text/html"], ["Content-Length", l_parameter_names.count.out]>>)
res.put_string (l_parameter_names)
elseif req.path_info.same_string ("/link") then
-- WSF_TABLE example
create l_parameter_names.make_from_string ("<h2>Parameters Name</h2>")
if attached {WSF_TABLE} req.query_parameter ("tab") as l_tab then
l_parameter_names.append ("<br>")
l_parameter_names.append (l_tab.name)
from
l_tab.values.start
until
l_tab.values.after
loop
l_parameter_names.append ("<br>")
l_parameter_names.append (l_tab.values.key_for_iteration)
if attached {WSF_STRING} l_tab.value (l_tab.values.key_for_iteration) as l_value then
l_parameter_names.append ("=")
l_parameter_names.append (l_value.value)
end
l_tab.values.forth
end
l_parameter_names.append ("<br>")
res.put_header ({HTTP_STATUS_CODE}.ok, <<["Content-Type", "text/html"], ["Content-Length", l_parameter_names.count.out]>>)
res.put_string (l_parameter_names)
end
else
-- Here we should handle unexpected errors.
end
end
end
end

View File

@@ -0,0 +1,101 @@
note
description : "Basic Service that show how to handle a GET request"
date : "$Date$"
revision : "$Revision$"
class
APPLICATION_EXECUTION
inherit
WSF_EXECUTION
create
make
feature -- Basic operations
execute
-- Execute the incomming request
local
file: WSF_FILE_RESPONSE
l_parameter_names: STRING
l_answer: STRING
idioms: LIST[STRING]
l_raw_data: STRING
do
if request.is_get_request_method then
if request.path_info.same_string ("/") then
create file.make_html ("form.html")
response.send (file)
elseif request.path_info.same_string ("/search") then
-- (1) the parameter is case sensitive
if not (attached request.query_parameter ("GIVEN-NAME")) then
-- Wrong `GIVEN-NAME' need to be in lower case.
end
-- (2) Multiple values
if attached {WSF_MULTIPLE_STRING} request.query_parameter ("languages") as l_languages then
-- Get all the associated values
create {ARRAYED_LIST[STRING]} idioms.make (2)
across l_languages as ic loop idioms.force (ic.item.value) end
elseif attached {WSF_STRING} request.query_parameter ("languages") as l_language then
-- Single value
print (l_language.value)
else
-- Value Missing
end
-- Read the all parameters names and his values.
create l_parameter_names.make_from_string ("<h2>Parameters Names</h2>")
l_parameter_names.append ("<br>")
create l_answer.make_from_string ("<h2>Parameter Names and Values</h2>")
l_answer.append ("<br>")
across request.query_parameters as ic loop
l_parameter_names.append (ic.item.key)
l_parameter_names.append ("<br>")
l_answer.append (ic.item.key)
l_answer.append_character ('=')
if attached {WSF_STRING} request.query_parameter (ic.item.key) as l_value then
l_answer.append_string (l_value.value)
end
l_answer.append ("<br>")
end
l_parameter_names.append ("<br>")
l_parameter_names.append_string (l_answer)
response.put_header ({HTTP_STATUS_CODE}.ok, <<["Content-Type", "text/html"], ["Content-Length", l_parameter_names.count.out]>>)
response.put_string (l_parameter_names)
elseif request.path_info.same_string ("/link") then
-- WSF_TABLE example
create l_parameter_names.make_from_string ("<h2>Parameters Name</h2>")
if attached {WSF_TABLE} request.query_parameter ("tab") as l_tab then
l_parameter_names.append ("<br>")
l_parameter_names.append (l_tab.name)
from
l_tab.values.start
until
l_tab.values.after
loop
l_parameter_names.append ("<br>")
l_parameter_names.append (l_tab.values.key_for_iteration)
if attached {WSF_STRING} l_tab.value (l_tab.values.key_for_iteration) as l_value then
l_parameter_names.append ("=")
l_parameter_names.append (l_value.value)
end
l_tab.values.forth
end
l_parameter_names.append ("<br>")
response.put_header ({HTTP_STATUS_CODE}.ok, <<["Content-Type", "text/html"], ["Content-Length", l_parameter_names.count.out]>>)
response.put_string (l_parameter_names)
end
else
-- Here we should handle unexpected errors.
end
end
end
end

View File

@@ -1,13 +1,11 @@
note
description : "Reading Parameters from a HTML FORM (method POST) "
date : "$Date$"
revision : "$Revision$"
description: "Basic Service launcher"
class
APPLICATION
inherit
WSF_DEFAULT_SERVICE
WSF_DEFAULT_SERVICE [APPLICATION_EXECUTION]
redefine
initialize
end
@@ -23,72 +21,4 @@ feature {NONE} -- Initialization
set_service_option ("port", 9090)
end
feature -- Basic operations
execute (req: WSF_REQUEST; res: WSF_RESPONSE)
-- Execute the incomming request
local
file: WSF_FILE_RESPONSE
l_parameter_names: STRING
l_answer: STRING
idioms: LIST[STRING]
l_raw_data: STRING
do
if req.is_get_request_method then
create file.make_html ("form.html")
res.send (file)
elseif req.is_post_request_method then
req.set_raw_input_data_recorded (True)
-- (3) Read Raw Data
create l_raw_data.make_empty
req.read_input_data_into (l_raw_data)
-- (1) the parameter is case sensitive
if not (attached req.form_parameter ("GIVEN-NAME")) then
-- Wrong `GIVEN-NAME' need to be in lower case.
end
-- (2) Multiple values
if attached {WSF_MULTIPLE_STRING} req.form_parameter ("languages") as l_languages then
-- Get all the associated values
create {ARRAYED_LIST[STRING]} idioms.make (2)
across l_languages as ic loop idioms.force (ic.item.value) end
elseif attached {WSF_STRING} req.form_parameter ("langauges") as l_language then
-- Single value
print (l_language.value)
else
-- Value Missing
end
-- Read the all parameters names and his values.
create l_parameter_names.make_from_string ("<h2>Parameters Names</h2>")
l_parameter_names.append ("<br>")
create l_answer.make_from_string ("<h2>Parameter Names and Values</h2>")
l_answer.append ("<br>")
across req.form_parameters as ic loop
l_parameter_names.append (ic.item.key)
l_parameter_names.append ("<br>")
l_answer.append (ic.item.key)
l_answer.append_character ('=')
if attached {WSF_STRING} req.form_parameter (ic.item.key) as l_value then
l_answer.append_string (l_value.value)
end
l_answer.append ("<br>")
end
l_parameter_names.append ("<br>")
l_parameter_names.append_string (l_answer)
l_parameter_names.append ("<br>")
l_parameter_names.append ("<h2>Raw content</h2>")
l_parameter_names.append (l_raw_data)
res.put_header ({HTTP_STATUS_CODE}.ok, <<["Content-Type", "text/html"], ["Content-Length", l_parameter_names.count.out]>>)
res.put_string (l_parameter_names)
else
-- Here we should handle unexpected errors.
end
end
end

View File

@@ -0,0 +1,83 @@
note
description : "Reading Parameters from a HTML FORM (method POST) "
date : "$Date$"
revision : "$Revision$"
class
APPLICATION_EXECUTION
inherit
WSF_EXECUTION
create
make
feature -- Basic operations
execute
-- Execute the incomming request
local
file: WSF_FILE_RESPONSE
l_parameter_names: STRING
l_answer: STRING
idioms: LIST[STRING]
l_raw_data: STRING
do
if request.is_get_request_method then
create file.make_html ("form.html")
response.send (file)
elseif request.is_post_request_method then
request.set_raw_input_data_recorded (True)
-- (3) Read Raw Data
create l_raw_data.make_empty
request.read_input_data_into (l_raw_data)
-- (1) the parameter is case sensitive
if not (attached request.form_parameter ("GIVEN-NAME")) then
-- Wrong `GIVEN-NAME' need to be in lower case.
end
-- (2) Multiple values
if attached {WSF_MULTIPLE_STRING} request.form_parameter ("languages") as l_languages then
-- Get all the associated values
create {ARRAYED_LIST[STRING]} idioms.make (2)
across l_languages as ic loop idioms.force (ic.item.value) end
elseif attached {WSF_STRING} request.form_parameter ("langauges") as l_language then
-- Single value
print (l_language.value)
else
-- Value Missing
end
-- Read the all parameters names and his values.
create l_parameter_names.make_from_string ("<h2>Parameters Names</h2>")
l_parameter_names.append ("<br>")
create l_answer.make_from_string ("<h2>Parameter Names and Values</h2>")
l_answer.append ("<br>")
across request.form_parameters as ic loop
l_parameter_names.append (ic.item.key)
l_parameter_names.append ("<br>")
l_answer.append (ic.item.key)
l_answer.append_character ('=')
if attached {WSF_STRING} request.form_parameter (ic.item.key) as l_value then
l_answer.append_string (l_value.value)
end
l_answer.append ("<br>")
end
l_parameter_names.append ("<br>")
l_parameter_names.append_string (l_answer)
l_parameter_names.append ("<br>")
l_parameter_names.append ("<h2>Raw content</h2>")
l_parameter_names.append (l_raw_data)
response.put_header ({HTTP_STATUS_CODE}.ok, <<["Content-Type", "text/html"], ["Content-Length", l_parameter_names.count.out]>>)
response.put_string (l_parameter_names)
else
-- Here we should handle unexpected errors.
end
end
end

View File

@@ -1,14 +1,11 @@
note
description : "Basic Service that Read a Request, a "
date : "$Date$"
revision : "$Revision$"
EIS: "name=Browser detection using user agent","src=https://developer.mozilla.org/en-US/docs/Browser_detection_using_the_user_agent", "protocol=url"
description: "Basic Service launcher"
class
APPLICATION
inherit
WSF_DEFAULT_SERVICE
WSF_DEFAULT_SERVICE [APPLICATION_EXECUTION]
redefine
initialize
end
@@ -22,88 +19,6 @@ feature {NONE} -- Initialization
-- Initialize current service.
do
set_service_option ("port", 9090)
set_service_option ("verbose", true)
end
feature -- Basic operations
execute (req: WSF_REQUEST; res: WSF_RESPONSE)
-- Execute the incomming request
local
l_raw_data: STRING
l_page_response: STRING
l_rows: STRING
do
create l_page_response.make_from_string (html_template)
if req.path_info.same_string ("/") then
-- retrieve the user-agent
if attached req.http_user_agent as l_user_agent then
l_page_response.replace_substring_all ("$user_agent", l_user_agent)
l_page_response.replace_substring_all ("$browser", get_browser_name (l_user_agent))
else
l_page_response.replace_substring_all ("$user_agent", "[]")
l_page_response.replace_substring_all ("$browser", "Unknown, the user-agent was not present.")
end
res.put_header ({HTTP_STATUS_CODE}.ok, <<["Content-Type", "text/html"], ["Content-Length", l_page_response.count.out]>>)
res.put_string (l_page_response)
end
end
feature -- Browser utility
get_browser_name (a_user_agent: READABLE_STRING_8):READABLE_STRING_32
-- Must contain Must not contain
-- Firefox Firefox/xyz Seamonkey/xyz
-- Seamonkey Seamonkey/xyz
-- Chrome Chrome/xyz Chromium/xyz
-- Chromium Chromium/xyz
-- Safari Safari/xyz Chrome/xyz
-- Chromium/xyz
-- Opera OPR/xyz [1]
-- Opera/xyz [2]
-- Internet Explorer ;MSIE xyz; Internet Explorer doesn't put its name in the BrowserName/VersionNumber format
do
if
a_user_agent.has_substring ("Firefox") and then
not a_user_agent.has_substring ("Seamonkey")
then
Result := "Firefox"
elseif a_user_agent.has_substring ("Seamonkey") then
Result := "Seamonkey"
elseif a_user_agent.has_substring ("Chrome") and then not a_user_agent.has_substring ("Chromium")then
Result := "Chrome"
elseif a_user_agent.has_substring ("Chromium") then
Result := "Chromiun"
elseif a_user_agent.has_substring ("Safari") and then not (a_user_agent.has_substring ("Chrome") or else a_user_agent.has_substring ("Chromium")) then
Result := "Safari"
elseif a_user_agent.has_substring ("OPR") or else a_user_agent.has_substring ("Opera") then
Result := "Opera"
elseif a_user_agent.has_substring ("MSIE") or else a_user_agent.has_substring ("Trident")then
Result := "Internet Explorer"
else
Result := "Unknown"
end
end
html_template: STRING = "[
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<h1>EWF service example: Showing Browser Dectection Using User-Agent</h1> <br>
<strong>User Agent:</strong> $user_agent <br>
<h2>Enjoy using $browser </h2>
</body>
</html>
]"
end

View File

@@ -0,0 +1,97 @@
note
description : "Basic Service that Read a Request, a "
date : "$Date$"
revision : "$Revision$"
EIS: "name=Browser detection using user agent","src=https://developer.mozilla.org/en-US/docs/Browser_detection_using_the_user_agent", "protocol=url"
class
APPLICATION_EXECUTION
inherit
WSF_EXECUTION
create
make
feature -- Basic operations
execute
-- Execute the incomming request
local
l_raw_data: STRING
l_page_response: STRING
l_rows: STRING
do
create l_page_response.make_from_string (html_template)
if request.path_info.same_string ("/") then
-- retrieve the user-agent
if attached request.http_user_agent as l_user_agent then
l_page_response.replace_substring_all ("$user_agent", l_user_agent)
l_page_response.replace_substring_all ("$browser", browser_name (l_user_agent))
else
l_page_response.replace_substring_all ("$user_agent", "[]")
l_page_response.replace_substring_all ("$browser", "Unknown, the user-agent was not present.")
end
response.put_header ({HTTP_STATUS_CODE}.ok, <<["Content-Type", "text/html"], ["Content-Length", l_page_response.count.out]>>)
response.put_string (l_page_response)
end
end
feature -- Browser utility
browser_name (a_user_agent: READABLE_STRING_8): STRING_8
-- Must contain Must not contain
-- Firefox Firefox/xyz Seamonkey/xyz
-- Seamonkey Seamonkey/xyz
-- Chrome Chrome/xyz Chromium/xyz
-- Chromium Chromium/xyz
-- Safari Safari/xyz Chrome/xyz
-- Chromium/xyz
-- Opera OPR/xyz [1]
-- Opera/xyz [2]
-- Internet Explorer ;MSIE xyz; Internet Explorer doesn't put its name in the BrowserName/VersionNumber format
do
if
a_user_agent.has_substring ("Firefox") and then
not a_user_agent.has_substring ("Seamonkey")
then
Result := "Firefox"
elseif a_user_agent.has_substring ("Seamonkey") then
Result := "Seamonkey"
elseif a_user_agent.has_substring ("Chrome") and then not a_user_agent.has_substring ("Chromium")then
Result := "Chrome"
elseif a_user_agent.has_substring ("Chromium") then
Result := "Chromiun"
elseif a_user_agent.has_substring ("Safari") and then not (a_user_agent.has_substring ("Chrome") or else a_user_agent.has_substring ("Chromium")) then
Result := "Safari"
elseif a_user_agent.has_substring ("OPR") or else a_user_agent.has_substring ("Opera") then
Result := "Opera"
elseif a_user_agent.has_substring ("MSIE") or else a_user_agent.has_substring ("Trident")then
Result := "Internet Explorer"
else
Result := "Unknown"
end
end
html_template: STRING = "[
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<h1>EWF service example: Showing Browser Dectection Using User-Agent</h1> <br>
<strong>User Agent:</strong> $user_agent <br>
<h2>Enjoy using $browser </h2>
</body>
</html>
]"
end

View File

@@ -1,14 +1,11 @@
note
description : "Basic Service that shows the standard CGI variables"
date : "$Date$"
revision : "$Revision$"
EIS: "name=CGI specification","src=(https://tools.ietf.org/html/rfc3875", "protocol=url"
description: "Basic Service launcher"
class
APPLICATION
inherit
WSF_DEFAULT_SERVICE
WSF_DEFAULT_SERVICE [APPLICATION_EXECUTION]
redefine
initialize
end
@@ -22,294 +19,6 @@ feature {NONE} -- Initialization
-- Initialize current service.
do
set_service_option ("port", 9090)
set_service_option ("verbose", true)
end
feature -- Basic operations
execute (req: WSF_REQUEST; res: WSF_RESPONSE)
-- Execute the incomming request
local
l_raw_data: STRING
l_page_response: STRING
l_rows: STRING
do
create l_page_response.make_from_string (html_template)
if req.path_info.same_string ("/") then
-- HTTP method
l_page_response.replace_substring_all ("$http_method", req.request_method)
-- URI
l_page_response.replace_substring_all ("$uri", req.path_info)
-- Protocol
l_page_response.replace_substring_all ("$protocol", req.server_protocol)
-- Fill the table rows with CGI standard variables
create l_rows.make_empty
-- Auth_type
l_rows.append ("<tr>")
l_rows.append ("<td>")
l_rows.append ("AUTH_TYPE")
l_rows.append ("</td>")
l_rows.append ("<td>")
if attached req.auth_type as l_type then
l_rows.append (l_type)
else
l_rows.append ("Not present")
end
l_rows.append ("</td>")
l_rows.append ("</tr>")
-- Content length
l_rows.append ("<tr>")
l_rows.append ("<td>")
l_rows.append ("CONTENT_LENGTH")
l_rows.append ("</td>")
l_rows.append ("<td>")
if attached req.content_length as l_content_length then
l_rows.append (l_content_length)
else
l_rows.append ("Not present")
end
l_rows.append ("</td>")
l_rows.append ("</tr>")
-- Content length
l_rows.append ("<tr>")
l_rows.append ("<td>")
l_rows.append ("CONTENT_TYPE")
l_rows.append ("</td>")
l_rows.append ("<td>")
if attached req.content_type as l_content_type then
l_rows.append (l_content_type.string)
else
l_rows.append ("Not present")
end
l_rows.append ("</td>")
l_rows.append ("</tr>")
-- Gateway interface
l_rows.append ("<tr>")
l_rows.append ("<td>")
l_rows.append ("GATEWAY_INTERFACE")
l_rows.append ("</td>")
l_rows.append ("<td>")
if attached req.gateway_interface as l_gateway_interface then
l_rows.append (l_gateway_interface)
else
l_rows.append ("Not present")
end
l_rows.append ("</td>")
l_rows.append ("</tr>")
-- Path info
l_rows.append ("<tr>")
l_rows.append ("<td>")
l_rows.append ("PATH_INFO")
l_rows.append ("</td>")
l_rows.append ("<td>")
if attached req.path_info as l_path_info then
l_rows.append (l_path_info)
else
l_rows.append ("Not present")
end
l_rows.append ("</td>")
l_rows.append ("</tr>")
-- Path translated
l_rows.append ("<tr>")
l_rows.append ("<td>")
l_rows.append ("PATH_TRANSLATED")
l_rows.append ("</td>")
l_rows.append ("<td>")
if attached req.path_translated as l_path_translated then
l_rows.append (l_path_translated)
else
l_rows.append ("Not present")
end
l_rows.append ("</td>")
l_rows.append ("</tr>")
-- Query string
l_rows.append ("<tr>")
l_rows.append ("<td>")
l_rows.append ("QUERY_STRING")
l_rows.append ("</td>")
l_rows.append ("<td>")
if attached req.query_string as l_query_string then
l_rows.append (l_query_string)
else
l_rows.append ("Not present")
end
l_rows.append ("</td>")
l_rows.append ("</tr>")
-- Remote addr
l_rows.append ("<tr>")
l_rows.append ("<td>")
l_rows.append ("REMOTE_ADDR")
l_rows.append ("</td>")
l_rows.append ("<td>")
l_rows.append (req.remote_addr)
l_rows.append ("</td>")
l_rows.append ("</tr>")
-- Remote host
l_rows.append ("<tr>")
l_rows.append ("<td>")
l_rows.append ("REMOTE_HOST")
l_rows.append ("</td>")
l_rows.append ("<td>")
if attached req.remote_host as l_remote_host then
l_rows.append (l_remote_host)
else
l_rows.append ("Not present")
end
l_rows.append ("</td>")
l_rows.append ("</tr>")
-- Remote ident
l_rows.append ("<tr>")
l_rows.append ("<td>")
l_rows.append ("REMOTE_IDENT")
l_rows.append ("</td>")
l_rows.append ("<td>")
if attached req.remote_ident as l_remote_ident then
l_rows.append (l_remote_ident)
else
l_rows.append ("Not present")
end
l_rows.append ("</td>")
l_rows.append ("</tr>")
-- Remote user
l_rows.append ("<tr>")
l_rows.append ("<td>")
l_rows.append ("REMOTE_USER")
l_rows.append ("</td>")
l_rows.append ("<td>")
if attached req.remote_user as l_remote_user then
l_rows.append (l_remote_user)
else
l_rows.append ("Not present")
end
l_rows.append ("</td>")
l_rows.append ("</tr>")
-- Request method
l_rows.append ("<tr>")
l_rows.append ("<td>")
l_rows.append ("REQUEST_METHOD")
l_rows.append ("</td>")
l_rows.append ("<td>")
l_rows.append (req.request_method)
l_rows.append ("</td>")
l_rows.append ("</tr>")
-- Script name
l_rows.append ("<tr>")
l_rows.append ("<td>")
l_rows.append ("SCRIPT_NAME")
l_rows.append ("</td>")
l_rows.append ("<td>")
l_rows.append (req.script_name)
l_rows.append ("</td>")
l_rows.append ("</tr>")
-- Server name
l_rows.append ("<tr>")
l_rows.append ("<td>")
l_rows.append ("SERVER_NAME")
l_rows.append ("</td>")
l_rows.append ("<td>")
l_rows.append (req.server_name)
l_rows.append ("</td>")
l_rows.append ("</tr>")
-- Server protocol
l_rows.append ("<tr>")
l_rows.append ("<td>")
l_rows.append ("SERVER_PROTOCOL")
l_rows.append ("</td>")
l_rows.append ("<td>")
l_rows.append (req.server_protocol)
l_rows.append ("</td>")
l_rows.append ("</tr>")
-- Server software
l_rows.append ("<tr>")
l_rows.append ("<td>")
l_rows.append ("SERVER_SOFTWARE")
l_rows.append ("</td>")
l_rows.append ("<td>")
l_rows.append (req.server_software)
l_rows.append ("</td>")
l_rows.append ("</tr>")
l_page_response.replace_substring_all ("$rows", l_rows)
-- Reading the raw header
if attached req.raw_header_data as l_raw_header then
l_page_response.replace_substring_all ("$raw_header", l_raw_header)
end
res.put_header ({HTTP_STATUS_CODE}.ok, <<["Content-Type", "text/html"], ["Content-Length", l_page_response.count.out]>>)
res.put_string (l_page_response)
end
end
html_template: STRING = "[
<!DOCTYPE html>
<html>
<head>
<style>
thead {color:green;}
tbody {color:blue;}
table, th, td {
border: 1px solid black;
}
</style>
</head>
<body>
<h1>EWF service example: Showing Standard CGI Variables</h1>
<strong>HTTP METHOD:</strong>$http_method<br>
<strong>URI:</strong>$uri<br>
<strong>PROTOCOL:</strong>$protocol<br>
<br>
<table>
<thead>
<tr>
<th>CGI Name</th>
<th>Value</th>
</tr>
</thead>
<tbody>
$rows
</tbody>
</table>
<h2>Raw header</h2>
$raw_header
</body>
</html>
]"
end

View File

@@ -0,0 +1,303 @@
note
description : "Basic Service that shows the standard CGI variables"
date : "$Date$"
revision : "$Revision$"
EIS: "name=CGI specification","src=(https://tools.ietf.org/html/rfc3875", "protocol=url"
class
APPLICATION_EXECUTION
inherit
WSF_EXECUTION
create
make
feature -- Basic operations
execute
-- Execute the incomming request
local
l_raw_data: STRING
l_page_response: STRING
l_rows: STRING
do
create l_page_response.make_from_string (html_template)
if request.path_info.same_string ("/") then
-- HTTP method
l_page_response.replace_substring_all ("$http_method", request.request_method)
-- URI
l_page_response.replace_substring_all ("$uri", request.path_info)
-- Protocol
l_page_response.replace_substring_all ("$protocol", request.server_protocol)
-- Fill the table rows with CGI standard variables
create l_rows.make_empty
-- Auth_type
l_rows.append ("<tr>")
l_rows.append ("<td>")
l_rows.append ("AUTH_TYPE")
l_rows.append ("</td>")
l_rows.append ("<td>")
if attached request.auth_type as l_type then
l_rows.append (l_type)
else
l_rows.append ("Not present")
end
l_rows.append ("</td>")
l_rows.append ("</tr>")
-- Content length
l_rows.append ("<tr>")
l_rows.append ("<td>")
l_rows.append ("CONTENT_LENGTH")
l_rows.append ("</td>")
l_rows.append ("<td>")
if attached request.content_length as l_content_length then
l_rows.append (l_content_length)
else
l_rows.append ("Not present")
end
l_rows.append ("</td>")
l_rows.append ("</tr>")
-- Content length
l_rows.append ("<tr>")
l_rows.append ("<td>")
l_rows.append ("CONTENT_TYPE")
l_rows.append ("</td>")
l_rows.append ("<td>")
if attached request.content_type as l_content_type then
l_rows.append (l_content_type.string)
else
l_rows.append ("Not present")
end
l_rows.append ("</td>")
l_rows.append ("</tr>")
-- Gateway interface
l_rows.append ("<tr>")
l_rows.append ("<td>")
l_rows.append ("GATEWAY_INTERFACE")
l_rows.append ("</td>")
l_rows.append ("<td>")
if attached request.gateway_interface as l_gateway_interface then
l_rows.append (l_gateway_interface)
else
l_rows.append ("Not present")
end
l_rows.append ("</td>")
l_rows.append ("</tr>")
-- Path info
l_rows.append ("<tr>")
l_rows.append ("<td>")
l_rows.append ("PATH_INFO")
l_rows.append ("</td>")
l_rows.append ("<td>")
if attached request.path_info as l_path_info then
l_rows.append (l_path_info)
else
l_rows.append ("Not present")
end
l_rows.append ("</td>")
l_rows.append ("</tr>")
-- Path translated
l_rows.append ("<tr>")
l_rows.append ("<td>")
l_rows.append ("PATH_TRANSLATED")
l_rows.append ("</td>")
l_rows.append ("<td>")
if attached request.path_translated as l_path_translated then
l_rows.append (l_path_translated)
else
l_rows.append ("Not present")
end
l_rows.append ("</td>")
l_rows.append ("</tr>")
-- Query string
l_rows.append ("<tr>")
l_rows.append ("<td>")
l_rows.append ("QUERY_STRING")
l_rows.append ("</td>")
l_rows.append ("<td>")
if attached request.query_string as l_query_string then
l_rows.append (l_query_string)
else
l_rows.append ("Not present")
end
l_rows.append ("</td>")
l_rows.append ("</tr>")
-- Remote addr
l_rows.append ("<tr>")
l_rows.append ("<td>")
l_rows.append ("REMOTE_ADDR")
l_rows.append ("</td>")
l_rows.append ("<td>")
l_rows.append (request.remote_addr)
l_rows.append ("</td>")
l_rows.append ("</tr>")
-- Remote host
l_rows.append ("<tr>")
l_rows.append ("<td>")
l_rows.append ("REMOTE_HOST")
l_rows.append ("</td>")
l_rows.append ("<td>")
if attached request.remote_host as l_remote_host then
l_rows.append (l_remote_host)
else
l_rows.append ("Not present")
end
l_rows.append ("</td>")
l_rows.append ("</tr>")
-- Remote ident
l_rows.append ("<tr>")
l_rows.append ("<td>")
l_rows.append ("REMOTE_IDENT")
l_rows.append ("</td>")
l_rows.append ("<td>")
if attached request.remote_ident as l_remote_ident then
l_rows.append (l_remote_ident)
else
l_rows.append ("Not present")
end
l_rows.append ("</td>")
l_rows.append ("</tr>")
-- Remote user
l_rows.append ("<tr>")
l_rows.append ("<td>")
l_rows.append ("REMOTE_USER")
l_rows.append ("</td>")
l_rows.append ("<td>")
if attached request.remote_user as l_remote_user then
l_rows.append (l_remote_user)
else
l_rows.append ("Not present")
end
l_rows.append ("</td>")
l_rows.append ("</tr>")
-- Request method
l_rows.append ("<tr>")
l_rows.append ("<td>")
l_rows.append ("REQUEST_METHOD")
l_rows.append ("</td>")
l_rows.append ("<td>")
l_rows.append (request.request_method)
l_rows.append ("</td>")
l_rows.append ("</tr>")
-- Script name
l_rows.append ("<tr>")
l_rows.append ("<td>")
l_rows.append ("SCRIPT_NAME")
l_rows.append ("</td>")
l_rows.append ("<td>")
l_rows.append (request.script_name)
l_rows.append ("</td>")
l_rows.append ("</tr>")
-- Server name
l_rows.append ("<tr>")
l_rows.append ("<td>")
l_rows.append ("SERVER_NAME")
l_rows.append ("</td>")
l_rows.append ("<td>")
l_rows.append (request.server_name)
l_rows.append ("</td>")
l_rows.append ("</tr>")
-- Server protocol
l_rows.append ("<tr>")
l_rows.append ("<td>")
l_rows.append ("SERVER_PROTOCOL")
l_rows.append ("</td>")
l_rows.append ("<td>")
l_rows.append (request.server_protocol)
l_rows.append ("</td>")
l_rows.append ("</tr>")
-- Server software
l_rows.append ("<tr>")
l_rows.append ("<td>")
l_rows.append ("SERVER_SOFTWARE")
l_rows.append ("</td>")
l_rows.append ("<td>")
l_rows.append (request.server_software)
l_rows.append ("</td>")
l_rows.append ("</tr>")
l_page_response.replace_substring_all ("$rows", l_rows)
-- Reading the raw header
if attached request.raw_header_data as l_raw_header then
l_page_response.replace_substring_all ("$raw_header", l_raw_header)
end
response.put_header ({HTTP_STATUS_CODE}.ok, <<["Content-Type", "text/html"], ["Content-Length", l_page_response.count.out]>>)
response.put_string (l_page_response)
end
end
html_template: STRING = "[
<!DOCTYPE html>
<html>
<head>
<style>
thead {color:green;}
tbody {color:blue;}
table, th, td {
border: 1px solid black;
}
</style>
</head>
<body>
<h1>EWF service example: Showing Standard CGI Variables</h1>
<strong>HTTP METHOD:</strong>$http_method<br>
<strong>URI:</strong>$uri<br>
<strong>PROTOCOL:</strong>$protocol<br>
<br>
<table>
<thead>
<tr>
<th>CGI Name</th>
<th>Value</th>
</tr>
</thead>
<tbody>
$rows
</tbody>
</table>
<h2>Raw header</h2>
$raw_header
</body>
</html>
]"
end

View File

@@ -1,13 +1,11 @@
note
description : "Basic Service that Read Request Headers"
date : "$Date$"
revision : "$Revision$"
description: "Basic Service launcher"
class
APPLICATION
inherit
WSF_DEFAULT_SERVICE
WSF_DEFAULT_SERVICE [APPLICATION_EXECUTION]
redefine
initialize
end
@@ -21,97 +19,6 @@ feature {NONE} -- Initialization
-- Initialize current service.
do
set_service_option ("port", 9090)
set_service_option ("verbose", true)
end
feature -- Basic operations
execute (req: WSF_REQUEST; res: WSF_RESPONSE)
-- Execute the incomming request
local
l_raw_data: STRING
l_page_response: STRING
l_rows: STRING
do
create l_page_response.make_from_string (html_template)
if req.path_info.same_string ("/") then
-- HTTP method
l_page_response.replace_substring_all ("$http_method", req.request_method)
-- URI
l_page_response.replace_substring_all ("$uri", req.path_info)
-- Protocol
l_page_response.replace_substring_all ("$protocol", req.server_protocol)
-- Fill the table rows with HTTP Headers
create l_rows.make_empty
across req.meta_variables as ic loop
if ic.item.name.starts_with ("HTTP_") then
l_rows.append ("<tr>")
l_rows.append ("<td>")
l_rows.append (ic.item.name)
l_rows.append ("</td>")
l_rows.append ("<td>")
l_rows.append (ic.item.value)
l_rows.append ("</td>")
l_rows.append ("</tr>")
end
end
l_page_response.replace_substring_all ("$rows", l_rows)
-- Reading the raw header
if attached req.raw_header_data as l_raw_header then
l_page_response.replace_substring_all ("$raw_header", l_raw_header)
end
res.put_header ({HTTP_STATUS_CODE}.ok, <<["Content-Type", "text/html"], ["Content-Length", l_page_response.count.out]>>)
res.put_string (l_page_response)
end
end
html_template: STRING = "[
<!DOCTYPE html>
<html>
<head>
<style>
thead {color:green;}
tbody {color:blue;}
table, th, td {
border: 1px solid black;
}
</style>
</head>
<body>
<h1>EWF service example: Showing Request Headers</h1>
<strong>HTTP METHOD:</strong>$http_method<br>
<strong>URI:</strong>$uri<br>
<strong>PROTOCOL:</strong>$protocol<br>
<strong>REQUEST TIME:</strong>$time<br>
<br>
<table>
<thead>
<tr>
<th>Header Name</th>
<th>Header Value</th>
</tr>
</thead>
<tbody>
$rows
</tbody>
</table>
<h2>Raw header</h2>
$raw_header
</body>
</html>
]"
end

View File

@@ -0,0 +1,105 @@
note
description : "Basic Service that Read Request Headers"
date : "$Date$"
revision : "$Revision$"
class
APPLICATION_EXECUTION
inherit
WSF_EXECUTION
create
make
feature -- Basic operations
execute
-- Execute the incomming request
local
l_raw_data: STRING
l_page_response: STRING
l_rows: STRING
do
create l_page_response.make_from_string (html_template)
if request.path_info.same_string ("/") then
-- HTTP method
l_page_response.replace_substring_all ("$http_method", request.request_method)
-- URI
l_page_response.replace_substring_all ("$uri", request.path_info)
-- Protocol
l_page_response.replace_substring_all ("$protocol", request.server_protocol)
-- Fill the table rows with HTTP Headers
create l_rows.make_empty
across request.meta_variables as ic loop
if ic.item.name.starts_with ("HTTP_") then
l_rows.append ("<tr>")
l_rows.append ("<td>")
l_rows.append (ic.item.name)
l_rows.append ("</td>")
l_rows.append ("<td>")
l_rows.append (ic.item.value)
l_rows.append ("</td>")
l_rows.append ("</tr>")
end
end
l_page_response.replace_substring_all ("$rows", l_rows)
-- Reading the raw header
if attached request.raw_header_data as l_raw_header then
l_page_response.replace_substring_all ("$raw_header", l_raw_header)
end
response.put_header ({HTTP_STATUS_CODE}.ok, <<["Content-Type", "text/html"], ["Content-Length", l_page_response.count.out]>>)
response.put_string (l_page_response)
end
end
html_template: STRING = "[
<!DOCTYPE html>
<html>
<head>
<style>
thead {color:green;}
tbody {color:blue;}
table, th, td {
border: 1px solid black;
}
</style>
</head>
<body>
<h1>EWF service example: Showing Request Headers</h1>
<strong>HTTP METHOD:</strong>$http_method<br>
<strong>URI:</strong>$uri<br>
<strong>PROTOCOL:</strong>$protocol<br>
<strong>REQUEST TIME:</strong>$time<br>
<br>
<table>
<thead>
<tr>
<th>Header Name</th>
<th>Header Value</th>
</tr>
</thead>
<tbody>
$rows
</tbody>
</table>
<h2>Raw header</h2>
$raw_header
</body>
</html>
]"
end

View File

@@ -1,13 +1,11 @@
note
description : "Basic Service that show how to Upload a file"
date : "$Date$"
revision : "$Revision$"
description: "Basic Service launcher"
class
APPLICATION
inherit
WSF_DEFAULT_SERVICE
WSF_DEFAULT_SERVICE [APPLICATION_EXECUTION]
redefine
initialize
end
@@ -23,47 +21,4 @@ feature {NONE} -- Initialization
set_service_option ("port", 9090)
end
feature -- Basic operations
execute (req: WSF_REQUEST; res: WSF_RESPONSE)
-- Execute the incomming request
local
file: WSF_FILE_RESPONSE
l_answer: STRING
do
if req.is_get_request_method then
if req.path_info.same_string ("/") then
create file.make_html ("upload.html")
res.send (file)
else
-- Here we should handle unexpected errors.
end
elseif req.is_post_request_method then
if req.path_info.same_string ("/upload") then
-- Check if we have an uploaded file
if req.has_uploaded_file then
-- iterate over all the uploaded files
create l_answer.make_from_string ("<h1>Uploaded File/s</h1><br>")
across req.uploaded_files as ic loop
l_answer.append ("<strong>FileName:</strong>")
l_answer.append (ic.item.filename)
l_answer.append ("<br><strong>Size:</strong>")
l_answer.append (ic.item.size.out)
l_answer.append ("<br>")
end
res.put_header ({HTTP_STATUS_CODE}.ok, <<["Content-type","text/html"],["Content-lenght", l_answer.count.out]>>)
res.put_string (l_answer)
else
-- Here we should handle unexpected errors.
create l_answer.make_from_string ("<strong>No uploaded files</strong><br>")
create l_answer.append ("Back to <a href='/'>Home</a>")
res.put_header ({HTTP_STATUS_CODE}.bad_request, <<["Content-type","text/html"],["Content-lenght", l_answer.count.out]>>)
res.put_string (l_answer)
end
else
-- Handle error
end
end
end
end

View File

@@ -0,0 +1,58 @@
note
description : "Basic Service that show how to Upload a file"
date : "$Date$"
revision : "$Revision$"
class
APPLICATION_EXECUTION
inherit
WSF_EXECUTION
create
make
feature -- Basic operations
execute
-- Execute the incomming request
local
file: WSF_FILE_RESPONSE
l_answer: STRING
do
if request.is_get_request_method then
if request.path_info.same_string ("/") then
create file.make_html ("upload.html")
response.send (file)
else
-- Here we should handle unexpected errors.
end
elseif request.is_post_request_method then
if request.path_info.same_string ("/upload") then
-- Check if we have an uploaded file
if request.has_uploaded_file then
-- iterate over all the uploaded files
create l_answer.make_from_string ("<h1>Uploaded File/s</h1><br>")
across request.uploaded_files as ic loop
l_answer.append ("<strong>FileName:</strong>")
l_answer.append (ic.item.filename)
l_answer.append ("<br><strong>Size:</strong>")
l_answer.append (ic.item.size.out)
l_answer.append ("<br>")
end
response.put_header ({HTTP_STATUS_CODE}.ok, <<["Content-type","text/html"],["Content-lenght", l_answer.count.out]>>)
response.put_string (l_answer)
else
-- Here we should handle unexpected errors.
create l_answer.make_from_string ("<strong>No uploaded files</strong><br>")
l_answer.append ("Back to <a href='/'>Home</a>")
response.put_header ({HTTP_STATUS_CODE}.bad_request, <<["Content-type","text/html"],["Content-lenght", l_answer.count.out]>>)
response.put_string (l_answer)
end
else
-- Handle error
end
end
end
end

View File

@@ -8,23 +8,11 @@ class
inherit
WSF_ROUTED_SERVICE
rename
execute as execute_router
end
WSF_FILTERED_SERVICE
WSF_DEFAULT_SERVICE
WSF_DEFAULT_SERVICE [APPLICATION_EXECUTION]
redefine
initialize
end
WSF_FILTER
rename
execute as execute_router
end
create
make_and_launch
@@ -33,75 +21,8 @@ feature {NONE} -- Initialization
initialize
-- Initialize current service.
do
initialize_router
initialize_filter
Precursor
set_service_option ("port", 7070)
end
feature -- Router and Filter
create_filter
-- Create `filter'
local
f, l_filter: detachable WSF_FILTER
do
l_filter := Void
-- Maintenance
create {WSF_MAINTENANCE_FILTER} f
f.set_next (l_filter)
l_filter := f
-- Logging
create {WSF_LOGGING_FILTER} f
f.set_next (l_filter)
l_filter := f
filter := l_filter
end
setup_filter
-- Setup `filter'
local
f: WSF_FILTER
do
from
f := filter
until
not attached f.next as l_next
loop
f := l_next
end
f.set_next (Current)
end
setup_router
do
map_agent_uri ("/", agent execute_hello, Void)
-- NOTE: you could put all those files in a specific folder, and use WSF_FILE_SYSTEM_HANDLER with "/"
-- this way, it handles the caching and so on
router.handle_with_request_methods ("/assets", create {WSF_FILE_SYSTEM_HANDLER}.make_hidden ("assets"), router.methods_GET)
end
feature -- Helper: mapping
map_agent_uri (a_uri: READABLE_STRING_8; a_action: like {WSF_URI_AGENT_HANDLER}.action; rqst_methods: detachable WSF_REQUEST_METHODS)
do
router.map_with_request_methods (create {WSF_URI_MAPPING}.make (a_uri, create {WSF_URI_AGENT_HANDLER}.make (a_action)), rqst_methods)
end
feature -- Execution
execute_hello (request: WSF_REQUEST; response: WSF_RESPONSE)
local
page: EMPTY_PAGE
do
-- To send a response we need to setup, the status code and
-- the response headers.
create page.make (request, response)
page.execute
end
end

View File

@@ -0,0 +1,60 @@
note
description: "simple application root class"
date: "$Date$"
revision: "$Revision$"
class
APPLICATION_EXECUTION
inherit
WSF_FILTERED_ROUTED_EXECUTION
create
make
feature -- Router and Filter
create_filter
-- Create `filter'
do
-- Maintenance
create {WSF_MAINTENANCE_FILTER} filter
end
setup_filter
-- Setup `filter'
do
append_filters (<<create {WSF_LOGGING_FILTER}>>)
end
setup_router
do
map_agent_uri ("/", agent execute_hello, Void)
-- NOTE: you could put all those files in a specific folder, and use WSF_FILE_SYSTEM_HANDLER with "/"
-- this way, it handles the caching and so on
router.handle_with_request_methods ("/assets", create {WSF_FILE_SYSTEM_HANDLER}.make_hidden ("assets"), router.methods_GET)
end
feature -- Helper: mapping
map_agent_uri (a_uri: READABLE_STRING_8; a_action: like {WSF_URI_AGENT_HANDLER}.action; rqst_methods: detachable WSF_REQUEST_METHODS)
do
router.map_with_request_methods (create {WSF_URI_MAPPING}.make (a_uri, create {WSF_URI_AGENT_HANDLER}.make (a_action)), rqst_methods)
end
feature -- Execution
execute_hello (req: WSF_REQUEST; res: WSF_RESPONSE)
local
page: EMPTY_PAGE
do
-- To send a response we need to setup, the status code and
-- the response headers.
create page.make (req, res)
page.execute
end
end

View File

@@ -8,23 +8,11 @@ class
inherit
WSF_ROUTED_SERVICE
rename
execute as execute_router
end
WSF_FILTERED_SERVICE
WSF_DEFAULT_SERVICE
WSF_DEFAULT_SERVICE [APPLICATION_EXECUTION]
redefine
initialize
end
WSF_FILTER
rename
execute as execute_router
end
create
make_and_launch
@@ -33,75 +21,8 @@ feature {NONE} -- Initialization
initialize
-- Initialize current service.
do
initialize_router
initialize_filter
Precursor
set_service_option ("port", 7070)
end
feature -- Router and Filter
create_filter
-- Create `filter'
local
f, l_filter: detachable WSF_FILTER
do
l_filter := Void
-- Maintenance
create {WSF_MAINTENANCE_FILTER} f
f.set_next (l_filter)
l_filter := f
-- Logging
create {WSF_LOGGING_FILTER} f
f.set_next (l_filter)
l_filter := f
filter := l_filter
end
setup_filter
-- Setup `filter'
local
f: WSF_FILTER
do
from
f := filter
until
not attached f.next as l_next
loop
f := l_next
end
f.set_next (Current)
end
setup_router
do
map_agent_uri ("/", agent execute_hello, Void)
-- NOTE: you could put all those files in a specific folder, and use WSF_FILE_SYSTEM_HANDLER with "/"
-- this way, it handles the caching and so on
router.handle_with_request_methods ("/assets", create {WSF_FILE_SYSTEM_HANDLER}.make_hidden ("assets"), router.methods_GET)
end
feature -- Helper: mapping
map_agent_uri (a_uri: READABLE_STRING_8; a_action: like {WSF_URI_AGENT_HANDLER}.action; rqst_methods: detachable WSF_REQUEST_METHODS)
do
router.map_with_request_methods (create {WSF_URI_MAPPING}.make (a_uri, create {WSF_URI_AGENT_HANDLER}.make (a_action)), rqst_methods)
end
feature -- Execution
execute_hello (request: WSF_REQUEST; response: WSF_RESPONSE)
local
page: EMPTY_PAGE
do
-- To send a response we need to setup, the status code and
-- the response headers.
create page.make (request, response)
page.execute
end
end

View File

@@ -0,0 +1,60 @@
note
description: "simple application root class"
date: "$Date$"
revision: "$Revision$"
class
APPLICATION_EXECUTION
inherit
WSF_FILTERED_ROUTED_EXECUTION
create
make
feature -- Router and Filter
create_filter
-- Create `filter'
do
-- Maintenance
create {WSF_MAINTENANCE_FILTER} filter
end
setup_filter
-- Setup `filter'
do
append_filters (<< create {WSF_LOGGING_FILTER} >>)
end
setup_router
do
map_agent_uri ("/", agent execute_hello, Void)
-- NOTE: you could put all those files in a specific folder, and use WSF_FILE_SYSTEM_HANDLER with "/"
-- this way, it handles the caching and so on
router.handle_with_request_methods ("/assets", create {WSF_FILE_SYSTEM_HANDLER}.make_hidden ("assets"), router.methods_GET)
end
feature -- Helper: mapping
map_agent_uri (a_uri: READABLE_STRING_8; a_action: like {WSF_URI_AGENT_HANDLER}.action; rqst_methods: detachable WSF_REQUEST_METHODS)
do
router.map_with_request_methods (create {WSF_URI_MAPPING}.make (a_uri, create {WSF_URI_AGENT_HANDLER}.make (a_action)), rqst_methods)
end
feature -- Execution
execute_hello (req: WSF_REQUEST; res: WSF_RESPONSE)
local
page: EMPTY_PAGE
do
-- To send a response we need to setup, the status code and
-- the response headers.
create page.make (req, res)
page.execute
end
end

View File

@@ -8,168 +8,22 @@ class
inherit
WSF_ROUTED_SERVICE
rename
execute as execute_router
end
WSF_FILTERED_SERVICE
WSF_DEFAULT_SERVICE
WSF_DEFAULT_SERVICE [APPLICATION_EXECUTION]
redefine
initialize
end
WSF_FILTER
rename
execute as execute_router
end
create
make_and_launch
feature {NONE} -- Initialization
-- tt
-- local
-- lst: ARRAYED_LIST [READABLE_STRING_GENERAL]
-- do
-- create lst.make (3)
-- lst.compare_objects
-- lst.extend ({STRING_32} "abc")
-- if lst.has ("abc") then
-- print ("found%N")
-- end
-- end
initialize
-- Initialize current service.
do
-- tt
initialize_router
initialize_filter
Precursor
set_service_option ("port", 9090)
end
feature -- Router and Filter
create_filter
-- Create `filter'
local
f, l_filter: detachable WSF_FILTER
do
l_filter := Void
-- Maintenance
create {WSF_MAINTENANCE_FILTER} f
f.set_next (l_filter)
l_filter := f
-- Logging
create {WSF_LOGGING_FILTER} f
f.set_next (l_filter)
l_filter := f
filter := l_filter
end
setup_filter
-- Setup `filter'
local
f: WSF_FILTER
do
from
f := filter
until
not attached f.next as l_next
loop
f := l_next
end
f.set_next (Current)
end
setup_router
do
map_agent_uri ("/", agent execute_hello, Void)
map_agent_uri ("/grid", agent grid_demo, Void)
map_agent_uri ("/repeater", agent repeater_demo, Void)
map_agent_uri ("/slider", agent slider_demo, Void)
map_agent_uri ("/upload", agent upload_demo, Void)
map_agent_uri ("/codeview", agent codeview, Void)
-- NOTE: you could put all those files in a specific folder, and use WSF_FILE_SYSTEM_HANDLER with "/"
-- this way, it handles the caching and so on
router.handle_with_request_methods ("/assets", create {WSF_FILE_SYSTEM_HANDLER}.make_hidden ("assets"), router.methods_GET)
end
feature -- Helper: mapping
map_agent_uri (a_uri: READABLE_STRING_8; a_action: like {WSF_URI_AGENT_HANDLER}.action; rqst_methods: detachable WSF_REQUEST_METHODS)
do
router.map_with_request_methods (create {WSF_URI_MAPPING}.make (a_uri, create {WSF_URI_AGENT_HANDLER}.make (a_action)), rqst_methods)
end
feature -- Execution
execute_hello (request: WSF_REQUEST; response: WSF_RESPONSE)
local
page: SAMPLE_PAGE
do
-- To send a response we need to setup, the status code and
-- the response headers.
create page.make (request, response)
page.execute
end
grid_demo (request: WSF_REQUEST; response: WSF_RESPONSE)
local
page: GRID_PAGE
do
-- To send a response we need to setup, the status code and
-- the response headers.
create page.make (request, response)
page.execute
end
repeater_demo (request: WSF_REQUEST; response: WSF_RESPONSE)
local
page: REPEATER_PAGE
do
-- To send a response we need to setup, the status code and
-- the response headers.
create page.make (request, response)
page.execute
end
slider_demo (request: WSF_REQUEST; response: WSF_RESPONSE)
local
page: SLIDER_PAGE
do
-- To send a response we need to setup, the status code and
-- the response headers.
create page.make (request, response)
page.execute
end
upload_demo (request: WSF_REQUEST; response: WSF_RESPONSE)
local
page: UPLOAD_PAGE
do
-- To send a response we need to setup, the status code and
-- the response headers.
create page.make (request, response)
page.execute
end
codeview (request: WSF_REQUEST; response: WSF_RESPONSE)
local
page: CODEVIEW_PAGE
do
-- To send a response we need to setup, the status code and
-- the response headers.
create page.make (request, response)
page.execute
end
end

View File

@@ -0,0 +1,113 @@
note
description: "Summary description for {APPLICATION_EXECUTION}."
date: "$Date$"
revision: "$Revision$"
class
APPLICATION_EXECUTION
inherit
WSF_FILTERED_ROUTED_EXECUTION
create
make
feature -- Router and Filter
create_filter
-- Create `filter'
do
-- Maintenance
create {WSF_MAINTENANCE_FILTER} filter
end
setup_filter
-- Setup `filter'
do
append_filters (<<create {WSF_LOGGING_FILTER}>>)
end
setup_router
do
map_agent_uri ("/", agent execute_hello, Void)
map_agent_uri ("/grid", agent grid_demo, Void)
map_agent_uri ("/repeater", agent repeater_demo, Void)
map_agent_uri ("/slider", agent slider_demo, Void)
map_agent_uri ("/upload", agent upload_demo, Void)
map_agent_uri ("/codeview", agent codeview, Void)
-- NOTE: you could put all those files in a specific folder, and use WSF_FILE_SYSTEM_HANDLER with "/"
-- this way, it handles the caching and so on
router.handle ("/assets", create {WSF_FILE_SYSTEM_HANDLER}.make_hidden ("assets"), router.methods_GET)
end
feature -- Helper: mapping
map_agent_uri (a_uri: READABLE_STRING_8; a_action: like {WSF_URI_AGENT_HANDLER}.action; rqst_methods: detachable WSF_REQUEST_METHODS)
do
router.map (create {WSF_URI_MAPPING}.make (a_uri, create {WSF_URI_AGENT_HANDLER}.make (a_action)), rqst_methods)
end
feature -- Execution
execute_hello (req: WSF_REQUEST; res: WSF_RESPONSE)
local
page: SAMPLE_PAGE
do
-- To send a response we need to setup, the status code and
-- the response headers.
create page.make (req, res)
page.execute
end
grid_demo (req: WSF_REQUEST; res: WSF_RESPONSE)
local
page: GRID_PAGE
do
-- To send a response we need to setup, the status code and
-- the response headers.
create page.make (req, res)
page.execute
end
repeater_demo (req: WSF_REQUEST; res: WSF_RESPONSE)
local
page: REPEATER_PAGE
do
-- To send a response we need to setup, the status code and
-- the response headers.
create page.make (req, res)
page.execute
end
slider_demo (req: WSF_REQUEST; res: WSF_RESPONSE)
local
page: SLIDER_PAGE
do
-- To send a response we need to setup, the status code and
-- the response headers.
create page.make (req, res)
page.execute
end
upload_demo (req: WSF_REQUEST; res: WSF_RESPONSE)
local
page: UPLOAD_PAGE
do
-- To send a response we need to setup, the status code and
-- the response headers.
create page.make (req, res)
page.execute
end
codeview (req: WSF_REQUEST; res: WSF_RESPONSE)
local
page: CODEVIEW_PAGE
do
-- To send a response we need to setup, the status code and
-- the response headers.
create page.make (req, res)
page.execute
end
end

View File

@@ -1,6 +1,5 @@
note
description: "Summary description for {CONTACT_AUTOCOMPLETION}."
author: ""
date: "$Date$"
revision: "$Revision$"

View File

@@ -1,6 +1,5 @@
note
description: "Summary description for {FLAG_AUTOCOMPLETION}."
author: ""
date: "$Date$"
revision: "$Revision$"

View File

@@ -1,6 +1,5 @@
note
description: "Summary description for {GOOGLE_AUTOCOMPLETION}."
author: ""
date: "$Date$"
revision: "$Revision$"

View File

@@ -1,6 +1,5 @@
note
description: "Summary description for {BASE_PAGE}."
author: ""
date: "$Date$"
revision: "$Revision$"

View File

@@ -1,6 +1,5 @@
note
description: "Summary description for {CODEVIEW_PAGE}."
author: ""
date: "$Date$"
revision: "$Revision$"

View File

@@ -1,6 +1,5 @@
note
description: "Summary description for {GOOGLE_NEWS}."
author: ""
date: "$Date$"
revision: "$Revision$"

View File

@@ -1,6 +1,5 @@
note
description: "Summary description for {GOOGLE_NEWS_DATASOURCE}."
author: ""
date: "$Date$"
revision: "$Revision$"

View File

@@ -1,6 +1,5 @@
note
description: "Summary description for {GOOGLE_NEWS_REPEATER}."
author: ""
date: "$Date$"
revision: "$Revision$"

View File

@@ -1,6 +1,5 @@
note
description: "Summary description for {GRID_PAGE}."
author: ""
date: "$Date$"
revision: "$Revision$"

View File

@@ -1,6 +1,5 @@
note
description: "Summary description for {OWN_VALIDATOR}."
author: ""
date: "$Date$"
revision: "$Revision$"

View File

@@ -1,6 +1,5 @@
note
description: "Summary description for {INCREASING_PROGRESSSOURCE}."
author: ""
date: "$Date$"
revision: "$Revision$"

View File

@@ -1,6 +1,5 @@
note
description: "Summary description for {REPEATER_PAGE}."
author: ""
date: "$Date$"
revision: "$Revision$"

View File

@@ -1,6 +1,5 @@
note
description: "Summary description for {SAMPLE_PAGE}."
author: ""
date: "$Date$"
revision: "$Revision$"

View File

@@ -1,6 +1,5 @@
note
description: "Summary description for {SLIDER_PAGE}."
author: ""
date: "$Date$"
revision: "$Revision$"

View File

@@ -1,6 +1,5 @@
note
description: "Summary description for {UPLOAD_PAGE}."
author: ""
date: "$Date$"
revision: "$Revision$"

View File

@@ -18,12 +18,21 @@
</target>
<target name="debug_any" extends="common">
<root class="EWF_DEBUG_SERVER" feature="make_and_launch"/>
<setting name="concurrency" value="thread"/>
<library name="cgi" location="..\..\library\server\wsf\connector\cgi-safe.ecf" readonly="false"/>
<library name="libfcgi" location="..\..\library\server\wsf\connector\libfcgi-safe.ecf"/>
<library name="nino" location="..\..\library\server\wsf\connector\nino-safe.ecf"/>
<library name="libfcgi" location="..\..\library\server\wsf\connector\libfcgi-safe.ecf" readonly="false"/>
<library name="nino" location="..\..\library\server\wsf\connector\nino-safe.ecf" readonly="false"/>
<library name="standalone" location="..\..\library\server\wsf\connector\standalone-safe.ecf" readonly="false"/>
<cluster name="launcher" location=".\launcher\any\" recursive="true"/>
<cluster name="src" location=".\src\" recursive="true"/>
</target>
<target name="debug_standalone" extends="common">
<root class="EWF_DEBUG_SERVER" feature="make_and_launch"/>
<setting name="concurrency" value="thread"/>
<library name="default_standalone" location="..\..\library\server\wsf\default\standalone-safe.ecf" readonly="false"/>
<cluster name="launcher" location=".\launcher\default\" recursive="true"/>
<cluster name="src" location=".\src\" recursive="true"/>
</target>
<target name="debug_nino" extends="common">
<root class="EWF_DEBUG_SERVER" feature="make_and_launch"/>
<library name="default_nino" location="..\..\library\server\wsf\default\nino-safe.ecf"/>
@@ -32,7 +41,7 @@
</target>
<target name="debug_cgi" extends="common">
<root class="EWF_DEBUG_SERVER" feature="make_and_launch"/>
<library name="default_cgi" location="..\..\library\server\wsf\default\cgi-safe.ecf"/>
<library name="default_cgi" location="..\..\library\server\wsf\default\cgi-safe.ecf" readonly="false"/>
<cluster name="launcher" location=".\launcher\default\" recursive="true"/>
<cluster name="src" location=".\src\" recursive="true"/>
</target>

View File

@@ -8,10 +8,10 @@ note
revision: "$Revision: 36 $"
class
APPLICATION_LAUNCHER
APPLICATION_LAUNCHER [G -> WSF_EXECUTION create make end]
inherit
APPLICATION_LAUNCHER_I
APPLICATION_LAUNCHER_I [G]
feature -- Custom

View File

@@ -10,24 +10,26 @@ note
revision: "$Revision: 36 $"
deferred class
APPLICATION_LAUNCHER_I
APPLICATION_LAUNCHER_I [G -> WSF_EXECUTION create make end]
inherit
SHARED_EXECUTION_ENVIRONMENT
feature -- Execution
launch (a_service: WSF_SERVICE; opts: detachable WSF_SERVICE_LAUNCHER_OPTIONS)
launch (opts: detachable WSF_SERVICE_LAUNCHER_OPTIONS)
local
nature: like launcher_nature
do
nature := launcher_nature
if nature = Void or else nature = nature_nino then
launch_nino (a_service, opts)
if nature = Void or else nature = nature_standalone then
launch_standalone (opts)
elseif nature = nature_nino then
launch_nino (opts)
elseif nature = nature_cgi then
launch_cgi (a_service, opts)
launch_cgi (opts)
elseif nature = nature_libfcgi then
launch_libfcgi (a_service, opts)
launch_libfcgi (opts)
else
-- bye bye
(create {EXCEPTIONS}).die (-1)
@@ -43,14 +45,16 @@ feature {NONE} -- Access
--| and we could use WSF_DEFAULT_SERVICE_LAUNCHER to configure this at compilation time.
local
p: PATH
l_entry_name: READABLE_STRING_32
ext: detachable READABLE_STRING_32
do
create p.make_from_string (execution_environment.arguments.command_name)
if attached p.entry as l_entry then
ext := l_entry.extension
ext := l_entry.extension
end
if ext /= Void then
if ext.same_string (nature_standalone) then
Result := nature_standalone
end
if ext.same_string (nature_nino) then
Result := nature_nino
end
@@ -61,39 +65,51 @@ feature {NONE} -- Access
Result := nature_libfcgi
end
end
Result := nature_standalone
end
feature {NONE} -- nino
nature_standalone: STRING = "standalone"
launch_standalone (opts: detachable WSF_SERVICE_LAUNCHER_OPTIONS)
local
launcher: WSF_STANDALONE_SERVICE_LAUNCHER [G]
do
create launcher.make_and_launch (opts)
end
feature {NONE} -- nino
nature_nino: STRING = "nino"
launch_nino (a_service: WSF_SERVICE; opts: detachable WSF_SERVICE_LAUNCHER_OPTIONS)
launch_nino (opts: detachable WSF_SERVICE_LAUNCHER_OPTIONS)
local
launcher: WSF_NINO_SERVICE_LAUNCHER
launcher: WSF_NINO_SERVICE_LAUNCHER [G]
do
create launcher.make_and_launch (a_service, opts)
create launcher.make_and_launch (opts)
end
feature {NONE} -- cgi
nature_cgi: STRING = "cgi"
launch_cgi (a_service: WSF_SERVICE; opts: detachable WSF_SERVICE_LAUNCHER_OPTIONS)
launch_cgi (opts: detachable WSF_SERVICE_LAUNCHER_OPTIONS)
local
launcher: WSF_CGI_SERVICE_LAUNCHER
launcher: WSF_CGI_SERVICE_LAUNCHER [G]
do
create launcher.make_and_launch (a_service, opts)
create launcher.make_and_launch (opts)
end
feature {NONE} -- libfcgi
nature_libfcgi: STRING = "libfcgi"
launch_libfcgi (a_service: WSF_SERVICE; opts: detachable WSF_SERVICE_LAUNCHER_OPTIONS)
launch_libfcgi (opts: detachable WSF_SERVICE_LAUNCHER_OPTIONS)
local
launcher: WSF_LIBFCGI_SERVICE_LAUNCHER
launcher: WSF_LIBFCGI_SERVICE_LAUNCHER [G]
do
create launcher.make_and_launch (a_service, opts)
create launcher.make_and_launch (opts)
end

View File

@@ -8,10 +8,10 @@ note
revision: "$Revision: 36 $"
class
APPLICATION_LAUNCHER
APPLICATION_LAUNCHER [G -> WSF_EXECUTION create make end]
inherit
APPLICATION_LAUNCHER_I
APPLICATION_LAUNCHER_I [G]
feature -- Custom

View File

@@ -10,15 +10,15 @@ note
revision: "$Revision: 36 $"
deferred class
APPLICATION_LAUNCHER_I
APPLICATION_LAUNCHER_I [G -> WSF_EXECUTION create make end]
feature -- Execution
launch (a_service: WSF_SERVICE; opts: detachable WSF_SERVICE_LAUNCHER_OPTIONS)
launch (opts: detachable WSF_SERVICE_LAUNCHER_OPTIONS)
local
launcher: WSF_SERVICE_LAUNCHER
launcher: WSF_SERVICE_LAUNCHER [G]
do
create {WSF_DEFAULT_SERVICE_LAUNCHER} launcher.make_and_launch (a_service, opts)
create {WSF_DEFAULT_SERVICE_LAUNCHER [G]} launcher.make_and_launch (opts)
end
end

View File

@@ -0,0 +1,27 @@
note
description: "Summary description for {EWF_DEBUG_EXECUTION}."
author: ""
date: "$Date$"
revision: "$Revision$"
class
EWF_DEBUG_EXECUTION
inherit
WSF_EXECUTION
create
make
feature -- Execution
execute
local
dbg: WSF_DEBUG_HANDLER
do
response.put_error ("DEBUG uri=" + request.request_uri + "%N")
create dbg.make
dbg.execute_starts_with ("", request, response)
end
end

View File

@@ -14,7 +14,7 @@ inherit
initialize
end
APPLICATION_LAUNCHER
APPLICATION_LAUNCHER [EWF_DEBUG_EXECUTION]
create
make_and_launch
@@ -30,14 +30,14 @@ feature {NONE} -- Initialization
-- set_service_option ("base", "/www-debug/debug_service.fcgi/")
end
execute (req: WSF_REQUEST; res: WSF_RESPONSE)
local
dbg: WSF_DEBUG_HANDLER
do
res.put_error ("DEBUG" + req.request_uri + "%N")
create dbg.make
dbg.execute_starts_with ("", req, res)
end
-- execute (req: WSF_REQUEST; res: WSF_RESPONSE)
-- local
-- dbg: WSF_DEBUG_HANDLER
-- do
-- res.put_error ("OH NO uri=" + req.request_uri + "%N")
-- create dbg.make
-- dbg.execute_starts_with ("", req, res)
-- end
end

View File

@@ -8,16 +8,15 @@
<assertions precondition="true" postcondition="true" check="true"/>
</option>
<setting name="concurrency" value="thread"/>
<precompile name="vision2-pre" location="$ISE_PRECOMP\vision2-mt-safe.ecf"/>
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
<library name="ewsgi" location="..\..\library\server\ewsgi\ewsgi-safe.ecf"/>
<library name="thread" location="$ISE_LIBRARY\library\thread\thread-safe.ecf"/>
<library name="vision2" location="$ISE_LIBRARY\library\vision2\vision2-safe.ecf"/>
<library name="web_browser" location="$ISE_LIBRARY\library\web_browser\web_browser-safe.ecf" readonly="false"/>
<library name="wsf" location="..\..\library\server\wsf\wsf-safe.ecf"/>
<library name="wsf_nino" location="..\..\library\server\wsf\connector\nino-safe.ecf"/>
<library name="wsf_nino_connector" location="..\..\library\server\ewsgi\connectors\nino\nino-safe.ecf"/>
<cluster name="src" location=".\src" recursive="true">
<library name="wsf_standalone" location="..\..\library\server\wsf\connector\standalone-safe.ecf"/>
<library name="wsf_standalone_connector" location="..\..\library\server\ewsgi\connectors\standalone\standalone-safe.ecf"/>
<cluster name="src" location=".\src\" recursive="true">
<file_rule>
<exclude>/EIFGENs$</exclude>
<exclude>/CVS$</exclude>

View File

@@ -0,0 +1,239 @@
note
description: "Summary description for {APP_EMBEDDED_WEB_EXECUTION}."
author: ""
date: "$Date$"
revision: "$Revision$"
class
APP_EMBEDDED_WEB_EXECUTION
inherit
EMBEDDED_WEB_EXECUTION
redefine
initialize
end
create
make
feature {NONE} -- Initialization
initialize
do
Precursor
create request_exit_operation_actions
local_connection_restriction_enabled := True
end
feature -- Execution
request_exit_operation_actions: ACTION_SEQUENCE [TUPLE]
execute
-- Execute the request
-- See `request.input' for input stream
-- `request.meta_variables' for the CGI meta variable
-- and `response' for output buffer
local
router: WSF_ROUTER
sess: detachable WSF_ROUTER_SESSION
m: WSF_HTML_PAGE_RESPONSE
b: STRING
fs: WSF_FILE_SYSTEM_HANDLER
req: like request
do
req := request
create router.make (3)
router.handle ("/test/{var}", create {WSF_URI_TEMPLATE_AGENT_HANDLER}.make (agent handle_test), Void)
router.handle ("/env", create {WSF_URI_AGENT_HANDLER}.make (agent handle_env), Void)
router.handle ("/exit", create {WSF_URI_TEMPLATE_AGENT_HANDLER}.make (agent handle_exit), Void)
create fs.make_with_path ((create {EXECUTION_ENVIRONMENT}).current_working_path.extended ("files"))
router.handle ("/files", fs, Void)
create sess
router.dispatch (req, response, sess)
if not sess.dispatched then
create m.make
create b.make_from_string ("<h1>Hello Eiffel desktop user</h1>")
b.append ("<li><a href=%"" + req.script_url ("/test/start") + "%">test</a></li>")
b.append ("<li><a href=%"" + req.script_url ("/env") + "%">env</a></li>")
b.append ("<li><a href=%"" + req.script_url ("/files") + "%">files</a></li>")
b.append ("<li><a href=%"" + req.script_url ("/exit") + "%">exit</a></li>")
m.set_body (b)
response.send (m)
end
end
handle_test (req: WSF_REQUEST; res: WSF_RESPONSE)
local
m: WSF_HTML_PAGE_RESPONSE
b: STRING
l_name: READABLE_STRING_32
do
if attached {WSF_STRING} req.item ("var") as p_name then
l_name := p_name.value
else
l_name := {STRING_32} "Embedded web service and web_browser in vision2 application"
end
create m.make
create b.make_from_string ("<h1>This is a test about "+ m.html_encoded_string (l_name) +"</h1>")
b.append ("<li><a href=%"" + req.script_url ("/") + "%">back to home</a></li>")
if l_name.is_case_insensitive_equal_general ("start") then
b.append ("<li><a href=%"" + req.script_url ("/test/js") + "%">test javascript+ajax</a></li>")
elseif l_name.is_case_insensitive_equal_general ("js") then
b.append ("[
<div id="myDiv"><h2>Let AJAX change this text</h2>
<button type="button" onclick="loadXMLDoc()">Change Content</button>
</div>
]")
m.add_javascript_content ("[
function loadXMLDoc()
{
var xmlhttp;
if (window.XMLHttpRequest)
{// code for IE7+, Firefox, Chrome, Opera, Safari
xmlhttp=new XMLHttpRequest();
}
else
{// code for IE6, IE5
xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
}
xmlhttp.onreadystatechange=function()
{
if (xmlhttp.readyState==4 && xmlhttp.status==200)
{
document.getElementById("myDiv").innerHTML=xmlhttp.responseText;
}
}
xmlhttp.open("GET","/test/ajax.txt",true);
xmlhttp.send();
}
]")
elseif l_name.is_case_insensitive_equal_general ("ajax.txt") then
b := "This is AJAX response ... from " + req.absolute_script_url ("")
end
m.set_body (b)
res.send (m)
end
handle_env (req: WSF_REQUEST; res: WSF_RESPONSE)
local
s: STRING_8
p: WSF_PAGE_RESPONSE
v: STRING_8
do
create s.make (2048)
s.append ("**DEBUG**%N")
req.set_raw_input_data_recorded (True)
append_iterable_to ("Meta variables:", req.meta_variables, s)
s.append_character ('%N')
append_iterable_to ("Path parameters", req.path_parameters, s)
s.append_character ('%N')
append_iterable_to ("Query parameters", req.query_parameters, s)
s.append_character ('%N')
append_iterable_to ("Form parameters", req.form_parameters, s)
s.append_character ('%N')
if attached req.content_type as l_type then
s.append ("Content: type=" + l_type.debug_output)
s.append (" length=")
s.append_natural_64 (req.content_length_value)
s.append_character ('%N')
create v.make (req.content_length_value.to_integer_32)
req.read_input_data_into (v)
across
v.split ('%N') as v_cursor
loop
s.append (" |")
s.append (v_cursor.item)
s.append_character ('%N')
end
end
create p.make_with_body (s)
p.header.put_content_type_text_plain
res.send (p)
end
handle_exit (req: WSF_REQUEST; res: WSF_RESPONSE)
local
m: WSF_HTML_PAGE_RESPONSE
b: STRING
do
create m.make
create b.make_from_string ("<h1>Embedded server is about to shutdown</h1>")
b.append ("<li><a href=%"" + req.script_url ("/") + "%">back to home</a></li>")
b.append ("<li><a href=%"" + req.script_url ("/bye") + "%">Click to confirm exit operation</a></li>")
m.set_body (b)
res.send (m)
if attached {separate WGI_STANDALONE_CONNECTOR [WGI_EXECUTION]} req.wgi_connector as conn then
shutdown_server (conn)
end
request_exit_operation_actions.call (Void)
end
shutdown_server (conn: separate WGI_STANDALONE_CONNECTOR [WGI_EXECUTION])
do
conn.shutdown_server
end
feature {NONE} -- Implementation
append_iterable_to (a_title: READABLE_STRING_8; it: detachable ITERABLE [WSF_VALUE]; s: STRING_8)
local
n: INTEGER
t: READABLE_STRING_8
v: READABLE_STRING_8
do
s.append (a_title)
s.append_character (':')
if it /= Void then
across it as c loop
n := n + 1
end
if n = 0 then
s.append (" empty")
s.append_character ('%N')
else
s.append_character ('%N')
across
it as c
loop
s.append (" - ")
s.append (c.item.url_encoded_name)
t := c.item.generating_type
if t.same_string ("WSF_STRING") then
else
s.append_character (' ')
s.append_character ('{')
s.append (t)
s.append_character ('}')
end
s.append_character ('=')
v := c.item.string_representation.as_string_8
if v.has ('%N') then
s.append_character ('%N')
across
v.split ('%N') as v_cursor
loop
s.append (" |")
s.append (v_cursor.item)
s.append_character ('%N')
end
else
s.append (v)
s.append_character ('%N')
end
end
end
else
s.append (" none")
s.append_character ('%N')
end
end
end

View File

@@ -8,223 +8,11 @@ class
APP_EMBEDDED_WEB_SERVICE
inherit
EMBEDDED_WEB_SERVICE
redefine
make
end
EMBEDDED_WEB_SERVICE [APP_EMBEDDED_WEB_EXECUTION]
create
make
feature {NONE} -- Initialization
make
do
Precursor
create request_exit_operation_actions
local_connection_restriction_enabled := True
end
feature -- Execution
request_exit_operation_actions: ACTION_SEQUENCE [TUPLE]
execute (req: WSF_REQUEST; res: WSF_RESPONSE)
-- Execute the request
-- See `req.input' for input stream
-- `req.meta_variables' for the CGI meta variable
-- and `res' for output buffer
local
router: WSF_ROUTER
sess: detachable WSF_ROUTER_SESSION
m: WSF_HTML_PAGE_RESPONSE
b: STRING
fs: WSF_FILE_SYSTEM_HANDLER
do
create router.make (3)
router.handle ("/test/{var}", create {WSF_URI_TEMPLATE_AGENT_HANDLER}.make (agent handle_test))
router.handle ("/env", create {WSF_URI_AGENT_HANDLER}.make (agent handle_env))
router.handle ("/exit", create {WSF_URI_TEMPLATE_AGENT_HANDLER}.make (agent handle_exit))
create fs.make_with_path ((create {EXECUTION_ENVIRONMENT}).current_working_path.extended ("files"))
router.handle ("/files", fs)
create sess
router.dispatch (req, res, sess)
if not sess.dispatched then
create m.make
create b.make_from_string ("<h1>Hello Eiffel desktop user</h1>")
b.append ("<li><a href=%"" + req.script_url ("/test/start") + "%">test</a></li>")
b.append ("<li><a href=%"" + req.script_url ("/env") + "%">env</a></li>")
b.append ("<li><a href=%"" + req.script_url ("/files") + "%">files</a></li>")
b.append ("<li><a href=%"" + req.script_url ("/exit") + "%">exit</a></li>")
m.set_body (b)
res.send (m)
end
end
handle_test (req: WSF_REQUEST; res: WSF_RESPONSE)
local
m: WSF_HTML_PAGE_RESPONSE
b: STRING
l_name: READABLE_STRING_32
do
if attached {WSF_STRING} req.item ("var") as p_name then
l_name := p_name.value
else
l_name := {STRING_32} "Embedded web service and web_browser in vision2 application"
end
create m.make
create b.make_from_string ("<h1>This is a test about "+ m.html_encoded_string (l_name) +"</h1>")
b.append ("<li><a href=%"" + req.script_url ("/") + "%">back to home</a></li>")
if l_name.is_case_insensitive_equal_general ("start") then
b.append ("<li><a href=%"" + req.script_url ("/test/js") + "%">test javascript+ajax</a></li>")
elseif l_name.is_case_insensitive_equal_general ("js") then
b.append ("[
<div id="myDiv"><h2>Let AJAX change this text</h2>
<button type="button" onclick="loadXMLDoc()">Change Content</button>
</div>
]")
m.add_javascript_content ("[
function loadXMLDoc()
{
var xmlhttp;
if (window.XMLHttpRequest)
{// code for IE7+, Firefox, Chrome, Opera, Safari
xmlhttp=new XMLHttpRequest();
}
else
{// code for IE6, IE5
xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
}
xmlhttp.onreadystatechange=function()
{
if (xmlhttp.readyState==4 && xmlhttp.status==200)
{
document.getElementById("myDiv").innerHTML=xmlhttp.responseText;
}
}
xmlhttp.open("GET","/test/ajax.txt",true);
xmlhttp.send();
}
]")
elseif l_name.is_case_insensitive_equal_general ("ajax.txt") then
b := "This is AJAX response ... from " + req.absolute_script_url ("")
end
m.set_body (b)
res.send (m)
end
handle_env (req: WSF_REQUEST; res: WSF_RESPONSE)
local
s: STRING_8
p: WSF_PAGE_RESPONSE
v: STRING_8
do
create s.make (2048)
s.append ("**DEBUG**%N")
req.set_raw_input_data_recorded (True)
append_iterable_to ("Meta variables:", req.meta_variables, s)
s.append_character ('%N')
append_iterable_to ("Path parameters", req.path_parameters, s)
s.append_character ('%N')
append_iterable_to ("Query parameters", req.query_parameters, s)
s.append_character ('%N')
append_iterable_to ("Form parameters", req.form_parameters, s)
s.append_character ('%N')
if attached req.content_type as l_type then
s.append ("Content: type=" + l_type.debug_output)
s.append (" length=")
s.append_natural_64 (req.content_length_value)
s.append_character ('%N')
create v.make (req.content_length_value.to_integer_32)
req.read_input_data_into (v)
across
v.split ('%N') as v_cursor
loop
s.append (" |")
s.append (v_cursor.item)
s.append_character ('%N')
end
end
create p.make_with_body (s)
p.header.put_content_type_text_plain
res.send (p)
end
handle_exit (req: WSF_REQUEST; res: WSF_RESPONSE)
local
m: WSF_HTML_PAGE_RESPONSE
b: STRING
do
create m.make
create b.make_from_string ("<h1>Embedded server is about to shutdown</h1>")
b.append ("<li><a href=%"" + req.script_url ("/") + "%">back to home</a></li>")
m.set_body (b)
res.send (m)
if attached {WGI_NINO_CONNECTOR} req.wgi_connector as nino then
nino.server.shutdown_server
end
request_exit_operation_actions.call (Void)
end
feature {NONE} -- Implementation
append_iterable_to (a_title: READABLE_STRING_8; it: detachable ITERABLE [WSF_VALUE]; s: STRING_8)
local
n: INTEGER
t: READABLE_STRING_8
v: READABLE_STRING_8
do
s.append (a_title)
s.append_character (':')
if it /= Void then
across it as c loop
n := n + 1
end
if n = 0 then
s.append (" empty")
s.append_character ('%N')
else
s.append_character ('%N')
across
it as c
loop
s.append (" - ")
s.append (c.item.url_encoded_name)
t := c.item.generating_type
if t.same_string ("WSF_STRING") then
else
s.append_character (' ')
s.append_character ('{')
s.append (t)
s.append_character ('}')
end
s.append_character ('=')
v := c.item.string_representation.as_string_8
if v.has ('%N') then
s.append_character ('%N')
across
v.split ('%N') as v_cursor
loop
s.append (" |")
s.append (v_cursor.item)
s.append_character ('%N')
end
else
s.append (v)
s.append_character ('%N')
end
end
end
else
s.append (" none")
s.append_character ('%N')
end
end
end

View File

@@ -24,18 +24,17 @@ feature {NONE} -- Initialization
-- then launch the application.
local
l_win: like main_window
l_embeded_services: APP_EMBEDDED_WEB_SERVICE
l_embedded_service: APP_EMBEDDED_WEB_SERVICE
do
default_create
create l_win.make
main_window := l_win
l_win.show
create l_embeded_services.make
l_embeded_services.set_port_number (0) -- Use first available port number
create l_embedded_service.make
l_embedded_service.set_port_number (0) -- Use first available port number
l_embeded_services.on_launched_actions.force (agent on_web_service_launched (l_win))
l_embeded_services.request_exit_operation_actions.force (agent on_quit)
l_embeded_services.launch
l_embedded_service.on_launched_actions.force (agent on_web_service_launched (l_win, l_embedded_service))
l_embedded_service.launch
launch
end
@@ -46,11 +45,44 @@ feature {NONE} -- Initialization
end
end
on_web_service_launched (a_win: attached like main_window)
on_web_service_launched (a_win: attached like main_window; s: APP_EMBEDDED_WEB_SERVICE)
do
add_idle_action_kamikaze (agent wait_for_termination (s, Void))
add_idle_action_kamikaze (agent a_win.open_link)
end
wait_for_termination (s: APP_EMBEDDED_WEB_SERVICE; a_timeout: detachable EV_TIMEOUT)
local
t: detachable EV_TIMEOUT
do
t := a_timeout
if t /= Void then
t.set_interval (0)
end
if
attached s.observer as obs and then
observer_has_terminaded (obs)
then
if t /= Void then
t.destroy
end
on_quit
else
if t = Void then
create t
t.actions.extend (agent wait_for_termination (s, t))
else
t.set_interval (1_000)
end
t.set_interval (1_000)
end
end
observer_has_terminaded (obs: separate WGI_STANDALONE_SERVER_OBSERVER): BOOLEAN
do
Result := obs.terminated
end
feature {NONE} -- Implementation
main_window: detachable MAIN_WINDOW

View File

@@ -0,0 +1,63 @@
note
description: "Summary description for {EMBEDDED_WEB_EXECUTION}."
author: ""
date: "$Date$"
revision: "$Revision$"
deferred class
EMBEDDED_WEB_EXECUTION
inherit
WSF_EXECUTION
rename
execute as execute_embedded
end
SHARED_EMBEDED_WEB_SERVICE_INFORMATION
feature {NONE} -- Execution
execute_embedded
-- Execute the request
-- See `request.input' for input stream
-- `request.meta_variables' for the CGI meta variable
-- and `response' for output buffer
local
filter: WSF_AGENT_FILTER
m: WSF_PAGE_RESPONSE
do
if local_connection_restriction_enabled then
if
attached request.remote_addr as l_remote_addr and then
l_remote_addr.is_case_insensitive_equal_general ("127.0.0.1")
then
execute
else
create m.make_with_body ("Only local connection is allowed")
m.set_status_code (403) -- Forbidden
response.send (m)
end
else
execute
end
end
execute
deferred
end
feature -- Status report
local_connection_restriction_enabled: BOOLEAN
-- Accept only local connection?
--| based on 127.0.0.1 IP
--| TO IMPROVE
feature -- Change
set_local_connection_restriction_enabled (b: BOOLEAN)
do
local_connection_restriction_enabled := b
end
end

View File

@@ -5,19 +5,10 @@ note
revision: "$Revision$"
deferred class
EMBEDDED_WEB_SERVICE
EMBEDDED_WEB_SERVICE [G -> EMBEDDED_WEB_EXECUTION create make end]
inherit
THREAD
rename
make as make_thread,
execute as execute_thread
end
WSF_SERVICE
rename
execute as execute_embedded
end
SHARED_EMBEDED_WEB_SERVICE_INFORMATION
@@ -25,90 +16,35 @@ feature -- Initialization
make
do
make_thread
create on_launched_actions
end
feature {NONE} -- Execution
feature -- Execution
execute_embedded (req: WSF_REQUEST; res: WSF_RESPONSE)
-- Execute the request
-- See `req.input' for input stream
-- `req.meta_variables' for the CGI meta variable
-- and `res' for output buffer
launch
local
filter: WSF_AGENT_FILTER
m: WSF_PAGE_RESPONSE
do
if local_connection_restriction_enabled then
if
attached req.remote_addr as l_remote_addr and then
l_remote_addr.is_case_insensitive_equal_general ("127.0.0.1")
then
execute (req, res)
else
create m.make_with_body ("Only local connection is allowed")
m.set_status_code (403) -- Forbidden
res.send (m)
end
else
execute (req, res)
end
end
execute_thread
local
nino: WSF_NINO_SERVICE_LAUNCHER
launcher: WSF_STANDALONE_SERVICE_LAUNCHER [G]
opts: WSF_SERVICE_LAUNCHER_OPTIONS
do
create opts.default_create
opts.set_verbose (True)
opts.set_option ("port", port_number)
create nino.make (Current, opts)
nino.on_launched_actions.force (agent on_launched)
nino.launch
create launcher.make (opts)
observer := launcher.connector.observer
launcher.on_launched_actions.force (agent on_launched)
launcher.launch
end
execute (req: WSF_REQUEST; res: WSF_RESPONSE)
-- Execute the request
-- See `req.input' for input stream
-- `req.meta_variables' for the CGI meta variable
-- and `res' for output buffer
deferred
end
observer: detachable separate WGI_STANDALONE_SERVER_OBSERVER
on_launched (conn: WGI_CONNECTOR)
on_launched (conn: WGI_STANDALONE_CONNECTOR [G])
do
if attached {WGI_NINO_CONNECTOR} conn as nino then
set_port_number (nino.port)
end
set_port_number (conn.port)
on_launched_actions.call (Void)
end
feature -- Control
wait
-- Wait for server to be terminated.
do
join
end
feature -- Access
on_launched_actions: ACTION_SEQUENCE [TUPLE]
feature -- Status report
local_connection_restriction_enabled: BOOLEAN
-- Accept only local connection?
--| based on 127.0.0.1 IP
--| TO IMPROVE
feature -- Change
set_local_connection_restriction_enabled (b: BOOLEAN)
do
local_connection_restriction_enabled := b
end
end

View File

@@ -11,15 +11,25 @@ feature -- Access
port_number: INTEGER
do
Result := port_number_cell.item
Result := separate_port_number (port_number_cell)
end
set_port_number (a_port: like port_number)
do
port_number_cell.replace (a_port)
separate_set_port_number (port_number_cell, a_port)
end
port_number_cell: CELL [INTEGER]
separate_port_number (cl: like port_number_cell): like port_number
do
Result := cl.item
end
separate_set_port_number (cl: like port_number_cell; a_port: like port_number)
do
cl.replace (a_port)
end
port_number_cell: separate CELL [INTEGER]
once ("process")
create Result.put (0)
end

View File

@@ -1,30 +1,36 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-9-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-9-0 http://www.eiffel.com/developers/xml/configuration-1-9-0.xsd" name="filter" uuid="52FF4B77-0614-4D8B-9B96-C07EC852793E" library_target="filter">
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-13-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-13-0 http://www.eiffel.com/developers/xml/configuration-1-13-0.xsd" name="filter" uuid="52FF4B77-0614-4D8B-9B96-C07EC852793E" library_target="filter">
<target name="common">
<file_rule>
<exclude>/EIFGENs$</exclude>
<exclude>/\.git$</exclude>
<exclude>/\.svn$</exclude>
</file_rule>
<option debug="true" warning="true" full_class_checking="true" is_attached_by_default="true" void_safety="all" syntax="provisional">
<option debug="true" warning="true" full_class_checking="true" is_attached_by_default="true" void_safety="transitional" syntax="provisional">
<debug name="nino" enabled="true"/>
<assertions precondition="true" postcondition="true" invariant="true" supplier_precondition="true"/>
</option>
<setting name="concurrency" value="thread"/>
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf" readonly="true"/>
<library name="net" location="$ISE_LIBRARY\library\net\net-safe.ecf" readonly="true"/>
<library name="http" location="../../library/network/protocol/http/http-safe.ecf" readonly="true"/>
<library name="json" location="..\..\contrib\library\text\parser\json\library\json-safe.ecf" readonly="true"/>
<library name="wsf" location="..\..\library\server\wsf\wsf-safe.ecf" readonly="true"/>
<library name="wsf_router_context" location="..\..\library\server\wsf\wsf_router_context-safe.ecf" readonly="true"/>
<library name="wsf_extension" location="..\..\library\server\wsf\wsf_extension-safe.ecf" readonly="true"/>
<library name="http" location="..\..\library\network\protocol\http\http-safe.ecf" readonly="true"/>
<library name="http_authorization" location="..\..\library\server\authentication\http_authorization\http_authorization-safe.ecf" readonly="true"/>
<library name="json" location="..\..\contrib\library\text\parser\json\library\json-safe.ecf" readonly="true"/>
<library name="net" location="$ISE_LIBRARY\library\net\net-safe.ecf" readonly="true"/>
<library name="wsf" location="..\..\library\server\wsf\wsf-safe.ecf" readonly="false"/>
<library name="wsf_extension" location="..\..\library\server\wsf\wsf_extension-safe.ecf" readonly="true"/>
<library name="wsf_router_context" location="..\..\library\server\wsf\wsf_router_context-safe.ecf" readonly="true"/>
</target>
<target name="filter_nino" extends="common">
<root class="FILTER_SERVER" feature="make"/>
<library name="default_nino" location="..\..\library\server\wsf\default\nino-safe.ecf" readonly="true"/>
<cluster name="filter" location="src\" recursive="true"/>
</target>
<target name="filter_standalone" extends="common">
<root class="FILTER_SERVER" feature="make"/>
<setting name="concurrency" value="thread"/>
<library name="default_standalone" location="..\..\library\server\wsf\default\standalone-safe.ecf" readonly="true"/>
<cluster name="filter" location="src\" recursive="true"/>
</target>
<target name="filter_fcgi" extends="common">
<root class="FILTER_SERVER" feature="make"/>
<library name="default_libfcgi" location="..\..\library\server\wsf\default\libfcgi-safe.ecf"/>

View File

@@ -24,26 +24,33 @@ feature -- Initialization
feature -- Access
user_by_id (a_id: INTEGER): detachable USER
do
Result := users.item (a_id)
end
user_by_name (a_name: READABLE_STRING_GENERAL): detachable USER
do
across
users as c
until
Result /= Void
loop
if attached c.item as u and then a_name.same_string (u.name) then
Result := u
end
end
end
user (a_id: INTEGER; a_name: detachable READABLE_STRING_GENERAL): detachable USER
-- User with id `a_id' or name `a_name'.
require
a_id > 0 xor a_name /= Void
local
n: like {USER}.name
do
if a_id > 0 then
Result := users.item (a_id)
Result := user_by_id (a_id)
elseif a_name /= Void then
n := a_name.as_string_8
across
users as c
until
Result /= Void
loop
if attached c.item as u and then u.name.same_string (n) then
Result := u
end
end
Result := user_by_name (a_name)
end
ensure
Result /= Void implies ((a_id > 0 and then Result.id = a_id) xor (a_name /= Void and then Result.name.same_string_general (a_name)))
@@ -52,6 +59,6 @@ feature -- Access
users: HASH_TABLE [USER, INTEGER]
;note
copyright: "2011-2012, Olivier Ligot, Jocelyn Fiat and others"
copyright: "2011-2015, Olivier Ligot, Jocelyn Fiat and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
end

View File

@@ -48,18 +48,31 @@ feature {NONE} -- Implementation
-- Handle forbidden.
local
h: HTTP_HEADER
s: STRING
do
create h.make
h.put_content_type_text_plain
h.put_content_length (a_description.count)
h.put_current_date
h.put_header_key_value ({HTTP_HEADER_NAMES}.header_www_authenticate, "Basic realm=%"User%"")
s := "Basic realm=%"For this demo, use any of: "
across
db_access.users as ic
loop
s.append_character ('(')
s.append (ic.item.name)
s.append_character (':')
s.append (ic.item.password)
s.append_character (')')
s.append_character (' ')
end
s.append_character ('"')
h.put_header_key_value ({HTTP_HEADER_NAMES}.header_www_authenticate, s)
res.set_status_code ({HTTP_STATUS_CODE}.unauthorized)
res.put_header_text (h.string)
res.put_string (a_description)
end
note
copyright: "2011-2014, Olivier Ligot, Jocelyn Fiat and others"
copyright: "2011-2015, Olivier Ligot, Jocelyn Fiat and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
end

View File

@@ -8,18 +8,7 @@ class
FILTER_SERVER
inherit
ANY
WSF_DEFAULT_SERVICE
WSF_ROUTED_SERVICE
undefine
execute
end
WSF_FILTERED_SERVICE
SHARED_EJSON
WSF_DEFAULT_SERVICE [FILTER_SERVER_EXECUTION]
create
make
@@ -31,9 +20,6 @@ feature {NONE} -- Initialization
l_message: STRING
l_factory: INET_ADDRESS_FACTORY
do
initialize_router
initialize_filter
initialize_json
set_service_option ("port", port)
create l_message.make_empty
l_message.append_string ("Launching filter server at ")
@@ -46,60 +32,13 @@ feature {NONE} -- Initialization
make_and_launch
end
create_filter
-- Create `filter'
do
create {WSF_CORS_FILTER} filter
end
setup_filter
-- Setup `filter'
local
l_routing_filter: WSF_ROUTING_FILTER
l_logging_filter: WSF_LOGGING_FILTER
do
create l_routing_filter.make (router)
l_routing_filter.set_execute_default_action (agent execute_default)
filter.set_next (l_routing_filter)
create l_logging_filter
l_routing_filter.set_next (l_logging_filter)
end
setup_router
-- Setup `router'
local
l_options_filter: WSF_CORS_OPTIONS_FILTER
l_authentication_filter: AUTHENTICATION_FILTER
l_user_filter: USER_HANDLER
l_methods: WSF_REQUEST_METHODS
do
create l_options_filter.make (router)
create l_authentication_filter
create l_user_filter
l_options_filter.set_next (l_authentication_filter)
l_authentication_filter.set_next (l_user_filter)
create l_methods
l_methods.enable_options
l_methods.enable_get
router.handle_with_request_methods ("/user/{userid}", create {WSF_URI_TEMPLATE_AGENT_HANDLER}.make (agent l_options_filter.execute), l_methods)
end
initialize_json
-- Initialize `json'.
do
json.add_converter (create {JSON_USER_CONVERTER}.make)
end
feature {NONE} -- Implementation
port: INTEGER = 9090
-- Port number
note
copyright: "2011-2014, Olivier Ligot, Jocelyn Fiat and others"
copyright: "2011-2015, Olivier Ligot, Jocelyn Fiat and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[
Eiffel Software

View File

@@ -0,0 +1,82 @@
note
description : "Filter example."
author : "Olivier Ligot"
date : "$Date$"
revision : "$Revision$"
class
FILTER_SERVER_EXECUTION
inherit
WSF_FILTERED_ROUTED_EXECUTION
redefine
initialize
end
SHARED_EJSON
create
make
feature {NONE} -- Initialization
initialize
do
Precursor
initialize_json
end
create_filter
-- Create `filter'
do
create {WSF_CORS_FILTER} filter
end
setup_filter
-- Setup `filter'
local
l_logging_filter: WSF_LOGGING_FILTER
do
create l_logging_filter
filter.set_next (l_logging_filter)
end
setup_router
-- Setup `router'
local
l_options_filter: WSF_CORS_OPTIONS_FILTER
l_authentication_filter: AUTHENTICATION_FILTER
l_user_filter: USER_HANDLER
l_methods: WSF_REQUEST_METHODS
do
create l_options_filter.make (router)
create l_authentication_filter
create l_user_filter
l_options_filter.set_next (l_authentication_filter)
l_authentication_filter.set_next (l_user_filter)
create l_methods
l_methods.enable_options
l_methods.enable_get
router.handle ("/user/{userid}", create {WSF_URI_TEMPLATE_AGENT_HANDLER}.make (agent l_options_filter.execute), l_methods)
end
initialize_json
-- Initialize `json'.
once
-- See SHARED_EJSON, and the once function per thread `json'.
json.add_converter (create {JSON_USER_CONVERTER}.make)
end
note
copyright: "2011-2015, Olivier Ligot, Jocelyn Fiat 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

@@ -41,7 +41,7 @@ feature -- Basic operations
local
id : STRING
do
if attached req.orig_path_info as orig_path then
if attached req.path_info as orig_path then
id := get_user_id_from_path (orig_path)
if attached retrieve_user (id) as l_user then
if l_user ~ req.execution_variable ("user") then
@@ -86,12 +86,14 @@ feature {NONE} -- Implementation
retrieve_user (id: STRING) : detachable USER
-- Retrieve the user by id if it exist, in other case, Void
do
if id.is_integer and then Db_access.users.has (id.to_integer) then
Result := db_access.users.item (id.to_integer)
if id.is_integer then
Result := db_access.user_by_id (id.to_integer)
else
Result := db_access.user_by_name (id)
end
end
note
copyright: "2011-2013, Olivier Ligot, Jocelyn Fiat and others"
copyright: "2011-2015, Olivier Ligot, Jocelyn Fiat and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
end

View File

@@ -0,0 +1,35 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-9-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-9-0 http://www.eiffel.com/developers/xml/configuration-1-9-0.xsd" name="filter" uuid="52FF4B77-0614-4D8B-9B96-C07EC852793E" library_target="filter">
<target name="common">
<file_rule>
<exclude>/EIFGENs$</exclude>
<exclude>/\.git$</exclude>
<exclude>/\.svn$</exclude>
</file_rule>
<option debug="true" warning="true" full_class_checking="true" is_attached_by_default="true" void_safety="all" syntax="provisional">
<debug name="nino" enabled="true"/>
<assertions precondition="true" postcondition="true" invariant="true" supplier_precondition="true"/>
</option>
<setting name="concurrency" value="thread"/>
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf" readonly="true"/>
<library name="net" location="$ISE_LIBRARY\library\net\net-safe.ecf" readonly="true"/>
<library name="http" location="../../../../library/network/protocol/http/http-safe.ecf" readonly="true"/>
<library name="json" location="..\..\..\..\contrib\library\text\parser\json\library\json-safe.ecf" readonly="true"/>
<library name="wsf" location="..\..\..\..\library\server\obsolete\v0\wsf\wsf-safe.ecf" readonly="true"/>
<library name="wsf_router_context" location="..\..\..\..\library\server\obsolete\v0\wsf\wsf_router_context-safe.ecf" readonly="true"/>
<library name="wsf_extension" location="..\..\..\..\library\server\obsolete\v0\wsf\wsf_extension-safe.ecf" readonly="true"/>
<library name="http_authorization" location="..\..\..\..\library\server\authentication\http_authorization\http_authorization-safe.ecf" readonly="true"/>
</target>
<target name="filter_nino" extends="common">
<root class="FILTER_SERVER" feature="make"/>
<library name="default_nino" location="..\..\..\..\library\server\obsolete\v0\wsf\default\nino-safe.ecf" readonly="true"/>
<cluster name="filter" location="src\" recursive="true"/>
</target>
<target name="filter_fcgi" extends="common">
<root class="FILTER_SERVER" feature="make"/>
<library name="default_libfcgi" location="..\..\..\..\library\server\obsolete\v0\wsf\default\libfcgi-safe.ecf"/>
<cluster name="filter" location="src\" recursive="true"/>
</target>
<target name="filter" extends="filter_nino">
</target>
</system>

View File

@@ -0,0 +1,6 @@
#include <windows.h>
STRINGTABLE
BEGIN
1 "This Program was made using EiffelStudio using Visual Studio C++"
END

View File

@@ -0,0 +1,4 @@
${NOTE_KEYWORD}
copyright: "2011-${YEAR}, Olivier Ligot, Jocelyn Fiat and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"

View File

@@ -0,0 +1,4 @@
Filter example
To test the example, you can just run in a terminal:
> curl -u foo:bar http://localhost:9090/user/1 -v

View File

@@ -0,0 +1,57 @@
note
description: "Summary description for {DATABASE_API}."
author: ""
date: "$Date$"
revision: "$Revision$"
class
DATABASE_API
create
make
feature -- Initialization
make
local
l_user: USER
do
create users.make (10)
create l_user.make (1, "foo", "bar")
users.put (l_user, l_user.id)
create l_user.make (2, "demo", "demo")
users.put (l_user, l_user.id)
end
feature -- Access
user (a_id: INTEGER; a_name: detachable READABLE_STRING_GENERAL): detachable USER
-- User with id `a_id' or name `a_name'.
require
a_id > 0 xor a_name /= Void
local
n: like {USER}.name
do
if a_id > 0 then
Result := users.item (a_id)
elseif a_name /= Void then
n := a_name.as_string_8
across
users as c
until
Result /= Void
loop
if attached c.item as u and then u.name.same_string (n) then
Result := u
end
end
end
ensure
Result /= Void implies ((a_id > 0 and then Result.id = a_id) xor (a_name /= Void and then Result.name.same_string_general (a_name)))
end
users: HASH_TABLE [USER, INTEGER]
;note
copyright: "2011-2012, Olivier Ligot, Jocelyn Fiat and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
end

View File

@@ -0,0 +1,20 @@
note
description: "Summary description for {SHARED_DATABASE_API}."
author: ""
date: "$Date$"
revision: "$Revision$"
class
SHARED_DATABASE_API
feature -- Access
db_access: DATABASE_API
once
create Result.make
end
note
copyright: "2011-2012, Olivier Ligot, Jocelyn Fiat and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
end

View File

@@ -0,0 +1,52 @@
note
description: "JSON user converter."
author: "Olivier Ligot"
date: "$Date$"
revision: "$Revision$"
class
JSON_USER_CONVERTER
inherit
JSON_CONVERTER
create
make
feature {NONE} -- Initialization
make
do
create object.make (0, "", "")
end
feature -- Access
object: USER
value: detachable JSON_OBJECT
feature -- Conversion
from_json (j: attached like value): detachable like object
-- Convert from JSON value.
do
end
to_json (o: like object): like value
-- Convert to JSON value.
do
create Result.make
Result.put (json.value (o.id), id_key)
Result.put (json.value (o.name), name_key)
end
feature {NONE} -- Implementation
id_key: STRING = "id"
name_key: STRING = "name"
note
copyright: "2011-2012, Olivier Ligot, Jocelyn Fiat and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
end

View File

@@ -0,0 +1,59 @@
note
description: "User."
author: ""
date: "$Date$"
revision: "$Revision$"
class
USER
inherit
ANY
redefine
is_equal
end
create
make
feature {NONE} -- Initialization
make (an_id: INTEGER; a_name, a_password: STRING)
do
id := an_id
name := a_name
password := a_password
ensure
id_set: id = an_id
name_set: name = a_name
password_set: password = a_password
end
feature -- Access
id: INTEGER
-- Identifier
name: STRING
-- Name
password: STRING
-- Password
feature -- Comparison
is_equal (other: like Current): BOOLEAN
-- Is `other' attached to an object considered
-- equal to current object?
do
if Current = other then
Result := True
else
Result := (id = other.id) and (name = other.name) and (password = other.password)
end
end
;note
copyright: "2011-2012, Olivier Ligot, Jocelyn Fiat and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
end

View File

@@ -0,0 +1,65 @@
note
description: "Authentication filter."
author: "Olivier Ligot"
date: "$Date$"
revision: "$Revision$"
class
AUTHENTICATION_FILTER
inherit
WSF_FILTER
WSF_URI_TEMPLATE_HANDLER
SHARED_DATABASE_API
SHARED_EJSON
feature -- Basic operations
execute (req: WSF_REQUEST; res: WSF_RESPONSE)
-- Execute the filter
local
l_auth: detachable HTTP_AUTHORIZATION
do
if attached req.http_authorization as l_http_authorization then
create l_auth.make (l_http_authorization)
end
if
l_auth /= Void and then
l_auth.is_basic and then
attached l_auth.login as l_auth_login and then
attached Db_access.user (0, l_auth_login) as l_user and then
l_auth_login.same_string (l_user.name) and then
attached l_auth.password as l_auth_password and then
l_auth_password.same_string (l_user.password)
then
req.set_execution_variable ("user", l_user)
execute_next (req, res)
else
handle_unauthorized ("Unauthorized", req, res)
end
end
feature {NONE} -- Implementation
handle_unauthorized (a_description: STRING; req: WSF_REQUEST; res: WSF_RESPONSE)
-- Handle forbidden.
local
h: HTTP_HEADER
do
create h.make
h.put_content_type_text_plain
h.put_content_length (a_description.count)
h.put_current_date
h.put_header_key_value ({HTTP_HEADER_NAMES}.header_www_authenticate, "Basic realm=%"User%"")
res.set_status_code ({HTTP_STATUS_CODE}.unauthorized)
res.put_header_text (h.string)
res.put_string (a_description)
end
note
copyright: "2011-2014, Olivier Ligot, Jocelyn Fiat and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
end

View File

@@ -0,0 +1,34 @@
note
description: "Summary description for {FILTER_HANDLER_CONTEXT}."
author: ""
date: "$Date$"
revision: "$Revision$"
class
FILTER_HANDLER_CONTEXT
inherit
WSF_HANDLER_CONTEXT
create
make
feature -- Access
user: detachable USER
-- Authenticated user
feature -- Element change
set_user (a_user: USER)
-- Set `user' to `a_user'
do
user := a_user
ensure
user_set: user = a_user
end
note
copyright: "2011-2012, Olivier Ligot, Jocelyn Fiat and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
end

View File

@@ -0,0 +1,111 @@
note
description : "Filter example."
author : "Olivier Ligot"
date : "$Date$"
revision : "$Revision$"
class
FILTER_SERVER
inherit
ANY
WSF_DEFAULT_SERVICE
WSF_ROUTED_SERVICE
undefine
execute
end
WSF_FILTERED_SERVICE
SHARED_EJSON
create
make
feature {NONE} -- Initialization
make
local
l_message: STRING
l_factory: INET_ADDRESS_FACTORY
do
initialize_router
initialize_filter
initialize_json
set_service_option ("port", port)
create l_message.make_empty
l_message.append_string ("Launching filter server at ")
create l_factory
l_message.append_string (l_factory.create_localhost.host_name)
l_message.append_string (" port ")
l_message.append_integer (port)
io.put_string (l_message)
io.put_new_line
make_and_launch
end
create_filter
-- Create `filter'
do
create {WSF_CORS_FILTER} filter
end
setup_filter
-- Setup `filter'
local
l_routing_filter: WSF_ROUTING_FILTER
l_logging_filter: WSF_LOGGING_FILTER
do
create l_routing_filter.make (router)
l_routing_filter.set_execute_default_action (agent execute_default)
filter.set_next (l_routing_filter)
create l_logging_filter
l_routing_filter.set_next (l_logging_filter)
end
setup_router
-- Setup `router'
local
l_options_filter: WSF_CORS_OPTIONS_FILTER
l_authentication_filter: AUTHENTICATION_FILTER
l_user_filter: USER_HANDLER
l_methods: WSF_REQUEST_METHODS
do
create l_options_filter.make (router)
create l_authentication_filter
create l_user_filter
l_options_filter.set_next (l_authentication_filter)
l_authentication_filter.set_next (l_user_filter)
create l_methods
l_methods.enable_options
l_methods.enable_get
router.handle_with_request_methods ("/user/{userid}", create {WSF_URI_TEMPLATE_AGENT_HANDLER}.make (agent l_options_filter.execute), l_methods)
end
initialize_json
-- Initialize `json'.
do
json.add_converter (create {JSON_USER_CONVERTER}.make)
end
feature {NONE} -- Implementation
port: INTEGER = 9090
-- Port number
note
copyright: "2011-2014, Olivier Ligot, Jocelyn Fiat 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,97 @@
note
description: "User handler."
author: "Olivier Ligot"
date: "$Date$"
revision: "$Revision$"
class
USER_HANDLER
inherit
WSF_FILTER
WSF_URI_TEMPLATE_HANDLER
WSF_RESOURCE_HANDLER_HELPER
redefine
do_get
end
SHARED_DATABASE_API
SHARED_EJSON
feature -- Basic operations
execute (req: WSF_REQUEST; res: WSF_RESPONSE)
-- Execute request handler
do
execute_methods (req, res)
execute_next (req, res)
end
do_get (req: WSF_REQUEST; res: WSF_RESPONSE)
-- Using GET to retrieve resource information.
-- If the GET request is SUCCESS, we response with
-- 200 OK, and a representation of the user
-- If the GET request is not SUCCESS, we response with
-- 404 Resource not found
require else
authenticated_user_attached: attached {USER} req.execution_variable ("user")
local
id : STRING
do
if attached req.orig_path_info as orig_path then
id := get_user_id_from_path (orig_path)
if attached retrieve_user (id) as l_user then
if l_user ~ req.execution_variable ("user") then
compute_response_get (req, res, l_user)
elseif attached {USER} req.execution_variable ("user") as l_auth_user then
-- Trying to access another user that the authenticated one,
-- which is forbidden in this example...
handle_forbidden ("You try to access the user " + id.out + " while authenticating with the user " + l_auth_user.id.out, req, res)
end
else
handle_resource_not_found_response ("The following resource " + orig_path + " is not found ", req, res)
end
end
end
feature {NONE} -- Implementation
compute_response_get (req: WSF_REQUEST; res: WSF_RESPONSE; l_user : USER)
local
h: HTTP_HEADER
l_msg : STRING
do
create h.make
h.put_content_type_application_json
if attached {JSON_VALUE} json.value (l_user) as jv then
l_msg := jv.representation
h.put_content_length (l_msg.count)
if attached req.request_time as time then
h.put_utc_date (time)
end
res.set_status_code ({HTTP_STATUS_CODE}.ok)
res.put_header_text (h.string)
res.put_string (l_msg)
end
end
get_user_id_from_path (a_path: READABLE_STRING_32): STRING
do
Result := a_path.split ('/').at (3)
end
retrieve_user (id: STRING) : detachable USER
-- Retrieve the user by id if it exist, in other case, Void
do
if id.is_integer and then Db_access.users.has (id.to_integer) then
Result := db_access.users.item (id.to_integer)
end
end
note
copyright: "2011-2013, Olivier Ligot, Jocelyn Fiat and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
end

View File

@@ -0,0 +1,3 @@
The current example has a main target for the server: "restbucks"
But we also provide "policy_driven_restbucks" target which is using the
policy-driven framework than help coder fulfill HTTP expectations.

View File

@@ -0,0 +1,11 @@
Make sure to have the Clib generated in the related cURL library
- if you use EiffelStudio >= 7.0
check %ISE_LIBRARY%\library\cURL\spec\%ISE_C_COMPILER%\$ISE_PLATFORM
or $ISE_LIBRARY/library/cURL/spec/$ISE_PLATFORM
- otherwise if you use earlier version
check under ext/ise_library/curl/spec/...
And on Windows, be sure to get the libcurl.dll from %ISE_LIBRARY%\studio\spec\%ISE_PLATFORM%\bin\libcurl.dll

View File

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-12-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-12-0 http://www.eiffel.com/developers/xml/configuration-1-12-0.xsd" name="client" uuid="D0059CEB-5F5C-4D21-8C71-842BD0F88468" library_target="client">
<target name="client">
<root class="RESTBUCK_CLIENT" feature="make"/>
<file_rule>
<exclude>/.git$</exclude>
<exclude>/EIFGENs$</exclude>
<exclude>/.svn$</exclude>
</file_rule>
<option warning="true" full_class_checking="true" is_attached_by_default="true" void_safety="all" syntax="standard">
</option>
<setting name="concurrency" value="thread"/>
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
<library name="http_client" location="..\..\..\..\..\library\network\http_client\http_client-safe.ecf" readonly="false"/>
<library name="json" location="..\..\..\..\..\contrib\library\text\parser\json\library\json-safe.ecf" readonly="false"/>
<library name="thread" location="$ISE_LIBRARY\library\thread\thread-safe.ecf"/>
<cluster name="src" location=".\src\" recursive="true"/>
</target>
</system>

View File

@@ -0,0 +1,19 @@
<?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="client" uuid="D0059CEB-5F5C-4D21-8C71-842BD0F88468">
<target name="client">
<root class="RESTBUCK_CLIENT" feature="make"/>
<file_rule>
<exclude>/.git$</exclude>
<exclude>/EIFGENs$</exclude>
<exclude>/.svn$</exclude>
</file_rule>
<option warning="true" full_class_checking="true" void_safety="none" syntax="standard">
</option>
<setting name="concurrency" value="thread"/>
<library name="http_client" location="../../../../../library/network/http_client/http_client.ecf"/>
<library name="json" location="..\..\..\..\..\contrib\library\text\parser\json\library\json.ecf" readonly="false"/>
<library name="base" location="$ISE_LIBRARY/library/base/base.ecf"/>
<library name="thread" location="$ISE_LIBRARY/library/thread/thread.ecf"/>
<cluster name="src" location="./src" recursive="true"/>
</target>
</system>

View File

@@ -0,0 +1,6 @@
#include <windows.h>
STRINGTABLE
BEGIN
1 "This Program was made using EiffelStudio using Visual Studio C++"
END

View File

@@ -0,0 +1,154 @@
note
description : "Objects that ..."
author : "$Author$"
date : "$Date$"
revision : "$Revision$"
class
RESTBUCK_CLIENT
create
make
feature {NONE} -- Initialization
make
-- Initialize `Current'.
local
h: LIBCURL_HTTP_CLIENT
sess: HTTP_CLIENT_SESSION
resp : detachable HTTP_CLIENT_RESPONSE
l_location : detachable READABLE_STRING_8
body : STRING
do
create h.make
sess := h.new_session ("http://127.0.0.1:9090")
-- Uncomment the following 2 lines, if you use fiddler2 web debugging tool
-- sess.set_is_debug (True)
-- sess.set_proxy ("127.0.0.1", 8888)
-- Create Order
print ("%N Create Order %N")
resp := create_order (sess)
-- Read the Order
print ("%N Read Order %N")
l_location := resp.header ("Location")
resp := read_order (sess, l_location)
-- Update the Order
if resp /= Void and then attached resp.body as l_body then
body := l_body.as_string_8
body.replace_substring_all ("takeAway", "in Shop")
print ("%N Update Order %N")
resp := update_order (sess, l_location, body)
end
end
update_order ( sess: HTTP_CLIENT_SESSION; uri : detachable READABLE_STRING_8; a_body : STRING): detachable HTTP_CLIENT_RESPONSE
local
context : HTTP_CLIENT_REQUEST_CONTEXT
do
if attached uri as l_uri then
sess.set_base_url (l_uri)
create context.make
context.headers.put ("application/json", "Content-Type")
Result := sess.put ("", context, a_body )
-- Show headers
across
Result.headers as l_headers
loop
print (l_headers.item.name)
print (":")
print (l_headers.item.value)
io.put_new_line
end
-- Show body
print (Result.body)
io.put_new_line
end
end
read_order ( sess: HTTP_CLIENT_SESSION; uri : detachable READABLE_STRING_8): detachable HTTP_CLIENT_RESPONSE
do
if attached uri as l_uri then
sess.set_base_url (l_uri)
Result := sess.get ("", Void)
-- Show headers
across
Result.headers as l_headers
loop
print (l_headers.item.name)
print (":")
print (l_headers.item.value)
io.put_new_line
end
-- Show body
print (Result.body)
io.put_new_line
end
end
create_order (sess: HTTP_CLIENT_SESSION) : HTTP_CLIENT_RESPONSE
local
s: READABLE_STRING_8
j: JSON_PARSER
id: detachable STRING
context : HTTP_CLIENT_REQUEST_CONTEXT
do
s := "[
{
"location":"takeAway",
"items":[
{
"name":"Late",
"option":"skim",
"size":"Small",
"quantity":1
}
]
}
]"
create context.make
context.headers.put ("application/json", "Content-Type")
Result := sess.post ("/order", context, s)
-- Show the Headers
across
Result.headers as l_headers
loop
print (l_headers.item.name)
print (":")
print (l_headers.item.value)
io.put_new_line
end
-- Show the Response body
if attached Result.body as m then
create j.make_with_string (m)
j.parse_content
if j.is_valid and then attached j.parsed_json_object as j_o then
if attached {JSON_STRING} j_o.item ("id") as l_id then
id := l_id.item
end
print (m)
io.put_new_line
end
end
end
feature {NONE} -- Implementation
invariant
-- invariant_clause: True
end

View File

@@ -0,0 +1,4 @@
${NOTE_KEYWORD}
copyright: "2011-${YEAR}, Javier Velilla and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"

View File

@@ -0,0 +1,297 @@
Restbuck Eiffel Implementation based on the book of REST in Practice
====================================================================
This is an implementation of CRUD pattern for manipulate resources, this is the first step to use
the HTTP protocol as an application protocol instead of a transport protocol.
Restbuck Protocol
-----------------
<table>
<TR><TH>Verb</TH> <TH>URI or template</TH> <TH>Use</TH></TR>
<TR><TD>POST</TD> <TD>/order</TD> <TD>Create a new order, and upon success, receive a Locationheader specifying the new order's URI.</TD></TR>
<TR><TD>GET</TD> <TD>/order/{orderId}</TD> <TD>Request the current state of the order specified by the URI.</TD></TR>
<TR><TD>PUT</TD> <TD>/order/{orderId}</TD> <TD>Update an order at the given URI with new information, providing the full representation.</TD></TR>
<TR><TD>DELETE</TD> <TD>/order/{orderId}</TD> <TD>Logically remove the order identified by the given URI.</TD></TR>
</table>
Resource Represenation
----------------------
The previous tables shows a contrat, the URI or URI template, allows us to indentify resources, now we will chose a
representacion, for this particular case we will use JSON.
Note: <br/>
1. *A resource can have multiple URIs*.<br/>
2. *A resource can have multiple Representations*.<br/>
RESTBUCKS_SERVER
----------------
This class implement the main entry of our REST CRUD service, we are using a default connector (Nino Connector,
using a WebServer written in Eiffel).
We are inheriting from URI_TEMPLATE_ROUTED_SERVICE, this allows us to map our service contrat, as is shown in the previous
table, the mapping is defined in the feature setup_router, this also show that the class ORDER_HANDLER will be encharge
of to handle different type of request to the ORDER resource.
class
RESTBUCKS_SERVER
inherit
ANY
URI_TEMPLATE_ROUTED_SERVICE
DEFAULT_SERVICE
-- Here we are using a default connector using the default Nino Connector,
-- but it's possible to use other connector (CGI or FCGI).
create
make
feature {NONE} -- Initialization
make
-- Initialize the router (this will have the request handler and
-- their context).
do
initialize_router
make_and_launch
end
create_router
do
create router.make (2)
end
setup_router
local
order_handler: ORDER_HANDLER [REQUEST_URI_TEMPLATE_HANDLER_CONTEXT]
do
create order_handler
router.map_with_request_methods ("/order", order_handler, <<"POST">>)
router.map_with_request_methods ("/order/{orderid}", order_handler, <<"GET", "DELETE", "PUT">>)
end
feature -- Execution
execute_default (req: WSF_REQUEST; res: WSF_RESPONSE)
-- I'm using this method to handle the method not allowed response
-- in the case that the given uri does not have a corresponding http method
-- to handle it.
local
h : HTTP_HEADER
l_description : STRING
l_api_doc : STRING
do
if req.content_length_value > 0 then
req.input.read_string (req.content_length_value.as_integer_32)
end
create h.make
h.put_status ({HTTP_STATUS_CODE}.method_not_allowed)
h.put_content_type_text_plain
l_api_doc := "%NPlease check the API%NURI:/order METHOD: POST%NURI:/order/{orderid} METHOD: GET, PUT, DELETE%N"
l_description := req.request_method + req.request_uri + " is not allowed" + "%N" + l_api_doc
h.put_content_length (l_description.count)
h.put_current_date
res.set_status_code ({HTTP_STATUS_CODE}.method_not_allowed)
res.write_header_text (h.string)
res.write_string (l_description)
end
end
How to Create an order with POST
--------------------------------
Here is the convention that we are using:
POST is used for creation and the server determines the URI of the created resource.
If the request POST is SUCCESS, the server will create the order and will response with
201 CREATED, the Location header will contains the newly created order's URI,
if the request POST is not SUCCESS, the server will response with
400 BAD REQUEST, the client send a bad request or
500 INTERNAL_SERVER_ERROR, when the server can deliver the request.
POST /order HTTP/1.1
Host: 127.0.0.1:8080
Connection: keep-alive
Content-Length: 196
Origin: chrome-extension://fhjcajmcbmldlhcimfajhfbgofnpcjmb
Content-Type: application/json
Accept: */*
Accept-Encoding: gzip,deflate,sdch
Accept-Language: es-419,es;q=0.8,en;q=0.6
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3
{
"location":"takeAway",
"items":[
{
"name":"Late",
"option":"skim",
"size":"Small",
"quantity":1
}
]
}
Response success
HTTP/1.1 201 Created
Status 201 Created
Content-Type application/json
Content-Length 123
Location http://localhost:8080/order/1
Date FRI,09 DEC 2011 20:34:20.00 GMT
{
"location" : "takeAway",
"status" : "submitted",
"items" : [ {
"name" : "late",
"size" : "small",
"quantity" : 1,
"option" : "skim"
} ]
}
note:
curl -vv http://localhost:9090/order -H "Content-Type: application/json" -d "{\"location\":\"takeAway\",\"items\":[{\"name\":\"Late\",\"option\":\"skim\",\"size\":\"Small\",\"quantity\":1}]}" -X POST
How to Read an order with GET
-----------------------------
Using GET to retrieve resource information.
If the GET request is SUCCESS, we response with 200 OK, and a representation of the order
If the GET request is not SUCCESS, we response with 404 Resource not found
If is a Conditional GET and the resource does not change we send a 304, Resource not modifed
GET /order/1 HTTP/1.1
Host: 127.0.0.1:8080
Connection: keep-alive
Accept: */*
Accept-Encoding: gzip,deflate,sdch
Accept-Language: es-419,es;q=0.8,en;q=0.6
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3
If-None-Match: 6542EF270D91D3EAF39CFB382E4CEBA7
Response
HTTP/1.1 200 OK
Status 200 OK
Content-Type application/json
Content-Length 123
Date FRI,09 DEC 2011 20:53:46.00 GMT
etag 2ED3A40954A95D766FC155682DC8BB52
{
"location" : "takeAway",
"status" : "submitted",
"items" : [ {
"name" : "late",
"size" : "small",
"quantity" : 1,
"option" : "skim"
} ]
}
note:
curl -vv http://localhost:9090/order/1
How to Update an order with PUT
-------------------------------
A successful PUT request will not create a new resource, instead it will change the state of the resource identified by the current uri.
If success we response with 200 and the updated order.
404 if the order is not found
400 in case of a bad request
500 internal server error
If the request is a Conditional PUT, and it does not mat we response 415, precondition failed.
Suposse that we had created an Order with the values shown in the _How to create an order with POST_
But we change our decision and we want to stay in the shop.
PUT /order/1 HTTP/1.1
Content-Length: 122
Content-Type: application/json; charset=UTF-8
Host: localhost:8080
Connection: Keep-Alive
Expect: 100-Continue
{
"location" : "in shop",
"status" : "submitted",
"items" : [ {
"name" : "late",
"size" : "small",
"quantity" : 1,
"option" : "skim"
} ]
}
Response success
HTTP/1.1 200 OK
Status 200 OK
Content-Type application/json
Date FRI,09 DEC 2011 21:06:26.00 GMT
etag 8767F900674B843E1F3F70BCF3E62403
Content-Length 122
{
"location" : "in shop",
"status" : "submitted",
"items" : [ {
"name" : "late",
"size" : "small",
"quantity" : 1,
"option" : "skim"
} ]
}
How to Delete an order with DELETE
----------------------------------
Here we use DELETE to cancel an order, if that order is in state where it can still be canceled.
204 if is ok
404 Resource not found
405 if consumer and service's view of the resouce state is inconsisent
500 if we have an internal server error
DELETE /order/1 HTTP/1.1
Host: localhost:8080
Connection: Keep-Alive
Response success
HTTP/1.1 204 No Content
Status 204 No Content
Content-Type application/json
Date FRI,09 DEC 2011 21:10:51.00 GMT
If we want to check that the resource does not exist anymore we can try to retrieve a GET /order/1 and we will receive a
404 No Found
GET /order/1 HTTP/1.1
Host: localhost:8080
Connection: Keep-Alive
Response
HTTP/1.1 404 Not Found
Status 404 Not Found
Content-Type application/json
Content-Length 44
Date FRI,09 DEC 2011 21:14:17.79 GMT
The following resource/order/1 is not found
References
----------
1. [How to get a cup of coffe](http://www.infoq.com/articles/webber-rest-workflow)
2. [Rest in Practice] (http://restinpractice.com/default.aspx)

View File

@@ -0,0 +1,55 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-12-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-12-0 http://www.eiffel.com/developers/xml/configuration-1-12-0.xsd" name="restbucks" uuid="2773FEAA-448F-410E-BEDE-9298C4749066" library_target="restbucks">
<target name="restbucks_common">
<file_rule>
<exclude>/EIFGENs$</exclude>
<exclude>/\.git$</exclude>
<exclude>/\.svn$</exclude>
</file_rule>
<option full_class_checking="false" void_safety="all">
</option>
<setting name="concurrency" value="thread"/>
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
<library name="connector_nino" location="..\..\..\..\library\server\obsolete\v0\ewsgi\connectors\nino\nino-safe.ecf" readonly="false">
<option debug="true">
<debug name="nino" enabled="true"/>
</option>
</library>
<library name="conneg" location="..\..\..\..\library\network\protocol\content_negotiation\conneg-safe.ecf"/>
<library name="crypto" location="$ISE_LIBRARY\unstable\library\text\encryption\crypto\crypto-safe.ecf" readonly="false"/>
<library name="default_nino" location="..\..\..\..\library\server\obsolete\v0\wsf\default\nino-safe.ecf" readonly="false"/>
<library name="encoder" location="..\..\..\..\library\text\encoder\encoder-safe.ecf" readonly="false"/>
<library name="http" location="..\..\..\..\library\network\protocol\http\http-safe.ecf" readonly="false"/>
<library name="json" location="..\..\..\..\contrib\library\text\parser\json\library\json-safe.ecf" readonly="false"/>
<library name="time" location="$ISE_LIBRARY\library\time\time-safe.ecf"/>
<library name="uri" location="$ISE_LIBRARY\library\text\uri\uri-safe.ecf"/>
<library name="uri_template" location="..\..\..\..\library\text\parser\uri_template\uri_template-safe.ecf" readonly="false"/>
<library name="wsf" location="..\..\..\..\library\server\obsolete\v0\wsf\wsf-safe.ecf" readonly="false"/>
<library name="wsf_extension" location="..\..\..\..\library\server\obsolete\v0\wsf\wsf_extension-safe.ecf" readonly="false"/>
</target>
<target name="restbucks" extends="restbucks_common">
<root class="RESTBUCKS_SERVER" feature="make"/>
<option debug="true" warning="true" full_class_checking="true" is_attached_by_default="true" void_safety="transitional" syntax="provisional">
<debug name="nino" enabled="true"/>
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
</option>
<cluster name="src" location="src\" recursive="true">
<file_rule>
<exclude>/policy_driven_resource$</exclude>
</file_rule>
</cluster>
</target>
<target name="policy_driven_restbucks" extends="restbucks_common">
<root class="RESTBUCKS_SERVER" feature="make"/>
<option debug="true" warning="true" full_class_checking="true" is_attached_by_default="true" void_safety="transitional" syntax="provisional">
<debug name="nino" enabled="true"/>
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
</option>
<library name="wsf_policy_driven" location="..\..\..\..\library\server\obsolete\v0\wsf\wsf_policy_driven-safe.ecf" readonly="false"/>
<cluster name="src" location="src\" recursive="true">
<file_rule>
<exclude>/resource$</exclude>
</file_rule>
</cluster>
</target>
</system>

View File

@@ -0,0 +1,6 @@
#include <windows.h>
STRINGTABLE
BEGIN
1 "This Program was made using EiffelStudio using Visual Studio C++"
END

View File

@@ -0,0 +1,26 @@
note
description: "Summary description for {DATABASE_API}."
author: ""
date: "$Date$"
revision: "$Revision$"
class
DATABASE_API
create
make
feature -- Initialization
make
do
create orders.make (10)
end
feature -- Access
orders: HASH_TABLE [ORDER, STRING]
;note
copyright: "2011-2012, Javier Velilla and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
end

View File

@@ -0,0 +1,19 @@
note
description: "Summary description for {SHARED_DATABASE_API}."
author: ""
date: "$Date$"
revision: "$Revision$"
class
SHARED_DATABASE_API
feature -- Access
db_access: DATABASE_API
once
create Result.make
end
note
copyright: "2011-2012, Javier Velilla and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
end

View File

@@ -0,0 +1,90 @@
note
description: "Summary description for {ITEM}."
author: ""
date: "$Date$"
revision: "$Revision$"
class
ITEM
inherit
ITEM_CONSTANTS
create
make
feature -- Initialization
make ( a_name : STRING_32 ; a_size:STRING_32; a_option: STRING_32; a_quantity:INTEGER_8)
do
set_name (a_name)
set_size (a_size)
set_option (a_option)
set_quantity (a_quantity)
end
feature -- Access
name : STRING
-- product name type of Coffee(Late, Cappuccino, Expresso)
option : STRING
-- customization option Milk (skim, semi, whole)
size : STRING
-- small, mediumm large
quantity :INTEGER
feature -- Element Change
set_name (a_name: STRING)
require
valid_name: is_valid_coffee_type (a_name)
do
name := a_name
ensure
name_assigned : name.same_string(a_name)
end
set_size (a_size: STRING)
require
valid_size : is_valid_size_option (a_size)
do
size := a_size
ensure
size_assigned : size.same_string(a_size)
end
set_option (an_option: STRING)
require
valid_option : is_valid_milk_type (an_option)
do
option := an_option
ensure
option_assigned : option.same_string (an_option)
end
set_quantity (a_quantity: INTEGER)
require
valid_quantity : a_quantity > 0
do
quantity := a_quantity
ensure
quantity_assigned : quantity = a_quantity
end
feature -- Report
hash_code: INTEGER
--Hash code value
do
Result := option.hash_code + name.hash_code + size.hash_code + quantity.hash_code
end
invariant
valid_size : is_valid_size_option (size)
valid_coffe : is_valid_coffee_type (name)
valid_customization : is_valid_milk_type (option)
valid_quantity : quantity > 0
note
copyright: "2011-2012, Javier Velilla and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
end

View File

@@ -0,0 +1,54 @@
note
description: "Summary description for {ITEM_CONSTANTS}."
author: ""
date: "$Date$"
revision: "$Revision$"
class
ITEM_CONSTANTS
feature -- Access
is_valid_coffee_type (a_type: STRING) : BOOLEAN
--is `a_type' a valid coffee type
do
a_type.to_lower
coffe_types.compare_objects
Result := coffe_types.has (a_type)
end
Coffe_types : ARRAY[STRING]
-- List of valid Coffee types
once
Result := <<"late","cappuccino", "expresso">>
end
is_valid_milk_type (a_type: STRING) : BOOLEAN
--is `a_type' a valid milk type
do
a_type.to_lower
milk_types.compare_objects
Result := milk_types.has (a_type)
end
Milk_types : ARRAY[STRING]
-- List of valid Milk types
once
Result := <<"skim","semi", "whole">>
end
is_valid_size_option (an_option: STRING) : BOOLEAN
--is `an_option' a valid size option
do
an_option.to_lower
size_options.compare_objects
Result := size_options.has (an_option)
end
Size_options : ARRAY[STRING]
-- List of valid Size_options
once
Result := <<"small","mediumn", "large">>
end
note
copyright: "2011-2012, Javier Velilla and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
end

View File

@@ -0,0 +1,176 @@
note
description: "Summary description for {JSON_ORDER_CONVERTER}."
author: ""
date: "$Date$"
revision: "$Revision$"
class
JSON_ORDER_CONVERTER
inherit
JSON_CONVERTER
create
make
feature -- Initialization
make
do
create object.make ("","","")
end
feature -- Access
object : ORDER
value : detachable JSON_OBJECT
feature -- Conversion
from_json (j: attached like value): detachable like object
-- Convert from JSON value. Returns Void if unable to convert
local
s_id, s_location, s_status: detachable STRING_32
q: INTEGER_8
o: ORDER
i : ITEM
l_array : detachable ARRAYED_LIST [JSON_VALUE]
is_valid_from_json : BOOLEAN
do
is_valid_from_json := True
if attached {STRING_32} json.object (j.item (id_key), Void) as l_id then
s_id := s_id
end
if attached {STRING_32} json.object (j.item (location_key), Void) as l_location then
s_location := l_location
end
if attached {STRING_32} json.object (j.item (status_key), Void) as l_status then
s_status := l_status
end
create o.make ("", s_location, s_status)
if attached {JSON_ARRAY} j.item (items_key) as l_val then
l_array := l_val.array_representation
from
l_array.start
until
l_array.after
loop
if attached {JSON_OBJECT} l_array.item_for_iteration as jv then
if attached {INTEGER_8} json.object (jv.item (quantity_key), Void) as l_integer then
q := l_integer
else
q := 0
end
if
attached {STRING_32} json.object (jv.item (name_key), Void) as s_name and then
attached {STRING_32} json.object (jv.item (size_key), Void) as s_key and then
attached {STRING_32} json.object (jv.item (option_key), Void) as s_option
then
if is_valid_item_customization (s_name, s_key, s_option,q) then
create i.make (s_name, s_key, s_option, q)
o.add_item (i)
else
is_valid_from_json := False
end
else
is_valid_from_json := False
end
end
l_array.forth
end
end
if not is_valid_from_json or o.items.is_empty then
Result := Void
else
Result := o
end
end
to_json (o: like object): like value
-- Convert to JSON value
local
ja : JSON_ARRAY
i : ITEM
jv: JSON_OBJECT
do
create Result.make
-- Result.put (json.value (o.id), id_key)
Result.put (json.value (o.location), location_key)
Result.put (json.value (o.status), status_key)
from
create ja.make_empty
o.items.start
until
o.items.after
loop
i := o.items.item_for_iteration
create jv.make
jv.put (json.value (i.name), name_key)
jv.put (json.value (i.size),size_key)
jv.put (json.value (i.quantity), quantity_key)
jv.put (json.value (i.option), option_key)
ja.add (jv)
o.items.forth
end
Result.put (ja, items_key)
end
feature {NONE} -- Implementation
id_key: JSON_STRING
once
create Result.make_from_string ("id")
end
location_key: JSON_STRING
once
create Result.make_from_string ("location")
end
status_key: JSON_STRING
once
create Result.make_from_string ("status")
end
items_key : JSON_STRING
once
create Result.make_from_string ("items")
end
name_key : JSON_STRING
once
create Result.make_from_string ("name")
end
size_key : JSON_STRING
once
create Result.make_from_string ("size")
end
quantity_key : JSON_STRING
once
create Result.make_from_string ("quantity")
end
option_key : JSON_STRING
once
create Result.make_from_string ("option")
end
feature -- Validation
is_valid_item_customization ( name : STRING_32; size: STRING_32; option : STRING_32; quantity : INTEGER_8 ) : BOOLEAN
local
ic : ITEM_CONSTANTS
do
create ic
Result := ic.is_valid_coffee_type (name) and ic.is_valid_milk_type (option) and ic.is_valid_size_option (size) and quantity > 0
end
note
copyright: "2011-2015, Javier Velilla and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
end

View File

@@ -0,0 +1,114 @@
note
description: "Summary description for {ORDER}."
author: ""
date: "$Date$"
revision: "$Revision$"
class
ORDER
create
make
feature -- Initialization
make ( an_id : detachable STRING_32; a_location: detachable STRING_32; a_status: detachable STRING_32)
do
create {ARRAYED_LIST [ITEM]} items.make (10)
if an_id /= Void then
set_id (an_id)
else
set_id ("")
end
if a_location /= Void then
set_location (a_location)
else
set_location ("")
end
if a_status /= Void then
set_status (a_status)
else
set_status ("")
end
revision := 0
end
feature -- Access
id : STRING_32
location : STRING_32
items: LIST[ITEM]
status : STRING_32
revision : INTEGER
feature -- element change
set_id (an_id : STRING_32)
do
id := an_id
ensure
id_assigned : id.same_string (an_id)
end
set_location (a_location : STRING_32)
do
location := a_location
ensure
location_assigned : location.same_string (a_location)
end
set_status (a_status : STRING_32)
do
status := a_status
ensure
status_asigned : status.same_string (a_status)
end
add_item (a_item : ITEM)
require
valid_item: a_item /= Void
do
items.force (a_item)
ensure
has_item : items.has (a_item)
end
add_revision
do
revision := revision + 1
ensure
revision_incremented : old revision + 1 = revision
end
feature -- Etag
etag : STRING_32
-- Etag generation for Order objects
do
Result := hash_code.out + revision.out
end
feature -- Output
feature -- Report
hash_code: INTEGER_32
-- Hash code value
do
from
items.start
Result := items.item.hash_code
until
items.off
loop
Result:= ((Result \\ 8388593) |<< 8) + items.item.hash_code
items.forth
end
if items.count > 1 then
Result := Result \\ items.count
end
end
note
copyright: "2011-2012, Javier Velilla and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
end

View File

@@ -0,0 +1,56 @@
note
description: "Summary description for {ORDER_TRANSITIONS}."
author: ""
date: "$Date$"
revision: "$Revision$"
class
ORDER_VALIDATION
feature -- Access
is_valid_status_state (a_status: STRING) : BOOLEAN
--is `a_status' a valid coffee order state
do
a_status.to_lower
Order_states.compare_objects
Result := Order_states.has (a_status)
end
Order_states : ARRAY[STRING]
-- List of valid status states
once
Result := <<"submitted","pay","payed", "cancel","canceled","prepare","prepared","deliver","completed">>
end
is_valid_transition (order:ORDER a_status : STRING) :BOOLEAN
-- Given the current order state, determine if the transition is valid
do
a_status.to_lower
if order.status.same_string ("submitted") then
Result := a_status.same_string ("pay") or a_status.same_string ("cancel") or order.status.same_string (a_status)
elseif order.status.same_string ("pay") then
Result := a_status.same_string ("payed") or order.status.same_string (a_status)
elseif order.status.same_string ("cancel") then
Result := a_status.same_string ("canceled") or order.status.same_string (a_status)
elseif order.status.same_string ("payed") then
Result := a_status.same_string ("prepared") or order.status.same_string (a_status)
elseif order.status.same_string ("prepared") then
Result := a_status.same_string ("deliver") or order.status.same_string (a_status)
elseif order.status.same_string ("deliver") then
Result := a_status.same_string ("completed") or order.status.same_string (a_status)
end
end
is_state_valid_to_update ( a_status : STRING) : BOOLEAN
-- Given the current state `a_status' of an order, is possible to update the order?
do
if a_status.same_string ("submitted") or else a_status.same_string ("pay") or else a_status.same_string ("payed") then
Result := true
end
end
note
copyright: "2011-2012, Javier Velilla and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
end

Some files were not shown because too many files have changed in this diff Show More