Compare commits
89 Commits
concurrent
...
ewf_http_d
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8f105f8626 | ||
|
|
df0ff1ac60 | ||
| 47c5b798b3 | |||
| f0cba1d536 | |||
| ed891546bc | |||
| 8651ff6e1e | |||
| 629edea991 | |||
|
|
1e10ce8518 | ||
| 4f8f17ad48 | |||
| 148518984e | |||
| 33150e34d6 | |||
| af60a5719e | |||
| 31557cfc33 | |||
| 78c0cd5b0d | |||
| 412534d0be | |||
|
|
0f6aa8d7ae | ||
|
|
2c745c63d3 | ||
|
|
efd80c1287 | ||
|
|
01f649fd88 | ||
|
|
f23aeb6412 | ||
|
|
1a4596c79b | ||
|
|
b16e4aa570 | ||
|
|
5255b15fa9 | ||
|
|
57048373f4 | ||
|
|
9e06fb2ab8 | ||
|
|
f2405e0ccd | ||
|
|
6e3a7deb6e | ||
|
|
f254b599c0 | ||
| 99a05b95ba | |||
| 54dd43c38a | |||
| 903f925a79 | |||
| 80709578d6 | |||
| c0d5b7c968 | |||
| 7bea163f46 | |||
| 8992dbc515 | |||
| c2d3ea6138 | |||
| 9e336deb49 | |||
| 0160ce05dd | |||
| 7d089a88c2 | |||
| ab0bc7b314 | |||
| 0e3e97a7fd | |||
| b790c7fd21 | |||
| d0836d49a4 | |||
| 9424b1e369 | |||
| 64463df552 | |||
|
|
61f90bba38 | ||
|
|
fac3dd3946 | ||
|
|
e1b583a2b3 | ||
|
|
8c8dfdd4a3 | ||
|
|
857397e226 | ||
|
|
94340c1c01 | ||
| 8b60ab08e3 | |||
|
|
28e51cc314 | ||
|
|
a7c8d40b3e | ||
| d4c0ff03b4 | |||
| 7fbfda3a66 | |||
| 9e467689df | |||
| dd5c89e31c | |||
| fffa763d05 | |||
| d015c065f6 | |||
| 8ea443c115 | |||
| 019393fdb1 | |||
| da8028f8b3 | |||
| 20ed000879 | |||
|
|
24620b228c | ||
|
|
9c7e29b836 | ||
|
|
a0e9a41e21 | ||
|
|
dd9aff03d3 | ||
|
|
dc35925eb0 | ||
| a1a620a9c3 | |||
| d8ea9ba63c | |||
| c42af5b2de | |||
| d9cbc72058 | |||
| 7e057b20b1 | |||
| 3165c1e5c6 | |||
| 89e26519e4 | |||
| 9d20e85c03 | |||
| 48cb99498c | |||
| 8246bc1444 | |||
| 9e1083eba8 | |||
| 4907bc3085 | |||
| 7d2ce8a77f | |||
| b4a9c92ffc | |||
| bf0eb9a02d | |||
| ddf73077b3 | |||
| 3da80fce0d | |||
| 0970de5dc6 | |||
| 557b11f4e6 | |||
| 7f27a6c797 |
@@ -1,38 +1,7 @@
|
||||
History for Eiffel-Web-Framework
|
||||
[2015-06-10]
|
||||
* Updated EWF design to better support concurrency, including SCOOP via
|
||||
the new standalone connector.
|
||||
|
||||
[2011-09-23] Jocelyn
|
||||
* library "ewsgi":
|
||||
- NEW simple autotest cases using Nino web server
|
||||
-fixed issue with RAW_POST_DATA being added in form_data_parameters
|
||||
instead of meta_variables ...
|
||||
- Implemented WGI_VALUE for parameter's type (query_parameter,
|
||||
form_data_parameter, item ...)
|
||||
* Nino connector: added feature to shutdown the server from the WGI application
|
||||
* NEW library "http_client": a new library to perform simple http requests
|
||||
such as get, head, post, put, ... (currently implemented with Eiffel cURL)
|
||||
* NEW library "http_authorization": added simple library to support
|
||||
HTTP_AUTHORIZATION. For now only "Basic" auth type is supported ..
|
||||
|
||||
[2011-09-22] Javier
|
||||
* NEW Example: added partial Restbuck example
|
||||
|
||||
[2011-09-21] Jocelyn
|
||||
* Nino connector: fixed an issue with missing value for Content-Type and Content-Length
|
||||
|
||||
[2011-09-13] Jocelyn
|
||||
* library "router": now using a generic design to allow customization of
|
||||
request handler context class.
|
||||
* NEW library "server/request/rest": first attempt to provide a library to
|
||||
help building RESTful application (the interfaces are likely to change
|
||||
soon) EXPERIMENTAL
|
||||
|
||||
[2011-09-09] Jocelyn
|
||||
* library "uri-template": better support for {/vars} and {?vars}
|
||||
|
||||
[2011-09-07] Jocelyn
|
||||
* library "router": now routing depends on uri (or uri template) and request methods
|
||||
* Nino connector: Fixed issue where HTTP_ prefix were missing for header meta variable.
|
||||
|
||||
[2011-09-07] Jocelyn
|
||||
* changelog: starting to write down changelogs file
|
||||
|
||||
[Previous ] Many significant changes in v0
|
||||
|
||||
121
MIGRATION.md
Normal file
121
MIGRATION.md
Normal file
@@ -0,0 +1,121 @@
|
||||
Date: 2015-june
|
||||
|
||||
# Goal:
|
||||
=======
|
||||
- support safe concurrency with EWF
|
||||
- provide a concurrent standalone connector
|
||||
|
||||
# Status:
|
||||
=========
|
||||
- The version v0 of EWF has mainly 3 connectors: CGI, libFCGI, and nino.
|
||||
- CGI and libFCGI connectors does not need any concurrency support.
|
||||
- But the nino connector had a pseudo concurrency support with Thread, however one could do write code that result in hasardeous concurrency execution.
|
||||
|
||||
So, it was decided to provide an improved Eiffel web nino connector, and update EWF design to make it concurrency compliant.
|
||||
|
||||
# Decisions:
|
||||
============
|
||||
- instead of updating current nino library, we now have a new "standalone" connector which is inspired by nino, but have support for the 3 concurrency modes: none, thread and SCOOP.
|
||||
|
||||
|
||||
# Overview
|
||||
==========
|
||||
Adding support for SCOOP concurrency mode add constraints to the design, but also helps ensuring the concurrency design of EWF is correct.
|
||||
|
||||
As a consequence, we had to introduce a new interface WSF_EXECUTION which is instantiated for each incoming request. See its simplified interface :
|
||||
<code lang="eiffel">
|
||||
deferred class WSF_EXECUTION
|
||||
|
||||
feature -- Initialization
|
||||
|
||||
make (req: WGI_REQUEST; res: WGI_RESPONSE)
|
||||
do
|
||||
...
|
||||
īnitialize
|
||||
end
|
||||
|
||||
initialize
|
||||
-- Initialize Current object.
|
||||
--| To be redefined if needed.
|
||||
do
|
||||
end
|
||||
|
||||
|
||||
feature -- Access
|
||||
|
||||
request: WSF_REQUEST
|
||||
-- Access to request data.
|
||||
-- Header, Query, Post, Input data..
|
||||
|
||||
response: WSF_RESPONSE
|
||||
-- Access to output stream, back to the client.
|
||||
|
||||
feature -- Execution
|
||||
|
||||
execute
|
||||
-- Execute Current `request',
|
||||
-- getting data from `request'
|
||||
-- and response to client via `response'.
|
||||
deferred
|
||||
ensure
|
||||
is_valid_end_of_execution: is_valid_end_of_execution
|
||||
end
|
||||
|
||||
end
|
||||
</code>
|
||||
|
||||
And the related request execution routines are extracted from WSF_SERVICE which becomes almost useless. The "service" part is not mostly responsible of launching the expected connector and set optional options, and declare the type of "execution" interface.
|
||||
|
||||
As a result, the well known WSF_DEFAULT_SERVICE has now a formal generic that should conform to WSF_EXECUTION with a `make' creation procedure. See update code:
|
||||
|
||||
<code lang="eiffel">
|
||||
class
|
||||
APPLICATION
|
||||
|
||||
inherit
|
||||
WSF_DEFAULT_SERVICE [APPLICATION_EXECUTION]
|
||||
redefine
|
||||
initialize
|
||||
end
|
||||
|
||||
create
|
||||
make_and_launch
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
initialize
|
||||
-- Initialize current service.
|
||||
do
|
||||
set_service_option ("port", 9090)
|
||||
end
|
||||
|
||||
end
|
||||
</code>
|
||||
|
||||
Where APPLICATION_EXECUTION is an implementation of the WSF_EXECUTION interface (with the `make' creation procedure).
|
||||
|
||||
In addition to add better and safer concurrency support, there are other advantages:
|
||||
- we now have a clear separation between the service launcher, and the request execution itself.
|
||||
- the WSF_EXECUTION is created per request, with two main attributes <code>request: WSF_REQUEST</code> and <code>response: WSF_RESPONSE</code>.
|
||||
|
||||
# How to migrate to new design
|
||||
- you can check the various example from the EWF repository, there should all be migrated to new design and comparing previous and new code, this will show you how the migration was done.
|
||||
- a frequent process:
|
||||
- identify the root class of your service, (the class implementing the WSF_SERVICE), let us name it APPLICATION_SERVICE
|
||||
- copy the APPLICATION_SERVICE file to APPLICATION_EXECUTION file.
|
||||
- change the class name to be APPLICATION_EXECUTION, and replace _SERVICE occurences by _EXECUTION (note the new WSF_ROUTED_EXECUTION and so on, which are mainly migration from previous WSF_ROUTED_SERVICE .., and also WSF_FILTERED_ROUTED_EXECUTION which is new.
|
||||
- replace "make_and_launch" by "make", remove the initialize redefinition if any.
|
||||
- in the APPLICATION_SERVICE class, remove most of the ROUTED, FILTERED ... inheritance, and keep WSF_DEFAULT_SERVICE, with a new formal generic i.e WSF_DEFAULT_SERVICE [APPLICATION_EXECUTION].
|
||||
- in the eventual redefined initialize, remove code related to routers, filters, ...
|
||||
- remove all the execution related code.
|
||||
- And you should be done.
|
||||
- To be short, this is mostly creating a new _EXECUTION class, and move the execution related code into this class from the _SERVICE class.
|
||||
- Then, you can replace the usage of nino connector by using the new "Standalone" connector, and switch to SCOOP concurrency mode, to ensure you are not messing up with concurrency. Your own code/libraris may not be SCOOP compliant, we recommend to migrate to SCOOP, but as an intermediate solutioņ, you can use the other concurrency mode (none or thread).
|
||||
|
||||
Note: the new design impacts the _SERVICE classes, connectors, but WSF_REQUEST, WSF_RESPONSE , WSF_ROUTER are compatible, so the migration is really easy.
|
||||
|
||||
We may take the opportunity to update the design deeper according to user feedback, and eventually "wsf" library will be renamed "wsf2".
|
||||
This is work in progress, all comments , feedback, suggestions, bug report are welcome.
|
||||
Hopefully before the final version of the new design is out.
|
||||
|
||||
|
||||
14
contrib/library/network/server/nino/package.iron
Normal file
14
contrib/library/network/server/nino/package.iron
Normal file
@@ -0,0 +1,14 @@
|
||||
package nino
|
||||
|
||||
project
|
||||
nino = "nino-safe.ecf"
|
||||
nino = "nino.ecf"
|
||||
|
||||
note
|
||||
title: Eiffel Nino Web Server
|
||||
description: Simple HTTPd server written in Eiffel
|
||||
tags: web, httpd, server
|
||||
license: Eiffel Forum v2
|
||||
copyright: Javier Velilla, Jocelyn Fiat.
|
||||
|
||||
end
|
||||
BIN
doc/workbook/basics/APPLICATION_EXECUTION.png
Normal file
BIN
doc/workbook/basics/APPLICATION_EXECUTION.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.8 KiB |
BIN
doc/workbook/basics/Launcher Hierarchy.png
Normal file
BIN
doc/workbook/basics/Launcher Hierarchy.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 16 KiB |
BIN
doc/workbook/basics/WSF_SERVICE_LAUNCHER_CGI.png
Normal file
BIN
doc/workbook/basics/WSF_SERVICE_LAUNCHER_CGI.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 9.2 KiB |
BIN
doc/workbook/basics/WSF_SERVICE_LAUNCHER_FCGI.png
Normal file
BIN
doc/workbook/basics/WSF_SERVICE_LAUNCHER_FCGI.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 9.3 KiB |
BIN
doc/workbook/basics/WSF_SERVICE_LAUNCHER_NINO.png
Normal file
BIN
doc/workbook/basics/WSF_SERVICE_LAUNCHER_NINO.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 9.3 KiB |
BIN
doc/workbook/basics/WSF_SERVICE_LAUNCHER_STANDALONE.png
Normal file
BIN
doc/workbook/basics/WSF_SERVICE_LAUNCHER_STANDALONE.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 11 KiB |
206
doc/workbook/basics/basics.md
Normal file
206
doc/workbook/basics/basics.md
Normal file
@@ -0,0 +1,206 @@
|
||||
Nav: [Workbook](../workbook.md) | [Handling Requests: Form/Query Parameter](/doc/workbook/handling_request/form.md)
|
||||
|
||||
|
||||
## 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"/>
|
||||
## 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
|
||||
APPLICACTION
|
||||
|
||||
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 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/ewd/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.md) | [Handling Requests: Form/Query Parameter](/doc/workbook/handling_request/form.md)
|
||||
|
||||
24
doc/workbook/basics/simple/application.e
Normal file
24
doc/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
doc/workbook/basics/simple/application_execution.e
Normal file
25
doc/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
doc/workbook/basics/simple/simple.ecf
Normal file
50
doc/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>
|
||||
52
doc/workbook/basics/simple_html/apache_config/Readme.md
Normal file
52
doc/workbook/basics/simple_html/apache_config/Readme.md
Normal file
@@ -0,0 +1,52 @@
|
||||
##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
doc/workbook/basics/simple_html/apache_config/config.conf
Normal file
12
doc/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
doc/workbook/basics/simple_html/application.e
Normal file
24
doc/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
doc/workbook/basics/simple_html/application_execution.e
Normal file
66
doc/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
doc/workbook/basics/simple_html/simple_html.ecf
Normal file
42
doc/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>
|
||||
6
doc/workbook/basics/simple_html/simple_html.rc
Normal file
6
doc/workbook/basics/simple_html/simple_html.rc
Normal file
@@ -0,0 +1,6 @@
|
||||
#include <windows.h>
|
||||
|
||||
STRINGTABLE
|
||||
BEGIN
|
||||
1 "This Program was made using EiffelStudio using Visual Studio C++"
|
||||
END
|
||||
24
doc/workbook/generating_response/exel/application.e
Normal file
24
doc/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
|
||||
@@ -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
doc/workbook/generating_response/exel/exel.ecf
Normal file
42
doc/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>
|
||||
999
doc/workbook/generating_response/generating_response.md
Normal file
999
doc/workbook/generating_response/generating_response.md
Normal file
@@ -0,0 +1,999 @@
|
||||
|
||||
Nav: [Workbook](../workbook.md) | [Handling Requests: Header Fields](/doc/workbook/handling_request/headers.md) | [Handling Cookies](/doc/workbook/handling_cookies/handling_cookies.md)
|
||||
|
||||
|
||||
## EWF Generating Response
|
||||
|
||||
##### Table of Contents
|
||||
- [Format of the HTTP response](#format)
|
||||
- [How to set status code](#status_set)
|
||||
- [How to redirect to a particular location.](#redirect)
|
||||
- [HTTP Status codes](#status)
|
||||
- [Example Staus Codes](#example_1)
|
||||
- [Generic Search Engine](#example_2)
|
||||
- [Response Header Fields](#header_fields)
|
||||
|
||||
|
||||
<a name="format"/>
|
||||
## Format of the HTTP response
|
||||
|
||||
As we saw in the previous documents, a request from a user-agent (browser or other client) consists of an HTTP command (usually GET or POST), zero or more request headers (one or more in HTTP 1.1, since Host is required), a blank line, and only in the case of POST/PUT requests, payload data. A typical request looks like the following.
|
||||
|
||||
```
|
||||
GET /url[query_string] HTTP/1.1
|
||||
Host: ...
|
||||
Header2: ...
|
||||
...
|
||||
HeaderN:
|
||||
(Blank Line)
|
||||
```
|
||||
|
||||
When a Web server responds to a request, the response typically consists of a status line, some response headers, a blank line, and the document. A typical response
|
||||
looks like this:
|
||||
|
||||
```
|
||||
HTTP/1.1 200 OK
|
||||
Content-Type: text/html
|
||||
Header2: ...
|
||||
...
|
||||
HeaderN: ...
|
||||
(Blank Line)
|
||||
<!DOCTYPE ...>
|
||||
<HTML>
|
||||
<HEAD>...</HEAD>
|
||||
<BODY>
|
||||
...
|
||||
</BODY>
|
||||
</HTML>
|
||||
```
|
||||
|
||||
The status line consists of the HTTP version (HTTP/1.1 in the preceding example), a status code (an integer 200 in the example), and a very short message corresponding to the status code (OK in the example). In most cases, the headers are optional except for Content-Type, which specifies the MIME type of the document that follows. Although most responses contain a document, some don’t. For example, responses to HEAD requests should never include a document, and various status codes essentially indicate failure or redirection (and thus either don’t include a document or include only a short error-message document).
|
||||
|
||||
<a name="status_set"/>
|
||||
## How to set the status code
|
||||
|
||||
If you need to set an arbitrary status code, you can use the ```WSF_RESPONSE.put_header``` feature or the ```WSF_RESPONSE.set_status_code``` feature. An status code of 200 is a default value. See below examples using the mentioned features.
|
||||
|
||||
### Using the WSF_RESPONSE.put_header feature.
|
||||
In this case you provide the status code with a collection of headers.
|
||||
|
||||
```eiffel
|
||||
put_header (a_status_code: INTEGER_32; a_headers: detachable ARRAY [TUPLE [name: READABLE_STRING_8; value: READABLE_STRING_8]])
|
||||
-- Put headers with status `a_status', and headers from `a_headers'
|
||||
require
|
||||
a_status_code_valid: a_status_code > 0
|
||||
status_not_committed: not status_committed
|
||||
header_not_committed: not header_committed
|
||||
ensure
|
||||
status_code_set: status_code = a_status_code
|
||||
status_set: status_is_set
|
||||
message_writable: message_writable
|
||||
|
||||
Example
|
||||
res.put_header ({HTTP_STATUS_CODE}.ok, <<["Content-Type", "text/html"], ["Content-Length", output_size]>>)
|
||||
res.put_string (web_page)
|
||||
```
|
||||
|
||||
### Using the WSF_RESPONSE.set_status code
|
||||
|
||||
```eiffel
|
||||
custom_response (req: WSF_REQUEST; res: WSF_RESPONSE; output: STRING)
|
||||
local
|
||||
h: HTTP_HEADER
|
||||
l_msg: STRING
|
||||
do
|
||||
create h.make
|
||||
create l_msg.make_from_string (output)
|
||||
h.put_content_type_text_html
|
||||
h.put_content_length (l_msg.count)
|
||||
h.put_current_date
|
||||
res.set_status_code ({HTTP_STATUS_CODE}.ok)
|
||||
res.put_header_text (h.string)
|
||||
res.put_string (l_msg)
|
||||
end
|
||||
```
|
||||
Both features takes an INTEGER (the status code) as an formal argument, you can use 200, 300, 500 etc directly, but instead of using explicit numbers, it's recommended to use the constants defined in the class [HTTP_STATUS_CODE](). The name of each constant is based from the standard [HTTP 1.1](https://httpwg.github.io/).
|
||||
|
||||
<a name="redirect"/>
|
||||
## How to redirect to a particular location.
|
||||
To redirect the response to a new location, we need to send a 302 status code, to do that we use ```{HTTP_STATUS_CODE}.found```
|
||||
|
||||
> The 302 (Found) status code indicates that the target resource resides temporarily under a different URI. Since the redirection might be altered on occasion, the client ought to continue to use the effective request URI for future requests.
|
||||
|
||||
Another way to do redirection is with 303 status code
|
||||
|
||||
> The 303 (See Other) status code indicates that the server is redirecting the user agent to a different resource, as indicated by a URI in the Location header field, which is intended to provide an indirect response to the original request.
|
||||
|
||||
The next code show a custom feature to write a redirection, you can use found or see_other based on your particular requirements.
|
||||
|
||||
```eiffel
|
||||
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}.found)
|
||||
res.put_header_text (h.string)
|
||||
end
|
||||
```
|
||||
|
||||
The class [WSF_RESPONSE]() provide features to work with redirection
|
||||
|
||||
```eiffel
|
||||
redirect_now (a_url: READABLE_STRING_8)
|
||||
-- Redirect to the given url `a_url'
|
||||
require
|
||||
header_not_committed: not header_committed
|
||||
|
||||
redirect_now_custom (a_url: READABLE_STRING_8; a_status_code: INTEGER_32; a_header: detachable HTTP_HEADER; a_content: detachable TUPLE [body: READABLE_STRING_8; type: READABLE_STRING_8])
|
||||
-- Redirect to the given url `a_url' and precise custom `a_status_code', custom header and content
|
||||
-- Please see http://www.faqs.org/rfcs/rfc2616 to use proper status code.
|
||||
-- if `a_status_code' is 0, use the default {HTTP_STATUS_CODE}.temp_redirect
|
||||
require
|
||||
header_not_committed: not header_committed
|
||||
|
||||
redirect_now_with_content (a_url: READABLE_STRING_8; a_content: READABLE_STRING_8; a_content_type: READABLE_STRING_8)
|
||||
-- Redirect to the given url `a_url'
|
||||
```
|
||||
|
||||
The ```WSF_RESPONSE.redirect_now``` feature use the status code ```{HTTP_STATUS_CODE}.found```,the other redirect features enable customize the status code and content based on your requirements.
|
||||
|
||||
|
||||
Using a similar approach we can build features to answer a bad request (400), internal server error (500), etc. We will build a simple example showing the most common HTTP status codes.
|
||||
|
||||
<a name="status"/>
|
||||
## [HTTP 1.1 Status Codes](https://httpwg.github.io/specs/rfc7231.html#status.codes)
|
||||
The status-code element is a three-digit integer code giving the result of the attempt to understand and satisfy the request. The first digit of the status-code defines the class of response.
|
||||
|
||||
General categories:
|
||||
* [1xx](https://httpwg.github.io/specs/rfc7231.html#status.1xx) Informational: The 1xx series of response codes are used only in negotiations with the HTTP server.
|
||||
* [2xx](https://httpwg.github.io/specs/rfc7231.html#status.2xx) Sucessful: The 2xx error codes indicate that an operation was successful.
|
||||
* [3xx](https://httpwg.github.io/specs/rfc7231.html#status.3xx) Redirection: The 3xx status codes indicate that the client needs to do some extra work to get what it wants.
|
||||
* [4xx](https://httpwg.github.io/specs/rfc7231.html#status.4xx) Client Error: These status codes indicate that something is wrong on the client side.
|
||||
* [5xx](https://httpwg.github.io/specs/rfc7231.html#status.5xx) Server Error: These status codes indicate that something is wrong on the server side.
|
||||
|
||||
Note: use ```res.set_status_code({HTTP_STATUS_CODE}.bad_request)``` rather than ```res.set_status_code(400)```.
|
||||
|
||||
|
||||
<a name="example_1"/>
|
||||
### Example Staus Codes
|
||||
Basic Service that builds a simple web page to show the most common status codes
|
||||
```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_message: STRING
|
||||
do
|
||||
-- To send a response we need to setup, the status code and
|
||||
-- the response headers.
|
||||
if req.is_get_request_method then
|
||||
if req.path_info.same_string ("/") then
|
||||
res.put_header ({HTTP_STATUS_CODE}.ok, <<["Content-Type", "text/html"], ["Content-Length", web_page.count.out]>>)
|
||||
res.put_string (web_page)
|
||||
elseif req.path_info.same_string ("/redirect") then
|
||||
send_redirect (req, res, "https://httpwg.github.io/")
|
||||
-- res.redirect_now (l_engine_url)
|
||||
elseif req.path_info.same_string ("/bad_request") then
|
||||
-- Here you can do some logic for example log, send emails to register the error, before to send the response.
|
||||
create l_message.make_from_string (message_template)
|
||||
l_message.replace_substring_all ("$title", "Bad Request")
|
||||
l_message.replace_substring_all ("$status", "Bad Request 400")
|
||||
res.put_header ({HTTP_STATUS_CODE}.bad_request, <<["Content-Type", "text/html"], ["Content-Length", l_message.count.out]>>)
|
||||
res.put_string (l_message)
|
||||
elseif req.path_info.same_string ("/internal_error") then
|
||||
-- Here you can do some logic for example log, send emails to register the error, before to send the response.
|
||||
create l_message.make_from_string (message_template)
|
||||
l_message.replace_substring_all ("$title", "Internal Server Error")
|
||||
l_message.replace_substring_all ("$status", "Internal Server Error 500")
|
||||
res.put_header ({HTTP_STATUS_CODE}.internal_server_error, <<["Content-Type", "text/html"], ["Content-Length", l_message.count.out]>>)
|
||||
res.put_string (l_message)
|
||||
else
|
||||
create l_message.make_from_string (message_template)
|
||||
l_message.replace_substring_all ("$title", "Resource not found")
|
||||
l_message.replace_substring_all ("$status", "Resource not found 400")
|
||||
res.put_header ({HTTP_STATUS_CODE}.not_found, <<["Content-Type", "text/html"], ["Content-Length", l_message.count.out]>>)
|
||||
res.put_string (l_message)
|
||||
end
|
||||
else
|
||||
create l_message.make_from_string (message_template)
|
||||
l_message.replace_substring_all ("$title", "Method Not Allowed")
|
||||
l_message.replace_substring_all ("$status", "Method Not Allowed 405")
|
||||
-- Method not allowed
|
||||
res.put_header ({HTTP_STATUS_CODE}.method_not_allowed, <<["Content-Type", "text/html"], ["Content-Length", l_message.count.out]>>)
|
||||
res.put_string (l_message)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
feature -- Home Page
|
||||
|
||||
send_redirect (req: WSF_REQUEST; res: WSF_RESPONSE; a_location: READABLE_STRING_32)
|
||||
-- Redirect to `a_location'
|
||||
local
|
||||
h: HTTP_HEADER
|
||||
do
|
||||
create h.make
|
||||
h.put_content_type_text_html
|
||||
h.put_current_date
|
||||
h.put_location (a_location)
|
||||
res.set_status_code ({HTTP_STATUS_CODE}.see_other)
|
||||
res.put_header_text (h.string)
|
||||
end
|
||||
|
||||
web_page: STRING = "[
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Example showing common status codes</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="header">
|
||||
<p id="name">Use a tool to see the request and header details, for example (Developers tools in Chrome or Firebugs in Firefox)</p>
|
||||
</div>
|
||||
<div class="left"></div>
|
||||
<div class="right">
|
||||
<h4>This page is an example of Status Code 200</h4>
|
||||
|
||||
<h4> Redirect Example </h4>
|
||||
<p> Click on the following link will redirect you to the HTTP Specifcation, we can do the redirect from the HTML directly but
|
||||
here we want to show you an exmaple, where you can do something before to send a redirect <a href="/redirect">Redirect</a></p>
|
||||
|
||||
<h4> Bad Request </h4>
|
||||
<p> Click on the following link, the server will answer with a 400 error, check the status code <a href="/bad_request">Bad Request</a></p>
|
||||
|
||||
<h4> Internal Server Error </h4>
|
||||
<p> Click on the following link, the server will answer with a 500 error, check the status code <a href="/internal_error">Internal Error</a></p>
|
||||
|
||||
<h4> Resource not found </h4>
|
||||
<p> Click on the following link or add to the end of the url something like /1030303 the server will answer with a 404 error, check the status code <a href="/not_foundd">Not found</a></p>
|
||||
|
||||
</div>
|
||||
<div id="footer">
|
||||
<p>Useful links for status codes <a href="httpstat.us">httpstat.us</a> and <a href="httpbing.org">httpbin.org</a></p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
]"
|
||||
|
||||
feature -- Generic Message
|
||||
|
||||
message_template: STRING="[
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>$title</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="header">
|
||||
<p id="name">Use a tool to see the request and header details, for example (Developers tools in Chrome or Firebugs in Firefox)</p>
|
||||
</div>
|
||||
<div class="left"></div>
|
||||
<div class="right">
|
||||
<h4>This page is an example of $status</h4>
|
||||
|
||||
<div id="footer">
|
||||
<p><a href="/">Back Home</a></p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
]"
|
||||
end
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
<a name="example_2"/>
|
||||
### Example Generic Search Engine
|
||||
The following example shows a basic EWF service that builds a generic front end for the most used search engines. This example shows how
|
||||
redirection works, and we will use a tools to play with the API to show differents responses.
|
||||
|
||||
```eiffel
|
||||
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 (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
-- Execute the incomming request
|
||||
local
|
||||
l_message: STRING
|
||||
do
|
||||
-- To send a response we need to setup, the status code and
|
||||
-- the response headers.
|
||||
if req.is_get_request_method then
|
||||
if req.path_info.same_string ("/") then
|
||||
res.put_header ({HTTP_STATUS_CODE}.ok, <<["Content-Type", "text/html"], ["Content-Length", web_page.count.out]>>)
|
||||
res.put_string (web_page)
|
||||
else
|
||||
send_resouce_not_found (req, res)
|
||||
end
|
||||
elseif req.is_post_request_method then
|
||||
if req.path_info.same_string ("/search") then
|
||||
if attached {WSF_STRING} req.form_parameter ("query") as l_query then
|
||||
if attached {WSF_STRING} req.form_parameter ("engine") as l_engine then
|
||||
if attached {STRING} map.at (l_engine.value) as l_engine_url then
|
||||
l_engine_url.append (l_query.value)
|
||||
send_redirect (req, res, l_engine_url)
|
||||
else
|
||||
send_bad_request (req, res, " <strong>search engine: " + l_engine.value + "</strong> not supported,<br> try with Google or Bing")
|
||||
end
|
||||
else
|
||||
send_bad_request (req, res, " <strong>search engine</strong> not selected")
|
||||
end
|
||||
else
|
||||
send_bad_request (req, res, " form_parameter <strong>query</strong> is not present")
|
||||
end
|
||||
else
|
||||
send_resouce_not_found (req, res)
|
||||
end
|
||||
else
|
||||
create l_message.make_from_string (message_template)
|
||||
l_message.replace_substring_all ("$title", "Method Not Allowed")
|
||||
l_message.replace_substring_all ("$status", "Method Not Allowed 405")
|
||||
-- Method not allowed
|
||||
res.put_header ({HTTP_STATUS_CODE}.method_not_allowed, <<["Content-Type", "text/html"], ["Content-Length", l_message.count.out]>>)
|
||||
res.put_string (l_message)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
feature -- Engine Map
|
||||
|
||||
map : STRING_TABLE[STRING]
|
||||
do
|
||||
create Result.make (2)
|
||||
Result.put ("http://www.google.com/search?q=", "Google")
|
||||
Result.put ("http://www.bing.com/search?q=", "Bing")
|
||||
end
|
||||
|
||||
feature -- Redirect
|
||||
|
||||
send_redirect (req: WSF_REQUEST; res: WSF_RESPONSE; a_location: READABLE_STRING_32)
|
||||
-- Redirect to `a_location'
|
||||
local
|
||||
h: HTTP_HEADER
|
||||
do
|
||||
create h.make
|
||||
h.put_content_type_text_html
|
||||
h.put_current_date
|
||||
h.put_location (a_location)
|
||||
res.set_status_code ({HTTP_STATUS_CODE}.see_other)
|
||||
res.put_header_text (h.string)
|
||||
end
|
||||
|
||||
feature -- Bad Request
|
||||
|
||||
send_bad_request (req: WSF_REQUEST; res: WSF_RESPONSE; description: STRING)
|
||||
local
|
||||
l_message: STRING
|
||||
do
|
||||
create l_message.make_from_string (message_template)
|
||||
l_message.replace_substring_all ("$title", "Bad Request")
|
||||
l_message.replace_substring_all ("$status", "Bad Request" + description)
|
||||
res.put_header ({HTTP_STATUS_CODE}.bad_request, <<["Content-Type", "text/html"], ["Content-Length", l_message.count.out]>>)
|
||||
res.put_string (l_message)
|
||||
end
|
||||
|
||||
feature -- Resource not found
|
||||
|
||||
send_resouce_not_found (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
local
|
||||
l_message: STRING
|
||||
do
|
||||
create l_message.make_from_string (message_template)
|
||||
l_message.replace_substring_all ("$title", "Resource not found")
|
||||
l_message.replace_substring_all ("$status", "Resource" + req.request_uri + "not found 404")
|
||||
res.put_header ({HTTP_STATUS_CODE}.not_found, <<["Content-Type", "text/html"], ["Content-Length", l_message.count.out]>>)
|
||||
res.put_string (l_message)
|
||||
end
|
||||
|
||||
feature -- Home Page
|
||||
|
||||
web_page: STRING = "[
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Generic Search Engine</title>
|
||||
</head>
|
||||
<body>
|
||||
<div class="right">
|
||||
<h2>Generic Search Engine</h2>
|
||||
<form method="POST" action="/search" target="_blank">
|
||||
<fieldset>
|
||||
Search: <input type="search" name="query" placeholder="EWF framework"><br>
|
||||
<div>
|
||||
<input type="radio" name="engine" value="Google" checked><img src="http://ebizmba.ebizmbainc.netdna-cdn.com/images/logos/google.gif" height="24" width="42">
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<input type="radio" name="engine" value="Bing"><img src="http://ebizmba.ebizmbainc.netdna-cdn.com/images/logos/bing.gif" height="24" width="42">
|
||||
</div><br>
|
||||
</fieldset>
|
||||
<input type="submit">
|
||||
</form>
|
||||
|
||||
|
||||
</div>
|
||||
<div id="footer">
|
||||
<p><a href="http://www.ebizmba.com/articles/search-engines">Top 15 Most Popular Search Engines | March 2015</a></p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
]"
|
||||
|
||||
feature -- Generic Message
|
||||
|
||||
message_template: STRING="[
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>$title</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="header">
|
||||
<p id="name">Use a tool to see the request and header details, for example (Developers tools in Chrome or Firebugs in Firefox)</p>
|
||||
</div>
|
||||
<div class="left"></div>
|
||||
<div class="right">
|
||||
<h4>This page is an example of $status</h4>
|
||||
|
||||
<div id="footer">
|
||||
<p><a href="/">Back Home</a></p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
]"
|
||||
|
||||
end
|
||||
|
||||
```
|
||||
|
||||
Using cURL to test the application
|
||||
|
||||
In the first call we use the ```res.redirect_now (l_engine_url)``` feature
|
||||
```
|
||||
#>curl -i -H -v -X POST -d "query=Eiffel&engine=Google" http://localhost:9090/search
|
||||
HTTP/1.1 302 Found
|
||||
Location: http://www.google.com/search?q=Eiffel
|
||||
Content-Length: 0
|
||||
Connection: close
|
||||
```
|
||||
|
||||
Here we use our custom send_redirect feature call.
|
||||
|
||||
```
|
||||
#>curl -i -H -v -X POST -d "query=Eiffel&engine=Google" http://localhost:9090/search
|
||||
HTTP/1.1 303 See Other
|
||||
Content-Type: text/html
|
||||
Date: Fri, 06 Mar 2015 14:37:33 GMT
|
||||
Location: http://www.google.com/search?q=Eiffel
|
||||
Connection: close
|
||||
```
|
||||
|
||||
#### Engine Ask Not supported
|
||||
|
||||
```
|
||||
#>curl -i -H -v -X POST -d "query=Eiffel&engine=Ask" http://localhost:9090/search
|
||||
HTTP/1.1 400 Bad Request
|
||||
Content-Type: text/html
|
||||
Content-Length: 503
|
||||
Connection: close
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Bad Request</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 Bad Request <strong>search engine: Ask</strong> not supported,<br> try with Google or Bing</h4>
|
||||
|
||||
<div id="footer">
|
||||
<p><a href="/">Back Home</a></p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
|
||||
#### Missing query form parameter
|
||||
|
||||
```
|
||||
#>curl -i -H -v -X POST -d "engine=Google" http://localhost:9090/search
|
||||
HTTP/1.1 400 Bad Request
|
||||
Content-Type: text/html
|
||||
Content-Length: 477
|
||||
Connection: close
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Bad Request</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 Bad Request form_parameter <strong>query</strong> is not present</h4>
|
||||
|
||||
<div id="footer">
|
||||
<p><a href="/">Back Home</a></p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
#### Resource searchs not found
|
||||
|
||||
```
|
||||
#>curl -i -H -v -X POST -d "query=Eiffel&engine=Google" http://localhost:9090/searchs
|
||||
HTTP/1.1 404 Not Found
|
||||
Content-Type: text/html
|
||||
Content-Length: 449
|
||||
Connection: close
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Resource not found</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 Resource /searchs not found 404</h4>
|
||||
|
||||
<div id="footer">
|
||||
<p><a href="/">Back Home</a></p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
<a name="header_fields"/>
|
||||
## [Response Header Fields](https://httpwg.github.io/specs/rfc7231.html#response.header.fields)
|
||||
|
||||
The response header fields allow the server to pass additional information about the response beyond what is placed in the status-line. These header fields give information about the server, about further access to the target resource, or about related resources. We can specify cookies, page modification date (for caching), reload a page after a designated period of time, size of the document.
|
||||
|
||||
|
||||
|
||||
### How to set response headers.
|
||||
|
||||
HTTP allows multiple occurrences of the same header name, the features ```put_XYZ``` replace existing headers with the same name and
|
||||
features ```add_XYZ``` add headers that can lead to duplicated entries.
|
||||
|
||||
|
||||
```eiffel
|
||||
add_header_line (h: READABLE_STRING_8)
|
||||
-- Add header `h'
|
||||
-- This can lead to duplicated header entries
|
||||
require
|
||||
header_not_committed: not header_committed
|
||||
|
||||
add_header_text (a_text: READABLE_STRING_8)
|
||||
-- Add the multiline header `a_text'
|
||||
-- Does not replace existing header with same name
|
||||
-- This could leads to multiple header with the same name
|
||||
require
|
||||
header_not_committed: not header_committed
|
||||
a_text_ends_with_single_crlf: a_text.count > 2 implies not a_text.substring (a_text.count - 2, a_text.count).same_string ("%R%N")
|
||||
a_text_does_not_end_with_double_crlf: a_text.count > 4 implies not a_text.substring (a_text.count - 4, a_text.count).same_string ("%R%N%R%N")
|
||||
ensure
|
||||
status_set: status_is_set
|
||||
message_writable: message_writable
|
||||
|
||||
put_header_line (h: READABLE_STRING_8)
|
||||
-- Put header `h'
|
||||
-- Replace any existing value
|
||||
require
|
||||
header_not_committed: not header_committed
|
||||
|
||||
put_header_text (a_text: READABLE_STRING_8)
|
||||
-- Put the multiline header `a_text'
|
||||
-- Overwite potential existing header
|
||||
require
|
||||
header_not_committed: not header_committed
|
||||
a_text_ends_with_single_crlf: a_text.count > 2 implies not a_text.substring (a_text.count - 2, a_text.count).same_string ("%R%N")
|
||||
a_text_does_not_end_with_double_crlf: a_text.count > 4 implies not a_text.substring (a_text.count - 4, a_text.count).same_string ("%R%N%R%N")
|
||||
ensure
|
||||
message_writable: message_writable
|
||||
|
||||
helpers
|
||||
|
||||
add_header (a_status_code: INTEGER_32; a_headers: detachable ARRAY [TUPLE [name: READABLE_STRING_8; value: READABLE_STRING_8]])
|
||||
-- Put headers with status `a_status', and headers from `a_headers'
|
||||
require
|
||||
a_status_code_valid: a_status_code > 0
|
||||
status_not_committed: not status_committed
|
||||
header_not_committed: not header_committed
|
||||
ensure
|
||||
status_code_set: status_code = a_status_code
|
||||
status_set: status_is_set
|
||||
message_writable: message_writable
|
||||
|
||||
add_header_lines (a_lines: ITERABLE [READABLE_STRING_8])
|
||||
-- Add headers from `a_lines'
|
||||
require
|
||||
header_not_committed: not header_committed
|
||||
|
||||
put_header (a_status_code: INTEGER_32; a_headers: detachable ARRAY [TUPLE [name: READABLE_STRING_8; value: READABLE_STRING_8]])
|
||||
-- Put headers with status `a_status', and headers from `a_headers'
|
||||
require
|
||||
a_status_code_valid: a_status_code > 0
|
||||
status_not_committed: not status_committed
|
||||
header_not_committed: not header_committed
|
||||
ensure
|
||||
status_code_set: status_code = a_status_code
|
||||
status_set: status_is_set
|
||||
message_writable: message_writable
|
||||
|
||||
put_header_lines (a_lines: ITERABLE [READABLE_STRING_8])
|
||||
-- Put headers from `a_lines'
|
||||
require
|
||||
header_not_committed: not header_committed
|
||||
|
||||
```
|
||||
|
||||
The other way to build headers is using the class [HTTP_HEADER](), that provide routines to build a header. It's recomended to
|
||||
take a look at constants classes such as [HTTP_MIME_TYPES](),[HTTP_HEADER_NAMES](),[HTTP_STATUS_CODE](),[HTTP_REQUEST_METHODS](), or
|
||||
[HTTP_CONSTANTS]() which groups them for convenience.
|
||||
|
||||
|
||||
```eiffel
|
||||
custom_answer (req: WSF_REQUEST; res: WSF_RESPONSE; output: STRING)
|
||||
local
|
||||
h: HTTP_HEADER
|
||||
l_msg: STRING
|
||||
do
|
||||
create h.make
|
||||
create l_msg.make_from_string (output)
|
||||
h.put_content_type_text_html
|
||||
h.put_content_length (l_msg.count)
|
||||
h.put_current_date
|
||||
res.set_status_code ({HTTP_STATUS_CODE}.bad_gateway)
|
||||
res.put_header_text (h.string)
|
||||
res.put_string (l_msg)
|
||||
end
|
||||
```
|
||||
The class [HTTP_HEADER]() also supplies a number of convenience routines for specifying common headers, in fact the features are inherited from the class [HTTP_HEADER_MODIFIER].
|
||||
|
||||
|
||||
```eiffel
|
||||
deferred class interface
|
||||
HTTP_HEADER_MODIFIER
|
||||
|
||||
feature -- Access
|
||||
|
||||
date_to_rfc1123_http_date_format (dt: DATE_TIME): STRING_8
|
||||
-- String representation of `dt' using the RFC 1123
|
||||
|
||||
item alias "[]" (a_header_name: READABLE_STRING_8): detachable READABLE_STRING_8 assign force
|
||||
-- First header item found for `a_name' if any
|
||||
|
||||
feature -- Status report
|
||||
|
||||
has (a_name: READABLE_STRING_8): BOOLEAN
|
||||
-- Has header item for `n'?
|
||||
-- Was declared in HTTP_HEADER_MODIFIER as synonym of has_header_named.
|
||||
|
||||
has_content_length: BOOLEAN
|
||||
-- Has header "Content-Length"
|
||||
|
||||
has_content_type: BOOLEAN
|
||||
-- Has header "Content-Type"
|
||||
|
||||
has_header_named (a_name: READABLE_STRING_8): BOOLEAN
|
||||
-- Has header item for `n'?
|
||||
-- Was declared in HTTP_HEADER_MODIFIER as synonym of has.
|
||||
|
||||
has_transfer_encoding_chunked: BOOLEAN
|
||||
-- Has "Transfer-Encoding: chunked" header
|
||||
|
||||
feature -- Access: deferred
|
||||
|
||||
new_cursor: INDEXABLE_ITERATION_CURSOR [READABLE_STRING_8]
|
||||
-- Fresh cursor associated with current structure.
|
||||
|
||||
feature -- Authorization
|
||||
|
||||
put_authorization (a_authorization: READABLE_STRING_8)
|
||||
-- Put `a_authorization' with "Authorization" header
|
||||
-- The Authorization header is constructed as follows:
|
||||
-- 1. Username and password are combined into a string "username:password".
|
||||
-- 2. The resulting string literal is then encoded using Base64.
|
||||
-- 3. The authorization method and a space, i.e. "Basic " is then put before the encoded string.
|
||||
-- ex: Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==
|
||||
|
||||
feature -- Content related header
|
||||
|
||||
add_content_type (a_content_type: READABLE_STRING_8)
|
||||
-- same as put_content_type, but allow multiple definition of "Content-Type"
|
||||
|
||||
add_content_type_with_charset (a_content_type: READABLE_STRING_8; a_charset: READABLE_STRING_8)
|
||||
-- Same as put_content_type_with_charset, but allow multiple definition of "Content-Type".
|
||||
|
||||
add_content_type_with_name (a_content_type: READABLE_STRING_8; a_name: READABLE_STRING_8)
|
||||
-- same as put_content_type_with_name, but allow multiple definition of "Content-Type"
|
||||
|
||||
add_content_type_with_parameters (a_content_type: READABLE_STRING_8; a_params: detachable ARRAY [TUPLE [name: READABLE_STRING_8; value: READABLE_STRING_8]])
|
||||
-- Add header line "Content-Type:" + type `a_content_type' and extra paramaters `a_params'.
|
||||
|
||||
put_content_disposition (a_type: READABLE_STRING_8; a_params: detachable READABLE_STRING_8)
|
||||
-- Put "Content-Disposition" header
|
||||
|
||||
put_content_encoding (a_encoding: READABLE_STRING_8)
|
||||
-- Put "Content-Encoding" header of value `a_encoding'.
|
||||
|
||||
put_content_language (a_lang: READABLE_STRING_8)
|
||||
-- Put "Content-Language" header of value `a_lang'.
|
||||
|
||||
put_content_length (a_length: INTEGER_32)
|
||||
-- Put "Content-Length:" + length `a_length'.
|
||||
|
||||
put_content_transfer_encoding (a_mechanism: READABLE_STRING_8)
|
||||
-- Put "Content-Transfer-Encoding" header with `a_mechanism'
|
||||
|
||||
put_content_type (a_content_type: READABLE_STRING_8)
|
||||
-- Put header line "Content-Type:" + type `a_content_type'
|
||||
|
||||
put_content_type_with_charset (a_content_type: READABLE_STRING_8; a_charset: READABLE_STRING_8)
|
||||
-- Put content type `a_content_type' with `a_charset' as "charset" parameter.
|
||||
|
||||
put_content_type_with_name (a_content_type: READABLE_STRING_8; a_name: READABLE_STRING_8)
|
||||
-- Put content type `a_content_type' with `a_name' as "name" parameter.
|
||||
|
||||
put_content_type_with_parameters (a_content_type: READABLE_STRING_8; a_params: detachable ARRAY [TUPLE [name: READABLE_STRING_8; value: READABLE_STRING_8]])
|
||||
-- Put header line "Content-Type:" + type `a_content_type' and extra paramaters `a_params'
|
||||
|
||||
put_transfer_encoding (a_encoding: READABLE_STRING_8)
|
||||
-- Put "Transfer-Encoding" header with `a_encoding' value.
|
||||
|
||||
put_transfer_encoding_binary
|
||||
-- Put "Transfer-Encoding: binary" header
|
||||
|
||||
put_transfer_encoding_chunked
|
||||
-- Put "Transfer-Encoding: chunked" header
|
||||
|
||||
feature -- Content-type helpers
|
||||
|
||||
put_content_type_application_javascript
|
||||
|
||||
put_content_type_application_json
|
||||
|
||||
put_content_type_application_pdf
|
||||
|
||||
put_content_type_application_x_www_form_encoded
|
||||
|
||||
put_content_type_application_zip
|
||||
|
||||
put_content_type_image_gif
|
||||
|
||||
put_content_type_image_jpg
|
||||
|
||||
put_content_type_image_png
|
||||
|
||||
put_content_type_image_svg_xml
|
||||
|
||||
put_content_type_message_http
|
||||
|
||||
put_content_type_multipart_alternative
|
||||
|
||||
put_content_type_multipart_encrypted
|
||||
|
||||
put_content_type_multipart_form_data
|
||||
|
||||
put_content_type_multipart_mixed
|
||||
|
||||
put_content_type_multipart_related
|
||||
|
||||
put_content_type_multipart_signed
|
||||
|
||||
put_content_type_text_css
|
||||
|
||||
put_content_type_text_csv
|
||||
|
||||
put_content_type_text_html
|
||||
|
||||
put_content_type_text_javascript
|
||||
|
||||
put_content_type_text_json
|
||||
|
||||
put_content_type_text_plain
|
||||
|
||||
put_content_type_text_xml
|
||||
|
||||
put_content_type_utf_8_text_plain
|
||||
|
||||
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)
|
||||
|
||||
feature -- Cross-Origin Resource Sharing
|
||||
|
||||
put_access_control_allow_all_origin
|
||||
-- Put "Access-Control-Allow-Origin: *" header.
|
||||
|
||||
put_access_control_allow_credentials (b: BOOLEAN)
|
||||
-- Indicates whether or not the response to the request can be exposed when the credentials flag is true.
|
||||
-- When used as part of a response to a preflight request, this indicates whether or not the actual request can be made using credentials.
|
||||
-- Note that simple GET requests are not preflighted, and so if a request is made for a resource with credentials,
|
||||
-- if this header is not returned with the resource, the response is ignored by the browser and not returned to web content.
|
||||
-- ex: Access-Control-Allow-Credentials: true | false
|
||||
|
||||
put_access_control_allow_headers (a_headers: READABLE_STRING_8)
|
||||
-- Put "Access-Control-Allow-Headers" header. with value `a_headers'
|
||||
-- Used in response to a preflight request to indicate which HTTP headers can be used when making the actual request.
|
||||
-- ex: Access-Control-Allow-Headers: <field-name>[, <field-name>]*
|
||||
|
||||
put_access_control_allow_iterable_headers (a_fields: ITERABLE [READABLE_STRING_8])
|
||||
-- Put "Access-Control-Allow-Headers" header. with value `a_headers'
|
||||
-- Used in response to a preflight request to indicate which HTTP headers can be used when making the actual request.
|
||||
-- ex: Access-Control-Allow-Headers: <field-name>[, <field-name>]*
|
||||
|
||||
put_access_control_allow_methods (a_methods: ITERABLE [READABLE_STRING_8])
|
||||
-- If `a_methods' is not empty, put `Access-Control-Allow-Methods' header with list `a_methods' of methods
|
||||
-- `a_methods' specifies the method or methods allowed when accessing the resource.
|
||||
-- This is used in response to a preflight request.
|
||||
-- ex: Access-Control-Allow-Methods: <method>[, <method>]*
|
||||
|
||||
put_access_control_allow_origin (a_origin: READABLE_STRING_8)
|
||||
-- Put "Access-Control-Allow-Origin: " + `a_origin' header.
|
||||
-- `a_origin' specifies a URI that may access the resource
|
||||
|
||||
feature -- Date
|
||||
|
||||
put_current_date
|
||||
-- Put current date time with "Date" header
|
||||
|
||||
put_date (a_date: READABLE_STRING_8)
|
||||
-- Put "Date: " header
|
||||
|
||||
put_last_modified (a_utc_date: DATE_TIME)
|
||||
-- Put UTC date time `dt' with "Last-Modified" header
|
||||
|
||||
put_utc_date (a_utc_date: DATE_TIME)
|
||||
-- Put UTC date time `a_utc_date' with "Date" header
|
||||
-- using RFC1123 date formating.
|
||||
|
||||
feature -- Header change: deferred
|
||||
|
||||
add_header (h: READABLE_STRING_8)
|
||||
-- Add header `h'
|
||||
-- if it already exists, there will be multiple header with same name
|
||||
-- which can also be valid
|
||||
require
|
||||
h_not_empty: h /= Void and then not h.is_empty
|
||||
|
||||
put_header (h: READABLE_STRING_8)
|
||||
-- Add header `h' or replace existing header of same header name
|
||||
require
|
||||
h_not_empty: h /= Void and then not h.is_empty
|
||||
|
||||
feature -- Header change: general
|
||||
|
||||
add_header_key_value (a_header_name, a_value: READABLE_STRING_8)
|
||||
-- Add header `a_header_name:a_value'.
|
||||
-- If it already exists, there will be multiple header with same name
|
||||
-- which can also be valid
|
||||
ensure
|
||||
added: has_header_named (a_header_name)
|
||||
|
||||
force (a_value: detachable READABLE_STRING_8; a_header_name: READABLE_STRING_8)
|
||||
-- Put header `a_header_name:a_value' or replace existing header of name `a_header_name'.
|
||||
|
||||
put_header_key_value (a_header_name, a_value: READABLE_STRING_8)
|
||||
-- Add header `a_header_name:a_value', or replace existing header of same header name/key
|
||||
ensure
|
||||
added: has_header_named (a_header_name)
|
||||
|
||||
put_header_key_values (a_header_name: READABLE_STRING_8; a_values: ITERABLE [READABLE_STRING_8]; a_separator: detachable READABLE_STRING_8)
|
||||
-- Add header `a_header_name: a_values', or replace existing header of same header values/key.
|
||||
-- Use Comma_space as default separator if `a_separator' is Void or empty.
|
||||
ensure
|
||||
added: has_header_named (a_header_name)
|
||||
|
||||
feature -- Method related
|
||||
|
||||
put_allow (a_methods: ITERABLE [READABLE_STRING_8])
|
||||
-- If `a_methods' is not empty, put `Allow' header with list `a_methods' of methods
|
||||
|
||||
feature -- Others
|
||||
|
||||
put_cache_control (a_cache_control: READABLE_STRING_8)
|
||||
-- Put "Cache-Control" header with value `a_cache_control'
|
||||
|
||||
put_expires (a_seconds: INTEGER_32)
|
||||
-- Put "Expires" header to `a_seconds' seconds
|
||||
|
||||
put_expires_date (a_utc_date: DATE_TIME)
|
||||
-- Put "Expires" header with UTC date time value
|
||||
-- formatted following RFC1123 specification.
|
||||
|
||||
put_expires_string (a_expires: STRING_8)
|
||||
-- Put "Expires" header with `a_expires' string value
|
||||
|
||||
put_pragma (a_pragma: READABLE_STRING_8)
|
||||
-- Put "Pragma" header with value `a_pragma'
|
||||
|
||||
put_pragma_no_cache
|
||||
-- Put "Pragma" header with "no-cache" a_pragma
|
||||
|
||||
feature -- Redirection
|
||||
|
||||
put_location (a_uri: READABLE_STRING_8)
|
||||
-- Tell the client the new location `a_uri'
|
||||
-- using "Location" header.
|
||||
require
|
||||
a_uri_valid: not a_uri.is_empty
|
||||
|
||||
put_refresh (a_uri: READABLE_STRING_8; a_timeout_in_seconds: INTEGER_32)
|
||||
-- Tell the client to refresh page with `a_uri' after `a_timeout_in_seconds' in seconds
|
||||
-- using "Refresh" header.
|
||||
require
|
||||
a_uri_valid: not a_uri.is_empty
|
||||
|
||||
end -- class HTTP_HEADER_MODIFIER
|
||||
```
|
||||
|
||||
|
||||
|
||||
## HTTP 1.1 Response Headers
|
||||
|
||||
There are four categories for response header fields:
|
||||
- [Control Data](https://httpwg.github.io/specs/rfc7231.html#response.control.data) : Supply control data that supplements the status code, directs caching, or instructs the client where to go next.
|
||||
- Age,Cache-Control,Expires,Date,Location,Retry-After,Vary,Warning.
|
||||
- [Validator](https://httpwg.github.io/specs/rfc7231.html#response.validator): Validator header fields convey metadata about the selected representation. In responses to safe requests, validator fields describe the selected representation chosen by the origin server while handling the response.
|
||||
- [Authentication Challenges](https://httpwg.github.io/specs/rfc7231.html#response.auth): Indicate what mechanisms are available for the client to provide authentication credentials in future requests.
|
||||
- [Response Context](https://httpwg.github.io/specs/rfc7231.html#response.context): Provide more information about the target resource for potential use in later requests.
|
||||
|
||||
|
||||
|
||||
Nav: [Workbook](../workbook.md) | [Handling Requests: Header Fields](/doc/workbook/handling_request/headers.md) | [Handling Cookies](/doc/workbook/handling_cookies/handling_cookies.md)
|
||||
24
doc/workbook/generating_response/headers/application.e
Normal file
24
doc/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
|
||||
@@ -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
doc/workbook/generating_response/headers/headers.ecf
Normal file
42
doc/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
doc/workbook/generating_response/search/application.e
Normal file
24
doc/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
doc/workbook/generating_response/search/application_execution.e
Normal file
172
doc/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
doc/workbook/generating_response/search/search.ecf
Normal file
42
doc/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
doc/workbook/generating_response/status/application.e
Normal file
24
doc/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
doc/workbook/generating_response/status/application_execution.e
Normal file
138
doc/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
doc/workbook/generating_response/status/status.ecf
Normal file
42
doc/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
doc/workbook/handling_cookies/example/application.e
Normal file
24
doc/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
doc/workbook/handling_cookies/example/application_execution.e
Normal file
141
doc/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
doc/workbook/handling_cookies/example/example.ecf
Normal file
42
doc/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>
|
||||
288
doc/workbook/handling_cookies/handling_cookies.md
Normal file
288
doc/workbook/handling_cookies/handling_cookies.md
Normal file
@@ -0,0 +1,288 @@
|
||||
Nav: [Workbook](../workbook.md) | [Generating Responses](/doc/workbook/generating_response/generating_response.md)
|
||||
|
||||
# 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"/>
|
||||
## [Cookie](http://httpwg.github.io/specs/rfc6265.html)
|
||||
A cookie 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"/>
|
||||
### 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"/>
|
||||
## 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"/>
|
||||
### 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"/>
|
||||
### 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"/>
|
||||
### 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.md) | [Generating Responses](/doc/workbook/generating_response/generating_response.md)
|
||||
307
doc/workbook/handling_request/form.md
Normal file
307
doc/workbook/handling_request/form.md
Normal file
@@ -0,0 +1,307 @@
|
||||
Nav: [Workbook](../workbook.md) | [Basic Concepts] (/doc/workbook/basics/basics.md) | [Handling Requests: Header Fields](/doc/workbook/handling_request/headers.md)
|
||||
|
||||
|
||||
#Handling Requests: Form/Query Data
|
||||
|
||||
|
||||
##### Table of Contents
|
||||
- [Reading Form Data](#read)
|
||||
- [Query Parameters](#query)
|
||||
- [Form Parameters](#form)
|
||||
- [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"/>
|
||||
## Reading Form Data
|
||||
EWF [WSF_REQUEST]() class, provides features to handling this form parsing automatically.
|
||||
|
||||
<a name="query"/>
|
||||
### 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"/>
|
||||
### 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"/>
|
||||
### 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">
|
||||
## 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">
|
||||
### 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">
|
||||
### 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">
|
||||
### 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">
|
||||
### 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">
|
||||
## 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>
|
||||
## 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.md) | [Basic Concepts] (/doc/workbook/basics/basics.md) | [Handling Requests: Header Fields](/doc/workbook/handling_request/headers.md)
|
||||
|
||||
24
doc/workbook/handling_request/form/get/application.e
Normal file
24
doc/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
doc/workbook/handling_request/form/get/application_execution.e
Normal file
101
doc/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
doc/workbook/handling_request/form/get/form.ecf
Normal file
42
doc/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
doc/workbook/handling_request/form/get/form.html
Normal file
49
doc/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
doc/workbook/handling_request/form/post/application.e
Normal file
24
doc/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
|
||||
@@ -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
doc/workbook/handling_request/form/post/form.ecf
Normal file
42
doc/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
doc/workbook/handling_request/form/post/form.html
Normal file
56
doc/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>
|
||||
438
doc/workbook/handling_request/headers.md
Normal file
438
doc/workbook/handling_request/headers.md
Normal file
@@ -0,0 +1,438 @@
|
||||
Nav: [Workbook](../workbook.md) | [Handling Requests: Form/Query parameters] (/doc/workbook/handling_request/form.md) | [Generating Responses](/doc/workbook/generating_response/generating_response.md)
|
||||
|
||||
|
||||
#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](/doc/workbook/handling_request/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](/doc/workbook/handling_request/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](/doc/workbook/handling_request/headers/cgi_variables/application.e) that shows the CGI variables, creates a table showing the values of all the CGI variables.
|
||||
|
||||
|
||||
Nav: [Workbook](../workbook.md) | [Handling Requests: Form/Query parameters] (/doc/workbook/handling_request/form.md) | [Generating Responses](/doc/workbook/generating_response/generating_response.md)
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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
doc/workbook/handling_request/upload_file/application.e
Normal file
24
doc/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
doc/workbook/handling_request/upload_file/upload.ecf
Normal file
42
doc/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
doc/workbook/handling_request/upload_file/upload.html
Normal file
22
doc/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>
|
||||
13
doc/workbook/readme.md
Normal file
13
doc/workbook/readme.md
Normal file
@@ -0,0 +1,13 @@
|
||||
Introduction
|
||||
|
||||
Basic Concepts
|
||||
Generating Plain Text
|
||||
Generation HTML
|
||||
|
||||
Handling Client Request:
|
||||
Form Data
|
||||
Request Heders
|
||||
Query Parameters.
|
||||
|
||||
|
||||
|
||||
38
doc/workbook/workbook.md
Normal file
38
doc/workbook/workbook.md
Normal file
@@ -0,0 +1,38 @@
|
||||
# EWF Workbook
|
||||
|
||||
##### Table of Contents
|
||||
* [EWF Core](#core)
|
||||
* [EWF Introduction](#introduction)
|
||||
* [Handling Requests: Form/Query Parameter](#form_query_parameters)
|
||||
* [Handling Requests: Header Fields](#header_fields)
|
||||
* [Generating Responses](/workbook/generating_response/generating_response.md)
|
||||
* [Handling Cookies](/workbook/handling_cookies/handling_cookies.md)
|
||||
*
|
||||
|
||||
<a name="core"></a>
|
||||
# EWF Core
|
||||
Before reading (or walking throught) the workbook, to get a quick overview of EWF, it is recommended to read the following articles:
|
||||
* [Getting Started with EWF](http://eiffelwebframework.github.io/EWF/getting-started/)
|
||||
* [EWF Documentation](http://eiffelwebframework.github.io/EWF/wiki/Documentation/)
|
||||
* [EWF Application Lifecyle](https://github.com/EiffelWebFramework/ewf_examples/wiki/Application-Lifecycle)
|
||||
|
||||
|
||||
<a name="introduction"></a>
|
||||
## Introduction
|
||||
[Basic Concepts] (/doc/workbook/basics/basics.md).
|
||||
|
||||
<a name="form_query_parameters"></a>
|
||||
## Handling Requests: Form/Query Parameter
|
||||
[Handling Requests: Form/Query Parameter] (/doc/workbook/handling_request/form.md).
|
||||
|
||||
<a name="header_fields"></a>
|
||||
## Handling Requests: Header Fields
|
||||
[Handling Requests: Header Fields](/doc/workbook/handling_request/headers.md).
|
||||
|
||||
<a name="header_fields"></a>
|
||||
## Generating Response
|
||||
[Generating Responses](/doc/workbook/generating_response/generating_response.md)
|
||||
|
||||
## Handling Cookies
|
||||
[Handling Cookies](/doc/workbook/handling_cookies/handling_cookies.md)
|
||||
|
||||
@@ -8,23 +8,11 @@ class
|
||||
|
||||
inherit
|
||||
|
||||
WSF_ROUTED_SERVICE
|
||||
rename
|
||||
execute as execute_router
|
||||
end
|
||||
|
||||
WSF_FILTERED_SERVICE
|
||||
|
||||
WSF_DEFAULT_SERVICE
|
||||
WSF_DEFAULT_SERVICE [APPLICATION_EXECUTION]
|
||||
redefine
|
||||
initialize
|
||||
end
|
||||
|
||||
WSF_FILTER
|
||||
rename
|
||||
execute as execute_router
|
||||
end
|
||||
|
||||
create
|
||||
make_and_launch
|
||||
|
||||
@@ -33,75 +21,8 @@ feature {NONE} -- Initialization
|
||||
initialize
|
||||
-- Initialize current service.
|
||||
do
|
||||
initialize_router
|
||||
initialize_filter
|
||||
Precursor
|
||||
set_service_option ("port", 7070)
|
||||
end
|
||||
|
||||
feature -- Router and Filter
|
||||
|
||||
create_filter
|
||||
-- Create `filter'
|
||||
local
|
||||
f, l_filter: detachable WSF_FILTER
|
||||
do
|
||||
l_filter := Void
|
||||
|
||||
-- Maintenance
|
||||
create {WSF_MAINTENANCE_FILTER} f
|
||||
f.set_next (l_filter)
|
||||
l_filter := f
|
||||
|
||||
-- Logging
|
||||
create {WSF_LOGGING_FILTER} f
|
||||
f.set_next (l_filter)
|
||||
l_filter := f
|
||||
filter := l_filter
|
||||
end
|
||||
|
||||
setup_filter
|
||||
-- Setup `filter'
|
||||
local
|
||||
f: WSF_FILTER
|
||||
do
|
||||
from
|
||||
f := filter
|
||||
until
|
||||
not attached f.next as l_next
|
||||
loop
|
||||
f := l_next
|
||||
end
|
||||
f.set_next (Current)
|
||||
end
|
||||
|
||||
setup_router
|
||||
do
|
||||
map_agent_uri ("/", agent execute_hello, Void)
|
||||
-- NOTE: you could put all those files in a specific folder, and use WSF_FILE_SYSTEM_HANDLER with "/"
|
||||
-- this way, it handles the caching and so on
|
||||
router.handle_with_request_methods ("/assets", create {WSF_FILE_SYSTEM_HANDLER}.make_hidden ("assets"), router.methods_GET)
|
||||
end
|
||||
|
||||
feature -- Helper: mapping
|
||||
|
||||
map_agent_uri (a_uri: READABLE_STRING_8; a_action: like {WSF_URI_AGENT_HANDLER}.action; rqst_methods: detachable WSF_REQUEST_METHODS)
|
||||
do
|
||||
router.map_with_request_methods (create {WSF_URI_MAPPING}.make (a_uri, create {WSF_URI_AGENT_HANDLER}.make (a_action)), rqst_methods)
|
||||
end
|
||||
|
||||
|
||||
feature -- Execution
|
||||
|
||||
execute_hello (request: WSF_REQUEST; response: WSF_RESPONSE)
|
||||
local
|
||||
page: EMPTY_PAGE
|
||||
do
|
||||
-- To send a response we need to setup, the status code and
|
||||
-- the response headers.
|
||||
create page.make (request, response)
|
||||
page.execute
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
note
|
||||
description: "simple application root class"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
APPLICATION_EXECUTION
|
||||
|
||||
inherit
|
||||
|
||||
WSF_FILTERED_ROUTED_EXECUTION
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature -- Router and Filter
|
||||
|
||||
create_filter
|
||||
-- Create `filter'
|
||||
do
|
||||
-- Maintenance
|
||||
create {WSF_MAINTENANCE_FILTER} filter
|
||||
end
|
||||
|
||||
setup_filter
|
||||
-- Setup `filter'
|
||||
do
|
||||
append_filters (<<create {WSF_LOGGING_FILTER}>>)
|
||||
end
|
||||
|
||||
setup_router
|
||||
do
|
||||
map_agent_uri ("/", agent execute_hello, Void)
|
||||
-- NOTE: you could put all those files in a specific folder, and use WSF_FILE_SYSTEM_HANDLER with "/"
|
||||
-- this way, it handles the caching and so on
|
||||
router.handle_with_request_methods ("/assets", create {WSF_FILE_SYSTEM_HANDLER}.make_hidden ("assets"), router.methods_GET)
|
||||
end
|
||||
|
||||
feature -- Helper: mapping
|
||||
|
||||
map_agent_uri (a_uri: READABLE_STRING_8; a_action: like {WSF_URI_AGENT_HANDLER}.action; rqst_methods: detachable WSF_REQUEST_METHODS)
|
||||
do
|
||||
router.map_with_request_methods (create {WSF_URI_MAPPING}.make (a_uri, create {WSF_URI_AGENT_HANDLER}.make (a_action)), rqst_methods)
|
||||
end
|
||||
|
||||
|
||||
feature -- Execution
|
||||
|
||||
execute_hello (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
local
|
||||
page: EMPTY_PAGE
|
||||
do
|
||||
-- To send a response we need to setup, the status code and
|
||||
-- the response headers.
|
||||
create page.make (req, res)
|
||||
page.execute
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
@@ -8,23 +8,11 @@ class
|
||||
|
||||
inherit
|
||||
|
||||
WSF_ROUTED_SERVICE
|
||||
rename
|
||||
execute as execute_router
|
||||
end
|
||||
|
||||
WSF_FILTERED_SERVICE
|
||||
|
||||
WSF_DEFAULT_SERVICE
|
||||
WSF_DEFAULT_SERVICE [APPLICATION_EXECUTION]
|
||||
redefine
|
||||
initialize
|
||||
end
|
||||
|
||||
WSF_FILTER
|
||||
rename
|
||||
execute as execute_router
|
||||
end
|
||||
|
||||
create
|
||||
make_and_launch
|
||||
|
||||
@@ -33,75 +21,8 @@ feature {NONE} -- Initialization
|
||||
initialize
|
||||
-- Initialize current service.
|
||||
do
|
||||
initialize_router
|
||||
initialize_filter
|
||||
Precursor
|
||||
set_service_option ("port", 7070)
|
||||
end
|
||||
|
||||
feature -- Router and Filter
|
||||
|
||||
create_filter
|
||||
-- Create `filter'
|
||||
local
|
||||
f, l_filter: detachable WSF_FILTER
|
||||
do
|
||||
l_filter := Void
|
||||
|
||||
-- Maintenance
|
||||
create {WSF_MAINTENANCE_FILTER} f
|
||||
f.set_next (l_filter)
|
||||
l_filter := f
|
||||
|
||||
-- Logging
|
||||
create {WSF_LOGGING_FILTER} f
|
||||
f.set_next (l_filter)
|
||||
l_filter := f
|
||||
filter := l_filter
|
||||
end
|
||||
|
||||
setup_filter
|
||||
-- Setup `filter'
|
||||
local
|
||||
f: WSF_FILTER
|
||||
do
|
||||
from
|
||||
f := filter
|
||||
until
|
||||
not attached f.next as l_next
|
||||
loop
|
||||
f := l_next
|
||||
end
|
||||
f.set_next (Current)
|
||||
end
|
||||
|
||||
setup_router
|
||||
do
|
||||
map_agent_uri ("/", agent execute_hello, Void)
|
||||
-- NOTE: you could put all those files in a specific folder, and use WSF_FILE_SYSTEM_HANDLER with "/"
|
||||
-- this way, it handles the caching and so on
|
||||
router.handle_with_request_methods ("/assets", create {WSF_FILE_SYSTEM_HANDLER}.make_hidden ("assets"), router.methods_GET)
|
||||
end
|
||||
|
||||
feature -- Helper: mapping
|
||||
|
||||
map_agent_uri (a_uri: READABLE_STRING_8; a_action: like {WSF_URI_AGENT_HANDLER}.action; rqst_methods: detachable WSF_REQUEST_METHODS)
|
||||
do
|
||||
router.map_with_request_methods (create {WSF_URI_MAPPING}.make (a_uri, create {WSF_URI_AGENT_HANDLER}.make (a_action)), rqst_methods)
|
||||
end
|
||||
|
||||
|
||||
feature -- Execution
|
||||
|
||||
execute_hello (request: WSF_REQUEST; response: WSF_RESPONSE)
|
||||
local
|
||||
page: EMPTY_PAGE
|
||||
do
|
||||
-- To send a response we need to setup, the status code and
|
||||
-- the response headers.
|
||||
create page.make (request, response)
|
||||
page.execute
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
note
|
||||
description: "simple application root class"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
APPLICATION_EXECUTION
|
||||
|
||||
inherit
|
||||
|
||||
WSF_FILTERED_ROUTED_EXECUTION
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature -- Router and Filter
|
||||
|
||||
create_filter
|
||||
-- Create `filter'
|
||||
do
|
||||
-- Maintenance
|
||||
create {WSF_MAINTENANCE_FILTER} filter
|
||||
end
|
||||
|
||||
setup_filter
|
||||
-- Setup `filter'
|
||||
do
|
||||
append_filters (<< create {WSF_LOGGING_FILTER} >>)
|
||||
end
|
||||
|
||||
setup_router
|
||||
do
|
||||
map_agent_uri ("/", agent execute_hello, Void)
|
||||
-- NOTE: you could put all those files in a specific folder, and use WSF_FILE_SYSTEM_HANDLER with "/"
|
||||
-- this way, it handles the caching and so on
|
||||
router.handle_with_request_methods ("/assets", create {WSF_FILE_SYSTEM_HANDLER}.make_hidden ("assets"), router.methods_GET)
|
||||
end
|
||||
|
||||
feature -- Helper: mapping
|
||||
|
||||
map_agent_uri (a_uri: READABLE_STRING_8; a_action: like {WSF_URI_AGENT_HANDLER}.action; rqst_methods: detachable WSF_REQUEST_METHODS)
|
||||
do
|
||||
router.map_with_request_methods (create {WSF_URI_MAPPING}.make (a_uri, create {WSF_URI_AGENT_HANDLER}.make (a_action)), rqst_methods)
|
||||
end
|
||||
|
||||
|
||||
feature -- Execution
|
||||
|
||||
execute_hello (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
local
|
||||
page: EMPTY_PAGE
|
||||
do
|
||||
-- To send a response we need to setup, the status code and
|
||||
-- the response headers.
|
||||
create page.make (req, res)
|
||||
page.execute
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
@@ -8,168 +8,22 @@ class
|
||||
|
||||
inherit
|
||||
|
||||
WSF_ROUTED_SERVICE
|
||||
rename
|
||||
execute as execute_router
|
||||
end
|
||||
|
||||
WSF_FILTERED_SERVICE
|
||||
|
||||
WSF_DEFAULT_SERVICE
|
||||
WSF_DEFAULT_SERVICE [APPLICATION_EXECUTION]
|
||||
redefine
|
||||
initialize
|
||||
end
|
||||
|
||||
WSF_FILTER
|
||||
rename
|
||||
execute as execute_router
|
||||
end
|
||||
|
||||
create
|
||||
make_and_launch
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
-- tt
|
||||
-- local
|
||||
-- lst: ARRAYED_LIST [READABLE_STRING_GENERAL]
|
||||
-- do
|
||||
-- create lst.make (3)
|
||||
-- lst.compare_objects
|
||||
-- lst.extend ({STRING_32} "abc")
|
||||
-- if lst.has ("abc") then
|
||||
-- print ("found%N")
|
||||
-- end
|
||||
-- end
|
||||
|
||||
initialize
|
||||
-- Initialize current service.
|
||||
do
|
||||
-- tt
|
||||
initialize_router
|
||||
initialize_filter
|
||||
Precursor
|
||||
set_service_option ("port", 9090)
|
||||
end
|
||||
|
||||
feature -- Router and Filter
|
||||
|
||||
create_filter
|
||||
-- Create `filter'
|
||||
local
|
||||
f, l_filter: detachable WSF_FILTER
|
||||
do
|
||||
l_filter := Void
|
||||
|
||||
-- Maintenance
|
||||
create {WSF_MAINTENANCE_FILTER} f
|
||||
f.set_next (l_filter)
|
||||
l_filter := f
|
||||
|
||||
-- Logging
|
||||
create {WSF_LOGGING_FILTER} f
|
||||
f.set_next (l_filter)
|
||||
l_filter := f
|
||||
filter := l_filter
|
||||
end
|
||||
|
||||
setup_filter
|
||||
-- Setup `filter'
|
||||
local
|
||||
f: WSF_FILTER
|
||||
do
|
||||
from
|
||||
f := filter
|
||||
until
|
||||
not attached f.next as l_next
|
||||
loop
|
||||
f := l_next
|
||||
end
|
||||
f.set_next (Current)
|
||||
end
|
||||
|
||||
setup_router
|
||||
do
|
||||
map_agent_uri ("/", agent execute_hello, Void)
|
||||
map_agent_uri ("/grid", agent grid_demo, Void)
|
||||
map_agent_uri ("/repeater", agent repeater_demo, Void)
|
||||
map_agent_uri ("/slider", agent slider_demo, Void)
|
||||
map_agent_uri ("/upload", agent upload_demo, Void)
|
||||
map_agent_uri ("/codeview", agent codeview, Void)
|
||||
|
||||
-- NOTE: you could put all those files in a specific folder, and use WSF_FILE_SYSTEM_HANDLER with "/"
|
||||
-- this way, it handles the caching and so on
|
||||
router.handle_with_request_methods ("/assets", create {WSF_FILE_SYSTEM_HANDLER}.make_hidden ("assets"), router.methods_GET)
|
||||
end
|
||||
|
||||
feature -- Helper: mapping
|
||||
|
||||
map_agent_uri (a_uri: READABLE_STRING_8; a_action: like {WSF_URI_AGENT_HANDLER}.action; rqst_methods: detachable WSF_REQUEST_METHODS)
|
||||
do
|
||||
router.map_with_request_methods (create {WSF_URI_MAPPING}.make (a_uri, create {WSF_URI_AGENT_HANDLER}.make (a_action)), rqst_methods)
|
||||
end
|
||||
|
||||
feature -- Execution
|
||||
|
||||
execute_hello (request: WSF_REQUEST; response: WSF_RESPONSE)
|
||||
local
|
||||
page: SAMPLE_PAGE
|
||||
do
|
||||
-- To send a response we need to setup, the status code and
|
||||
-- the response headers.
|
||||
create page.make (request, response)
|
||||
page.execute
|
||||
end
|
||||
|
||||
grid_demo (request: WSF_REQUEST; response: WSF_RESPONSE)
|
||||
local
|
||||
page: GRID_PAGE
|
||||
do
|
||||
-- To send a response we need to setup, the status code and
|
||||
-- the response headers.
|
||||
create page.make (request, response)
|
||||
page.execute
|
||||
end
|
||||
|
||||
repeater_demo (request: WSF_REQUEST; response: WSF_RESPONSE)
|
||||
local
|
||||
page: REPEATER_PAGE
|
||||
do
|
||||
-- To send a response we need to setup, the status code and
|
||||
-- the response headers.
|
||||
create page.make (request, response)
|
||||
page.execute
|
||||
end
|
||||
|
||||
slider_demo (request: WSF_REQUEST; response: WSF_RESPONSE)
|
||||
local
|
||||
page: SLIDER_PAGE
|
||||
do
|
||||
-- To send a response we need to setup, the status code and
|
||||
-- the response headers.
|
||||
create page.make (request, response)
|
||||
page.execute
|
||||
end
|
||||
|
||||
upload_demo (request: WSF_REQUEST; response: WSF_RESPONSE)
|
||||
local
|
||||
page: UPLOAD_PAGE
|
||||
do
|
||||
-- To send a response we need to setup, the status code and
|
||||
-- the response headers.
|
||||
create page.make (request, response)
|
||||
page.execute
|
||||
end
|
||||
|
||||
codeview (request: WSF_REQUEST; response: WSF_RESPONSE)
|
||||
local
|
||||
page: CODEVIEW_PAGE
|
||||
do
|
||||
-- To send a response we need to setup, the status code and
|
||||
-- the response headers.
|
||||
create page.make (request, response)
|
||||
page.execute
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
note
|
||||
description: "Summary description for {APPLICATION_EXECUTION}."
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
APPLICATION_EXECUTION
|
||||
|
||||
inherit
|
||||
WSF_FILTERED_ROUTED_EXECUTION
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature -- Router and Filter
|
||||
|
||||
create_filter
|
||||
-- Create `filter'
|
||||
do
|
||||
-- Maintenance
|
||||
create {WSF_MAINTENANCE_FILTER} filter
|
||||
end
|
||||
|
||||
setup_filter
|
||||
-- Setup `filter'
|
||||
do
|
||||
append_filters (<<create {WSF_LOGGING_FILTER}>>)
|
||||
end
|
||||
|
||||
setup_router
|
||||
do
|
||||
map_agent_uri ("/", agent execute_hello, Void)
|
||||
map_agent_uri ("/grid", agent grid_demo, Void)
|
||||
map_agent_uri ("/repeater", agent repeater_demo, Void)
|
||||
map_agent_uri ("/slider", agent slider_demo, Void)
|
||||
map_agent_uri ("/upload", agent upload_demo, Void)
|
||||
map_agent_uri ("/codeview", agent codeview, Void)
|
||||
|
||||
-- NOTE: you could put all those files in a specific folder, and use WSF_FILE_SYSTEM_HANDLER with "/"
|
||||
-- this way, it handles the caching and so on
|
||||
router.handle ("/assets", create {WSF_FILE_SYSTEM_HANDLER}.make_hidden ("assets"), router.methods_GET)
|
||||
end
|
||||
|
||||
feature -- Helper: mapping
|
||||
|
||||
map_agent_uri (a_uri: READABLE_STRING_8; a_action: like {WSF_URI_AGENT_HANDLER}.action; rqst_methods: detachable WSF_REQUEST_METHODS)
|
||||
do
|
||||
router.map (create {WSF_URI_MAPPING}.make (a_uri, create {WSF_URI_AGENT_HANDLER}.make (a_action)), rqst_methods)
|
||||
end
|
||||
|
||||
feature -- Execution
|
||||
|
||||
execute_hello (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
local
|
||||
page: SAMPLE_PAGE
|
||||
do
|
||||
-- To send a response we need to setup, the status code and
|
||||
-- the response headers.
|
||||
create page.make (req, res)
|
||||
page.execute
|
||||
end
|
||||
|
||||
grid_demo (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
local
|
||||
page: GRID_PAGE
|
||||
do
|
||||
-- To send a response we need to setup, the status code and
|
||||
-- the response headers.
|
||||
create page.make (req, res)
|
||||
page.execute
|
||||
end
|
||||
|
||||
repeater_demo (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
local
|
||||
page: REPEATER_PAGE
|
||||
do
|
||||
-- To send a response we need to setup, the status code and
|
||||
-- the response headers.
|
||||
create page.make (req, res)
|
||||
page.execute
|
||||
end
|
||||
|
||||
slider_demo (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
local
|
||||
page: SLIDER_PAGE
|
||||
do
|
||||
-- To send a response we need to setup, the status code and
|
||||
-- the response headers.
|
||||
create page.make (req, res)
|
||||
page.execute
|
||||
end
|
||||
|
||||
upload_demo (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
local
|
||||
page: UPLOAD_PAGE
|
||||
do
|
||||
-- To send a response we need to setup, the status code and
|
||||
-- the response headers.
|
||||
create page.make (req, res)
|
||||
page.execute
|
||||
end
|
||||
|
||||
codeview (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
local
|
||||
page: CODEVIEW_PAGE
|
||||
do
|
||||
-- To send a response we need to setup, the status code and
|
||||
-- the response headers.
|
||||
create page.make (req, res)
|
||||
page.execute
|
||||
end
|
||||
|
||||
end
|
||||
@@ -1,6 +1,5 @@
|
||||
note
|
||||
description: "Summary description for {CONTACT_AUTOCOMPLETION}."
|
||||
author: ""
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
note
|
||||
description: "Summary description for {FLAG_AUTOCOMPLETION}."
|
||||
author: ""
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
note
|
||||
description: "Summary description for {GOOGLE_AUTOCOMPLETION}."
|
||||
author: ""
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
note
|
||||
description: "Summary description for {BASE_PAGE}."
|
||||
author: ""
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
note
|
||||
description: "Summary description for {CODEVIEW_PAGE}."
|
||||
author: ""
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
note
|
||||
description: "Summary description for {GOOGLE_NEWS}."
|
||||
author: ""
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
note
|
||||
description: "Summary description for {GOOGLE_NEWS_DATASOURCE}."
|
||||
author: ""
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
note
|
||||
description: "Summary description for {GOOGLE_NEWS_REPEATER}."
|
||||
author: ""
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
note
|
||||
description: "Summary description for {GRID_PAGE}."
|
||||
author: ""
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
note
|
||||
description: "Summary description for {OWN_VALIDATOR}."
|
||||
author: ""
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
note
|
||||
description: "Summary description for {INCREASING_PROGRESSSOURCE}."
|
||||
author: ""
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
note
|
||||
description: "Summary description for {REPEATER_PAGE}."
|
||||
author: ""
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
note
|
||||
description: "Summary description for {SAMPLE_PAGE}."
|
||||
author: ""
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
note
|
||||
description: "Summary description for {SLIDER_PAGE}."
|
||||
author: ""
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
note
|
||||
description: "Summary description for {UPLOAD_PAGE}."
|
||||
author: ""
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
|
||||
@@ -18,12 +18,21 @@
|
||||
</target>
|
||||
<target name="debug_any" extends="common">
|
||||
<root class="EWF_DEBUG_SERVER" feature="make_and_launch"/>
|
||||
<setting name="concurrency" value="thread"/>
|
||||
<library name="cgi" location="..\..\library\server\wsf\connector\cgi-safe.ecf" readonly="false"/>
|
||||
<library name="libfcgi" location="..\..\library\server\wsf\connector\libfcgi-safe.ecf"/>
|
||||
<library name="nino" location="..\..\library\server\wsf\connector\nino-safe.ecf"/>
|
||||
<library name="libfcgi" location="..\..\library\server\wsf\connector\libfcgi-safe.ecf" readonly="false"/>
|
||||
<library name="nino" location="..\..\library\server\wsf\connector\nino-safe.ecf" readonly="false"/>
|
||||
<library name="standalone" location="..\..\library\server\wsf\connector\standalone-safe.ecf" readonly="false"/>
|
||||
<cluster name="launcher" location=".\launcher\any\" recursive="true"/>
|
||||
<cluster name="src" location=".\src\" recursive="true"/>
|
||||
</target>
|
||||
<target name="debug_standalone" extends="common">
|
||||
<root class="EWF_DEBUG_SERVER" feature="make_and_launch"/>
|
||||
<setting name="concurrency" value="thread"/>
|
||||
<library name="default_standalone" location="..\..\library\server\wsf\default\standalone-safe.ecf" readonly="false"/>
|
||||
<cluster name="launcher" location=".\launcher\default\" recursive="true"/>
|
||||
<cluster name="src" location=".\src\" recursive="true"/>
|
||||
</target>
|
||||
<target name="debug_nino" extends="common">
|
||||
<root class="EWF_DEBUG_SERVER" feature="make_and_launch"/>
|
||||
<library name="default_nino" location="..\..\library\server\wsf\default\nino-safe.ecf"/>
|
||||
@@ -32,7 +41,7 @@
|
||||
</target>
|
||||
<target name="debug_cgi" extends="common">
|
||||
<root class="EWF_DEBUG_SERVER" feature="make_and_launch"/>
|
||||
<library name="default_cgi" location="..\..\library\server\wsf\default\cgi-safe.ecf"/>
|
||||
<library name="default_cgi" location="..\..\library\server\wsf\default\cgi-safe.ecf" readonly="false"/>
|
||||
<cluster name="launcher" location=".\launcher\default\" recursive="true"/>
|
||||
<cluster name="src" location=".\src\" recursive="true"/>
|
||||
</target>
|
||||
|
||||
@@ -8,10 +8,10 @@ note
|
||||
revision: "$Revision: 36 $"
|
||||
|
||||
class
|
||||
APPLICATION_LAUNCHER
|
||||
APPLICATION_LAUNCHER [G -> WSF_EXECUTION create make end]
|
||||
|
||||
inherit
|
||||
APPLICATION_LAUNCHER_I
|
||||
APPLICATION_LAUNCHER_I [G]
|
||||
|
||||
feature -- Custom
|
||||
|
||||
|
||||
@@ -10,24 +10,26 @@ note
|
||||
revision: "$Revision: 36 $"
|
||||
|
||||
deferred class
|
||||
APPLICATION_LAUNCHER_I
|
||||
APPLICATION_LAUNCHER_I [G -> WSF_EXECUTION create make end]
|
||||
|
||||
inherit
|
||||
SHARED_EXECUTION_ENVIRONMENT
|
||||
|
||||
feature -- Execution
|
||||
|
||||
launch (a_service: WSF_SERVICE; opts: detachable WSF_SERVICE_LAUNCHER_OPTIONS)
|
||||
launch (opts: detachable WSF_SERVICE_LAUNCHER_OPTIONS)
|
||||
local
|
||||
nature: like launcher_nature
|
||||
do
|
||||
nature := launcher_nature
|
||||
if nature = Void or else nature = nature_nino then
|
||||
launch_nino (a_service, opts)
|
||||
if nature = Void or else nature = nature_standalone then
|
||||
launch_standalone (opts)
|
||||
elseif nature = nature_nino then
|
||||
launch_nino (opts)
|
||||
elseif nature = nature_cgi then
|
||||
launch_cgi (a_service, opts)
|
||||
launch_cgi (opts)
|
||||
elseif nature = nature_libfcgi then
|
||||
launch_libfcgi (a_service, opts)
|
||||
launch_libfcgi (opts)
|
||||
else
|
||||
-- bye bye
|
||||
(create {EXCEPTIONS}).die (-1)
|
||||
@@ -43,14 +45,16 @@ feature {NONE} -- Access
|
||||
--| and we could use WSF_DEFAULT_SERVICE_LAUNCHER to configure this at compilation time.
|
||||
local
|
||||
p: PATH
|
||||
l_entry_name: READABLE_STRING_32
|
||||
ext: detachable READABLE_STRING_32
|
||||
do
|
||||
create p.make_from_string (execution_environment.arguments.command_name)
|
||||
if attached p.entry as l_entry then
|
||||
ext := l_entry.extension
|
||||
ext := l_entry.extension
|
||||
end
|
||||
if ext /= Void then
|
||||
if ext.same_string (nature_standalone) then
|
||||
Result := nature_standalone
|
||||
end
|
||||
if ext.same_string (nature_nino) then
|
||||
Result := nature_nino
|
||||
end
|
||||
@@ -61,39 +65,51 @@ feature {NONE} -- Access
|
||||
Result := nature_libfcgi
|
||||
end
|
||||
end
|
||||
Result := nature_standalone
|
||||
end
|
||||
|
||||
feature {NONE} -- nino
|
||||
|
||||
nature_standalone: STRING = "standalone"
|
||||
|
||||
launch_standalone (opts: detachable WSF_SERVICE_LAUNCHER_OPTIONS)
|
||||
local
|
||||
launcher: WSF_STANDALONE_SERVICE_LAUNCHER [G]
|
||||
do
|
||||
create launcher.make_and_launch (opts)
|
||||
end
|
||||
|
||||
feature {NONE} -- nino
|
||||
|
||||
nature_nino: STRING = "nino"
|
||||
|
||||
launch_nino (a_service: WSF_SERVICE; opts: detachable WSF_SERVICE_LAUNCHER_OPTIONS)
|
||||
launch_nino (opts: detachable WSF_SERVICE_LAUNCHER_OPTIONS)
|
||||
local
|
||||
launcher: WSF_NINO_SERVICE_LAUNCHER
|
||||
launcher: WSF_NINO_SERVICE_LAUNCHER [G]
|
||||
do
|
||||
create launcher.make_and_launch (a_service, opts)
|
||||
create launcher.make_and_launch (opts)
|
||||
end
|
||||
|
||||
feature {NONE} -- cgi
|
||||
|
||||
nature_cgi: STRING = "cgi"
|
||||
|
||||
launch_cgi (a_service: WSF_SERVICE; opts: detachable WSF_SERVICE_LAUNCHER_OPTIONS)
|
||||
launch_cgi (opts: detachable WSF_SERVICE_LAUNCHER_OPTIONS)
|
||||
local
|
||||
launcher: WSF_CGI_SERVICE_LAUNCHER
|
||||
launcher: WSF_CGI_SERVICE_LAUNCHER [G]
|
||||
do
|
||||
create launcher.make_and_launch (a_service, opts)
|
||||
create launcher.make_and_launch (opts)
|
||||
end
|
||||
|
||||
feature {NONE} -- libfcgi
|
||||
|
||||
nature_libfcgi: STRING = "libfcgi"
|
||||
|
||||
launch_libfcgi (a_service: WSF_SERVICE; opts: detachable WSF_SERVICE_LAUNCHER_OPTIONS)
|
||||
launch_libfcgi (opts: detachable WSF_SERVICE_LAUNCHER_OPTIONS)
|
||||
local
|
||||
launcher: WSF_LIBFCGI_SERVICE_LAUNCHER
|
||||
launcher: WSF_LIBFCGI_SERVICE_LAUNCHER [G]
|
||||
do
|
||||
create launcher.make_and_launch (a_service, opts)
|
||||
create launcher.make_and_launch (opts)
|
||||
end
|
||||
|
||||
|
||||
|
||||
@@ -8,10 +8,10 @@ note
|
||||
revision: "$Revision: 36 $"
|
||||
|
||||
class
|
||||
APPLICATION_LAUNCHER
|
||||
APPLICATION_LAUNCHER [G -> WSF_EXECUTION create make end]
|
||||
|
||||
inherit
|
||||
APPLICATION_LAUNCHER_I
|
||||
APPLICATION_LAUNCHER_I [G]
|
||||
|
||||
feature -- Custom
|
||||
|
||||
|
||||
@@ -10,15 +10,15 @@ note
|
||||
revision: "$Revision: 36 $"
|
||||
|
||||
deferred class
|
||||
APPLICATION_LAUNCHER_I
|
||||
APPLICATION_LAUNCHER_I [G -> WSF_EXECUTION create make end]
|
||||
|
||||
feature -- Execution
|
||||
|
||||
launch (a_service: WSF_SERVICE; opts: detachable WSF_SERVICE_LAUNCHER_OPTIONS)
|
||||
launch (opts: detachable WSF_SERVICE_LAUNCHER_OPTIONS)
|
||||
local
|
||||
launcher: WSF_SERVICE_LAUNCHER
|
||||
launcher: WSF_SERVICE_LAUNCHER [G]
|
||||
do
|
||||
create {WSF_DEFAULT_SERVICE_LAUNCHER} launcher.make_and_launch (a_service, opts)
|
||||
create {WSF_DEFAULT_SERVICE_LAUNCHER [G]} launcher.make_and_launch (opts)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
27
examples/debug/src/ewf_debug_execution.e
Normal file
27
examples/debug/src/ewf_debug_execution.e
Normal file
@@ -0,0 +1,27 @@
|
||||
note
|
||||
description: "Summary description for {EWF_DEBUG_EXECUTION}."
|
||||
author: ""
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
EWF_DEBUG_EXECUTION
|
||||
|
||||
inherit
|
||||
WSF_EXECUTION
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature -- Execution
|
||||
|
||||
execute
|
||||
local
|
||||
dbg: WSF_DEBUG_HANDLER
|
||||
do
|
||||
response.put_error ("DEBUG uri=" + request.request_uri + "%N")
|
||||
create dbg.make
|
||||
dbg.execute_starts_with ("", request, response)
|
||||
end
|
||||
|
||||
end
|
||||
@@ -14,7 +14,7 @@ inherit
|
||||
initialize
|
||||
end
|
||||
|
||||
APPLICATION_LAUNCHER
|
||||
APPLICATION_LAUNCHER [EWF_DEBUG_EXECUTION]
|
||||
|
||||
create
|
||||
make_and_launch
|
||||
@@ -30,14 +30,14 @@ feature {NONE} -- Initialization
|
||||
-- set_service_option ("base", "/www-debug/debug_service.fcgi/")
|
||||
end
|
||||
|
||||
execute (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
local
|
||||
dbg: WSF_DEBUG_HANDLER
|
||||
do
|
||||
res.put_error ("DEBUG" + req.request_uri + "%N")
|
||||
create dbg.make
|
||||
dbg.execute_starts_with ("", req, res)
|
||||
end
|
||||
-- execute (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
-- local
|
||||
-- dbg: WSF_DEBUG_HANDLER
|
||||
-- do
|
||||
-- res.put_error ("OH NO uri=" + req.request_uri + "%N")
|
||||
-- create dbg.make
|
||||
-- dbg.execute_starts_with ("", req, res)
|
||||
-- end
|
||||
|
||||
end
|
||||
|
||||
|
||||
@@ -8,16 +8,15 @@
|
||||
<assertions precondition="true" postcondition="true" check="true"/>
|
||||
</option>
|
||||
<setting name="concurrency" value="thread"/>
|
||||
<precompile name="vision2-pre" location="$ISE_PRECOMP\vision2-mt-safe.ecf"/>
|
||||
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
|
||||
<library name="ewsgi" location="..\..\library\server\ewsgi\ewsgi-safe.ecf"/>
|
||||
<library name="thread" location="$ISE_LIBRARY\library\thread\thread-safe.ecf"/>
|
||||
<library name="vision2" location="$ISE_LIBRARY\library\vision2\vision2-safe.ecf"/>
|
||||
<library name="web_browser" location="$ISE_LIBRARY\library\web_browser\web_browser-safe.ecf" readonly="false"/>
|
||||
<library name="wsf" location="..\..\library\server\wsf\wsf-safe.ecf"/>
|
||||
<library name="wsf_nino" location="..\..\library\server\wsf\connector\nino-safe.ecf"/>
|
||||
<library name="wsf_nino_connector" location="..\..\library\server\ewsgi\connectors\nino\nino-safe.ecf"/>
|
||||
<cluster name="src" location=".\src" recursive="true">
|
||||
<library name="wsf_standalone" location="..\..\library\server\wsf\connector\standalone-safe.ecf"/>
|
||||
<library name="wsf_standalone_connector" location="..\..\library\server\ewsgi\connectors\standalone\standalone-safe.ecf"/>
|
||||
<cluster name="src" location=".\src\" recursive="true">
|
||||
<file_rule>
|
||||
<exclude>/EIFGENs$</exclude>
|
||||
<exclude>/CVS$</exclude>
|
||||
|
||||
239
examples/desktop_app/src/app_embedded_web_execution.e
Normal file
239
examples/desktop_app/src/app_embedded_web_execution.e
Normal file
@@ -0,0 +1,239 @@
|
||||
note
|
||||
description: "Summary description for {APP_EMBEDDED_WEB_EXECUTION}."
|
||||
author: ""
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
APP_EMBEDDED_WEB_EXECUTION
|
||||
|
||||
inherit
|
||||
EMBEDDED_WEB_EXECUTION
|
||||
redefine
|
||||
initialize
|
||||
end
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
initialize
|
||||
do
|
||||
Precursor
|
||||
create request_exit_operation_actions
|
||||
local_connection_restriction_enabled := True
|
||||
end
|
||||
|
||||
feature -- Execution
|
||||
|
||||
request_exit_operation_actions: ACTION_SEQUENCE [TUPLE]
|
||||
|
||||
execute
|
||||
-- Execute the request
|
||||
-- See `request.input' for input stream
|
||||
-- `request.meta_variables' for the CGI meta variable
|
||||
-- and `response' for output buffer
|
||||
local
|
||||
router: WSF_ROUTER
|
||||
sess: detachable WSF_ROUTER_SESSION
|
||||
m: WSF_HTML_PAGE_RESPONSE
|
||||
b: STRING
|
||||
fs: WSF_FILE_SYSTEM_HANDLER
|
||||
req: like request
|
||||
do
|
||||
req := request
|
||||
|
||||
create router.make (3)
|
||||
router.handle ("/test/{var}", create {WSF_URI_TEMPLATE_AGENT_HANDLER}.make (agent handle_test), Void)
|
||||
router.handle ("/env", create {WSF_URI_AGENT_HANDLER}.make (agent handle_env), Void)
|
||||
router.handle ("/exit", create {WSF_URI_TEMPLATE_AGENT_HANDLER}.make (agent handle_exit), Void)
|
||||
create fs.make_with_path ((create {EXECUTION_ENVIRONMENT}).current_working_path.extended ("files"))
|
||||
router.handle ("/files", fs, Void)
|
||||
create sess
|
||||
router.dispatch (req, response, sess)
|
||||
if not sess.dispatched then
|
||||
create m.make
|
||||
create b.make_from_string ("<h1>Hello Eiffel desktop user</h1>")
|
||||
b.append ("<li><a href=%"" + req.script_url ("/test/start") + "%">test</a></li>")
|
||||
b.append ("<li><a href=%"" + req.script_url ("/env") + "%">env</a></li>")
|
||||
b.append ("<li><a href=%"" + req.script_url ("/files") + "%">files</a></li>")
|
||||
b.append ("<li><a href=%"" + req.script_url ("/exit") + "%">exit</a></li>")
|
||||
m.set_body (b)
|
||||
response.send (m)
|
||||
end
|
||||
end
|
||||
|
||||
handle_test (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
local
|
||||
m: WSF_HTML_PAGE_RESPONSE
|
||||
b: STRING
|
||||
l_name: READABLE_STRING_32
|
||||
do
|
||||
if attached {WSF_STRING} req.item ("var") as p_name then
|
||||
l_name := p_name.value
|
||||
else
|
||||
l_name := {STRING_32} "Embedded web service and web_browser in vision2 application"
|
||||
end
|
||||
create m.make
|
||||
create b.make_from_string ("<h1>This is a test about "+ m.html_encoded_string (l_name) +"</h1>")
|
||||
b.append ("<li><a href=%"" + req.script_url ("/") + "%">back to home</a></li>")
|
||||
if l_name.is_case_insensitive_equal_general ("start") then
|
||||
b.append ("<li><a href=%"" + req.script_url ("/test/js") + "%">test javascript+ajax</a></li>")
|
||||
elseif l_name.is_case_insensitive_equal_general ("js") then
|
||||
b.append ("[
|
||||
<div id="myDiv"><h2>Let AJAX change this text</h2>
|
||||
<button type="button" onclick="loadXMLDoc()">Change Content</button>
|
||||
</div>
|
||||
]")
|
||||
m.add_javascript_content ("[
|
||||
function loadXMLDoc()
|
||||
{
|
||||
var xmlhttp;
|
||||
if (window.XMLHttpRequest)
|
||||
{// code for IE7+, Firefox, Chrome, Opera, Safari
|
||||
xmlhttp=new XMLHttpRequest();
|
||||
}
|
||||
else
|
||||
{// code for IE6, IE5
|
||||
xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
|
||||
}
|
||||
xmlhttp.onreadystatechange=function()
|
||||
{
|
||||
if (xmlhttp.readyState==4 && xmlhttp.status==200)
|
||||
{
|
||||
document.getElementById("myDiv").innerHTML=xmlhttp.responseText;
|
||||
}
|
||||
}
|
||||
xmlhttp.open("GET","/test/ajax.txt",true);
|
||||
xmlhttp.send();
|
||||
}
|
||||
]")
|
||||
elseif l_name.is_case_insensitive_equal_general ("ajax.txt") then
|
||||
b := "This is AJAX response ... from " + req.absolute_script_url ("")
|
||||
end
|
||||
m.set_body (b)
|
||||
res.send (m)
|
||||
end
|
||||
|
||||
handle_env (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
local
|
||||
s: STRING_8
|
||||
p: WSF_PAGE_RESPONSE
|
||||
v: STRING_8
|
||||
do
|
||||
create s.make (2048)
|
||||
s.append ("**DEBUG**%N")
|
||||
req.set_raw_input_data_recorded (True)
|
||||
|
||||
append_iterable_to ("Meta variables:", req.meta_variables, s)
|
||||
s.append_character ('%N')
|
||||
|
||||
append_iterable_to ("Path parameters", req.path_parameters, s)
|
||||
s.append_character ('%N')
|
||||
|
||||
append_iterable_to ("Query parameters", req.query_parameters, s)
|
||||
s.append_character ('%N')
|
||||
|
||||
append_iterable_to ("Form parameters", req.form_parameters, s)
|
||||
s.append_character ('%N')
|
||||
|
||||
if attached req.content_type as l_type then
|
||||
s.append ("Content: type=" + l_type.debug_output)
|
||||
s.append (" length=")
|
||||
s.append_natural_64 (req.content_length_value)
|
||||
s.append_character ('%N')
|
||||
create v.make (req.content_length_value.to_integer_32)
|
||||
req.read_input_data_into (v)
|
||||
across
|
||||
v.split ('%N') as v_cursor
|
||||
loop
|
||||
s.append (" |")
|
||||
s.append (v_cursor.item)
|
||||
s.append_character ('%N')
|
||||
end
|
||||
end
|
||||
|
||||
create p.make_with_body (s)
|
||||
p.header.put_content_type_text_plain
|
||||
res.send (p)
|
||||
end
|
||||
|
||||
handle_exit (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
local
|
||||
m: WSF_HTML_PAGE_RESPONSE
|
||||
b: STRING
|
||||
do
|
||||
create m.make
|
||||
create b.make_from_string ("<h1>Embedded server is about to shutdown</h1>")
|
||||
b.append ("<li><a href=%"" + req.script_url ("/") + "%">back to home</a></li>")
|
||||
b.append ("<li><a href=%"" + req.script_url ("/bye") + "%">Click to confirm exit operation</a></li>")
|
||||
m.set_body (b)
|
||||
res.send (m)
|
||||
if attached {separate WGI_STANDALONE_CONNECTOR [WGI_EXECUTION]} req.wgi_connector as conn then
|
||||
shutdown_server (conn)
|
||||
end
|
||||
request_exit_operation_actions.call (Void)
|
||||
end
|
||||
|
||||
shutdown_server (conn: separate WGI_STANDALONE_CONNECTOR [WGI_EXECUTION])
|
||||
do
|
||||
conn.shutdown_server
|
||||
end
|
||||
|
||||
feature {NONE} -- Implementation
|
||||
|
||||
append_iterable_to (a_title: READABLE_STRING_8; it: detachable ITERABLE [WSF_VALUE]; s: STRING_8)
|
||||
local
|
||||
n: INTEGER
|
||||
t: READABLE_STRING_8
|
||||
v: READABLE_STRING_8
|
||||
do
|
||||
s.append (a_title)
|
||||
s.append_character (':')
|
||||
if it /= Void then
|
||||
across it as c loop
|
||||
n := n + 1
|
||||
end
|
||||
if n = 0 then
|
||||
s.append (" empty")
|
||||
s.append_character ('%N')
|
||||
else
|
||||
s.append_character ('%N')
|
||||
across
|
||||
it as c
|
||||
loop
|
||||
s.append (" - ")
|
||||
s.append (c.item.url_encoded_name)
|
||||
t := c.item.generating_type
|
||||
if t.same_string ("WSF_STRING") then
|
||||
else
|
||||
s.append_character (' ')
|
||||
s.append_character ('{')
|
||||
s.append (t)
|
||||
s.append_character ('}')
|
||||
end
|
||||
s.append_character ('=')
|
||||
v := c.item.string_representation.as_string_8
|
||||
if v.has ('%N') then
|
||||
s.append_character ('%N')
|
||||
across
|
||||
v.split ('%N') as v_cursor
|
||||
loop
|
||||
s.append (" |")
|
||||
s.append (v_cursor.item)
|
||||
s.append_character ('%N')
|
||||
end
|
||||
else
|
||||
s.append (v)
|
||||
s.append_character ('%N')
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
s.append (" none")
|
||||
s.append_character ('%N')
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
@@ -8,223 +8,11 @@ class
|
||||
APP_EMBEDDED_WEB_SERVICE
|
||||
|
||||
inherit
|
||||
EMBEDDED_WEB_SERVICE
|
||||
redefine
|
||||
make
|
||||
end
|
||||
EMBEDDED_WEB_SERVICE [APP_EMBEDDED_WEB_EXECUTION]
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
make
|
||||
do
|
||||
Precursor
|
||||
create request_exit_operation_actions
|
||||
local_connection_restriction_enabled := True
|
||||
end
|
||||
|
||||
feature -- Execution
|
||||
|
||||
request_exit_operation_actions: ACTION_SEQUENCE [TUPLE]
|
||||
|
||||
execute (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
-- Execute the request
|
||||
-- See `req.input' for input stream
|
||||
-- `req.meta_variables' for the CGI meta variable
|
||||
-- and `res' for output buffer
|
||||
local
|
||||
router: WSF_ROUTER
|
||||
sess: detachable WSF_ROUTER_SESSION
|
||||
m: WSF_HTML_PAGE_RESPONSE
|
||||
b: STRING
|
||||
fs: WSF_FILE_SYSTEM_HANDLER
|
||||
do
|
||||
create router.make (3)
|
||||
router.handle ("/test/{var}", create {WSF_URI_TEMPLATE_AGENT_HANDLER}.make (agent handle_test))
|
||||
router.handle ("/env", create {WSF_URI_AGENT_HANDLER}.make (agent handle_env))
|
||||
router.handle ("/exit", create {WSF_URI_TEMPLATE_AGENT_HANDLER}.make (agent handle_exit))
|
||||
create fs.make_with_path ((create {EXECUTION_ENVIRONMENT}).current_working_path.extended ("files"))
|
||||
router.handle ("/files", fs)
|
||||
create sess
|
||||
router.dispatch (req, res, sess)
|
||||
if not sess.dispatched then
|
||||
create m.make
|
||||
create b.make_from_string ("<h1>Hello Eiffel desktop user</h1>")
|
||||
b.append ("<li><a href=%"" + req.script_url ("/test/start") + "%">test</a></li>")
|
||||
b.append ("<li><a href=%"" + req.script_url ("/env") + "%">env</a></li>")
|
||||
b.append ("<li><a href=%"" + req.script_url ("/files") + "%">files</a></li>")
|
||||
b.append ("<li><a href=%"" + req.script_url ("/exit") + "%">exit</a></li>")
|
||||
m.set_body (b)
|
||||
res.send (m)
|
||||
end
|
||||
end
|
||||
|
||||
handle_test (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
local
|
||||
m: WSF_HTML_PAGE_RESPONSE
|
||||
b: STRING
|
||||
l_name: READABLE_STRING_32
|
||||
do
|
||||
if attached {WSF_STRING} req.item ("var") as p_name then
|
||||
l_name := p_name.value
|
||||
else
|
||||
l_name := {STRING_32} "Embedded web service and web_browser in vision2 application"
|
||||
end
|
||||
create m.make
|
||||
create b.make_from_string ("<h1>This is a test about "+ m.html_encoded_string (l_name) +"</h1>")
|
||||
b.append ("<li><a href=%"" + req.script_url ("/") + "%">back to home</a></li>")
|
||||
if l_name.is_case_insensitive_equal_general ("start") then
|
||||
b.append ("<li><a href=%"" + req.script_url ("/test/js") + "%">test javascript+ajax</a></li>")
|
||||
elseif l_name.is_case_insensitive_equal_general ("js") then
|
||||
b.append ("[
|
||||
<div id="myDiv"><h2>Let AJAX change this text</h2>
|
||||
<button type="button" onclick="loadXMLDoc()">Change Content</button>
|
||||
</div>
|
||||
]")
|
||||
m.add_javascript_content ("[
|
||||
function loadXMLDoc()
|
||||
{
|
||||
var xmlhttp;
|
||||
if (window.XMLHttpRequest)
|
||||
{// code for IE7+, Firefox, Chrome, Opera, Safari
|
||||
xmlhttp=new XMLHttpRequest();
|
||||
}
|
||||
else
|
||||
{// code for IE6, IE5
|
||||
xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
|
||||
}
|
||||
xmlhttp.onreadystatechange=function()
|
||||
{
|
||||
if (xmlhttp.readyState==4 && xmlhttp.status==200)
|
||||
{
|
||||
document.getElementById("myDiv").innerHTML=xmlhttp.responseText;
|
||||
}
|
||||
}
|
||||
xmlhttp.open("GET","/test/ajax.txt",true);
|
||||
xmlhttp.send();
|
||||
}
|
||||
]")
|
||||
elseif l_name.is_case_insensitive_equal_general ("ajax.txt") then
|
||||
b := "This is AJAX response ... from " + req.absolute_script_url ("")
|
||||
end
|
||||
m.set_body (b)
|
||||
res.send (m)
|
||||
end
|
||||
|
||||
handle_env (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
local
|
||||
s: STRING_8
|
||||
p: WSF_PAGE_RESPONSE
|
||||
v: STRING_8
|
||||
do
|
||||
create s.make (2048)
|
||||
s.append ("**DEBUG**%N")
|
||||
req.set_raw_input_data_recorded (True)
|
||||
|
||||
append_iterable_to ("Meta variables:", req.meta_variables, s)
|
||||
s.append_character ('%N')
|
||||
|
||||
append_iterable_to ("Path parameters", req.path_parameters, s)
|
||||
s.append_character ('%N')
|
||||
|
||||
append_iterable_to ("Query parameters", req.query_parameters, s)
|
||||
s.append_character ('%N')
|
||||
|
||||
append_iterable_to ("Form parameters", req.form_parameters, s)
|
||||
s.append_character ('%N')
|
||||
|
||||
if attached req.content_type as l_type then
|
||||
s.append ("Content: type=" + l_type.debug_output)
|
||||
s.append (" length=")
|
||||
s.append_natural_64 (req.content_length_value)
|
||||
s.append_character ('%N')
|
||||
create v.make (req.content_length_value.to_integer_32)
|
||||
req.read_input_data_into (v)
|
||||
across
|
||||
v.split ('%N') as v_cursor
|
||||
loop
|
||||
s.append (" |")
|
||||
s.append (v_cursor.item)
|
||||
s.append_character ('%N')
|
||||
end
|
||||
end
|
||||
|
||||
create p.make_with_body (s)
|
||||
p.header.put_content_type_text_plain
|
||||
res.send (p)
|
||||
end
|
||||
|
||||
handle_exit (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
local
|
||||
m: WSF_HTML_PAGE_RESPONSE
|
||||
b: STRING
|
||||
do
|
||||
create m.make
|
||||
create b.make_from_string ("<h1>Embedded server is about to shutdown</h1>")
|
||||
b.append ("<li><a href=%"" + req.script_url ("/") + "%">back to home</a></li>")
|
||||
m.set_body (b)
|
||||
res.send (m)
|
||||
if attached {WGI_NINO_CONNECTOR} req.wgi_connector as nino then
|
||||
nino.server.shutdown_server
|
||||
end
|
||||
request_exit_operation_actions.call (Void)
|
||||
end
|
||||
|
||||
feature {NONE} -- Implementation
|
||||
|
||||
append_iterable_to (a_title: READABLE_STRING_8; it: detachable ITERABLE [WSF_VALUE]; s: STRING_8)
|
||||
local
|
||||
n: INTEGER
|
||||
t: READABLE_STRING_8
|
||||
v: READABLE_STRING_8
|
||||
do
|
||||
s.append (a_title)
|
||||
s.append_character (':')
|
||||
if it /= Void then
|
||||
across it as c loop
|
||||
n := n + 1
|
||||
end
|
||||
if n = 0 then
|
||||
s.append (" empty")
|
||||
s.append_character ('%N')
|
||||
else
|
||||
s.append_character ('%N')
|
||||
across
|
||||
it as c
|
||||
loop
|
||||
s.append (" - ")
|
||||
s.append (c.item.url_encoded_name)
|
||||
t := c.item.generating_type
|
||||
if t.same_string ("WSF_STRING") then
|
||||
else
|
||||
s.append_character (' ')
|
||||
s.append_character ('{')
|
||||
s.append (t)
|
||||
s.append_character ('}')
|
||||
end
|
||||
s.append_character ('=')
|
||||
v := c.item.string_representation.as_string_8
|
||||
if v.has ('%N') then
|
||||
s.append_character ('%N')
|
||||
across
|
||||
v.split ('%N') as v_cursor
|
||||
loop
|
||||
s.append (" |")
|
||||
s.append (v_cursor.item)
|
||||
s.append_character ('%N')
|
||||
end
|
||||
else
|
||||
s.append (v)
|
||||
s.append_character ('%N')
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
s.append (" none")
|
||||
s.append_character ('%N')
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -24,18 +24,17 @@ feature {NONE} -- Initialization
|
||||
-- then launch the application.
|
||||
local
|
||||
l_win: like main_window
|
||||
l_embeded_services: APP_EMBEDDED_WEB_SERVICE
|
||||
l_embedded_service: APP_EMBEDDED_WEB_SERVICE
|
||||
do
|
||||
default_create
|
||||
create l_win.make
|
||||
main_window := l_win
|
||||
l_win.show
|
||||
create l_embeded_services.make
|
||||
l_embeded_services.set_port_number (0) -- Use first available port number
|
||||
create l_embedded_service.make
|
||||
l_embedded_service.set_port_number (0) -- Use first available port number
|
||||
|
||||
l_embeded_services.on_launched_actions.force (agent on_web_service_launched (l_win))
|
||||
l_embeded_services.request_exit_operation_actions.force (agent on_quit)
|
||||
l_embeded_services.launch
|
||||
l_embedded_service.on_launched_actions.force (agent on_web_service_launched (l_win, l_embedded_service))
|
||||
l_embedded_service.launch
|
||||
launch
|
||||
end
|
||||
|
||||
@@ -46,11 +45,44 @@ feature {NONE} -- Initialization
|
||||
end
|
||||
end
|
||||
|
||||
on_web_service_launched (a_win: attached like main_window)
|
||||
on_web_service_launched (a_win: attached like main_window; s: APP_EMBEDDED_WEB_SERVICE)
|
||||
do
|
||||
add_idle_action_kamikaze (agent wait_for_termination (s, Void))
|
||||
add_idle_action_kamikaze (agent a_win.open_link)
|
||||
end
|
||||
|
||||
wait_for_termination (s: APP_EMBEDDED_WEB_SERVICE; a_timeout: detachable EV_TIMEOUT)
|
||||
local
|
||||
t: detachable EV_TIMEOUT
|
||||
do
|
||||
t := a_timeout
|
||||
if t /= Void then
|
||||
t.set_interval (0)
|
||||
end
|
||||
if
|
||||
attached s.observer as obs and then
|
||||
observer_has_terminaded (obs)
|
||||
then
|
||||
if t /= Void then
|
||||
t.destroy
|
||||
end
|
||||
on_quit
|
||||
else
|
||||
if t = Void then
|
||||
create t
|
||||
t.actions.extend (agent wait_for_termination (s, t))
|
||||
else
|
||||
t.set_interval (1_000)
|
||||
end
|
||||
t.set_interval (1_000)
|
||||
end
|
||||
end
|
||||
|
||||
observer_has_terminaded (obs: separate WGI_STANDALONE_SERVER_OBSERVER): BOOLEAN
|
||||
do
|
||||
Result := obs.terminated
|
||||
end
|
||||
|
||||
feature {NONE} -- Implementation
|
||||
|
||||
main_window: detachable MAIN_WINDOW
|
||||
|
||||
63
examples/desktop_app/src/service/embedded_web_execution.e
Normal file
63
examples/desktop_app/src/service/embedded_web_execution.e
Normal file
@@ -0,0 +1,63 @@
|
||||
note
|
||||
description: "Summary description for {EMBEDDED_WEB_EXECUTION}."
|
||||
author: ""
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
deferred class
|
||||
EMBEDDED_WEB_EXECUTION
|
||||
|
||||
inherit
|
||||
WSF_EXECUTION
|
||||
rename
|
||||
execute as execute_embedded
|
||||
end
|
||||
|
||||
SHARED_EMBEDED_WEB_SERVICE_INFORMATION
|
||||
|
||||
feature {NONE} -- Execution
|
||||
|
||||
execute_embedded
|
||||
-- Execute the request
|
||||
-- See `request.input' for input stream
|
||||
-- `request.meta_variables' for the CGI meta variable
|
||||
-- and `response' for output buffer
|
||||
local
|
||||
filter: WSF_AGENT_FILTER
|
||||
m: WSF_PAGE_RESPONSE
|
||||
do
|
||||
if local_connection_restriction_enabled then
|
||||
if
|
||||
attached request.remote_addr as l_remote_addr and then
|
||||
l_remote_addr.is_case_insensitive_equal_general ("127.0.0.1")
|
||||
then
|
||||
execute
|
||||
else
|
||||
create m.make_with_body ("Only local connection is allowed")
|
||||
m.set_status_code (403) -- Forbidden
|
||||
response.send (m)
|
||||
end
|
||||
else
|
||||
execute
|
||||
end
|
||||
end
|
||||
|
||||
execute
|
||||
deferred
|
||||
end
|
||||
|
||||
feature -- Status report
|
||||
|
||||
local_connection_restriction_enabled: BOOLEAN
|
||||
-- Accept only local connection?
|
||||
--| based on 127.0.0.1 IP
|
||||
--| TO IMPROVE
|
||||
|
||||
feature -- Change
|
||||
|
||||
set_local_connection_restriction_enabled (b: BOOLEAN)
|
||||
do
|
||||
local_connection_restriction_enabled := b
|
||||
end
|
||||
|
||||
end
|
||||
@@ -5,19 +5,10 @@ note
|
||||
revision: "$Revision$"
|
||||
|
||||
deferred class
|
||||
EMBEDDED_WEB_SERVICE
|
||||
EMBEDDED_WEB_SERVICE [G -> EMBEDDED_WEB_EXECUTION create make end]
|
||||
|
||||
inherit
|
||||
THREAD
|
||||
rename
|
||||
make as make_thread,
|
||||
execute as execute_thread
|
||||
end
|
||||
|
||||
WSF_SERVICE
|
||||
rename
|
||||
execute as execute_embedded
|
||||
end
|
||||
|
||||
SHARED_EMBEDED_WEB_SERVICE_INFORMATION
|
||||
|
||||
@@ -25,90 +16,35 @@ feature -- Initialization
|
||||
|
||||
make
|
||||
do
|
||||
make_thread
|
||||
create on_launched_actions
|
||||
end
|
||||
|
||||
feature {NONE} -- Execution
|
||||
feature -- Execution
|
||||
|
||||
execute_embedded (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
-- Execute the request
|
||||
-- See `req.input' for input stream
|
||||
-- `req.meta_variables' for the CGI meta variable
|
||||
-- and `res' for output buffer
|
||||
launch
|
||||
local
|
||||
filter: WSF_AGENT_FILTER
|
||||
m: WSF_PAGE_RESPONSE
|
||||
do
|
||||
if local_connection_restriction_enabled then
|
||||
if
|
||||
attached req.remote_addr as l_remote_addr and then
|
||||
l_remote_addr.is_case_insensitive_equal_general ("127.0.0.1")
|
||||
then
|
||||
execute (req, res)
|
||||
else
|
||||
create m.make_with_body ("Only local connection is allowed")
|
||||
m.set_status_code (403) -- Forbidden
|
||||
res.send (m)
|
||||
end
|
||||
else
|
||||
execute (req, res)
|
||||
end
|
||||
end
|
||||
|
||||
execute_thread
|
||||
local
|
||||
nino: WSF_NINO_SERVICE_LAUNCHER
|
||||
launcher: WSF_STANDALONE_SERVICE_LAUNCHER [G]
|
||||
opts: WSF_SERVICE_LAUNCHER_OPTIONS
|
||||
do
|
||||
create opts.default_create
|
||||
opts.set_verbose (True)
|
||||
opts.set_option ("port", port_number)
|
||||
create nino.make (Current, opts)
|
||||
nino.on_launched_actions.force (agent on_launched)
|
||||
nino.launch
|
||||
create launcher.make (opts)
|
||||
observer := launcher.connector.observer
|
||||
launcher.on_launched_actions.force (agent on_launched)
|
||||
launcher.launch
|
||||
end
|
||||
|
||||
execute (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
-- Execute the request
|
||||
-- See `req.input' for input stream
|
||||
-- `req.meta_variables' for the CGI meta variable
|
||||
-- and `res' for output buffer
|
||||
deferred
|
||||
end
|
||||
observer: detachable separate WGI_STANDALONE_SERVER_OBSERVER
|
||||
|
||||
on_launched (conn: WGI_CONNECTOR)
|
||||
on_launched (conn: WGI_STANDALONE_CONNECTOR [G])
|
||||
do
|
||||
if attached {WGI_NINO_CONNECTOR} conn as nino then
|
||||
set_port_number (nino.port)
|
||||
end
|
||||
set_port_number (conn.port)
|
||||
on_launched_actions.call (Void)
|
||||
end
|
||||
|
||||
feature -- Control
|
||||
|
||||
wait
|
||||
-- Wait for server to be terminated.
|
||||
do
|
||||
join
|
||||
end
|
||||
|
||||
feature -- Access
|
||||
|
||||
on_launched_actions: ACTION_SEQUENCE [TUPLE]
|
||||
|
||||
feature -- Status report
|
||||
|
||||
local_connection_restriction_enabled: BOOLEAN
|
||||
-- Accept only local connection?
|
||||
--| based on 127.0.0.1 IP
|
||||
--| TO IMPROVE
|
||||
|
||||
feature -- Change
|
||||
|
||||
set_local_connection_restriction_enabled (b: BOOLEAN)
|
||||
do
|
||||
local_connection_restriction_enabled := b
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -11,15 +11,25 @@ feature -- Access
|
||||
|
||||
port_number: INTEGER
|
||||
do
|
||||
Result := port_number_cell.item
|
||||
Result := separate_port_number (port_number_cell)
|
||||
end
|
||||
|
||||
set_port_number (a_port: like port_number)
|
||||
do
|
||||
port_number_cell.replace (a_port)
|
||||
separate_set_port_number (port_number_cell, a_port)
|
||||
end
|
||||
|
||||
port_number_cell: CELL [INTEGER]
|
||||
separate_port_number (cl: like port_number_cell): like port_number
|
||||
do
|
||||
Result := cl.item
|
||||
end
|
||||
|
||||
separate_set_port_number (cl: like port_number_cell; a_port: like port_number)
|
||||
do
|
||||
cl.replace (a_port)
|
||||
end
|
||||
|
||||
port_number_cell: separate CELL [INTEGER]
|
||||
once ("process")
|
||||
create Result.put (0)
|
||||
end
|
||||
|
||||
@@ -1,30 +1,36 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-9-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-9-0 http://www.eiffel.com/developers/xml/configuration-1-9-0.xsd" name="filter" uuid="52FF4B77-0614-4D8B-9B96-C07EC852793E" library_target="filter">
|
||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-13-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-13-0 http://www.eiffel.com/developers/xml/configuration-1-13-0.xsd" name="filter" uuid="52FF4B77-0614-4D8B-9B96-C07EC852793E" library_target="filter">
|
||||
<target name="common">
|
||||
<file_rule>
|
||||
<exclude>/EIFGENs$</exclude>
|
||||
<exclude>/\.git$</exclude>
|
||||
<exclude>/\.svn$</exclude>
|
||||
</file_rule>
|
||||
<option debug="true" warning="true" full_class_checking="true" is_attached_by_default="true" void_safety="all" syntax="provisional">
|
||||
<option debug="true" warning="true" full_class_checking="true" is_attached_by_default="true" void_safety="transitional" syntax="provisional">
|
||||
<debug name="nino" enabled="true"/>
|
||||
<assertions precondition="true" postcondition="true" invariant="true" supplier_precondition="true"/>
|
||||
</option>
|
||||
<setting name="concurrency" value="thread"/>
|
||||
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf" readonly="true"/>
|
||||
<library name="net" location="$ISE_LIBRARY\library\net\net-safe.ecf" readonly="true"/>
|
||||
<library name="http" location="../../library/network/protocol/http/http-safe.ecf" readonly="true"/>
|
||||
<library name="json" location="..\..\contrib\library\text\parser\json\library\json-safe.ecf" readonly="true"/>
|
||||
<library name="wsf" location="..\..\library\server\wsf\wsf-safe.ecf" readonly="true"/>
|
||||
<library name="wsf_router_context" location="..\..\library\server\wsf\wsf_router_context-safe.ecf" readonly="true"/>
|
||||
<library name="wsf_extension" location="..\..\library\server\wsf\wsf_extension-safe.ecf" readonly="true"/>
|
||||
<library name="http" location="..\..\library\network\protocol\http\http-safe.ecf" readonly="true"/>
|
||||
<library name="http_authorization" location="..\..\library\server\authentication\http_authorization\http_authorization-safe.ecf" readonly="true"/>
|
||||
<library name="json" location="..\..\contrib\library\text\parser\json\library\json-safe.ecf" readonly="true"/>
|
||||
<library name="net" location="$ISE_LIBRARY\library\net\net-safe.ecf" readonly="true"/>
|
||||
<library name="wsf" location="..\..\library\server\wsf\wsf-safe.ecf" readonly="false"/>
|
||||
<library name="wsf_extension" location="..\..\library\server\wsf\wsf_extension-safe.ecf" readonly="true"/>
|
||||
<library name="wsf_router_context" location="..\..\library\server\wsf\wsf_router_context-safe.ecf" readonly="true"/>
|
||||
</target>
|
||||
<target name="filter_nino" extends="common">
|
||||
<root class="FILTER_SERVER" feature="make"/>
|
||||
<library name="default_nino" location="..\..\library\server\wsf\default\nino-safe.ecf" readonly="true"/>
|
||||
<cluster name="filter" location="src\" recursive="true"/>
|
||||
</target>
|
||||
<target name="filter_standalone" extends="common">
|
||||
<root class="FILTER_SERVER" feature="make"/>
|
||||
<setting name="concurrency" value="thread"/>
|
||||
<library name="default_standalone" location="..\..\library\server\wsf\default\standalone-safe.ecf" readonly="true"/>
|
||||
<cluster name="filter" location="src\" recursive="true"/>
|
||||
</target>
|
||||
<target name="filter_fcgi" extends="common">
|
||||
<root class="FILTER_SERVER" feature="make"/>
|
||||
<library name="default_libfcgi" location="..\..\library\server\wsf\default\libfcgi-safe.ecf"/>
|
||||
|
||||
@@ -24,26 +24,33 @@ feature -- Initialization
|
||||
|
||||
feature -- Access
|
||||
|
||||
user_by_id (a_id: INTEGER): detachable USER
|
||||
do
|
||||
Result := users.item (a_id)
|
||||
end
|
||||
|
||||
user_by_name (a_name: READABLE_STRING_GENERAL): detachable USER
|
||||
do
|
||||
across
|
||||
users as c
|
||||
until
|
||||
Result /= Void
|
||||
loop
|
||||
if attached c.item as u and then a_name.same_string (u.name) then
|
||||
Result := u
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
user (a_id: INTEGER; a_name: detachable READABLE_STRING_GENERAL): detachable USER
|
||||
-- User with id `a_id' or name `a_name'.
|
||||
require
|
||||
a_id > 0 xor a_name /= Void
|
||||
local
|
||||
n: like {USER}.name
|
||||
do
|
||||
if a_id > 0 then
|
||||
Result := users.item (a_id)
|
||||
Result := user_by_id (a_id)
|
||||
elseif a_name /= Void then
|
||||
n := a_name.as_string_8
|
||||
across
|
||||
users as c
|
||||
until
|
||||
Result /= Void
|
||||
loop
|
||||
if attached c.item as u and then u.name.same_string (n) then
|
||||
Result := u
|
||||
end
|
||||
end
|
||||
Result := user_by_name (a_name)
|
||||
end
|
||||
ensure
|
||||
Result /= Void implies ((a_id > 0 and then Result.id = a_id) xor (a_name /= Void and then Result.name.same_string_general (a_name)))
|
||||
@@ -52,6 +59,6 @@ feature -- Access
|
||||
users: HASH_TABLE [USER, INTEGER]
|
||||
|
||||
;note
|
||||
copyright: "2011-2012, Olivier Ligot, Jocelyn Fiat and others"
|
||||
copyright: "2011-2015, Olivier Ligot, Jocelyn Fiat and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
end
|
||||
|
||||
@@ -48,18 +48,31 @@ feature {NONE} -- Implementation
|
||||
-- Handle forbidden.
|
||||
local
|
||||
h: HTTP_HEADER
|
||||
s: STRING
|
||||
do
|
||||
create h.make
|
||||
h.put_content_type_text_plain
|
||||
h.put_content_length (a_description.count)
|
||||
h.put_current_date
|
||||
h.put_header_key_value ({HTTP_HEADER_NAMES}.header_www_authenticate, "Basic realm=%"User%"")
|
||||
s := "Basic realm=%"For this demo, use any of: "
|
||||
across
|
||||
db_access.users as ic
|
||||
loop
|
||||
s.append_character ('(')
|
||||
s.append (ic.item.name)
|
||||
s.append_character (':')
|
||||
s.append (ic.item.password)
|
||||
s.append_character (')')
|
||||
s.append_character (' ')
|
||||
end
|
||||
s.append_character ('"')
|
||||
h.put_header_key_value ({HTTP_HEADER_NAMES}.header_www_authenticate, s)
|
||||
res.set_status_code ({HTTP_STATUS_CODE}.unauthorized)
|
||||
res.put_header_text (h.string)
|
||||
res.put_string (a_description)
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2014, Olivier Ligot, Jocelyn Fiat and others"
|
||||
copyright: "2011-2015, Olivier Ligot, Jocelyn Fiat and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
end
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user