Merge branch 'master' of github.com:EiffelWebFramework/EWF

This commit is contained in:
Jocelyn Fiat
2012-09-20 10:31:04 +02:00
52 changed files with 1689 additions and 169 deletions

View File

@@ -0,0 +1,175 @@
note
description: "JSON_PRETTY_STRING_VISITOR Generates the JSON-String for a JSON_VALUE"
revision: "0.1"
class
JSON_PRETTY_STRING_VISITOR
inherit
JSON_VISITOR
create
make,
make_custom
feature -- Initialization
make (a_output: like output)
-- Create a new instance
do
make_custom (a_output, 1, 1)
end
make_custom (a_output: like output; a_object_count_inlining, a_array_count_inlining: INTEGER)
-- Create a new instance
do
output := a_output
create indentation.make_empty
indentation_step := "%T"
object_count_inlining := a_object_count_inlining
array_count_inlining := a_array_count_inlining
end
feature -- Access
output: STRING_32
-- JSON representation
indentation: like output
indentation_step: like indentation
line_number: INTEGER
indent
do
indentation.append (indentation_step)
end
exdent
do
indentation.remove_tail (indentation_step.count)
end
new_line
do
output.append ("%N")
output.append (indentation)
line_number := line_number + 1
end
object_count_inlining: INTEGER
array_count_inlining: INTEGER
feature -- Visitor Pattern
visit_json_array (a_json_array: JSON_ARRAY)
-- Visit `a_json_array'.
local
value: JSON_VALUE
l_json_array: ARRAYED_LIST [JSON_VALUE]
l_line: like line_number
l_multiple_lines: BOOLEAN
do
l_json_array := a_json_array.array_representation
l_multiple_lines := l_json_array.count >= array_count_inlining or across l_json_array as p some attached {JSON_OBJECT} p.item or attached {JSON_ARRAY} p.item end
output.append ("[")
l_line := line_number
indent
from
l_json_array.start
until
l_json_array.off
loop
if
line_number > l_line or
l_multiple_lines
then
new_line
end
value := l_json_array.item
value.accept (Current)
l_json_array.forth
if not l_json_array.after then
output.append (", ")
end
end
exdent
if
line_number > l_line or
l_json_array.count >= array_count_inlining
then
new_line
end
output.append ("]")
end
visit_json_boolean (a_json_boolean: JSON_BOOLEAN)
-- Visit `a_json_boolean'.
do
output.append (a_json_boolean.item.out)
end
visit_json_null (a_json_null: JSON_NULL)
-- Visit `a_json_null'.
do
output.append ("null")
end
visit_json_number (a_json_number: JSON_NUMBER)
-- Visit `a_json_number'.
do
output.append (a_json_number.item)
end
visit_json_object (a_json_object: JSON_OBJECT)
-- Visit `a_json_object'.
local
l_pairs: HASH_TABLE [JSON_VALUE, JSON_STRING]
l_line: like line_number
l_multiple_lines: BOOLEAN
do
l_pairs := a_json_object.map_representation
l_multiple_lines := l_pairs.count >= object_count_inlining or across l_pairs as p some attached {JSON_OBJECT} p.item or attached {JSON_ARRAY} p.item end
output.append ("{")
l_line := line_number
indent
from
l_pairs.start
until
l_pairs.off
loop
if
line_number > l_line or
l_multiple_lines
then
new_line
end
l_pairs.key_for_iteration.accept (Current)
output.append (": ")
l_pairs.item_for_iteration.accept (Current)
l_pairs.forth
if not l_pairs.after then
output.append (", ")
end
end
exdent
if
line_number > l_line or
l_pairs.count >= object_count_inlining
then
new_line
end
output.append ("}")
end
visit_json_string (a_json_string: JSON_STRING)
-- Visit `a_json_string'.
do
output.append ("%"")
output.append (a_json_string.item)
output.append ("%"")
end
end

View File

@@ -29,4 +29,4 @@ feature
Result.add_converter (jhtc)
end
end -- class SHARED_GOBO_EJSON
end -- class SHARED_GOBO_EJSON

View File

@@ -0,0 +1,30 @@
note
description: "A JSON converter for ARRAYED_LIST [ANY]"
author: "Paul Cohen"
date: "$Date$"
revision: "$Revision$"
file: "$HeadURL: $"
class JSON_ARRAYED_LIST_CONVERTER
inherit
JSON_LIST_CONVERTER
redefine
object
end
create
make
feature -- Access
object: ARRAYED_LIST [detachable ANY]
feature {NONE} -- Factory
new_object (nb: INTEGER): like object
do
create Result.make (nb)
end
end -- class JSON_ARRAYED_LIST_CONVERTER

View File

@@ -1,8 +1,8 @@
note
description: "A JSON converter"
author: "Paul Cohen"
date: "$Date: $"
revision: "$Revision: $"
date: "$Date$"
revision: "$Revision$"
file: "$HeadURL: $"
deferred class JSON_CONVERTER
@@ -19,7 +19,7 @@ feature -- Access
feature -- Conversion
from_json (j: attached like to_json): detachable like object
from_json (j: attached like to_json): detachable like object
-- Convert from JSON value.
-- Returns Void if unable to convert
deferred

View File

@@ -1,8 +1,8 @@
note
description: "A JSON converter for HASH_TABLE [ANY, HASHABLE]"
author: "Paul Cohen"
date: "$Date$"
revision: "$Revision$"
date: "$Date$"
revision: "$Revision$"
file: "$HeadURL: $"
class JSON_HASH_TABLE_CONVERTER
@@ -26,7 +26,7 @@ feature -- Access
feature -- Conversion
from_json (j: attached like to_json): like object
from_json (j: attached like to_json): like object
local
keys: ARRAY [JSON_STRING]
i: INTEGER
@@ -41,7 +41,7 @@ feature -- Conversion
until
i > keys.count
loop
h ?= json.object (keys [i], void)
h ?= json.object (keys [i], Void)
check h /= Void end
jv := j.item (keys [i])
if jv /= Void then

View File

@@ -1,63 +1,30 @@
note
description: "A JSON converter for LINKED_LIST [ANY]"
author: "Paul Cohen"
date: "$Date$"
revision: "$Revision$"
file: "$HeadURL: $"
description: "A JSON converter for LINKED_LIST [ANY]"
author: "Paul Cohen"
date: "$Date$"
revision: "$Revision$"
file: "$HeadURL: $"
class JSON_LINKED_LIST_CONVERTER
inherit
JSON_CONVERTER
JSON_LIST_CONVERTER
redefine
object
end
create
make
feature {NONE} -- Initialization
make
do
create object.make
end
feature -- Access
object: LINKED_LIST [detachable ANY]
feature -- Conversion
feature {NONE} -- Factory
from_json (j: like to_json): detachable like object
local
i: INTEGER
do
create Result.make
from
i := 1
until
i > j.count
loop
Result.extend (json.object (j [i], Void))
i := i + 1
end
end
to_json (o: like object): JSON_ARRAY
local
c: ITERATION_CURSOR [detachable ANY]
do
create Result.make_array
from
c := o.new_cursor
until
c.after
loop
if attached json.value (c.item) as v then
Result.add (v)
else
check attached_value: False end
end
c.forth
end
end
new_object (nb: INTEGER): like object
do
create Result.make
end
end -- class JSON_LINKED_LIST_CONVERTER

View File

@@ -0,0 +1,74 @@
note
description: "A JSON converter for LIST [ANY]"
author: "Paul Cohen"
date: "$Date$"
revision: "$Revision$"
file: "$HeadURL: $"
deferred class JSON_LIST_CONVERTER
inherit
JSON_CONVERTER
feature {NONE} -- Initialization
make
do
object := new_object (0)
end
feature -- Access
object: LIST [detachable ANY]
feature {NONE} -- Factory
new_object (nb: INTEGER): like object
deferred
ensure
Result /= Void
end
feature -- Conversion
from_json (j: attached like to_json): detachable like object
local
i: INTEGER
do
Result := new_object (j.count)
from
i := 1
until
i > j.count
loop
Result.extend (json.object (j [i], Void))
i := i + 1
end
end
to_json (o: like object): detachable JSON_ARRAY
local
c: ITERATION_CURSOR [detachable ANY]
jv: detachable JSON_VALUE
failed: BOOLEAN
do
create Result.make_array
from
c := o.new_cursor
until
c.after
loop
jv := json.value (c.item)
if jv /= Void then
Result.add (jv)
else
failed := True
end
c.forth
end
if failed then
Result := Void
end
end
end -- class JSON_ARRAYED_LIST_CONVERTER

View File

@@ -1,8 +1,8 @@
note
description: "Core factory class for creating JSON objects and corresponding Eiffel objects."
author: "Paul Cohen"
date: "$Date: $"
revision: "$Revision: $"
date: "$Date$"
revision: "$Revision$"
file: "$HeadURL: $"
class EJSON
@@ -69,7 +69,7 @@ feature -- Access
elseif attached {STRING_8} an_object as s8 then
create {JSON_STRING} Result.make_json (s8)
elseif attached {STRING_32} an_object as s32 then
create {JSON_STRING} Result.make_json (s32.as_string_8) -- FIXME: need correct convertion/encoding here ...
create {JSON_STRING} Result.make_json_from_string_32 (s32)
end
if Result = Void then
@@ -253,9 +253,9 @@ feature {NONE} -- Implementation (Exceptions)
-- Exception message for failing to convert `a' to a JSON_VALUE.
do
Result := exception_prefix + "Failed to convert Eiffel object to a JSON_VALUE"
if an_object /= Void then
Result := ": " + an_object.generator
end
if an_object /= Void then
Result := ": " + an_object.generator
end
end
feature {NONE} -- Implementation (JSON parser)

View File

@@ -1,14 +1,14 @@
note
description: "[
Shared factory class for creating JSON objects. Maps JSON
objects to ELKS HASH_TABLEs and JSON arrays to ELKS
LINKED_LISTs. Use non-conforming inheritance from this
class to ensure that your classes share the same
JSON_FACTORY instance.
Shared factory class for creating JSON objects. Maps JSON
objects to ELKS HASH_TABLEs and JSON arrays to ELKS
LINKED_LISTs. Use non-conforming inheritance from this
class to ensure that your classes share the same
JSON_FACTORY instance.
]"
author: "Paul Cohen"
date: "$Date: $"
revision: "$Revision: $"
date: "$Date$"
revision: "$Revision: 89185 $"
file: "$HeadURL: $"
class SHARED_EJSON
@@ -17,16 +17,22 @@ feature
json: EJSON
-- A shared EJSON instance with default converters for
-- DS_LINKED_LIST [ANY] and DS_HASH_TABLE [ANY, HASHABLE]
--LINKED_LIST [ANY] and HASH_TABLE [ANY, HASHABLE]
local
jalc: JSON_ARRAYED_LIST_CONVERTER
jllc: JSON_LINKED_LIST_CONVERTER
jhtc: JSON_HASH_TABLE_CONVERTER
once
create Result
create jalc.make
Result.add_converter (jalc)
create jllc.make
Result.add_converter (jllc)
create jhtc.make
Result.add_converter (jhtc)
end
end -- class SHARED_EJSON
end -- class SHARED_EJSON

View File

@@ -1,9 +1,8 @@
note
description: "A JSON converter for AUTHOR"
author: "Paul Cohen"
date: "$Date: 2010-03-08 20:46:59 -0300 (Mon, 08 Mar 2010) $"
revision: "$Revision: 82 $"
file: "$HeadURL: https://svn.origo.ethz.ch/ejson/branches/POC-converters-factory/test/json_author_converter.e $"
date: "$Date$"
revision: "$Revision$"
class JSON_AUTHOR_CONVERTER

View File

@@ -1,9 +1,8 @@
note
description: "A JSON converter for BOOK_COLLECTION"
author: "Paul Cohen"
date: "$Date: 2010-03-08 20:46:59 -0300 (Mon, 08 Mar 2010) $"
revision: "$Revision: 82 $"
file: "$HeadURL: https://svn.origo.ethz.ch/ejson/branches/POC-converters-factory/test/json_book_collection_converter.e $"
date: "$Date$"
revision: "$Revision$"
class JSON_BOOK_COLLECTION_CONVERTER

View File

@@ -1,9 +1,8 @@
note
description: "A JSON converter for BOOK"
author: "Paul Cohen"
date: "$Date: 2010-03-08 20:46:59 -0300 (Mon, 08 Mar 2010) $"
revision: "$Revision: 82 $"
file: "$HeadURL: https://svn.origo.ethz.ch/ejson/branches/POC-converters-factory/test/json_book_converter.e $"
date: "$Date$"
revision: "$Revision$"
class JSON_BOOK_CONVERTER

View File

@@ -632,7 +632,7 @@ feature -- Test
jrep := "%"foo\\bar%""
create parser.make_parser (jrep)
if attached {JSON_STRING} parser.parse as jstring then
assert ("unescaped string %"foo\\bar%" to %"foo\bar%"", jstring.unescaped_string_8.same_string ("foo\bar"))
assert ("unescaped string %"foo\\bar%" to %"foo\bar%"", jstring.unescaped_string.same_string ("foo\bar"))
end
create js.make_json_from_string_32 ({STRING_32}"%/20320/%/22909/")

View File

@@ -1,16 +1,26 @@
## Eiffel-Web-Framework ##
# 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]].
## Organization ##
- Mailing list: please visit and subscribe to the mailing list page [[http://groups.google.com/group/eiffel-web-framework]] ![logo](http://groups.google.com/intl/en/images/logos/groups_logo_sm.gif)
- Most of the topics are discussed on the mailing list (google group).
- For time to time we have web meeting, and less frequently physical meetings that occurs usually during other Eiffel related events.
- See also
- You want to contribute or follow the progress/discussion, see the [[collaboration page| Community-collaboration]]
## Documentation ##
- to redo
## Contributions ##
- You want to contribute or follow the progress/discussion, see the [[collaboration page| Community-collaboration]]
- Potential tasks/projects on EWF: [[Projects page| 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
## 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

View File

@@ -0,0 +1,14 @@
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.
For any entry, please use this template
----
## Short title
* _Suggested by **your name**_": so that we know who suggested a feature.
* **Requirement**: ... if any, otherwise remove this line
* _Description_: ... a few lines to describe the project
* _References_: ... if any, otherwise remove this line
----

231
doc/wiki/Projects.md Normal file
View File

@@ -0,0 +1,231 @@
This page lists potential projects on EWF, this is open for contribution.
If you are a student, don't hesitate to pick one, or even suggest a new project, or a project being a merge of several, in any case, you will get close support from EWF's team.
----
# Study/Analysis/Documentation
## Evaluate EWF according to the following constraints ...
* _Suggested by **Javier**_
* _Description_: According to http://www.amundsen.com/blog/archives/1130 , 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.
## 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
## Build a video to demonstrate how an Hypermedia API works, and how to build it using EWF
* _Suggested by **Javier**_
* _Supervisor_:
* _Suitability_: TODO
* _Description_: produce a audio+video+slide that demonstrates how to build an hypermedia API using EWF. This could be based on upcoming "graphviz server" example, or an extension of existing RestBucksCRUD example, or any new example.
----
# Works related to EWF / framework / tools
## Improve EWF
* _Suggested by **Jocelyn**_
* _Supervisor_:
* _Suitability_: TODO
* _Description_: Improve existing EWF source, this is a permanent task for EWF, and this can be code, documentation, tests, ... Among others , here is a list of needed effort:
** Improve encoding support
* Better MIME handler
** _Support for configuration _
** Ready to use logging facilities
** Smart handler for HEAD or similar
** Adding component to ease the caching functionalities
** Adding Session support
** URL rewriting ?
** Mass testing
** ...
## Eiffel Web Nino
* _Suggested by **Javier & Jocelyn**_
* _Supervisor_:
* _Suitability_: TODO
* _Description_: Currently Eiffel Web Nino, is a standalone httpd server written in Eiffel. It is great for development, or embedding httpd component in application. However there are room for improvement so that one can also use it as replacement for apache, iis, ... To reach this state, here are a list of task that should be achieved:
** Implement persistent connection
** Complete implementation of Eiffel Web Nino using pool of threads
** Complete migration of Eiffel Web Nino to SCOOP
** Improve Nino to become a real solution to host any web services/sites
** ...
## New EWF connectors
* _Suggested by **Jocelyn & Javier**_
* _Supervisor_:
* _Suitability_: TODO
* _Description_: EWF is relying on the notion of "connector" to achieve portability on various platform and underlying httpd server, currently EWF support any CGI or libFCGI system (i.e apache, IIS, ...), and provide a standalone version thanks to Eiffel Web Nino. The goal now, would be to support specific connector for:
** LightHTTP (http://www.lighttpd.net/)
** nginx (http://nginx.org/en/)
## Concurrenty and EWF
* _Suggested by **Jocelyn**_
* _Supervisor_:
* _Suitability_: TODO
* _Description_: Check that EWF is compliant with concurrency (Eiffel Thread, and SCOOP), and provide an example using concurrency.
## Design and build something like Ruby on Rails or Grails
* _Suggested by **Javier**_
* _Supervisor_:
* _Suitability_: TODO
* _Description_: Using EWF, design and build the set of tools to provide a conventional MVC to create Web sites. This could be useful even if this is not the taste of everyone.
## Provide a Websocket implementation
* _Suggested by **Jocelyn**_
* _Supervisor_:
* _Suitability_: TODO
* _Description_: Provide an implementation of websocket with EWF and eventually Eiffel Web Nino, then demonstrate it on a simple example. WebSocket is a web technology providing for bi-directional, full-duplex communications channels over a single TCP connection.
* See http://en.wikipedia.org/wiki/Websocket
----
# Usage of EWF
## HAL browser
* _Suggested by **Javier**_
* _Supervisor_:
* _Suitability_: TODO
* _Description_: Build a HAL browser to discover an API using HAL mediatype. The browser will be able to follow the links, and display the transmitted data. This could be a vision2 application inspired by http://haltalk.herokuapp.com/explorer/hal_browser.html#/. HAL stands for Hypertext Application Language see 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/
## Build a simple CMS with EWF
* _Suggested by **Jocelyn**_
* _Supervisor_:
* _Suitability_: TODO
* _Description_: Using EWF, Build a simple CMS (Content Management System) framework and then an example. It should provide common features such as:
- user management (register, login, lost password -> send email)
- page editing
- blog
- template / theme
- persistency / storage / ...
- extension at compilation time
* The result should be usable by any user to build his own CMS website, and extend it easily.
## Build P2P connector
* _Suggested by **Jocelyn**_
* _Supervisor_:
* _Suitability_: TODO
* _Description_: Imagine you want to publish a website (or web service, API) running on your machine (behind firewall). One would need to initiate the connection via a public website, this is common for P2P software such as remote assistance (i.e: join.me, teamviewer, showmypc, ...)
----
# Libraries
## Hypermedia API library to work with XHTML
* _Suggested by **Javier**_
* _Supervisor_:
* _Suitability_: TODO
* _Description_: Use XHTML as a media type to for hypermedia API. See http://codeartisan.blogspot.com.ar/2012/07/using-html-as-media-type-for-your-api.html
## Add support for Mediatype such as RSS, ATOM, ...
* _Suggested by **Jocelyn**_
* _Supervisor_:
* _Suitability_: TODO
* _Description_: In addition to JSON, HAL, Collection+JSON, XHTML, application might want to support (read and write) standard media type such as RSS, ATOM, ...
## Security: provide popular authentication mechanisms
* _Suggested by **Jocelyn**_
* _Supervisor_:
* _Suitability_: TODO
* _Description_: Any web service, web site, API need a reliable authentication mechanism, the could be on the server side or the client side to build mashup service (integrate with other web API such as google, flicker, ...). So far, EWF provides only basic HTTP Authorization, and application would need more solutions such as :
- OAuth: consumer and provider
- OpenID
- Google Connect
- Facebook Connect
* The goal is to provide component to consume other popular API/service, but also component for your own service so that other can consume it.
## Security: provide popular authentication mechanisms
* _Suggested by **Jocelyn**_
* _Supervisor_:
* _Suitability_: TODO
* _Description_: Any web service, web site, API need a reliable authentication mechanism, the could be on the server side or the client side to build mashup service (integrate with other web API such as google, flicker, ...). So far, EWF provides only basic HTTP Authorization, and application would need more solutions such as :
- OAuth: consumer and provider
- OpenID
- Google Connect
- Facebook Connect
* The goal is to provide component to consume other popular API/service, but also component for your own service so that other can consume it.
## Provide a SSO (Single Sign On) implementation (server, and clients)
* _Suggested by **Jocelyn**_
* _Supervisor_:
* _Suitability_: TODO
* _Description_: Design and build a Single Sign On implementation for Eiffel. That should include the authentication server, and at least one Eiffel client component (it would be convenient to also provide php, js, ...). In the same spirit, having Eiffel client for popular SSO server would be appreciated as well.
* _Reference_:
- http://en.wikipedia.org/wiki/Single_sign-on
- http://en.wikipedia.org/wiki/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.
* 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.
* Then , a nice addition would be to render those lightweight markup lang into Vision2 widget (not related to EWF, but could be useful to build (editor) desktop application)
## library: Web component to build HTML5 widget
* _Suggested by **Jocelyn**_
* _Supervisor_:
* _Suitability_: TODO
* _Description_: Build set of Eiffel components to ease development of websites. First this should be based on HTML5. Idea for components:
- table widget (with sorting ...)
- suggestive typing widget
- tab ...
- WYSIWYG textarea widget (could reuse existing Javascript solution TinyMCE, CKEditor, OpenWysiwyg, ...)
- ...
----
# Clients
## Libraries: Reusable Client Design based on J.Moore Presentation
* _Suggested by **Javier**_
* _Supervisor_:
* _Suitability_: TODO
* _Description_: TODO
* Generic client that can be customized (see design in slide 12)
* http://s3.amazonaws.com/cimlabs/Oredev-Hypermedia-APIs.pdf
* video 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
## Add SSL support to Eiffel Net
* _Suggested by **Jocelyn**_
* _Supervisor_:
* _Suitability_: TODO
* _Description_: Currently Eiffel Net does not provide any support to SSL (thus no HTTPS). For now Eiffel application often use the Eiffel cURL wrapper which provide SSL, but it would be more convenient to use directly Eiffel Net. Then find solution to add SSL support to EiffelNet, or to extend EiffelNet with an EiffelNet+OpenSSL solution, or other.
## Build clients to consume popular RESTful APIs
* _Suggested by **Jocelyn & Javier**_
* _Supervisor_:
* _Suitability_: TODO
* _Description_: Build Eiffel libraries to consume popular web APIs, such as:
- Google Discovery APIs
- Twitter
- Facebook
- Github
- Flickr
- ... etc
* This should reuse and improve the "http_client" provided by EWF. Eventually also write the EiffelNet implementation to be independant from cURL
* **Requirement**: OAuth client eiffel component
----
# Feel free to add new idea below this line
----
Use the following page [[Projects new suggestions]] to suggest new project, or request a feature.

View File

@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-9-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-9-0 http://www.eiffel.com/developers/xml/configuration-1-9-0.xsd" name="filter" uuid="7C9887BD-4AE4-47F2-A0AA-4BBB6736D433">
<target name="filter">
<root class="FILTER_SERVER" feature="make"/>
<file_rule>
<exclude>/EIFGENs$</exclude>
<exclude>/\.git$</exclude>
<exclude>/\.svn$</exclude>
</file_rule>
<option debug="true" warning="true" full_class_checking="true" is_attached_by_default="true" void_safety="all" syntax="provisional">
<debug name="nino" enabled="true"/>
<assertions precondition="true" postcondition="true" invariant="true" supplier_precondition="true"/>
</option>
<setting name="concurrency" value="thread"/>
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
<library name="connector_nino" location="..\..\library\server\ewsgi\connectors\nino\nino-safe.ecf" readonly="false">
<option debug="true">
<debug name="nino" enabled="true"/>
</option>
</library>
<library name="default_nino" location="..\..\library\server\wsf\default\nino-safe.ecf" readonly="false"/>
<library name="eel" location="..\..\contrib\ise_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"/>
<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"/>
<library name="wsf_extension" location="..\..\library\server\wsf_extension\wsf_extension-safe.ecf" readonly="false"/>
<library name="http_authorization" location="..\..\library\server\authentication\http_authorization\http_authorization-safe.ecf" readonly="false"/>
<library name="time" location="$ISE_LIBRARY\library\time\time-safe.ecf"/>
<cluster name="src" location="src\" recursive="true"/>
</target>
</system>

View File

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

View File

@@ -0,0 +1,30 @@
note
description: "Summary description for {DATABASE_API}."
author: ""
date: "$Date$"
revision: "$Revision$"
class
DATABASE_API
create
make
feature -- Initialization
make
local
l_user: USER
do
create users.make (10)
create l_user.make (1, "foo", "bar")
users.put (l_user, l_user.id)
end
feature -- Access
users: HASH_TABLE [USER, INTEGER]
;note
copyright: "2011-2011, Javier Velilla and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
end

View File

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

View File

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

View File

@@ -0,0 +1,40 @@
note
description: "User."
author: ""
date: "$Date$"
revision: "$Revision$"
class
USER
create
make
feature {NONE} -- Initialization
make (an_id: INTEGER; a_name, a_password: STRING)
do
id := an_id
name := a_name
password := a_password
ensure
id_set: id = an_id
name_set: name = a_name
password_set: password = a_password
end
feature -- Access
id: INTEGER
-- Identifier
name: STRING
-- Name
password: STRING
-- Password
;note
copyright: "2011-2011, Javier Velilla and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
end

View File

@@ -0,0 +1,50 @@
note
description: "Authentication filter."
author: "Olivier Ligot"
date: "$Date$"
revision: "$Revision$"
class
AUTHENTICATION_FILTER [C -> WSF_URI_TEMPLATE_HANDLER_CONTEXT]
inherit
WSF_FILTER_HANDLER [C]
SHARED_DATABASE_API
feature -- Basic operations
execute (ctx: C; req: WSF_REQUEST; res: WSF_RESPONSE)
-- Execute the filter
local
l_auth: 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
attached Db_access.users.item (1) as l_user and then
(attached l_auth.login as l_auth_login and then l_auth_login.is_equal (l_user.name)
and attached l_auth.password as l_auth_password and then l_auth_password.is_equal (l_user.password)) then
execute_next (ctx, req, res)
else
handle_unauthorized ("Unauthorized", ctx, req, res)
end
end
feature {NONE} -- Implementation
handle_unauthorized (a_description: STRING; ctx: C; req: WSF_REQUEST; res: WSF_RESPONSE)
-- Handle forbidden.
local
h: HTTP_HEADER
do
create h.make
h.put_content_type_text_plain
h.put_content_length (a_description.count)
h.put_current_date
h.put_header_key_value ({HTTP_HEADER_NAMES}.header_www_authenticate, "Basic realm=%"User%"")
res.set_status_code ({HTTP_STATUS_CODE}.unauthorized)
res.put_header_text (h.string)
res.put_string (a_description)
end
end

View File

@@ -0,0 +1,40 @@
note
description: "Logging filter."
author: "Olivier Ligot"
date: "$Date$"
revision: "$Revision$"
class
LOGGING_FILTER
inherit
WSF_FILTER
feature -- Basic operations
execute (req: WSF_REQUEST; res: WSF_RESPONSE)
-- Execute the filter
local
l_user_agent: STRING
l_date: DATE_TIME
do
if attached req.http_user_agent as ua then
l_user_agent := ua.as_string_8
else
l_user_agent := "-"
end
create l_date.make_now
io.put_string ("[" + l_date.formatted_out (Date_time_format) + "] %"" + req.request_method + " " + req.request_uri
+ " " + {HTTP_CONSTANTS}.http_version_1_1 + "%" " + res.status_code.out + " " + l_user_agent)
io.put_new_line
execute_next (req, res)
end
feature -- Constants
Date_time_format: STRING = "yyyy/[0]mm/[0]dd [0]hh:[0]mi:[0]ss.ff3"
note
copyright: "2011-2012, Javier Velilla and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
end

View File

@@ -0,0 +1,109 @@
note
description : "Filter example."
author : "Olivier Ligot"
date : "$Date$"
revision : "$Revision$"
class
FILTER_SERVER
inherit
ANY
WSF_URI_TEMPLATE_FILTERED_SERVICE
WSF_HANDLER_HELPER
WSF_DEFAULT_SERVICE
SHARED_EJSON
create
make
feature {NONE} -- Initialization
make
do
initialize_filter
initialize_json
set_service_option ("port", 9090)
make_and_launch
end
create_filter
-- Create `filter'
local
l_router: WSF_URI_TEMPLATE_ROUTER
l_authentication_filter: AUTHENTICATION_FILTER [WSF_URI_TEMPLATE_HANDLER_CONTEXT]
l_user_filter: USER_HANDLER [WSF_URI_TEMPLATE_HANDLER_CONTEXT]
l_user_handler: WSF_HANDLER [WSF_URI_TEMPLATE_HANDLER_CONTEXT]
l_routing_filter: WSF_ROUTING_FILTER [WSF_HANDLER [WSF_URI_TEMPLATE_HANDLER_CONTEXT], WSF_URI_TEMPLATE_HANDLER_CONTEXT]
do
create l_router.make (1)
create l_authentication_filter
create l_user_filter
l_authentication_filter.set_next (l_user_filter)
l_user_handler := l_authentication_filter
l_router.map_with_request_methods ("/user/{userid}", l_user_handler, << "GET" >>)
create l_routing_filter.make (l_router)
l_routing_filter.set_execute_default_action (agent execute_default)
filter := l_routing_filter
end
setup_filter
-- Setup `filter'
local
l_logging_filter: LOGGING_FILTER
do
create l_logging_filter
filter.set_next (l_logging_filter)
end
initialize_json
-- Initialize `json'.
do
json.add_converter (create {JSON_USER_CONVERTER}.make)
end
feature -- Basic operations
execute (req: WSF_REQUEST; res: WSF_RESPONSE)
do
filter.execute (req, res)
end
execute_default (req: WSF_REQUEST; res: WSF_RESPONSE)
-- I'm using this method to handle the method not allowed response
-- in the case that the given uri does not have a corresponding http method
-- to handle it.
local
h : HTTP_HEADER
l_description : STRING
l_api_doc : STRING
do
if req.content_length_value > 0 then
req.input.read_string (req.content_length_value.as_integer_32)
end
create h.make
h.put_content_type_text_plain
l_api_doc := "%NPlease check the API%NURI:/user/{userid} METHOD: GET%N"
l_description := req.request_method + req.request_uri + " is not allowed" + "%N" + l_api_doc
h.put_content_length (l_description.count)
h.put_current_date
res.set_status_code ({HTTP_STATUS_CODE}.method_not_allowed)
res.put_header_text (h.string)
res.put_string (l_description)
end
note
copyright: "2011-2012, Javier Velilla and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[
Eiffel Software
5949 Hollister Ave., Goleta, CA 93117 USA
Telephone 805-685-1006, Fax 805-685-6869
Website http://www.eiffel.com
Customer support http://support.eiffel.com
]"
end

View File

@@ -0,0 +1,86 @@
note
description: "User handler."
author: "Olivier Ligot"
date: "$Date$"
revision: "$Revision$"
class
USER_HANDLER [C -> WSF_HANDLER_CONTEXT]
inherit
WSF_FILTER_HANDLER [C]
WSF_RESOURCE_HANDLER_HELPER [C]
redefine
do_get
end
SHARED_DATABASE_API
SHARED_EJSON
feature -- Basic operations
execute (ctx: C; req: WSF_REQUEST; res: WSF_RESPONSE)
-- Execute request handler
do
execute_methods (ctx, req, res)
end
do_get (ctx: C; req: WSF_REQUEST; res: WSF_RESPONSE)
-- Using GET to retrieve resource information.
-- If the GET request is SUCCESS, we response with
-- 200 OK, and a representation of the user
-- If the GET request is not SUCCESS, we response with
-- 404 Resource not found
local
id : STRING
do
if attached req.orig_path_info as orig_path then
id := get_user_id_from_path (orig_path)
if attached retrieve_user (id) as l_user then
compute_response_get (ctx, req, res, l_user)
else
handle_resource_not_found_response ("The following resource " + orig_path + " is not found ", ctx, req, res)
end
end
end
feature {NONE} -- Implementation
compute_response_get (ctx: C; req: WSF_REQUEST; res: WSF_RESPONSE; l_user : USER)
local
h: HTTP_HEADER
l_msg : STRING
do
create h.make
h.put_content_type_application_json
if attached {JSON_VALUE} json.value (l_user) as jv then
l_msg := jv.representation
h.put_content_length (l_msg.count)
if attached req.request_time as time then
h.add_header ("Date:" + time.formatted_out ("ddd,[0]dd mmm yyyy [0]hh:[0]mi:[0]ss.ff2") + " GMT")
end
res.set_status_code ({HTTP_STATUS_CODE}.ok)
res.put_header_text (h.string)
res.put_string (l_msg)
end
end
get_user_id_from_path (a_path: READABLE_STRING_32) : STRING
do
Result := a_path.split ('/').at (3)
end
retrieve_user (id: STRING) : detachable USER
-- Retrieve the user by id if it exist, in other case, Void
do
if id.is_integer and then Db_access.users.has (id.to_integer) then
Result := db_access.users.item (id.to_integer)
end
end
note
copyright: "2011-2012, Javier Velilla and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
end

View File

@@ -17,10 +17,15 @@ or go to [[step_3.wiki|step 3]]
** This is used to build the application in a portable manner, but for this compilation, it uses Eiffel Web Nino as connector.
** We use Eiffel Web Nino for this tutorial, because there is no need to configure any apache, iis, and so on. And it is convenient to execute inside EiffelStudio
* To see the result, you should open http://localhost/ on your web browser. Note if the application is using another port such as 9999, you should open http://localhost:9999/
* You will find inside [[step_2]] the "hello" project
** target "hello" provides a very simple implementation (But by default, it is using port 80 with Eiffel Web Nino, which might already be busy by other application)
** target "hello_custom" which uses almost the same code, but in addition, you can use the ewf.ini file to precise the port number (9999 for this example)
* To see the result, open http://localhost/ in a web browser.
* Note, if the application is using port different from 80, such as 9999, open http://localhost:9999/
* Eiffel code
class
HELLO_APPLICATION

View File

@@ -390,23 +390,33 @@ feature -- Date
put_utc_date (create {DATE_TIME}.make_now_utc)
end
put_utc_date (dt: DATE_TIME)
put_utc_date (a_utc_date: DATE_TIME)
-- Put UTC date time `dt' with "Date" header
do
put_date (date_to_rfc1123_http_date_format (dt))
put_date (date_to_rfc1123_http_date_format (a_utc_date))
end
put_last_modified (dt: DATE_TIME)
put_last_modified (a_utc_date: DATE_TIME)
-- Put UTC date time `dt' with "Date" header
do
put_header_key_value ({HTTP_HEADER_NAMES}.header_last_modified, date_to_rfc1123_http_date_format (dt))
put_header_key_value ({HTTP_HEADER_NAMES}.header_last_modified, date_to_rfc1123_http_date_format (a_utc_date))
end
feature -- Others
feature -- Others
put_expires (n: INTEGER)
put_expires (sec: INTEGER)
do
put_header_key_value ("Expires", n.out)
put_expires_string (sec.out)
end
put_expires_string (s: STRING)
do
put_header_key_value ("Expires", s)
end
put_expires_date (a_utc_date: DATE_TIME)
do
put_header_key_value ("Expires", date_to_rfc1123_http_date_format (a_utc_date))
end
put_cache_control (s: READABLE_STRING_8)

View 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

View File

@@ -0,0 +1,52 @@
note
description: "Objects than can pre-process incoming data and post-process outgoing data."
author: "Olivier Ligot"
date: "$Date$"
revision: "$Revision$"
deferred class
WSF_FILTER
feature -- Access
next: detachable WSF_FILTER
-- Next filter
feature -- Element change
set_next (a_next: WSF_FILTER)
-- Set `next' to `a_next'
do
next := a_next
ensure
next_set: next = a_next
end
feature -- Basic operations
execute (req: WSF_REQUEST; res: WSF_RESPONSE)
-- Execute the filter.
deferred
end
feature {NONE} -- Implementation
execute_next (req: WSF_REQUEST; res: WSF_RESPONSE)
-- Execute the `next' filter.
do
if attached next as n then
n.execute (req, res)
end
end
note
copyright: "2011-2012, Jocelyn Fiat, Javier Velilla, 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

View File

@@ -0,0 +1,51 @@
note
description: "[
Handler that can also play the role of a filter, i.e.
than can pre-process incoming data and post-process outgoing data.
]"
author: ""
date: "$Date$"
revision: "$Revision$"
deferred class
WSF_FILTER_HANDLER [C -> WSF_HANDLER_CONTEXT]
inherit
WSF_HANDLER [C]
feature -- Access
next: detachable WSF_FILTER_HANDLER [C]
-- Next filter
feature -- Element change
set_next (a_next: WSF_FILTER_HANDLER [C])
-- Set `next' to `a_next'
do
next := a_next
ensure
next_set: next = a_next
end
feature {NONE} -- Implementation
execute_next (ctx: C; req: WSF_REQUEST; res: WSF_RESPONSE)
-- Execute the `next' filter.
do
if attached next as n then
n.execute (ctx, req, res)
end
end
note
copyright: "2011-2012, Jocelyn Fiat, Javier Velilla, 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

View File

@@ -0,0 +1,75 @@
note
description: "Routing filter."
author: "Olivier Ligot"
date: "$Date$"
revision: "$Revision$"
class
WSF_ROUTING_FILTER [H -> WSF_HANDLER [C], C -> WSF_HANDLER_CONTEXT]
inherit
WSF_FILTER
create
make
feature {NONE} -- Initialization
make (a_router: WSF_ROUTER [H, C])
do
router := a_router
ensure
router_set: router = a_router
end
feature -- Access
router: WSF_ROUTER [H, C]
-- Router
execute_default_action: detachable PROCEDURE [ANY, TUPLE [req: WSF_REQUEST; res: WSF_RESPONSE]]
-- `execute_default' action
feature -- Element change
set_execute_default_action (an_action: like execute_default_action)
-- Set `execute_default_action' to `an_action'
do
execute_default_action := an_action
ensure
execute_default_action_set: execute_default_action = an_action
end
feature -- Basic operations
execute (req: WSF_REQUEST; res: WSF_RESPONSE)
-- Execute the filter
do
if attached router.route (req) as r then
router.execute_route (r, req, res)
else
execute_default (req, res)
end
execute_next (req, res)
end
execute_default (req: WSF_REQUEST; res: WSF_RESPONSE)
do
if attached execute_default_action as action then
action.call ([req, res])
else
do_nothing
end
end
note
copyright: "2011-2012, Jocelyn Fiat, Javier Velilla, 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

View File

@@ -0,0 +1,48 @@
note
description: "Summary description for {WSF_URI_TEMPLATE_FILTERED_SERVICE}."
author: ""
date: "$Date$"
revision: "$Revision$"
deferred class
WSF_URI_TEMPLATE_FILTERED_SERVICE
feature {NONE} -- Initialization
initialize_filter
-- Initialize `filter'
do
create_filter
setup_filter
end
create_filter
-- Create `filter'
deferred
ensure
filter_created: filter /= Void
end
setup_filter
-- Setup `filter'
require
filter_created: filter /= Void
deferred
end
feature -- Access
filter: WSF_FILTER
-- Filter
;note
copyright: "2011-2012, Jocelyn Fiat, Javier Velilla, 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

View File

@@ -45,6 +45,11 @@ feature -- Status report
is_string: BOOLEAN = False
-- Is Current as a WSF_STRING representation?
is_empty: BOOLEAN
do
Result := value = Void
end
feature -- Query
string_representation: STRING_32

View File

@@ -97,6 +97,12 @@ feature -- Status report
Result := values.count = 1
end
is_empty: BOOLEAN
-- Is Current empty?
do
Result := values.is_empty
end
feature -- Conversion
as_string: WSF_STRING

View File

@@ -97,6 +97,12 @@ feature -- Status report
end
end
is_empty: BOOLEAN
-- Is Current empty?
do
Result := values.is_empty
end
feature -- Conversion
as_string: WSF_STRING
@@ -108,6 +114,59 @@ feature -- Conversion
end
end
as_array_of_string: detachable ARRAY [READABLE_STRING_32]
-- Return an array of STRING if possible., otherwise Void
local
i,n: INTEGER
nb: INTEGER
do
from
i := 1
n := count
create Result.make_filled ("", 1, n)
until
i > n or Result = Void
loop
if attached {WSF_STRING} value (i.out) as s then
Result.put (s.value, i)
nb := nb + 1
else
Result := Void
end
i := i + 1
end
if Result /= Void and then nb /= n then
Result := Void
end
ensure
is_array_of_string implies Result /= Void and then Result.count = count and then Result.lower = 1
end
is_array_of_string: BOOLEAN
-- Is Current representable as an array of string?
local
i,n, nb: INTEGER
do
from
i := 1
n := count
nb := 0
Result := True
until
i > n or not Result
loop
if attached {WSF_STRING} value (i.out) then
nb := nb + 1
else
Result := False
end
i := i + 1
end
if nb /= n then
Result := False
end
end
feature -- Element change
add_value (a_value: WSF_VALUE; k: READABLE_STRING_32)

View File

@@ -44,7 +44,13 @@ feature -- Element change
feature -- Status report
is_string: BOOLEAN = False
-- Is Current as a WSF_STRING representation?
-- Is Current as a WSF_STRING representation?
is_empty: BOOLEAN
-- Is Current empty?
do
Result := size = 0
end
feature -- Conversion

View File

@@ -35,6 +35,12 @@ feature -- Status report
deferred
end
is_empty: BOOLEAN
-- Is Current empty?
--| i.e empty string, empty table, ...
deferred
end
feature -- Query
as_string: WSF_STRING

View File

@@ -42,18 +42,23 @@ feature {NONE} -- Initialization
local
h: like header
do
get_file_exists
create h.make
header := h
h.put_content_type (content_type)
if attached file_last_modified as dt then
h.put_last_modified (dt)
end
get_file_size
if file_size = 0 then
set_status_code ({HTTP_STATUS_CODE}.not_found)
if file_exists then
if attached file_last_modified as dt then
h.put_last_modified (dt)
end
get_file_size
if file_size = 0 then
set_status_code ({HTTP_STATUS_CODE}.not_found)
else
set_status_code ({HTTP_STATUS_CODE}.ok)
end
else
set_status_code ({HTTP_STATUS_CODE}.ok)
set_status_code ({HTTP_STATUS_CODE}.not_found)
end
update_content_length
end
@@ -62,12 +67,16 @@ feature {NONE} -- Initialization
local
n: INTEGER
do
n := file_size
if attached head as h then
n := n + h.count
end
if attached bottom as b then
n := n + b.count
if file_exists then
n := file_size
if attached head as h then
n := n + h.count
end
if attached bottom as b then
n := n + b.count
end
else
n := 0
end
content_length := n
header.put_content_length (n)
@@ -75,9 +84,14 @@ feature {NONE} -- Initialization
feature -- Element change
set_expires (t: INTEGER)
set_expires_in_seconds (sec: INTEGER)
do
header.put_expires (t)
set_expires (sec.out)
end
set_expires (s: STRING)
do
header.put_expires_string (s)
end
set_no_cache
@@ -104,6 +118,9 @@ feature -- Access
file_name: READABLE_STRING_8
file_exists: BOOLEAN
-- File exists?
file_size: INTEGER
-- Size of file named `file_name'
@@ -157,42 +174,54 @@ feature {WSF_RESPONSE} -- Output
s: detachable READABLE_STRING_8
do
res.set_status_code (status_code)
res.put_header_text (header.string)
s := head
if s /= Void then
res.put_string (s)
end
if not answer_head_request_method then
send_file_content_to (file_name, res)
end
s := bottom
if s /= Void then
res.put_string (s)
if status_code = {HTTP_STATUS_CODE}.not_found then
else
res.put_header_text (header.string)
s := head
if s /= Void then
res.put_string (s)
end
if not answer_head_request_method then
send_file_content_to (file_name, res)
end
s := bottom
if s /= Void then
res.put_string (s)
end
end
end
feature {NONE} -- Implementation: file system helper
get_file_size
-- Get `file_size' from file named `file_name'
get_file_exists
-- Get `file_exists'
local
f: RAW_FILE
do
create f.make (file_name)
if f.exists then
file_size := f.count
end
file_exists := f.exists
end
get_file_size
-- Get `file_size' from file named `file_name'
require
file_exists: file_exists
local
f: RAW_FILE
do
create f.make (file_name)
file_size := f.count
end
file_last_modified: detachable DATE_TIME
-- Get `file_size' from file named `file_name'
require
file_exists: file_exists
local
f: RAW_FILE
do
create f.make (file_name)
if f.exists then
create Result.make_from_epoch (f.change_date)
end
create Result.make_from_epoch (f.change_date)
end
file_extension (fn: STRING): STRING
@@ -231,11 +260,12 @@ feature {NONE} -- Implementation: output
require
string_not_empty: not fn.is_empty
is_readable: (create {RAW_FILE}.make (fn)).is_readable
file_exists: file_exists
local
f: RAW_FILE
do
create f.make (fn)
check f.exists and then f.is_readable end
check f.is_readable end
f.open_read
from

View File

@@ -177,6 +177,18 @@ feature -- Helper
Result := request_method.is_case_insensitive_equal (m.as_string_8)
end
is_post_request_method: BOOLEAN
-- Is Current a POST request method?
do
Result := request_method.is_case_insensitive_equal ({HTTP_REQUEST_METHODS}.method_post)
end
is_get_request_method: BOOLEAN
-- Is Current a GET request method?
do
Result := request_method.is_case_insensitive_equal ({HTTP_REQUEST_METHODS}.method_get)
end
feature -- Eiffel WGI access
wgi_version: READABLE_STRING_8
@@ -259,6 +271,36 @@ feature -- Access: global variable
end
end
string_array_item (a_name: READABLE_STRING_GENERAL): detachable ARRAY [READABLE_STRING_32]
-- Array of string values for path parameter `a_name' if relevant.
do
Result := string_array_item_for (a_name, agent item)
end
string_array_item_for (a_name: READABLE_STRING_GENERAL; a_item_fct: FUNCTION [ANY, TUPLE [READABLE_STRING_GENERAL], detachable WSF_VALUE]): detachable ARRAY [READABLE_STRING_32]
-- Array of string values for query parameter `a_name' if relevant.
local
i: INTEGER
n: INTEGER
do
from
i := 1
n := 1
create Result.make_filled ("", 1, 5)
until
i = 0
loop
if attached {WSF_STRING} a_item_fct.item ([a_name + "[" + i.out + "]"]) as v then
Result.force (v.value, n)
n := n + 1
i := i + 1
else
i := 0 -- Exit
end
end
Result.keep_head (n - 1)
end
feature -- Execution variables
execution_variable (a_name: READABLE_STRING_GENERAL): detachable ANY
@@ -1231,6 +1273,7 @@ feature {NONE} -- Implementation: smart parameter identification
k.append_integer (tb.count + 1)
end
v := tb
create n.make_from_string (n)
n.append_character ('[')
n.append (k)
n.append_character (']')
@@ -1280,6 +1323,9 @@ feature {NONE} -- Implementation: smart parameter identification
--| Already done in previous part
elseif attached {WSF_MULTIPLE_STRING} l_existing_value as l_multi then
l_multi.add_value (v)
elseif attached {WSF_TABLE} l_existing_value as l_table then
-- Keep previous values (most likely we have table[1]=foo, table[2]=bar ..and table=/foo/bar
-- Anyway for this case, we keep the previous version, and ignore this "conflict"
else
a_table.force (create {WSF_MULTIPLE_STRING}.make_with_array (<<l_existing_value, v>>), v.name)
check replaced: a_table.found and then a_table.found_item ~ l_existing_value end

View File

@@ -36,19 +36,25 @@ feature -- Encoder
encoded_string (s: READABLE_STRING_32): STRING_8
-- HTML-encoded value of `s'.
do
Result := general_encoded_string (s)
end
general_encoded_string (s: READABLE_STRING_GENERAL): STRING_8
-- The HTML-encoded equivalent of the given string
-- HTML-encoded value of `s'.
local
i, n: INTEGER
uc: CHARACTER_32
l_code: INTEGER
l_code: NATURAL_32
c: CHARACTER_8
do
has_error := False
create Result.make (s.count + s.count // 10)
n := s.count
from i := 1 until i > n loop
uc := s.item (i)
if uc.is_character_8 then
c := uc.to_character_8
l_code := s.code (i)
if l_code.is_valid_character_8_code then
c := l_code.to_character_8
inspect c
when '%T', '%N', '%R' then
@@ -59,7 +65,6 @@ feature -- Encoder
when '<' then Result.append_string ("&lt;")
when '>' then Result.append_string ("&gt;")
else
l_code := c.code
if
l_code <= 31 or -- Hexa 1F
l_code = 127 -- Hexa 7F
@@ -77,7 +82,7 @@ feature -- Encoder
end
else
Result.append ("&#")
Result.append (uc.code.out)
Result.append (l_code.out)
Result.extend (';')
end
i := i + 1

View File

@@ -35,18 +35,24 @@ feature -- Encoder
encoded_string (s: READABLE_STRING_32): STRING_8
-- URL-encoded value of `s'.
do
Result := general_encoded_string (s)
end
general_encoded_string (s: READABLE_STRING_GENERAL): STRING_8
-- URL-encoded value of `s'.
local
i, n: INTEGER
uc: CHARACTER_32
c: CHARACTER_8
l_code: NATURAL_32
do
has_error := False
create Result.make (s.count + s.count // 10)
n := s.count
from i := 1 until i > n loop
uc := s.item (i)
if uc.is_character_8 then
c := uc.to_character_8
l_code := s.code (i)
if l_code.is_valid_character_8_code then
c := l_code.to_character_8
inspect c
when
'A' .. 'Z',
@@ -55,20 +61,20 @@ feature -- Encoder
then
Result.extend (c)
else
Result.append (url_encoded_char (uc))
Result.append (url_encoded_char (l_code))
end
else
Result.append (url_encoded_char (uc))
Result.append (url_encoded_char (l_code))
end
i := i + 1
end
end
partial_encoded_string (s: READABLE_STRING_32; a_ignore: ARRAY [CHARACTER]): READABLE_STRING_8
partial_encoded_string (s: READABLE_STRING_GENERAL; a_ignore: ARRAY [CHARACTER]): STRING_8
-- URL-encoded value of `s'.
local
i, n: INTEGER
uc: CHARACTER_32
l_code: NATURAL_32
c: CHARACTER_8
s8: STRING_8
do
@@ -77,9 +83,9 @@ feature -- Encoder
Result := s8
n := s.count
from i := 1 until i > n loop
uc := s.item (i)
if uc.is_character_8 then
c := uc.to_character_8
l_code := s.code (i)
if l_code.is_valid_character_8_code then
c := l_code.to_character_8
inspect c
when
'A' .. 'Z',
@@ -91,14 +97,14 @@ feature -- Encoder
if a_ignore.has (c) then
s8.extend (c)
else
s8.append (url_encoded_char (uc))
s8.append (url_encoded_char (l_code))
end
end
else
if a_ignore.has (c) then
s8.extend (c)
else
s8.append (url_encoded_char (uc))
s8.append (url_encoded_char (l_code))
end
end
i := i + 1
@@ -107,12 +113,12 @@ feature -- Encoder
feature {NONE} -- encoder character
url_encoded_char (uc: CHARACTER_32): STRING_8
url_encoded_char (a_code: NATURAL_32): STRING_8
do
create Result.make (3)
if uc.is_character_8 then
if a_code.is_valid_character_8_code then
Result.extend ('%%')
Result.append (uc.code.to_hex_string)
Result.append (a_code.to_hex_string)
from
until
Result.count < 2 or else Result[2] /= '0'

View File

@@ -17,6 +17,7 @@ inherit
redefine
default_create,
name,
general_encoded_string,
encoded_string, partial_encoded_string,
decoded_string
end
@@ -49,7 +50,17 @@ feature -- Encoder
Result := Precursor (Result)
end
partial_encoded_string (s: READABLE_STRING_32; a_ignore: ARRAY [CHARACTER]): READABLE_STRING_8
general_encoded_string (s: READABLE_STRING_GENERAL): STRING_8
do
if attached {READABLE_STRING_32} s as s32 then
Result := utf32_to_utf8 (s32)
else
Result := s.as_string_8
end
Result := Precursor (Result)
end
partial_encoded_string (s: READABLE_STRING_GENERAL; a_ignore: ARRAY [CHARACTER]): STRING_8
-- URL-encoded value of `s'.
do
Result := Precursor (s, a_ignore)

View File

@@ -24,7 +24,7 @@ create {URI_TEMPLATE}
make_from_uri_template
convert
make ({READABLE_STRING_8})
make ({READABLE_STRING_8, STRING_8})
feature {NONE} -- Initialization

View File

@@ -0,0 +1,4 @@
NAME="EWF: Eiffel Web Framework application, multi-platform, with EWF source code"
DESCRIPTION="Create a web server application based on the cross-platform library EWF. Using the original source code of EWF (i.e: not the one in $ISE_LIBRARY)"
LOCATION="ewf_custom"
PLATFORM="all"

View File

@@ -0,0 +1,21 @@
<?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="${WIZ:TARGET_NAME}" uuid="${WIZ:UUID}">
<target name="${WIZ:TARGET_NAME}">
<root class="EWF_APPLICATION" feature="make_and_launch"/>
<file_rule>
<exclude>/EIFGENs$</exclude>
<exclude>/CVS$</exclude>
<exclude>/.svn$</exclude>
</file_rule>
<option warning="true" is_attached_by_default="true" void_safety="all" syntax="transitional">
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
</option>
<setting name="concurrency" value="${WIZ:CONCURRENCY}"/>
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
<library name="default_${WIZ:EWF_CONNECTOR}" location="${WIZ:EWF_DIR}/library/server/wsf/default/${WIZ:EWF_CONNECTOR}-safe.ecf"/>
<library name="encoder" location="${WIZ:EWF_DIR}/library/text/encoder/encoder-safe.ecf" readonly="false"/>
<library name="wsf" location="${WIZ:EWF_DIR}/library/server/wsf/wsf-safe.ecf" readonly="false"/>
<library name="http" location="${WIZ:EWF_DIR}/library/network/protocol/http/http-safe.ecf" readonly="false"/>
<cluster name="src" location=".\src\" recursive="true"/>
</target>
</system>

View File

@@ -1,4 +1,4 @@
NAME="Eiffel Web Framework application, multi-platform, with EWF"
NAME="EWF: Eiffel Web Framework application, multi-platform, with EWF"
DESCRIPTION="Create a web server application based on the cross-platform library EWF."
LOCATION="ewf"
PLATFORM="all"

View File

@@ -1,6 +1,6 @@
<?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="ewf_ise_wizard" uuid="F881A707-745E-4C6D-90D1-F820EE3B1470">
<target name="ewf_ise_wizard">
<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="wizard" uuid="F881A707-745E-4C6D-90D1-F820EE3B1470">
<target name="wizard">
<root class="EWF_WIZARD" feature="make"/>
<file_rule>
<exclude>/.git$</exclude>
@@ -15,4 +15,7 @@
<library name="vision2" location="$ISE_LIBRARY\library\vision2\vision2-safe.ecf"/>
<cluster name="src" location=".\src\" recursive="true"/>
</target>
<target name="custom_wizard" extends="wizard">
<root class="EWF_CUSTOM_WIZARD" feature="make"/>
</target>
</system>

View File

@@ -2,10 +2,10 @@ setlocal
rd /q/s tmp
mkdir tmp
ecb -config ewf_ise_wizard-safe.ecf -finalize -c_compile -project_path tmp
ecb -config ewf_ise_wizard-safe.ecf -target wizard -finalize -c_compile -project_path tmp
mkdir spec
mkdir spec\%ISE_PLATFORM%
move tmp\EIFGENs\ewf_ise_wizard\F_code\ewf_ise_wizard.exe spec\%ISE_PLATFORM%\wizard.exe
move tmp\EIFGENs\wizard\F_code\wizard.exe spec\%ISE_PLATFORM%\wizard.exe
rd /q/s tmp
set WIZ_TARGET=%ISE_EIFFEL%\studio\wizards\new_projects\ewf

View File

@@ -0,0 +1,21 @@
setlocal
rd /q/s tmp
mkdir tmp
ecb -config ewf_ise_wizard-safe.ecf -target custom_wizard -finalize -c_compile -project_path tmp
mkdir custom
mkdir custom\spec
mkdir custom\spec\%ISE_PLATFORM%
move tmp\EIFGENs\custom_wizard\F_code\wizard.exe custom\spec\%ISE_PLATFORM%\wizard.exe
rd /q/s tmp
set WIZ_TARGET=%ISE_EIFFEL%\studio\wizards\new_projects\ewf_custom
rd /q/s %WIZ_TARGET%
mkdir %WIZ_TARGET%
xcopy /I /E /Y %~dp0\pixmaps %WIZ_TARGET%\pixmaps
xcopy /I /E /Y %~dp0\resources %WIZ_TARGET%\resources
copy %~dp0\custom\resources\* %WIZ_TARGET%\resources
xcopy /I /E /Y %~dp0\custom\spec %WIZ_TARGET%\spec
copy %~dp0custom\ewf.dsc %WIZ_TARGET%\..\ewf_custom.dsc
endlocal

View File

@@ -0,0 +1,55 @@
note
description : "Objects that ..."
author : "$Author$"
date : "$Date$"
revision : "$Revision$"
class
EWF_CUSTOM_WIZARD
inherit
EWF_WIZARD
redefine
get_information,
generate_project
end
create
make
feature -- Form
get_information
do
if attached string_question ("Location of EWF source code (by default $EWF_DIR)?", Void, Void, False) as pn then
ewf_dir := pn.string
else
ewf_dir := "$EWF_DIR"
end
Precursor
end
feature -- Generation
generate_project (a_layout: WIZARD_LAYOUT)
do
if attached ewf_dir as d then
variables.force (d, "EWF_DIR")
Precursor (a_layout)
else
die (-1)
end
end
feature -- Access
ewf_dir: detachable READABLE_STRING_8
feature -- Change
feature {NONE} -- Implementation
invariant
-- invariant_clause: True
end

View File

@@ -25,8 +25,6 @@ feature {NONE} -- Initialization
end
end
feature -- Status
feature -- Access
project_directory_name: detachable READABLE_STRING_8
@@ -65,7 +63,7 @@ feature -- Form
projet_name := "ewf"
end
if boolean_question ("Do you want to use router (Y|n) ? ", <<["y", True], ["Y", True]>>, "Y") then
if boolean_question ("Do you want to use WSF_ROUTER (Y|n) ? ", <<["y", True], ["Y", True]>>, "Y") then
use_router := True
router_type := "uri-template"
else
@@ -121,6 +119,13 @@ feature -- Form
tfn.add_extension ("ecf")
copy_resource_template ("template.ecf", tfn.string)
create tfn.make_from_string (dn.string)
tfn.set_file_name ("ewf")
tfn.add_extension ("ini")
copy_resource_template ("ewf.ini", tfn.string)
create res.make (tfn.string, d.name)
create dn.make_from_string (pdn)
@@ -131,20 +136,15 @@ feature -- Form
d.recursive_create_dir
end
create tfn.make_from_string (dn.string)
tfn.set_file_name ("ewb_application")
tfn.set_file_name ("ewf_application")
tfn.add_extension ("e")
if attached router_type as rt then
check rt.same_string ("uri-template") end
copy_resource_template ("ewb_application-"+ rt +".e", tfn.string)
copy_resource_template ("ewf_application-"+ rt +".e", tfn.string)
else
copy_resource_template ("ewb_application.e", tfn.string)
copy_resource_template ("ewf_application.e", tfn.string)
end
create tfn.make_from_string (dn.string)
tfn.set_file_name ("ewf")
tfn.add_extension ("ini")
copy_resource_template ("ewf.ini", tfn.string)
send_response (res)
end