Updated github web page, and replaced wiki by workbook.
This commit is contained in:
2
Gemfile
2
Gemfile
@@ -1,2 +1,2 @@
|
||||
source 'https://rubygems.org'
|
||||
gem 'github-pages'
|
||||
gem 'github-pages', group: :jekyll_plugins
|
||||
|
||||
@@ -7,9 +7,9 @@ permalink: pretty
|
||||
|
||||
# Server
|
||||
destination: ./_gh_pages
|
||||
exclude: [".gitignore", "Gruntfile.js", "package.json", "node_modules", "README.md", "server_architecture.png", "wiki.py"]
|
||||
exclude: [".gitignore", "Gruntfile.js", "package.json", "node_modules", "/README.md", "*.py", "*.png"]
|
||||
port: 9000
|
||||
|
||||
# Custom vars
|
||||
repo: https://github.com/EiffelWebFramework/EWF
|
||||
download: https://github.com/EiffelWebFramework/EWF/archive/v0.4.0.zip
|
||||
download: https://github.com/EiffelWebFramework/EWF/archive/v1.0.3.zip
|
||||
|
||||
@@ -28,10 +28,7 @@
|
||||
<a href="{{ page.base_url }}community">Community</a>
|
||||
</li>
|
||||
<li{% if page.title == "Documentation" %} class="active"{% endif %}>
|
||||
<a href="{{ page.base_url }}wiki/Documentation">Overview</a>
|
||||
</li>
|
||||
<li{% if page.title == "Wiki" %} class="active"{% endif %}>
|
||||
<a href="{{ page.base_url }}wiki">Wiki</a>
|
||||
<a href="{{ page.base_url }}workbook">Documentation</a>
|
||||
</li>
|
||||
</ul>
|
||||
<h3 class="muted">Eiffel Web Framework</h3>
|
||||
@@ -40,7 +37,7 @@
|
||||
{{ content }}
|
||||
<hr>
|
||||
<div class="footer">
|
||||
<p>© EiffelWebFramework 2014</p>
|
||||
<p>© EiffelWebFramework 2012-2016</p>
|
||||
</div>
|
||||
</div> <!-- /container -->
|
||||
<script src="{{ page.base_url }}js/vendor/jquery.min.js"></script>
|
||||
|
||||
@@ -39,7 +39,7 @@ base_url: "../"
|
||||
<li>located in <i>$ISE_EIFFEL/contrib/examples/web/ewf</i></li>
|
||||
<li><a href="https://github.com/EiffelWebFramework/ewf_examples">available on Github</a></li>
|
||||
</ul>
|
||||
<li>Read the <a href="{{ page.base_url }}wiki/Documentation">Overview</a></li>
|
||||
<li>Read the <a href="{{ page.base_url }}doc/workbook">Overview</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
52
wiki.html
52
wiki.html
@@ -1,52 +0,0 @@
|
||||
---
|
||||
layout: default
|
||||
title: Wiki
|
||||
base_url: ../
|
||||
---
|
||||
|
||||
<ul>
|
||||
<li><a href="community.md">community</a></li>
|
||||
<li><a href="Deployment.md">Deployment</a></li>
|
||||
<li><a href="documentation">documentation</a>
|
||||
<ul><ul>
|
||||
<li><a href="documentation/Connectors.md">Connectors</a></li>
|
||||
<li><a href="documentation/Filter.md">Filter</a></li>
|
||||
<li><a href="documentation/HTTP-client-library.md">HTTP client library</a></li>
|
||||
<li><a href="documentation/Library-conneg.md">Library conneg</a></li>
|
||||
<li><a href="documentation/README.md">README</a></li>
|
||||
<li><a href="documentation/Request-and-response.md">Request and response</a></li>
|
||||
<li><a href="documentation/Request.md">Request</a></li>
|
||||
<li><a href="documentation/Response.md">Response</a></li>
|
||||
<li><a href="documentation/Router.md">Router</a></li>
|
||||
<li><a href="documentation/Service.md">Service</a></li>
|
||||
</ul></ul></li><li><a href="EWSGI">EWSGI</a>
|
||||
<ul><ul>
|
||||
<li><a href="EWSGI/Open-Questions.md">Open Questions</a></li>
|
||||
<li><a href="EWSGI/README.md">README</a></li>
|
||||
<li><a href="EWSGI/specification---difference-in-main-proposals.md">specification difference in main proposals</a></li>
|
||||
<li><a href="EWSGI/specification.md">specification</a></li>
|
||||
</ul></ul></li> <li><a href="Libraries.md">Libraries</a></li>
|
||||
<li><a href="policy-driven-framework">policy driven framework</a>
|
||||
<ul><ul>
|
||||
<li><a href="policy-driven-framework/Using-the-policy-driven-framework.md">Using the policy driven framework</a></li>
|
||||
<li><a href="policy-driven-framework/Writing-the-handlers.md">Writing the handlers</a></li>
|
||||
<li><a href="policy-driven-framework/Wsf-caching-policy.md">Wsf caching policy</a></li>
|
||||
<li><a href="policy-driven-framework/Wsf-previous-policy.md">Wsf previous policy</a></li>
|
||||
<li><a href="policy-driven-framework/WSF_OPTIONS_POLICY.md">WSF OPTIONS POLICY</a></li>
|
||||
</ul></ul></li><li><a href="project">project</a>
|
||||
<ul><ul>
|
||||
<li><a href="project/meetings">meetings</a>
|
||||
<ul><ul>
|
||||
<li><a href="project/meetings/Web-meeting-2012-09-18.md">Web meeting 2012 09 18</a></li>
|
||||
</ul></ul></li> <li><a href="project/Meetings.md">Meetings</a></li>
|
||||
<li><a href="project/Projects-new-suggestions.md">Projects new suggestions</a></li>
|
||||
<li><a href="project/Projects.md">Projects</a></li>
|
||||
<li><a href="project/roadmap.md">roadmap</a></li>
|
||||
<li><a href="project/Task-json.md">Task json</a></li>
|
||||
<li><a href="project/Tasks-Roadmap.md">Tasks Roadmap</a></li>
|
||||
<li><a href="project/Useful-links.md">Useful links</a></li>
|
||||
</ul></ul></li> <li><a href="README.md">README</a></li>
|
||||
<li><a href="server_architecture.png">server architecture</a></li>
|
||||
<li><a href="Source-structure.md">Source structure</a></li>
|
||||
<li><a href="Spec-server-architecture.md">Spec server architecture</a></li>
|
||||
</ul>
|
||||
@@ -1,138 +0,0 @@
|
||||
---
|
||||
layout: default
|
||||
title: Deployment
|
||||
base_url: ../../
|
||||
---
|
||||
EWF Deployment
|
||||
==============
|
||||
|
||||
##Apache on Windows
|
||||
###Apache Install
|
||||
|
||||
- Check the correct version (Win 32 or Win64)
|
||||
- Apache Version: Apache 2.4.4
|
||||
- Windows: http://www.apachelounge.com/download/
|
||||
|
||||
###Deploying EWF CGI
|
||||
|
||||
####CGI overview
|
||||
> A new process is started for each HTTP request. So if there are N request to the same CGI
|
||||
> program, the code of the CGI program is loaded into memory N times.
|
||||
> When a CGI program finished handling a request, the program terminates
|
||||
|
||||
1. Build EWF application.
|
||||
|
||||
```
|
||||
ec -config app.ecf -target app_cgi -finalize -c_compile -project_path .
|
||||
Note: change app.ecf and target app_cgi based on your own configuration.
|
||||
```
|
||||
2. Copy the generated exe file and the www content.
|
||||
|
||||
```
|
||||
Copy the app.exe and the folder www into a folder served by apache2, for example under
|
||||
<APACHE_PATH>/htdocs
|
||||
<APACHE_PATH> = path to your apache installation
|
||||
Edit httpd.conf under c:/<APACHE_PATH>/conf
|
||||
DocumentRoot "c:/<APACHE_PATH>/htdocs"
|
||||
<Directory "c:/<APACHE_PATH>/htdocs">
|
||||
AllowOverride All --
|
||||
Require all granted -- this is required in Apache 2.4.4
|
||||
</Directory>
|
||||
```
|
||||
|
||||
3. Check that you have the following modules enabled.
|
||||
|
||||
```
|
||||
LoadModule cgi_module modules/mod_cgi.so
|
||||
LoadModule rewrite_module modules/mod_rewrite.so
|
||||
```
|
||||
|
||||
*Tip:*
|
||||
To check the syntax of your httpd.conf file. From command line run the following.
|
||||
|
||||
```
|
||||
$>httpd - t
|
||||
```
|
||||
####.htaccess CGI
|
||||
http://perishablepress.com/stupid-htaccess-trics/
|
||||
|
||||
```
|
||||
Options +ExecCGI +Includes +FollowSymLinks -Indexes
|
||||
AddHandler cgi-script exe
|
||||
<IfModule mod_rewrite.c>
|
||||
RewriteEngine on
|
||||
RewriteRule ^$ $service [L]
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteCond %{REQUEST_FILENAME} !-d
|
||||
RewriteCond %{REQUEST_URI} !$service
|
||||
RewriteRule ^(.*)$ $service/$1
|
||||
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization},L]
|
||||
</IfModule
|
||||
```
|
||||
Replace $service with the name of your executable service, for example app_service.exe
|
||||
|
||||
|
||||
##Deploying EWF FCGI
|
||||
|
||||
- To deploy FCGI you will need to download the mod_fcgi module.
|
||||
- You can get it from here http://www.apachelounge.com/download/
|
||||
|
||||
###FCGI overview
|
||||
> FastCGI allows a single, long-running process to handle more than one user request while keeping close to
|
||||
> the CGI programming model, retaining the simplicity while eliminating the overhead of creating a new
|
||||
> process for each request. Unlike converting an application to a web server plug-in, FastCGI applications
|
||||
> remain independent of the web server.
|
||||
|
||||
1. Build EWF application
|
||||
|
||||
```
|
||||
ec -config app.ecf -target app_fcgi -finalize -c_compile -project_path .
|
||||
Note: change app.ecf and target app_fcgi based on your own configuration.
|
||||
```
|
||||
2. Copy the generated exe file and the www content
|
||||
|
||||
```
|
||||
Copy the app.exe and the folder "www" into a folder served by apache2, for example under
|
||||
<APACHE_PATH>/htdocs.
|
||||
<APACHE_PATH> = path to your apache installation
|
||||
Edit httpd.conf under c:/<APACHE_PATH>/conf
|
||||
|
||||
DocumentRoot "c:/<APACHE_PATH>/htdocs"
|
||||
<Directory "c:/<APACHE_PATH>/htdocs">
|
||||
AllowOverride All --
|
||||
Require all granted -- this is required in Apache 2.4.4
|
||||
</Directory>
|
||||
```
|
||||
Check that you have the following modules enabled.
|
||||
|
||||
```
|
||||
LoadModule rewrite_module modules/mod_rewrite.so
|
||||
LoadModule fcgid_module modules/mod_fcgid.so
|
||||
```
|
||||
|
||||
By default Apache does not comes with fcgid module, so you will need to
|
||||
download it, and put the module under Apache2/modules
|
||||
|
||||
####.htaccess FCGI
|
||||
http://perishablepress.com/stupid-htaccess-tricks/
|
||||
|
||||
```
|
||||
Options +ExecCGI +Includes +FollowSymLinks -Indexes
|
||||
<IfModule mod_fcgid.c>
|
||||
AddHandler fcgid-script .ews
|
||||
FcgidWrapper $FULL_PATH/$service .ews
|
||||
</IfModule>
|
||||
<IfModule mod_rewrite.c>
|
||||
RewriteEngine on
|
||||
RewriteBase /
|
||||
RewriteRule ^$ service.ews [L]
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteCond %{REQUEST_FILENAME} !-d
|
||||
RewriteCond %{REQUEST_URI} !=/favicon.ico
|
||||
RewriteCond %{REQUEST_URI} !service.ews
|
||||
RewriteRule ^(.*)$ service.ews/$1 RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization},L]
|
||||
</IfModule>
|
||||
```
|
||||
Replace $service with the name of your executable $service, for example app_service.exe
|
||||
You will need to create an service.ews file, this file will be located at the same place where you
|
||||
copy your app service executable.
|
||||
@@ -1,57 +0,0 @@
|
||||
Menu :: [[Doc_Getting_Started.mediawiki|Getting Started]] :: [[Community-collaboration.md|Community]]
|
||||
|
||||
= Getting Started =
|
||||
This page will help you to get started with EWF. We will first see how to install EWF and then how to compile and run the venerable Hello World example.
|
||||
|
||||
== Installation ==
|
||||
=== EiffelStudio 7.2 ===
|
||||
EWF is already included in EiffelStudio 7.2: you don't have to do anything in this case! This is the recommanded solution if you are a new developer or are new to Eiffel.
|
||||
|
||||
=== Other EiffelStudio versions ===
|
||||
If you have another version of EiffelStudio than 7.2, you have to
|
||||
|
||||
* dowload EWF
|
||||
* create a directory where you will put your custum Eiffel libraries
|
||||
* extract EWF in the newly created directory
|
||||
* define the environment variable EIFFEL_LIBRARY to point to the newly created directory
|
||||
|
||||
=== Source code ===
|
||||
The source code is available on Github. You can get it by running the command:
|
||||
|
||||
<code>git clone git://github.com/EiffelWebFramework/EWF.git</code>
|
||||
|
||||
== Hello World ==
|
||||
The hello world example is located in the directory $ISE_EIFFEL/contrib/examples/web/ewf/simple. Just double click on the simple.ecf file and select the simple target or if you prefer the command line, run the command:
|
||||
<code>estudio -config simple.ecf -target simple</code>
|
||||
|
||||
Once the project is compiled, we will adapt the root class to point to port number 9090.
|
||||
|
||||
'''Note''': By default, the application listens on port 80, which is often already used by standard webservers (Apache, nginx, ...). Moreover, on Linux, ports below 1024 can only be opened by root.
|
||||
|
||||
To do this, we will redefine the feature initialize as follows:
|
||||
|
||||
|
||||
class
|
||||
APPLICATION
|
||||
|
||||
inherit
|
||||
WSF_DEFAULT_SERVICE
|
||||
redefine
|
||||
initialize
|
||||
end
|
||||
|
||||
create
|
||||
make_and_launch
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
initialize
|
||||
-- Initialize current service
|
||||
do
|
||||
set_service_option ("port", 9090)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
After one more compile, you can now launch the application and point your browser to [http://localhost:9090].
|
||||
You should now see a simple page with Hello World.
|
||||
@@ -1,7 +0,0 @@
|
||||
Menu :: [[Doc_Getting_Started.mediawiki|Getting Started]] :: [[community.md|Community]]
|
||||
|
||||
= Eiffel Web Framework =
|
||||
Framework to build web applications in Eiffel
|
||||
|
||||
[ [http://github.com/EiffelWebFramework/EWF/zipball/ Download Current] ]
|
||||
[ [http://github.com/EiffelWebFramework/EWF/zipball/release-0.3 Download v0.3] ]
|
||||
@@ -1,30 +0,0 @@
|
||||
---
|
||||
layout: default
|
||||
title: Open Questions
|
||||
base_url: ../../
|
||||
---
|
||||
## STRING_32, UTF-8, ... ? ##
|
||||
Berend raised the point that using STRING_32 is consuming 4 times the space used for STRING_8.
|
||||
And CPU is cheaper than memory, so we should try to use as less memory as possible.
|
||||
And then for Berend, STRING_32 is not the solution.
|
||||
|
||||
Most of the data are just STRING_8 in CGI
|
||||
so let's list the various request data
|
||||
|
||||
- **query_parameter** (from the query string ?foo=bar&extra=blabla )
|
||||
in this case, I think the name can be url-encoded, and obviously the value too
|
||||
I guess it makes sense to url-decode them
|
||||
but on the other hand, we could just keep them url-encoded (as they are), and it is up to the application to url-decode them if needed.
|
||||
Of course, we should provide facilities to url-decode those strings.
|
||||
|
||||
- **form_data_parameter** (from the POST method)
|
||||
quite often, it is same kind of content that `parameters'
|
||||
but .. here this might depends on the encoding for multi-parts encoding.
|
||||
|
||||
- **meta_variable** (from the request itself ... CGI meta variables..)
|
||||
I am wondering about unicode domain name ...
|
||||
|
||||
- **input data** ...
|
||||
I think this is up to the application
|
||||
|
||||
... to be continued ...
|
||||
@@ -1,77 +0,0 @@
|
||||
---
|
||||
layout: default
|
||||
title: README
|
||||
base_url: ../../
|
||||
---
|
||||
- See proposed specifications: [EWSGI specification](EWSGI-specification.md)
|
||||
- See [Open questions](EWSGI-Open-Questions.md)
|
||||
- And below the various proposals and associated decision
|
||||
|
||||
----
|
||||
# Waiting for decision
|
||||
|
||||
## Include EWF_HEADER into EWSGI as WGI_HEADERS
|
||||
- Code: **P-2011-09-26-include-wgi-headers**
|
||||
- Status: proposed on 2011-09-26 **WAITING FOR APPROVAL**
|
||||
|
||||
> Include WGI_HEADERS to help the user to build HTTP Header.
|
||||
> So that he doesn't have to know the HTTP specification for usual needs
|
||||
|
||||
|
||||
----
|
||||
# Adopted entries
|
||||
|
||||
## Rename `parameter` into `item`
|
||||
- Code: **P-2011-09-07-renaming_REQUEST_item**
|
||||
- Status: proposed on 2011-09-07 **ADOPTED-by-default**
|
||||
|
||||
> rename `{REQUEST}.parameter (n: READABLE_STRING_GENERAL): detachable WGI_VALUE`
|
||||
> into `{REQUEST}.item (n: READABLE_STRING_GENERAL): detachable WGI_VALUE`
|
||||
> and similar for `parameters` -> `items`
|
||||
|
||||
## Return type of `parameter' (and similar query_, form_data_ ...) should be deferred WGI_VALUE
|
||||
- Code: **P-2011-09-05-WGI_VALUE**
|
||||
- Status: proposed on 2011-09-05 **ADOPTED-by-default**
|
||||
|
||||
> Instead of returning just `READABLE_STRING_32` , it would be better to use **WGI_VALUE** .
|
||||
> Mainly to address the multiple value for the same param name, but also for uploaded files.
|
||||
> This allows to have various types such as WGI_STRING_VALUE, WGI_LIST_VALUE, WGI_TABLE_VALUE, WGI_FILE_VALUE .
|
||||
>
|
||||
> Thus we would have: <code>parameter (a_name: READABLE_STRING_GENERAL): detachable WGI_VALUE </code>
|
||||
|
||||
## Include the parameter's name in WGI_VALUE interface
|
||||
- Code: **P-2011-09-05-WGI_VALUE_interface**
|
||||
- Dependence: adoption of P-2011-09-05-WGI_VALUE , may impact on P-2011-09-05-parameters_ITERABLE
|
||||
- Status: proposed on 2011-09-05 **ADOPTED-by-default**
|
||||
|
||||
> include the corresponding parameter's name in WGI_VALUE interface.
|
||||
> Such as `{WGI_VALUE}.name: READABLE_STRING_GENERAL` (or READABLE_STRING_32).
|
||||
>
|
||||
> This would also allow to replace
|
||||
> signature `parameters: ITERABLE [TUPLE [name: READABLE_STRING_GENERAL; value: WGI__VALUE]]'
|
||||
> by a nicer signature `parameters: ITERABLE [WGI__VALUE]`
|
||||
|
||||
## Signature of parameters (and similar) using ITERABLE [...]
|
||||
- Code: **P-2011-09-05-parameters_ITERABLE**
|
||||
- Status: proposed on 2011-09-05 **ADOPTED-by-default**
|
||||
|
||||
> Description: Instead of forcing the implementation to use HASH_TABLE, DS_HASH_TABLE, DS_HASH_SET, ... or similar
|
||||
> we should use `ITERABLE`
|
||||
>
|
||||
> `parameters: ITERABLE [TUPLE [name: READABLE_STRING_GENERAL; value: WGI_VALUE]]`
|
||||
>
|
||||
> Or, if `P-2011-09-05-WGI_VALUE_interface` is adopted (WGI_VALUE.name holds the related parameter's name)
|
||||
>
|
||||
> `parameters: ITERABLE [WGI_VALUE]`
|
||||
|
||||
## Change prefix from EWSGI_ to WGI_
|
||||
- Code: **P-2011-08-29-WGI_prefix**
|
||||
- Status: **adopted**
|
||||
- Decision: **WGI_**
|
||||
|
||||
> shorter and pronouncable prefix for EWSGI class names
|
||||
|
||||
----
|
||||
# Rejected entries
|
||||
|
||||
...
|
||||
@@ -1,110 +0,0 @@
|
||||
---
|
||||
layout: default
|
||||
title: specification difference in main proposals
|
||||
base_url: ../../
|
||||
---
|
||||
Currently the **design of the EWSGI** is not going very fast, mainly due to conflicts for the core design.
|
||||
|
||||
Let's try to summary today's **points of conflict** between Paul's proposal, and Jocelyn's proposal.
|
||||
Since Paul put the specification on the wiki from Seibo Solution Studio,
|
||||
and Jocelyn is implementing the proposal withing the current Eiffel Web Framework.
|
||||
(If other proposals are provided, we'll compare them to those 2 existing proposals.)
|
||||
|
||||
Let's name the **Seibo-EWSGI** proposal and the **EWF-EWSGI** proposal.
|
||||
|
||||
Of course, **the goal is to have only one specification**, but it is good to have more than one proposal. So that we really try to specify the best EWSGI as possible.
|
||||
|
||||
Let's remind that EWSGI is meant to be a specification, and we are looking at **Specification-Compliance** for the future implementation (so this is mainly about class names, and feature signatures. I.e the Eiffel system interface).
|
||||
|
||||
_Note_: to make the code shorter, we will not always include the prefix EWSGI_ to the class name.
|
||||
_Note2_: we will use the term of "user" for the developer using the EWSGI specification and/or implementation(s)
|
||||
|
||||
## General goal ##
|
||||
At first, the main difference between Seibo-EWSGI and EWF-EWSGI is a tiny nuance on the general goal.
|
||||
_Seibo-EWSGI_: get web application portability across a variety of web servers.
|
||||
_EWF-EWSGI_: get web application portability across a variety of web servers including connectors.
|
||||
|
||||
Both are following the CGI specification.
|
||||
|
||||
To resume, the goal is to get an Eiffel Web ecosystem based on EWSGI specification which allow to write components, libraries, applications based on EWSGI, and which can be compiled and used without changes on the various EWSGI implementations.
|
||||
EWF-EWSGI is also targetting to make the connector implementations portable on the various EWSGI implementation. However this is not the most critical point, and could be address in later specification version, or maybe a specific EWSGI/Connector specification ...
|
||||
|
||||
**Conclusion**: the general goal is (merely) the same for both proposals.
|
||||
That is a good point, otherwise no need to compare the 2 proposals
|
||||
|
||||
## Main entry point for a Web application component ##
|
||||
This is the first important difference in the class EWSGI_APPLICATION
|
||||
|
||||
Seibo-EWSGI:
|
||||
|
||||
response (request: EWSGI_REQUEST): EWSGI_RESPONSE is
|
||||
-- The response to the given 'request'.
|
||||
deferred
|
||||
ensure
|
||||
Result.status_is_set
|
||||
Result.ready_to_transmit
|
||||
end
|
||||
|
||||
EWF-EWSGI:
|
||||
|
||||
execute (req: EWSGI_REQUEST; res: EWSGI_RESPONSE_BUFFER)
|
||||
-- Execute the request
|
||||
-- See `req.input' for input stream
|
||||
-- `req.environment' for the Gateway environment
|
||||
-- and `res' for the output buffer
|
||||
require
|
||||
res_status_unset: not res.status_is_set
|
||||
deferred
|
||||
ensure
|
||||
res_status_set: res.status_is_set
|
||||
end
|
||||
|
||||
So the main difference is for the user/developer
|
||||
Seibo-EWSGI: the user has to create the RESPONSE object.
|
||||
EWF-EWSGI: the user has the RESPONSE_BUFFER object passed as argument, and it is ready to use.
|
||||
|
||||
The consequences are important because
|
||||
|
||||
* Seibo-EWSGI:
|
||||
|
||||
- to make the creation of RESPONSE simple, the decision was to remove any notion of output stream in EWSGI.
|
||||
- Then the RESPONSE has to be built before sending it. But the proposal also provides a way to get the response message by block during the transmission. A kind of delay RESPONSE filling which is for now using a read_block pattern. And this mimics the send immediately the message parts to the client.
|
||||
- This design allows the developer to use its own descendant of RESPONSE and then implement the "read_block" pattern when needed.
|
||||
- This also allows to control carefully that the status+headers are sent before the body. And if not using the read_block pattern, this allows to set/change the headers even after setting the message body, in fact you really build the Response as an object, and you set the various attributes whenever you want.
|
||||
- The read_block pattern is meant to address the case where the message would be too big to stay in memory, and/or to send some part immediately to the client, but for that you must stick to the read_block pattern.
|
||||
- Seibo team find the Response-as-Result design more natural, and adopt the Request/Response model of HTTP protocol
|
||||
|
||||
* EWF-EWSGI:
|
||||
|
||||
- the RESPONSE_BUFFER is passed by argument to the user, then he does not have to worry about how to create it.
|
||||
- it is a buffer and could be implemented in various way by the implementation(s) to access the output stream if any
|
||||
- For now, there is no easy way for the user, from the proposed specification, to have a customized RESPONSE_BUFFER
|
||||
- It is easy to implement the Seibo-EWSGI design on top of EWF-EWSGI (the code is provided in EWF-ewsgi implementation)
|
||||
- the buffer specification allows the user to send the message parts immediately to the client without complicated pattern. As today implementation, the only restriction imposed by the design is to pass the Status code, and the headers before starting sending the message parts; this is checked by the associated assertions. However this part might be thinked again, to be more flexible, and let this to the responsibility of the user, or of other frameworks built on top of EWSGI spec.
|
||||
|
||||
## Notion of output stream ##
|
||||
|
||||
As you might noticed
|
||||
In EWF-EWSGI, the output stream is kind of hidden and replaced by the output buffer, which is in fact quite similar. However the goal is to allow the user to send immediately the message parts to the client as simple as writing in the buffer.
|
||||
The EWSGI implementation will handle this buffer to integrate nicely with the underlying web server techno. This will be done through the implementation of the various connectors.
|
||||
|
||||
In Seibo-EWSGI there is NO notion of output stream, this is to handle the potential case of a HTTP server technologies following CGI specification but without any output stream.
|
||||
The read_block pattern allows the user to send a big response part by part, and/or also send some message parts immediately to the client during the transmission, this looks like a delayed computation of the messages.
|
||||
|
||||
---
|
||||
So for now, those are the main critical (blocking) differences I (Jocelyn) can see.
|
||||
|
||||
## Personal point of views ##
|
||||
|
||||
(Feel free to add your own point of view)
|
||||
|
||||
### Jocelyn ###
|
||||
|
||||
* I prefer the EWF-EWSGI solution (obviously this is my proposal) mainly because with it, you can also implement the Seibo-EWSGI design. And the "more you can do, the less you can also do".
|
||||
* I find the read_block pattern really not natural to use in non trivial application (even in trivial application, this might not be that easy to handle, and thus a potential source of bugs)
|
||||
* I admit the need to send immediately to the client might not be the vast majority of cases. But this is useful for big messages, and for long computing message where the client wants to follow the progression (concrete cases: drupal batch, wordpress online update, ...)
|
||||
* I would prefer to include the connector part into the EWSGI specification, this way any EWSGI_connector implementation could work with any EWSGI implementation. Otherwise we might end up with many different EWSGI implementations, and according to the underlying connector(s) you want to use, you will need to use different EWSGI implementations.
|
||||
|
||||
### Others ... ? ###
|
||||
Please contribute ... correct also the wiki page if there are mistake or misunderstanding.
|
||||
|
||||
@@ -1,793 +0,0 @@
|
||||
---
|
||||
layout: default
|
||||
title: specification
|
||||
base_url: ../../
|
||||
---
|
||||
**WARNING** **THIS PAGE IS IN PROGRESS, AS IT IS NOW, IT NEEDS UPDATE SINCE IT DOES NOT REFLECT THE FUTURE INTERFACE**
|
||||
|
||||
# The Eiffel Web Server Gateway Interface
|
||||
## Preface
|
||||
This specification is a proposition based on recent discussion on the mailing list.
|
||||
This is work in progress, so far nothing had been decided.
|
||||
You can find another proposal at [http://eiffel.seibostudios.se/wiki/EWSGI](http://eiffel.seibostudios.se/wiki/EWSGI) , it has common background and goal, however still differ on specific parts.
|
||||
The main goal for now is to unified those 2 specifications.
|
||||
|
||||
---
|
||||
Note the following is work in progress, and reflect a specification proposal, rather than the final specification.
|
||||
2011-08-01
|
||||
---
|
||||
For now, the specification from EWF is done in Eiffel interface
|
||||
please see: [https://github.com/Eiffel-World/Eiffel-Web-Framework/tree/master/library/server/ewsgi/specification](https://github.com/Eiffel-World/Eiffel-Web-Framework/tree/master/library/server/ewsgi/specification)
|
||||
|
||||
WGI_APPLICATION
|
||||
|
||||
deferred class
|
||||
WGI_APPLICATION
|
||||
|
||||
feature {NONE} -- Execution
|
||||
|
||||
execute (req: WGI_REQUEST; res: WGI_RESPONSE_BUFFER)
|
||||
-- Execute the request
|
||||
-- See `req.input' for input stream
|
||||
-- `req.meta_variables' for the CGI meta variable
|
||||
-- and `res' for the output buffer
|
||||
require
|
||||
res_status_unset: not res.status_is_set
|
||||
deferred
|
||||
ensure
|
||||
res_status_set: res.status_is_set
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
WGI_REQUEST
|
||||
|
||||
deferred class
|
||||
WGI_REQUEST
|
||||
|
||||
feature -- Access: Input
|
||||
|
||||
input: WGI_INPUT_STREAM
|
||||
-- Server input channel
|
||||
deferred
|
||||
end
|
||||
|
||||
feature -- Access: extra values
|
||||
|
||||
request_time: detachable DATE_TIME
|
||||
-- Request time (UTC)
|
||||
deferred
|
||||
end
|
||||
|
||||
feature -- Access: CGI meta variables
|
||||
|
||||
meta_variable (a_name: READABLE_STRING_GENERAL): detachable WGI_VALUE
|
||||
-- Environment variable related to `a_name'
|
||||
require
|
||||
a_name_valid: a_name /= Void and then not a_name.is_empty
|
||||
deferred
|
||||
end
|
||||
|
||||
meta_string_variable (a_name: READABLE_STRING_GENERAL): detachable READABLE_STRING_32
|
||||
-- Environment variable related to `a_name'
|
||||
require
|
||||
a_name_valid: a_name /= Void and then not a_name.is_empty
|
||||
do
|
||||
if attached meta_variable (a_name) as val then
|
||||
Result := val.as_string
|
||||
end
|
||||
end
|
||||
|
||||
meta_variables: ITERABLE [WGI_VALUE]
|
||||
-- These variables are specific to requests made with HTTP.
|
||||
-- Interpretation of these variables may depend on the value of
|
||||
-- SERVER_PROTOCOL.
|
||||
--
|
||||
-- Environment variables with names beginning with "HTTP_" contain
|
||||
-- header data read from the client, if the protocol used was HTTP.
|
||||
-- The HTTP header name is converted to upper case, has all
|
||||
-- occurrences of "-" replaced with "_" and has "HTTP_" prepended to
|
||||
-- give the environment variable name. The header data may be
|
||||
-- presented as sent by the client, or may be rewritten in ways which
|
||||
-- do not change its semantics. If multiple headers with the same
|
||||
-- field-name are received then they must be rewritten as a single
|
||||
-- header having the same semantics. Similarly, a header that is
|
||||
-- received on more than one line must be merged onto a single line.
|
||||
-- The server must, if necessary, change the representation of the
|
||||
-- data (for example, the character set) to be appropriate for a CGI
|
||||
-- environment variable.
|
||||
--
|
||||
-- The server is not required to create environment variables for all
|
||||
-- the headers that it receives. In particular, it may remove any
|
||||
-- headers carrying authentication information, such as
|
||||
-- "Authorization"; it may remove headers whose value is available to
|
||||
-- the script via other variables, such as "Content-Length" and
|
||||
-- "Content-Type".
|
||||
--
|
||||
-- For convenience it might also include the following CGI entries
|
||||
deferred
|
||||
end
|
||||
|
||||
feature -- Common Gateway Interface - 1.1 8 January 1996
|
||||
|
||||
auth_type: detachable READABLE_STRING_32
|
||||
-- This variable is specific to requests made via the "http"
|
||||
-- scheme.
|
||||
--
|
||||
-- If the Script-URI required access authentication for external
|
||||
-- access, then the server MUST set the value of this variable
|
||||
-- from the 'auth-scheme' token in the request's "Authorization"
|
||||
-- header field. Otherwise it is set to NULL.
|
||||
--
|
||||
-- AUTH_TYPE = "" | auth-scheme
|
||||
-- auth-scheme = "Basic" | "Digest" | token
|
||||
--
|
||||
-- HTTP access authentication schemes are described in section 11
|
||||
-- of the HTTP/1.1 specification [8]. The auth-scheme is not
|
||||
-- case-sensitive.
|
||||
--
|
||||
-- Servers MUST provide this metavariable to scripts if the
|
||||
-- request header included an "Authorization" field that was
|
||||
-- authenticated.
|
||||
deferred
|
||||
end
|
||||
|
||||
content_length: detachable READABLE_STRING_32
|
||||
-- This metavariable is set to the size of the message-body
|
||||
-- entity attached to the request, if any, in decimal number of
|
||||
-- octets. If no data are attached, then this metavariable is
|
||||
-- either NULL or not defined. The syntax is the same as for the
|
||||
-- HTTP "Content-Length" header field (section 14.14, HTTP/1.1
|
||||
-- specification [8]).
|
||||
--
|
||||
-- CONTENT_LENGTH = "" | 1*digit
|
||||
--
|
||||
-- Servers MUST provide this metavariable to scripts if the
|
||||
-- request was accompanied by a message-body entity.
|
||||
deferred
|
||||
end
|
||||
|
||||
content_length_value: NATURAL_64
|
||||
-- Integer value related to `content_length"
|
||||
deferred
|
||||
end
|
||||
|
||||
content_type: detachable READABLE_STRING_32
|
||||
-- If the request includes a message-body, CONTENT_TYPE is set to
|
||||
-- the Internet Media Type [9] of the attached entity if the type
|
||||
-- was provided via a "Content-type" field in the request header,
|
||||
-- or if the server can determine it in the absence of a supplied
|
||||
-- "Content-type" field. The syntax is the same as for the HTTP
|
||||
-- "Content-Type" header field.
|
||||
--
|
||||
-- CONTENT_TYPE = "" | media-type
|
||||
-- media-type = type "/" subtype *( ";" parameter)
|
||||
-- type = token
|
||||
-- subtype = token
|
||||
-- parameter = attribute "=" value
|
||||
-- attribute = token
|
||||
-- value = token | quoted-string
|
||||
--
|
||||
-- The type, subtype, and parameter attribute names are not
|
||||
-- case-sensitive. Parameter values MAY be case sensitive. Media
|
||||
-- types and their use in HTTP are described in section 3.7 of
|
||||
-- the HTTP/1.1 specification [8].
|
||||
--
|
||||
-- Example:
|
||||
--
|
||||
-- application/x-www-form-urlencoded
|
||||
--
|
||||
-- There is no default value for this variable. If and only if it
|
||||
-- is unset, then the script MAY attempt to determine the media
|
||||
-- type from the data received. If the type remains unknown, then
|
||||
-- the script MAY choose to either assume a content-type of
|
||||
-- application/octet-stream or reject the request with a 415
|
||||
-- ("Unsupported Media Type") error. See section 7.2.1.3 for more
|
||||
-- information about returning error status values.
|
||||
--
|
||||
-- Servers MUST provide this metavariable to scripts if a
|
||||
-- "Content-Type" field was present in the original request
|
||||
-- header. If the server receives a request with an attached
|
||||
-- entity but no "Content-Type" header field, it MAY attempt to
|
||||
-- determine the correct datatype, or it MAY omit this
|
||||
-- metavariable when communicating the request information to the
|
||||
-- script.
|
||||
deferred
|
||||
end
|
||||
|
||||
gateway_interface: READABLE_STRING_32
|
||||
-- This metavariable is set to the dialect of CGI being used by
|
||||
-- the server to communicate with the script. Syntax:
|
||||
--
|
||||
-- GATEWAY_INTERFACE = "CGI" "/" major "." minor
|
||||
-- major = 1*digit
|
||||
-- minor = 1*digit
|
||||
--
|
||||
-- Note that the major and minor numbers are treated as separate
|
||||
-- integers and hence each may be more than a single digit. Thus
|
||||
-- CGI/2.4 is a lower version than CGI/2.13 which in turn is
|
||||
-- lower than CGI/12.3. Leading zeros in either the major or the
|
||||
-- minor number MUST be ignored by scripts and SHOULD NOT be
|
||||
-- generated by servers.
|
||||
--
|
||||
-- This document defines the 1.1 version of the CGI interface
|
||||
-- ("CGI/1.1").
|
||||
--
|
||||
-- Servers MUST provide this metavariable to scripts.
|
||||
--
|
||||
-- The version of the CGI specification to which this server
|
||||
-- complies. Syntax:
|
||||
--
|
||||
-- GATEWAY_INTERFACE = "CGI" "/" 1*digit "." 1*digit
|
||||
--
|
||||
-- Note that the major and minor numbers are treated as separate
|
||||
-- integers and that each may be incremented higher than a single
|
||||
-- digit. Thus CGI/2.4 is a lower version than CGI/2.13 which in
|
||||
-- turn is lower than CGI/12.3. Leading zeros must be ignored by
|
||||
-- scripts and should never be generated by servers.
|
||||
deferred
|
||||
end
|
||||
|
||||
path_info: READABLE_STRING_32
|
||||
-- The PATH_INFO metavariable specifies a path to be interpreted
|
||||
-- by the CGI script. It identifies the resource or sub-resource
|
||||
-- to be returned by the CGI script, and it is derived from the
|
||||
-- portion of the URI path following the script name but
|
||||
-- preceding any query data. The syntax and semantics are similar
|
||||
-- to a decoded HTTP URL 'path' token (defined in RFC 2396 [4]),
|
||||
-- with the exception that a PATH_INFO of "/" represents a single
|
||||
-- void path segment.
|
||||
--
|
||||
-- PATH_INFO = "" | ( "/" path )
|
||||
-- path = segment *( "/" segment )
|
||||
-- segment = *pchar
|
||||
-- pchar = <any CHAR except "/">
|
||||
--
|
||||
-- The PATH_INFO string is the trailing part of the <path>
|
||||
-- component of the Script-URI (see section 3.2) that follows the
|
||||
-- SCRIPT_NAME portion of the path.
|
||||
--
|
||||
-- Servers MAY impose their own restrictions and limitations on
|
||||
-- what values they will accept for PATH_INFO, and MAY reject or
|
||||
-- edit any values they consider objectionable before passing
|
||||
-- them to the script.
|
||||
--
|
||||
-- Servers MUST make this URI component available to CGI scripts.
|
||||
-- The PATH_INFO value is case-sensitive, and the server MUST
|
||||
-- preserve the case of the PATH_INFO element of the URI when
|
||||
-- making it available to scripts.
|
||||
deferred
|
||||
end
|
||||
|
||||
path_translated: detachable READABLE_STRING_32
|
||||
-- PATH_TRANSLATED is derived by taking any path-info component
|
||||
-- of the request URI (see section 6.1.6), decoding it (see
|
||||
-- section 3.1), parsing it as a URI in its own right, and
|
||||
-- performing any virtual-to-physical translation appropriate to
|
||||
-- map it onto the server's document repository structure. If the
|
||||
-- request URI includes no path-info component, the
|
||||
-- PATH_TRANSLATED metavariable SHOULD NOT be defined.
|
||||
--
|
||||
--
|
||||
-- PATH_TRANSLATED = *CHAR
|
||||
--
|
||||
-- For a request such as the following:
|
||||
--
|
||||
-- http://somehost.com/cgi-bin/somescript/this%2eis%2epath%2einfo
|
||||
--
|
||||
-- the PATH_INFO component would be decoded, and the result
|
||||
-- parsed as though it were a request for the following:
|
||||
--
|
||||
-- http://somehost.com/this.is.the.path.info
|
||||
--
|
||||
-- This would then be translated to a location in the server's
|
||||
-- document repository, perhaps a filesystem path something like
|
||||
-- this:
|
||||
--
|
||||
-- /usr/local/www/htdocs/this.is.the.path.info
|
||||
--
|
||||
-- The result of the translation is the value of PATH_TRANSLATED.
|
||||
--
|
||||
-- The value of PATH_TRANSLATED may or may not map to a valid
|
||||
-- repository location. Servers MUST preserve the case of the
|
||||
-- path-info segment if and only if the underlying repository
|
||||
-- supports case-sensitive names. If the repository is only
|
||||
-- case-aware, case-preserving, or case-blind with regard to
|
||||
-- document names, servers are not required to preserve the case
|
||||
-- of the original segment through the translation.
|
||||
--
|
||||
-- The translation algorithm the server uses to derive
|
||||
-- PATH_TRANSLATED is implementation defined; CGI scripts which
|
||||
-- use this variable may suffer limited portability.
|
||||
--
|
||||
-- Servers SHOULD provide this metavariable to scripts if and
|
||||
-- only if the request URI includes a path-info component.
|
||||
deferred
|
||||
end
|
||||
|
||||
query_string: READABLE_STRING_32
|
||||
-- A URL-encoded string; the <query> part of the Script-URI. (See
|
||||
-- section 3.2.)
|
||||
--
|
||||
-- QUERY_STRING = query-string
|
||||
-- query-string = *uric
|
||||
-- The URL syntax for a query string is described in section 3 of
|
||||
-- RFC 2396 [4].
|
||||
--
|
||||
-- Servers MUST supply this value to scripts. The QUERY_STRING
|
||||
-- value is case-sensitive. If the Script-URI does not include a
|
||||
-- query component, the QUERY_STRING metavariable MUST be defined
|
||||
-- as an empty string ("").
|
||||
deferred
|
||||
end
|
||||
|
||||
remote_addr: READABLE_STRING_32
|
||||
-- The IP address of the client sending the request to the
|
||||
-- server. This is not necessarily that of the user agent (such
|
||||
-- as if the request came through a proxy).
|
||||
--
|
||||
-- REMOTE_ADDR = hostnumber
|
||||
-- hostnumber = ipv4-address | ipv6-address
|
||||
-- The definitions of ipv4-address and ipv6-address are provided
|
||||
-- in Appendix B of RFC 2373 [13].
|
||||
--
|
||||
-- Servers MUST supply this value to scripts.
|
||||
deferred
|
||||
end
|
||||
|
||||
remote_host: detachable READABLE_STRING_32
|
||||
-- The fully qualified domain name of the client sending the
|
||||
-- request to the server, if available, otherwise NULL. (See
|
||||
-- section 6.1.9.) Fully qualified domain names take the form as
|
||||
-- described in section 3.5 of RFC 1034 [10] and section 2.1 of
|
||||
-- RFC 1123 [5]. Domain names are not case sensitive.
|
||||
--
|
||||
-- Servers SHOULD provide this information to scripts.
|
||||
deferred
|
||||
end
|
||||
|
||||
remote_ident: detachable READABLE_STRING_32
|
||||
-- The identity information reported about the connection by a
|
||||
-- RFC 1413 [11] request to the remote agent, if available.
|
||||
-- Servers MAY choose not to support this feature, or not to
|
||||
-- request the data for efficiency reasons.
|
||||
--
|
||||
-- REMOTE_IDENT = *CHAR
|
||||
--
|
||||
-- The data returned may be used for authentication purposes, but
|
||||
-- the level of trust reposed in them should be minimal.
|
||||
--
|
||||
-- Servers MAY supply this information to scripts if the RFC1413
|
||||
-- [11] lookup is performed.
|
||||
deferred
|
||||
end
|
||||
|
||||
remote_user: detachable READABLE_STRING_32
|
||||
-- If the request required authentication using the "Basic"
|
||||
-- mechanism (i.e., the AUTH_TYPE metavariable is set to
|
||||
-- "Basic"), then the value of the REMOTE_USER metavariable is
|
||||
-- set to the user-ID supplied. In all other cases the value of
|
||||
-- this metavariable is undefined.
|
||||
--
|
||||
-- REMOTE_USER = *OCTET
|
||||
--
|
||||
-- This variable is specific to requests made via the HTTP
|
||||
-- protocol.
|
||||
--
|
||||
-- Servers SHOULD provide this metavariable to scripts.
|
||||
deferred
|
||||
end
|
||||
|
||||
request_method: READABLE_STRING_32
|
||||
-- The REQUEST_METHOD metavariable is set to the method with
|
||||
-- which the request was made, as described in section 5.1.1 of
|
||||
-- the HTTP/1.0 specification [3] and section 5.1.1 of the
|
||||
-- HTTP/1.1 specification [8].
|
||||
--
|
||||
-- REQUEST_METHOD = http-method
|
||||
-- http-method = "GET" | "HEAD" | "POST" | "PUT" | "DELETE"
|
||||
-- | "OPTIONS" | "TRACE" | extension-method
|
||||
-- extension-method = token
|
||||
--
|
||||
-- The method is case sensitive. CGI/1.1 servers MAY choose to
|
||||
-- process some methods directly rather than passing them to
|
||||
-- scripts.
|
||||
--
|
||||
-- This variable is specific to requests made with HTTP.
|
||||
--
|
||||
-- Servers MUST provide this metavariable to scripts.
|
||||
deferred
|
||||
end
|
||||
|
||||
script_name: READABLE_STRING_32
|
||||
-- The SCRIPT_NAME metavariable is set to a URL path that could
|
||||
-- identify the CGI script (rather than the script's output). The
|
||||
-- syntax and semantics are identical to a decoded HTTP URL
|
||||
-- 'path' token (see RFC 2396 [4]).
|
||||
--
|
||||
-- SCRIPT_NAME = "" | ( "/" [ path ] )
|
||||
--
|
||||
-- The SCRIPT_NAME string is some leading part of the <path>
|
||||
-- component of the Script-URI derived in some implementation
|
||||
-- defined manner. No PATH_INFO or QUERY_STRING segments (see
|
||||
-- sections 6.1.6 and 6.1.8) are included in the SCRIPT_NAME
|
||||
-- value.
|
||||
--
|
||||
-- Servers MUST provide this metavariable to scripts.
|
||||
deferred
|
||||
end
|
||||
|
||||
server_name: READABLE_STRING_32
|
||||
-- The SERVER_NAME metavariable is set to the name of the server,
|
||||
-- as derived from the <host> part of the Script-URI (see section
|
||||
-- 3.2).
|
||||
--
|
||||
-- SERVER_NAME = hostname | hostnumber
|
||||
--
|
||||
-- Servers MUST provide this metavariable to scripts.
|
||||
deferred
|
||||
end
|
||||
|
||||
server_port: INTEGER_32
|
||||
-- The SERVER_PORT metavariable is set to the port on which the
|
||||
-- request was received, as used in the <port> part of the
|
||||
-- Script-URI.
|
||||
--
|
||||
-- SERVER_PORT = 1*digit
|
||||
--
|
||||
-- If the <port> portion of the script-URI is blank, the actual
|
||||
-- port number upon which the request was received MUST be
|
||||
-- supplied.
|
||||
--
|
||||
-- Servers MUST provide this metavariable to scripts.
|
||||
deferred
|
||||
end
|
||||
|
||||
server_protocol: READABLE_STRING_32
|
||||
-- The SERVER_PROTOCOL metavariable is set to the name and
|
||||
-- revision of the information protocol with which the request
|
||||
-- arrived. This is not necessarily the same as the protocol
|
||||
-- version used by the server in its response to the client.
|
||||
--
|
||||
-- SERVER_PROTOCOL = HTTP-Version | extension-version
|
||||
-- | extension-token
|
||||
-- HTTP-Version = "HTTP" "/" 1*digit "." 1*digit
|
||||
-- extension-version = protocol "/" 1*digit "." 1*digit
|
||||
-- protocol = 1*( alpha | digit | "+" | "-" | "." )
|
||||
-- extension-token = token
|
||||
--
|
||||
-- 'protocol' is a version of the <scheme> part of the
|
||||
-- Script-URI, but is not identical to it. For example, the
|
||||
-- scheme of a request may be "https" while the protocol remains
|
||||
-- "http". The protocol is not case sensitive, but by convention,
|
||||
-- 'protocol' is in upper case.
|
||||
--
|
||||
-- A well-known extension token value is "INCLUDED", which
|
||||
-- signals that the current document is being included as part of
|
||||
-- a composite document, rather than being the direct target of
|
||||
-- the client request.
|
||||
--
|
||||
-- Servers MUST provide this metavariable to scripts.
|
||||
deferred
|
||||
end
|
||||
|
||||
server_software: READABLE_STRING_32
|
||||
-- The SERVER_SOFTWARE metavariable is set to the name and
|
||||
-- version of the information server software answering the
|
||||
-- request (and running the gateway).
|
||||
--
|
||||
-- SERVER_SOFTWARE = 1*product
|
||||
-- product = token [ "/" product-version ]
|
||||
-- product-version = token
|
||||
-- Servers MUST provide this metavariable to scripts.
|
||||
deferred
|
||||
end
|
||||
|
||||
feature -- HTTP_*
|
||||
|
||||
http_accept: detachable READABLE_STRING_32
|
||||
-- Contents of the Accept: header from the current request, if there is one.
|
||||
-- Example: 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8'
|
||||
deferred
|
||||
end
|
||||
|
||||
http_accept_charset: detachable READABLE_STRING_32
|
||||
-- Contents of the Accept-Charset: header from the current request, if there is one.
|
||||
-- Example: 'iso-8859-1,*,utf-8'.
|
||||
deferred
|
||||
end
|
||||
|
||||
http_accept_encoding: detachable READABLE_STRING_32
|
||||
-- Contents of the Accept-Encoding: header from the current request, if there is one.
|
||||
-- Example: 'gzip'.
|
||||
deferred
|
||||
end
|
||||
|
||||
http_accept_language: detachable READABLE_STRING_32
|
||||
-- Contents of the Accept-Language: header from the current request, if there is one.
|
||||
-- Example: 'en'.
|
||||
deferred
|
||||
end
|
||||
|
||||
http_connection: detachable READABLE_STRING_32
|
||||
-- Contents of the Connection: header from the current request, if there is one.
|
||||
-- Example: 'Keep-Alive'.
|
||||
deferred
|
||||
end
|
||||
|
||||
http_host: detachable READABLE_STRING_32
|
||||
-- Contents of the Host: header from the current request, if there is one.
|
||||
deferred
|
||||
end
|
||||
|
||||
http_referer: detachable READABLE_STRING_32
|
||||
-- The address of the page (if any) which referred the user agent to the current page.
|
||||
-- This is set by the user agent.
|
||||
-- Not all user agents will set this, and some provide the ability to modify HTTP_REFERER as a feature.
|
||||
-- In short, it cannot really be trusted.
|
||||
deferred
|
||||
end
|
||||
|
||||
http_user_agent: detachable READABLE_STRING_32
|
||||
-- Contents of the User-Agent: header from the current request, if there is one.
|
||||
-- This is a string denoting the user agent being which is accessing the page.
|
||||
-- A typical example is: Mozilla/4.5 [en] (X11; U; Linux 2.2.9 i586).
|
||||
-- Among other things, you can use this value to tailor your page's
|
||||
-- output to the capabilities of the user agent.
|
||||
deferred
|
||||
end
|
||||
|
||||
http_authorization: detachable READABLE_STRING_32
|
||||
-- Contents of the Authorization: header from the current request, if there is one.
|
||||
deferred
|
||||
end
|
||||
|
||||
feature -- Extra CGI environment variables
|
||||
|
||||
request_uri: READABLE_STRING_32
|
||||
-- The URI which was given in order to access this page; for instance, '/index.html'.
|
||||
deferred
|
||||
end
|
||||
|
||||
orig_path_info: detachable READABLE_STRING_32
|
||||
-- Original version of path_info before processed by Current environment
|
||||
deferred
|
||||
end
|
||||
|
||||
feature -- Query string Parameters
|
||||
|
||||
query_parameters: ITERABLE [WGI_VALUE]
|
||||
-- Variables extracted from QUERY_STRING
|
||||
deferred
|
||||
end
|
||||
|
||||
query_parameter (a_name: READABLE_STRING_GENERAL): detachable WGI_VALUE
|
||||
-- Parameter for name `n'.
|
||||
require
|
||||
a_name_valid: a_name /= Void and then not a_name.is_empty
|
||||
deferred
|
||||
end
|
||||
|
||||
feature -- Form fields and related
|
||||
|
||||
form_data_parameters: ITERABLE [WGI_VALUE]
|
||||
-- Variables sent by POST request
|
||||
deferred
|
||||
end
|
||||
|
||||
form_data_parameter (a_name: READABLE_STRING_GENERAL): detachable WGI_VALUE
|
||||
-- Field for name `a_name'.
|
||||
require
|
||||
a_name_valid: a_name /= Void and then not a_name.is_empty
|
||||
deferred
|
||||
end
|
||||
|
||||
uploaded_files: HASH_TABLE [WGI_UPLOADED_FILE_DATA, READABLE_STRING_GENERAL]
|
||||
-- Table of uploaded files information
|
||||
deferred
|
||||
end
|
||||
|
||||
feature -- Cookies
|
||||
|
||||
cookies: ITERABLE [WGI_VALUE]
|
||||
-- Expanded cookies variable
|
||||
deferred
|
||||
end
|
||||
|
||||
cookie (a_name: READABLE_STRING_GENERAL): detachable WGI_VALUE
|
||||
-- Field for name `a_name'.
|
||||
require
|
||||
a_name_valid: a_name /= Void and then not a_name.is_empty
|
||||
deferred
|
||||
end
|
||||
|
||||
feature -- Access: all variables
|
||||
|
||||
items: ITERABLE [WGI_VALUE]
|
||||
-- Table containing all the various variables
|
||||
-- Warning: this is computed each time, if you change the content of other containers
|
||||
-- this won't update this Result's content, unless you query it again
|
||||
deferred
|
||||
end
|
||||
|
||||
item (a_name: READABLE_STRING_GENERAL): detachable WGI_VALUE
|
||||
-- Variable named `a_name' from any of the variables container
|
||||
-- and following a specific order
|
||||
-- execution, environment, get, post, cookies
|
||||
require
|
||||
a_name_valid: a_name /= Void and then not a_name.is_empty
|
||||
deferred
|
||||
end
|
||||
|
||||
feature -- Uploaded File Handling
|
||||
|
||||
is_uploaded_file (a_filename: READABLE_STRING_GENERAL): BOOLEAN
|
||||
-- Is `a_filename' a file uploaded via HTTP POST
|
||||
deferred
|
||||
end
|
||||
|
||||
feature -- URL Utility
|
||||
|
||||
absolute_script_url (a_path: STRING_8): STRING_8
|
||||
-- Absolute Url for the script if any, extended by `a_path'
|
||||
deferred
|
||||
end
|
||||
|
||||
script_url (a_path: STRING_8): STRING_8
|
||||
-- Url relative to script name if any, extended by `a_path'
|
||||
require
|
||||
a_path_attached: a_path /= Void
|
||||
deferred
|
||||
end
|
||||
|
||||
invariant
|
||||
server_name_not_empty: not server_name.is_empty
|
||||
server_port_set: server_port /= 0
|
||||
request_method_attached: request_method /= Void
|
||||
path_info_attached: path_info /= Void
|
||||
query_string_attached: query_string /= Void
|
||||
remote_addr_attached: remote_addr /= Void
|
||||
same_orig_path_info: orig_path_info ~ meta_string_variable ({WGI_META_NAMES}.orig_path_info)
|
||||
same_path_info: path_info ~ meta_string_variable ({WGI_META_NAMES}.path_info)
|
||||
path_info_identical: path_info ~ meta_string_variable ({WGI_META_NAMES}.path_info)
|
||||
end
|
||||
|
||||
WGI_RESPONSE_BUFFER
|
||||
|
||||
deferred class
|
||||
WGI_RESPONSE_BUFFER
|
||||
|
||||
feature {WGI_APPLICATION} -- Commit
|
||||
|
||||
commit
|
||||
-- Commit the current response
|
||||
deferred
|
||||
ensure
|
||||
status_is_set: status_is_set
|
||||
header_committed: header_committed
|
||||
message_committed: message_committed
|
||||
end
|
||||
|
||||
feature -- Status report
|
||||
|
||||
header_committed: BOOLEAN
|
||||
-- Header committed?
|
||||
deferred
|
||||
end
|
||||
|
||||
message_committed: BOOLEAN
|
||||
-- Message committed?
|
||||
deferred
|
||||
end
|
||||
|
||||
message_writable: BOOLEAN
|
||||
-- Can message be written?
|
||||
deferred
|
||||
end
|
||||
|
||||
feature {WGI_RESPONSE_BUFFER} -- Core output operation
|
||||
|
||||
write (s: READABLE_STRING_8)
|
||||
-- Send the string `s'
|
||||
-- this can be used for header and body
|
||||
deferred
|
||||
end
|
||||
|
||||
feature -- Status setting
|
||||
|
||||
status_is_set: BOOLEAN
|
||||
-- Is status set?
|
||||
deferred
|
||||
end
|
||||
|
||||
set_status_code (a_code: INTEGER)
|
||||
-- Set response status code
|
||||
-- Should be done before sending any data back to the client
|
||||
require
|
||||
status_not_set: not status_is_set
|
||||
header_not_committed: not header_committed
|
||||
deferred
|
||||
ensure
|
||||
status_code_set: status_code = a_code
|
||||
status_set: status_is_set
|
||||
end
|
||||
|
||||
status_code: INTEGER
|
||||
-- Response status
|
||||
deferred
|
||||
end
|
||||
|
||||
feature -- Header output operation
|
||||
|
||||
write_headers_string (a_headers: READABLE_STRING_8)
|
||||
require
|
||||
status_set: status_is_set
|
||||
header_not_committed: not header_committed
|
||||
deferred
|
||||
ensure
|
||||
status_set: status_is_set
|
||||
header_committed: header_committed
|
||||
message_writable: message_writable
|
||||
end
|
||||
|
||||
write_header (a_status_code: INTEGER; a_headers: detachable ARRAY [TUPLE [key: READABLE_STRING_8; value: READABLE_STRING_8]])
|
||||
-- Send headers with status `a_status', and headers from `a_headers'
|
||||
require
|
||||
status_not_set: not status_is_set
|
||||
header_not_committed: not header_committed
|
||||
deferred
|
||||
ensure
|
||||
header_committed: header_committed
|
||||
status_set: status_is_set
|
||||
message_writable: message_writable
|
||||
end
|
||||
|
||||
feature -- Output operation
|
||||
|
||||
write_string (s: READABLE_STRING_8)
|
||||
-- Send the string `s'
|
||||
require
|
||||
message_writable: message_writable
|
||||
deferred
|
||||
end
|
||||
|
||||
write_substring (s: READABLE_STRING_8; a_begin_index, a_end_index: INTEGER)
|
||||
-- Send the substring `s[a_begin_index:a_end_index]'
|
||||
require
|
||||
message_writable: message_writable
|
||||
deferred
|
||||
end
|
||||
|
||||
write_file_content (fn: READABLE_STRING_8)
|
||||
-- Send the content of file `fn'
|
||||
require
|
||||
message_writable: message_writable
|
||||
deferred
|
||||
end
|
||||
|
||||
flush
|
||||
-- Flush if it makes sense
|
||||
deferred
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
## Proof-of-concept and reference implementation
|
||||
|
||||
# Specification overview
|
||||
|
||||
## The Server/Gateway Side
|
||||
|
||||
## The Application/Framework Side
|
||||
|
||||
## Specification Details
|
||||
|
||||
## Implementation/Application Notes
|
||||
|
||||
## Questions and Answers
|
||||
|
||||
## Proposed/Under Discussion
|
||||
|
||||
## Acknowledgements
|
||||
|
||||
## References
|
||||
@@ -1,20 +0,0 @@
|
||||
---
|
||||
layout: default
|
||||
title: Libraries
|
||||
base_url: ../../
|
||||
---
|
||||
## libraries currently part of the Eiffel Web Framework ##
|
||||
* [[Library-EWSGI]]: Eiffel Web Server Gateway Interface (prefix WGI_ for **W**eb**G**ateway**I**nterface)
|
||||
* [[Library-libFCGI]]: Eiffel wrapper of libfcgi SDK (http://www.fastcgi.com/devkit/libfcgi/)
|
||||
* [[Library-encoder]]: simple encoder for base64, url-encode, xml entities
|
||||
* [[Library-error]]: simple framework to handle error in EWSGI and related
|
||||
* [[Library-http]]: utility classes to handle HTTP protocol, status, ...
|
||||
* [[Library-uri-template]]: URI Template parser and expander (follow draft spec 0.5)
|
||||
|
||||
## libraries needed but not (yet) in the current framework ##
|
||||
* [[Library-EiffelWebNino]]: Eiffel Web Server written in Eiffel
|
||||
* [[Library-JSON]]: JSON format parser and converter
|
||||
|
||||
## libraries that could be included, or or used ##
|
||||
* eMIME: MIME parser, and encoder ...
|
||||
* ...
|
||||
@@ -1,31 +0,0 @@
|
||||
---
|
||||
layout: default
|
||||
title: README
|
||||
base_url: ../../
|
||||
---
|
||||
# Eiffel-Web-Framework #
|
||||
|
||||
## Location ##
|
||||
The official documentation/wiki is located at [https://github.com/EiffelWebFramework/EWF/wiki](https://github.com/EiffelWebFramework/EWF/wiki) , if you are visiting a "clone/fork", please always check the [official wiki](https://github.com/EiffelWebFramework/EWF/wiki).
|
||||
|
||||
## Organization ##
|
||||
- Mailing list: please visit and subscribe to the mailing list page [http://groups.google.com/group/eiffel-web-framework](http://groups.google.com/group/eiffel-web-framework) 
|
||||
- Most of the topics are discussed on the mailing list (google group).
|
||||
- For time to time we have [web meetings](project/Meetings.md), and less frequently [physical meetings](project/Meetings.md) that occurs usually during other Eiffel related events.
|
||||
|
||||
## Documentation ##
|
||||
- [Documentation](documentation/README.md)
|
||||
|
||||
## Contributions ##
|
||||
- You want to contribute or follow the progress/discussion, see the [collaboration page](community.md)
|
||||
- Potential tasks/projects on EWF: [Projects page](project/Projects.md)
|
||||
|
||||
## See also ##
|
||||
- [list of tasks, and a potential roadmap](project/Tasks-Roadmap.md)
|
||||
- [General source structure of this project](Source-structure.md)
|
||||
- EWSGI: [Eiffel Web Server Gateway Interface](EWSGI/README.md)
|
||||
- [Overview of the server side architecture](Spec-Server-Architecture.md)
|
||||
- This project is also a collection of [Libraries](Libraries.md) related to the Web
|
||||
|
||||
## Note ##
|
||||
- This wiki needs to be updated, in the meantime, please have a look at the presentation: [https://docs.google.com/presentation/pub?id=1GPFv6aHhTjFSLMnlAt-J4WeIHSGfHdB42dQxmOVOH8s&start=false&loop=false&delayms=3000](https://docs.google.com/presentation/pub?id=1GPFv6aHhTjFSLMnlAt-J4WeIHSGfHdB42dQxmOVOH8s&start=false&loop=false&delayms=3000)
|
||||
@@ -1,33 +0,0 @@
|
||||
---
|
||||
layout: default
|
||||
title: Source structure
|
||||
base_url: ../../
|
||||
---
|
||||
## Currently ##
|
||||
|
||||
- LICENSE : file containing the global license
|
||||
- README : quick README to point to the github project
|
||||
- doc/
|
||||
- doc/wiki : clone of the associated github wiki repository
|
||||
- examples/
|
||||
- tests/
|
||||
- library/
|
||||
- library/server/ewsgi/ewsgi.ecf
|
||||
- library/server/ewsgi/ewsgi.ecf
|
||||
|
||||
Any library/component (in development mode) should follow the following structure (when not needed, there is no need to create the associated folder(s) ):
|
||||
|
||||
- **README**
|
||||
- **COPYRIGHT**
|
||||
- **LICENSE**
|
||||
- **.ecf** and **-safe.ecf** : configuration file
|
||||
- **library/** : the place to put the source code of the related library/component
|
||||
- **doc/** : notes, documentations, ...
|
||||
- **tests/** : standard place to put your tests, if you are using Eiffel Software auto-tests, I would suggest to add a new target into the associated .ecf (instead of building a new .ecf under test/ )
|
||||
- **examples/** : standard place to put the example
|
||||
- **build/** : a convenient place to compile your project, using this convention, you can setup utilities such as backup to ignore this folder.
|
||||
- **resources/** : contains pixmap files, ....
|
||||
- **install/** : contains installation scripts for each platform
|
||||
- **data/** : contains eventual data for the related tools
|
||||
|
||||
See that we use the singular, since it is an Eiffel convention for naming cluster or folder.
|
||||
@@ -1,8 +0,0 @@
|
||||
---
|
||||
layout: default
|
||||
title: Spec server architecture
|
||||
base_url: ../../
|
||||
---
|
||||
## Diagram: Overview of the server architecture ##
|
||||
|
||||

|
||||
@@ -1,30 +0,0 @@
|
||||
---
|
||||
layout: default
|
||||
title: community
|
||||
base_url: ../../
|
||||
---
|
||||
This project is a community project
|
||||
|
||||
## Mailing list ##
|
||||
- Google group: [http://groups.google.com/group/eiffel-web-framework](http://groups.google.com/group/eiffel-web-framework)
|
||||
|
||||
## Materials ##
|
||||
- wiki: github wiki at [https://github.com/Eiffel-World/Eiffel-Web-Framework/wiki](https://github.com/Eiffel-World/Eiffel-Web-Framework/wiki)
|
||||
- Shared documents: on google docs at [http://goo.gl/M8WLP](http://goo.gl/M8WLP)
|
||||
- source code: git repository at [https://github.com/Eiffel-World/Eiffel-Web-Framework](https://github.com/Eiffel-World/Eiffel-Web-Framework)
|
||||
- Proposal from Paul Cohen for a EWSGI spec at [http://eiffel.seibostudios.se/wiki/EWSGI](http://eiffel.seibostudios.se/wiki/EWSGI)
|
||||
|
||||
## Main contributors ##
|
||||
- **jfiat**: Jocelyn Fiat (Eiffel Software)
|
||||
- **jvelilla**: Hector Javier Velilla (Seibo Software Studios)
|
||||
- **paco**: Paul Cohen (Seibo Software Studios)
|
||||
- **daro**: Daniel Rodriguez (Seibo Software Studios)
|
||||
- Olivier Ligot (Groupe-S)
|
||||
- Paul G.Crismer (Groupe-S)
|
||||
- Berend de Boer (XplainHosting)
|
||||
- Colin Adams (AXA R.)
|
||||
- Alexander Kogtenkov (Eiffel Software)
|
||||
|
||||
## You want to participate ##
|
||||
- You are welcome to contribute (code, test, doc, code review, feedback, suggestion, spread the words ...)
|
||||
- Feel free to subscribe to the mailing list
|
||||
@@ -1,56 +0,0 @@
|
||||
---
|
||||
layout: default
|
||||
title: Connectors
|
||||
base_url: ../../
|
||||
---
|
||||
The main goal of the connectors is to let you choose a target at compile time.
|
||||
This allows you to concentrate on your business during development time and then decide which target you choose at deployment time.
|
||||
The current connectors are:
|
||||
* Nino
|
||||
* FastCGI
|
||||
* CGI
|
||||
* OpenShift
|
||||
|
||||
The most widely used workflow is to use Nino on your development machine and FastCGI on your production server.
|
||||
Nino being a web server written entirely in Eiffel, you can inspect your HTTP requests and respones in EiffelStudio which is great during development.
|
||||
On the other hand, FastCGI is great at handling concurrent requests and coupled with Apache (or another web production server), you don't even need to worry about the lifecyle of your application (creation and destruction) as Apache will do it for you!
|
||||
|
||||
Let's now dig into each of the connecters.
|
||||
|
||||
# Nino
|
||||
|
||||
Nino is a web server entirely written in Eiffel.
|
||||
The goal of Nino is to provide a simple web server for development (like Java, Python and Ruby provide).
|
||||
Nino is currently maintained by Javier Velilla and the repository can be found here: https://github.com/jvelilla/EiffelWebNino
|
||||
|
||||
# FastCGI
|
||||
|
||||
FastCGI is a protocol for interfacing an application server with a web server.
|
||||
It is an improvement over CGI as FastCGI supports long running processes, i.e. processes than can handle multipe requests during their lifecyle. CGI, on the other hand, launches a new process for every new request which is quite time consuming.
|
||||
FastCGI is implemented by every major web servers: Apache, IIS, Nginx, ...
|
||||
We recommend to use FastCGI instead of CGI as it is way more faster.
|
||||
You can read more about FastCGI here: http://www.fastcgi.com/
|
||||
|
||||
# CGI
|
||||
|
||||
CGI predates FastCGI and is also a protocol for interfacing an application server with a web server.
|
||||
His main drawback (and the reason why FastCGI was created) is that it launches a new process for every new request, which is quite time consuming.
|
||||
We recommend to use FastCGI instead of CGI as it is way more faster.
|
||||
|
||||
# OpenShift
|
||||
|
||||
OpenShift is a cloud computing platform as a service product from Red Hat.
|
||||
It basically let's you run your application in the cloud.
|
||||
More informations are available here: https://www.openshift.com
|
||||
|
||||
# Writing your own
|
||||
|
||||
It's fairly easy to write your own connector. Just inherit from these classes:
|
||||
* WGI_CONNECTOR
|
||||
* WGI_ERROR_STREAM
|
||||
* WGI_INPUT_STREAM
|
||||
* WGI_OUTPUT_STREAM
|
||||
* WSF_SERVICE_LAUNCHER
|
||||
|
||||
|
||||
See WSF_CONNECTOR
|
||||
@@ -1,32 +0,0 @@
|
||||
---
|
||||
layout: default
|
||||
title: Filter
|
||||
base_url: ../../
|
||||
---
|
||||
# Introduction
|
||||
|
||||
The basic idea of a filter is to pre-process incoming data and post-process outgoing data.
|
||||
Filters are part of a filter chain, thus following the [chain of responsability design pattern](http://en.wikipedia.org/wiki/Chain-of-responsibility_pattern).
|
||||
|
||||
Each filter decides to call the next filter or not.
|
||||
|
||||
# Levels
|
||||
|
||||
In EWF, there are two levels of filters.
|
||||
|
||||
## WSF_FILTER
|
||||
|
||||
Typical examples of such filters are: logging, compression, routing (WSF_ROUTING_FILTER), ...
|
||||
|
||||
## WSF_FILTER_HANDLER
|
||||
|
||||
Handler that can also play the role of a filter.
|
||||
|
||||
Typical examples of such filters are: authentication, ...
|
||||
|
||||
# References
|
||||
|
||||
Filters (also called middelwares) in other environments:
|
||||
* in Python: http://www.wsgi.org/en/latest/libraries.html
|
||||
* in Node.js: http://expressjs.com/guide.html#middleware
|
||||
* in Apache: http://httpd.apache.org/docs/2.2/en/filter.html
|
||||
@@ -1,26 +0,0 @@
|
||||
---
|
||||
layout: default
|
||||
title: HTTP client library
|
||||
base_url: ../../
|
||||
---
|
||||
# HTTP Library Features
|
||||
The following list of features are taken form the book [RESTful Web Services](http://www.amazon.com/Restful-Web-Services-Leonard-Richardson/dp/0596529260/ref=sr_1_1?ie=UTF8&qid=1322155984&sr=8-1)
|
||||
|
||||
* **HTTPS**: _It must support HTTPS and SSL certificate validation_
|
||||
|
||||
* **HTTP methods**: _It must support at least the five main HTTP methods: GET, HEAD, POST, PUT, and DELETE. Optional methods
|
||||
OPTIONS and TRACE, and WebDAV extensions like MOVE, ._
|
||||
* **Custom data** : _It must allow the programmer to customize the data sent as the entity-body of a
|
||||
PUT or POST request._
|
||||
* **Custom headers** : _It must allow the programmer to customize a request’s HTTP headers_
|
||||
* **Response Codes** : _It must give the programmer access to the response code and headers of an HTTP
|
||||
response; not just access to the entity-body._
|
||||
* **Proxies**: _It must be able to communicate through an HTTP proxy_
|
||||
|
||||
* **Compression**:_it should automatically request data in compressed form to save
|
||||
bandwidth, and transparently decompress the data it receives._
|
||||
* **Caching**:_It should automatically cache the responses to your requests._
|
||||
* **Auth methods** : _It should transparently support the most common forms of HTTP authentication:
|
||||
Basic, Digest, and WSSE._
|
||||
* **Cookies** :_It should be able to parse and create HTTP cookie strings_
|
||||
* **Redirects**:_It should be able to transparently follow HTTP redirects_
|
||||
@@ -1,193 +0,0 @@
|
||||
---
|
||||
layout: default
|
||||
title: Library conneg
|
||||
base_url: ../../
|
||||
---
|
||||
# Server-driven content negotiation
|
||||
|
||||
EWF supports server-driven content content negotiation, as defined in [HTTP/1.1 Content Negotiation](http://www.w3.org/Protocols/rfc2616/rfc2616-sec12.html#sec12.1) . To enable this facility:
|
||||
|
||||
1. Add ${EWF}/library/network/protocol/conneg/conneg.ecf to your system ECF.
|
||||
1. In the class where your handlers reside, add an attribute `conneg: CONNEG_SERVER_SIDE`, and ensure it is always attached (create in the creation procedure, or make it a once, or add an attribute body). An example creation call is: `create conneg.make ({HTTP_MIME_TYPES}.application_json, "en", "UTF-8", "")`.
|
||||
|
||||
That call defines our defaults for media-type, language, charset and encoding, respectively. The encoding could also be written as `"identity"`. It means no compression. As an alternative, we might code `"gzip"`.
|
||||
|
||||
The user agent (a web browser, for example. or the curl program), can request different representations by using headers. For example, `Accept: application/json; q=0.2, application/xml` says the client would be very happy to get back an XML representation (if you omit the q for quality parameter, it defaults to 1, which is best), but (s)he will tolerate JSON. Clearly, we are going to be able to satisfy that client, as we serve JSON by default. But what if the client had requested `Accept: application/xml;q=0.8, text/html`? In this example, we are going to serve both JSON and XML representations upon request. A client who requests `Accept: text/html, text/plain` is going to be disappointed. For the other aspects (language, charset and encoding), we are not going to offer any choices. That does not mean we ignore the client's headers for these aspects. We are going to check if our representation is acceptable to the client, and if not, return a 406 Not Acceptable response (an alternative is to send our representation anyway, and let the user decide whether or not to use it).
|
||||
|
||||
Next, we need to declare all the representations we support:
|
||||
|
||||
mime_types_supported: LINKED_LIST [STRING] is
|
||||
-- Media types `Current' supports
|
||||
once
|
||||
create Result.make
|
||||
Result.put_front ({HTTP_MIME_TYPES}.application_xml)
|
||||
Result.put_front ({HTTP_MIME_TYPES}.application_json)
|
||||
ensure
|
||||
mime_types_supported_not_void: Result /= Void
|
||||
no_void_entry: not Result.has (Void)
|
||||
end
|
||||
|
||||
charsets_supported: LINKED_LIST [STRING] is
|
||||
-- Character sets `Current' supports
|
||||
once
|
||||
create Result.make
|
||||
Result.put_front ("UTF-8")
|
||||
ensure
|
||||
charsets_supported_not_void: Result /= Void
|
||||
no_void_entry: not Result.has (Void)
|
||||
end
|
||||
|
||||
encodings_supported: LINKED_LIST [STRING] is
|
||||
-- Encodings `Current' supports
|
||||
once
|
||||
create Result.make
|
||||
Result.put_front ("identity")
|
||||
Result.put_front ("") -- identity encoding
|
||||
ensure
|
||||
encoding_supported_not_void: Result /= Void
|
||||
no_void_entry: not Result.has (Void)
|
||||
end
|
||||
|
||||
languages_supported: LINKED_LIST [STRING] is
|
||||
-- Languages `Current' supports
|
||||
once
|
||||
create Result.make
|
||||
Result.put_front ("en")
|
||||
ensure
|
||||
languages_supported_not_void: Result /= Void
|
||||
no_void_entry: not Result.has (Void)
|
||||
end
|
||||
|
||||
Now we are in a position to do some negotiating. At the beginning of your handler(s), code:
|
||||
|
||||
|
||||
local
|
||||
l_media_variants: MEDIA_TYPE_VARIANT_RESULTS
|
||||
l_is_head: BOOLEAN
|
||||
h: HTTP_HEADER
|
||||
l_msg: STRING
|
||||
do
|
||||
l_is_head := False -- or `True' if this is for a HEAD handler
|
||||
l_media_variants:= conneg.media_type_preference (mime_types_supported, a_req.http_accept)
|
||||
if not l_media_variants.is_acceptable then
|
||||
send_unacceptable_media_type (a_res, l_is_head)
|
||||
elseif not conneg.charset_preference (charsets_supported, a_req.http_accept_charset).is_acceptable then
|
||||
send_unacceptable_charset (a_res, l_is_head)
|
||||
elseif not conneg.encoding_preference (encodings_supported, a_req.http_accept_encoding).is_acceptable then
|
||||
send_unacceptable_encoding (a_res, l_is_head)
|
||||
elseif not conneg.language_preference (languages_supported, a_req.http_accept_language).is_acceptable then
|
||||
send_unacceptable_encoding (a_res)
|
||||
else
|
||||
-- We have agreed a representation, let's go and serve it to the client
|
||||
`
|
||||
|
||||
Now for those `send_unnacceptable_...` routines. They are fairly simple:
|
||||
|
||||
send_unacceptable_media_type (a_res: WSF_RESPONSE; a_is_head: BOOLEAN) is
|
||||
-- Send error result as text/plain that the media type is unnacceptable.
|
||||
require
|
||||
a_res_not_void: a_res /= Void
|
||||
status_not_set: not a_res.status_is_set
|
||||
header_not_committed: not a_res.header_committed
|
||||
local
|
||||
l_error_text: STRING
|
||||
do
|
||||
l_error_text := "The requested media type(s) is/are not supported by this server."
|
||||
send_unacceptable (a_res, a_is_head, l_error_text)
|
||||
end
|
||||
|
||||
send_unacceptable_charset (a_res: WSF_RESPONSE; a_is_head: BOOLEAN) is
|
||||
-- Send error result as text/plain that the character set is unnacceptable.
|
||||
require
|
||||
a_res_not_void: a_res /= Void
|
||||
status_not_set: not a_res.status_is_set
|
||||
header_not_committed: not a_res.header_committed
|
||||
local
|
||||
l_error_text: STRING
|
||||
do
|
||||
l_error_text := "The requested character set(s) is/are not supported by this server. Only UTF-8 is supported."
|
||||
send_unacceptable (a_res, a_is_head, l_error_text)
|
||||
end
|
||||
|
||||
send_unacceptable_encoding (a_res: WSF_RESPONSE; a_is_head: BOOLEAN) is
|
||||
-- Send error result as text/plain that the encoding is unnacceptable.
|
||||
require
|
||||
a_res_not_void: a_res /= Void
|
||||
status_not_set: not a_res.status_is_set
|
||||
header_not_committed: not a_res.header_committed
|
||||
local
|
||||
l_error_text: STRING
|
||||
do
|
||||
l_error_text := "The requested encoding(s) is/are not supported by this server. Only identity is supported."
|
||||
send_unacceptable (a_res, a_is_head, l_error_text)
|
||||
end
|
||||
|
||||
send_unacceptable_language (a_res: WSF_RESPONSE; a_is_head: BOOLEAN) is
|
||||
-- Send error result as text/plain that the language unnacceptable.
|
||||
require
|
||||
a_res_not_void: a_res /= Void
|
||||
status_not_set: not a_res.status_is_set
|
||||
header_not_committed: not a_res.header_committed
|
||||
local
|
||||
l_error_text: STRING
|
||||
do
|
||||
l_error_text := "The requested language(s) is/are not supported by this server. Only en (English) is supported."
|
||||
send_unacceptable (a_res, a_is_head, l_error_text)
|
||||
end
|
||||
|
||||
send_unacceptable (a_res: WSF_RESPONSE; a_is_head: BOOLEAN; a_error_text: STRING) is
|
||||
-- Send a_error_text as text/plain that a header is unnacceptable.
|
||||
require
|
||||
a_res_not_void: a_res /= Void
|
||||
status_not_set: not a_res.status_is_set
|
||||
header_not_committed: not a_res.header_committed
|
||||
a_error_text_not_void: a_error_text /= Void
|
||||
local
|
||||
h: HTTP_HEADER
|
||||
do
|
||||
create h.make
|
||||
set_content_type (h, Void)
|
||||
h.put_content_length (a_error_text.count)
|
||||
h.put_current_date
|
||||
a_res.set_status_code ({HTTP_STATUS_CODE}.not_acceptable)
|
||||
a_res.put_header_text (h.string)
|
||||
if not a_is_head then
|
||||
a_res.put_string (a_error_text)
|
||||
end
|
||||
end
|
||||
|
||||
We'll see that `set_content_type` routine in a bit. But for now, we just have to generate the response. Let's go back to that `else ...` bit:
|
||||
|
||||
else
|
||||
-- We have agreed a representation, let's go and serve it to the client
|
||||
create h.make
|
||||
set_content_type (h, a_media_variants)
|
||||
if a_media_variants.media_type ~ {HTTP_MIME_TYPES}.application_xml then
|
||||
l_msg := "<?xml version='1.0' encoding='UTF-8' ?><my-tag>etc."
|
||||
else
|
||||
l_msg := json.value (<some-value>).representation
|
||||
end
|
||||
h.put_content_length (l_msg.count)
|
||||
h.put_current_date
|
||||
a_res.set_status_code ({HTTP_STATUS_CODE}.ok)
|
||||
a_res.put_header_text (h.string)
|
||||
if not a_is_head then
|
||||
a_res.put_string (l_msg)
|
||||
end
|
||||
|
||||
There's that `set_content_type` again. Finally, we will take a look at it:
|
||||
|
||||
set_content_type (a_h: HTTP_HEADER; a_media_variants: MEDIA_TYPE_VARIANT_RESULTS) is
|
||||
-- Set the content=type header in `a_h' according to `a_media_variants'.
|
||||
require
|
||||
a_h_not_void: a_h /= Void
|
||||
do
|
||||
if a_media_variants = Void or else not a_media_variants.is_acceptable then
|
||||
a_h.put_content_type ({HTTP_MIME_TYPES}.text_plain)
|
||||
else
|
||||
a_h.put_content_type (a_media_variants.media_type)
|
||||
end
|
||||
a_h.put_header_key_value ({HTTP_HEADER_NAMES}.header_vary, {HTTP_HEADER_NAMES}.header_accept)
|
||||
end
|
||||
|
||||
Firstly, if we haven't agreed a media-type, then we send our (negative) response as `plain/text`. Otherwise we will send the response in the agreed media-type. But in each case we add a `Vary:Accept` header. This tells proxy caches that they have to check the media-type before returning a cached result, as there may be a different representation available. Since we do not vary our representation by language, charset or encoding, we don't add `Vary:Accept-Language,Accept-Charset,Accept-Encoding`. But if we were to negotiate different representations on those dimensions also, we would need to list the appropriate headers in the `Vary` header.
|
||||
@@ -1,215 +0,0 @@
|
||||
---
|
||||
layout: default
|
||||
title: README
|
||||
base_url: ../../
|
||||
---
|
||||
# Current Status
|
||||
* Official repository: <https://github.com/EiffelWebFramework/EWF>
|
||||
* Official website: <http://eiffelwebframework.github.io/EWF/getting-started/>
|
||||
|
||||
# What is EWF?
|
||||
|
||||
Eiffel Web Framework, is mainly a collection of Eiffel libraries designed to be integrated with each other. One benefit is that it supports all core HTTP features, so enable you embrace HTTP as an application protocol to develop web applications. So you do not need to adapt your applications to the web, instead you use the web power. It means you can build different kind of web applications, from Web APIs following the Hypermedia API style (REST style), CRUD web services or just conventional web applications building a session on top of an stateless protocol.
|
||||
|
||||
# EWF core/kernel
|
||||
> The Web Server Foundation (WSF\_) is the core of the framework. It is compliant with the EWSGI interface (WGI\_).
|
||||
|
||||
To build a web [service](#service), the framework provides a set of core components to launch the service, for each [request](#request-and-response), access the data, and send the [response](#request-and-response).
|
||||
The framework also provides a router component to help dispatching the incoming request.
|
||||
|
||||
A service can be a web api, a web interface, … what ever run on top of HTTP.
|
||||
|
||||
<a name="wiki-service"></a>
|
||||
<a name="service"></a>
|
||||
# Service
|
||||
> see interface: **WSF_SERVICE**
|
||||
|
||||
Each incoming http request is processed by the following routine.
|
||||
|
||||
> `{WSF_SERVICE}.execute (req: WSF_REQUEST; res: WSF_RESPONSE)`
|
||||
|
||||
This is the low level of the framework, at this point, `req` provides access to the query and form parameters, input data, headers, ... as specified by the Common Gateway Interface (CGI).
|
||||
The response `res` is the interface to send data back to the client.
|
||||
For convenience, the framework provides richer service interface that handles the most common needs (filter, router, ...).
|
||||
|
||||
> [Learn more about service](Service.md)
|
||||
|
||||
<a name="wiki-request"></a><a name="wiki-response"></a><a name="wiki-request-and-response"></a>
|
||||
<a name="request"></a><a name="response"></a><a name="request-and-response"></a>
|
||||
# Request and Response
|
||||
> see interface: **WSF_REQUEST** and **WSF_RESPONSE**
|
||||
|
||||
Any incoming http request is represented by an new object of type **WSF_REQUEST**.
|
||||
|
||||
**WSF_REQUEST** provides access to
|
||||
+ __meta variables__: CGI variables (coming from the request http header)
|
||||
+ __query parameters__: from the uri ex: `?q=abc&type=pdf`
|
||||
+ __input data__: the message of the request, if this is a web form, this is parsed to build the form parameters. It can be retrieved once.
|
||||
+ __form parameters__: standard parameters from the request input data.
|
||||
- typically available when a web form is sent using POST as content of type `multipart/form-data` or `application/x-www-form-urlencoded`
|
||||
- (advanced usage: it is possible to write mime handler that can processed other type of content, even custom format.)
|
||||
+ __uploaded files__: if files are uploaded, their value will be available from the form parameters, and from the uploaded files as well.
|
||||
+ __cookies variable__: cookies extracted from the http header.
|
||||
+ __path parameters__: note this is related to the router and carry the semantic of the mapping (see the section on router )
|
||||
+ __execution variables__: used by the application to keep value associated with the request.
|
||||
|
||||
The **WSF_RESPONSE** represents the communication toward the client, a service need to provide correct headers, and content. For instance the `Content-Type`, and `Content-Length`. It also allows to send data with chunked encoding.
|
||||
|
||||
> [Learn more about request](Request.md) and [about response](Response.md)
|
||||
|
||||
<a name="wiki-connector"></a>
|
||||
<a name="connector"></a>
|
||||
# Connectors:
|
||||
> see **WGI_CONNECTOR**
|
||||
|
||||
Using EWF, your service is built on top of underlying httpd solution/connectors.
|
||||
Currently 3 main connectors are available:
|
||||
* __CGI__: following the CGI interface, this is an easy solution to run the service on any platform.
|
||||
* __libFCGI__: based on the libfcgi solution, this can be used with Apache, IIS, nginx, ...
|
||||
* __nino__: a standalone server: Eiffel Web Nino allow you to embed a web server anywhere, on any platform without any dependencies on other httpd server.
|
||||
|
||||
At compilation time, you can use a default connector (by using the associated default lib), but you can also use a mixed of them and choose which one to execute at runtime.
|
||||
It is fairly easy to add new connector, it just has to follow the EWSGI interface
|
||||
|
||||
> [Learn more about connector](Connector.md)
|
||||
|
||||
<a name="wiki-router"></a>
|
||||
<a name="router"></a>
|
||||
# Router or Request Dispatcher:
|
||||
> Routes HTTP requests to the proper execution code
|
||||
|
||||
A web application needs to have a clean and elegant URL scheme, and EWF provides a router component to design URLs.
|
||||
|
||||
The association between a URL pattern and the code handling the URL request is called a Router mapping in EWF.
|
||||
|
||||
EWF provides 3 main kinds of mappings
|
||||
+ __URI__: any URL with path being the specified uri.
|
||||
- example: “/users/” redirects any “/users/” and “/users/?query=...”
|
||||
+ __URI-template__: any URL matching the specified URI-template
|
||||
- example: “/project/{name}/” redirects any “/project/foo” or “/project/bar”
|
||||
+ __Starts-with__: any URL starting with the specified path
|
||||
|
||||
Note: in the future, a Regular-Expression based kind will be added in the future, and it is possible to use custom mapping on top of EWF.
|
||||
|
||||
Code:
|
||||
|
||||
router.map ( create {WSF_URI_TEMPLATE_MAPPING}.make (
|
||||
“/project/{name}”, project_handler)
|
||||
)
|
||||
-- And precising the request methods
|
||||
router.map_with_request_methods ( ... , router.methods_GET_POST)
|
||||
|
||||
In the previous code, the `project_handler` is an object conforming to **WSF_HANDLER**, that will process the incoming requests matching URI-template “/project/{name}”.
|
||||
|
||||
Usually, the service will inherit from WSF_ROUTED_SERVICE, which has a `router` attribute.
|
||||
Configuring the URL scheme is done by implementing `{WSF_ROUTED_SERVICE}.setup_router`.
|
||||
|
||||
To make life easier, by inheriting from WSF_URI_TEMPLATE_HELPER_FOR_ROUTED_SERVICE, a few help methods are available to `map` URI template with agent, and so on.
|
||||
See
|
||||
+ `map_uri_template (a_tpl: STRING; h: WSF_URI_TEMPLATE_HANDLER)`
|
||||
+ `map_uri_template_agent (a_tpl: READABLE_STRING_8; proc: PROCEDURE [ANY, TUPLE [req: WSF_REQUEST; res: WSF_RESPONSE]])`
|
||||
+ and same with request methods ...
|
||||
|
||||
...
|
||||
|
||||
Check WSF_\*_HELPER_FOR_ROUTED_SERVICE for other available helper classes.
|
||||
|
||||
How we do that in EWF? : Router with (or without context).
|
||||
Related code: wsf_router, wsf_router_context
|
||||
Examples
|
||||
|
||||
> [Learn more about router](Router.md)
|
||||
|
||||
# EWF components
|
||||
## URI Handler:
|
||||
> Parses the details of the URI (scheme, path, query info, etc.) and exposes them for use.
|
||||
|
||||
How we do that in EWF?: URI Templates, but we could also use regex.
|
||||
Related code: uri_template
|
||||
Examples:
|
||||
|
||||
|
||||
## Mime Parser/ Content Negotiation:
|
||||
> Handles the details of determining the media type, language, encoding, compression (conneg).
|
||||
|
||||
How do we do that in EWF? Content_Negotiation library.
|
||||
Example
|
||||
|
||||
|
||||
## Request Handler
|
||||
> target of request dispatcher + uri handler.
|
||||
|
||||
Here is where we handle GET, POST PUT, etc.
|
||||
|
||||
## Representation Mapping
|
||||
> Converts stored data into the proper representation for responses and handles incoming representations from requests.
|
||||
|
||||
We don’t have a representation library, the developer need to do that.
|
||||
If we want to provide different kind of representations: JSON, XML, HTML, the responsibility is let
|
||||
to the developer to map their domain to the target representation.
|
||||
|
||||
## Http Client:
|
||||
> A simple library to make requests and handle responses from other http servers.
|
||||
|
||||
How we do that in EWF? http client library
|
||||
examples:
|
||||
|
||||
## Authentication/Security:
|
||||
> Handle different auth models. (Basic, Digest?, OAuth, OpenId)
|
||||
|
||||
How we do that in EWF? http_authorization, OpenId, and Cypress
|
||||
examples.
|
||||
|
||||
## Caching:
|
||||
> Support for Caching and conditional request
|
||||
|
||||
How we do that in Eiffel? Policy framework on top of EWF. {{{need_review}}}
|
||||
examples
|
||||
|
||||
|
||||
## EWF HTML5 Widgets
|
||||
|
||||
## EWF policy Framework
|
||||
|
||||
## EWF application generators
|
||||
|
||||
|
||||
<a name="wiki-EWSGI"></a>
|
||||
<a name="EWSGI"></a>
|
||||
# EWSGI Specification
|
||||
|
||||
<a name="wiki-libraries"></a>
|
||||
<a name="libraries"></a>
|
||||
# Libraries
|
||||
|
||||
External libraries are included, such as Cypress OAuth (Security), HTML parsing library, Template Engine Smarty.
|
||||
|
||||
## server
|
||||
* __ewsgi__: Eiffel Web Server Gateway Interface [read more](../EWSGI/index.md).
|
||||
* connectors: various web server connectors for EWSGI
|
||||
* __libfcgi__: Wrapper for libfcgi SDK
|
||||
* __wsf__: Web Server Framework
|
||||
* __router__: URL dispatching/routing based on uri, uri_template, or custom [read more](Router.md).
|
||||
* __filter__: Filter chain [read more](Filter.md).
|
||||
* __wsf_html__: (html and css) Content generator from the server side.
|
||||
* CMS example: <https://github.com/EiffelWebFramework/cms/tree/master/example>
|
||||
|
||||
## protocol
|
||||
* __http__: HTTP related classes, constants for status code, content types, ...
|
||||
* __uri_template__: URI Template library (parsing and expander)
|
||||
* __content_negotiation__: [CONNEG](Library-conneg.md) library (Content-type Negociation)
|
||||
|
||||
## Client
|
||||
* __http_client__: simple [HTTP client](HTTP-client.library.md) based on cURL
|
||||
* __Firebase API__: <https://github.com/EiffelWebFramework/Redwood>
|
||||
|
||||
## Text
|
||||
* __encoder__: Various simple encoders: base64, url-encoder, xml entities, html entities
|
||||
|
||||
## Utils
|
||||
* __error__: very simple/basic library to handle error
|
||||
|
||||
## Security
|
||||
* __http_authentication__ (under EWF/library/server/authentication)
|
||||
* __open_id__ (under EWF/library/security)
|
||||
* __OAuth__ see <https://github.com/EiffelWebFramework/cypress>
|
||||
@@ -1,14 +0,0 @@
|
||||
---
|
||||
layout: default
|
||||
title: Request and response
|
||||
base_url: ../../
|
||||
---
|
||||
# Request
|
||||
The class _WSF_REQUEST_ can be used to access data related to the HTTP request.
|
||||
|
||||
**TODO**: describe the request interface
|
||||
|
||||
# Response
|
||||
The class _WSF_RESPONSE_ is the media to send data back to the client.
|
||||
|
||||
**TODO**: describe the response interface
|
||||
@@ -1,22 +0,0 @@
|
||||
---
|
||||
layout: default
|
||||
title: Request
|
||||
base_url: ../../
|
||||
---
|
||||
See WSF_REQUEST
|
||||
|
||||
## About parameters
|
||||
Note that by default there is a smart computation for the query/post/... parameters:
|
||||
for instance
|
||||
- `q=a&q=b` : will create a **WSF_MULTIPLE_STRING** parameter with name **q** and value `[a,b]`
|
||||
- `tab[a]=ewf&tab[b]=demo` : will create a **WSF_TABLE** parameter with name **tab** and value `{ "a": "ewf", "b": "demo"}`
|
||||
- `tab[]=ewf&tab[]=demo` : will create a **WSF_TABLE** parameter with name **tab** and value `{ "1": "ewf", "2": "demo"}`
|
||||
- `tab[foo]=foo&tab[foo]=bar` : will create a **WSF_TABLE** parameter with name **tab** and value `{ "foo": "bar"}` **WARNING: only the last `tab[foo]` is kept**.
|
||||
|
||||
Those rules are applied to query, post, path, .... parameters.
|
||||
|
||||
## How to get the input data (i.e entity-body) ?
|
||||
See `{WSF_REQUEST}.read_input_data_into (buf: STRING)`
|
||||
|
||||
## How to get the raw header data (i.e the http header text) ?
|
||||
See `{WSF_REQUEST}.raw_header_data: detachable READABLE_STRING_32`
|
||||
@@ -1,6 +0,0 @@
|
||||
---
|
||||
layout: default
|
||||
title: Response
|
||||
base_url: ../../
|
||||
---
|
||||
See WSF_RESPONSE
|
||||
@@ -1,11 +0,0 @@
|
||||
---
|
||||
layout: default
|
||||
title: Router
|
||||
base_url: ../../
|
||||
---
|
||||
The primary goal of the router (class _WSF_ROUTER_) is to dispatch requests according to the request URI.
|
||||
|
||||
See WSF_ROUTER
|
||||
|
||||
**TODO**: describe the router interface
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
---
|
||||
layout: default
|
||||
title: Service
|
||||
base_url: ../../
|
||||
---
|
||||
EWF Services
|
||||
> See WSF\_SERVICE
|
||||
@@ -1,31 +0,0 @@
|
||||
---
|
||||
layout: default
|
||||
title: Using the policy driven framework
|
||||
base_url: ../../
|
||||
---
|
||||
# Using the policy driven framework
|
||||
|
||||
## Introduction
|
||||
|
||||
The aim of the policy-driven framework is to allow authors of web-servers to concentrate on the business logic (e.g., in the case of a GET request, generating the content), without having to worry about the details of the HTTP protocol (such as headers and response codes). However, there are so many possibilities in the HTTP protocol, that it is impossible to correctly guess what to do in all cases. Therefore the author has to supply policy decisions to the framework, in areas such as caching decisions. These are implemented as a set of deferred classes for which the author needs to provide effective implementations.
|
||||
|
||||
We aim to provide unconditional compliance [See HTTP/1.1 specification](http://www.w3.org/Protocols/rfc2616/rfc2616-sec1.html#sec1) for you. Note that byte-ranges are not yet supported.
|
||||
|
||||
## Mapping the URI space
|
||||
|
||||
The authors first task is to decide which URIs the server will respond to (we do this using [URI templates](http://tools.ietf.org/html/rfc6570) ) and which methods are supported for each template.This is done in the class that that defines the service (which is often the root class for the application). This class must be a descendant of WSF_ROUTED_SKELETON_SERVICE. Throughout this tutorial, we will refer to the restbucksCRUD example application, which can be found in the EWF distribution in the examples directory. It's root class, RESTBUCKS_SERVER, inherits from WSF_ROUTED_SKELETON_SERVICE, as well as WSF_DEFAULT_SERVICE. The latter class means that you must specify in the ECF which connector you will use by default.This means you can easily change connectors just by changing the ECF and recompiling.
|
||||
|
||||
### Declaring your URI templates
|
||||
|
||||
In order to map your URI space to handlers (which you will write), you need to implement the routine setup_router. You can see in the example that the ORDER_HANDLER handler is associated with two URI templates. The URI /order is associated with the POST method (only). Any requests to /order with the GET method (or any other method) will result in an automatically generated compliant response being sent on your behalf to the client. The other principle methods (you get compliant responses to the HEAD method for free whenever you allow the GET method) are associated with the URI template /order/{orderid}. Here, orderid is a template variable. It's value for any given request is provided to your application as {WSF_REQUEST}.path_parameter ("orderid"). If the client passes a URI of /order/21, then you will see the value 21. If the client passes /order/fred, you will see the value fred. But if the client passes /order/21/new, he will see a compliant error response generated by the framework.
|
||||
|
||||
## Declaring your policy in responding to OPTIONS
|
||||
|
||||
WSF_ROUTED_SKELETON_SERVICE inherits from WSF_SYSTEM_OPTIONS_ACCESS_POLICY. This policy declares that the framework will provide a compliant default response to OPTIONS * requests. If you prefer to not respond to OPTIONS * requests (and I am doubtful if it is fully compliant to make that choice), then you can redefine
|
||||
is_system_options_forbidden.
|
||||
|
||||
## Declaring your policy on requiring use of a proxy server
|
||||
|
||||
WSF_ROUTED_SKELETON_SERVICE also inherits from WSF_PROXY_USE_POLICY. This determines if the server will require clients to use a proxy server. By default, it will do so for HTTP/1.0 clients. This is a sensible default, as the framework assumes an HTTP/1.1 client throughout. If you are sure that you will only ever have HTTP/1.1 clients, then you can instead inherit from WSF_NO_PROXY_POLICY, as RESTBUCKS_SERVER does. If not, then you need to implement proxy_server.
|
||||
|
||||
Next you have to [write your handler(s)](Writing-the-handlers.md)
|
||||
@@ -1,8 +0,0 @@
|
||||
---
|
||||
layout: default
|
||||
title: WSF OPTIONS POLICY
|
||||
base_url: ../../
|
||||
---
|
||||
# Implementing routines in WSF_OPTIONS_POLICY
|
||||
|
||||
This class provides a default response to OPTIONS requests other than OPTIONS *. So you don't have to do anything. The default response just includes the mandatory Allow headers for all the methods that are allowed for the request URI. if you want to include a body text, or additional header, then you should redefine this routine.
|
||||
@@ -1,228 +0,0 @@
|
||||
---
|
||||
layout: default
|
||||
title: Writing the handlers
|
||||
base_url: ../../
|
||||
---
|
||||
# Writing the handlers
|
||||
|
||||
Now you have to implement each handler. You need to inherit from WSF_SKELETON_HANDLER (as ORDER_HANDLER does). This involves implementing a lot of deferred routines. There are other routines for which default implementations are provided, which you might want to override. This applies to both routines defined in this class, and those declared in the three policy classes from which it inherits.
|
||||
|
||||
## Communicating between routines
|
||||
|
||||
Depending upon the connector (Nino, CGI, FastCGI etc.) that you are using, your handler may be invoked concurrently for multiple requests. Therefore it is unsafe to save state in normal attributes. WSF_REQUEST has a pair of getter/setter routines, execution_variable/set_execution_variable, which you can use for this purpose.
|
||||
Internally, the framework uses the following execution variable names, so you must avoid them:
|
||||
|
||||
1. REQUEST_ENTITY
|
||||
1. NEGOTIATED_LANGUAGE
|
||||
1. NEGOTIATED_CHARSET
|
||||
1. NEGOTIATED_MEDIA_TYPE
|
||||
1. NEGOTIATED_ENCODING
|
||||
1. NEGOTIATED_HTTP_HEADER
|
||||
1. CONFLICT_CHECK_CODE
|
||||
1. CONTENT_CHECK_CODE
|
||||
1. REQUEST_CHECK_CODE
|
||||
|
||||
The first one makes the request entity from a PULL or POST request available to your routines.
|
||||
|
||||
The next four make the results of content negotiation available to your routines. The sixth one makes an HTTP_HEADER available to your routines. You should use this rather than create your own, as it may contain a **Vary** header as a by-product of content negotiation.
|
||||
The last three are for reporting the result from check_conflict, check_content and check_request.
|
||||
|
||||
All names are defined as constants in WSF_SKELETON_HANDLER, to make it easier for you to refer to them.
|
||||
|
||||
## Implementing the routines declared directly in WSF_SKELETON_HANDLER
|
||||
|
||||
### check_resource_exists
|
||||
|
||||
Here you check for the existence of the resource named by the request URI. If it does, then you need to call set_resource_exists on the helper argument.
|
||||
Note that if you support multiple representations through content negotiation, then etags are dependent upon
|
||||
the selected variant. If you support etags, then you will need to make the response entity available at this point, rather than in ensure_content_available.
|
||||
|
||||
### is_chunking
|
||||
|
||||
HTTP/1.1 supports streaming responses (and providing you have configured your server to use a proxy server in WSF_PROXY_USE_POLICY, this framework guarantees you have an HTTP/1.1 client to deal with). It is up to you whether or not you choose to make use of it. If so, then you have to serve the response one chunk at a time (but you could generate it all at once, and slice it up as you go). In this routine you just say whether or not you will be doing this. So the framework n=knows which other routines to call.
|
||||
Currently we only support chunking for GET or HEAD routines. This might change in the future, so if you intend to return True, you should call req.is_get_head_request_method.
|
||||
Note that currently this framework does not support writing a trailer.
|
||||
|
||||
### includes_response_entity
|
||||
|
||||
The response to a DELETE, PUT or POST will include HTTP headers. It may or may not include a body. It is up to you, and this is where you tell the framework.
|
||||
|
||||
### conneg
|
||||
|
||||
[The HTTP/1.1 specification](http://www.w3.org/Protocols/rfc2616/rfc2616-sec12.html#sec12.1) defines server-driven content negotiation. Based on the Accept* headers in the request, we can determine whether we have a format for the response entity that is acceptable to the client. You need to indicate what formats you support. The framework does the rest. Normally you will have the same options for all requests, in which case you can use a once object.
|
||||
|
||||
### mime_types_supported
|
||||
|
||||
Here you need to indicate which media types you support for responses. One of the entries must be passed to the creation routine for conneg.
|
||||
|
||||
### languages_supported
|
||||
|
||||
Here you need to indicate which languages you support for responses. One of the entries must be passed to the creation routine for conneg.
|
||||
|
||||
|
||||
### charsets_supported
|
||||
|
||||
Here you need to indicate which character sets you support for responses. One of the entries must be passed to the creation routine for conneg.
|
||||
|
||||
|
||||
### encodings_supported
|
||||
|
||||
Here you need to indicate which compression encodings you support for responses. One of the entries must be passed to the creation routine for conneg.
|
||||
|
||||
### additional_variant_headers
|
||||
|
||||
The framework will write a Vary header if conneg indicates that different formats are supported. This warns caches that they may not be able to use a cached response if the Accept* headers in the request differ. If the author knows that the response may be affected by other request headers in addition to these, then they must be indicated here, so they can be included in a Vary header with the response.
|
||||
|
||||
### predictable_response
|
||||
|
||||
If the response may vary in other ways not predictable from the request headers, then redefine this routine to return True. In that case we will generate a Vary: * header to inform the cache that the response is not necessarily repeatable.
|
||||
|
||||
### matching_etag
|
||||
|
||||
An **ETag** header is a kind of message digest. Clients can use etags to avoid re-fetching responses for unchanged resources, or to avoid updating a resource that may have changed since the client last updated it.
|
||||
You must implement this routine to test for matches **if and only if** you return non-Void responses for the etag routine.
|
||||
Note that if you support multiple representations through content negotiation, then etags are dependent upon
|
||||
the selected variant. Therefore you will need to have the response entity available for this routine. This can be done in check_resource_exists.
|
||||
|
||||
### etag
|
||||
|
||||
You are strongly encouraged to return non-Void for this routine. See [Validation Model](http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.3) for more details.
|
||||
Note that if you support multiple representations through content negotiation, then etags are dependent upon
|
||||
the selected variant. Therefore you will need to have the response entity available for this routine. This can be done in check_resource_exists.
|
||||
|
||||
### modified_since
|
||||
|
||||
You need to implement this. If you do not have information about when a resource was last modified, then return True as a precaution. Of course, you return false for a static resource.
|
||||
|
||||
### treat_as_moved_permanently
|
||||
|
||||
This routine when a PUT request is made to a resource that does not exist. See [PUT](http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.6) in the HTTP/1.1 specification for why you might want to return zero.
|
||||
|
||||
### allow_post_to_missing_resource
|
||||
|
||||
POST requests are normally made to an existing entity. However it is possible to create new resources using a POST, if the server allows it. This is where you make that decision.
|
||||
|
||||
If you return True, and the resource is created, a 201 Created response will be returned.
|
||||
|
||||
### content_length
|
||||
|
||||
If you are not streaming the result, the the HTTP protocol requires that the length of the entity is known. You need to implement this routine to provide that information.
|
||||
|
||||
### finished
|
||||
|
||||
If you are streaming the response, then you need to tell the framework when the last chunk has been sent.
|
||||
To implement this routine, you will probably need to call req.set_execution_variable (some-name, True) in ensure_content_avaiable and generate_next_chunk, and call attached {BOOLEAN} req.execution_variable (some-name) in this routine.
|
||||
|
||||
### description
|
||||
|
||||
This is for the automatically generated documentation that the framework will generate in response to a request that you have not mapped into an handler.
|
||||
|
||||
### delete
|
||||
|
||||
This routine is for carrying out a DELETE request to a resource. If it is valid to delete the named resource, then you should either go ahead and do it, or queue a deletion request somewhere (if you do that then you will probably need to call req.set_execution_variable (some-name-or-other, True). Otherwise you should call req.error_handler.add_custom_error to explain why the DELETE could not proceed (you should also do this if the attempt to delete the resource fails).
|
||||
Of course, if you have not mapped any DELETE requests to the URI space of this handler, then you can just do nothing.
|
||||
|
||||
### delete_queued
|
||||
|
||||
If in the delete routine, you elected to queue the request, then you need to return True here. You will probably need to check the execution variable you set in the delete routine.
|
||||
|
||||
### ensure_content_available
|
||||
|
||||
This routine is called for GET and DELETE (when a entity is provided in the response) processing. It's purpose is to make the text of the entity (body of the response) available for future routines (if is_chunking is true, then only the first chunk needs to be made available, although if you only serve, as opposed to generate, the result in chunks, then you will make the entire entity available here). This is necessary so that we can compute the length before we start to serve the response. You would normally save it in an execution variable on the request object (as ORDER_HANDLER does). Note that this usage of execution variables ensures your routines can successfully cope with simultaneous requests. If you encounter a problem generating the content, then add an error to req.error_handler.
|
||||
|
||||
As well as the request object, we provide the results of content negotiation, so you can generate the entity in the agreed format. If you only support one format (i.e. all of mime_types_supported, charsets_supported, encodings_supported and languages_supported are one-element lists), then you are guaranteed that this is what you are being asked for, and so you can ignore them.
|
||||
|
||||
Note that if you support multiple representations through content negotiation, then etags are dependent upon
|
||||
the selected variant. Therefore you will need to have the response entity available for this routine. In such cases, this will have to be done in check_resource_exists, rather than here, as this routine is called later on.
|
||||
|
||||
### content
|
||||
|
||||
When not streaming, this routine provides the entity to the framework (for GET or DELETE). Normally you would just access the execution variable that you set in ensure_content_available. Again, the results of content negotiation are made available, but you probably don't need them at this stage. If you only stream responses (for GET), and if you don't support DELETE, then you don't need to do anything here.
|
||||
|
||||
### generate_next_chunk
|
||||
|
||||
When streaming the response, this routine is called to enable you to generate chunks beyond the first, so that you can incrementally generate the response entity. If you generated the entire response entity in
|
||||
ensure_content_available, then you do nothing here. Otherwise, you will generate the next chunk, and save it in the same execution variable that you use in ensure_content_available (or add an error to req.error_handler). If you don't support streaming, then you don't need to do anything here.
|
||||
|
||||
### next_chunk
|
||||
|
||||
When streaming the response, the framework calls this routine to provides the contents of each generated chunk. If you generated the entire response entity in ensure_content_available, then you need to slice it in this routine (you will have to keep track of where you are with execution variables). If instead you generate the response incrementally, then your task is much easier - you just access the execution variable saved in ensure_content_available/generate_next_chunk.
|
||||
As in all these content-serving routines, we provide the results of content negotiation. This might be necessary, for instance, if you were compressing an incrementally generated response (it might be more convenient to do the compression here rather than in both ensure_content_available and generate_next_chunk).
|
||||
|
||||
### read_entity
|
||||
|
||||
This is called for PUT and POST processing, to read the entity provided in the request. A default implementation is provided. This assumes that no decoding (e.g. decompression or character set conversion) is necessary. And it saves it in the execution variable REQUEST_ENTITY.
|
||||
|
||||
Currently the framework provides very little support for PUT and POST requests (so you may well need to redefine this routine). There are several reasons for this:
|
||||
|
||||
1. I personally don't have much experience with PUT and POST.
|
||||
1. It has taken a long time to develop this framework, and to some extent I was working in the dark (I couldn't check what I was doing until the entire framework was written - it wouldn't even compile before then).
|
||||
1. The idea for the framework came from a code review process on servers I had written for the company that I work for. I had acquired a lot of knowledge of the HTTP protocol in the process, and some of it showed in the code that I had written. It was thought that it would be a good idea if this knowledge were encapsulated in Eiffel, so other developers would be able to write servers without such knowledge. So this framework has been developed in company time. However, at present, we are only using GET requests.
|
||||
|
||||
Experience with converting the restbucksCRUD example to use the framework, shows that it is certainly possible to do POST and PUT processing with it. But enhancements are needed, especially in the area of decoding the request entity.
|
||||
|
||||
### is_entity_too_large
|
||||
|
||||
If your application has limits on the size of entities that it can store, then you implement them here.
|
||||
|
||||
### check_content_headers
|
||||
|
||||
This is called after is_entity_too_large returns False. You are supposed to check the following request headers, and take any appropriate actions (such as setting an error, decompression the entity, or converting it to a different character set):
|
||||
|
||||
* Content-Encoding
|
||||
* Content-Language
|
||||
* Content-MD5
|
||||
* Content-Range
|
||||
* Content-Type
|
||||
|
||||
At the moment, your duty is to set the execution variable CONTENT_CHECK_CODE to zero, or an HTTP error status code. A future enhancement of the framework might be to provide more support for this.
|
||||
|
||||
### content_check_code
|
||||
|
||||
This simply accesses the execution variable CONTENT_CHECK_CODE set in check_content_headers. if you want to use some other mechanism, then you can redefine this routine.
|
||||
|
||||
### create_resource
|
||||
|
||||
This routine is called when a PUT request is made with a URI that refers to a resource that does not exist (PUT is normally used for updating an existing resource), and you have already decided to allow this.
|
||||
In this routine you have the responsibilities of:
|
||||
|
||||
1. Creating the resource using the entity in REQUEST_ENTITY (or some decoded version that you have stored elsewhere).
|
||||
1. Writing the entire response yourself (as I said before, support for PUT and POST processing is poor at present), including setting the status code of 201 Created or 303 See Other or 500 Internal server error).
|
||||
|
||||
### append_resource
|
||||
|
||||
This routine is called for POST requests on an existing resource (normal usage).
|
||||
|
||||
In this routine you have the responsibilities of:
|
||||
|
||||
1. Storing the entity from REQUEST_ENTITY (or some decoded version that you have stored elsewhere), or whatever other action is appropriate for the semantics of POST requests to this URI.
|
||||
1. Writing the entire response yourself (as I said before, support for PUT and POST processing is poor at present), including setting the status code of 200 OK, 204 No Content, 303 See Other or 500 Internal server error).
|
||||
|
||||
### check_conflict
|
||||
|
||||
This is called for a normal (updating) PUT request. You have to check to see if the current state of the resource makes updating impossible. If so, then you need to write the entire response with a status code of 409 Conflict, and set the execution variable CONFLICT_CHECK_CODE to 409.
|
||||
Otherwise you just set the execution variable CONFLICT_CHECK_CODE to 0.
|
||||
|
||||
See [the HTTP/1.1 specification](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.10) for when you are allowed to use the 409 response, and what to write in the response entity. If this is not appropriate then a 500 Internal server error would be more appropriate (and set CONFLICT_CHECK_CODE to 500 - the framework only tests for non-zero).
|
||||
|
||||
### conflict_check_code
|
||||
|
||||
This is implemented to check CONFLICT_CHECK_CODE from the previous routine. If you choose to use a different mechanism, then you need to redefine this.
|
||||
|
||||
### check_request
|
||||
|
||||
This is called for PUT and POST requests. You need to check that the request entity (available in the execution variable REQUEST_ENTITY) is valid for the semantics of the request URI. You should set the execution variable REQUEST_CHECK_CODE to 0 if it is OK. If not, set it to 400 and write the full response, including a status code of 400 Bad Request.
|
||||
|
||||
### request_check_code
|
||||
|
||||
This routine just checks REQUEST_CHECK_CODE. if you choose to use a different mechanism, then redefine it.
|
||||
|
||||
### update_resource
|
||||
|
||||
This routine is called for a normal (updating) PUT request. You have to update the state of the resource using the entity saved in the execution environment variable REQUEST_ENTITY (or more likely elsewhere - see what ORDER_HANDLER does). Then write the entire response including a status code of 204 No Content or 500 Internal server error.
|
||||
|
||||
## Implementing the policies
|
||||
|
||||
* [WSF_OPTIONS_POLICY](WSF_OPTIONS_POLICY.md)
|
||||
* [WSF_PREVIOUS_POLICY](Wsf-previous-policy.md)
|
||||
* [WSF_CACHING_POLICY](Wsf-caching-policy.md)
|
||||
@@ -1,57 +0,0 @@
|
||||
---
|
||||
layout: default
|
||||
title: Wsf caching policy
|
||||
base_url: ../../
|
||||
---
|
||||
# Implementing WSF_CACHING_POLICY
|
||||
|
||||
This class contains a large number of routines, some of which have sensible defaults.
|
||||
|
||||
## age
|
||||
|
||||
This is used to generate a **Cache-Control: max-age** header. It says how old the response can before a cache will consider it stale (and therefore will need to revalidate with the server). Common values are zero (always consider it stale) and Never_expires (never always mean up to one year) and 1440 (one day).
|
||||
|
||||
## shared_age
|
||||
|
||||
This defaults to the same as age, so you only have to redefine it if you want a different value. If different from age, then we generate a **Cache-Control: s-max-age** header. This applies to shared caches only. Otherwise it has the same meaning as age. This overrides the value specified in age for shared caches.
|
||||
|
||||
## http_1_0_age
|
||||
|
||||
This generates an **Expires** header, and has the same meaning as age, but is understood by HTTP/1.0 caches. By default it has the same value as age. You only need to redefine this if you want to treat HTTP/1.0 caches differently (you might not trust them so well, so you might want to return 0 here).
|
||||
|
||||
## is_freely_cacheable
|
||||
|
||||
This routine says whether a shared cache can use this response for all client. If True, then it generates a **Cache-Control: public** header. If your data is at all sensitive, then you want to return False here.
|
||||
|
||||
## is_transformable
|
||||
|
||||
Non-transparent proxies are allowed to make some modifications to headers. If your application relies on this _not_ happening, then you want to return False here. This is the default, so you don't have to do anything. This means a **Cache-Control: no-transform** header will be generated.
|
||||
But most applications can return True.
|
||||
|
||||
## must_revalidate
|
||||
|
||||
Some clients request that their private cache ignores server expiry times (and so freely reuse stale responses). If you want to force revalidation anyway in such circumstances, then redefine to return True. In which case, we generate a **Cache-Control: must-revalidate** header.
|
||||
|
||||
## must_proxy_revalidate
|
||||
|
||||
This is the same as must_revalidate, but only applies to shared caches that are configured to serve stale responses. If you redefine to return True, then we generate a **Cache-Control: proxy-revalidate** header.
|
||||
|
||||
## private_headers
|
||||
|
||||
This is used to indicate that parts (or all) of a response are considered private to a single user, and should not be freely served from a shared cache. You must implement this routine. Your choices are:
|
||||
|
||||
1. Return Void. None of the response is considered private.
|
||||
1. Return and empty list. All of the response is considered private.
|
||||
1. Return a list of header names.
|
||||
|
||||
If you don't return Void, then a **Cache-Control: private** header will be generated.
|
||||
|
||||
## non_cacheable_headers
|
||||
|
||||
This is similar to private_headers, and you have the same three choices. the difference is that it is a list of headers (or the whole response) that will not be sent from a cache without revalidation.
|
||||
|
||||
If you don't return Void, then a **Cache-Control: no-cache** header will be generated.
|
||||
|
||||
## is_sensitive
|
||||
|
||||
Is the response to be considered of a sensitive nature? If so, then it will not be archived from a cache. We generate a **Cache-Control: no-store** header.
|
||||
@@ -1,24 +0,0 @@
|
||||
---
|
||||
layout: default
|
||||
title: Wsf previous policy
|
||||
base_url: ../../
|
||||
---
|
||||
# WSF_PREVIOUS_POLICY
|
||||
|
||||
This class deals with resources that have moved or gone. The default assumes no such resources. It exists as a separate class, rather than have the routines directly in WSF_SKELETON_HANDLER, as sub-classing it may be convenient for an organisation.
|
||||
|
||||
## resource_previously_existed
|
||||
|
||||
Redefining this routine is always necessary if you want to deal with any previous resources.
|
||||
|
||||
## resource_moved_permanently
|
||||
|
||||
Redefine this routine for any resources that have permanently changed location. The framework will generate a 301 Moved Permanently response, and the user agent will automatically redirect the request to (one of) the new location(s) you provide. The user agent will use the new URI for future requests.
|
||||
|
||||
## resource_moved_temporarily
|
||||
|
||||
This is for resource that have only been moved for a short period. The framework will generate a 302 Found response. The only substantial difference between this and resource_moved_permanently, is that the agent will use the old URI for future requests.
|
||||
|
||||
## previous_location
|
||||
|
||||
When you redefine resource_moved_permanently or resource_moved_temporarily, the framework will generate a Location header for the new URI, and a hypertext document to the new URI(s). You **must** redefine this routine to provide those locations (the first one you provide will be in the location header).
|
||||
@@ -1,9 +0,0 @@
|
||||
---
|
||||
layout: default
|
||||
title: Meetings
|
||||
base_url: ../../
|
||||
---
|
||||
# Previous and future meetings
|
||||
|
||||
* [Web-meeting: 2012-09-18](meetings/Web-meeting-2012-09-18.md)
|
||||
* For previous meetings, check the ["meeting" topics](https://groups.google.com/forum/?fromgroups=#!tags/eiffel-web-framework/meeting) on the [forum](http://groups.google.com/group/eiffel-web-framework)
|
||||
@@ -1,25 +0,0 @@
|
||||
---
|
||||
layout: default
|
||||
title: Projects new suggestions
|
||||
base_url: ../../
|
||||
---
|
||||
Use this to suggest new projects, or request features.
|
||||
The content of this page will be moved to the main [Projects](Projects.md) page for time to time.
|
||||
For any entry, please use this template
|
||||
|
||||
----
|
||||
|
||||
## Short title
|
||||
* _Suggested by **your name**_": so that we know who suggested a feature.
|
||||
* **Requirement**: ... if any, otherwise remove this line
|
||||
* _Description_: ... a few lines to describe the project
|
||||
* _References_: ... if any, otherwise remove this line
|
||||
|
||||
----
|
||||
|
||||
## Add support for Swagger
|
||||
* _Suggested by **Olivier**_
|
||||
* _Description_: Build a Swagger Eiffel implementation
|
||||
* _References_: [http://swagger.wordnik.com/](http://swagger.wordnik.com/)
|
||||
|
||||
----
|
||||
@@ -1,244 +0,0 @@
|
||||
---
|
||||
layout: default
|
||||
title: Projects
|
||||
base_url: ../../
|
||||
---
|
||||
This page lists potential projects on EWF, this is open for contribution.
|
||||
If you are a student, don't hesitate to pick one, or even suggest a new project, or a project being a merge of several, in any case, you will get close support from EWF's team.
|
||||
|
||||
----
|
||||
# Study/Analysis/Documentation
|
||||
|
||||
## Evaluate EWF according to the following constraints ...
|
||||
* _Suggested by **Javier**_
|
||||
* _Description_: According to [http://www.amundsen.com/blog/archives/1130](http://www.amundsen.com/blog/archives/1130) , evaluate the current design of EWF to see if this match the different points. An other option would be to take the following REST implementation toolkit as a guide to evaluate EWF [http://code.google.com/p/implementing-rest/wiki/RESTImplementationToolkit](http://code.google.com/p/implementing-rest/wiki/RESTImplementationToolkit).
|
||||
|
||||
## Road to Hypermedia API
|
||||
* _Suggested by **Javier**_
|
||||
* _Supervisor_:
|
||||
* _Suitability_:
|
||||
* _Description_: describe differents types of Web API, and how you can build them using EWF. Describing Pros and Cons. This should be on [http://martinfowler.com/articles/richardsonMaturityModel.html](http://martinfowler.com/articles/richardsonMaturityModel.html)
|
||||
|
||||
## Build a video to demonstrate how an Hypermedia API works, and how to build it using EWF
|
||||
* _Suggested by **Javier**_
|
||||
* _Supervisor_:
|
||||
* _Suitability_: TODO
|
||||
* _Description_: produce a audio+video+slide that demonstrates how to build an hypermedia API using EWF. This could be based on upcoming "graphviz server" example, or an extension of existing RestBucksCRUD example, or any new example.
|
||||
|
||||
----
|
||||
# Works related to EWF / framework / tools
|
||||
|
||||
## Improve EWF
|
||||
* _Suggested by **Jocelyn**_
|
||||
* _Supervisor_:
|
||||
* _Suitability_: TODO
|
||||
* _Description_: Improve existing EWF source, this is a permanent task for EWF, and this can be code, documentation, tests, ... Among others , here is a list of needed effort:
|
||||
** Improve encoding support
|
||||
*∗ Better MIME handler
|
||||
** _Support for configuration _
|
||||
** Ready to use logging facilities
|
||||
** Smart handler for HEAD or similar
|
||||
** Adding component to ease the caching functionalities
|
||||
** Adding Session support
|
||||
** URL rewriting ?
|
||||
** Mass testing
|
||||
** ...
|
||||
|
||||
## Eiffel Web Nino
|
||||
* _Suggested by **Javier & Jocelyn**_
|
||||
* _Supervisor_:
|
||||
* _Suitability_: TODO
|
||||
* _Description_: Currently Eiffel Web Nino, is a standalone httpd server written in Eiffel. It is great for development, or embedding httpd component in application. However there are room for improvement so that one can also use it as replacement for apache, iis, ... To reach this state, here are a list of task that should be achieved:
|
||||
** Implement persistent connection
|
||||
** Complete implementation of Eiffel Web Nino using pool of threads
|
||||
** Complete migration of Eiffel Web Nino to SCOOP
|
||||
** Improve Nino to become a real solution to host any web services/sites
|
||||
** ...
|
||||
|
||||
## New EWF connectors
|
||||
* _Suggested by **Jocelyn & Javier**_
|
||||
* _Supervisor_:
|
||||
* _Suitability_: TODO
|
||||
* _Description_: EWF is relying on the notion of "connector" to achieve portability on various platform and underlying httpd server, currently EWF support any CGI or libFCGI system (i.e apache, IIS, ...), and provide a standalone version thanks to Eiffel Web Nino. The goal now, would be to support specific connector for:
|
||||
** LightHTTP ([http://www.lighttpd.net/](http://www.lighttpd.net/))
|
||||
** nginx ([http://nginx.org/en/](http://nginx.org/en/))
|
||||
|
||||
## Concurrenty and EWF
|
||||
* _Suggested by **Jocelyn**_
|
||||
* _Supervisor_:
|
||||
* _Suitability_: TODO
|
||||
* _Description_: Check that EWF is compliant with concurrency (Eiffel Thread, and SCOOP), and provide an example using concurrency.
|
||||
|
||||
## Design and build something like Ruby on Rails or Grails
|
||||
* _Suggested by **Javier**_
|
||||
* _Supervisor_:
|
||||
* _Suitability_: TODO
|
||||
* _Description_: Using EWF, design and build the set of tools to provide a conventional MVC to create Web sites. This could be useful even if this is not the taste of everyone.
|
||||
|
||||
## Provide a Websocket implementation
|
||||
* _Suggested by **Jocelyn**_
|
||||
* _Supervisor_:
|
||||
* _Suitability_: TODO
|
||||
* _Description_: Provide an implementation of websocket with EWF and eventually Eiffel Web Nino, then demonstrate it on a simple example. WebSocket is a web technology providing for bi-directional, full-duplex communications channels over a single TCP connection.
|
||||
* See [http://en.wikipedia.org/wiki/Websocket](http://en.wikipedia.org/wiki/Websocket)
|
||||
|
||||
----
|
||||
# Usage of EWF
|
||||
|
||||
## HAL browser
|
||||
* _Suggested by **Javier**_
|
||||
* _Supervisor_:
|
||||
* _Suitability_: TODO
|
||||
* _Description_: Build a HAL browser to discover an API using HAL mediatype. The browser will be able to follow the links, and display the transmitted data. This could be a vision2 application inspired by [http://haltalk.herokuapp.com/explorer/hal_browser.html#/](http://haltalk.herokuapp.com/explorer/hal_browser.html#/). HAL stands for Hypertext Application Language see [http://stateless.co/hal_specification.html](http://stateless.co/hal_specification.html).
|
||||
|
||||
## Collection-JSON browser
|
||||
* _Suggested by **Javier**_
|
||||
* _Supervisor_:
|
||||
* _Suitability_: TODO
|
||||
* _Description_: Build a Collection/JSON browser to discover an API using Collection/JSON mediatype. The browser will be able to follow the links, and display the transmitted data. This could be a vision2 application inspired by [http://haltalk.herokuapp.com/explorer/hal_browser.html#/](http://haltalk.herokuapp.com/explorer/hal_browser.html#/). Collection+JSON is a JSON-based read/write hypermedia-type, see [http://www.amundsen.com/media-types/collection/](http://www.amundsen.com/media-types/collection/)
|
||||
|
||||
## Build a simple CMS with EWF
|
||||
* _Suggested by **Jocelyn**_
|
||||
* _Supervisor_:
|
||||
* _Suitability_: TODO
|
||||
* _Status_: started, and open for contribution, collaboration, please contact Jocelyn.
|
||||
* _Description_: Using EWF, Build a simple CMS (Content Management System) framework and then an example. It should provide common features such as:
|
||||
- user management (register, login, lost password -> send email)
|
||||
- page editing
|
||||
- blog
|
||||
- template / theme
|
||||
- persistency / storage / ...
|
||||
- extension at compilation time
|
||||
* The result should be usable by any user to build his own CMS website, and extend it easily.
|
||||
|
||||
## Build P2P connector
|
||||
* _Suggested by **Jocelyn**_
|
||||
* _Supervisor_:
|
||||
* _Suitability_: TODO
|
||||
* _Description_: Imagine you want to publish a website (or web service, API) running on your machine (behind firewall). One would need to initiate the connection via a public website, this is common for P2P software such as remote assistance (i.e: join.me, teamviewer, showmypc, ...)
|
||||
|
||||
----
|
||||
# Libraries
|
||||
|
||||
## Hypermedia API library to work with XHTML
|
||||
* _Suggested by **Javier**_
|
||||
* _Supervisor_:
|
||||
* _Suitability_: TODO
|
||||
* _Description_: Use XHTML as a media type to for hypermedia API. See [http://codeartisan.blogspot.com.ar/2012/07/using-html-as-media-type-for-your-api.html](http://codeartisan.blogspot.com.ar/2012/07/using-html-as-media-type-for-your-api.html)
|
||||
|
||||
## Add support for Mediatype such as RSS, ATOM, ...
|
||||
* _Suggested by **Jocelyn**_
|
||||
* _Supervisor_:
|
||||
* _Suitability_: TODO
|
||||
* _Description_: In addition to JSON, HAL, Collection+JSON, XHTML, application might want to support (read and write) standard media type such as RSS, ATOM, ...
|
||||
|
||||
## Security: provide popular authentication mechanisms
|
||||
* _Suggested by **Jocelyn**_
|
||||
* _Supervisor_:
|
||||
* _Suitability_: TODO
|
||||
* _Description_: Any web service, web site, API need a reliable authentication mechanism, the could be on the server side or the client side to build mashup service (integrate with other web API such as google, flicker, ...). So far, EWF provides only basic HTTP Authorization, and application would need more solutions such as :
|
||||
- OAuth: consumer and provider
|
||||
- OpenID
|
||||
- Google Connect
|
||||
- Facebook Connect
|
||||
* The goal is to provide component to consume other popular API/service, but also component for your own service so that other can consume it.
|
||||
|
||||
## Security: provide popular authentication mechanisms
|
||||
* _Suggested by **Jocelyn**_
|
||||
* _Supervisor_:
|
||||
* _Suitability_: TODO
|
||||
* _Description_: Any web service, web site, API need a reliable authentication mechanism, the could be on the server side or the client side to build mashup service (integrate with other web API such as google, flicker, ...). So far, EWF provides only basic HTTP Authorization, and application would need more solutions such as :
|
||||
- OAuth: consumer and provider
|
||||
- OpenID
|
||||
- Google Connect
|
||||
- Facebook Connect
|
||||
* The goal is to provide component to consume other popular API/service, but also component for your own service so that other can consume it.
|
||||
|
||||
## Provide a SSO (Single Sign On) implementation (server, and clients)
|
||||
* _Suggested by **Jocelyn**_
|
||||
* _Supervisor_:
|
||||
* _Suitability_: TODO
|
||||
* _Description_: Design and build a Single Sign On implementation for Eiffel. That should include the authentication server, and at least one Eiffel client component (it would be convenient to also provide php, js, ...). In the same spirit, having Eiffel client for popular SSO server would be appreciated as well.
|
||||
* _Reference_:
|
||||
- [http://en.wikipedia.org/wiki/Single_sign-on](http://en.wikipedia.org/wiki/Single_sign-on)
|
||||
- [http://en.wikipedia.org/wiki/List_of_single_sign-on_implementations](http://en.wikipedia.org/wiki/List_of_single_sign-on_implementations)
|
||||
|
||||
## library: Template engine
|
||||
* _Suggested by **Jocelyn**_
|
||||
* _Supervisor_:
|
||||
* _Suitability_: TODO
|
||||
* _Description_: Get inspired by any existing template engine, and build one for Eiffel, this should be easily usable within a web application. This could be inspired, or implementation of standard template engine, this way people can reuse existing content, or migrate easily their application to EWF. For inspiration, one can look at:
|
||||
- [http://www.smarty.net/](http://www.smarty.net/)
|
||||
- [http://mustache.github.com/](http://mustache.github.com/)
|
||||
- [http://en.wikipedia.org/wiki/Web_template_system](http://en.wikipedia.org/wiki/Web_template_system) ... they are plenty of them, a comparison of the different engine would help.
|
||||
* This is not specific to EWF, but it will be very useful in website context.
|
||||
|
||||
## library: Wikitext, markdown parser and render engine
|
||||
* _Suggested by **Jocelyn**_
|
||||
* _Supervisor_:
|
||||
* _Suitability_: TODO
|
||||
* _Description_: Build component to support (read and write, and why not convert), lightweight markup language (see [http://en.wikipedia.org/wiki/Lightweight_markup_language](http://en.wikipedia.org/wiki/Lightweight_markup_language)) such as wikitext, markdown, and other. The component should be able to read/scan, but also produce an HTML output. Focus first on wikitext, and markdown since they seems to be the most popular.
|
||||
* Then , a nice addition would be to render those lightweight markup lang into Vision2 widget (not related to EWF, but could be useful to build (editor) desktop application)
|
||||
|
||||
## library: Web component to build HTML5 widget
|
||||
* _Suggested by **Jocelyn**_
|
||||
* _Supervisor_:
|
||||
* _Suitability_: TODO
|
||||
* _Description_: Build set of Eiffel components to ease development of websites. First this should be based on HTML5. Idea for components:
|
||||
- table widget (with sorting ...)
|
||||
- suggestive typing widget
|
||||
- tab ...
|
||||
- WYSIWYG textarea widget (could reuse existing Javascript solution TinyMCE, CKEditor, OpenWysiwyg, ...)
|
||||
- ...
|
||||
|
||||
----
|
||||
# Clients
|
||||
|
||||
## Libraries: Reusable Client Design based on J.Moore Presentation
|
||||
* _Suggested by **Javier**_
|
||||
* _Supervisor_:
|
||||
* _Suitability_: TODO
|
||||
* _Description_: TODO
|
||||
* Generic client that can be customized (see design in slide 12)
|
||||
* [http://s3.amazonaws.com/cimlabs/Oredev-Hypermedia-APIs.pdf](http://s3.amazonaws.com/cimlabs/Oredev-Hypermedia-APIs.pdf)
|
||||
* video [http://vimeo.com/20781278](http://vimeo.com/20781278)
|
||||
|
||||
## Create a Client Cache based on Apache commons Client Cache.
|
||||
* _Suggested by **Javier**_
|
||||
* _Supervisor_:
|
||||
* _Suitability_: TODO
|
||||
* _Description_: TODO
|
||||
* [http://hc.apache.org/httpcomponents-client-ga/httpclient-cache/index.html](http://hc.apache.org/httpcomponents-client-ga/httpclient-cache/index.html)
|
||||
* [http://labs.xfinity.com/benchmarking-the-httpclient-caching-module](http://labs.xfinity.com/benchmarking-the-httpclient-caching-module)
|
||||
|
||||
## Add SSL support to Eiffel Net
|
||||
* _Suggested by **Jocelyn**_
|
||||
* _Supervisor_:
|
||||
* _Suitability_: TODO
|
||||
* _Description_: Currently Eiffel Net does not provide any support to SSL (thus no HTTPS). For now Eiffel application often use the Eiffel cURL wrapper which provide SSL, but it would be more convenient to use directly Eiffel Net. Then find solution to add SSL support to EiffelNet, or to extend EiffelNet with an EiffelNet+OpenSSL solution, or other.
|
||||
|
||||
## Build clients to consume popular RESTful APIs
|
||||
* _Suggested by **Jocelyn & Javier**_
|
||||
* _Supervisor_:
|
||||
* _Suitability_: TODO
|
||||
* _Description_: Build Eiffel libraries to consume popular web APIs, such as:
|
||||
- Google Discovery APIs
|
||||
- Twitter
|
||||
- Facebook
|
||||
- Github
|
||||
- Flickr
|
||||
- ... etc
|
||||
* This should reuse and improve the "http_client" provided by EWF. Eventually also write the EiffelNet implementation to be independant from cURL
|
||||
* **Requirement**: OAuth client eiffel component
|
||||
|
||||
## Build a ESI preprocessor, or proxy
|
||||
* _Suggested by **Jocelyn**_
|
||||
* _Supervisor_:
|
||||
* _Suitability_: TODO
|
||||
* _Description_: TODO
|
||||
* See: [http://en.wikipedia.org/wiki/Edge_Side_Includes](http://en.wikipedia.org/wiki/Edge_Side_Includes)
|
||||
|
||||
----
|
||||
# Feel free to add new idea below this line
|
||||
----
|
||||
Use the following page [Projects new suggestions](Projects-new-suggestions.md) to suggest new project, or request a feature.
|
||||
@@ -1,16 +0,0 @@
|
||||
---
|
||||
layout: default
|
||||
title: Task json
|
||||
base_url: ../../
|
||||
---
|
||||
## Goal ##
|
||||
- Make this JSON library the default one for the community
|
||||
|
||||
## Steps ##
|
||||
- restructure to follow the (doc,library,test,...) structure
|
||||
- extract gobo from it, and provide json.ecf, json-safe.ecf, json_with_gobo.ecf (and json_with_gobo-safe.ecf : not possible right now, but we can _cheat_)
|
||||
- and then let Eiffel Software include it, in the official libraries
|
||||
|
||||
## Roadmap ##
|
||||
- This task is completed.
|
||||
- Future task: review the library, and improve it.
|
||||
@@ -1,41 +0,0 @@
|
||||
---
|
||||
layout: default
|
||||
title: Tasks Roadmap
|
||||
base_url: ../../
|
||||
---
|
||||
Check new roadmap wiki page: [roadmap](roadmap.md)
|
||||
## Future
|
||||
* Focus on REST API
|
||||
- Hypermedia API
|
||||
- HAL, Collection/JSON ...
|
||||
- ATOM, RSS, XHTML, ... ?
|
||||
* Extend WSF with libraries addressing common needs
|
||||
- Logging
|
||||
- Caching
|
||||
- Security (authentication) + OAuth (consumer+provider) + OpenID?
|
||||
- Filter chain
|
||||
|
||||
* Start thinking about application friendly libraries
|
||||
- Template engine
|
||||
- State machine
|
||||
- HTML5 (XHTML+JS) generation for widgets (table, suggestive box, ...)
|
||||
- Google API, Twitter API, ... ?
|
||||
|
||||
* Improve documentation
|
||||
- WSF documentation + tutorial
|
||||
- Topic: how to contribute ?
|
||||
- Example: add a "graphviz server" example, which will demonstrate an REST Hypermedia API, with logging, caching and security
|
||||
|
||||
## Version 0.1 june 2012 ##
|
||||
* "Eiffel WSGI" spec
|
||||
* Core of Eiffel Web Framework
|
||||
- EWSGI connectors: CGI, libfcgi, Nino
|
||||
- WSF: request, response, router
|
||||
- And utility lib, error, http, encoders, ...)
|
||||
* Examples
|
||||
* Documentation (tutorial inside the examples folder)
|
||||
* Installation scripts
|
||||
|
||||
## Contributors ##
|
||||
- See [the collaboration page](../community.md)
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
---
|
||||
layout: default
|
||||
title: Useful links
|
||||
base_url: ../../
|
||||
---
|
||||
## Eiffel
|
||||
|
||||
* [http://www.scoop.it/t/eiffel-resources](http://www.scoop.it/t/eiffel-resources)
|
||||
* [http://www.scoop.it/t/eiffel](http://www.scoop.it/t/eiffel)
|
||||
|
||||
## Hypermedia
|
||||
|
||||
* [http://www.scoop.it/t/hyper-media-apis](http://www.scoop.it/t/hyper-media-apis)
|
||||
* [http://www.scoop.it/t/hypermedia-api](http://www.scoop.it/t/hypermedia-api)
|
||||
|
||||
## ETags
|
||||
|
||||
* [http://www.mnot.net/blog/2007/08/07/etags](http://www.mnot.net/blog/2007/08/07/etags)
|
||||
* [http://bitworking.org/news/150/REST-Tip-Deep-etags-give-you-more-benefits](http://bitworking.org/news/150/REST-Tip-Deep-etags-give-you-more-benefits)
|
||||
@@ -1,67 +0,0 @@
|
||||
---
|
||||
layout: default
|
||||
title: Web meeting 2012 09 18
|
||||
base_url: ../../
|
||||
---
|
||||
## Participants
|
||||
|
||||
* Jocelyn Fiat
|
||||
* Berend de Boer
|
||||
* Olivier Ligot
|
||||
* Javier Velilla
|
||||
|
||||
## Information
|
||||
|
||||
### When ?
|
||||
* Tuesday 18th of september, 19:00 - 20:00 UTC/GMT time (see 3rd time in [http://www.doodle.com/8v2sekiyebp4dpyh](http://www.doodle.com/8v2sekiyebp4dpyh))
|
||||
|
||||
### Where ?
|
||||
Web meeting using webex
|
||||
|
||||
* Short url: [http://goo.gl/wBz11](http://goo.gl/wBz11)
|
||||
* Long url: [https://eiffel.webex.com/eiffel/j.php?ED=211265702&UID=0&PW=NZWNiMjBiZWIz&RT=MiMyMA%3D%3D](https://eiffel.webex.com/eiffel/j.php?ED=211265702&UID=0&PW=NZWNiMjBiZWIz&RT=MiMyMA%3D%3D)
|
||||
* Related Google group topic: [https://groups.google.com/d/topic/eiffel-web-framework/A7ADPAT3nj8/discussion](https://groups.google.com/d/topic/eiffel-web-framework/A7ADPAT3nj8/discussion)
|
||||
|
||||
## Agenda
|
||||
|
||||
* Current status of EWF
|
||||
* Focus on new design for the router system, and take decision
|
||||
* decide if this replace the previous system,
|
||||
* or if this is provided as another solution (we would then have 2 routers system).
|
||||
* It might be possible to implement the previous uri and uri-template router with the new design, and mark them obsolete, this would avoid breaking existing code, but if no-one ask for it, no need to spend time doing it.
|
||||
* Current activities
|
||||
* Technology forecasting about REST, Hypermedia API, Collection/JSON, HAL, ...
|
||||
* Building a CMS framework inspired by Drupal, and using EWF
|
||||
* Libraries in-progress or draft: OAuth (consumer), Google API, Github API, Template engine, Wikitext parser, CMS (including sub libraries which will be part of EWF, such as session handling, mailer, ...)
|
||||
* Documentation
|
||||
* Remaining issues
|
||||
* Review design in relation to concurrency, and provide example demonstrating concurrency with EWF
|
||||
* Review design to allow easier extension/customization of EWF, such as using its own MIME handlers.
|
||||
* Demo for a CMS built with EWF (inspired by Drupal)
|
||||
* Future tasks
|
||||
* [graphviz-server](https://github.com/EiffelWebFramework/graphviz-server)
|
||||
* Improving Eiffel Web Nino: to support persistent connection, and better concurrency design.
|
||||
* Provide friendly components to generate HTML (DHTML, HTML5, ...), (coders do not want to learn HTML and
|
||||
CSS)
|
||||
* Users feedback, suggestions and requests
|
||||
* ...
|
||||
* Next meeting
|
||||
|
||||
## Materials
|
||||
|
||||
## Minutes
|
||||
* swagger: see if we could generate EWF code from a swagger specification
|
||||
* Jocelyn will publish its attempt to build a CMS with EWF
|
||||
* CMS demo: ... as announced ... some parts look very like drupal.
|
||||
* Jocelyn will publish a few in-progress draft libraries
|
||||
* Javier will focus on graphviz-server and hypermedia API
|
||||
* Berend may send a short note on how he uses EWF (and generate code from description)
|
||||
* Jocelyn will try to find time to complete the thread and SCOOP implementation of Eiffel Web Nino
|
||||
* Olivier will have a closer look at swagger
|
||||
* EWF will adopt the new WSF_ROUTER design as no-one expressed opposition. Olivier said converting his code is not a big task. Same for other users.
|
||||
* The current state of EWF/WSF seems to be ok for users, we can focus on libraries on top of EWF/WSF
|
||||
* We might need an HTML parser, if we want to support HTML as an hypermedia API (maybe we can require XHTML for now)
|
||||
* No high priority to improve Eiffel Web Nino , for now it is mainly used during development.
|
||||
|
||||
* It seems RESTful + Hypermedia API is the top priority for EWF.
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
---
|
||||
layout: default
|
||||
title: roadmap
|
||||
base_url: ../../
|
||||
---
|
||||
# Upcoming versions
|
||||
|
||||
# Current state: oct-2013
|
||||
- check previous wiki page: [Tasks roadmap](Tasks-roadmap.md)
|
||||
46
workbook.html
Normal file
46
workbook.html
Normal file
@@ -0,0 +1,46 @@
|
||||
---
|
||||
layout: default
|
||||
title: Workbook
|
||||
base_url: ../
|
||||
---
|
||||
|
||||
<h2>Workbook index</h2>
|
||||
<ul>
|
||||
<li class="page"><a href="readme">readme</a></li>
|
||||
<li class="page"><a href="workbook">workbook</a></li>
|
||||
<li class="folder">handling request/
|
||||
<ul>
|
||||
<li class="page"><a href="handling_request/headers">headers</a></li>
|
||||
<li class="page"><a href="handling_request/form">form</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="folder">generating response/
|
||||
<ul>
|
||||
<li class="page"><a href="generating_response/generating_response">generating response</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="folder">deployment/
|
||||
<ul>
|
||||
<li class="page"><a href="deployment/readme">readme</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="folder">basics/
|
||||
<ul>
|
||||
<li class="page"><a href="basics/basics">basics</a></li>
|
||||
<li class="folder">simple html/
|
||||
<ul>
|
||||
<li class="folder">apache config/
|
||||
<ul>
|
||||
<li class="page"><a href="basics/simple_html/apache_config/Readme">Readme</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="folder">handling cookies/
|
||||
<ul>
|
||||
<li class="page"><a href="handling_cookies/handling_cookies">handling cookies</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
65
workbook.py
Executable file
65
workbook.py
Executable file
@@ -0,0 +1,65 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- encoding: utf-8 -*-
|
||||
|
||||
"Update the workbook pages to work with Jekyll"
|
||||
|
||||
import os
|
||||
|
||||
HEADER = """---
|
||||
layout: default
|
||||
title: %s
|
||||
base_url: %s
|
||||
---
|
||||
"""
|
||||
|
||||
def process_dir (dn,base,rel):
|
||||
s = '<ul>\n'
|
||||
nodes = os.listdir(dn)
|
||||
for file in nodes:
|
||||
path = os.path.join (dn, file)
|
||||
if not file.startswith('.') and not os.path.isdir(path) and not file.endswith('.mediawiki'):
|
||||
originalname = os.path.splitext(file)[0]
|
||||
name = " ".join(originalname.replace('-', ' ').replace('_', ' ').split())
|
||||
uri = "%s%s" % (base,file)
|
||||
if uri.endswith(".md"):
|
||||
uri = uri[:-3]
|
||||
if file.endswith('.md'):
|
||||
print "Processing [%s@%s] %s" % (name, uri, path)
|
||||
with open(path, 'r') as f:
|
||||
content = f.read()
|
||||
if not content.startswith('---'):
|
||||
content = (HEADER % (name, rel) + content)
|
||||
with open(path, 'w') as f:
|
||||
f.write(content.replace('.md)',')')
|
||||
.replace('(../','(../../')
|
||||
.replace('(/doc/workbook/', '(../')
|
||||
.replace('(./', '(../')
|
||||
)
|
||||
s += ' <li class="page"><a href="%s">%s</a></li>\n' % (uri, name)
|
||||
for file in nodes:
|
||||
path = os.path.join (dn, file)
|
||||
if not file.startswith('.') and os.path.isdir(path):
|
||||
originalname = os.path.splitext(file)[0]
|
||||
name = " ".join(originalname.replace('-', ' ').replace('_', ' ').split())
|
||||
path = os.path.join (dn, file)
|
||||
uri = "%s%s" % (base,file)
|
||||
if uri.endswith(".md"):
|
||||
uri = uri[:-3]
|
||||
print 'Enter', path
|
||||
sub_s = process_dir (path, uri + '/', rel + '../')
|
||||
if sub_s != '<ul>\n</ul>\n':
|
||||
if "%s.md" % (file) in nodes:
|
||||
s += '<li class="page folder"><a href="%s">%s/</a>\n' % (uri, name)
|
||||
else:
|
||||
s += '<li class="folder">%s/\n' % (name)
|
||||
s += sub_s
|
||||
s += '</li>\n'
|
||||
s += '</ul>\n'
|
||||
return s
|
||||
|
||||
txt = HEADER % ('Workbook', '../') + '\n';
|
||||
txt += "<h2>Workbook index</h2>\n"
|
||||
txt += process_dir ("workbook",'', '../../');
|
||||
with open('workbook.html', 'w') as f:
|
||||
f.write(txt)
|
||||
print 'Done'
|
||||
9
workbook/_
Normal file
9
workbook/_
Normal file
@@ -0,0 +1,9 @@
|
||||
readme.md
|
||||
handling_request/headers.md
|
||||
handling_request/form.md
|
||||
deployment.md
|
||||
generating_response/generating_response.md
|
||||
basics/simple_html/apache_config/Readme.md
|
||||
basics/basics.md
|
||||
workbook.md
|
||||
handling_cookies/handling_cookies.md
|
||||
BIN
workbook/basics/APPLICATION_EXECUTION.png
Normal file
BIN
workbook/basics/APPLICATION_EXECUTION.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.8 KiB |
BIN
workbook/basics/Launcher Hierarchy.png
Normal file
BIN
workbook/basics/Launcher Hierarchy.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 16 KiB |
BIN
workbook/basics/WSF_SERVICE_LAUNCHER_CGI.png
Normal file
BIN
workbook/basics/WSF_SERVICE_LAUNCHER_CGI.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 9.2 KiB |
BIN
workbook/basics/WSF_SERVICE_LAUNCHER_FCGI.png
Normal file
BIN
workbook/basics/WSF_SERVICE_LAUNCHER_FCGI.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 9.3 KiB |
BIN
workbook/basics/WSF_SERVICE_LAUNCHER_NINO.png
Normal file
BIN
workbook/basics/WSF_SERVICE_LAUNCHER_NINO.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 9.3 KiB |
BIN
workbook/basics/WSF_SERVICE_LAUNCHER_STANDALONE.png
Normal file
BIN
workbook/basics/WSF_SERVICE_LAUNCHER_STANDALONE.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 11 KiB |
211
workbook/basics/basics.md
Normal file
211
workbook/basics/basics.md
Normal file
@@ -0,0 +1,211 @@
|
||||
---
|
||||
layout: default
|
||||
title: basics
|
||||
base_url: ../../../
|
||||
---
|
||||
Nav: [Workbook](../../workbook) | [Handling Requests: Form/Query Parameter](../../handling_request/form)
|
||||
|
||||
|
||||
## EWF basic service
|
||||
|
||||
##### Table of Contents
|
||||
- [Basic Structure](#structure)
|
||||
- [Service to Generate Plain Text](#text)
|
||||
- [Source code](#source_1)
|
||||
- [Service to Generate HTML](#html)
|
||||
- [Source code](#source_2)
|
||||
|
||||
|
||||
<a name="structure"></a>
|
||||
## EWF service structure
|
||||
|
||||
The following code describes the basic structure of an EWF basic service that handles HTTP requests. We will need to define a Service Launcher and a Request Execution implementation.
|
||||
|
||||
```eiffel
|
||||
class
|
||||
APPLICATION
|
||||
|
||||
inherit
|
||||
WSF_DEFAULT_SERVICE [APPLICATION_EXECUTION]
|
||||
|
||||
create
|
||||
make_and_launch
|
||||
|
||||
end
|
||||
```
|
||||
|
||||
The class ```APPLICATION``` inherit from
|
||||
```WSF_DEFAULT_SERVICE [G ->WSF_EXECUTION create make end]``` it will be responsible to launch the service and set optional options.
|
||||
|
||||
The class ```APPLICATION_EXECUTION``` is an implementation of ```WSF_EXECUTION``` interface, which is instantiated for each incoming request.
|
||||
|
||||
```eiffel
|
||||
class
|
||||
APPLICATION_EXECUTION
|
||||
|
||||
inherit
|
||||
WSF_EXECUTION
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature -- Basic operations
|
||||
|
||||
execute (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
do
|
||||
-- To read incoming HTTP request, we need to use `req'
|
||||
|
||||
-- May require talking to databases or other services.
|
||||
|
||||
-- To send a response we need to setup, the status code and
|
||||
-- the response headers and the content we want to send out our client
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
When using the "nino" connector or the new "standalone" connector, by default the service listens on port 80, but often this port is already used by other applications, so it is recommended to use another port.
|
||||
To define another port, redefine the feature `initialize' and set up a new port number using the service options (see below).
|
||||
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
The **WSF_REQUEST** gives access to the incoming data; the class provides features to get information such as request method, form data, query parameters, uploaded files, HTTP request headers, and hostname of the client among others.
|
||||
|
||||
The **WSF_RESPONSE** provides features to define the response with information such as HTTP status codes (10x,20x, 30x, 40x, and 50x), response headers (Content-Type, Content-Length, etc.) and obviously the body of the message itself.
|
||||
|
||||
**APPLICATION** is the root class of our example, it launches the application, using the corresponding connector, Which connector? this depends how you want to run it cgi, fcgi,nino or standalone. For development is recommended to use a standalone web server written in Eiffel, and run the execution within the EiffelStudio debugger. For production fcgi (or cgi) using Apache or another popular web server.
|
||||
|
||||

|
||||
|
||||
**WS_LAUNCHABLE_SERVICE** inherit from **WS_SERVICE** class, which is a marker interface in EWF. And also provides a way to launch our application using different kind of connectors. The class **WSF_DEFAULT_SERVICE_I**, inherit from **WS_LAUNCHABLE_SERVICE** and has a formal generic that should conform to **WSF_SERVICE_LAUNCHER [WSF_EXECUTION]**. Below a [BON diagram](http://www.bon-method.com/index_normal.htm) showing one of the possible options.
|
||||
|
||||

|
||||
Other connectors:
|
||||
|
||||
**WSF_STANDALONE_SERVICE_LAUNCHER**
|
||||
**WSF_CGI_SERVICE_LAUNCHER**
|
||||
**WSF_NINO_SERVICE_LAUNCHER**
|
||||
**WSF_LIBFCGI_SERVICE_LAUNCHER**
|
||||
|
||||
A basic EWF service inherits from **WSF_DEFAULT_SERVICE**, which has a formal generic that should conform to **WSF_EXECUTION** class with a `make' creation procedure, in our case the class **APPLICATION_EXECUTION**.
|
||||
|
||||
The **APPLICATION_EXECUTION** class inherits from **WSF_EXECUTION** interface, which is instantiated for each incoming request. **WSF_EXECUTION** inherit from **WGI_EXECUTION** which is the low level entry point in EWF, handling each incoming request with a single procedure ```execute (req: WSF_REQUEST; res: WSF_RESPONSE) ...```.
|
||||
|
||||
In the **APPLICATION_EXECUTION** class class you will need to implement implement the **execute** feature, get data from the request *req* and write the response in *res*.
|
||||
|
||||

|
||||
|
||||
The WSF_EXECUTION instance, in this case ```APPLICATION_EXECUTION``` is created per request, with two main attributes request: ```WSF_REQUEST``` and response: ```WSF_RESPONSE```.
|
||||
|
||||
<a name="text"></a>
|
||||
## A simple Service to Generate Plain Text.
|
||||
|
||||
Before to continue, it is recommended to review the getting started guided. In the example we will only shows the implementation of the WSF_EXECUTION interface.
|
||||
|
||||
```eiffel
|
||||
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
|
||||
|
||||
```
|
||||
<a name="source_1"></a>
|
||||
##### Source code
|
||||
The source code is available on Github. You can get it by running the command:
|
||||
|
||||
```git clone https://github.com/EiffelWebFramework/ewf.git```
|
||||
|
||||
The example of simple service that generate plain text response is located in the directory $PATH/ewf/doc/workbook/basics/simple, where $PATH is where you run ```git clone``` . Just double click on the simple.ecf file and select the simple_nino target or if you prefer the command line, run the command:
|
||||
|
||||
```estudio -config simple.ecf -target simple_nino```
|
||||
|
||||
<a name="html"></a>
|
||||
## A Service to Generate HTML.
|
||||
To generate HTML, it's needed
|
||||
|
||||
1. Change the Content-Type : "text/html"
|
||||
2. Build an HTML page
|
||||
|
||||
```eiffel
|
||||
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>
|
||||
Hello World
|
||||
</body>
|
||||
</html>
|
||||
]"
|
||||
|
||||
end
|
||||
```
|
||||
##### Source code
|
||||
The source code is available on Github. You can get it by running the command:
|
||||
|
||||
```git clone https://github.com/EiffelWebFramework/ewf.git```
|
||||
|
||||
The example of the service that generates HTML is located in the directory $PATH/ewf/doc/workbook/basics/simple_html, where $PATH is where you run ```git clone``` . Just double click on the simple_html.ecf file and select the simple_html_nino target or if you prefer the command line, run the command:
|
||||
|
||||
```estudio -config simple_html.ecf -target simple_html_nino```
|
||||
|
||||
Nav: [Workbook](../../workbook) | [Handling Requests: Form/Query Parameter](../../handling_request/form)
|
||||
|
||||
24
workbook/basics/simple/application.e
Normal file
24
workbook/basics/simple/application.e
Normal file
@@ -0,0 +1,24 @@
|
||||
note
|
||||
description: "Basic Service launcher"
|
||||
|
||||
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
|
||||
25
workbook/basics/simple/application_execution.e
Normal file
25
workbook/basics/simple/application_execution.e
Normal file
@@ -0,0 +1,25 @@
|
||||
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
|
||||
50
workbook/basics/simple/simple.ecf
Normal file
50
workbook/basics/simple/simple.ecf
Normal file
@@ -0,0 +1,50 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<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="simple" uuid="C28C4F53-9963-46C0-A080-8F13E94E7486" library_target="simple">
|
||||
<target name="common" abstract="true">
|
||||
<file_rule>
|
||||
<exclude>/EIFGENs$</exclude>
|
||||
<exclude>/CVS$</exclude>
|
||||
<exclude>/.svn$</exclude>
|
||||
</file_rule>
|
||||
<option warning="true" full_class_checking="false" is_attached_by_default="true" void_safety="transitional" syntax="transitional">
|
||||
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
|
||||
</option>
|
||||
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
|
||||
<library name="http" location="$ISE_LIBRARY\contrib\library\network\protocol\http\http-safe.ecf"/>
|
||||
<library name="wsf" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\wsf-safe.ecf"/>
|
||||
</target>
|
||||
<target name="simple_nino" extends="common">
|
||||
<root class="APPLICATION" feature="make_and_launch"/>
|
||||
<option warning="true" is_attached_by_default="true" void_safety="transitional" syntax="transitional">
|
||||
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
|
||||
</option>
|
||||
<library name="default_nino" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\default\nino-safe.ecf"/>
|
||||
<cluster name="simple" location=".\" recursive="true"/>
|
||||
</target>
|
||||
<target name="simple_cgi" extends="common">
|
||||
<root class="APPLICATION" feature="make_and_launch"/>
|
||||
<option warning="true" is_attached_by_default="true" void_safety="transitional" syntax="transitional">
|
||||
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
|
||||
</option>
|
||||
<library name="default_cgi" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\default\cgi-safe.ecf"/>
|
||||
<cluster name="simple" location=".\" recursive="true"/>
|
||||
</target>
|
||||
<target name="simple_libfcgi" extends="common">
|
||||
<root class="APPLICATION" feature="make_and_launch"/>
|
||||
<option warning="true" is_attached_by_default="true" void_safety="transitional" syntax="transitional">
|
||||
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
|
||||
</option>
|
||||
<library name="default_libfcgi" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\default\libfcgi-safe.ecf"/>
|
||||
<cluster name="simple" location=".\" recursive="true"/>
|
||||
</target>
|
||||
<target name="simple_standalone" extends="common">
|
||||
<root class="APPLICATION" feature="make_and_launch"/>
|
||||
<option warning="true" is_attached_by_default="true" void_safety="transitional" syntax="transitional">
|
||||
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
|
||||
</option>
|
||||
<library name="default_standalone" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\default\standalone-safe.ecf"/>
|
||||
<cluster name="simple" location=".\" recursive="true"/>
|
||||
</target>
|
||||
<target name="simple" extends="simple_nino">
|
||||
</target>
|
||||
</system>
|
||||
57
workbook/basics/simple_html/apache_config/Readme.md
Normal file
57
workbook/basics/simple_html/apache_config/Readme.md
Normal file
@@ -0,0 +1,57 @@
|
||||
---
|
||||
layout: default
|
||||
title: Readme
|
||||
base_url: ../../../../../
|
||||
---
|
||||
##Run simple_html example on Apache with FCGI on Windows.
|
||||
|
||||
|
||||
|
||||
####Prerequisites
|
||||
|
||||
* This tutorial was written for people working under Windows environment, and using Apache Server with FCGI connector
|
||||
* Compile the ewf application from command line.
|
||||
* Assuming you have installed Apache Server under C:/home/server/Apache24.
|
||||
* Assuming you have placed your current project under C:/home/server/Apache24/fcgi-bin.
|
||||
* Assuming you have setted the Listen to 8888, the defautl value is 80 .
|
||||
|
||||
|
||||
|
||||
####FCGI module
|
||||
If you don't have the FCGI module installed, you can get it from https://www.apachelounge.com/download/, download the module based on your platform [modules-2.4-win64-VC11.zip](https://www.apachelounge.com/download/VC11/modules/modules-2.4-win64-VC11.zip) or [modules-2.4-win32-VC11.zip](https://www.apachelounge.com/download/VC11/modules/modules-2.4-win32-VC11.zip), uncompress it
|
||||
and copy the _mod_fcgid.so_ to C:/home/server/Apache24/modules
|
||||
|
||||
####Compile the project simple_html using the fcgi connector.
|
||||
|
||||
ec -config simple_html.ecf -target simple_html_fcgi -finalize -c_compile -project_path .
|
||||
|
||||
Copy the genereted exe to C:/home/server/Apache24/fcgi-bin folder.
|
||||
|
||||
Check if you have _libfcgi.dll_ in your PATH.
|
||||
|
||||
|
||||
####Apache configuration
|
||||
Add to httpd.conf the content, you can get the configuration file [here](config.conf)
|
||||
|
||||
```
|
||||
LoadModule fcgid_module modules/mod_fcgid.so
|
||||
|
||||
<IfModule mod_fcgid.c>
|
||||
<Directory "C:/home/server/Apache24/fcgi-bin">
|
||||
SetHandler fcgid-script
|
||||
Options +ExecCGI +Includes +FollowSymLinks -Indexes
|
||||
AllowOverride All
|
||||
Require all granted
|
||||
</Directory>
|
||||
ScriptAlias /simple "C:/home/server/Apache24/fcgi-bin/simple_html.exe"
|
||||
</IfModule>
|
||||
```
|
||||
|
||||
Test if your httpd.conf is ok
|
||||
>httpd -t
|
||||
|
||||
Luanch the server
|
||||
>httpd
|
||||
|
||||
Check the application
|
||||
>http://localhost:8888/simple
|
||||
12
workbook/basics/simple_html/apache_config/config.conf
Normal file
12
workbook/basics/simple_html/apache_config/config.conf
Normal file
@@ -0,0 +1,12 @@
|
||||
LoadModule fcgid_module modules/mod_fcgid.so
|
||||
|
||||
<IfModule mod_fcgid.c>
|
||||
<Directory "C:/home/server/Apache24/fcgi-bin">
|
||||
SetHandler fcgid-script
|
||||
Options +ExecCGI +Includes +FollowSymLinks -Indexes
|
||||
AllowOverride All
|
||||
Require all granted
|
||||
</Directory>
|
||||
ScriptAlias /simple "C:/home/server/Apache24/fcgi-bin/simple_html.exe"
|
||||
</IfModule>
|
||||
|
||||
24
workbook/basics/simple_html/application.e
Normal file
24
workbook/basics/simple_html/application.e
Normal file
@@ -0,0 +1,24 @@
|
||||
note
|
||||
description: "Basic Service launcher"
|
||||
|
||||
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
|
||||
66
workbook/basics/simple_html/application_execution.e
Normal file
66
workbook/basics/simple_html/application_execution.e
Normal 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
|
||||
42
workbook/basics/simple_html/simple_html.ecf
Normal file
42
workbook/basics/simple_html/simple_html.ecf
Normal file
@@ -0,0 +1,42 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-10-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-10-0 http://www.eiffel.com/developers/xml/configuration-1-10-0.xsd" name="simple_html" uuid="C28C4F53-9963-46C0-A080-8F13E94E7486" library_target="simple_html">
|
||||
<target name="common" abstract="true">
|
||||
<file_rule>
|
||||
<exclude>/EIFGENs$</exclude>
|
||||
<exclude>/CVS$</exclude>
|
||||
<exclude>/.svn$</exclude>
|
||||
</file_rule>
|
||||
<option warning="true" is_attached_by_default="true" void_safety="all" syntax="transitional">
|
||||
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
|
||||
</option>
|
||||
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
|
||||
<library name="http" location="$ISE_LIBRARY\contrib\library\network\protocol\http\http-safe.ecf"/>
|
||||
<library name="wsf" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\wsf-safe.ecf"/>
|
||||
</target>
|
||||
<target name="simple_html_nino" extends="common">
|
||||
<root class="APPLICATION" feature="make_and_launch"/>
|
||||
<option warning="true" is_attached_by_default="true" void_safety="all" syntax="transitional">
|
||||
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
|
||||
</option>
|
||||
<library name="default_nino" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\default\nino-safe.ecf"/>
|
||||
<cluster name="simple_html" location=".\" recursive="true"/>
|
||||
</target>
|
||||
<target name="simple_html_cgi" extends="common">
|
||||
<root class="APPLICATION" feature="make_and_launch"/>
|
||||
<option warning="true" is_attached_by_default="true" void_safety="all" syntax="transitional">
|
||||
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
|
||||
</option>
|
||||
<library name="default_cgi" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\default\cgi-safe.ecf"/>
|
||||
<cluster name="simple_html" location=".\" recursive="true"/>
|
||||
</target>
|
||||
<target name="simple_html_libfcgi" extends="common">
|
||||
<root class="APPLICATION" feature="make_and_launch"/>
|
||||
<option warning="true" is_attached_by_default="true" void_safety="all" syntax="transitional">
|
||||
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
|
||||
</option>
|
||||
<library name="default_libfcgi" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\default\libfcgi-safe.ecf"/>
|
||||
<cluster name="simple_html" location=".\" recursive="true"/>
|
||||
</target>
|
||||
<target name="simple_html" extends="simple_html_nino">
|
||||
</target>
|
||||
</system>
|
||||
168
workbook/deployment/readme.md
Normal file
168
workbook/deployment/readme.md
Normal file
@@ -0,0 +1,168 @@
|
||||
---
|
||||
layout: default
|
||||
title: readme
|
||||
base_url: ../../../
|
||||
---
|
||||
Nav: [Workbook](../../workbook)
|
||||
|
||||
EWF Deployment
|
||||
==============
|
||||
|
||||
#Apache on Windows#
|
||||
|
||||
1. Apache Install
|
||||
2. Deploying EWF CGI
|
||||
3. CGI overview
|
||||
1. Build EWF application
|
||||
2. Copy the generated exe file and the www content .htaccess CGI
|
||||
4. Deploying EWF FCGI
|
||||
5. FCGI overview
|
||||
1. Build EWF application
|
||||
2. Copy the generated exe file and the www content.htaccess CGI
|
||||
|
||||
|
||||
|
||||
##Apache on Windows
|
||||
|
||||
###Apache Install
|
||||
|
||||
>Check the correct version (Win 32 or Win64)
|
||||
>Apache Version: Apache 2.4.4
|
||||
>Windows: http://www.apachelounge.com/download/
|
||||
|
||||
####Deploying EWF CGI
|
||||
|
||||
####CGI overview
|
||||
>A new process is started for each HTTP request. So if there are N requests to the same >CGI program, the code of the CGI program is loaded into memory N times.
|
||||
>When a CGI program finishes handling a request, the program terminates.
|
||||
|
||||
* Build EWF application
|
||||
|
||||
ec -config [app.ecf] -target [app_cgi] -finalize -c_compile -project_path
|
||||
|
||||
|
||||
>Note: change app.ecf and target app_cgi based on your own configuration.
|
||||
|
||||
* Copy the generated exe file and the www content
|
||||
|
||||
Copy the app.exe and the folder _www_ into a folder served by apache2, for example under.
|
||||
|
||||
|
||||
<APACHE_PATH>/htdocs.
|
||||
|
||||
<APACHE_PATH> = path to your apache installation
|
||||
|
||||
Edit httpd.conf under c:/<APACHE_PATH>/conf
|
||||
|
||||
DocumentRoot "c:/<APACHE_PATH>/htdocs"
|
||||
|
||||
<Directory "c:/<APACHE_PATH>/htdocs">
|
||||
AllowOverride All --
|
||||
Require all granted -- this is required in Apache 2.4.4
|
||||
</Directory>
|
||||
|
||||
Check that you have the following modules enabled
|
||||
|
||||
LoadModule cgi_module modules/mod_cgi.so
|
||||
LoadModule rewrite_module modules/mod_rewrite.so
|
||||
|
||||
####Tip:
|
||||
>To check the syntax of your httpd.conf file. From command line run the following
|
||||
|
||||
$>httpd - t
|
||||
|
||||
|
||||
>.htaccess CGI
|
||||
http://perishablepress.com/stupid-htaccess-tricks/
|
||||
|
||||
####.htaccess
|
||||
|
||||
Options +ExecCGI +Includes +FollowSymLinks -Indexes
|
||||
AddHandler cgi-script exe
|
||||
|
||||
<IfModule mod_rewrite.c>
|
||||
RewriteEngine on
|
||||
|
||||
RewriteRule ^$ $service [L]
|
||||
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteCond %{REQUEST_FILENAME} !-d
|
||||
RewriteCond %{REQUEST_URI} !$service
|
||||
RewriteRule ^(.*)$ $service/$1
|
||||
|
||||
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization},L]
|
||||
</IfModule
|
||||
|
||||
>Replace $service with the name of your executable service, for example app_service.exe
|
||||
|
||||
|
||||
####Deploying EWF FCGI
|
||||
>To deploy FCGI you will need to download the mod_fcgi module.
|
||||
>You can get it from here http://www.apachelounge.com/download/
|
||||
|
||||
####FCGI overview
|
||||
>FastCGI allows a single, long-running process to handle more than one user request while keeping close to the CGI programming model, retaining the simplicity while eliminating the overhead of creating a new process for each request. Unlike converting an application to a web server plug-in, FastCGI applications remain independent of the web server.
|
||||
|
||||
* Build EWF application
|
||||
|
||||
ec -config [app.ecf] -target [app_fcgi] -finalize -c_compile -project_path .
|
||||
|
||||
>Note: change app.ecf and target app_fcgi based on your own configuration.
|
||||
|
||||
* Copy the generated exe file and the www content
|
||||
|
||||
Copy the app.exe and the folder "www" into a folder served by apache2, for example under
|
||||
|
||||
<APACHE_PATH>/htdocs.
|
||||
|
||||
<APACHE_PATH> = path to your apache installation
|
||||
|
||||
Edit httpd.conf under c:/<APACHE_PATH>/conf
|
||||
|
||||
DocumentRoot "c:/<APACHE_PATH>/htdocs"
|
||||
|
||||
<Directory "c:/<APACHE_PATH>/htdocs">
|
||||
AllowOverride All --
|
||||
Require all granted -- this is required in Apache 2.4.4
|
||||
</Directory>
|
||||
|
||||
>Check that you have the following modules enabled
|
||||
|
||||
LoadModule rewrite_module modules/mod_rewrite.so
|
||||
LoadModule fcgid_module modules/mod_fcgid.so
|
||||
|
||||
>NOTE: By default Apache does not come with fcgid module, so you will need to download it, and put the module under Apache2/modules
|
||||
|
||||
#.htaccess FCGI
|
||||
>http://perishablepress.com/stupid-htaccess-tricks/
|
||||
|
||||
####.htaccess
|
||||
|
||||
Options +ExecCGI +Includes +FollowSymLinks -Indexes
|
||||
|
||||
<IfModule mod_fcgid.c>
|
||||
AddHandler fcgid-script .ews
|
||||
FcgidWrapper $FULL_PATH/$service .ews
|
||||
</IfModule>
|
||||
|
||||
|
||||
<IfModule mod_rewrite.c>
|
||||
RewriteEngine on
|
||||
|
||||
RewriteBase /
|
||||
RewriteRule ^$ service.ews [L]
|
||||
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteCond %{REQUEST_FILENAME} !-d
|
||||
RewriteCond %{REQUEST_URI} !=/favicon.ico
|
||||
RewriteCond %{REQUEST_URI} !service.ews
|
||||
RewriteRule ^(.*)$ service.ews/$1
|
||||
|
||||
|
||||
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization},L]
|
||||
</IfModule>
|
||||
|
||||
Replace $service with the name of your executable $service, for example app_service.exe
|
||||
You will need to create an service.ews file, this file will be located at the same place where you copy your app service executable.
|
||||
|
||||
Nav: [Workbook](../../workbook)
|
||||
24
workbook/generating_response/exel/application.e
Normal file
24
workbook/generating_response/exel/application.e
Normal file
@@ -0,0 +1,24 @@
|
||||
note
|
||||
description: "Basic Service launcher"
|
||||
|
||||
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
|
||||
36
workbook/generating_response/exel/application_execution.e
Normal file
36
workbook/generating_response/exel/application_execution.e
Normal 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
|
||||
42
workbook/generating_response/exel/exel.ecf
Normal file
42
workbook/generating_response/exel/exel.ecf
Normal file
@@ -0,0 +1,42 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-10-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-10-0 http://www.eiffel.com/developers/xml/configuration-1-10-0.xsd" name="exel" uuid="C28C4F53-9963-46C0-A080-8F13E94E7486" library_target="exel">l
|
||||
<target name="common" abstract="true">
|
||||
<file_rule>
|
||||
<exclude>/EIFGENs$</exclude>
|
||||
<exclude>/CVS$</exclude>
|
||||
<exclude>/.svn$</exclude>
|
||||
</file_rule>
|
||||
<option warning="true" is_attached_by_default="true" void_safety="all" syntax="transitional">
|
||||
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
|
||||
</option>
|
||||
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
|
||||
<library name="http" location="$ISE_LIBRARY\contrib\library\network\protocol\http\http-safe.ecf"/>
|
||||
<library name="wsf" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\wsf-safe.ecf"/>
|
||||
</target>
|
||||
<target name="exel_nino" extends="common">
|
||||
<root class="APPLICATION" feature="make_and_launch"/>
|
||||
<option warning="true" is_attached_by_default="true" void_safety="all" syntax="transitional">
|
||||
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
|
||||
</option>
|
||||
<library name="default_nino" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\default\nino-safe.ecf"/>
|
||||
<cluster name="exel" location=".\" recursive="true"/>
|
||||
</target>
|
||||
<target name="exel_cgi" extends="common">
|
||||
<root class="APPLICATION" feature="make_and_launch"/>
|
||||
<option warning="true" is_attached_by_default="true" void_safety="all" syntax="transitional">
|
||||
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
|
||||
</option>
|
||||
<library name="default_cgi" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\default\cgi-safe.ecf"/>
|
||||
<cluster name="exel" location=".\" recursive="true"/>
|
||||
</target>
|
||||
<target name="exel_libfcgi" extends="common">
|
||||
<root class="APPLICATION" feature="make_and_launch"/>
|
||||
<option warning="true" is_attached_by_default="true" void_safety="all" syntax="transitional">
|
||||
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
|
||||
</option>
|
||||
<library name="default_libfcgi" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\default\libfcgi-safe.ecf"/>
|
||||
<cluster name="exel" location=".\" recursive="true"/>
|
||||
</target>
|
||||
<target name="exel" extends="exel_nino">
|
||||
</target>
|
||||
</system>
|
||||
1004
workbook/generating_response/generating_response.md
Normal file
1004
workbook/generating_response/generating_response.md
Normal file
File diff suppressed because it is too large
Load Diff
24
workbook/generating_response/headers/application.e
Normal file
24
workbook/generating_response/headers/application.e
Normal file
@@ -0,0 +1,24 @@
|
||||
note
|
||||
description: "Basic Service launcher"
|
||||
|
||||
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
|
||||
57
workbook/generating_response/headers/application_execution.e
Normal file
57
workbook/generating_response/headers/application_execution.e
Normal 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
|
||||
42
workbook/generating_response/headers/headers.ecf
Normal file
42
workbook/generating_response/headers/headers.ecf
Normal file
@@ -0,0 +1,42 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-10-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-10-0 http://www.eiffel.com/developers/xml/configuration-1-10-0.xsd" name="headers" uuid="C28C4F53-9963-46C0-A080-8F13E94E7486" library_target="headers">
|
||||
<target name="common" abstract="true">
|
||||
<file_rule>
|
||||
<exclude>/EIFGENs$</exclude>
|
||||
<exclude>/CVS$</exclude>
|
||||
<exclude>/.svn$</exclude>
|
||||
</file_rule>
|
||||
<option warning="true" is_attached_by_default="true" void_safety="all" syntax="transitional">
|
||||
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
|
||||
</option>
|
||||
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
|
||||
<library name="http" location="$ISE_LIBRARY\contrib\library\network\protocol\http\http-safe.ecf"/>
|
||||
<library name="wsf" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\wsf-safe.ecf"/>
|
||||
</target>
|
||||
<target name="headers_nino" extends="common">
|
||||
<root class="APPLICATION" feature="make_and_launch"/>
|
||||
<option warning="true" is_attached_by_default="true" void_safety="all" syntax="transitional">
|
||||
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
|
||||
</option>
|
||||
<library name="default_nino" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\default\nino-safe.ecf"/>
|
||||
<cluster name="headers" location=".\" recursive="true"/>
|
||||
</target>
|
||||
<target name="headers_cgi" extends="common">
|
||||
<root class="APPLICATION" feature="make_and_launch"/>
|
||||
<option warning="true" is_attached_by_default="true" void_safety="all" syntax="transitional">
|
||||
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
|
||||
</option>
|
||||
<library name="default_cgi" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\default\cgi-safe.ecf"/>
|
||||
<cluster name="headers" location=".\" recursive="true"/>
|
||||
</target>
|
||||
<target name="headers_libfcgi" extends="common">
|
||||
<root class="APPLICATION" feature="make_and_launch"/>
|
||||
<option warning="true" is_attached_by_default="true" void_safety="all" syntax="transitional">
|
||||
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
|
||||
</option>
|
||||
<library name="default_libfcgi" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\default\libfcgi-safe.ecf"/>
|
||||
<cluster name="headers" location=".\" recursive="true"/>
|
||||
</target>
|
||||
<target name="headers" extends="headers_nino">
|
||||
</target>
|
||||
</system>
|
||||
24
workbook/generating_response/search/application.e
Normal file
24
workbook/generating_response/search/application.e
Normal file
@@ -0,0 +1,24 @@
|
||||
note
|
||||
description: "Basic Service launcher"
|
||||
|
||||
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
|
||||
172
workbook/generating_response/search/application_execution.e
Normal file
172
workbook/generating_response/search/application_execution.e
Normal 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
|
||||
42
workbook/generating_response/search/search.ecf
Normal file
42
workbook/generating_response/search/search.ecf
Normal file
@@ -0,0 +1,42 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-10-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-10-0 http://www.eiffel.com/developers/xml/configuration-1-10-0.xsd" name="search" uuid="C28C4F53-9963-46C0-A080-8F13E94E7486" library_target="search">
|
||||
<target name="common" abstract="true">
|
||||
<file_rule>
|
||||
<exclude>/EIFGENs$</exclude>
|
||||
<exclude>/CVS$</exclude>
|
||||
<exclude>/.svn$</exclude>
|
||||
</file_rule>
|
||||
<option warning="true" is_attached_by_default="true" void_safety="all" syntax="transitional">
|
||||
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
|
||||
</option>
|
||||
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
|
||||
<library name="http" location="$ISE_LIBRARY\contrib\library\network\protocol\http\http-safe.ecf"/>
|
||||
<library name="wsf" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\wsf-safe.ecf"/>
|
||||
</target>
|
||||
<target name="search_nino" extends="common">
|
||||
<root class="APPLICATION" feature="make_and_launch"/>
|
||||
<option warning="true" is_attached_by_default="true" void_safety="all" syntax="transitional">
|
||||
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
|
||||
</option>
|
||||
<library name="default_nino" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\default\nino-safe.ecf"/>
|
||||
<cluster name="search" location=".\" recursive="true"/>
|
||||
</target>
|
||||
<target name="search_cgi" extends="common">
|
||||
<root class="APPLICATION" feature="make_and_launch"/>
|
||||
<option warning="true" is_attached_by_default="true" void_safety="all" syntax="transitional">
|
||||
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
|
||||
</option>
|
||||
<library name="default_cgi" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\default\cgi-safe.ecf"/>
|
||||
<cluster name="search" location=".\" recursive="true"/>
|
||||
</target>
|
||||
<target name="search_libfcgi" extends="common">
|
||||
<root class="APPLICATION" feature="make_and_launch"/>
|
||||
<option warning="true" is_attached_by_default="true" void_safety="all" syntax="transitional">
|
||||
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
|
||||
</option>
|
||||
<library name="default_libfcgi" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\default\libfcgi-safe.ecf"/>
|
||||
<cluster name="search" location=".\" recursive="true"/>
|
||||
</target>
|
||||
<target name="search" extends="search_nino">
|
||||
</target>
|
||||
</system>
|
||||
24
workbook/generating_response/status/application.e
Normal file
24
workbook/generating_response/status/application.e
Normal file
@@ -0,0 +1,24 @@
|
||||
note
|
||||
description: "Basic Service launcher"
|
||||
|
||||
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
|
||||
138
workbook/generating_response/status/application_execution.e
Normal file
138
workbook/generating_response/status/application_execution.e
Normal 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
|
||||
42
workbook/generating_response/status/status.ecf
Normal file
42
workbook/generating_response/status/status.ecf
Normal file
@@ -0,0 +1,42 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-10-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-10-0 http://www.eiffel.com/developers/xml/configuration-1-10-0.xsd" name="status" uuid="C28C4F53-9963-46C0-A080-8F13E94E7486" library_target="status">l
|
||||
<target name="common" abstract="true">
|
||||
<file_rule>
|
||||
<exclude>/EIFGENs$</exclude>
|
||||
<exclude>/CVS$</exclude>
|
||||
<exclude>/.svn$</exclude>
|
||||
</file_rule>
|
||||
<option warning="true" is_attached_by_default="true" void_safety="all" syntax="transitional">
|
||||
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
|
||||
</option>
|
||||
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
|
||||
<library name="http" location="$ISE_LIBRARY\contrib\library\network\protocol\http\http-safe.ecf"/>
|
||||
<library name="wsf" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\wsf-safe.ecf"/>
|
||||
</target>
|
||||
<target name="status_nino" extends="common">
|
||||
<root class="APPLICATION" feature="make_and_launch"/>
|
||||
<option warning="true" is_attached_by_default="true" void_safety="all" syntax="transitional">
|
||||
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
|
||||
</option>
|
||||
<library name="default_nino" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\default\nino-safe.ecf"/>
|
||||
<cluster name="status" location=".\" recursive="true"/>
|
||||
</target>
|
||||
<target name="status_cgi" extends="common">
|
||||
<root class="APPLICATION" feature="make_and_launch"/>
|
||||
<option warning="true" is_attached_by_default="true" void_safety="all" syntax="transitional">
|
||||
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
|
||||
</option>
|
||||
<library name="default_cgi" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\default\cgi-safe.ecf"/>
|
||||
<cluster name="status" location=".\" recursive="true"/>
|
||||
</target>
|
||||
<target name="status_libfcgi" extends="common">
|
||||
<root class="APPLICATION" feature="make_and_launch"/>
|
||||
<option warning="true" is_attached_by_default="true" void_safety="all" syntax="transitional">
|
||||
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
|
||||
</option>
|
||||
<library name="default_libfcgi" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\default\libfcgi-safe.ecf"/>
|
||||
<cluster name="status" location=".\" recursive="true"/>
|
||||
</target>
|
||||
<target name="status" extends="status_nino">
|
||||
</target>
|
||||
</system>
|
||||
24
workbook/handling_cookies/example/application.e
Normal file
24
workbook/handling_cookies/example/application.e
Normal file
@@ -0,0 +1,24 @@
|
||||
note
|
||||
description: "Basic Service launcher"
|
||||
|
||||
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
|
||||
141
workbook/handling_cookies/example/application_execution.e
Normal file
141
workbook/handling_cookies/example/application_execution.e
Normal 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
|
||||
42
workbook/handling_cookies/example/example.ecf
Normal file
42
workbook/handling_cookies/example/example.ecf
Normal file
@@ -0,0 +1,42 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<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="example" uuid="C28C4F53-9963-46C0-A080-8F13E94E7486" library_target="example">
|
||||
<target name="common" abstract="true">
|
||||
<file_rule>
|
||||
<exclude>/EIFGENs$</exclude>
|
||||
<exclude>/CVS$</exclude>
|
||||
<exclude>/.svn$</exclude>
|
||||
</file_rule>
|
||||
<option warning="true" full_class_checking="false" is_attached_by_default="true" void_safety="transitional" syntax="transitional">
|
||||
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
|
||||
</option>
|
||||
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
|
||||
<library name="http" location="$ISE_LIBRARY\contrib\library\network\protocol\http\http-safe.ecf"/>
|
||||
<library name="wsf" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\wsf-safe.ecf"/>
|
||||
</target>
|
||||
<target name="example_nino" extends="common">
|
||||
<root class="APPLICATION" feature="make_and_launch"/>
|
||||
<option warning="true" is_attached_by_default="true" void_safety="transitional" syntax="transitional">
|
||||
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
|
||||
</option>
|
||||
<library name="default_nino" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\default\nino-safe.ecf"/>
|
||||
<cluster name="example" location=".\" recursive="true"/>
|
||||
</target>
|
||||
<target name="example_cgi" extends="common">
|
||||
<root class="APPLICATION" feature="make_and_launch"/>
|
||||
<option warning="true" is_attached_by_default="true" void_safety="transitional" syntax="transitional">
|
||||
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
|
||||
</option>
|
||||
<library name="default_cgi" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\default\cgi-safe.ecf"/>
|
||||
<cluster name="example" location=".\" recursive="true"/>
|
||||
</target>
|
||||
<target name="example_libfcgi" extends="common">
|
||||
<root class="APPLICATION" feature="make_and_launch"/>
|
||||
<option warning="true" is_attached_by_default="true" void_safety="transitional" syntax="transitional">
|
||||
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
|
||||
</option>
|
||||
<library name="default_libfcgi" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\default\libfcgi-safe.ecf"/>
|
||||
<cluster name="example" location=".\" recursive="true"/>
|
||||
</target>
|
||||
<target name="example" extends="example_nino">
|
||||
</target>
|
||||
</system>
|
||||
293
workbook/handling_cookies/handling_cookies.md
Normal file
293
workbook/handling_cookies/handling_cookies.md
Normal file
@@ -0,0 +1,293 @@
|
||||
---
|
||||
layout: default
|
||||
title: handling cookies
|
||||
base_url: ../../../
|
||||
---
|
||||
Nav: [Workbook](../../workbook) | [Generating Responses](../../generating_response/generating_response)
|
||||
|
||||
# Handling Cookies
|
||||
|
||||
- [Cookie](#cookie)
|
||||
- [Cookie Porperties](#properties)
|
||||
- [Write and Read Cookies](#set_get)
|
||||
- [How to set a cookie](#set_cookie)
|
||||
- [How to read a cookie](#read_cookie)
|
||||
- [Examples](#examples)
|
||||
|
||||
<a name="cookie"></a>
|
||||
## Cookie
|
||||
A [cookie](http://httpwg.github.io/specs/rfc6265.html) is a piece of data that can be stored in a browser's cache. If you visit a web site and then revisit it, the cookie data can be used to identify you as a return visitor. Cookies enable state information, such as an online shopping cart, to be remembered. A cookie can be short term, holding data for a single web session, that is, until you close the browser, or a cookie can be longer term, holding data for a week or a year.
|
||||
|
||||
Cookies are used a lot in web client-server communication.
|
||||
|
||||
- HTTP State Management With Cookies
|
||||
|
||||
- Personalized response to the client based on their preference, for example we can set background color as cookie in client browser and then use it to customize response background color, image etc.
|
||||
|
||||
Server send cookies to the client
|
||||
|
||||
>Set-Cookie: _Framework=EWF; Path=/; Expires=Tue, 10 Mar 2015 13:28:10 GMT; HttpOnly%R
|
||||
|
||||
|
||||
Client send cookies to server
|
||||
|
||||
>Cookie: _Framework=EWF
|
||||
|
||||
|
||||
|
||||
<a name="properties"></a>
|
||||
### Cookie properties
|
||||
|
||||
- Comment: describe the purpose of the cookie. Note that server doesn’t receive this information when client sends cookie in request header.
|
||||
- Domain: domain name for the cookie.
|
||||
- Expiration/MaxAge: Expiration time of the cookie, we could also set it in seconds. (At the moment Max-Age attribute is not supported)
|
||||
- Name: name of the cookie.
|
||||
- Path: path on the server to which the browser returns this cookie. Path instruct the browser to send cookie to a particular resource.
|
||||
- Secure: True, if the browser is sending cookies only over a secure protocol, False in other case.
|
||||
- Value: Value of th cookie as string.
|
||||
- HttpOnly: Checks whether this Cookie has been marked as HttpOnly.
|
||||
- Version:
|
||||
|
||||
<a name="set_get"></a>
|
||||
## Write and Read Cookies.
|
||||
|
||||
To send a cookie to the client we should use the [HTTP_HEADER] class, and call ```h.put_cookie``` feature or
|
||||
```h.put_cookie_with_expiration_date``` feature, see [How to set Cookies]() to learn the details, and the set it to response object [WSF_RESPONSE] as we saw previously.
|
||||
|
||||
We will show an example.
|
||||
|
||||
To Read incomming cookies we can read all the cookies with
|
||||
|
||||
```
|
||||
cookies: ITERABLE [WSF_VALUE]
|
||||
-- All cookies.
|
||||
```
|
||||
which return an interable of WSF_VALUE objects corresponding to the cookies the browser has associated with the web site.
|
||||
We can also check if a particular cookie by name using
|
||||
|
||||
```
|
||||
WSF_REQUEST.cookie (a_name: READABLE_STRING_GENERAL): detachable WSF_VALUE
|
||||
-- Field for name `a_name'.
|
||||
```
|
||||
feature.
|
||||
|
||||
|
||||
<a name="set_cookie"></a>
|
||||
### How to set Cookies
|
||||
Here we have the feature definitions to set cookies
|
||||
|
||||
```eiffel
|
||||
deferred class interface
|
||||
HTTP_HEADER_MODIFIER
|
||||
|
||||
feature -- Cookie
|
||||
|
||||
put_cookie (key, value: READABLE_STRING_8; expiration, path, domain: detachable READABLE_STRING_8; secure, http_only: BOOLEAN)
|
||||
-- Set a cookie on the client's machine
|
||||
-- with key 'key' and value 'value'.
|
||||
-- Note: you should avoid using "localhost" as `domain' for local cookies
|
||||
-- since they are not always handled by browser (for instance Chrome)
|
||||
require
|
||||
make_sense: (key /= Void and value /= Void) and then (not key.is_empty and not value.is_empty)
|
||||
domain_without_port_info: domain /= Void implies domain.index_of (':', 1) = 0
|
||||
|
||||
put_cookie_with_expiration_date (key, value: READABLE_STRING_8; expiration: DATE_TIME; path, domain: detachable READABLE_STRING_8; secure, http_only: BOOLEAN)
|
||||
-- Set a cookie on the client's machine
|
||||
-- with key 'key' and value 'value'.
|
||||
require
|
||||
make_sense: (key /= Void and value /= Void) and then (not key.is_empty and not value.is_empty)
|
||||
```
|
||||
|
||||
Example of use:
|
||||
|
||||
```eiffel
|
||||
response_with_cookies (res: WSF_RESPONSE)
|
||||
local
|
||||
l_message: STRING
|
||||
l_header: HTTP_HEADER
|
||||
l_time: HTTP_DATE
|
||||
do
|
||||
create l_header.make
|
||||
create l_time.make_now_utc
|
||||
l_time.date_time.day_add (40)
|
||||
l_header.put_content_type_text_html
|
||||
l_header.put_cookie_with_expiration_date ("EWFCookie", "EXAMPLE",l_time.date_time, "/", Void, False, True)
|
||||
res.put_header_text (l_header.string)
|
||||
res.put_string (web_page)
|
||||
end
|
||||
```
|
||||
<a name="read_cookie"></a>
|
||||
### How to read Cookies
|
||||
|
||||
Reading a particular cookie
|
||||
```eiffel
|
||||
if req.cookie ("EWFCookie") = Void then
|
||||
do_something
|
||||
end
|
||||
````
|
||||
|
||||
Reading all the cookies
|
||||
|
||||
```Eiffel
|
||||
across req.cookies as ic loop
|
||||
print (ic.item.name)
|
||||
end
|
||||
```
|
||||
|
||||
|
||||
<a name="examples"></a>
|
||||
### Example
|
||||
The following EWF service shows a basic use of cookies.
|
||||
1. It display a message to first-time visitors.
|
||||
2. Display a welcome back message if a visitor return.
|
||||
3. A visitor page, counting the number of visits to the page (track user access counts).
|
||||
4. A cookie with an expiration of 120 seconds.
|
||||
5. A cookie with an session level, valid in browser session.
|
||||
|
||||
```eiffel
|
||||
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 (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
|
||||
|
||||
```
|
||||
|
||||
|
||||
Nav: [Workbook](../../workbook) | [Generating Responses](../../generating_response/generating_response)
|
||||
311
workbook/handling_request/form.md
Normal file
311
workbook/handling_request/form.md
Normal file
@@ -0,0 +1,311 @@
|
||||
---
|
||||
layout: default
|
||||
title: form
|
||||
base_url: ../../../
|
||||
---
|
||||
Nav: [Workbook](../../workbook) | [Basic Concepts](../../basics/basics) | [Handling Requests: Header Fields](../headers)
|
||||
|
||||
|
||||
#Handling Requests: Form/Query Data
|
||||
|
||||
|
||||
##### Table of Contents
|
||||
- [Reading Form Data](#read)
|
||||
- [Query Parameters](#query)
|
||||
- [Form Parameters](#form_parameters)
|
||||
- [Uniform Read](#uniform)
|
||||
- [Reading Parameters and Values](#reading_pv)
|
||||
- [How to read all parameters names](#all_names)
|
||||
- [How to read single values](#single_values)
|
||||
- [How to read multiple values](#multiple_values)
|
||||
- [How to read table values](#table_values)
|
||||
- [Reading raw data](#raw_data)
|
||||
- [Upload Files](#upload)
|
||||
- [Examples](#examples)
|
||||
|
||||
|
||||
An HTML Form can handle GET and POST requests.
|
||||
When we use a form with method GET, the data is attached at the end of the url for example:
|
||||
|
||||
>http://wwww.example.com?key1=value1&...keyn=valuen
|
||||
|
||||
If we use the method POST, the data is sent to the server in a different line.
|
||||
|
||||
Extracting form data from the server side is one of the most tedious parts. If you do it by hand, you will need
|
||||
to parse the input, you'll have to URL-decode the value.
|
||||
|
||||
Here we will show you how to read input submitted by a user using a Form (GET and POST).
|
||||
* How to handle missing values:
|
||||
* client side validattion, server side validations, set default if it's a valid option.
|
||||
* How to populate Eiffel objects from the request data.
|
||||
|
||||
<a name="read"></a>
|
||||
## Reading Form Data
|
||||
EWF [WSF_REQUEST]() class, provides features to handling this form parsing automatically.
|
||||
|
||||
<a name="query"></a>
|
||||
### Query Parameters
|
||||
|
||||
WSF_REQUEST.query_parameters: ITERABLE [WSF_VALUE]
|
||||
-- All query parameters
|
||||
|
||||
WSF_REQUEST.query_parameter (a_name: READABLE_STRING_GENERAL): detachable WSF_VALUE
|
||||
-- Query parameter for name `a_name'.
|
||||
<a name="form_parameters"></a>
|
||||
### Form Parameters
|
||||
|
||||
WSF_REQUEST.form_parameters: ITERABLE [WSF_VALUE]
|
||||
-- All form parameters sent by a POST
|
||||
|
||||
WSF_REQUEST.form_parameter (a_name: READABLE_STRING_GENERAL): detachable WSF_VALUE
|
||||
-- Field for name `a_name'.
|
||||
|
||||
The values supplied to form_parameter and query_parameter are case sensitive.
|
||||
|
||||
<a name="uniform"></a>
|
||||
### Read Data
|
||||
The previous features, let you read the data one way for GET request and a different way for POST request. WSF_REQUEST provide a feature to read all the data in a uniform way.
|
||||
|
||||
WSF_REQUEST.item (a_name: READABLE_STRING_GENERAL): detachable WSF_VALUE
|
||||
-- Variable named `a_name' from any of the variables container
|
||||
-- and following a specific order: form_, query_ and path_ parameters
|
||||
|
||||
So, you use **WSF_REQUEST.item** feature exactly the same way for GET and POST request.
|
||||
|
||||
>Note: if a query parameter has the same name as a form paramenter req.item will retrieve the form paramenter. Remember the precedence: form > query > path
|
||||
|
||||
|
||||
<a name="reading_pv"></a>
|
||||
## Reading Parameters and Values
|
||||
|
||||
Suppose we have the following HTML5 form using Method POST. This HTML5 form has client side form validation using the new HTML5 attribute, you can do the same using Javascript. So in this case if the user does not fill the fields as expected the form will not be submitted to the server.
|
||||
|
||||
>Note: You want to validate on the server side because you can protect against the malicious user, who can easily bypass your JavaScript and submit dangerous input to the server.
|
||||
|
||||
```
|
||||
<h1> EWF Handling Client Request: Form example </h1>
|
||||
<form action="/" method="POST">
|
||||
<fieldset>
|
||||
<legend>Personal details</legend>
|
||||
<div>
|
||||
<label>First Name
|
||||
<input id="given-name" name="given-name" type="text" placeholder="First name only" required autofocus>
|
||||
</label>
|
||||
</div>
|
||||
<div>
|
||||
<label>Last Name
|
||||
<input id="family-name" name="family-name" type="text" placeholder="Last name only" required autofocus>
|
||||
</label>
|
||||
</div>
|
||||
<div>
|
||||
<label>Email
|
||||
<input id="email" name="email" type="email" placeholder="example@domain.com" required>
|
||||
</label>
|
||||
</div>
|
||||
<div>
|
||||
<label>Languages
|
||||
<input type="checkbox" name="languages" value="Spanish"> Spanish
|
||||
<input type="checkbox" name="languages" value="English"> English
|
||||
</label>
|
||||
</div>
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
<div>
|
||||
<button type=submit>Submit Form</button>
|
||||
</div>
|
||||
</fieldset>
|
||||
</form>
|
||||
```
|
||||
<a name="all_names"></a>
|
||||
### How to read all parameter names
|
||||
To read all the parameters names we simple call WSF_REQUEST.form_parameters.
|
||||
|
||||
```
|
||||
req: WSF_REQUEST
|
||||
across req.form_parameters as ic loop show_parameter_name (ic.item.key) end
|
||||
```
|
||||
<a name="single_values"></a>
|
||||
### How to read single values
|
||||
To read a particular parameter, a single value, for example `given-name', we simple call WSF_REQUEST.form_parameter (a_name) and we check if it's attached to WSF_STRING (represents a String parameter)
|
||||
```
|
||||
req: WSF_REQUEST
|
||||
if attached {WSF_STRING} req.form_paramenter ('given-name') as l_given_name then
|
||||
-- Work with the given parameter, for example populate an USER object
|
||||
-- the argument is case sensitive
|
||||
else
|
||||
-- Value missing, check the name against the HTML form
|
||||
end
|
||||
```
|
||||
<a name="multiple_values"></a>
|
||||
### How to read multiple values
|
||||
|
||||
To read multiple values, for example in the case of `languages', we simple call WSF_REQUEST.form_parameter (a_name) and we check if it's attached to WSF_MULTIPLE_STRING (represents a String parameter)
|
||||
|
||||
```
|
||||
req: WSF_REQUEST
|
||||
idioms: LIST[STRING]
|
||||
-- the argument is case sensitive
|
||||
if attached {WSF_MULTIPLE_STRING} req.form_paramenter ('languages') as l_languages then
|
||||
-- Work with the given parameter, for example populate an USER object
|
||||
-- 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_paramenter ('languages') as l_language then
|
||||
-- Value missing, check the name against the HTML form
|
||||
create {ARRAYED_LIST[STRING]} idioms.make (1)
|
||||
idioms.force (l_language.value)
|
||||
else
|
||||
-- Value missing
|
||||
end
|
||||
```
|
||||
In this case we are handling strings values, but in some cases you will need to do a conversion, betweend the strings that came from the request to map them to your domain model.
|
||||
|
||||
<a name="table_values"></a>
|
||||
### How to read table values
|
||||
This is particularly useful when you have a request with the following format
|
||||
|
||||
``` <a href="/link?tab[a]=1&tab[b]=2&tab[c]=foo"> ```
|
||||
|
||||
To read table values, for example in the case of `tab', we simple call WSF_REQUEST.form_parameter (a_name) and we check if it's attached to WSF_TABLE.
|
||||
|
||||
```
|
||||
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
|
||||
end
|
||||
```
|
||||
|
||||
<a name="raw_data"></a>
|
||||
## Reading Raw Data
|
||||
You can also access the data in raw format, it means you will need to parse and url-decode it, and also you will not be able to use the previous features, by default, to enable that you need to call `req.set_raw_input_data_recorded (True)'. This feature (reading raw data) is useful if you are reading POST data with JSON or XML formats, but it's not convinient for HTML forms.
|
||||
|
||||
To read raw data you need to do this
|
||||
|
||||
```
|
||||
l_raw_data:STRING
|
||||
|
||||
req.set_raw_input_data_recorded (True) --
|
||||
create l_raw_data.make_empty
|
||||
req.read_input_data_into (l_raw_data)
|
||||
```
|
||||
|
||||
> given-name=testr&family-name=test&dob=1976-08-26&email=test%40gmail.com&url=http%3A%2F%2Fwww.eiffelroom.com&phone=455555555555&languages=Spanish&languages=English
|
||||
|
||||
<a name="upload"></a>
|
||||
## Upload Files
|
||||
How can we read data when the date come from an uploaded file/s?.
|
||||
HTML supports a form element ```<input type="File" ... > ``` to upload a single file and ```<input type="File" ... multiplr> ``` to upload multiple files.
|
||||
|
||||
So supose we have the following form
|
||||
|
||||
```
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>EWF Handling Client Request: File Upload Example</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1> EWF Handling Client Request: File Upload Example</h1>
|
||||
<form action="/upload" enctype="multipart/form-data" method="POST">
|
||||
<fieldset>
|
||||
<legend>Upload file/s</legend>
|
||||
<div>
|
||||
<label>File
|
||||
<input name="file-name[]" type="file" multiple>
|
||||
</label>
|
||||
<fieldset>
|
||||
<div>
|
||||
<button type=submit>Send</button>
|
||||
</div>
|
||||
</fieldset>
|
||||
</form>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
The class WSF_REQUEST has defines mechanism to work with uploaded files. We can call the query
|
||||
|
||||
```
|
||||
WSF_REQUEST.has_uploaded_file: BOOLEAN
|
||||
-- Has any uploaded file?
|
||||
```
|
||||
|
||||
to check if the request form parameters has any uploaded file, and we can call the feature
|
||||
|
||||
```
|
||||
WSF_REQUEST.uploaded_files: ITERABLE [WSF_UPLOADED_FILE]
|
||||
-- uploaded files values
|
||||
--| filename: original path from the user
|
||||
--| type: content type
|
||||
--| tmp_name: path to temp file that resides on server
|
||||
--| tmp_base_name: basename of `tmp_name'
|
||||
--| error: if /= 0 , there was an error : TODO ...
|
||||
--| size: size of the file given by the http request
|
||||
```
|
||||
to iterate over the uploaded files if any, and the details in the class [WSF_UPLOADED_FILE].
|
||||
|
||||
The following snipet code show how to work with Uploaded files using EWF [WSF_REQUEST] class, in the example
|
||||
we build a simple html answer with basic information, if there is not uploaded files, we send a 400 status code
|
||||
and a simple message.
|
||||
|
||||
```eiffel
|
||||
|
||||
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
|
||||
```
|
||||
The source code is available on Github. You can get it by running the command:
|
||||
|
||||
```git clone https://github.com/EiffelWebFramework/ewf.git```
|
||||
|
||||
The example is located in the directory $PATH/ewf/doc/workbook/upload_file where $PATH is where you run git clone.
|
||||
|
||||
|
||||
<a name="examples"></a>
|
||||
## Examples
|
||||
The source code is available on Github. You can get it by running the command:
|
||||
|
||||
```git clone https://github.com/EiffelWebFramework/ewf.git```
|
||||
|
||||
The GET example is located in the directory $PATH/ewf/doc/workbook/form/get, and the post example is located in the directory $PATH/ewf_examples/workbook/form/post where $PATH is where you run git clone . To run open it using Eiffel Studio or just run theg following command
|
||||
|
||||
```estudio -config <ecf_name>.ecf -target <target_name>```
|
||||
|
||||
>Note: replace <ecf_name> and<target_name> with the corresponding values.
|
||||
|
||||
|
||||
Nav: [Workbook](../../workbook) | [Basic Concepts](../../basics/basics) | [Handling Requests: Header Fields](../headers)
|
||||
24
workbook/handling_request/form/get/application.e
Normal file
24
workbook/handling_request/form/get/application.e
Normal file
@@ -0,0 +1,24 @@
|
||||
note
|
||||
description: "Basic Service launcher"
|
||||
|
||||
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
|
||||
101
workbook/handling_request/form/get/application_execution.e
Normal file
101
workbook/handling_request/form/get/application_execution.e
Normal 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
|
||||
42
workbook/handling_request/form/get/form.ecf
Normal file
42
workbook/handling_request/form/get/form.ecf
Normal file
@@ -0,0 +1,42 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-10-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-10-0 http://www.eiffel.com/developers/xml/configuration-1-10-0.xsd" name="form" library_target="form">
|
||||
<target name="common" abstract="true">
|
||||
<file_rule>
|
||||
<exclude>/EIFGENs$</exclude>
|
||||
<exclude>/CVS$</exclude>
|
||||
<exclude>/.svn$</exclude>
|
||||
</file_rule>
|
||||
<option warning="true" is_attached_by_default="true" void_safety="all" syntax="transitional">
|
||||
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
|
||||
</option>
|
||||
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
|
||||
<library name="http" location="$ISE_LIBRARY\contrib\library\network\protocol\http\http-safe.ecf"/>
|
||||
<library name="wsf" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\wsf-safe.ecf"/>
|
||||
</target>
|
||||
<target name="form_nino" extends="common">
|
||||
<root class="APPLICATION" feature="make_and_launch"/>
|
||||
<option warning="true" is_attached_by_default="true" void_safety="all" syntax="transitional">
|
||||
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
|
||||
</option>
|
||||
<library name="default_nino" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\default\nino-safe.ecf"/>
|
||||
<cluster name="form" location=".\" recursive="true"/>
|
||||
</target>
|
||||
<target name="form_cgi" extends="common">
|
||||
<root class="APPLICATION" feature="make_and_launch"/>
|
||||
<option warning="true" is_attached_by_default="true" void_safety="all" syntax="transitional">
|
||||
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
|
||||
</option>
|
||||
<library name="default_cgi" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\default\cgi-safe.ecf"/>
|
||||
<cluster name="form" location=".\" recursive="true"/>
|
||||
</target>
|
||||
<target name="form_libfcgi" extends="common">
|
||||
<root class="APPLICATION" feature="make_and_launch"/>
|
||||
<option warning="true" is_attached_by_default="true" void_safety="all" syntax="transitional">
|
||||
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
|
||||
</option>
|
||||
<library name="default_libfcgi" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\default\libfcgi-safe.ecf"/>
|
||||
<cluster name="form" location=".\" recursive="true"/>
|
||||
</target>
|
||||
<target name="form" extends="form_nino">
|
||||
</target>
|
||||
</system>
|
||||
49
workbook/handling_request/form/get/form.html
Normal file
49
workbook/handling_request/form/get/form.html
Normal file
@@ -0,0 +1,49 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>EWF Handling Client Request: Search Form example </title>
|
||||
</head>
|
||||
<body>
|
||||
<h1> EWF Handling Search Request: Form example </h1>
|
||||
<form action="/search" method="GET">
|
||||
<fieldset>
|
||||
<legend>Search by</legend>
|
||||
<div>
|
||||
<label>First Name
|
||||
<input id="given-name" name="given-name" type="text" placeholder="First name only" autofocus>
|
||||
</label>
|
||||
</div>
|
||||
<div>
|
||||
<label>Last Name
|
||||
<input id="family-name" name="family-name" type="text" placeholder="Last name only" autofocus>
|
||||
</label>
|
||||
</div>
|
||||
<div>
|
||||
<label>Date of Birth
|
||||
<input id="dob" name="dob" type="date">
|
||||
</label>
|
||||
</div>
|
||||
<div>
|
||||
<label>Email
|
||||
<input id="email" name="email" type="email" placeholder="example@domain.com">
|
||||
</label>
|
||||
</div>
|
||||
<div>
|
||||
<label>Languages
|
||||
<input type="checkbox" name="languages" value="Spanish"> Spanish
|
||||
<input type="checkbox" name="languages" value="English"> English
|
||||
</label>
|
||||
</div>
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
<div>
|
||||
<button type=submit>Submit Form</button>
|
||||
</div>
|
||||
</fieldset>
|
||||
</form>
|
||||
<h2> Example link </h2>
|
||||
<div>
|
||||
<a href="/link?tab[a]=1&tab[b]=2&tab[c]=foo">Link</a>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
24
workbook/handling_request/form/post/application.e
Normal file
24
workbook/handling_request/form/post/application.e
Normal file
@@ -0,0 +1,24 @@
|
||||
note
|
||||
description: "Basic Service launcher"
|
||||
|
||||
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
|
||||
83
workbook/handling_request/form/post/application_execution.e
Normal file
83
workbook/handling_request/form/post/application_execution.e
Normal 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
|
||||
42
workbook/handling_request/form/post/form.ecf
Normal file
42
workbook/handling_request/form/post/form.ecf
Normal file
@@ -0,0 +1,42 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-10-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-10-0 http://www.eiffel.com/developers/xml/configuration-1-10-0.xsd" name="form" uuid="C28C4F53-9963-46C0-A080-8F13E94E7486" library_target="form">
|
||||
<target name="common" abstract="true">
|
||||
<file_rule>
|
||||
<exclude>/EIFGENs$</exclude>
|
||||
<exclude>/CVS$</exclude>
|
||||
<exclude>/.svn$</exclude>
|
||||
</file_rule>
|
||||
<option warning="true" is_attached_by_default="true" void_safety="all" syntax="transitional">
|
||||
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
|
||||
</option>
|
||||
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
|
||||
<library name="http" location="$ISE_LIBRARY\contrib\library\network\protocol\http\http-safe.ecf"/>
|
||||
<library name="wsf" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\wsf-safe.ecf"/>
|
||||
</target>
|
||||
<target name="form_nino" extends="common">
|
||||
<root class="APPLICATION" feature="make_and_launch"/>
|
||||
<option warning="true" is_attached_by_default="true" void_safety="all" syntax="transitional">
|
||||
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
|
||||
</option>
|
||||
<library name="default_nino" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\default\nino-safe.ecf"/>
|
||||
<cluster name="form" location=".\" recursive="true"/>
|
||||
</target>
|
||||
<target name="form_cgi" extends="common">
|
||||
<root class="APPLICATION" feature="make_and_launch"/>
|
||||
<option warning="true" is_attached_by_default="true" void_safety="all" syntax="transitional">
|
||||
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
|
||||
</option>
|
||||
<library name="default_cgi" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\default\cgi-safe.ecf"/>
|
||||
<cluster name="form" location=".\" recursive="true"/>
|
||||
</target>
|
||||
<target name="form_libfcgi" extends="common">
|
||||
<root class="APPLICATION" feature="make_and_launch"/>
|
||||
<option warning="true" is_attached_by_default="true" void_safety="all" syntax="transitional">
|
||||
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
|
||||
</option>
|
||||
<library name="default_libfcgi" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\default\libfcgi-safe.ecf"/>
|
||||
<cluster name="form" location=".\" recursive="true"/>
|
||||
</target>
|
||||
<target name="form" extends="form_nino">
|
||||
</target>
|
||||
</system>
|
||||
56
workbook/handling_request/form/post/form.html
Normal file
56
workbook/handling_request/form/post/form.html
Normal file
@@ -0,0 +1,56 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>EWF Handling Client Request: Form example </title>
|
||||
</head>
|
||||
<body>
|
||||
<h1> EWF Handling Client Request: Form example </h1>
|
||||
<form action="/" method="POST">
|
||||
<fieldset>
|
||||
<legend>Personal details</legend>
|
||||
<div>
|
||||
<label>First Name
|
||||
<input id="given-name" name="given-name" type="text" placeholder="First name only" required autofocus>
|
||||
</label>
|
||||
</div>
|
||||
<div>
|
||||
<label>Last Name
|
||||
<input id="family-name" name="family-name" type="text" placeholder="Last name only" required autofocus>
|
||||
</label>
|
||||
</div>
|
||||
<div>
|
||||
<label>Date of Birth
|
||||
<input id="dob" name="dob" type="date" required>
|
||||
</label>
|
||||
</div>
|
||||
<div>
|
||||
<label>Email
|
||||
<input id="email" name="email" type="email" placeholder="example@domain.com" required>
|
||||
</label>
|
||||
</div>
|
||||
<div>
|
||||
<label>URL
|
||||
<input id="url" name="url" type="url" placeholder="http://mysite.com">
|
||||
</label>
|
||||
</div>
|
||||
<div>
|
||||
<label>Telephone
|
||||
<input id="phone" name="phone" type="tel" placeholder="Eg. +447000 000000" required>
|
||||
</label>
|
||||
</div>
|
||||
<div>
|
||||
<label>Languages
|
||||
<input type="checkbox" name="languages" value="Spanish"> Spanish
|
||||
<input type="checkbox" name="languages" value="English"> English
|
||||
</label>
|
||||
</div>
|
||||
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
<div>
|
||||
<button type=submit>Submit Form</button>
|
||||
</div>
|
||||
</fieldset>
|
||||
</form>
|
||||
</body>
|
||||
</html>
|
||||
441
workbook/handling_request/headers.md
Normal file
441
workbook/handling_request/headers.md
Normal file
@@ -0,0 +1,441 @@
|
||||
---
|
||||
layout: default
|
||||
title: headers
|
||||
base_url: ../../../
|
||||
---
|
||||
Nav: [Workbook](../../workbook) | [Handling Requests: Form/Query parameters](../form) | [Generating Responses](../../generating_response/generating_response)
|
||||
|
||||
|
||||
#Handling Requests: Headers
|
||||
|
||||
##### Introduction
|
||||
- The [HTTP request header fields (also known as "headers")](https://httpwg.github.io/specs/rfc7231.html#request.header.fields) are set by the client (usually web browser) and sent in the header of the http request text (see http protocol), as opposed to form or query parameters [Form Data]().
|
||||
- Query parameters are encoded in the URL [GET requests](https://httpwg.github.io/specs/rfc7230.html#http.message).
|
||||
- Form parameters are encoded in the request message for [POST/PUT requests.](https://httpwg.github.io/specs/rfc7230.html#http.message).
|
||||
|
||||
A request usually includes the header fields [Accept, Accept-Encoding, Connection, Cookie, Host, Referer, and User-Agent](https://httpwg.github.io/specs/rfc7231.html#request.header), defining important information about how the server should process the request. And then, the server needs to read the request header fields to use those informations.
|
||||
|
||||
##### Table of Contents
|
||||
- [Reading HTTP Header fields](#read_header)
|
||||
- [Reading HTTP Request line](#read_line)
|
||||
- [Understanding HTTP header fields](#understand)
|
||||
- [Accept](#accept)
|
||||
- [Accept-Charset](#accept_charset)
|
||||
- [Accept-Encoding](#accept_encoding)
|
||||
- [Accept-Language](#accept_language)
|
||||
- [Connection](#connection)
|
||||
- [Authorization](#authorization)
|
||||
- [Content-length](#content-length)
|
||||
- [Cookie](#cookie)
|
||||
- [Host](#host)
|
||||
- [If-Modified-Since](#if-modified-since)
|
||||
- [If-Unmodified-Since](#if-unmodified-since)
|
||||
- [Referer](#referer)
|
||||
- [User-Agent](#user-agent)
|
||||
- [Example: Request Headers](#example)
|
||||
- [Example: How to compress pages](#compress)
|
||||
- [Example: Detecting Browser Types](#browser-types)
|
||||
- [Example: CGI Variables](#cgi-variables)
|
||||
|
||||
|
||||
That section explains how to read HTTP information sent by the browser via the request header fields. Mostly by defining the most important HTTP request header fields, for more information, read [HTTP 1.1 specification](https://httpwg.github.io/specs/).
|
||||
|
||||
## Prerequisites
|
||||
The Eiffel Web Framework is using the traditional Common Gateway Interface (CGI) programming interface to access the header fields, query and form parameters.
|
||||
Among other, this means the header fields are exposed with associated CGI field names:
|
||||
- the header field name are uppercased, and any dash "-" replaced by underscore "_".
|
||||
- and also prefixed by "HTTP_" except for CONTENT_TYPE and CONTENT_LENGTH.
|
||||
- For instance `X-Server` will be known as `HTTP_X_SERVER`.
|
||||
|
||||
<a name="read_header"></a>
|
||||
## Reading HTTP Header fields
|
||||
EWF [WSF_REQUEST]() class provides features to access HTTP headers.
|
||||
|
||||
Reading most headers is straightforward by calling:
|
||||
- the corresponding `http_*` functions such as `http_accept` for header "Accept".
|
||||
- or indirectly using the `meta_string_variable (a_name)` function by passing the associated CGI field name.
|
||||
In both cases, if the related header field is supplied by the request, the result is a string value, otherwise it is Void.
|
||||
|
||||
Note: always check if the result of those functions is non-void before using it.
|
||||
|
||||
* Cookies:
|
||||
- To iterate on all cookies valued, use `cookies: ITERABLE [WSF_VALUE]`
|
||||
- To retrieve a specific cookie value, use `cookie (a_name: READABLE_STRING_GENERAL): detachable WSF_VALUE`
|
||||
|
||||
* Authorization
|
||||
- To read the Authorization header, first check its type with: `auth_type: detachable READABLE_STRING_8`
|
||||
- And its value via `http_authorization: detachable READABLE_STRING_8 --Contents of the Authorization: header from the current wgi_request, if there is one.`
|
||||
|
||||
* Content_length
|
||||
- If supplied, get the content length as an string value: `content_length: detachable READABLE_STRING_8`
|
||||
- or directly as a natural value with `content_length_value: NATURAL_64`
|
||||
|
||||
* Content_type
|
||||
- If supplied, get the content type as an string value with `content_type: detachable HTTP_CONTENT_TYPE`
|
||||
|
||||
Due to CGI compliance, the original header names are not available, however the function `raw_header_data` may return the http header data as a string value (warning: this may not be available, depending on the underlying connector). Apart from very specific cases (proxy, debugging, ...), it should not be useful.
|
||||
Note: CGI variables are information about the current request (and also about the server). Some are based on the HTTP request line and headers (e.g., form parameters, query parameters), others are derived from the socket itself (e.g., the name and IP address of the requesting host), and still others are taken from server installation parameters (e.g., the mapping of URLs to actual paths).
|
||||
|
||||
<a name="read_line"></a>
|
||||
####Retrieve information from the Request Line
|
||||
|
||||
For convenience, the following sections refer to a request starting with line:
|
||||
```
|
||||
GET http://eiffel.org/search?q=EiffelBase HTTP/1.1
|
||||
```
|
||||
|
||||
Overview of the features
|
||||
|
||||
* HTTP method
|
||||
- The function `request_method: READABLE_STRING_8` gives access to the HTTP request method, (usually GET or POST in conventional Web Applications), but with the raise of REST APIs other methods are also frequently used such as HEAD, PUT, DELETE, OPTIONS, or TRACE.
|
||||
A few functions helps determining quickly the nature of the request method:
|
||||
- `is_get_request_method: BOOLEAN -- Is Current a GET request method?`
|
||||
- `is_put_request_method: BOOLEAN -- Is Current a PUT request method?`
|
||||
- `is_post_request_method: BOOLEAN -- Is Current a POST request method?`
|
||||
- `is_delete_request_method: BOOLEAN -- Is Current a DELETE request method?`
|
||||
|
||||
In our example the request method is `GET`
|
||||
|
||||
* Query String
|
||||
- The query string for the example is `q=EiffelBase`
|
||||
- `query_string: READABLE_STRING_8`
|
||||
|
||||
* Protocol
|
||||
- The feature return the third part of the request line, which is generally HTTP/1.0 or HTTP/1.1.
|
||||
- `server_protocol: READABLE_STRING_8`
|
||||
In the example the request method is `HTTP/1.1`
|
||||
|
||||
|
||||
<a name="understand"></a>
|
||||
#### Understanding HTTP 1.1 Request Headers
|
||||
Access to the request headers permits the web server applications or APIs to perform optimizations and provide behavior that would not be possible without them for instance such as adapting the response according to the browser preferences.
|
||||
This section summarizes the headers most often used; for more information, see the [HTTP 1.1 specification](https://httpwg.github.io/specs/), note that [RFC 2616 is dead](https://www.mnot.net/blog/2014/06/07/rfc2616_is_dead).
|
||||
|
||||
<a name="accept"></a>
|
||||
* [Accept](https://httpwg.github.io/specs/rfc7231.html#header.accept)
|
||||
- The "Accept" header field can be used by user agents (browser or other clients) to define response media types that are acceptable. Accept header fields can be used to indicate that the request is limited to a small set of desired types, as in the case of a request for an inline image.
|
||||
For example, assume an APIs Learn4Kids can respond with XML or JSON data (JSON format have some advantages over XML, readability, parsing etc...), a client can define its preference using "Accept: application/json" to request data in JSON format, or "Accept: application/xml" to get XML format. In other case the server sends a not acceptable response. Note that the client can define an ordered list of accepted content types, including "*", the client will get the response and know the content type via the response header field "Content-Type". Related [Content-Negotiation]()
|
||||
|
||||
<a name="accept_charset"></a>
|
||||
* [Accept-Charset](https://httpwg.github.io/specs/rfc7231.html#header.accept-charset)
|
||||
- The "Accept-Charset" header field can be sent by a user agent (browser or other clients) to indicate which charsets are acceptable in textual response content (e.g., ISO-8859-1).
|
||||
|
||||
<a name="accept_encoding"></a>
|
||||
* [Accept-Encoding](https://httpwg.github.io/specs/rfc7231.html#header.accept-encoding)
|
||||
- The "Accept-Encoding" header field can be used by user agents (browser or other clients) to indicate which response content-codings (`gzip`, `compress`) are acceptable in the response. An "identity" token is used as a synonym for "no encoding" in order to communicate when no encoding is preferred. If the server receives this header, it is free to encode the page by using one of the content-encodings specified (usually to reduce transmission time), sending the `Content-Encoding` response header to indicate that it has done so.
|
||||
|
||||
<a name="accept_language"></a>
|
||||
* [Accept-Language](https://httpwg.github.io/specs/rfc7231.html#header.accept-language)
|
||||
- The "Accept-Language" header field can be used by user agents (browser or other client) to indicate the set of natural languages that are preferred in the response in case the server can produce representation in more than one language. The value of the header should be one of the standard language codes such as en, en-us, da, etc. See RFC 1766 for details (start at http://www.rfc-editor.org/ to get a current list of the RFC archive sites).
|
||||
|
||||
<a name="connection"></a>
|
||||
* [Connection](https://httpwg.github.io/specs/rfc7230.html#header.connection)
|
||||
- The "Connection" header field allows the sender to indicate desired control options for the current connection, for example if it can hanlde persistent HTTP connections.
|
||||
By default HTTP/1.1 uses "persistent connections", allowing multiple requests and responses to be carried over a single connection. The "close" connection option is used to signal that a connection will not persist after the current request/response.
|
||||
|
||||
<a name="authorization"></a>
|
||||
* [Authorization](https://httpwg.github.io/specs/rfc7235.html#header.authorization)
|
||||
- The header is used by user agents to authenticate themselves when accessing password protected resources.
|
||||
|
||||
<a name="content-length"></a>
|
||||
* [Content-Length](https://httpwg.github.io/specs/rfc7230.html#header.content-length)
|
||||
- For messages that includes a payload body, the Content-Length field-value provides the framing information necessary to determine where the body (and message) ends.
|
||||
|
||||
<a name="cookie"></a>
|
||||
* [Cookie](https://httpwg.github.io/specs/rfc6265.html)
|
||||
- The Cookie header contains cookies received by the user agent in previous Set-Cookie headers. The origin server is free to ignore the Cookie header or use its contents for an application-specific purpose. (Related State Management).
|
||||
|
||||
<a name="host"></a>
|
||||
* [Host](https://httpwg.github.io/specs/rfc7230.html#header.host)
|
||||
- The "Host" header field provides the host and port information from the target URI, enabling the origin server to distinguish among resources while serving requests for multiple host names on a single IP address. In HTTP 1.1, browsers and other clients are required to specify this header, which indicates the host and port as given in the original URL.
|
||||
|
||||
<a name="if-modified-since"></a>
|
||||
* [If-Modified-Since](https://httpwg.github.io/specs/rfc7232.html#header.if-modified-since)
|
||||
- The "If-Modified-Since" header field makes a GET or HEAD request method conditional on the selected representation's modification date being more recent than the date provided in the field-value. Transfering of the selected representation's data is avoided if that data has not changed. So, indicates that the user agents wants the page only if it has been changes after the specified date. The server sends a 304 resource not modified if not has a newer result representation available.
|
||||
|
||||
<a name="if-unmodified-since"></a>
|
||||
* [If-Unmodified-Since](https://httpwg.github.io/specs/rfc7232.html#header.if-unmodified-since)
|
||||
- The "If-Unmodified-Since" header field makes the request method conditional on the selected representation's last modification date being earlier than or equal to the date provided in the field-value. The operation should succeed only if the document is older than the specified date.
|
||||
|
||||
Generally, If-Modified-Since is used for GET requests (“give me the document only if it is newer than my cached version”), whereas If-Unmodified-Since is used for PUT requests (“update this document only if nobody else has changed it since I generated it”).
|
||||
|
||||
<a name="referer"></a>
|
||||
* [Referer](https://httpwg.github.io/specs/rfc7231.html#header.referer)
|
||||
- The "Referer" header field allows the user agent to specify a URI reference for the resource from which the target URI was obtained (i.e., the "referrer", though the field name is misspelled). A user agent MUST NOT include the fragment and userinfo components of the URI reference [RFC3986], if any, when generating the Referer field value. This header indicates the URL of the referring Web page.
|
||||
|
||||
For example, if you are at Web page A and click on a link to Web page B, the URL of Web page A is
|
||||
included in the Referer header when the browser requests Web page B.
|
||||
|
||||
<a name="user-agent"></a>
|
||||
* [User-Agent](https://httpwg.github.io/specs/rfc7231.html#header.user-agent)
|
||||
- The "User-Agent" header field contains information about the user agent of the request, which is often used by servers to help identify the scope of reported interoperability problems, to work around or tailor responses to avoid particular user agent limitations, and for analytics regarding browser or operating system use or device.
|
||||
|
||||
**Note**: the example shows the **WSF_EXECUTION** implementation, that will be used by the service launcher.
|
||||
|
||||
<a name="example"></a>
|
||||
#### Building a Table of All Request Headers
|
||||
|
||||
The following [EWF service](../headers/header_fields/application.e) code simply uses an ```html_template``` to fill a table (names and values) with all the headers fields it receives.
|
||||
|
||||
The service accomplishes this task by calling ```req.meta_variables``` feature to get an ```ITERABLE [WSF_STRING]```, an structure that can be iterated over using ```across...loop...end```, then it checks if the name has the prefix ```HTTP_``` and if it is true, put the header name and value in a row. (the name in the left cell, the value in the right cell).
|
||||
|
||||
The service also writes three components of the main request line (method, URI, and protocol), and also the raw header.
|
||||
|
||||
```eiffel
|
||||
class
|
||||
APPLICATION_EXECUTION
|
||||
|
||||
inherit
|
||||
WSF_EXECUTION
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
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
|
||||
```
|
||||
|
||||
<a name="compress"></a>
|
||||
#### How to compress pages
|
||||
To be completed.
|
||||
|
||||
|
||||
<a name="browser-types"></a>
|
||||
|
||||
#### Detecting Browser Types
|
||||
|
||||
The User-Agent header identifies the specific browser/client that is sending the request. The following code shows a [EWF service](../headers/browser_name/application.e) that sends browser-specific responses.
|
||||
|
||||
The examples uses the ideas based on the [Browser detection using the user agent](https://developer.mozilla.org/en-US/docs/Browser_detection_using_the_user_agent) article.
|
||||
Basically the code check if the header user_agent exist and then call the ```browser_name (a_user_agent: READABLE_STRING_8): READABLE_STRING_32```
|
||||
feature to retrieve the current browser name or Unknown in other case.
|
||||
|
||||
```eiffel
|
||||
class
|
||||
APPLICATION_EXECUTION
|
||||
|
||||
inherit
|
||||
WSF_EXECUTION
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
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", 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
|
||||
|
||||
browser_name (a_user_agent: READABLE_STRING_8): READABLE_STRING_32
|
||||
-- Browser name.
|
||||
-- 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
|
||||
```
|
||||
Let see some results, we will show the html returned
|
||||
|
||||
**Internet Explorer**
|
||||
---
|
||||
```
|
||||
<h1>EWF service example: Showing Browser Dectection Using User-Agent</h1></br>
|
||||
|
||||
<strong>User Agent:</strong> Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; MDDCJS; rv:11.0) like Gecko <br>
|
||||
|
||||
<h2> Enjoy using Internet Explorer </h2>
|
||||
```
|
||||
|
||||
**Chrome**
|
||||
---
|
||||
```
|
||||
<h1>EWF service example: Showing Browser Dectection Using User-Agent</h1></br>
|
||||
|
||||
<strong>User Agent:</strong> Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/40.0.2214.91 Safari/537.36 <br>
|
||||
|
||||
<h2> Enjoy using Chrome </h2>
|
||||
```
|
||||
|
||||
As an exercise, try to write a similar service to retrieve the OS family using the User-Agent information.
|
||||
|
||||
<a name="cgi-variables"></a>
|
||||
|
||||
[Meta-variables](https://tools.ietf.org/html/rfc3875#section-4.1) contains data about the request, they are identified by case-insensitive names. In this section, the purpose is to show a similar example to HEADERS FIELDS, but in this case building a table showing the standard CGI variables.
|
||||
|
||||
|
||||
* [AUTH_TYPE](https://tools.ietf.org/html/rfc3875#section-4.1.1).
|
||||
* [CONTENT_LENGTH](https://tools.ietf.org/html/rfc3875#section-4.1.2)
|
||||
* [CONTENT_TYPE](https://tools.ietf.org/html/rfc3875#section-4.1.3)
|
||||
* [GATEWAY_INTERFACE](https://tools.ietf.org/html/rfc3875#section-4.1.4)
|
||||
* [PATH_INFO](https://tools.ietf.org/html/rfc3875#section-4.1.5)
|
||||
* [PATH_TRANSLATED](https://tools.ietf.org/html/rfc3875#section-4.1.6)
|
||||
* [QUERY_STRING](https://tools.ietf.org/html/rfc3875#section-4.1.7)
|
||||
* [REMOTE_ADDR](https://tools.ietf.org/html/rfc3875#section-4.1.8)
|
||||
* [REMOTE_HOST](https://tools.ietf.org/html/rfc3875#section-4.1.9)
|
||||
* [REMOTE_IDENT](https://tools.ietf.org/html/rfc3875#section-4.1.10)
|
||||
* [REMOTE_USER](https://tools.ietf.org/html/rfc3875#section-4.1.11)
|
||||
* [REQUEST_METHOD](https://tools.ietf.org/html/rfc3875#section-4.1.12)
|
||||
* [SCRIPT_NAME](https://tools.ietf.org/html/rfc3875#section-4.1.13)
|
||||
* [SERVER_NAME](https://tools.ietf.org/html/rfc3875#section-4.1.14)
|
||||
* [SERVER_PROTOCOL](https://tools.ietf.org/html/rfc3875#section-4.1.15)
|
||||
* [SERVER_SOFTWARE](https://tools.ietf.org/html/rfc3875#section-4.1.16)
|
||||
|
||||
**Example**
|
||||
An [EWF service](../cgi_variables/application.e) that shows the CGI variables, creates a table showing the values of all the CGI variables.
|
||||
|
||||
|
||||
Nav: [Workbook](../../workbook) | [Handling Requests: Form/Query parameters](../form) | [Generating Responses](../../generating_response/generating_response)
|
||||
|
||||
24
workbook/handling_request/headers/browser_name/application.e
Normal file
24
workbook/handling_request/headers/browser_name/application.e
Normal file
@@ -0,0 +1,24 @@
|
||||
note
|
||||
description: "Basic Service launcher"
|
||||
|
||||
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
|
||||
@@ -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
|
||||
42
workbook/handling_request/headers/browser_name/browsers.ecf
Normal file
42
workbook/handling_request/headers/browser_name/browsers.ecf
Normal file
@@ -0,0 +1,42 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-10-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-10-0 http://www.eiffel.com/developers/xml/configuration-1-10-0.xsd" name="browsers" library_target="browsers">
|
||||
<target name="common" abstract="true">
|
||||
<file_rule>
|
||||
<exclude>/EIFGENs$</exclude>
|
||||
<exclude>/CVS$</exclude>
|
||||
<exclude>/.svn$</exclude>
|
||||
</file_rule>
|
||||
<option warning="true" is_attached_by_default="true" void_safety="all" syntax="transitional">
|
||||
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
|
||||
</option>
|
||||
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
|
||||
<library name="http" location="$ISE_LIBRARY\contrib\library\network\protocol\http\http-safe.ecf"/>
|
||||
<library name="wsf" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\wsf-safe.ecf"/>
|
||||
</target>
|
||||
<target name="browsers_nino" extends="common">
|
||||
<root class="APPLICATION" feature="make_and_launch"/>
|
||||
<option warning="true" is_attached_by_default="true" void_safety="all" syntax="transitional">
|
||||
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
|
||||
</option>
|
||||
<library name="default_nino" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\default\nino-safe.ecf"/>
|
||||
<cluster name="browsers" location=".\" recursive="true"/>
|
||||
</target>
|
||||
<target name="browsers_cgi" extends="common">
|
||||
<root class="APPLICATION" feature="make_and_launch"/>
|
||||
<option warning="true" is_attached_by_default="true" void_safety="all" syntax="transitional">
|
||||
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
|
||||
</option>
|
||||
<library name="default_cgi" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\default\cgi-safe.ecf"/>
|
||||
<cluster name="browsers" location=".\" recursive="true"/>
|
||||
</target>
|
||||
<target name="browsers_libfcgi" extends="common">
|
||||
<root class="APPLICATION" feature="make_and_launch"/>
|
||||
<option warning="true" is_attached_by_default="true" void_safety="all" syntax="transitional">
|
||||
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
|
||||
</option>
|
||||
<library name="default_libfcgi" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\default\libfcgi-safe.ecf"/>
|
||||
<cluster name="browsers" location=".\" recursive="true"/>
|
||||
</target>
|
||||
<target name="browsers" extends="browsers_nino">
|
||||
</target>
|
||||
</system>
|
||||
@@ -0,0 +1,24 @@
|
||||
note
|
||||
description: "Basic Service launcher"
|
||||
|
||||
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
|
||||
@@ -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
|
||||
@@ -0,0 +1,42 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-10-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-10-0 http://www.eiffel.com/developers/xml/configuration-1-10-0.xsd" name="cgi_variables" library_target="cgi_variables">
|
||||
<target name="common" abstract="true">
|
||||
<file_rule>
|
||||
<exclude>/EIFGENs$</exclude>
|
||||
<exclude>/CVS$</exclude>
|
||||
<exclude>/.svn$</exclude>
|
||||
</file_rule>
|
||||
<option warning="true" is_attached_by_default="true" void_safety="all" syntax="transitional">
|
||||
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
|
||||
</option>
|
||||
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
|
||||
<library name="http" location="$ISE_LIBRARY\contrib\library\network\protocol\http\http-safe.ecf"/>
|
||||
<library name="wsf" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\wsf-safe.ecf"/>
|
||||
</target>
|
||||
<target name="cgi_variables_nino" extends="common">
|
||||
<root class="APPLICATION" feature="make_and_launch"/>
|
||||
<option warning="true" is_attached_by_default="true" void_safety="all" syntax="transitional">
|
||||
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
|
||||
</option>
|
||||
<library name="default_nino" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\default\nino-safe.ecf"/>
|
||||
<cluster name="cgi_variables" location=".\" recursive="true"/>
|
||||
</target>
|
||||
<target name="cgi_variables_cgi" extends="common">
|
||||
<root class="APPLICATION" feature="make_and_launch"/>
|
||||
<option warning="true" is_attached_by_default="true" void_safety="all" syntax="transitional">
|
||||
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
|
||||
</option>
|
||||
<library name="default_cgi" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\default\cgi-safe.ecf"/>
|
||||
<cluster name="cgi_variables" location=".\" recursive="true"/>
|
||||
</target>
|
||||
<target name="cgi_variables_libfcgi" extends="common">
|
||||
<root class="APPLICATION" feature="make_and_launch"/>
|
||||
<option warning="true" is_attached_by_default="true" void_safety="all" syntax="transitional">
|
||||
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
|
||||
</option>
|
||||
<library name="default_libfcgi" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\default\libfcgi-safe.ecf"/>
|
||||
<cluster name="cgi_variables" location=".\" recursive="true"/>
|
||||
</target>
|
||||
<target name="cgi_variables" extends="cgi_variables_nino">
|
||||
</target>
|
||||
</system>
|
||||
@@ -0,0 +1,24 @@
|
||||
note
|
||||
description: "Basic Service launcher"
|
||||
|
||||
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
|
||||
@@ -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
|
||||
@@ -0,0 +1,42 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-10-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-10-0 http://www.eiffel.com/developers/xml/configuration-1-10-0.xsd" name="header_fields" library_target="header_fields">
|
||||
<target name="common" abstract="true">
|
||||
<file_rule>
|
||||
<exclude>/EIFGENs$</exclude>
|
||||
<exclude>/CVS$</exclude>
|
||||
<exclude>/.svn$</exclude>
|
||||
</file_rule>
|
||||
<option warning="true" is_attached_by_default="true" void_safety="all" syntax="transitional">
|
||||
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
|
||||
</option>
|
||||
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
|
||||
<library name="http" location="$ISE_LIBRARY\contrib\library\network\protocol\http\http-safe.ecf"/>
|
||||
<library name="wsf" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\wsf-safe.ecf"/>
|
||||
</target>
|
||||
<target name="header_fields_nino" extends="common">
|
||||
<root class="APPLICATION" feature="make_and_launch"/>
|
||||
<option warning="true" is_attached_by_default="true" void_safety="all" syntax="transitional">
|
||||
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
|
||||
</option>
|
||||
<library name="default_nino" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\default\nino-safe.ecf"/>
|
||||
<cluster name="header_fields" location=".\" recursive="true"/>
|
||||
</target>
|
||||
<target name="header_fields_cgi" extends="common">
|
||||
<root class="APPLICATION" feature="make_and_launch"/>
|
||||
<option warning="true" is_attached_by_default="true" void_safety="all" syntax="transitional">
|
||||
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
|
||||
</option>
|
||||
<library name="default_cgi" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\default\cgi-safe.ecf"/>
|
||||
<cluster name="header_fields" location=".\" recursive="true"/>
|
||||
</target>
|
||||
<target name="header_fields_libfcgi" extends="common">
|
||||
<root class="APPLICATION" feature="make_and_launch"/>
|
||||
<option warning="true" is_attached_by_default="true" void_safety="all" syntax="transitional">
|
||||
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
|
||||
</option>
|
||||
<library name="default_libfcgi" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\default\libfcgi-safe.ecf"/>
|
||||
<cluster name="header_fields" location=".\" recursive="true"/>
|
||||
</target>
|
||||
<target name="header_fields" extends="header_fields_nino">
|
||||
</target>
|
||||
</system>
|
||||
24
workbook/handling_request/upload_file/application.e
Normal file
24
workbook/handling_request/upload_file/application.e
Normal file
@@ -0,0 +1,24 @@
|
||||
note
|
||||
description: "Basic Service launcher"
|
||||
|
||||
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
|
||||
@@ -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
|
||||
42
workbook/handling_request/upload_file/upload.ecf
Normal file
42
workbook/handling_request/upload_file/upload.ecf
Normal file
@@ -0,0 +1,42 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-10-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-10-0 http://www.eiffel.com/developers/xml/configuration-1-10-0.xsd" name="upload" library_target="upload">
|
||||
<target name="common" abstract="true">
|
||||
<file_rule>
|
||||
<exclude>/EIFGENs$</exclude>
|
||||
<exclude>/CVS$</exclude>
|
||||
<exclude>/.svn$</exclude>
|
||||
</file_rule>
|
||||
<option warning="true" is_attached_by_default="true" void_safety="all" syntax="transitional">
|
||||
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
|
||||
</option>
|
||||
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
|
||||
<library name="http" location="$ISE_LIBRARY\contrib\library\network\protocol\http\http-safe.ecf"/>
|
||||
<library name="wsf" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\wsf-safe.ecf"/>
|
||||
</target>
|
||||
<target name="upload_nino" extends="common">
|
||||
<root class="APPLICATION" feature="make_and_launch"/>
|
||||
<option warning="true" is_attached_by_default="true" void_safety="all" syntax="transitional">
|
||||
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
|
||||
</option>
|
||||
<library name="default_nino" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\default\nino-safe.ecf"/>
|
||||
<cluster name="upload" location=".\" recursive="true"/>
|
||||
</target>
|
||||
<target name="upload_cgi" extends="common">
|
||||
<root class="APPLICATION" feature="make_and_launch"/>
|
||||
<option warning="true" is_attached_by_default="true" void_safety="all" syntax="transitional">
|
||||
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
|
||||
</option>
|
||||
<library name="default_cgi" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\default\cgi-safe.ecf"/>
|
||||
<cluster name="upload" location=".\" recursive="true"/>
|
||||
</target>
|
||||
<target name="upload_libfcgi" extends="common">
|
||||
<root class="APPLICATION" feature="make_and_launch"/>
|
||||
<option warning="true" is_attached_by_default="true" void_safety="all" syntax="transitional">
|
||||
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
|
||||
</option>
|
||||
<library name="default_libfcgi" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\default\libfcgi-safe.ecf"/>
|
||||
<cluster name="upload" location=".\" recursive="true"/>
|
||||
</target>
|
||||
<target name="upload" extends="upload_nino">
|
||||
</target>
|
||||
</system>
|
||||
22
workbook/handling_request/upload_file/upload.html
Normal file
22
workbook/handling_request/upload_file/upload.html
Normal file
@@ -0,0 +1,22 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>EWF Handling Client Request: File Upload Example</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1> EWF Handling Client Request: File Upload Example</h1>
|
||||
<form action="/upload" enctype="multipart/form-data" method="POST">
|
||||
<fieldset>
|
||||
<legend>Upload file/s</legend>
|
||||
<div>
|
||||
<label>File
|
||||
<input name="file-name[]" type="file" multiple>
|
||||
</label>
|
||||
<fieldset>
|
||||
<div>
|
||||
<button type=submit>Send</button>
|
||||
</div>
|
||||
</fieldset>
|
||||
</form>
|
||||
</body>
|
||||
</html>
|
||||
6
workbook/handling_request/upload_file/upload.rc
Normal file
6
workbook/handling_request/upload_file/upload.rc
Normal file
@@ -0,0 +1,6 @@
|
||||
#include <windows.h>
|
||||
|
||||
STRINGTABLE
|
||||
BEGIN
|
||||
1 "This Program was made using EiffelStudio using Visual Studio C++"
|
||||
END
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user