Merge branch 'master' of github.com:EiffelWebFramework/EWF into widget_integration
This commit is contained in:
142
CONTRIBUTING.md
Normal file
142
CONTRIBUTING.md
Normal file
@@ -0,0 +1,142 @@
|
||||
# Contributing to this project
|
||||
|
||||
Please take a moment to review this document in order to make the contribution
|
||||
process easy and effective for everyone involved.
|
||||
|
||||
Following these guidelines helps to communicate that you respect the time of
|
||||
the developers managing and developing this open source project. In return,
|
||||
they should reciprocate that respect in addressing your issue or assessing
|
||||
patches and features.
|
||||
|
||||
|
||||
## Using the issue tracker
|
||||
|
||||
The issue tracker is the preferred channel for [bug reports](#bugs),
|
||||
[features requests](#features) and [submitting pull
|
||||
requests](#pull-requests), but please respect the following restrictions:
|
||||
|
||||
* Please **do not** use the issue tracker for personal support requests (use
|
||||
[Stack Overflow](http://stackoverflow.com) or IRC).
|
||||
|
||||
* Please **do not** derail or troll issues. Keep the discussion on topic and
|
||||
respect the opinions of others.
|
||||
|
||||
|
||||
<a name="bugs"></a>
|
||||
## Bug reports
|
||||
|
||||
A bug is a _demonstrable problem_ that is caused by the code in the repository.
|
||||
Good bug reports are extremely helpful - thank you!
|
||||
|
||||
Guidelines for bug reports:
|
||||
|
||||
1. **Use the GitHub issue search** — check if the issue has already been
|
||||
reported.
|
||||
|
||||
2. **Check if the issue has been fixed** — try to reproduce it using the
|
||||
latest `master` or development branch in the repository.
|
||||
|
||||
3. **Isolate the problem** — ideally create a [reduced test
|
||||
case](http://css-tricks.com/6263-reduced-test-cases/) and a live example.
|
||||
|
||||
A good bug report shouldn't leave others needing to chase you up for more
|
||||
information. Please try to be as detailed as possible in your report. What is
|
||||
your environment? What steps will reproduce the issue? What browser(s) and OS
|
||||
experience the problem? What would you expect to be the outcome? All these
|
||||
details will help people to fix any potential bugs.
|
||||
|
||||
Example:
|
||||
|
||||
> Short and descriptive example bug report title
|
||||
>
|
||||
> A summary of the issue and the browser/OS environment in which it occurs. If
|
||||
> suitable, include the steps required to reproduce the bug.
|
||||
>
|
||||
> 1. This is the first step
|
||||
> 2. This is the second step
|
||||
> 3. Further steps, etc.
|
||||
>
|
||||
> `<url>` - a link to the reduced test case
|
||||
>
|
||||
> Any other information you want to share that is relevant to the issue being
|
||||
> reported. This might include the lines of code that you have identified as
|
||||
> causing the bug, and potential solutions (and your opinions on their
|
||||
> merits).
|
||||
|
||||
|
||||
<a name="features"></a>
|
||||
## Feature requests
|
||||
|
||||
Feature requests are welcome. But take a moment to find out whether your idea
|
||||
fits with the scope and aims of the project. It's up to *you* to make a strong
|
||||
case to convince the project's developers of the merits of this feature. Please
|
||||
provide as much detail and context as possible.
|
||||
|
||||
|
||||
<a name="pull-requests"></a>
|
||||
## Pull requests
|
||||
|
||||
Good pull requests - patches, improvements, new features - are a fantastic
|
||||
help. They should remain focused in scope and avoid containing unrelated
|
||||
commits.
|
||||
|
||||
**Please ask first** before embarking on any significant pull request (e.g.
|
||||
implementing features, refactoring code, porting to a different language),
|
||||
otherwise you risk spending a lot of time working on something that the
|
||||
project's developers might not want to merge into the project.
|
||||
|
||||
Please adhere to the coding conventions used throughout a project (indentation,
|
||||
accurate comments, etc.) and any other requirements (such as test coverage).
|
||||
|
||||
Adhering to the following this process is the best way to get your work
|
||||
included in the project:
|
||||
|
||||
1. [Fork](http://help.github.com/fork-a-repo/) the project, clone your fork,
|
||||
and configure the remotes:
|
||||
|
||||
```bash
|
||||
# Clone your fork of the repo into the current directory
|
||||
git clone https://github.com/<your-username>/<repo-name>
|
||||
# Navigate to the newly cloned directory
|
||||
cd <repo-name>
|
||||
# Assign the original repo to a remote called "upstream"
|
||||
git remote add upstream https://github.com/<upstream-owner>/<repo-name>
|
||||
```
|
||||
|
||||
2. If you cloned a while ago, get the latest changes from upstream:
|
||||
|
||||
```bash
|
||||
git checkout <dev-branch>
|
||||
git pull upstream <dev-branch>
|
||||
```
|
||||
|
||||
3. Create a new topic branch (off the main project development branch) to
|
||||
contain your feature, change, or fix:
|
||||
|
||||
```bash
|
||||
git checkout -b <topic-branch-name>
|
||||
```
|
||||
|
||||
4. Commit your changes in logical chunks. Please adhere to these [git commit
|
||||
message guidelines](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html)
|
||||
or your code is unlikely be merged into the main project. Use Git's
|
||||
[interactive rebase](https://help.github.com/articles/interactive-rebase)
|
||||
feature to tidy up your commits before making them public.
|
||||
|
||||
5. Locally merge (or rebase) the upstream development branch into your topic branch:
|
||||
|
||||
```bash
|
||||
git pull [--rebase] upstream <dev-branch>
|
||||
```
|
||||
|
||||
6. Push your topic branch up to your fork:
|
||||
|
||||
```bash
|
||||
git push origin <topic-branch-name>
|
||||
```
|
||||
|
||||
7. [Open a Pull Request](https://help.github.com/articles/using-pull-requests/)
|
||||
with a clear title and description.
|
||||
|
||||
**IMPORTANT**: By submitting a patch, you agree to allow the project owner to
|
||||
license your work under the same license as that used by the project.
|
||||
50
README.md
50
README.md
@@ -3,6 +3,27 @@
|
||||
|
||||
## Overview
|
||||
|
||||
The Eiffel Web Framework (EWF) provides Eiffel users with a layer to build anything on top of the http protocol such as websites, web API/services.
|
||||
|
||||
This layer is multi-platform: it can be set on Windows, Linux operating systems, and in addition it can run on top of any httpd servers such as Apache2, IIS, nginx, lighttpd. EWF includes as well a standalone httpd web server component, written in Eiffel, which enables users to run easily a web server on their machine, or even embed this component in any application written with Eiffel.
|
||||
|
||||
Currently EWF offers a collection of Eiffel libraries designed to be integrated with each others, and among other functionalities, it give simple access to the request data, to handle content negotiation, url dispatcher, integrate with openid system, and so on.
|
||||
|
||||
There is a growing ecosystem around EWF, that provides useful components:
|
||||
* OpenID and OAuth consumer library
|
||||
* Various hypermedia format such as HAL, Collection+json, …
|
||||
* Websocket server and client
|
||||
* Template engine
|
||||
* API Auto-documentation with swagger
|
||||
* A simple experimental CMS.
|
||||
* ...
|
||||
|
||||
So if you want to build a website, a web api, RESTful service, …or even if you want to consume other web api, EWF is a solution.
|
||||
|
||||
EWF brings with it all the advantages of the Eiffel technology and tools with its powerful features such as Design by Contract, debugging, testing tools which enable to build efficient systems expected to be repeatedly refined, extended, and improved in a predictable and controllable way so as to become with time bugfree systems. Enjoy the full power of debugging your web server application from the IDE.
|
||||
|
||||
## Project
|
||||
|
||||
Official project site for Eiffel Web Framework:
|
||||
|
||||
* http://eiffelwebframework.github.com/EWF/
|
||||
@@ -14,10 +35,14 @@ For more information please have a look at the related wiki:
|
||||
For download, check
|
||||
* https://github.com/EiffelWebFramework/EWF/downloads
|
||||
|
||||
Tasks and issues are managed with github issue system
|
||||
* See https://github.com/EiffelWebFramework/EWF/issues
|
||||
* And visual dashboard: https://waffle.io/eiffelwebframework/ewf
|
||||
|
||||
## Requirements
|
||||
* Compiling from EiffelStudio 7.2
|
||||
* Developped using EiffelStudio 7.3 (on Windows, Linux)
|
||||
* Tested using EiffelStudio 7.2 with "jenkins" CI server (not anymore compatible with 6.8 due to use of `TABLE_ITERABLE')
|
||||
* Compiling from EiffelStudio 7.2 to 13.11 and more recent version of the compiler.
|
||||
* Developped using EiffelStudio 13.11 (on Windows, Linux)
|
||||
* Tested using EiffelStudio 13.11 with "jenkins" CI server (not anymore compatible with 6.8 due to use of `TABLE_ITERABLE')
|
||||
* The code have to allow __void-safe__ compilation and non void-safe system (see [more about void-safety](http://docs.eiffel.com/book/method/void-safe-programming-eiffel) )
|
||||
|
||||
## How to get the source code?
|
||||
@@ -41,7 +66,7 @@ Using git
|
||||
### protocol
|
||||
* __http__: HTTP related classes, constants for status code, content types, ... [read more](library/protocol/http)
|
||||
* __uri_template__: URI Template library (parsing and expander) [read more](library/protocol/uri_template)
|
||||
* __CONNEG__: CONNEG library (Content-type Negociation) [read more](library/protocol/CONNEG)
|
||||
* __CONNEG__: Content negotiation library (Content-type Negociation) [read more](library/protocol/content_negotiation)
|
||||
|
||||
### client
|
||||
* __http_client__: simple HTTP client based on cURL [read more](library/client/http_client)
|
||||
@@ -61,6 +86,23 @@ Using git
|
||||
## Examples
|
||||
..
|
||||
|
||||
## Contributing to this project
|
||||
|
||||
Anyone and everyone is welcome to contribute. Please take a moment to
|
||||
review the [guidelines for contributing](CONTRIBUTING.md).
|
||||
|
||||
* [Bug reports](CONTRIBUTING.md#bugs)
|
||||
* [Feature requests](CONTRIBUTING.md#features)
|
||||
* [Pull requests](CONTRIBUTING.md#pull-requests)
|
||||
|
||||
## Community
|
||||
|
||||
Keep track of development and community news.
|
||||
|
||||
* Follow [@EiffelWeb](https://twitter.com/EiffelWeb) on Twitter
|
||||
* Follow our [page](https://plus.google.com/u/0/110650349519032194479) and [community](https://plus.google.com/communities/110457383244374256721) on Google+
|
||||
* Have a question that's not a feature request or bug report? [Ask on the mailing list](http://groups.google.com/group/eiffel-web-framework)
|
||||
|
||||
|
||||
For more information please have a look at the related wiki:
|
||||
* https://github.com/EiffelWebFramework/EWF/wiki
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-8-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-8-0 http://www.eiffel.com/developers/xml/configuration-1-8-0.xsd" name="nino" uuid="32C1D67D-33DE-4F1E-864B-D45388F2E3E6" library_target="nino">
|
||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-12-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-12-0 http://www.eiffel.com/developers/xml/configuration-1-12-0.xsd" name="nino" uuid="32C1D67D-33DE-4F1E-864B-D45388F2E3E6" library_target="nino">
|
||||
<target name="nino">
|
||||
<root all_classes="true"/>
|
||||
<file_rule>
|
||||
@@ -8,7 +8,7 @@
|
||||
<exclude>/CVS$</exclude>
|
||||
<exclude>/.svn$</exclude>
|
||||
</file_rule>
|
||||
<option warning="true" is_attached_by_default="true" void_safety="all">
|
||||
<option warning="true" full_class_checking="false" 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>
|
||||
<setting name="concurrency" value="thread"/>
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
|
||||
9
contrib/library/text/parser/json/.gitattributes
vendored
Normal file
9
contrib/library/text/parser/json/.gitattributes
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
# Set default behaviour, in case users don't have core.autocrlf set.
|
||||
* text=auto
|
||||
|
||||
# Explicitly declare text files we want to always be normalized and converted
|
||||
# to native line endings on checkout.
|
||||
*.e text
|
||||
*.ecf text
|
||||
*.bat text
|
||||
*.json text
|
||||
@@ -92,6 +92,8 @@ history.txt.
|
||||
|
||||
Version Date Description
|
||||
------- ---- -----------
|
||||
0.5.0 2013-11-dd Added JSON_ITERATOR, simplified JSON_OBJECT
|
||||
0.4.0 2012-12-12 Updated documentation URI
|
||||
0.3.0 2011-07-06 JSON Factory Converters
|
||||
0.2.0 2010-02-07 Adapted to EiffelStudio 6.4 or later, supports void-safety
|
||||
0.1.0 2010-02-07 First release, Adapted to SmartEiffel 1.2r7 and EiffelStudio 6.2 or previous
|
||||
@@ -1,6 +1,5 @@
|
||||
note
|
||||
description: "Objects that ..."
|
||||
author: ""
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
@@ -15,7 +14,7 @@ feature -- Access
|
||||
template_content: STRING
|
||||
l_last_string: detachable STRING
|
||||
do
|
||||
create l_file.make (a_path)
|
||||
create l_file.make_with_name (a_path)
|
||||
-- We perform several checks until we make a real attempt to open the file.
|
||||
if not l_file.exists then
|
||||
print ("error: '" + a_path + "' does not exist%N")
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
note
|
||||
description:
|
||||
|
||||
"JSON Iterator"
|
||||
|
||||
pattern: "Iterator visitor"
|
||||
author: "Jocelyn Fiat"
|
||||
license:"MIT (see http://www.opensource.org/licenses/mit-license.php)"
|
||||
date: "2013/08/01"
|
||||
revision: "Revision 0.1"
|
||||
|
||||
deferred class
|
||||
JSON_ITERATOR
|
||||
|
||||
inherit
|
||||
JSON_VISITOR
|
||||
|
||||
feature -- Visitor Pattern
|
||||
|
||||
visit_json_array (a_json_array: JSON_ARRAY)
|
||||
-- Visit `a_json_array'.
|
||||
do
|
||||
across
|
||||
a_json_array as c
|
||||
loop
|
||||
c.item.accept (Current)
|
||||
end
|
||||
end
|
||||
|
||||
visit_json_boolean (a_json_boolean: JSON_BOOLEAN)
|
||||
-- Visit `a_json_boolean'.
|
||||
do
|
||||
end
|
||||
|
||||
visit_json_null (a_json_null: JSON_NULL)
|
||||
-- Visit `a_json_null'.
|
||||
do
|
||||
end
|
||||
|
||||
visit_json_number (a_json_number: JSON_NUMBER)
|
||||
-- Visit `a_json_number'.
|
||||
do
|
||||
end
|
||||
|
||||
visit_json_object (a_json_object: JSON_OBJECT)
|
||||
-- Visit `a_json_object'.
|
||||
do
|
||||
across
|
||||
a_json_object as c
|
||||
loop
|
||||
c.item.accept (Current)
|
||||
end
|
||||
end
|
||||
|
||||
visit_json_string (a_json_string: JSON_STRING)
|
||||
-- Visit `a_json_string'.
|
||||
do
|
||||
end
|
||||
|
||||
end
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-8-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-8-0 http://www.eiffel.com/developers/xml/configuration-1-8-0.xsd" name="json" uuid="4E21C3BD-7951-4C6E-A673-431E762D7414" library_target="json">
|
||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-12-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-12-0 http://www.eiffel.com/developers/xml/configuration-1-12-0.xsd" name="json" uuid="4E21C3BD-7951-4C6E-A673-431E762D7414" library_target="json">
|
||||
<target name="json">
|
||||
<root all_classes="true"/>
|
||||
<file_rule>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
note
|
||||
description: "A JSON converter for HASH_TABLE [ANY, HASHABLE]"
|
||||
author: "Paul Cohen"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
date: "$Date: 2014-01-30 15:27:41 +0100 (jeu., 30 janv. 2014) $"
|
||||
revision: "$Revision: 94128 $"
|
||||
file: "$HeadURL: $"
|
||||
|
||||
class JSON_HASH_TABLE_CONVERTER
|
||||
@@ -27,34 +27,20 @@ feature -- Access
|
||||
feature -- Conversion
|
||||
|
||||
from_json (j: attached like to_json): like object
|
||||
local
|
||||
keys: ARRAY [JSON_STRING]
|
||||
i: INTEGER
|
||||
h: detachable HASHABLE
|
||||
jv: detachable JSON_VALUE
|
||||
a: detachable ANY
|
||||
do
|
||||
keys := j.current_keys
|
||||
create Result.make (keys.count)
|
||||
from
|
||||
i := 1
|
||||
until
|
||||
i > keys.count
|
||||
create Result.make (j.count)
|
||||
across
|
||||
j as ic
|
||||
loop
|
||||
h ?= json.object (keys [i], Void)
|
||||
check h /= Void end
|
||||
jv := j.item (keys [i])
|
||||
if jv /= Void then
|
||||
a := json.object (jv, Void)
|
||||
if a /= Void then
|
||||
Result.put (a, h)
|
||||
if attached json.object (ic.item, Void) as l_object then
|
||||
if attached {HASHABLE} json.object (ic.key, Void) as h then
|
||||
Result.put (l_object, h)
|
||||
else
|
||||
check a_attached: a /= Void end
|
||||
check key_is_hashable: False end
|
||||
end
|
||||
else
|
||||
check j_has_item: False end
|
||||
check object_attached: False end
|
||||
end
|
||||
i := i + 1
|
||||
end
|
||||
end
|
||||
|
||||
@@ -62,7 +48,6 @@ feature -- Conversion
|
||||
local
|
||||
c: HASH_TABLE_ITERATION_CURSOR [ANY, HASHABLE]
|
||||
js: JSON_STRING
|
||||
jv: detachable JSON_VALUE
|
||||
failed: BOOLEAN
|
||||
do
|
||||
create Result.make
|
||||
@@ -76,8 +61,7 @@ feature -- Conversion
|
||||
else
|
||||
create js.make_json (c.key.out)
|
||||
end
|
||||
jv := json.value (c.item)
|
||||
if jv /= Void then
|
||||
if attached json.value (c.item) as jv then
|
||||
Result.put (jv, js)
|
||||
else
|
||||
failed := True
|
||||
|
||||
@@ -18,6 +18,8 @@ class
|
||||
inherit
|
||||
JSON_VALUE
|
||||
|
||||
ITERABLE [JSON_VALUE]
|
||||
|
||||
DEBUG_OUTPUT
|
||||
|
||||
create
|
||||
@@ -69,6 +71,14 @@ feature -- Visitor pattern
|
||||
a_visitor.visit_json_array (Current)
|
||||
end
|
||||
|
||||
feature -- Access
|
||||
|
||||
new_cursor: ITERATION_CURSOR [JSON_VALUE]
|
||||
-- Fresh cursor associated with current structure
|
||||
do
|
||||
Result := values.new_cursor
|
||||
end
|
||||
|
||||
feature -- Mesurement
|
||||
|
||||
count: INTEGER
|
||||
@@ -87,14 +97,40 @@ feature -- Status report
|
||||
|
||||
feature -- Change Element
|
||||
|
||||
add (value: JSON_VALUE)
|
||||
put_front (v: JSON_VALUE)
|
||||
require
|
||||
value_not_null: value /= void
|
||||
v_not_void: v /= Void
|
||||
do
|
||||
values.extend (value)
|
||||
values.put_front (v)
|
||||
ensure
|
||||
has_new_value: old values.count + 1 = values.count and
|
||||
values.has (value)
|
||||
values.first = v
|
||||
end
|
||||
|
||||
add, extend (v: JSON_VALUE)
|
||||
require
|
||||
v_not_void: v /= Void
|
||||
do
|
||||
values.extend (v)
|
||||
ensure
|
||||
has_new_value: old values.count + 1 = values.count and
|
||||
values.has (v)
|
||||
end
|
||||
|
||||
prune_all (v: JSON_VALUE)
|
||||
-- Remove all occurrences of `v'.
|
||||
require
|
||||
v_not_void: v /= Void
|
||||
do
|
||||
values.prune_all (v)
|
||||
ensure
|
||||
not_has_new_value: not values.has (v)
|
||||
end
|
||||
|
||||
wipe_out
|
||||
-- Remove all items.
|
||||
do
|
||||
values.wipe_out
|
||||
end
|
||||
|
||||
feature -- Report
|
||||
@@ -118,6 +154,7 @@ feature -- Conversion
|
||||
|
||||
array_representation: ARRAYED_LIST [JSON_VALUE]
|
||||
-- Representation as a sequences of values
|
||||
-- be careful, modifying the return object may have impact on the original JSON_ARRAY object
|
||||
do
|
||||
Result := values
|
||||
end
|
||||
@@ -127,7 +164,7 @@ feature -- Status report
|
||||
debug_output: STRING
|
||||
-- String that should be displayed in debugger to represent `Current'.
|
||||
do
|
||||
Result := count.out
|
||||
Result := count.out + " item(s)"
|
||||
end
|
||||
|
||||
feature {NONE} -- Implementation
|
||||
|
||||
@@ -8,7 +8,7 @@ note
|
||||
|
||||
object
|
||||
{}
|
||||
{"key","value"}
|
||||
{"key": "value"}
|
||||
|
||||
]"
|
||||
author: "Javier Velilla"
|
||||
@@ -24,6 +24,8 @@ inherit
|
||||
|
||||
TABLE_ITERABLE [JSON_VALUE, JSON_STRING]
|
||||
|
||||
DEBUG_OUTPUT
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
@@ -52,6 +54,67 @@ feature -- Change Element
|
||||
object.extend (l_value, key)
|
||||
end
|
||||
|
||||
put_string (value: READABLE_STRING_GENERAL; key: JSON_STRING)
|
||||
-- Assuming there is no item of key `key',
|
||||
-- insert `value' with `key'.
|
||||
require
|
||||
key_not_present: not has_key (key)
|
||||
local
|
||||
l_value: JSON_STRING
|
||||
do
|
||||
create l_value.make_json_from_string_32 (value.as_string_32)
|
||||
put (l_value, key)
|
||||
end
|
||||
|
||||
|
||||
put_integer (value: INTEGER_64; key: JSON_STRING)
|
||||
-- Assuming there is no item of key `key',
|
||||
-- insert `value' with `key'.
|
||||
require
|
||||
key_not_present: not has_key (key)
|
||||
local
|
||||
l_value: JSON_NUMBER
|
||||
do
|
||||
create l_value.make_integer (value)
|
||||
put (l_value, key)
|
||||
end
|
||||
|
||||
put_natural (value: NATURAL_64; key: JSON_STRING)
|
||||
-- Assuming there is no item of key `key',
|
||||
-- insert `value' with `key'.
|
||||
require
|
||||
key_not_present: not has_key (key)
|
||||
local
|
||||
l_value: JSON_NUMBER
|
||||
do
|
||||
create l_value.make_natural (value)
|
||||
put (l_value, key)
|
||||
end
|
||||
|
||||
put_real (value: DOUBLE; key: JSON_STRING)
|
||||
-- Assuming there is no item of key `key',
|
||||
-- insert `value' with `key'.
|
||||
require
|
||||
key_not_present: not has_key (key)
|
||||
local
|
||||
l_value: JSON_NUMBER
|
||||
do
|
||||
create l_value.make_real (value)
|
||||
put (l_value, key)
|
||||
end
|
||||
|
||||
put_boolean (value: BOOLEAN; key: JSON_STRING)
|
||||
-- Assuming there is no item of key `key',
|
||||
-- insert `value' with `key'.
|
||||
require
|
||||
key_not_present: not has_key (key)
|
||||
local
|
||||
l_value: JSON_BOOLEAN
|
||||
do
|
||||
create l_value.make_boolean (value)
|
||||
put (l_value, key)
|
||||
end
|
||||
|
||||
replace (value: detachable JSON_VALUE; key: JSON_STRING)
|
||||
-- Assuming there is no item of key `key',
|
||||
-- insert `value' with `key'.
|
||||
@@ -65,6 +128,68 @@ feature -- Change Element
|
||||
object.force (l_value, key)
|
||||
end
|
||||
|
||||
replace_with_string (value: READABLE_STRING_GENERAL; key: JSON_STRING)
|
||||
-- Assuming there is no item of key `key',
|
||||
-- insert `value' with `key'.
|
||||
local
|
||||
l_value: JSON_STRING
|
||||
do
|
||||
create l_value.make_json_from_string_32 (value.as_string_32)
|
||||
replace (l_value, key)
|
||||
end
|
||||
|
||||
replace_with_integer (value: INTEGER_64; key: JSON_STRING)
|
||||
-- Assuming there is no item of key `key',
|
||||
-- insert `value' with `key'.
|
||||
local
|
||||
l_value: JSON_NUMBER
|
||||
do
|
||||
create l_value.make_integer (value)
|
||||
replace (l_value, key)
|
||||
end
|
||||
|
||||
replace_with_with_natural (value: NATURAL_64; key: JSON_STRING)
|
||||
-- Assuming there is no item of key `key',
|
||||
-- insert `value' with `key'.
|
||||
local
|
||||
l_value: JSON_NUMBER
|
||||
do
|
||||
create l_value.make_natural (value)
|
||||
replace (l_value, key)
|
||||
end
|
||||
|
||||
replace_with_real (value: DOUBLE; key: JSON_STRING)
|
||||
-- Assuming there is no item of key `key',
|
||||
-- insert `value' with `key'.
|
||||
local
|
||||
l_value: JSON_NUMBER
|
||||
do
|
||||
create l_value.make_real (value)
|
||||
replace (l_value, key)
|
||||
end
|
||||
|
||||
replace_with_boolean (value: BOOLEAN; key: JSON_STRING)
|
||||
-- Assuming there is no item of key `key',
|
||||
-- insert `value' with `key'.
|
||||
local
|
||||
l_value: JSON_BOOLEAN
|
||||
do
|
||||
create l_value.make_boolean (value)
|
||||
replace (l_value, key)
|
||||
end
|
||||
|
||||
remove (key: JSON_STRING)
|
||||
-- Remove item indexed by `key' if any.
|
||||
do
|
||||
object.remove (key)
|
||||
end
|
||||
|
||||
wipe_out
|
||||
-- Reset all items to default values; reset status.
|
||||
do
|
||||
object.wipe_out
|
||||
end
|
||||
|
||||
feature -- Access
|
||||
|
||||
has_key (key: JSON_STRING): BOOLEAN
|
||||
@@ -95,7 +220,8 @@ feature -- Access
|
||||
local
|
||||
t: HASH_TABLE [JSON_VALUE, JSON_STRING]
|
||||
do
|
||||
Result := "{"
|
||||
create Result.make (2)
|
||||
Result.append_character ('{')
|
||||
from
|
||||
t := map_representation
|
||||
t.start
|
||||
@@ -103,7 +229,7 @@ feature -- Access
|
||||
t.after
|
||||
loop
|
||||
Result.append (t.key_for_iteration.representation)
|
||||
Result.append (":")
|
||||
Result.append_character (':')
|
||||
Result.append (t.item_for_iteration.representation)
|
||||
t.forth
|
||||
if not t.after then
|
||||
@@ -177,7 +303,7 @@ feature -- Status report
|
||||
debug_output: STRING
|
||||
-- String that should be displayed in debugger to represent `Current'.
|
||||
do
|
||||
Result := object.count.out
|
||||
Result := count.out + " item(s)"
|
||||
end
|
||||
|
||||
feature {NONE} -- Implementation
|
||||
@@ -186,6 +312,6 @@ feature {NONE} -- Implementation
|
||||
-- Value container
|
||||
|
||||
invariant
|
||||
object_not_null: object /= Void
|
||||
object_not_void: object /= Void
|
||||
|
||||
end
|
||||
|
||||
@@ -61,99 +61,29 @@ feature -- Access
|
||||
item: STRING
|
||||
-- Contents with escaped entities if any
|
||||
|
||||
feature -- Conversion
|
||||
|
||||
unescaped_string_8: STRING_8
|
||||
-- Unescaped string from `item'
|
||||
-- Unescaped string from `item'.
|
||||
--| note: valid only if `item' does not encode any unicode character.
|
||||
local
|
||||
s: like item
|
||||
i, n: INTEGER
|
||||
c: CHARACTER
|
||||
do
|
||||
s := item
|
||||
n := s.count
|
||||
create Result.make (n)
|
||||
from i := 1 until i > n loop
|
||||
c := s[i]
|
||||
if c = '\' then
|
||||
if i < n then
|
||||
inspect s[i+1]
|
||||
when '\' then
|
||||
Result.append_character ('\')
|
||||
i := i + 2
|
||||
when '%"' then
|
||||
Result.append_character ('%"')
|
||||
i := i + 2
|
||||
when 'n' then
|
||||
Result.append_character ('%N')
|
||||
i := i + 2
|
||||
when 'r' then
|
||||
Result.append_character ('%R')
|
||||
i := i + 2
|
||||
when 'u' then
|
||||
--| Leave unicode \uXXXX unescaped
|
||||
Result.append_character ('\')
|
||||
i := i + 1
|
||||
else
|
||||
Result.append_character ('\')
|
||||
i := i + 1
|
||||
end
|
||||
else
|
||||
Result.append_character ('\')
|
||||
i := i + 1
|
||||
end
|
||||
else
|
||||
Result.append_character (c)
|
||||
i := i + 1
|
||||
end
|
||||
end
|
||||
create Result.make (s.count)
|
||||
unescape_to_string_8 (Result)
|
||||
end
|
||||
|
||||
unescaped_string_32: STRING_32
|
||||
-- Unescaped string 32 from `item'
|
||||
--| some encoders uses UTF-8 , and not the recommended pure json encoding
|
||||
--| thus, let's support the UTF-8 encoding during decoding.
|
||||
local
|
||||
s: like item
|
||||
i, n: INTEGER
|
||||
c: CHARACTER
|
||||
hex: STRING
|
||||
s: READABLE_STRING_8
|
||||
do
|
||||
s := item
|
||||
n := s.count
|
||||
create Result.make (n)
|
||||
from i := 1 until i > n loop
|
||||
c := s[i]
|
||||
if c = '\' then
|
||||
if i < n then
|
||||
inspect s[i+1]
|
||||
when '\' then
|
||||
Result.append_character ('\')
|
||||
i := i + 2
|
||||
when '%"' then
|
||||
Result.append_character ('%"')
|
||||
i := i + 2
|
||||
when 'n' then
|
||||
Result.append_character ('%N')
|
||||
i := i + 2
|
||||
when 'r' then
|
||||
Result.append_character ('%R')
|
||||
i := i + 2
|
||||
when 'u' then
|
||||
hex := s.substring (i+2, i+2+4 - 1)
|
||||
if hex.count = 4 then
|
||||
Result.append_code (hexadecimal_to_natural_32 (hex))
|
||||
end
|
||||
i := i + 2 + 4
|
||||
else
|
||||
Result.append_character ('\')
|
||||
i := i + 1
|
||||
end
|
||||
else
|
||||
Result.append_character ('\')
|
||||
i := i + 1
|
||||
end
|
||||
else
|
||||
Result.append_character (c.to_character_32)
|
||||
i := i + 1
|
||||
end
|
||||
end
|
||||
create Result.make (s.count)
|
||||
unescape_to_string_32 (Result)
|
||||
end
|
||||
|
||||
representation: STRING
|
||||
@@ -165,6 +95,156 @@ feature -- Access
|
||||
Result.append_character ('%"')
|
||||
end
|
||||
|
||||
unescape_to_string_8 (a_output: STRING_8)
|
||||
-- Unescape string `item' into `a_output'.
|
||||
--| note: valid only if `item' does not encode any unicode character.
|
||||
local
|
||||
s: like item
|
||||
i, n: INTEGER
|
||||
c: CHARACTER
|
||||
do
|
||||
s := item
|
||||
n := s.count
|
||||
from i := 1 until i > n loop
|
||||
c := s[i]
|
||||
if c = '\' then
|
||||
if i < n then
|
||||
inspect s[i+1]
|
||||
when '\' then
|
||||
a_output.append_character ('\')
|
||||
i := i + 2
|
||||
when '%"' then
|
||||
a_output.append_character ('%"')
|
||||
i := i + 2
|
||||
when 'b' then
|
||||
a_output.append_character ('%B')
|
||||
i := i + 2
|
||||
when 'f' then
|
||||
a_output.append_character ('%F')
|
||||
i := i + 2
|
||||
when 'n' then
|
||||
a_output.append_character ('%N')
|
||||
i := i + 2
|
||||
when 'r' then
|
||||
a_output.append_character ('%R')
|
||||
i := i + 2
|
||||
when 't' then
|
||||
a_output.append_character ('%T')
|
||||
i := i + 2
|
||||
when 'u' then
|
||||
--| Leave Unicode \uXXXX unescaped
|
||||
a_output.append_character ('\')
|
||||
i := i + 1
|
||||
else
|
||||
a_output.append_character ('\')
|
||||
i := i + 1
|
||||
end
|
||||
else
|
||||
a_output.append_character ('\')
|
||||
i := i + 1
|
||||
end
|
||||
else
|
||||
a_output.append_character (c)
|
||||
i := i + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
unescape_to_string_32 (a_output: STRING_32)
|
||||
-- Unescape string `item' into `a_output' string 32.
|
||||
--| some encoders uses UTF-8 , and not the recommended pure json encoding
|
||||
--| thus, let's support the UTF-8 encoding during decoding.
|
||||
local
|
||||
s: READABLE_STRING_8
|
||||
i, n: INTEGER
|
||||
c: NATURAL_32
|
||||
ch: CHARACTER_8
|
||||
hex: READABLE_STRING_8
|
||||
do
|
||||
s := item
|
||||
n := s.count
|
||||
from i := 1 until i > n loop
|
||||
ch := s.item (i)
|
||||
if ch = '\' then
|
||||
if i < n then
|
||||
inspect s[i+1]
|
||||
when '\' then
|
||||
a_output.append_character ('\')
|
||||
i := i + 2
|
||||
when '%"' then
|
||||
a_output.append_character ('%"')
|
||||
i := i + 2
|
||||
when 'b' then
|
||||
a_output.append_character ('%B')
|
||||
i := i + 2
|
||||
when 'f' then
|
||||
a_output.append_character ('%F')
|
||||
i := i + 2
|
||||
when 'n' then
|
||||
a_output.append_character ('%N')
|
||||
i := i + 2
|
||||
when 'r' then
|
||||
a_output.append_character ('%R')
|
||||
i := i + 2
|
||||
when 't' then
|
||||
a_output.append_character ('%T')
|
||||
i := i + 2
|
||||
when 'u' then
|
||||
hex := s.substring (i + 2, i + 5) -- i+2 , i+2+4-1
|
||||
if hex.count = 4 then
|
||||
a_output.append_code (hexadecimal_to_natural_32 (hex))
|
||||
end
|
||||
i := i + 6 -- i +2 +4
|
||||
else
|
||||
a_output.append_character ('\')
|
||||
i := i + 1
|
||||
end
|
||||
else
|
||||
a_output.append_character ('\')
|
||||
i := i + 1
|
||||
end
|
||||
else
|
||||
c := ch.natural_32_code
|
||||
if c <= 0x7F then
|
||||
-- 0xxxxxxx
|
||||
check ch = c.to_character_32 end
|
||||
a_output.append_character (ch)
|
||||
elseif c <= 0xDF then
|
||||
-- 110xxxxx 10xxxxxx
|
||||
i := i + 1
|
||||
if i <= n then
|
||||
a_output.append_code (
|
||||
((c & 0x1F) |<< 6) |
|
||||
(s.code (i) & 0x3F)
|
||||
)
|
||||
end
|
||||
elseif c <= 0xEF then
|
||||
-- 1110xxxx 10xxxxxx 10xxxxxx
|
||||
i := i + 2
|
||||
if i <= n then
|
||||
a_output.append_code (
|
||||
((c & 0xF) |<< 12) |
|
||||
((s.code (i - 1) & 0x3F) |<< 6) |
|
||||
(s.code (i) & 0x3F)
|
||||
)
|
||||
end
|
||||
elseif c <= 0xF7 then
|
||||
-- 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
|
||||
i := i + 3
|
||||
if i <= n then
|
||||
a_output.append_code (
|
||||
((c & 0x7) |<< 18) |
|
||||
((s.code (i - 2) & 0x3F) |<< 12) |
|
||||
((s.code (i - 1) & 0x3F) |<< 6) |
|
||||
(s.code (i) & 0x3F)
|
||||
)
|
||||
end
|
||||
end
|
||||
i := i + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
feature -- Visitor pattern
|
||||
|
||||
accept (a_visitor: JSON_VISITOR)
|
||||
@@ -213,8 +293,18 @@ feature {NONE} -- Implementation
|
||||
|
||||
is_hexadecimal (s: READABLE_STRING_8): BOOLEAN
|
||||
-- Is `s' an hexadecimal value?
|
||||
local
|
||||
i: INTEGER
|
||||
do
|
||||
Result := across s as scur all scur.item.is_hexa_digit end
|
||||
from
|
||||
Result := True
|
||||
i := 1
|
||||
until
|
||||
i > s.count or not Result
|
||||
loop
|
||||
Result := s[i].is_hexa_digit
|
||||
i := i + 1
|
||||
end
|
||||
end
|
||||
|
||||
hexadecimal_to_natural_32 (s: READABLE_STRING_8): NATURAL_32
|
||||
@@ -264,8 +354,11 @@ feature {NONE} -- Implementation
|
||||
inspect c
|
||||
when '%"' then Result.append_string ("\%"")
|
||||
when '\' then Result.append_string ("\\")
|
||||
when '%R' then Result.append_string ("\r")
|
||||
when '%B' then Result.append_string ("\b")
|
||||
when '%F' then Result.append_string ("\f")
|
||||
when '%N' then Result.append_string ("\n")
|
||||
when '%R' then Result.append_string ("\r")
|
||||
when '%T' then Result.append_string ("\t")
|
||||
else
|
||||
Result.extend (c)
|
||||
end
|
||||
@@ -274,7 +367,7 @@ feature {NONE} -- Implementation
|
||||
end
|
||||
|
||||
escaped_json_string_32 (s: READABLE_STRING_32): STRING_8
|
||||
-- JSON string with '"' and '\' characters and unicode escaped
|
||||
-- JSON string with '"' and '\' characters and Unicode escaped
|
||||
require
|
||||
s_not_void: s /= Void
|
||||
local
|
||||
@@ -292,8 +385,11 @@ feature {NONE} -- Implementation
|
||||
inspect c
|
||||
when '%"' then Result.append_string ("\%"")
|
||||
when '\' then Result.append_string ("\\")
|
||||
when '%R' then Result.append_string ("\r")
|
||||
when '%B' then Result.append_string ("\b")
|
||||
when '%F' then Result.append_string ("\f")
|
||||
when '%N' then Result.append_string ("\n")
|
||||
when '%R' then Result.append_string ("\r")
|
||||
when '%T' then Result.append_string ("\t")
|
||||
else
|
||||
Result.extend (c)
|
||||
end
|
||||
|
||||
@@ -357,7 +357,7 @@ feature -- Commands
|
||||
end
|
||||
|
||||
read_unicode: STRING
|
||||
-- Read unicode and return value
|
||||
-- Read Unicode and return value
|
||||
local
|
||||
i: INTEGER
|
||||
do
|
||||
@@ -449,7 +449,7 @@ feature {NONE} -- Implementation
|
||||
end
|
||||
|
||||
is_valid_unicode (a_unicode: STRING): BOOLEAN
|
||||
-- is 'a_unicode' a valid unicode based on this regular expression
|
||||
-- is 'a_unicode' a valid Unicode based on this regular expression
|
||||
-- "\\u[0-9a-fA-F]{4}"
|
||||
local
|
||||
i: INTEGER
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{"menu": {
|
||||
{ "menu": {
|
||||
"id": "file",
|
||||
"value": "File",
|
||||
"popup": {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
note
|
||||
note
|
||||
description: "[
|
||||
Eiffel tests that can be executed by testing tool.
|
||||
]"
|
||||
@@ -62,6 +62,27 @@ feature -- Tests Pass
|
||||
end
|
||||
end
|
||||
|
||||
test_json_utf_8_pass1
|
||||
local
|
||||
parse_json: like new_json_parser
|
||||
utf: UTF_CONVERTER
|
||||
s: READABLE_STRING_32
|
||||
do
|
||||
s := {STRING_32} "{ %"nihaoma%": %"你好吗\t?%" }"
|
||||
|
||||
parse_json := new_json_parser (utf.string_32_to_utf_8_string_8 (s))
|
||||
json_value := parse_json.parse_json
|
||||
assert ("utf8.pass1.json", parse_json.is_parsed = True)
|
||||
if
|
||||
attached {JSON_OBJECT} json_value as jo and then
|
||||
attached {JSON_STRING} jo.item ("nihaoma") as js
|
||||
then
|
||||
assert ("utf8.nihaoma", js.unescaped_string_32.same_string ({STRING_32} "你好吗%T?"))
|
||||
else
|
||||
assert ("utf8.nihaoma", False)
|
||||
end
|
||||
end
|
||||
|
||||
feature -- Tests Failures
|
||||
test_json_fail1
|
||||
--
|
||||
@@ -475,8 +496,38 @@ feature -- JSON_FROM_FILE
|
||||
json_value: detachable JSON_VALUE
|
||||
|
||||
json_file_from (fn: STRING): detachable STRING
|
||||
local
|
||||
f: RAW_FILE
|
||||
l_path: STRING
|
||||
test_dir: STRING
|
||||
i: INTEGER
|
||||
do
|
||||
Result := file_reader.read_json_from (test_dir + fn)
|
||||
test_dir := (create {EXECUTION_ENVIRONMENT}).current_working_directory
|
||||
test_dir.append_character ((create {OPERATING_ENVIRONMENT}).directory_separator)
|
||||
|
||||
l_path := test_dir + fn
|
||||
create f.make_with_name (l_path)
|
||||
if f.exists then
|
||||
-- Found json file
|
||||
else
|
||||
-- before EiffelStudio 7.3 , the current dir of autotest execution was not the parent dir of ecf but something like
|
||||
-- ..json\test\autotest\test_suite\EIFGENs\test_suite\Testing\execution\TEST_JSON_SUITE.test_json_fail1\..\..\..\..\..\fail1.json
|
||||
from
|
||||
i := 5
|
||||
until
|
||||
i = 0
|
||||
loop
|
||||
test_dir.append_character ('.')
|
||||
test_dir.append_character ('.')
|
||||
test_dir.append_character ((create {OPERATING_ENVIRONMENT}).directory_separator)
|
||||
i := i - 1
|
||||
end
|
||||
l_path := test_dir + fn
|
||||
end
|
||||
create f.make_with_name (l_path)
|
||||
if f.exists then
|
||||
Result := file_reader.read_json_from (l_path)
|
||||
end
|
||||
assert ("File contains json data", Result /= Void)
|
||||
end
|
||||
|
||||
@@ -485,30 +536,9 @@ feature -- JSON_FROM_FILE
|
||||
create Result.make_parser (a_string)
|
||||
end
|
||||
|
||||
test_dir: STRING
|
||||
local
|
||||
i: INTEGER
|
||||
do
|
||||
Result := (create {EXECUTION_ENVIRONMENT}).current_working_directory
|
||||
Result.append_character ((create {OPERATING_ENVIRONMENT}).directory_separator)
|
||||
-- The should looks like
|
||||
-- ..json\test\autotest\test_suite\EIFGENs\test_suite\Testing\execution\TEST_JSON_SUITE.test_json_fail1\..\..\..\..\..\fail1.json
|
||||
from
|
||||
i := 5
|
||||
until
|
||||
i = 0
|
||||
loop
|
||||
Result.append_character ('.')
|
||||
Result.append_character ('.')
|
||||
Result.append_character ((create {OPERATING_ENVIRONMENT}).directory_separator)
|
||||
i := i - 1
|
||||
end
|
||||
-- Result := "/home/jvelilla/work/project/Eiffel/ejson_dev/trunk/test/autotest/test_suite/"
|
||||
end
|
||||
|
||||
invariant
|
||||
file_reader /= Void
|
||||
|
||||
end
|
||||
|
||||
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
This project is a community project
|
||||
|
||||
## Mailing list ##
|
||||
- Google group: http://groups.google.com/group/eiffel-web-framework
|
||||
- Google group: [http://groups.google.com/group/eiffel-web-framework](http://groups.google.com/group/eiffel-web-framework)
|
||||
|
||||
## Materials ##
|
||||
- wiki: github wiki at https://github.com/Eiffel-World/Eiffel-Web-Framework/wiki
|
||||
- Shared documents: on google docs at http://goo.gl/M8WLP
|
||||
- source code: git repository at https://github.com/Eiffel-World/Eiffel-Web-Framework
|
||||
- Proposal from Paul Cohen for a EWSGI spec at http://eiffel.seibostudios.se/wiki/EWSGI
|
||||
- wiki: github wiki at [https://github.com/Eiffel-World/Eiffel-Web-Framework/wiki](https://github.com/Eiffel-World/Eiffel-Web-Framework/wiki)
|
||||
- Shared documents: on google docs at [http://goo.gl/M8WLP](http://goo.gl/M8WLP)
|
||||
- source code: git repository at [https://github.com/Eiffel-World/Eiffel-Web-Framework](https://github.com/Eiffel-World/Eiffel-Web-Framework)
|
||||
- Proposal from Paul Cohen for a EWSGI spec at [http://eiffel.seibostudios.se/wiki/EWSGI](http://eiffel.seibostudios.se/wiki/EWSGI)
|
||||
|
||||
## Main contributors ##
|
||||
- **jfiat**: Jocelyn Fiat (Eiffel Software)
|
||||
|
||||
48
doc/wiki/Connectors.md
Normal file
48
doc/wiki/Connectors.md
Normal file
@@ -0,0 +1,48 @@
|
||||
The main goal of the connectors is to let you choose a target at compile time.
|
||||
This allows you to concentrate on your business during development time and then decide which target you choose at deployment time.
|
||||
The current connectors are:
|
||||
* Nino
|
||||
* FastCGI
|
||||
* CGI
|
||||
* OpenShift
|
||||
|
||||
The most widely used workflow is to use Nino on your development machine and FastCGI on your production server.
|
||||
Nino being a web server written entirely in Eiffel, you can inspect your HTTP requests and respones in EiffelStudio which is great during development.
|
||||
On the other hand, FastCGI is great at handling concurrent requests and coupled with Apache (or another web production server), you don't even need to worry about the lifecyle of your application (creation and destruction) as Apache will do it for you!
|
||||
|
||||
Let's now dig into each of the connecters.
|
||||
|
||||
# Nino
|
||||
|
||||
Nino is a web server entirely written in Eiffel.
|
||||
The goal of Nino is to provide a simple web server for development (like Java, Python and Ruby provide).
|
||||
Nino is currently maintained by Javier Velilla and the repository can be found here: https://github.com/jvelilla/EiffelWebNino
|
||||
|
||||
# FastCGI
|
||||
|
||||
FastCGI is a protocol for interfacing an application server with a web server.
|
||||
It is an improvement over CGI as FastCGI supports long running processes, i.e. processes than can handle multipe requests during their lifecyle. CGI, on the other hand, launches a new process for every new request which is quite time consuming.
|
||||
FastCGI is implemented by every major web servers: Apache, IIS, Nginx, ...
|
||||
We recommend to use FastCGI instead of CGI as it is way more faster.
|
||||
You can read more about FastCGI here: http://www.fastcgi.com/
|
||||
|
||||
# CGI
|
||||
|
||||
CGI predates FastCGI and is also a protocol for interfacing an application server with a web server.
|
||||
His main drawback (and the reason why FastCGI was created) is that it launches a new process for every new request, which is quite time consuming.
|
||||
We recommend to use FastCGI instead of CGI as it is way more faster.
|
||||
|
||||
# OpenShift
|
||||
|
||||
OpenShift is a cloud computing platform as a service product from Red Hat.
|
||||
It basically let's you run your application in the cloud.
|
||||
More informations are available here: https://www.openshift.com
|
||||
|
||||
# Writing your own
|
||||
|
||||
It's fairly easy to write your own connector. Just inherit from these classes:
|
||||
* WGI_CONNECTOR
|
||||
* WGI_ERROR_STREAM
|
||||
* WGI_INPUT_STREAM
|
||||
* WGI_OUTPUT_STREAM
|
||||
* WSF_SERVICE_LAUNCHER
|
||||
1
doc/wiki/Documentation-_Router.md
Normal file
1
doc/wiki/Documentation-_Router.md
Normal file
@@ -0,0 +1 @@
|
||||
See WSF_ROUTER
|
||||
209
doc/wiki/Documentation.md
Normal file
209
doc/wiki/Documentation.md
Normal file
@@ -0,0 +1,209 @@
|
||||
# Current Status
|
||||
* Official repository: <https://github.com/EiffelWebFramework/EWF>
|
||||
* Official website: <http://eiffelwebframework.github.io/EWF/getting-started/>
|
||||
|
||||
# What is EWF?
|
||||
|
||||
Eiffel Web Framework, is mainly a collection of Eiffel libraries designed to be integrated with each other. One benefit is that it supports all core HTTP features, so enable you embrace HTTP as an application protocol to develop web applications. So you do not need to adapt your applications to the web, instead you use the web power. It means you can build different kind of web applications, from Web APIs following the Hypermedia API style (REST style), CRUD web services or just conventional web applications building a session on top of an stateless protocol.
|
||||
|
||||
# EWF core/kernel
|
||||
> The Web Server Foundation (WSF\_) is the core of the framework. It is compliant with the EWSGI interface (WGI\_).
|
||||
|
||||
To build a web [service](#service), the framework provides a set of core components to launch the service, for each [request](#request-and-response), access the data, and send the [response](#request-and-response).
|
||||
The framework also provides a router component to help dispatching the incoming request.
|
||||
|
||||
A service can be a web api, a web interface, … what ever run on top of HTTP.
|
||||
|
||||
<a name="wiki-service"></a>
|
||||
<a name="service"></a>
|
||||
# Service
|
||||
> see interface: **WSF_SERVICE**
|
||||
|
||||
Each incoming http request is processed by the following routine.
|
||||
|
||||
> `{WSF_SERVICE}.execute (req: WSF_REQUEST; res: WSF_RESPONSE)`
|
||||
|
||||
This is the low level of the framework, at this point, `req` provides access to the query and form parameters, input data, headers, ... as specified by the Common Gateway Interface (CGI).
|
||||
The response `res` is the interface to send data back to the client.
|
||||
For convenience, the framework provides richer service interface that handles the most common needs (filter, router, ...).
|
||||
|
||||
> [Learn more about service](Documentation__Service)
|
||||
|
||||
<a name="wiki-request"></a><a name="wiki-response"></a><a name="wiki-request-and-response"></a>
|
||||
<a name="request"></a><a name="response"></a><a name="request-and-response"></a>
|
||||
# Request and Response
|
||||
> see interface: **WSF_REQUEST** and **WSF_RESPONSE**
|
||||
|
||||
Any incoming http request is represented by an new object of type **WSF_REQUEST**.
|
||||
|
||||
**WSF_REQUEST** provides access to
|
||||
+ __meta variables__: CGI variables (coming from the request http header)
|
||||
+ __query parameters__: from the uri ex: `?q=abc&type=pdf`
|
||||
+ __input data__: the message of the request, if this is a web form, this is parsed to build the form parameters. It can be retrieved once.
|
||||
+ __form parameters__: standard parameters from the request input data.
|
||||
- typically available when a web form is sent using POST as content of type `multipart/form-data` or `application/x-www-form-urlencoded`
|
||||
- (advanced usage: it is possible to write mime handler that can processed other type of content, even custom format.)
|
||||
+ __uploaded files__: if files are uploaded, their value will be available from the form parameters, and from the uploaded files as well.
|
||||
+ __cookies variable__: cookies extracted from the http header.
|
||||
+ __path parameters__: note this is related to the router and carry the semantic of the mapping (see the section on router )
|
||||
+ __execution variables__: used by the application to keep value associated with the request.
|
||||
|
||||
The **WSF_RESPONSE** represents the communication toward the client, a service need to provide correct headers, and content. For instance the `Content-Type`, and `Content-Length`. It also allows to send data with chunked encoding.
|
||||
|
||||
> [Learn more about request](Documentation__Request) and [about response](Documentation__Response)
|
||||
|
||||
<a name="wiki-connector"></a>
|
||||
<a name="connector"></a>
|
||||
# Connectors:
|
||||
> see **WGI_CONNECTOR**
|
||||
|
||||
Using EWF, your service is built on top of underlying httpd solution/connectors.
|
||||
Currently 3 main connectors are available:
|
||||
* __CGI__: following the CGI interface, this is an easy solution to run the service on any platform.
|
||||
* __libFCGI__: based on the libfcgi solution, this can be used with Apache, IIS, nginx, ...
|
||||
* __nino__: a standalone server: Eiffel Web Nino allow you to embed a web server anywhere, on any platform without any dependencies on other httpd server.
|
||||
|
||||
At compilation time, you can use a default connector (by using the associated default lib), but you can also use a mixed of them and choose which one to execute at runtime.
|
||||
It is fairly easy to add new connector, it just has to follow the EWSGI interface
|
||||
|
||||
> [Learn more about connector](Documentation__Connector)
|
||||
|
||||
<a name="wiki-router"></a>
|
||||
<a name="router"></a>
|
||||
# Router or Request Dispatcher:
|
||||
> Routes HTTP requests to the proper execution code
|
||||
|
||||
A web application needs to have a clean and elegant URL scheme, and EWF provides a router component to design URLs.
|
||||
|
||||
The association between a URL pattern and the code handling the URL request is called a Router mapping in EWF.
|
||||
|
||||
EWF provides 3 main kinds of mappings
|
||||
+ __URI__: any URL with path being the specified uri.
|
||||
- example: “/users/” redirects any “/users/” and “/users/?query=...”
|
||||
+ __URI-template__: any URL matching the specified URI-template
|
||||
- example: “/project/{name}/” redirects any “/project/foo” or “/project/bar”
|
||||
+ __Starts-with__: any URL starting with the specified path
|
||||
|
||||
Note: in the future, a Regular-Expression based kind will be added in the future, and it is possible to use custom mapping on top of EWF.
|
||||
|
||||
Code:
|
||||
|
||||
router.map ( create {WSF_URI_TEMPLATE_MAPPING}.make (
|
||||
“/project/{name}”, project_handler)
|
||||
)
|
||||
-- And precising the request methods
|
||||
router.map_with_request_methods ( ... , router.methods_GET_POST)
|
||||
|
||||
In the previous code, the `project_handler` is an object conforming to **WSF_HANDLER**, that will process the incoming requests matching URI-template “/project/{name}”.
|
||||
|
||||
Usually, the service will inherit from WSF_ROUTED_SERVICE, which has a `router` attribute.
|
||||
Configuring the URL scheme is done by implementing `{WSF_ROUTED_SERVICE}.setup_router`.
|
||||
|
||||
To make life easier, by inheriting from WSF_URI_TEMPLATE_HELPER_FOR_ROUTED_SERVICE, a few help methods are available to `map` URI template with agent, and so on.
|
||||
See
|
||||
+ `map_uri_template (a_tpl: STRING; h: WSF_URI_TEMPLATE_HANDLER)`
|
||||
+ `map_uri_template_agent (a_tpl: READABLE_STRING_8; proc: PROCEDURE [ANY, TUPLE [req: WSF_REQUEST; res: WSF_RESPONSE]])`
|
||||
+ and same with request methods ...
|
||||
|
||||
...
|
||||
|
||||
Check WSF_\*_HELPER_FOR_ROUTED_SERVICE for other available helper classes.
|
||||
|
||||
How we do that in EWF? : Router with (or without context).
|
||||
Related code: wsf_router, wsf_router_context
|
||||
Examples
|
||||
|
||||
> [Learn more about router](Documentation__Router)
|
||||
|
||||
# EWF components
|
||||
## URI Handler:
|
||||
> Parses the details of the URI (scheme, path, query info, etc.) and exposes them for use.
|
||||
|
||||
How we do that in EWF?: URI Templates, but we could also use regex.
|
||||
Related code: uri_template
|
||||
Examples:
|
||||
|
||||
|
||||
## Mime Parser/ Content Negotiation:
|
||||
> Handles the details of determining the media type, language, encoding, compression (conneg).
|
||||
|
||||
How do we do that in EWF? Content_Negotiation library.
|
||||
Example
|
||||
|
||||
|
||||
## Request Handler
|
||||
> target of request dispatcher + uri handler.
|
||||
|
||||
Here is where we handle GET, POST PUT, etc.
|
||||
|
||||
## Representation Mapping
|
||||
> Converts stored data into the proper representation for responses and handles incoming representations from requests.
|
||||
|
||||
We don’t have a representation library, the developer need to do that.
|
||||
If we want to provide different kind of representations: JSON, XML, HTML, the responsibility is let
|
||||
to the developer to map their domain to the target representation.
|
||||
|
||||
## Http Client:
|
||||
> A simple library to make requests and handle responses from other http servers.
|
||||
|
||||
How we do that in EWF? http client library
|
||||
examples:
|
||||
|
||||
## Authentication/Security:
|
||||
> Handle different auth models. (Basic, Digest?, OAuth, OpenId)
|
||||
|
||||
How we do that in EWF? http_authorization, OpenId, and Cypress
|
||||
examples.
|
||||
|
||||
## Caching:
|
||||
> Support for Caching and conditional request
|
||||
|
||||
How we do that in Eiffel? Policy framework on top of EWF. {{{need_review}}}
|
||||
examples
|
||||
|
||||
|
||||
## EWF HTML5 Widgets
|
||||
|
||||
## EWF policy Framework
|
||||
|
||||
## EWF application generators
|
||||
|
||||
|
||||
<a name="wiki-EWSGI"></a>
|
||||
<a name="EWSGI"></a>
|
||||
# EWSGI Specification
|
||||
|
||||
<a name="wiki-libraries"></a>
|
||||
<a name="libraries"></a>
|
||||
# Libraries
|
||||
|
||||
External libraries are included, such as Cypress OAuth (Security), HTML parsing library, Template Engine Smarty.
|
||||
|
||||
## server
|
||||
* __ewsgi__: Eiffel Web Server Gateway Interface read more
|
||||
* connectors: various web server connectors for EWSGI
|
||||
* __libfcgi__: Wrapper for libfcgi SDK
|
||||
* __wsf__: Web Server Framework [read more]
|
||||
* __router__: URL dispatching/routing based on uri, uri_template, or custom read more
|
||||
* __wsf_html__: (html and css) Content generator from the server side.
|
||||
* CMS example: <https://github.com/EiffelWebFramework/cms/tree/master/example>
|
||||
|
||||
## protocol
|
||||
* __http__: HTTP related classes, constants for status code, content types, ... read more
|
||||
* __uri_template__: URI Template library (parsing and expander) read more
|
||||
* __content_negotiation__: CONNEG library (Content-type Negociation) read more
|
||||
|
||||
## Client
|
||||
* __http_client__: simple HTTP client based on cURL readmore
|
||||
* __Firebase API__: <https://github.com/EiffelWebFramework/Redwood>
|
||||
|
||||
## Text
|
||||
* __encoder__: Various simple encoders: base64, url-encoder, xml entities, html entities read more
|
||||
|
||||
## Utils
|
||||
* __error__: very simple/basic library to handle error
|
||||
|
||||
## Security
|
||||
* __http_authentication__ (under EWF/library/server/authentication)
|
||||
* __open_id__ (under EWF/library/security)
|
||||
* __OAuth__ see <https://github.com/EiffelWebFramework/cypress>
|
||||
1
doc/wiki/Documentation__Connector.md
Normal file
1
doc/wiki/Documentation__Connector.md
Normal file
@@ -0,0 +1 @@
|
||||
See WSF_CONNECTOR
|
||||
17
doc/wiki/Documentation__Request.md
Normal file
17
doc/wiki/Documentation__Request.md
Normal file
@@ -0,0 +1,17 @@
|
||||
See WSF_REQUEST
|
||||
|
||||
## About parameters
|
||||
Note that by default there is a smart computation for the query/post/... parameters:
|
||||
for instance
|
||||
- `q=a&q=b` : will create a **WSF_MULTIPLE_STRING** parameter with name **q** and value `[a,b]`
|
||||
- `tab[a]=ewf&tab[b]=demo` : will create a **WSF_TABLE** parameter with name **tab** and value `{ "a": "ewf", "b": "demo"}`
|
||||
- `tab[]=ewf&tab[]=demo` : will create a **WSF_TABLE** parameter with name **tab** and value `{ "1": "ewf", "2": "demo"}`
|
||||
- `tab[foo]=foo&tab[foo]=bar` : will create a **WSF_TABLE** parameter with name **tab** and value `{ "foo": "bar"}` **WARNING: only the last `tab[foo]` is kept**.
|
||||
|
||||
Those rules are applied to query, post, path, .... parameters.
|
||||
|
||||
## How to get the input data (i.e entity-body) ?
|
||||
See `{WSF_REQUEST}.read_input_data_into (buf: STRING)`
|
||||
|
||||
## How to get the raw header data (i.e the http header text) ?
|
||||
See `{WSF_REQUEST}.raw_header_data: detachable READABLE_STRING_32`
|
||||
1
doc/wiki/Documentation__Response.md
Normal file
1
doc/wiki/Documentation__Response.md
Normal file
@@ -0,0 +1 @@
|
||||
See WSF_RESPONSE
|
||||
1
doc/wiki/Documentation__Router.md
Normal file
1
doc/wiki/Documentation__Router.md
Normal file
@@ -0,0 +1 @@
|
||||
See WSF_ROUTER
|
||||
2
doc/wiki/Documentation__Service.md
Normal file
2
doc/wiki/Documentation__Service.md
Normal file
@@ -0,0 +1,2 @@
|
||||
EWF Services
|
||||
> See WSF\_SERVICE
|
||||
@@ -4,7 +4,7 @@
|
||||
## Preface
|
||||
This specification is a proposition based on recent discussion on the mailing list.
|
||||
This is work in progress, so far nothing had been decided.
|
||||
You can find another proposal at http://eiffel.seibostudios.se/wiki/EWSGI , it has common background and goal, however still differ on specific parts.
|
||||
You can find another proposal at [http://eiffel.seibostudios.se/wiki/EWSGI](http://eiffel.seibostudios.se/wiki/EWSGI) , it has common background and goal, however still differ on specific parts.
|
||||
The main goal for now is to unified those 2 specifications.
|
||||
|
||||
---
|
||||
@@ -12,7 +12,7 @@ Note the following is work in progress, and reflect a specification proposal, ra
|
||||
2011-08-01
|
||||
---
|
||||
For now, the specification from EWF is done in Eiffel interface
|
||||
please see: https://github.com/Eiffel-World/Eiffel-Web-Framework/tree/master/library/server/ewsgi/specification
|
||||
please see: [https://github.com/Eiffel-World/Eiffel-Web-Framework/tree/master/library/server/ewsgi/specification](https://github.com/Eiffel-World/Eiffel-Web-Framework/tree/master/library/server/ewsgi/specification)
|
||||
|
||||
WGI_APPLICATION
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
- See proposed specifications: [[EWSGI specification| EWSGI-specification]]
|
||||
- See [[Open questions| EWSGI-open-questions]]
|
||||
- See proposed specifications: [EWSGI specification](./EWSGI-specification)
|
||||
- See [Open questions](./EWSGI-Open-Questions)
|
||||
- And below the various proposals and associated decision
|
||||
|
||||
----
|
||||
|
||||
27
doc/wiki/Filter.md
Normal file
27
doc/wiki/Filter.md
Normal file
@@ -0,0 +1,27 @@
|
||||
# Introduction
|
||||
|
||||
The basic idea of a filter is to pre-process incoming data and post-process outgoing data.
|
||||
Filters are part of a filter chain, thus following the [chain of responsability design pattern](http://en.wikipedia.org/wiki/Chain-of-responsibility_pattern).
|
||||
|
||||
Each filter decides to call the next filter or not.
|
||||
|
||||
# Levels
|
||||
|
||||
In EWF, there are two levels of filters.
|
||||
|
||||
## WSF_FILTER
|
||||
|
||||
Typical examples of such filters are: logging, compression, routing (WSF_ROUTING_FILTER), ...
|
||||
|
||||
## WSF_FILTER_HANDLER
|
||||
|
||||
Handler that can also play the role of a filter.
|
||||
|
||||
Typical examples of such filters are: authentication, ...
|
||||
|
||||
# References
|
||||
|
||||
Filters (also called middelwares) in other environments:
|
||||
* in Python: http://www.wsgi.org/en/latest/libraries.html
|
||||
* in Node.js: http://expressjs.com/guide.html#middleware
|
||||
* in Apache: http://httpd.apache.org/docs/2.2/en/filter.html
|
||||
@@ -1,26 +1,26 @@
|
||||
# Eiffel-Web-Framework #
|
||||
|
||||
## Location ##
|
||||
The official documentation/wiki is located at https://github.com/EiffelWebFramework/EWF/wiki , if you are visiting a "clone/fork", please always check the [[official wiki|https://github.com/EiffelWebFramework/EWF/wiki]].
|
||||
The official documentation/wiki is located at [https://github.com/EiffelWebFramework/EWF/wiki](https://github.com/EiffelWebFramework/EWF/wiki) , if you are visiting a "clone/fork", please always check the [official wiki](https://github.com/EiffelWebFramework/EWF/wiki).
|
||||
|
||||
## Organization ##
|
||||
- Mailing list: please visit and subscribe to the mailing list page [[http://groups.google.com/group/eiffel-web-framework]] 
|
||||
- Mailing list: please visit and subscribe to the mailing list page [http://groups.google.com/group/eiffel-web-framework](http://groups.google.com/group/eiffel-web-framework) 
|
||||
- Most of the topics are discussed on the mailing list (google group).
|
||||
- For time to time we have [[web meetings|meetings]], and less frequently [[physical meetings|meetings]] that occurs usually during other Eiffel related events.
|
||||
- For time to time we have [web meetings](./wiki/Meetings), and less frequently [physical meetings](./wiki/Meetings) that occurs usually during other Eiffel related events.
|
||||
|
||||
## Documentation ##
|
||||
- to redo
|
||||
- [Documentation](./Documentation)
|
||||
|
||||
## Contributions ##
|
||||
- You want to contribute or follow the progress/discussion, see the [[collaboration page| Community-collaboration]]
|
||||
- Potential tasks/projects on EWF: [[Projects page| Projects]]
|
||||
- You want to contribute or follow the progress/discussion, see the [collaboration page](./wiki/Community-collaboration)
|
||||
- Potential tasks/projects on EWF: [Projects page](./wiki/Projects)
|
||||
|
||||
## See also ##
|
||||
- [[list of tasks, and a potential roadmap| Tasks-Roadmap]]
|
||||
- [[General source structure of this project| Source-structure]]
|
||||
- EWSGI: [[Eiffel Web Server Gateway Interface| EWSGI]]
|
||||
- [[Overview of the server side architecture| Spec-Server-Architecture]]
|
||||
- This project is also a collection of [[Libraries]] related to the Web
|
||||
- [list of tasks, and a potential roadmap](./wiki/Tasks-Roadmap)
|
||||
- [General source structure of this project](./wiki/Source-structure)
|
||||
- EWSGI: [Eiffel Web Server Gateway Interface](./wiki/EWSGI)
|
||||
- [Overview of the server side architecture](./wiki/Spec-Server-Architecture)
|
||||
- This project is also a collection of [Libraries](./wiki/Libraries) related to the Web
|
||||
|
||||
## Note ##
|
||||
- This wiki needs to be updated, in the meantime, please have a look at the presentation: https://docs.google.com/presentation/pub?id=1GPFv6aHhTjFSLMnlAt-J4WeIHSGfHdB42dQxmOVOH8s&start=false&loop=false&delayms=3000
|
||||
- This wiki needs to be updated, in the meantime, please have a look at the presentation: [https://docs.google.com/presentation/pub?id=1GPFv6aHhTjFSLMnlAt-J4WeIHSGfHdB42dQxmOVOH8s&start=false&loop=false&delayms=3000](https://docs.google.com/presentation/pub?id=1GPFv6aHhTjFSLMnlAt-J4WeIHSGfHdB42dQxmOVOH8s&start=false&loop=false&delayms=3000)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Previous and future meetings
|
||||
|
||||
* [[Web-meeting: 2012-09-18|Web-meeting-2012-09-18]]
|
||||
* [Web-meeting: 2012-09-18](./Web-meeting-2012-09-18)
|
||||
* For previous meetings, check the ["meeting" topics](https://groups.google.com/forum/?fromgroups=#!tags/eiffel-web-framework/meeting) on the [forum](http://groups.google.com/group/eiffel-web-framework)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
Use this to suggest new projects, or request features.
|
||||
The content of this page will be moved to the main [[Projects]] page for time to time.
|
||||
The content of this page will be moved to the main [Projects](./Projects) page for time to time.
|
||||
For any entry, please use this template
|
||||
|
||||
----
|
||||
@@ -15,6 +15,6 @@ For any entry, please use this template
|
||||
## Add support for Swagger
|
||||
* _Suggested by **Olivier**_
|
||||
* _Description_: Build a Swagger Eiffel implementation
|
||||
* _References_: http://swagger.wordnik.com/
|
||||
* _References_: [http://swagger.wordnik.com/](http://swagger.wordnik.com/)
|
||||
|
||||
----
|
||||
|
||||
@@ -6,13 +6,13 @@ If you are a student, don't hesitate to pick one, or even suggest a new project,
|
||||
|
||||
## Evaluate EWF according to the following constraints ...
|
||||
* _Suggested by **Javier**_
|
||||
* _Description_: According to http://www.amundsen.com/blog/archives/1130 , evaluate the current design of EWF to see if this match the different points. An other option would be to take the following REST implementation toolkit as a guide to evaluate EWF http://code.google.com/p/implementing-rest/wiki/RESTImplementationToolkit.
|
||||
* _Description_: According to [http://www.amundsen.com/blog/archives/1130](http://www.amundsen.com/blog/archives/1130) , evaluate the current design of EWF to see if this match the different points. An other option would be to take the following REST implementation toolkit as a guide to evaluate EWF [http://code.google.com/p/implementing-rest/wiki/RESTImplementationToolkit](http://code.google.com/p/implementing-rest/wiki/RESTImplementationToolkit).
|
||||
|
||||
## Road to Hypermedia API
|
||||
* _Suggested by **Javier**_
|
||||
* _Supervisor_:
|
||||
* _Suitability_:
|
||||
* _Description_: describe differents types of Web API, and how you can build them using EWF. Describing Pros and Cons. This should be on http://martinfowler.com/articles/richardsonMaturityModel.html
|
||||
* _Description_: describe differents types of Web API, and how you can build them using EWF. Describing Pros and Cons. This should be on [http://martinfowler.com/articles/richardsonMaturityModel.html](http://martinfowler.com/articles/richardsonMaturityModel.html)
|
||||
|
||||
## Build a video to demonstrate how an Hypermedia API works, and how to build it using EWF
|
||||
* _Suggested by **Javier**_
|
||||
@@ -55,8 +55,8 @@ If you are a student, don't hesitate to pick one, or even suggest a new project,
|
||||
* _Supervisor_:
|
||||
* _Suitability_: TODO
|
||||
* _Description_: EWF is relying on the notion of "connector" to achieve portability on various platform and underlying httpd server, currently EWF support any CGI or libFCGI system (i.e apache, IIS, ...), and provide a standalone version thanks to Eiffel Web Nino. The goal now, would be to support specific connector for:
|
||||
** LightHTTP (http://www.lighttpd.net/)
|
||||
** nginx (http://nginx.org/en/)
|
||||
** LightHTTP ([http://www.lighttpd.net/](http://www.lighttpd.net/))
|
||||
** nginx ([http://nginx.org/en/](http://nginx.org/en/))
|
||||
|
||||
## Concurrenty and EWF
|
||||
* _Suggested by **Jocelyn**_
|
||||
@@ -75,7 +75,7 @@ If you are a student, don't hesitate to pick one, or even suggest a new project,
|
||||
* _Supervisor_:
|
||||
* _Suitability_: TODO
|
||||
* _Description_: Provide an implementation of websocket with EWF and eventually Eiffel Web Nino, then demonstrate it on a simple example. WebSocket is a web technology providing for bi-directional, full-duplex communications channels over a single TCP connection.
|
||||
* See http://en.wikipedia.org/wiki/Websocket
|
||||
* See [http://en.wikipedia.org/wiki/Websocket](http://en.wikipedia.org/wiki/Websocket)
|
||||
|
||||
----
|
||||
# Usage of EWF
|
||||
@@ -84,13 +84,13 @@ If you are a student, don't hesitate to pick one, or even suggest a new project,
|
||||
* _Suggested by **Javier**_
|
||||
* _Supervisor_:
|
||||
* _Suitability_: TODO
|
||||
* _Description_: Build a HAL browser to discover an API using HAL mediatype. The browser will be able to follow the links, and display the transmitted data. This could be a vision2 application inspired by http://haltalk.herokuapp.com/explorer/hal_browser.html#/. HAL stands for Hypertext Application Language see http://stateless.co/hal_specification.html.
|
||||
* _Description_: Build a HAL browser to discover an API using HAL mediatype. The browser will be able to follow the links, and display the transmitted data. This could be a vision2 application inspired by [http://haltalk.herokuapp.com/explorer/hal_browser.html#/](http://haltalk.herokuapp.com/explorer/hal_browser.html#/). HAL stands for Hypertext Application Language see [http://stateless.co/hal_specification.html](http://stateless.co/hal_specification.html).
|
||||
|
||||
## Collection-JSON browser
|
||||
* _Suggested by **Javier**_
|
||||
* _Supervisor_:
|
||||
* _Suitability_: TODO
|
||||
* _Description_: Build a Collection/JSON browser to discover an API using Collection/JSON mediatype. The browser will be able to follow the links, and display the transmitted data. This could be a vision2 application inspired by http://haltalk.herokuapp.com/explorer/hal_browser.html#/. Collection+JSON is a JSON-based read/write hypermedia-type, see http://www.amundsen.com/media-types/collection/
|
||||
* _Description_: Build a Collection/JSON browser to discover an API using Collection/JSON mediatype. The browser will be able to follow the links, and display the transmitted data. This could be a vision2 application inspired by [http://haltalk.herokuapp.com/explorer/hal_browser.html#/](http://haltalk.herokuapp.com/explorer/hal_browser.html#/). Collection+JSON is a JSON-based read/write hypermedia-type, see [http://www.amundsen.com/media-types/collection/](http://www.amundsen.com/media-types/collection/)
|
||||
|
||||
## Build a simple CMS with EWF
|
||||
* _Suggested by **Jocelyn**_
|
||||
@@ -119,7 +119,7 @@ If you are a student, don't hesitate to pick one, or even suggest a new project,
|
||||
* _Suggested by **Javier**_
|
||||
* _Supervisor_:
|
||||
* _Suitability_: TODO
|
||||
* _Description_: Use XHTML as a media type to for hypermedia API. See http://codeartisan.blogspot.com.ar/2012/07/using-html-as-media-type-for-your-api.html
|
||||
* _Description_: Use XHTML as a media type to for hypermedia API. See [http://codeartisan.blogspot.com.ar/2012/07/using-html-as-media-type-for-your-api.html](http://codeartisan.blogspot.com.ar/2012/07/using-html-as-media-type-for-your-api.html)
|
||||
|
||||
## Add support for Mediatype such as RSS, ATOM, ...
|
||||
* _Suggested by **Jocelyn**_
|
||||
@@ -155,24 +155,24 @@ If you are a student, don't hesitate to pick one, or even suggest a new project,
|
||||
* _Suitability_: TODO
|
||||
* _Description_: Design and build a Single Sign On implementation for Eiffel. That should include the authentication server, and at least one Eiffel client component (it would be convenient to also provide php, js, ...). In the same spirit, having Eiffel client for popular SSO server would be appreciated as well.
|
||||
* _Reference_:
|
||||
- http://en.wikipedia.org/wiki/Single_sign-on
|
||||
- http://en.wikipedia.org/wiki/List_of_single_sign-on_implementations
|
||||
- [http://en.wikipedia.org/wiki/Single_sign-on](http://en.wikipedia.org/wiki/Single_sign-on)
|
||||
- [http://en.wikipedia.org/wiki/List_of_single_sign-on_implementations](http://en.wikipedia.org/wiki/List_of_single_sign-on_implementations)
|
||||
|
||||
## library: Template engine
|
||||
* _Suggested by **Jocelyn**_
|
||||
* _Supervisor_:
|
||||
* _Suitability_: TODO
|
||||
* _Description_: Get inspired by any existing template engine, and build one for Eiffel, this should be easily usable within a web application. This could be inspired, or implementation of standard template engine, this way people can reuse existing content, or migrate easily their application to EWF. For inspiration, one can look at:
|
||||
- http://www.smarty.net/
|
||||
- http://mustache.github.com/
|
||||
- http://en.wikipedia.org/wiki/Template_engine_(web) ... they are plenty of them, a comparison of the different engine would help.
|
||||
- [http://www.smarty.net/](http://www.smarty.net/)
|
||||
- [http://mustache.github.com/](http://mustache.github.com/)
|
||||
- [http://en.wikipedia.org/wiki/Web_template_system](http://en.wikipedia.org/wiki/Web_template_system) ... they are plenty of them, a comparison of the different engine would help.
|
||||
* This is not specific to EWF, but it will be very useful in website context.
|
||||
|
||||
## library: Wikitext, markdown parser and render engine
|
||||
* _Suggested by **Jocelyn**_
|
||||
* _Supervisor_:
|
||||
* _Suitability_: TODO
|
||||
* _Description_: Build component to support (read and write, and why not convert), lightweight markup language (see http://en.wikipedia.org/wiki/Lightweight_markup_language) such as wikitext, markdown, and other. The component should be able to read/scan, but also produce an HTML output. Focus first on wikitext, and markdown since they seems to be the most popular.
|
||||
* _Description_: Build component to support (read and write, and why not convert), lightweight markup language (see [http://en.wikipedia.org/wiki/Lightweight_markup_language](http://en.wikipedia.org/wiki/Lightweight_markup_language)) such as wikitext, markdown, and other. The component should be able to read/scan, but also produce an HTML output. Focus first on wikitext, and markdown since they seems to be the most popular.
|
||||
* Then , a nice addition would be to render those lightweight markup lang into Vision2 widget (not related to EWF, but could be useful to build (editor) desktop application)
|
||||
|
||||
## library: Web component to build HTML5 widget
|
||||
@@ -195,16 +195,16 @@ If you are a student, don't hesitate to pick one, or even suggest a new project,
|
||||
* _Suitability_: TODO
|
||||
* _Description_: TODO
|
||||
* Generic client that can be customized (see design in slide 12)
|
||||
* http://s3.amazonaws.com/cimlabs/Oredev-Hypermedia-APIs.pdf
|
||||
* video http://vimeo.com/20781278
|
||||
* [http://s3.amazonaws.com/cimlabs/Oredev-Hypermedia-APIs.pdf](http://s3.amazonaws.com/cimlabs/Oredev-Hypermedia-APIs.pdf)
|
||||
* video [http://vimeo.com/20781278](http://vimeo.com/20781278)
|
||||
|
||||
## Create a Client Cache based on Apache commons Client Cache.
|
||||
* _Suggested by **Javier**_
|
||||
* _Supervisor_:
|
||||
* _Suitability_: TODO
|
||||
* _Description_: TODO
|
||||
* http://hc.apache.org/httpcomponents-client-ga/httpclient-cache/index.html
|
||||
* http://labs.xfinity.com/benchmarking-the-httpclient-caching-module
|
||||
* [http://hc.apache.org/httpcomponents-client-ga/httpclient-cache/index.html](http://hc.apache.org/httpcomponents-client-ga/httpclient-cache/index.html)
|
||||
* [http://labs.xfinity.com/benchmarking-the-httpclient-caching-module](http://labs.xfinity.com/benchmarking-the-httpclient-caching-module)
|
||||
|
||||
## Add SSL support to Eiffel Net
|
||||
* _Suggested by **Jocelyn**_
|
||||
@@ -231,9 +231,9 @@ If you are a student, don't hesitate to pick one, or even suggest a new project,
|
||||
* _Supervisor_:
|
||||
* _Suitability_: TODO
|
||||
* _Description_: TODO
|
||||
* See: http://en.wikipedia.org/wiki/Edge_Side_Includes
|
||||
* See: [http://en.wikipedia.org/wiki/Edge_Side_Includes](http://en.wikipedia.org/wiki/Edge_Side_Includes)
|
||||
|
||||
----
|
||||
# Feel free to add new idea below this line
|
||||
----
|
||||
Use the following page [[Projects new suggestions]] to suggest new project, or request a feature.
|
||||
Use the following page [Projects new suggestions](./Projects new suggestions) to suggest new project, or request a feature.
|
||||
9
doc/wiki/Request-and-response.md
Normal file
9
doc/wiki/Request-and-response.md
Normal file
@@ -0,0 +1,9 @@
|
||||
# Request
|
||||
The class _WSF_REQUEST_ can be used to access data related to the HTTP request.
|
||||
|
||||
**TODO**: describe the request interface
|
||||
|
||||
# Response
|
||||
The class _WSF_RESPONSE_ is the media to send data back to the client.
|
||||
|
||||
**TODO**: describe the response interface
|
||||
3
doc/wiki/Router.md
Normal file
3
doc/wiki/Router.md
Normal file
@@ -0,0 +1,3 @@
|
||||
The primary goal of the router (class _WSF_ROUTER_) is to dispatch requests according to the request URI.
|
||||
|
||||
**TODO**: describe the router interface
|
||||
@@ -1,3 +1,4 @@
|
||||
Check new roadmap wiki page: [roadmap](./roadmap)
|
||||
## Future
|
||||
* Focus on REST API
|
||||
- Hypermedia API
|
||||
@@ -31,5 +32,5 @@
|
||||
* Installation scripts
|
||||
|
||||
## Contributors ##
|
||||
- See [[the collaboration page|Community-collaboration]]
|
||||
- See [the collaboration page](./Community-collaboration)
|
||||
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
## Eiffel
|
||||
|
||||
* http://www.scoop.it/t/eiffel-resources
|
||||
* http://www.scoop.it/t/eiffel
|
||||
* [http://www.scoop.it/t/eiffel-resources](http://www.scoop.it/t/eiffel-resources)
|
||||
* [http://www.scoop.it/t/eiffel](http://www.scoop.it/t/eiffel)
|
||||
|
||||
## Hypermedia
|
||||
|
||||
* http://www.scoop.it/t/hyper-media-apis
|
||||
* http://www.scoop.it/t/hypermedia-api
|
||||
* [http://www.scoop.it/t/hyper-media-apis](http://www.scoop.it/t/hyper-media-apis)
|
||||
* [http://www.scoop.it/t/hypermedia-api](http://www.scoop.it/t/hypermedia-api)
|
||||
|
||||
## ETags
|
||||
|
||||
* http://www.mnot.net/blog/2007/08/07/etags
|
||||
* http://bitworking.org/news/150/REST-Tip-Deep-etags-give-you-more-benefits
|
||||
* [http://www.mnot.net/blog/2007/08/07/etags](http://www.mnot.net/blog/2007/08/07/etags)
|
||||
* [http://bitworking.org/news/150/REST-Tip-Deep-etags-give-you-more-benefits](http://bitworking.org/news/150/REST-Tip-Deep-etags-give-you-more-benefits)
|
||||
@@ -1,7 +1,5 @@
|
||||
# Using the policy driven framework
|
||||
|
||||
**This describes a new facility that is not yet in the EWF release**
|
||||
|
||||
## Introduction
|
||||
|
||||
The aim of the policy-driven framework is to allow authors of web-servers to concentrate on the business logic (e.g., in the case of a GET request, generating the content), without having to worry about the details of the HTTP protocol (such as headers and response codes). However, there are so many possibilities in the HTTP protocol, that it is impossible to correctly guess what to do in all cases. Therefore the author has to supply policy decisions to the framework, in areas such as caching decisions. These are implemented as a set of deferred classes for which the author needs to provide effective implementations.
|
||||
|
||||
@@ -8,14 +8,14 @@
|
||||
## Information
|
||||
|
||||
### When ?
|
||||
* Tuesday 18th of september, 19:00 - 20:00 UTC/GMT time (see 3rd time in http://www.doodle.com/8v2sekiyebp4dpyh)
|
||||
* Tuesday 18th of september, 19:00 - 20:00 UTC/GMT time (see 3rd time in [http://www.doodle.com/8v2sekiyebp4dpyh](http://www.doodle.com/8v2sekiyebp4dpyh))
|
||||
|
||||
### Where ?
|
||||
Web meeting using webex
|
||||
|
||||
* Short url: http://goo.gl/wBz11
|
||||
* Long url: https://eiffel.webex.com/eiffel/j.php?ED=211265702&UID=0&PW=NZWNiMjBiZWIz&RT=MiMyMA%3D%3D
|
||||
* Related Google group topic: https://groups.google.com/d/topic/eiffel-web-framework/A7ADPAT3nj8/discussion
|
||||
* Short url: [http://goo.gl/wBz11](http://goo.gl/wBz11)
|
||||
* Long url: [https://eiffel.webex.com/eiffel/j.php?ED=211265702&UID=0&PW=NZWNiMjBiZWIz&RT=MiMyMA%3D%3D](https://eiffel.webex.com/eiffel/j.php?ED=211265702&UID=0&PW=NZWNiMjBiZWIz&RT=MiMyMA%3D%3D)
|
||||
* Related Google group topic: [https://groups.google.com/d/topic/eiffel-web-framework/A7ADPAT3nj8/discussion](https://groups.google.com/d/topic/eiffel-web-framework/A7ADPAT3nj8/discussion)
|
||||
|
||||
## Agenda
|
||||
|
||||
|
||||
4
doc/wiki/roadmap.md
Normal file
4
doc/wiki/roadmap.md
Normal file
@@ -0,0 +1,4 @@
|
||||
# Upcoming versions
|
||||
|
||||
# Current state: oct-2013
|
||||
- check previous wiki page: [Tasks roadmap](./Tasks roadmap)
|
||||
@@ -1,3 +1,4 @@
|
||||
The gewf tool, is an experimentation to generate EWF project from template.
|
||||
|
||||
status: experimental, POC, in-progress, draft
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
</file_rule>
|
||||
<option warning="true" full_class_checking="true" is_attached_by_default="true" void_safety="transitional" syntax="standard">
|
||||
</option>
|
||||
<setting name="console_application" value="true"/>
|
||||
<setting name="executable_name" value="gewf"/>
|
||||
<setting name="concurrency" value="none"/>
|
||||
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
|
||||
|
||||
@@ -18,6 +18,8 @@ feature {NONE} -- Initialization
|
||||
args: ARGUMENTS_32
|
||||
cfg: detachable READABLE_STRING_32
|
||||
do
|
||||
create setup.make
|
||||
|
||||
create args
|
||||
if args.argument_count > 0 then
|
||||
cfg := args.argument (1)
|
||||
@@ -28,13 +30,11 @@ feature {NONE} -- Initialization
|
||||
execute
|
||||
end
|
||||
|
||||
feature -- Status
|
||||
setup: GEWF_SETUP
|
||||
|
||||
feature -- Access
|
||||
|
||||
config (k: READABLE_STRING_GENERAL): detachable READABLE_STRING_32
|
||||
local
|
||||
l_keys: LIST [READABLE_STRING_GENERAL]
|
||||
do
|
||||
if attached {JSON_STRING} json_item (json, k) as js then
|
||||
Result := js.unescaped_string_32
|
||||
@@ -95,6 +95,9 @@ feature -- Access
|
||||
|
||||
create p.make_parser (s)
|
||||
json := p.parse
|
||||
if attached config ("gewf.template_dir") as d then
|
||||
setup.set_template_dir_from_string (d)
|
||||
end
|
||||
end
|
||||
|
||||
json: detachable JSON_VALUE
|
||||
@@ -141,8 +144,7 @@ feature -- Execution
|
||||
p: PATH
|
||||
appname: detachable READABLE_STRING_GENERAL
|
||||
do
|
||||
create p.make_from_string ("template")
|
||||
p := p.extended (tpl)
|
||||
p := setup.template_dir.extended (tpl)
|
||||
appname := vals.item ("APPNAME")
|
||||
if appname = Void then
|
||||
appname := "_generated"
|
||||
|
||||
109
draft/src/gewf/src/gewf_setup.e
Normal file
109
draft/src/gewf/src/gewf_setup.e
Normal file
@@ -0,0 +1,109 @@
|
||||
note
|
||||
description: "[
|
||||
Configuration of GEWF tool.
|
||||
]"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
GEWF_SETUP
|
||||
|
||||
inherit
|
||||
SHARED_EXECUTION_ENVIRONMENT
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature -- Initialization
|
||||
|
||||
make
|
||||
|
||||
do
|
||||
--| root_dir
|
||||
get_root_dir
|
||||
|
||||
--| template_dir
|
||||
get_template_dir
|
||||
|
||||
end
|
||||
|
||||
get_root_dir
|
||||
local
|
||||
ut: FILE_UTILITIES
|
||||
p: detachable PATH
|
||||
do
|
||||
--| either $GEWF, or $HOME/.gewf or cwd/.gewf or cwd
|
||||
if attached execution_environment.item ("GEWF") as s then
|
||||
create p.make_from_string (s)
|
||||
elseif attached execution_environment.item ("HOME") as s then
|
||||
create p.make_from_string (s)
|
||||
p := p.extended (".gewf")
|
||||
create ut
|
||||
if not ut.directory_path_exists (p) then
|
||||
p := Void
|
||||
end
|
||||
end
|
||||
if p = Void then
|
||||
p := execution_environment.current_working_path
|
||||
if ut.directory_path_exists (p.extended (".gewf")) then
|
||||
p := p.extended (".gewf")
|
||||
end
|
||||
end
|
||||
root_dir := p
|
||||
end
|
||||
|
||||
get_template_dir
|
||||
do
|
||||
if attached execution_environment.item ("GEWF_TEMPLATE_DIR") as tpl_dir then
|
||||
create template_dir.make_from_string (tpl_dir)
|
||||
else
|
||||
template_dir := root_dir.extended ("template")
|
||||
end
|
||||
end
|
||||
|
||||
feature -- Access
|
||||
|
||||
root_dir: PATH
|
||||
|
||||
template_dir: PATH
|
||||
|
||||
feature -- Status report
|
||||
|
||||
is_custom_template_dir: BOOLEAN
|
||||
|
||||
is_custom_root_dir: BOOLEAN
|
||||
|
||||
feature -- Change
|
||||
|
||||
set_root_dir (p: PATH)
|
||||
do
|
||||
is_custom_root_dir := True
|
||||
root_dir := p
|
||||
if not is_custom_template_dir then
|
||||
-- update template_dir
|
||||
get_template_dir
|
||||
end
|
||||
end
|
||||
|
||||
set_template_dir_from_string (dn: READABLE_STRING_GENERAL)
|
||||
do
|
||||
set_template_dir (create {PATH} .make_from_string (dn))
|
||||
end
|
||||
|
||||
set_template_dir (p: PATH)
|
||||
do
|
||||
is_custom_template_dir := True
|
||||
template_dir := p
|
||||
end
|
||||
|
||||
;note
|
||||
copyright: "2011-2013, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
source: "[
|
||||
Eiffel Software
|
||||
5949 Hollister Ave., Goleta, CA 93117 USA
|
||||
Telephone 805-685-1006, Fax 805-685-6869
|
||||
Website http://www.eiffel.com
|
||||
Customer support http://support.eiffel.com
|
||||
]"
|
||||
end
|
||||
@@ -1,85 +1,18 @@
|
||||
note
|
||||
description: "Summary description for {APPLICATION_LAUNCHER}."
|
||||
author: ""
|
||||
description: "[
|
||||
Effective class for APPLICATION_LAUNCHER_I
|
||||
|
||||
You can put modification in this class
|
||||
]"
|
||||
date: "$Date: 2013-06-12 13:55:42 +0200 (mer., 12 juin 2013) $"
|
||||
revision: "$Revision: 36 $"
|
||||
|
||||
deferred class
|
||||
class
|
||||
APPLICATION_LAUNCHER
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
inherit
|
||||
APPLICATION_LAUNCHER_I
|
||||
|
||||
launcher_nature: detachable READABLE_STRING_8
|
||||
-- Initialize the launcher nature
|
||||
-- either cgi, libfcgi, or nino.
|
||||
--| We could extend with more connector if needed.
|
||||
--| 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
|
||||
end
|
||||
if ext /= Void then
|
||||
if ext.same_string (nature_nino) then
|
||||
Result := nature_nino
|
||||
end
|
||||
if ext.same_string (nature_cgi) then
|
||||
Result := nature_cgi
|
||||
end
|
||||
if ext.same_string (nature_libfcgi) or else ext.same_string ("fcgi") then
|
||||
Result := nature_libfcgi
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
feature {NONE} -- nino
|
||||
|
||||
nature_nino: STRING = "nino"
|
||||
|
||||
launch_nino (a_service: WSF_SERVICE; opts: detachable WSF_SERVICE_LAUNCHER_OPTIONS)
|
||||
do
|
||||
create {WSF_NINO_SERVICE_LAUNCHER} launcher.make_and_launch (a_service, opts)
|
||||
end
|
||||
|
||||
feature {NONE} -- cgi
|
||||
|
||||
nature_cgi: STRING = "cgi"
|
||||
|
||||
launch_cgi (a_service: WSF_SERVICE; opts: detachable WSF_SERVICE_LAUNCHER_OPTIONS)
|
||||
do
|
||||
create {WSF_CGI_SERVICE_LAUNCHER} launcher.make_and_launch (a_service, opts)
|
||||
end
|
||||
|
||||
feature {NONE} -- libfcgi
|
||||
|
||||
nature_libfcgi: STRING = "libfcgi"
|
||||
|
||||
launch_libfcgi (a_service: WSF_SERVICE; opts: detachable WSF_SERVICE_LAUNCHER_OPTIONS)
|
||||
do
|
||||
create {WSF_LIBFCGI_SERVICE_LAUNCHER} launcher.make_and_launch (a_service, opts)
|
||||
end
|
||||
|
||||
feature {NONE} -- Launcher
|
||||
|
||||
launch (a_service: WSF_SERVICE; 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)
|
||||
elseif nature = nature_cgi then
|
||||
launch_cgi (a_service, opts)
|
||||
elseif nature = nature_libfcgi then
|
||||
launch_libfcgi (a_service, opts)
|
||||
else
|
||||
-- bye bye
|
||||
(create {EXCEPTIONS}).die (-1)
|
||||
end
|
||||
end
|
||||
feature -- Custom
|
||||
|
||||
end
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
note
|
||||
description: "[
|
||||
Specific application launcher
|
||||
|
||||
DO NOT EDIT THIS CLASS
|
||||
|
||||
you can customize APPLICATION_LAUNCHER
|
||||
]"
|
||||
date: "$Date: 2013-06-12 13:55:42 +0200 (mer., 12 juin 2013) $"
|
||||
revision: "$Revision: 36 $"
|
||||
|
||||
deferred class
|
||||
APPLICATION_LAUNCHER_I
|
||||
|
||||
inherit
|
||||
SHARED_EXECUTION_ENVIRONMENT
|
||||
|
||||
feature -- Execution
|
||||
|
||||
launch (a_service: WSF_SERVICE; 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)
|
||||
elseif nature = nature_cgi then
|
||||
launch_cgi (a_service, opts)
|
||||
elseif nature = nature_libfcgi then
|
||||
launch_libfcgi (a_service, opts)
|
||||
else
|
||||
-- bye bye
|
||||
(create {EXCEPTIONS}).die (-1)
|
||||
end
|
||||
end
|
||||
|
||||
feature {NONE} -- Access
|
||||
|
||||
launcher_nature: detachable READABLE_STRING_8
|
||||
-- Initialize the launcher nature
|
||||
-- either cgi, libfcgi, or nino.
|
||||
--| We could extend with more connector if needed.
|
||||
--| 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
|
||||
end
|
||||
if ext /= Void then
|
||||
if ext.same_string (nature_nino) then
|
||||
Result := nature_nino
|
||||
end
|
||||
if ext.same_string (nature_cgi) then
|
||||
Result := nature_cgi
|
||||
end
|
||||
if ext.same_string (nature_libfcgi) or else ext.same_string ("fcgi") then
|
||||
Result := nature_libfcgi
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
feature {NONE} -- nino
|
||||
|
||||
nature_nino: STRING = "nino"
|
||||
|
||||
launch_nino (a_service: WSF_SERVICE; opts: detachable WSF_SERVICE_LAUNCHER_OPTIONS)
|
||||
local
|
||||
launcher: WSF_NINO_SERVICE_LAUNCHER
|
||||
do
|
||||
create launcher.make_and_launch (a_service, opts)
|
||||
end
|
||||
|
||||
feature {NONE} -- cgi
|
||||
|
||||
nature_cgi: STRING = "cgi"
|
||||
|
||||
launch_cgi (a_service: WSF_SERVICE; opts: detachable WSF_SERVICE_LAUNCHER_OPTIONS)
|
||||
local
|
||||
launcher: WSF_CGI_SERVICE_LAUNCHER
|
||||
do
|
||||
create launcher.make_and_launch (a_service, opts)
|
||||
end
|
||||
|
||||
feature {NONE} -- libfcgi
|
||||
|
||||
nature_libfcgi: STRING = "libfcgi"
|
||||
|
||||
launch_libfcgi (a_service: WSF_SERVICE; opts: detachable WSF_SERVICE_LAUNCHER_OPTIONS)
|
||||
local
|
||||
launcher: WSF_LIBFCGI_SERVICE_LAUNCHER
|
||||
do
|
||||
create launcher.make_and_launch (a_service, opts)
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
@@ -1,19 +1,18 @@
|
||||
note
|
||||
description: "Summary description for {APPLICATION}."
|
||||
author: ""
|
||||
description: "[
|
||||
Effective class for APPLICATION_LAUNCHER_I
|
||||
|
||||
You can put modification in this class
|
||||
]"
|
||||
date: "$Date: 2013-06-12 13:55:42 +0200 (mer., 12 juin 2013) $"
|
||||
revision: "$Revision: 36 $"
|
||||
|
||||
deferred class
|
||||
class
|
||||
APPLICATION_LAUNCHER
|
||||
|
||||
feature {NONE} -- Launcher
|
||||
inherit
|
||||
APPLICATION_LAUNCHER_I
|
||||
|
||||
launch (a_service: WSF_SERVICE; opts: detachable WSF_SERVICE_LAUNCHER_OPTIONS)
|
||||
local
|
||||
launcher: WSF_SERVICE_LAUNCHER
|
||||
do
|
||||
create {WSF_DEFAULT_SERVICE_LAUNCHER} launcher.make_and_launch (a_service, opts)
|
||||
end
|
||||
feature -- Custom
|
||||
|
||||
end
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
note
|
||||
description: "[
|
||||
Specific application launcher
|
||||
|
||||
DO NOT EDIT THIS CLASS
|
||||
|
||||
you can customize APPLICATION_LAUNCHER
|
||||
]"
|
||||
date: "$Date: 2013-06-12 13:55:42 +0200 (mer., 12 juin 2013) $"
|
||||
revision: "$Revision: 36 $"
|
||||
|
||||
deferred class
|
||||
APPLICATION_LAUNCHER_I
|
||||
|
||||
feature -- Execution
|
||||
|
||||
launch (a_service: WSF_SERVICE; opts: detachable WSF_SERVICE_LAUNCHER_OPTIONS)
|
||||
local
|
||||
launcher: WSF_SERVICE_LAUNCHER
|
||||
do
|
||||
create {WSF_DEFAULT_SERVICE_LAUNCHER} launcher.make_and_launch (a_service, opts)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
2
examples/desktop_app/README.md
Normal file
2
examples/desktop_app/README.md
Normal file
@@ -0,0 +1,2 @@
|
||||
This example demonstrates the use of embedded Vision2 web browser component, and embedded EWF server (using nino).
|
||||
|
||||
28
examples/desktop_app/desktop_app.ecf
Normal file
28
examples/desktop_app/desktop_app.ecf
Normal file
@@ -0,0 +1,28 @@
|
||||
<?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="desktop_app" uuid="E015841A-D456-46E1-8A18-E0CEB9E69CD5">
|
||||
<description>Vision2+web browser widget+embedded web service</description>
|
||||
<target name="desktop_app">
|
||||
<description>This example demonstrates how to build a vision2 desktop application that embed a web browser accessing the service of an embedded web service.</description>
|
||||
<root class="DESKTOP_APP" feature="make_and_launch"/>
|
||||
<option warning="true" full_class_checking="true" is_attached_by_default="true" void_safety="transitional" syntax="transitional">
|
||||
<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">
|
||||
<file_rule>
|
||||
<exclude>/EIFGENs$</exclude>
|
||||
<exclude>/CVS$</exclude>
|
||||
<exclude>/.svn$</exclude>
|
||||
</file_rule>
|
||||
</cluster>
|
||||
</target>
|
||||
</system>
|
||||
1
examples/desktop_app/files/index.html
Normal file
1
examples/desktop_app/files/index.html
Normal file
@@ -0,0 +1 @@
|
||||
Test
|
||||
32
examples/desktop_app/home.html
Normal file
32
examples/desktop_app/home.html
Normal file
@@ -0,0 +1,32 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<script type="text/javascript">
|
||||
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","http://localhost:52367/test/ajax.txt",true);
|
||||
xmlhttp.send();
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<h1>This is a local file test with js</h1><li><a href="http://localhost:52367/">back to home</a></li><div id="myDiv"><h2>Let AJAX change this text</h2>
|
||||
<button type="button" onclick="loadXMLDoc()">Change Content</button>
|
||||
</div>
|
||||
</body></html>
|
||||
230
examples/desktop_app/src/app_embedded_web_service.e
Normal file
230
examples/desktop_app/src/app_embedded_web_service.e
Normal file
@@ -0,0 +1,230 @@
|
||||
note
|
||||
description: "Summary description for {APP_EMBEDDED_WEB_SERVICE}."
|
||||
author: ""
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
APP_EMBEDDED_WEB_SERVICE
|
||||
|
||||
inherit
|
||||
EMBEDDED_WEB_SERVICE
|
||||
redefine
|
||||
make
|
||||
end
|
||||
|
||||
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
|
||||
71
examples/desktop_app/src/desktop_app.e
Normal file
71
examples/desktop_app/src/desktop_app.e
Normal file
@@ -0,0 +1,71 @@
|
||||
note
|
||||
description: "Objects that represent the Vision2 application.%
|
||||
%The original version of this class has been generated by EiffelBuild."
|
||||
generator: "EiffelBuild"
|
||||
legal: "See notice at end of class."
|
||||
status: "See notice at end of class."
|
||||
date: "$Date: 2012-09-29 01:29:13 +0200 (sam., 29 sept. 2012) $"
|
||||
revision: "$Revision: 89488 $"
|
||||
|
||||
|
||||
class
|
||||
DESKTOP_APP
|
||||
|
||||
inherit
|
||||
EV_APPLICATION
|
||||
|
||||
create
|
||||
make_and_launch
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
make_and_launch
|
||||
-- Create `Current', build and display `main_window',
|
||||
-- then launch the application.
|
||||
local
|
||||
l_win: like main_window
|
||||
l_embeded_services: 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
|
||||
|
||||
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
|
||||
launch
|
||||
end
|
||||
|
||||
on_quit
|
||||
do
|
||||
if attached main_window as win then
|
||||
win.destroy_and_exit_if_last
|
||||
end
|
||||
end
|
||||
|
||||
on_web_service_launched (a_win: attached like main_window)
|
||||
do
|
||||
add_idle_action_kamikaze (agent a_win.open_link)
|
||||
end
|
||||
|
||||
feature {NONE} -- Implementation
|
||||
|
||||
main_window: detachable MAIN_WINDOW
|
||||
-- Main window of `Current'
|
||||
|
||||
;note
|
||||
copyright: "Copyright (c) 1984-2009, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
source: "[
|
||||
Eiffel Software
|
||||
356 Storke Road, Goleta, CA 93117 USA
|
||||
Telephone 805-685-1006, Fax 805-685-6869
|
||||
Website http://www.eiffel.com
|
||||
Customer support http://support.eiffel.com
|
||||
]"
|
||||
|
||||
|
||||
end
|
||||
202
examples/desktop_app/src/main_window.e
Normal file
202
examples/desktop_app/src/main_window.e
Normal file
@@ -0,0 +1,202 @@
|
||||
note
|
||||
description: "Objects that represent an EV_TITLED_WINDOW.%
|
||||
%The original version of this class was generated by EiffelBuild."
|
||||
generator: "EiffelBuild"
|
||||
legal: "See notice at end of class."
|
||||
status: "See notice at end of class."
|
||||
date: "$Date: 2010-08-17 10:49:12 +0200 (mar., 17 août 2010) $"
|
||||
revision: "$Revision: 84189 $"
|
||||
|
||||
class
|
||||
MAIN_WINDOW
|
||||
|
||||
inherit
|
||||
EV_TITLED_WINDOW
|
||||
redefine
|
||||
create_interface_objects, initialize, is_in_default_state
|
||||
end
|
||||
|
||||
SHARED_EMBEDED_WEB_SERVICE_INFORMATION
|
||||
undefine
|
||||
default_create, copy
|
||||
end
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
make
|
||||
-- Creation method
|
||||
do
|
||||
default_create
|
||||
end
|
||||
|
||||
initialize
|
||||
-- Initialize `Current'.
|
||||
do
|
||||
Precursor {EV_TITLED_WINDOW}
|
||||
|
||||
set_title ("Desktop Application (demo embedded EWF+browser)")
|
||||
|
||||
-- Connect events.
|
||||
-- Close the application when an interface close
|
||||
-- request is received on `Current'. i.e. the cross is clicked.
|
||||
close_request_actions.extend (agent destroy_and_exit_if_last)
|
||||
|
||||
-- Call `user_initialization'.
|
||||
user_initialization
|
||||
end
|
||||
|
||||
create_interface_objects
|
||||
-- Create objects
|
||||
do
|
||||
create home_button.make_with_text ("Home")
|
||||
create back_button.make_with_text ("Back")
|
||||
create forth_button.make_with_text ("Forth")
|
||||
create refresh_button.make_with_text ("Refresh")
|
||||
create stop_button.make_with_text ("Stop")
|
||||
create url_text_field.make_with_text ("http://localhost:" + port_number.out)
|
||||
create go_button.make_with_text ("Go")
|
||||
|
||||
create web_browser
|
||||
end
|
||||
|
||||
user_initialization
|
||||
-- Called by `initialize'.
|
||||
-- Any custom user initialization that
|
||||
-- could not be performed in `initialize',
|
||||
-- (due to regeneration of implementation class)
|
||||
-- can be added here.
|
||||
local
|
||||
l_browser_box: EV_VERTICAL_BOX
|
||||
l_server_box: EV_VERTICAL_BOX
|
||||
l_hor_box: EV_HORIZONTAL_BOX
|
||||
vb: EV_VERTICAL_BOX
|
||||
do
|
||||
set_size (800, 600)
|
||||
|
||||
create vb
|
||||
extend (vb)
|
||||
vb.set_border_width (3)
|
||||
vb.set_padding_width (3)
|
||||
|
||||
-- browser part
|
||||
create l_browser_box
|
||||
|
||||
create l_hor_box
|
||||
l_browser_box.extend (l_hor_box)
|
||||
l_browser_box.disable_item_expand (l_hor_box)
|
||||
|
||||
home_button.select_actions.force_extend (agent on_home_button_action)
|
||||
l_hor_box.extend (home_button)
|
||||
l_hor_box.disable_item_expand (home_button)
|
||||
|
||||
back_button.select_actions.force_extend (agent on_back_button_action)
|
||||
l_hor_box.extend (back_button)
|
||||
l_hor_box.disable_item_expand (back_button)
|
||||
|
||||
forth_button.select_actions.force_extend (agent on_forth_button_action)
|
||||
l_hor_box.extend (forth_button)
|
||||
l_hor_box.disable_item_expand (forth_button)
|
||||
|
||||
refresh_button.select_actions.force_extend (agent on_refresh_button_action)
|
||||
l_hor_box.extend (refresh_button)
|
||||
l_hor_box.disable_item_expand (refresh_button)
|
||||
|
||||
stop_button.select_actions.force_extend (agent on_stop_button_action)
|
||||
l_hor_box.extend (stop_button)
|
||||
l_hor_box.disable_item_expand (stop_button)
|
||||
|
||||
l_hor_box.extend (url_text_field)
|
||||
|
||||
go_button.select_actions.force_extend (agent on_go_button_action)
|
||||
l_hor_box.extend (go_button)
|
||||
l_hor_box.disable_item_expand (go_button)
|
||||
|
||||
l_browser_box.extend (web_browser)
|
||||
|
||||
--------------------
|
||||
vb.extend (l_browser_box)
|
||||
end
|
||||
|
||||
is_in_default_state: BOOLEAN
|
||||
do
|
||||
Result := True
|
||||
end
|
||||
|
||||
feature -- Basic operation
|
||||
|
||||
open_link
|
||||
do
|
||||
url_text_field.set_text ("http://localhost:" + port_number.out)
|
||||
on_go_button_action
|
||||
end
|
||||
|
||||
feature {NONE} -- Implementation
|
||||
|
||||
home_button, go_button, back_button, forth_button, stop_button, refresh_button: EV_BUTTON
|
||||
-- Buttons
|
||||
|
||||
url_text_field: EV_TEXT_FIELD
|
||||
-- URL text field
|
||||
|
||||
on_go_button_action
|
||||
-- Action for `go_button'
|
||||
local
|
||||
l_uri: STRING_32
|
||||
do
|
||||
l_uri := url_text_field.text
|
||||
if l_uri /= Void and then not l_uri.is_empty then
|
||||
web_browser.load_uri (l_uri)
|
||||
else
|
||||
on_home_button_action
|
||||
end
|
||||
end
|
||||
|
||||
on_home_button_action
|
||||
-- Action for `home_button'
|
||||
do
|
||||
web_browser.load_uri ("http://localhost:" + port_number.out)
|
||||
end
|
||||
|
||||
on_back_button_action
|
||||
-- Action for `back_button'
|
||||
do
|
||||
web_browser.back
|
||||
end
|
||||
|
||||
on_forth_button_action
|
||||
-- Action for `forth_button'
|
||||
do
|
||||
web_browser.forth
|
||||
end
|
||||
|
||||
on_refresh_button_action
|
||||
-- Action for `refresh_button'
|
||||
do
|
||||
web_browser.refresh
|
||||
end
|
||||
|
||||
on_stop_button_action
|
||||
-- Action for `stop_button'
|
||||
do
|
||||
web_browser.stop
|
||||
end
|
||||
|
||||
web_browser: EV_WEB_BROWSER
|
||||
-- Web browser widget
|
||||
|
||||
;note
|
||||
copyright: "Copyright (c) 1984-2009, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
source: "[
|
||||
Eiffel Software
|
||||
356 Storke Road, Goleta, CA 93117 USA
|
||||
Telephone 805-685-1006, Fax 805-685-6869
|
||||
Website http://www.eiffel.com
|
||||
Customer support http://support.eiffel.com
|
||||
]"
|
||||
|
||||
|
||||
end
|
||||
114
examples/desktop_app/src/service/embedded_web_service.e
Normal file
114
examples/desktop_app/src/service/embedded_web_service.e
Normal file
@@ -0,0 +1,114 @@
|
||||
note
|
||||
description: "Summary description for {EMBEDDED_WEB_SERVICE}."
|
||||
author: ""
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
deferred class
|
||||
EMBEDDED_WEB_SERVICE
|
||||
|
||||
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
|
||||
|
||||
feature -- Initialization
|
||||
|
||||
make
|
||||
do
|
||||
make_thread
|
||||
create on_launched_actions
|
||||
end
|
||||
|
||||
feature {NONE} -- 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
|
||||
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
|
||||
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
|
||||
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
|
||||
|
||||
on_launched (conn: WGI_CONNECTOR)
|
||||
do
|
||||
if attached {WGI_NINO_CONNECTOR} conn as nino then
|
||||
set_port_number (nino.port)
|
||||
end
|
||||
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
|
||||
@@ -0,0 +1,27 @@
|
||||
note
|
||||
description: "Summary description for {SHARED_EMBEDED_WEB_SERVICE_INFORMATION}."
|
||||
author: ""
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
SHARED_EMBEDED_WEB_SERVICE_INFORMATION
|
||||
|
||||
feature -- Access
|
||||
|
||||
port_number: INTEGER
|
||||
do
|
||||
Result := port_number_cell.item
|
||||
end
|
||||
|
||||
set_port_number (a_port: like port_number)
|
||||
do
|
||||
port_number_cell.replace (a_port)
|
||||
end
|
||||
|
||||
port_number_cell: CELL [INTEGER]
|
||||
once ("process")
|
||||
create Result.put (0)
|
||||
end
|
||||
|
||||
end
|
||||
@@ -21,10 +21,14 @@ feature -- Basic operations
|
||||
execute (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
-- Execute the filter
|
||||
local
|
||||
l_auth: HTTP_AUTHORIZATION
|
||||
l_auth: detachable HTTP_AUTHORIZATION
|
||||
do
|
||||
create l_auth.make (req.http_authorization)
|
||||
if (attached l_auth.type as l_auth_type and then l_auth_type.is_equal ("basic")) and
|
||||
if attached req.http_authorization as l_http_authorization then
|
||||
create l_auth.make (l_http_authorization)
|
||||
end
|
||||
if
|
||||
l_auth /= Void and then
|
||||
(attached l_auth.type as l_auth_type and then l_auth_type.same_string ("basic")) and then
|
||||
attached l_auth.login as l_auth_login and then
|
||||
attached Db_access.user (0, l_auth_login) as l_user and then
|
||||
l_auth_login.same_string (l_user.name) and then
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?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="client" uuid="D0059CEB-5F5C-4D21-8C71-842BD0F88468" library_target="client">
|
||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-12-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-12-0 http://www.eiffel.com/developers/xml/configuration-1-12-0.xsd" name="client" uuid="D0059CEB-5F5C-4D21-8C71-842BD0F88468" library_target="client">
|
||||
<target name="client">
|
||||
<root class="RESTBUCK_CLIENT" feature="make"/>
|
||||
<file_rule>
|
||||
@@ -11,7 +11,7 @@
|
||||
</option>
|
||||
<setting name="concurrency" value="thread"/>
|
||||
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
|
||||
<library name="http_client" location="../../../library/network/http_client/http_client-safe.ecf" readonly="false"/>
|
||||
<library name="http_client" location="..\..\..\library\network\http_client\http_client-safe.ecf" readonly="false"/>
|
||||
<library name="json" location="..\..\..\contrib\library\text\parser\json\library\json-safe.ecf" readonly="false"/>
|
||||
<library name="thread" location="$ISE_LIBRARY\library\thread\thread-safe.ecf"/>
|
||||
<cluster name="src" location=".\src\" recursive="true"/>
|
||||
|
||||
@@ -155,6 +155,9 @@ Response success
|
||||
} ]
|
||||
}
|
||||
|
||||
note:
|
||||
curl -vv http://localhost:9090/order -H "Content-Type: application/json" -d "{\"location\":\"takeAway\",\"items\":[{\"name\":\"Late\",\"option\":\"skim\",\"size\":\"Small\",\"quantity\":1}]}" -X POST
|
||||
|
||||
|
||||
How to Read an order with GET
|
||||
-----------------------------
|
||||
@@ -192,7 +195,8 @@ Response
|
||||
} ]
|
||||
}
|
||||
|
||||
|
||||
note:
|
||||
curl -vv http://localhost:9090/order/1
|
||||
|
||||
How to Update an order with PUT
|
||||
-------------------------------
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
<?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="restbucks" uuid="2773FEAA-448F-410E-BEDE-9298C4749066" library_target="restbucks">
|
||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-12-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-12-0 http://www.eiffel.com/developers/xml/configuration-1-12-0.xsd" name="restbucks" uuid="2773FEAA-448F-410E-BEDE-9298C4749066" library_target="restbucks">
|
||||
<target name="restbucks_common">
|
||||
<file_rule>
|
||||
<exclude>/EIFGENs$</exclude>
|
||||
<exclude>/\.git$</exclude>
|
||||
<exclude>/\.svn$</exclude>
|
||||
</file_rule>
|
||||
<option full_class_checking="false" void_safety="all">
|
||||
</option>
|
||||
<setting name="concurrency" value="thread"/>
|
||||
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
|
||||
<library name="connector_nino" location="..\..\library\server\ewsgi\connectors\nino\nino-safe.ecf" readonly="false">
|
||||
@@ -13,9 +15,9 @@
|
||||
<debug name="nino" enabled="true"/>
|
||||
</option>
|
||||
</library>
|
||||
<library name="conneg" location="..\..\library\network\protocol\CONNEG\conneg-safe.ecf"/>
|
||||
<library name="conneg" location="..\..\library\network\protocol\content_negotiation\conneg-safe.ecf"/>
|
||||
<library name="crypto" location="$ISE_LIBRARY\unstable\library\text\encryption\crypto\crypto-safe.ecf" readonly="false"/>
|
||||
<library name="default_nino" location="..\..\library\server\wsf\default\nino-safe.ecf" readonly="false"/>
|
||||
<library name="eel" location="$ISE_LIBRARY\contrib\library\text\encryption\eel\eel-safe.ecf" readonly="false"/>
|
||||
<library name="encoder" location="..\..\library\text\encoder\encoder-safe.ecf" readonly="false"/>
|
||||
<library name="http" location="..\..\library\network\protocol\http\http-safe.ecf" readonly="false"/>
|
||||
<library name="json" location="..\..\contrib\library\text\parser\json\library\json-safe.ecf" readonly="false"/>
|
||||
@@ -27,7 +29,7 @@
|
||||
</target>
|
||||
<target name="restbucks" extends="restbucks_common">
|
||||
<root class="RESTBUCKS_SERVER" feature="make"/>
|
||||
<option debug="true" warning="true" full_class_checking="true" is_attached_by_default="true" void_safety="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" check="true" invariant="true" loop="true" supplier_precondition="true"/>
|
||||
</option>
|
||||
@@ -39,7 +41,7 @@
|
||||
</target>
|
||||
<target name="policy_driven_restbucks" extends="restbucks_common">
|
||||
<root class="RESTBUCKS_SERVER" feature="make"/>
|
||||
<option debug="true" warning="true" full_class_checking="true" is_attached_by_default="true" void_safety="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" check="true" invariant="true" loop="true" supplier_precondition="true"/>
|
||||
</option>
|
||||
|
||||
@@ -65,7 +65,7 @@ feature -- Access
|
||||
-- At present, there is no support for this except for DELETE.
|
||||
end
|
||||
|
||||
conneg (req: WSF_REQUEST): CONNEG_SERVER_SIDE
|
||||
conneg (req: WSF_REQUEST): SERVER_CONTENT_NEGOTIATION
|
||||
-- Content negotiatior for all requests
|
||||
once
|
||||
create Result.make ({HTTP_MIME_TYPES}.application_json, "en", "UTF-8", "identity")
|
||||
@@ -227,8 +227,7 @@ feature -- Execution
|
||||
end
|
||||
end
|
||||
ensure then
|
||||
order_saved_only_for_get_head: req.is_get_head_request_method =
|
||||
attached {ORDER} req.execution_variable (Order_execution_variable)
|
||||
order_saved_only_for_get_head: attached {ORDER} req.execution_variable (Order_execution_variable) implies req.is_get_head_request_method
|
||||
end
|
||||
|
||||
feature -- GET/HEAD content
|
||||
|
||||
@@ -6,28 +6,19 @@ note
|
||||
class
|
||||
ETAG_UTILS
|
||||
|
||||
inherit
|
||||
ARRAY_FACILITIES
|
||||
|
||||
feature -- Access
|
||||
|
||||
md5_digest (a_string: STRING): STRING
|
||||
-- Cryptographic hash function that produces a 128-bit (16-byte) hash value, based on `a_string'
|
||||
local
|
||||
md5: MD5
|
||||
output: SPECIAL [NATURAL_8]
|
||||
do
|
||||
create md5.make
|
||||
create output.make_filled (0, 16)
|
||||
md5.sink_string (a_string)
|
||||
md5.do_final (output, 0)
|
||||
Result := as_natural_32_be (output, 0).to_hex_string
|
||||
Result.append (as_natural_32_be (output, 4).to_hex_string)
|
||||
Result.append (as_natural_32_be (output, 8).to_hex_string)
|
||||
Result.append (as_natural_32_be (output, 12).to_hex_string)
|
||||
md5.update_from_string (a_string)
|
||||
Result := md5.digest_as_string
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2012, Javier Velilla and others"
|
||||
copyright: "2011-2014, Javier Velilla and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
end
|
||||
|
||||
@@ -43,6 +43,7 @@ feature {NONE} -- Initialization
|
||||
local
|
||||
www: WSF_FILE_SYSTEM_HANDLER
|
||||
do
|
||||
map_uri_template_agent_with_request_methods ("/upload/{name}{?nb}", agent execute_upload_put, router.methods_put)
|
||||
map_uri_template_agent ("/upload{?nb}", agent execute_upload)
|
||||
|
||||
create www.make (document_root)
|
||||
@@ -95,7 +96,7 @@ feature -- Execution
|
||||
local
|
||||
l_body: STRING_8
|
||||
l_safe_filename: STRING_8
|
||||
fn: FILE_NAME
|
||||
fn: PATH
|
||||
page: WSF_HTML_PAGE_RESPONSE
|
||||
n: INTEGER
|
||||
do
|
||||
@@ -114,8 +115,8 @@ feature -- Execution
|
||||
end
|
||||
if attached {WSF_STRING} req.query_parameter ("demo") as p_demo then
|
||||
create fn.make_from_string (document_root)
|
||||
fn.set_file_name (p_demo.value)
|
||||
l_body.append ("File: <input type=%"file%" name=%"uploaded_file[]%" size=%"60%" value=%""+ html_encode (fn.string) +"%"></br>%N")
|
||||
fn := fn.extended (p_demo.value)
|
||||
l_body.append ("File: <input type=%"file%" name=%"uploaded_file[]%" size=%"60%" value=%""+ html_encode (fn.name) +"%"></br>%N")
|
||||
end
|
||||
|
||||
from
|
||||
@@ -131,15 +132,17 @@ feature -- Execution
|
||||
create l_body.make (255)
|
||||
l_body.append ("<h1>EWF: Uploaded files</h1>%N")
|
||||
l_body.append ("<ul>")
|
||||
n := 0
|
||||
across
|
||||
req.uploaded_files as c
|
||||
loop
|
||||
n := n + 1
|
||||
l_body.append ("<li>")
|
||||
l_body.append ("<div>" + c.item.name + "=" + html_encode (c.item.filename) + " size=" + c.item.size.out + " type=" + c.item.content_type + "</div>")
|
||||
create fn.make_from_string (files_root)
|
||||
l_safe_filename := c.item.safe_filename
|
||||
fn.set_file_name (l_safe_filename)
|
||||
if c.item.move_to (fn.string) then
|
||||
fn := fn.extended (l_safe_filename)
|
||||
if c.item.move_to (fn.name) then
|
||||
if c.item.content_type.starts_with ("image") then
|
||||
l_body.append ("%N<a href=%"../files/" + url_encode (l_safe_filename) + "%"><img src=%"../files/"+ l_safe_filename +"%" /></a>")
|
||||
else
|
||||
@@ -148,6 +151,7 @@ feature -- Execution
|
||||
end
|
||||
l_body.append ("</li>")
|
||||
end
|
||||
|
||||
l_body.append ("</ul>")
|
||||
|
||||
create page.make
|
||||
@@ -158,8 +162,85 @@ feature -- Execution
|
||||
end
|
||||
end
|
||||
|
||||
execute_upload_put (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
-- Upload page is requested, PUT
|
||||
require
|
||||
is_put_request_method: req.is_put_request_method
|
||||
local
|
||||
l_body: STRING_8
|
||||
l_safe_filename: detachable READABLE_STRING_32
|
||||
fn: PATH
|
||||
page: WSF_HTML_PAGE_RESPONSE
|
||||
n: INTEGER
|
||||
do
|
||||
create l_body.make (255)
|
||||
l_body.append ("<h1>EWF: Uploaded files</h1>%N")
|
||||
l_body.append ("<ul>")
|
||||
n := 0
|
||||
if attached {WSF_STRING} req.path_parameter ("name") as p_name then
|
||||
l_safe_filename := p_name.value
|
||||
end
|
||||
if l_safe_filename = Void or else l_safe_filename.is_empty then
|
||||
l_safe_filename := "input_data"
|
||||
end
|
||||
if n = 0 and req.content_length_value > 0 then
|
||||
if attached new_temporary_output_file ("tmp-uploaded-file_" + n.out) as f then
|
||||
req.read_input_data_into_file (f)
|
||||
f.close
|
||||
create fn.make_from_string (files_root)
|
||||
fn := fn.extended (l_safe_filename)
|
||||
f.rename_file (fn.name)
|
||||
l_body.append ("<li>")
|
||||
l_body.append ("<div>Input data : size=" + f.count.out + " (" + req.content_length_value.out + ")</div>")
|
||||
l_body.append ("%N<a href=%"../files/" + url_encode (l_safe_filename) + "%">"+ html_encode (l_safe_filename) +"</a>")
|
||||
l_body.append ("</li>")
|
||||
end
|
||||
end
|
||||
l_body.append ("</ul>")
|
||||
|
||||
create page.make
|
||||
page.set_title ("EWF: uploaded image")
|
||||
page.add_style ("../style.css", "all")
|
||||
page.set_body (l_body)
|
||||
res.send (page)
|
||||
end
|
||||
|
||||
|
||||
feature {NONE} -- Encoder
|
||||
|
||||
new_temporary_output_file (n: detachable READABLE_STRING_8): detachable FILE
|
||||
local
|
||||
bp: detachable PATH
|
||||
d: DIRECTORY
|
||||
i: INTEGER
|
||||
do
|
||||
create bp.make_current
|
||||
create d.make_with_path (bp)
|
||||
if not d.exists then
|
||||
d.recursive_create_dir
|
||||
end
|
||||
if n /= Void then
|
||||
bp := bp.extended ("tmp-download-" + n)
|
||||
else
|
||||
bp := bp.extended ("tmp")
|
||||
end
|
||||
from
|
||||
i := 0
|
||||
until
|
||||
Result /= Void or i > 100
|
||||
loop
|
||||
i := i + 1
|
||||
create {RAW_FILE} Result.make_with_path (bp.appended ("__" + i.out))
|
||||
if Result.exists then
|
||||
Result := Void
|
||||
else
|
||||
Result.open_write
|
||||
end
|
||||
end
|
||||
ensure
|
||||
Result /= Void implies Result.is_open_write
|
||||
end
|
||||
|
||||
url_encode (s: READABLE_STRING_32): STRING_8
|
||||
-- URL Encode `s' as Result
|
||||
do
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?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="upload_image" uuid="F2400BE8-D8EB-48EB-B4E4-5D4377062A7F" library_target="upload_image">
|
||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-12-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-12-0 http://www.eiffel.com/developers/xml/configuration-1-12-0.xsd" name="upload_image" uuid="F2400BE8-D8EB-48EB-B4E4-5D4377062A7F" library_target="upload_image">
|
||||
<target name="upload_image">
|
||||
<root class="IMAGE_UPLOADER" feature="make"/>
|
||||
<file_rule>
|
||||
@@ -20,11 +20,10 @@
|
||||
</library>
|
||||
<library name="default_nino" location="..\..\library\server\wsf\default\nino-safe.ecf" readonly="false" use_application_options="true"/>
|
||||
<library name="encoder" location="..\..\library\text\encoder\encoder-safe.ecf" readonly="false"/>
|
||||
<library name="http" location="../../library/network/protocol/http/http-safe.ecf" readonly="false"/>
|
||||
<library name="http" location="..\..\library\network\protocol\http\http-safe.ecf" readonly="false"/>
|
||||
<library name="testing" location="$ISE_LIBRARY\library\testing\testing-safe.ecf"/>
|
||||
<library name="uri_template" location="../../library/text/parser/uri_template/uri_template-safe.ecf" readonly="false"/>
|
||||
<library name="uri_template" location="..\..\library\text\parser\uri_template\uri_template-safe.ecf" readonly="false"/>
|
||||
<library name="wsf" location="..\..\library\server\wsf\wsf-safe.ecf" readonly="false" use_application_options="true"/>
|
||||
<cluster name="src" location="src\" recursive="true">
|
||||
</cluster>
|
||||
<cluster name="src" location="src\" recursive="true"/>
|
||||
</target>
|
||||
</system>
|
||||
|
||||
@@ -1,51 +1,62 @@
|
||||
|
||||
# Available libraries.
|
||||
|
||||
http_client-safe : c:\_dev\EWF\EWF-dev\library\network\http_client\http_client-safe.ecf
|
||||
http_client : c:\_dev\EWF\EWF-dev\library\network\http_client\http_client.ecf
|
||||
conneg-safe : c:\_dev\EWF\EWF-dev\library\network\protocol\CONNEG\conneg-safe.ecf
|
||||
conneg : c:\_dev\EWF\EWF-dev\library\network\protocol\CONNEG\conneg.ecf
|
||||
http-safe : c:\_dev\EWF\EWF-dev\library\network\protocol\http\http-safe.ecf
|
||||
http : c:\_dev\EWF\EWF-dev\library\network\protocol\http\http.ecf
|
||||
http_authorization-safe : c:\_dev\EWF\EWF-dev\library\server\authentication\http_authorization\http_authorization-safe.ecf
|
||||
http_authorization : c:\_dev\EWF\EWF-dev\library\server\authentication\http_authorization\http_authorization.ecf
|
||||
ewsgi-safe : c:\_dev\EWF\EWF-dev\library\server\ewsgi\ewsgi-safe.ecf
|
||||
ewsgi : c:\_dev\EWF\EWF-dev\library\server\ewsgi\ewsgi.ecf
|
||||
ewsgi_spec-safe : c:\_dev\EWF\EWF-dev\library\server\ewsgi\ewsgi_spec-safe.ecf
|
||||
ewsgi_spec : c:\_dev\EWF\EWF-dev\library\server\ewsgi\ewsgi_spec.ecf
|
||||
cgi-safe : c:\_dev\EWF\EWF-dev\library\server\ewsgi\connectors\cgi\cgi-safe.ecf
|
||||
cgi : c:\_dev\EWF\EWF-dev\library\server\ewsgi\connectors\cgi\cgi.ecf
|
||||
libfcgi-safe : c:\_dev\EWF\EWF-dev\library\server\ewsgi\connectors\libfcgi\libfcgi-safe.ecf
|
||||
libfcgi : c:\_dev\EWF\EWF-dev\library\server\ewsgi\connectors\libfcgi\libfcgi.ecf
|
||||
nino-safe : c:\_dev\EWF\EWF-dev\library\server\ewsgi\connectors\nino\nino-safe.ecf
|
||||
nino : c:\_dev\EWF\EWF-dev\library\server\ewsgi\connectors\nino\nino.ecf
|
||||
null-safe : c:\_dev\EWF\EWF-dev\library\server\ewsgi\connectors\null\null-safe.ecf
|
||||
null : c:\_dev\EWF\EWF-dev\library\server\ewsgi\connectors\null\null.ecf
|
||||
fcgi-safe : c:\_dev\EWF\EWF-dev\library\server\libfcgi\fcgi-safe.ecf
|
||||
fcgi : c:\_dev\EWF\EWF-dev\library\server\libfcgi\fcgi.ecf
|
||||
libfcgi-safe : c:\_dev\EWF\EWF-dev\library\server\libfcgi\libfcgi-safe.ecf
|
||||
libfcgi : c:\_dev\EWF\EWF-dev\library\server\libfcgi\libfcgi.ecf
|
||||
router-safe : c:\_dev\EWF\EWF-dev\library\server\request\router\router-safe.ecf
|
||||
router : c:\_dev\EWF\EWF-dev\library\server\request\router\router.ecf
|
||||
wsf-safe : c:\_dev\EWF\EWF-dev\library\server\wsf\wsf-safe.ecf
|
||||
wsf : c:\_dev\EWF\EWF-dev\library\server\wsf\wsf.ecf
|
||||
all-safe : c:\_dev\EWF\EWF-dev\library\server\wsf\connector\all-safe.ecf
|
||||
cgi-safe : c:\_dev\EWF\EWF-dev\library\server\wsf\connector\cgi-safe.ecf
|
||||
cgi : c:\_dev\EWF\EWF-dev\library\server\wsf\connector\cgi.ecf
|
||||
libfcgi-safe : c:\_dev\EWF\EWF-dev\library\server\wsf\connector\libfcgi-safe.ecf
|
||||
libfcgi : c:\_dev\EWF\EWF-dev\library\server\wsf\connector\libfcgi.ecf
|
||||
nino-safe : c:\_dev\EWF\EWF-dev\library\server\wsf\connector\nino-safe.ecf
|
||||
nino : c:\_dev\EWF\EWF-dev\library\server\wsf\connector\nino.ecf
|
||||
cgi-safe : c:\_dev\EWF\EWF-dev\library\server\wsf\default\cgi-safe.ecf
|
||||
cgi : c:\_dev\EWF\EWF-dev\library\server\wsf\default\cgi.ecf
|
||||
libfcgi-safe : c:\_dev\EWF\EWF-dev\library\server\wsf\default\libfcgi-safe.ecf
|
||||
libfcgi : c:\_dev\EWF\EWF-dev\library\server\wsf\default\libfcgi.ecf
|
||||
nino-safe : c:\_dev\EWF\EWF-dev\library\server\wsf\default\nino-safe.ecf
|
||||
nino : c:\_dev\EWF\EWF-dev\library\server\wsf\default\nino.ecf
|
||||
wsf_extension-safe : c:\_dev\EWF\EWF-dev\library\server\wsf_extension\wsf_extension-safe.ecf
|
||||
wsf_extension : c:\_dev\EWF\EWF-dev\library\server\wsf_extension\wsf_extension.ecf
|
||||
encoder-safe : c:\_dev\EWF\EWF-dev\library\text\encoder\encoder-safe.ecf
|
||||
encoder : c:\_dev\EWF\EWF-dev\library\text\encoder\encoder.ecf
|
||||
error-safe : c:\_dev\EWF\EWF-dev\library\utility\general\error\error-safe.ecf
|
||||
error : c:\_dev\EWF\EWF-dev\library\utility\general\error\error.ecf
|
||||
http_client-safe : C:\_dev\projects\ewf\ewf\library\network\http_client\http_client-safe.ecf
|
||||
http_client : C:\_dev\projects\ewf\ewf\library\network\http_client\http_client.ecf
|
||||
conneg-safe : C:\_dev\projects\ewf\ewf\library\network\protocol\content_negotiation\conneg-safe.ecf
|
||||
conneg : C:\_dev\projects\ewf\ewf\library\network\protocol\content_negotiation\conneg.ecf
|
||||
http-safe : C:\_dev\projects\ewf\ewf\library\network\protocol\http\http-safe.ecf
|
||||
http : C:\_dev\projects\ewf\ewf\library\network\protocol\http\http.ecf
|
||||
notification_email-safe : C:\_dev\projects\ewf\ewf\library\runtime\process\notification_email\notification_email-safe.ecf
|
||||
notification_email : C:\_dev\projects\ewf\ewf\library\runtime\process\notification_email\notification_email.ecf
|
||||
openid-safe : C:\_dev\projects\ewf\ewf\library\security\openid\consumer\openid-safe.ecf
|
||||
openid : C:\_dev\projects\ewf\ewf\library\security\openid\consumer\openid.ecf
|
||||
demo-safe : C:\_dev\projects\ewf\ewf\library\security\openid\consumer\demo\demo-safe.ecf
|
||||
http_authorization-safe : C:\_dev\projects\ewf\ewf\library\server\authentication\http_authorization\http_authorization-safe.ecf
|
||||
http_authorization : C:\_dev\projects\ewf\ewf\library\server\authentication\http_authorization\http_authorization.ecf
|
||||
ewsgi-safe : C:\_dev\projects\ewf\ewf\library\server\ewsgi\ewsgi-safe.ecf
|
||||
ewsgi : C:\_dev\projects\ewf\ewf\library\server\ewsgi\ewsgi.ecf
|
||||
ewsgi_spec-safe : C:\_dev\projects\ewf\ewf\library\server\ewsgi\ewsgi_spec-safe.ecf
|
||||
ewsgi_spec : C:\_dev\projects\ewf\ewf\library\server\ewsgi\ewsgi_spec.ecf
|
||||
cgi-safe : C:\_dev\projects\ewf\ewf\library\server\ewsgi\connectors\cgi\cgi-safe.ecf
|
||||
cgi : C:\_dev\projects\ewf\ewf\library\server\ewsgi\connectors\cgi\cgi.ecf
|
||||
libfcgi-safe : C:\_dev\projects\ewf\ewf\library\server\ewsgi\connectors\libfcgi\libfcgi-safe.ecf
|
||||
libfcgi : C:\_dev\projects\ewf\ewf\library\server\ewsgi\connectors\libfcgi\libfcgi.ecf
|
||||
nino-safe : C:\_dev\projects\ewf\ewf\library\server\ewsgi\connectors\nino\nino-safe.ecf
|
||||
nino : C:\_dev\projects\ewf\ewf\library\server\ewsgi\connectors\nino\nino.ecf
|
||||
null-safe : C:\_dev\projects\ewf\ewf\library\server\ewsgi\connectors\null\null-safe.ecf
|
||||
null : C:\_dev\projects\ewf\ewf\library\server\ewsgi\connectors\null\null.ecf
|
||||
libfcgi-safe : C:\_dev\projects\ewf\ewf\library\server\libfcgi\libfcgi-safe.ecf
|
||||
libfcgi : C:\_dev\projects\ewf\ewf\library\server\libfcgi\libfcgi.ecf
|
||||
wsf-safe : C:\_dev\projects\ewf\ewf\library\server\wsf\wsf-safe.ecf
|
||||
wsf : C:\_dev\projects\ewf\ewf\library\server\wsf\wsf.ecf
|
||||
wsf_extension-safe : C:\_dev\projects\ewf\ewf\library\server\wsf\wsf_extension-safe.ecf
|
||||
wsf_extension : C:\_dev\projects\ewf\ewf\library\server\wsf\wsf_extension.ecf
|
||||
wsf_policy_driven-safe : C:\_dev\projects\ewf\ewf\library\server\wsf\wsf_policy_driven-safe.ecf
|
||||
wsf_policy_driven : C:\_dev\projects\ewf\ewf\library\server\wsf\wsf_policy_driven.ecf
|
||||
wsf_router_context-safe : C:\_dev\projects\ewf\ewf\library\server\wsf\wsf_router_context-safe.ecf
|
||||
wsf_router_context : C:\_dev\projects\ewf\ewf\library\server\wsf\wsf_router_context.ecf
|
||||
wsf_session-safe : C:\_dev\projects\ewf\ewf\library\server\wsf\wsf_session-safe.ecf
|
||||
wsf_session : C:\_dev\projects\ewf\ewf\library\server\wsf\wsf_session.ecf
|
||||
all-safe : C:\_dev\projects\ewf\ewf\library\server\wsf\connector\all-safe.ecf
|
||||
cgi-safe : C:\_dev\projects\ewf\ewf\library\server\wsf\connector\cgi-safe.ecf
|
||||
cgi : C:\_dev\projects\ewf\ewf\library\server\wsf\connector\cgi.ecf
|
||||
libfcgi-safe : C:\_dev\projects\ewf\ewf\library\server\wsf\connector\libfcgi-safe.ecf
|
||||
libfcgi : C:\_dev\projects\ewf\ewf\library\server\wsf\connector\libfcgi.ecf
|
||||
nino-safe : C:\_dev\projects\ewf\ewf\library\server\wsf\connector\nino-safe.ecf
|
||||
nino : C:\_dev\projects\ewf\ewf\library\server\wsf\connector\nino.ecf
|
||||
openshift-safe : C:\_dev\projects\ewf\ewf\library\server\wsf\connector\openshift-safe.ecf
|
||||
cgi-safe : C:\_dev\projects\ewf\ewf\library\server\wsf\default\cgi-safe.ecf
|
||||
cgi : C:\_dev\projects\ewf\ewf\library\server\wsf\default\cgi.ecf
|
||||
libfcgi-safe : C:\_dev\projects\ewf\ewf\library\server\wsf\default\libfcgi-safe.ecf
|
||||
libfcgi : C:\_dev\projects\ewf\ewf\library\server\wsf\default\libfcgi.ecf
|
||||
nino-safe : C:\_dev\projects\ewf\ewf\library\server\wsf\default\nino-safe.ecf
|
||||
nino : C:\_dev\projects\ewf\ewf\library\server\wsf\default\nino.ecf
|
||||
openshift-safe : C:\_dev\projects\ewf\ewf\library\server\wsf\default\openshift-safe.ecf
|
||||
wsf_html-safe : C:\_dev\projects\ewf\ewf\library\server\wsf_html\wsf_html-safe.ecf
|
||||
wsf_html : C:\_dev\projects\ewf\ewf\library\server\wsf_html\wsf_html.ecf
|
||||
encoder-safe : C:\_dev\projects\ewf\ewf\library\text\encoder\encoder-safe.ecf
|
||||
encoder : C:\_dev\projects\ewf\ewf\library\text\encoder\encoder.ecf
|
||||
error-safe : C:\_dev\projects\ewf\ewf\library\utility\general\error\error-safe.ecf
|
||||
error : C:\_dev\projects\ewf\ewf\library\utility\general\error\error.ecf
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?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="http_client" uuid="628F5A96-021B-4191-926B-B3BF49272866" library_target="http_client">
|
||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-12-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-12-0 http://www.eiffel.com/developers/xml/configuration-1-12-0.xsd" name="http_client" uuid="628F5A96-021B-4191-926B-B3BF49272866" library_target="http_client">
|
||||
<target name="http_client">
|
||||
<root all_classes="true"/>
|
||||
<file_rule>
|
||||
@@ -10,16 +10,7 @@
|
||||
<option warning="true" full_class_checking="true" is_attached_by_default="true" void_safety="all" syntax="provisional">
|
||||
</option>
|
||||
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
|
||||
<library name="curl" location="$ISE_LIBRARY\library\cURL\cURL-safe.ecf">
|
||||
<condition>
|
||||
<version type="compiler" min="7.1.8.8674"/>
|
||||
</condition>
|
||||
</library>
|
||||
<library name="curl_local" location="..\..\..\contrib\ise_library\cURL-safe.ecf">
|
||||
<condition>
|
||||
<version type="compiler" max="7.1.8.8673"/>
|
||||
</condition>
|
||||
</library>
|
||||
<library name="curl" location="$ISE_LIBRARY\library\cURL\cURL-safe.ecf"/>
|
||||
<library name="encoder" location="..\..\text\encoder\encoder-safe.ecf"/>
|
||||
<library name="net" location="$ISE_LIBRARY\library\net\net-safe.ecf"/>
|
||||
<cluster name="src" location=".\src\" recursive="true"/>
|
||||
|
||||
@@ -11,16 +11,7 @@
|
||||
</option>
|
||||
<library name="base" location="$ISE_LIBRARY/library/base/base.ecf"/>
|
||||
<library name="net" location="$ISE_LIBRARY\library\net\net.ecf"/>
|
||||
<library name="curl" location="$ISE_LIBRARY\library\cURL\cURL.ecf">
|
||||
<condition>
|
||||
<version type="compiler" min="7.1.8.8674"/>
|
||||
</condition>
|
||||
</library>
|
||||
<library name="curl_local" location="..\..\..\contrib\ise_library\cURL.ecf">
|
||||
<condition>
|
||||
<version type="compiler" max="7.1.8.8673"/>
|
||||
</condition>
|
||||
</library>
|
||||
<library name="curl" location="$ISE_LIBRARY\library\cURL\cURL.ecf"/>
|
||||
<library name="encoder" location="../../text/encoder/encoder.ecf"/>
|
||||
<cluster name="src" location=".\src\" recursive="true"/>
|
||||
</target>
|
||||
|
||||
15
library/network/http_client/package.iron
Normal file
15
library/network/http_client/package.iron
Normal file
@@ -0,0 +1,15 @@
|
||||
package http_client
|
||||
|
||||
project
|
||||
http_client = "http_client-safe.ecf"
|
||||
http_client = "http_client.ecf"
|
||||
|
||||
note
|
||||
-- title:
|
||||
-- description:
|
||||
-- tags:
|
||||
-- license:
|
||||
-- copyright:
|
||||
-- link[doc]: "Documentation" http://
|
||||
|
||||
end
|
||||
@@ -111,12 +111,14 @@ feature -- Settings
|
||||
|
||||
timeout: INTEGER
|
||||
-- HTTP transaction timeout in seconds.
|
||||
--| 0 means it nevers timeout
|
||||
do
|
||||
Result := session.timeout
|
||||
end
|
||||
|
||||
connect_timeout: INTEGER
|
||||
-- HTTP connection timeout in seconds.
|
||||
--| 0 means it nevers timeout
|
||||
do
|
||||
Result := session.connect_timeout
|
||||
end
|
||||
@@ -218,7 +220,7 @@ feature {NONE} -- Utilities: encoding
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2012, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||
copyright: "2011-2013, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
source: "[
|
||||
Eiffel Software
|
||||
|
||||
@@ -5,7 +5,7 @@ note
|
||||
- headers
|
||||
- query_parameters
|
||||
- form parameters
|
||||
- upload_data or upload_filename
|
||||
- upload_data xor upload_filename
|
||||
And in addition it has
|
||||
- credentials_required
|
||||
- proxy
|
||||
@@ -86,16 +86,20 @@ feature -- Access
|
||||
feature -- Status report
|
||||
|
||||
has_form_data: BOOLEAN
|
||||
-- Has any form parameters?
|
||||
--| i.e coming from POST or PUT content.
|
||||
do
|
||||
Result := not form_parameters.is_empty
|
||||
end
|
||||
|
||||
has_upload_data: BOOLEAN
|
||||
-- Has associated upload_data?
|
||||
do
|
||||
Result := attached upload_data as d and then not d.is_empty
|
||||
end
|
||||
|
||||
has_upload_filename: BOOLEAN
|
||||
-- Has associated upload_filename?
|
||||
do
|
||||
Result := attached upload_filename as fn and then not fn.is_empty
|
||||
end
|
||||
@@ -109,11 +113,13 @@ feature -- Status report
|
||||
feature -- Element change
|
||||
|
||||
add_header (k: READABLE_STRING_8; v: READABLE_STRING_8)
|
||||
-- Add http header line `k:v'.
|
||||
do
|
||||
headers.force (v, k)
|
||||
end
|
||||
|
||||
add_header_line (s: READABLE_STRING_8)
|
||||
-- Add http header line `s'.
|
||||
local
|
||||
i: INTEGER
|
||||
do
|
||||
@@ -124,6 +130,7 @@ feature -- Element change
|
||||
end
|
||||
|
||||
add_header_lines (lst: ITERABLE [READABLE_STRING_8])
|
||||
-- Add collection of http header lines `lst'
|
||||
do
|
||||
across
|
||||
lst as c
|
||||
@@ -133,44 +140,61 @@ feature -- Element change
|
||||
end
|
||||
|
||||
add_query_parameter (k: READABLE_STRING_32; v: READABLE_STRING_32)
|
||||
-- Add a query parameter `k=v'.
|
||||
do
|
||||
query_parameters.force (v, k)
|
||||
end
|
||||
|
||||
add_form_parameter (k: READABLE_STRING_32; v: READABLE_STRING_32)
|
||||
-- Add a form parameter `k'= `v'.
|
||||
do
|
||||
form_parameters.force (v, k)
|
||||
end
|
||||
|
||||
set_credentials_required (b: BOOLEAN)
|
||||
-- If b is True, credentials are required, otherwise just optional.
|
||||
do
|
||||
credentials_required := b
|
||||
end
|
||||
|
||||
set_upload_data (a_data: like upload_data)
|
||||
-- Set `upload_data' to `a_data'
|
||||
--| note: the Current context can have upload_data XOR upload_filename, but not both.
|
||||
require
|
||||
has_no_upload_data: a_data /= Void implies not has_upload_data
|
||||
has_upload_filename: (a_data /= Void and then not a_data.is_empty) implies not has_upload_filename
|
||||
do
|
||||
if a_data = Void or else a_data.is_empty then
|
||||
upload_data := Void
|
||||
else
|
||||
upload_data := a_data
|
||||
end
|
||||
ensure
|
||||
(a_data /= Void and then not a_data.is_empty) implies (has_upload_data and not has_upload_filename)
|
||||
end
|
||||
|
||||
set_upload_filename (a_fn: detachable READABLE_STRING_GENERAL)
|
||||
-- Set `upload_filename' to `a_fn'
|
||||
--| note: the Current context can have upload_data XOR upload_filename, but not both.
|
||||
require
|
||||
has_no_upload_filename: a_fn /= Void implies not has_upload_filename
|
||||
has_no_upload_data: (a_fn /= Void and then not a_fn.is_empty) implies not has_upload_data
|
||||
do
|
||||
if a_fn = Void then
|
||||
if a_fn = Void or else a_fn.is_empty then
|
||||
upload_filename := Void
|
||||
else
|
||||
create upload_filename.make_from_string_general (a_fn)
|
||||
end
|
||||
ensure
|
||||
(a_fn /= Void and then not a_fn.is_empty) implies (has_upload_filename and not has_upload_data)
|
||||
end
|
||||
|
||||
set_write_agent (agt: like write_agent)
|
||||
-- Set `write_agent' to `agt'.
|
||||
do
|
||||
write_agent := agt
|
||||
end
|
||||
|
||||
set_output_file (f: FILE)
|
||||
-- Set `output_file' to `f'.
|
||||
require
|
||||
f_is_open_write: f.is_open_write
|
||||
do
|
||||
@@ -178,6 +202,7 @@ feature -- Element change
|
||||
end
|
||||
|
||||
set_output_content_file (f: FILE)
|
||||
-- Set `output_content_file' to `f'.
|
||||
require
|
||||
f_is_open_write: f.is_open_write
|
||||
do
|
||||
@@ -187,6 +212,8 @@ feature -- Element change
|
||||
feature -- Status setting
|
||||
|
||||
set_proxy (a_host: detachable READABLE_STRING_8; a_port: INTEGER)
|
||||
-- Set proxy to `a_host:a_port'.
|
||||
--| this can be used for instance with "http://fiddler2.com/" web debugging proxy.
|
||||
do
|
||||
if a_host = Void then
|
||||
proxy := Void
|
||||
@@ -212,6 +239,7 @@ feature -- Conversion helpers
|
||||
feature {NONE} -- Implementation
|
||||
|
||||
parameters_to_url_encoded_string (ht: HASH_TABLE [READABLE_STRING_32, READABLE_STRING_32]): STRING_8
|
||||
-- Build url encoded string using parameters from `ht'.
|
||||
do
|
||||
create Result.make (64)
|
||||
from
|
||||
@@ -230,6 +258,7 @@ feature {NONE} -- Implementation
|
||||
end
|
||||
|
||||
url_encoder: URL_ENCODER
|
||||
-- Shared URL encoder.
|
||||
once
|
||||
create Result
|
||||
end
|
||||
|
||||
@@ -36,9 +36,10 @@ feature {NONE} -- Initialization
|
||||
end
|
||||
|
||||
set_defaults
|
||||
-- Set default settings.
|
||||
do
|
||||
timeout := 5
|
||||
connect_timeout := 1
|
||||
timeout := 0 --| never timeout
|
||||
connect_timeout := 0 --| never timeout
|
||||
max_redirects := 5
|
||||
set_basic_auth_type
|
||||
end
|
||||
@@ -76,11 +77,15 @@ feature -- Basic operation
|
||||
end
|
||||
end
|
||||
|
||||
feature -- Custom
|
||||
|
||||
custom (a_method: READABLE_STRING_8; a_path: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): HTTP_CLIENT_RESPONSE
|
||||
-- Response for `a_method' request based on Current, `a_path' and `ctx'.
|
||||
deferred
|
||||
end
|
||||
|
||||
feature -- Helper
|
||||
|
||||
get (a_path: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): HTTP_CLIENT_RESPONSE
|
||||
-- Response for GET request based on Current, `a_path' and `ctx'.
|
||||
deferred
|
||||
@@ -103,6 +108,18 @@ feature -- Basic operation
|
||||
deferred
|
||||
end
|
||||
|
||||
patch (a_path: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; data: detachable READABLE_STRING_8): HTTP_CLIENT_RESPONSE
|
||||
-- Response for PATCH request based on Current, `a_path' and `ctx'
|
||||
-- with input `data'
|
||||
deferred
|
||||
end
|
||||
|
||||
patch_file (a_path: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; fn: detachable READABLE_STRING_8): HTTP_CLIENT_RESPONSE
|
||||
-- Response for PATCH request based on Current, `a_path' and `ctx'
|
||||
-- with uploaded data file `fn'
|
||||
deferred
|
||||
end
|
||||
|
||||
put (a_path: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; data: detachable READABLE_STRING_8): HTTP_CLIENT_RESPONSE
|
||||
-- Response for PUT request based on Current, `a_path' and `ctx'
|
||||
-- with input `data'
|
||||
@@ -133,10 +150,12 @@ feature -- Status report
|
||||
feature -- Settings
|
||||
|
||||
timeout: INTEGER
|
||||
-- HTTP transaction timeout in seconds. Defaults to 5 seconds.
|
||||
-- HTTP transaction timeout in seconds.
|
||||
-- Defaults to 0 second i.e never timeout.
|
||||
|
||||
connect_timeout: INTEGER
|
||||
-- HTTP connection timeout in seconds. Defaults to 1 second.
|
||||
-- HTTP connection timeout in seconds.
|
||||
-- Defaults to 0 second i.e never timeout.
|
||||
|
||||
max_redirects: INTEGER
|
||||
-- Maximum number of times to follow redirects.
|
||||
@@ -281,7 +300,7 @@ feature -- Element change
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2012, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||
copyright: "2011-2013, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
source: "[
|
||||
Eiffel Software
|
||||
|
||||
@@ -162,6 +162,7 @@ feature -- Execution
|
||||
check
|
||||
post_or_put_request_method: request_method.is_case_insensitive_equal ("POST")
|
||||
or request_method.is_case_insensitive_equal ("PUT")
|
||||
or request_method.is_case_insensitive_equal ("PATCH")
|
||||
end
|
||||
|
||||
curl_easy.setopt_string (curl_handle, {CURL_OPT_CONSTANTS}.curlopt_postfields, l_upload_data)
|
||||
@@ -170,6 +171,7 @@ feature -- Execution
|
||||
check
|
||||
post_or_put_request_method: request_method.is_case_insensitive_equal ("POST")
|
||||
or request_method.is_case_insensitive_equal ("PUT")
|
||||
or request_method.is_case_insensitive_equal ("PATCH")
|
||||
end
|
||||
|
||||
create l_upload_file.make_with_name (l_upload_filename)
|
||||
|
||||
@@ -31,7 +31,7 @@ feature -- Status report
|
||||
Result := curl.is_dynamic_library_exists
|
||||
end
|
||||
|
||||
feature -- Basic operation
|
||||
feature -- Custom
|
||||
|
||||
custom (a_method: READABLE_STRING_8; a_path: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): HTTP_CLIENT_RESPONSE
|
||||
local
|
||||
@@ -41,6 +41,18 @@ feature -- Basic operation
|
||||
Result := req.execute
|
||||
end
|
||||
|
||||
custom_with_upload_data (a_method: READABLE_STRING_8; a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; data: READABLE_STRING_8): HTTP_CLIENT_RESPONSE
|
||||
do
|
||||
Result := impl_custom (a_method, a_path, a_ctx, data, Void)
|
||||
end
|
||||
|
||||
custom_with_upload_file (a_method: READABLE_STRING_8; a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; fn: READABLE_STRING_8): HTTP_CLIENT_RESPONSE
|
||||
do
|
||||
Result := impl_custom (a_method, a_path, a_ctx, Void, fn)
|
||||
end
|
||||
|
||||
feature -- Helper
|
||||
|
||||
get (a_path: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): HTTP_CLIENT_RESPONSE
|
||||
do
|
||||
Result := custom ("GET", a_path, ctx)
|
||||
@@ -53,63 +65,32 @@ feature -- Basic operation
|
||||
|
||||
post (a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; data: detachable READABLE_STRING_8): HTTP_CLIENT_RESPONSE
|
||||
do
|
||||
Result := impl_post (a_path, a_ctx, data, Void)
|
||||
Result := impl_custom ("POST", a_path, a_ctx, data, Void)
|
||||
end
|
||||
|
||||
post_file (a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; fn: detachable READABLE_STRING_8): HTTP_CLIENT_RESPONSE
|
||||
do
|
||||
Result := impl_post (a_path, a_ctx, Void, fn)
|
||||
Result := impl_custom ("POST", a_path, a_ctx, Void, fn)
|
||||
end
|
||||
|
||||
patch (a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; data: detachable READABLE_STRING_8): HTTP_CLIENT_RESPONSE
|
||||
do
|
||||
Result := impl_custom ("PATCH", a_path, a_ctx, data, Void)
|
||||
end
|
||||
|
||||
patch_file (a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; fn: detachable READABLE_STRING_8): HTTP_CLIENT_RESPONSE
|
||||
do
|
||||
Result := impl_custom ("PATCH", a_path, a_ctx, Void, fn)
|
||||
end
|
||||
|
||||
put (a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; data: detachable READABLE_STRING_8): HTTP_CLIENT_RESPONSE
|
||||
local
|
||||
ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT
|
||||
f: detachable RAW_FILE
|
||||
l_data: detachable READABLE_STRING_8
|
||||
do
|
||||
--| Quick and dirty hack using real file, for PUT uploaded data
|
||||
--| FIXME [2012-05-23]: better use libcurl for that purpose
|
||||
ctx := a_ctx
|
||||
if data /= Void then
|
||||
if ctx = Void then
|
||||
create ctx.make
|
||||
end
|
||||
ctx.set_upload_data (data)
|
||||
end
|
||||
if ctx /= Void then
|
||||
l_data := ctx.upload_data
|
||||
end
|
||||
if l_data /= Void then
|
||||
create f.make_open_write (create {FILE_NAME}.make_temporary_name)
|
||||
f.put_string (l_data)
|
||||
f.close
|
||||
check ctx /= Void then
|
||||
ctx.set_upload_data (Void)
|
||||
ctx.set_upload_filename (f.path.name)
|
||||
end
|
||||
end
|
||||
Result := custom ("PUT", a_path, ctx)
|
||||
if f /= Void then
|
||||
f.delete
|
||||
end
|
||||
if l_data /= Void and a_ctx /= Void then
|
||||
a_ctx.set_upload_filename (Void)
|
||||
a_ctx.set_upload_data (l_data)
|
||||
end
|
||||
Result := impl_custom ("PUT", a_path, a_ctx, data, Void)
|
||||
end
|
||||
|
||||
put_file (a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; fn: detachable READABLE_STRING_8): HTTP_CLIENT_RESPONSE
|
||||
local
|
||||
ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT
|
||||
do
|
||||
ctx := a_ctx
|
||||
if fn /= Void then
|
||||
if ctx = Void then
|
||||
create ctx.make
|
||||
end
|
||||
ctx.set_upload_filename (fn)
|
||||
end
|
||||
Result := custom ("PUT", a_path, ctx)
|
||||
Result := impl_custom ("PUT", a_path, a_ctx, Void, fn)
|
||||
end
|
||||
|
||||
delete (a_path: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): HTTP_CLIENT_RESPONSE
|
||||
@@ -119,10 +100,12 @@ feature -- Basic operation
|
||||
|
||||
feature {NONE} -- Implementation
|
||||
|
||||
impl_post (a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; data: detachable READABLE_STRING_8; fn: detachable READABLE_STRING_8): HTTP_CLIENT_RESPONSE
|
||||
impl_custom (a_method: READABLE_STRING_8; a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; data: detachable READABLE_STRING_8; fn: detachable READABLE_STRING_8): HTTP_CLIENT_RESPONSE
|
||||
local
|
||||
req: HTTP_CLIENT_REQUEST
|
||||
ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT
|
||||
f: detachable RAW_FILE
|
||||
l_data: detachable READABLE_STRING_8
|
||||
do
|
||||
ctx := a_ctx
|
||||
if data /= Void then
|
||||
@@ -137,8 +120,35 @@ feature {NONE} -- Implementation
|
||||
end
|
||||
ctx.set_upload_filename (fn)
|
||||
end
|
||||
create {LIBCURL_HTTP_CLIENT_REQUEST} req.make (base_url + a_path, "POST", Current, ctx)
|
||||
if ctx /= Void then
|
||||
l_data := ctx.upload_data
|
||||
if l_data /= Void and a_method.is_case_insensitive_equal_general ("PUT") then
|
||||
--| Quick and dirty hack using real file, for PUT uploaded data
|
||||
--| FIXME [2012-05-23]: better use libcurl for that purpose
|
||||
|
||||
if ctx.has_upload_filename then
|
||||
check put_conflict_file_and_data: False end
|
||||
end
|
||||
create f.make_open_write (create {FILE_NAME}.make_temporary_name)
|
||||
f.put_string (l_data)
|
||||
f.close
|
||||
check ctx /= Void then
|
||||
ctx.set_upload_data (Void)
|
||||
ctx.set_upload_filename (f.path.name)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
create {LIBCURL_HTTP_CLIENT_REQUEST} req.make (base_url + a_path, a_method, Current, ctx)
|
||||
Result := req.execute
|
||||
|
||||
if f /= Void then
|
||||
f.delete
|
||||
end
|
||||
if l_data /= Void and a_ctx /= Void then
|
||||
a_ctx.set_upload_filename (Void)
|
||||
a_ctx.set_upload_data (l_data)
|
||||
end
|
||||
end
|
||||
|
||||
feature {LIBCURL_HTTP_CLIENT_REQUEST} -- Curl implementation
|
||||
|
||||
@@ -8,10 +8,14 @@
|
||||
<exclude>/.svn$</exclude>
|
||||
</file_rule>
|
||||
<option warning="true" full_class_checking="true" is_attached_by_default="true" void_safety="all">
|
||||
<assertions precondition="true"/>
|
||||
<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_client" location="..\http_client-safe.ecf" readonly="false"/>
|
||||
<library name="http_client" location="..\http_client-safe.ecf" readonly="false" use_application_options="true">
|
||||
<option>
|
||||
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
|
||||
</option>
|
||||
</library>
|
||||
<library name="testing" location="$ISE_LIBRARY\library\testing\testing-safe.ecf"/>
|
||||
<tests name="tests" location=".\"/>
|
||||
</target>
|
||||
|
||||
@@ -1,125 +0,0 @@
|
||||
CONNEG is a library that provides utilities to select the best repesentation of a resource for a client
|
||||
where there are multiple representations available.
|
||||
|
||||
Using this labrary you can retrieve the best Variant for media type, language preference, enconding and compression.
|
||||
The library is based on eMIME Eiffel MIME library based on Joe Gregorio code
|
||||
|
||||
Take into account that the library is under development so is expected that the API change.
|
||||
|
||||
The library contains utilities that deal with content negotiation (server driven negotiation).This utility class
|
||||
is based on ideas taken from the Book Restful WebServices Cookbook
|
||||
|
||||
The class CONNEG_SERVER_SIDE contains several features that helps to write different type of negotiations (media type, language,
|
||||
charset and compression).
|
||||
So for each of the following questions, you will have a corresponding method to help in the solution.
|
||||
|
||||
- How to implement Media type negotiation?
|
||||
Hint: Use CONNEG_SERVER_SIDE.media_type_preference
|
||||
|
||||
- How to implement Language Negotiation?
|
||||
Hint: Use CONNEG_SERVER_SIDE.language_preference
|
||||
|
||||
- How to implement Character encoding Negotiation?
|
||||
Hint: Use CONNEG_SERVER_SIDE.charset_preference
|
||||
|
||||
- How to implement Compression Negotiation?
|
||||
Hint: Use CONNEG_SERVER_SIDE.encoding_preference
|
||||
|
||||
There is also a [test case](test/conneg_server_side_test.e "conneg_server_side_test") where you can check how to use this class.
|
||||
|
||||
note
|
||||
description: "Summary description for CONNEG_SERVER_SIDE. Utility class to support Server Side Content Negotiation "
|
||||
author: ""
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
description: "[
|
||||
Reference : http://www.w3.org/Protocols/rfc2616/rfc2616-sec12.html#sec12.1
|
||||
Server-driven Negotiation : If the selection of the best representation for a response is made by an algorithm located at the server,
|
||||
it is called server-driven negotiation. Selection is based on the available representations of the response (the dimensions over which it can vary; e.g. language, content-coding, etc.)
|
||||
and the contents of particular header fields in the request message or on other information pertaining to the request (such as the network address of the client).
|
||||
Server-driven negotiation is advantageous when the algorithm for selecting from among the available representations is difficult to describe to the user agent,
|
||||
or when the server desires to send its "best guess" to the client along with the first response (hoping to avoid the round-trip delay of a subsequent request if the "best guess" is good enough for the user).
|
||||
In order to improve the server's guess, the user agent MAY include request header fields (Accept, Accept-Language, Accept-Encoding, etc.) which describe its preferences for such a response.
|
||||
]"
|
||||
|
||||
class interface
|
||||
CONNEG_SERVER_SIDE
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature -- Initialization
|
||||
|
||||
make (a_mime: STRING_8; a_language: STRING_8; a_charset: STRING_8; an_encoding: STRING_8)
|
||||
|
||||
feature -- Compression Negotiation
|
||||
|
||||
encoding_preference (server_encoding_supported: LIST [STRING_8]; header: STRING_8): COMPRESSION_VARIANT_RESULTS
|
||||
-- server_encoding_supported represent a list of encoding supported by the server.
|
||||
-- header represent the Accept-Encoding header, ie, the client preferences.
|
||||
-- Return which Encoding to use in a response, if the server support
|
||||
-- one Encoding, or empty in other case.
|
||||
-- Representation: http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.3
|
||||
|
||||
feature -- Encoding Negotiation
|
||||
|
||||
charset_preference (server_charset_supported: LIST [STRING_8]; header: STRING_8): CHARACTER_ENCODING_VARIANT_RESULTS
|
||||
-- server_charset_supported represent a list of charset supported by the server.
|
||||
-- header represent the Accept-Charset header, ie, the client preferences.
|
||||
-- Return which Charset to use in a response, if the server support
|
||||
-- one Charset, or empty in other case.
|
||||
-- Reference: http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.2
|
||||
|
||||
feature -- Language Negotiation
|
||||
|
||||
language_preference (server_language_supported: LIST [STRING_8]; header: STRING_8): LANGUAGE_VARIANT_RESULTS
|
||||
-- server_language_supported represent a list of languages supported by the server.
|
||||
-- header represent the Accept-Language header, ie, the client preferences.
|
||||
-- Return which Language to use in a response, if the server support
|
||||
-- one Language, or empty in other case.
|
||||
-- Reference: http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.4
|
||||
|
||||
feature -- Media Type Negotiation
|
||||
|
||||
media_type_preference (mime_types_supported: LIST [STRING_8]; header: STRING_8): MEDIA_TYPE_VARIANT_RESULTS
|
||||
-- mime_types_supported represent media types supported by the server.
|
||||
-- header represent the Accept header, ie, the client preferences.
|
||||
-- Return which media type to use for representaion in a response, if the server support
|
||||
-- one media type, or empty in other case.
|
||||
-- Reference : http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.1
|
||||
|
||||
feature -- Server Side Defaults Formats
|
||||
|
||||
charset_default: STRING_8
|
||||
|
||||
encoding_default: STRING_8
|
||||
|
||||
language_default: STRING_8
|
||||
|
||||
mime_default: STRING_8
|
||||
|
||||
set_charset_default (a_charset: STRING_8)
|
||||
-- set the charset_default with `a_charset'
|
||||
ensure
|
||||
set_charset: a_charset ~ charset_default
|
||||
|
||||
set_encoding_defautl (an_encoding: STRING_8)
|
||||
ensure
|
||||
set_encoding: an_encoding ~ encoding_default
|
||||
|
||||
set_language_default (a_language: STRING_8)
|
||||
-- set the language_default with `a_language'
|
||||
ensure
|
||||
set_language: a_language ~ language_default
|
||||
|
||||
set_mime_default (a_mime: STRING_8)
|
||||
-- set the mime_default with `a_mime'
|
||||
ensure
|
||||
set_mime_default: a_mime ~ mime_default
|
||||
|
||||
note
|
||||
copyright: "2011-2011, Javier Velilla, Jocelyn Fiat and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
|
||||
end -- class CONNEG_SERVER_SIDE
|
||||
|
||||
@@ -1,79 +0,0 @@
|
||||
#!/usr/bin/env ruby
|
||||
# Niklaus Giger, 15.01.2011
|
||||
# Small ruby-script run all tests using ec (the Eiffel compiler)
|
||||
# we assumen that ec outputs everything in english!
|
||||
|
||||
# For the command line options look at
|
||||
# http://docs.eiffel.com/book/eiffelstudio/eiffelstudio-command-line-options
|
||||
# we use often the -batch open.
|
||||
#
|
||||
# TODO: Fix problems when compiling takes too long and/or there
|
||||
# are ec process lingering around from a previous failed build
|
||||
|
||||
require 'tempfile'
|
||||
require 'fileutils'
|
||||
|
||||
# Override system command.
|
||||
# run command. if not successful, complain and exit with error
|
||||
def system(cmd)
|
||||
puts cmd
|
||||
res = Kernel.system(cmd)
|
||||
if !res
|
||||
puts "Failed running: #{cmd}"
|
||||
exit 2
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def runTestForProject(where)
|
||||
if !File.directory?(where)
|
||||
puts "Directory #{where} does not exist"
|
||||
exit 2
|
||||
end
|
||||
|
||||
# create a temporary file with input for the
|
||||
# interactive mode of ec
|
||||
commands2run=<<EOF
|
||||
T
|
||||
E
|
||||
q
|
||||
EOF
|
||||
file = Tempfile.new('commands2run')
|
||||
file.puts commands2run
|
||||
file.close
|
||||
|
||||
Dir.chdir(where)
|
||||
# First we have to remove old compilation
|
||||
FileUtils.rm_rf("EIFGENs")
|
||||
|
||||
# compile the library
|
||||
cmd = "ec -config library/emime-safe.ecf -target emime -batch -c_compile"
|
||||
res = system(cmd)
|
||||
|
||||
# compile the test
|
||||
cmd = "ec -config test/test-safe.ecf -target test -batch -c_compile"
|
||||
res = system(cmd)
|
||||
|
||||
|
||||
logFile = "#{__FILE__}.log"
|
||||
sleep 1
|
||||
cmd = "ec -config test/test-safe.ecf -target test -batch -loop 1>#{logFile} 2>#{__FILE__}.auto_test_output <#{file.path}"
|
||||
res = system(cmd)
|
||||
m= nil
|
||||
IO.readlines(logFile).each{
|
||||
|line|
|
||||
m = /(\d+) tests total \((\d+) executed, (\d+) failing, (\d+) unresolved/.match(line)
|
||||
break if m
|
||||
}
|
||||
|
||||
puts
|
||||
if m[3].to_i == 0 and m[4].to_i == 0 then
|
||||
puts "#{m[1]} tests completed successfully"
|
||||
else
|
||||
puts "Failures while running #{m[1]} failed. #{m[2]} executed #{m[3]} failures #{m[4]} unresolved"
|
||||
exit 2
|
||||
end
|
||||
end
|
||||
|
||||
runTestForProject(Dir.pwd)
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
note
|
||||
description: "Summary description for {CHARACTER_ENCODING_VARIANT_RESULTS}."
|
||||
author: ""
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
CHARACTER_ENCODING_VARIANT_RESULTS
|
||||
feature
|
||||
character_type : detachable STRING
|
||||
set_character_type ( a_character_type: STRING)
|
||||
do
|
||||
character_type := a_character_type
|
||||
ensure
|
||||
set_character_type : a_character_type ~ character_type
|
||||
end
|
||||
|
||||
variant_header : detachable STRING
|
||||
set_variant_header
|
||||
do
|
||||
variant_header := "Accept-Charset"
|
||||
end
|
||||
|
||||
supported_variants : detachable LIST[STRING]
|
||||
set_supported_variants (a_supported : LIST[STRING])
|
||||
do
|
||||
supported_variants := a_supported
|
||||
ensure
|
||||
set_supported_variants : supported_variants = a_supported
|
||||
end
|
||||
|
||||
is_acceptable : BOOLEAN
|
||||
|
||||
set_acceptable ( acceptable : BOOLEAN)
|
||||
do
|
||||
is_acceptable := acceptable
|
||||
ensure
|
||||
is_acceptable = acceptable
|
||||
end
|
||||
|
||||
|
||||
note
|
||||
copyright: "2011-2011, Javier Velilla, Jocelyn Fiat and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
end
|
||||
@@ -1,282 +0,0 @@
|
||||
note
|
||||
description: "COMMON_ACCEPT_HEADER_PARSER, this class allows to parse Accept-Charset and Accept-Encoding headers"
|
||||
author: ""
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
description : "[
|
||||
Charset Reference : http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.2
|
||||
Encoding Reference : http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.3
|
||||
]"
|
||||
|
||||
class
|
||||
COMMON_ACCEPT_HEADER_PARSER
|
||||
|
||||
|
||||
feature -- Parser
|
||||
parse_common (header: STRING): COMMON_RESULTS
|
||||
-- Parses `header' charset/encoding into its component parts.
|
||||
-- For example, the charset 'iso-8889-5' would get parsed
|
||||
-- into:
|
||||
-- ('iso-8889-5', {'q':'1.0'})
|
||||
local
|
||||
l_parts: LIST [STRING]
|
||||
sub_parts: LIST [STRING]
|
||||
p: STRING
|
||||
i: INTEGER
|
||||
l_header: STRING
|
||||
do
|
||||
create Result.make
|
||||
l_parts := header.split (';')
|
||||
if l_parts.count = 1 then
|
||||
Result.put ("1.0", "q")
|
||||
else
|
||||
from
|
||||
i := 1
|
||||
until
|
||||
i > l_parts.count
|
||||
loop
|
||||
p := l_parts.at (i)
|
||||
sub_parts := p.split ('=')
|
||||
if sub_parts.count = 2 then
|
||||
Result.put (trim (sub_parts[2]), trim (sub_parts[1]))
|
||||
end
|
||||
i := i + 1
|
||||
end
|
||||
end
|
||||
l_header := trim (l_parts[1])
|
||||
Result.set_field (trim (l_header))
|
||||
end
|
||||
|
||||
|
||||
fitness_and_quality_parsed (a_field: STRING; parsed_charsets: LIST [COMMON_RESULTS]): FITNESS_AND_QUALITY
|
||||
-- Find the best match for a given charset/encoding against a list of charsets/encodings
|
||||
-- that have already been parsed by parse_common. Returns a
|
||||
-- tuple of the fitness value and the value of the 'q' quality parameter of
|
||||
-- the best match, or (-1, 0) if no match was found. Just as for
|
||||
-- quality_parsed().
|
||||
local
|
||||
best_fitness: INTEGER
|
||||
target_q: REAL_64
|
||||
best_fit_q: REAL_64
|
||||
target: COMMON_RESULTS
|
||||
range: COMMON_RESULTS
|
||||
element: detachable STRING
|
||||
l_fitness: INTEGER
|
||||
do
|
||||
best_fitness := -1
|
||||
best_fit_q := 0.0
|
||||
target := parse_common(a_field)
|
||||
if attached target.item ("q") as q and then q.is_double then
|
||||
target_q := q.to_double
|
||||
if target_q < 0.0 then
|
||||
target_q := 0.0
|
||||
elseif target_q > 1.0 then
|
||||
target_q := 1.0
|
||||
end
|
||||
else
|
||||
target_q := 1.0
|
||||
end
|
||||
|
||||
if attached target.field as l_target_field
|
||||
then
|
||||
from
|
||||
parsed_charsets.start
|
||||
until
|
||||
parsed_charsets.after
|
||||
loop
|
||||
range := parsed_charsets.item_for_iteration
|
||||
if attached range.field as l_range_common then
|
||||
if l_target_field.same_string (l_range_common) or l_target_field.same_string ("*") or l_range_common.same_string ("*") then
|
||||
if l_range_common.same_string (l_target_field) then
|
||||
l_fitness := 100
|
||||
else
|
||||
l_fitness := 0
|
||||
end
|
||||
if l_fitness > best_fitness then
|
||||
best_fitness := l_fitness
|
||||
element := range.item ("q")
|
||||
if element /= Void then
|
||||
best_fit_q := element.to_double.min (target_q)
|
||||
else
|
||||
best_fit_q := 0.0
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
parsed_charsets.forth
|
||||
end
|
||||
end
|
||||
create Result.make (best_fitness, best_fit_q)
|
||||
end
|
||||
|
||||
|
||||
quality_parsed (a_field: STRING; parsed_common: LIST [COMMON_RESULTS]): REAL_64
|
||||
-- Find the best match for a given charset/encoding against a list of charsets/encodings that
|
||||
-- have already been parsed by parse_charsets(). Returns the 'q' quality
|
||||
-- parameter of the best match, 0 if no match was found. This function
|
||||
-- bahaves the same as quality()
|
||||
do
|
||||
Result := fitness_and_quality_parsed (a_field, parsed_common).quality
|
||||
end
|
||||
|
||||
|
||||
quality (a_field: STRING; commons: STRING): REAL_64
|
||||
-- Returns the quality 'q' of a charset/encoding when compared against the
|
||||
-- a list of charsets/encodings/
|
||||
local
|
||||
l_commons : LIST [STRING]
|
||||
res : ARRAYED_LIST [COMMON_RESULTS]
|
||||
p_res : COMMON_RESULTS
|
||||
do
|
||||
l_commons := commons.split (',')
|
||||
from
|
||||
create res.make (10);
|
||||
l_commons.start
|
||||
until
|
||||
l_commons.after
|
||||
loop
|
||||
p_res := parse_common (l_commons.item_for_iteration)
|
||||
res.put_left (p_res)
|
||||
l_commons.forth
|
||||
end
|
||||
Result := quality_parsed (a_field, res)
|
||||
end
|
||||
|
||||
best_match (supported: LIST [STRING]; header: STRING): STRING
|
||||
-- Choose the accept with the highest fitness score and quality ('q') from a list of candidates.
|
||||
local
|
||||
l_header_results: LIST [COMMON_RESULTS]
|
||||
weighted_matches: LIST [FITNESS_AND_QUALITY]
|
||||
l_res: LIST [STRING]
|
||||
p_res: COMMON_RESULTS
|
||||
fitness_and_quality, first_one: detachable FITNESS_AND_QUALITY
|
||||
do
|
||||
l_res := header.split (',')
|
||||
create {ARRAYED_LIST [COMMON_RESULTS]} l_header_results.make (l_res.count)
|
||||
|
||||
|
||||
from
|
||||
l_res.start
|
||||
until
|
||||
l_res.after
|
||||
loop
|
||||
p_res := parse_common (l_res.item_for_iteration)
|
||||
l_header_results.force (p_res)
|
||||
l_res.forth
|
||||
end
|
||||
|
||||
create {ARRAYED_LIST [FITNESS_AND_QUALITY]} weighted_matches.make (supported.count)
|
||||
|
||||
from
|
||||
supported.start
|
||||
until
|
||||
supported.after
|
||||
loop
|
||||
fitness_and_quality := fitness_and_quality_parsed (supported.item_for_iteration, l_header_results)
|
||||
fitness_and_quality.set_mime_type (mime_type (supported.item_for_iteration))
|
||||
weighted_matches.force (fitness_and_quality)
|
||||
supported.forth
|
||||
end
|
||||
|
||||
--| Keep only top quality+fitness types
|
||||
--| TODO extract method
|
||||
from
|
||||
weighted_matches.start
|
||||
first_one := weighted_matches.item
|
||||
weighted_matches.forth
|
||||
until
|
||||
weighted_matches.after
|
||||
loop
|
||||
fitness_and_quality := weighted_matches.item
|
||||
if first_one < fitness_and_quality then
|
||||
first_one := fitness_and_quality
|
||||
if not weighted_matches.isfirst then
|
||||
from
|
||||
weighted_matches.back
|
||||
until
|
||||
weighted_matches.before
|
||||
loop
|
||||
weighted_matches.remove
|
||||
weighted_matches.back
|
||||
end
|
||||
weighted_matches.forth
|
||||
end
|
||||
check weighted_matches.item = fitness_and_quality end
|
||||
weighted_matches.forth
|
||||
elseif first_one.is_equal (fitness_and_quality) then
|
||||
weighted_matches.forth
|
||||
else
|
||||
check first_one > fitness_and_quality end
|
||||
weighted_matches.remove
|
||||
end
|
||||
end
|
||||
if first_one /= Void and then first_one.quality /= 0.0 then
|
||||
if weighted_matches.count = 1 then
|
||||
Result := first_one.mime_type
|
||||
else
|
||||
from
|
||||
fitness_and_quality := Void
|
||||
l_header_results.start
|
||||
until
|
||||
l_header_results.after or fitness_and_quality /= Void
|
||||
loop
|
||||
if attached l_header_results.item.field as l_field then
|
||||
from
|
||||
weighted_matches.start
|
||||
until
|
||||
weighted_matches.after or fitness_and_quality /= Void
|
||||
loop
|
||||
fitness_and_quality := weighted_matches.item
|
||||
if fitness_and_quality.mime_type.same_string (l_field) then
|
||||
--| Found
|
||||
else
|
||||
fitness_and_quality := Void
|
||||
weighted_matches.forth
|
||||
end
|
||||
end
|
||||
else
|
||||
check has_field: False end
|
||||
end
|
||||
l_header_results.forth
|
||||
end
|
||||
if fitness_and_quality /= Void then
|
||||
Result := fitness_and_quality.mime_type
|
||||
else
|
||||
Result := first_one.mime_type
|
||||
end
|
||||
end
|
||||
else
|
||||
Result := ""
|
||||
end
|
||||
end
|
||||
|
||||
feature -- Util
|
||||
|
||||
mime_type (s: STRING): STRING
|
||||
local
|
||||
p: INTEGER
|
||||
do
|
||||
p := s.index_of (';', 1)
|
||||
if p > 0 then
|
||||
Result := trim (s.substring (1, p - 1))
|
||||
else
|
||||
Result := trim (s.string)
|
||||
end
|
||||
end
|
||||
|
||||
trim (a_string: STRING): STRING
|
||||
-- trim whitespace from the beginning and end of a string
|
||||
require
|
||||
valid_argument : a_string /= Void
|
||||
do
|
||||
a_string.left_adjust
|
||||
a_string.right_justify
|
||||
Result := a_string
|
||||
ensure
|
||||
result_same_as_argument: a_string = Result
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2011, Javier Velilla, Jocelyn Fiat and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
end
|
||||
@@ -1,119 +0,0 @@
|
||||
note
|
||||
description: "Summary description for {COMMON_RESULTS}."
|
||||
author: ""
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
COMMON_RESULTS
|
||||
inherit
|
||||
ANY
|
||||
redefine
|
||||
out
|
||||
end
|
||||
|
||||
DEBUG_OUTPUT
|
||||
redefine
|
||||
out
|
||||
end
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature -- Initialization
|
||||
|
||||
make
|
||||
do
|
||||
create params.make (2)
|
||||
end
|
||||
|
||||
feature -- Access
|
||||
|
||||
field: detachable STRING
|
||||
|
||||
|
||||
item (a_key: STRING): detachable STRING
|
||||
-- Item associated with `a_key', if present
|
||||
-- otherwise default value of type `STRING'
|
||||
do
|
||||
Result := params.item (a_key)
|
||||
end
|
||||
|
||||
keys: LIST [STRING]
|
||||
-- arrays of currents keys
|
||||
local
|
||||
res: ARRAYED_LIST [STRING]
|
||||
do
|
||||
create res.make_from_array (params.current_keys)
|
||||
Result := res
|
||||
end
|
||||
|
||||
has_key (a_key: STRING): BOOLEAN
|
||||
-- Is there an item in the table with key `a_key'?
|
||||
do
|
||||
Result := params.has_key (a_key)
|
||||
end
|
||||
|
||||
feature -- Element change
|
||||
|
||||
set_field (a_field: STRING)
|
||||
-- Set type with `a_charset'
|
||||
do
|
||||
field := a_field
|
||||
ensure
|
||||
field_assigned: field ~ field
|
||||
end
|
||||
|
||||
|
||||
put (new: STRING; key: STRING)
|
||||
-- Insert `new' with `key' if there is no other item
|
||||
-- associated with the same key. If present, replace
|
||||
-- the old value with `new'
|
||||
do
|
||||
if params.has_key (key) then
|
||||
params.replace (new, key)
|
||||
else
|
||||
params.force (new, key)
|
||||
end
|
||||
ensure
|
||||
has_key: params.has_key (key)
|
||||
has_item: params.has_item (new)
|
||||
end
|
||||
|
||||
feature -- Status Report
|
||||
|
||||
out: STRING
|
||||
-- Representation of the current object
|
||||
do
|
||||
create Result.make_from_string ("(")
|
||||
if attached field as t then
|
||||
Result.append_string ("'" + t + "',")
|
||||
end
|
||||
Result.append_string (" {")
|
||||
|
||||
from
|
||||
params.start
|
||||
until
|
||||
params.after
|
||||
loop
|
||||
Result.append ("'" + params.key_for_iteration + "':'" + params.item_for_iteration + "',");
|
||||
params.forth
|
||||
end
|
||||
Result.append ("})")
|
||||
end
|
||||
|
||||
debug_output: STRING
|
||||
-- String that should be displayed in debugger to represent `Current'.
|
||||
do
|
||||
Result := out
|
||||
end
|
||||
|
||||
feature {NONE} -- Implementation
|
||||
|
||||
params: HASH_TABLE [STRING, STRING]
|
||||
--dictionary of all the parameters for the media range
|
||||
|
||||
;note
|
||||
copyright: "2011-2011, Javier Velilla, Jocelyn Fiat and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
end
|
||||
@@ -1,44 +0,0 @@
|
||||
note
|
||||
description: "Summary description for {COMPRESSION_VARIANT_RESULTS}."
|
||||
author: ""
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
COMPRESSION_VARIANT_RESULTS
|
||||
|
||||
feature
|
||||
compression_type : detachable STRING
|
||||
set_compression_type ( a_compression_type: STRING)
|
||||
do
|
||||
compression_type := a_compression_type
|
||||
ensure
|
||||
set_compression_type : a_compression_type ~ compression_type
|
||||
end
|
||||
|
||||
variant_header : detachable STRING
|
||||
set_variant_header
|
||||
do
|
||||
variant_header := "Accept-Encoding"
|
||||
end
|
||||
|
||||
supported_variants : detachable LIST[STRING]
|
||||
set_supported_variants (a_supported : LIST[STRING])
|
||||
do
|
||||
supported_variants := a_supported
|
||||
ensure
|
||||
set_supported_variants : supported_variants = a_supported
|
||||
end
|
||||
|
||||
is_acceptable : BOOLEAN
|
||||
|
||||
set_acceptable ( acceptable : BOOLEAN)
|
||||
do
|
||||
is_acceptable := acceptable
|
||||
ensure
|
||||
is_acceptable = acceptable
|
||||
end
|
||||
note
|
||||
copyright: "2011-2011, Javier Velilla, Jocelyn Fiat and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
end
|
||||
@@ -1,216 +0,0 @@
|
||||
note
|
||||
description: "Summary description for {CONNEG_SERVER_SIDE}. Utility class to support Server Side Content Negotiation "
|
||||
author: ""
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
description: "[
|
||||
Reference : http://www.w3.org/Protocols/rfc2616/rfc2616-sec12.html#sec12.1
|
||||
Server-driven Negotiation : If the selection of the best representation for a response is made by an algorithm located at the server,
|
||||
it is called server-driven negotiation. Selection is based on the available representations of the response (the dimensions over which it can vary; e.g. language, content-coding, etc.)
|
||||
and the contents of particular header fields in the request message or on other information pertaining to the request (such as the network address of the client).
|
||||
Server-driven negotiation is advantageous when the algorithm for selecting from among the available representations is difficult to describe to the user agent,
|
||||
or when the server desires to send its "best guess" to the client along with the first response (hoping to avoid the round-trip delay of a subsequent request if the "best guess" is good enough for the user).
|
||||
In order to improve the server's guess, the user agent MAY include request header fields (Accept, Accept-Language, Accept-Encoding, etc.) which describe its preferences for such a response.
|
||||
]"
|
||||
class
|
||||
CONNEG_SERVER_SIDE
|
||||
|
||||
inherit
|
||||
SHARED_CONNEG
|
||||
REFACTORING_HELPER
|
||||
create
|
||||
make
|
||||
|
||||
feature -- Initialization
|
||||
make ( a_mime: STRING; a_language : STRING; a_charset :STRING; an_encoding: STRING)
|
||||
do
|
||||
set_mime_default (a_mime)
|
||||
set_language_default (a_language)
|
||||
set_charset_default (a_charset)
|
||||
set_encoding_defautl (an_encoding)
|
||||
end
|
||||
feature -- Server Side Defaults Formats
|
||||
mime_default : STRING
|
||||
|
||||
set_mime_default ( a_mime: STRING)
|
||||
-- set the mime_default with `a_mime'
|
||||
do
|
||||
mime_default := a_mime
|
||||
ensure
|
||||
set_mime_default: a_mime ~ mime_default
|
||||
end
|
||||
|
||||
|
||||
language_default : STRING
|
||||
|
||||
set_language_default (a_language : STRING)
|
||||
-- set the language_default with `a_language'
|
||||
do
|
||||
language_default := a_language
|
||||
ensure
|
||||
set_language : a_language ~ language_default
|
||||
end
|
||||
|
||||
|
||||
charset_default : STRING
|
||||
|
||||
set_charset_default (a_charset : STRING)
|
||||
-- set the charset_default with `a_charset'
|
||||
do
|
||||
charset_default := a_charset
|
||||
ensure
|
||||
set_charset : a_charset ~ charset_default
|
||||
end
|
||||
|
||||
|
||||
encoding_default : STRING
|
||||
|
||||
set_encoding_defautl (an_encoding : STRING)
|
||||
do
|
||||
encoding_default := an_encoding
|
||||
ensure
|
||||
set_encoding : an_encoding ~ encoding_default
|
||||
end
|
||||
|
||||
|
||||
|
||||
feature -- Media Type Negotiation
|
||||
|
||||
media_type_preference ( mime_types_supported : LIST[STRING]; header: detachable READABLE_STRING_8) : MEDIA_TYPE_VARIANT_RESULTS
|
||||
-- mime_types_supported represent media types supported by the server.
|
||||
-- header represent the Accept header, ie, the client preferences.
|
||||
-- Return which media type to use for representaion in a response, if the server support
|
||||
-- one media type, or empty in other case.
|
||||
-- Reference : http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.1
|
||||
local
|
||||
mime_match: STRING
|
||||
do
|
||||
create Result
|
||||
if header = Void or else header.is_empty then
|
||||
-- the request has no Accept header, ie the header is empty, in this case we use the default format
|
||||
Result.set_acceptable (TRUE)
|
||||
Result.set_media_type (mime_default)
|
||||
else
|
||||
-- select the best match, server support, client preferences
|
||||
mime_match := mime.best_match (mime_types_supported, header)
|
||||
if mime_match.is_empty then
|
||||
-- The server does not support any of the media types prefered by the client
|
||||
Result.set_acceptable (False)
|
||||
Result.set_supported_variants (mime_types_supported)
|
||||
else
|
||||
-- Set the best match
|
||||
Result.set_media_type(mime_match)
|
||||
Result.set_acceptable (True)
|
||||
Result.set_variant_header
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
feature -- Encoding Negotiation
|
||||
|
||||
charset_preference (server_charset_supported : LIST[STRING]; header: detachable READABLE_STRING_8) : CHARACTER_ENCODING_VARIANT_RESULTS
|
||||
-- server_charset_supported represent a list of charset supported by the server.
|
||||
-- header represent the Accept-Charset header, ie, the client preferences.
|
||||
-- Return which Charset to use in a response, if the server support
|
||||
-- one Charset, or empty in other case.
|
||||
-- Reference: http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.2
|
||||
local
|
||||
charset_match : STRING
|
||||
do
|
||||
create Result
|
||||
if header = Void or else header.is_empty then
|
||||
-- the request has no Accept-Charset header, ie the header is empty, in this case use default charset encoding
|
||||
-- (UTF-8)
|
||||
Result.set_acceptable (TRUE)
|
||||
Result.set_character_type (charset_default)
|
||||
else
|
||||
-- select the best match, server support, client preferences
|
||||
charset_match := common.best_match (server_charset_supported, header)
|
||||
if charset_match.is_empty then
|
||||
-- The server does not support any of the compression types prefered by the client
|
||||
Result.set_acceptable (False)
|
||||
Result.set_supported_variants (server_charset_supported)
|
||||
else
|
||||
-- Set the best match
|
||||
Result.set_character_type(charset_match)
|
||||
Result.set_acceptable (True)
|
||||
Result.set_variant_header
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
feature -- Compression Negotiation
|
||||
|
||||
encoding_preference (server_encoding_supported : LIST[STRING]; header: detachable READABLE_STRING_8) : COMPRESSION_VARIANT_RESULTS
|
||||
-- server_encoding_supported represent a list of encoding supported by the server.
|
||||
-- header represent the Accept-Encoding header, ie, the client preferences.
|
||||
-- Return which Encoding to use in a response, if the server support
|
||||
-- one Encoding, or empty in other case.
|
||||
-- Representation: http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.3
|
||||
local
|
||||
compression_match : STRING
|
||||
do
|
||||
create Result
|
||||
if header = Void or else header.is_empty then
|
||||
-- the request has no Accept-Encoding header, ie the header is empty, in this case do not compress representations
|
||||
Result.set_acceptable (TRUE)
|
||||
Result.set_compression_type (encoding_default)
|
||||
else
|
||||
-- select the best match, server support, client preferences
|
||||
compression_match := common.best_match (server_encoding_supported, header)
|
||||
if compression_match.is_empty then
|
||||
-- The server does not support any of the compression types prefered by the client
|
||||
Result.set_acceptable (False)
|
||||
Result.set_supported_variants (server_encoding_supported)
|
||||
else
|
||||
-- Set the best match
|
||||
Result.set_compression_type(compression_match)
|
||||
Result.set_acceptable (True)
|
||||
Result.set_variant_header
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
feature -- Language Negotiation
|
||||
|
||||
language_preference (server_language_supported : LIST[STRING]; header: detachable READABLE_STRING_8) : LANGUAGE_VARIANT_RESULTS
|
||||
-- server_language_supported represent a list of languages supported by the server.
|
||||
-- header represent the Accept-Language header, ie, the client preferences.
|
||||
-- Return which Language to use in a response, if the server support
|
||||
-- one Language, or empty in other case.
|
||||
-- Reference: http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.4
|
||||
local
|
||||
language_match: STRING
|
||||
do
|
||||
create Result
|
||||
if header = Void or else header.is_empty then
|
||||
-- the request has no Accept header, ie the header is empty, in this case we use the default format
|
||||
Result.set_acceptable (TRUE)
|
||||
Result.set_language_type (language_default)
|
||||
else
|
||||
-- select the best match, server support, client preferences
|
||||
language_match := language.best_match (server_language_supported, header)
|
||||
if language_match.is_empty then
|
||||
-- The server does not support any of the media types prefered by the client
|
||||
Result.set_acceptable (False)
|
||||
Result.set_supported_variants (server_language_supported)
|
||||
else
|
||||
-- Set the best match
|
||||
Result.set_language_type(language_match)
|
||||
Result.set_acceptable (True)
|
||||
Result.set_variant_header
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
feature -- Apache Conneg Algorithm
|
||||
|
||||
note
|
||||
copyright: "2011-2011, Javier Velilla, Jocelyn Fiat and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
end
|
||||
|
||||
|
||||
@@ -1,352 +0,0 @@
|
||||
note
|
||||
description: "Summary description for {LANGUAGE_PARSE}."
|
||||
author: ""
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
description : "Language Reference: http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.4"
|
||||
|
||||
class
|
||||
LANGUAGE_PARSE
|
||||
inherit
|
||||
REFACTORING_HELPER
|
||||
|
||||
feature -- Parser
|
||||
|
||||
parse_mime_type (a_mime_type: STRING): LANGUAGE_RESULTS
|
||||
-- Parses a mime-type into its component parts.
|
||||
-- For example, the media range 'application/xhtml;q=0.5' would get parsed
|
||||
-- into:
|
||||
-- ('application', 'xhtml', {'q', '0.5'})
|
||||
local
|
||||
l_parts: LIST [STRING]
|
||||
p: STRING
|
||||
sub_parts: LIST [STRING]
|
||||
i: INTEGER
|
||||
l_full_type: STRING
|
||||
l_types: LIST [STRING]
|
||||
do
|
||||
fixme ("Improve code!!!")
|
||||
create Result.make
|
||||
l_parts := a_mime_type.split (';')
|
||||
from
|
||||
i := 1
|
||||
until
|
||||
i > l_parts.count
|
||||
loop
|
||||
p := l_parts.at (i)
|
||||
sub_parts := p.split ('=')
|
||||
if sub_parts.count = 2 then
|
||||
Result.put (trim (sub_parts[2]), trim (sub_parts[1]))
|
||||
end
|
||||
i := i + 1
|
||||
end
|
||||
--Java URLConnection class sends an Accept header that includes a
|
||||
--single "*" - Turn it into a legal wildcard.
|
||||
|
||||
l_full_type := trim (l_parts[1])
|
||||
if l_full_type.same_string ("*") then
|
||||
l_full_type := "*"
|
||||
end
|
||||
l_types := l_full_type.split ('-')
|
||||
if l_types.count = 1 then
|
||||
Result.set_type (trim (l_types[1]))
|
||||
else
|
||||
Result.set_type (trim (l_types[1]))
|
||||
Result.set_sub_type (trim (l_types[2]))
|
||||
end
|
||||
end
|
||||
|
||||
parse_media_range (a_range: STRING): LANGUAGE_RESULTS
|
||||
-- Media-ranges are mime-types with wild-cards and a 'q' quality parameter.
|
||||
-- For example, the media range 'application/*;q=0.5' would get parsed into:
|
||||
-- ('application', '*', {'q', '0.5'})
|
||||
-- In addition this function also guarantees that there is a value for 'q'
|
||||
-- in the params dictionary, filling it in with a proper default if
|
||||
-- necessary.
|
||||
do
|
||||
fixme ("Improve the code!!!")
|
||||
Result := parse_mime_type (a_range)
|
||||
if attached Result.item ("q") as q then
|
||||
if
|
||||
q.is_double and then
|
||||
attached {REAL_64} q.to_double as r and then
|
||||
(r >= 0.0 and r <= 1.0)
|
||||
then
|
||||
--| Keep current value
|
||||
if q.same_string ("1") then
|
||||
--| Use 1.0 formatting
|
||||
Result.put ("1.0", "q")
|
||||
end
|
||||
else
|
||||
Result.put ("1.0", "q")
|
||||
end
|
||||
else
|
||||
Result.put ("1.0", "q")
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
fitness_and_quality_parsed (a_mime_type: STRING; parsed_ranges: LIST [LANGUAGE_RESULTS]): FITNESS_AND_QUALITY
|
||||
-- Find the best match for a given mimeType against a list of media_ranges
|
||||
-- that have already been parsed by parse_media_range. Returns a
|
||||
-- tuple of the fitness value and the value of the 'q' quality parameter of
|
||||
-- the best match, or (-1, 0) if no match was found. Just as for
|
||||
-- quality_parsed(), 'parsed_ranges' must be a list of parsed media ranges.
|
||||
local
|
||||
best_fitness: INTEGER
|
||||
target_q: REAL_64
|
||||
best_fit_q: REAL_64
|
||||
target: LANGUAGE_RESULTS
|
||||
range: LANGUAGE_RESULTS
|
||||
keys: LIST [STRING]
|
||||
param_matches: INTEGER
|
||||
element: detachable STRING
|
||||
l_fitness: INTEGER
|
||||
do
|
||||
best_fitness := -1
|
||||
best_fit_q := 0.0
|
||||
target := parse_media_range (a_mime_type)
|
||||
if attached target.item ("q") as q and then q.is_double then
|
||||
target_q := q.to_double
|
||||
if target_q < 0.0 then
|
||||
target_q := 0.0
|
||||
elseif target_q > 1.0 then
|
||||
target_q := 1.0
|
||||
end
|
||||
else
|
||||
target_q := 1.0
|
||||
end
|
||||
|
||||
if
|
||||
attached target.type as l_target_type
|
||||
then
|
||||
from
|
||||
parsed_ranges.start
|
||||
until
|
||||
parsed_ranges.after
|
||||
loop
|
||||
range := parsed_ranges.item_for_iteration
|
||||
if
|
||||
(
|
||||
attached range.type as l_range_type and then
|
||||
(l_target_type.same_string (l_range_type) or l_range_type.same_string ("*") or l_target_type.same_string ("*"))
|
||||
)
|
||||
then
|
||||
from
|
||||
param_matches := 0
|
||||
keys := target.keys
|
||||
keys.start
|
||||
until
|
||||
keys.after
|
||||
loop
|
||||
element := keys.item_for_iteration
|
||||
if
|
||||
not element.same_string ("q") and then
|
||||
range.has_key (element) and then
|
||||
(attached target.item (element) as t_item and attached range.item (element) as r_item) and then
|
||||
t_item.same_string (r_item)
|
||||
then
|
||||
param_matches := param_matches + 1
|
||||
end
|
||||
keys.forth
|
||||
end
|
||||
|
||||
if l_range_type.same_string (l_target_type) then
|
||||
l_fitness := 100
|
||||
else
|
||||
l_fitness := 0
|
||||
end
|
||||
if (
|
||||
attached range.sub_type as l_range_sub_type and then attached target.sub_type as l_target_sub_type and then
|
||||
(l_target_sub_type.same_string (l_range_sub_type) or l_range_sub_type.same_string ("*") or l_target_sub_type.same_string ("*"))
|
||||
) then
|
||||
if l_range_sub_type.same_string (l_target_sub_type) then
|
||||
l_fitness := l_fitness + 10
|
||||
end
|
||||
end
|
||||
|
||||
l_fitness := l_fitness + param_matches
|
||||
|
||||
if l_fitness > best_fitness then
|
||||
best_fitness := l_fitness
|
||||
element := range.item ("q")
|
||||
if element /= Void then
|
||||
best_fit_q := element.to_double.min (target_q)
|
||||
else
|
||||
best_fit_q := 0.0
|
||||
end
|
||||
end
|
||||
end
|
||||
parsed_ranges.forth
|
||||
end
|
||||
end
|
||||
create Result.make (best_fitness, best_fit_q)
|
||||
end
|
||||
|
||||
quality_parsed (a_mime_type: STRING; parsed_ranges: LIST [LANGUAGE_RESULTS]): REAL_64
|
||||
-- Find the best match for a given mime-type against a list of ranges that
|
||||
-- have already been parsed by parseMediaRange(). Returns the 'q' quality
|
||||
-- parameter of the best match, 0 if no match was found. This function
|
||||
-- bahaves the same as quality() except that 'parsed_ranges' must be a list
|
||||
-- of parsed media ranges.
|
||||
do
|
||||
Result := fitness_and_quality_parsed (a_mime_type, parsed_ranges).quality
|
||||
end
|
||||
|
||||
quality (a_mime_type: STRING; ranges: STRING): REAL_64
|
||||
-- Returns the quality 'q' of a mime-type when compared against the
|
||||
-- mediaRanges in ranges.
|
||||
local
|
||||
l_ranges : LIST [STRING]
|
||||
res : ARRAYED_LIST [LANGUAGE_RESULTS]
|
||||
p_res : LANGUAGE_RESULTS
|
||||
do
|
||||
l_ranges := ranges.split (',')
|
||||
from
|
||||
create res.make (10);
|
||||
l_ranges.start
|
||||
until
|
||||
l_ranges.after
|
||||
loop
|
||||
p_res := parse_media_range (l_ranges.item_for_iteration)
|
||||
res.put_left (p_res)
|
||||
l_ranges.forth
|
||||
end
|
||||
Result := quality_parsed (a_mime_type, res)
|
||||
end
|
||||
|
||||
best_match (supported: LIST [STRING]; header: STRING): STRING
|
||||
-- Choose the mime-type with the highest fitness score and quality ('q') from a list of candidates.
|
||||
local
|
||||
l_header_results: LIST [LANGUAGE_RESULTS]
|
||||
weighted_matches: LIST [FITNESS_AND_QUALITY]
|
||||
l_res: LIST [STRING]
|
||||
p_res: LANGUAGE_RESULTS
|
||||
fitness_and_quality, first_one: detachable FITNESS_AND_QUALITY
|
||||
s: STRING
|
||||
do
|
||||
l_res := header.split (',')
|
||||
create {ARRAYED_LIST [LANGUAGE_RESULTS]} l_header_results.make (l_res.count)
|
||||
|
||||
fixme("Extract method!!!")
|
||||
from
|
||||
l_res.start
|
||||
until
|
||||
l_res.after
|
||||
loop
|
||||
p_res := parse_media_range (l_res.item_for_iteration)
|
||||
l_header_results.force (p_res)
|
||||
l_res.forth
|
||||
end
|
||||
|
||||
create {ARRAYED_LIST [FITNESS_AND_QUALITY]} weighted_matches.make (supported.count)
|
||||
|
||||
from
|
||||
supported.start
|
||||
until
|
||||
supported.after
|
||||
loop
|
||||
fitness_and_quality := fitness_and_quality_parsed (supported.item_for_iteration, l_header_results)
|
||||
fitness_and_quality.set_mime_type (mime_type (supported.item_for_iteration))
|
||||
weighted_matches.force (fitness_and_quality)
|
||||
supported.forth
|
||||
end
|
||||
|
||||
--| Keep only top quality+fitness types
|
||||
from
|
||||
weighted_matches.start
|
||||
first_one := weighted_matches.item
|
||||
weighted_matches.forth
|
||||
until
|
||||
weighted_matches.after
|
||||
loop
|
||||
fitness_and_quality := weighted_matches.item
|
||||
if first_one < fitness_and_quality then
|
||||
first_one := fitness_and_quality
|
||||
if not weighted_matches.isfirst then
|
||||
from
|
||||
weighted_matches.back
|
||||
until
|
||||
weighted_matches.before
|
||||
loop
|
||||
weighted_matches.remove
|
||||
weighted_matches.back
|
||||
end
|
||||
weighted_matches.forth
|
||||
end
|
||||
check weighted_matches.item = fitness_and_quality end
|
||||
weighted_matches.forth
|
||||
elseif first_one.is_equal (fitness_and_quality) then
|
||||
weighted_matches.forth
|
||||
else
|
||||
check first_one > fitness_and_quality end
|
||||
weighted_matches.remove
|
||||
end
|
||||
end
|
||||
if first_one /= Void and then first_one.quality /= 0.0 then
|
||||
if weighted_matches.count = 1 then
|
||||
Result := first_one.mime_type
|
||||
else
|
||||
from
|
||||
fitness_and_quality := Void
|
||||
l_header_results.start
|
||||
until
|
||||
l_header_results.after or fitness_and_quality /= Void
|
||||
loop
|
||||
s := l_header_results.item.mime_type
|
||||
from
|
||||
weighted_matches.start
|
||||
until
|
||||
weighted_matches.after or fitness_and_quality /= Void
|
||||
loop
|
||||
fitness_and_quality := weighted_matches.item
|
||||
if fitness_and_quality.mime_type.same_string (s) then
|
||||
--| Found
|
||||
else
|
||||
fitness_and_quality := Void
|
||||
weighted_matches.forth
|
||||
end
|
||||
end
|
||||
l_header_results.forth
|
||||
end
|
||||
if fitness_and_quality /= Void then
|
||||
Result := fitness_and_quality.mime_type
|
||||
else
|
||||
Result := first_one.mime_type
|
||||
end
|
||||
end
|
||||
else
|
||||
Result := ""
|
||||
end
|
||||
end
|
||||
|
||||
feature {NONE} -- Implementation
|
||||
|
||||
mime_type (s: STRING): STRING
|
||||
local
|
||||
p: INTEGER
|
||||
do
|
||||
p := s.index_of (';', 1)
|
||||
if p > 0 then
|
||||
Result := trim (s.substring (1, p - 1))
|
||||
else
|
||||
Result := trim (s.string)
|
||||
end
|
||||
end
|
||||
|
||||
trim (a_string: STRING): STRING
|
||||
-- trim whitespace from the beginning and end of a string
|
||||
require
|
||||
valid_argument : a_string /= Void
|
||||
do
|
||||
a_string.left_adjust
|
||||
a_string.right_justify
|
||||
Result := a_string
|
||||
ensure
|
||||
result_same_as_argument: a_string = Result
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2011, Javier Velilla, Jocelyn Fiat and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
end
|
||||
@@ -1,143 +0,0 @@
|
||||
note
|
||||
description: "Summary description for {LANGUAGE_RESULTS}."
|
||||
author: ""
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
LANGUAGE_RESULTS
|
||||
inherit
|
||||
ANY
|
||||
redefine
|
||||
out
|
||||
end
|
||||
|
||||
DEBUG_OUTPUT
|
||||
redefine
|
||||
out
|
||||
end
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature -- Initialization
|
||||
|
||||
make
|
||||
do
|
||||
create params.make (2)
|
||||
create mime_type.make_from_string ("*")
|
||||
end
|
||||
|
||||
feature -- Access
|
||||
|
||||
type: detachable STRING
|
||||
|
||||
sub_type: detachable STRING
|
||||
|
||||
mime_type: STRING
|
||||
|
||||
item (a_key: STRING): detachable STRING
|
||||
-- Item associated with `a_key', if present
|
||||
-- otherwise default value of type `STRING'
|
||||
do
|
||||
Result := params.item (a_key)
|
||||
end
|
||||
|
||||
keys: LIST [STRING]
|
||||
-- arrays of currents keys
|
||||
local
|
||||
res: ARRAYED_LIST [STRING]
|
||||
do
|
||||
create res.make_from_array (params.current_keys)
|
||||
Result := res
|
||||
end
|
||||
|
||||
has_key (a_key: STRING): BOOLEAN
|
||||
-- Is there an item in the table with key `a_key'?
|
||||
do
|
||||
Result := params.has_key (a_key)
|
||||
end
|
||||
|
||||
feature -- Element change
|
||||
|
||||
set_type (a_type: STRING)
|
||||
-- Set type with `a_type'
|
||||
do
|
||||
type := a_type
|
||||
if attached sub_type as st then
|
||||
mime_type := a_type + "-" + st
|
||||
else
|
||||
mime_type := a_type
|
||||
end
|
||||
ensure
|
||||
type_assigned: type ~ a_type
|
||||
end
|
||||
|
||||
set_sub_type (a_sub_type: STRING)
|
||||
-- Set sub_type with `a_sub_type
|
||||
do
|
||||
sub_type := a_sub_type
|
||||
if attached type as t then
|
||||
mime_type := t + "-" + a_sub_type
|
||||
else
|
||||
mime_type := "*"
|
||||
end
|
||||
ensure
|
||||
sub_type_assigned: sub_type ~ a_sub_type
|
||||
end
|
||||
|
||||
put (new: STRING; key: STRING)
|
||||
-- Insert `new' with `key' if there is no other item
|
||||
-- associated with the same key. If present, replace
|
||||
-- the old value with `new'
|
||||
do
|
||||
if params.has_key (key) then
|
||||
params.replace (new, key)
|
||||
else
|
||||
params.force (new, key)
|
||||
end
|
||||
ensure
|
||||
has_key: params.has_key (key)
|
||||
has_item: params.has_item (new)
|
||||
end
|
||||
|
||||
feature -- Status Report
|
||||
|
||||
out: STRING
|
||||
-- Representation of the current object
|
||||
do
|
||||
create Result.make_from_string ("(")
|
||||
if attached type as t then
|
||||
Result.append_string ("'" + t + "',")
|
||||
end
|
||||
if attached sub_type as st then
|
||||
Result.append_string (" '" + st + "',")
|
||||
end
|
||||
Result.append_string (" {")
|
||||
|
||||
from
|
||||
params.start
|
||||
until
|
||||
params.after
|
||||
loop
|
||||
Result.append ("'" + params.key_for_iteration + "':'" + params.item_for_iteration + "',");
|
||||
params.forth
|
||||
end
|
||||
Result.append ("})")
|
||||
end
|
||||
|
||||
debug_output: STRING
|
||||
-- String that should be displayed in debugger to represent `Current'.
|
||||
do
|
||||
Result := out
|
||||
end
|
||||
|
||||
feature {NONE} -- Implementation
|
||||
|
||||
params: HASH_TABLE [STRING, STRING]
|
||||
--dictionary of all the parameters for the media range
|
||||
|
||||
;note
|
||||
copyright: "2011-2011, Javier Velilla, Jocelyn Fiat and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
end
|
||||
@@ -1,46 +0,0 @@
|
||||
note
|
||||
description: "Summary description for {LANGUAGE_VARIANT_RESULTS}."
|
||||
author: ""
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
LANGUAGE_VARIANT_RESULTS
|
||||
|
||||
feature
|
||||
language_type : detachable STRING
|
||||
set_language_type ( a_language_type: STRING)
|
||||
do
|
||||
language_type := a_language_type
|
||||
ensure
|
||||
set_language_type : a_language_type ~ language_type
|
||||
end
|
||||
|
||||
variant_header : detachable STRING
|
||||
set_variant_header
|
||||
do
|
||||
variant_header := "Accept-Language"
|
||||
end
|
||||
|
||||
supported_variants : detachable LIST[STRING]
|
||||
set_supported_variants (a_supported : LIST[STRING])
|
||||
do
|
||||
supported_variants := a_supported
|
||||
ensure
|
||||
set_supported_variants : supported_variants = a_supported
|
||||
end
|
||||
|
||||
is_acceptable : BOOLEAN
|
||||
|
||||
set_acceptable ( acceptable : BOOLEAN)
|
||||
do
|
||||
is_acceptable := acceptable
|
||||
ensure
|
||||
is_acceptable = acceptable
|
||||
end
|
||||
|
||||
|
||||
note
|
||||
copyright: "2011-2011, Javier Velilla, Jocelyn Fiat and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
end
|
||||
@@ -1,47 +0,0 @@
|
||||
note
|
||||
description: "Summary description for {MEDIA_TYPE_VARIANT_RESULTS}."
|
||||
author: ""
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
MEDIA_TYPE_VARIANT_RESULTS
|
||||
|
||||
feature
|
||||
|
||||
media_type : detachable STRING
|
||||
set_media_type ( a_media_type: STRING)
|
||||
do
|
||||
media_type := a_media_type
|
||||
ensure
|
||||
set_media_type : a_media_type ~ media_type
|
||||
end
|
||||
|
||||
variant_header : detachable STRING
|
||||
set_variant_header
|
||||
do
|
||||
variant_header := "Accept"
|
||||
end
|
||||
|
||||
supported_variants : detachable LIST[STRING]
|
||||
set_supported_variants (a_supported : LIST[STRING])
|
||||
do
|
||||
supported_variants := a_supported
|
||||
ensure
|
||||
set_supported_variants : supported_variants = a_supported
|
||||
end
|
||||
|
||||
is_acceptable : BOOLEAN
|
||||
|
||||
set_acceptable ( acceptable : BOOLEAN)
|
||||
do
|
||||
is_acceptable := acceptable
|
||||
ensure
|
||||
is_acceptable = acceptable
|
||||
end
|
||||
|
||||
|
||||
note
|
||||
copyright: "2011-2011, Javier Velilla, Jocelyn Fiat and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
end
|
||||
@@ -1,144 +0,0 @@
|
||||
note
|
||||
description: "Summary description for {PARSE_RESULTS}."
|
||||
author: ""
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
PARSE_RESULTS
|
||||
|
||||
inherit
|
||||
ANY
|
||||
redefine
|
||||
out
|
||||
end
|
||||
|
||||
DEBUG_OUTPUT
|
||||
redefine
|
||||
out
|
||||
end
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature -- Initialization
|
||||
|
||||
make
|
||||
do
|
||||
create params.make (2)
|
||||
create mime_type.make_from_string ("*/*")
|
||||
end
|
||||
|
||||
feature -- Access
|
||||
|
||||
type: detachable STRING
|
||||
|
||||
sub_type: detachable STRING
|
||||
|
||||
mime_type: STRING
|
||||
|
||||
item (a_key: STRING): detachable STRING
|
||||
-- Item associated with `a_key', if present
|
||||
-- otherwise default value of type `STRING'
|
||||
do
|
||||
Result := params.item (a_key)
|
||||
end
|
||||
|
||||
keys: LIST [STRING]
|
||||
-- arrays of currents keys
|
||||
local
|
||||
res: ARRAYED_LIST [STRING]
|
||||
do
|
||||
create res.make_from_array (params.current_keys)
|
||||
Result := res
|
||||
end
|
||||
|
||||
has_key (a_key: STRING): BOOLEAN
|
||||
-- Is there an item in the table with key `a_key'?
|
||||
do
|
||||
Result := params.has_key (a_key)
|
||||
end
|
||||
|
||||
feature -- Element change
|
||||
|
||||
set_type (a_type: STRING)
|
||||
-- Set type with `a_type'
|
||||
do
|
||||
type := a_type
|
||||
if attached sub_type as st then
|
||||
mime_type := a_type + "/" + st
|
||||
else
|
||||
mime_type := a_type + "/*"
|
||||
end
|
||||
ensure
|
||||
type_assigned: type ~ a_type
|
||||
end
|
||||
|
||||
set_sub_type (a_sub_type: STRING)
|
||||
-- Set sub_type with `a_sub_type
|
||||
do
|
||||
sub_type := a_sub_type
|
||||
if attached type as t then
|
||||
mime_type := t + "/" + a_sub_type
|
||||
else
|
||||
mime_type := "*/" + a_sub_type
|
||||
end
|
||||
ensure
|
||||
sub_type_assigned: sub_type ~ a_sub_type
|
||||
end
|
||||
|
||||
put (new: STRING; key: STRING)
|
||||
-- Insert `new' with `key' if there is no other item
|
||||
-- associated with the same key. If present, replace
|
||||
-- the old value with `new'
|
||||
do
|
||||
if params.has_key (key) then
|
||||
params.replace (new, key)
|
||||
else
|
||||
params.force (new, key)
|
||||
end
|
||||
ensure
|
||||
has_key: params.has_key (key)
|
||||
has_item: params.has_item (new)
|
||||
end
|
||||
|
||||
feature -- Status Report
|
||||
|
||||
out: STRING
|
||||
-- Representation of the current object
|
||||
do
|
||||
create Result.make_from_string ("(")
|
||||
if attached type as t then
|
||||
Result.append_string ("'" + t + "',")
|
||||
end
|
||||
if attached sub_type as st then
|
||||
Result.append_string (" '" + st + "',")
|
||||
end
|
||||
Result.append_string (" {")
|
||||
|
||||
from
|
||||
params.start
|
||||
until
|
||||
params.after
|
||||
loop
|
||||
Result.append ("'" + params.key_for_iteration + "':'" + params.item_for_iteration + "',");
|
||||
params.forth
|
||||
end
|
||||
Result.append ("})")
|
||||
end
|
||||
|
||||
debug_output: STRING
|
||||
-- String that should be displayed in debugger to represent `Current'.
|
||||
do
|
||||
Result := out
|
||||
end
|
||||
|
||||
feature {NONE} -- Implementation
|
||||
|
||||
params: HASH_TABLE [STRING, STRING]
|
||||
--dictionary of all the parameters for the media range
|
||||
|
||||
;note
|
||||
copyright: "2011-2011, Javier Velilla, Jocelyn Fiat and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
end
|
||||
@@ -1,30 +0,0 @@
|
||||
note
|
||||
description: "Summary description for {SHARED_MIME}."
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
SHARED_CONNEG
|
||||
|
||||
feature
|
||||
|
||||
mime: MIME_PARSE
|
||||
once
|
||||
create Result
|
||||
end
|
||||
|
||||
common: COMMON_ACCEPT_HEADER_PARSER
|
||||
-- Charset and Encoding
|
||||
once
|
||||
create Result
|
||||
end
|
||||
|
||||
language: LANGUAGE_PARSE
|
||||
once
|
||||
create Result
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2011, Javier Velilla, Jocelyn Fiat and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
end
|
||||
@@ -1,58 +0,0 @@
|
||||
note
|
||||
description: "Summary description for {VARIANT_RESULTS}."
|
||||
author: ""
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
VARIANT_RESULTS
|
||||
|
||||
feature -- Mime, Language, Charset and Encoding Results
|
||||
|
||||
mime_result : detachable STRING
|
||||
|
||||
set_mime_result ( a_mime: STRING)
|
||||
-- set the mime_result with `a_mime'
|
||||
do
|
||||
mime_result := a_mime
|
||||
ensure
|
||||
set_mime_result: a_mime ~ mime_result
|
||||
end
|
||||
|
||||
|
||||
language_result : detachable STRING
|
||||
|
||||
set_language_result (a_language : STRING)
|
||||
-- set the language_result with `a_language'
|
||||
do
|
||||
language_result := a_language
|
||||
ensure
|
||||
set_language : a_language ~ language_result
|
||||
end
|
||||
|
||||
|
||||
charset_result : detachable STRING
|
||||
|
||||
set_charset_result (a_charset : STRING)
|
||||
-- set the charset_result with `a_charset'
|
||||
do
|
||||
charset_result := a_charset
|
||||
ensure
|
||||
set_charset : a_charset ~ charset_result
|
||||
end
|
||||
|
||||
|
||||
encoding_result : detachable STRING
|
||||
|
||||
set_encoding_defautl (an_encoding : STRING)
|
||||
do
|
||||
encoding_result := an_encoding
|
||||
ensure
|
||||
set_encoding : an_encoding ~ encoding_result
|
||||
end
|
||||
|
||||
|
||||
note
|
||||
copyright: "2011-2011, Javier Velilla, Jocelyn Fiat and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
end
|
||||
@@ -1,4 +0,0 @@
|
||||
*~
|
||||
EIFGEN* # ignore all files in the EIFGENs/ directory
|
||||
|
||||
|
||||
@@ -1,167 +0,0 @@
|
||||
note
|
||||
description: "Summary description for {CONNEG_SERVER_SIDE_TEST}."
|
||||
author: ""
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
CONNEG_SERVER_SIDE_TEST
|
||||
inherit
|
||||
EQA_TEST_SET
|
||||
redefine
|
||||
on_prepare
|
||||
end
|
||||
|
||||
feature {NONE} -- Events
|
||||
|
||||
on_prepare
|
||||
-- Called after all initializations in `default_create'.
|
||||
do
|
||||
create conneg.make ("application/json", "es", "UTF-8", "")
|
||||
-- set default values
|
||||
end
|
||||
|
||||
feature -- Test routines
|
||||
test_media_type_negotiation
|
||||
local
|
||||
media_variants : MEDIA_TYPE_VARIANT_RESULTS
|
||||
mime_types_supported : LIST [STRING]
|
||||
l_types : STRING
|
||||
do
|
||||
-- Scenario 1, the server side does not support client preferences
|
||||
l_types := "application/json,application/xbel+xml,application/xml"
|
||||
mime_types_supported := l_types.split(',')
|
||||
media_variants := conneg.media_type_preference (mime_types_supported, "text/html")
|
||||
assert ("Expected Not Acceptable", not media_variants.is_acceptable)
|
||||
assert ("Same Value at 1",mime_types_supported.at (1).is_equal (media_variants.supported_variants.at (1)))
|
||||
assert ("Same count",mime_types_supported.count = media_variants.supported_variants.count)
|
||||
assert ("Variant header is void",media_variants.variant_header = Void)
|
||||
assert ("Media type is void",media_variants.media_type = Void)
|
||||
|
||||
-- Scenario 2, the client doesnt send values in the header, Accept:
|
||||
media_variants := conneg.media_type_preference (mime_types_supported, "")
|
||||
assert ("Expected Acceptable", media_variants.is_acceptable)
|
||||
assert ("Variants is dettached",media_variants.supported_variants = Void)
|
||||
assert ("Mime is defaul", conneg.mime_default.is_equal (media_variants.media_type))
|
||||
assert ("Variant header", media_variants.variant_header = Void)
|
||||
|
||||
--Scenario 3, the server select the best match, and set the vary header
|
||||
media_variants := conneg.media_type_preference (mime_types_supported, "text/*,application/json;q=0.5")
|
||||
assert ("Expected Acceptable", media_variants.is_acceptable)
|
||||
assert ("Variants is dettached",media_variants.supported_variants = Void)
|
||||
assert ("Variant Header", media_variants.variant_header.is_equal ("Accept"))
|
||||
assert ("Media Type is application/json", media_variants.media_type.is_equal ("application/json"))
|
||||
|
||||
end
|
||||
|
||||
|
||||
|
||||
test_charset_negotiation
|
||||
local
|
||||
charset_variants : CHARACTER_ENCODING_VARIANT_RESULTS
|
||||
charset_supported : LIST [STRING]
|
||||
l_charset : STRING
|
||||
do
|
||||
-- Scenario 1, the server side does not support client preferences
|
||||
l_charset := "UTF-8, iso-8859-5"
|
||||
charset_supported := l_charset.split(',')
|
||||
charset_variants := conneg.charset_preference (charset_supported, "unicode-1-1")
|
||||
assert ("Expected Not Acceptable", not charset_variants.is_acceptable)
|
||||
assert ("Same Value at 1",charset_supported.at (1).is_equal (charset_variants.supported_variants.at (1)))
|
||||
assert ("Same count",charset_supported.count = charset_variants.supported_variants.count)
|
||||
assert ("Variant header is void",charset_variants.variant_header = Void)
|
||||
assert ("Character type is void",charset_variants.character_type = Void)
|
||||
|
||||
|
||||
-- Scenario 2, the client doesnt send values in the header, Accept-Charset:
|
||||
charset_variants := conneg.charset_preference (charset_supported, "")
|
||||
assert ("Expected Acceptable", charset_variants.is_acceptable)
|
||||
assert ("Variants is dettached",charset_variants.supported_variants = Void)
|
||||
assert ("Charset is defaul", conneg.charset_default.is_equal (charset_variants.character_type))
|
||||
assert ("Variant header", charset_variants.variant_header = Void)
|
||||
|
||||
|
||||
--Scenario 3, the server select the best match, and set the vary header
|
||||
charset_variants := conneg.charset_preference (charset_supported, "unicode-1-1, UTF-8;q=0.3, iso-8859-5")
|
||||
assert ("Expected Acceptable", charset_variants.is_acceptable)
|
||||
assert ("Variants is dettached",charset_variants.supported_variants = Void)
|
||||
assert ("Variant Header", charset_variants.variant_header.is_equal ("Accept-Charset"))
|
||||
assert ("Character Type is iso-8859-5", charset_variants.character_type.is_equal ("iso-8859-5"))
|
||||
end
|
||||
|
||||
test_compression_negotiation
|
||||
local
|
||||
compression_variants : COMPRESSION_VARIANT_RESULTS
|
||||
compression_supported : LIST [STRING]
|
||||
l_compression : STRING
|
||||
do
|
||||
-- Scenario 1, the server side does not support client preferences
|
||||
l_compression := ""
|
||||
compression_supported := l_compression.split(',')
|
||||
compression_variants := conneg.encoding_preference (compression_supported, "gzip")
|
||||
assert ("Expected Not Acceptable", not compression_variants.is_acceptable)
|
||||
assert ("Same Value at 1",compression_supported.at (1).is_equal (compression_variants.supported_variants.at (1)))
|
||||
assert ("Same count",compression_supported.count = compression_variants.supported_variants.count)
|
||||
assert ("Variant header is void",compression_variants.variant_header = Void)
|
||||
assert ("Compression type is void",compression_variants.compression_type = Void)
|
||||
|
||||
|
||||
-- Scenario 2, the client doesnt send values in the header, Accept-Encoding
|
||||
compression_variants := conneg.encoding_preference (compression_supported, "")
|
||||
assert ("Expected Acceptable", compression_variants.is_acceptable)
|
||||
assert ("Variants is dettached",compression_variants.supported_variants = Void)
|
||||
assert ("Compression is defaul", conneg.encoding_default.is_equal (compression_variants.compression_type))
|
||||
assert ("Variant header", compression_variants.variant_header = Void)
|
||||
|
||||
|
||||
--Scenario 3, the server select the best match, and set the vary header
|
||||
l_compression := "gzip"
|
||||
compression_supported := l_compression.split(',')
|
||||
conneg.set_encoding_defautl ("gzip")
|
||||
compression_variants := conneg.encoding_preference (compression_supported, "compress,gzip;q=0.7")
|
||||
assert ("Expected Acceptable", compression_variants.is_acceptable)
|
||||
assert ("Variants is dettached",compression_variants.supported_variants = Void)
|
||||
assert ("Variant Header", compression_variants.variant_header.is_equal ("Accept-Encoding"))
|
||||
assert ("Encoding Type is gzip", compression_variants.compression_type.is_equal ("gzip"))
|
||||
end
|
||||
|
||||
|
||||
|
||||
test_language_negotiation
|
||||
local
|
||||
language_variants : LANGUAGE_VARIANT_RESULTS
|
||||
languages_supported : LIST [STRING]
|
||||
l_languages : STRING
|
||||
do
|
||||
-- Scenario 1, the server side does not support client preferences
|
||||
l_languages := "es,en,en-US,fr;q=0.6"
|
||||
languages_supported := l_languages.split(',')
|
||||
language_variants := conneg.language_preference (languages_supported, "de")
|
||||
assert ("Expected Not Acceptable", not language_variants.is_acceptable)
|
||||
assert ("Same Value at 1",languages_supported.at (1).is_equal (language_variants.supported_variants.at (1)))
|
||||
assert ("Same count",languages_supported.count = language_variants.supported_variants.count)
|
||||
assert ("Variant header is void",language_variants.variant_header = Void)
|
||||
assert ("Language type is void",language_variants.language_type = Void)
|
||||
|
||||
|
||||
-- Scenario 2, the client doesnt send values in the header, Accept-Language:
|
||||
language_variants := conneg.language_preference (languages_supported, "")
|
||||
assert ("Expected Acceptable", language_variants.is_acceptable)
|
||||
assert ("Variants is dettached",language_variants.supported_variants = Void)
|
||||
assert ("Language is defaul", conneg.language_default.is_equal (language_variants.language_type))
|
||||
assert ("Variant header", language_variants.variant_header = Void)
|
||||
|
||||
|
||||
--Scenario 3, the server select the best match, and set the vary header
|
||||
language_variants := conneg.language_preference (languages_supported, "fr,es;q=0.4")
|
||||
assert ("Expected Acceptable", language_variants.is_acceptable)
|
||||
assert ("Variants is dettached",language_variants.supported_variants = Void)
|
||||
assert ("Variant Header", language_variants.variant_header.is_equal ("Accept-Language"))
|
||||
assert ("Language Type is fr", language_variants.language_type.is_equal ("fr"))
|
||||
|
||||
|
||||
end
|
||||
|
||||
feature -- Implementation
|
||||
conneg : CONNEG_SERVER_SIDE
|
||||
end
|
||||
@@ -1,117 +0,0 @@
|
||||
note
|
||||
description: "Summary description for {LANGUAGE_PARSER_TEST}."
|
||||
author: ""
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
LANGUAGE_PARSER_TEST
|
||||
|
||||
inherit
|
||||
EQA_TEST_SET
|
||||
redefine
|
||||
on_prepare
|
||||
end
|
||||
|
||||
feature {NONE} -- Events
|
||||
|
||||
on_prepare
|
||||
-- Called after all initializations in `default_create'.
|
||||
do
|
||||
create parser
|
||||
end
|
||||
|
||||
feature -- Test routines
|
||||
|
||||
test_parse_media_range
|
||||
do
|
||||
assert ("Expected ('da', {'q':'1.0',})", parser.parse_media_range ("da").out.same_string ("('da', {'q':'1.0',})"));
|
||||
assert ("Expected ('en', 'gb', {'q':'0.8',})", parser.parse_media_range ("en-gb;q=0.8").out.same_string ("('en', 'gb', {'q':'0.8',})"));
|
||||
assert ("Expected ('en', {'q':'0.7',})", parser.parse_media_range ("en;q=0.7").out.same_string ("('en', {'q':'0.7',})"));
|
||||
assert ("Expected ('en', '*', {'q':'1.0',})", parser.parse_media_range ("en-*").out.same_string ("('en', '*', {'q':'1.0',})"));
|
||||
end
|
||||
|
||||
|
||||
test_RFC2616_example
|
||||
local
|
||||
accept : STRING
|
||||
do
|
||||
accept := "da, en-gb;q=0.8, en;q=0.7";
|
||||
assert ("Expected 1.0", 1.0 = parser.quality ("da", accept))
|
||||
assert ("Expected 0.8", 0.8 = parser.quality ("en-gb", accept))
|
||||
assert ("Expected 0.8", 0.8 = parser.quality ("en", accept))
|
||||
assert ("Expected 0.8", 0.8 = parser.quality ("en-*", accept))
|
||||
end
|
||||
|
||||
|
||||
test_best_match
|
||||
local
|
||||
mime_types_supported : LIST [STRING]
|
||||
l_types : STRING
|
||||
do
|
||||
l_types := "en-gb,en-us"
|
||||
mime_types_supported := l_types.split(',')
|
||||
assert ("Expected en-us", parser.best_match (mime_types_supported, "en-us").same_string ("en-us"))
|
||||
assert ("Direct match with a q parameter", parser.best_match (mime_types_supported, "en-gb;q=1").same_string ("en-gb"))
|
||||
assert ("Direct match second choice with a q parameter", parser.best_match (mime_types_supported, "en-us;q=1").same_string ("en-us"))
|
||||
assert ("Direct match using a subtype wildcard", parser.best_match (mime_types_supported, "en-*;q=1").is_equal ("en-gb"))
|
||||
assert ("Match using a type wildcard", parser.best_match (mime_types_supported, "*").same_string ("en-gb"))
|
||||
|
||||
l_types := "en-gb,es"
|
||||
mime_types_supported := l_types.split(',')
|
||||
assert ("Match using a type versus a lower weighted subtype", parser.best_match (mime_types_supported, "es-*;q=0.5,*;q=0.1").same_string ("es"))
|
||||
assert ("Fail to match anything",parser.best_match (mime_types_supported, "fr; q=0.9").same_string (""))
|
||||
|
||||
l_types := "en-gb,en-us"
|
||||
mime_types_supported := l_types.split(',')
|
||||
assert ("Test 1 verify fitness ordering", parser.best_match (mime_types_supported, "en-gb,en-us,*").same_string ("en-gb"))
|
||||
|
||||
l_types := "es,en-gb;q=1.0,fr;q=0.6"
|
||||
mime_types_supported := l_types.split(',')
|
||||
assert ("Match default es at first position", parser.best_match (mime_types_supported, "es;q=1.0,*;q=0.1,fr").same_string ("es"))
|
||||
|
||||
l_types := "en-gb;q=1.0,fr;q=0.6,es"
|
||||
mime_types_supported := l_types.split(',')
|
||||
assert ("Match default es at last position", parser.best_match (mime_types_supported, "es;q=1.0,*;q=0.1,fr").same_string ("es"))
|
||||
|
||||
l_types := "en-gb;q=1.0,fr,es"
|
||||
mime_types_supported := l_types.split(',')
|
||||
assert ("Match first top quality and fitness", parser.best_match (mime_types_supported, "es;q=1.0,*;q=0.1,fr").same_string ("es"))
|
||||
|
||||
l_types := "fr;q=1.0,en,es"
|
||||
mime_types_supported := l_types.split(',')
|
||||
assert ("Test 1", parser.best_match (mime_types_supported, "es;q=1.0,*/*;q=0.1,en;q=0.9").same_string ("es"))
|
||||
|
||||
l_types := "fr;q=1.0,en,es"
|
||||
mime_types_supported := l_types.split(',')
|
||||
assert ("Test 1", parser.best_match (mime_types_supported, "es,*/*;q=0.1,en").same_string ("es"))
|
||||
|
||||
l_types := "fr;q=1.0,en,es"
|
||||
mime_types_supported := l_types.split(',')
|
||||
assert ("Test 2", parser.best_match (mime_types_supported, "en,es,*/*;q=0.1").same_string ("en"))
|
||||
|
||||
l_types := "es,en;q=0.6"
|
||||
mime_types_supported := l_types.split(',')
|
||||
assert ("Test 2", parser.best_match (mime_types_supported, "fr;q=1.0, en;q=0.6, es").same_string ("es"))
|
||||
|
||||
end
|
||||
|
||||
|
||||
test_support_wildcard
|
||||
local
|
||||
mime_types_supported : LIST[STRING]
|
||||
l_types : STRING
|
||||
do
|
||||
l_types := "en-*,fr"
|
||||
mime_types_supported := l_types.split(',')
|
||||
assert ("match using a type wildcard", parser.best_match (mime_types_supported, "en-gb").same_string ("en-*"))
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
parser : LANGUAGE_PARSE
|
||||
|
||||
end
|
||||
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
|
||||
31
library/network/protocol/content_negotiation/README.md
Normal file
31
library/network/protocol/content_negotiation/README.md
Normal file
@@ -0,0 +1,31 @@
|
||||
CONNEG is a library that provides utilities to select the best repesentation of a resource for a client where there are multiple representations available.
|
||||
|
||||
Using this library you can retrieve the best variant for media type, language preference, charset, and enconding/compression.
|
||||
|
||||
Take into account that the library is under development so is expected that the API change.
|
||||
|
||||
The library contains utilities that deal with content negotiation (server driven negotiation).This utility class
|
||||
is based on ideas taken from the Book Restful WebServices Cookbook
|
||||
|
||||
The class SERVER_CONTENT_NEGOTIATION contains several features that helps to write different types of negotiation (media type, language,
|
||||
charset and compression).
|
||||
So for each of the following questions, you will have a corresponding method to help in the solution.
|
||||
|
||||
- How to implement Media type negotiation?
|
||||
Hint: Use SERVER_CONTENT_NEGOTIATION.media_type_preference
|
||||
or SERVER_MEDIA_TYPE_NEGOTIATION.preference
|
||||
|
||||
- How to implement Language Negotiation?
|
||||
Hint: Use SERVER_CONTENT_NEGOTIATION.language_preference
|
||||
or SERVER_LANGUAGE_NEGOTIATION.preference
|
||||
|
||||
- How to implement Character Negotiation?
|
||||
Hint: Use SERVER_CONTENT_NEGOTIATION.charset_preference
|
||||
or SERVER_CHARSET_NEGOTIATION.preference
|
||||
|
||||
- How to implement Encoding Negotiation?
|
||||
Hint: Use SERVER_CONTENT_NEGOTIATION.encoding_preference
|
||||
or SERVER_ENCODING_NEGOTIATION.preference
|
||||
|
||||
There is also a [test case](test/conneg_server_side_test.e "conneg_server_side_test") where you can check how to use this class.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-11-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-11-0 http://www.eiffel.com/developers/xml/configuration-1-11-0.xsd" name="conneg" uuid="D15DC4C4-D74F-4E4B-B4B1-2B03A35A284D" library_target="conneg">
|
||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-12-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-12-0 http://www.eiffel.com/developers/xml/configuration-1-12-0.xsd" name="conneg" uuid="D15DC4C4-D74F-4E4B-B4B1-2B03A35A284D" library_target="conneg">
|
||||
<target name="conneg">
|
||||
<root all_classes="true"/>
|
||||
<file_rule>
|
||||
@@ -12,7 +12,12 @@
|
||||
<assertions precondition="true"/>
|
||||
</option>
|
||||
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
|
||||
<library name="http" location="..\http\http-safe.ecf"/>
|
||||
<cluster name="conneg" location=".\src\" recursive="true"/>
|
||||
<library name="http" location="..\http\http-safe.ecf" readonly="false"/>
|
||||
<cluster name="conneg" location=".\src\" recursive="true">
|
||||
<file_rule>
|
||||
<exclude>/implementation</exclude>
|
||||
</file_rule>
|
||||
</cluster>
|
||||
<cluster name="conneg_imp" location=".\src\implementation\" recursive="true" hidden="true"/>
|
||||
</target>
|
||||
</system>
|
||||
@@ -13,6 +13,12 @@
|
||||
</option>
|
||||
<library name="base" location="$ISE_LIBRARY\library\base\base.ecf"/>
|
||||
<library name="http" location="../http/http.ecf"/>
|
||||
<cluster name="conneg" location=".\src" recursive="true"/>
|
||||
<cluster name="conneg" location=".\src\" recursive="true">
|
||||
<file_rule>
|
||||
<exclude>/implementation</exclude>
|
||||
</file_rule>
|
||||
</cluster>
|
||||
<cluster name="conneg_imp" location=".\src\implementation" recursive="true" hidden="true">
|
||||
</cluster>
|
||||
</target>
|
||||
</system>
|
||||
15
library/network/protocol/content_negotiation/package.iron
Normal file
15
library/network/protocol/content_negotiation/package.iron
Normal file
@@ -0,0 +1,15 @@
|
||||
package content_negotiation
|
||||
|
||||
project
|
||||
conneg = "conneg-safe.ecf"
|
||||
conneg = "conneg.ecf"
|
||||
|
||||
note
|
||||
-- title:
|
||||
-- description:
|
||||
-- tags:
|
||||
-- license:
|
||||
-- copyright:
|
||||
-- link[doc]: "Documentation" http://
|
||||
|
||||
end
|
||||
@@ -1,6 +1,5 @@
|
||||
note
|
||||
description: "Summary description for {FITNESS_AND_QUALITY}."
|
||||
author: ""
|
||||
description: "FITNESS_AND_QUALITY. Object holding a fitness/quality values."
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
@@ -21,13 +20,15 @@ create
|
||||
feature -- Initialization
|
||||
|
||||
make (a_fitness: INTEGER; a_quality: REAL_64)
|
||||
-- Create an object with `a_fitness' and `a_quality'
|
||||
do
|
||||
fitness := a_fitness
|
||||
quality := a_quality
|
||||
create mime_type.make_empty
|
||||
create {STRING_8} entity.make_empty
|
||||
ensure
|
||||
fitness_assigned : fitness = a_fitness
|
||||
quality_assigned : quality = a_quality
|
||||
entity_empty: entity.is_empty
|
||||
end
|
||||
|
||||
feature -- Access
|
||||
@@ -36,17 +37,17 @@ feature -- Access
|
||||
|
||||
quality: REAL_64
|
||||
|
||||
mime_type: STRING
|
||||
entity: READABLE_STRING_8
|
||||
-- optionally used
|
||||
-- empty by default
|
||||
|
||||
--| Could be a mime type, an encoding, ...
|
||||
|
||||
feature -- Status report
|
||||
|
||||
debug_output: STRING
|
||||
-- String that should be displayed in debugger to represent `Current'.
|
||||
do
|
||||
create Result.make_from_string (mime_type)
|
||||
create Result.make_from_string (entity)
|
||||
Result.append (" (")
|
||||
Result.append ("quality=" + quality.out)
|
||||
Result.append (" ; fitness=" + fitness.out)
|
||||
@@ -55,12 +56,12 @@ feature -- Status report
|
||||
|
||||
feature -- Element Change
|
||||
|
||||
set_mime_type (a_mime_type: STRING)
|
||||
-- set mime_type with `a_mime_type'
|
||||
set_entity (a_entity: READABLE_STRING_8)
|
||||
-- set `entity' with `a_entity'
|
||||
do
|
||||
mime_type := a_mime_type
|
||||
entity := a_entity
|
||||
ensure
|
||||
mime_type_assigned : mime_type.same_string (a_mime_type)
|
||||
entity_assigned : entity.same_string (a_entity)
|
||||
end
|
||||
|
||||
feature -- Comparision
|
||||
@@ -75,7 +76,7 @@ feature -- Comparision
|
||||
end
|
||||
end
|
||||
note
|
||||
copyright: "2011-2011, Javier Velilla, Jocelyn Fiat and others"
|
||||
copyright: "2011-2013, Javier Velilla, Jocelyn Fiat, Eiffel Software 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