diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..c1e66d77 --- /dev/null +++ b/CONTRIBUTING.md @@ -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. + + + +## 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. +> +> `` - 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). + + + +## 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. + + + +## 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// + # Navigate to the newly cloned directory + cd + # Assign the original repo to a remote called "upstream" + git remote add upstream https://github.com// + ``` + +2. If you cloned a while ago, get the latest changes from upstream: + + ```bash + git checkout + git pull upstream + ``` + +3. Create a new topic branch (off the main project development branch) to + contain your feature, change, or fix: + + ```bash + git checkout -b + ``` + +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 + ``` + +6. Push your topic branch up to your fork: + + ```bash + git push origin + ``` + +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. diff --git a/README.md b/README.md index 282d281e..029f4f1e 100644 --- a/README.md +++ b/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 diff --git a/contrib/library/network/server/nino/example/SimpleWebServer/application.e b/contrib/library/network/server/nino/example/SimpleWebServer/application.e index 51666fd4..16426905 100644 --- a/contrib/library/network/server/nino/example/SimpleWebServer/application.e +++ b/contrib/library/network/server/nino/example/SimpleWebServer/application.e @@ -1,44 +1,44 @@ -note - description : "nino application root class" - date : "$Date$" - revision : "$Revision$" - -class - APPLICATION - -inherit - ARGUMENTS - - HTTP_SERVER_SHARED_CONFIGURATION - -create - make - -feature {NONE} -- Initialization - - make - -- Run application. - local - l_server : HTTP_SERVER - l_cfg: HTTP_SERVER_CONFIGURATION - l_http_handler : HTTP_HANDLER - do - create l_cfg.make - l_cfg.http_server_port := 9_000 - l_cfg.document_root := default_document_root - set_server_configuration (l_cfg) - debug ("nino") - l_cfg.set_is_verbose (True) - end - - create l_server.make (l_cfg) - create {APPLICATION_CONNECTION_HANDLER} l_http_handler.make (l_server) - l_server.setup (l_http_handler) - end - -feature -- Access - - default_document_root: STRING = "webroot" - -end - +note + description : "nino application root class" + date : "$Date$" + revision : "$Revision$" + +class + APPLICATION + +inherit + ARGUMENTS + + HTTP_SERVER_SHARED_CONFIGURATION + +create + make + +feature {NONE} -- Initialization + + make + -- Run application. + local + l_server : HTTP_SERVER + l_cfg: HTTP_SERVER_CONFIGURATION + l_http_handler : HTTP_HANDLER + do + create l_cfg.make + l_cfg.http_server_port := 9_000 + l_cfg.document_root := default_document_root + set_server_configuration (l_cfg) + debug ("nino") + l_cfg.set_is_verbose (True) + end + + create l_server.make (l_cfg) + create {APPLICATION_CONNECTION_HANDLER} l_http_handler.make (l_server) + l_server.setup (l_http_handler) + end + +feature -- Access + + default_document_root: STRING = "webroot" + +end + diff --git a/contrib/library/network/server/nino/example/SimpleWebServer/request/head_request_handler.e b/contrib/library/network/server/nino/example/SimpleWebServer/request/head_request_handler.e index bf0cdd54..772d121a 100644 --- a/contrib/library/network/server/nino/example/SimpleWebServer/request/head_request_handler.e +++ b/contrib/library/network/server/nino/example/SimpleWebServer/request/head_request_handler.e @@ -1,115 +1,115 @@ -note - description: "Summary description for {HEAD_REQUEST_HANDLER}." - author: "" - date: "$Date$" - revision: "$Revision$" - -class - HEAD_REQUEST_HANDLER - -inherit - - SHARED_DOCUMENT_ROOT - - SHARED_URI_CONTENTS_TYPES - - HTTP_REQUEST_HANDLER - - HTTP_CONSTANTS - -feature - - - process - -- process the request and create an answer - local - fname: STRING - f: RAW_FILE - ctype, extension: STRING - do - fname := document_root_cell.item.twin - fname.append (request_uri) - debug - print ("URI name: " + fname ) - end - create f.make (fname) - create answer.make - if f.exists then - extension := ct_table.extension (request_uri) - ctype := ct_table.content_types.item (extension) - -- TODO: This code could be improved to avoid string - -- comparisons - if ctype = Void then - process_default - answer.set_content_type ("text/html") - else - if ctype.is_equal ("text/html") then - process_text_file (f) - else - process_raw_file (f) - end - answer.set_content_type (ctype) - end - else - answer.set_status_code (not_found) - answer.set_reason_phrase (not_found_message) - answer.set_reply_text ("Not found on this server%N%R") - end - end - - process_default - -- - local - html : STRING - do - answer.set_reply_text ("") - html := " Micro HTTPD " + - " " + - " " + - "

Welcome to Micro HTTPD!

"+ - "

Default page " + - - "

" + - " " + - " " - answer.append_reply_text (html) - end - - - process_text_file (f: FILE) - -- send a text file reply - require - valid_f: f /= Void - do - f.open_read - from - answer.set_reply_text ("") - f.read_line - until f.end_of_file - loop - answer.append_reply_text (f.last_string) - answer.append_reply_text (crlf) - f.read_line - end - f.close - end - - process_raw_file (f: FILE) - -- send a raw file reply - require - valid_f: f /= Void - do - -- this is not quite right.... - f.open_read - from - answer.set_reply_text ("") - until f.end_of_file - loop - f.read_stream (1024) - answer.append_reply_text (f.last_string) - end - f.close - end - - -end +note + description: "Summary description for {HEAD_REQUEST_HANDLER}." + author: "" + date: "$Date$" + revision: "$Revision$" + +class + HEAD_REQUEST_HANDLER + +inherit + + SHARED_DOCUMENT_ROOT + + SHARED_URI_CONTENTS_TYPES + + HTTP_REQUEST_HANDLER + + HTTP_CONSTANTS + +feature + + + process + -- process the request and create an answer + local + fname: STRING + f: RAW_FILE + ctype, extension: STRING + do + fname := document_root_cell.item.twin + fname.append (request_uri) + debug + print ("URI name: " + fname ) + end + create f.make (fname) + create answer.make + if f.exists then + extension := ct_table.extension (request_uri) + ctype := ct_table.content_types.item (extension) + -- TODO: This code could be improved to avoid string + -- comparisons + if ctype = Void then + process_default + answer.set_content_type ("text/html") + else + if ctype.is_equal ("text/html") then + process_text_file (f) + else + process_raw_file (f) + end + answer.set_content_type (ctype) + end + else + answer.set_status_code (not_found) + answer.set_reason_phrase (not_found_message) + answer.set_reply_text ("Not found on this server%N%R") + end + end + + process_default + -- + local + html : STRING + do + answer.set_reply_text ("") + html := " Micro HTTPD " + + " " + + " " + + "

Welcome to Micro HTTPD!

"+ + "

Default page " + + + "

" + + " " + + " " + answer.append_reply_text (html) + end + + + process_text_file (f: FILE) + -- send a text file reply + require + valid_f: f /= Void + do + f.open_read + from + answer.set_reply_text ("") + f.read_line + until f.end_of_file + loop + answer.append_reply_text (f.last_string) + answer.append_reply_text (crlf) + f.read_line + end + f.close + end + + process_raw_file (f: FILE) + -- send a raw file reply + require + valid_f: f /= Void + do + -- this is not quite right.... + f.open_read + from + answer.set_reply_text ("") + until f.end_of_file + loop + f.read_stream (1024) + answer.append_reply_text (f.last_string) + end + f.close + end + + +end diff --git a/contrib/library/network/server/nino/example/SimpleWebServer/request/post_request_handler.e b/contrib/library/network/server/nino/example/SimpleWebServer/request/post_request_handler.e index 478660eb..ef130e8d 100644 --- a/contrib/library/network/server/nino/example/SimpleWebServer/request/post_request_handler.e +++ b/contrib/library/network/server/nino/example/SimpleWebServer/request/post_request_handler.e @@ -1,53 +1,53 @@ -note - description: "Summary description for {POST_REQUEST_HANDLER}." - author: "" - date: "$Date$" - revision: "$Revision$" - -class - POST_REQUEST_HANDLER - -inherit - GET_REQUEST_HANDLER - redefine - process - end - -create - make - -feature -- Execution - - process - -- process the request and create an answer - local - l_data: STRING - s: detachable STRING - n: INTEGER - sock: like socket - do - from - n := 1_024 - sock := socket - if sock.socket_ok then - sock.read_stream_thread_aware (n) - s := sock.last_string - else - s := Void - end - create l_data.make_empty - until - s = Void or else s.count < n - loop - l_data.append_string (s) - if sock.socket_ok then - sock.read_stream_thread_aware (n) - s := sock.last_string - else - s := Void - end - end - Precursor - end - -end +note + description: "Summary description for {POST_REQUEST_HANDLER}." + author: "" + date: "$Date$" + revision: "$Revision$" + +class + POST_REQUEST_HANDLER + +inherit + GET_REQUEST_HANDLER + redefine + process + end + +create + make + +feature -- Execution + + process + -- process the request and create an answer + local + l_data: STRING + s: detachable STRING + n: INTEGER + sock: like socket + do + from + n := 1_024 + sock := socket + if sock.socket_ok then + sock.read_stream_thread_aware (n) + s := sock.last_string + else + s := Void + end + create l_data.make_empty + until + s = Void or else s.count < n + loop + l_data.append_string (s) + if sock.socket_ok then + sock.read_stream_thread_aware (n) + s := sock.last_string + else + s := Void + end + end + Precursor + end + +end diff --git a/contrib/library/network/server/nino/example/SimpleWebServer/webroot/demo1/jquery.scrollTo-1.4.2/changes.txt b/contrib/library/network/server/nino/example/SimpleWebServer/webroot/demo1/jquery.scrollTo-1.4.2/changes.txt index 4b2933f6..06459d60 100644 --- a/contrib/library/network/server/nino/example/SimpleWebServer/webroot/demo1/jquery.scrollTo-1.4.2/changes.txt +++ b/contrib/library/network/server/nino/example/SimpleWebServer/webroot/demo1/jquery.scrollTo-1.4.2/changes.txt @@ -1,91 +1,91 @@ -1.4.2 -[Feature] -- The plugin support percentages as target ('50%' or {top:'50%', left:'45%'}) -- Exposed the max() calculation as $.scrollTo.max -[Enhancement] -- Renamed $.fn.scrollable to $.fn._scrollable to avoid conflicts with other plugins -[Fix] -- Fixing max calculations for regular DOM elements - -1.4.1 -[Feature] -- The target can be 'max' to scroll to the end while keeping it elegant. -[Enhancement] -- Default duration is 0 for jquery +1.3. Means sync animation -- The plugin works on all major browsers, on compat & quirks modes, including iframes. -- In addition to window/document, if html or body are received, the plugin will choose the right one. -[Fix] -- The plugin accepts floating numbers, Thanks Ramin -- Using jQuery.nodeName where neccessary so that this works on xml+xhtml -- The max() internal function wasn't completely accurrate, now it is 98% (except for IE on quirks mode and it's not too noticeable). - -1.4 -[Fix] -- Fixed the problem when scrolling the window to absolute positioned elements on Safari. -- Fixed the problem on Opera 9.5 when scrolling the window. That it always scrolls to 0. -[Feature] -- Added the settings object as 2nd argument to the onAfter callback. -- The 3rd argument of scrollTo can be just a function and it's used as the onAfter. -- Added full support for iframes (even max scroll calculation). -- Instead of $.scrollTo, $(window).scrollTo() and $(document).scrollTo() can be used. -- Added $().scrollable() that returns the real element to scroll, f.e: $(window).scrollable() == [body|html], works for iframes. -[Enhancement] -- Cleaned the code a bit, specially the comments - -1.3.3 -[Change] -- Changed the licensing from GPL to GPL+MIT. - -1.3.2 -[Enhancement] -- Small improvements to make the code shorter. -[Change] -- Removed the last argument received by onAfter as it was the same as the 'this' but jqueryfied. - -1.3.1 -[Feature] -- Exposed $.scrollTo.window() to get the element that needs to be animated, to scroll the window. -- Added option 'over'. -[Enhancement] -- Made the code as short as possible. -[Change] -- Changed the arguments received by onAfter - -1.3 -[Enhancement] -- Added semicolon to the start, for safe file concatenation -- Added a limit check, values below 0 or over the maximum are fixed. -- Now it should work faster, only one of html or body go through all the processing, instead of both for all browsers. -[Fix] -- Fixed the behavior for Opera, which seemed to react to both changes on and . -- The border is also reduced, when 'margin' is set to true. -[Change] -- The option speed has been renamed to duration. -[Feature] -- The duration can be specified with a number as 2nd argument, and the rest of the settings as the third ( like $().animate ) -- Remade the demo - -1.2.4 -[Enhancement] -- The target can be in the form of { top:x, left:y } allowing different position for each axis. -[Feature] -- The option 'offset' has been added, to scroll behind or past the target. Can be a number(both axes) or { top:x, left:y }. - -1.2.3 -[Feature] -- Exposed the defaults. -[Enhancement] -- Made the callback functions receive more parameters. - -1.2.2 -[Fix] -- Fixed a bug, I didn't have to add the scrolled amount if it was body or html. - -1.2 -[Change] -- The option 'onafter' is now called 'onAfter'. -[Feature] -- Two axes can be scrolled together, this is set with the option 'axis'. -- In case 2 axes are chosen, the scrolling can be queued: one scrolls, and then the other. -- There's an intermediary event, 'onAfterFirst' called in case the axes are queued, after the first ends. +1.4.2 +[Feature] +- The plugin support percentages as target ('50%' or {top:'50%', left:'45%'}) +- Exposed the max() calculation as $.scrollTo.max +[Enhancement] +- Renamed $.fn.scrollable to $.fn._scrollable to avoid conflicts with other plugins +[Fix] +- Fixing max calculations for regular DOM elements + +1.4.1 +[Feature] +- The target can be 'max' to scroll to the end while keeping it elegant. +[Enhancement] +- Default duration is 0 for jquery +1.3. Means sync animation +- The plugin works on all major browsers, on compat & quirks modes, including iframes. +- In addition to window/document, if html or body are received, the plugin will choose the right one. +[Fix] +- The plugin accepts floating numbers, Thanks Ramin +- Using jQuery.nodeName where neccessary so that this works on xml+xhtml +- The max() internal function wasn't completely accurrate, now it is 98% (except for IE on quirks mode and it's not too noticeable). + +1.4 +[Fix] +- Fixed the problem when scrolling the window to absolute positioned elements on Safari. +- Fixed the problem on Opera 9.5 when scrolling the window. That it always scrolls to 0. +[Feature] +- Added the settings object as 2nd argument to the onAfter callback. +- The 3rd argument of scrollTo can be just a function and it's used as the onAfter. +- Added full support for iframes (even max scroll calculation). +- Instead of $.scrollTo, $(window).scrollTo() and $(document).scrollTo() can be used. +- Added $().scrollable() that returns the real element to scroll, f.e: $(window).scrollable() == [body|html], works for iframes. +[Enhancement] +- Cleaned the code a bit, specially the comments + +1.3.3 +[Change] +- Changed the licensing from GPL to GPL+MIT. + +1.3.2 +[Enhancement] +- Small improvements to make the code shorter. +[Change] +- Removed the last argument received by onAfter as it was the same as the 'this' but jqueryfied. + +1.3.1 +[Feature] +- Exposed $.scrollTo.window() to get the element that needs to be animated, to scroll the window. +- Added option 'over'. +[Enhancement] +- Made the code as short as possible. +[Change] +- Changed the arguments received by onAfter + +1.3 +[Enhancement] +- Added semicolon to the start, for safe file concatenation +- Added a limit check, values below 0 or over the maximum are fixed. +- Now it should work faster, only one of html or body go through all the processing, instead of both for all browsers. +[Fix] +- Fixed the behavior for Opera, which seemed to react to both changes on and . +- The border is also reduced, when 'margin' is set to true. +[Change] +- The option speed has been renamed to duration. +[Feature] +- The duration can be specified with a number as 2nd argument, and the rest of the settings as the third ( like $().animate ) +- Remade the demo + +1.2.4 +[Enhancement] +- The target can be in the form of { top:x, left:y } allowing different position for each axis. +[Feature] +- The option 'offset' has been added, to scroll behind or past the target. Can be a number(both axes) or { top:x, left:y }. + +1.2.3 +[Feature] +- Exposed the defaults. +[Enhancement] +- Made the callback functions receive more parameters. + +1.2.2 +[Fix] +- Fixed a bug, I didn't have to add the scrolled amount if it was body or html. + +1.2 +[Change] +- The option 'onafter' is now called 'onAfter'. +[Feature] +- Two axes can be scrolled together, this is set with the option 'axis'. +- In case 2 axes are chosen, the scrolling can be queued: one scrolls, and then the other. +- There's an intermediary event, 'onAfterFirst' called in case the axes are queued, after the first ends. - If the option 'margin' is set to true, the plugin will take in account, the margin of the target(no use if target is a value). \ No newline at end of file diff --git a/contrib/library/network/server/nino/example/SimpleWebServer/webroot/demo1/jquery.scrollTo-1.4.2/jquery.scrollTo-min.js b/contrib/library/network/server/nino/example/SimpleWebServer/webroot/demo1/jquery.scrollTo-1.4.2/jquery.scrollTo-min.js index 73a33418..5e787781 100644 --- a/contrib/library/network/server/nino/example/SimpleWebServer/webroot/demo1/jquery.scrollTo-1.4.2/jquery.scrollTo-min.js +++ b/contrib/library/network/server/nino/example/SimpleWebServer/webroot/demo1/jquery.scrollTo-1.4.2/jquery.scrollTo-min.js @@ -1,11 +1,11 @@ -/** - * jQuery.ScrollTo - Easy element scrolling using jQuery. - * Copyright (c) 2007-2009 Ariel Flesler - aflesler(at)gmail(dot)com | http://flesler.blogspot.com - * Dual licensed under MIT and GPL. - * Date: 5/25/2009 - * @author Ariel Flesler - * @version 1.4.2 - * - * http://flesler.blogspot.com/2007/10/jqueryscrollto.html - */ +/** + * jQuery.ScrollTo - Easy element scrolling using jQuery. + * Copyright (c) 2007-2009 Ariel Flesler - aflesler(at)gmail(dot)com | http://flesler.blogspot.com + * Dual licensed under MIT and GPL. + * Date: 5/25/2009 + * @author Ariel Flesler + * @version 1.4.2 + * + * http://flesler.blogspot.com/2007/10/jqueryscrollto.html + */ ;(function(d){var k=d.scrollTo=function(a,i,e){d(window).scrollTo(a,i,e)};k.defaults={axis:'xy',duration:parseFloat(d.fn.jquery)>=1.3?0:1};k.window=function(a){return d(window)._scrollable()};d.fn._scrollable=function(){return this.map(function(){var a=this,i=!a.nodeName||d.inArray(a.nodeName.toLowerCase(),['iframe','#document','html','body'])!=-1;if(!i)return a;var e=(a.contentWindow||a).document||a.ownerDocument||a;return d.browser.safari||e.compatMode=='BackCompat'?e.body:e.documentElement})};d.fn.scrollTo=function(n,j,b){if(typeof j=='object'){b=j;j=0}if(typeof b=='function')b={onAfter:b};if(n=='max')n=9e9;b=d.extend({},k.defaults,b);j=j||b.speed||b.duration;b.queue=b.queue&&b.axis.length>1;if(b.queue)j/=2;b.offset=p(b.offset);b.over=p(b.over);return this._scrollable().each(function(){var q=this,r=d(q),f=n,s,g={},u=r.is('html,body');switch(typeof f){case'number':case'string':if(/^([+-]=)?\d+(\.\d+)?(px|%)?$/.test(f)){f=p(f);break}f=d(f,this);case'object':if(f.is||f.style)s=(f=d(f)).offset()}d.each(b.axis.split(''),function(a,i){var e=i=='x'?'Left':'Top',h=e.toLowerCase(),c='scroll'+e,l=q[c],m=k.max(q,i);if(s){g[c]=s[h]+(u?0:l-r.offset()[h]);if(b.margin){g[c]-=parseInt(f.css('margin'+e))||0;g[c]-=parseInt(f.css('border'+e+'Width'))||0}g[c]+=b.offset[h]||0;if(b.over[h])g[c]+=f[i=='x'?'width':'height']()*b.over[h]}else{var o=f[h];g[c]=o.slice&&o.slice(-1)=='%'?parseFloat(o)/100*m:o}if(/^\d+$/.test(g[c]))g[c]=g[c]<=0?0:Math.min(g[c],m);if(!a&&b.queue){if(l!=g[c])t(b.onAfterFirst);delete g[c]}});t(b.onAfter);function t(a){r.animate(g,j,b.easing,a&&function(){a.call(this,n,b)})}}).end()};k.max=function(a,i){var e=i=='x'?'Width':'Height',h='scroll'+e;if(!d(a).is('html,body'))return a[h]-d(a)[e.toLowerCase()]();var c='client'+e,l=a.ownerDocument.documentElement,m=a.ownerDocument.body;return Math.max(l[h],m[h])-Math.min(l[c],m[c])};function p(a){return typeof a=='object'?a:{top:a,left:a}}})(jQuery); \ No newline at end of file diff --git a/contrib/library/network/server/nino/example/SimpleWebServer/webroot/demo1/jquery.scrollTo-1.4.2/jquery.scrollTo.js b/contrib/library/network/server/nino/example/SimpleWebServer/webroot/demo1/jquery.scrollTo-1.4.2/jquery.scrollTo.js index eec31e19..753e62c4 100644 --- a/contrib/library/network/server/nino/example/SimpleWebServer/webroot/demo1/jquery.scrollTo-1.4.2/jquery.scrollTo.js +++ b/contrib/library/network/server/nino/example/SimpleWebServer/webroot/demo1/jquery.scrollTo-1.4.2/jquery.scrollTo.js @@ -1,215 +1,215 @@ -/** - * jQuery.ScrollTo - * Copyright (c) 2007-2009 Ariel Flesler - aflesler(at)gmail(dot)com | http://flesler.blogspot.com - * Dual licensed under MIT and GPL. - * Date: 5/25/2009 - * - * @projectDescription Easy element scrolling using jQuery. - * http://flesler.blogspot.com/2007/10/jqueryscrollto.html - * Works with jQuery +1.2.6. Tested on FF 2/3, IE 6/7/8, Opera 9.5/6, Safari 3, Chrome 1 on WinXP. - * - * @author Ariel Flesler - * @version 1.4.2 - * - * @id jQuery.scrollTo - * @id jQuery.fn.scrollTo - * @param {String, Number, DOMElement, jQuery, Object} target Where to scroll the matched elements. - * The different options for target are: - * - A number position (will be applied to all axes). - * - A string position ('44', '100px', '+=90', etc ) will be applied to all axes - * - A jQuery/DOM element ( logically, child of the element to scroll ) - * - A string selector, that will be relative to the element to scroll ( 'li:eq(2)', etc ) - * - A hash { top:x, left:y }, x and y can be any kind of number/string like above. -* - A percentage of the container's dimension/s, for example: 50% to go to the middle. - * - The string 'max' for go-to-end. - * @param {Number} duration The OVERALL length of the animation, this argument can be the settings object instead. - * @param {Object,Function} settings Optional set of settings or the onAfter callback. - * @option {String} axis Which axis must be scrolled, use 'x', 'y', 'xy' or 'yx'. - * @option {Number} duration The OVERALL length of the animation. - * @option {String} easing The easing method for the animation. - * @option {Boolean} margin If true, the margin of the target element will be deducted from the final position. - * @option {Object, Number} offset Add/deduct from the end position. One number for both axes or { top:x, left:y }. - * @option {Object, Number} over Add/deduct the height/width multiplied by 'over', can be { top:x, left:y } when using both axes. - * @option {Boolean} queue If true, and both axis are given, the 2nd axis will only be animated after the first one ends. - * @option {Function} onAfter Function to be called after the scrolling ends. - * @option {Function} onAfterFirst If queuing is activated, this function will be called after the first scrolling ends. - * @return {jQuery} Returns the same jQuery object, for chaining. - * - * @desc Scroll to a fixed position - * @example $('div').scrollTo( 340 ); - * - * @desc Scroll relatively to the actual position - * @example $('div').scrollTo( '+=340px', { axis:'y' } ); - * - * @dec Scroll using a selector (relative to the scrolled element) - * @example $('div').scrollTo( 'p.paragraph:eq(2)', 500, { easing:'swing', queue:true, axis:'xy' } ); - * - * @ Scroll to a DOM element (same for jQuery object) - * @example var second_child = document.getElementById('container').firstChild.nextSibling; - * $('#container').scrollTo( second_child, { duration:500, axis:'x', onAfter:function(){ - * alert('scrolled!!'); - * }}); - * - * @desc Scroll on both axes, to different values - * @example $('div').scrollTo( { top: 300, left:'+=200' }, { axis:'xy', offset:-20 } ); - */ -;(function( $ ){ - - var $scrollTo = $.scrollTo = function( target, duration, settings ){ - $(window).scrollTo( target, duration, settings ); - }; - - $scrollTo.defaults = { - axis:'xy', - duration: parseFloat($.fn.jquery) >= 1.3 ? 0 : 1 - }; - - // Returns the element that needs to be animated to scroll the window. - // Kept for backwards compatibility (specially for localScroll & serialScroll) - $scrollTo.window = function( scope ){ - return $(window)._scrollable(); - }; - - // Hack, hack, hack :) - // Returns the real elements to scroll (supports window/iframes, documents and regular nodes) - $.fn._scrollable = function(){ - return this.map(function(){ - var elem = this, - isWin = !elem.nodeName || $.inArray( elem.nodeName.toLowerCase(), ['iframe','#document','html','body'] ) != -1; - - if( !isWin ) - return elem; - - var doc = (elem.contentWindow || elem).document || elem.ownerDocument || elem; - - return $.browser.safari || doc.compatMode == 'BackCompat' ? - doc.body : - doc.documentElement; - }); - }; - - $.fn.scrollTo = function( target, duration, settings ){ - if( typeof duration == 'object' ){ - settings = duration; - duration = 0; - } - if( typeof settings == 'function' ) - settings = { onAfter:settings }; - - if( target == 'max' ) - target = 9e9; - - settings = $.extend( {}, $scrollTo.defaults, settings ); - // Speed is still recognized for backwards compatibility - duration = duration || settings.speed || settings.duration; - // Make sure the settings are given right - settings.queue = settings.queue && settings.axis.length > 1; - - if( settings.queue ) - // Let's keep the overall duration - duration /= 2; - settings.offset = both( settings.offset ); - settings.over = both( settings.over ); - - return this._scrollable().each(function(){ - var elem = this, - $elem = $(elem), - targ = target, toff, attr = {}, - win = $elem.is('html,body'); - - switch( typeof targ ){ - // A number will pass the regex - case 'number': - case 'string': - if( /^([+-]=)?\d+(\.\d+)?(px|%)?$/.test(targ) ){ - targ = both( targ ); - // We are done - break; - } - // Relative selector, no break! - targ = $(targ,this); - case 'object': - // DOMElement / jQuery - if( targ.is || targ.style ) - // Get the real position of the target - toff = (targ = $(targ)).offset(); - } - $.each( settings.axis.split(''), function( i, axis ){ - var Pos = axis == 'x' ? 'Left' : 'Top', - pos = Pos.toLowerCase(), - key = 'scroll' + Pos, - old = elem[key], - max = $scrollTo.max(elem, axis); - - if( toff ){// jQuery / DOMElement - attr[key] = toff[pos] + ( win ? 0 : old - $elem.offset()[pos] ); - - // If it's a dom element, reduce the margin - if( settings.margin ){ - attr[key] -= parseInt(targ.css('margin'+Pos)) || 0; - attr[key] -= parseInt(targ.css('border'+Pos+'Width')) || 0; - } - - attr[key] += settings.offset[pos] || 0; - - if( settings.over[pos] ) - // Scroll to a fraction of its width/height - attr[key] += targ[axis=='x'?'width':'height']() * settings.over[pos]; - }else{ - var val = targ[pos]; - // Handle percentage values - attr[key] = val.slice && val.slice(-1) == '%' ? - parseFloat(val) / 100 * max - : val; - } - - // Number or 'number' - if( /^\d+$/.test(attr[key]) ) - // Check the limits - attr[key] = attr[key] <= 0 ? 0 : Math.min( attr[key], max ); - - // Queueing axes - if( !i && settings.queue ){ - // Don't waste time animating, if there's no need. - if( old != attr[key] ) - // Intermediate animation - animate( settings.onAfterFirst ); - // Don't animate this axis again in the next iteration. - delete attr[key]; - } - }); - - animate( settings.onAfter ); - - function animate( callback ){ - $elem.animate( attr, duration, settings.easing, callback && function(){ - callback.call(this, target, settings); - }); - }; - - }).end(); - }; - - // Max scrolling position, works on quirks mode - // It only fails (not too badly) on IE, quirks mode. - $scrollTo.max = function( elem, axis ){ - var Dim = axis == 'x' ? 'Width' : 'Height', - scroll = 'scroll'+Dim; - - if( !$(elem).is('html,body') ) - return elem[scroll] - $(elem)[Dim.toLowerCase()](); - - var size = 'client' + Dim, - html = elem.ownerDocument.documentElement, - body = elem.ownerDocument.body; - - return Math.max( html[scroll], body[scroll] ) - - Math.min( html[size] , body[size] ); - - }; - - function both( val ){ - return typeof val == 'object' ? val : { top:val, left:val }; - }; - +/** + * jQuery.ScrollTo + * Copyright (c) 2007-2009 Ariel Flesler - aflesler(at)gmail(dot)com | http://flesler.blogspot.com + * Dual licensed under MIT and GPL. + * Date: 5/25/2009 + * + * @projectDescription Easy element scrolling using jQuery. + * http://flesler.blogspot.com/2007/10/jqueryscrollto.html + * Works with jQuery +1.2.6. Tested on FF 2/3, IE 6/7/8, Opera 9.5/6, Safari 3, Chrome 1 on WinXP. + * + * @author Ariel Flesler + * @version 1.4.2 + * + * @id jQuery.scrollTo + * @id jQuery.fn.scrollTo + * @param {String, Number, DOMElement, jQuery, Object} target Where to scroll the matched elements. + * The different options for target are: + * - A number position (will be applied to all axes). + * - A string position ('44', '100px', '+=90', etc ) will be applied to all axes + * - A jQuery/DOM element ( logically, child of the element to scroll ) + * - A string selector, that will be relative to the element to scroll ( 'li:eq(2)', etc ) + * - A hash { top:x, left:y }, x and y can be any kind of number/string like above. +* - A percentage of the container's dimension/s, for example: 50% to go to the middle. + * - The string 'max' for go-to-end. + * @param {Number} duration The OVERALL length of the animation, this argument can be the settings object instead. + * @param {Object,Function} settings Optional set of settings or the onAfter callback. + * @option {String} axis Which axis must be scrolled, use 'x', 'y', 'xy' or 'yx'. + * @option {Number} duration The OVERALL length of the animation. + * @option {String} easing The easing method for the animation. + * @option {Boolean} margin If true, the margin of the target element will be deducted from the final position. + * @option {Object, Number} offset Add/deduct from the end position. One number for both axes or { top:x, left:y }. + * @option {Object, Number} over Add/deduct the height/width multiplied by 'over', can be { top:x, left:y } when using both axes. + * @option {Boolean} queue If true, and both axis are given, the 2nd axis will only be animated after the first one ends. + * @option {Function} onAfter Function to be called after the scrolling ends. + * @option {Function} onAfterFirst If queuing is activated, this function will be called after the first scrolling ends. + * @return {jQuery} Returns the same jQuery object, for chaining. + * + * @desc Scroll to a fixed position + * @example $('div').scrollTo( 340 ); + * + * @desc Scroll relatively to the actual position + * @example $('div').scrollTo( '+=340px', { axis:'y' } ); + * + * @dec Scroll using a selector (relative to the scrolled element) + * @example $('div').scrollTo( 'p.paragraph:eq(2)', 500, { easing:'swing', queue:true, axis:'xy' } ); + * + * @ Scroll to a DOM element (same for jQuery object) + * @example var second_child = document.getElementById('container').firstChild.nextSibling; + * $('#container').scrollTo( second_child, { duration:500, axis:'x', onAfter:function(){ + * alert('scrolled!!'); + * }}); + * + * @desc Scroll on both axes, to different values + * @example $('div').scrollTo( { top: 300, left:'+=200' }, { axis:'xy', offset:-20 } ); + */ +;(function( $ ){ + + var $scrollTo = $.scrollTo = function( target, duration, settings ){ + $(window).scrollTo( target, duration, settings ); + }; + + $scrollTo.defaults = { + axis:'xy', + duration: parseFloat($.fn.jquery) >= 1.3 ? 0 : 1 + }; + + // Returns the element that needs to be animated to scroll the window. + // Kept for backwards compatibility (specially for localScroll & serialScroll) + $scrollTo.window = function( scope ){ + return $(window)._scrollable(); + }; + + // Hack, hack, hack :) + // Returns the real elements to scroll (supports window/iframes, documents and regular nodes) + $.fn._scrollable = function(){ + return this.map(function(){ + var elem = this, + isWin = !elem.nodeName || $.inArray( elem.nodeName.toLowerCase(), ['iframe','#document','html','body'] ) != -1; + + if( !isWin ) + return elem; + + var doc = (elem.contentWindow || elem).document || elem.ownerDocument || elem; + + return $.browser.safari || doc.compatMode == 'BackCompat' ? + doc.body : + doc.documentElement; + }); + }; + + $.fn.scrollTo = function( target, duration, settings ){ + if( typeof duration == 'object' ){ + settings = duration; + duration = 0; + } + if( typeof settings == 'function' ) + settings = { onAfter:settings }; + + if( target == 'max' ) + target = 9e9; + + settings = $.extend( {}, $scrollTo.defaults, settings ); + // Speed is still recognized for backwards compatibility + duration = duration || settings.speed || settings.duration; + // Make sure the settings are given right + settings.queue = settings.queue && settings.axis.length > 1; + + if( settings.queue ) + // Let's keep the overall duration + duration /= 2; + settings.offset = both( settings.offset ); + settings.over = both( settings.over ); + + return this._scrollable().each(function(){ + var elem = this, + $elem = $(elem), + targ = target, toff, attr = {}, + win = $elem.is('html,body'); + + switch( typeof targ ){ + // A number will pass the regex + case 'number': + case 'string': + if( /^([+-]=)?\d+(\.\d+)?(px|%)?$/.test(targ) ){ + targ = both( targ ); + // We are done + break; + } + // Relative selector, no break! + targ = $(targ,this); + case 'object': + // DOMElement / jQuery + if( targ.is || targ.style ) + // Get the real position of the target + toff = (targ = $(targ)).offset(); + } + $.each( settings.axis.split(''), function( i, axis ){ + var Pos = axis == 'x' ? 'Left' : 'Top', + pos = Pos.toLowerCase(), + key = 'scroll' + Pos, + old = elem[key], + max = $scrollTo.max(elem, axis); + + if( toff ){// jQuery / DOMElement + attr[key] = toff[pos] + ( win ? 0 : old - $elem.offset()[pos] ); + + // If it's a dom element, reduce the margin + if( settings.margin ){ + attr[key] -= parseInt(targ.css('margin'+Pos)) || 0; + attr[key] -= parseInt(targ.css('border'+Pos+'Width')) || 0; + } + + attr[key] += settings.offset[pos] || 0; + + if( settings.over[pos] ) + // Scroll to a fraction of its width/height + attr[key] += targ[axis=='x'?'width':'height']() * settings.over[pos]; + }else{ + var val = targ[pos]; + // Handle percentage values + attr[key] = val.slice && val.slice(-1) == '%' ? + parseFloat(val) / 100 * max + : val; + } + + // Number or 'number' + if( /^\d+$/.test(attr[key]) ) + // Check the limits + attr[key] = attr[key] <= 0 ? 0 : Math.min( attr[key], max ); + + // Queueing axes + if( !i && settings.queue ){ + // Don't waste time animating, if there's no need. + if( old != attr[key] ) + // Intermediate animation + animate( settings.onAfterFirst ); + // Don't animate this axis again in the next iteration. + delete attr[key]; + } + }); + + animate( settings.onAfter ); + + function animate( callback ){ + $elem.animate( attr, duration, settings.easing, callback && function(){ + callback.call(this, target, settings); + }); + }; + + }).end(); + }; + + // Max scrolling position, works on quirks mode + // It only fails (not too badly) on IE, quirks mode. + $scrollTo.max = function( elem, axis ){ + var Dim = axis == 'x' ? 'Width' : 'Height', + scroll = 'scroll'+Dim; + + if( !$(elem).is('html,body') ) + return elem[scroll] - $(elem)[Dim.toLowerCase()](); + + var size = 'client' + Dim, + html = elem.ownerDocument.documentElement, + body = elem.ownerDocument.body; + + return Math.max( html[scroll], body[scroll] ) + - Math.min( html[size] , body[size] ); + + }; + + function both( val ){ + return typeof val == 'object' ? val : { top:val, left:val }; + }; + })( jQuery ); \ No newline at end of file diff --git a/contrib/library/network/server/nino/example/SimpleWebServer/webroot/demo1/script.js b/contrib/library/network/server/nino/example/SimpleWebServer/webroot/demo1/script.js index 55571713..62d1b396 100644 --- a/contrib/library/network/server/nino/example/SimpleWebServer/webroot/demo1/script.js +++ b/contrib/library/network/server/nino/example/SimpleWebServer/webroot/demo1/script.js @@ -1,12 +1,12 @@ -$(document).ready(function(){ - /* This code is executed after the DOM has been completely loaded */ - - $('nav a,footer a.up').click(function(e){ - - // If a link has been clicked, scroll the page to the link's hash target: - - $.scrollTo( this.hash || 0, 1500); - e.preventDefault(); - }); - +$(document).ready(function(){ + /* This code is executed after the DOM has been completely loaded */ + + $('nav a,footer a.up').click(function(e){ + + // If a link has been clicked, scroll the page to the link's hash target: + + $.scrollTo( this.hash || 0, 1500); + e.preventDefault(); + }); + }); \ No newline at end of file diff --git a/contrib/library/network/server/nino/example/SimpleWebServer/webroot/demo1/styles.css b/contrib/library/network/server/nino/example/SimpleWebServer/webroot/demo1/styles.css index ee4db145..0e0ee702 100644 --- a/contrib/library/network/server/nino/example/SimpleWebServer/webroot/demo1/styles.css +++ b/contrib/library/network/server/nino/example/SimpleWebServer/webroot/demo1/styles.css @@ -1,219 +1,219 @@ -*{ - /* Universal reset: */ - margin:0; - padding:0; -} - -header,footer, -article,section, -hgroup,nav, -figure{ - /* Giving a display value to the HTML5 rendered elements: */ - display:block; -} - -body{ - /* Setting the default text color, size, page background and a font stack: */ - font-size:0.825em; - color:#fcfcfc; - background-color:#355664; - font-family:Arial, Helvetica, sans-serif; -} - -/* Hyperlink Styles: */ - -a, a:visited { - color:#0196e3; - text-decoration:none; - outline:none; -} - -a:hover{ - text-decoration:underline; -} - -a img{ - border:none; -} - -/* Headings: */ - -h1,h2,h3{ - font-family:"Myriad Pro","Helvetica Neue",Helvetica,Arial,Sans-Serif; - text-shadow:0 1px 1px black; -} - -h1{ - /* The logo text */ - font-size:3.5em; - padding:0.5em 0 0; - text-transform:uppercase; -} - -h3{ - /* The slogan text */ - font-family:forte,"Myriad Pro","Helvetica Neue",Helvetica,Arial,Sans-Serif; - font-size:2em; - font-weight:normal; - margin:0 0 1em; -} - - -h2{ - font-size:2.2em; - font-weight:normal; - letter-spacing:0.01em; - text-transform:uppercase; -} - -p{ - line-height:1.5em; - padding-bottom:1em; -} - -.line{ - /* The dividing line: */ - height:1px; - background-color:#24404c; - border-bottom:1px solid #416371; - margin:1em 0; - overflow:hidden; -} - -article .line{ - /* The dividing line inside of the article is darker: */ - background-color:#15242a; - border-bottom-color:#204656; - margin:1.3em 0; -} - -footer .line{ - margin:2em 0; -} - -nav{ - background:url(img/gradient_light.jpg) repeat-x 50% 50% #f8f8f8; - padding:0 5px; - position:absolute; - right:0; - top:4em; - - border:1px solid #FCFCFC; - - -moz-box-shadow:0 1px 1px #333333; - -webkit-box-shadow:0 1px 1px #333333; - box-shadow:0 1px 1px #333333; -} - -/* The clearfix hack to clear the floats: */ - -.clear:after{ - content: "."; - display: block; - height: 0; - clear: both; - visibility: hidden; -} - -/* The navigation styling: */ - -nav ul li{ - display:inline; -} - -nav ul li a, -nav ul li a:visited{ - color:#565656; - display:block; - float:left; - font-size:1.25em; - font-weight:bold; - margin:5px 2px; - padding:7px 10px 4px; - text-shadow:0 1px 1px white; - text-transform:uppercase; -} - -nav ul li a:hover{ - text-decoration:none; - background-color:#f0f0f0; -} - -nav, article, nav ul li a,figure{ - /* Applying CSS3 rounded corners: */ - -moz-border-radius:10px; - -webkit-border-radius:10px; - border-radius:10px; -} - -/* Article styles: */ - -#page{ - width:960px; - margin:0 auto; - position:relative; -} - -article{ - background-color:#213E4A; - margin:3em 0; - padding:20px; - - text-shadow:0 2px 0 black; -} - -figure{ - border:3px solid #142830; - float:right; - height:300px; - margin-left:15px; - overflow:hidden; - width:500px; -} - -figure:hover{ - -moz-box-shadow:0 0 2px #4D7788; - -webkit-box-shadow:0 0 2px #4D7788; - box-shadow:0 0 2px #4D7788; -} - -figure img{ - margin-left:-60px; -} - -/* Footer styling: */ - -footer{ - margin-bottom:30px; - text-align:center; - font-size:0.825em; -} - - -footer p{ - margin-bottom:-2.5em; - position:relative; -} - -footer a,footer a:visited{ - color:#cccccc; - background-color:#213e4a; - display:block; - padding:2px 4px; - z-index:100; - position:relative; -} - -footer a:hover{ - text-decoration:none; - background-color:#142830; -} - -footer a.by{ - float:left; - -} - -footer a.up{ - float:right; -} +*{ + /* Universal reset: */ + margin:0; + padding:0; +} + +header,footer, +article,section, +hgroup,nav, +figure{ + /* Giving a display value to the HTML5 rendered elements: */ + display:block; +} + +body{ + /* Setting the default text color, size, page background and a font stack: */ + font-size:0.825em; + color:#fcfcfc; + background-color:#355664; + font-family:Arial, Helvetica, sans-serif; +} + +/* Hyperlink Styles: */ + +a, a:visited { + color:#0196e3; + text-decoration:none; + outline:none; +} + +a:hover{ + text-decoration:underline; +} + +a img{ + border:none; +} + +/* Headings: */ + +h1,h2,h3{ + font-family:"Myriad Pro","Helvetica Neue",Helvetica,Arial,Sans-Serif; + text-shadow:0 1px 1px black; +} + +h1{ + /* The logo text */ + font-size:3.5em; + padding:0.5em 0 0; + text-transform:uppercase; +} + +h3{ + /* The slogan text */ + font-family:forte,"Myriad Pro","Helvetica Neue",Helvetica,Arial,Sans-Serif; + font-size:2em; + font-weight:normal; + margin:0 0 1em; +} + + +h2{ + font-size:2.2em; + font-weight:normal; + letter-spacing:0.01em; + text-transform:uppercase; +} + +p{ + line-height:1.5em; + padding-bottom:1em; +} + +.line{ + /* The dividing line: */ + height:1px; + background-color:#24404c; + border-bottom:1px solid #416371; + margin:1em 0; + overflow:hidden; +} + +article .line{ + /* The dividing line inside of the article is darker: */ + background-color:#15242a; + border-bottom-color:#204656; + margin:1.3em 0; +} + +footer .line{ + margin:2em 0; +} + +nav{ + background:url(img/gradient_light.jpg) repeat-x 50% 50% #f8f8f8; + padding:0 5px; + position:absolute; + right:0; + top:4em; + + border:1px solid #FCFCFC; + + -moz-box-shadow:0 1px 1px #333333; + -webkit-box-shadow:0 1px 1px #333333; + box-shadow:0 1px 1px #333333; +} + +/* The clearfix hack to clear the floats: */ + +.clear:after{ + content: "."; + display: block; + height: 0; + clear: both; + visibility: hidden; +} + +/* The navigation styling: */ + +nav ul li{ + display:inline; +} + +nav ul li a, +nav ul li a:visited{ + color:#565656; + display:block; + float:left; + font-size:1.25em; + font-weight:bold; + margin:5px 2px; + padding:7px 10px 4px; + text-shadow:0 1px 1px white; + text-transform:uppercase; +} + +nav ul li a:hover{ + text-decoration:none; + background-color:#f0f0f0; +} + +nav, article, nav ul li a,figure{ + /* Applying CSS3 rounded corners: */ + -moz-border-radius:10px; + -webkit-border-radius:10px; + border-radius:10px; +} + +/* Article styles: */ + +#page{ + width:960px; + margin:0 auto; + position:relative; +} + +article{ + background-color:#213E4A; + margin:3em 0; + padding:20px; + + text-shadow:0 2px 0 black; +} + +figure{ + border:3px solid #142830; + float:right; + height:300px; + margin-left:15px; + overflow:hidden; + width:500px; +} + +figure:hover{ + -moz-box-shadow:0 0 2px #4D7788; + -webkit-box-shadow:0 0 2px #4D7788; + box-shadow:0 0 2px #4D7788; +} + +figure img{ + margin-left:-60px; +} + +/* Footer styling: */ + +footer{ + margin-bottom:30px; + text-align:center; + font-size:0.825em; +} + + +footer p{ + margin-bottom:-2.5em; + position:relative; +} + +footer a,footer a:visited{ + color:#cccccc; + background-color:#213e4a; + display:block; + padding:2px 4px; + z-index:100; + position:relative; +} + +footer a:hover{ + text-decoration:none; + background-color:#142830; +} + +footer a.by{ + float:left; + +} + +footer a.up{ + float:right; +} diff --git a/contrib/library/network/server/nino/example/SimpleWebServer/webroot/demo1/template.html b/contrib/library/network/server/nino/example/SimpleWebServer/webroot/demo1/template.html index cde688dc..63f1f5d7 100644 --- a/contrib/library/network/server/nino/example/SimpleWebServer/webroot/demo1/template.html +++ b/contrib/library/network/server/nino/example/SimpleWebServer/webroot/demo1/template.html @@ -1,139 +1,139 @@ - - - - - - - Coding A CSS3 & HTML5 One Page Template | Tutorialzine demo - - - - - - - - - - - -
- -
- -
-

Your Logo

-

and a fancy slogan

-
- - - -
- -
- - - -
- -
-

Photoshoot Effect

- -
- -
- -
- -
- -

In this tutorial, we are creating a photo shoot effect with our just-released PhotoShoot jQuery plug-in. With it you can convert a regular div on the page into a photo shooting stage simulating a camera-like feel.

-

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer luctus quam quis nibh fringilla sit amet consectetur lectus malesuada. Sed nec libero erat. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc mi nisi, rhoncus ut vestibulum ac, sollicitudin quis lorem. Duis felis dui, vulputate nec adipiscing nec, interdum vel tortor. Sed gravida, erat nec rutrum tincidunt, metus mauris imperdiet nunc, et elementum tortor nunc at eros. Donec malesuada congue molestie. Suspendisse potenti. Vestibulum cursus congue sem et feugiat. Morbi quis elit odio.

-
-
- - - - - - -
- -
-

Sweet AJAX Tabs

- -
- -
-
- -
- -

Here we are making sweet AJAX-powered tabs with CSS3 and the newly released version 1.4 of jQuery.

-

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer luctus quam quis nibh fringilla sit amet consectetur lectus malesuada. Sed nec libero erat. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc mi nisi, rhoncus ut vestibulum ac, sollicitudin quis lorem. Duis felis dui, vulputate nec adipiscing nec, interdum vel tortor. Sed gravida, erat nec rutrum tincidunt, metus mauris imperdiet nunc, et elementum tortor nunc at eros. Donec malesuada congue molestie. Suspendisse potenti. Vestibulum cursus congue sem et feugiat. Morbi quis elit odio.

-
-
- - - - - -
- -
-

Halftone Navigation Menu

- -
- -
-
- -
- -

Today we are making a CSS3 & jQuery halftone-style navigation menu, which will allow you to display animated halftone-style shapes in accordance with the navigation links, and will provide a simple editor for creating additional shapes as well.

-

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer luctus quam quis nibh fringilla sit amet consectetur lectus malesuada. Sed nec libero erat. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc mi nisi, rhoncus ut vestibulum ac, sollicitudin quis lorem. Duis felis dui, vulputate nec adipiscing nec, interdum vel tortor. Sed gravida, erat nec rutrum tincidunt, metus mauris imperdiet nunc, et elementum tortor nunc at eros. Donec malesuada congue molestie. Suspendisse potenti. Vestibulum cursus congue sem et feugiat. Morbi quis elit odio.

-
-
- - - - -
- - - -
- - - - - - - - + + + + + + + Coding A CSS3 & HTML5 One Page Template | Tutorialzine demo + + + + + + + + + + + +
+ +
+ +
+

Your Logo

+

and a fancy slogan

+
+ + + +
+ +
+ + + +
+ +
+

Photoshoot Effect

+ +
+ +
+ +
+ +
+ +

In this tutorial, we are creating a photo shoot effect with our just-released PhotoShoot jQuery plug-in. With it you can convert a regular div on the page into a photo shooting stage simulating a camera-like feel.

+

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer luctus quam quis nibh fringilla sit amet consectetur lectus malesuada. Sed nec libero erat. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc mi nisi, rhoncus ut vestibulum ac, sollicitudin quis lorem. Duis felis dui, vulputate nec adipiscing nec, interdum vel tortor. Sed gravida, erat nec rutrum tincidunt, metus mauris imperdiet nunc, et elementum tortor nunc at eros. Donec malesuada congue molestie. Suspendisse potenti. Vestibulum cursus congue sem et feugiat. Morbi quis elit odio.

+
+
+ + + + + + +
+ +
+

Sweet AJAX Tabs

+ +
+ +
+
+ +
+ +

Here we are making sweet AJAX-powered tabs with CSS3 and the newly released version 1.4 of jQuery.

+

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer luctus quam quis nibh fringilla sit amet consectetur lectus malesuada. Sed nec libero erat. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc mi nisi, rhoncus ut vestibulum ac, sollicitudin quis lorem. Duis felis dui, vulputate nec adipiscing nec, interdum vel tortor. Sed gravida, erat nec rutrum tincidunt, metus mauris imperdiet nunc, et elementum tortor nunc at eros. Donec malesuada congue molestie. Suspendisse potenti. Vestibulum cursus congue sem et feugiat. Morbi quis elit odio.

+
+
+ + + + + +
+ +
+

Halftone Navigation Menu

+ +
+ +
+
+ +
+ +

Today we are making a CSS3 & jQuery halftone-style navigation menu, which will allow you to display animated halftone-style shapes in accordance with the navigation links, and will provide a simple editor for creating additional shapes as well.

+

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer luctus quam quis nibh fringilla sit amet consectetur lectus malesuada. Sed nec libero erat. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc mi nisi, rhoncus ut vestibulum ac, sollicitudin quis lorem. Duis felis dui, vulputate nec adipiscing nec, interdum vel tortor. Sed gravida, erat nec rutrum tincidunt, metus mauris imperdiet nunc, et elementum tortor nunc at eros. Donec malesuada congue molestie. Suspendisse potenti. Vestibulum cursus congue sem et feugiat. Morbi quis elit odio.

+
+
+ + + + +
+ + + +
+ + + + + + + + diff --git a/contrib/library/network/server/nino/example/SimpleWebServer/webroot/demo2/demo.html b/contrib/library/network/server/nino/example/SimpleWebServer/webroot/demo2/demo.html index fa7a7881..38112636 100644 --- a/contrib/library/network/server/nino/example/SimpleWebServer/webroot/demo2/demo.html +++ b/contrib/library/network/server/nino/example/SimpleWebServer/webroot/demo2/demo.html @@ -1,43 +1,43 @@ - - - - -Halftone Navigation Menu With jQuery & CSS3 | Tutorialzine demo - - - - - - - - - - - -

Halftone Navigation Menu With jQuery & CSS3

-

View the original tutorial »

- - -
- - - -
- -
-
- -

This is a tutorialzine demo. View the original tutorial, or download the source files.

- - - - + + + + +Halftone Navigation Menu With jQuery & CSS3 | Tutorialzine demo + + + + + + + + + + + +

Halftone Navigation Menu With jQuery & CSS3

+

View the original tutorial »

+ + +
+ + + +
+ +
+
+ +

This is a tutorialzine demo. View the original tutorial, or download the source files.

+ + + + diff --git a/contrib/library/network/server/nino/example/SimpleWebServer/webroot/demo2/script.js b/contrib/library/network/server/nino/example/SimpleWebServer/webroot/demo2/script.js index 7cd3fbe5..150302f2 100644 --- a/contrib/library/network/server/nino/example/SimpleWebServer/webroot/demo2/script.js +++ b/contrib/library/network/server/nino/example/SimpleWebServer/webroot/demo2/script.js @@ -1,121 +1,121 @@ -/* Set serviceMode to true to create your own shapes: */ -var serviceMode = false; - -$(document).ready(function(){ - /* This code is executed after the DOM has been completely loaded */ - - var str=[]; - var perRow = 16; - - /* Generating the dot divs: */ - - for(var i=0;i<192;i++) - { - str.push('
'); - } - - /* Joining the array into a string and adding it to the inner html of the stage div: */ - - $('#stage').html(str.join('')); - - /* Using the hover method: */ - - $('#navigation li a').hover(function(e){ - - /* serviceDraw is a cut-out version of the draw function, used for shape editing and composing: */ - - if(serviceMode) - serviceDraw($(this).attr('class')); - else - draw($(this).attr('class')); - }, function(e){ - - }); - - /* Caching the dot divs into a variable for performance: */ - dots = $('.dot'); - - if(serviceMode) - { - /* If we are in service mode, show borders around the dot divs, add the export link, and listen for clicks: */ - - dots.css({ - border:'1px solid black', - width:dots.eq(0).width()-2, - height:dots.eq(0).height()-2, - cursor:'pointer' - }) - - $('
').css({ - position:'absolute', - bottom:-20, - right:0 - }).html('[Export Shape]').appendTo('#stage'); - - dots.click(function(){ - $(this).toggleClass('active'); - }); - } - -}); - -var shapes={ - - /* Each shape is described by an array of points. You can add your own shapes here, - just don't forget to add a coma after each array, except for the last one */ - - house:[22,37,38,39,52,53,54,55,56,67,68,69,70,71,72,73,82,83,84,85,86,87,88,89,90,99,100,104,105,115,116,120,121,131,132,136,137,147,148,150,151,152,153,163,164,166,167,168,169], - wrench:[22,23,24,25,26,27,38,39,40,41,42,43,54,55,58,59,70,71,86,87,88,89,101,102,103,104,105,116,117,118,131,132,133,146,147,148,163], - envelope:[34,35,36,37,38,39,40,41,42,43,44,50,51,52,58,59,60,66,68,69,73,74,76,82,85,86,88,89,92,98,102,103,104,108,114,119,124,130,140,146,147,148,149,150,151,152,153,154,155,156], - info:[22,23,38,39,69,70,71,86,87,102,103,118,119,134,135,150,151,166,167,168] -} - -var stopCounter = 0; -var dots; - -function draw(shape) -{ - /* This function draws a shape from the shapes object */ - - stopCounter++; - var currentCounter = stopCounter; - - dots.removeClass('active').css('opacity',0); - - $.each(shapes[shape],function(i,j){ - setTimeout(function(){ - - /* If a different shape animaton has been started during the showing of the current one, exit the function */ - if(currentCounter!=stopCounter) return false; - - dots.eq(j).addClass('active').fadeTo('slow',0.4); - - /* The fade animation is scheduled for 10*i millisecond in the future: */ - },10*i); - - }); -} - -function serviceDraw(shape) -{ - /* A cut out version of the draw function, used in service mode */ - - dots.removeClass('active'); - - $.each(shapes[shape],function(i,j){ - dots.eq(j).addClass('active'); - }); -} - -function outputString() -{ - /* Outputs the positions of the active dot divs as a comma-separated string: */ - - var str=[]; - $('.dot.active').each(function(){ - - str.push(this.id.replace('d-','')); - }) - - prompt('Insert this string as an array in the shapes object',str.join(',')); +/* Set serviceMode to true to create your own shapes: */ +var serviceMode = false; + +$(document).ready(function(){ + /* This code is executed after the DOM has been completely loaded */ + + var str=[]; + var perRow = 16; + + /* Generating the dot divs: */ + + for(var i=0;i<192;i++) + { + str.push('
'); + } + + /* Joining the array into a string and adding it to the inner html of the stage div: */ + + $('#stage').html(str.join('')); + + /* Using the hover method: */ + + $('#navigation li a').hover(function(e){ + + /* serviceDraw is a cut-out version of the draw function, used for shape editing and composing: */ + + if(serviceMode) + serviceDraw($(this).attr('class')); + else + draw($(this).attr('class')); + }, function(e){ + + }); + + /* Caching the dot divs into a variable for performance: */ + dots = $('.dot'); + + if(serviceMode) + { + /* If we are in service mode, show borders around the dot divs, add the export link, and listen for clicks: */ + + dots.css({ + border:'1px solid black', + width:dots.eq(0).width()-2, + height:dots.eq(0).height()-2, + cursor:'pointer' + }) + + $('
').css({ + position:'absolute', + bottom:-20, + right:0 + }).html('[Export Shape]').appendTo('#stage'); + + dots.click(function(){ + $(this).toggleClass('active'); + }); + } + +}); + +var shapes={ + + /* Each shape is described by an array of points. You can add your own shapes here, + just don't forget to add a coma after each array, except for the last one */ + + house:[22,37,38,39,52,53,54,55,56,67,68,69,70,71,72,73,82,83,84,85,86,87,88,89,90,99,100,104,105,115,116,120,121,131,132,136,137,147,148,150,151,152,153,163,164,166,167,168,169], + wrench:[22,23,24,25,26,27,38,39,40,41,42,43,54,55,58,59,70,71,86,87,88,89,101,102,103,104,105,116,117,118,131,132,133,146,147,148,163], + envelope:[34,35,36,37,38,39,40,41,42,43,44,50,51,52,58,59,60,66,68,69,73,74,76,82,85,86,88,89,92,98,102,103,104,108,114,119,124,130,140,146,147,148,149,150,151,152,153,154,155,156], + info:[22,23,38,39,69,70,71,86,87,102,103,118,119,134,135,150,151,166,167,168] +} + +var stopCounter = 0; +var dots; + +function draw(shape) +{ + /* This function draws a shape from the shapes object */ + + stopCounter++; + var currentCounter = stopCounter; + + dots.removeClass('active').css('opacity',0); + + $.each(shapes[shape],function(i,j){ + setTimeout(function(){ + + /* If a different shape animaton has been started during the showing of the current one, exit the function */ + if(currentCounter!=stopCounter) return false; + + dots.eq(j).addClass('active').fadeTo('slow',0.4); + + /* The fade animation is scheduled for 10*i millisecond in the future: */ + },10*i); + + }); +} + +function serviceDraw(shape) +{ + /* A cut out version of the draw function, used in service mode */ + + dots.removeClass('active'); + + $.each(shapes[shape],function(i,j){ + dots.eq(j).addClass('active'); + }); +} + +function outputString() +{ + /* Outputs the positions of the active dot divs as a comma-separated string: */ + + var str=[]; + $('.dot.active').each(function(){ + + str.push(this.id.replace('d-','')); + }) + + prompt('Insert this string as an array in the shapes object',str.join(',')); } \ No newline at end of file diff --git a/contrib/library/network/server/nino/example/SimpleWebServer/webroot/demo2/styles.css b/contrib/library/network/server/nino/example/SimpleWebServer/webroot/demo2/styles.css index 73897265..e0882dd2 100644 --- a/contrib/library/network/server/nino/example/SimpleWebServer/webroot/demo2/styles.css +++ b/contrib/library/network/server/nino/example/SimpleWebServer/webroot/demo2/styles.css @@ -1,148 +1,148 @@ -body,h1,h2,h3,p,quote,small,form,input,ul,li,ol,label{ - /* Simple page reset */ - margin:0; - padding:0; -} - -body{ - /* Setting default text color, background and a font stack */ - color:#cccccc; - font-size:0.825em; - background: url(img/background.jpg) no-repeat center top #252525; - font-family:Arial, Helvetica, sans-serif; -} - -.menuUL li{ - /* This will arrange the LI-s next to each other */ - display:inline; -} - -.menuUL li a,.menuUL li a:visited{ - /* Styling the hyperlinks of the menu as buttons */ - - float:left; - font-weight:bold; - background:url(img/button_bg.jpg) repeat-x center bottom #666666; - - /* display:block allows for additinal CSS rules to take effect, such as paddings: */ - display:block; - border:1px solid #4D4D4D; - color:#CCCCCC; - border-top-color:#565656; - - padding:4px 6px; - margin:4px 5px; - height:16px; - - - /* Setting a CSS3 box shadow around the button */ - - -moz-box-shadow:0 0 1px black; - -webkit-box-shadow:0 0 1px black; - box-shadow:0 0 1px black; - - /* CSS3 text shadow */ - text-shadow:0 1px black; -} - -.menuUL li a:hover{ - /* On hover show the top, lighter, part of the background: */ - background-position:center top; - text-decoration:none; -} - -#navigation{ - /* The navigation menu bar: */ - background:#222222; - border:1px solid #111111; - float:left; - padding:5px 10px; -} - -#navigation,.menuUL li a{ - /* CSS3 rounded corners for both the navigation bar and the buttons: */ - -moz-border-radius:4px; - -webkit-border-radius:4px; - -khtml-border-radius:4px; - border-radius:4px; -} - -#stage{ - /* The stage contains the individual divs that comprise the halftone icon: */ - height:300px; - position:absolute; - right:50px; - top:20px; - width:400px; -} - -.dot{ - /* The stage contains 192 .dot divs: */ - float:left; - height:25px; - width:25px; -} - -.dot.active{ - /* When assigned the active class, the div shows a background image of a dot: */ - background:url(img/dot.png) no-repeat center center; -} - -.clear{ - /* Old-school clear fix hack to clear the floats: */ - clear:both; -} - -#main{ - margin:0 auto; - position:relative; - width:900px; -} - -/* The styles below are only necessary for the demo page */ - -h1{ - background:#222222; - border-bottom:1px solid black; - font-size:1.5em; - font-weight:normal; - margin-bottom:15px; - padding:15px; - text-align:center; -} - -h2 { - font-size:0.9em; - font-weight:normal; - padding-right:40px; - position:relative; - right:0; - text-align:right; - text-transform:uppercase; - top:-48px; -} - -a, a:visited { - color:#0196e3; - text-decoration:none; - outline:none; -} - -a:hover{ - text-decoration:underline; -} - -p.tutInfo{ - /* The tutorial info on the bottom of the page */ - padding:10px 0; - text-align:center; - position:absolute; - bottom:0px; - background:#222222; - border-top:1px solid black; - width:100%; -} - -h1,h2,p.tutInfo{ - font-family:"Myriad Pro",Arial,Helvetica,sans-serif; -} +body,h1,h2,h3,p,quote,small,form,input,ul,li,ol,label{ + /* Simple page reset */ + margin:0; + padding:0; +} + +body{ + /* Setting default text color, background and a font stack */ + color:#cccccc; + font-size:0.825em; + background: url(img/background.jpg) no-repeat center top #252525; + font-family:Arial, Helvetica, sans-serif; +} + +.menuUL li{ + /* This will arrange the LI-s next to each other */ + display:inline; +} + +.menuUL li a,.menuUL li a:visited{ + /* Styling the hyperlinks of the menu as buttons */ + + float:left; + font-weight:bold; + background:url(img/button_bg.jpg) repeat-x center bottom #666666; + + /* display:block allows for additinal CSS rules to take effect, such as paddings: */ + display:block; + border:1px solid #4D4D4D; + color:#CCCCCC; + border-top-color:#565656; + + padding:4px 6px; + margin:4px 5px; + height:16px; + + + /* Setting a CSS3 box shadow around the button */ + + -moz-box-shadow:0 0 1px black; + -webkit-box-shadow:0 0 1px black; + box-shadow:0 0 1px black; + + /* CSS3 text shadow */ + text-shadow:0 1px black; +} + +.menuUL li a:hover{ + /* On hover show the top, lighter, part of the background: */ + background-position:center top; + text-decoration:none; +} + +#navigation{ + /* The navigation menu bar: */ + background:#222222; + border:1px solid #111111; + float:left; + padding:5px 10px; +} + +#navigation,.menuUL li a{ + /* CSS3 rounded corners for both the navigation bar and the buttons: */ + -moz-border-radius:4px; + -webkit-border-radius:4px; + -khtml-border-radius:4px; + border-radius:4px; +} + +#stage{ + /* The stage contains the individual divs that comprise the halftone icon: */ + height:300px; + position:absolute; + right:50px; + top:20px; + width:400px; +} + +.dot{ + /* The stage contains 192 .dot divs: */ + float:left; + height:25px; + width:25px; +} + +.dot.active{ + /* When assigned the active class, the div shows a background image of a dot: */ + background:url(img/dot.png) no-repeat center center; +} + +.clear{ + /* Old-school clear fix hack to clear the floats: */ + clear:both; +} + +#main{ + margin:0 auto; + position:relative; + width:900px; +} + +/* The styles below are only necessary for the demo page */ + +h1{ + background:#222222; + border-bottom:1px solid black; + font-size:1.5em; + font-weight:normal; + margin-bottom:15px; + padding:15px; + text-align:center; +} + +h2 { + font-size:0.9em; + font-weight:normal; + padding-right:40px; + position:relative; + right:0; + text-align:right; + text-transform:uppercase; + top:-48px; +} + +a, a:visited { + color:#0196e3; + text-decoration:none; + outline:none; +} + +a:hover{ + text-decoration:underline; +} + +p.tutInfo{ + /* The tutorial info on the bottom of the page */ + padding:10px 0; + text-align:center; + position:absolute; + bottom:0px; + background:#222222; + border-top:1px solid black; + width:100%; +} + +h1,h2,p.tutInfo{ + font-family:"Myriad Pro",Arial,Helvetica,sans-serif; +} diff --git a/contrib/library/network/server/nino/example/SimpleWebServer/webroot/example/html/contentpage.html b/contrib/library/network/server/nino/example/SimpleWebServer/webroot/example/html/contentpage.html index efc4f6e0..3b82eda3 100644 --- a/contrib/library/network/server/nino/example/SimpleWebServer/webroot/example/html/contentpage.html +++ b/contrib/library/network/server/nino/example/SimpleWebServer/webroot/example/html/contentpage.html @@ -1,153 +1,153 @@ - - - -Business Co. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- SEPTEMBER 29, 2009 -
- - - - -
-
Content Page
HOME | ABOUT US | SERVICES | SOLUTIONS | SUPPORT | CONTACTS
- Copyright © Your Company Name
- Design by Templates Box. Create a free website. -
- - + + + +Business Co. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ SEPTEMBER 29, 2009 +
+ + + + +
+
Content Page
HOME | ABOUT US | SERVICES | SOLUTIONS | SUPPORT | CONTACTS
+ Copyright © Your Company Name
+ Design by Templates Box. Create a free website. +
+ + \ No newline at end of file diff --git a/contrib/library/network/server/nino/example/SimpleWebServer/webroot/example/html/css/styles.css b/contrib/library/network/server/nino/example/SimpleWebServer/webroot/example/html/css/styles.css index ae53ecae..3607601c 100644 --- a/contrib/library/network/server/nino/example/SimpleWebServer/webroot/example/html/css/styles.css +++ b/contrib/library/network/server/nino/example/SimpleWebServer/webroot/example/html/css/styles.css @@ -1,160 +1,160 @@ -body{ - padding:0px; - margin:0px; - background:#c7c7c7; - color:#848484; - font:10px/14px Tahoma, sans-serif; -} - -div, p, ul, h2, h3, h4, img, form{padding:0px; margin:0px;} -ul{list-style-type:none;} - -.clear{ -clear:both; -} - -.frame { - border: 1px solid #D5E6E0; -} - -.text1 { - font: 11px/14px "Trebuchet MS", Arial, Helvetica, sans-serif; - color:#000; - font-weight:bold; -} -.text2 { - font: 11px/14px Tahoma, Geneva, sans-serif; - color:#000; - font-weight:normal; -} -.text3 { - font: 10px/14px Tahoma, Geneva, sans-serif; - color:#000; - font-weight:normal; -} - -.text4 { - font: 10px/12px Tahoma, Geneva, sans-serif; - color:#052578; - font-weight:bold; -} -.text5 { - font: 10px/12px Tahoma, Geneva, sans-serif; - color:#052578; - font-weight:bold; -} - - -a:link { - font: 11px/14px Tahoma, Geneva, sans-serif; - color:#000; - font-weight:normal; - text-decoration:none; -} - -a:visited{ - font: 11px/14px Tahoma, Geneva, sans-serif; - color:#000; - font-weight:normal; - text-decoration:none; -} - -a:hover { - font: 11px/14px Tahoma, Geneva, sans-serif; - color:#000; - font-weight:normal; - text-decoration:underline; -} - -a.a:link { - font: 10px/14px Tahoma, Geneva, sans-serif; - color:#19a1cb; - font-weight:normal; - text-decoration:underline; -} - -a.a:visited{ - font: 10px/14px Tahoma, Geneva, sans-serif; - color:#19a1cb; - font-weight:normal; - text-decoration:underline; -} - -a.a:hover { - font: 10px/14px Tahoma, Geneva, sans-serif; - color:#000; - font-weight:normal; - text-decoration:none; -} - -a.b:link { - font: 10px/18px Tahoma, Geneva, sans-serif; - color:#848484; - font-weight:normal; - text-decoration:underline; -} - -a.b:visited{ - font: 10px/18px Tahoma, Geneva, sans-serif; - color:#848484; - font-weight:normal; - text-decoration:underline; -} - -a.b:hover { - font: 10px/18px Tahoma, Geneva, sans-serif; - color:#000; - font-weight:normal; - text-decoration:none; -} - -a.c:link { - font: 10px/12px Tahoma, Geneva, sans-serif; - color:#FFF; - font-weight:normal; - text-decoration:none; -} - -a.c:visited{ - font: 10px/12px Tahoma, Geneva, sans-serif; - color:#FFF; - font-weight:normal; - text-decoration:none; -} - -a.c:hover { - font: 10px/12px Tahoma, Geneva, sans-serif; - color:#FFF; - font-weight:normal; - text-decoration:underline; -} - -a.d:link { - font: 10px/12px Tahoma, Geneva, sans-serif; - color:#FFF; - font-weight:normal; - text-decoration:none; -} - -a.d:visited{ - font: 10px/12px Tahoma, Geneva, sans-serif; - color:#FFF; - font-weight:normal; - text-decoration:none; -} - -a.d:hover { - font: 10px/12px Tahoma, Geneva, sans-serif; - color:#FFF; - font-weight:normal; - text-decoration:underline; -} - -input, textarea, select{ -border:#fff 1px solid; -background-color:#d6e6e0; -font:10px/12px Tahoma, sans-serif; color:#000; -} -a.adv:link {text-decoration: none; font-weight:bold; color:#000;} -a.adv:hover {text-decoration: none; font-weight:bold; color:#000;} -a.adv:visited {text-decoration: none; font-weight:bold; color:#000;} +body{ + padding:0px; + margin:0px; + background:#c7c7c7; + color:#848484; + font:10px/14px Tahoma, sans-serif; +} + +div, p, ul, h2, h3, h4, img, form{padding:0px; margin:0px;} +ul{list-style-type:none;} + +.clear{ +clear:both; +} + +.frame { + border: 1px solid #D5E6E0; +} + +.text1 { + font: 11px/14px "Trebuchet MS", Arial, Helvetica, sans-serif; + color:#000; + font-weight:bold; +} +.text2 { + font: 11px/14px Tahoma, Geneva, sans-serif; + color:#000; + font-weight:normal; +} +.text3 { + font: 10px/14px Tahoma, Geneva, sans-serif; + color:#000; + font-weight:normal; +} + +.text4 { + font: 10px/12px Tahoma, Geneva, sans-serif; + color:#052578; + font-weight:bold; +} +.text5 { + font: 10px/12px Tahoma, Geneva, sans-serif; + color:#052578; + font-weight:bold; +} + + +a:link { + font: 11px/14px Tahoma, Geneva, sans-serif; + color:#000; + font-weight:normal; + text-decoration:none; +} + +a:visited{ + font: 11px/14px Tahoma, Geneva, sans-serif; + color:#000; + font-weight:normal; + text-decoration:none; +} + +a:hover { + font: 11px/14px Tahoma, Geneva, sans-serif; + color:#000; + font-weight:normal; + text-decoration:underline; +} + +a.a:link { + font: 10px/14px Tahoma, Geneva, sans-serif; + color:#19a1cb; + font-weight:normal; + text-decoration:underline; +} + +a.a:visited{ + font: 10px/14px Tahoma, Geneva, sans-serif; + color:#19a1cb; + font-weight:normal; + text-decoration:underline; +} + +a.a:hover { + font: 10px/14px Tahoma, Geneva, sans-serif; + color:#000; + font-weight:normal; + text-decoration:none; +} + +a.b:link { + font: 10px/18px Tahoma, Geneva, sans-serif; + color:#848484; + font-weight:normal; + text-decoration:underline; +} + +a.b:visited{ + font: 10px/18px Tahoma, Geneva, sans-serif; + color:#848484; + font-weight:normal; + text-decoration:underline; +} + +a.b:hover { + font: 10px/18px Tahoma, Geneva, sans-serif; + color:#000; + font-weight:normal; + text-decoration:none; +} + +a.c:link { + font: 10px/12px Tahoma, Geneva, sans-serif; + color:#FFF; + font-weight:normal; + text-decoration:none; +} + +a.c:visited{ + font: 10px/12px Tahoma, Geneva, sans-serif; + color:#FFF; + font-weight:normal; + text-decoration:none; +} + +a.c:hover { + font: 10px/12px Tahoma, Geneva, sans-serif; + color:#FFF; + font-weight:normal; + text-decoration:underline; +} + +a.d:link { + font: 10px/12px Tahoma, Geneva, sans-serif; + color:#FFF; + font-weight:normal; + text-decoration:none; +} + +a.d:visited{ + font: 10px/12px Tahoma, Geneva, sans-serif; + color:#FFF; + font-weight:normal; + text-decoration:none; +} + +a.d:hover { + font: 10px/12px Tahoma, Geneva, sans-serif; + color:#FFF; + font-weight:normal; + text-decoration:underline; +} + +input, textarea, select{ +border:#fff 1px solid; +background-color:#d6e6e0; +font:10px/12px Tahoma, sans-serif; color:#000; +} +a.adv:link {text-decoration: none; font-weight:bold; color:#000;} +a.adv:hover {text-decoration: none; font-weight:bold; color:#000;} +a.adv:visited {text-decoration: none; font-weight:bold; color:#000;} diff --git a/contrib/library/network/server/nino/example/SimpleWebServer/webroot/example/html/index.html b/contrib/library/network/server/nino/example/SimpleWebServer/webroot/example/html/index.html index ee237b52..1a6274e9 100644 --- a/contrib/library/network/server/nino/example/SimpleWebServer/webroot/example/html/index.html +++ b/contrib/library/network/server/nino/example/SimpleWebServer/webroot/example/html/index.html @@ -1,197 +1,197 @@ - - - -Business Co. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- SEPTEMBER 29, 2009 -
- - - - -
-
- -
- Lorem ipsum dolor sit amet, consectetuer
-Nam eu nulla. Donec lobortis purus vel urna. Nunc laoreet lacinia nunc. In volutpat sodales ipsum. Sed vestibulum. Integer in ante. Sed posuere ligula rhoncus erat. Fusce urna dui, sollicitudin ac, pulvinar quis
-

September 29 -

-

Integer in ante. Sed posuere ligula rhoncus erat. Fusce urna dui

September 28 -
- Integer in ante. Sed posuere ligula rhoncus erat. Fusce urna dui
-
-
- Lorem ipsum dolor sit amet, consectetuer
- Nam eu nulla. Donec lobortis purus vel urna. Nunc laoreet lacinia nunc. In volutpat sodales ipsum. Sed vestibulum. rhoncus erat. Fusce urna dui, sollicitudin ac, pulvinar quis
- Morbi volutpat leo in ligula. Inter vel
- magna. sagittis. Fusce elit ligula,
- sodales sit amet, tincid unt in, Fusce
- interdum. Sed laoreet. Aenean. Sed
-laoreet. magna. sagittis. Fusce elit
- Morbi volutpat leo in ligula. Inter vel magna. sagittis. Fusce elit ligula, sodales
- sit amet, tincid unt in, Fusce interdum. Sed laoreet. Aenean. Sed laoreet.
HOME | ABOUT US | SERVICES | SOLUTIONS | SUPPORT | CONTACTS
- Copyright © Your Company Name
- Design by Templates Box. Create a free website. -
- - + + + +Business Co. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ SEPTEMBER 29, 2009 +
+ + + + +
+
+ +
+ Lorem ipsum dolor sit amet, consectetuer
+Nam eu nulla. Donec lobortis purus vel urna. Nunc laoreet lacinia nunc. In volutpat sodales ipsum. Sed vestibulum. Integer in ante. Sed posuere ligula rhoncus erat. Fusce urna dui, sollicitudin ac, pulvinar quis
+

September 29 +

+

Integer in ante. Sed posuere ligula rhoncus erat. Fusce urna dui

September 28 +
+ Integer in ante. Sed posuere ligula rhoncus erat. Fusce urna dui
+
+
+ Lorem ipsum dolor sit amet, consectetuer
+ Nam eu nulla. Donec lobortis purus vel urna. Nunc laoreet lacinia nunc. In volutpat sodales ipsum. Sed vestibulum. rhoncus erat. Fusce urna dui, sollicitudin ac, pulvinar quis
+ Morbi volutpat leo in ligula. Inter vel
+ magna. sagittis. Fusce elit ligula,
+ sodales sit amet, tincid unt in, Fusce
+ interdum. Sed laoreet. Aenean. Sed
+laoreet. magna. sagittis. Fusce elit
+ Morbi volutpat leo in ligula. Inter vel magna. sagittis. Fusce elit ligula, sodales
+ sit amet, tincid unt in, Fusce interdum. Sed laoreet. Aenean. Sed laoreet.
HOME | ABOUT US | SERVICES | SOLUTIONS | SUPPORT | CONTACTS
+ Copyright © Your Company Name
+ Design by Templates Box. Create a free website. +
+ + \ No newline at end of file diff --git a/contrib/library/network/server/nino/example/SimpleWebServer/webroot/example/readme.html b/contrib/library/network/server/nino/example/SimpleWebServer/webroot/example/readme.html index 4e6cf8f0..f6a428bf 100644 --- a/contrib/library/network/server/nino/example/SimpleWebServer/webroot/example/readme.html +++ b/contrib/library/network/server/nino/example/SimpleWebServer/webroot/example/readme.html @@ -1,86 +1,86 @@ - - - -TemplatesBox.com | Terms of Use - - - - - - - - - - - - - - - - - -
TemplatesBox - | Terms of Use
- - - - -
-

By - downloading a template from TemplatesBox.com you agree to the following - Terms of Use: -

Our - web templates may be used for your own and/or your clients' websites, - but you may not sell/offer for free our templates in any sort of collection, - such as distributing to a third party via CD, diskette, or letting others - to download off your websites etc.
-
- Link back to www.templatesbox.com is required and always appreciated. - Also, please visit the
-
Link - Us section.
-
- The templates are offered "as is" without warranty of any kind, - either expressed or implied. TemplatesBox.com will not be liable for any - damage or loss of data whatsoever due to downloading or using a template. - In no event shall TemplatesBox.com be liable for any damages including, - but not limited to, direct, indirect, special, incidental or consequential - damages or other losses arising out of the use of or inability to use - the templates and/or information from TemplatesBox.com.
-
- TemplatesBox.com team reserves the right to change or modify these terms - with no prior notice.

-
-
Featured - Partners
- - - - -
-

Premium Website - Templates
- Over 9000 High-end Website templates, Flash intros and Logo templates.
-
-
Free - Photos, Free Stock Photography
-
1000's of FREE high quality stock -Photos!
-
-
Webmaster Resources & -Directory
-
A large web directory with webmaster -resources.
-
-
Photovations.com
-
Online Photo Sharing. Free Image -Hosting

-
-
- - + + + +TemplatesBox.com | Terms of Use + + + + + + + + + + + + + + + + + +
TemplatesBox + | Terms of Use
+ + + + +
+

By + downloading a template from TemplatesBox.com you agree to the following + Terms of Use: +

Our + web templates may be used for your own and/or your clients' websites, + but you may not sell/offer for free our templates in any sort of collection, + such as distributing to a third party via CD, diskette, or letting others + to download off your websites etc.
+
+ Link back to www.templatesbox.com is required and always appreciated. + Also, please visit the
+
Link + Us section.
+
+ The templates are offered "as is" without warranty of any kind, + either expressed or implied. TemplatesBox.com will not be liable for any + damage or loss of data whatsoever due to downloading or using a template. + In no event shall TemplatesBox.com be liable for any damages including, + but not limited to, direct, indirect, special, incidental or consequential + damages or other losses arising out of the use of or inability to use + the templates and/or information from TemplatesBox.com.
+
+ TemplatesBox.com team reserves the right to change or modify these terms + with no prior notice.

+
+
Featured + Partners
+ + + + +
+

Premium Website + Templates
+ Over 9000 High-end Website templates, Flash intros and Logo templates.
+
+
Free + Photos, Free Stock Photography
+
1000's of FREE high quality stock +Photos!
+
+
Webmaster Resources & +Directory
+
A large web directory with webmaster +resources.
+
+
Photovations.com
+
Online Photo Sharing. Free Image +Hosting

+
+
+ + diff --git a/contrib/library/network/server/nino/example/SimpleWebServer/webroot/html/images.html b/contrib/library/network/server/nino/example/SimpleWebServer/webroot/html/images.html index 032d3175..99ceb851 100644 --- a/contrib/library/network/server/nino/example/SimpleWebServer/webroot/html/images.html +++ b/contrib/library/network/server/nino/example/SimpleWebServer/webroot/html/images.html @@ -1,9 +1,9 @@ - - - -

Norwegian Mountain Trip

-Pulpit rock - - - - + + + +

Norwegian Mountain Trip

+Pulpit rock + + + + diff --git a/contrib/library/network/server/nino/example/SimpleWebServer/webroot/html/simple.html b/contrib/library/network/server/nino/example/SimpleWebServer/webroot/html/simple.html index f61058f3..15894d06 100644 --- a/contrib/library/network/server/nino/example/SimpleWebServer/webroot/html/simple.html +++ b/contrib/library/network/server/nino/example/SimpleWebServer/webroot/html/simple.html @@ -1,10 +1,10 @@ - - - -

My First Heading

- -

My first paragraph.

- - - - + + + +

My First Heading

+ +

My first paragraph.

+ + + + diff --git a/contrib/library/network/server/nino/example/SimpleWebServer/webroot/html5/dataset.html b/contrib/library/network/server/nino/example/SimpleWebServer/webroot/html5/dataset.html index b4db4c29..cb94ab98 100644 --- a/contrib/library/network/server/nino/example/SimpleWebServer/webroot/html5/dataset.html +++ b/contrib/library/network/server/nino/example/SimpleWebServer/webroot/html5/dataset.html @@ -1,99 +1,99 @@ - - - - - -HTML5 Demo: data-* - - - -
-
-

data-*

-
- -
-
-

The data-[name] attribute on elements can now be accessed directly via the DOM using element.dataset.[attr].

-

Try openning the Web Console and editing element.dataset directly:
element.dataset.foo = 'bar';

-
-

Not connected

-
-
This element has data
- - - -
-
[click buttons above to show element html]
-
- - -
-Fork me on GitHub - - - - + + + + + +HTML5 Demo: data-* + + + +
+
+

data-*

+
+ +
+
+

The data-[name] attribute on elements can now be accessed directly via the DOM using element.dataset.[attr].

+

Try openning the Web Console and editing element.dataset directly:
element.dataset.foo = 'bar';

+
+

Not connected

+
+
This element has data
+ + + +
+
[click buttons above to show element html]
+
+ + +
+Fork me on GitHub + + + + \ No newline at end of file diff --git a/contrib/library/network/server/nino/library/http_constants.e b/contrib/library/network/server/nino/library/http_constants.e index 15b53088..c3cc1b0c 100644 --- a/contrib/library/network/server/nino/library/http_constants.e +++ b/contrib/library/network/server/nino/library/http_constants.e @@ -1,149 +1,149 @@ -note - description: "Summary description for {HTTP_CONSTANTS}." - author: "" - date: "$Date$" - revision: "$Revision$" - -class - HTTP_CONSTANTS - -feature - - http_version_1_1: STRING = "HTTP/1.1" - http_version_1_0: STRING = "HTTP/1.0" - crlf: STRING = "%/13/%/10/" - -feature -- Status codes - - -- 1xx Informational -Request received, continuing process - Continue : STRING = "100" - Switching_Protocols : STRING = "101" - - - -- 2xx Success - The action was successfully received, understood, and accepted - Ok: STRING = "200" - Created : STRING = "201" - Accepted : STRING = "202" - Non_Authoritative_Information : STRING = "203" - No_Content : STRING = "204" - Reset_Content : STRING = "205" - Parcial_Content : STRING = "206" - - - -- 3xx Redirection - Further Action must be taken in order to complete the request - Multiple_Choices : STRING = "300" - Moved_Permanently: STRING = "301" - Found : STRING = "302" - See_Other : STRING = "303" - Not_Modified : STRING = "304" - Use_Proxy : STRING = "305" - Temporary_Redirect : STRING = "307" - - - --4xx Client Error - The request contains bad syntax or cannot be fulfilled - Bad_Request : STRING = "400" - Unauthorized : STRING = "401" - Payment_Required : STRING = "402" - Forbidden : STRING = "403" - Not_Found : STRING = "404" - Method_Not_Allowed : STRING = "405" - Not_Acceptable : STRING = "406" - Proxy_Authentication_Required : STRING = "407" - Request_Time_out : STRING = "408" - Conflict : STRING = "409" - Gone : STRING = "410" - Length_Required : STRING = "411" - Precondition_Failed : STRING = "412" - Request_Entity_Too_Large : STRING = "413" - Request_URI_Too_Large : STRING = "414" - Unsupported_Media_Type : STRING = "415" - Requested_range_not_satisfiable : STRING = "416" - Expectation_Failed : STRING = "417" - - - --5xx Server Error - The server failed to fulfill an apparently valid request - server_error: STRING = "500" - Internal_Server_Error : STRING = "500" - Not_Implemented : STRING = "501" - Bad_Gateway : STRING = "502" - Service_Unavailable : STRING = "503" - Gateway_Time_out : STRING = "504" - HTTP_Version_not_supported : STRING = "505" - - - -- messages - ok_message: STRING = "OK" - continue_message : STRING = "Continue" - not_found_message: STRING = "URI not found" - not_implemented_message: STRING = "Not Implemented" - -feature -- content types - - text_html: STRING = "text/html" - -feature -- General Header Fields - - -- There are a few header fields which have general applicability for both request and response messages, - -- but which do not apply to the entity being transferred. - -- These header fields apply only to the message being transmitted. - - Cache_control : STRING = "Cache-Control" - Connection : STRING = "Connection" - Date : STRING = "Date" - Pragma : STRING = "PRAGMA" - Trailer : STRING = "Trailer" - Transfer_encoding : STRING = "Transfer-Encoding" - Upgrade : STRING = "Upgrade" - Via : STRING = "Via" - Warning : STRING = "Warning" - - -feature -- Request Header - Accept : STRING = "Accept" - Accept_charset : STRING = "Accept-Charset" - Accept_encoding : STRING = "Accept-Encoding" - Accept_language : STRING = "Accept-Language" - Authorization : STRING = "Authorization" - Expect : STRING = "Expect" - From_header : STRING = "From" - Host : STRING = "Host" - If_match : STRING = "If-Match" - If_modified_since : STRING = "If-Modified-Since" - If_none_match : STRING = "If-None-Match" - If_range : STRING = "If-Range" - If_unmodified_since : STRING = "If-Unmodified-Since" - Max_forwards : STRING = "Max-Forwards" - Proxy_authorization : STRING = "Proxy-Authorization" - Range : STRING = "Range" - Referer : STRING = "Referrer" - TE : STRING = "TE" - User_agent : STRING = "User-Agent" - - -feature -- Entity Header - - Allow : STRING = "Allow" - Content_encoding : STRING = "Content-Encoding" - Content_language : STRING = "Content-Language" - Content_length : STRING = "Content-Length" - Content_location : STRING = "Content-Location" - Content_MD5 : STRING = "Content-MD5" - Content_range : STRING = "Content-Range" - Content_type : STRING = "Content-Type" - Expires : STRING = "Expires" - Last_modified : STRING = "Last-Modified" - - -feature -- Http Method - Options : STRING = "OPTIONS" - Get : STRING = "GET" - Head : STRING = "HEAD" - Post : STRING = "POST" - Put : STRING = "PUT" - Delete : STRING = "DELETE" - Trace : STRING = "TRACE" - Connect : STRING = "CONNECT" -note - copyright: "2011-2011, Javier Velilla and others" - license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" -end +note + description: "Summary description for {HTTP_CONSTANTS}." + author: "" + date: "$Date$" + revision: "$Revision$" + +class + HTTP_CONSTANTS + +feature + + http_version_1_1: STRING = "HTTP/1.1" + http_version_1_0: STRING = "HTTP/1.0" + crlf: STRING = "%/13/%/10/" + +feature -- Status codes + + -- 1xx Informational -Request received, continuing process + Continue : STRING = "100" + Switching_Protocols : STRING = "101" + + + -- 2xx Success - The action was successfully received, understood, and accepted + Ok: STRING = "200" + Created : STRING = "201" + Accepted : STRING = "202" + Non_Authoritative_Information : STRING = "203" + No_Content : STRING = "204" + Reset_Content : STRING = "205" + Parcial_Content : STRING = "206" + + + -- 3xx Redirection - Further Action must be taken in order to complete the request + Multiple_Choices : STRING = "300" + Moved_Permanently: STRING = "301" + Found : STRING = "302" + See_Other : STRING = "303" + Not_Modified : STRING = "304" + Use_Proxy : STRING = "305" + Temporary_Redirect : STRING = "307" + + + --4xx Client Error - The request contains bad syntax or cannot be fulfilled + Bad_Request : STRING = "400" + Unauthorized : STRING = "401" + Payment_Required : STRING = "402" + Forbidden : STRING = "403" + Not_Found : STRING = "404" + Method_Not_Allowed : STRING = "405" + Not_Acceptable : STRING = "406" + Proxy_Authentication_Required : STRING = "407" + Request_Time_out : STRING = "408" + Conflict : STRING = "409" + Gone : STRING = "410" + Length_Required : STRING = "411" + Precondition_Failed : STRING = "412" + Request_Entity_Too_Large : STRING = "413" + Request_URI_Too_Large : STRING = "414" + Unsupported_Media_Type : STRING = "415" + Requested_range_not_satisfiable : STRING = "416" + Expectation_Failed : STRING = "417" + + + --5xx Server Error - The server failed to fulfill an apparently valid request + server_error: STRING = "500" + Internal_Server_Error : STRING = "500" + Not_Implemented : STRING = "501" + Bad_Gateway : STRING = "502" + Service_Unavailable : STRING = "503" + Gateway_Time_out : STRING = "504" + HTTP_Version_not_supported : STRING = "505" + + + -- messages + ok_message: STRING = "OK" + continue_message : STRING = "Continue" + not_found_message: STRING = "URI not found" + not_implemented_message: STRING = "Not Implemented" + +feature -- content types + + text_html: STRING = "text/html" + +feature -- General Header Fields + + -- There are a few header fields which have general applicability for both request and response messages, + -- but which do not apply to the entity being transferred. + -- These header fields apply only to the message being transmitted. + + Cache_control : STRING = "Cache-Control" + Connection : STRING = "Connection" + Date : STRING = "Date" + Pragma : STRING = "PRAGMA" + Trailer : STRING = "Trailer" + Transfer_encoding : STRING = "Transfer-Encoding" + Upgrade : STRING = "Upgrade" + Via : STRING = "Via" + Warning : STRING = "Warning" + + +feature -- Request Header + Accept : STRING = "Accept" + Accept_charset : STRING = "Accept-Charset" + Accept_encoding : STRING = "Accept-Encoding" + Accept_language : STRING = "Accept-Language" + Authorization : STRING = "Authorization" + Expect : STRING = "Expect" + From_header : STRING = "From" + Host : STRING = "Host" + If_match : STRING = "If-Match" + If_modified_since : STRING = "If-Modified-Since" + If_none_match : STRING = "If-None-Match" + If_range : STRING = "If-Range" + If_unmodified_since : STRING = "If-Unmodified-Since" + Max_forwards : STRING = "Max-Forwards" + Proxy_authorization : STRING = "Proxy-Authorization" + Range : STRING = "Range" + Referer : STRING = "Referrer" + TE : STRING = "TE" + User_agent : STRING = "User-Agent" + + +feature -- Entity Header + + Allow : STRING = "Allow" + Content_encoding : STRING = "Content-Encoding" + Content_language : STRING = "Content-Language" + Content_length : STRING = "Content-Length" + Content_location : STRING = "Content-Location" + Content_MD5 : STRING = "Content-MD5" + Content_range : STRING = "Content-Range" + Content_type : STRING = "Content-Type" + Expires : STRING = "Expires" + Last_modified : STRING = "Last-Modified" + + +feature -- Http Method + Options : STRING = "OPTIONS" + Get : STRING = "GET" + Head : STRING = "HEAD" + Post : STRING = "POST" + Put : STRING = "PUT" + Delete : STRING = "DELETE" + Trace : STRING = "TRACE" + Connect : STRING = "CONNECT" +note + copyright: "2011-2011, Javier Velilla and others" + license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" +end diff --git a/contrib/library/network/server/nino/library/http_encoding_facilities.e b/contrib/library/network/server/nino/library/http_encoding_facilities.e index 81bc94ec..0b53cd12 100644 --- a/contrib/library/network/server/nino/library/http_encoding_facilities.e +++ b/contrib/library/network/server/nino/library/http_encoding_facilities.e @@ -1,60 +1,60 @@ -note - description: "[ - Provides features to encode and decode messages - ]" - legal: "See notice at end of class." - status: "Community Preview 1.0" - date: "$Date: 2009-09-01 19:15:37 -0300 (mar 01 de sep de 2009) $" - revision: "$Revision: 80577 $" - -class - HTTP_ENCODING_FACILITIES - -create - make - -feature -- Initialization - - make - do - end - -feature -- Conversion - - encode_natural(a_i: NATURAL; a_is_fragmented: BOOLEAN): NATURAL - -- Leftshift of the natural (don't use numbers >= 2^31) and subsequent append of the flag bit. - -- Use decode_natural and decode_flag for decoding. - require - no_too_big: a_i < 2147483648 - do - Result := (a_i |<< 1) + a_is_fragmented.to_integer.as_natural_32 - end - - change_flag(a_i: NATURAL; a_new_flag: BOOLEAN): NATURAL - -- Changes the flag to "new_flag" and doesn't change the encoded natural. - do - Result := (a_i & 0xFFFFFFFE) + a_new_flag.to_integer.as_natural_32 - end - - decode_natural_and_flag (a_i: NATURAL): TUPLE [NATURAL, BOOLEAN] - -- Convenience feature which combines both decodings (natural and flag) - do - Result := [decode_natural (a_i), decode_flag (a_i)] - end - - decode_natural (a_i: NATURAL): NATURAL - -- The natural that was encoded in {ENCODING_FACILITIES}.encode_natural. - do - Result := (a_i |>> 1) - end - - decode_flag (a_i: NATURAL): BOOLEAN - --`Result': the flag that was encoded in encode_natural - do - Result := (a_i.bit_and (1) = 1) - end - -note - copyright: "2011-2011, Javier Velilla and others" - license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" -end +note + description: "[ + Provides features to encode and decode messages + ]" + legal: "See notice at end of class." + status: "Community Preview 1.0" + date: "$Date: 2009-09-01 19:15:37 -0300 (mar 01 de sep de 2009) $" + revision: "$Revision: 80577 $" + +class + HTTP_ENCODING_FACILITIES + +create + make + +feature -- Initialization + + make + do + end + +feature -- Conversion + + encode_natural(a_i: NATURAL; a_is_fragmented: BOOLEAN): NATURAL + -- Leftshift of the natural (don't use numbers >= 2^31) and subsequent append of the flag bit. + -- Use decode_natural and decode_flag for decoding. + require + no_too_big: a_i < 2147483648 + do + Result := (a_i |<< 1) + a_is_fragmented.to_integer.as_natural_32 + end + + change_flag(a_i: NATURAL; a_new_flag: BOOLEAN): NATURAL + -- Changes the flag to "new_flag" and doesn't change the encoded natural. + do + Result := (a_i & 0xFFFFFFFE) + a_new_flag.to_integer.as_natural_32 + end + + decode_natural_and_flag (a_i: NATURAL): TUPLE [NATURAL, BOOLEAN] + -- Convenience feature which combines both decodings (natural and flag) + do + Result := [decode_natural (a_i), decode_flag (a_i)] + end + + decode_natural (a_i: NATURAL): NATURAL + -- The natural that was encoded in {ENCODING_FACILITIES}.encode_natural. + do + Result := (a_i |>> 1) + end + + decode_flag (a_i: NATURAL): BOOLEAN + --`Result': the flag that was encoded in encode_natural + do + Result := (a_i.bit_and (1) = 1) + end + +note + copyright: "2011-2011, Javier Velilla and others" + license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" +end diff --git a/contrib/library/network/server/nino/library/http_server.e b/contrib/library/network/server/nino/library/http_server.e index 26fb0df0..d7fae176 100644 --- a/contrib/library/network/server/nino/library/http_server.e +++ b/contrib/library/network/server/nino/library/http_server.e @@ -1,56 +1,56 @@ -note - description: "Summary description for {HTTP_SERVER}." - author: "" - date: "$Date$" - revision: "$Revision$" - -class - HTTP_SERVER - -create - make - -feature -- Initialization - - make (cfg: like configuration) - do - configuration := cfg - end - - setup (a_http_handler: HTTP_HANDLER) - require - a_http_handler_valid: a_http_handler /= Void - do - if configuration.is_verbose then - log ("%N%N%N") - log ("Starting Web Application Server (port="+ configuration.http_server_port.out +"):%N") - end - stop_requested := False - a_http_handler.execute - end - - shutdown_server - do - stop_requested := True - end - -feature -- Access - - configuration: HTTP_SERVER_CONFIGURATION - -- Configuration of the server - - stop_requested: BOOLEAN - -- Stops the server - -feature -- Output - - log (a_message: READABLE_STRING_8) - -- Log `a_message' - do - io.put_string (a_message) - end - -;note - copyright: "2011-2011, Javier Velilla and others" - license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" -end +note + description: "Summary description for {HTTP_SERVER}." + author: "" + date: "$Date$" + revision: "$Revision$" + +class + HTTP_SERVER + +create + make + +feature -- Initialization + + make (cfg: like configuration) + do + configuration := cfg + end + + setup (a_http_handler: HTTP_HANDLER) + require + a_http_handler_valid: a_http_handler /= Void + do + if configuration.is_verbose then + log ("%N%N%N") + log ("Starting Web Application Server (port="+ configuration.http_server_port.out +"):%N") + end + stop_requested := False + a_http_handler.execute + end + + shutdown_server + do + stop_requested := True + end + +feature -- Access + + configuration: HTTP_SERVER_CONFIGURATION + -- Configuration of the server + + stop_requested: BOOLEAN + -- Stops the server + +feature -- Output + + log (a_message: READABLE_STRING_8) + -- Log `a_message' + do + io.put_string (a_message) + end + +;note + copyright: "2011-2011, Javier Velilla and others" + license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" +end diff --git a/contrib/library/network/server/nino/library/request/http_request_handler.e b/contrib/library/network/server/nino/library/request/http_request_handler.e index 8c2121c9..6d25c029 100644 --- a/contrib/library/network/server/nino/library/request/http_request_handler.e +++ b/contrib/library/network/server/nino/library/request/http_request_handler.e @@ -1,118 +1,118 @@ -deferred class HTTP_REQUEST_HANDLER - -inherit - ANY - redefine - default_create - end - -feature {NONE} -- Initialization - - default_create - do - Precursor - create request_uri.make_empty - create script_name.make_empty - create query_string.make_empty - create answer - create headers.make (0) - end - -feature -- Access - - request_uri: STRING - -- requested url - - script_name: STRING - -- Script name - - query_string: STRING - -- Query string - - data: detachable STRING - -- the entire request message - - headers : HASH_TABLE [STRING, STRING] - -- Provides access to the request's HTTP headers, for example: - -- headers["Content-Type"] is "text/plain" - - answer: HTTP_RESPONSE - -- reply to this request - -feature -- Execution - - process - -- process the request and create an answer - require - valid_uri: request_uri /= Void - deferred - end - -feature -- Recycle - - reset - -- reinit the fields - do - request_uri.wipe_out - script_name.wipe_out - query_string.wipe_out - data := Void - answer.reset - end - -feature -- Element change - - set_uri (new_uri: STRING) - -- set new URI - require - valid_uri: new_uri /= Void - local - p: INTEGER - do - request_uri := new_uri - p := new_uri.index_of ('?', 1) - if p > 0 then - script_name := new_uri.substring (1, p - 1) - query_string := new_uri.substring (p + 1, new_uri.count) - else - script_name := new_uri.string - query_string := "" - end - end - - set_data (new_data: STRING) - -- set new data - do - data := new_data - end - - set_headers ( a_header : HASH_TABLE [STRING, STRING] ) - do - headers := a_header - end - -feature {NONE} -- Implementation - - real_filename (fn: STRING): STRING - -- Real filename from url-path `fn' - --| Find a better design for this piece of code - --| Eventually in a spec/$ISE_PLATFORM/ specific cluster - do - if {PLATFORM}.is_windows then - create Result.make_from_string (fn) - Result.replace_substring_all ("/", "\") - if Result[Result.count] = '\' then - Result.remove_tail (1) - end - else - Result := fn - if Result[Result.count] = '/' then - Result := Result.substring (1, Result.count - 1) - end - end - end - -note - copyright: "2011-2011, Javier Velilla and others" - license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" -end +deferred class HTTP_REQUEST_HANDLER + +inherit + ANY + redefine + default_create + end + +feature {NONE} -- Initialization + + default_create + do + Precursor + create request_uri.make_empty + create script_name.make_empty + create query_string.make_empty + create answer + create headers.make (0) + end + +feature -- Access + + request_uri: STRING + -- requested url + + script_name: STRING + -- Script name + + query_string: STRING + -- Query string + + data: detachable STRING + -- the entire request message + + headers : HASH_TABLE [STRING, STRING] + -- Provides access to the request's HTTP headers, for example: + -- headers["Content-Type"] is "text/plain" + + answer: HTTP_RESPONSE + -- reply to this request + +feature -- Execution + + process + -- process the request and create an answer + require + valid_uri: request_uri /= Void + deferred + end + +feature -- Recycle + + reset + -- reinit the fields + do + request_uri.wipe_out + script_name.wipe_out + query_string.wipe_out + data := Void + answer.reset + end + +feature -- Element change + + set_uri (new_uri: STRING) + -- set new URI + require + valid_uri: new_uri /= Void + local + p: INTEGER + do + request_uri := new_uri + p := new_uri.index_of ('?', 1) + if p > 0 then + script_name := new_uri.substring (1, p - 1) + query_string := new_uri.substring (p + 1, new_uri.count) + else + script_name := new_uri.string + query_string := "" + end + end + + set_data (new_data: STRING) + -- set new data + do + data := new_data + end + + set_headers ( a_header : HASH_TABLE [STRING, STRING] ) + do + headers := a_header + end + +feature {NONE} -- Implementation + + real_filename (fn: STRING): STRING + -- Real filename from url-path `fn' + --| Find a better design for this piece of code + --| Eventually in a spec/$ISE_PLATFORM/ specific cluster + do + if {PLATFORM}.is_windows then + create Result.make_from_string (fn) + Result.replace_substring_all ("/", "\") + if Result[Result.count] = '\' then + Result.remove_tail (1) + end + else + Result := fn + if Result[Result.count] = '/' then + Result := Result.substring (1, Result.count - 1) + end + end + end + +note + copyright: "2011-2011, Javier Velilla and others" + license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" +end diff --git a/contrib/library/network/server/nino/library/response/http_response.e b/contrib/library/network/server/nino/library/response/http_response.e index eba6ee17..5860d6ec 100644 --- a/contrib/library/network/server/nino/library/response/http_response.e +++ b/contrib/library/network/server/nino/library/response/http_response.e @@ -1,147 +1,147 @@ - -class HTTP_RESPONSE - -inherit - HTTP_CONSTANTS - redefine - default_create - end - -create - default_create - -feature -- creation - - default_create - do - Precursor - set_defaults - end - - set_defaults - -- Set default values for the reply - do - status_code := ok - create content_length_data.make_empty - reason_phrase := ok_message - content_type_data := text_html - set_reply_text (Void) - end - -feature -- Recycle - - reset - do - set_defaults - end - -feature -- response header fields - - status_code: STRING - -- status - - content_length_data : STRING - -- length - - reason_phrase: STRING - -- message, if any - - content_type_data: STRING - -- type of content in this reply (eg. text/html) - -feature -- Element change - - set_content_length (new_content_length: INTEGER) - require - positive_or_zero: new_content_length >= 0 - do - content_length_data := new_content_length.out - end - - set_status_code (new_status_code: STRING) - require - not_void: new_status_code /= Void - do - status_code := new_status_code - end - - set_reason_phrase (new_reason_phrase: STRING) - require - not_void: new_reason_phrase /= Void - do - reason_phrase := new_reason_phrase - end - - set_content_type (new_content_type: STRING) - require - not_void: new_content_type /= Void - do - content_type_data := new_content_type - end - -feature -- Access: send reply - - reply_header: STRING - -- header - do - Result := http_version_1_1.twin - Result.extend (' ') - Result.append (status_code) - Result.extend (' ') - Result.append (reason_phrase) - Result.append (crlf) - Result.append ({HTTP_SERVER_CONFIGURATION}.Server_details) - Result.append (crlf) - Result.append (Content_type + ": ") - Result.append (content_type_data) - Result.append (crlf) - Result.append (Content_length + ": ") - Result.append (content_length_data) - Result.append (crlf) - Result.append (crlf) - -- TODO: could add the size of data being sent here and - -- then keep the connection alive - end - - reply_header_continue: STRING - -- header - do - Result := http_version_1_1.twin - Result.extend (' ') - Result.append (status_code) - Result.extend (' ') - Result.append (continue_message) - Result.append (crlf) - Result.append (crlf) - -- TODO: could add the size of data being sent here and - -- then keep the connection alive - end - - reply_text: STRING - -- reply text - -feature -- Change element: send reply - - set_reply_text (new_text: detachable STRING) - -- text could be Void - do - if new_text = Void then - create reply_text.make_empty - else - reply_text := new_text - end - end - - append_reply_text (more_text: STRING) - -- add more text to the reply - require - reply_text /= Void - more_text /= Void - do - reply_text.append (more_text) - end - -note - copyright: "2011-2011, Javier Velilla and others" - license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" -end + +class HTTP_RESPONSE + +inherit + HTTP_CONSTANTS + redefine + default_create + end + +create + default_create + +feature -- creation + + default_create + do + Precursor + set_defaults + end + + set_defaults + -- Set default values for the reply + do + status_code := ok + create content_length_data.make_empty + reason_phrase := ok_message + content_type_data := text_html + set_reply_text (Void) + end + +feature -- Recycle + + reset + do + set_defaults + end + +feature -- response header fields + + status_code: STRING + -- status + + content_length_data : STRING + -- length + + reason_phrase: STRING + -- message, if any + + content_type_data: STRING + -- type of content in this reply (eg. text/html) + +feature -- Element change + + set_content_length (new_content_length: INTEGER) + require + positive_or_zero: new_content_length >= 0 + do + content_length_data := new_content_length.out + end + + set_status_code (new_status_code: STRING) + require + not_void: new_status_code /= Void + do + status_code := new_status_code + end + + set_reason_phrase (new_reason_phrase: STRING) + require + not_void: new_reason_phrase /= Void + do + reason_phrase := new_reason_phrase + end + + set_content_type (new_content_type: STRING) + require + not_void: new_content_type /= Void + do + content_type_data := new_content_type + end + +feature -- Access: send reply + + reply_header: STRING + -- header + do + Result := http_version_1_1.twin + Result.extend (' ') + Result.append (status_code) + Result.extend (' ') + Result.append (reason_phrase) + Result.append (crlf) + Result.append ({HTTP_SERVER_CONFIGURATION}.Server_details) + Result.append (crlf) + Result.append (Content_type + ": ") + Result.append (content_type_data) + Result.append (crlf) + Result.append (Content_length + ": ") + Result.append (content_length_data) + Result.append (crlf) + Result.append (crlf) + -- TODO: could add the size of data being sent here and + -- then keep the connection alive + end + + reply_header_continue: STRING + -- header + do + Result := http_version_1_1.twin + Result.extend (' ') + Result.append (status_code) + Result.extend (' ') + Result.append (continue_message) + Result.append (crlf) + Result.append (crlf) + -- TODO: could add the size of data being sent here and + -- then keep the connection alive + end + + reply_text: STRING + -- reply text + +feature -- Change element: send reply + + set_reply_text (new_text: detachable STRING) + -- text could be Void + do + if new_text = Void then + create reply_text.make_empty + else + reply_text := new_text + end + end + + append_reply_text (more_text: STRING) + -- add more text to the reply + require + reply_text /= Void + more_text /= Void + do + reply_text.append (more_text) + end + +note + copyright: "2011-2011, Javier Velilla and others" + license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" +end diff --git a/contrib/library/network/server/nino/library/shared_uri_contents_types.e b/contrib/library/network/server/nino/library/shared_uri_contents_types.e index 1909737b..787737d0 100644 --- a/contrib/library/network/server/nino/library/shared_uri_contents_types.e +++ b/contrib/library/network/server/nino/library/shared_uri_contents_types.e @@ -1,18 +1,18 @@ -note - description: "Summary description for {SHARED_URI_CONTENTS_TYPES}." - author: "" - date: "$Date$" - revision: "$Revision$" - -class - SHARED_URI_CONTENTS_TYPES -feature - - ct_table: URI_CONTENTS_TYPES - 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 +note + description: "Summary description for {SHARED_URI_CONTENTS_TYPES}." + author: "" + date: "$Date$" + revision: "$Revision$" + +class + SHARED_URI_CONTENTS_TYPES +feature + + ct_table: URI_CONTENTS_TYPES + 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 diff --git a/contrib/library/network/server/nino/library/tcp_stream_socket.e b/contrib/library/network/server/nino/library/tcp_stream_socket.e index 5c3ccf07..bf48a991 100644 --- a/contrib/library/network/server/nino/library/tcp_stream_socket.e +++ b/contrib/library/network/server/nino/library/tcp_stream_socket.e @@ -1,82 +1,82 @@ -note - description: "Summary description for {TCP_STREAM_SOCKET}." - date: "$Date$" - revision: "$Revision$" - -class - TCP_STREAM_SOCKET - -inherit - NETWORK_STREAM_SOCKET - redefine - make - end - -create - make_server_by_address_and_port, - make_server_by_port - -create {NETWORK_STREAM_SOCKET} - make_from_descriptor_and_address - -feature {NONE} -- Initialization - - make - -- Create a network stream socket. - do - Precursor - set_reuse_address - end - - make_server_by_address_and_port (an_address: INET_ADDRESS; a_port: INTEGER) - -- Create server socket on `an_address' and `a_port'. - require - valid_port: a_port >= 0 - do - make - create address.make_from_address_and_port (an_address, a_port) - bind - end - -feature -- Basic operation - - send_message (a_msg: STRING) - local - a_package : PACKET - a_data : MANAGED_POINTER - c_string : C_STRING - do - create c_string.make (a_msg) - create a_data.make_from_pointer (c_string.item, a_msg.count + 1) - create a_package.make_from_managed_pointer (a_data) - send (a_package, 1) - end - -feature -- Output - - put_readable_string_8 (s: READABLE_STRING_8) - -- Write readable string `s' to socket. - local - ext: C_STRING - do - create ext.make (s) - put_managed_pointer (ext.managed_data, 0, s.count) - end - -feature -- Status report - - try_ready_for_reading: BOOLEAN - -- Is data available for reading from the socket right now? - require - socket_exists: exists - local - retval: INTEGER - do - retval := c_select_poll_with_timeout (descriptor, True, 0) - Result := (retval > 0) - end - -note - copyright: "2011-2011, Javier Velilla and others" - license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" -end +note + description: "Summary description for {TCP_STREAM_SOCKET}." + date: "$Date$" + revision: "$Revision$" + +class + TCP_STREAM_SOCKET + +inherit + NETWORK_STREAM_SOCKET + redefine + make + end + +create + make_server_by_address_and_port, + make_server_by_port + +create {NETWORK_STREAM_SOCKET} + make_from_descriptor_and_address + +feature {NONE} -- Initialization + + make + -- Create a network stream socket. + do + Precursor + set_reuse_address + end + + make_server_by_address_and_port (an_address: INET_ADDRESS; a_port: INTEGER) + -- Create server socket on `an_address' and `a_port'. + require + valid_port: a_port >= 0 + do + make + create address.make_from_address_and_port (an_address, a_port) + bind + end + +feature -- Basic operation + + send_message (a_msg: STRING) + local + a_package : PACKET + a_data : MANAGED_POINTER + c_string : C_STRING + do + create c_string.make (a_msg) + create a_data.make_from_pointer (c_string.item, a_msg.count + 1) + create a_package.make_from_managed_pointer (a_data) + send (a_package, 1) + end + +feature -- Output + + put_readable_string_8 (s: READABLE_STRING_8) + -- Write readable string `s' to socket. + local + ext: C_STRING + do + create ext.make (s) + put_managed_pointer (ext.managed_data, 0, s.count) + end + +feature -- Status report + + try_ready_for_reading: BOOLEAN + -- Is data available for reading from the socket right now? + require + socket_exists: exists + local + retval: INTEGER + do + retval := c_select_poll_with_timeout (descriptor, True, 0) + Result := (retval > 0) + end + +note + copyright: "2011-2011, Javier Velilla and others" + license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" +end diff --git a/contrib/library/network/server/nino/library/uri_contents_types.e b/contrib/library/network/server/nino/library/uri_contents_types.e index 9c60b3dc..9cc59856 100644 --- a/contrib/library/network/server/nino/library/uri_contents_types.e +++ b/contrib/library/network/server/nino/library/uri_contents_types.e @@ -1,91 +1,91 @@ -class URI_CONTENTS_TYPES - -create - - make - - -feature - - content_types: HASH_TABLE [STRING, STRING] - - extension (uri: STRING): STRING - -- extract extendion from a URI - local - i: INTEGER - do - -- going from the end find the position of the "." - from - i := uri.count - until - i = 0 or else uri.item (i) = '.' - loop - i := i - 1 - end - Result := uri.substring (i+1, uri.count) - end - -feature {NONE} - - make - do - create content_types.make (30) - content_types.put ("text/html", "html") - content_types.put ("text/html", "htm") - content_types.put ("image/gif", "gif") - content_types.put ("image/jpeg", "jpeg") - content_types.put ("image/png", "jpg") - content_types.put ("image/png", "png") - end - - - -feature -- Access: Encoding - - urlencode (s: STRING): STRING - -- URL encode `s' - do - Result := s.string - Result.replace_substring_all ("#", "%%23") - Result.replace_substring_all (" ", "%%20") - Result.replace_substring_all ("%T", "%%09") - Result.replace_substring_all ("%N", "%%0A") - Result.replace_substring_all ("/", "%%2F") - Result.replace_substring_all ("&", "%%26") - Result.replace_substring_all ("<", "%%3C") - Result.replace_substring_all ("=", "%%3D") - Result.replace_substring_all (">", "%%3E") - Result.replace_substring_all ("%"", "%%22") - Result.replace_substring_all ("%'", "%%27") - end - - urldecode (s: STRING): STRING - -- URL decode `s' - do - Result := s.string - Result.replace_substring_all ("%%23", "#") - Result.replace_substring_all ("%%20", " ") - Result.replace_substring_all ("%%09", "%T") - Result.replace_substring_all ("%%0A", "%N") - Result.replace_substring_all ("%%2F", "/") - Result.replace_substring_all ("%%26", "&") - Result.replace_substring_all ("%%3C", "<") - Result.replace_substring_all ("%%3D", "=") - Result.replace_substring_all ("%%3E", ">") - Result.replace_substring_all ("%%22", "%"") - Result.replace_substring_all ("%%27", "%'") - end - - stripslashes (s: STRING): STRING - do - Result := s.string - Result.replace_substring_all ("\%"", "%"") - Result.replace_substring_all ("\'", "'") - Result.replace_substring_all ("\/", "/") - Result.replace_substring_all ("\\", "\") - end - -note - copyright: "2011-2011, Javier Velilla and others" - license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" -end +class URI_CONTENTS_TYPES + +create + + make + + +feature + + content_types: HASH_TABLE [STRING, STRING] + + extension (uri: STRING): STRING + -- extract extendion from a URI + local + i: INTEGER + do + -- going from the end find the position of the "." + from + i := uri.count + until + i = 0 or else uri.item (i) = '.' + loop + i := i - 1 + end + Result := uri.substring (i+1, uri.count) + end + +feature {NONE} + + make + do + create content_types.make (30) + content_types.put ("text/html", "html") + content_types.put ("text/html", "htm") + content_types.put ("image/gif", "gif") + content_types.put ("image/jpeg", "jpeg") + content_types.put ("image/png", "jpg") + content_types.put ("image/png", "png") + end + + + +feature -- Access: Encoding + + urlencode (s: STRING): STRING + -- URL encode `s' + do + Result := s.string + Result.replace_substring_all ("#", "%%23") + Result.replace_substring_all (" ", "%%20") + Result.replace_substring_all ("%T", "%%09") + Result.replace_substring_all ("%N", "%%0A") + Result.replace_substring_all ("/", "%%2F") + Result.replace_substring_all ("&", "%%26") + Result.replace_substring_all ("<", "%%3C") + Result.replace_substring_all ("=", "%%3D") + Result.replace_substring_all (">", "%%3E") + Result.replace_substring_all ("%"", "%%22") + Result.replace_substring_all ("%'", "%%27") + end + + urldecode (s: STRING): STRING + -- URL decode `s' + do + Result := s.string + Result.replace_substring_all ("%%23", "#") + Result.replace_substring_all ("%%20", " ") + Result.replace_substring_all ("%%09", "%T") + Result.replace_substring_all ("%%0A", "%N") + Result.replace_substring_all ("%%2F", "/") + Result.replace_substring_all ("%%26", "&") + Result.replace_substring_all ("%%3C", "<") + Result.replace_substring_all ("%%3D", "=") + Result.replace_substring_all ("%%3E", ">") + Result.replace_substring_all ("%%22", "%"") + Result.replace_substring_all ("%%27", "%'") + end + + stripslashes (s: STRING): STRING + do + Result := s.string + Result.replace_substring_all ("\%"", "%"") + Result.replace_substring_all ("\'", "'") + Result.replace_substring_all ("\/", "/") + Result.replace_substring_all ("\\", "\") + end + +note + copyright: "2011-2011, Javier Velilla and others" + license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" +end diff --git a/contrib/library/network/server/nino/nino-safe.ecf b/contrib/library/network/server/nino/nino-safe.ecf index b76c24df..11e63dfd 100644 --- a/contrib/library/network/server/nino/nino-safe.ecf +++ b/contrib/library/network/server/nino/nino-safe.ecf @@ -1,5 +1,5 @@ - + @@ -8,7 +8,7 @@ /CVS$ /.svn$ - diff --git a/contrib/library/network/server/nino/nino.rc b/contrib/library/network/server/nino/nino.rc deleted file mode 100644 index d3f5a12f..00000000 --- a/contrib/library/network/server/nino/nino.rc +++ /dev/null @@ -1 +0,0 @@ - diff --git a/contrib/library/network/server/nino/readme.txt b/contrib/library/network/server/nino/readme.txt index 14e7c33f..c2ea2eb5 100644 --- a/contrib/library/network/server/nino/readme.txt +++ b/contrib/library/network/server/nino/readme.txt @@ -1,38 +1,38 @@ -Eiffel Nino HTTPD -================= -Eiffel Nino is and HTTPD server. It's a work in progress, so maybe it will be refactored. -The goal of is to provide a simple web server for development (like Java, Python and Ruby provide) -The code is based on Xebra and Emu Web Server. - - -Goal -======== -HTTPD server for development, support for HTTP 1.1. - - - - -Testing -======= -To test the HTTPD server, you could run the [example https://github.com/jvelilla/EiffelWebNino/tree/master/example/SimpleWebServer] -The server work fine in Windows and Linux. - -Run the server and point your browser to one of the following URIs - -1) http://localhost:9000/post/index.html -2) http://localhost:9000/demo1/template.html -3) http://localhost:9000/demo2/demo.html -4) http://localhost:9000/example/html/index.html -5) http://localhost:9000/html/simple.html -6) http://localhost:9000/html/images.html -7) http://localhost:9000/html/images.html -8) http://localhost:9000/html5/dataset.html - -Known Issues -============ - - - - - - +Eiffel Nino HTTPD +================= +Eiffel Nino is and HTTPD server. It's a work in progress, so maybe it will be refactored. +The goal of is to provide a simple web server for development (like Java, Python and Ruby provide) +The code is based on Xebra and Emu Web Server. + + +Goal +======== +HTTPD server for development, support for HTTP 1.1. + + + + +Testing +======= +To test the HTTPD server, you could run the [example https://github.com/jvelilla/EiffelWebNino/tree/master/example/SimpleWebServer] +The server work fine in Windows and Linux. + +Run the server and point your browser to one of the following URIs + +1) http://localhost:9000/post/index.html +2) http://localhost:9000/demo1/template.html +3) http://localhost:9000/demo2/demo.html +4) http://localhost:9000/example/html/index.html +5) http://localhost:9000/html/simple.html +6) http://localhost:9000/html/images.html +7) http://localhost:9000/html/images.html +8) http://localhost:9000/html5/dataset.html + +Known Issues +============ + + + + + + diff --git a/contrib/library/text/parser/json/.gitattributes b/contrib/library/text/parser/json/.gitattributes new file mode 100644 index 00000000..98d61a9f --- /dev/null +++ b/contrib/library/text/parser/json/.gitattributes @@ -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 diff --git a/contrib/library/text/parser/json/History.txt b/contrib/library/text/parser/json/History.txt index 10ff48c6..5cf7f2ec 100644 --- a/contrib/library/text/parser/json/History.txt +++ b/contrib/library/text/parser/json/History.txt @@ -1,26 +1,26 @@ -History file for EJSON -====================== - -team: "" -date: "2011-07-06" -revision: "0.3.0" - - -+++++++++++++++++++++Important Changes since 0.2.0 version++++++++++++++++++++++++++++++++++++++++++++++ - -*Updated skip_white_spaces, now check %U and %T codes - -*Undo changes to is_a_valid_number, because it's doesn't follow the -JSON spec. Tests : fail13.json, fail29.json and fail30.json are valid -with this implementation, so we go back to the previous -implementation. - -*Added autotest test suite - -*Added getest based test program - -*Updated Eiffel configuration file, updated to the new clusters - -*Added converters and factory classes - +History file for EJSON +====================== + +team: "" +date: "2011-07-06" +revision: "0.3.0" + + ++++++++++++++++++++++Important Changes since 0.2.0 version++++++++++++++++++++++++++++++++++++++++++++++ + +*Updated skip_white_spaces, now check %U and %T codes + +*Undo changes to is_a_valid_number, because it's doesn't follow the +JSON spec. Tests : fail13.json, fail29.json and fail30.json are valid +with this implementation, so we go back to the previous +implementation. + +*Added autotest test suite + +*Added getest based test program + +*Updated Eiffel configuration file, updated to the new clusters + +*Added converters and factory classes + *Added new top level directories; library, test, build and example \ No newline at end of file diff --git a/contrib/library/text/parser/json/License.txt b/contrib/library/text/parser/json/License.txt index 24c06993..1c17b708 100644 --- a/contrib/library/text/parser/json/License.txt +++ b/contrib/library/text/parser/json/License.txt @@ -1,20 +1,20 @@ -Copyright (c) 2010 Javier Velilla and others, http://ejson.origo.ethz.ch - - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +Copyright (c) 2010 Javier Velilla and others, http://ejson.origo.ethz.ch + + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/contrib/library/text/parser/json/Readme.txt b/contrib/library/text/parser/json/Readme.txt index 1dc997a7..c0085d5d 100644 --- a/contrib/library/text/parser/json/Readme.txt +++ b/contrib/library/text/parser/json/Readme.txt @@ -1,97 +1,99 @@ -Readme file for eJSON -===================== - -team: "Javier Velilla,Jocelyn Fiat, Paul Cohen" -date: "$Date$" -revision: "$Revision$" - -1. Introduction ---------------- - -eJSON stands for Eiffel JSON library and is a small Eiffel library for dealing -with the JSON format. The objective of the library is to provide two basic -features Eiffel2JSON and JSON2Eiffel. - -2. Legal stuff --------------- - -eJSON is copyrighted by the author Javier Velilla and others. It is licensed -under the MIT License. See the file license.txt in the same directory as this -readme file. - -3. Versioning scheme --------------------- - -eJSON version numbers has the form: - - «major number».«minor number».«patch level» - -eJSON will retain the major number 0 as long as it has beta status. A change in -major number indicates that a release is not backward compatible. A change in -minor number indicates that a release is backward compatible (within that major -number) but that new useful features may have been added. A change in patch -level simply indicates that the release contains bug fixes for the previous -release. Note that as long as eJSON is in beta status (0.Y.Z) backward -compatibility is not guranteed for changes in minor numbers! - -4. Documentation ---------------- - -Currently the only documentation on eJSON is available at: - - https://github.com/eiffelhub/json/wiki/User-guide - -5. Requirements and installation --------------------------------- - -EJSON requires that you have: - -1. Gobo 3.9 installed or later -2. One of the following compiler combinations installed: - * ISE Eiffel 6.5 or later. - * gec [try to test] - * tecomp [try to test] - -eJSON probably works fine with other versions of the above compilers. -There are no known platform dependencies (Windows, Linux). - -To install eJSON simply extract the ejson-X.Y.Z.zip file to some appropriate -place on your hard disk. There are no requirements on environment variables or -registry variables. - -To verify that everything works you should compile the example programs and/or -the test program. - -6. Contents of eJSON --------------------- - -All directory names below are relative to the root directory of your ejson -installation. - -Directory Description ---------- ----------- -doc Contains the eJSON.pdf documentation file. -examples Contains the two example programs. -ejson Contains the actual eJSON library classes. -test Contains a test program for eJSON. - -7. Contacting the Team ----------------------- - -Contact the team: - - Javier Velilla «javier.hector@gmail.com» - Paul Cohen «paco@seibostudios.se» - Jocelyn Fiat «jfiat@eiffel.com» - -8. Releases ------------ - -For more information on what was changed in each release look in the file -history.txt. - -Version Date Description -------- ---- ----------- -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 +Readme file for eJSON +===================== + +team: "Javier Velilla,Jocelyn Fiat, Paul Cohen" +date: "$Date$" +revision: "$Revision$" + +1. Introduction +--------------- + +eJSON stands for Eiffel JSON library and is a small Eiffel library for dealing +with the JSON format. The objective of the library is to provide two basic +features Eiffel2JSON and JSON2Eiffel. + +2. Legal stuff +-------------- + +eJSON is copyrighted by the author Javier Velilla and others. It is licensed +under the MIT License. See the file license.txt in the same directory as this +readme file. + +3. Versioning scheme +-------------------- + +eJSON version numbers has the form: + + «major number».«minor number».«patch level» + +eJSON will retain the major number 0 as long as it has beta status. A change in +major number indicates that a release is not backward compatible. A change in +minor number indicates that a release is backward compatible (within that major +number) but that new useful features may have been added. A change in patch +level simply indicates that the release contains bug fixes for the previous +release. Note that as long as eJSON is in beta status (0.Y.Z) backward +compatibility is not guranteed for changes in minor numbers! + +4. Documentation +--------------- + +Currently the only documentation on eJSON is available at: + + https://github.com/eiffelhub/json/wiki/User-guide + +5. Requirements and installation +-------------------------------- + +EJSON requires that you have: + +1. Gobo 3.9 installed or later +2. One of the following compiler combinations installed: + * ISE Eiffel 6.5 or later. + * gec [try to test] + * tecomp [try to test] + +eJSON probably works fine with other versions of the above compilers. +There are no known platform dependencies (Windows, Linux). + +To install eJSON simply extract the ejson-X.Y.Z.zip file to some appropriate +place on your hard disk. There are no requirements on environment variables or +registry variables. + +To verify that everything works you should compile the example programs and/or +the test program. + +6. Contents of eJSON +-------------------- + +All directory names below are relative to the root directory of your ejson +installation. + +Directory Description +--------- ----------- +doc Contains the eJSON.pdf documentation file. +examples Contains the two example programs. +ejson Contains the actual eJSON library classes. +test Contains a test program for eJSON. + +7. Contacting the Team +---------------------- + +Contact the team: + + Javier Velilla «javier.hector@gmail.com» + Paul Cohen «paco@seibostudios.se» + Jocelyn Fiat «jfiat@eiffel.com» + +8. Releases +----------- + +For more information on what was changed in each release look in the file +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 \ No newline at end of file diff --git a/contrib/library/text/parser/json/library/extras/file/json_file_reader.e b/contrib/library/text/parser/json/library/extras/file/json_file_reader.e index 3629c475..a7c60481 100644 --- a/contrib/library/text/parser/json/library/extras/file/json_file_reader.e +++ b/contrib/library/text/parser/json/library/extras/file/json_file_reader.e @@ -1,38 +1,37 @@ -note - description: "Objects that ..." - author: "" - date: "$Date$" - revision: "$Revision$" - -class - JSON_FILE_READER - -feature -- Access - - read_json_from (a_path: STRING): detachable STRING - local - l_file: PLAIN_TEXT_FILE - template_content: STRING - l_last_string: detachable STRING - do - create l_file.make (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") - else - if not l_file.is_readable then - print ("error: '" + a_path + "' is not readable.%N") - else - l_file.open_read - create template_content.make_empty - l_file.read_stream (l_file.count) - l_last_string := l_file.last_string - check l_last_string /= Void end -- implied by postcondition of `l_file.read_stream' - template_content.append (l_last_string.string) - Result := template_content - l_file.close - end - end - end - -end +note + description: "Objects that ..." + date: "$Date$" + revision: "$Revision$" + +class + JSON_FILE_READER + +feature -- Access + + read_json_from (a_path: STRING): detachable STRING + local + l_file: PLAIN_TEXT_FILE + template_content: STRING + l_last_string: detachable STRING + do + 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") + else + if not l_file.is_readable then + print ("error: '" + a_path + "' is not readable.%N") + else + l_file.open_read + create template_content.make_empty + l_file.read_stream (l_file.count) + l_last_string := l_file.last_string + check l_last_string /= Void end -- implied by postcondition of `l_file.read_stream' + template_content.append (l_last_string.string) + Result := template_content + l_file.close + end + end + end + +end diff --git a/contrib/library/text/parser/json/library/extras/visitor/json_iterator.e b/contrib/library/text/parser/json/library/extras/visitor/json_iterator.e new file mode 100644 index 00000000..dae48dd1 --- /dev/null +++ b/contrib/library/text/parser/json/library/extras/visitor/json_iterator.e @@ -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 diff --git a/contrib/library/text/parser/json/library/extras/visitor/json_visitor.e b/contrib/library/text/parser/json/library/extras/visitor/json_visitor.e index bf2f583e..f032b564 100644 --- a/contrib/library/text/parser/json/library/extras/visitor/json_visitor.e +++ b/contrib/library/text/parser/json/library/extras/visitor/json_visitor.e @@ -1,59 +1,59 @@ -note - description: - - "JSON Visitor" - - pattern: "Visitor" - author: "Javier Velilla" - license:"MIT (see http://www.opensource.org/licenses/mit-license.php)" - date: "2008/08/24" - revision: "Revision 0.1" - -deferred class - JSON_VISITOR - -feature -- Visitor Pattern - - visit_json_array (a_json_array: JSON_ARRAY) - -- Visit `a_json_array'. - require - a_json_array_not_void: a_json_array /= Void - deferred - end - - visit_json_boolean (a_json_boolean: JSON_BOOLEAN) - -- Visit `a_json_boolean'. - require - a_json_boolean_not_void: a_json_boolean /= Void - deferred - end - - visit_json_null (a_json_null: JSON_NULL) - -- Visit `a_json_null'. - require - a_json_null_not_void: a_json_null /= Void - deferred - end - - visit_json_number (a_json_number: JSON_NUMBER) - -- Visit `a_json_number'. - require - a_json_number_not_void: a_json_number /= Void - deferred - end - - visit_json_object (a_json_object: JSON_OBJECT) - -- Visit `a_json_object'. - require - a_json_object_not_void: a_json_object /= Void - deferred - end - - visit_json_string (a_json_string: JSON_STRING) - -- Visit `a_json_string'. - require - a_json_string_not_void: a_json_string /= Void - deferred - end - -end +note + description: + + "JSON Visitor" + + pattern: "Visitor" + author: "Javier Velilla" + license:"MIT (see http://www.opensource.org/licenses/mit-license.php)" + date: "2008/08/24" + revision: "Revision 0.1" + +deferred class + JSON_VISITOR + +feature -- Visitor Pattern + + visit_json_array (a_json_array: JSON_ARRAY) + -- Visit `a_json_array'. + require + a_json_array_not_void: a_json_array /= Void + deferred + end + + visit_json_boolean (a_json_boolean: JSON_BOOLEAN) + -- Visit `a_json_boolean'. + require + a_json_boolean_not_void: a_json_boolean /= Void + deferred + end + + visit_json_null (a_json_null: JSON_NULL) + -- Visit `a_json_null'. + require + a_json_null_not_void: a_json_null /= Void + deferred + end + + visit_json_number (a_json_number: JSON_NUMBER) + -- Visit `a_json_number'. + require + a_json_number_not_void: a_json_number /= Void + deferred + end + + visit_json_object (a_json_object: JSON_OBJECT) + -- Visit `a_json_object'. + require + a_json_object_not_void: a_json_object /= Void + deferred + end + + visit_json_string (a_json_string: JSON_STRING) + -- Visit `a_json_string'. + require + a_json_string_not_void: a_json_string /= Void + deferred + end + +end diff --git a/contrib/library/text/parser/json/library/extras/visitor/print_json_visitor.e b/contrib/library/text/parser/json/library/extras/visitor/print_json_visitor.e index 6fa64558..6072769b 100644 --- a/contrib/library/text/parser/json/library/extras/visitor/print_json_visitor.e +++ b/contrib/library/text/parser/json/library/extras/visitor/print_json_visitor.e @@ -1,102 +1,102 @@ -note - description: "PRINT_JSON_VISITOR Generates the JSON-String for a JSON_VALUE" - author: "jvelilla" - date: "2008/08/24" - revision: "0.1" - -class - PRINT_JSON_VISITOR - -inherit - JSON_VISITOR - -create make - -feature -- Initialization - - make - -- Create a new instance - do - create to_json.make_empty - end - -feature -- Access - - to_json: STRING - -- JSON representation - -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] - do - l_json_array:=a_json_array.array_representation - to_json.append ("[") - from - l_json_array.start - until - l_json_array.off - loop - value := l_json_array.item - value.accept (Current) - l_json_array.forth - if not l_json_array.after then - to_json.append(",") - end - end - to_json.append ("]") - end - - visit_json_boolean (a_json_boolean: JSON_BOOLEAN) - -- Visit `a_json_boolean'. - do - to_json.append (a_json_boolean.item.out) - end - - visit_json_null (a_json_null: JSON_NULL) - -- Visit `a_json_null'. - do - to_json.append ("null") - end - - visit_json_number (a_json_number: JSON_NUMBER) - -- Visit `a_json_number'. - do - to_json.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] - do - l_pairs := a_json_object.map_representation - to_json.append ("{") - from - l_pairs.start - until - l_pairs.off - loop - l_pairs.key_for_iteration.accept (Current) - to_json.append (":") - l_pairs.item_for_iteration.accept (Current) - l_pairs.forth - if not l_pairs.after then - to_json.append (",") - end - end - to_json.append ("}") - end - - visit_json_string (a_json_string: JSON_STRING) - -- Visit `a_json_string'. - do - to_json.append ("%"") - to_json.append (a_json_string.item) - to_json.append ("%"") - end - -end +note + description: "PRINT_JSON_VISITOR Generates the JSON-String for a JSON_VALUE" + author: "jvelilla" + date: "2008/08/24" + revision: "0.1" + +class + PRINT_JSON_VISITOR + +inherit + JSON_VISITOR + +create make + +feature -- Initialization + + make + -- Create a new instance + do + create to_json.make_empty + end + +feature -- Access + + to_json: STRING + -- JSON representation + +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] + do + l_json_array:=a_json_array.array_representation + to_json.append ("[") + from + l_json_array.start + until + l_json_array.off + loop + value := l_json_array.item + value.accept (Current) + l_json_array.forth + if not l_json_array.after then + to_json.append(",") + end + end + to_json.append ("]") + end + + visit_json_boolean (a_json_boolean: JSON_BOOLEAN) + -- Visit `a_json_boolean'. + do + to_json.append (a_json_boolean.item.out) + end + + visit_json_null (a_json_null: JSON_NULL) + -- Visit `a_json_null'. + do + to_json.append ("null") + end + + visit_json_number (a_json_number: JSON_NUMBER) + -- Visit `a_json_number'. + do + to_json.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] + do + l_pairs := a_json_object.map_representation + to_json.append ("{") + from + l_pairs.start + until + l_pairs.off + loop + l_pairs.key_for_iteration.accept (Current) + to_json.append (":") + l_pairs.item_for_iteration.accept (Current) + l_pairs.forth + if not l_pairs.after then + to_json.append (",") + end + end + to_json.append ("}") + end + + visit_json_string (a_json_string: JSON_STRING) + -- Visit `a_json_string'. + do + to_json.append ("%"") + to_json.append (a_json_string.item) + to_json.append ("%"") + end + +end diff --git a/contrib/library/text/parser/json/library/gobo/converters/json_ds_hash_table_converter.e b/contrib/library/text/parser/json/library/gobo/converters/json_ds_hash_table_converter.e index 9f244fe0..46cf0ff9 100644 --- a/contrib/library/text/parser/json/library/gobo/converters/json_ds_hash_table_converter.e +++ b/contrib/library/text/parser/json/library/gobo/converters/json_ds_hash_table_converter.e @@ -1,85 +1,85 @@ -note - description: "A JSON converter for DS_HASH_TABLE [ANY, HASHABLE]" - author: "Paul Cohen" - date: "$Date: $" - revision: "$Revision: $" - file: "$HeadURL: $" - -class JSON_DS_HASH_TABLE_CONVERTER - -inherit - JSON_CONVERTER - -create - make - -feature {NONE} -- Initialization - - make - do - create object.make (0) - end - -feature -- Access - - value: JSON_OBJECT - - object: DS_HASH_TABLE [ANY, HASHABLE] - -feature -- Conversion - - from_json (j: like value): detachable like object - local - keys: ARRAY [JSON_STRING] - i: INTEGER - h: HASHABLE - a: ANY - do - keys := j.current_keys - create Result.make (keys.count) - from - i := 1 - until - i > keys.count - loop - h ?= json.object (keys [i], void) - check h /= Void end - a := json.object (j.item (keys [i]), Void) - Result.put (a, h) - i := i + 1 - end - end - - to_json (o: like object): like value - local - c: DS_HASH_TABLE_CURSOR [ANY, HASHABLE] - js: JSON_STRING - jv: JSON_VALUE - failed: BOOLEAN - do - create Result.make - from - c := o.new_cursor - c.start - until - c.after - loop - if attached {JSON_STRING} json.value (c.key) as l_key then - js := l_key - else - create js.make_json (c.key.out) - end - jv := json.value (c.item) - if jv /= Void then - Result.put (jv, js) - else - failed := True - end - c.forth - end - if failed then - Result := Void - end - end - -end -- class JSON_DS_HASH_TABLE_CONVERTER +note + description: "A JSON converter for DS_HASH_TABLE [ANY, HASHABLE]" + author: "Paul Cohen" + date: "$Date: $" + revision: "$Revision: $" + file: "$HeadURL: $" + +class JSON_DS_HASH_TABLE_CONVERTER + +inherit + JSON_CONVERTER + +create + make + +feature {NONE} -- Initialization + + make + do + create object.make (0) + end + +feature -- Access + + value: JSON_OBJECT + + object: DS_HASH_TABLE [ANY, HASHABLE] + +feature -- Conversion + + from_json (j: like value): detachable like object + local + keys: ARRAY [JSON_STRING] + i: INTEGER + h: HASHABLE + a: ANY + do + keys := j.current_keys + create Result.make (keys.count) + from + i := 1 + until + i > keys.count + loop + h ?= json.object (keys [i], void) + check h /= Void end + a := json.object (j.item (keys [i]), Void) + Result.put (a, h) + i := i + 1 + end + end + + to_json (o: like object): like value + local + c: DS_HASH_TABLE_CURSOR [ANY, HASHABLE] + js: JSON_STRING + jv: JSON_VALUE + failed: BOOLEAN + do + create Result.make + from + c := o.new_cursor + c.start + until + c.after + loop + if attached {JSON_STRING} json.value (c.key) as l_key then + js := l_key + else + create js.make_json (c.key.out) + end + jv := json.value (c.item) + if jv /= Void then + Result.put (jv, js) + else + failed := True + end + c.forth + end + if failed then + Result := Void + end + end + +end -- class JSON_DS_HASH_TABLE_CONVERTER diff --git a/contrib/library/text/parser/json/library/gobo/converters/json_ds_linked_list_converter.e b/contrib/library/text/parser/json/library/gobo/converters/json_ds_linked_list_converter.e index bf1aa516..4696051e 100644 --- a/contrib/library/text/parser/json/library/gobo/converters/json_ds_linked_list_converter.e +++ b/contrib/library/text/parser/json/library/gobo/converters/json_ds_linked_list_converter.e @@ -1,62 +1,62 @@ -note - description: "A JSON converter for DS_LINKED_LIST [ANY]" - author: "Paul Cohen" - date: "$Date: $" - revision: "$Revision: $" - file: "$HeadURL: $" - -class JSON_DS_LINKED_LIST_CONVERTER - -inherit - JSON_CONVERTER - -create - make - -feature {NONE} -- Initialization - - make - do - create object.make - end - -feature -- Access - - value: JSON_ARRAY - - object: DS_LINKED_LIST [ANY] - -feature -- Conversion - - from_json (j: like value): detachable like object - local - i: INTEGER - do - create Result.make - from - i := 1 - until - i > j.count - loop - Result.put_last (json.object (j [i], Void)) - i := i + 1 - end - end - - to_json (o: like object): like value - local - c: DS_LIST_CURSOR [ANY] - do - create Result.make_array - from - c := o.new_cursor - c.start - until - c.after - loop - Result.add (json.value (c.item)) - c.forth - end - end - -end -- class JSON_DS_LINKED_LIST_CONVERTER +note + description: "A JSON converter for DS_LINKED_LIST [ANY]" + author: "Paul Cohen" + date: "$Date: $" + revision: "$Revision: $" + file: "$HeadURL: $" + +class JSON_DS_LINKED_LIST_CONVERTER + +inherit + JSON_CONVERTER + +create + make + +feature {NONE} -- Initialization + + make + do + create object.make + end + +feature -- Access + + value: JSON_ARRAY + + object: DS_LINKED_LIST [ANY] + +feature -- Conversion + + from_json (j: like value): detachable like object + local + i: INTEGER + do + create Result.make + from + i := 1 + until + i > j.count + loop + Result.put_last (json.object (j [i], Void)) + i := i + 1 + end + end + + to_json (o: like object): like value + local + c: DS_LIST_CURSOR [ANY] + do + create Result.make_array + from + c := o.new_cursor + c.start + until + c.after + loop + Result.add (json.value (c.item)) + c.forth + end + end + +end -- class JSON_DS_LINKED_LIST_CONVERTER diff --git a/contrib/library/text/parser/json/library/json-safe.ecf b/contrib/library/text/parser/json/library/json-safe.ecf index b957513f..d1936431 100644 --- a/contrib/library/text/parser/json/library/json-safe.ecf +++ b/contrib/library/text/parser/json/library/json-safe.ecf @@ -1,5 +1,5 @@ - + diff --git a/contrib/library/text/parser/json/library/json.ecf b/contrib/library/text/parser/json/library/json.ecf index ee954e09..8d6dc612 100644 --- a/contrib/library/text/parser/json/library/json.ecf +++ b/contrib/library/text/parser/json/library/json.ecf @@ -1,28 +1,28 @@ - - - - - - /EIFGENs$ - /CVS$ - /.svn$ - - - - - - ^/gobo$ - ^/kernel$ - ^/extras$ - - - - - - + + + + + + /EIFGENs$ + /CVS$ + /.svn$ + + + + + + ^/gobo$ + ^/kernel$ + ^/extras$ + + + + + + diff --git a/contrib/library/text/parser/json/library/json.rc b/contrib/library/text/parser/json/library/json.rc index d3f5a12f..8b137891 100644 --- a/contrib/library/text/parser/json/library/json.rc +++ b/contrib/library/text/parser/json/library/json.rc @@ -1 +1 @@ - + diff --git a/contrib/library/text/parser/json/library/kernel/converters/json_hash_table_converter.e b/contrib/library/text/parser/json/library/kernel/converters/json_hash_table_converter.e index cf3f9cfc..e1264de7 100644 --- a/contrib/library/text/parser/json/library/kernel/converters/json_hash_table_converter.e +++ b/contrib/library/text/parser/json/library/kernel/converters/json_hash_table_converter.e @@ -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) - else - check a_attached: a /= Void end - end - else - check j_has_item: False end + 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 key_is_hashable: False end + end + else + 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 diff --git a/contrib/library/text/parser/json/library/kernel/ejson.e b/contrib/library/text/parser/json/library/kernel/ejson.e index 802d785b..cb5ed967 100644 --- a/contrib/library/text/parser/json/library/kernel/ejson.e +++ b/contrib/library/text/parser/json/library/kernel/ejson.e @@ -1,268 +1,268 @@ -note - description: "Core factory class for creating JSON objects and corresponding Eiffel objects." - author: "Paul Cohen" - date: "$Date$" - revision: "$Revision$" - file: "$HeadURL: $" - -class EJSON - -inherit - EXCEPTIONS - -feature -- Access - - value (an_object: detachable ANY): detachable JSON_VALUE - -- JSON value from Eiffel object. Raises an "eJSON exception" if - -- unable to convert value. - local - i: INTEGER - ja: JSON_ARRAY - do - -- Try to convert from basic Eiffel types. Note that we check with - -- `conforms_to' since the client may have subclassed the base class - -- that these basic types are derived from. - if an_object = Void then - create {JSON_NULL} Result - elseif attached {BOOLEAN} an_object as b then - create {JSON_BOOLEAN} Result.make_boolean (b) - elseif attached {INTEGER_8} an_object as i8 then - create {JSON_NUMBER} Result.make_integer (i8) - elseif attached {INTEGER_16} an_object as i16 then - create {JSON_NUMBER} Result.make_integer (i16) - elseif attached {INTEGER_32} an_object as i32 then - create {JSON_NUMBER} Result.make_integer (i32) - elseif attached {INTEGER_64} an_object as i64 then - create {JSON_NUMBER} Result.make_integer (i64) - elseif attached {NATURAL_8} an_object as n8 then - create {JSON_NUMBER} Result.make_natural (n8) - elseif attached {NATURAL_16} an_object as n16 then - create {JSON_NUMBER} Result.make_natural (n16) - elseif attached {NATURAL_32} an_object as n32 then - create {JSON_NUMBER} Result.make_natural (n32) - elseif attached {NATURAL_64} an_object as n64 then - create {JSON_NUMBER} Result.make_natural (n64) - elseif attached {REAL_32} an_object as r32 then - create {JSON_NUMBER} Result.make_real (r32) - elseif attached {REAL_64} an_object as r64 then - create {JSON_NUMBER} Result.make_real (r64) - elseif attached {ARRAY [detachable ANY]} an_object as a then - create ja.make_array - from - i := a.lower - until - i > a.upper - loop - if attached value (a @ i) as v then - ja.add (v) - else - check value_attached: False end - end - i := i + 1 - end - Result := ja - elseif attached {CHARACTER_8} an_object as c8 then - create {JSON_STRING} Result.make_json (c8.out) - elseif attached {CHARACTER_32} an_object as c32 then - create {JSON_STRING} Result.make_json (c32.out) - - 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_from_string_32 (s32) - end - - if Result = Void then - -- Now check the converters - if an_object /= Void and then attached converter_for (an_object) as jc then - Result := jc.to_json (an_object) - else - raise (exception_failed_to_convert_to_json (an_object)) - end - end - end - - object (a_value: detachable JSON_VALUE; base_class: detachable STRING): detachable ANY - -- Eiffel object from JSON value. If `base_class' /= Void an eiffel - -- object based on `base_class' will be returned. Raises an "eJSON - -- exception" if unable to convert value. - local - i: INTEGER - ll: LINKED_LIST [detachable ANY] - t: HASH_TABLE [detachable ANY, STRING_GENERAL] - keys: ARRAY [JSON_STRING] - do - if a_value = Void then - Result := Void - else - if base_class = Void then - if a_value = Void then - Result := Void - elseif attached {JSON_NULL} a_value then - Result := Void - elseif attached {JSON_BOOLEAN} a_value as jb then - Result := jb.item - elseif attached {JSON_NUMBER} a_value as jn then - if jn.item.is_integer_8 then - Result := jn.item.to_integer_8 - elseif jn.item.is_integer_16 then - Result := jn.item.to_integer_16 - elseif jn.item.is_integer_32 then - Result := jn.item.to_integer_32 - elseif jn.item.is_integer_64 then - Result := jn.item.to_integer_64 - elseif jn.item.is_natural_64 then - Result := jn.item.to_natural_64 - elseif jn.item.is_double then - Result := jn.item.to_double - end - elseif attached {JSON_STRING} a_value as js then - create {STRING_32} Result.make_from_string (js.unescaped_string_32) - elseif attached {JSON_ARRAY} a_value as ja then - from - create ll.make - i := 1 - until - i > ja.count - loop - ll.extend (object (ja [i], Void)) - i := i + 1 - end - Result := ll - elseif attached {JSON_OBJECT} a_value as jo then - keys := jo.current_keys - create t.make (keys.count) - from - i := keys.lower - until - i > keys.upper - loop - if attached {STRING_GENERAL} object (keys [i], Void) as s then - t.put (object (jo.item (keys [i]), Void), s) - end - i := i + 1 - end - Result := t - end - else - if converters.has_key (base_class) and then attached converters.found_item as jc then - Result := jc.from_json (a_value) - else - raise (exception_failed_to_convert_to_eiffel (a_value, base_class)) - end - end - end - end - - object_from_json (json: STRING; base_class: detachable STRING): detachable ANY - -- Eiffel object from JSON representation. If `base_class' /= Void an - -- Eiffel object based on `base_class' will be returned. Raises an - -- "eJSON exception" if unable to convert value. - require - json_not_void: json /= Void - local - jv: detachable JSON_VALUE - do - json_parser.set_representation (json) - jv := json_parser.parse - if jv /= Void then - Result := object (jv, base_class) - end - end - - converter_for (an_object: ANY): detachable JSON_CONVERTER - -- Converter for objects. Returns Void if none found. - require - an_object_not_void: an_object /= Void - do - if converters.has_key (an_object.generator) then - Result := converters.found_item - end - end - - json_reference (s: STRING): JSON_OBJECT - -- A JSON (Dojo style) reference object using `s' as the - -- reference value. The caller is responsable for ensuring - -- the validity of `s' as a json reference. - require - s_not_void: s /= Void - local - js_key, js_value: JSON_STRING - do - create Result.make - create js_key.make_json ("$ref") - create js_value.make_json (s) - Result.put (js_value, js_key) - end - - json_references (l: LIST [STRING]): JSON_ARRAY - -- A JSON array of JSON (Dojo style) reference objects using the - -- strings in `l' as reference values. The caller is responsable - -- for ensuring the validity of all strings in `l' as json - -- references. - require - l_not_void: l /= Void - local - c: ITERATION_CURSOR [STRING] - do - create Result.make_array - from - c := l.new_cursor - until - c.after - loop - Result.add (json_reference (c.item)) - c.forth - end - end - -feature -- Change - - add_converter (jc: JSON_CONVERTER) - -- Add the converter `jc'. - require - jc_not_void: jc /= Void - do - converters.force (jc, jc.object.generator) - ensure - has_converter: converter_for (jc.object) /= Void - end - -feature {NONE} -- Implementation - - converters: HASH_TABLE [JSON_CONVERTER, STRING] - -- Converters hashed by generator (base class) - once - create Result.make (10) - end - -feature {NONE} -- Implementation (Exceptions) - - exception_prefix: STRING = "eJSON exception: " - - exception_failed_to_convert_to_eiffel (a_value: JSON_VALUE; base_class: detachable STRING): STRING - -- Exception message for failing to convert a JSON_VALUE to an instance of `a'. - do - Result := exception_prefix + "Failed to convert JSON_VALUE to an Eiffel object: " + a_value.generator - if base_class /= Void then - Result.append (" -> {" + base_class + "}") - end - end - - exception_failed_to_convert_to_json (an_object: detachable ANY): STRING - -- 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.append (" : {" + an_object.generator + "}") - end - end - -feature {NONE} -- Implementation (JSON parser) - - json_parser: JSON_PARSER - once - create Result.make_parser ("") - end - -end -- class EJSON +note + description: "Core factory class for creating JSON objects and corresponding Eiffel objects." + author: "Paul Cohen" + date: "$Date$" + revision: "$Revision$" + file: "$HeadURL: $" + +class EJSON + +inherit + EXCEPTIONS + +feature -- Access + + value (an_object: detachable ANY): detachable JSON_VALUE + -- JSON value from Eiffel object. Raises an "eJSON exception" if + -- unable to convert value. + local + i: INTEGER + ja: JSON_ARRAY + do + -- Try to convert from basic Eiffel types. Note that we check with + -- `conforms_to' since the client may have subclassed the base class + -- that these basic types are derived from. + if an_object = Void then + create {JSON_NULL} Result + elseif attached {BOOLEAN} an_object as b then + create {JSON_BOOLEAN} Result.make_boolean (b) + elseif attached {INTEGER_8} an_object as i8 then + create {JSON_NUMBER} Result.make_integer (i8) + elseif attached {INTEGER_16} an_object as i16 then + create {JSON_NUMBER} Result.make_integer (i16) + elseif attached {INTEGER_32} an_object as i32 then + create {JSON_NUMBER} Result.make_integer (i32) + elseif attached {INTEGER_64} an_object as i64 then + create {JSON_NUMBER} Result.make_integer (i64) + elseif attached {NATURAL_8} an_object as n8 then + create {JSON_NUMBER} Result.make_natural (n8) + elseif attached {NATURAL_16} an_object as n16 then + create {JSON_NUMBER} Result.make_natural (n16) + elseif attached {NATURAL_32} an_object as n32 then + create {JSON_NUMBER} Result.make_natural (n32) + elseif attached {NATURAL_64} an_object as n64 then + create {JSON_NUMBER} Result.make_natural (n64) + elseif attached {REAL_32} an_object as r32 then + create {JSON_NUMBER} Result.make_real (r32) + elseif attached {REAL_64} an_object as r64 then + create {JSON_NUMBER} Result.make_real (r64) + elseif attached {ARRAY [detachable ANY]} an_object as a then + create ja.make_array + from + i := a.lower + until + i > a.upper + loop + if attached value (a @ i) as v then + ja.add (v) + else + check value_attached: False end + end + i := i + 1 + end + Result := ja + elseif attached {CHARACTER_8} an_object as c8 then + create {JSON_STRING} Result.make_json (c8.out) + elseif attached {CHARACTER_32} an_object as c32 then + create {JSON_STRING} Result.make_json (c32.out) + + 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_from_string_32 (s32) + end + + if Result = Void then + -- Now check the converters + if an_object /= Void and then attached converter_for (an_object) as jc then + Result := jc.to_json (an_object) + else + raise (exception_failed_to_convert_to_json (an_object)) + end + end + end + + object (a_value: detachable JSON_VALUE; base_class: detachable STRING): detachable ANY + -- Eiffel object from JSON value. If `base_class' /= Void an eiffel + -- object based on `base_class' will be returned. Raises an "eJSON + -- exception" if unable to convert value. + local + i: INTEGER + ll: LINKED_LIST [detachable ANY] + t: HASH_TABLE [detachable ANY, STRING_GENERAL] + keys: ARRAY [JSON_STRING] + do + if a_value = Void then + Result := Void + else + if base_class = Void then + if a_value = Void then + Result := Void + elseif attached {JSON_NULL} a_value then + Result := Void + elseif attached {JSON_BOOLEAN} a_value as jb then + Result := jb.item + elseif attached {JSON_NUMBER} a_value as jn then + if jn.item.is_integer_8 then + Result := jn.item.to_integer_8 + elseif jn.item.is_integer_16 then + Result := jn.item.to_integer_16 + elseif jn.item.is_integer_32 then + Result := jn.item.to_integer_32 + elseif jn.item.is_integer_64 then + Result := jn.item.to_integer_64 + elseif jn.item.is_natural_64 then + Result := jn.item.to_natural_64 + elseif jn.item.is_double then + Result := jn.item.to_double + end + elseif attached {JSON_STRING} a_value as js then + create {STRING_32} Result.make_from_string (js.unescaped_string_32) + elseif attached {JSON_ARRAY} a_value as ja then + from + create ll.make + i := 1 + until + i > ja.count + loop + ll.extend (object (ja [i], Void)) + i := i + 1 + end + Result := ll + elseif attached {JSON_OBJECT} a_value as jo then + keys := jo.current_keys + create t.make (keys.count) + from + i := keys.lower + until + i > keys.upper + loop + if attached {STRING_GENERAL} object (keys [i], Void) as s then + t.put (object (jo.item (keys [i]), Void), s) + end + i := i + 1 + end + Result := t + end + else + if converters.has_key (base_class) and then attached converters.found_item as jc then + Result := jc.from_json (a_value) + else + raise (exception_failed_to_convert_to_eiffel (a_value, base_class)) + end + end + end + end + + object_from_json (json: STRING; base_class: detachable STRING): detachable ANY + -- Eiffel object from JSON representation. If `base_class' /= Void an + -- Eiffel object based on `base_class' will be returned. Raises an + -- "eJSON exception" if unable to convert value. + require + json_not_void: json /= Void + local + jv: detachable JSON_VALUE + do + json_parser.set_representation (json) + jv := json_parser.parse + if jv /= Void then + Result := object (jv, base_class) + end + end + + converter_for (an_object: ANY): detachable JSON_CONVERTER + -- Converter for objects. Returns Void if none found. + require + an_object_not_void: an_object /= Void + do + if converters.has_key (an_object.generator) then + Result := converters.found_item + end + end + + json_reference (s: STRING): JSON_OBJECT + -- A JSON (Dojo style) reference object using `s' as the + -- reference value. The caller is responsable for ensuring + -- the validity of `s' as a json reference. + require + s_not_void: s /= Void + local + js_key, js_value: JSON_STRING + do + create Result.make + create js_key.make_json ("$ref") + create js_value.make_json (s) + Result.put (js_value, js_key) + end + + json_references (l: LIST [STRING]): JSON_ARRAY + -- A JSON array of JSON (Dojo style) reference objects using the + -- strings in `l' as reference values. The caller is responsable + -- for ensuring the validity of all strings in `l' as json + -- references. + require + l_not_void: l /= Void + local + c: ITERATION_CURSOR [STRING] + do + create Result.make_array + from + c := l.new_cursor + until + c.after + loop + Result.add (json_reference (c.item)) + c.forth + end + end + +feature -- Change + + add_converter (jc: JSON_CONVERTER) + -- Add the converter `jc'. + require + jc_not_void: jc /= Void + do + converters.force (jc, jc.object.generator) + ensure + has_converter: converter_for (jc.object) /= Void + end + +feature {NONE} -- Implementation + + converters: HASH_TABLE [JSON_CONVERTER, STRING] + -- Converters hashed by generator (base class) + once + create Result.make (10) + end + +feature {NONE} -- Implementation (Exceptions) + + exception_prefix: STRING = "eJSON exception: " + + exception_failed_to_convert_to_eiffel (a_value: JSON_VALUE; base_class: detachable STRING): STRING + -- Exception message for failing to convert a JSON_VALUE to an instance of `a'. + do + Result := exception_prefix + "Failed to convert JSON_VALUE to an Eiffel object: " + a_value.generator + if base_class /= Void then + Result.append (" -> {" + base_class + "}") + end + end + + exception_failed_to_convert_to_json (an_object: detachable ANY): STRING + -- 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.append (" : {" + an_object.generator + "}") + end + end + +feature {NONE} -- Implementation (JSON parser) + + json_parser: JSON_PARSER + once + create Result.make_parser ("") + end + +end -- class EJSON diff --git a/contrib/library/text/parser/json/library/kernel/json_array.e b/contrib/library/text/parser/json/library/kernel/json_array.e index dc51e1cb..cd693f5c 100644 --- a/contrib/library/text/parser/json/library/kernel/json_array.e +++ b/contrib/library/text/parser/json/library/kernel/json_array.e @@ -1,141 +1,178 @@ -note - description: "[ - JSON_ARRAY represent an array in JSON. - An array in JSON is an ordered set of names. - Examples - array - [] - [elements] - ]" - - author: "Javier Velilla" - date: "2008/08/24" - revision: "Revision 0.1" - -class - JSON_ARRAY - -inherit - JSON_VALUE - - DEBUG_OUTPUT - -create - make_array - -feature {NONE} -- Initialization - - make_array - -- Initialize JSON Array - do - create values.make (10) - end - -feature -- Access - - i_th alias "[]" (i: INTEGER): JSON_VALUE - -- Item at `i'-th position - require - is_valid_index: valid_index (i) - do - Result := values.i_th (i) - end - - representation: STRING - local - i: INTEGER - do - Result := "[" - from - i := 1 - until - i > count - loop - Result.append (i_th (i).representation) - i := i + 1 - if i <= count then - Result.append_character (',') - end - end - Result.append_character (']') - end - -feature -- Visitor pattern - - accept (a_visitor: JSON_VISITOR) - -- Accept `a_visitor'. - -- (Call `visit_json_array' procedure on `a_visitor'.) - do - a_visitor.visit_json_array (Current) - end - -feature -- Mesurement - - count: INTEGER - -- Number of items. - do - Result := values.count - end - -feature -- Status report - - valid_index (i: INTEGER): BOOLEAN - -- Is `i' a valid index? - do - Result := (1 <= i) and (i <= count) - end - -feature -- Change Element - - add (value: JSON_VALUE) - require - value_not_null: value /= void - do - values.extend (value) - ensure - has_new_value: old values.count + 1 = values.count and - values.has (value) - end - -feature -- Report - - hash_code: INTEGER - -- Hash code value - do - from - values.start - Result := values.item.hash_code - until - values.off - loop - Result:= ((Result \\ 8388593) |<< 8) + values.item.hash_code - values.forth - end - Result := Result \\ values.count - end - -feature -- Conversion - - array_representation: ARRAYED_LIST [JSON_VALUE] - -- Representation as a sequences of values - do - Result := values - end - -feature -- Status report - - debug_output: STRING - -- String that should be displayed in debugger to represent `Current'. - do - Result := count.out - end - -feature {NONE} -- Implementation - - values: ARRAYED_LIST [JSON_VALUE] - -- Value container - -invariant - value_not_void: values /= Void - -end +note + description: "[ + JSON_ARRAY represent an array in JSON. + An array in JSON is an ordered set of names. + Examples + array + [] + [elements] + ]" + + author: "Javier Velilla" + date: "2008/08/24" + revision: "Revision 0.1" + +class + JSON_ARRAY + +inherit + JSON_VALUE + + ITERABLE [JSON_VALUE] + + DEBUG_OUTPUT + +create + make_array + +feature {NONE} -- Initialization + + make_array + -- Initialize JSON Array + do + create values.make (10) + end + +feature -- Access + + i_th alias "[]" (i: INTEGER): JSON_VALUE + -- Item at `i'-th position + require + is_valid_index: valid_index (i) + do + Result := values.i_th (i) + end + + representation: STRING + local + i: INTEGER + do + Result := "[" + from + i := 1 + until + i > count + loop + Result.append (i_th (i).representation) + i := i + 1 + if i <= count then + Result.append_character (',') + end + end + Result.append_character (']') + end + +feature -- Visitor pattern + + accept (a_visitor: JSON_VISITOR) + -- Accept `a_visitor'. + -- (Call `visit_json_array' procedure on `a_visitor'.) + do + 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 + -- Number of items. + do + Result := values.count + end + +feature -- Status report + + valid_index (i: INTEGER): BOOLEAN + -- Is `i' a valid index? + do + Result := (1 <= i) and (i <= count) + end + +feature -- Change Element + + put_front (v: JSON_VALUE) + require + v_not_void: v /= Void + do + values.put_front (v) + ensure + has_new_value: old values.count + 1 = values.count and + 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 + + hash_code: INTEGER + -- Hash code value + do + from + values.start + Result := values.item.hash_code + until + values.off + loop + Result:= ((Result \\ 8388593) |<< 8) + values.item.hash_code + values.forth + end + Result := Result \\ values.count + end + +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 + +feature -- Status report + + debug_output: STRING + -- String that should be displayed in debugger to represent `Current'. + do + Result := count.out + " item(s)" + end + +feature {NONE} -- Implementation + + values: ARRAYED_LIST [JSON_VALUE] + -- Value container + +invariant + value_not_void: values /= Void + +end diff --git a/contrib/library/text/parser/json/library/kernel/json_boolean.e b/contrib/library/text/parser/json/library/kernel/json_boolean.e index 32e7634a..cc17681c 100644 --- a/contrib/library/text/parser/json/library/kernel/json_boolean.e +++ b/contrib/library/text/parser/json/library/kernel/json_boolean.e @@ -1,61 +1,61 @@ -note - description: "JSON Truth values" - author: "Javier Velilla" - date: "2008/08/24" - revision: "Revision 0.1" - -class - JSON_BOOLEAN - -inherit - JSON_VALUE - -create - make_boolean - -feature {NONE} -- Initialization - - make_boolean (an_item: BOOLEAN) - --Initialize. - do - item := an_item - end - -feature -- Access - - item: BOOLEAN - -- Content - - hash_code: INTEGER - -- Hash code value - do - Result := item.hash_code - end - - representation: STRING - do - if item then - Result := "true" - else - Result := "false" - end - end - -feature -- Visitor pattern - - accept (a_visitor: JSON_VISITOR) - -- Accept `a_visitor'. - -- (Call `visit_json_boolean' procedure on `a_visitor'.) - do - a_visitor.visit_json_boolean (Current) - end - -feature -- Status report - - debug_output: STRING - -- String that should be displayed in debugger to represent `Current'. - do - Result := item.out - end - -end +note + description: "JSON Truth values" + author: "Javier Velilla" + date: "2008/08/24" + revision: "Revision 0.1" + +class + JSON_BOOLEAN + +inherit + JSON_VALUE + +create + make_boolean + +feature {NONE} -- Initialization + + make_boolean (an_item: BOOLEAN) + --Initialize. + do + item := an_item + end + +feature -- Access + + item: BOOLEAN + -- Content + + hash_code: INTEGER + -- Hash code value + do + Result := item.hash_code + end + + representation: STRING + do + if item then + Result := "true" + else + Result := "false" + end + end + +feature -- Visitor pattern + + accept (a_visitor: JSON_VISITOR) + -- Accept `a_visitor'. + -- (Call `visit_json_boolean' procedure on `a_visitor'.) + do + a_visitor.visit_json_boolean (Current) + end + +feature -- Status report + + debug_output: STRING + -- String that should be displayed in debugger to represent `Current'. + do + Result := item.out + end + +end diff --git a/contrib/library/text/parser/json/library/kernel/json_null.e b/contrib/library/text/parser/json/library/kernel/json_null.e index 176b7d38..9c6f4ed7 100644 --- a/contrib/library/text/parser/json/library/kernel/json_null.e +++ b/contrib/library/text/parser/json/library/kernel/json_null.e @@ -1,47 +1,47 @@ -note - description: "JSON Null Values" - author: "Javier Velilla" - date: "2008/08/24" - revision: "Revision 0.1" - -class - JSON_NULL - -inherit - JSON_VALUE - -feature --Access - - hash_code: INTEGER - -- Hash code value - do - Result := null_value.hash_code - end - - representation: STRING - do - Result := "null" - end - -feature -- Visitor pattern - - accept (a_visitor: JSON_VISITOR) - -- Accept `a_visitor'. - -- (Call `visit_element_a' procedure on `a_visitor'.) - do - a_visitor.visit_json_null (Current) - end - -feature -- Status report - - debug_output: STRING - -- String that should be displayed in debugger to represent `Current'. - do - Result := null_value - end - -feature {NONE}-- Implementation - - null_value: STRING = "null" - -end +note + description: "JSON Null Values" + author: "Javier Velilla" + date: "2008/08/24" + revision: "Revision 0.1" + +class + JSON_NULL + +inherit + JSON_VALUE + +feature --Access + + hash_code: INTEGER + -- Hash code value + do + Result := null_value.hash_code + end + + representation: STRING + do + Result := "null" + end + +feature -- Visitor pattern + + accept (a_visitor: JSON_VISITOR) + -- Accept `a_visitor'. + -- (Call `visit_element_a' procedure on `a_visitor'.) + do + a_visitor.visit_json_null (Current) + end + +feature -- Status report + + debug_output: STRING + -- String that should be displayed in debugger to represent `Current'. + do + Result := null_value + end + +feature {NONE}-- Implementation + + null_value: STRING = "null" + +end diff --git a/contrib/library/text/parser/json/library/kernel/json_number.e b/contrib/library/text/parser/json/library/kernel/json_number.e index 69b5011e..5f8ea60f 100644 --- a/contrib/library/text/parser/json/library/kernel/json_number.e +++ b/contrib/library/text/parser/json/library/kernel/json_number.e @@ -1,99 +1,99 @@ -note - - description: "JSON Numbers, octal and hexadecimal formats are not used." - author: "Javier Velilla" - date: "2008/08/24" - revision: "Revision 0.1" - license:"MIT (see http://www.opensource.org/licenses/mit-license.php)" - -class - JSON_NUMBER - -inherit - JSON_VALUE - redefine - is_equal - end - -create - make_integer, - make_natural, - make_real - -feature {NONE} -- initialization - - make_integer (an_argument: INTEGER_64) - -- Initialize an instance of JSON_NUMBER from the integer value of `an_argument'. - do - item := an_argument.out - numeric_type := INTEGER_TYPE - end - - make_natural (an_argument: NATURAL_64) - -- Initialize an instance of JSON_NUMBER from the unsigned integer value of `an_argument'. - do - item := an_argument.out - numeric_type := NATURAL_TYPE - end - - make_real (an_argument: DOUBLE) - -- Initialize an instance of JSON_NUMBER from the floating point value of `an_argument'. - do - item := an_argument.out - numeric_type := DOUBLE_TYPE - end - -feature -- Access - - item: STRING - -- Content - - hash_code: INTEGER - --Hash code value - do - Result := item.hash_code - end - - representation: STRING - do - Result := item - end - -feature -- Visitor pattern - - accept (a_visitor: JSON_VISITOR) - -- Accept `a_visitor'. - -- (Call `visit_json_number' procedure on `a_visitor'.) - do - a_visitor.visit_json_number (Current) - end - -feature -- Status - - is_equal (other: like Current): BOOLEAN - -- Is `other' attached to an object of the same type - -- as current object and identical to it? - do - Result := item.is_equal (other.item) - end - -feature -- Status report - - debug_output: STRING - -- String that should be displayed in debugger to represent `Current'. - do - Result := item - end - -feature -- Implementation - - INTEGER_TYPE: INTEGER = 1 - DOUBLE_TYPE: INTEGER = 2 - NATURAL_TYPE: INTEGER = 3 - - numeric_type: INTEGER - -invariant - item_not_void: item /= Void - -end +note + + description: "JSON Numbers, octal and hexadecimal formats are not used." + author: "Javier Velilla" + date: "2008/08/24" + revision: "Revision 0.1" + license:"MIT (see http://www.opensource.org/licenses/mit-license.php)" + +class + JSON_NUMBER + +inherit + JSON_VALUE + redefine + is_equal + end + +create + make_integer, + make_natural, + make_real + +feature {NONE} -- initialization + + make_integer (an_argument: INTEGER_64) + -- Initialize an instance of JSON_NUMBER from the integer value of `an_argument'. + do + item := an_argument.out + numeric_type := INTEGER_TYPE + end + + make_natural (an_argument: NATURAL_64) + -- Initialize an instance of JSON_NUMBER from the unsigned integer value of `an_argument'. + do + item := an_argument.out + numeric_type := NATURAL_TYPE + end + + make_real (an_argument: DOUBLE) + -- Initialize an instance of JSON_NUMBER from the floating point value of `an_argument'. + do + item := an_argument.out + numeric_type := DOUBLE_TYPE + end + +feature -- Access + + item: STRING + -- Content + + hash_code: INTEGER + --Hash code value + do + Result := item.hash_code + end + + representation: STRING + do + Result := item + end + +feature -- Visitor pattern + + accept (a_visitor: JSON_VISITOR) + -- Accept `a_visitor'. + -- (Call `visit_json_number' procedure on `a_visitor'.) + do + a_visitor.visit_json_number (Current) + end + +feature -- Status + + is_equal (other: like Current): BOOLEAN + -- Is `other' attached to an object of the same type + -- as current object and identical to it? + do + Result := item.is_equal (other.item) + end + +feature -- Status report + + debug_output: STRING + -- String that should be displayed in debugger to represent `Current'. + do + Result := item + end + +feature -- Implementation + + INTEGER_TYPE: INTEGER = 1 + DOUBLE_TYPE: INTEGER = 2 + NATURAL_TYPE: INTEGER = 3 + + numeric_type: INTEGER + +invariant + item_not_void: item /= Void + +end diff --git a/contrib/library/text/parser/json/library/kernel/json_object.e b/contrib/library/text/parser/json/library/kernel/json_object.e index e4a16192..8a13c64c 100644 --- a/contrib/library/text/parser/json/library/kernel/json_object.e +++ b/contrib/library/text/parser/json/library/kernel/json_object.e @@ -8,7 +8,7 @@ note object {} - {"key","value"} + {"key": "value"} ]" author: "Javier Velilla" @@ -22,7 +22,9 @@ class inherit JSON_VALUE - TABLE_ITERABLE [JSON_VALUE, JSON_STRING] + 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 diff --git a/contrib/library/text/parser/json/library/kernel/json_string.e b/contrib/library/text/parser/json/library/kernel/json_string.e index ccd8e60d..89ec4648 100644 --- a/contrib/library/text/parser/json/library/kernel/json_string.e +++ b/contrib/library/text/parser/json/library/kernel/json_string.e @@ -1,329 +1,425 @@ -note - - description: "[ - A JSON_STRING represent a string in JSON. - A string is a collection of zero or more Unicodes characters, wrapped in double - quotes, using blackslash espaces. - ]" - - author: "Javier Velilla" - date: "2008/08/24" - revision: "Revision 0.1" - license:"MIT (see http://www.opensource.org/licenses/mit-license.php)" - - -class - JSON_STRING - -inherit - JSON_VALUE - redefine - is_equal - end - -create - make_json, - make_json_from_string_32, - make_with_escaped_json - -convert - make_json ({READABLE_STRING_8, STRING_8, IMMUTABLE_STRING_8}), - make_json_from_string_32 ({READABLE_STRING_32, STRING_32, IMMUTABLE_STRING_32}) - -feature {NONE} -- Initialization - - make_json (s: READABLE_STRING_8) - -- Initialize. - require - item_not_void: s /= Void - do - make_with_escaped_json (escaped_json_string (s)) - end - - make_json_from_string_32 (s: READABLE_STRING_32) - -- Initialize from STRING_32 `s'. - require - item_not_void: s /= Void - do - make_with_escaped_json (escaped_json_string_32 (s)) - end - - make_with_escaped_json (s: READABLE_STRING_8) - -- Initialize with an_item already escaped - require - item_not_void: s /= Void - do - item := s - end - -feature -- Access - - item: STRING - -- Contents with escaped entities if any - - unescaped_string_8: STRING_8 - -- Unescaped string from `item' - 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 - end - - unescaped_string_32: STRING_32 - -- Unescaped string 32 from `item' - local - s: like item - i, n: INTEGER - c: CHARACTER - hex: STRING - 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 - end - - representation: STRING - -- String representation of `item' with escaped entities if any - do - create Result.make (item.count + 2) - Result.append_character ('%"') - Result.append (item) - Result.append_character ('%"') - end - -feature -- Visitor pattern - - accept (a_visitor: JSON_VISITOR) - -- Accept `a_visitor'. - -- (Call `visit_json_string' procedure on `a_visitor'.) - do - a_visitor.visit_json_string (Current) - end - -feature -- Comparison - - is_equal (other: like Current): BOOLEAN - -- Is JSON_STRING made of same character sequence as `other' - -- (possibly with a different capacity)? - do - Result := item.same_string (other.item) - end - -feature -- Change Element - - append (a_string: STRING) - -- Add a_string - require - a_string_not_void: a_string /= Void - do - item.append_string (a_string) - end - -feature -- Status report - - hash_code: INTEGER - -- Hash code value - do - Result := item.hash_code - end - -feature -- Status report - - debug_output: STRING - -- String that should be displayed in debugger to represent `Current'. - do - Result := item - end - -feature {NONE} -- Implementation - - is_hexadecimal (s: READABLE_STRING_8): BOOLEAN - -- Is `s' an hexadecimal value? - do - Result := across s as scur all scur.item.is_hexa_digit end - end - - hexadecimal_to_natural_32 (s: READABLE_STRING_8): NATURAL_32 - -- Hexadecimal string `s' converted to NATURAL_32 value - require - s_not_void: s /= Void - is_hexadecimal: is_hexadecimal (s) - local - i, nb: INTEGER - char: CHARACTER - do - nb := s.count - - if nb >= 2 and then s.item (2) = 'x' then - i := 3 - else - i := 1 - end - - from - until - i > nb - loop - Result := Result * 16 - char := s.item (i) - if char >= '0' and then char <= '9' then - Result := Result + (char |-| '0').to_natural_32 - else - Result := Result + (char.lower |-| 'a' + 10).to_natural_32 - end - i := i + 1 - end - end - - escaped_json_string (s: READABLE_STRING_8): STRING_8 - -- JSON string with '"' and '\' characters escaped - require - s_not_void: s /= Void - local - i, n: INTEGER - c: CHARACTER_8 - do - n := s.count - create Result.make (n + n // 10) - from i := 1 until i > n loop - c := s.item (i) - inspect c - when '%"' then Result.append_string ("\%"") - when '\' then Result.append_string ("\\") - when '%R' then Result.append_string ("\r") - when '%N' then Result.append_string ("\n") - else - Result.extend (c) - end - i := i + 1 - end - end - - escaped_json_string_32 (s: READABLE_STRING_32): STRING_8 - -- JSON string with '"' and '\' characters and unicode escaped - require - s_not_void: s /= Void - local - i, j, n: INTEGER - uc: CHARACTER_32 - c: CHARACTER_8 - h: STRING_8 - do - n := s.count - create Result.make (n + n // 10) - from i := 1 until i > n loop - uc := s.item (i) - if uc.is_character_8 then - c := uc.to_character_8 - inspect c - when '%"' then Result.append_string ("\%"") - when '\' then Result.append_string ("\\") - when '%R' then Result.append_string ("\r") - when '%N' then Result.append_string ("\n") - else - Result.extend (c) - end - else - Result.append ("\u") - h := uc.code.to_hex_string - -- Remove first 0 and keep 4 hexa digit - from - j := 1 - until - h.count = 4 or (j <= h.count and then h.item (j) /= '0') - loop - j := j + 1 - end - h := h.substring (j, h.count) - - from - until - h.count >= 4 - loop - h.prepend_integer (0) - end - check h.count = 4 end - Result.append (h) - end - i := i + 1 - end - end - -invariant - item_not_void: item /= Void - -end +note + + description: "[ + A JSON_STRING represent a string in JSON. + A string is a collection of zero or more Unicodes characters, wrapped in double + quotes, using blackslash espaces. + ]" + + author: "Javier Velilla" + date: "2008/08/24" + revision: "Revision 0.1" + license:"MIT (see http://www.opensource.org/licenses/mit-license.php)" + + +class + JSON_STRING + +inherit + JSON_VALUE + redefine + is_equal + end + +create + make_json, + make_json_from_string_32, + make_with_escaped_json + +convert + make_json ({READABLE_STRING_8, STRING_8, IMMUTABLE_STRING_8}), + make_json_from_string_32 ({READABLE_STRING_32, STRING_32, IMMUTABLE_STRING_32}) + +feature {NONE} -- Initialization + + make_json (s: READABLE_STRING_8) + -- Initialize. + require + item_not_void: s /= Void + do + make_with_escaped_json (escaped_json_string (s)) + end + + make_json_from_string_32 (s: READABLE_STRING_32) + -- Initialize from STRING_32 `s'. + require + item_not_void: s /= Void + do + make_with_escaped_json (escaped_json_string_32 (s)) + end + + make_with_escaped_json (s: READABLE_STRING_8) + -- Initialize with an_item already escaped + require + item_not_void: s /= Void + do + item := s + end + +feature -- Access + + item: STRING + -- Contents with escaped entities if any + +feature -- Conversion + + unescaped_string_8: STRING_8 + -- Unescaped string from `item'. + --| note: valid only if `item' does not encode any unicode character. + local + s: like item + do + s := item + 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: READABLE_STRING_8 + do + s := item + create Result.make (s.count) + unescape_to_string_32 (Result) + end + + representation: STRING + -- String representation of `item' with escaped entities if any + do + create Result.make (item.count + 2) + Result.append_character ('%"') + Result.append (item) + 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) + -- Accept `a_visitor'. + -- (Call `visit_json_string' procedure on `a_visitor'.) + do + a_visitor.visit_json_string (Current) + end + +feature -- Comparison + + is_equal (other: like Current): BOOLEAN + -- Is JSON_STRING made of same character sequence as `other' + -- (possibly with a different capacity)? + do + Result := item.same_string (other.item) + end + +feature -- Change Element + + append (a_string: STRING) + -- Add a_string + require + a_string_not_void: a_string /= Void + do + item.append_string (a_string) + end + +feature -- Status report + + hash_code: INTEGER + -- Hash code value + do + Result := item.hash_code + end + +feature -- Status report + + debug_output: STRING + -- String that should be displayed in debugger to represent `Current'. + do + Result := item + end + +feature {NONE} -- Implementation + + is_hexadecimal (s: READABLE_STRING_8): BOOLEAN + -- Is `s' an hexadecimal value? + local + i: INTEGER + do + 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 + -- Hexadecimal string `s' converted to NATURAL_32 value + require + s_not_void: s /= Void + is_hexadecimal: is_hexadecimal (s) + local + i, nb: INTEGER + char: CHARACTER + do + nb := s.count + + if nb >= 2 and then s.item (2) = 'x' then + i := 3 + else + i := 1 + end + + from + until + i > nb + loop + Result := Result * 16 + char := s.item (i) + if char >= '0' and then char <= '9' then + Result := Result + (char |-| '0').to_natural_32 + else + Result := Result + (char.lower |-| 'a' + 10).to_natural_32 + end + i := i + 1 + end + end + + escaped_json_string (s: READABLE_STRING_8): STRING_8 + -- JSON string with '"' and '\' characters escaped + require + s_not_void: s /= Void + local + i, n: INTEGER + c: CHARACTER_8 + do + n := s.count + create Result.make (n + n // 10) + from i := 1 until i > n loop + c := s.item (i) + inspect c + when '%"' then Result.append_string ("\%"") + when '\' then Result.append_string ("\\") + 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 + i := i + 1 + end + end + + escaped_json_string_32 (s: READABLE_STRING_32): STRING_8 + -- JSON string with '"' and '\' characters and Unicode escaped + require + s_not_void: s /= Void + local + i, j, n: INTEGER + uc: CHARACTER_32 + c: CHARACTER_8 + h: STRING_8 + do + n := s.count + create Result.make (n + n // 10) + from i := 1 until i > n loop + uc := s.item (i) + if uc.is_character_8 then + c := uc.to_character_8 + inspect c + when '%"' then Result.append_string ("\%"") + when '\' then Result.append_string ("\\") + 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 + else + Result.append ("\u") + h := uc.code.to_hex_string + -- Remove first 0 and keep 4 hexa digit + from + j := 1 + until + h.count = 4 or (j <= h.count and then h.item (j) /= '0') + loop + j := j + 1 + end + h := h.substring (j, h.count) + + from + until + h.count >= 4 + loop + h.prepend_integer (0) + end + check h.count = 4 end + Result.append (h) + end + i := i + 1 + end + end + +invariant + item_not_void: item /= Void + +end diff --git a/contrib/library/text/parser/json/library/kernel/scanner/json_parser.e b/contrib/library/text/parser/json/library/kernel/scanner/json_parser.e index 3eaa7388..a3bf29f3 100644 --- a/contrib/library/text/parser/json/library/kernel/scanner/json_parser.e +++ b/contrib/library/text/parser/json/library/kernel/scanner/json_parser.e @@ -1,513 +1,513 @@ -note - - description: "Parse serialized JSON data" - author: "jvelilla" - date: "2008/08/24" - revision: "Revision 0.1" - -class - JSON_PARSER - -inherit - JSON_READER - JSON_TOKENS - -create - make_parser - -feature {NONE} -- Initialize - - make_parser (a_json: STRING) - -- Initialize. - require - json_not_empty: a_json /= Void and then not a_json.is_empty - do - make (a_json) - is_parsed := True - create errors.make - end - -feature -- Status report - - is_parsed: BOOLEAN - -- Is parsed? - - errors: LINKED_LIST [STRING] - -- Current errors - - current_errors: STRING - -- Current errors as string - do - create Result.make_empty - from - errors.start - until - errors.after - loop - Result.append_string (errors.item + "%N") - errors.forth - end - end - -feature -- Element change - - report_error (e: STRING) - -- Report error `e' - require - e_not_void: e /= Void - do - errors.force (e) - end - -feature -- Commands - - parse_json: detachable JSON_VALUE - -- Parse JSON data `representation' - -- start ::= object | array - do - if is_valid_start_symbol then - Result := parse - if extra_elements then - is_parsed := False - end - else - is_parsed := False - report_error ("Syntax error unexpected token, expecting `{' or `['") - end - end - - parse: detachable JSON_VALUE - -- Parse JSON data `representation' - local - c: CHARACTER - do - if is_parsed then - skip_white_spaces - c := actual - inspect c - when j_OBJECT_OPEN then - Result := parse_object - when j_STRING then - Result := parse_string - when j_ARRAY_OPEN then - Result := parse_array - else - if c.is_digit or c = j_MINUS then - Result := parse_number - elseif is_null then - Result := create {JSON_NULL} - next - next - next - elseif is_true then - Result := create {JSON_BOOLEAN}.make_boolean (True) - next - next - next - elseif is_false then - Result := create {JSON_BOOLEAN}.make_boolean (False) - next - next - next - next - else - is_parsed := False - report_error ("JSON is not well formed in parse") - Result := Void - end - end - end - ensure - is_parsed_implies_result_not_void: is_parsed implies Result /= Void - end - - parse_object: JSON_OBJECT - -- object - -- {} - -- {"key" : "value" [,]} - local - has_more: BOOLEAN - l_json_string: detachable JSON_STRING - l_value: detachable JSON_VALUE - do - create Result.make - -- check if is an empty object {} - next - skip_white_spaces - if actual = j_OBJECT_CLOSE then - --is an empty object - else - -- a complex object {"key" : "value"} - previous - from has_more := True until not has_more loop - next - skip_white_spaces - l_json_string := parse_string - next - skip_white_spaces - if actual = ':' then - next - skip_white_spaces - else - is_parsed := False - report_error ("%N Input string is a not well formed JSON, expected: : found: " + actual.out) - has_more := False - end - - l_value := parse - if is_parsed and then (l_value /= Void and l_json_string /= Void) then - Result.put (l_value, l_json_string) - next - skip_white_spaces - if actual = j_OBJECT_CLOSE then - has_more := False - elseif actual /= ',' then - has_more := False - is_parsed := False - report_error ("JSON Object syntactically malformed expected , found: [" + actual.out + "]") - end - else - has_more := False - -- explain the error - end - end - end - end - - parse_string: detachable JSON_STRING - -- Parsed string - local - has_more: BOOLEAN - l_json_string: STRING - l_unicode: STRING - c: like actual - do - create l_json_string.make_empty - if actual = j_STRING then - from - has_more := True - until - not has_more - loop - next - c := actual - if c = j_STRING then - has_more := False - elseif c = '%H' then - next - c := actual - if c = 'u' then - create l_unicode.make_from_string ("\u") - l_unicode.append (read_unicode) - c := actual - if is_valid_unicode (l_unicode) then - l_json_string.append (l_unicode) - else - has_more := False - is_parsed := False - report_error ("Input String is not well formed JSON, expected a Unicode value, found [" + c.out + " ]") - end - elseif (not is_special_character (c) and not is_special_control (c)) or c = '%N' then - has_more := False - is_parsed := False - report_error ("Input String is not well formed JSON, found [" + c.out + " ]") - else - l_json_string.append_character ('\') - l_json_string.append_character (c) - end - else - if is_special_character (c) and c /= '/' then - has_more := False - is_parsed := False - report_error ("Input String is not well formed JSON, found [" + c.out + " ]") - else - l_json_string.append_character (c) - end - end - end - create Result.make_with_escaped_json (l_json_string) - else - Result := Void - end - end - - parse_array: JSON_ARRAY - -- array - -- [] - -- [elements [,]] - local - flag: BOOLEAN - l_value: detachable JSON_VALUE - c: like actual - do - create Result.make_array - --check if is an empty array [] - next - skip_white_spaces - if actual = j_array_close then - --is an empty array - else - previous - from - flag := True - until - not flag - loop - next - skip_white_spaces - l_value := parse - if is_parsed and then l_value /= Void then - Result.add (l_value) - next - skip_white_spaces - c := actual - if c = j_ARRAY_CLOSE then - flag := False - elseif c /= ',' then - flag := False - is_parsed := False - report_error ("Array is not well formed JSON, found [" + c.out + " ]") - end - else - flag := False - report_error ("Array is not well formed JSON, found [" + actual.out + " ]") - end - end - end - end - - parse_number: detachable JSON_NUMBER - -- Parsed number - local - sb: STRING - flag: BOOLEAN - is_integer: BOOLEAN - c: like actual - do - create sb.make_empty - sb.append_character (actual) - - from - flag := True - until - not flag - loop - next - c := actual - if not has_next or is_close_token (c) - or c = ',' or c = '%N' or c = '%R' - then - flag := False - previous - else - sb.append_character (c) - end - end - - if is_valid_number (sb) then - if sb.is_integer then - create Result.make_integer (sb.to_integer) - is_integer := True - elseif sb.is_double and not is_integer then - create Result.make_real (sb.to_double) - end - else - is_parsed := False - report_error ("Expected a number, found: [ " + sb + " ]") - end - end - - is_null: BOOLEAN - -- Word at index represents null? - local - l_null: STRING - l_string: STRING - do - l_null := null_id - l_string := json_substring (index,index + l_null.count - 1) - if l_string.is_equal (l_null) then - Result := True - end - end - - is_false: BOOLEAN - -- Word at index represents false? - local - l_false: STRING - l_string: STRING - do - l_false := false_id - l_string := json_substring (index, index + l_false.count - 1) - if l_string.is_equal (l_false) then - Result := True - end - end - - is_true: BOOLEAN - -- Word at index represents true? - local - l_true: STRING - l_string: STRING - do - l_true := true_id - l_string := json_substring (index,index + l_true.count - 1) - if l_string.is_equal (l_true) then - Result := True - end - end - - read_unicode: STRING - -- Read unicode and return value - local - i: INTEGER - do - create Result.make_empty - from - i := 1 - until - i > 4 or not has_next - loop - next - Result.append_character (actual) - i := i + 1 - end - end - -feature {NONE} -- Implementation - - is_valid_number (a_number: STRING): BOOLEAN - -- is 'a_number' a valid number based on this regular expression - -- "-?(?: 0|[1-9]\d+)(?: \.\d+)?(?: [eE][+-]?\d+)?\b"? - local - s: detachable STRING - c: CHARACTER - i,n: INTEGER - do - create s.make_empty - n := a_number.count - if n = 0 then - Result := False - else - Result := True - i := 1 - --| "-?" - c := a_number[i] - if c = '-' then - s.extend (c); i := i + 1; c := a_number[i] - end - --| "0|[1-9]\d* - if c.is_digit then - if c = '0' then - --| "0" - s.extend (c); i := i + 1; c := a_number[i] - else - --| "[1-9]" - s.extend (c); i := i + 1; c := a_number[i] - --| "\d*" - from until i > n or not c.is_digit loop - s.extend (c); i := i + 1; c := a_number[i] - end - end - end - end - if Result then - --| "(\.\d+)?" - if c = '.' then - --| "\.\d+" = "\.\d\d*" - s.extend (c); i := i + 1; c := a_number[i] - if c.is_digit then - from until i > n or not c.is_digit loop - s.extend (c); i := i + 1; c := a_number[i] - end - else - Result := False --| expecting digit - end - end - end - if Result then --| "(?:[eE][+-]?\d+)?\b" - if c = 'e' or c = 'E' then - --| "[eE][+-]?\d+" - s.extend (c); i := i + 1; c := a_number[i] - if c = '+' or c = '-' then - s.extend (c); i := i + 1; c := a_number[i] - end - if c.is_digit then - from until i > n or not c.is_digit loop - s.extend (c); i := i + 1; c := a_number[i] - end - else - Result := False --| expecting digit - end - end - end - if Result then --| "\b" - from until i > n or not c.is_space loop - s.extend (c); i := i + 1; c := a_number[i] - end - Result := i > n and then s.same_string (a_number) - end - end - - is_valid_unicode (a_unicode: STRING): BOOLEAN - -- is 'a_unicode' a valid unicode based on this regular expression - -- "\\u[0-9a-fA-F]{4}" - local - i: INTEGER - do - if - a_unicode.count = 6 and then - a_unicode[1] = '\' and then - a_unicode[2] = 'u' - then - from - Result := True - i := 3 - until - i > 6 or Result = False - loop - inspect a_unicode[i] - when '0'..'9', 'a'..'f', 'A'..'F' then - else - Result := False - end - i := i + 1 - end - end - end - - extra_elements: BOOLEAN - -- has more elements? - local - c: like actual - do - if has_next then - next - end - from - c := actual - until - c /= ' ' or c /= '%R' or c /= '%U' or c /= '%T' or c /= '%N' or not has_next - loop - next - end - Result := has_next - end - - is_valid_start_symbol : BOOLEAN - -- expecting `{' or `[' as start symbol - do - if attached representation as s and then s.count > 0 then - Result := s[1] = '{' or s[1] = '[' - end - end - -feature {NONE} -- Constants - - false_id: STRING = "false" - - true_id: STRING = "true" - - null_id: STRING = "null" - - -end +note + + description: "Parse serialized JSON data" + author: "jvelilla" + date: "2008/08/24" + revision: "Revision 0.1" + +class + JSON_PARSER + +inherit + JSON_READER + JSON_TOKENS + +create + make_parser + +feature {NONE} -- Initialize + + make_parser (a_json: STRING) + -- Initialize. + require + json_not_empty: a_json /= Void and then not a_json.is_empty + do + make (a_json) + is_parsed := True + create errors.make + end + +feature -- Status report + + is_parsed: BOOLEAN + -- Is parsed? + + errors: LINKED_LIST [STRING] + -- Current errors + + current_errors: STRING + -- Current errors as string + do + create Result.make_empty + from + errors.start + until + errors.after + loop + Result.append_string (errors.item + "%N") + errors.forth + end + end + +feature -- Element change + + report_error (e: STRING) + -- Report error `e' + require + e_not_void: e /= Void + do + errors.force (e) + end + +feature -- Commands + + parse_json: detachable JSON_VALUE + -- Parse JSON data `representation' + -- start ::= object | array + do + if is_valid_start_symbol then + Result := parse + if extra_elements then + is_parsed := False + end + else + is_parsed := False + report_error ("Syntax error unexpected token, expecting `{' or `['") + end + end + + parse: detachable JSON_VALUE + -- Parse JSON data `representation' + local + c: CHARACTER + do + if is_parsed then + skip_white_spaces + c := actual + inspect c + when j_OBJECT_OPEN then + Result := parse_object + when j_STRING then + Result := parse_string + when j_ARRAY_OPEN then + Result := parse_array + else + if c.is_digit or c = j_MINUS then + Result := parse_number + elseif is_null then + Result := create {JSON_NULL} + next + next + next + elseif is_true then + Result := create {JSON_BOOLEAN}.make_boolean (True) + next + next + next + elseif is_false then + Result := create {JSON_BOOLEAN}.make_boolean (False) + next + next + next + next + else + is_parsed := False + report_error ("JSON is not well formed in parse") + Result := Void + end + end + end + ensure + is_parsed_implies_result_not_void: is_parsed implies Result /= Void + end + + parse_object: JSON_OBJECT + -- object + -- {} + -- {"key" : "value" [,]} + local + has_more: BOOLEAN + l_json_string: detachable JSON_STRING + l_value: detachable JSON_VALUE + do + create Result.make + -- check if is an empty object {} + next + skip_white_spaces + if actual = j_OBJECT_CLOSE then + --is an empty object + else + -- a complex object {"key" : "value"} + previous + from has_more := True until not has_more loop + next + skip_white_spaces + l_json_string := parse_string + next + skip_white_spaces + if actual = ':' then + next + skip_white_spaces + else + is_parsed := False + report_error ("%N Input string is a not well formed JSON, expected: : found: " + actual.out) + has_more := False + end + + l_value := parse + if is_parsed and then (l_value /= Void and l_json_string /= Void) then + Result.put (l_value, l_json_string) + next + skip_white_spaces + if actual = j_OBJECT_CLOSE then + has_more := False + elseif actual /= ',' then + has_more := False + is_parsed := False + report_error ("JSON Object syntactically malformed expected , found: [" + actual.out + "]") + end + else + has_more := False + -- explain the error + end + end + end + end + + parse_string: detachable JSON_STRING + -- Parsed string + local + has_more: BOOLEAN + l_json_string: STRING + l_unicode: STRING + c: like actual + do + create l_json_string.make_empty + if actual = j_STRING then + from + has_more := True + until + not has_more + loop + next + c := actual + if c = j_STRING then + has_more := False + elseif c = '%H' then + next + c := actual + if c = 'u' then + create l_unicode.make_from_string ("\u") + l_unicode.append (read_unicode) + c := actual + if is_valid_unicode (l_unicode) then + l_json_string.append (l_unicode) + else + has_more := False + is_parsed := False + report_error ("Input String is not well formed JSON, expected a Unicode value, found [" + c.out + " ]") + end + elseif (not is_special_character (c) and not is_special_control (c)) or c = '%N' then + has_more := False + is_parsed := False + report_error ("Input String is not well formed JSON, found [" + c.out + " ]") + else + l_json_string.append_character ('\') + l_json_string.append_character (c) + end + else + if is_special_character (c) and c /= '/' then + has_more := False + is_parsed := False + report_error ("Input String is not well formed JSON, found [" + c.out + " ]") + else + l_json_string.append_character (c) + end + end + end + create Result.make_with_escaped_json (l_json_string) + else + Result := Void + end + end + + parse_array: JSON_ARRAY + -- array + -- [] + -- [elements [,]] + local + flag: BOOLEAN + l_value: detachable JSON_VALUE + c: like actual + do + create Result.make_array + --check if is an empty array [] + next + skip_white_spaces + if actual = j_array_close then + --is an empty array + else + previous + from + flag := True + until + not flag + loop + next + skip_white_spaces + l_value := parse + if is_parsed and then l_value /= Void then + Result.add (l_value) + next + skip_white_spaces + c := actual + if c = j_ARRAY_CLOSE then + flag := False + elseif c /= ',' then + flag := False + is_parsed := False + report_error ("Array is not well formed JSON, found [" + c.out + " ]") + end + else + flag := False + report_error ("Array is not well formed JSON, found [" + actual.out + " ]") + end + end + end + end + + parse_number: detachable JSON_NUMBER + -- Parsed number + local + sb: STRING + flag: BOOLEAN + is_integer: BOOLEAN + c: like actual + do + create sb.make_empty + sb.append_character (actual) + + from + flag := True + until + not flag + loop + next + c := actual + if not has_next or is_close_token (c) + or c = ',' or c = '%N' or c = '%R' + then + flag := False + previous + else + sb.append_character (c) + end + end + + if is_valid_number (sb) then + if sb.is_integer then + create Result.make_integer (sb.to_integer) + is_integer := True + elseif sb.is_double and not is_integer then + create Result.make_real (sb.to_double) + end + else + is_parsed := False + report_error ("Expected a number, found: [ " + sb + " ]") + end + end + + is_null: BOOLEAN + -- Word at index represents null? + local + l_null: STRING + l_string: STRING + do + l_null := null_id + l_string := json_substring (index,index + l_null.count - 1) + if l_string.is_equal (l_null) then + Result := True + end + end + + is_false: BOOLEAN + -- Word at index represents false? + local + l_false: STRING + l_string: STRING + do + l_false := false_id + l_string := json_substring (index, index + l_false.count - 1) + if l_string.is_equal (l_false) then + Result := True + end + end + + is_true: BOOLEAN + -- Word at index represents true? + local + l_true: STRING + l_string: STRING + do + l_true := true_id + l_string := json_substring (index,index + l_true.count - 1) + if l_string.is_equal (l_true) then + Result := True + end + end + + read_unicode: STRING + -- Read Unicode and return value + local + i: INTEGER + do + create Result.make_empty + from + i := 1 + until + i > 4 or not has_next + loop + next + Result.append_character (actual) + i := i + 1 + end + end + +feature {NONE} -- Implementation + + is_valid_number (a_number: STRING): BOOLEAN + -- is 'a_number' a valid number based on this regular expression + -- "-?(?: 0|[1-9]\d+)(?: \.\d+)?(?: [eE][+-]?\d+)?\b"? + local + s: detachable STRING + c: CHARACTER + i,n: INTEGER + do + create s.make_empty + n := a_number.count + if n = 0 then + Result := False + else + Result := True + i := 1 + --| "-?" + c := a_number[i] + if c = '-' then + s.extend (c); i := i + 1; c := a_number[i] + end + --| "0|[1-9]\d* + if c.is_digit then + if c = '0' then + --| "0" + s.extend (c); i := i + 1; c := a_number[i] + else + --| "[1-9]" + s.extend (c); i := i + 1; c := a_number[i] + --| "\d*" + from until i > n or not c.is_digit loop + s.extend (c); i := i + 1; c := a_number[i] + end + end + end + end + if Result then + --| "(\.\d+)?" + if c = '.' then + --| "\.\d+" = "\.\d\d*" + s.extend (c); i := i + 1; c := a_number[i] + if c.is_digit then + from until i > n or not c.is_digit loop + s.extend (c); i := i + 1; c := a_number[i] + end + else + Result := False --| expecting digit + end + end + end + if Result then --| "(?:[eE][+-]?\d+)?\b" + if c = 'e' or c = 'E' then + --| "[eE][+-]?\d+" + s.extend (c); i := i + 1; c := a_number[i] + if c = '+' or c = '-' then + s.extend (c); i := i + 1; c := a_number[i] + end + if c.is_digit then + from until i > n or not c.is_digit loop + s.extend (c); i := i + 1; c := a_number[i] + end + else + Result := False --| expecting digit + end + end + end + if Result then --| "\b" + from until i > n or not c.is_space loop + s.extend (c); i := i + 1; c := a_number[i] + end + Result := i > n and then s.same_string (a_number) + end + end + + is_valid_unicode (a_unicode: STRING): BOOLEAN + -- is 'a_unicode' a valid Unicode based on this regular expression + -- "\\u[0-9a-fA-F]{4}" + local + i: INTEGER + do + if + a_unicode.count = 6 and then + a_unicode[1] = '\' and then + a_unicode[2] = 'u' + then + from + Result := True + i := 3 + until + i > 6 or Result = False + loop + inspect a_unicode[i] + when '0'..'9', 'a'..'f', 'A'..'F' then + else + Result := False + end + i := i + 1 + end + end + end + + extra_elements: BOOLEAN + -- has more elements? + local + c: like actual + do + if has_next then + next + end + from + c := actual + until + c /= ' ' or c /= '%R' or c /= '%U' or c /= '%T' or c /= '%N' or not has_next + loop + next + end + Result := has_next + end + + is_valid_start_symbol : BOOLEAN + -- expecting `{' or `[' as start symbol + do + if attached representation as s and then s.count > 0 then + Result := s[1] = '{' or s[1] = '[' + end + end + +feature {NONE} -- Constants + + false_id: STRING = "false" + + true_id: STRING = "true" + + null_id: STRING = "null" + + +end diff --git a/contrib/library/text/parser/json/library/kernel/scanner/json_reader.e b/contrib/library/text/parser/json/library/kernel/scanner/json_reader.e index 112857bd..977e91ae 100644 --- a/contrib/library/text/parser/json/library/kernel/scanner/json_reader.e +++ b/contrib/library/text/parser/json/library/kernel/scanner/json_reader.e @@ -1,118 +1,118 @@ -note - description: "Objects that ..." - author: "jvelilla" - date: "2008/08/24" - revision: "0.1" - -class - JSON_READER - -create - make - -feature {NONE} -- Initialization - - make (a_json: STRING) - -- Initialize Reader - do - set_representation (a_json) - end - -feature -- Commands - - set_representation (a_json: STRING) - -- Set `representation'. - do - a_json.left_adjust - a_json.right_adjust - representation := a_json - index := 1 - end - - read: CHARACTER - -- Read character - do - if not representation.is_empty then - Result := representation.item (index) - end - end - - next - -- Move to next index - require - has_more_elements: has_next - do - index := index + 1 - ensure - incremented: old index + 1 = index - end - - previous - -- Move to previous index - require - not_is_first: has_previous - do - index := index - 1 - ensure - incremented: old index - 1 = index - end - - skip_white_spaces - -- Remove white spaces - local - c: like actual - do - from - c := actual - until - (c /= ' ' and c /= '%N' and c /= '%R' and c /= '%U' and c /= '%T' ) or not has_next - loop - next - c := actual - end - end - - json_substring (start_index, end_index: INTEGER_32): STRING - -- JSON representation between `start_index' and `end_index' - do - Result := representation.substring (start_index, end_index) - end - -feature -- Status report - - has_next: BOOLEAN - -- Has a next character? - do - Result := index <= representation.count - end - - has_previous: BOOLEAN - -- Has a previous character? - do - Result := index >= 1 - end - -feature -- Access - - representation: STRING - -- Serialized representation of the original JSON string - -feature {NONE} -- Implementation - - actual: CHARACTER - -- Current character or '%U' if none - do - if index > representation.count then - Result := '%U' - else - Result := representation.item (index) - end - end - - index: INTEGER - -- Actual index - -invariant - representation_not_void: representation /= Void - -end +note + description: "Objects that ..." + author: "jvelilla" + date: "2008/08/24" + revision: "0.1" + +class + JSON_READER + +create + make + +feature {NONE} -- Initialization + + make (a_json: STRING) + -- Initialize Reader + do + set_representation (a_json) + end + +feature -- Commands + + set_representation (a_json: STRING) + -- Set `representation'. + do + a_json.left_adjust + a_json.right_adjust + representation := a_json + index := 1 + end + + read: CHARACTER + -- Read character + do + if not representation.is_empty then + Result := representation.item (index) + end + end + + next + -- Move to next index + require + has_more_elements: has_next + do + index := index + 1 + ensure + incremented: old index + 1 = index + end + + previous + -- Move to previous index + require + not_is_first: has_previous + do + index := index - 1 + ensure + incremented: old index - 1 = index + end + + skip_white_spaces + -- Remove white spaces + local + c: like actual + do + from + c := actual + until + (c /= ' ' and c /= '%N' and c /= '%R' and c /= '%U' and c /= '%T' ) or not has_next + loop + next + c := actual + end + end + + json_substring (start_index, end_index: INTEGER_32): STRING + -- JSON representation between `start_index' and `end_index' + do + Result := representation.substring (start_index, end_index) + end + +feature -- Status report + + has_next: BOOLEAN + -- Has a next character? + do + Result := index <= representation.count + end + + has_previous: BOOLEAN + -- Has a previous character? + do + Result := index >= 1 + end + +feature -- Access + + representation: STRING + -- Serialized representation of the original JSON string + +feature {NONE} -- Implementation + + actual: CHARACTER + -- Current character or '%U' if none + do + if index > representation.count then + Result := '%U' + else + Result := representation.item (index) + end + end + + index: INTEGER + -- Actual index + +invariant + representation_not_void: representation /= Void + +end diff --git a/contrib/library/text/parser/json/library/kernel/scanner/json_tokens.e b/contrib/library/text/parser/json/library/kernel/scanner/json_tokens.e index db8dd9df..86f86ba3 100644 --- a/contrib/library/text/parser/json/library/kernel/scanner/json_tokens.e +++ b/contrib/library/text/parser/json/library/kernel/scanner/json_tokens.e @@ -1,77 +1,77 @@ -note - description: "" - author: "jvelilla" - date: "2008/08/24" - revision: "0.1" - -class - JSON_TOKENS - -feature -- Access - - j_OBJECT_OPEN: CHARACTER = '{' - j_ARRAY_OPEN: CHARACTER = '[' - j_OBJECT_CLOSE: CHARACTER = '}' - j_ARRAY_CLOSE: CHARACTER = ']' - - j_STRING: CHARACTER = '"' - j_PLUS: CHARACTER = '+' - j_MINUS: CHARACTER = '-' - j_DOT: CHARACTER = '.' - -feature -- Status report - - is_open_token (c: CHARACTER): BOOLEAN - -- Characters which open a type - do - inspect c - when j_OBJECT_OPEN, j_ARRAY_OPEN, j_STRING, j_PLUS, j_MINUS, j_DOT then - Result := True - else - - end - end - - is_close_token (c: CHARACTER): BOOLEAN - -- Characters which close a type - do - inspect c - when j_OBJECT_CLOSE, j_ARRAY_CLOSE, j_STRING then - Result := True - else - - end - end - - is_special_character (c: CHARACTER): BOOLEAN - -- Control Characters - -- %F Form feed - -- %H backslasH - -- %N Newline - -- %R carriage Return - -- %T horizontal Tab - -- %B Backspace - -- / Solidus - -- " Quotation - do - inspect c - when '%F', '%H', '%N', '%R', '%T', '%B', '/', '"' then - Result := True - else - - end - end - - is_special_control (c: CHARACTER): BOOLEAN - --Control Characters - -- \b\f\n\r\t - do - inspect c - when 'b', 'f', 'n', 'r', 't' then - Result := True - else - - end - end - -end +note + description: "" + author: "jvelilla" + date: "2008/08/24" + revision: "0.1" + +class + JSON_TOKENS + +feature -- Access + + j_OBJECT_OPEN: CHARACTER = '{' + j_ARRAY_OPEN: CHARACTER = '[' + j_OBJECT_CLOSE: CHARACTER = '}' + j_ARRAY_CLOSE: CHARACTER = ']' + + j_STRING: CHARACTER = '"' + j_PLUS: CHARACTER = '+' + j_MINUS: CHARACTER = '-' + j_DOT: CHARACTER = '.' + +feature -- Status report + + is_open_token (c: CHARACTER): BOOLEAN + -- Characters which open a type + do + inspect c + when j_OBJECT_OPEN, j_ARRAY_OPEN, j_STRING, j_PLUS, j_MINUS, j_DOT then + Result := True + else + + end + end + + is_close_token (c: CHARACTER): BOOLEAN + -- Characters which close a type + do + inspect c + when j_OBJECT_CLOSE, j_ARRAY_CLOSE, j_STRING then + Result := True + else + + end + end + + is_special_character (c: CHARACTER): BOOLEAN + -- Control Characters + -- %F Form feed + -- %H backslasH + -- %N Newline + -- %R carriage Return + -- %T horizontal Tab + -- %B Backspace + -- / Solidus + -- " Quotation + do + inspect c + when '%F', '%H', '%N', '%R', '%T', '%B', '/', '"' then + Result := True + else + + end + end + + is_special_control (c: CHARACTER): BOOLEAN + --Control Characters + -- \b\f\n\r\t + do + inspect c + when 'b', 'f', 'n', 'r', 't' then + Result := True + else + + end + end + +end diff --git a/contrib/library/text/parser/json/test/autotest/test_suite/json_menu_example.txt b/contrib/library/text/parser/json/test/autotest/test_suite/json_menu_example.txt index bce42e86..de6a4e94 100644 --- a/contrib/library/text/parser/json/test/autotest/test_suite/json_menu_example.txt +++ b/contrib/library/text/parser/json/test/autotest/test_suite/json_menu_example.txt @@ -1,11 +1,11 @@ -{"menu": { - "id": "file", - "value": "File", - "popup": { - "menuitem": [ - {"value": "New", "onclick": "CreateNewDoc()"}, - {"value": "Open", "onclick": "OpenDoc()"}, - {"value": "Close", "onclick": "CloseDoc()"} - ] - } +{ "menu": { + "id": "file", + "value": "File", + "popup": { + "menuitem": [ + {"value": "New", "onclick": "CreateNewDoc()"}, + {"value": "Open", "onclick": "OpenDoc()"}, + {"value": "Close", "onclick": "CloseDoc()"} + ] + } }} \ No newline at end of file diff --git a/contrib/library/text/parser/json/test/autotest/test_suite/test_json_core.e b/contrib/library/text/parser/json/test/autotest/test_suite/test_json_core.e index ec17537a..9c2febac 100644 --- a/contrib/library/text/parser/json/test/autotest/test_suite/test_json_core.e +++ b/contrib/library/text/parser/json/test/autotest/test_suite/test_json_core.e @@ -1,833 +1,833 @@ -class TEST_JSON_CORE - -inherit - SHARED_EJSON - rename default_create as shared_default_create end - EQA_TEST_SET - select default_create end - -feature -- Test - - test_json_number_and_integer - local - i: INTEGER - jn: JSON_NUMBER - jrep: STRING - parser: JSON_PARSER - do - i := 42 - -- Eiffel value -> JSON value -> JSON representation - create jn.make_integer (i) - assert ("jn.representation.same_string (%"42%")", jn.representation.same_string ("42")) - -- Eiffel value -> JSON value -> JSON representation with factory - if attached {JSON_NUMBER} json.value (i) as l_jn then - assert ("l_jn.representation.same_string (%"42%")", jn.representation.same_string ("42")) - else - assert ("json.value (i) is a JSON_NUMBER", False) - end - - -- JSON representation-> JSON value -> Eiffel value - -- Note: The JSON_FACTORY will return the smallest INTEGER_* object - -- that can represent the value of the JSON number, in this case - -- we know it is INTEGER_8 since the value is 42 - jrep := "42" - create parser.make_parser (jrep) - if attached {JSON_NUMBER} parser.parse as l_jn then - if attached {INTEGER_8} json.object (jn, Void) as l_i8 then - assert ("l_i8 = 42", l_i8 = 42) - else - assert ("json.object (jn, Void) is a INTEGER_8", False) - end - else - assert ("parser.parse is a JSON_NUMBER", False) - end - end - - test_json_number_and_integer_8 - local - i8: INTEGER_8 - jn: JSON_NUMBER - jrep: STRING - parser: JSON_PARSER - do - i8 := 42 - -- Eiffel value -> JSON value -> JSON representation - create jn.make_integer (i8) - assert ("jn.representation.same_string (%"42%")", jn.representation.same_string ("42")) - -- Eiffel value -> JSON value -> JSON representation with factory - if attached {JSON_NUMBER} json.value (i8) as l_jn then - assert ("l_jn.representation.same_string (%"42%")", jn.representation.same_string ("42")) - else - assert ("json.value (i8) is a JSON_NUMBER", False) - end - - -- JSON representation -> JSON value -> Eiffel value - -- Note: The JSON_FACTORY will return the smallest INTEGER_* object - -- that can represent the value of the JSON number, in this case - -- we know it is INTEGER_8 since the value is 42 - jrep := "42" - create parser.make_parser (jrep) - if attached {JSON_NUMBER} parser.parse as l_jn then - if attached {INTEGER_8} json.object (jn, Void) as l_i8 then - assert ("l_i8 = 42", l_i8 = 42) - else - assert ("json.object (jn, Void) is a INTEGER_8", False) - end - else - assert ("parser.parse is a JSON_NUMBER", False) - end - end - - test_json_number_and_integer_16 - local - i16: INTEGER_16 - jn: JSON_NUMBER - jrep: STRING - parser: JSON_PARSER - do - i16 := 300 - -- Eiffel value -> JSON value -> JSON representation - create jn.make_integer (i16) - assert ("jn.representation.same_string (%"300%")", jn.representation.same_string ("300")) - -- Eiffel value -> JSON with factory - if attached {JSON_NUMBER} json.value (i16) as l_jn then - assert ("l_jn.representation.same_string (%"300%")", l_jn.representation.same_string ("300")) - else - assert ("json.value (i16) is a JSON_NUMBER", False) - end - - -- JSON representation -> JSON value -> Eiffel value - -- Note: The JSON_FACTORY will return the smallest INTEGER_* object - -- that can represent the value of the JSON number, in this case - -- we know it is INTEGER_16 since the value is 300 - jrep := "300" - create parser.make_parser (jrep) - if attached {JSON_NUMBER} parser.parse as l_jn then - if attached {INTEGER_16} json.object (jn, Void) as l_i16 then - assert ("l_i16 = 300", l_i16 = 300) - else - assert ("json.object (jn, Void) is a INTEGER_16", False) - end - else - assert ("parser.parse is a JSON_NUMBER", False) - end - end - - test_json_number_and_integer_32 - local - i32: INTEGER_32 - jn: JSON_NUMBER - jrep: STRING - parser: JSON_PARSER - do - i32 := 100000 - -- Eiffel value -> JSON representation -> JSON value - create jn.make_integer (i32) - assert ("jn.representation.same_string (%"100000%")", jn.representation.same_string ("100000")) - -- Eiffel value -> JSON representation -> JSON value with factory - if attached {JSON_NUMBER} json.value (i32) as l_jn then - assert ("l_jn.representation.same_string (%"100000%")", l_jn.representation.same_string ("100000")) - else - assert ("json.value (i32) is a JSON_NUMBER", False) - end - - -- JSON representation -> JSON value -> Eiffel value - -- Note: The JSON_FACTORY will return the smallest INTEGER_* object - -- that can represent the value of the JSON number, in this case - -- we know it is INTEGER_32 since the value is 100000 - jrep := "100000" - create parser.make_parser (jrep) - if attached {JSON_NUMBER} parser.parse as l_jn then - if attached {INTEGER_32} json.object (jn, Void) as l_i32 then - assert ("l_i32 = 100000", l_i32 = 100000) - else - assert ("json.object (jn, Void) is a INTEGER_32", False) - end - else - assert ("parser.parse is a JSON_NUMBER", False) - end - end - - test_json_number_and_integer_64 - local - i64: INTEGER_64 - jn: JSON_NUMBER - jrep: STRING - parser: JSON_PARSER - do - i64 := 42949672960 - -- Eiffel value -> JSON value -> JSON representation - create jn.make_integer (i64) - assert ("jn.representation.same_string (%"42949672960%")", jn.representation.same_string ("42949672960")) - -- Eiffel value -> JSON value -> JSON representation with factory - if attached {JSON_NUMBER} json.value (i64) as l_jn then - assert ("l_jn.representation.same_string (%"42949672960%")", l_jn.representation.same_string ("42949672960")) - else - assert ("json.value (i64) is a JSON_NUMBER", False) - end - - -- JSON representation -> JSON value -> Eiffel value - -- Note: The JSON_FACTORY will return the smallest INTEGER_* object - -- that can represent the value of the JSON number, in this case - -- we know it is INTEGER_32 since the value is 42949672960 - jrep := "42949672960" - create parser.make_parser (jrep) - if attached {JSON_NUMBER} parser.parse as l_jn then - if attached {INTEGER_64} json.object (jn, Void) as l_i64 then - assert ("l_i64 = 42949672960", l_i64 = 42949672960) - else - assert ("json.object (jn, Void) is a INTEGER_64", False) - end - else - assert ("parser.parse is a JSON_NUMBER", False) - end - end - - test_json_number_and_natural_8 - local - n8: NATURAL_8 - jn: JSON_NUMBER - jrep: STRING - parser: JSON_PARSER - do - n8 := 200 - -- Eiffel value -> JSON value -> JSON representation - create jn.make_natural (n8) - assert ("jn.representation.same_string (%"200%")", jn.representation.same_string ("200")) - -- Eiffel value -> JSON value -> JSON representation with factory - if attached {JSON_NUMBER} json.value (n8) as l_jn then - assert ("l_jn.representation.same_string (%"200%")", l_jn.representation.same_string ("200")) - else - assert ("json.value (n8) is a JSON_NUMBER}", False) - end - - -- JSON representation -> JSON value -> Eiffel value - -- Note: The JSON_FACTORY will return the smallest INTEGER_* object - -- that can represent the value of the JSON number, in this case - -- we know it is INTEGER_16 since the value is 200 - jrep := "200" - create parser.make_parser (jrep) - if attached {JSON_NUMBER} parser.parse as l_jn then - if attached {INTEGER_16} json.object (jn, Void) as i16 then - assert ("i16 = 200", i16 = 200) - else - assert ("json.object (jn, Void) is an INTEGER_16", False) - end - else - assert ("parser.parse is a JSON_NUMBER", False) - end - end - - test_json_number_and_natural_16 - local - n16: NATURAL_16 - jn: JSON_NUMBER - jrep: STRING - parser: JSON_PARSER - do - n16 := 32768 - -- Eiffel value -> JSON value -> JSON representation - create jn.make_natural (n16) - assert ("jn.representation.same_string (%"32768%")", jn.representation.same_string ("32768")) - -- Eiffel value -> JSON value -> JSON representation with factory - if attached {JSON_NUMBER} json.value (n16) as l_jn then - assert ("l_jn.representation.same_string (%"32768%")", l_jn.representation.same_string ("32768")) - else - assert ("json.value (n16) is a JSON_NUMBER", False) - end - - -- JSON representation -> JSON value -> Eiffel value - -- Note: The JSON_FACTORY will return the smallest INTEGER_* object - -- that can represent the value of the JSON number, in this case - -- we know it is INTEGER_32 since the value is 32768 - jrep := "32768" - create parser.make_parser (jrep) - if attached {JSON_NUMBER} parser.parse as l_jn then - if attached {INTEGER_32} json.object (jn, Void) as i32 then - assert ("i32 = 32768", i32 = 32768) - else - assert ("json.object (jn, Void) is a INTEGER_32", False) - end - else - assert ("parser.parse is a JSON_NUMBER", False) - end - end - - test_json_number_and_natural_32 - local - n32: NATURAL_32 - jn: JSON_NUMBER - jrep: STRING - parser: JSON_PARSER - do - n32 := 2147483648 - -- Eiffel value -> JSON value -> JSON representation - create jn.make_natural (n32) - assert ("jn.representation.same_string (%"2147483648%")", jn.representation.same_string ("2147483648")) - -- Eiffel value -> JSON value -> JSON representation with factory - if attached json.value (n32) as l_jn then - assert ("l_jn.representation.same_string (%"2147483648%")", l_jn.representation.same_string ("2147483648")) - else - assert ("json.value (n32) is a JSON_NUMBER", False) - end - - -- JSON representation -> JSON value -> Eiffel value - -- Note: The JSON_FACTORY will return the smallest INTEGER_* object - -- that can represent the value of the JSON number, in this case - -- we know it is INTEGER_64 since the value is 2147483648 - jrep := "2147483648" - create parser.make_parser (jrep) - if attached {JSON_NUMBER} parser.parse as l_jn then - if attached {INTEGER_64} json.object (jn, Void) as i64 then - assert ("i64 = 2147483648", i64 = 2147483648) - else - assert ("json.object (jn, Void) is a INTEGER_64", False) - end - else - assert ("parser.parse is a JSON_NUMBER", False) - end - end - - test_json_number_and_large_integers - local - jrep: STRING - n64: NATURAL_64 - jn: JSON_NUMBER - parser: JSON_PARSER - do - n64 := 9223372036854775808 - -- Eiffel value -> JSON value -> JSON representation - create jn.make_natural (n64) - assert ("jn.representation.same_string (%"9223372036854775808%")", jn.representation.same_string ("9223372036854775808")) - -- Eiffel value -> JSON value -> JSON representation with factory - if attached {JSON_NUMBER} json.value (n64) as l_jn then - assert ("l_jn.representation.same_string (%"9223372036854775808%")", l_jn.representation.same_string ("9223372036854775808")) - else - assert ("json.value (n64) is a JSON_NUMBER", False) - end - - -- JSON representation -> JSON value -> Eiffel value - -- Note: The JSON_FACTORY will return the smallest INTEGER_* object - -- that can represent the value of the JSON number, in this case - -- we know it is INTEGER_32 since the value is 42949672960 - jrep := "9223372036854775808" -- 1 higher than largest positive number that can be represented by INTEGER 64 - create parser.make_parser (jrep) - if attached {JSON_NUMBER} parser.parse as l_jn then - if attached {NATURAL_64} json.object (jn, Void) as l_n64 then - assert ("l_n64 = 9223372036854775808", l_n64 = 9223372036854775808) - else - assert ("json.object (jn, Void) is a NATURAL_64", False) - end - else - assert ("parser.parse is a JSON_NUMBER", False) - end - end - - test_json_number_and_eiffel_real - local - r: REAL - jn: JSON_NUMBER - jrep: STRING - parser: JSON_PARSER - do - r := 3.14 - -- Eiffel value -> JSON value -> JSON representation - create jn.make_real (r) - assert ("jn.representation.same_string (%"3.1400001049041748%")", jn.representation.same_string ("3.1400001049041748")) - -- Eiffel value -> JSON value -> JSON representation with factory - if attached {JSON_NUMBER} json.value (r) as l_jn then - assert ("l_jn.representation.same_string (%"3.1400001049041748%")", l_jn.representation.same_string ("3.1400001049041748")) - else - assert ("json.value (r) is a JSON_NUMBER", False) - end - - -- JSON representation -> JSON value -> Eiffel value - -- Note: The JSON_FACTORY will always return a REAL_64 if the value - -- of the JSON number is a floating point number - jrep := "3.14" - create parser.make_parser (jrep) - if attached {JSON_NUMBER} parser.parse as l_jn then - if attached {REAL_64} json.object (jn, Void) as r64 then - assert ("3.14 <= r64 and r64 <= 3.141", 3.14 <= r64 and r64 <= 3.141) - else - assert ("json.object (jn, Void) is a REAL_64", False) - end - else - assert ("parser.parse is a JSON_NUMBER", False) - end - end - - test_json_number_and_eiffel_real_32 - local - r32: REAL_32 - jn: JSON_NUMBER - jrep: STRING - parser: JSON_PARSER - do - r32 := 3.14 - -- Eiffel value -> JSON value -> JSON representation - create jn.make_real (r32) - assert ("jn.representation.same_string (%"3.1400001049041748%")", jn.representation.same_string ("3.1400001049041748")) - -- Eiffel value -> JSON value -> JSON representation with factory - if attached {JSON_NUMBER} json.value (r32) as l_jn then - assert ("l_jn.representation.same_string (%"3.1400001049041748%")", l_jn.representation.same_string ("3.1400001049041748")) - else - assert ("json.value (r32) is a JSON_NUMBER", False) - end - - -- JSON representation -> JSON value -> Eiffel value - jrep := "3.1400001049041748" - create parser.make_parser (jrep) - if attached {JSON_NUMBER} parser.parse as l_jn then - if attached {REAL_64} json.object (l_jn, Void) as r64 then - assert ("r64 = 3.1400001049041748", r64 = 3.1400001049041748) - else - assert ("json.object (l_jn, Void) is a REAL_64", False) - end - else - assert ("parser.parse is a JSON_NUMBER", False) - end - end - - test_json_number_and_eiffel_real_64 - local - r64: REAL_64 - jn: JSON_NUMBER - jrep: STRING - parser: JSON_PARSER - do - r64 := 3.1415926535897931 - -- Eiffel value -> JSON value -> JSON representation - create jn.make_real (r64) - assert ("jn.representation.same_string (%"3.1415926535897931%")", jn.representation.same_string ("3.1415926535897931")) - - -- Eiffel value -> JSON value -> JSON representation with factory - if attached {JSON_NUMBER} json.value (r64) as l_jn then - assert ("l_jn.representation.same_string (%"3.1415926535897931%")", l_jn.representation.same_string ("3.1415926535897931")) - else - assert ("json.value (r64) is a JSON_NUMBER", False) - end - - -- JSON representation -> JSON value -> Eiffel value - jrep := "3.1415926535897931" - create parser.make_parser (jrep) - if attached {JSON_NUMBER} parser.parse as l_jn then - if attached {REAL_64} json.object (jn, Void) as l_r64 then - assert ("l_r64 = 3.1415926535897931", l_r64 = 3.1415926535897931) - else - assert ("json.object (jn, Void) is a REAL_64", False) - end - else - assert ("parser.parse is a JSON_NUMBER", False) - end - end - - test_json_boolean - local - parser: JSON_PARSER - jb: JSON_BOOLEAN - b: BOOLEAN - do - -- Eiffel value -> JSON value -> JSON representation - b := True - create jb.make_boolean (b) - assert ("jb.representation.is_equal (%"true%")", jb.representation.is_equal ("true")) - -- Eiffel value -> JSON value -> JSON representation with factory - if attached {JSON_BOOLEAN} json.value (b) as l_jb then - assert ("l_jb.representation.same_string (%"true%")", l_jb.representation.same_string ("true")) - else - assert ("l_jb /= Void", False) - end - - -- JSON representation -> JSON value -> Eiffel value - create parser.make_parser ("true") - if attached {JSON_BOOLEAN} parser.parse as l_jb then - if attached {BOOLEAN} json.object (l_jb, Void) as l_b then - assert ("l_b = True", l_b = True) - else - assert ("json.object (l_jb, Void) is BOOLEAN", False) - end - else - assert ("parser.parse is a JSON_BOOLEAN", False) - end - - -- Eiffel value -> JSON value -> JSON representation - b := False - create jb.make_boolean (b) - assert ("jb.representation.same_string (%"false%")", jb.representation.same_string ("false")) - -- Eiffel value -> JSON value -> JSON representation with factory - if attached {JSON_BOOLEAN} json.value (b) as l_jb then - assert ("l_jb.representation.same_string (%"false%")", l_jb.representation.same_string ("false")) - else - assert ("json.value (b) is a JSON_BOOLEAN", False) - end - - -- JSON representation -> JSON value -> Eiffel value - create parser.make_parser ("false") - if attached {JSON_BOOLEAN} parser.parse as l_jb then - if attached {BOOLEAN} json.object (l_jb, Void) as l_b then - assert ("l_b = False", l_b = False) - else - assert ("json.object (l_jb, Void) is a BOOLEAN", False) - end - else - assert ("parser.parse is a JSON_BOOLEAN", False) - end - end - - test_json_null - local - a: detachable ANY - dummy_object: STRING - jn: detachable JSON_NULL - jrep: STRING - parser: JSON_PARSER - do - -- Eiffel value -> JSON value -> JSON representation - create jn - assert ("jn /= Void", jn /= Void) - assert ("jn.representation.is_equal (%"%"null%"%")", jn.representation.is_equal ("null")) - -- Eiffel value -> JSON value -> JSON representation with factory - jn ?= json.value (Void) - assert ("jn /= Void", jn /= Void) - if attached jn as l_jn then - assert ("jn.representation.is_equal (%"null%")", l_jn.representation.is_equal ("null")) - end - - -- JSON representation -> JSON value -> Eiffel value - jrep := "null" - create parser.make_parser (jrep) - jn := Void - jn ?= parser.parse - assert ("jn /= Void", jn /= Void) - create dummy_object.make_empty - a := dummy_object - a ?= json.object (jn, Void) - assert ("a = Void", a = Void) - end - - test_json_string_and_character - local - c: CHARACTER - js: detachable JSON_STRING - jrep: STRING - parser: JSON_PARSER - do - c := 'a' - -- Eiffel value -> JSON value -> JSON representation - create js.make_json (c.out) - assert ("js /= Void", js /= Void) - assert ("js.representation.is_equal (%"%"a%"%")", js.representation.is_equal ("%"a%"")) - -- Eiffel value -> JSON value -> JSON representation with factory - js ?= json.value (c) - assert ("js /= Void", js /= Void) - if attached js as l_js then - assert ("js.representation.is_equal (%"%"a%"%")", l_js.representation.is_equal ("%"a%"")) - end - - -- JSON representation -> JSON value -> Eiffel value - jrep := "%"a%"" - create parser.make_parser (jrep) - js := Void - js ?= parser.parse - assert ("js /= Void", js /= Void) - if attached {STRING_32} json.object (js, Void) as ucs then - assert ("ucs.string.is_equal (%"a%")", ucs.string.is_equal ("a")) - end - - end - - test_json_string_and_string - local - s: STRING - js: detachable JSON_STRING - jrep: STRING - parser: JSON_PARSER - do - s := "foobar" - -- Eiffel value -> JSON value -> JSON representation - create js.make_json (s) - assert ("js /= Void", js /= Void) - assert ("js.representation.is_equal (%"%"foobar%"%")", js.representation.is_equal ("%"foobar%"")) - -- Eiffel value -> JSON value -> JSON representation with factory - js ?= json.value (s) - assert ("js /= Void", js /= Void) - if attached js as l_js then - assert ("js.representation.is_equal (%"%"foobar%"%")", js.representation.is_equal ("%"foobar%"")) - end - - -- JSON representation -> JSON value -> Eiffel value - jrep := "%"foobar%"" - create parser.make_parser (jrep) - js := Void - js ?= parser.parse - assert ("js /= Void", js /= Void) - if attached {STRING_32} json.object (js, Void) as l_ucs then - assert ("ucs.string.is_equal (%"foobar%")", l_ucs.string.is_equal ("foobar")) - end - end - - test_json_string_and_uc_string - local - js: detachable JSON_STRING - ucs: detachable STRING_32 - jrep: STRING - parser: JSON_PARSER - do - create ucs.make_from_string ("foobar") - -- Eiffel value -> JSON value -> JSON representation - create js.make_json (ucs) - assert ("js /= Void", js /= Void) - assert ("js.representation.is_equal (%"%"foobar%"%")", js.representation.is_equal ("%"foobar%"")) - -- Eiffel value -> JSON value -> JSON representation with factory - js ?= json.value (ucs) - assert ("js /= Void", js /= Void) - if attached js as l_js then - assert ("js.representation.is_equal (%"%"foobar%"%")", l_js.representation.is_equal ("%"foobar%"")) - end - - -- JSON representation -> JSON value -> Eiffel value - jrep := "%"foobar%"" - create parser.make_parser (jrep) - js := Void - js ?= parser.parse - assert ("js /= Void", js /= Void) - ucs := Void - ucs ?= json.object (js, Void) - if attached ucs as l_ucs then - assert ("ucs.string.is_equal (%"foobar%")", l_ucs.string.is_equal ("foobar")) - end - end - - test_json_string_and_special_characters - local - js: detachable JSON_STRING - s: detachable STRING_8 - ucs: detachable STRING_32 - jrep: STRING - parser: JSON_PARSER - do - create s.make_from_string ("foo\bar") - create js.make_json (s) - - assert ("js.representation.same_string (%"%"foo\\bar%"%")", js.representation.same_string ("%"foo\\bar%"")) - - -- Eiffel value -> JSON value -> JSON representation with factory - js ?= json.value (s) - assert ("js /= Void", js /= Void) - if js /= Void then - assert ("js.representation.is_equal (%"%"foobar%"%")", js.representation.same_string ("%"foo\\bar%"")) - end - - -- JSON representation -> JSON value -> Eiffel value - jrep := "%"foo\\bar%"" - create parser.make_parser (jrep) - js ?= parser.parse - assert ("js /= Void", js /= Void) - ucs ?= json.object (js, Void) - if ucs /= Void then - assert ("ucs.same_string (%"foo\bar%")", ucs.same_string ("foo\bar")) - end - - 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")) - end - - create js.make_json_from_string_32 ({STRING_32}"%/20320/%/22909/") - assert ("escaping unicode string32 %"%%/20320/%%/22909/%" %"\u4F60\u597D%"", js.item.same_string ("\u4F60\u597D")) - - jrep := "%"\u4F60\u597D%"" --| Ni hao - create parser.make_parser (jrep) - if attached {JSON_STRING} parser.parse as jstring then - assert ("same unicode string32 %"%%/20320/%%/22909/%"", jstring.unescaped_string_32.same_string ({STRING_32}"%/20320/%/22909/")) - end - end - - test_json_array - local - ll: LINKED_LIST [INTEGER_8] - ll2: detachable LINKED_LIST [detachable ANY] - ja: detachable JSON_ARRAY - jn: JSON_NUMBER - jrep: STRING - parser: JSON_PARSER - do - -- Eiffel value -> JSON value -> JSON representation - create ll.make - ll.extend (0) - ll.extend (1) - ll.extend (1) - ll.extend (2) - ll.extend (3) - ll.extend (5) - -- Note: Currently there is no simple way of creating a JSON_ARRAY - -- from an LINKED_LIST. - create ja.make_array - from - ll.start - until - ll.after - loop - create jn.make_integer (ll.item) - ja.add (jn) - ll.forth - end - assert ("ja /= Void", ja /= Void) - assert ("ja.representation.is_equal (%"[0,1,1,2,3,5]%")", ja.representation.is_equal ("[0,1,1,2,3,5]")) - -- Eiffel value -> JSON value -> JSON representation with factory - ja := Void - ja ?= json.value (ll) - assert ("ja /= Void", ja /= Void) - if attached ja as l_ja then - assert ("ja.representation.is_equal (%"[0,1,1,2,3,5]%")", l_ja.representation.is_equal ("[0,1,1,2,3,5]")) - end - - -- JSON representation -> JSON value -> Eiffel value - -- Note: The JSON_FACTORY will return the smallest INTEGER_* object - -- that can represent the value of the JSON number, in this case - -- it means we will get an LINKED_LIST [ANY] containing the INTEGER_8 - -- values 0, 1, 1, 2, 3, 5 - jrep := "[0,1,1,2,3,5]" - create parser.make_parser (jrep) - ja := Void - ja ?= parser.parse - assert ("ja /= Void", ja /= Void) - ll2 ?= json.object (ja, Void) - assert ("ll2 /= Void", ll2 /= Void) - --ll.compare_objects - --ll2.compare_objects - if attached ll2 as l_ll2 then - assert ("ll2.is_equal (ll)", l_ll2.is_equal (ll)) - end - - end - - test_json_object - local - t, t2: detachable HASH_TABLE [detachable ANY, STRING_GENERAL] - i: INTEGER - ucs_key, ucs: STRING_32 - a: ARRAY [INTEGER] - jo: detachable JSON_OBJECT - jn: JSON_NUMBER - js_key, js: JSON_STRING - ja: JSON_ARRAY - jrep: STRING - parser: JSON_PARSER - do - -- Eiffel value -> JSON value -> JSON representation - -- Note: Currently there is now way of creating a JSON_OBJECT from - -- a HASH_TABLE, so we do it manually. - -- t = {"name": "foobar", "size": 42, "contents", [0, 1, 1, 2, 3, 5]} - create jo.make - create js_key.make_json ("name") - create js.make_json ("foobar") - jo.put (js, js_key) - create js_key.make_json ("size") - create jn.make_integer (42) - jo.put (jn, js_key) - create js_key.make_json ("contents") - create ja.make_array - create jn.make_integer (0) - ja.add (jn) - create jn.make_integer (1) - ja.add (jn) - create jn.make_integer (1) - ja.add (jn) - create jn.make_integer (2) - ja.add (jn) - create jn.make_integer (3) - ja.add (jn) - create jn.make_integer (5) - ja.add (jn) - jo.put (ja, js_key) - assert ("jo /= Void", jo /= Void) - assert ("jo.representation.is_equal (%"{%"name%":%"foobar%",%"size%":42,%"contents%":[0,1,1,2,3,5]}%")", jo.representation.is_equal ("{%"name%":%"foobar%",%"size%":42,%"contents%":[0,1,1,2,3,5]}")) - -- Eiffel value -> JSON value -> JSON representation with factory - create t.make (3) - create ucs_key.make_from_string ("name") - create ucs.make_from_string ("foobar") - t.put (ucs, ucs_key) - create ucs_key.make_from_string ("size") - i := 42 - t.put (i, ucs_key) - create ucs_key.make_from_string ("contents") - a := <<0, 1, 1, 2, 3, 5>> - t.put (a, ucs_key) - jo := Void - jo ?= json.value (t) - assert ("jo /= Void", jo /= Void) - if attached jo as l_jo then - assert ("jo.representation.is_equal (%"{%"name%":%"foobar%",%"size%":42,%"contents%":[0,1,1,2,3,5]}%")", l_jo.representation.is_equal ("{%"name%":%"foobar%",%"size%":42,%"contents%":[0,1,1,2,3,5]}")) - end - -- JSON representation -> JSON value -> Eiffel value -> JSON value -> JSON representation - jrep := "{%"name%":%"foobar%",%"size%":42,%"contents%":[0,1,1,2,3,5]}" - create parser.make_parser (jrep) - jo := Void - jo ?= parser.parse - assert ("jo /= Void", jo /= Void) - t2 ?= json.object (jo, Void) - assert ("t2 /= Void", t2 /= Void) - jo ?= json.value (t2) - assert ("jo /= Void", jo /= Void) - if attached jo as l_jo then - assert ("jrep.is_equal (jo.representation)", jrep.is_equal (jo.representation)) - end - - end - - test_json_object_hash_code - local - ht: HASH_TABLE [ANY, JSON_VALUE] - jo: JSON_OBJECT - do - create ht.make (1) - create jo.make - ht.force ("", jo) - assert ("ht.has_key (jo)", ht.has_key (jo)) - end - - test_json_failed_json_conversion - -- Test converting an Eiffel object to JSON that is based on a class - -- for which no JSON converter has been registered. - local - gv: OPERATING_ENVIRONMENT - jv: detachable JSON_VALUE - exception: BOOLEAN - do - if not exception then - create gv - jv := json.value (gv) - else - assert ("exceptions.is_developer_exception", json.is_developer_exception) --- assert ("exceptions.is_developer_exception_of_name", json.is_developer_exception_of_name ("eJSON exception: Failed to convert Eiffel object to a JSON_VALUE: OPERATING_ENVIRONMENT")) - end - rescue - exception := True - retry - end - - test_json_failed_eiffel_conversion - -- Test converting from a JSON value to an Eiffel object based on a - -- class for which no JSON converter has been registered. - local - gv : detachable OPERATING_ENVIRONMENT - jo: JSON_OBJECT - exception: BOOLEAN - do - if not exception then - create jo.make - gv ?= json.object (jo, "OPERATING_ENVIRONMENT") - else - assert ("exceptions.is_developer_exception", json.is_developer_exception) --- assert ("exceptions.is_developer_exception_of_name", json.is_developer_exception_of_name ("eJSON exception: Failed to convert JSON_VALUE to an Eiffel object: JSON_OBJECT -> OPERATING_ENVIRONMENT")) - - end - rescue - exception := True - retry - end - -end -- class TEST_JSON_CORE +class TEST_JSON_CORE + +inherit + SHARED_EJSON + rename default_create as shared_default_create end + EQA_TEST_SET + select default_create end + +feature -- Test + + test_json_number_and_integer + local + i: INTEGER + jn: JSON_NUMBER + jrep: STRING + parser: JSON_PARSER + do + i := 42 + -- Eiffel value -> JSON value -> JSON representation + create jn.make_integer (i) + assert ("jn.representation.same_string (%"42%")", jn.representation.same_string ("42")) + -- Eiffel value -> JSON value -> JSON representation with factory + if attached {JSON_NUMBER} json.value (i) as l_jn then + assert ("l_jn.representation.same_string (%"42%")", jn.representation.same_string ("42")) + else + assert ("json.value (i) is a JSON_NUMBER", False) + end + + -- JSON representation-> JSON value -> Eiffel value + -- Note: The JSON_FACTORY will return the smallest INTEGER_* object + -- that can represent the value of the JSON number, in this case + -- we know it is INTEGER_8 since the value is 42 + jrep := "42" + create parser.make_parser (jrep) + if attached {JSON_NUMBER} parser.parse as l_jn then + if attached {INTEGER_8} json.object (jn, Void) as l_i8 then + assert ("l_i8 = 42", l_i8 = 42) + else + assert ("json.object (jn, Void) is a INTEGER_8", False) + end + else + assert ("parser.parse is a JSON_NUMBER", False) + end + end + + test_json_number_and_integer_8 + local + i8: INTEGER_8 + jn: JSON_NUMBER + jrep: STRING + parser: JSON_PARSER + do + i8 := 42 + -- Eiffel value -> JSON value -> JSON representation + create jn.make_integer (i8) + assert ("jn.representation.same_string (%"42%")", jn.representation.same_string ("42")) + -- Eiffel value -> JSON value -> JSON representation with factory + if attached {JSON_NUMBER} json.value (i8) as l_jn then + assert ("l_jn.representation.same_string (%"42%")", jn.representation.same_string ("42")) + else + assert ("json.value (i8) is a JSON_NUMBER", False) + end + + -- JSON representation -> JSON value -> Eiffel value + -- Note: The JSON_FACTORY will return the smallest INTEGER_* object + -- that can represent the value of the JSON number, in this case + -- we know it is INTEGER_8 since the value is 42 + jrep := "42" + create parser.make_parser (jrep) + if attached {JSON_NUMBER} parser.parse as l_jn then + if attached {INTEGER_8} json.object (jn, Void) as l_i8 then + assert ("l_i8 = 42", l_i8 = 42) + else + assert ("json.object (jn, Void) is a INTEGER_8", False) + end + else + assert ("parser.parse is a JSON_NUMBER", False) + end + end + + test_json_number_and_integer_16 + local + i16: INTEGER_16 + jn: JSON_NUMBER + jrep: STRING + parser: JSON_PARSER + do + i16 := 300 + -- Eiffel value -> JSON value -> JSON representation + create jn.make_integer (i16) + assert ("jn.representation.same_string (%"300%")", jn.representation.same_string ("300")) + -- Eiffel value -> JSON with factory + if attached {JSON_NUMBER} json.value (i16) as l_jn then + assert ("l_jn.representation.same_string (%"300%")", l_jn.representation.same_string ("300")) + else + assert ("json.value (i16) is a JSON_NUMBER", False) + end + + -- JSON representation -> JSON value -> Eiffel value + -- Note: The JSON_FACTORY will return the smallest INTEGER_* object + -- that can represent the value of the JSON number, in this case + -- we know it is INTEGER_16 since the value is 300 + jrep := "300" + create parser.make_parser (jrep) + if attached {JSON_NUMBER} parser.parse as l_jn then + if attached {INTEGER_16} json.object (jn, Void) as l_i16 then + assert ("l_i16 = 300", l_i16 = 300) + else + assert ("json.object (jn, Void) is a INTEGER_16", False) + end + else + assert ("parser.parse is a JSON_NUMBER", False) + end + end + + test_json_number_and_integer_32 + local + i32: INTEGER_32 + jn: JSON_NUMBER + jrep: STRING + parser: JSON_PARSER + do + i32 := 100000 + -- Eiffel value -> JSON representation -> JSON value + create jn.make_integer (i32) + assert ("jn.representation.same_string (%"100000%")", jn.representation.same_string ("100000")) + -- Eiffel value -> JSON representation -> JSON value with factory + if attached {JSON_NUMBER} json.value (i32) as l_jn then + assert ("l_jn.representation.same_string (%"100000%")", l_jn.representation.same_string ("100000")) + else + assert ("json.value (i32) is a JSON_NUMBER", False) + end + + -- JSON representation -> JSON value -> Eiffel value + -- Note: The JSON_FACTORY will return the smallest INTEGER_* object + -- that can represent the value of the JSON number, in this case + -- we know it is INTEGER_32 since the value is 100000 + jrep := "100000" + create parser.make_parser (jrep) + if attached {JSON_NUMBER} parser.parse as l_jn then + if attached {INTEGER_32} json.object (jn, Void) as l_i32 then + assert ("l_i32 = 100000", l_i32 = 100000) + else + assert ("json.object (jn, Void) is a INTEGER_32", False) + end + else + assert ("parser.parse is a JSON_NUMBER", False) + end + end + + test_json_number_and_integer_64 + local + i64: INTEGER_64 + jn: JSON_NUMBER + jrep: STRING + parser: JSON_PARSER + do + i64 := 42949672960 + -- Eiffel value -> JSON value -> JSON representation + create jn.make_integer (i64) + assert ("jn.representation.same_string (%"42949672960%")", jn.representation.same_string ("42949672960")) + -- Eiffel value -> JSON value -> JSON representation with factory + if attached {JSON_NUMBER} json.value (i64) as l_jn then + assert ("l_jn.representation.same_string (%"42949672960%")", l_jn.representation.same_string ("42949672960")) + else + assert ("json.value (i64) is a JSON_NUMBER", False) + end + + -- JSON representation -> JSON value -> Eiffel value + -- Note: The JSON_FACTORY will return the smallest INTEGER_* object + -- that can represent the value of the JSON number, in this case + -- we know it is INTEGER_32 since the value is 42949672960 + jrep := "42949672960" + create parser.make_parser (jrep) + if attached {JSON_NUMBER} parser.parse as l_jn then + if attached {INTEGER_64} json.object (jn, Void) as l_i64 then + assert ("l_i64 = 42949672960", l_i64 = 42949672960) + else + assert ("json.object (jn, Void) is a INTEGER_64", False) + end + else + assert ("parser.parse is a JSON_NUMBER", False) + end + end + + test_json_number_and_natural_8 + local + n8: NATURAL_8 + jn: JSON_NUMBER + jrep: STRING + parser: JSON_PARSER + do + n8 := 200 + -- Eiffel value -> JSON value -> JSON representation + create jn.make_natural (n8) + assert ("jn.representation.same_string (%"200%")", jn.representation.same_string ("200")) + -- Eiffel value -> JSON value -> JSON representation with factory + if attached {JSON_NUMBER} json.value (n8) as l_jn then + assert ("l_jn.representation.same_string (%"200%")", l_jn.representation.same_string ("200")) + else + assert ("json.value (n8) is a JSON_NUMBER}", False) + end + + -- JSON representation -> JSON value -> Eiffel value + -- Note: The JSON_FACTORY will return the smallest INTEGER_* object + -- that can represent the value of the JSON number, in this case + -- we know it is INTEGER_16 since the value is 200 + jrep := "200" + create parser.make_parser (jrep) + if attached {JSON_NUMBER} parser.parse as l_jn then + if attached {INTEGER_16} json.object (jn, Void) as i16 then + assert ("i16 = 200", i16 = 200) + else + assert ("json.object (jn, Void) is an INTEGER_16", False) + end + else + assert ("parser.parse is a JSON_NUMBER", False) + end + end + + test_json_number_and_natural_16 + local + n16: NATURAL_16 + jn: JSON_NUMBER + jrep: STRING + parser: JSON_PARSER + do + n16 := 32768 + -- Eiffel value -> JSON value -> JSON representation + create jn.make_natural (n16) + assert ("jn.representation.same_string (%"32768%")", jn.representation.same_string ("32768")) + -- Eiffel value -> JSON value -> JSON representation with factory + if attached {JSON_NUMBER} json.value (n16) as l_jn then + assert ("l_jn.representation.same_string (%"32768%")", l_jn.representation.same_string ("32768")) + else + assert ("json.value (n16) is a JSON_NUMBER", False) + end + + -- JSON representation -> JSON value -> Eiffel value + -- Note: The JSON_FACTORY will return the smallest INTEGER_* object + -- that can represent the value of the JSON number, in this case + -- we know it is INTEGER_32 since the value is 32768 + jrep := "32768" + create parser.make_parser (jrep) + if attached {JSON_NUMBER} parser.parse as l_jn then + if attached {INTEGER_32} json.object (jn, Void) as i32 then + assert ("i32 = 32768", i32 = 32768) + else + assert ("json.object (jn, Void) is a INTEGER_32", False) + end + else + assert ("parser.parse is a JSON_NUMBER", False) + end + end + + test_json_number_and_natural_32 + local + n32: NATURAL_32 + jn: JSON_NUMBER + jrep: STRING + parser: JSON_PARSER + do + n32 := 2147483648 + -- Eiffel value -> JSON value -> JSON representation + create jn.make_natural (n32) + assert ("jn.representation.same_string (%"2147483648%")", jn.representation.same_string ("2147483648")) + -- Eiffel value -> JSON value -> JSON representation with factory + if attached json.value (n32) as l_jn then + assert ("l_jn.representation.same_string (%"2147483648%")", l_jn.representation.same_string ("2147483648")) + else + assert ("json.value (n32) is a JSON_NUMBER", False) + end + + -- JSON representation -> JSON value -> Eiffel value + -- Note: The JSON_FACTORY will return the smallest INTEGER_* object + -- that can represent the value of the JSON number, in this case + -- we know it is INTEGER_64 since the value is 2147483648 + jrep := "2147483648" + create parser.make_parser (jrep) + if attached {JSON_NUMBER} parser.parse as l_jn then + if attached {INTEGER_64} json.object (jn, Void) as i64 then + assert ("i64 = 2147483648", i64 = 2147483648) + else + assert ("json.object (jn, Void) is a INTEGER_64", False) + end + else + assert ("parser.parse is a JSON_NUMBER", False) + end + end + + test_json_number_and_large_integers + local + jrep: STRING + n64: NATURAL_64 + jn: JSON_NUMBER + parser: JSON_PARSER + do + n64 := 9223372036854775808 + -- Eiffel value -> JSON value -> JSON representation + create jn.make_natural (n64) + assert ("jn.representation.same_string (%"9223372036854775808%")", jn.representation.same_string ("9223372036854775808")) + -- Eiffel value -> JSON value -> JSON representation with factory + if attached {JSON_NUMBER} json.value (n64) as l_jn then + assert ("l_jn.representation.same_string (%"9223372036854775808%")", l_jn.representation.same_string ("9223372036854775808")) + else + assert ("json.value (n64) is a JSON_NUMBER", False) + end + + -- JSON representation -> JSON value -> Eiffel value + -- Note: The JSON_FACTORY will return the smallest INTEGER_* object + -- that can represent the value of the JSON number, in this case + -- we know it is INTEGER_32 since the value is 42949672960 + jrep := "9223372036854775808" -- 1 higher than largest positive number that can be represented by INTEGER 64 + create parser.make_parser (jrep) + if attached {JSON_NUMBER} parser.parse as l_jn then + if attached {NATURAL_64} json.object (jn, Void) as l_n64 then + assert ("l_n64 = 9223372036854775808", l_n64 = 9223372036854775808) + else + assert ("json.object (jn, Void) is a NATURAL_64", False) + end + else + assert ("parser.parse is a JSON_NUMBER", False) + end + end + + test_json_number_and_eiffel_real + local + r: REAL + jn: JSON_NUMBER + jrep: STRING + parser: JSON_PARSER + do + r := 3.14 + -- Eiffel value -> JSON value -> JSON representation + create jn.make_real (r) + assert ("jn.representation.same_string (%"3.1400001049041748%")", jn.representation.same_string ("3.1400001049041748")) + -- Eiffel value -> JSON value -> JSON representation with factory + if attached {JSON_NUMBER} json.value (r) as l_jn then + assert ("l_jn.representation.same_string (%"3.1400001049041748%")", l_jn.representation.same_string ("3.1400001049041748")) + else + assert ("json.value (r) is a JSON_NUMBER", False) + end + + -- JSON representation -> JSON value -> Eiffel value + -- Note: The JSON_FACTORY will always return a REAL_64 if the value + -- of the JSON number is a floating point number + jrep := "3.14" + create parser.make_parser (jrep) + if attached {JSON_NUMBER} parser.parse as l_jn then + if attached {REAL_64} json.object (jn, Void) as r64 then + assert ("3.14 <= r64 and r64 <= 3.141", 3.14 <= r64 and r64 <= 3.141) + else + assert ("json.object (jn, Void) is a REAL_64", False) + end + else + assert ("parser.parse is a JSON_NUMBER", False) + end + end + + test_json_number_and_eiffel_real_32 + local + r32: REAL_32 + jn: JSON_NUMBER + jrep: STRING + parser: JSON_PARSER + do + r32 := 3.14 + -- Eiffel value -> JSON value -> JSON representation + create jn.make_real (r32) + assert ("jn.representation.same_string (%"3.1400001049041748%")", jn.representation.same_string ("3.1400001049041748")) + -- Eiffel value -> JSON value -> JSON representation with factory + if attached {JSON_NUMBER} json.value (r32) as l_jn then + assert ("l_jn.representation.same_string (%"3.1400001049041748%")", l_jn.representation.same_string ("3.1400001049041748")) + else + assert ("json.value (r32) is a JSON_NUMBER", False) + end + + -- JSON representation -> JSON value -> Eiffel value + jrep := "3.1400001049041748" + create parser.make_parser (jrep) + if attached {JSON_NUMBER} parser.parse as l_jn then + if attached {REAL_64} json.object (l_jn, Void) as r64 then + assert ("r64 = 3.1400001049041748", r64 = 3.1400001049041748) + else + assert ("json.object (l_jn, Void) is a REAL_64", False) + end + else + assert ("parser.parse is a JSON_NUMBER", False) + end + end + + test_json_number_and_eiffel_real_64 + local + r64: REAL_64 + jn: JSON_NUMBER + jrep: STRING + parser: JSON_PARSER + do + r64 := 3.1415926535897931 + -- Eiffel value -> JSON value -> JSON representation + create jn.make_real (r64) + assert ("jn.representation.same_string (%"3.1415926535897931%")", jn.representation.same_string ("3.1415926535897931")) + + -- Eiffel value -> JSON value -> JSON representation with factory + if attached {JSON_NUMBER} json.value (r64) as l_jn then + assert ("l_jn.representation.same_string (%"3.1415926535897931%")", l_jn.representation.same_string ("3.1415926535897931")) + else + assert ("json.value (r64) is a JSON_NUMBER", False) + end + + -- JSON representation -> JSON value -> Eiffel value + jrep := "3.1415926535897931" + create parser.make_parser (jrep) + if attached {JSON_NUMBER} parser.parse as l_jn then + if attached {REAL_64} json.object (jn, Void) as l_r64 then + assert ("l_r64 = 3.1415926535897931", l_r64 = 3.1415926535897931) + else + assert ("json.object (jn, Void) is a REAL_64", False) + end + else + assert ("parser.parse is a JSON_NUMBER", False) + end + end + + test_json_boolean + local + parser: JSON_PARSER + jb: JSON_BOOLEAN + b: BOOLEAN + do + -- Eiffel value -> JSON value -> JSON representation + b := True + create jb.make_boolean (b) + assert ("jb.representation.is_equal (%"true%")", jb.representation.is_equal ("true")) + -- Eiffel value -> JSON value -> JSON representation with factory + if attached {JSON_BOOLEAN} json.value (b) as l_jb then + assert ("l_jb.representation.same_string (%"true%")", l_jb.representation.same_string ("true")) + else + assert ("l_jb /= Void", False) + end + + -- JSON representation -> JSON value -> Eiffel value + create parser.make_parser ("true") + if attached {JSON_BOOLEAN} parser.parse as l_jb then + if attached {BOOLEAN} json.object (l_jb, Void) as l_b then + assert ("l_b = True", l_b = True) + else + assert ("json.object (l_jb, Void) is BOOLEAN", False) + end + else + assert ("parser.parse is a JSON_BOOLEAN", False) + end + + -- Eiffel value -> JSON value -> JSON representation + b := False + create jb.make_boolean (b) + assert ("jb.representation.same_string (%"false%")", jb.representation.same_string ("false")) + -- Eiffel value -> JSON value -> JSON representation with factory + if attached {JSON_BOOLEAN} json.value (b) as l_jb then + assert ("l_jb.representation.same_string (%"false%")", l_jb.representation.same_string ("false")) + else + assert ("json.value (b) is a JSON_BOOLEAN", False) + end + + -- JSON representation -> JSON value -> Eiffel value + create parser.make_parser ("false") + if attached {JSON_BOOLEAN} parser.parse as l_jb then + if attached {BOOLEAN} json.object (l_jb, Void) as l_b then + assert ("l_b = False", l_b = False) + else + assert ("json.object (l_jb, Void) is a BOOLEAN", False) + end + else + assert ("parser.parse is a JSON_BOOLEAN", False) + end + end + + test_json_null + local + a: detachable ANY + dummy_object: STRING + jn: detachable JSON_NULL + jrep: STRING + parser: JSON_PARSER + do + -- Eiffel value -> JSON value -> JSON representation + create jn + assert ("jn /= Void", jn /= Void) + assert ("jn.representation.is_equal (%"%"null%"%")", jn.representation.is_equal ("null")) + -- Eiffel value -> JSON value -> JSON representation with factory + jn ?= json.value (Void) + assert ("jn /= Void", jn /= Void) + if attached jn as l_jn then + assert ("jn.representation.is_equal (%"null%")", l_jn.representation.is_equal ("null")) + end + + -- JSON representation -> JSON value -> Eiffel value + jrep := "null" + create parser.make_parser (jrep) + jn := Void + jn ?= parser.parse + assert ("jn /= Void", jn /= Void) + create dummy_object.make_empty + a := dummy_object + a ?= json.object (jn, Void) + assert ("a = Void", a = Void) + end + + test_json_string_and_character + local + c: CHARACTER + js: detachable JSON_STRING + jrep: STRING + parser: JSON_PARSER + do + c := 'a' + -- Eiffel value -> JSON value -> JSON representation + create js.make_json (c.out) + assert ("js /= Void", js /= Void) + assert ("js.representation.is_equal (%"%"a%"%")", js.representation.is_equal ("%"a%"")) + -- Eiffel value -> JSON value -> JSON representation with factory + js ?= json.value (c) + assert ("js /= Void", js /= Void) + if attached js as l_js then + assert ("js.representation.is_equal (%"%"a%"%")", l_js.representation.is_equal ("%"a%"")) + end + + -- JSON representation -> JSON value -> Eiffel value + jrep := "%"a%"" + create parser.make_parser (jrep) + js := Void + js ?= parser.parse + assert ("js /= Void", js /= Void) + if attached {STRING_32} json.object (js, Void) as ucs then + assert ("ucs.string.is_equal (%"a%")", ucs.string.is_equal ("a")) + end + + end + + test_json_string_and_string + local + s: STRING + js: detachable JSON_STRING + jrep: STRING + parser: JSON_PARSER + do + s := "foobar" + -- Eiffel value -> JSON value -> JSON representation + create js.make_json (s) + assert ("js /= Void", js /= Void) + assert ("js.representation.is_equal (%"%"foobar%"%")", js.representation.is_equal ("%"foobar%"")) + -- Eiffel value -> JSON value -> JSON representation with factory + js ?= json.value (s) + assert ("js /= Void", js /= Void) + if attached js as l_js then + assert ("js.representation.is_equal (%"%"foobar%"%")", js.representation.is_equal ("%"foobar%"")) + end + + -- JSON representation -> JSON value -> Eiffel value + jrep := "%"foobar%"" + create parser.make_parser (jrep) + js := Void + js ?= parser.parse + assert ("js /= Void", js /= Void) + if attached {STRING_32} json.object (js, Void) as l_ucs then + assert ("ucs.string.is_equal (%"foobar%")", l_ucs.string.is_equal ("foobar")) + end + end + + test_json_string_and_uc_string + local + js: detachable JSON_STRING + ucs: detachable STRING_32 + jrep: STRING + parser: JSON_PARSER + do + create ucs.make_from_string ("foobar") + -- Eiffel value -> JSON value -> JSON representation + create js.make_json (ucs) + assert ("js /= Void", js /= Void) + assert ("js.representation.is_equal (%"%"foobar%"%")", js.representation.is_equal ("%"foobar%"")) + -- Eiffel value -> JSON value -> JSON representation with factory + js ?= json.value (ucs) + assert ("js /= Void", js /= Void) + if attached js as l_js then + assert ("js.representation.is_equal (%"%"foobar%"%")", l_js.representation.is_equal ("%"foobar%"")) + end + + -- JSON representation -> JSON value -> Eiffel value + jrep := "%"foobar%"" + create parser.make_parser (jrep) + js := Void + js ?= parser.parse + assert ("js /= Void", js /= Void) + ucs := Void + ucs ?= json.object (js, Void) + if attached ucs as l_ucs then + assert ("ucs.string.is_equal (%"foobar%")", l_ucs.string.is_equal ("foobar")) + end + end + + test_json_string_and_special_characters + local + js: detachable JSON_STRING + s: detachable STRING_8 + ucs: detachable STRING_32 + jrep: STRING + parser: JSON_PARSER + do + create s.make_from_string ("foo\bar") + create js.make_json (s) + + assert ("js.representation.same_string (%"%"foo\\bar%"%")", js.representation.same_string ("%"foo\\bar%"")) + + -- Eiffel value -> JSON value -> JSON representation with factory + js ?= json.value (s) + assert ("js /= Void", js /= Void) + if js /= Void then + assert ("js.representation.is_equal (%"%"foobar%"%")", js.representation.same_string ("%"foo\\bar%"")) + end + + -- JSON representation -> JSON value -> Eiffel value + jrep := "%"foo\\bar%"" + create parser.make_parser (jrep) + js ?= parser.parse + assert ("js /= Void", js /= Void) + ucs ?= json.object (js, Void) + if ucs /= Void then + assert ("ucs.same_string (%"foo\bar%")", ucs.same_string ("foo\bar")) + end + + 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")) + end + + create js.make_json_from_string_32 ({STRING_32}"%/20320/%/22909/") + assert ("escaping unicode string32 %"%%/20320/%%/22909/%" %"\u4F60\u597D%"", js.item.same_string ("\u4F60\u597D")) + + jrep := "%"\u4F60\u597D%"" --| Ni hao + create parser.make_parser (jrep) + if attached {JSON_STRING} parser.parse as jstring then + assert ("same unicode string32 %"%%/20320/%%/22909/%"", jstring.unescaped_string_32.same_string ({STRING_32}"%/20320/%/22909/")) + end + end + + test_json_array + local + ll: LINKED_LIST [INTEGER_8] + ll2: detachable LINKED_LIST [detachable ANY] + ja: detachable JSON_ARRAY + jn: JSON_NUMBER + jrep: STRING + parser: JSON_PARSER + do + -- Eiffel value -> JSON value -> JSON representation + create ll.make + ll.extend (0) + ll.extend (1) + ll.extend (1) + ll.extend (2) + ll.extend (3) + ll.extend (5) + -- Note: Currently there is no simple way of creating a JSON_ARRAY + -- from an LINKED_LIST. + create ja.make_array + from + ll.start + until + ll.after + loop + create jn.make_integer (ll.item) + ja.add (jn) + ll.forth + end + assert ("ja /= Void", ja /= Void) + assert ("ja.representation.is_equal (%"[0,1,1,2,3,5]%")", ja.representation.is_equal ("[0,1,1,2,3,5]")) + -- Eiffel value -> JSON value -> JSON representation with factory + ja := Void + ja ?= json.value (ll) + assert ("ja /= Void", ja /= Void) + if attached ja as l_ja then + assert ("ja.representation.is_equal (%"[0,1,1,2,3,5]%")", l_ja.representation.is_equal ("[0,1,1,2,3,5]")) + end + + -- JSON representation -> JSON value -> Eiffel value + -- Note: The JSON_FACTORY will return the smallest INTEGER_* object + -- that can represent the value of the JSON number, in this case + -- it means we will get an LINKED_LIST [ANY] containing the INTEGER_8 + -- values 0, 1, 1, 2, 3, 5 + jrep := "[0,1,1,2,3,5]" + create parser.make_parser (jrep) + ja := Void + ja ?= parser.parse + assert ("ja /= Void", ja /= Void) + ll2 ?= json.object (ja, Void) + assert ("ll2 /= Void", ll2 /= Void) + --ll.compare_objects + --ll2.compare_objects + if attached ll2 as l_ll2 then + assert ("ll2.is_equal (ll)", l_ll2.is_equal (ll)) + end + + end + + test_json_object + local + t, t2: detachable HASH_TABLE [detachable ANY, STRING_GENERAL] + i: INTEGER + ucs_key, ucs: STRING_32 + a: ARRAY [INTEGER] + jo: detachable JSON_OBJECT + jn: JSON_NUMBER + js_key, js: JSON_STRING + ja: JSON_ARRAY + jrep: STRING + parser: JSON_PARSER + do + -- Eiffel value -> JSON value -> JSON representation + -- Note: Currently there is now way of creating a JSON_OBJECT from + -- a HASH_TABLE, so we do it manually. + -- t = {"name": "foobar", "size": 42, "contents", [0, 1, 1, 2, 3, 5]} + create jo.make + create js_key.make_json ("name") + create js.make_json ("foobar") + jo.put (js, js_key) + create js_key.make_json ("size") + create jn.make_integer (42) + jo.put (jn, js_key) + create js_key.make_json ("contents") + create ja.make_array + create jn.make_integer (0) + ja.add (jn) + create jn.make_integer (1) + ja.add (jn) + create jn.make_integer (1) + ja.add (jn) + create jn.make_integer (2) + ja.add (jn) + create jn.make_integer (3) + ja.add (jn) + create jn.make_integer (5) + ja.add (jn) + jo.put (ja, js_key) + assert ("jo /= Void", jo /= Void) + assert ("jo.representation.is_equal (%"{%"name%":%"foobar%",%"size%":42,%"contents%":[0,1,1,2,3,5]}%")", jo.representation.is_equal ("{%"name%":%"foobar%",%"size%":42,%"contents%":[0,1,1,2,3,5]}")) + -- Eiffel value -> JSON value -> JSON representation with factory + create t.make (3) + create ucs_key.make_from_string ("name") + create ucs.make_from_string ("foobar") + t.put (ucs, ucs_key) + create ucs_key.make_from_string ("size") + i := 42 + t.put (i, ucs_key) + create ucs_key.make_from_string ("contents") + a := <<0, 1, 1, 2, 3, 5>> + t.put (a, ucs_key) + jo := Void + jo ?= json.value (t) + assert ("jo /= Void", jo /= Void) + if attached jo as l_jo then + assert ("jo.representation.is_equal (%"{%"name%":%"foobar%",%"size%":42,%"contents%":[0,1,1,2,3,5]}%")", l_jo.representation.is_equal ("{%"name%":%"foobar%",%"size%":42,%"contents%":[0,1,1,2,3,5]}")) + end + -- JSON representation -> JSON value -> Eiffel value -> JSON value -> JSON representation + jrep := "{%"name%":%"foobar%",%"size%":42,%"contents%":[0,1,1,2,3,5]}" + create parser.make_parser (jrep) + jo := Void + jo ?= parser.parse + assert ("jo /= Void", jo /= Void) + t2 ?= json.object (jo, Void) + assert ("t2 /= Void", t2 /= Void) + jo ?= json.value (t2) + assert ("jo /= Void", jo /= Void) + if attached jo as l_jo then + assert ("jrep.is_equal (jo.representation)", jrep.is_equal (jo.representation)) + end + + end + + test_json_object_hash_code + local + ht: HASH_TABLE [ANY, JSON_VALUE] + jo: JSON_OBJECT + do + create ht.make (1) + create jo.make + ht.force ("", jo) + assert ("ht.has_key (jo)", ht.has_key (jo)) + end + + test_json_failed_json_conversion + -- Test converting an Eiffel object to JSON that is based on a class + -- for which no JSON converter has been registered. + local + gv: OPERATING_ENVIRONMENT + jv: detachable JSON_VALUE + exception: BOOLEAN + do + if not exception then + create gv + jv := json.value (gv) + else + assert ("exceptions.is_developer_exception", json.is_developer_exception) +-- assert ("exceptions.is_developer_exception_of_name", json.is_developer_exception_of_name ("eJSON exception: Failed to convert Eiffel object to a JSON_VALUE: OPERATING_ENVIRONMENT")) + end + rescue + exception := True + retry + end + + test_json_failed_eiffel_conversion + -- Test converting from a JSON value to an Eiffel object based on a + -- class for which no JSON converter has been registered. + local + gv : detachable OPERATING_ENVIRONMENT + jo: JSON_OBJECT + exception: BOOLEAN + do + if not exception then + create jo.make + gv ?= json.object (jo, "OPERATING_ENVIRONMENT") + else + assert ("exceptions.is_developer_exception", json.is_developer_exception) +-- assert ("exceptions.is_developer_exception_of_name", json.is_developer_exception_of_name ("eJSON exception: Failed to convert JSON_VALUE to an Eiffel object: JSON_OBJECT -> OPERATING_ENVIRONMENT")) + + end + rescue + exception := True + retry + end + +end -- class TEST_JSON_CORE diff --git a/contrib/library/text/parser/json/test/autotest/test_suite/test_json_suite.e b/contrib/library/text/parser/json/test/autotest/test_suite/test_json_suite.e index 812fb391..c2a66b71 100644 --- a/contrib/library/text/parser/json/test/autotest/test_suite/test_json_suite.e +++ b/contrib/library/text/parser/json/test/autotest/test_suite/test_json_suite.e @@ -1,514 +1,544 @@ -note - description: "[ - Eiffel tests that can be executed by testing tool. - ]" - author: "EiffelStudio test wizard" - date: "$Date$" - revision: "$Revision$" - testing: "type/manual" - -class - TEST_JSON_SUITE - -inherit - EQA_TEST_SET - redefine - on_prepare - end - -feature {NONE} -- Events - - on_prepare - -- - do - create file_reader - end - -feature -- Tests Pass - - test_json_pass1 - -- - local - parse_json: like new_json_parser - do - if attached json_file_from ("pass1.json") as json_file then - parse_json := new_json_parser (json_file) - json_value := parse_json.parse_json - assert ("pass1.json",parse_json.is_parsed = True) - end - end - - test_json_pass2 - -- - local - parse_json: like new_json_parser - do - if attached json_file_from ("pass2.json") as json_file then - parse_json := new_json_parser (json_file) - json_value := parse_json.parse_json - assert ("pass2.json",parse_json.is_parsed = True) - end - end - - test_json_pass3 - -- - local - parse_json: like new_json_parser - do - if attached json_file_from ("pass3.json") as json_file then - parse_json := new_json_parser (json_file) - json_value := parse_json.parse_json - assert ("pass3.json",parse_json.is_parsed = True) - end - end - -feature -- Tests Failures - test_json_fail1 - -- - local - parse_json: like new_json_parser - do - if attached json_file_from ("fail1.json") as json_file then - parse_json := new_json_parser (json_file) - json_value := parse_json.parse_json - assert ("fail1.json",parse_json.is_parsed = False) - end - end - - test_json_fail2 - -- - local - parse_json: like new_json_parser - do - if attached json_file_from ("fail2.json") as json_file then - parse_json := new_json_parser (json_file) - json_value := parse_json.parse_json - assert ("fail2.json",parse_json.is_parsed = False) - end - end - - test_json_fail3 - -- - local - parse_json: like new_json_parser - do - if attached json_file_from ("fail3.json") as json_file then - parse_json := new_json_parser (json_file) - json_value := parse_json.parse_json - assert ("fail3.json",parse_json.is_parsed = False) - end - end - - test_json_fail4 - -- - local - parse_json: like new_json_parser - do - if attached json_file_from ("fail4.json") as json_file then - parse_json := new_json_parser (json_file) - json_value := parse_json.parse_json - assert ("fail4.json",parse_json.is_parsed = False) - end - end - - test_json_fail5 - -- - local - parse_json: like new_json_parser - do - if attached json_file_from ("fail5.json") as json_file then - parse_json := new_json_parser (json_file) - json_value := parse_json.parse_json - assert ("fail5.json",parse_json.is_parsed = False) - end - end - - - test_json_fail6 - -- - local - parse_json: like new_json_parser - do - if attached json_file_from ("fail6.json") as json_file then - parse_json := new_json_parser (json_file) - json_value := parse_json.parse_json - assert ("fail6.json",parse_json.is_parsed = False ) - end - end - - test_json_fail7 - -- - local - parse_json: like new_json_parser - do - if attached json_file_from ("fail7.json") as json_file then - parse_json := new_json_parser (json_file) - json_value := parse_json.parse_json - assert ("fail7.json",parse_json.is_parsed = False) - end - end - - test_json_fail8 - -- - local - parse_json: like new_json_parser - do - if attached json_file_from ("fail8.json") as json_file then - parse_json := new_json_parser (json_file) - json_value := parse_json.parse_json - assert ("fail8.json",parse_json.is_parsed = False ) - end - end - - - test_json_fail9 - -- - local - parse_json: like new_json_parser - do - if attached json_file_from ("fail9.json") as json_file then - parse_json := new_json_parser (json_file) - json_value := parse_json.parse_json - assert ("fail9.json",parse_json.is_parsed = False) - end - end - - - test_json_fail10 - -- - local - parse_json: like new_json_parser - do - if attached json_file_from ("fail10.json") as json_file then - parse_json := new_json_parser (json_file) - json_value := parse_json.parse_json - assert ("fail10.json",parse_json.is_parsed = False) - end - end - - test_json_fail11 - -- - local - parse_json: like new_json_parser - do - if attached json_file_from ("fail11.json") as json_file then - parse_json := new_json_parser (json_file) - json_value := parse_json.parse_json - assert ("fail11.json",parse_json.is_parsed = False) - end - end - - test_json_fail12 - -- - local - parse_json: like new_json_parser - do - if attached json_file_from ("fail12.json") as json_file then - parse_json := new_json_parser (json_file) - json_value := parse_json.parse_json - assert ("fail12.json",parse_json.is_parsed = False) - end - end - - test_json_fail13 - -- - local - parse_json: like new_json_parser - do - if attached json_file_from ("fail13.json") as json_file then - parse_json := new_json_parser (json_file) - json_value := parse_json.parse_json - assert ("fail13.json",parse_json.is_parsed = False) - end - end - - test_json_fail14 - -- - local - parse_json: like new_json_parser - do - if attached json_file_from ("fail14.json") as json_file then - parse_json := new_json_parser (json_file) - json_value := parse_json.parse_json - assert ("fail14.json",parse_json.is_parsed = False) - end - end - - test_json_fail15 - -- - local - parse_json: like new_json_parser - do - if attached json_file_from ("fail15.json") as json_file then - parse_json := new_json_parser (json_file) - json_value := parse_json.parse_json - assert ("fail15.json",parse_json.is_parsed = False) - end - end - - test_json_fail16 - -- - local - parse_json: like new_json_parser - do - if attached json_file_from ("fail16.json") as json_file then - parse_json := new_json_parser (json_file) - json_value := parse_json.parse_json - assert ("fail16.json",parse_json.is_parsed = False) - end - end - - test_json_fail17 - -- - local - parse_json: like new_json_parser - do - if attached json_file_from ("fail17.json") as json_file then - parse_json := new_json_parser (json_file) - json_value := parse_json.parse_json - assert ("fail17.json",parse_json.is_parsed = False) - end - end - - test_json_fail18 - -- - local - parse_json: like new_json_parser - do - if attached json_file_from ("fail18.json") as json_file then - parse_json := new_json_parser (json_file) - json_value := parse_json.parse_json - assert ("fail18.json",parse_json.is_parsed = True) - end - end - - test_json_fail19 - -- - local - parse_json: like new_json_parser - do - if attached json_file_from ("fail19.json") as json_file then - parse_json := new_json_parser (json_file) - json_value := parse_json.parse_json - assert ("fail19.json",parse_json.is_parsed = False) - end - end - - test_json_fail20 - -- - local - parse_json: like new_json_parser - do - if attached json_file_from ("fail20.json") as json_file then - parse_json := new_json_parser (json_file) - json_value := parse_json.parse_json - assert ("fail20.json",parse_json.is_parsed = False) - end - end - - test_json_fail21 - -- - local - parse_json: like new_json_parser - do - if attached json_file_from ("fail21.json") as json_file then - parse_json := new_json_parser (json_file) - json_value := parse_json.parse_json - assert ("fail21.json",parse_json.is_parsed = False) - end - end - - - test_json_fail22 - -- - local - parse_json: like new_json_parser - do - if attached json_file_from ("fail22.json") as json_file then - parse_json := new_json_parser (json_file) - json_value := parse_json.parse_json - assert ("fail22.json",parse_json.is_parsed = False) - end - end - - test_json_fail23 - -- - local - parse_json: like new_json_parser - do - if attached json_file_from ("fail23.json") as json_file then - parse_json := new_json_parser (json_file) - json_value := parse_json.parse_json - assert ("fail23.json",parse_json.is_parsed = False) - end - end - - test_json_fail24 - -- - local - parse_json: like new_json_parser - do - if attached json_file_from ("fail24.json") as json_file then - parse_json := new_json_parser (json_file) - json_value := parse_json.parse_json - assert ("fail24.json",parse_json.is_parsed = False) - end - end - - test_json_fail25 - -- - local - parse_json: like new_json_parser - do - if attached json_file_from ("fail25.json") as json_file then - parse_json := new_json_parser (json_file) - json_value := parse_json.parse_json - assert ("fail25.json",parse_json.is_parsed = False) - end - end - - - test_json_fail26 - -- - local - parse_json: like new_json_parser - do - if attached json_file_from ("fail26.json") as json_file then - parse_json := new_json_parser (json_file) - json_value := parse_json.parse_json - assert ("fail26.json",parse_json.is_parsed = False) - end - end - - - test_json_fail27 - -- - local - parse_json: like new_json_parser - do - if attached json_file_from ("fail27.json") as json_file then - parse_json := new_json_parser (json_file) - json_value := parse_json.parse_json - assert ("fail27.json",parse_json.is_parsed = False) - end - end - - - test_json_fail28 - -- - local - parse_json: like new_json_parser - do - if attached json_file_from ("fail28.json") as json_file then - parse_json := new_json_parser (json_file) - json_value := parse_json.parse_json - assert ("fail28.json",parse_json.is_parsed = False) - end - end - - - test_json_fail29 - -- - local - parse_json: like new_json_parser - do - if attached json_file_from ("fail29.json") as json_file then - parse_json := new_json_parser (json_file) - json_value := parse_json.parse_json - assert ("fail29.json",parse_json.is_parsed = False ) - end - end - - - test_json_fail30 - -- - local - parse_json: like new_json_parser - do - if attached json_file_from ("fail30.json") as json_file then - parse_json := new_json_parser (json_file) - json_value := parse_json.parse_json - assert ("fail30.json",parse_json.is_parsed = False) - end - end - - test_json_fail31 - -- - local - parse_json: like new_json_parser - do - if attached json_file_from ("fail31.json") as json_file then - parse_json := new_json_parser (json_file) - json_value := parse_json.parse_json - assert ("fail31.json",parse_json.is_parsed = False) - end - end - - test_json_fail32 - -- - local - parse_json: like new_json_parser - do - if attached json_file_from ("fail32.json") as json_file then - parse_json := new_json_parser (json_file) - json_value := parse_json.parse_json - assert ("fail32.json",parse_json.is_parsed = False) - end - end - - test_json_fail33 - -- - local - parse_json: like new_json_parser - do - if attached json_file_from ("fail33.json") as json_file then - parse_json := new_json_parser (json_file) - json_value := parse_json.parse_json - assert ("fail33.json",parse_json.is_parsed = False) - end - end - -feature -- JSON_FROM_FILE - - file_reader: JSON_FILE_READER - - json_value: detachable JSON_VALUE - - json_file_from (fn: STRING): detachable STRING - do - Result := file_reader.read_json_from (test_dir + fn) - assert ("File contains json data", Result /= Void) - end - - new_json_parser (a_string: STRING): JSON_PARSER - do - 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 - - +note + description: "[ + Eiffel tests that can be executed by testing tool. + ]" + author: "EiffelStudio test wizard" + date: "$Date$" + revision: "$Revision$" + testing: "type/manual" + +class + TEST_JSON_SUITE + +inherit + EQA_TEST_SET + redefine + on_prepare + end + +feature {NONE} -- Events + + on_prepare + -- + do + create file_reader + end + +feature -- Tests Pass + + test_json_pass1 + -- + local + parse_json: like new_json_parser + do + if attached json_file_from ("pass1.json") as json_file then + parse_json := new_json_parser (json_file) + json_value := parse_json.parse_json + assert ("pass1.json",parse_json.is_parsed = True) + end + end + + test_json_pass2 + -- + local + parse_json: like new_json_parser + do + if attached json_file_from ("pass2.json") as json_file then + parse_json := new_json_parser (json_file) + json_value := parse_json.parse_json + assert ("pass2.json",parse_json.is_parsed = True) + end + end + + test_json_pass3 + -- + local + parse_json: like new_json_parser + do + if attached json_file_from ("pass3.json") as json_file then + parse_json := new_json_parser (json_file) + json_value := parse_json.parse_json + assert ("pass3.json",parse_json.is_parsed = True) + 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 + -- + local + parse_json: like new_json_parser + do + if attached json_file_from ("fail1.json") as json_file then + parse_json := new_json_parser (json_file) + json_value := parse_json.parse_json + assert ("fail1.json",parse_json.is_parsed = False) + end + end + + test_json_fail2 + -- + local + parse_json: like new_json_parser + do + if attached json_file_from ("fail2.json") as json_file then + parse_json := new_json_parser (json_file) + json_value := parse_json.parse_json + assert ("fail2.json",parse_json.is_parsed = False) + end + end + + test_json_fail3 + -- + local + parse_json: like new_json_parser + do + if attached json_file_from ("fail3.json") as json_file then + parse_json := new_json_parser (json_file) + json_value := parse_json.parse_json + assert ("fail3.json",parse_json.is_parsed = False) + end + end + + test_json_fail4 + -- + local + parse_json: like new_json_parser + do + if attached json_file_from ("fail4.json") as json_file then + parse_json := new_json_parser (json_file) + json_value := parse_json.parse_json + assert ("fail4.json",parse_json.is_parsed = False) + end + end + + test_json_fail5 + -- + local + parse_json: like new_json_parser + do + if attached json_file_from ("fail5.json") as json_file then + parse_json := new_json_parser (json_file) + json_value := parse_json.parse_json + assert ("fail5.json",parse_json.is_parsed = False) + end + end + + + test_json_fail6 + -- + local + parse_json: like new_json_parser + do + if attached json_file_from ("fail6.json") as json_file then + parse_json := new_json_parser (json_file) + json_value := parse_json.parse_json + assert ("fail6.json",parse_json.is_parsed = False ) + end + end + + test_json_fail7 + -- + local + parse_json: like new_json_parser + do + if attached json_file_from ("fail7.json") as json_file then + parse_json := new_json_parser (json_file) + json_value := parse_json.parse_json + assert ("fail7.json",parse_json.is_parsed = False) + end + end + + test_json_fail8 + -- + local + parse_json: like new_json_parser + do + if attached json_file_from ("fail8.json") as json_file then + parse_json := new_json_parser (json_file) + json_value := parse_json.parse_json + assert ("fail8.json",parse_json.is_parsed = False ) + end + end + + + test_json_fail9 + -- + local + parse_json: like new_json_parser + do + if attached json_file_from ("fail9.json") as json_file then + parse_json := new_json_parser (json_file) + json_value := parse_json.parse_json + assert ("fail9.json",parse_json.is_parsed = False) + end + end + + + test_json_fail10 + -- + local + parse_json: like new_json_parser + do + if attached json_file_from ("fail10.json") as json_file then + parse_json := new_json_parser (json_file) + json_value := parse_json.parse_json + assert ("fail10.json",parse_json.is_parsed = False) + end + end + + test_json_fail11 + -- + local + parse_json: like new_json_parser + do + if attached json_file_from ("fail11.json") as json_file then + parse_json := new_json_parser (json_file) + json_value := parse_json.parse_json + assert ("fail11.json",parse_json.is_parsed = False) + end + end + + test_json_fail12 + -- + local + parse_json: like new_json_parser + do + if attached json_file_from ("fail12.json") as json_file then + parse_json := new_json_parser (json_file) + json_value := parse_json.parse_json + assert ("fail12.json",parse_json.is_parsed = False) + end + end + + test_json_fail13 + -- + local + parse_json: like new_json_parser + do + if attached json_file_from ("fail13.json") as json_file then + parse_json := new_json_parser (json_file) + json_value := parse_json.parse_json + assert ("fail13.json",parse_json.is_parsed = False) + end + end + + test_json_fail14 + -- + local + parse_json: like new_json_parser + do + if attached json_file_from ("fail14.json") as json_file then + parse_json := new_json_parser (json_file) + json_value := parse_json.parse_json + assert ("fail14.json",parse_json.is_parsed = False) + end + end + + test_json_fail15 + -- + local + parse_json: like new_json_parser + do + if attached json_file_from ("fail15.json") as json_file then + parse_json := new_json_parser (json_file) + json_value := parse_json.parse_json + assert ("fail15.json",parse_json.is_parsed = False) + end + end + + test_json_fail16 + -- + local + parse_json: like new_json_parser + do + if attached json_file_from ("fail16.json") as json_file then + parse_json := new_json_parser (json_file) + json_value := parse_json.parse_json + assert ("fail16.json",parse_json.is_parsed = False) + end + end + + test_json_fail17 + -- + local + parse_json: like new_json_parser + do + if attached json_file_from ("fail17.json") as json_file then + parse_json := new_json_parser (json_file) + json_value := parse_json.parse_json + assert ("fail17.json",parse_json.is_parsed = False) + end + end + + test_json_fail18 + -- + local + parse_json: like new_json_parser + do + if attached json_file_from ("fail18.json") as json_file then + parse_json := new_json_parser (json_file) + json_value := parse_json.parse_json + assert ("fail18.json",parse_json.is_parsed = True) + end + end + + test_json_fail19 + -- + local + parse_json: like new_json_parser + do + if attached json_file_from ("fail19.json") as json_file then + parse_json := new_json_parser (json_file) + json_value := parse_json.parse_json + assert ("fail19.json",parse_json.is_parsed = False) + end + end + + test_json_fail20 + -- + local + parse_json: like new_json_parser + do + if attached json_file_from ("fail20.json") as json_file then + parse_json := new_json_parser (json_file) + json_value := parse_json.parse_json + assert ("fail20.json",parse_json.is_parsed = False) + end + end + + test_json_fail21 + -- + local + parse_json: like new_json_parser + do + if attached json_file_from ("fail21.json") as json_file then + parse_json := new_json_parser (json_file) + json_value := parse_json.parse_json + assert ("fail21.json",parse_json.is_parsed = False) + end + end + + + test_json_fail22 + -- + local + parse_json: like new_json_parser + do + if attached json_file_from ("fail22.json") as json_file then + parse_json := new_json_parser (json_file) + json_value := parse_json.parse_json + assert ("fail22.json",parse_json.is_parsed = False) + end + end + + test_json_fail23 + -- + local + parse_json: like new_json_parser + do + if attached json_file_from ("fail23.json") as json_file then + parse_json := new_json_parser (json_file) + json_value := parse_json.parse_json + assert ("fail23.json",parse_json.is_parsed = False) + end + end + + test_json_fail24 + -- + local + parse_json: like new_json_parser + do + if attached json_file_from ("fail24.json") as json_file then + parse_json := new_json_parser (json_file) + json_value := parse_json.parse_json + assert ("fail24.json",parse_json.is_parsed = False) + end + end + + test_json_fail25 + -- + local + parse_json: like new_json_parser + do + if attached json_file_from ("fail25.json") as json_file then + parse_json := new_json_parser (json_file) + json_value := parse_json.parse_json + assert ("fail25.json",parse_json.is_parsed = False) + end + end + + + test_json_fail26 + -- + local + parse_json: like new_json_parser + do + if attached json_file_from ("fail26.json") as json_file then + parse_json := new_json_parser (json_file) + json_value := parse_json.parse_json + assert ("fail26.json",parse_json.is_parsed = False) + end + end + + + test_json_fail27 + -- + local + parse_json: like new_json_parser + do + if attached json_file_from ("fail27.json") as json_file then + parse_json := new_json_parser (json_file) + json_value := parse_json.parse_json + assert ("fail27.json",parse_json.is_parsed = False) + end + end + + + test_json_fail28 + -- + local + parse_json: like new_json_parser + do + if attached json_file_from ("fail28.json") as json_file then + parse_json := new_json_parser (json_file) + json_value := parse_json.parse_json + assert ("fail28.json",parse_json.is_parsed = False) + end + end + + + test_json_fail29 + -- + local + parse_json: like new_json_parser + do + if attached json_file_from ("fail29.json") as json_file then + parse_json := new_json_parser (json_file) + json_value := parse_json.parse_json + assert ("fail29.json",parse_json.is_parsed = False ) + end + end + + + test_json_fail30 + -- + local + parse_json: like new_json_parser + do + if attached json_file_from ("fail30.json") as json_file then + parse_json := new_json_parser (json_file) + json_value := parse_json.parse_json + assert ("fail30.json",parse_json.is_parsed = False) + end + end + + test_json_fail31 + -- + local + parse_json: like new_json_parser + do + if attached json_file_from ("fail31.json") as json_file then + parse_json := new_json_parser (json_file) + json_value := parse_json.parse_json + assert ("fail31.json",parse_json.is_parsed = False) + end + end + + test_json_fail32 + -- + local + parse_json: like new_json_parser + do + if attached json_file_from ("fail32.json") as json_file then + parse_json := new_json_parser (json_file) + json_value := parse_json.parse_json + assert ("fail32.json",parse_json.is_parsed = False) + end + end + + test_json_fail33 + -- + local + parse_json: like new_json_parser + do + if attached json_file_from ("fail33.json") as json_file then + parse_json := new_json_parser (json_file) + json_value := parse_json.parse_json + assert ("fail33.json",parse_json.is_parsed = False) + end + end + +feature -- JSON_FROM_FILE + + file_reader: JSON_FILE_READER + + 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 + 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 + + new_json_parser (a_string: STRING): JSON_PARSER + do + create Result.make_parser (a_string) + end + + +invariant + file_reader /= Void + +end + diff --git a/contrib/library/text/parser/json/test/autotest/test_suite/test_suite-safe.ecf b/contrib/library/text/parser/json/test/autotest/test_suite/test_suite-safe.ecf index 00552058..541c8d9e 100644 --- a/contrib/library/text/parser/json/test/autotest/test_suite/test_suite-safe.ecf +++ b/contrib/library/text/parser/json/test/autotest/test_suite/test_suite-safe.ecf @@ -1,18 +1,18 @@ - - - - - - /EIFGENs$ - /CVS$ - /.svn$ - - - - - - - - + + + + + + /EIFGENs$ + /CVS$ + /.svn$ + + + + + + + + diff --git a/doc/wiki/Community-collaboration.md b/doc/wiki/Community-collaboration.md index 13819d95..0be32409 100644 --- a/doc/wiki/Community-collaboration.md +++ b/doc/wiki/Community-collaboration.md @@ -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) diff --git a/doc/wiki/Connectors.md b/doc/wiki/Connectors.md new file mode 100644 index 00000000..c3d8f646 --- /dev/null +++ b/doc/wiki/Connectors.md @@ -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 diff --git a/doc/wiki/Documentation-_Router.md b/doc/wiki/Documentation-_Router.md new file mode 100644 index 00000000..3207e4a6 --- /dev/null +++ b/doc/wiki/Documentation-_Router.md @@ -0,0 +1 @@ +See WSF_ROUTER \ No newline at end of file diff --git a/doc/wiki/Documentation.md b/doc/wiki/Documentation.md new file mode 100644 index 00000000..89c4264c --- /dev/null +++ b/doc/wiki/Documentation.md @@ -0,0 +1,209 @@ +# Current Status +* Official repository: +* Official website: + +# 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. + + + +# 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) + + + +# 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) + + + +# 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) + + + +# 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 + + + + +# EWSGI Specification + + + +# 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: + +## 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__: + +## 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 diff --git a/doc/wiki/Documentation__Connector.md b/doc/wiki/Documentation__Connector.md new file mode 100644 index 00000000..b76dce29 --- /dev/null +++ b/doc/wiki/Documentation__Connector.md @@ -0,0 +1 @@ +See WSF_CONNECTOR \ No newline at end of file diff --git a/doc/wiki/Documentation__Request.md b/doc/wiki/Documentation__Request.md new file mode 100644 index 00000000..379df595 --- /dev/null +++ b/doc/wiki/Documentation__Request.md @@ -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` diff --git a/doc/wiki/Documentation__Response.md b/doc/wiki/Documentation__Response.md new file mode 100644 index 00000000..400c54e3 --- /dev/null +++ b/doc/wiki/Documentation__Response.md @@ -0,0 +1 @@ +See WSF_RESPONSE \ No newline at end of file diff --git a/doc/wiki/Documentation__Router.md b/doc/wiki/Documentation__Router.md new file mode 100644 index 00000000..3207e4a6 --- /dev/null +++ b/doc/wiki/Documentation__Router.md @@ -0,0 +1 @@ +See WSF_ROUTER \ No newline at end of file diff --git a/doc/wiki/Documentation__Service.md b/doc/wiki/Documentation__Service.md new file mode 100644 index 00000000..ea6d16b5 --- /dev/null +++ b/doc/wiki/Documentation__Service.md @@ -0,0 +1,2 @@ +EWF Services +> See WSF\_SERVICE \ No newline at end of file diff --git a/doc/wiki/EWSGI-specification.md b/doc/wiki/EWSGI-specification.md index 750c1874..734613f5 100644 --- a/doc/wiki/EWSGI-specification.md +++ b/doc/wiki/EWSGI-specification.md @@ -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 diff --git a/doc/wiki/EWSGI.md b/doc/wiki/EWSGI.md index 7258848c..42a4fbbc 100644 --- a/doc/wiki/EWSGI.md +++ b/doc/wiki/EWSGI.md @@ -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 ---- diff --git a/doc/wiki/Filter.md b/doc/wiki/Filter.md new file mode 100644 index 00000000..20f22149 --- /dev/null +++ b/doc/wiki/Filter.md @@ -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 \ No newline at end of file diff --git a/doc/wiki/Home.md b/doc/wiki/Home.md index 23fa81e5..636a66b2 100644 --- a/doc/wiki/Home.md +++ b/doc/wiki/Home.md @@ -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]] ![logo](http://groups.google.com/intl/en/images/logos/groups_logo_sm.gif) +- 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) ![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 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) diff --git a/doc/wiki/Meetings.md b/doc/wiki/Meetings.md index a75a124f..05faf598 100644 --- a/doc/wiki/Meetings.md +++ b/doc/wiki/Meetings.md @@ -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) diff --git a/doc/wiki/Projects-new-suggestions.md b/doc/wiki/Projects-new-suggestions.md index 6093b9c9..8ebc7ea9 100644 --- a/doc/wiki/Projects-new-suggestions.md +++ b/doc/wiki/Projects-new-suggestions.md @@ -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/) ---- diff --git a/doc/wiki/Projects.md b/doc/wiki/Projects.md index 2aaa7a50..92bce9d8 100644 --- a/doc/wiki/Projects.md +++ b/doc/wiki/Projects.md @@ -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. \ No newline at end of file +Use the following page [Projects new suggestions](./Projects new suggestions) to suggest new project, or request a feature. \ No newline at end of file diff --git a/doc/wiki/Request-and-response.md b/doc/wiki/Request-and-response.md new file mode 100644 index 00000000..dbeaa8c9 --- /dev/null +++ b/doc/wiki/Request-and-response.md @@ -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 \ No newline at end of file diff --git a/doc/wiki/Router.md b/doc/wiki/Router.md new file mode 100644 index 00000000..0b208861 --- /dev/null +++ b/doc/wiki/Router.md @@ -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 \ No newline at end of file diff --git a/doc/wiki/Tasks-Roadmap.md b/doc/wiki/Tasks-Roadmap.md index 7bb685de..1a1a1a75 100644 --- a/doc/wiki/Tasks-Roadmap.md +++ b/doc/wiki/Tasks-Roadmap.md @@ -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) diff --git a/doc/wiki/Useful-links.md b/doc/wiki/Useful-links.md index 29c810b7..715ddd2d 100644 --- a/doc/wiki/Useful-links.md +++ b/doc/wiki/Useful-links.md @@ -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 \ No newline at end of file +* [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) \ No newline at end of file diff --git a/doc/wiki/Using-the-policy-driven-framework.md b/doc/wiki/Using-the-policy-driven-framework.md index bf85c9ef..12d085e4 100644 --- a/doc/wiki/Using-the-policy-driven-framework.md +++ b/doc/wiki/Using-the-policy-driven-framework.md @@ -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. diff --git a/doc/wiki/Web-meeting-2012-09-18.md b/doc/wiki/Web-meeting-2012-09-18.md index 65f538af..50cda2e2 100644 --- a/doc/wiki/Web-meeting-2012-09-18.md +++ b/doc/wiki/Web-meeting-2012-09-18.md @@ -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 diff --git a/doc/wiki/roadmap.md b/doc/wiki/roadmap.md new file mode 100644 index 00000000..a0b733b5 --- /dev/null +++ b/doc/wiki/roadmap.md @@ -0,0 +1,4 @@ +# Upcoming versions + +# Current state: oct-2013 +- check previous wiki page: [Tasks roadmap](./Tasks roadmap) \ No newline at end of file diff --git a/draft/src/gewf/README.md b/draft/src/gewf/README.md index a5cfd020..a16266b0 100644 --- a/draft/src/gewf/README.md +++ b/draft/src/gewf/README.md @@ -1,3 +1,4 @@ The gewf tool, is an experimentation to generate EWF project from template. status: experimental, POC, in-progress, draft + diff --git a/draft/src/gewf/gewf.ecf b/draft/src/gewf/gewf.ecf index 140da892..fc6cfa94 100644 --- a/draft/src/gewf/gewf.ecf +++ b/draft/src/gewf/gewf.ecf @@ -10,6 +10,7 @@ + diff --git a/draft/src/gewf/src/gewf.e b/draft/src/gewf/src/gewf.e index 41f04891..a5192539 100644 --- a/draft/src/gewf/src/gewf.e +++ b/draft/src/gewf/src/gewf.e @@ -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" diff --git a/draft/src/gewf/src/gewf_setup.e b/draft/src/gewf/src/gewf_setup.e new file mode 100644 index 00000000..829c3859 --- /dev/null +++ b/draft/src/gewf/src/gewf_setup.e @@ -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 diff --git a/draft/src/gewf/template/basic/launcher/any/application_launcher.e b/draft/src/gewf/template/basic/launcher/any/application_launcher.e index 7182d350..7754546d 100644 --- a/draft/src/gewf/template/basic/launcher/any/application_launcher.e +++ b/draft/src/gewf/template/basic/launcher/any/application_launcher.e @@ -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 diff --git a/draft/src/gewf/template/basic/launcher/any/application_launcher_i.e b/draft/src/gewf/template/basic/launcher/any/application_launcher_i.e new file mode 100644 index 00000000..60238588 --- /dev/null +++ b/draft/src/gewf/template/basic/launcher/any/application_launcher_i.e @@ -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 + diff --git a/draft/src/gewf/template/basic/launcher/default/application_launcher.e b/draft/src/gewf/template/basic/launcher/default/application_launcher.e index e20b06ac..7754546d 100644 --- a/draft/src/gewf/template/basic/launcher/default/application_launcher.e +++ b/draft/src/gewf/template/basic/launcher/default/application_launcher.e @@ -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 diff --git a/draft/src/gewf/template/basic/launcher/default/application_launcher_i.e b/draft/src/gewf/template/basic/launcher/default/application_launcher_i.e new file mode 100644 index 00000000..6110e4fa --- /dev/null +++ b/draft/src/gewf/template/basic/launcher/default/application_launcher_i.e @@ -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 + diff --git a/examples/desktop_app/README.md b/examples/desktop_app/README.md new file mode 100644 index 00000000..f9054fb4 --- /dev/null +++ b/examples/desktop_app/README.md @@ -0,0 +1,2 @@ +This example demonstrates the use of embedded Vision2 web browser component, and embedded EWF server (using nino). + diff --git a/examples/desktop_app/desktop_app.ecf b/examples/desktop_app/desktop_app.ecf new file mode 100644 index 00000000..78fd6e17 --- /dev/null +++ b/examples/desktop_app/desktop_app.ecf @@ -0,0 +1,28 @@ + + + Vision2+web browser widget+embedded web service + + This example demonstrates how to build a vision2 desktop application that embed a web browser accessing the service of an embedded web service. + + + + + + + + + + + + + + + /EIFGENs$ + /CVS$ + /.svn$ + + + + diff --git a/examples/desktop_app/files/index.html b/examples/desktop_app/files/index.html new file mode 100644 index 00000000..345e6aef --- /dev/null +++ b/examples/desktop_app/files/index.html @@ -0,0 +1 @@ +Test diff --git a/examples/desktop_app/home.html b/examples/desktop_app/home.html new file mode 100644 index 00000000..26e7c6e2 --- /dev/null +++ b/examples/desktop_app/home.html @@ -0,0 +1,32 @@ + + + + + + +

This is a local file test with js

  • back to home
  • Let AJAX change this text

    + +
    + diff --git a/examples/desktop_app/src/app_embedded_web_service.e b/examples/desktop_app/src/app_embedded_web_service.e new file mode 100644 index 00000000..e9ebed88 --- /dev/null +++ b/examples/desktop_app/src/app_embedded_web_service.e @@ -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 ("

    Hello Eiffel desktop user

    ") + b.append ("
  • test
  • ") + b.append ("
  • env
  • ") + b.append ("
  • files
  • ") + b.append ("
  • exit
  • ") + 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 ("

    This is a test about "+ m.html_encoded_string (l_name) +"

    ") + b.append ("
  • back to home
  • ") + if l_name.is_case_insensitive_equal_general ("start") then + b.append ("
  • test javascript+ajax
  • ") + elseif l_name.is_case_insensitive_equal_general ("js") then + b.append ("[ +

    Let AJAX change this text

    + +
    + ]") + 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 ("

    Embedded server is about to shutdown

    ") + b.append ("
  • back to home
  • ") + 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 diff --git a/examples/desktop_app/src/desktop_app.e b/examples/desktop_app/src/desktop_app.e new file mode 100644 index 00000000..85aaf27c --- /dev/null +++ b/examples/desktop_app/src/desktop_app.e @@ -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 diff --git a/examples/desktop_app/src/main_window.e b/examples/desktop_app/src/main_window.e new file mode 100644 index 00000000..b259db99 --- /dev/null +++ b/examples/desktop_app/src/main_window.e @@ -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 diff --git a/examples/desktop_app/src/service/embedded_web_service.e b/examples/desktop_app/src/service/embedded_web_service.e new file mode 100644 index 00000000..995f2393 --- /dev/null +++ b/examples/desktop_app/src/service/embedded_web_service.e @@ -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 diff --git a/examples/desktop_app/src/service/shared_embeded_web_service_information.e b/examples/desktop_app/src/service/shared_embeded_web_service_information.e new file mode 100644 index 00000000..ba04e855 --- /dev/null +++ b/examples/desktop_app/src/service/shared_embeded_web_service_information.e @@ -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 diff --git a/examples/filter/src/filter/authentication_filter.e b/examples/filter/src/filter/authentication_filter.e index a18da1e7..0587ec76 100644 --- a/examples/filter/src/filter/authentication_filter.e +++ b/examples/filter/src/filter/authentication_filter.e @@ -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 diff --git a/examples/restbucksCRUD/client/client-safe.ecf b/examples/restbucksCRUD/client/client-safe.ecf index 5c53670e..69acd23e 100644 --- a/examples/restbucksCRUD/client/client-safe.ecf +++ b/examples/restbucksCRUD/client/client-safe.ecf @@ -1,5 +1,5 @@ - + @@ -11,7 +11,7 @@ - + diff --git a/examples/restbucksCRUD/readme.md b/examples/restbucksCRUD/readme.md index 6d06a051..6b85f132 100644 --- a/examples/restbucksCRUD/readme.md +++ b/examples/restbucksCRUD/readme.md @@ -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 ------------------------------- diff --git a/examples/restbucksCRUD/restbucks-safe.ecf b/examples/restbucksCRUD/restbucks-safe.ecf index d25369fa..626f0d5e 100644 --- a/examples/restbucksCRUD/restbucks-safe.ecf +++ b/examples/restbucksCRUD/restbucks-safe.ecf @@ -1,11 +1,13 @@ - + /EIFGENs$ /\.git$ /\.svn$ + @@ -13,9 +15,9 @@ - + + - @@ -27,7 +29,7 @@ - @@ -39,7 +41,7 @@ - diff --git a/examples/restbucksCRUD/src/policy_driven_resource/order_handler.e b/examples/restbucksCRUD/src/policy_driven_resource/order_handler.e index 91d1b9ae..f20881fe 100644 --- a/examples/restbucksCRUD/src/policy_driven_resource/order_handler.e +++ b/examples/restbucksCRUD/src/policy_driven_resource/order_handler.e @@ -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 diff --git a/examples/restbucksCRUD/src/utils/etag_utils.e b/examples/restbucksCRUD/src/utils/etag_utils.e index 99b6113a..9aed6c1d 100644 --- a/examples/restbucksCRUD/src/utils/etag_utils.e +++ b/examples/restbucksCRUD/src/utils/etag_utils.e @@ -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 diff --git a/examples/upload_image/src/image_uploader.e b/examples/upload_image/src/image_uploader.e index 87c03aeb..7d35203e 100644 --- a/examples/upload_image/src/image_uploader.e +++ b/examples/upload_image/src/image_uploader.e @@ -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:
    %N") + fn := fn.extended (p_demo.value) + l_body.append ("File:
    %N") end from @@ -131,15 +132,17 @@ feature -- Execution create l_body.make (255) l_body.append ("

    EWF: Uploaded files

    %N") l_body.append ("
      ") + n := 0 across req.uploaded_files as c loop + n := n + 1 l_body.append ("
    • ") l_body.append ("
      " + c.item.name + "=" + html_encode (c.item.filename) + " size=" + c.item.size.out + " type=" + c.item.content_type + "
      ") 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") else @@ -148,6 +151,7 @@ feature -- Execution end l_body.append ("
    • ") end + l_body.append ("
    ") 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 ("

    EWF: Uploaded files

    %N") + l_body.append ("
      ") + 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 ("
    • ") + l_body.append ("
      Input data : size=" + f.count.out + " (" + req.content_length_value.out + ")
      ") + l_body.append ("%N"+ html_encode (l_safe_filename) +"") + l_body.append ("
    • ") + end + end + l_body.append ("
    ") + + 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 diff --git a/examples/upload_image/upload_image-safe.ecf b/examples/upload_image/upload_image-safe.ecf index ba2e34de..66fb0fcc 100644 --- a/examples/upload_image/upload_image-safe.ecf +++ b/examples/upload_image/upload_image-safe.ecf @@ -1,5 +1,5 @@ - + @@ -20,11 +20,10 @@
    - + - + - - +
    diff --git a/library/library.index b/library/library.index index fbc1ac0e..379ba989 100644 --- a/library/library.index +++ b/library/library.index @@ -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 diff --git a/library/network/http_client/http_client-safe.ecf b/library/network/http_client/http_client-safe.ecf index c9c641e3..041fabd1 100644 --- a/library/network/http_client/http_client-safe.ecf +++ b/library/network/http_client/http_client-safe.ecf @@ -1,5 +1,5 @@ - + @@ -10,16 +10,7 @@ - - - - - - - - - - + diff --git a/library/network/http_client/http_client.ecf b/library/network/http_client/http_client.ecf index ffce8e4e..6400022b 100644 --- a/library/network/http_client/http_client.ecf +++ b/library/network/http_client/http_client.ecf @@ -11,16 +11,7 @@ - - - - - - - - - - + diff --git a/library/network/http_client/package.iron b/library/network/http_client/package.iron new file mode 100644 index 00000000..72eed711 --- /dev/null +++ b/library/network/http_client/package.iron @@ -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 diff --git a/library/network/http_client/src/http_client_request.e b/library/network/http_client/src/http_client_request.e index 7d8cd09c..030eddf6 100644 --- a/library/network/http_client/src/http_client_request.e +++ b/library/network/http_client/src/http_client_request.e @@ -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 diff --git a/library/network/http_client/src/http_client_request_context.e b/library/network/http_client/src/http_client_request_context.e index 78819831..09661290 100644 --- a/library/network/http_client/src/http_client_request_context.e +++ b/library/network/http_client/src/http_client_request_context.e @@ -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 - upload_data := a_data + 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 diff --git a/library/network/http_client/src/http_client_session.e b/library/network/http_client/src/http_client_session.e index a0aeec09..2a9b04c8 100644 --- a/library/network/http_client/src/http_client_session.e +++ b/library/network/http_client/src/http_client_session.e @@ -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 diff --git a/library/network/http_client/src/spec/libcurl/libcurl_http_client_request.e b/library/network/http_client/src/spec/libcurl/libcurl_http_client_request.e index 083ca687..0dcb127a 100644 --- a/library/network/http_client/src/spec/libcurl/libcurl_http_client_request.e +++ b/library/network/http_client/src/spec/libcurl/libcurl_http_client_request.e @@ -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) diff --git a/library/network/http_client/src/spec/libcurl/libcurl_http_client_session.e b/library/network/http_client/src/spec/libcurl/libcurl_http_client_session.e index d68eb98b..bffe43c5 100644 --- a/library/network/http_client/src/spec/libcurl/libcurl_http_client_session.e +++ b/library/network/http_client/src/spec/libcurl/libcurl_http_client_session.e @@ -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 diff --git a/library/network/http_client/tests/test-safe.ecf b/library/network/http_client/tests/test-safe.ecf index 5b2c90d0..f2bce7ee 100644 --- a/library/network/http_client/tests/test-safe.ecf +++ b/library/network/http_client/tests/test-safe.ecf @@ -8,10 +8,14 @@ /.svn$ - + + +
    diff --git a/library/network/protocol/CONNEG/README.md b/library/network/protocol/CONNEG/README.md deleted file mode 100644 index d86bcedb..00000000 --- a/library/network/protocol/CONNEG/README.md +++ /dev/null @@ -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 - diff --git a/library/network/protocol/CONNEG/run_test.rb b/library/network/protocol/CONNEG/run_test.rb deleted file mode 100644 index 4ee95c80..00000000 --- a/library/network/protocol/CONNEG/run_test.rb +++ /dev/null @@ -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=< 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 diff --git a/library/network/protocol/CONNEG/src/common_results.e b/library/network/protocol/CONNEG/src/common_results.e deleted file mode 100644 index 5d71a733..00000000 --- a/library/network/protocol/CONNEG/src/common_results.e +++ /dev/null @@ -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 diff --git a/library/network/protocol/CONNEG/src/compression_variant_results.e b/library/network/protocol/CONNEG/src/compression_variant_results.e deleted file mode 100644 index 333cd351..00000000 --- a/library/network/protocol/CONNEG/src/compression_variant_results.e +++ /dev/null @@ -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 diff --git a/library/network/protocol/CONNEG/src/conneg_server_side.e b/library/network/protocol/CONNEG/src/conneg_server_side.e deleted file mode 100644 index 1d08c7a4..00000000 --- a/library/network/protocol/CONNEG/src/conneg_server_side.e +++ /dev/null @@ -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 - - diff --git a/library/network/protocol/CONNEG/src/language_parse.e b/library/network/protocol/CONNEG/src/language_parse.e deleted file mode 100644 index 4373a65a..00000000 --- a/library/network/protocol/CONNEG/src/language_parse.e +++ /dev/null @@ -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 diff --git a/library/network/protocol/CONNEG/src/language_results.e b/library/network/protocol/CONNEG/src/language_results.e deleted file mode 100644 index fe8d0aa0..00000000 --- a/library/network/protocol/CONNEG/src/language_results.e +++ /dev/null @@ -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 diff --git a/library/network/protocol/CONNEG/src/language_variant_results.e b/library/network/protocol/CONNEG/src/language_variant_results.e deleted file mode 100644 index 096f4293..00000000 --- a/library/network/protocol/CONNEG/src/language_variant_results.e +++ /dev/null @@ -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 diff --git a/library/network/protocol/CONNEG/src/media_type_variant_results.e b/library/network/protocol/CONNEG/src/media_type_variant_results.e deleted file mode 100644 index 8e4a6234..00000000 --- a/library/network/protocol/CONNEG/src/media_type_variant_results.e +++ /dev/null @@ -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 diff --git a/library/network/protocol/CONNEG/src/parse_results.e b/library/network/protocol/CONNEG/src/parse_results.e deleted file mode 100644 index 185995ea..00000000 --- a/library/network/protocol/CONNEG/src/parse_results.e +++ /dev/null @@ -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 diff --git a/library/network/protocol/CONNEG/src/shared_conneg.e b/library/network/protocol/CONNEG/src/shared_conneg.e deleted file mode 100644 index ffa6a47f..00000000 --- a/library/network/protocol/CONNEG/src/shared_conneg.e +++ /dev/null @@ -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 diff --git a/library/network/protocol/CONNEG/src/variant_results.e b/library/network/protocol/CONNEG/src/variant_results.e deleted file mode 100644 index 65272762..00000000 --- a/library/network/protocol/CONNEG/src/variant_results.e +++ /dev/null @@ -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 diff --git a/library/network/protocol/CONNEG/test/.gitignore b/library/network/protocol/CONNEG/test/.gitignore deleted file mode 100644 index ac53b3c2..00000000 --- a/library/network/protocol/CONNEG/test/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -*~ -EIFGEN* # ignore all files in the EIFGENs/ directory - - diff --git a/library/network/protocol/CONNEG/test/conneg_server_side_test.e b/library/network/protocol/CONNEG/test/conneg_server_side_test.e deleted file mode 100644 index e9d181f4..00000000 --- a/library/network/protocol/CONNEG/test/conneg_server_side_test.e +++ /dev/null @@ -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 diff --git a/library/network/protocol/CONNEG/test/language_parser_test.e b/library/network/protocol/CONNEG/test/language_parser_test.e deleted file mode 100644 index bac690bc..00000000 --- a/library/network/protocol/CONNEG/test/language_parser_test.e +++ /dev/null @@ -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 - - diff --git a/library/network/protocol/CONNEG/test/test.rc b/library/network/protocol/CONNEG/test/test.rc deleted file mode 100644 index 8b137891..00000000 --- a/library/network/protocol/CONNEG/test/test.rc +++ /dev/null @@ -1 +0,0 @@ - diff --git a/library/network/protocol/CONNEG/.gitignore b/library/network/protocol/content_negotiation/.gitignore similarity index 100% rename from library/network/protocol/CONNEG/.gitignore rename to library/network/protocol/content_negotiation/.gitignore diff --git a/library/network/protocol/content_negotiation/README.md b/library/network/protocol/content_negotiation/README.md new file mode 100644 index 00000000..bb4f9b2e --- /dev/null +++ b/library/network/protocol/content_negotiation/README.md @@ -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. + diff --git a/library/network/protocol/CONNEG/conneg-safe.ecf b/library/network/protocol/content_negotiation/conneg-safe.ecf similarity index 56% rename from library/network/protocol/CONNEG/conneg-safe.ecf rename to library/network/protocol/content_negotiation/conneg-safe.ecf index 42207d69..af7edc59 100644 --- a/library/network/protocol/CONNEG/conneg-safe.ecf +++ b/library/network/protocol/content_negotiation/conneg-safe.ecf @@ -1,5 +1,5 @@ - + @@ -12,7 +12,12 @@ - - + + + + /implementation + + + diff --git a/library/network/protocol/CONNEG/conneg.ecf b/library/network/protocol/content_negotiation/conneg.ecf similarity index 77% rename from library/network/protocol/CONNEG/conneg.ecf rename to library/network/protocol/content_negotiation/conneg.ecf index a4db8e63..6677144a 100644 --- a/library/network/protocol/CONNEG/conneg.ecf +++ b/library/network/protocol/content_negotiation/conneg.ecf @@ -13,6 +13,12 @@ - + + + /implementation + + +
    diff --git a/library/network/protocol/CONNEG/license.lic b/library/network/protocol/content_negotiation/license.lic similarity index 100% rename from library/network/protocol/CONNEG/license.lic rename to library/network/protocol/content_negotiation/license.lic diff --git a/library/network/protocol/content_negotiation/package.iron b/library/network/protocol/content_negotiation/package.iron new file mode 100644 index 00000000..b01d71f6 --- /dev/null +++ b/library/network/protocol/content_negotiation/package.iron @@ -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 diff --git a/library/network/protocol/CONNEG/src/.gitignore b/library/network/protocol/content_negotiation/src/.gitignore similarity index 100% rename from library/network/protocol/CONNEG/src/.gitignore rename to library/network/protocol/content_negotiation/src/.gitignore diff --git a/library/network/protocol/CONNEG/src/fitness_and_quality.e b/library/network/protocol/content_negotiation/src/implementation/fitness_and_quality.e similarity index 67% rename from library/network/protocol/CONNEG/src/fitness_and_quality.e rename to library/network/protocol/content_negotiation/src/implementation/fitness_and_quality.e index a851ecde..01b38c1f 100644 --- a/library/network/protocol/CONNEG/src/fitness_and_quality.e +++ b/library/network/protocol/content_negotiation/src/implementation/fitness_and_quality.e @@ -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 diff --git a/library/network/protocol/content_negotiation/src/parsers/http_accept_language_utilities.e b/library/network/protocol/content_negotiation/src/parsers/http_accept_language_utilities.e new file mode 100644 index 00000000..6d69f6f8 --- /dev/null +++ b/library/network/protocol/content_negotiation/src/parsers/http_accept_language_utilities.e @@ -0,0 +1,256 @@ +note + description: "[ + {HTTP_ACCEPT_LANGUAGE_UTILITIES} is in charge to parse language tags defined as follow: + + Accept-Language = "Accept-Language" ":" + 1#( language-l_range [ ";" "q" "=" qvalue ] ) + language-l_range = ( ( 1*8ALPHA *( "-" 1*8ALPHA ) ) | "*" ) + + Example: + Accept-Language: da, en-gb;q=0.8, en;q=0.7 + + ]" + date: "$Date$" + revision: "$Revision$" + EIS: "name=Accept-Language", "src=http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.4", "protocol=uri" + +class + HTTP_ACCEPT_LANGUAGE_UTILITIES + +inherit + HTTP_HEADER_UTILITIES + +feature -- Parser + + accept_language_list (a_header_value: READABLE_STRING_8): LIST [HTTP_ACCEPT_LANGUAGE] + -- Languages-ranges are languages with wild-cards and a 'q' quality parameter. + -- For example, the language l_range ('en-* ;q=0.5') would get parsed into: + -- ('en', '*', {'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. + local + l_res: LIST [READABLE_STRING_8] + l_lang: HTTP_ACCEPT_LANGUAGE + do + l_res := a_header_value.split (',') + create {ARRAYED_LIST [HTTP_ACCEPT_LANGUAGE]} Result.make (l_res.count) + + from + l_res.start + until + l_res.after + loop + create l_lang.make_from_string (l_res.item_for_iteration) + Result.force (l_lang) + l_res.forth + end + end + + accept_language (a_accept_language_item: READABLE_STRING_8): HTTP_ACCEPT_LANGUAGE + -- Languages-ranges are languages with wild-cards and a 'q' quality parameter. + -- For example, the language l_range ('en-* ;q=0.5') would get parsed into: + -- ('en', '*', {'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 + create Result.make_from_string (a_accept_language_item) + end + + quality (a_language: READABLE_STRING_8; a_ranges: READABLE_STRING_8): REAL_64 + -- Returns the quality 'q' of a `a_language' when compared against the + -- language l_range in `a_ranges'. + do + Result := quality_from_list (a_language, accept_language_list (a_ranges)) + end + + best_match (a_supported: ITERABLE [READABLE_STRING_8]; a_header_value: READABLE_STRING_8): READABLE_STRING_8 + -- Choose the `parse_language' with the highest fitness score and quality ('q') from a list of candidates. + local + l_header_results: LIST [HTTP_ACCEPT_LANGUAGE] + l_weighted_matches: LIST [FITNESS_AND_QUALITY] + l_fitness_and_quality, l_first_one: detachable FITNESS_AND_QUALITY + s: READABLE_STRING_8 + do + l_header_results := accept_language_list (a_header_value) + + --| weighted matches + create {ARRAYED_LIST [FITNESS_AND_QUALITY]} l_weighted_matches.make (0) + across a_supported as ic loop + l_fitness_and_quality := fitness_and_quality_from_list (ic.item, l_header_results) + l_fitness_and_quality.set_entity (entity_value (ic.item)) + l_weighted_matches.force (l_fitness_and_quality) + end + + --| Keep only top quality+fitness types + from + l_weighted_matches.start + l_first_one := l_weighted_matches.item + l_weighted_matches.forth + until + l_weighted_matches.after + loop + l_fitness_and_quality := l_weighted_matches.item + if l_first_one < l_fitness_and_quality then + l_first_one := l_fitness_and_quality + if not l_weighted_matches.isfirst then + from + l_weighted_matches.back + until + l_weighted_matches.before + loop + l_weighted_matches.remove + l_weighted_matches.back + end + l_weighted_matches.forth + end + check + l_weighted_matches.item = l_fitness_and_quality + end + l_weighted_matches.forth + elseif l_first_one ~ l_fitness_and_quality then + l_weighted_matches.forth + else + check + l_first_one > l_fitness_and_quality + end + l_weighted_matches.remove + end + end + if l_first_one /= Void and then l_first_one.quality /= 0.0 then + if l_weighted_matches.count = 1 then + Result := l_first_one.entity + else + from + l_fitness_and_quality := Void + l_header_results.start + until + l_header_results.after or l_fitness_and_quality /= Void + loop + s := l_header_results.item.language_range + from + l_weighted_matches.start + until + l_weighted_matches.after or l_fitness_and_quality /= Void + loop + l_fitness_and_quality := l_weighted_matches.item + if l_fitness_and_quality.entity.same_string (s) then + --| Found + else + l_fitness_and_quality := Void + l_weighted_matches.forth + end + end + l_header_results.forth + end + if l_fitness_and_quality /= Void then + Result := l_fitness_and_quality.entity + else + Result := l_first_one.entity + end + end + else + Result := "" + end + end + +feature {NONE} -- Implementation + + fitness_and_quality_from_list (a_language: READABLE_STRING_8; a_parsed_ranges: LIST [HTTP_ACCEPT_LANGUAGE]): FITNESS_AND_QUALITY + -- Find the best match for a given `a_language' against a list of language ranges `a_parsed_ranges' + -- that have already been parsed by parse_language_range. + local + l_best_fitness: INTEGER + l_target_q: REAL_64 + l_best_fit_q: REAL_64 + l_target: HTTP_ACCEPT_LANGUAGE + l_target_type: READABLE_STRING_8 + l_range: HTTP_ACCEPT_LANGUAGE + l_param_matches: INTEGER + l_element: detachable READABLE_STRING_8 + l_fitness: INTEGER + do + l_best_fitness := -1 + l_best_fit_q := 0.0 + create l_target.make_from_string (a_language) + l_target_q := l_target.quality + + l_target_type := l_target.language + from + a_parsed_ranges.start + until + a_parsed_ranges.after + loop + l_range := a_parsed_ranges.item_for_iteration + if + attached l_range.language 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 + l_param_matches := 0 + if attached l_target.parameters as l_target_parameters then + across + l_target_parameters as ic + loop + l_element := ic.key + if + not l_element.same_string ("q") and then + l_range.has_parameter (l_element) and then + (attached ic.item as t_item and attached l_range.parameter (l_element) as r_item) and then + t_item.same_string (r_item) + then + l_param_matches := l_param_matches + 1 + end + end + end + if l_range_type.same_string (l_target_type) then + l_fitness := 100 + else + l_fitness := 0 + end + if + attached l_range.specialization as l_range_sub_type and then + attached l_target.specialization 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 + l_param_matches + if l_fitness > l_best_fitness then + l_best_fitness := l_fitness + l_element := l_range.parameter ("q") + if l_element /= Void then + l_best_fit_q := l_element.to_real_64.min (l_target_q) + else + l_best_fit_q := 0.0 + end + end + end + a_parsed_ranges.forth + end + create Result.make (l_best_fitness, l_best_fit_q) + end + + quality_from_list (a_language: READABLE_STRING_8; a_parsed_ranges: LIST [HTTP_ACCEPT_LANGUAGE]): REAL_64 + -- Find the best match for a given `a_language' against a list of ranges `parsed_ranges' that + -- have already been parsed by parse_language_range. Returns the 'q' quality + -- parameter of the best match, 0 if no match was found. This function + -- bahaves the same as quality except that 'a_parsed_ranges' must be a list + -- of parsed language ranges. + do + Result := fitness_and_quality_from_list (a_language, a_parsed_ranges).quality + end + +note + 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 diff --git a/library/network/protocol/CONNEG/src/mime_parse.e b/library/network/protocol/content_negotiation/src/parsers/http_accept_media_type_utilities.e similarity index 51% rename from library/network/protocol/CONNEG/src/mime_parse.e rename to library/network/protocol/content_negotiation/src/parsers/http_accept_media_type_utilities.e index 9d92ba36..c57e060a 100644 --- a/library/network/protocol/CONNEG/src/mime_parse.e +++ b/library/network/protocol/content_negotiation/src/parsers/http_accept_media_type_utilities.e @@ -1,58 +1,33 @@ note - description: "Summary description for {MIME_PARSE}." - author: "" + description: "[ + {HTTP_ACCEPT_MEDIA_TYPE_UTILITIES}. is encharge to parse Accept request-header field defined as follow: + + Accept = "Accept" ":" + #( media-range [ accept-params ] ) + media-range = ( "*/*" + | ( type "/" "*" ) + | ( type "/" subtype ) + ) *( ";" parameter ) + accept-params = ";" "q" "=" qvalue *( accept-extension ) + accept-extension = ";" token [ "=" ( token | quoted-string ) ] + + Example: + + Accept: text/plain; q=0.5, text/html, text/x-dvi; q=0.8, text/x-c + ]" date: "$Date$" revision: "$Revision$" - description : "Accept Reference: http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.1" + EIS: "name=Accept", "src=http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.1", "protocol=uri" + class - MIME_PARSE + HTTP_ACCEPT_MEDIA_TYPE_UTILITIES inherit - REFACTORING_HELPER + HTTP_HEADER_UTILITIES feature -- Parser - parse_mime_type (a_mime_type: STRING): PARSE_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 ('/') - Result.set_type (trim (l_types[1])) - Result.set_sub_type (trim (l_types[2])) - end - - parse_media_range (a_range: STRING): PARSE_RESULTS + media_type (a_range: READABLE_STRING_8): HTTP_MEDIA_TYPE -- 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'}) @@ -61,142 +36,33 @@ feature -- Parser -- necessary. do fixme ("Improve the code!!!") - Result := parse_mime_type (a_range) - if attached Result.item ("q") as q then + create Result.make_from_string (a_range) + if attached Result.parameter ("q") as q then if q.is_double and then - attached {REAL_64} q.to_double as r and then + attached {REAL_64} q.to_real_64 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") + Result.add_parameter ("q", "1.0") end else - Result.put ("1.0", "q") + Result.add_parameter ("q", "1.0") end else - Result.put ("1.0", "q") + Result.add_parameter ("q", "1.0") end end - - fitness_and_quality_parsed (a_mime_type: STRING; parsed_ranges: LIST [PARSE_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: PARSE_RESULTS - range: PARSE_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 and - attached target.sub_type as l_target_sub_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 ("*")) - ) and - ( - attached range.sub_type as l_range_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 - 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 l_range_sub_type.same_string (l_target_sub_type) then - l_fitness := l_fitness + 10 - 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 [PARSE_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 + quality (a_mime_type: READABLE_STRING_8; ranges: READABLE_STRING_8): 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 [PARSE_RESULTS] - p_res : PARSE_RESULTS + l_ranges : LIST [READABLE_STRING_8] + res : ARRAYED_LIST [HTTP_MEDIA_TYPE] + p_res : HTTP_MEDIA_TYPE do l_ranges := ranges.split (',') from @@ -205,25 +71,25 @@ feature -- Parser until l_ranges.after loop - p_res := parse_media_range (l_ranges.item_for_iteration) - res.put_left (p_res) + p_res := media_type (l_ranges.item_for_iteration) + res.force (p_res) l_ranges.forth end - Result := quality_parsed (a_mime_type, res) + Result := quality_from_list (a_mime_type, res) end - best_match (supported: LIST [STRING]; header: STRING): STRING + best_match (supported: ITERABLE [READABLE_STRING_8]; header: READABLE_STRING_8): READABLE_STRING_8 -- Choose the mime-type with the highest fitness score and quality ('q') from a list of candidates. local - l_header_results: LIST [PARSE_RESULTS] + l_header_results: LIST [HTTP_MEDIA_TYPE] weighted_matches: LIST [FITNESS_AND_QUALITY] - l_res: LIST [STRING] - p_res: PARSE_RESULTS + l_res: LIST [READABLE_STRING_8] + p_res: HTTP_MEDIA_TYPE fitness_and_quality, first_one: detachable FITNESS_AND_QUALITY - s: STRING + s: READABLE_STRING_8 do l_res := header.split (',') - create {ARRAYED_LIST [PARSE_RESULTS]} l_header_results.make (l_res.count) + create {ARRAYED_LIST [HTTP_MEDIA_TYPE]} l_header_results.make (l_res.count) fixme("Extract method!!!") from @@ -231,22 +97,17 @@ feature -- Parser until l_res.after loop - p_res := parse_media_range (l_res.item_for_iteration) + p_res := media_type (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) + create {ARRAYED_LIST [FITNESS_AND_QUALITY]} weighted_matches.make (0) - 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)) + across supported as ic loop + fitness_and_quality := fitness_and_quality_from_list (ic.item, l_header_results) + fitness_and_quality.set_entity (entity_value (ic.item)) weighted_matches.force (fitness_and_quality) - supported.forth end --| Keep only top quality+fitness types @@ -282,7 +143,7 @@ feature -- Parser end if first_one /= Void and then first_one.quality /= 0.0 then if weighted_matches.count = 1 then - Result := first_one.mime_type + Result := first_one.entity else from fitness_and_quality := Void @@ -290,14 +151,14 @@ feature -- Parser until l_header_results.after or fitness_and_quality /= Void loop - s := l_header_results.item.mime_type + s := l_header_results.item.simple_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 + if fitness_and_quality.entity.same_string (s) then --| Found else fitness_and_quality := Void @@ -307,9 +168,9 @@ feature -- Parser l_header_results.forth end if fitness_and_quality /= Void then - Result := fitness_and_quality.mime_type + Result := fitness_and_quality.entity else - Result := first_one.mime_type + Result := first_one.entity end end else @@ -319,31 +180,107 @@ feature -- Parser feature {NONE} -- Implementation - mime_type (s: STRING): STRING + fitness_and_quality_from_list (a_mime_type: READABLE_STRING_8; parsed_ranges: LIST [HTTP_MEDIA_TYPE]): 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. local - p: INTEGER + best_fitness: INTEGER + target_q: REAL_64 + best_fit_q: REAL_64 + target: HTTP_MEDIA_TYPE + range: HTTP_MEDIA_TYPE + param_matches: INTEGER + element: detachable READABLE_STRING_8 + l_fitness: INTEGER do - p := s.index_of (';', 1) - if p > 0 then - Result := trim (s.substring (1, p - 1)) + best_fitness := -1 + best_fit_q := 0.0 + target := media_type (a_mime_type) + if attached target.parameter ("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 - Result := trim (s.string) + target_q := 1.0 end + + if + attached target.type as l_target_type and + attached target.subtype as l_target_sub_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 ("*")) + ) and + ( + attached range.subtype as l_range_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 attached target.parameters as l_keys then + param_matches := 0 + across l_keys as ic loop + element := ic.key + if + not element.same_string ("q") and then + range.has_parameter (element) and then + (attached target.parameter (element) as t_item and attached range.parameter (element) as r_item) and then + t_item.same_string (r_item) + then + param_matches := param_matches + 1 + end + end + end + if l_range_type.same_string (l_target_type) then + l_fitness := 100 + else + l_fitness := 0 + end + + if l_range_sub_type.same_string (l_target_sub_type) then + l_fitness := l_fitness + 10 + end + + l_fitness := l_fitness + param_matches + + if l_fitness > best_fitness then + best_fitness := l_fitness + element := range.parameter ("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 - trim (a_string: STRING): STRING - -- trim whitespace from the beginning and end of a string - require - valid_argument : a_string /= Void + quality_from_list (a_mime_type: READABLE_STRING_8; parsed_ranges: LIST [HTTP_MEDIA_TYPE]): REAL_64 + -- Find the best match for a given mime-type against a list of ranges that + -- have already been parsed by parse_media_range. 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 - a_string.left_adjust - a_string.right_justify - Result := a_string - ensure - result_same_as_argument: a_string = Result + Result := fitness_and_quality_from_list (a_mime_type, parsed_ranges).quality 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 diff --git a/library/network/protocol/content_negotiation/src/parsers/http_any_accept_header_utilities.e b/library/network/protocol/content_negotiation/src/parsers/http_any_accept_header_utilities.e new file mode 100644 index 00000000..a0cb05d4 --- /dev/null +++ b/library/network/protocol/content_negotiation/src/parsers/http_any_accept_header_utilities.e @@ -0,0 +1,237 @@ +note + description: "[ + HTTP_ANY_ACCEPT_HEADER_PARSER, this class allows to parse Accept-* headers + + ]" + date: "$Date$" + revision: "$Revision$" + EIS: "name=Charset", "src=http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.2", "protocol=uri" + EIS: "name=Encoding", "src=http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.3", "protocol=uri" + +class + HTTP_ANY_ACCEPT_HEADER_UTILITIES + +inherit + HTTP_HEADER_UTILITIES + +feature -- Parser + + header (a_header: READABLE_STRING_8): HTTP_ANY_ACCEPT + -- Parses `a_header' charset/encoding into its component parts. + -- For example, the charset 'iso-8889-5' would get parsed + -- into: + -- ('iso-8889-5', {'q':'1.0'}) + do + create Result.make_from_string (a_header) + if Result.parameter ("q") = Void then + Result.put_parameter ("1.0", "q") + end + end + + quality (a_field: READABLE_STRING_8; a_header: READABLE_STRING_8): REAL_64 + -- Returns the quality 'q' of a charset/encoding when compared against the + -- a list of charsets/encodings/ + local + l_commons: LIST [READABLE_STRING_8] + res: ARRAYED_LIST [HTTP_ANY_ACCEPT] + p_res: HTTP_ANY_ACCEPT + do + l_commons := a_header.split (',') + from + create res.make (10) + l_commons.start + until + l_commons.after + loop + p_res := header (l_commons.item_for_iteration) + res.force (p_res) + l_commons.forth + end + Result := quality_from_list (a_field, res) + end + + best_match (a_supported: ITERABLE [READABLE_STRING_8]; a_header: READABLE_STRING_8): READABLE_STRING_8 + -- Choose the accept with the highest fitness score and quality ('q') from a list of candidates. + local + l_header_results: LIST [HTTP_ANY_ACCEPT] + l_weighted_matches: LIST [FITNESS_AND_QUALITY] + l_res: LIST [READABLE_STRING_8] + p_res: HTTP_ANY_ACCEPT + l_fitness_and_quality, l_first_one: detachable FITNESS_AND_QUALITY + do + l_res := a_header.split (',') + create {ARRAYED_LIST [HTTP_ANY_ACCEPT]} l_header_results.make (l_res.count) + from + l_res.start + until + l_res.after + loop + p_res := header (l_res.item_for_iteration) + l_header_results.force (p_res) + l_res.forth + end + create {ARRAYED_LIST [FITNESS_AND_QUALITY]} l_weighted_matches.make (0) + across a_supported as ic loop + l_fitness_and_quality := fitness_and_quality_from_list (ic.item, l_header_results) + l_fitness_and_quality.set_entity (entity_value (ic.item)) + l_weighted_matches.force (l_fitness_and_quality) + end + + --| Keep only top quality+fitness types + --| TODO extract method + from + l_weighted_matches.start + l_first_one := l_weighted_matches.item + l_weighted_matches.forth + until + l_weighted_matches.after + loop + l_fitness_and_quality := l_weighted_matches.item + if l_first_one < l_fitness_and_quality then + l_first_one := l_fitness_and_quality + if not l_weighted_matches.isfirst then + from + l_weighted_matches.back + until + l_weighted_matches.before + loop + l_weighted_matches.remove + l_weighted_matches.back + end + l_weighted_matches.forth + end + check + l_weighted_matches.item = l_fitness_and_quality + end + l_weighted_matches.forth + elseif l_first_one.is_equal (l_fitness_and_quality) then + l_weighted_matches.forth + else + check + l_first_one > l_fitness_and_quality + end + l_weighted_matches.remove + end + end + if l_first_one /= Void and then l_first_one.quality /= 0.0 then + if l_weighted_matches.count = 1 then + Result := l_first_one.entity + else + from + l_fitness_and_quality := Void + l_header_results.start + until + l_header_results.after or l_fitness_and_quality /= Void + loop + if attached l_header_results.item.value as l_field then + from + l_weighted_matches.start + until + l_weighted_matches.after or l_fitness_and_quality /= Void + loop + l_fitness_and_quality := l_weighted_matches.item + if l_fitness_and_quality.entity.same_string (l_field) then + --| Found + else + l_fitness_and_quality := Void + l_weighted_matches.forth + end + end + else + check + has_field: False + end + end + l_header_results.forth + end + if l_fitness_and_quality /= Void then + Result := l_fitness_and_quality.entity + else + Result := l_first_one.entity + end + end + else + Result := "" + end + end + +feature {NONE} -- Implementation + + fitness_and_quality_from_list (a_field: READABLE_STRING_8; a_parsed_charsets: LIST [HTTP_ANY_ACCEPT]): 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: HTTP_ANY_ACCEPT + range: HTTP_ANY_ACCEPT + element: detachable READABLE_STRING_8 + l_fitness: INTEGER + do + best_fitness := -1 + best_fit_q := 0.0 + target := header (a_field) + if attached target.parameter ("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.value as l_target_field then + from + a_parsed_charsets.start + until + a_parsed_charsets.after + loop + range := a_parsed_charsets.item_for_iteration + if attached range.value 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 ("*") + or l_target_field.same_string ("identity") + 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.parameter ("q") + if element /= Void then + best_fit_q := element.to_double.min (target_q) + else + best_fit_q := 0.0 + end + end + end + end + a_parsed_charsets.forth + end + end + create Result.make (best_fitness, best_fit_q) + end + + quality_from_list (a_field: READABLE_STRING_8; a_parsed_common: LIST [HTTP_ANY_ACCEPT]): 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_from_list (a_field, a_parsed_common).quality + end + +note + copyright: "2011-2014, Javier Velilla, Jocelyn Fiat, Eiffel Software and others" + license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" + +end diff --git a/library/network/protocol/content_negotiation/src/parsers/http_header_utilities.e b/library/network/protocol/content_negotiation/src/parsers/http_header_utilities.e new file mode 100644 index 00000000..99879284 --- /dev/null +++ b/library/network/protocol/content_negotiation/src/parsers/http_header_utilities.e @@ -0,0 +1,33 @@ +note + description: "Summary description for {HTTP_HEADER_UTILITIES}." + author: "" + date: "$Date$" + revision: "$Revision$" + +deferred class + HTTP_HEADER_UTILITIES + +inherit + REFACTORING_HELPER + +feature {NONE} -- Helpers + + entity_value (a_str: READABLE_STRING_8): STRING_8 + -- `s' with any trailing parameters stripped + local + p: INTEGER + do + p := a_str.index_of (';', 1) + if p > 0 then + create Result.make_from_string (a_str.substring (1, p - 1)) + else + create Result.make_from_string (a_str) + end + Result.left_adjust + Result.right_adjust + end + +note + 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 diff --git a/library/network/protocol/content_negotiation/src/results/http_accept_language.e b/library/network/protocol/content_negotiation/src/results/http_accept_language.e new file mode 100644 index 00000000..a58137c5 --- /dev/null +++ b/library/network/protocol/content_negotiation/src/results/http_accept_language.e @@ -0,0 +1,240 @@ +note + description: "Object that represents a result after parsing Language Headers." + date: "$Date$" + revision: "$Revision$" + +class + HTTP_ACCEPT_LANGUAGE + +inherit + HTTP_HEADER_UTILITIES + + REFACTORING_HELPER + + DEBUG_OUTPUT + +create + make_from_string, + make, + make_with_language, + make_default + +feature {NONE} -- Initialization + + make_from_string (a_accept_language_item: READABLE_STRING_8) + -- Instantiate Current from part of accept-language header, i.e language tag and parameters. + -- + -- Languages-ranges are languages with specialization and a 'q' quality parameter. + -- For example, the language l_range ('en-* ;q=0.5') would get parsed into: + -- ('en', '*', {'q', '0.5'}) + -- In addition this also guarantees that there is a value for 'q' + -- in the params dictionary, filling it in with a proper default if + -- necessary. + local + i: INTEGER + s: STRING + do + fixme (generator + ".make_from_string: improve code!!!") + i := a_accept_language_item.index_of (';', 1) + if i > 0 then + create s.make_from_string (a_accept_language_item.substring (1, i - 1)) + else + create s.make_from_string (a_accept_language_item) + end + s.left_adjust + s.right_adjust + make_with_language (s) + if i > 0 then + create parameters.make_from_substring (a_accept_language_item, i + 1, a_accept_language_item.count) + check attached parameters as l_params and then not l_params.has_error end + end + + check quality_initialized_to_1: quality = 1.0 end + + -- Get quality from parameter if any, and format the value as expected. + if attached parameter ("q") as q then + if q.same_string ("1") then + --| Use 1.0 formatting + put_parameter ("1.0", "q") + elseif q.is_double and then attached q.to_real_64 as r then + if r <= 0.0 then + quality := 0.0 --| Should it be 1.0 ? + put_parameter ("0.0", "q") + elseif r >= 1.0 then + quality := 1.0 + put_parameter ("1.0", "q") + else + quality := r + end + else + put_parameter ("1.0", "q") + quality := 1.0 + end + else + put_parameter ("1.0", "q") + end + end + + make_with_language (a_lang_tag: READABLE_STRING_8) + -- Instantiate Current from language tag `a_lang_tag'. + do + initialize + set_language_range (a_lang_tag) + ensure + language_range_set: language_range.same_string (a_lang_tag) and a_lang_tag /= language_range + end + + make (a_root_lang: READABLE_STRING_8; a_specialization: detachable READABLE_STRING_8) + -- Instantiate Current with `a_root_lang' and `a_specialization'. + do + initialize + create language_range.make_empty + language := a_root_lang + specialization := a_specialization + update_language_range (a_root_lang, a_specialization) + end + + make_default + -- Instantiate Current with default "*" language. + do + make ("*", Void) + end + + initialize + -- Initialize Current + do + create parameters.make (1) + quality := 1.0 + end + +feature -- Access + + language_range: STRING_8 + -- language-range = ( ( 1*8ALPHA *( "-" 1*8ALPHA ) ) | "*" ) + + language: READABLE_STRING_8 + -- First part of the language range, i.e the root language + + specialization: detachable READABLE_STRING_8 + -- Optional second part of the language range, i.e the dialect, or specialized language type + + quality: REAL_64 + -- Associated quality, by default 1.0 + +feature -- Status report + + debug_output: STRING + -- String that should be displayed in debugger to represent `Current'. + do + create Result.make_from_string (language_range) + Result.append_character (';') + Result.append ("q=") + Result.append_double (quality) + end + +feature -- Element change + + set_language_range (a_lang_range: READABLE_STRING_8) + local + i: INTEGER + do + create language_range.make_from_string (a_lang_range) + i := a_lang_range.index_of ('-', 1) + if i > 0 then + language := a_lang_range.substring (1, i - 1) + specialization := a_lang_range.substring (i + 1, a_lang_range.count) + else + language := a_lang_range + end + ensure + language_range_set: language_range.same_string (a_lang_range) and a_lang_range /= language_range + end + + set_language (a_root_lang: READABLE_STRING_8) + -- Set `'anguage' with `a_root_lang' + require + a_root_lang_attached: a_root_lang /= Void + do + language := a_root_lang + update_language_range (a_root_lang, specialization) + ensure + type_assigned: language ~ a_root_lang + end + + set_specialization (a_specialization: detachable READABLE_STRING_8) + -- Set `specialization' with `a_specialization' + do + specialization := a_specialization + update_language_range (language, a_specialization) + ensure + specialization_assigned: specialization ~ a_specialization + end + +feature -- Parameters: Access + + parameter (a_key: READABLE_STRING_8): detachable READABLE_STRING_8 + -- Parameter associated with `a_key', if present + -- otherwise default value of type `STRING' + do + if attached parameters as l_params then + Result := l_params.item (a_key) + end + end + + parameters: detachable HTTP_PARAMETER_TABLE + -- Table of all parameters for the media range + +feature -- Parameters: Status report + + has_parameter (a_key: READABLE_STRING_8): BOOLEAN + -- Is there an parameter in the parameters table with key `a_key'? + do + if attached parameters as l_params then + Result := l_params.has_key (a_key) + end + end + +feature -- Parameters: Change + + put_parameter (a_value: READABLE_STRING_8; a_key: READABLE_STRING_8) + -- Insert `a_value' with `a_key' if there is no other item + -- associated with the same key. If present, replace + -- the old value with `a_value' + local + l_parameters: like parameters + do + l_parameters := parameters + if l_parameters = Void then + create l_parameters.make (1) + parameters := l_parameters + end + l_parameters.force (a_value, a_key) + ensure + is_set: attached parameters as l_params and then (l_params.has_key (a_key) and l_params.has_item (a_value)) + end + +feature {NONE} -- Implementation + + update_language_range (a_lang: like language; a_specialization: like specialization) + -- Update `language_range' with `a_lang' and `a_specialization' + local + l_language_range: like language_range + do + l_language_range := language_range -- Reuse same object, be careful not to keep reference on existing string at first. + l_language_range.wipe_out + l_language_range.append (a_lang) + + if a_specialization /= Void then + l_language_range.append_character ('-') + l_language_range.append (a_specialization) + end + end + +invariant + valid_quality: 0.0 <= quality and quality <= 1.0 + +note + 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 diff --git a/library/network/protocol/content_negotiation/src/results/http_any_accept.e b/library/network/protocol/content_negotiation/src/results/http_any_accept.e new file mode 100644 index 00000000..13de5607 --- /dev/null +++ b/library/network/protocol/content_negotiation/src/results/http_any_accept.e @@ -0,0 +1,114 @@ +note + description: "Object that represents a results after parsing Accept(-*) headers." + date: "$Date$" + revision: "$Revision$" + +class + HTTP_ANY_ACCEPT + +inherit + HTTP_HEADER_UTILITIES + + REFACTORING_HELPER + + DEBUG_OUTPUT + +create + make_from_string + +feature -- Initialization + + make_from_string (a_string: READABLE_STRING_8) + local + s: STRING_8 + i: INTEGER + do + initialize + i := a_string.index_of (';', 1) + if i > 0 then + create s.make_from_string (a_string.substring (1, i - 1)) + else + create s.make_from_string (a_string) + end + + s.left_adjust + s.right_adjust + set_value (s) + if i > 0 then + create parameters.make_from_substring (a_string, i + 1, a_string.count) + end + end + + initialize + do + end + +feature -- Access + + value: READABLE_STRING_8 + -- Value composing an Accept(-*) header value + +feature -- Access: parameters + + parameter (a_key: READABLE_STRING_8): detachable READABLE_STRING_8 + -- Item associated with `a_key', if present + -- otherwise default value of type `STRING' + do + if attached parameters as l_parameters then + Result := l_parameters.item (a_key) + end + end + + parameters: detachable HTTP_PARAMETER_TABLE + -- Table of all parameters for the media range + +feature -- Status Report + + has_parameter (a_key: READABLE_STRING_8): BOOLEAN + -- Is there an item in the table with key `a_key'? + do + if attached parameters as l_parameters then + Result := l_parameters.has_key (a_key) + end + end + +feature -- Element change + + set_value (v: READABLE_STRING_8) + -- Set `value' with `v' + do + value := v + ensure + value_set: attached value as l_value implies l_value.same_string (v) + end + + put_parameter (a_value: READABLE_STRING_8; a_key: READABLE_STRING_8) + -- Insert `a_value' with `a_key' if there is no other item + -- associated with the same key. If present, replace + -- the old value with `a_value' + local + l_parameters: like parameters + do + l_parameters := parameters + if l_parameters = Void then + create l_parameters.make (1) + parameters := l_parameters + end + l_parameters.force (a_value, a_key) + ensure + is_set: attached parameters as l_params and then (l_params.has_key (a_key) and l_params.has_item (a_value)) + end + +feature -- Status Report + + debug_output: STRING + -- String that should be displayed in debugger to represent `Current'. + do + create Result.make_from_string (value) + end + +note + 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 diff --git a/library/network/protocol/content_negotiation/src/server_charset_negotiation.e b/library/network/protocol/content_negotiation/src/server_charset_negotiation.e new file mode 100644 index 00000000..9652fcf3 --- /dev/null +++ b/library/network/protocol/content_negotiation/src/server_charset_negotiation.e @@ -0,0 +1,92 @@ +note + description: "Summary description for {SERVER_CHARSET_NEGOTIATION}. Utility class to support Server Side Content Negotiation on charset " + 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. + ]" + EIS: "name=server driven negotiation", "src=http://www.w3.org/Protocols/rfc2616/rfc2616-sec12.html#sec12.", "protocol=uri" + +class + SERVER_CHARSET_NEGOTIATION + +inherit + REFACTORING_HELPER + +create + make + +feature {NONE} -- Initialization + + make (a_charset_dft: READABLE_STRING_8) + do + create accept_charset_utilities + set_default_charset (a_charset_dft) + ensure + default_charset_set: default_charset = a_charset_dft + end + + accept_charset_utilities: HTTP_ANY_ACCEPT_HEADER_UTILITIES + -- Charset + +feature -- Access: Server Side Defaults Formats + + default_charset: READABLE_STRING_8 + -- Character set that is acceptable for the response. + +feature -- Change Element + + set_default_charset (a_charset: READABLE_STRING_8) + -- Set `default_charset' with `a_charset' + do + default_charset := a_charset + ensure + default_charset_set: a_charset = default_charset + end + +feature -- Charset Negotiation + + preference (a_server_charset_supported: ITERABLE [READABLE_STRING_8]; a_header: detachable READABLE_STRING_8): HTTP_ACCEPT_CHARSET_VARIANTS + -- `a_server_charset_supported' represent a list of character sets supported by the server. + -- `a_header' represents the Accept-Charset header, ie, the client preferences. + -- Return which Charset to use in a response, if the server supports + -- the requested Charset, or empty in other case. + note + EIS: "name=charset", "src=http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.2", "protocol=uri" + local + l_charset_match: READABLE_STRING_8 + do + create Result.make + Result.set_supported_variants (a_server_charset_supported) + if a_header = Void or else a_header.is_empty then + -- the request has no Accept-Charset header, ie the header is empty, in this case use default charset encoding + Result.set_acceptable (True) + Result.set_variant_value (default_charset) + else + Result.set_vary_header_value + + -- select the best match, server support, client preferences + l_charset_match := accept_charset_utilities.best_match (a_server_charset_supported, a_header) + if l_charset_match.is_empty then + -- The server does not support any of the compression types prefered by the client + Result.set_acceptable (False) + else + -- Set the best match + Result.set_variant_value (l_charset_match) + Result.set_acceptable (True) + end + end + end + + +note + 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 diff --git a/library/network/protocol/content_negotiation/src/server_content_negotiation.e b/library/network/protocol/content_negotiation/src/server_content_negotiation.e new file mode 100644 index 00000000..a8f9319b --- /dev/null +++ b/library/network/protocol/content_negotiation/src/server_content_negotiation.e @@ -0,0 +1,67 @@ +note + description: "Summary description for {SERVER_CONTENT_NEGOTIATION}. Utility class to support Server Side Content Negotiation " + 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. + ]" + EIS: "name=server driven negotiation", "src=http://www.w3.org/Protocols/rfc2616/rfc2616-sec12.html#sec12.", "protocol=uri" + +class + SERVER_CONTENT_NEGOTIATION + +inherit + SERVER_MEDIA_TYPE_NEGOTIATION + rename + make as make_media_type, + preference as media_type_preference + end + + SERVER_LANGUAGE_NEGOTIATION + rename + make as make_language, + preference as language_preference + end + + SERVER_CHARSET_NEGOTIATION + rename + make as make_charset, + preference as charset_preference + end + + SERVER_ENCODING_NEGOTIATION + rename + make as make_encoding, + preference as encoding_preference + end + +create + make + +feature {NONE} -- Initialization + + make (a_mediatype_dft: READABLE_STRING_8; a_language_dft: READABLE_STRING_8; a_charset_dft: READABLE_STRING_8; a_encoding_dft: READABLE_STRING_8) + -- Initialize Current with default Media type, language, charset and encoding. + do + make_media_type (a_mediatype_dft) + make_language (a_language_dft) + make_charset (a_charset_dft) + make_encoding (a_encoding_dft) + ensure + default_media_type_set: default_media_type = a_mediatype_dft + default_language_set: default_language = a_language_dft + default_charset_set: default_charset = a_charset_dft + default_encoding_set: default_encoding = a_encoding_dft + end + +note + 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 diff --git a/library/network/protocol/content_negotiation/src/server_encoding_negotiation.e b/library/network/protocol/content_negotiation/src/server_encoding_negotiation.e new file mode 100644 index 00000000..65e04bb2 --- /dev/null +++ b/library/network/protocol/content_negotiation/src/server_encoding_negotiation.e @@ -0,0 +1,91 @@ +note + description: "Summary description for {SERVER_ENCODING_NEGOTIATION}. Utility class to support Server Side Content Negotiation on encoding" + 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. + ]" + EIS: "name=server driven negotiation", "src=http://www.w3.org/Protocols/rfc2616/rfc2616-sec12.html#sec12.", "protocol=uri" + +class + SERVER_ENCODING_NEGOTIATION + +inherit + REFACTORING_HELPER + +create + make + +feature {NONE} -- Initialization + + make (a_encoding_dft: READABLE_STRING_8) + do + create accept_encoding_utilities + set_default_encoding (a_encoding_dft) + ensure + default_encoding_set: default_encoding = a_encoding_dft + end + + accept_encoding_utilities: HTTP_ANY_ACCEPT_HEADER_UTILITIES + -- Encoding + +feature -- Access: Server Side Defaults Formats + + default_encoding: READABLE_STRING_8 + -- Content-coding that is acceptable in the response. + +feature -- Change Element + + set_default_encoding (a_encoding: READABLE_STRING_8) + -- Set `default_encoding' with `a_encoding' + do + default_encoding := a_encoding + ensure + default_encoding_set: a_encoding = default_encoding + end + +feature -- Encoding Negotiation + + preference (a_server_encoding_supported: ITERABLE [READABLE_STRING_8]; a_header_value: detachable READABLE_STRING_8): HTTP_ACCEPT_ENCODING_VARIANTS + -- `a_server_encoding_supported' represent a list of encoding supported by the server. + -- `a_header_value' represent the Accept-Encoding header, ie, the client preferences. + -- Return which Encoding to use in a response, if the server supports + -- the requested Encoding, or empty in other case. + note + EIS: "name=encoding", "src=http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.3", "protocol=uri" + local + l_compression_match: READABLE_STRING_8 + do + create Result.make + Result.set_supported_variants (a_server_encoding_supported) + if a_header_value = Void or else a_header_value.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_variant_value (default_encoding) + else + Result.set_vary_header_value + + -- select the best match, server support, client preferences + l_compression_match := accept_encoding_utilities.best_match (a_server_encoding_supported, a_header_value) + if l_compression_match.is_empty then + -- The server does not support any of the compression types prefered by the client + Result.set_acceptable (False) + else + -- Set the best match + Result.set_variant_value (l_compression_match) + Result.set_acceptable (True) + end + end + end + +note + 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 diff --git a/library/network/protocol/content_negotiation/src/server_language_negotiation.e b/library/network/protocol/content_negotiation/src/server_language_negotiation.e new file mode 100644 index 00000000..f01d51d2 --- /dev/null +++ b/library/network/protocol/content_negotiation/src/server_language_negotiation.e @@ -0,0 +1,93 @@ +note + description: "Summary description for {SERVER_LANGUAGE_NEGOTIATION}. Utility class to support Server Side Content Negotiation on language" + 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. + ]" + EIS: "name=server driven negotiation", "src=http://www.w3.org/Protocols/rfc2616/rfc2616-sec12.html#sec12.", "protocol=uri" + +class + SERVER_LANGUAGE_NEGOTIATION + +inherit + REFACTORING_HELPER + +create + make + +feature {NONE} -- Initialization + + make (a_language_dft: READABLE_STRING_8) + do + create accept_language_utilities + set_default_language (a_language_dft) + ensure + default_language_set: default_language = a_language_dft + end + + accept_language_utilities: HTTP_ACCEPT_LANGUAGE_UTILITIES + -- Language + +feature -- Access: Server Side Defaults Formats + + default_language: READABLE_STRING_8 + -- Natural language that is preferred as a response to the request. + +feature -- Change Element + + set_default_language (a_language: READABLE_STRING_8) + -- Set `default_language' with `a_language' + do + default_language := a_language + ensure + default_language_set: a_language = default_language + end + +feature -- Language Negotiation + + preference (a_server_language_supported: ITERABLE [READABLE_STRING_8]; a_header_value: detachable READABLE_STRING_8): HTTP_ACCEPT_LANGUAGE_VARIANTS + -- `a_server_language_supported' represent a list of languages supported by the server. + -- `a_header_value' represent the Accept-Language header, ie, the client preferences. + -- Return which Language to use in a response, if the server supports + -- the requested Language, or empty in other case. + note + EIS: "name=language", "src=http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.4", "protocol=uri" + + local + l_language_match: READABLE_STRING_8 + do + create Result.make + Result.set_supported_variants (a_server_language_supported) + + if a_header_value = Void or else a_header_value.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_variant_value (default_language) + else + Result.set_vary_header_value + + -- select the best match, server support, client preferences + l_language_match := accept_language_utilities.best_match (a_server_language_supported, a_header_value) + if l_language_match.is_empty then + -- The server does not support any of the media types prefered by the client + Result.set_acceptable (False) + else + -- Set the best match + Result.set_variant_value (l_language_match) + Result.set_acceptable (True) + end + end + end + +note + 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 diff --git a/library/network/protocol/content_negotiation/src/server_media_type_negotiation.e b/library/network/protocol/content_negotiation/src/server_media_type_negotiation.e new file mode 100644 index 00000000..76c84a70 --- /dev/null +++ b/library/network/protocol/content_negotiation/src/server_media_type_negotiation.e @@ -0,0 +1,91 @@ +note + description: "Summary description for {SERVER_MEDIA_TYPE_NEGOTIATION}. Utility class to support Server Side Content Negotiation on media type" + 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. + ]" + EIS: "name=server driven negotiation", "src=http://www.w3.org/Protocols/rfc2616/rfc2616-sec12.html#sec12.", "protocol=uri" + +class + SERVER_MEDIA_TYPE_NEGOTIATION + +inherit + REFACTORING_HELPER + +create + make + +feature {NONE} -- Initialization + + make (a_mediatype_dft: READABLE_STRING_8) + do + create accept_media_type_utilities + set_default_media_type (a_mediatype_dft) + ensure + default_media_type_set: default_media_type = a_mediatype_dft + end + + accept_media_type_utilities: HTTP_ACCEPT_MEDIA_TYPE_UTILITIES + -- MIME + +feature -- Access: Server Side Defaults Formats + + default_media_type: READABLE_STRING_8 + -- Media type which is acceptable for the response. + +feature -- Change Element + + set_default_media_type (a_mediatype: READABLE_STRING_8) + -- Set `default_media_type' with `a_mediatype' + do + default_media_type := a_mediatype + ensure + default_media_type_set: a_mediatype = default_media_type + end + +feature -- Media Type Negotiation + + preference (a_mime_types_supported: ITERABLE [READABLE_STRING_8]; a_header: detachable READABLE_STRING_8): HTTP_ACCEPT_MEDIA_TYPE_VARIANTS + -- `a_mime_types_supported' represent media types supported by the server. + -- `a_header represent' the Accept header, ie, the client preferences. + -- Return which media type to use for representation in a response, if the server supports + -- the requested media type, or empty in other case. + note + EIS: "name=media type", "src=http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.1", "protocol=uri" + local + l_mime_match: READABLE_STRING_8 + do + create Result.make + Result.set_supported_variants (a_mime_types_supported) + if a_header = Void or else a_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_variant_value (default_media_type) + else + Result.set_vary_header_value + + -- select the best match, server support, client preferences + l_mime_match := accept_media_type_utilities.best_match (a_mime_types_supported, a_header) + if l_mime_match.is_empty then + -- The server does not support any of the media types preferred by the client + Result.set_acceptable (False) + else + -- Set the best match + Result.set_variant_value (l_mime_match) + Result.set_acceptable (True) + end + end + end + +note + 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 diff --git a/library/network/protocol/content_negotiation/src/variants/http_accept_charset_variants.e b/library/network/protocol/content_negotiation/src/variants/http_accept_charset_variants.e new file mode 100644 index 00000000..13775ebd --- /dev/null +++ b/library/network/protocol/content_negotiation/src/variants/http_accept_charset_variants.e @@ -0,0 +1,36 @@ +note + description: "[ + {HTTP_ACCEPT_CHARSET_VARIANTS} + Represent the character sets results between client preferences and character sets variants supported by the server. + If the server is unable to supports the requested Accept-Charset values, the server can build + a response with the list of supported character sets. + ]" + + date: "$Date$" + revision: "$Revision$" + +class + HTTP_ACCEPT_CHARSET_VARIANTS + +inherit + HTTP_ACCEPT_VARIANTS + rename + variant_value as charset + end + +create + make + +feature -- Change + + set_vary_header_value + -- + do + vary_header_value := {HTTP_HEADER_NAMES}.header_accept_charset -- "Accept-Charset" + end + +note + 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 diff --git a/library/network/protocol/content_negotiation/src/variants/http_accept_encoding_variants.e b/library/network/protocol/content_negotiation/src/variants/http_accept_encoding_variants.e new file mode 100644 index 00000000..d814d5fc --- /dev/null +++ b/library/network/protocol/content_negotiation/src/variants/http_accept_encoding_variants.e @@ -0,0 +1,36 @@ +note + description: "[ + {HTTP_ACCEPT_ENCODING_VARIANTS} + Represent the encoding results between client preferences and encoding variants supported by the server. + If the server is unable to supports the requested Accept-Encoding values, the server can build + a response with the list of supported encodings + ]" + date: "$Date$" + revision: "$Revision$" + EIS: "name= Compression", "src=http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.3", "protocol=uri" + +class + HTTP_ACCEPT_ENCODING_VARIANTS + +inherit + HTTP_ACCEPT_VARIANTS + rename + variant_value as encoding + end + +create + make + +feature -- Change + + set_vary_header_value + -- + do + vary_header_value := {HTTP_HEADER_NAMES}.header_accept_encoding -- "Accept-Encoding" + end + +note + 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 diff --git a/library/network/protocol/content_negotiation/src/variants/http_accept_language_variants.e b/library/network/protocol/content_negotiation/src/variants/http_accept_language_variants.e new file mode 100644 index 00000000..78320048 --- /dev/null +++ b/library/network/protocol/content_negotiation/src/variants/http_accept_language_variants.e @@ -0,0 +1,35 @@ +note + description: "[ + {HTTP_ACCEPT_LANGUAGE_VARIANTS}. + Represent the language results between client preferences and language variants supported by the server. + If the server is unable to supports the requested Accept-Language values, the server can build + a response with the list of supported languages + ]" + date: "$Date$" + revision: "$Revision$" + +class + HTTP_ACCEPT_LANGUAGE_VARIANTS + +inherit + HTTP_ACCEPT_VARIANTS + rename + variant_value as language + end + +create + make + +feature -- Change + + set_vary_header_value + -- + do + vary_header_value := {HTTP_HEADER_NAMES}.header_accept_language -- "Accept-Language" + end + +note + 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 diff --git a/library/network/protocol/content_negotiation/src/variants/http_accept_media_type_variants.e b/library/network/protocol/content_negotiation/src/variants/http_accept_media_type_variants.e new file mode 100644 index 00000000..1f15ce31 --- /dev/null +++ b/library/network/protocol/content_negotiation/src/variants/http_accept_media_type_variants.e @@ -0,0 +1,35 @@ +note + description: "[ + {HTTP_ACCEPT_MEDIA_TYPE_VARIANTS}. + Represents the media type results between client preferences and media type variants supported by the server.. + If the server is unable to supports the requested Accept values, the server can build + a response with the list of supported representations + ]" + date: "$Date$" + revision: "$Revision$" + +class + HTTP_ACCEPT_MEDIA_TYPE_VARIANTS + +inherit + HTTP_ACCEPT_VARIANTS + rename + variant_value as media_type + end + +create + make + +feature -- Change + + set_vary_header_value + -- + do + vary_header_value := {HTTP_HEADER_NAMES}.header_accept -- "Accept" + end + +note + 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 diff --git a/library/network/protocol/content_negotiation/src/variants/http_accept_variants.e b/library/network/protocol/content_negotiation/src/variants/http_accept_variants.e new file mode 100644 index 00000000..78a39be2 --- /dev/null +++ b/library/network/protocol/content_negotiation/src/variants/http_accept_variants.e @@ -0,0 +1,92 @@ +note + description: "Generic {HTTP_ACCEPT_VARIANTS}.with common functionality to most header variants.." + date: "$Date$" + revision: "$Revision$" + +deferred class + HTTP_ACCEPT_VARIANTS + +feature {NONE} -- Initialization + + make + do + end + +feature -- Change + + set_vary_header_value + -- Set the `vary_header_value' + deferred + ensure + is_valid_header_set : is_valid_header_name (vary_header_value) + end + +feature -- Access + + vary_header_value: detachable READABLE_STRING_8 + -- Name of header to be added to the Vary header of the response + -- this indicates the Accept-* header source of the matched `variant_value' if any, + -- if this is using the default, the `vary_header_value' is Void. + + supported_variants: detachable ITERABLE [READABLE_STRING_8] + -- Set of supported variants for the response + + variant_value: detachable READABLE_STRING_8 + -- Associated value, it could be value of: + -- content type + -- language + -- character set + -- encoding. + +feature -- Status_Report + + is_acceptable: BOOLEAN + -- is the current variant accepted? + + is_valid_header_name (a_header_name: detachable READABLE_STRING_8): BOOLEAN + -- is `a_header_name' a valid accept header name? + note + EIS:"name=Accept", "src=http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.1", "protocol=uri" + EIS:"name=Accept-Charset", "src=http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.2", "protocol=uri" + EIS:"name=Accept-Encoding", "src=http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.3", "protocol=uri" + EIS:"name=Accept-Language", "src=http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.4", "protocol=uri" + do + if a_header_name /= Void then + Result := a_header_name.same_string ({HTTP_HEADER_NAMES}.header_accept) -- "Accept", + or else a_header_name.same_string ({HTTP_HEADER_NAMES}.header_accept_language) -- "Accept-Language", + or else a_header_name.same_string ({HTTP_HEADER_NAMES}.header_accept_encoding) -- "Accept-Encoding", + or else a_header_name.same_string ({HTTP_HEADER_NAMES}.header_accept_charset) -- "Accept-Charset" + end + end + +feature -- Change Element + + set_variant_value (v: READABLE_STRING_8) + -- Set `variant_value' as `v' + do + variant_value := v + ensure + type_set: attached variant_value as l_variant implies l_variant = v + end + + set_acceptable (b: BOOLEAN) + -- Set `is_acceptable' with `b' + do + is_acceptable := b + ensure + is_acceptable_set: is_acceptable = b + end + + set_supported_variants (a_supported: ITERABLE [READABLE_STRING_8]) + -- Set `supported variants' with `a_supported' + do + supported_variants := a_supported + ensure + set_supported_variants: attached supported_variants as l_supported_variants implies l_supported_variants = a_supported + end + +note + 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 diff --git a/library/network/protocol/CONNEG/test/application.e b/library/network/protocol/content_negotiation/test/application.e similarity index 83% rename from library/network/protocol/CONNEG/test/application.e rename to library/network/protocol/content_negotiation/test/application.e index 2c16ebe1..74cd00d7 100644 --- a/library/network/protocol/CONNEG/test/application.e +++ b/library/network/protocol/content_negotiation/test/application.e @@ -16,10 +16,10 @@ feature {NONE} -- Initialization make local - mime_parse : MIME_PARSE + mime_parse : HTTP_ACCEPT_MEDIA_TYPE_UTILITIES accept : STRING - charset_parse : COMMON_ACCEPT_HEADER_PARSER - language : LANGUAGE_PARSE + charset_parse : HTTP_ANY_ACCEPT_HEADER_UTILITIES + language : HTTP_ACCEPT_LANGUAGE_UTILITIES do create mime_parse -- parse_result := mime_parse.parse_mime_type ("application/xhtml;q=0.5") @@ -59,12 +59,12 @@ feature {NONE} -- Initialization print ("%N"+mime_parse.quality ("*/*;q=0.1", accept).out) accept := "application/atom+xml" - print ("%N"+mime_parse.parse_mime_type (accept).out) + print ("%N"+mime_parse.media_type (accept).out) create charset_parse accept := "iso-8859-5" - print ("%N" + charset_parse.parse_common (accept).out) + print ("%N" + charset_parse.header (accept).out) accept := "unicode-1-1;q=0.8" - print ("%N" + charset_parse.parse_common (accept).out) + print ("%N" + charset_parse.header (accept).out) accept:= "iso-8859-5, unicode-1-1;q=0.8" @@ -78,10 +78,10 @@ feature {NONE} -- Initialization print (language.best_match (accept.split (','), "da")) print (language.best_match (accept.split (','), "en-*")) - print ("%N"+language.parse_media_range ("da").out) - print ("%N"+language.parse_media_range ("en-gb;q=0.8").out) - print ("%N"+language.parse_media_range ("en;q=0.7").out) - print ("%N"+language.parse_media_range ("en-*").out) + print ("%N"+language.accept_language ("da").out) + print ("%N"+language.accept_language ("en-gb;q=0.8").out) + print ("%N"+language.accept_language ("en;q=0.7").out) + print ("%N"+language.accept_language ("en-*").out) end end diff --git a/library/network/protocol/CONNEG/test/common_accept_header_parser_test.e b/library/network/protocol/content_negotiation/test/common_accept_header_parser_test.e similarity index 54% rename from library/network/protocol/CONNEG/test/common_accept_header_parser_test.e rename to library/network/protocol/content_negotiation/test/common_accept_header_parser_test.e index ae392525..1c736576 100644 --- a/library/network/protocol/CONNEG/test/common_accept_header_parser_test.e +++ b/library/network/protocol/content_negotiation/test/common_accept_header_parser_test.e @@ -21,13 +21,33 @@ feature {NONE} -- Events create parser end +feature -- Helpers + + format (a_common: HTTP_ANY_ACCEPT): STRING + -- Representation of the current object + do + create Result.make_from_string ("(") + if attached a_common.value as t then + Result.append_string ("'" + t + "',") + end + Result.append_string (" {") + if attached a_common.parameters as l_parameters then + across + l_parameters as ic + loop + Result.append ("'" + ic.key + "':'" + ic.item + "',"); + end + end + Result.append ("})") + end + feature -- Test routines test_parse_charsets do - assert ("Expected ('iso-8859-5', {'q':'1.0',})", parser.parse_common("iso-8859-5").out.same_string("('iso-8859-5', {'q':'1.0',})") ) - assert ("Expected ('unicode-1-1', {'q':'0.8',})", parser.parse_common("unicode-1-1;q=0.8").out.same_string("('unicode-1-1', {'q':'0.8',})") ) - assert ("Expected ('*', {'q':'1.0',})", parser.parse_common("*").out.same_string("('*', {'q':'1.0',})") ) + assert ("Expected ('iso-8859-5', {'q':'1.0',})", format (parser.header("iso-8859-5")).same_string("('iso-8859-5', {'q':'1.0',})") ) + assert ("Expected ('unicode-1-1', {'q':'0.8',})", format (parser.header("unicode-1-1;q=0.8")).same_string("('unicode-1-1', {'q':'0.8',})") ) + assert ("Expected ('*', {'q':'1.0',})", format (parser.header("*")).same_string("('*', {'q':'1.0',})") ) end @@ -52,6 +72,6 @@ feature -- Test routines assert ("Expected unicode-1-1", parser.best_match (charset_supported, "unicode-1-1;q=1").same_string ("unicode-1-1")) end - parser : COMMON_ACCEPT_HEADER_PARSER + parser : HTTP_ANY_ACCEPT_HEADER_UTILITIES end diff --git a/library/network/protocol/content_negotiation/test/conneg_server_side_test.e b/library/network/protocol/content_negotiation/test/conneg_server_side_test.e new file mode 100644 index 00000000..73f4a1f3 --- /dev/null +++ b/library/network/protocol/content_negotiation/test/conneg_server_side_test.e @@ -0,0 +1,260 @@ +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 : HTTP_ACCEPT_MEDIA_TYPE_VARIANTS + 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) + if attached media_variants.supported_variants as l_supported_variants then + assert ("Same Value at 1", same_text (first_of (mime_types_supported), first_of (l_supported_variants))) + assert ("Same count", count_of (mime_types_supported) = count_of (l_supported_variants)) + else + assert ("Has supported_variants results", False) + end + assert ("Variant Header", attached media_variants.vary_header_value as l_variant_header and then l_variant_header.same_string ("Accept")) + 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 set",media_variants.supported_variants = mime_types_supported) + assert ("Mime is default", attached media_variants.media_type as l_media_type and then conneg.default_media_type.same_string (l_media_type)) + assert ("Variant header", media_variants.vary_header_value = 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/xml;q=0.5,application/json;q=0.6") + assert ("Expected Acceptable", media_variants.is_acceptable) + assert ("Variants is set",media_variants.supported_variants = mime_types_supported) + assert ("Variant Header", attached media_variants.vary_header_value as l_variant_header and then l_variant_header.same_string ("Accept")) + assert ("Media Type is application/json", attached media_variants.media_type as l_media_type and then l_media_type.same_string ("application/json")) + + end + + + + test_charset_negotiation + local + charset_variants : HTTP_ACCEPT_CHARSET_VARIANTS + charset_supported : LIST [STRING] + l_charset_value : STRING + do + -- Scenario 1, the server side does not support client preferences + l_charset_value := "UTF-8, iso-8859-5" + charset_supported := l_charset_value.split(',') + charset_variants := conneg.charset_preference (charset_supported, "unicode-1-1") + assert ("Expected Not Acceptable", not charset_variants.is_acceptable) + if attached charset_variants.supported_variants as l_supported_variants then + assert ("Same Value at 1", same_text (first_of (charset_supported), first_of (l_supported_variants))) + assert ("Same count",charset_supported.count = count_of (l_supported_variants)) + else + assert("Has supported_variants results", False) + end + assert ("Variant Header", attached charset_variants.vary_header_value as l_variant_header and then l_variant_header.same_string ("Accept-Charset")) + assert ("Character type is void",charset_variants.charset = 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 set",charset_variants.supported_variants = charset_supported) + assert ("Charset is default", attached charset_variants.charset as l_charset and then conneg.default_charset.same_string (l_charset)) + assert ("Variant header", charset_variants.vary_header_value = 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 set",charset_variants.supported_variants = charset_supported) + assert ("Variant Header", attached charset_variants.vary_header_value as l_variant_header and then l_variant_header.same_string ("Accept-Charset")) + assert ("Character Type is iso-8859-5", attached charset_variants.charset as l_charset and then l_charset.same_string ("iso-8859-5")) + end + + test_compression_negotiation + local + compression_variants : HTTP_ACCEPT_ENCODING_VARIANTS + 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) + if attached compression_variants.supported_variants as l_supported_variants then + assert ("Same Value at 1", same_text (first_of (compression_supported), first_of (l_supported_variants))) + assert ("Same count",compression_supported.count = count_of (l_supported_variants)) + else + assert ("Has supported_variants results", False) + end + assert ("Variant Header", attached compression_variants.vary_header_value as l_variant_header and then l_variant_header.same_string ("Accept-Encoding")) + assert ("Compression type is void",compression_variants.encoding = 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 set",compression_variants.supported_variants = compression_supported) + assert ("Compression is default", attached compression_variants.encoding as l_encoding and then conneg.default_encoding.same_string (l_encoding)) + assert ("Variant header", compression_variants.vary_header_value = Void) + + + --Scenario 3, the server select the best match, and set the vary header + l_compression := "gzip" + compression_supported := l_compression.split(',') + conneg.set_default_encoding("gzip") + compression_variants := conneg.encoding_preference (compression_supported, "compress,gzip;q=0.7") + assert ("Expected Acceptable", compression_variants.is_acceptable) + assert ("Variants is set",compression_variants.supported_variants = compression_supported) + assert ("Variant Header", attached compression_variants.vary_header_value as l_variant_header and then l_variant_header.same_string ("Accept-Encoding")) + assert ("Encoding Type is gzip", attached compression_variants.encoding as l_type and then l_type.same_string ("gzip")) + + -- Scenario 4, the server set `identity' and the client doesn't mention identity + l_compression := "identity" + compression_supported := l_compression.split(',') + conneg.set_default_encoding ("gzip") + compression_variants := conneg.encoding_preference (compression_supported, "gzip;q=0.7") + assert ("Expected Acceptable", compression_variants.is_acceptable) + assert ("Variants is set",compression_variants.supported_variants = compression_supported) + assert ("Variant Header", attached compression_variants.vary_header_value as l_variant_header and then l_variant_header.same_string ("Accept-Encoding")) + assert ("Encoding Type is Identity", attached compression_variants.encoding as l_type and then l_type.same_string ("identity")) + + -- Scenario 5, the server set `identity' and the client mention identity,q=0 + l_compression := "identity" + compression_supported := l_compression.split(',') + conneg.set_default_encoding ("gzip") + compression_variants := conneg.encoding_preference (compression_supported, "identity;q=0") + assert ("Expected Not Acceptable", not compression_variants.is_acceptable) + assert ("Variants is attached",attached compression_variants.supported_variants ) + assert ("Variant Header", attached compression_variants.vary_header_value as l_variant_header and then l_variant_header.same_string ("Accept-Encoding")) + assert ("Encoding Type is Void", compression_variants.encoding = Void) + + -- Scenario 6, the server set `identity' and the client mention *,q=0 + l_compression := "identity" + compression_supported := l_compression.split(',') + conneg.set_default_encoding ("gzip") + compression_variants := conneg.encoding_preference (compression_supported, "*;q=0") + assert ("Expected Not Acceptable", not compression_variants.is_acceptable) + assert ("Variants is attached",attached compression_variants.supported_variants ) + assert ("Variant Header", attached compression_variants.vary_header_value as l_variant_header and then l_variant_header.same_string ("Accept-Encoding")) + assert ("Encoding Type is Void", compression_variants.encoding = Void) + + + -- Scenario 7, the server set `identity' and the client mention identity;q=0.5, gzip;q=0.7,compress + l_compression := "identity" + compression_supported := l_compression.split(',') + conneg.set_default_encoding ("gzip") + compression_variants := conneg.encoding_preference (compression_supported, "identity;q=0.5, gzip;q=0.7,compress") + assert ("Expected Acceptable",compression_variants.is_acceptable) + assert ("Variants is set",compression_variants.supported_variants = compression_supported) + assert ("Variant Header", attached compression_variants.vary_header_value as l_variant_header and then l_variant_header.same_string ("Accept-Encoding")) + assert ("Encoding Type is identity", attached compression_variants.encoding as l_type and then l_type.same_string ("identity")) + + + -- Scenario 8, the server set `identity' and the client mention identity;q=0.5 + l_compression := "identity" + compression_supported := l_compression.split(',') + conneg.set_default_encoding ("gzip") + compression_variants := conneg.encoding_preference (compression_supported, "identity;q=0.5") + assert ("Expected Acceptable",compression_variants.is_acceptable) + assert ("Variants is set",compression_variants.supported_variants = compression_supported) + assert ("Variant Header", attached compression_variants.vary_header_value as l_variant_header and then l_variant_header.same_string ("Accept-Encoding")) + assert ("Encoding Type is identity", attached compression_variants.encoding as l_type and then l_type.same_string ("identity")) + end + + test_language_negotiation + local + language_variants : HTTP_ACCEPT_LANGUAGE_VARIANTS + 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 ("Variant Header", attached language_variants.vary_header_value as l_variant_header and then l_variant_header.same_string ("Accept-Language")) + assert ("Language type is Void",language_variants.language = Void) + if attached language_variants.supported_variants as l_supported_variants then + assert ("Same Value at 1", same_text (first_of (languages_supported), first_of (l_supported_variants))) + assert ("Same count",languages_supported.count = count_of (l_supported_variants)) + else + assert ("Has supported variants results", False) + end + + -- 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 attached",language_variants.supported_variants = languages_supported) + assert ("Language is default", attached language_variants.language as l_lang and then conneg.default_language.same_string (l_lang)) + assert ("Variant header", language_variants.vary_header_value = 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 detached",language_variants.supported_variants = languages_supported) + assert ("Variant Header", attached language_variants.vary_header_value as l_variant_header and then l_variant_header.same_string ("Accept-Language")) + assert ("Language Type is fr", attached language_variants.language as l_lang and then l_lang.same_string ("fr")) + end + +feature -- Implementation + conneg : SERVER_CONTENT_NEGOTIATION + + same_text (s1,s2: detachable READABLE_STRING_8): BOOLEAN + do + if s1 = Void then + Result := s2 = Void + elseif s2 = Void then + Result := False + else + Result := s1.same_string (s2) + end + end + + count_of (i: ITERABLE [READABLE_STRING_8]): INTEGER + do + across + i as ic + loop + Result := Result + 1 + end + end + + first_of (i: ITERABLE [READABLE_STRING_8]): detachable READABLE_STRING_8 + do + across + i as ic + until + ic.item /= Void + loop + end + end + +end diff --git a/library/network/protocol/content_negotiation/test/language_parser_test.e b/library/network/protocol/content_negotiation/test/language_parser_test.e new file mode 100644 index 00000000..fe937a20 --- /dev/null +++ b/library/network/protocol/content_negotiation/test/language_parser_test.e @@ -0,0 +1,141 @@ +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 -- Helpers + + format (a_language: HTTP_ACCEPT_LANGUAGE): STRING + -- Representation of the current object + do + create Result.make_from_string ("(") + if attached a_language.language as t then + Result.append_string ("'" + t + "',") + end + if attached a_language.specialization as st then + Result.append_string (" '" + st + "',") + end + Result.append_string (" {") + if attached a_language.parameters as l_params then + across + l_params as ic + loop + Result.append ("'" + ic.key + "':'"+ ic.item + "',"); + end + end + Result.append ("})") + end + + +feature -- Test routines + + test_parse_language + do + assert ("Expected ('da', {'q':'1.0',})", format (parser.accept_language ("da")).same_string ("('da', {'q':'1.0',})")); + assert ("Expected ('en', 'gb', {'q':'0.8',})", format (parser.accept_language ("en-gb;q=0.8")).same_string ("('en', 'gb', {'q':'0.8',})")); + assert ("Expected ('en', {'q':'0.7',})", format (parser.accept_language ("en;q=0.7")).same_string ("('en', {'q':'0.7',})")); + assert ("Expected ('en', '*', {'q':'1.0',})", format (parser.accept_language ("en-*")).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 + langs_supported : LIST [STRING] + l_types : STRING + do + l_types := "en-gb,en-us" + langs_supported := l_types.split(',') + assert ("Expected en-us", parser.best_match (langs_supported, "en-us").same_string ("en-us")) + assert ("Direct match with a q parameter", parser.best_match (langs_supported, "en-gb;q=1").same_string ("en-gb")) + assert ("Direct match second choice with a q parameter", parser.best_match (langs_supported, "en-us;q=1").same_string ("en-us")) + assert ("Direct match using a subtype wildcard", parser.best_match (langs_supported, "en-*;q=1").is_equal ("en-gb")) + assert ("Match using a type wildcard", parser.best_match (langs_supported, "*").same_string ("en-gb")) + + l_types := "en-gb,es" + langs_supported := l_types.split(',') + assert ("Match using a type versus a lower weighted subtype", parser.best_match (langs_supported, "es-*;q=0.5,*;q=0.1").same_string ("es")) + assert ("Fail to match anything",parser.best_match (langs_supported, "fr; q=0.9").same_string ("")) + + l_types := "en-gb,en-us" + langs_supported := l_types.split(',') + assert ("Test 1 verify fitness ordering", parser.best_match (langs_supported, "en-gb,en-us,*").same_string ("en-gb")) + + l_types := "es,en-gb;q=1.0,fr;q=0.6" + langs_supported := l_types.split(',') + assert ("Match default es at first position", parser.best_match (langs_supported, "es;q=1.0,*;q=0.1,fr").same_string ("es")) + + l_types := "en-gb;q=1.0,fr;q=0.6,es" + langs_supported := l_types.split(',') + assert ("Match default es at last position", parser.best_match (langs_supported, "es;q=1.0,*;q=0.1,fr").same_string ("es")) + + l_types := "en-gb;q=1.0,fr,es" + langs_supported := l_types.split(',') + assert ("Match first top quality and fitness", parser.best_match (langs_supported, "es;q=1.0,*;q=0.1,fr").same_string ("es")) + + l_types := "fr;q=1.0,en,es" + langs_supported := l_types.split(',') + assert ("Test 1", parser.best_match (langs_supported, "es;q=1.0,*/*;q=0.1,en;q=0.9").same_string ("es")) + + l_types := "fr;q=1.0,en,es" + langs_supported := l_types.split(',') + assert ("Test 1", parser.best_match (langs_supported, "es,*/*;q=0.1,en").same_string ("es")) + + l_types := "fr;q=1.0,en,es" + langs_supported := l_types.split(',') + assert ("Test 2", parser.best_match (langs_supported, "en,es,*/*;q=0.1").same_string ("en")) + + l_types := "es,en;q=0.6" + langs_supported := l_types.split(',') + assert ("Test 2", parser.best_match (langs_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 : HTTP_ACCEPT_LANGUAGE_UTILITIES + +end + + diff --git a/library/network/protocol/CONNEG/test/mime_parser_test.e b/library/network/protocol/content_negotiation/test/mime_parser_test.e similarity index 76% rename from library/network/protocol/CONNEG/test/mime_parser_test.e rename to library/network/protocol/content_negotiation/test/mime_parser_test.e index ef6de97f..2a861868 100644 --- a/library/network/protocol/CONNEG/test/mime_parser_test.e +++ b/library/network/protocol/content_negotiation/test/mime_parser_test.e @@ -24,19 +24,42 @@ feature {NONE} -- Events create parser end +feature -- Helper + + format (a_mediatype: HTTP_MEDIA_TYPE): STRING + -- Representation of the current object + do + create Result.make_from_string ("(") + if attached a_mediatype.type as t then + Result.append_string ("'" + t + "',") + end + if attached a_mediatype.subtype as st then + Result.append_string (" '" + st + "',") + end + Result.append_string (" {") + if attached a_mediatype.parameters as l_params then + across + l_params as ic + loop + Result.append ("'" + ic.key + "':'" + ic.item + "',"); + end + end + Result.append ("})") + end + feature -- Test routines test_parse_media_range do - assert ("Expected ('application', 'xml', {'q':'1',})", parser.parse_media_range("application/xml;q=1").out.same_string("('application', 'xml', {'q':'1.0',})") ) + assert ("Expected ('application', 'xml', {'q':'1',})", format (parser.media_type("application/xml;q=1")).same_string("('application', 'xml', {'q':'1.0',})") ) - assert ("Expected ('application', 'xml', {'q':'1',})", parser.parse_media_range("application/xml").out.same_string("('application', 'xml', {'q':'1.0',})") ) - assert ("Expected ('application', 'xml', {'q':'1',})", parser.parse_media_range("application/xml;q=").out.same_string("('application', 'xml', {'q':'1.0',})") ) - assert ("Expected ('application', 'xml', {'q':'1',})", parser.parse_media_range("application/xml ; q=").out.same_string("('application', 'xml', {'q':'1.0',})") ) - assert ("Expected ('application', 'xml', {'q':'1','b':'other',})", parser.parse_media_range("application/xml ; q=1;b=other").out.same_string("('application', 'xml', {'q':'1.0','b':'other',})") ) - assert ("Expected ('application', 'xml', {'q':'1','b':'other',})", parser.parse_media_range("application/xml ; q=2;b=other").out.same_string("('application', 'xml', {'q':'1.0','b':'other',})") ) + assert ("Expected ('application', 'xml', {'q':'1',})", format (parser.media_type("application/xml")).same_string("('application', 'xml', {'q':'1.0',})") ) + assert ("Expected ('application', 'xml', {'q':'1',})", format (parser.media_type("application/xml;q=")).same_string("('application', 'xml', {'q':'1.0',})") ) + assert ("Expected ('application', 'xml', {'q':'1',})", format (parser.media_type("application/xml ; q=")).same_string("('application', 'xml', {'q':'1.0',})") ) + assert ("Expected ('application', 'xml', {'q':'1','b':'other',})", format (parser.media_type("application/xml ; q=1;b=other")).same_string("('application', 'xml', {'q':'1.0','b':'other',})") ) + assert ("Expected ('application', 'xml', {'q':'1','b':'other',})", format (parser.media_type("application/xml ; q=2;b=other")).same_string("('application', 'xml', {'q':'1.0','b':'other',})") ) -- Accept header that includes * - assert ("Expected ('*', '*', {'q':'.2',})", parser.parse_media_range(" *; q=.2").out.same_string("('*', '*', {'q':'.2',})")) + assert ("Expected ('*', '*', {'q':'.2',})", format (parser.media_type(" *; q=.2")).same_string("('*', '*', {'q':'.2',})")) end @@ -122,7 +145,7 @@ feature -- Test routines - parser : MIME_PARSE + parser : HTTP_ACCEPT_MEDIA_TYPE_UTILITIES end diff --git a/library/network/protocol/CONNEG/test/test-safe.ecf b/library/network/protocol/content_negotiation/test/test-safe.ecf similarity index 50% rename from library/network/protocol/CONNEG/test/test-safe.ecf rename to library/network/protocol/content_negotiation/test/test-safe.ecf index 9fb46bd4..996f55a6 100644 --- a/library/network/protocol/CONNEG/test/test-safe.ecf +++ b/library/network/protocol/content_negotiation/test/test-safe.ecf @@ -1,18 +1,23 @@ - + + /.git$ /EIFGENs$ /CVS$ /.svn$ - /.git$ - - + + + + diff --git a/library/network/protocol/CONNEG/test/test.ecf b/library/network/protocol/content_negotiation/test/test.ecf similarity index 93% rename from library/network/protocol/CONNEG/test/test.ecf rename to library/network/protocol/content_negotiation/test/test.ecf index 88d3dec7..6da3deb1 100644 --- a/library/network/protocol/CONNEG/test/test.ecf +++ b/library/network/protocol/content_negotiation/test/test.ecf @@ -12,6 +12,7 @@ + diff --git a/library/network/protocol/http/http-safe.ecf b/library/network/protocol/http/http-safe.ecf index 024bdb8c..4cffe316 100644 --- a/library/network/protocol/http/http-safe.ecf +++ b/library/network/protocol/http/http-safe.ecf @@ -1,5 +1,5 @@ - + diff --git a/library/network/protocol/http/package.iron b/library/network/protocol/http/package.iron new file mode 100644 index 00000000..b1045d62 --- /dev/null +++ b/library/network/protocol/http/package.iron @@ -0,0 +1,15 @@ +package http + +project + http = "http-safe.ecf" + http = "http.ecf" + +note +-- title: +-- description: +-- tags: +-- license: +-- copyright: +-- link[doc]: "Documentation" http:// + +end diff --git a/library/network/protocol/http/src/http_content_type.e b/library/network/protocol/http/src/http_content_type.e index 0a819aac..b159ddc7 100644 --- a/library/network/protocol/http/src/http_content_type.e +++ b/library/network/protocol/http/src/http_content_type.e @@ -4,6 +4,7 @@ note ]" date: "$Date$" revision: "$Revision$" + EIS: "name=RFC3875", "protocol=URI", "src=http://tools.ietf.org/html/rfc3875" class HTTP_CONTENT_TYPE @@ -34,7 +35,7 @@ feature -- Constant charset_parameter_name: STRING_8 = "charset" note - copyright: "2011-2012, Jocelyn Fiat, Eiffel Software and others" + copyright: "2011-2013, Jocelyn Fiat, Eiffel Software and others" license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" source: "[ Eiffel Software diff --git a/library/network/protocol/http/src/http_header.e b/library/network/protocol/http/src/http_header.e index d70ac062..db797f9a 100644 --- a/library/network/protocol/http/src/http_header.e +++ b/library/network/protocol/http/src/http_header.e @@ -1,8 +1,9 @@ note description: "[ - The class provides an easy way to build HTTP header. + The class represents a HTTP header, and it provides simple routine + to build it. - You will also find some helper feature to help coding most common usage + You will also find some helper features to help coding most common usages Please, have a look at constants classes such as HTTP_MIME_TYPES @@ -24,6 +25,8 @@ class inherit ITERABLE [READABLE_STRING_8] + HTTP_HEADER_MODIFIER + create make, make_with_count, @@ -116,6 +119,8 @@ feature -- Access result_has_single_ending_cr_lf: Result.count >= 4 implies not Result.substring (Result.count - 3, Result.count).same_string ("%R%N%R%N") end +feature -- Conversion + to_name_value_iterable: ITERABLE [TUPLE [name: READABLE_STRING_8; value: READABLE_STRING_8]] -- Iterable representation of the header entries. local @@ -132,7 +137,7 @@ feature -- Access Result := res end -feature -- Conversion +feature -- append_string_to (a_result: STRING_8) -- Append current as string representation to `a_result' @@ -250,61 +255,6 @@ feature -- Header: merging end end -feature -- Status report - - has, has_header_named (a_name: READABLE_STRING_8): BOOLEAN - -- Has header item for `n'? - do - Result := across headers as c some has_same_header_name (c.item, a_name) end - end - - has_content_length: BOOLEAN - -- Has header "Content-Length" - do - Result := has_header_named ({HTTP_HEADER_NAMES}.header_content_length) - end - - has_content_type: BOOLEAN - -- Has header "Content-Type" - do - Result := has_header_named ({HTTP_HEADER_NAMES}.header_content_type) - end - - has_transfer_encoding_chunked: BOOLEAN - -- Has "Transfer-Encoding: chunked" header - do - if has_header_named ({HTTP_HEADER_NAMES}.header_transfer_encoding) then - Result := attached header_named_value ({HTTP_HEADER_NAMES}.header_transfer_encoding) as v and then v.same_string (str_chunked) - end - end - -feature -- Access - - header_named_value (a_name: READABLE_STRING_8): detachable STRING_8 - -- First header item found for `a_name' if any - require - has_header: has_header_named (a_name) - local - c: like headers.new_cursor - n: INTEGER - l_line: READABLE_STRING_8 - do - from - n := a_name.count - c := headers.new_cursor - until - c.after or Result /= Void - loop - l_line := c.item - if has_same_header_name (l_line, a_name) then - Result := l_line.substring (n + 2, l_line.count) - Result.left_adjust - Result.right_adjust - end - c.forth - end - end - feature -- Removal remove_header_named (a_name: READABLE_STRING_8) @@ -335,375 +285,16 @@ feature -- Header change: general -- Add header `h' -- if it already exists, there will be multiple header with same name -- which can also be valid - require - h_not_empty: not h.is_empty do headers.force (h) end put_header (h: READABLE_STRING_8) -- Add header `h' or replace existing header of same header name - require - h_not_empty: not h.is_empty do force_header_by_name (header_name_colon (h), h) end - add_header_key_value (k,v: READABLE_STRING_8) - -- Add header `k:v'. - -- If it already exists, there will be multiple header with same name - -- which can also be valid - local - s: STRING_8 - do - create s.make (k.count + 2 + v.count) - s.append (k) - s.append (colon_space) - s.append (v) - add_header (s) - ensure - added: has_header_named (k) - end - - put_header_key_value (k,v: READABLE_STRING_8) - -- Add header `k:v', or replace existing header of same header name/key - local - s: STRING_8 - do - create s.make (k.count + 2 + v.count) - s.append (k) - s.append (colon_space) - s.append (v) - put_header (s) - ensure - added: has_header_named (k) - end - - put_header_key_values (k: READABLE_STRING_8; a_values: ITERABLE [READABLE_STRING_8]; a_separator: detachable READABLE_STRING_8) - -- Add header `k: a_values', or replace existing header of same header values/key. - -- Use `comma_space' as default separator if `a_separator' is Void or empty. - local - s: STRING_8 - l_separator: READABLE_STRING_8 - do - if a_separator /= Void and then not a_separator.is_empty then - l_separator := a_separator - else - l_separator := comma_space - end - create s.make_empty - across - a_values as c - loop - if not s.is_empty then - s.append_string (l_separator) - end - s.append (c.item) - end - if not s.is_empty then - put_header_key_value (k, s) - end - ensure - added: has_header_named (k) - end - -feature -- Content related header - - put_content_type (t: READABLE_STRING_8) - do - put_header_key_value ({HTTP_HEADER_NAMES}.header_content_type, t) - end - - add_content_type (t: READABLE_STRING_8) - -- same as `put_content_type', but allow multiple definition of "Content-Type" - do - add_header_key_value ({HTTP_HEADER_NAMES}.header_content_type, t) - end - - put_content_type_with_parameters (t: READABLE_STRING_8; a_params: detachable ARRAY [TUPLE [name: READABLE_STRING_8; value: READABLE_STRING_8]]) - local - s: STRING_8 - do - if a_params /= Void and then not a_params.is_empty then - create s.make_from_string (t) - across - a_params as p - loop - if attached p.item as nv then - s.append_character (';') - s.append_character (' ') - s.append (nv.name) - s.append_character ('=') - s.append_character ('%"') - s.append (nv.value) - s.append_character ('%"') - end - end - put_header_key_value ({HTTP_HEADER_NAMES}.header_content_type, s) - else - put_content_type (t) - end - end - - add_content_type_with_parameters (t: READABLE_STRING_8; a_params: detachable ARRAY [TUPLE [name: READABLE_STRING_8; value: READABLE_STRING_8]]) - local - s: STRING_8 - do - if a_params /= Void and then not a_params.is_empty then - create s.make_from_string (t) - across - a_params as p - loop - if attached p.item as nv then - s.append_character (';') - s.append_character (' ') - s.append (nv.name) - s.append_character ('=') - s.append_character ('%"') - s.append (nv.value) - s.append_character ('%"') - end - end - add_header_key_value ({HTTP_HEADER_NAMES}.header_content_type, s) - else - add_content_type (t) - end - end - - put_content_type_with_charset (t: READABLE_STRING_8; c: READABLE_STRING_8) - do - put_content_type_with_parameters (t, <<["charset", c]>>) - end - - add_content_type_with_charset (t: READABLE_STRING_8; c: READABLE_STRING_8) - -- same as `put_content_type_with_charset', but allow multiple definition of "Content-Type" - do - add_content_type_with_parameters (t, <<["charset", c]>>) - end - - put_content_type_with_name (t: READABLE_STRING_8; n: READABLE_STRING_8) - do - put_content_type_with_parameters (t, <<["name", n]>>) - end - - add_content_type_with_name (t: READABLE_STRING_8; n: READABLE_STRING_8) - -- same as `put_content_type_with_name', but allow multiple definition of "Content-Type" - do - add_content_type_with_parameters (t, <<["name", n]>>) - end - - put_content_length (n: INTEGER) - do - put_header_key_value ({HTTP_HEADER_NAMES}.header_content_length, n.out) - end - - put_content_transfer_encoding (a_mechanism: READABLE_STRING_8) - -- Put "Content-Transfer-Encoding" header with for instance "binary" - --| encoding := "Content-Transfer-Encoding" ":" mechanism - --| - --| mechanism := "7bit" ; case-insensitive - --| / "quoted-printable" - --| / "base64" - --| / "8bit" - --| / "binary" - --| / x-token - do - put_header_key_value ({HTTP_HEADER_NAMES}.header_content_transfer_encoding, a_mechanism) - end - - put_content_language (a_lang: READABLE_STRING_8) - -- Put "Content-Language" header of value `a_lang'. - do - put_header_key_value ({HTTP_HEADER_NAMES}.header_content_language, a_lang) - end - - put_content_encoding (a_enc: READABLE_STRING_8) - -- Put "Content-Encoding" header of value `a_enc'. - do - put_header_key_value ({HTTP_HEADER_NAMES}.header_content_encoding, a_enc) - end - - put_transfer_encoding (a_enc: READABLE_STRING_8) - -- Put "Transfer-Encoding" header with for instance "chunked" - do - put_header_key_value ({HTTP_HEADER_NAMES}.header_transfer_encoding, a_enc) - end - - put_transfer_encoding_binary - -- Put "Transfer-Encoding: binary" header - do - put_transfer_encoding (str_binary) - end - - put_transfer_encoding_chunked - -- Put "Transfer-Encoding: chunked" header - do - put_transfer_encoding (str_chunked) - end - - put_content_disposition (a_type: READABLE_STRING_8; a_params: detachable READABLE_STRING_8) - -- Put "Content-Disposition" header - --| See RFC2183 - --| disposition := "Content-Disposition" ":" - --| disposition-type - --| *(";" disposition-parm) - --| disposition-type := "inline" - --| / "attachment" - --| / extension-token - --| ; values are not case-sensitive - --| disposition-parm := filename-parm - --| / creation-date-parm - --| / modification-date-parm - --| / read-date-parm - --| / size-parm - --| / parameter - --| filename-parm := "filename" "=" value - --| creation-date-parm := "creation-date" "=" quoted-date-time - --| modification-date-parm := "modification-date" "=" quoted-date-time - --| read-date-parm := "read-date" "=" quoted-date-time - --| size-parm := "size" "=" 1*DIGIT - --| quoted-date-time := quoted-string - --| ; contents MUST be an RFC 822 `date-time' - --| ; numeric timezones (+HHMM or -HHMM) MUST be used - do - if a_params /= Void then - put_header_key_value ({HTTP_HEADER_NAMES}.header_content_disposition, a_type + semi_colon_space + a_params) - else - put_header_key_value ({HTTP_HEADER_NAMES}.header_content_disposition, a_type) - end - end - -feature -- Content-type helpers - - put_content_type_text_css do put_content_type ({HTTP_MIME_TYPES}.text_css) end - put_content_type_text_csv do put_content_type ({HTTP_MIME_TYPES}.text_csv) end - put_content_type_text_html do put_content_type ({HTTP_MIME_TYPES}.text_html) end - put_content_type_text_javascript do put_content_type ({HTTP_MIME_TYPES}.text_javascript) end - put_content_type_text_json do put_content_type ({HTTP_MIME_TYPES}.text_json) end - put_content_type_text_plain do put_content_type ({HTTP_MIME_TYPES}.text_plain) end - put_content_type_text_xml do put_content_type ({HTTP_MIME_TYPES}.text_xml) end - - put_content_type_application_json do put_content_type ({HTTP_MIME_TYPES}.application_json) end - put_content_type_application_javascript do put_content_type ({HTTP_MIME_TYPES}.application_javascript) end - put_content_type_application_zip do put_content_type ({HTTP_MIME_TYPES}.application_zip) end - put_content_type_application_pdf do put_content_type ({HTTP_MIME_TYPES}.application_pdf) end - - put_content_type_image_gif do put_content_type ({HTTP_MIME_TYPES}.image_gif) end - put_content_type_image_png do put_content_type ({HTTP_MIME_TYPES}.image_png) end - put_content_type_image_jpg do put_content_type ({HTTP_MIME_TYPES}.image_jpg) end - put_content_type_image_svg_xml do put_content_type ({HTTP_MIME_TYPES}.image_svg_xml) end - - put_content_type_message_http do put_content_type ({HTTP_MIME_TYPES}.message_http) end - - put_content_type_multipart_mixed do put_content_type ({HTTP_MIME_TYPES}.multipart_mixed) end - put_content_type_multipart_alternative do put_content_type ({HTTP_MIME_TYPES}.multipart_alternative) end - put_content_type_multipart_related do put_content_type ({HTTP_MIME_TYPES}.multipart_related) end - put_content_type_multipart_form_data do put_content_type ({HTTP_MIME_TYPES}.multipart_form_data) end - put_content_type_multipart_signed do put_content_type ({HTTP_MIME_TYPES}.multipart_signed) end - put_content_type_multipart_encrypted do put_content_type ({HTTP_MIME_TYPES}.multipart_encrypted) end - put_content_type_application_x_www_form_encoded do put_content_type ({HTTP_MIME_TYPES}.application_x_www_form_encoded) end - -feature -- Cross-Origin Resource Sharing - - put_access_control_allow_origin (s: READABLE_STRING_8) - -- Put "Access-Control-Allow-Origin" header. - do - put_header_key_value ({HTTP_HEADER_NAMES}.header_access_control_allow_origin, s) - end - - put_access_control_allow_all_origin - -- Put "Access-Control-Allow-Origin: *" header. - do - put_access_control_allow_origin ("*") - end - - put_access_control_allow_methods (a_methods: ITERABLE [READABLE_STRING_8]) - -- If `a_methods' is not empty, put `Access-Control-Allow-Methods' header with list `a_methods' of methods - do - put_header_key_values ({HTTP_HEADER_NAMES}.header_access_control_allow_methods, a_methods, Void) - end - - put_access_control_allow_headers (s: READABLE_STRING_8) - -- Put "Access-Control-Allow-Headers" header. - do - put_header_key_value ({HTTP_HEADER_NAMES}.header_access_control_allow_headers, s) - end - -feature -- Method related - - put_allow (a_methods: ITERABLE [READABLE_STRING_8]) - -- If `a_methods' is not empty, put `Allow' header with list `a_methods' of methods - do - put_header_key_values ({HTTP_HEADER_NAMES}.header_allow, a_methods, Void) - end - -feature -- Date - - put_date (s: READABLE_STRING_8) - -- Put "Date: " header - do - put_header_key_value ({HTTP_HEADER_NAMES}.header_date, s) - end - - put_current_date - -- Put current date time with "Date" header - do - put_utc_date (create {DATE_TIME}.make_now_utc) - end - - 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 (a_utc_date)) - end - - 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 (a_utc_date)) - end - -feature -- Authorization - - put_authorization (s: READABLE_STRING_8) - -- Put authorization `s' with "Authorization" header - do - put_header_key_value ({HTTP_HEADER_NAMES}.header_authorization, s) - end - -feature -- Others - - put_expires (sec: INTEGER) - do - 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) - -- `s' could be for instance "no-cache, must-revalidate" - do - put_header_key_value ("Cache-Control", s) - end - - put_pragma (s: READABLE_STRING_8) - do - put_header_key_value ("Pragma", s) - end - - put_pragma_no_cache - do - put_pragma ("no-cache") - end - feature -- Redirection remove_location @@ -712,79 +303,7 @@ feature -- Redirection remove_header_named ({HTTP_HEADER_NAMES}.header_location) end - put_location (a_location: READABLE_STRING_8) - -- Tell the client the new location `a_location' - require - a_location_valid: not a_location.is_empty - do - put_header_key_value ({HTTP_HEADER_NAMES}.header_location, a_location) - end - - put_refresh (a_location: READABLE_STRING_8; a_timeout_in_seconds: INTEGER) - -- Tell the client to refresh page with `a_location' after `a_timeout_in_seconds' in seconds - require - a_location_valid: not a_location.is_empty - do - put_header_key_value ({HTTP_HEADER_NAMES}.header_refresh, a_timeout_in_seconds.out + "; url=" + a_location) - end - -feature -- Cookie - - put_cookie (key, value: READABLE_STRING_8; expiration, path, domain: detachable READABLE_STRING_8; secure, http_only: BOOLEAN) - -- Set a cookie on the client's machine - -- with key 'key' and value 'value'. - -- Note: you should avoid using "localhost" as `domain' for local cookies - -- since they are not always handled by browser (for instance Chrome) - require - make_sense: (key /= Void and value /= Void) and then (not key.is_empty and not value.is_empty) - domain_without_port_info: domain /= Void implies domain.index_of (':', 1) = 0 - local - s: STRING - do - s := {HTTP_HEADER_NAMES}.header_set_cookie + colon_space + key + "=" + value - if - domain /= Void and then not domain.same_string ("localhost") - then - s.append ("; Domain=") - s.append (domain) - end - if path /= Void then - s.append ("; Path=") - s.append (path) - end - if expiration /= Void then - s.append ("; Expires=") - s.append (expiration) - end - if secure then - s.append ("; Secure") - end - if http_only then - s.append ("; HttpOnly") - end - add_header (s) - end - - put_cookie_with_expiration_date (key, value: READABLE_STRING_8; expiration: DATE_TIME; path, domain: detachable READABLE_STRING_8; secure, http_only: BOOLEAN) - -- Set a cookie on the client's machine - -- with key 'key' and value 'value'. - require - make_sense: (key /= Void and value /= Void) and then (not key.is_empty and not value.is_empty) - do - put_cookie (key, value, date_to_rfc1123_http_date_format (expiration), path, domain, secure, http_only) - end - -feature {NONE} -- Implementation: Header - - has_same_header_name (h: READABLE_STRING_8; a_name: READABLE_STRING_8): BOOLEAN - -- Header line `h' has same name as `a_name' ? - do - if h.starts_with (a_name) then - if h.valid_index (a_name.count + 1) then - Result := h[a_name.count + 1] = ':' - end - end - end +feature {NONE} -- Implementation: Header change force_header_by_name (n: detachable READABLE_STRING_8; h: READABLE_STRING_8) -- Add header `h' or replace existing header of same header name `n' @@ -812,6 +331,27 @@ feature {NONE} -- Implementation: Header end end +feature {NONE} -- Implementation: Header conversion + + append_line_to (a_line: READABLE_STRING_8; h: STRING_8) + -- Append header line `a_line' to string `h'. + --| this is used to build the header text + require + not_ending_with_new_line: not a_line.ends_with_general ("%N") + do + h.append_string (a_line) + append_end_of_line_to (h) + end + + append_end_of_line_to (h: STRING_8) + -- Append the CRLN end of header line to string `h'. + do + h.append_character ('%R') + h.append_character ('%N') + end + +feature {NONE} -- Implementation: Header queries + header_name_colon (h: READABLE_STRING_8): detachable STRING_8 -- If any, header's name with colon --| ex: for "Foo-bar: something", this will return "Foo-bar:" @@ -871,53 +411,8 @@ feature {NONE} -- Implementation: Header end end -feature {NONE} -- Implementation - - append_line_to (s: READABLE_STRING_8; h: STRING_8) - do - h.append_string (s) - append_end_of_line_to (h) - end - - append_end_of_line_to (h: STRING_8) - do - h.append_character ('%R') - h.append_character ('%N') - end - -feature -- Access - - date_to_rfc1123_http_date_format (dt: DATE_TIME): STRING_8 - -- String representation of `dt' using the RFC 1123 - local - d: HTTP_DATE - do - create d.make_from_date_time (dt) - Result := d.string - end - -feature {NONE} -- Constants - - str_binary: STRING = "binary" - str_chunked: STRING = "chunked" - - colon_space: IMMUTABLE_STRING_8 - once - create Result.make_from_string (": ") - end - - semi_colon_space: IMMUTABLE_STRING_8 - once - create Result.make_from_string ("; ") - end - - comma_space: IMMUTABLE_STRING_8 - once - create Result.make_from_string (", ") - end - note - copyright: "2011-2013, Jocelyn Fiat, Eiffel Software and others" + copyright: "2011-2014, Jocelyn Fiat, Eiffel Software and others" license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" source: "[ Eiffel Software diff --git a/library/network/protocol/http/src/http_header_modifier.e b/library/network/protocol/http/src/http_header_modifier.e new file mode 100644 index 00000000..3af4e45b --- /dev/null +++ b/library/network/protocol/http/src/http_header_modifier.e @@ -0,0 +1,667 @@ +note + description: "[ + The class provides an easy way to build and modify HTTP header text + thanks to add_header (..) and put_header (..) + + You will also find some helper features to help coding most common usages + + Please, have a look at constants classes such as + HTTP_MIME_TYPES + HTTP_HEADER_NAMES + HTTP_STATUS_CODE + HTTP_REQUEST_METHODS + (or HTTP_CONSTANTS which groups them for convenience) + + Note the return status code is not part of the HTTP header + ]" + legal: "See notice at end of class." + status: "See notice at end of class." + date: "$Date$" + revision: "$Revision$" + +deferred class + HTTP_HEADER_MODIFIER + +inherit + ITERABLE [READABLE_STRING_8] + +feature -- Access: deferred + + new_cursor: INDEXABLE_ITERATION_CURSOR [READABLE_STRING_8] + -- Fresh cursor associated with current structure. + deferred + end + +feature -- Header change: deferred + + add_header (h: READABLE_STRING_8) + -- Add header `h' + -- if it already exists, there will be multiple header with same name + -- which can also be valid + require + h_not_empty: h /= Void and then not h.is_empty + deferred + end + + put_header (h: READABLE_STRING_8) + -- Add header `h' or replace existing header of same header name + require + h_not_empty: h /= Void and then not h.is_empty + deferred + end + +feature -- Status report + + has, has_header_named (a_name: READABLE_STRING_8): BOOLEAN + -- Has header item for `n'? + local + ic: like new_cursor + do + from + ic := new_cursor + until + ic.after or Result + loop + Result := has_same_header_name (ic.item, a_name) + ic.forth + end + end + + has_content_length: BOOLEAN + -- Has header "Content-Length" + do + Result := has_header_named ({HTTP_HEADER_NAMES}.header_content_length) + end + + has_content_type: BOOLEAN + -- Has header "Content-Type" + do + Result := has_header_named ({HTTP_HEADER_NAMES}.header_content_type) + end + + has_transfer_encoding_chunked: BOOLEAN + -- Has "Transfer-Encoding: chunked" header + do + if has_header_named ({HTTP_HEADER_NAMES}.header_transfer_encoding) then + Result := attached item ({HTTP_HEADER_NAMES}.header_transfer_encoding) as v and then v.same_string (str_chunked) + end + end + +feature -- Access + + item alias "[]" (a_header_name: READABLE_STRING_8): detachable READABLE_STRING_8 assign force + -- First header item found for `a_name' if any + local + res: STRING_8 + n: INTEGER + l_line: READABLE_STRING_8 + ic: like new_cursor + do + n := a_header_name.count + + from + ic := new_cursor + until + ic.after or Result /= Void + loop + l_line := ic.item + if has_same_header_name (l_line, a_header_name) then + res := l_line.substring (n + 2, l_line.count) + res.left_adjust + res.right_adjust + Result := res + end + ic.forth + end + end + + header_named_value (a_name: READABLE_STRING_8): like item + -- First header item found for `a_name' if any + obsolete + "Use `item' [2014-03]" + do + Result := item (a_name) + end + +feature -- Header change: general + + force (a_value: detachable READABLE_STRING_8; a_header_name: READABLE_STRING_8) + -- Put header `a_header_name:a_value' or replace existing header of name `a_header_name'. + --| this is used as assigner for `item' + do + if a_value = Void then + put_header_key_value (a_header_name, "") + else + put_header_key_value (a_header_name, a_value) + end + end + + add_header_key_value (a_header_name, a_value: READABLE_STRING_8) + -- Add header `a_header_name:a_value'. + -- If it already exists, there will be multiple header with same name + -- which can also be valid + local + s: STRING_8 + do + create s.make (a_header_name.count + 2 + a_value.count) + s.append (a_header_name) + s.append (colon_space) + s.append (a_value) + add_header (s) + ensure + added: has_header_named (a_header_name) + end + + put_header_key_value (a_header_name, a_value: READABLE_STRING_8) + -- Add header `a_header_name:a_value', or replace existing header of same header name/key + local + s: STRING_8 + do + create s.make (a_header_name.count + 2 + a_value.count) + s.append (a_header_name) + s.append (colon_space) + s.append (a_value) + put_header (s) + ensure + added: has_header_named (a_header_name) + end + + put_header_key_values (a_header_name: READABLE_STRING_8; a_values: ITERABLE [READABLE_STRING_8]; a_separator: detachable READABLE_STRING_8) + -- Add header `a_header_name: a_values', or replace existing header of same header values/key. + -- Use `comma_space' as default separator if `a_separator' is Void or empty. + local + s: STRING_8 + l_separator: READABLE_STRING_8 + do + if a_separator /= Void and then not a_separator.is_empty then + l_separator := a_separator + else + l_separator := comma_space + end + create s.make_empty + across + a_values as c + loop + if not s.is_empty then + s.append_string (l_separator) + end + s.append (c.item) + end + if not s.is_empty then + put_header_key_value (a_header_name, s) + end + ensure + added: has_header_named (a_header_name) + end + +feature -- Content related header + + put_content_type (a_content_type: READABLE_STRING_8) + -- Put header line "Content-Type:" + type `a_content_type' + do + put_header_key_value ({HTTP_HEADER_NAMES}.header_content_type, a_content_type) + end + + add_content_type (a_content_type: READABLE_STRING_8) + -- same as `put_content_type', but allow multiple definition of "Content-Type" + do + add_header_key_value ({HTTP_HEADER_NAMES}.header_content_type, a_content_type) + end + + put_content_type_with_parameters (a_content_type: READABLE_STRING_8; a_params: detachable ARRAY [TUPLE [name: READABLE_STRING_8; value: READABLE_STRING_8]]) + -- Put header line "Content-Type:" + type `a_content_type' and extra paramaters `a_params' + --| note: see `put_content_type_with_charset' for examples. + local + s: STRING_8 + do + if a_params /= Void and then not a_params.is_empty then + create s.make_from_string (a_content_type) + across + a_params as p + loop + if attached p.item as nv then + s.append_character (';') + s.append_character (' ') + s.append (nv.name) + s.append_character ('=') + s.append_character ('%"') + s.append (nv.value) + s.append_character ('%"') + end + end + put_header_key_value ({HTTP_HEADER_NAMES}.header_content_type, s) + else + put_content_type (a_content_type) + end + end + + add_content_type_with_parameters (a_content_type: READABLE_STRING_8; a_params: detachable ARRAY [TUPLE [name: READABLE_STRING_8; value: READABLE_STRING_8]]) + -- Add header line "Content-Type:" + type `a_content_type' and extra paramaters `a_params'. + --| note: see `put_content_type_with_charset' for examples. + local + s: STRING_8 + do + if a_params /= Void and then not a_params.is_empty then + create s.make_from_string (a_content_type) + across + a_params as p + loop + if attached p.item as nv then + s.append_character (';') + s.append_character (' ') + s.append (nv.name) + s.append_character ('=') + s.append_character ('%"') + s.append (nv.value) + s.append_character ('%"') + end + end + add_header_key_value ({HTTP_HEADER_NAMES}.header_content_type, s) + else + add_content_type (a_content_type) + end + end + + put_content_type_with_charset (a_content_type: READABLE_STRING_8; a_charset: READABLE_STRING_8) + -- Put content type `a_content_type' with `a_charset' as "charset" parameter. + do + put_content_type_with_parameters (a_content_type, <<["charset", a_charset]>>) + end + + add_content_type_with_charset (a_content_type: READABLE_STRING_8; a_charset: READABLE_STRING_8) + -- Same as `put_content_type_with_charset', but allow multiple definition of "Content-Type". + do + add_content_type_with_parameters (a_content_type, <<["charset", a_charset]>>) + end + + put_content_type_with_name (a_content_type: READABLE_STRING_8; a_name: READABLE_STRING_8) + -- Put content type `a_content_type' with `a_name' as "name" parameter. + do + put_content_type_with_parameters (a_content_type, <<["name", a_name]>>) + end + + add_content_type_with_name (a_content_type: READABLE_STRING_8; a_name: READABLE_STRING_8) + -- same as `put_content_type_with_name', but allow multiple definition of "Content-Type" + do + add_content_type_with_parameters (a_content_type, <<["name", a_name]>>) + end + + put_content_length (a_length: INTEGER) + -- Put "Content-Length:" + length `a_length'. + do + put_header_key_value ({HTTP_HEADER_NAMES}.header_content_length, a_length.out) + end + + put_content_transfer_encoding (a_mechanism: READABLE_STRING_8) + -- Put "Content-Transfer-Encoding" header with `a_mechanism' + --| encoding := "Content-Transfer-Encoding" ":" mechanism + --| + --| mechanism := "7bit" ; case-insensitive + --| / "quoted-printable" + --| / "base64" + --| / "8bit" + --| / "binary" + --| / x-token + do + put_header_key_value ({HTTP_HEADER_NAMES}.header_content_transfer_encoding, a_mechanism) + end + + put_content_language (a_lang: READABLE_STRING_8) + -- Put "Content-Language" header of value `a_lang'. + do + put_header_key_value ({HTTP_HEADER_NAMES}.header_content_language, a_lang) + end + + put_content_encoding (a_encoding: READABLE_STRING_8) + -- Put "Content-Encoding" header of value `a_encoding'. + do + put_header_key_value ({HTTP_HEADER_NAMES}.header_content_encoding, a_encoding) + end + + put_transfer_encoding (a_encoding: READABLE_STRING_8) + -- Put "Transfer-Encoding" header with `a_encoding' value. + --| for instance "chunked" + do + put_header_key_value ({HTTP_HEADER_NAMES}.header_transfer_encoding, a_encoding) + end + + put_transfer_encoding_binary + -- Put "Transfer-Encoding: binary" header + do + put_transfer_encoding (str_binary) + end + + put_transfer_encoding_chunked + -- Put "Transfer-Encoding: chunked" header + do + put_transfer_encoding (str_chunked) + end + + put_content_disposition (a_type: READABLE_STRING_8; a_params: detachable READABLE_STRING_8) + -- Put "Content-Disposition" header + --| See RFC2183 + --| disposition := "Content-Disposition" ":" + --| disposition-type + --| *(";" disposition-parm) + --| disposition-type := "inline" + --| / "attachment" + --| / extension-token + --| ; values are not case-sensitive + --| disposition-parm := filename-parm + --| / creation-date-parm + --| / modification-date-parm + --| / read-date-parm + --| / size-parm + --| / parameter + --| filename-parm := "filename" "=" value + --| creation-date-parm := "creation-date" "=" quoted-date-time + --| modification-date-parm := "modification-date" "=" quoted-date-time + --| read-date-parm := "read-date" "=" quoted-date-time + --| size-parm := "size" "=" 1*DIGIT + --| quoted-date-time := quoted-string + --| ; contents MUST be an RFC 822 `date-time' + --| ; numeric timezones (+HHMM or -HHMM) MUST be used + do + if a_params /= Void then + put_header_key_value ({HTTP_HEADER_NAMES}.header_content_disposition, a_type + semi_colon_space + a_params) + else + put_header_key_value ({HTTP_HEADER_NAMES}.header_content_disposition, a_type) + end + end + +feature -- Content-type helpers + + put_content_type_text_css do put_content_type ({HTTP_MIME_TYPES}.text_css) end + put_content_type_text_csv do put_content_type ({HTTP_MIME_TYPES}.text_csv) end + put_content_type_text_html do put_content_type ({HTTP_MIME_TYPES}.text_html) end + put_content_type_text_javascript do put_content_type ({HTTP_MIME_TYPES}.text_javascript) end + put_content_type_text_json do put_content_type ({HTTP_MIME_TYPES}.text_json) end + put_content_type_text_plain do put_content_type ({HTTP_MIME_TYPES}.text_plain) end + put_content_type_text_xml do put_content_type ({HTTP_MIME_TYPES}.text_xml) end + + put_content_type_application_json do put_content_type ({HTTP_MIME_TYPES}.application_json) end + put_content_type_application_javascript do put_content_type ({HTTP_MIME_TYPES}.application_javascript) end + put_content_type_application_zip do put_content_type ({HTTP_MIME_TYPES}.application_zip) end + put_content_type_application_pdf do put_content_type ({HTTP_MIME_TYPES}.application_pdf) end + + put_content_type_image_gif do put_content_type ({HTTP_MIME_TYPES}.image_gif) end + put_content_type_image_png do put_content_type ({HTTP_MIME_TYPES}.image_png) end + put_content_type_image_jpg do put_content_type ({HTTP_MIME_TYPES}.image_jpg) end + put_content_type_image_svg_xml do put_content_type ({HTTP_MIME_TYPES}.image_svg_xml) end + + put_content_type_message_http do put_content_type ({HTTP_MIME_TYPES}.message_http) end + + put_content_type_multipart_mixed do put_content_type ({HTTP_MIME_TYPES}.multipart_mixed) end + put_content_type_multipart_alternative do put_content_type ({HTTP_MIME_TYPES}.multipart_alternative) end + put_content_type_multipart_related do put_content_type ({HTTP_MIME_TYPES}.multipart_related) end + put_content_type_multipart_form_data do put_content_type ({HTTP_MIME_TYPES}.multipart_form_data) end + put_content_type_multipart_signed do put_content_type ({HTTP_MIME_TYPES}.multipart_signed) end + put_content_type_multipart_encrypted do put_content_type ({HTTP_MIME_TYPES}.multipart_encrypted) end + put_content_type_application_x_www_form_encoded do put_content_type ({HTTP_MIME_TYPES}.application_x_www_form_encoded) end + + put_content_type_utf_8_text_plain do put_content_type_with_charset ({HTTP_MIME_TYPES}.text_plain, "utf-8") end + +feature -- Cross-Origin Resource Sharing + + put_access_control_allow_origin (a_origin: READABLE_STRING_8) + -- Put "Access-Control-Allow-Origin: " + `a_origin' header. + -- `a_origin' specifies a URI that may access the resource + --| for instance "http://example.com" + do + put_header_key_value ({HTTP_HEADER_NAMES}.header_access_control_allow_origin, a_origin) + end + + put_access_control_allow_all_origin + -- Put "Access-Control-Allow-Origin: *" header. + do + put_access_control_allow_origin ("*") + end + + put_access_control_allow_credentials (b: BOOLEAN) + -- Indicates whether or not the response to the request can be exposed when the credentials flag is true. + -- When used as part of a response to a preflight request, this indicates whether or not the actual request can be made using credentials. + -- Note that simple GET requests are not preflighted, and so if a request is made for a resource with credentials, + -- if this header is not returned with the resource, the response is ignored by the browser and not returned to web content. + -- ex: Access-Control-Allow-Credentials: true | false + do + if b then + put_header_key_value ({HTTP_HEADER_NAMES}.header_access_control_allow_Credentials, "true") + else + put_header_key_value ({HTTP_HEADER_NAMES}.header_access_control_allow_Credentials, "false") + end + end + + put_access_control_allow_methods (a_methods: ITERABLE [READABLE_STRING_8]) + -- If `a_methods' is not empty, put `Access-Control-Allow-Methods' header with list `a_methods' of methods + -- `a_methods' specifies the method or methods allowed when accessing the resource. + -- This is used in response to a preflight request. + -- ex: Access-Control-Allow-Methods: [, ]* + do + put_header_key_values ({HTTP_HEADER_NAMES}.header_access_control_allow_methods, a_methods, Void) + end + + put_access_control_allow_headers (a_headers: READABLE_STRING_8) + -- Put "Access-Control-Allow-Headers" header. with value `a_headers' + -- Used in response to a preflight request to indicate which HTTP headers can be used when making the actual request. + -- ex: Access-Control-Allow-Headers: [, ]* + do + put_header_key_value ({HTTP_HEADER_NAMES}.header_access_control_allow_headers, a_headers) + end + + put_access_control_allow_iterable_headers (a_fields: ITERABLE [READABLE_STRING_8]) + -- Put "Access-Control-Allow-Headers" header. with value `a_headers' + -- Used in response to a preflight request to indicate which HTTP headers can be used when making the actual request. + -- ex: Access-Control-Allow-Headers: [, ]* + do + put_header_key_values ({HTTP_HEADER_NAMES}.header_access_control_allow_headers, a_fields, Void) + end + +feature -- Method related + + put_allow (a_methods: ITERABLE [READABLE_STRING_8]) + -- If `a_methods' is not empty, put `Allow' header with list `a_methods' of methods + do + put_header_key_values ({HTTP_HEADER_NAMES}.header_allow, a_methods, Void) + end + +feature -- Date + + put_date (a_date: READABLE_STRING_8) + -- Put "Date: " header + do + put_header_key_value ({HTTP_HEADER_NAMES}.header_date, a_date) + end + + put_current_date + -- Put current date time with "Date" header + do + put_utc_date (create {DATE_TIME}.make_now_utc) + end + + put_utc_date (a_utc_date: DATE_TIME) + -- Put UTC date time `a_utc_date' with "Date" header + -- using RFC1123 date formating. + do + put_date (date_to_rfc1123_http_date_format (a_utc_date)) + end + + put_last_modified (a_utc_date: DATE_TIME) + -- Put UTC date time `dt' with "Last-Modified" header + do + put_header_key_value ({HTTP_HEADER_NAMES}.header_last_modified, date_to_rfc1123_http_date_format (a_utc_date)) + end + +feature -- Authorization + + put_authorization (a_authorization: READABLE_STRING_8) + -- Put `a_authorization' with "Authorization" header + -- The Authorization header is constructed as follows: + -- 1. Username and password are combined into a string "username:password". + -- 2. The resulting string literal is then encoded using Base64. + -- 3. The authorization method and a space, i.e. "Basic " is then put before the encoded string. + -- ex: Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ== + do + put_header_key_value ({HTTP_HEADER_NAMES}.header_authorization, a_authorization) + end + +feature -- Others + + put_expires (a_seconds: INTEGER) + -- Put "Expires" header to `a_seconds' seconds + do + put_expires_string (a_seconds.out) + end + + put_expires_string (a_expires: STRING) + -- Put "Expires" header with `a_expires' string value + do + put_header_key_value ("Expires", a_expires) + end + + put_expires_date (a_utc_date: DATE_TIME) + -- Put "Expires" header with UTC date time value + -- formatted following RFC1123 specification. + do + put_header_key_value ("Expires", date_to_rfc1123_http_date_format (a_utc_date)) + end + + put_cache_control (a_cache_control: READABLE_STRING_8) + -- Put "Cache-Control" header with value `a_cache_control' + --| note: ex "Cache-Control: no-cache, must-revalidate" + do + put_header_key_value ("Cache-Control", a_cache_control) + end + + put_pragma (a_pragma: READABLE_STRING_8) + -- Put "Pragma" header with value `a_pragma' + do + put_header_key_value ("Pragma", a_pragma) + end + + put_pragma_no_cache + -- Put "Pragma" header with "no-cache" a_pragma + do + put_pragma ("no-cache") + end + +feature -- Redirection + + put_location (a_uri: READABLE_STRING_8) + -- Tell the client the new location `a_uri' + -- using "Location" header. + require + a_uri_valid: not a_uri.is_empty + do + put_header_key_value ({HTTP_HEADER_NAMES}.header_location, a_uri) + end + + put_refresh (a_uri: READABLE_STRING_8; a_timeout_in_seconds: INTEGER) + -- Tell the client to refresh page with `a_uri' after `a_timeout_in_seconds' in seconds + -- using "Refresh" header. + require + a_uri_valid: not a_uri.is_empty + do + put_header_key_value ({HTTP_HEADER_NAMES}.header_refresh, a_timeout_in_seconds.out + "; url=" + a_uri) + end + +feature -- Cookie + + put_cookie (key, value: READABLE_STRING_8; expiration, path, domain: detachable READABLE_STRING_8; secure, http_only: BOOLEAN) + -- Set a cookie on the client's machine + -- with key 'key' and value 'value'. + -- Note: you should avoid using "localhost" as `domain' for local cookies + -- since they are not always handled by browser (for instance Chrome) + require + make_sense: (key /= Void and value /= Void) and then (not key.is_empty and not value.is_empty) + domain_without_port_info: domain /= Void implies domain.index_of (':', 1) = 0 + local + s: STRING + do + s := {HTTP_HEADER_NAMES}.header_set_cookie + colon_space + key + "=" + value + if + domain /= Void and then not domain.same_string ("localhost") + then + s.append ("; Domain=") + s.append (domain) + end + if path /= Void then + s.append ("; Path=") + s.append (path) + end + if expiration /= Void then + s.append ("; Expires=") + s.append (expiration) + end + if secure then + s.append ("; Secure") + end + if http_only then + s.append ("; HttpOnly") + end + add_header (s) + end + + put_cookie_with_expiration_date (key, value: READABLE_STRING_8; expiration: DATE_TIME; path, domain: detachable READABLE_STRING_8; secure, http_only: BOOLEAN) + -- Set a cookie on the client's machine + -- with key 'key' and value 'value'. + require + make_sense: (key /= Void and value /= Void) and then (not key.is_empty and not value.is_empty) + do + put_cookie (key, value, date_to_rfc1123_http_date_format (expiration), path, domain, secure, http_only) + end + + +feature -- Access + + date_to_rfc1123_http_date_format (dt: DATE_TIME): STRING_8 + -- String representation of `dt' using the RFC 1123 + local + d: HTTP_DATE + do + create d.make_from_date_time (dt) + Result := d.string + end + +feature {NONE} -- Implementation + + has_same_header_name (h: READABLE_STRING_8; a_name: READABLE_STRING_8): BOOLEAN + -- Header line `h' has same name as `a_name' ? + do + if h.starts_with (a_name) then + if h.valid_index (a_name.count + 1) then + Result := h[a_name.count + 1] = ':' + end + end + end + +feature {NONE} -- Constants + + str_binary: STRING = "binary" + str_chunked: STRING = "chunked" + + colon_space: IMMUTABLE_STRING_8 + once + create Result.make_from_string (": ") + end + + semi_colon_space: IMMUTABLE_STRING_8 + once + create Result.make_from_string ("; ") + end + + comma_space: IMMUTABLE_STRING_8 + once + create Result.make_from_string (", ") + end + +note + copyright: "2011-2014, Jocelyn Fiat, 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 diff --git a/library/network/protocol/http/src/http_header_names.e b/library/network/protocol/http/src/http_header_names.e index 4bab76e8..f64bdc26 100644 --- a/library/network/protocol/http/src/http_header_names.e +++ b/library/network/protocol/http/src/http_header_names.e @@ -199,17 +199,26 @@ feature -- Cross-Origin Resource Sharing header_access_control_allow_origin: STRING = "Access-Control-Allow-Origin" -- Indicates whether a resource can be shared based by returning -- the value of the Origin request header in the response. - -- | Example: Access-Control-Allow-Origin: http://example.org + --| Example: Access-Control-Allow-Origin: http://example.org + + header_access_control_allow_credentials: STRING = "Access-Control-Allow-Credentials" + -- Indicates whether or not the response to the request can be exposed when the credentials flag is true. + -- When used as part of a response to a preflight request, this indicates whether or not the actual request can be made using credentials. + -- Note that simple GET requests are not preflighted, and so if a request is made for a resource with credentials, + -- if this header is not returned with the resource, the response is ignored by the browser and not returned to web content. + --| Access-Control-Allow-Credentials: true | false header_access_control_allow_methods: STRING = "Access-Control-Allow-Methods" -- Indicates, as part of the response to a preflight request, -- which methods can be used during the actual request. - -- | Example: Access-Control-Allow-Methods: PUT, DELETE + --| Access-Control-Allow-Methods: [, ]* + --| Example: Access-Control-Allow-Methods: PUT, DELETE header_access_control_allow_headers: STRING = "Access-Control-Allow-Headers" -- Indicates, as part of the response to a preflight request, -- which header field names can be used during the actual request. - -- | Example: Access-Control-Allow-Headers: Authorization + --| Access-Control-Allow-Headers: [, ]* + --| Example: Access-Control-Allow-Headers: Authorization feature -- Request or Response header name @@ -265,7 +274,7 @@ feature -- MIME related header_content_transfer_encoding: STRING = "Content-Transfer-Encoding" note - copyright: "2011-2013, Jocelyn Fiat, Eiffel Software and others" + copyright: "2011-2014, Jocelyn Fiat, Eiffel Software and others" license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" source: "[ Eiffel Software diff --git a/library/network/protocol/http/src/http_media_type.e b/library/network/protocol/http/src/http_media_type.e index 0b9d0055..d2d9614e 100644 --- a/library/network/protocol/http/src/http_media_type.e +++ b/library/network/protocol/http/src/http_media_type.e @@ -2,7 +2,7 @@ note description: "[ This class is to represent a media type - the Internet Media Type [9] of the attached entity if the type + the Internet Media Type of the attached entity if the type was provided via a "Content-type" field in the wgi_request header, or if the server can determine it in the absence of a supplied "Content-type" field. The syntax is the same as for the HTTP @@ -29,6 +29,8 @@ note ]" date: "$Date$" revision: "$Revision$" + EIS: "name=Wikipedia/Media Type", "protocol=URI", "src=http://en.wikipedia.org/wiki/Internet_media_type" + EIS: "name=RFC2046", "protocol=URI", "src=http://tools.ietf.org/html/rfc2046" class HTTP_MEDIA_TYPE @@ -76,15 +78,7 @@ feature {NONE} -- Initialization p := s.index_of (';', i) if p > 0 then t := s.substring (i, p - 1) - i := p + 1 - p := s.index_of (';', i) - if p = 0 then - add_parameter_from_string (s, i, n) - i := n - else - add_parameter_from_string (s, i, p - 1) - i := p + 1 - end + create parameters.make_from_substring (s, p + 1, s.count) else t := s.substring (i, n) end @@ -94,9 +88,14 @@ feature {NONE} -- Initialization -- Extract type and subtype p := t.index_of ('/', 1) if p = 0 then - has_error := True + t.right_adjust type := t - subtype := "" + if t.same_string ("*") then + --| Accept *; should be */* + else + has_error := True + end + subtype := "*" else subtype := t.substring (p + 1, t.count) type := t @@ -108,7 +107,7 @@ feature {NONE} -- Initialization subtype := type end ensure - not has_error implies (create {HTTP_CONTENT_TYPE}.make_from_string (string)).same_string (string) + not has_error implies (create {HTTP_MEDIA_TYPE}.make_from_string (string)).same_string (string) end feature -- Status report @@ -141,7 +140,7 @@ feature -- Access end end - parameters: detachable HASH_TABLE [READABLE_STRING_8, READABLE_STRING_8] + parameters: detachable HTTP_PARAMETER_TABLE -- Parameters feature -- Conversion @@ -254,7 +253,7 @@ feature -- Element change -- Remove parameter named `a_name' do if attached parameters as plst then - plst.prune (a_name) + plst.remove (a_name) if plst.is_empty then parameters := Void end @@ -262,49 +261,6 @@ feature -- Element change internal_string := Void end -feature {NONE} -- Implementation - - add_parameter_from_string (s: READABLE_STRING_8; start_index, end_index: INTEGER) - -- Add parameter from string " attribute=value " - local - pn,pv: STRING_8 - i: INTEGER - p: INTEGER - err: BOOLEAN - do - -- Skip spaces - from - i := start_index - until - i > end_index or not s[i].is_space - loop - i := i + 1 - end - if i < end_index then - p := s.index_of ('=', i) - if p > 0 and p < end_index then - pn := s.substring (i, p - 1) - pv := s.substring (p + 1, end_index) - pv.right_adjust - if pv.count > 0 and pv [1] = '%"' then - if pv [pv.count] = '%"' then - pv := pv.substring (2, pv.count - 1) - else - err := True - -- missing closing double quote. - end - end - if not err then - add_parameter (pn, pv) - end - else - -- expecting: attribute "=" value - err := True - end - end - has_error := has_error or err - end - feature {NONE} -- Internal internal_string: detachable STRING_8 @@ -334,7 +290,7 @@ invariant type_and_subtype_not_empty: not has_error implies not type.is_empty and not subtype.is_empty note - copyright: "2011-2012, Jocelyn Fiat, Eiffel Software and others" + copyright: "2011-2013, Jocelyn Fiat, Eiffel Software and others" license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" source: "[ Eiffel Software diff --git a/library/network/protocol/http/src/http_parameter_table.e b/library/network/protocol/http/src/http_parameter_table.e new file mode 100644 index 00000000..21d8995c --- /dev/null +++ b/library/network/protocol/http/src/http_parameter_table.e @@ -0,0 +1,150 @@ +note + description: "[ + Table representing parameters of the form q=1.0;note="blabla";foo=bar + ]" + date: "$Date$" + revision: "$Revision$" + +class + HTTP_PARAMETER_TABLE + +inherit + HASH_TABLE [READABLE_STRING_8, STRING_8] + redefine + empty_duplicate + end + +create + make, + make_from_string, + make_from_substring + +feature {NONE} -- Initialization + + make_from_string (s: READABLE_STRING_8) + -- Build table of parameters for `s' + do + make_from_substring (s, 1, s.count) + end + + make_from_substring (s: READABLE_STRING_8; a_start_index: INTEGER; a_end_index: INTEGER) + -- Build table of parameters for `s.substring (a_start_index, a_end_index)' + local + cl: CELL [INTEGER] + i: INTEGER + do + make (1) + from + i := a_start_index + create cl.put (i) + until + i >= a_end_index + loop + force_substring (s, i, cl) + i := cl.item + end + end + +feature -- Status report + + has_error: BOOLEAN + -- Current has error? + --| Mainly in relation with `make_from_string' and `force_substring' + +feature {NONE} -- Implementation + + force_substring (s: READABLE_STRING_8; start_index: INTEGER; out_end_index: CELL [INTEGER]) + -- Add parameter from string " attribute=value " + -- and put in `out_end_index' the index after found parameter. + local + n: INTEGER + pn,pv: STRING_8 + i: INTEGER + p, q: INTEGER + err: BOOLEAN + do + n := s.count + -- Skip spaces + from + i := start_index + until + i > n or not s[i].is_space + loop + i := i + 1 + end + if s[i] = ';' then + -- empty parameter + out_end_index.replace (i + 1) + elseif i < n then + p := s.index_of ('=', i) + if p > 0 then + pn := s.substring (i, p - 1) + if p >= n then + pv := "" + out_end_index.replace (n + 1) + else + if s[p+1] = '%"' then + q := s.index_of ('%"', p + 2) + if q > 0 then + pv := s.substring (p + 2, q - 1) + from + i := q + 1 + until + i > n or not s[i].is_space + loop + i := i + 1 + end + if s[i] = ';' then + i := i + 1 + end + out_end_index.replace (i) + else + err := True + pv := "" + -- missing closing double quote. + end + else + q := s.index_of (';', p + 1) + if q = 0 then + q := n + 1 + end + pv := s.substring (p + 1, q - 1) + out_end_index.replace (q + 1) + end + pv.right_adjust + if not err then + force (pv, pn) + end + end + else + -- expecting: attribute "=" value + err := True + end + end + if err then + out_end_index.replace (n + 1) + end + has_error := has_error or err + ensure + entry_processed: out_end_index.item > start_index + end + +feature {NONE} -- Duplication + + empty_duplicate (n: INTEGER): like Current + -- Create an empty copy of Current that can accommodate `n' items + do + Result := Precursor (n) + end + +note + copyright: "2011-2013, Jocelyn Fiat, 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 diff --git a/library/network/protocol/http/tests/http_content_type_set.e b/library/network/protocol/http/tests/http_content_type_set.e new file mode 100644 index 00000000..f93b4eab --- /dev/null +++ b/library/network/protocol/http/tests/http_content_type_set.e @@ -0,0 +1,214 @@ +note + description : "Objects that ..." + author : "$Author$" + date : "$Date$" + revision : "$Revision$" + +class + HTTP_CONTENT_TYPE_SET + +inherit + EQA_TEST_SET + +feature -- Content type + + + test_http_content_types_with_params + local + do + test_content_type_with_params ("text/plain; param1=%"something;foo=bar%"; param2=%"another-thing%"", + "text", "plain", <<["param1", "something;foo=bar"], ["param2", "another-thing"]>> + ) + test_content_type ("*", "*", "*") + test_content_type_with_params ("* ; q=0.8", "*", "*", <<["q", "0.8"]>>) + test_content_type ("*/*", "*", "*") + test_content_type ("text/*", "text", "*") + end + + test_http_content_type + do + test_content_type ("application/atom+xml", "application", "atom+xml") -- Atom feeds + test_content_type ("application/ecmascript", "application", "ecmascript") -- ECMAScript/JavaScript; Defined in RFC 4329 (equivalent to application/javascript but with stricter processing rules) + test_content_type ("application/EDI-X12", "application", "EDI-X12") -- EDI X12 data; Defined in RFC 1767 + test_content_type ("application/EDIFACT", "application", "EDIFACT") -- EDI EDIFACT data; Defined in RFC 1767 + test_content_type ("application/json", "application", "json") -- JavaScript Object Notation JSON; Defined in RFC 4627 + test_content_type ("application/javascript", "application", "javascript") -- ECMAScript/JavaScript; Defined in RFC 4329 (equivalent to application/ecmascript but with looser processing rules) It is not accepted in IE 8 or earlier - text/javascript is accepted but it is defined as obsolete in RFC 4329. The "type" attribute of the