diff --git a/contrib/ise_library/cURL b/contrib/ise_library/cURL
index f17f785e..2b7043f6 160000
--- a/contrib/ise_library/cURL
+++ b/contrib/ise_library/cURL
@@ -1 +1 @@
-Subproject commit f17f785ee36db5b424ce40dfcd7bbd41924613bc
+Subproject commit 2b7043f670f6efc70ebed5fbf62d83a22c095ffe
diff --git a/contrib/library/text/parser/json b/contrib/library/text/parser/json
index 5ec1bdd2..24d08d4f 160000
--- a/contrib/library/text/parser/json
+++ b/contrib/library/text/parser/json
@@ -1 +1 @@
-Subproject commit 5ec1bdd2ea01358153de6887f572bdb2e70c02dd
+Subproject commit 24d08d4fcedf1d54f1c8b4eb6526f2dd1b2fb943
diff --git a/examples/restbucks/client/src/restbuck_client.e b/examples/restbucks/client/src/restbuck_client.e
deleted file mode 100644
index dec021d0..00000000
--- a/examples/restbucks/client/src/restbuck_client.e
+++ /dev/null
@@ -1,72 +0,0 @@
-note
- description : "Objects that ..."
- author : "$Author$"
- date : "$Date$"
- revision : "$Revision$"
-
-class
- RESTBUCK_CLIENT
-
-create
- make
-
-feature {NONE} -- Initialization
-
- make
- -- Initialize `Current'.
- local
- h: LIBCURL_HTTP_CLIENT
- sess: HTTP_CLIENT_SESSION
- s: READABLE_STRING_8
- j: JSON_PARSER
- id: detachable STRING
- do
- create h.make
- sess := h.new_session ("http://127.0.0.1")
- s := "[
- {
- "location":"takeAway",
- "items":[
- {
- "name":"Late",
- "option":"skim",
- "size":"Small",
- "quantity":1
- }
- ]
- }
- ]"
-
- if attached sess.post ("/order", Void, s) as r then
- if attached r.body as m then
- create j.make_parser (m)
-
- if j.is_parsed and attached j.parse_object as j_o then
- if attached {JSON_STRING} j_o.item ("id") as l_id then
- id := l_id.item
- end
- print (m)
- io.put_new_line
-
- end
- end
- end
-
- if id /= Void and then attached sess.get ("/order/" + id, Void) as r then
- print (r.body)
- io.put_new_line
- end
- end
-
-feature -- Status
-
-feature -- Access
-
-feature -- Change
-
-feature {NONE} -- Implementation
-
-invariant
--- invariant_clause: True
-
-end
diff --git a/examples/restbucks/readme.txt b/examples/restbucks/readme.txt
deleted file mode 100644
index dfff1b30..00000000
--- a/examples/restbucks/readme.txt
+++ /dev/null
@@ -1,51 +0,0 @@
-Restbuck Eiffel Implementation based on the book of REST in Practice
-
-
-Verb URI or template Use
-POST /order Create a new order, and upon success, receive a Locationheader specifying the new order’s URI.
-GET /order/{orderId} Request the current state of the order specified by the URI.
-PUT /order/{orderId} Update an order at the given URI with new information, providing the full representation.
-DELETE /order/{orderId} Logically remove the order identified by the given URI.
-
-
-How to Create an order
-
- * Uri: http://localhost:8080/order
- * Method: POST
- * Note: you will get in the response the "location" of the new your order.
- * HEADERS:
-
- Content-Type: application/json
-
- * Example CONTENT
-
- {
- "location":"takeAway",
- "items":[
- {
- "name":"Late",
- "option":"skim",
- "size":"Small",
- "quantity":1
- }
- ]
- }
-
-
-
-How to Read an order
- * Uri: http://localhost:8080/order/{order_id}
- * Method: GET
-
-
-
-
-How to Update an order
- * Uri: http://localhost:8080/order/{order_id}
- * Method: PUT
-
-
-How to Delete an order
- * Uri: http://localhost:8080/order/{order_id}
- * Method: DELETE
-
diff --git a/examples/restbucks/client/README.txt b/examples/restbucksCRUD/client/README.txt
similarity index 100%
rename from examples/restbucks/client/README.txt
rename to examples/restbucksCRUD/client/README.txt
diff --git a/examples/restbucks/client/client-safe.ecf b/examples/restbucksCRUD/client/client-safe.ecf
similarity index 100%
rename from examples/restbucks/client/client-safe.ecf
rename to examples/restbucksCRUD/client/client-safe.ecf
diff --git a/examples/restbucks/client/client.ecf b/examples/restbucksCRUD/client/client.ecf
similarity index 100%
rename from examples/restbucks/client/client.ecf
rename to examples/restbucksCRUD/client/client.ecf
diff --git a/examples/restbucksCRUD/client/src/restbuck_client.e b/examples/restbucksCRUD/client/src/restbuck_client.e
new file mode 100644
index 00000000..2e0cd8b2
--- /dev/null
+++ b/examples/restbucksCRUD/client/src/restbuck_client.e
@@ -0,0 +1,170 @@
+note
+ description : "Objects that ..."
+ author : "$Author$"
+ date : "$Date$"
+ revision : "$Revision$"
+
+class
+ RESTBUCK_CLIENT
+
+create
+ make
+
+feature {NONE} -- Initialization
+
+ make
+ -- Initialize `Current'.
+ local
+ h: LIBCURL_HTTP_CLIENT
+ sess: HTTP_CLIENT_SESSION
+ resp : HTTP_CLIENT_RESPONSE
+ l_location : detachable READABLE_STRING_8
+ body : STRING
+ do
+ create h.make
+ sess := h.new_session ("http://127.0.0.1:8080")
+ -- Create Order
+ print ("%N Create Order %N")
+ resp := create_order (sess)
+
+ -- Read the Order
+ print ("%N Read Order %N")
+ l_location := resp.headers.at ("Location")
+ resp := read_order (sess, l_location)
+
+
+ -- Update the Order
+
+ if attached resp.body as l_body then
+ body := l_body.as_string_8
+ body.replace_substring_all ("takeAway", "in Shop")
+ print ("%N Update Order %N")
+ resp := update_order (sess, l_location, body)
+ end
+ end
+
+ update_order ( sess: HTTP_CLIENT_SESSION; uri : detachable READABLE_STRING_8; a_body : STRING) : HTTP_CLIENT_RESPONSE
+ local
+ l_headers: HASH_TABLE [READABLE_STRING_8,READABLE_STRING_8]
+ do
+ create Result.make
+ if attached uri as l_uri then
+ sess.set_base_url (l_uri)
+ Result := sess.put ("", Void, a_body )
+ if attached Result as r then
+ -- Show headers
+ l_headers := r.headers
+ from
+ l_headers.start
+ until
+ l_headers.after
+ loop
+ print (l_headers.key_for_iteration)
+ print (":")
+ print (l_headers.item_for_iteration)
+ l_headers.forth
+ io.put_new_line
+ end
+
+ -- Show body
+ print (r.body)
+ io.put_new_line
+ end
+ end
+ end
+
+
+ read_order ( sess: HTTP_CLIENT_SESSION; uri : detachable READABLE_STRING_8) : HTTP_CLIENT_RESPONSE
+ local
+ l_headers: HASH_TABLE [READABLE_STRING_8,READABLE_STRING_8]
+ do
+ create Result.make
+ if attached uri as l_uri then
+ sess.set_base_url (l_uri)
+ Result := sess.get ("", Void)
+ if attached Result as r then
+ -- Show headers
+ l_headers := r.headers
+ from
+ l_headers.start
+ until
+ l_headers.after
+ loop
+ print (l_headers.key_for_iteration)
+ print (":")
+ print (l_headers.item_for_iteration)
+ l_headers.forth
+ io.put_new_line
+ end
+
+ -- Show body
+ print (r.body)
+ io.put_new_line
+ end
+ end
+ end
+
+
+
+ create_order (sess: HTTP_CLIENT_SESSION) : HTTP_CLIENT_RESPONSE
+ local
+ s: READABLE_STRING_8
+ j: JSON_PARSER
+ id: detachable STRING
+ context : HTTP_CLIENT_REQUEST_CONTEXT
+ l_headers: HASH_TABLE [READABLE_STRING_8,READABLE_STRING_8]
+ do
+ s := "[
+ {
+ "location":"takeAway",
+ "items":[
+ {
+ "name":"Late",
+ "option":"skim",
+ "size":"Small",
+ "quantity":1
+ }
+ ]
+ }
+ ]"
+
+ create context.make
+ context.headers.put ("application/json", "Content-Type")
+ Result := sess.post ("/order", context, s)
+ if attached Result as r then
+ -- Show the Headers
+ l_headers := r.headers
+ from
+ l_headers.start
+ until
+ l_headers.after
+ loop
+ print (l_headers.key_for_iteration)
+ print (":")
+ print (l_headers.item_for_iteration)
+ l_headers.forth
+ io.put_new_line
+ end
+
+
+ -- Show the Response body
+ if attached r.body as m then
+ create j.make_parser (m)
+ if j.is_parsed and attached j.parse_object as j_o then
+ if attached {JSON_STRING} j_o.item ("id") as l_id then
+ id := l_id.item
+ end
+ print (m)
+ io.put_new_line
+ end
+ end
+ end
+ end
+
+
+feature {NONE} -- Implementation
+
+invariant
+-- invariant_clause: True
+
+end
diff --git a/examples/restbucks/license.lic b/examples/restbucksCRUD/license.lic
similarity index 100%
rename from examples/restbucks/license.lic
rename to examples/restbucksCRUD/license.lic
diff --git a/examples/restbucksCRUD/readme.md b/examples/restbucksCRUD/readme.md
new file mode 100644
index 00000000..c833a939
--- /dev/null
+++ b/examples/restbucksCRUD/readme.md
@@ -0,0 +1,293 @@
+Restbuck Eiffel Implementation based on the book of REST in Practice
+====================================================================
+This is an ihmplementation of CRUD pattern for manipulate resources, this is the first step to use
+the HTTP protocol as an application protocol instead of a transport protocol.
+
+Restbuck Protocol
+-----------------
+
+
+
Verb
URI or template
Use
+
POST
/order
Create a new order, and upon success, receive a Locationheader specifying the new order's URI.
+
GET
/order/{orderId}
Request the current state of the order specified by the URI.
+
PUT
/order/{orderId}
Update an order at the given URI with new information, providing the full representation.
+
DELETE
/order/{orderId}
Logically remove the order identified by the given URI.
+
+
+Resource Represenation
+----------------------
+The previous tables shows a contrat, the URI or URI template, allows us to indentify resources, now we will chose a
+representacion, for this particular case we will use JSON.
+
+Note:
+1. *A resource can have multiple URIs*.
+2. *A resource can have multiple Representations*.
+
+RESTBUCKS_SERVER
+----------------
+This class implement the main entry of our REST CRUD service, we are using a default connector (Nino Connector,
+using a WebServer written in Eiffel).
+We are inheriting from URI_TEMPLATE_ROUTED_SERVICE, this allows us to map our service contrat, as is shown in the previous
+table, the mapping is defined in the feature setup_router, this also show that the class ORDER_HANDLER will be encharge
+of to handle different type of request to the ORDER resource.
+
+
+ class
+ RESTBUCKS_SERVER
+
+ inherit
+ ANY
+
+ URI_TEMPLATE_ROUTED_SERVICE
+
+ DEFAULT_SERVICE
+ -- Here we are using a default connector using the default Nino Connector,
+ -- but it's possible to use other connector (CGI or FCGI).
+
+ create
+ make
+
+ feature {NONE} -- Initialization
+
+ make
+ -- Initialize the router (this will have the request handler and
+ -- their context).
+ do
+ initialize_router
+ make_and_launch
+ end
+
+ create_router
+ do
+ create router.make (2)
+ end
+
+ setup_router
+ local
+ order_handler: ORDER_HANDLER [REQUEST_URI_TEMPLATE_HANDLER_CONTEXT]
+ do
+ create order_handler
+ router.map_with_request_methods ("/order", order_handler, <<"POST">>)
+ router.map_with_request_methods ("/order/{orderid}", order_handler, <<"GET", "DELETE", "PUT">>)
+ end
+
+ feature -- Execution
+
+ execute_default (req: WSF_REQUEST; res: WSF_RESPONSE)
+ -- I'm using this method to handle the method not allowed response
+ -- in the case that the given uri does not have a corresponding http method
+ -- to handle it.
+ local
+ h : HTTP_HEADER
+ l_description : STRING
+ l_api_doc : STRING
+ do
+ if req.content_length_value > 0 then
+ req.input.read_string (req.content_length_value.as_integer_32)
+ end
+ create h.make
+ h.put_status ({HTTP_STATUS_CODE}.method_not_allowed)
+ h.put_content_type_text_plain
+ l_api_doc := "%NPlease check the API%NURI:/order METHOD: POST%NURI:/order/{orderid} METHOD: GET, PUT, DELETE%N"
+ l_description := req.request_method + req.request_uri + " is not allowed" + "%N" + l_api_doc
+ h.put_content_length (l_description.count)
+ h.put_current_date
+ res.set_status_code ({HTTP_STATUS_CODE}.method_not_allowed)
+ res.write_header_text (h.string)
+ res.write_string (l_description)
+ end
+
+ end
+
+
+
+How to Create an order with POST
+--------------------------------
+
+Here is the convention that we are using:
+POST is used for creation and the server determines the URI of the created resource.
+If the request POST is SUCCESS, the server will create the order and will response with
+201 CREATED, the Location header will contains the newly created order's URI,
+if the request POST is not SUCCESS, the server will response with
+400 BAD REQUEST, the client send a bad request or
+500 INTERNAL_SERVER_ERROR, when the server can deliver the request.
+
+ POST /order HTTP/1.1
+ Host: 127.0.0.1:8080
+ Connection: keep-alive
+ Content-Length: 196
+ Origin: chrome-extension://fhjcajmcbmldlhcimfajhfbgofnpcjmb
+ Content-Type: application/json
+ Accept: */*
+ Accept-Encoding: gzip,deflate,sdch
+ Accept-Language: es-419,es;q=0.8,en;q=0.6
+ Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3
+
+ {
+ "location":"takeAway",
+ "items":[
+ {
+ "name":"Late",
+ "option":"skim",
+ "size":"Small",
+ "quantity":1
+ }
+ ]
+ }
+
+Response success
+
+ HTTP/1.1 201 Created
+ Status 201 Created
+ Content-Type application/json
+ Content-Length 123
+ Location http://localhost:8080/order/1
+ Date FRI,09 DEC 2011 20:34:20.00 GMT
+
+ {
+ "location" : "takeAway",
+ "status" : "submitted",
+ "items" : [ {
+ "name" : "late",
+ "size" : "small",
+ "quantity" : 1,
+ "option" : "skim"
+ } ]
+ }
+
+
+How to Read an order with GET
+-----------------------------
+Using GET to retrieve resource information.
+If the GET request is SUCCESS, we response with 200 OK, and a representation of the order
+If the GET request is not SUCCESS, we response with 404 Resource not found
+If is a Conditional GET and the resource does not change we send a 304, Resource not modifed
+
+ GET /order/1 HTTP/1.1
+ Host: 127.0.0.1:8080
+ Connection: keep-alive
+ Accept: */*
+ Accept-Encoding: gzip,deflate,sdch
+ Accept-Language: es-419,es;q=0.8,en;q=0.6
+ Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3
+ If-None-Match: 6542EF270D91D3EAF39CFB382E4CEBA7
+
+Response
+ HTTP/1.1 200 OK
+
+ Status 200 OK
+ Content-Type application/json
+ Content-Length 123
+ Date FRI,09 DEC 2011 20:53:46.00 GMT
+ etag 2ED3A40954A95D766FC155682DC8BB52
+
+ {
+ "location" : "takeAway",
+ "status" : "submitted",
+ "items" : [ {
+ "name" : "late",
+ "size" : "small",
+ "quantity" : 1,
+ "option" : "skim"
+ } ]
+ }
+
+
+
+How to Update an order with PUT
+-------------------------------
+A successful PUT request will not create a new resource, instead it will change the state of the resource identified by the current uri.
+If success we response with 200 and the updated order.
+404 if the order is not found
+400 in case of a bad request
+500 internal server error
+If the request is a Conditional PUT, and it does not mat we response 415, precondition failed.
+
+Suposse that we had created an Order with the values shown in the _How to create an order with POST_
+But we change our decision and we want to stay in the shop.
+
+
+
+ PUT /order/1 HTTP/1.1
+ Content-Length: 122
+ Content-Type: application/json; charset=UTF-8
+ Host: localhost:8080
+ Connection: Keep-Alive
+ Expect: 100-Continue
+
+ {
+ "location" : "in shop",
+ "status" : "submitted",
+ "items" : [ {
+ "name" : "late",
+ "size" : "small",
+ "quantity" : 1,
+ "option" : "skim"
+ } ]
+ }
+
+
+Response success
+
+ HTTP/1.1 200 OK
+ Status 200 OK
+ Content-Type application/json
+ Date FRI,09 DEC 2011 21:06:26.00 GMT
+ etag 8767F900674B843E1F3F70BCF3E62403
+ Content-Length 122
+
+ {
+ "location" : "in shop",
+ "status" : "submitted",
+ "items" : [ {
+ "name" : "late",
+ "size" : "small",
+ "quantity" : 1,
+ "option" : "skim"
+ } ]
+ }
+
+How to Delete an order with DELETE
+----------------------------------
+Here we use DELETE to cancel an order, if that order is in state where it can still be canceled.
+204 if is ok
+404 Resource not found
+405 if consumer and service's view of the resouce state is inconsisent
+500 if we have an internal server error
+
+
+ DELETE /order/1 HTTP/1.1
+ Host: localhost:8080
+ Connection: Keep-Alive
+
+Response success
+
+ HTTP/1.1 204 No Content
+
+ Status 204 No Content
+ Content-Type application/json
+ Date FRI,09 DEC 2011 21:10:51.00 GMT
+
+If we want to check that the resource does not exist anymore we can try to retrieve a GET /order/1 and we will receive a
+404 No Found
+
+ GET /order/1 HTTP/1.1
+ Host: localhost:8080
+ Connection: Keep-Alive
+
+Response
+
+ HTTP/1.1 404 Not Found
+
+ Status 404 Not Found
+ Content-Type application/json
+ Content-Length 44
+ Date FRI,09 DEC 2011 21:14:17.79 GMT
+
+ The following resource/order/1 is not found
+
+
+References
+----------
+1. [How to get a cup of coffe](http://www.infoq.com/articles/webber-rest-workflow)
+2. [Rest in Practice] (http://restinpractice.com/default.aspx)
diff --git a/examples/restbucksCRUD/readme.txt b/examples/restbucksCRUD/readme.txt
new file mode 100644
index 00000000..e69de29b
diff --git a/examples/restbucks/restbucks-safe.ecf b/examples/restbucksCRUD/restbucks-safe.ecf
similarity index 100%
rename from examples/restbucks/restbucks-safe.ecf
rename to examples/restbucksCRUD/restbucks-safe.ecf
diff --git a/examples/restbucks/restbucks.rc b/examples/restbucksCRUD/restbucks.rc
similarity index 100%
rename from examples/restbucks/restbucks.rc
rename to examples/restbucksCRUD/restbucks.rc
diff --git a/examples/restbucks/src/database/database_api.e b/examples/restbucksCRUD/src/database/database_api.e
similarity index 100%
rename from examples/restbucks/src/database/database_api.e
rename to examples/restbucksCRUD/src/database/database_api.e
diff --git a/examples/restbucks/src/database/shared_database_api.e b/examples/restbucksCRUD/src/database/shared_database_api.e
similarity index 100%
rename from examples/restbucks/src/database/shared_database_api.e
rename to examples/restbucksCRUD/src/database/shared_database_api.e
diff --git a/examples/restbucks/src/domain/item.e b/examples/restbucksCRUD/src/domain/item.e
similarity index 100%
rename from examples/restbucks/src/domain/item.e
rename to examples/restbucksCRUD/src/domain/item.e
diff --git a/examples/restbucks/src/domain/item_constants.e b/examples/restbucksCRUD/src/domain/item_constants.e
similarity index 100%
rename from examples/restbucks/src/domain/item_constants.e
rename to examples/restbucksCRUD/src/domain/item_constants.e
diff --git a/examples/restbucks/src/domain/json_order_converter.e b/examples/restbucksCRUD/src/domain/json_order_converter.e
similarity index 97%
rename from examples/restbucks/src/domain/json_order_converter.e
rename to examples/restbucksCRUD/src/domain/json_order_converter.e
index d911bf6a..711f6dd3 100644
--- a/examples/restbucks/src/domain/json_order_converter.e
+++ b/examples/restbucksCRUD/src/domain/json_order_converter.e
@@ -38,7 +38,7 @@ feature -- Conversion
s_location ?= json.object (j.item (location_key), Void)
s_status ?= json.object (j.item (status_key), Void)
- create o.make (s_id, s_location, s_status)
+ create o.make ("", s_location, s_status)
if attached {JSON_ARRAY} j.item (items_key) as l_val then
l_array := l_val.array_representation
@@ -87,7 +87,7 @@ feature -- Conversion
jv: JSON_OBJECT
do
create Result.make
- Result.put (json.value (o.id), id_key)
+-- Result.put (json.value (o.id), id_key)
Result.put (json.value (o.location), location_key)
Result.put (json.value (o.status), status_key)
from
diff --git a/examples/restbucks/src/domain/order.e b/examples/restbucksCRUD/src/domain/order.e
similarity index 100%
rename from examples/restbucks/src/domain/order.e
rename to examples/restbucksCRUD/src/domain/order.e
diff --git a/examples/restbucks/src/domain/order_validation.e b/examples/restbucksCRUD/src/domain/order_validation.e
similarity index 100%
rename from examples/restbucks/src/domain/order_validation.e
rename to examples/restbucksCRUD/src/domain/order_validation.e
diff --git a/examples/restbucks/src/domain/shared_order_validation.e b/examples/restbucksCRUD/src/domain/shared_order_validation.e
similarity index 100%
rename from examples/restbucks/src/domain/shared_order_validation.e
rename to examples/restbucksCRUD/src/domain/shared_order_validation.e
diff --git a/examples/restbucks/src/resource/order_handler.e b/examples/restbucksCRUD/src/resource/order_handler.e
similarity index 90%
rename from examples/restbucks/src/resource/order_handler.e
rename to examples/restbucksCRUD/src/resource/order_handler.e
index b66b9fe5..561e31ac 100644
--- a/examples/restbucks/src/resource/order_handler.e
+++ b/examples/restbucksCRUD/src/resource/order_handler.e
@@ -66,9 +66,9 @@ feature -- HTTP Methods
local
etag_util : ETAG_UTILS
do
- if attached req.meta_variable ("HTTP_IF_NONE_MATCH") as if_none_match then
+ if attached req.meta_string_variable ("HTTP_IF_NONE_MATCH") as if_none_match then
create etag_util
- if if_none_match.as_string.same_string (etag_util.md5_digest (l_order.out).as_string_32) then
+ if if_none_match.same_string (etag_util.md5_digest (l_order.out).as_string_32) then
Result := True
end
end
@@ -108,26 +108,31 @@ feature -- HTTP Methods
-- If the request is a Conditional PUT, and it does not mat we response
-- 415, precondition failed.
local
- l_post: STRING
+ l_put: STRING
l_order : detachable ORDER
+ id : STRING
do
- req.input.read_string (req.content_length_value.as_integer_32)
- l_post := req.input.last_string
- l_order := extract_order_request(l_post)
- if l_order /= Void and then db_access.orders.has_key (l_order.id) then
- if is_valid_to_update(l_order) then
- if is_conditional_put (req, l_order) then
- update_order( l_order)
- compute_response_put (ctx, req, res, l_order)
+ if attached req.orig_path_info as orig_path then
+ id := get_order_id_from_path (orig_path)
+ req.input.read_string (req.content_length_value.as_integer_32)
+ l_put := req.input.last_string
+ l_order := extract_order_request(l_put)
+ if l_order /= Void and then db_access.orders.has_key (id) then
+ l_order.set_id (id)
+ if is_valid_to_update(l_order) then
+ if is_conditional_put (req, l_order) then
+ update_order( l_order)
+ compute_response_put (ctx, req, res, l_order)
+ else
+ handle_precondition_fail_response ("", ctx, req, res)
+ end
else
- handle_precondition_fail_response ("", ctx, req, res)
+ --| FIXME: Here we need to define the Allow methods
+ handle_resource_conflict_response (l_put +"%N There is conflict while trying to update the order, the order could not be update in the current state", ctx, req, res)
end
else
- --| FIXME: Here we need to define the Allow methods
- handle_resource_conflict_response (l_post +"%N There is conflict while trying to update the order, the order could not be update in the current state", ctx, req, res)
+ handle_bad_request_response (l_put +"%N is not a valid ORDER, maybe the order does not exist in the system", ctx, req, res)
end
- else
- handle_bad_request_response (l_post +"%N is not a valid ORDER, maybe the order does not exist in the system", ctx, req, res)
end
end
@@ -138,9 +143,9 @@ feature -- HTTP Methods
etag_util : ETAG_UTILS
do
if attached retrieve_order (order.id) as l_order then
- if attached req.meta_variable ("HTTP_IF_MATCH") as if_match then
+ if attached req.meta_string_variable ("HTTP_IF_MATCH") as if_match then
create etag_util
- if if_match.as_string.same_string (etag_util.md5_digest (l_order.out).as_string_32) then
+ if if_match.same_string (etag_util.md5_digest (l_order.out).as_string_32) then
Result := True
end
else
diff --git a/examples/restbucks/src/restbucks_server.e b/examples/restbucksCRUD/src/restbucks_server.e
similarity index 100%
rename from examples/restbucks/src/restbucks_server.e
rename to examples/restbucksCRUD/src/restbucks_server.e
diff --git a/examples/restbucks/src/utils/etag_utils.e b/examples/restbucksCRUD/src/utils/etag_utils.e
similarity index 100%
rename from examples/restbucks/src/utils/etag_utils.e
rename to examples/restbucksCRUD/src/utils/etag_utils.e
diff --git a/examples/simple/application.e b/examples/simple/application.e
new file mode 100644
index 00000000..619de17e
--- /dev/null
+++ b/examples/simple/application.e
@@ -0,0 +1,30 @@
+note
+ description : "simple application root class"
+ date : "$Date$"
+ revision : "$Revision$"
+
+class
+ APPLICATION
+
+create
+ make
+
+feature {NONE} -- Initialization
+
+ make
+ -- Run application.
+ local
+ s: DEFAULT_SERVICE
+ do
+ create s.make_and_launch (agent execute)
+ end
+
+ execute (req: WSF_REQUEST; res: WSF_RESPONSE)
+ do
+ -- To send a response we need to setup, the status code and
+ -- the response headers.
+ res.write_header ({HTTP_STATUS_CODE}.ok, <<["Content-Type", "text/plain"], ["Content-Length", "11"]>>)
+ res.write_string ("Hello World")
+ end
+
+end
diff --git a/examples/simple/simple.ecf b/examples/simple/simple.ecf
new file mode 100644
index 00000000..e225d468
--- /dev/null
+++ b/examples/simple/simple.ecf
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+
+
+ /EIFGENs$
+ /CVS$
+ /.svn$
+
+
+
+
+
+
+
diff --git a/examples/simple/simple.rc b/examples/simple/simple.rc
new file mode 100644
index 00000000..8b137891
--- /dev/null
+++ b/examples/simple/simple.rc
@@ -0,0 +1 @@
+
diff --git a/examples/simple_file/home.html b/examples/simple_file/home.html
new file mode 100644
index 00000000..35ab8098
--- /dev/null
+++ b/examples/simple_file/home.html
@@ -0,0 +1,13 @@
+
+
+ Eiffel REST services
+
+
+
+ Welcome to the Eiffel REST services site, here you will find a lot of
+ resources about REST and our solution
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/simple_file/service_file.e b/examples/simple_file/service_file.e
new file mode 100644
index 00000000..df365de6
--- /dev/null
+++ b/examples/simple_file/service_file.e
@@ -0,0 +1,29 @@
+note
+ description : "simple application root class"
+ date : "$Date$"
+ revision : "$Revision$"
+
+class
+ SERVICE_FILE
+
+create
+ make
+
+feature {NONE} -- Initialization
+
+ make
+ -- Run application.
+ local
+ s: DEFAULT_SERVICE
+ do
+ create s.make_and_launch (agent execute)
+ end
+
+ execute (req: WSF_REQUEST; res: WSF_RESPONSE)
+ local
+ f: WSF_FILE_RESPONSE
+ do
+ create f.make_html ("home.html")
+ res.put_response (f)
+ end
+end
diff --git a/examples/simple_file/service_file.ecf b/examples/simple_file/service_file.ecf
new file mode 100644
index 00000000..1471600d
--- /dev/null
+++ b/examples/simple_file/service_file.ecf
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
+
+
+
+ /EIFGENs$
+ /CVS$
+ /.svn$
+
+
+
+
+
+
+
diff --git a/examples/simple_file/service_file.rc b/examples/simple_file/service_file.rc
new file mode 100644
index 00000000..8b137891
--- /dev/null
+++ b/examples/simple_file/service_file.rc
@@ -0,0 +1 @@
+
diff --git a/library/client/http_client/src/http_client_request.e b/library/client/http_client/src/http_client_request.e
index 597ce816..8749640c 100644
--- a/library/client/http_client/src/http_client_request.e
+++ b/library/client/http_client/src/http_client_request.e
@@ -12,16 +12,25 @@ inherit
feature {NONE} -- Initialization
- make (a_url: READABLE_STRING_8; a_session: like session)
+ make (a_url: READABLE_STRING_8; a_session: like session; ctx: like context)
-- Initialize `Current'.
do
session := a_session
url := a_url
headers := session.headers.twin
+ if ctx /= Void then
+ context := ctx
+ import (ctx)
+ end
+ ensure
+ context_set: context = ctx
+ ctx_header_set: ctx /= Void implies across ctx.headers as ctx_h all attached headers.item (ctx_h.key) as v and then v.same_string (ctx_h.item) end
end
session: HTTP_CLIENT_SESSION
+ context: detachable HTTP_CLIENT_REQUEST_CONTEXT
+
feature -- Access
request_method: READABLE_STRING_8
@@ -32,14 +41,23 @@ feature -- Access
headers: HASH_TABLE [READABLE_STRING_8, READABLE_STRING_8]
-feature -- Execution
+feature {HTTP_CLIENT_SESSION} -- Execution
import (ctx: HTTP_CLIENT_REQUEST_CONTEXT)
+ local
+ l_headers: like headers
do
- headers.fill (ctx.headers)
+ l_headers := headers
+ across
+ ctx.headers as ctx_headers
+ loop
+ --| fill header from `ctx'
+ --| and use `force' to overwrite the "session" value if any
+ l_headers.force (ctx_headers.item, ctx_headers.key)
+ end
end
- execute (ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): HTTP_CLIENT_RESPONSE
+ execute: HTTP_CLIENT_RESPONSE
deferred
end
diff --git a/library/client/http_client/src/http_client_response.e b/library/client/http_client/src/http_client_response.e
index bafa8523..5685ff73 100644
--- a/library/client/http_client/src/http_client_response.e
+++ b/library/client/http_client/src/http_client_response.e
@@ -41,8 +41,43 @@ feature -- Access
raw_header: READABLE_STRING_8
-- Raw http header of the response.
+ header (a_name: READABLE_STRING_8): detachable READABLE_STRING_8
+ -- Header entry value related to `a_name'
+ -- if multiple entries, just concatenate them using comma character
+ --| See: http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html
+ --| Multiple message-header fields with the same field-name MAY be present in a message
+ --| if and only if the entire field-value for that header field is defined as a comma-separated list [i.e., #(values)].
+ --| It MUST be possible to combine the multiple header fields into one "field-name: field-value" pair,
+ --| without changing the semantics of the message, by appending each subsequent field-value to the first, each separated by a comma.
+ --| The order in which header fields with the same field-name are received is therefore significant
+ --| to the interpretation of the combined field value, and thus a proxy MUST NOT change the order of
+ --| these field values when a message is forwarded.
+ local
+ s: detachable STRING_8
+ k,v: READABLE_STRING_8
+ do
+ across
+ headers as hds
+ loop
+ k := hds.item.key
+ if k.same_string (a_name) then
+ v := hds.item.value
+ if s = Void then
+ create s.make_from_string (v)
+ else
+ s.append_character (',')
+ s.append (v)
+ end
+ end
+ end
+ Result := s
+ end
+
headers: LIST [TUPLE [key: READABLE_STRING_8; value: READABLE_STRING_8]]
-- Computed table of http headers of the response.
+ --| We use a LIST since one might have multiple message-header fields with the same field-name
+ --| Then the user can handle those case using default or custom concatenation
+ --| (note: `header' is concatenating using comma)
local
tb: like internal_headers
pos, l_start, l_end, n, c: INTEGER
@@ -126,6 +161,6 @@ feature -- Change
feature {NONE} -- Implementation
internal_headers: detachable ARRAYED_LIST [TUPLE [key: READABLE_STRING_8; value: READABLE_STRING_8]]
- -- Internal cached value for the headers
+ -- Internal cached value for the headers
end
diff --git a/library/client/http_client/src/spec/libcurl/libcurl_http_client_request.e b/library/client/http_client/src/spec/libcurl/libcurl_http_client_request.e
index b29bfeee..160f6715 100644
--- a/library/client/http_client/src/spec/libcurl/libcurl_http_client_request.e
+++ b/library/client/http_client/src/spec/libcurl/libcurl_http_client_request.e
@@ -20,9 +20,9 @@ create
feature {NONE} -- Initialization
- make (a_url: READABLE_STRING_8; a_request_method: like request_method; a_session: like session)
+ make (a_url: READABLE_STRING_8; a_request_method: like request_method; a_session: like session; ctx: like context)
do
- make_request (a_url, a_session)
+ make_request (a_url, a_session, ctx)
request_method := a_request_method
end
@@ -34,7 +34,7 @@ feature -- Access
feature -- Execution
- execute (ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): HTTP_CLIENT_RESPONSE
+ execute: HTTP_CLIENT_RESPONSE
local
l_result: INTEGER
l_curl_string: CURL_STRING
@@ -45,7 +45,9 @@ feature -- Execution
curl: CURL_EXTERNALS
curl_easy: CURL_EASY_EXTERNALS
curl_handle: POINTER
+ ctx: like context
do
+ ctx := context
curl := session.curl
curl_easy := session.curl_easy
@@ -167,7 +169,15 @@ feature -- Execution
p := curl.slist_append (p, curs.key + ": " + curs.item)
end
end
-
+ if ctx /= Void then
+ if attached ctx.headers as l_headers_2 then
+ across
+ l_headers_2 as curs_2
+ loop
+ p := curl.slist_append (p, curs_2.key + ": " + curs_2.item)
+ end
+ end
+ end
p := curl.slist_append (p, "Expect:")
curl_easy.setopt_slist (curl_handle, {CURL_OPT_CONSTANTS}.curlopt_httpheader, p)
diff --git a/library/client/http_client/src/spec/libcurl/libcurl_http_client_session.e b/library/client/http_client/src/spec/libcurl/libcurl_http_client_session.e
index 24ccce32..7853657b 100644
--- a/library/client/http_client/src/spec/libcurl/libcurl_http_client_session.e
+++ b/library/client/http_client/src/spec/libcurl/libcurl_http_client_session.e
@@ -27,16 +27,16 @@ feature -- Basic operation
local
req: HTTP_CLIENT_REQUEST
do
- create {LIBCURL_HTTP_CLIENT_REQUEST} req.make (base_url + a_path, "GET", Current)
- Result := execute_request (req, ctx)
+ create {LIBCURL_HTTP_CLIENT_REQUEST} req.make (base_url + a_path, "GET", Current, ctx)
+ Result := req.execute
end
head (a_path: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): HTTP_CLIENT_RESPONSE
local
req: HTTP_CLIENT_REQUEST
do
- create {LIBCURL_HTTP_CLIENT_REQUEST} req.make (base_url + a_path, "HEAD", Current)
- Result := execute_request (req, ctx)
+ create {LIBCURL_HTTP_CLIENT_REQUEST} req.make (base_url + a_path, "HEAD", Current, ctx)
+ Result := req.execute
end
post (a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; data: detachable READABLE_STRING_8): HTTP_CLIENT_RESPONSE
@@ -54,7 +54,6 @@ feature -- Basic operation
req: HTTP_CLIENT_REQUEST
ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT
do
- create {LIBCURL_HTTP_CLIENT_REQUEST} req.make (base_url + a_path, "POST", Current)
ctx := a_ctx
if data /= Void then
if ctx = Void then
@@ -68,7 +67,8 @@ feature -- Basic operation
end
ctx.set_upload_filename (fn)
end
- Result := execute_request (req, ctx)
+ create {LIBCURL_HTTP_CLIENT_REQUEST} req.make (base_url + a_path, "POST", Current, ctx)
+ Result := req.execute
end
put (a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; data: detachable READABLE_STRING_8): HTTP_CLIENT_RESPONSE
@@ -76,7 +76,6 @@ feature -- Basic operation
req: HTTP_CLIENT_REQUEST
ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT
do
- create {LIBCURL_HTTP_CLIENT_REQUEST} req.make (base_url + a_path, "PUT", Current)
ctx := a_ctx
if data /= Void then
if ctx = Void then
@@ -84,7 +83,8 @@ feature -- Basic operation
end
ctx.set_upload_data (data)
end
- Result := execute_request (req, ctx)
+ create {LIBCURL_HTTP_CLIENT_REQUEST} req.make (base_url + a_path, "PUT", Current, ctx)
+ Result := req.execute
end
put_file (a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; fn: detachable READABLE_STRING_8): HTTP_CLIENT_RESPONSE
@@ -92,7 +92,6 @@ feature -- Basic operation
req: HTTP_CLIENT_REQUEST
ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT
do
- create {LIBCURL_HTTP_CLIENT_REQUEST} req.make (base_url + a_path, "PUT", Current)
ctx := a_ctx
if fn /= Void then
if ctx = Void then
@@ -100,25 +99,16 @@ feature -- Basic operation
end
ctx.set_upload_filename (fn)
end
- Result := execute_request (req, ctx)
+ create {LIBCURL_HTTP_CLIENT_REQUEST} req.make (base_url + a_path, "PUT", Current, ctx)
+ Result := req.execute
end
delete (a_path: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): HTTP_CLIENT_RESPONSE
local
req: HTTP_CLIENT_REQUEST
do
- create {LIBCURL_HTTP_CLIENT_REQUEST} req.make (base_url + a_path, "DELETE", Current)
- Result := execute_request (req, ctx)
- end
-
-feature {NONE} -- Implementation
-
- execute_request (req: HTTP_CLIENT_REQUEST; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): HTTP_CLIENT_RESPONSE
- do
- if ctx /= Void then
- req.import (ctx)
- end
- Result := req.execute (ctx)
+ create {LIBCURL_HTTP_CLIENT_REQUEST} req.make (base_url + a_path, "DELETE", Current, ctx)
+ Result := req.execute
end
feature {LIBCURL_HTTP_CLIENT_REQUEST} -- Curl implementation
diff --git a/library/protocol/http/src/http_header.e b/library/protocol/http/src/http_header.e
index 19385b1c..441eeb5a 100644
--- a/library/protocol/http/src/http_header.e
+++ b/library/protocol/http/src/http_header.e
@@ -196,13 +196,13 @@ feature -- Content related header
put_transfer_encoding_binary
-- Put "Transfer-Encoding: binary" header
do
- put_transfer_encoding ("binary")
+ put_transfer_encoding (str_binary)
end
put_transfer_encoding_chunked
-- Put "Transfer-Encoding: chunked" header
do
- put_transfer_encoding ("chunked")
+ put_transfer_encoding (str_chunked)
end
put_content_disposition (a_type: READABLE_STRING_8; a_params: detachable READABLE_STRING_8)
@@ -354,6 +354,35 @@ feature -- Cookie
feature -- Status report
+ header_named_value (a_name: READABLE_STRING_8): detachable STRING_8
+ -- Has header item for `n'?
+ 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 l_line.starts_with (a_name) then
+ if l_line.valid_index (n + 1) then
+ if l_line [n + 1] = ':' then
+ Result := l_line.substring (n + 2, l_line.count)
+ Result.left_adjust
+ Result.right_adjust
+ end
+ end
+ end
+ c.forth
+ end
+ end
+
has_header_named (a_name: READABLE_STRING_8): BOOLEAN
-- Has header item for `n'?
local
@@ -378,11 +407,26 @@ feature -- Status report
end
has_content_length: BOOLEAN
- -- Has header "content_length"
+ -- 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 {NONE} -- Implementation: Header
force_header_by_name (n: detachable READABLE_STRING_8; h: READABLE_STRING_8)
@@ -456,6 +500,9 @@ feature {NONE} -- Implementation
feature {NONE} -- Constants
+ str_binary: STRING = "binary"
+ str_chunked: STRING = "chunked"
+
colon_space: STRING = ": "
semi_colon_space: STRING = "; "
diff --git a/library/server/wsf/src/response/wsf_page_response.e b/library/server/wsf/src/response/wsf_page_response.e
index 25773eef..bbe85b38 100644
--- a/library/server/wsf/src/response/wsf_page_response.e
+++ b/library/server/wsf/src/response/wsf_page_response.e
@@ -11,7 +11,11 @@ inherit
WSF_RESPONSE_MESSAGE
create
- make
+ make,
+ make_with_body
+
+convert
+ make_with_body ({READABLE_STRING_8, STRING_8, IMMUTABLE_STRING_8})
feature {NONE} -- Initialization
@@ -21,6 +25,12 @@ feature {NONE} -- Initialization
create header.make
end
+ make_with_body (a_body: READABLE_STRING_8)
+ do
+ make
+ body := a_body
+ end
+
feature -- Status
status_code: INTEGER
@@ -34,10 +44,24 @@ feature -- Header
feature -- Output
send_to (res: WSF_RESPONSE)
+ local
+ b: like body
+ h: like header
do
+ h := header
+ b := body
res.set_status_code (status_code)
- res.write_header_text (header.string)
- if attached body as b then
+
+ if b /= Void then
+ if not h.has_content_length then
+ h.put_content_length (b.count)
+ end
+ if not h.has_content_type then
+ h.put_content_type_text_plain
+ end
+ end
+ res.write_header_text (h.string)
+ if b /= Void then
res.write_string (b)
end
end
diff --git a/library/server/wsf/src/wsf_request.e b/library/server/wsf/src/wsf_request.e
index ea92efd8..0db688c9 100644
--- a/library/server/wsf/src/wsf_request.e
+++ b/library/server/wsf/src/wsf_request.e
@@ -26,19 +26,22 @@ convert
feature {NONE} -- Initialization
make_from_wgi (r: WGI_REQUEST)
+ local
+ tb: like meta_variables_table
do
wgi_request := r
if attached r.meta_variables as l_vars then
- create meta_variables_table.make (l_vars.count)
+ create tb.make (l_vars.count)
across
l_vars as c
loop
- meta_variables_table.force (new_string_value (c.key, c.item), c.item)
+ tb.force (new_string_value (c.key, c.item), c.key)
end
else
- create meta_variables_table.make (0)
+ create tb.make (0)
end
- meta_variables := meta_variables_table
+ meta_variables_table := tb
+ meta_variables := tb
create error_handler.make
create uploaded_files.make (0)
raw_post_data_recorded := True