Merge branch 'master' of https://github.com/jocelyn/EWF
This commit is contained in:
18
README.md
18
README.md
@@ -15,25 +15,15 @@ For download, check
|
||||
* https://github.com/EiffelWebFramework/EWF/downloads
|
||||
|
||||
## Requirements
|
||||
* Compiling from EiffelStudio 7.0
|
||||
* Developped using EiffelStudio 7.1 (on Windows, Linux)
|
||||
* Tested using EiffelStudio 7.1 with "jenkins" CI server (not anymore compatible with 6.8 due to use of `TABLE_ITERABLE')
|
||||
* 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')
|
||||
* 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?
|
||||
|
||||
Using git version >= 1.6.5
|
||||
* git clone --recursive https://github.com/EiffelWebFramework/EWF.git
|
||||
|
||||
Otherwise, try
|
||||
Using git
|
||||
* git clone https://github.com/EiffelWebFramework/EWF.git
|
||||
* cd Eiffel-Web-Framework
|
||||
* git submodule update --init
|
||||
* git submodule foreach --recursive git checkout master
|
||||
|
||||
An alternative to the last 2 instructions is to use the script from tools folder:
|
||||
* cd tools
|
||||
* update_git_working_copy
|
||||
|
||||
* And to build the required and related Clibs
|
||||
* cd contrib/ise_library/cURL
|
||||
|
||||
@@ -29,7 +29,6 @@
|
||||
|
||||
<target name="_build_tpl_" >
|
||||
<argument name="_target_name" />
|
||||
<geant target="${_target_name}" dir="cURL" file="build.eant" reuse_variables="true" />
|
||||
</target>
|
||||
|
||||
</project>
|
||||
|
||||
28
doc/wiki/Using-the-policy-driven-framework.md
Normal file
28
doc/wiki/Using-the-policy-driven-framework.md
Normal file
@@ -0,0 +1,28 @@
|
||||
# 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.
|
||||
|
||||
We aim to provide unconditional compliance [See HTTP/1.1 specification](http://www.w3.org/Protocols/rfc2616/rfc2616-sec1.html#sec1) for you. Note that byte-ranges are not yet supported.
|
||||
|
||||
## Mapping the URI space
|
||||
|
||||
The authors first task is to decide which URIs the server will respond to (we do this using [URI templates](http://tools.ietf.org/html/rfc6570) ) and which methods are supported for each template.This is done in the class that that defines the service (which is often the root class for the application). This class must be a descendant of WSF_ROUTED_SKELETON_SERVICE. Throughout this tutorial, we will refer to the restbucksCRUD example application, which can be found in the EWF distribution in the examples directory. It's root class, RESTBUCKS_SERVER, inherits from WSF_ROUTED_SKELETON_SERVICE, as well as WSF_DEFAULT_SERVICE. The latter class means that you must specify in the ECF which connector you will use by default.This means you can easily change connectors just by changing the ECF and recompiling.
|
||||
|
||||
### Declaring your URI templates
|
||||
|
||||
In order to map your URI space to handlers (which you will write), you need to implement the routine setup_router. You can see in the example that the ORDER_HANDLER handler is associated with two URI templates. The URI /order is associated with the POST method (only). Any requests to /order with the GET method (or any other method) will result in an automatically generated compliant response being sent on your behalf to the client. The other principle methods (you get compliant responses to the HEAD method for free whenever you allow the GET method) are associated with the URI template /order/{orderid}. Here, orderid is a template variable. It's value for any given request is provided to your application as {WSF_REQUEST}.path_parameter ("orderid"). If the client passes a URI of /order/21, then you will see the value 21. If the client passes /order/fred, you will see the value fred. But if the client passes /order/21/new, he will see a compliant error response generated by the framework.
|
||||
|
||||
## Declaring your policy in responding to OPTIONS
|
||||
|
||||
WSF_ROUTED_SKELETON_SERVICE inherits from WSF_SYSTEM_OPTIONS_ACCESS_POLICY. This policy declares that the framework will provide a compliant default response to OPTIONS * requests. If you prefer to not respond to OPTIONS * requests (and I am doubtful if it is fully compliant to make that choice), then you can redefine
|
||||
is_system_options_forbidden.
|
||||
|
||||
## Declaring your policy on requiring use of a proxy server
|
||||
|
||||
WSF_ROUTED_SKELETON_SERVICE also inherits from WSF_PROXY_USE_POLICY. This determines if the server will require clients to use a proxy server. By default, it will do so for HTTP/1.0 clients. This is a sensible default, as the framework assumes an HTTP/1.1 client throughout. If you are sure that you will only ever have HTTP/1.1 clients, then you can instead inherit from WSF_NO_PROXY_POLICY, as RESTBUCKS_SERVER does. If not, then you need to implement proxy_server.
|
||||
|
||||
Next you have to [write your handler(s)](./Writing-the-handlers)
|
||||
3
doc/wiki/WSF_OPTIONS_POLICY.md
Normal file
3
doc/wiki/WSF_OPTIONS_POLICY.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# Implementing routines in WSF_OPTIONS_POLICY
|
||||
|
||||
This class provides a default response to OPTIONS requests other than OPTIONS *. So you don't have to do anything. The default response just includes the mandatory Allow headers for all the methods that are allowed for the request URI. if you want to include a body text, or additional header, then you should redefine this routine.
|
||||
223
doc/wiki/Writing-the-handlers.md
Normal file
223
doc/wiki/Writing-the-handlers.md
Normal file
@@ -0,0 +1,223 @@
|
||||
# Writing the handlers
|
||||
|
||||
Now you have to implement each handler. You need to inherit from WSF_SKELETON_HANDLER (as ORDER_HANDLER does). This involves implementing a lot of deferred routines. There are other routines for which default implementations are provided, which you might want to override. This applies to both routines defined in this class, and those declared in the three policy classes from which it inherits.
|
||||
|
||||
## Communicating between routines
|
||||
|
||||
Depending upon the connector (Nino, CGI, FastCGI etc.) that you are using, your handler may be invoked concurrently for multiple requests. Therefore it is unsafe to save state in normal attributes. WSF_REQUEST has a pair of getter/setter routines, execution_variable/set_execution_variable, which you can use for this purpose.
|
||||
Internally, the framework uses the following execution variable names, so you must avoid them:
|
||||
|
||||
1. REQUEST_ENTITY
|
||||
1. NEGOTIATED_LANGUAGE
|
||||
1. NEGOTIATED_CHARSET
|
||||
1. NEGOTIATED_MEDIA_TYPE
|
||||
1. NEGOTIATED_ENCODING
|
||||
1. NEGOTIATED_HTTP_HEADER
|
||||
1. CONFLICT_CHECK_CODE
|
||||
1. CONTENT_CHECK_CODE
|
||||
1. REQUEST_CHECK_CODE
|
||||
|
||||
The first one makes the request entity from a PULL or POST request available to your routines.
|
||||
|
||||
The next four make the results of content negotiation available to your routines. The sixth one makes an HTTP_HEADER available to your routines. You should use this rather than create your own, as it may contain a **Vary** header as a by-product of content negotiation.
|
||||
The last three are for reporting the result from check_conflict, check_content and check_request.
|
||||
|
||||
All names are defined as constants in WSF_SKELETON_HANDLER, to make it easier for you to refer to them.
|
||||
|
||||
## Implementing the routines declared directly in WSF_SKELETON_HANDLER
|
||||
|
||||
### check_resource_exists
|
||||
|
||||
Here you check for the existence of the resource named by the request URI. If it does, then you need to call set_resource_exists on the helper argument.
|
||||
Note that if you support multiple representations through content negotiation, then etags are dependent upon
|
||||
the selected variant. If you support etags, then you will need to make the response entity available at this point, rather than in ensure_content_available.
|
||||
|
||||
### is_chunking
|
||||
|
||||
HTTP/1.1 supports streaming responses (and providing you have configured your server to use a proxy server in WSF_PROXY_USE_POLICY, this framework guarantees you have an HTTP/1.1 client to deal with). It is up to you whether or not you choose to make use of it. If so, then you have to serve the response one chunk at a time (but you could generate it all at once, and slice it up as you go). In this routine you just say whether or not you will be doing this. So the framework n=knows which other routines to call.
|
||||
Currently we only support chunking for GET or HEAD routines. This might change in the future, so if you intend to return True, you should call req.is_get_head_request_method.
|
||||
Note that currently this framework does not support writing a trailer.
|
||||
|
||||
### includes_response_entity
|
||||
|
||||
The response to a DELETE, PUT or POST will include HTTP headers. It may or may not include a body. It is up to you, and this is where you tell the framework.
|
||||
|
||||
### conneg
|
||||
|
||||
[The HTTP/1.1 specification](http://www.w3.org/Protocols/rfc2616/rfc2616-sec12.html#sec12.1) defines server-driven content negotiation. Based on the Accept* headers in the request, we can determine whether we have a format for the response entity that is acceptable to the client. You need to indicate what formats you support. The framework does the rest. Normally you will have the same options for all requests, in which case you can use a once object.
|
||||
|
||||
### mime_types_supported
|
||||
|
||||
Here you need to indicate which media types you support for responses. One of the entries must be passed to the creation routine for conneg.
|
||||
|
||||
### languages_supported
|
||||
|
||||
Here you need to indicate which languages you support for responses. One of the entries must be passed to the creation routine for conneg.
|
||||
|
||||
|
||||
### charsets_supported
|
||||
|
||||
Here you need to indicate which character sets you support for responses. One of the entries must be passed to the creation routine for conneg.
|
||||
|
||||
|
||||
### encodings_supported
|
||||
|
||||
Here you need to indicate which compression encodings you support for responses. One of the entries must be passed to the creation routine for conneg.
|
||||
|
||||
### additional_variant_headers
|
||||
|
||||
The framework will write a Vary header if conneg indicates that different formats are supported. This warns caches that they may not be able to use a cached response if the Accept* headers in the request differ. If the author knows that the response may be affected by other request headers in addition to these, then they must be indicated here, so they can be included in a Vary header with the response.
|
||||
|
||||
### predictable_response
|
||||
|
||||
If the response may vary in other ways not predictable from the request headers, then redefine this routine to return True. In that case we will generate a Vary: * header to inform the cache that the response is not necessarily repeatable.
|
||||
|
||||
### matching_etag
|
||||
|
||||
An **ETag** header is a kind of message digest. Clients can use etags to avoid re-fetching responses for unchanged resources, or to avoid updating a resource that may have changed since the client last updated it.
|
||||
You must implement this routine to test for matches **if and only if** you return non-Void responses for the etag routine.
|
||||
Note that if you support multiple representations through content negotiation, then etags are dependent upon
|
||||
the selected variant. Therefore you will need to have the response entity available for this routine. This can be done in check_resource_exists.
|
||||
|
||||
### etag
|
||||
|
||||
You are strongly encouraged to return non-Void for this routine. See [Validation Model](http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.3) for more details.
|
||||
Note that if you support multiple representations through content negotiation, then etags are dependent upon
|
||||
the selected variant. Therefore you will need to have the response entity available for this routine. This can be done in check_resource_exists.
|
||||
|
||||
### modified_since
|
||||
|
||||
You need to implement this. If you do not have information about when a resource was last modified, then return True as a precaution. Of course, you return false for a static resource.
|
||||
|
||||
### treat_as_moved_permanently
|
||||
|
||||
This routine when a PUT request is made to a resource that does not exist. See [PUT](http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.6) in the HTTP/1.1 specification for why you might want to return zero.
|
||||
|
||||
### allow_post_to_missing_resource
|
||||
|
||||
POST requests are normally made to an existing entity. However it is possible to create new resources using a POST, if the server allows it. This is where you make that decision.
|
||||
|
||||
If you return True, and the resource is created, a 201 Created response will be returned.
|
||||
|
||||
### content_length
|
||||
|
||||
If you are not streaming the result, the the HTTP protocol requires that the length of the entity is known. You need to implement this routine to provide that information.
|
||||
|
||||
### finished
|
||||
|
||||
If you are streaming the response, then you need to tell the framework when the last chunk has been sent.
|
||||
To implement this routine, you will probably need to call req.set_execution_variable (some-name, True) in ensure_content_avaiable and generate_next_chunk, and call attached {BOOLEAN} req.execution_variable (some-name) in this routine.
|
||||
|
||||
### description
|
||||
|
||||
This is for the automatically generated documentation that the framework will generate in response to a request that you have not mapped into an handler.
|
||||
|
||||
### delete
|
||||
|
||||
This routine is for carrying out a DELETE request to a resource. If it is valid to delete the named resource, then you should either go ahead and do it, or queue a deletion request somewhere (if you do that then you will probably need to call req.set_execution_variable (some-name-or-other, True). Otherwise you should call req.error_handler.add_custom_error to explain why the DELETE could not proceed (you should also do this if the attempt to delete the resource fails).
|
||||
Of course, if you have not mapped any DELETE requests to the URI space of this handler, then you can just do nothing.
|
||||
|
||||
### delete_queued
|
||||
|
||||
If in the delete routine, you elected to queue the request, then you need to return True here. You will probably need to check the execution variable you set in the delete routine.
|
||||
|
||||
### ensure_content_available
|
||||
|
||||
This routine is called for GET and DELETE (when a entity is provided in the response) processing. It's purpose is to make the text of the entity (body of the response) available for future routines (if is_chunking is true, then only the first chunk needs to be made available, although if you only serve, as opposed to generate, the result in chunks, then you will make the entire entity available here). This is necessary so that we can compute the length before we start to serve the response. You would normally save it in an execution variable on the request object (as ORDER_HANDLER does). Note that this usage of execution variables ensures your routines can successfully cope with simultaneous requests. If you encounter a problem generating the content, then add an error to req.error_handler.
|
||||
|
||||
As well as the request object, we provide the results of content negotiation, so you can generate the entity in the agreed format. If you only support one format (i.e. all of mime_types_supported, charsets_supported, encodings_supported and languages_supported are one-element lists), then you are guaranteed that this is what you are being asked for, and so you can ignore them.
|
||||
|
||||
Note that if you support multiple representations through content negotiation, then etags are dependent upon
|
||||
the selected variant. Therefore you will need to have the response entity available for this routine. In such cases, this will have to be done in check_resource_exists, rather than here, as this routine is called later on.
|
||||
|
||||
### content
|
||||
|
||||
When not streaming, this routine provides the entity to the framework (for GET or DELETE). Normally you would just access the execution variable that you set in ensure_content_available. Again, the results of content negotiation are made available, but you probably don't need them at this stage. If you only stream responses (for GET), and if you don't support DELETE, then you don't need to do anything here.
|
||||
|
||||
### generate_next_chunk
|
||||
|
||||
When streaming the response, this routine is called to enable you to generate chunks beyond the first, so that you can incrementally generate the response entity. If you generated the entire response entity in
|
||||
ensure_content_available, then you do nothing here. Otherwise, you will generate the next chunk, and save it in the same execution variable that you use in ensure_content_available (or add an error to req.error_handler). If you don't support streaming, then you don't need to do anything here.
|
||||
|
||||
### next_chunk
|
||||
|
||||
When streaming the response, the framework calls this routine to provides the contents of each generated chunk. If you generated the entire response entity in ensure_content_available, then you need to slice it in this routine (you will have to keep track of where you are with execution variables). If instead you generate the response incrementally, then your task is much easier - you just access the execution variable saved in ensure_content_available/generate_next_chunk.
|
||||
As in all these content-serving routines, we provide the results of content negotiation. This might be necessary, for instance, if you were compressing an incrementally generated response (it might be more convenient to do the compression here rather than in both ensure_content_available and generate_next_chunk).
|
||||
|
||||
### read_entity
|
||||
|
||||
This is called for PUT and POST processing, to read the entity provided in the request. A default implementation is provided. This assumes that no decoding (e.g. decompression or character set conversion) is necessary. And it saves it in the execution variable REQUEST_ENTITY.
|
||||
|
||||
Currently the framework provides very little support for PUT and POST requests (so you may well need to redefine this routine). There are several reasons for this:
|
||||
|
||||
1. I personally don't have much experience with PUT and POST.
|
||||
1. It has taken a long time to develop this framework, and to some extent I was working in the dark (I couldn't check what I was doing until the entire framework was written - it wouldn't even compile before then).
|
||||
1. The idea for the framework came from a code review process on servers I had written for the company that I work for. I had acquired a lot of knowledge of the HTTP protocol in the process, and some of it showed in the code that I had written. It was thought that it would be a good idea if this knowledge were encapsulated in Eiffel, so other developers would be able to write servers without such knowledge. So this framework has been developed in company time. However, at present, we are only using GET requests.
|
||||
|
||||
Experience with converting the restbucksCRUD example to use the framework, shows that it is certainly possible to do POST and PUT processing with it. But enhancements are needed, especially in the area of decoding the request entity.
|
||||
|
||||
### is_entity_too_large
|
||||
|
||||
If your application has limits on the size of entities that it can store, then you implement them here.
|
||||
|
||||
### check_content_headers
|
||||
|
||||
This is called after is_entity_too_large returns False. You are supposed to check the following request headers, and take any appropriate actions (such as setting an error, decompression the entity, or converting it to a different character set):
|
||||
|
||||
* Content-Encoding
|
||||
* Content-Language
|
||||
* Content-MD5
|
||||
* Content-Range
|
||||
* Content-Type
|
||||
|
||||
At the moment, your duty is to set the execution variable CONTENT_CHECK_CODE to zero, or an HTTP error status code. A future enhancement of the framework might be to provide more support for this.
|
||||
|
||||
### content_check_code
|
||||
|
||||
This simply accesses the execution variable CONTENT_CHECK_CODE set in check_content_headers. if you want to use some other mechanism, then you can redefine this routine.
|
||||
|
||||
### create_resource
|
||||
|
||||
This routine is called when a PUT request is made with a URI that refers to a resource that does not exist (PUT is normally used for updating an existing resource), and you have already decided to allow this.
|
||||
In this routine you have the responsibilities of:
|
||||
|
||||
1. Creating the resource using the entity in REQUEST_ENTITY (or some decoded version that you have stored elsewhere).
|
||||
1. Writing the entire response yourself (as I said before, support for PUT and POST processing is poor at present), including setting the status code of 201 Created or 303 See Other or 500 Internal server error).
|
||||
|
||||
### append_resource
|
||||
|
||||
This routine is called for POST requests on an existing resource (normal usage).
|
||||
|
||||
In this routine you have the responsibilities of:
|
||||
|
||||
1. Storing the entity from REQUEST_ENTITY (or some decoded version that you have stored elsewhere), or whatever other action is appropriate for the semantics of POST requests to this URI.
|
||||
1. Writing the entire response yourself (as I said before, support for PUT and POST processing is poor at present), including setting the status code of 200 OK, 204 No Content, 303 See Other or 500 Internal server error).
|
||||
|
||||
### check_conflict
|
||||
|
||||
This is called for a normal (updating) PUT request. You have to check to see if the current state of the resource makes updating impossible. If so, then you need to write the entire response with a status code of 409 Conflict, and set the execution variable CONFLICT_CHECK_CODE to 409.
|
||||
Otherwise you just set the execution variable CONFLICT_CHECK_CODE to 0.
|
||||
|
||||
See [the HTTP/1.1 specification](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.10) for when you are allowed to use the 409 response, and what to write in the response entity. If this is not appropriate then a 500 Internal server error would be more appropriate (and set CONFLICT_CHECK_CODE to 500 - the framework only tests for non-zero).
|
||||
|
||||
### conflict_check_code
|
||||
|
||||
This is implemented to check CONFLICT_CHECK_CODE from the previous routine. If you choose to use a different mechanism, then you need to redefine this.
|
||||
|
||||
### check_request
|
||||
|
||||
This is called for PUT and POST requests. You need to check that the request entity (available in the execution variable REQUEST_ENTITY) is valid for the semantics of the request URI. You should set the execution variable REQUEST_CHECK_CODE to 0 if it is OK. If not, set it to 400 and write the full response, including a status code of 400 Bad Request.
|
||||
|
||||
### request_check_code
|
||||
|
||||
This routine just checks REQUEST_CHECK_CODE. if you choose to use a different mechanism, then redefine it.
|
||||
|
||||
### update_resource
|
||||
|
||||
This routine is called for a normal (updating) PUT request. You have to update the state of the resource using the entity saved in the execution environment variable REQUEST_ENTITY (or more likely elsewhere - see what ORDER_HANDLER does). Then write the entire response including a status code of 204 No Content or 500 Internal server error.
|
||||
|
||||
## Implementing the policies
|
||||
|
||||
* [WSF_OPTIONS_POLICY](./WSF_OPTIONS_POLICY)
|
||||
* [WSF_PREVIOUS_POLICY](./Wsf-previous-policy)
|
||||
* [WSF_CACHING_POLICY](./Wsf-caching-policy)
|
||||
52
doc/wiki/Wsf-caching-policy.md
Normal file
52
doc/wiki/Wsf-caching-policy.md
Normal file
@@ -0,0 +1,52 @@
|
||||
# Implementing WSF_CACHING_POLICY
|
||||
|
||||
This class contains a large number of routines, some of which have sensible defaults.
|
||||
|
||||
## age
|
||||
|
||||
This is used to generate a **Cache-Control: max-age** header. It says how old the response can before a cache will consider it stale (and therefore will need to revalidate with the server). Common values are zero (always consider it stale) and Never_expires (never always mean up to one year) and 1440 (one day).
|
||||
|
||||
## shared_age
|
||||
|
||||
This defaults to the same as age, so you only have to redefine it if you want a different value. If different from age, then we generate a **Cache-Control: s-max-age** header. This applies to shared caches only. Otherwise it has the same meaning as age. This overrides the value specified in age for shared caches.
|
||||
|
||||
## http_1_0_age
|
||||
|
||||
This generates an **Expires** header, and has the same meaning as age, but is understood by HTTP/1.0 caches. By default it has the same value as age. You only need to redefine this if you want to treat HTTP/1.0 caches differently (you might not trust them so well, so you might want to return 0 here).
|
||||
|
||||
## is_freely_cacheable
|
||||
|
||||
This routine says whether a shared cache can use this response for all client. If True, then it generates a **Cache-Control: public** header. If your data is at all sensitive, then you want to return False here.
|
||||
|
||||
## is_transformable
|
||||
|
||||
Non-transparent proxies are allowed to make some modifications to headers. If your application relies on this _not_ happening, then you want to return False here. This is the default, so you don't have to do anything. This means a **Cache-Control: no-transform** header will be generated.
|
||||
But most applications can return True.
|
||||
|
||||
## must_revalidate
|
||||
|
||||
Some clients request that their private cache ignores server expiry times (and so freely reuse stale responses). If you want to force revalidation anyway in such circumstances, then redefine to return True. In which case, we generate a **Cache-Control: must-revalidate** header.
|
||||
|
||||
## must_proxy_revalidate
|
||||
|
||||
This is the same as must_revalidate, but only applies to shared caches that are configured to serve stale responses. If you redefine to return True, then we generate a **Cache-Control: proxy-revalidate** header.
|
||||
|
||||
## private_headers
|
||||
|
||||
This is used to indicate that parts (or all) of a response are considered private to a single user, and should not be freely served from a shared cache. You must implement this routine. Your choices are:
|
||||
|
||||
1. Return Void. None of the response is considered private.
|
||||
1. Return and empty list. All of the response is considered private.
|
||||
1. Return a list of header names.
|
||||
|
||||
If you don't return Void, then a **Cache-Control: private** header will be generated.
|
||||
|
||||
## non_cacheable_headers
|
||||
|
||||
This is similar to private_headers, and you have the same three choices. the difference is that it is a list of headers (or the whole response) that will not be sent from a cache without revalidation.
|
||||
|
||||
If you don't return Void, then a **Cache-Control: no-cache** header will be generated.
|
||||
|
||||
## is_sensitive
|
||||
|
||||
Is the response to be considered of a sensitive nature? If so, then it will not be archived from a cache. We generate a **Cache-Control: no-store** header.
|
||||
19
doc/wiki/Wsf-previous-policy.md
Normal file
19
doc/wiki/Wsf-previous-policy.md
Normal file
@@ -0,0 +1,19 @@
|
||||
# WSF_PREVIOUS_POLICY
|
||||
|
||||
This class deals with resources that have moved or gone. The default assumes no such resources. It exists as a separate class, rather than have the routines directly in WSF_SKELETON_HANDLER, as sub-classing it may be convenient for an organisation.
|
||||
|
||||
## resource_previously_existed
|
||||
|
||||
Redefining this routine is always necessary if you want to deal with any previous resources.
|
||||
|
||||
## resource_moved_permanently
|
||||
|
||||
Redefine this routine for any resources that have permanently changed location. The framework will generate a 301 Moved Permanently response, and the user agent will automatically redirect the request to (one of) the new location(s) you provide. The user agent will use the new URI for future requests.
|
||||
|
||||
## resource_moved_temporarily
|
||||
|
||||
This is for resource that have only been moved for a short period. The framework will generate a 302 Found response. The only substantial difference between this and resource_moved_permanently, is that the agent will use the old URI for future requests.
|
||||
|
||||
## previous_location
|
||||
|
||||
When you redefine resource_moved_permanently or resource_moved_temporarily, the framework will generate a Location header for the new URI, and a hypertext document to the new URI(s). You **must** redefine this routine to provide those locations (the first one you provide will be in the location header).
|
||||
3
examples/restbucksCRUD/README-compilation.txt
Normal file
3
examples/restbucksCRUD/README-compilation.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
The current example has a main target for the server: "restbucks"
|
||||
But we also provide "policy_driven_restbucks" target which is using the
|
||||
policy-driven framework than help coder fulfill HTTP expectations.
|
||||
@@ -1,16 +1,11 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-10-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-10-0 http://www.eiffel.com/developers/xml/configuration-1-10-0.xsd" name="restbucks" uuid="7C9887BD-4AE4-47F2-A0AA-4BBB6736D433" library_target="restbucks">
|
||||
<target name="restbucks">
|
||||
<root class="RESTBUCKS_SERVER" feature="make"/>
|
||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-10-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-10-0 http://www.eiffel.com/developers/xml/configuration-1-10-0.xsd" name="restbucks" uuid="2773FEAA-448F-410E-BEDE-9298C4749066" library_target="restbucks">
|
||||
<target name="restbucks_common">
|
||||
<file_rule>
|
||||
<exclude>/EIFGENs$</exclude>
|
||||
<exclude>/\.git$</exclude>
|
||||
<exclude>/\.svn$</exclude>
|
||||
</file_rule>
|
||||
<option debug="true" warning="true" full_class_checking="true" is_attached_by_default="true" void_safety="all" syntax="provisional">
|
||||
<debug name="nino" enabled="true"/>
|
||||
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
|
||||
</option>
|
||||
<setting name="concurrency" value="thread"/>
|
||||
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
|
||||
<library name="connector_nino" location="..\..\library\server\ewsgi\connectors\nino\nino-safe.ecf" readonly="false">
|
||||
@@ -18,14 +13,41 @@
|
||||
<debug name="nino" enabled="true"/>
|
||||
</option>
|
||||
</library>
|
||||
<library name="conneg" location="..\..\library\network\protocol\CONNEG\conneg-safe.ecf"/>
|
||||
<library name="default_nino" location="..\..\library\server\wsf\default\nino-safe.ecf" readonly="false"/>
|
||||
<library name="eel" location="$ISE_LIBRARY\contrib\library\text\encryption\eel\eel-safe.ecf" readonly="false"/>
|
||||
<library name="encoder" location="..\..\library\text\encoder\encoder-safe.ecf" readonly="false"/>
|
||||
<library name="http" location="..\..\library\network\protocol\http\http-safe.ecf" readonly="false"/>
|
||||
<library name="json" location="..\..\contrib\library\text\parser\json\library\json-safe.ecf" readonly="false"/>
|
||||
<library name="time" location="$ISE_LIBRARY\library\time\time-safe.ecf"/>
|
||||
<library name="uri" location="$ISE_LIBRARY\library\text\uri\uri-safe.ecf"/>
|
||||
<library name="uri_template" location="..\..\library\text\parser\uri_template\uri_template-safe.ecf" readonly="false"/>
|
||||
<library name="wsf" location="..\..\library\server\wsf\wsf-safe.ecf" readonly="false"/>
|
||||
<library name="wsf_extension" location="..\..\library\server\wsf\wsf_extension-safe.ecf" readonly="false"/>
|
||||
<cluster name="src" location="src\" recursive="true"/>
|
||||
</target>
|
||||
<target name="restbucks" extends="restbucks_common">
|
||||
<root class="RESTBUCKS_SERVER" feature="make"/>
|
||||
<option debug="true" warning="true" full_class_checking="true" is_attached_by_default="true" void_safety="all" syntax="provisional">
|
||||
<debug name="nino" enabled="true"/>
|
||||
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
|
||||
</option>
|
||||
<cluster name="src" location="src\" recursive="true">
|
||||
<file_rule>
|
||||
<exclude>/policy_driven_resource$</exclude>
|
||||
</file_rule>
|
||||
</cluster>
|
||||
</target>
|
||||
<target name="policy_driven_restbucks" extends="restbucks_common">
|
||||
<root class="RESTBUCKS_SERVER" feature="make"/>
|
||||
<option debug="true" warning="true" full_class_checking="true" is_attached_by_default="true" void_safety="all" syntax="provisional">
|
||||
<debug name="nino" enabled="true"/>
|
||||
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
|
||||
</option>
|
||||
<library name="wsf_policy_driven" location="..\..\library\server\wsf\wsf_policy_driven-safe.ecf" readonly="false"/>
|
||||
<cluster name="src" location="src\" recursive="true">
|
||||
<file_rule>
|
||||
<exclude>/resource$</exclude>
|
||||
</file_rule>
|
||||
</cluster>
|
||||
</target>
|
||||
</system>
|
||||
|
||||
@@ -0,0 +1,558 @@
|
||||
note
|
||||
description: "{ORDER_HANDLER} handle the resources that we want to expose"
|
||||
author: ""
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class ORDER_HANDLER
|
||||
|
||||
inherit
|
||||
|
||||
WSF_SKELETON_HANDLER
|
||||
|
||||
SHARED_DATABASE_API
|
||||
|
||||
SHARED_EJSON
|
||||
|
||||
REFACTORING_HELPER
|
||||
|
||||
SHARED_ORDER_VALIDATION
|
||||
|
||||
WSF_RESOURCE_HANDLER_HELPER
|
||||
rename
|
||||
execute_options as helper_execute_options,
|
||||
handle_internal_server_error as helper_handle_internal_server_error
|
||||
end
|
||||
|
||||
create
|
||||
|
||||
make_with_router
|
||||
|
||||
|
||||
feature -- Execution variables
|
||||
|
||||
Order_execution_variable: STRING = "ORDER"
|
||||
-- Execution variable used by application
|
||||
|
||||
Generated_content_execution_variable: STRING = "GENERATED_CONTENT"
|
||||
-- Execution variable used by application
|
||||
|
||||
Extracted_order_execution_variable: STRING = "EXTRACTED_ORDER"
|
||||
-- Execution variable used by application
|
||||
|
||||
feature -- Documentation
|
||||
|
||||
description: READABLE_STRING_GENERAL
|
||||
-- General description for self-generated documentation;
|
||||
-- The specific URI templates supported will be described automatically
|
||||
do
|
||||
Result := "Create, Read, Update or Delete an ORDER."
|
||||
end
|
||||
|
||||
feature -- Access
|
||||
|
||||
is_chunking (req: WSF_REQUEST): BOOLEAN
|
||||
-- Will the response to `req' using chunked transfer encoding?
|
||||
do
|
||||
-- No.
|
||||
end
|
||||
|
||||
includes_response_entity (req: WSF_REQUEST): BOOLEAN
|
||||
-- Does the response to `req' include an entity?
|
||||
-- Method will be DELETE, POST, PUT or an extension method.
|
||||
do
|
||||
Result := False
|
||||
-- At present, there is no support for this except for DELETE.
|
||||
end
|
||||
|
||||
conneg (req: WSF_REQUEST): CONNEG_SERVER_SIDE
|
||||
-- Content negotiatior for all requests
|
||||
once
|
||||
create Result.make ({HTTP_MIME_TYPES}.application_json, "en", "UTF-8", "identity")
|
||||
end
|
||||
|
||||
mime_types_supported (req: WSF_REQUEST): LIST [STRING]
|
||||
-- All values for Accept header that `Current' can serve
|
||||
do
|
||||
create {ARRAYED_LIST [STRING]} Result.make_from_array (<<{HTTP_MIME_TYPES}.application_json>>)
|
||||
Result.compare_objects
|
||||
end
|
||||
|
||||
languages_supported (req: WSF_REQUEST): LIST [STRING]
|
||||
-- All values for Accept-Language header that `Current' can serve
|
||||
do
|
||||
create {ARRAYED_LIST [STRING]} Result.make_from_array (<<"en">>)
|
||||
Result.compare_objects
|
||||
end
|
||||
|
||||
charsets_supported (req: WSF_REQUEST): LIST [STRING]
|
||||
-- All values for Accept-Charset header that `Current' can serve
|
||||
do
|
||||
create {ARRAYED_LIST [STRING]} Result.make_from_array (<<"UTF-8">>)
|
||||
Result.compare_objects
|
||||
end
|
||||
|
||||
encodings_supported (req: WSF_REQUEST): LIST [STRING]
|
||||
-- All values for Accept-Encoding header that `Current' can serve
|
||||
do
|
||||
create {ARRAYED_LIST [STRING]} Result.make_from_array (<<"identity">>)
|
||||
Result.compare_objects
|
||||
end
|
||||
|
||||
max_age (req: WSF_REQUEST): NATURAL
|
||||
-- Maximum age in seconds before response to `req` is considered stale;
|
||||
-- This is used to generate a Cache-Control: max-age header.
|
||||
-- Return 0 to indicate already expired.
|
||||
-- Return Never_expires to indicate never expires.
|
||||
do
|
||||
-- All our responses are considered stale.
|
||||
end
|
||||
|
||||
is_freely_cacheable (req: WSF_REQUEST): BOOLEAN
|
||||
-- Should the response to `req' be freely cachable in shared caches?
|
||||
-- If `True', then a Cache-Control: public header will be generated.
|
||||
do
|
||||
-- definitely not!
|
||||
end
|
||||
|
||||
private_headers (req: WSF_REQUEST): detachable LIST [READABLE_STRING_8]
|
||||
-- Header names intended for a single user.
|
||||
-- If non-Void, then a Cache-Control: private header will be generated.
|
||||
-- Returning an empty list prevents the entire response from being served from a shared cache.
|
||||
do
|
||||
create {ARRAYED_LIST [READABLE_STRING_8]} Result.make (0)
|
||||
end
|
||||
|
||||
non_cacheable_headers (req: WSF_REQUEST): detachable LIST [READABLE_STRING_8]
|
||||
-- Header names that will not be sent from a cache without revalidation;
|
||||
-- If non-Void, then a Cache-Control: no-cache header will be generated.
|
||||
-- Returning an empty list prevents the response being served from a cache
|
||||
-- without revalidation.
|
||||
do
|
||||
create {ARRAYED_LIST [READABLE_STRING_8]} Result.make (0)
|
||||
end
|
||||
|
||||
is_sensitive (req: WSF_REQUEST): BOOLEAN
|
||||
-- Is the response to `req' of a sensitive nature?
|
||||
-- If `True' then a Cache-Control: no-store header will be generated.
|
||||
do
|
||||
Result := True
|
||||
-- since it's commercial data.
|
||||
end
|
||||
|
||||
matching_etag (req: WSF_REQUEST; a_etag: READABLE_STRING_32; a_strong: BOOLEAN): BOOLEAN
|
||||
-- Is `a_etag' a match for resource requested in `req'?
|
||||
-- If `a_strong' then the strong comparison function must be used.
|
||||
local
|
||||
l_id: STRING
|
||||
l_etag_util: ETAG_UTILS
|
||||
do
|
||||
l_id := order_id_from_request (req)
|
||||
if db_access.orders.has_key (l_id) then
|
||||
check attached db_access.orders.item (l_id) as l_order then
|
||||
-- postcondition of `has_key'
|
||||
create l_etag_util
|
||||
Result := a_etag.same_string (l_etag_util.md5_digest (l_order.out).as_string_32)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
etag (req: WSF_REQUEST): detachable READABLE_STRING_8
|
||||
-- Optional Etag for `req' in the requested variant
|
||||
local
|
||||
l_etag_utils: ETAG_UTILS
|
||||
do
|
||||
create l_etag_utils
|
||||
if attached {ORDER} req.execution_variable (Order_execution_variable) as l_order then
|
||||
Result := l_etag_utils.md5_digest (l_order.out)
|
||||
end
|
||||
end
|
||||
|
||||
modified_since (req: WSF_REQUEST; a_date_time: DATE_TIME): BOOLEAN
|
||||
-- Has resource requested in `req' been modified since `a_date_time' (UTC)?
|
||||
do
|
||||
-- We don't track this information. It is safe to always say yes.
|
||||
Result := True
|
||||
end
|
||||
|
||||
feature -- Measurement
|
||||
|
||||
content_length (req: WSF_REQUEST): NATURAL
|
||||
-- Length of entity-body of the response to `req'
|
||||
do
|
||||
check attached {READABLE_STRING_8} req.execution_variable (Generated_content_execution_variable) as l_response then
|
||||
-- postcondition generated_content_set_for_get_head of `ensure_content_available'
|
||||
-- We only call this for GET/HEAD in this example.
|
||||
Result := l_response.count.as_natural_32
|
||||
end
|
||||
end
|
||||
|
||||
allow_post_to_missing_resource (req: WSF_REQUEST): BOOLEAN
|
||||
-- The resource named in `req' does not exist, and this is a POST. Do we allow it?
|
||||
do
|
||||
-- No.
|
||||
end
|
||||
|
||||
feature -- Status report
|
||||
|
||||
finished (req: WSF_REQUEST): BOOLEAN
|
||||
-- Has the last chunk been generated for `req'?
|
||||
do
|
||||
-- precondition is never met
|
||||
end
|
||||
|
||||
feature -- Execution
|
||||
|
||||
check_resource_exists (req: WSF_REQUEST; a_helper: WSF_METHOD_HELPER)
|
||||
-- Call `a_helper.set_resource_exists' to indicate that `req.path_translated'
|
||||
-- is the name of an existing resource.
|
||||
-- We also put the order into `req.execution_variable (Order_execution_variable)' for GET or HEAD responses.
|
||||
local
|
||||
l_id: STRING
|
||||
do
|
||||
if req.is_post_request_method then
|
||||
a_helper.set_resource_exists
|
||||
-- because only /order is defined to this handler for POST
|
||||
else
|
||||
-- the request is of the form /order/{orderid}
|
||||
l_id := order_id_from_request (req)
|
||||
if db_access.orders.has_key (l_id) then
|
||||
a_helper.set_resource_exists
|
||||
if req.is_get_head_request_method then
|
||||
check attached db_access.orders.item (l_id) as l_order then
|
||||
-- postcondition `item_if_found' of `has_key'
|
||||
req.set_execution_variable (Order_execution_variable, l_order)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
ensure then
|
||||
order_saved_only_for_get_head: req.is_get_head_request_method =
|
||||
attached {ORDER} req.execution_variable (Order_execution_variable)
|
||||
end
|
||||
|
||||
feature -- GET/HEAD content
|
||||
|
||||
ensure_content_available (req: WSF_REQUEST)
|
||||
-- Commence generation of response text (entity-body).
|
||||
-- If not chunked, then this will create the entire entity-body so as to be available
|
||||
-- for a subsequent call to `content'.
|
||||
-- If chunked, only the first chunk will be made available to `next_chunk'. If chunk extensions
|
||||
-- are used, then this will also generate the chunk extension for the first chunk.
|
||||
-- We save the text in `req.execution_variable (Generated_content_execution_variable)'
|
||||
-- We ignore the results of content negotiation, as there is only one possible combination.
|
||||
do
|
||||
check attached {ORDER} req.execution_variable (Order_execution_variable) as l_order then
|
||||
-- precondition get_or_head and postcondition order_saved_only_for_get_head of `check_resource_exists' and
|
||||
if attached {JSON_VALUE} json.value (l_order) as jv then
|
||||
req.set_execution_variable (Generated_content_execution_variable, jv.representation)
|
||||
else
|
||||
req.set_execution_variable (Generated_content_execution_variable, "")
|
||||
end
|
||||
end
|
||||
ensure then
|
||||
generated_content_set_for_get_head: req.is_get_head_request_method implies
|
||||
attached {READABLE_STRING_8} req.execution_variable (Generated_content_execution_variable)
|
||||
end
|
||||
|
||||
content (req: WSF_REQUEST): READABLE_STRING_8
|
||||
-- Non-chunked entity body in response to `req';
|
||||
-- We only call this for GET/HEAD in this example.
|
||||
do
|
||||
check attached {READABLE_STRING_8} req.execution_variable (Generated_content_execution_variable) as l_response then
|
||||
-- postcondition generated_content_set_for_get_head of `ensure_content_available'
|
||||
Result := l_response
|
||||
end
|
||||
end
|
||||
|
||||
next_chunk (req: WSF_REQUEST): TUPLE [a_chunk: READABLE_STRING_8; a_extension: detachable READABLE_STRING_8]
|
||||
-- Next chunk of entity body in response to `req';
|
||||
-- The second field of the result is an optional chunk extension.
|
||||
do
|
||||
-- precondition `is_chunking' is never met, but we need a dummy `Result'
|
||||
-- to satisfy the compiler in void-safe mode
|
||||
Result := ["", Void]
|
||||
end
|
||||
|
||||
generate_next_chunk (req: WSF_REQUEST)
|
||||
-- Prepare next chunk (including optional chunk extension) of entity body in response to `req'.
|
||||
-- This is not called for the first chunk.
|
||||
do
|
||||
-- precondition `is_chunking' is never met
|
||||
end
|
||||
|
||||
feature -- DELETE
|
||||
|
||||
delete (req: WSF_REQUEST)
|
||||
-- Delete resource named in `req' or set an error on `req.error_handler'.
|
||||
local
|
||||
l_id: STRING
|
||||
do
|
||||
l_id := order_id_from_request (req)
|
||||
if db_access.orders.has_key (l_id) then
|
||||
if is_valid_to_delete (l_id) then
|
||||
delete_order (l_id)
|
||||
else
|
||||
req.error_handler.add_custom_error ({HTTP_STATUS_CODE}.method_not_allowed, "DELETE not valid",
|
||||
"There is conflict while trying to delete the order, the order could not be deleted in the current state")
|
||||
end
|
||||
else
|
||||
req.error_handler.add_custom_error ({HTTP_STATUS_CODE}.not_found, "DELETE not valid",
|
||||
"There is no such order to delete")
|
||||
end
|
||||
end
|
||||
|
||||
delete_queued (req: WSF_REQUEST): BOOLEAN
|
||||
-- Has resource named by `req' been queued for deletion?
|
||||
do
|
||||
-- No
|
||||
end
|
||||
|
||||
|
||||
feature -- PUT/POST
|
||||
|
||||
is_entity_too_large (req: WSF_REQUEST): BOOLEAN
|
||||
-- Is the entity stored in `req.execution_variable (Request_entity_execution_variable)' too large for the application?
|
||||
do
|
||||
-- No. We don't care for this example.
|
||||
end
|
||||
|
||||
check_content_headers (req: WSF_REQUEST)
|
||||
-- Check we can support all content headers on request entity.
|
||||
-- Set `req.execution_variable (Content_check_code_execution_variable)' to {NATURAL} zero if OK, or 415 or 501 if not.
|
||||
do
|
||||
-- We don't bother for this example. Note that this is equivalent to setting zero.
|
||||
end
|
||||
|
||||
create_resource (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
-- Create new resource in response to a PUT request when `check_resource_exists' returns `False'.
|
||||
-- Implementor must set error code of 200 OK or 500 Server Error.
|
||||
do
|
||||
-- We don't support creating a new resource with PUT. But this can't happen
|
||||
-- with our router mappings, so we don't bother to set a 500 response.
|
||||
end
|
||||
|
||||
append_resource (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
-- Create new resource in response to a POST request.
|
||||
-- Implementor must set error code of 200 OK or 204 No Content or 303 See Other or 500 Server Error.
|
||||
do
|
||||
if attached {ORDER} req.execution_variable (Extracted_order_execution_variable) as l_order then
|
||||
save_order (l_order)
|
||||
compute_response_post (req, res, l_order)
|
||||
else
|
||||
handle_bad_request_response ("Not a valid order", req, res)
|
||||
end
|
||||
end
|
||||
|
||||
check_conflict (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
-- Check we can support all content headers on request entity.
|
||||
-- Set `req.execution_variable (Conflict_check_code_execution_variable)' to {NATURAL} zero if OK, or 409 if not.
|
||||
-- In the latter case, write the full error response to `res'.
|
||||
do
|
||||
if attached {ORDER} req.execution_variable (Extracted_order_execution_variable) as l_order then
|
||||
if not is_valid_to_update (l_order) then
|
||||
req.set_execution_variable (Conflict_check_code_execution_variable, {NATURAL} 409)
|
||||
handle_resource_conflict_response (l_order.out +"%N There is conflict while trying to update the order, the order could not be update in the current state", req, res)
|
||||
end
|
||||
else
|
||||
req.set_execution_variable (Conflict_check_code_execution_variable, {NATURAL} 409)
|
||||
--| This ought to be a 500, as if attached should probably be check attached. But as yet I lack a proof.
|
||||
handle_resource_conflict_response ("There is conflict while trying to update the order, the order could not be update in the current state", req, res)
|
||||
end
|
||||
end
|
||||
|
||||
check_request (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
-- Check that the request entity is a valid request.
|
||||
-- The entity is available as `req.execution_variable (Conflict_check_code_execution_variable)'.
|
||||
-- Set `req.execution_variable (Request_check_code_execution_variable)' to {NATURAL} zero if OK, or 400 if not.
|
||||
-- In the latter case, write the full error response to `res'.
|
||||
local
|
||||
l_order: detachable ORDER
|
||||
l_id: STRING
|
||||
do
|
||||
if attached {READABLE_STRING_8} req.execution_variable (Request_entity_execution_variable) as l_request then
|
||||
l_order := extract_order_request (l_request)
|
||||
if req.is_put_request_method then
|
||||
l_id := order_id_from_request (req)
|
||||
if l_order /= Void and then db_access.orders.has_key (l_id) then
|
||||
l_order.set_id (l_id)
|
||||
req.set_execution_variable (Request_check_code_execution_variable, {NATURAL} 0)
|
||||
req.set_execution_variable (Extracted_order_execution_variable, l_order)
|
||||
else
|
||||
req.set_execution_variable (Request_check_code_execution_variable, {NATURAL} 400)
|
||||
handle_bad_request_response (l_request +"%N is not a valid ORDER, maybe the order does not exist in the system", req, res)
|
||||
end
|
||||
else
|
||||
req.set_execution_variable (Request_check_code_execution_variable, {NATURAL} 0)
|
||||
req.set_execution_variable (Extracted_order_execution_variable, l_order)
|
||||
end
|
||||
else
|
||||
req.set_execution_variable (Request_check_code_execution_variable, {NATURAL} 400)
|
||||
handle_bad_request_response ("Request is not a valid ORDER", req, res)
|
||||
end
|
||||
end
|
||||
|
||||
update_resource (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
-- Perform the update requested in `req'.
|
||||
-- Write a response to `res' with a code of 204 or 500.
|
||||
do
|
||||
if attached {ORDER} req.execution_variable (Extracted_order_execution_variable) as l_order then
|
||||
update_order (l_order)
|
||||
compute_response_put (req, res, l_order)
|
||||
else
|
||||
handle_internal_server_error (res)
|
||||
end
|
||||
end
|
||||
|
||||
feature -- HTTP Methods
|
||||
|
||||
compute_response_put (req: WSF_REQUEST; res: WSF_RESPONSE; l_order : ORDER)
|
||||
local
|
||||
h: HTTP_HEADER
|
||||
joc : JSON_ORDER_CONVERTER
|
||||
etag_utils : ETAG_UTILS
|
||||
do
|
||||
create h.make
|
||||
create joc.make
|
||||
create etag_utils
|
||||
json.add_converter(joc)
|
||||
|
||||
create h.make
|
||||
h.put_content_type_application_json
|
||||
if attached req.request_time as time then
|
||||
h.add_header ("Date:" +time.formatted_out ("ddd,[0]dd mmm yyyy [0]hh:[0]mi:[0]ss.ff2") + " GMT")
|
||||
end
|
||||
h.add_header ("etag:" + etag_utils.md5_digest (l_order.out))
|
||||
if attached {JSON_VALUE} json.value (l_order) as jv then
|
||||
h.put_content_length (jv.representation.count)
|
||||
res.set_status_code ({HTTP_STATUS_CODE}.ok)
|
||||
res.put_header_text (h.string)
|
||||
res.put_string (jv.representation)
|
||||
end
|
||||
end
|
||||
|
||||
compute_response_post (req: WSF_REQUEST; res: WSF_RESPONSE; l_order : ORDER)
|
||||
local
|
||||
h: HTTP_HEADER
|
||||
l_msg : STRING
|
||||
l_location : STRING
|
||||
joc : JSON_ORDER_CONVERTER
|
||||
do
|
||||
create h.make
|
||||
|
||||
create joc.make
|
||||
json.add_converter(joc)
|
||||
|
||||
h.put_content_type_application_json
|
||||
if attached {JSON_VALUE} json.value (l_order) as jv then
|
||||
l_msg := jv.representation
|
||||
h.put_content_length (l_msg.count)
|
||||
if attached req.http_host as host then
|
||||
l_location := "http://" + host + req.request_uri + "/" + l_order.id
|
||||
h.put_location (l_location)
|
||||
end
|
||||
if attached req.request_time as time then
|
||||
h.put_utc_date (time)
|
||||
end
|
||||
res.set_status_code ({HTTP_STATUS_CODE}.created)
|
||||
res.put_header_text (h.string)
|
||||
res.put_string (l_msg)
|
||||
end
|
||||
end
|
||||
|
||||
feature {NONE} -- URI helper methods
|
||||
|
||||
order_id_from_request (req: WSF_REQUEST): STRING
|
||||
-- Value of "orderid" template URI variable in `req'
|
||||
require
|
||||
req_attached: req /= Void
|
||||
do
|
||||
if attached {WSF_VALUE} req.path_parameter ("orderid") as l_value then
|
||||
Result := l_value.as_string.value.as_string_8
|
||||
else
|
||||
Result := ""
|
||||
end
|
||||
end
|
||||
|
||||
feature {NONE} -- Implementation Repository Layer
|
||||
|
||||
retrieve_order ( id : STRING) : detachable ORDER
|
||||
-- get the order by id if it exist, in other case, Void
|
||||
do
|
||||
Result := db_access.orders.item (id)
|
||||
end
|
||||
|
||||
save_order (an_order: ORDER)
|
||||
-- save the order to the repository
|
||||
local
|
||||
i : INTEGER
|
||||
do
|
||||
from
|
||||
i := 1
|
||||
until
|
||||
not db_access.orders.has_key ((db_access.orders.count + i).out)
|
||||
loop
|
||||
i := i + 1
|
||||
end
|
||||
an_order.set_id ((db_access.orders.count + i).out)
|
||||
an_order.set_status ("submitted")
|
||||
an_order.add_revision
|
||||
db_access.orders.force (an_order, an_order.id)
|
||||
end
|
||||
|
||||
|
||||
is_valid_to_delete ( an_id : STRING) : BOOLEAN
|
||||
-- Is the order identified by `an_id' in a state whre it can still be deleted?
|
||||
do
|
||||
if attached retrieve_order (an_id) as l_order then
|
||||
if order_validation.is_state_valid_to_update (l_order.status) then
|
||||
Result := True
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
is_valid_to_update (an_order: ORDER) : BOOLEAN
|
||||
-- Check if there is a conflict while trying to update the order
|
||||
do
|
||||
if attached retrieve_order (an_order.id) as l_order then
|
||||
if order_validation.is_state_valid_to_update (l_order.status) and then order_validation.is_valid_status_state (an_order.status) and then
|
||||
order_validation.is_valid_transition (l_order, an_order.status) then
|
||||
Result := True
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
update_order (an_order: ORDER)
|
||||
-- update the order to the repository
|
||||
do
|
||||
an_order.add_revision
|
||||
db_access.orders.force (an_order, an_order.id)
|
||||
end
|
||||
|
||||
delete_order (an_order: STRING)
|
||||
-- update the order to the repository
|
||||
do
|
||||
db_access.orders.remove (an_order)
|
||||
end
|
||||
|
||||
extract_order_request (l_post : STRING) : detachable ORDER
|
||||
-- extract an object Order from the request, or Void
|
||||
-- if the request is invalid
|
||||
local
|
||||
parser : JSON_PARSER
|
||||
joc : JSON_ORDER_CONVERTER
|
||||
do
|
||||
create joc.make
|
||||
json.add_converter(joc)
|
||||
create parser.make_parser (l_post)
|
||||
if attached parser.parse as jv and parser.is_parsed then
|
||||
if attached {like extract_order_request} json.object (jv, "ORDER") as res then
|
||||
Result := res
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2013, Javier Velilla and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
end
|
||||
@@ -27,6 +27,26 @@ inherit
|
||||
|
||||
WSF_SELF_DOCUMENTED_HANDLER
|
||||
|
||||
create
|
||||
make_with_router
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
make_with_router (a_router: WSF_ROUTER)
|
||||
-- Initialize `router'.
|
||||
require
|
||||
a_router_attached: a_router /= Void
|
||||
do
|
||||
router := a_router
|
||||
ensure
|
||||
router_aliased: router = a_router
|
||||
end
|
||||
|
||||
feature -- Router
|
||||
|
||||
router: WSF_ROUTER
|
||||
-- Associated router that could be used for advanced strategy
|
||||
|
||||
feature -- Execute
|
||||
|
||||
execute (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
@@ -65,16 +85,16 @@ feature -- HTTP Methods
|
||||
local
|
||||
id: STRING
|
||||
do
|
||||
if attached req.orig_path_info as orig_path then
|
||||
id := get_order_id_from_path (orig_path)
|
||||
if attached req.path_info as l_path_info then
|
||||
id := get_order_id_from_path (l_path_info)
|
||||
if attached retrieve_order (id) as l_order then
|
||||
if is_conditional_get (req, l_order) then
|
||||
handle_resource_not_modified_response ("The resource" + orig_path + "does not change", req, res)
|
||||
handle_resource_not_modified_response ("The resource" + l_path_info + "does not change", req, res)
|
||||
else
|
||||
compute_response_get (req, res, l_order)
|
||||
end
|
||||
else
|
||||
handle_resource_not_found_response ("The following resource" + orig_path + " is not found ", req, res)
|
||||
handle_resource_not_found_response ("The following resource" + l_path_info + " is not found ", req, res)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -131,8 +151,8 @@ feature -- HTTP Methods
|
||||
l_order : detachable ORDER
|
||||
id : STRING
|
||||
do
|
||||
if attached req.orig_path_info as orig_path then
|
||||
id := get_order_id_from_path (orig_path)
|
||||
if attached req.path_info as l_path_info then
|
||||
id := get_order_id_from_path (l_path_info)
|
||||
l_put := retrieve_data (req)
|
||||
l_order := extract_order_request(l_put)
|
||||
if l_order /= Void and then db_access.orders.has_key (id) then
|
||||
@@ -209,18 +229,18 @@ feature -- HTTP Methods
|
||||
local
|
||||
id: STRING
|
||||
do
|
||||
if attached req.orig_path_info as orig_path then
|
||||
id := get_order_id_from_path (orig_path)
|
||||
if attached req.path_info as l_path_info then
|
||||
id := get_order_id_from_path (l_path_info)
|
||||
if db_access.orders.has_key (id) then
|
||||
if is_valid_to_delete (id) then
|
||||
delete_order( id)
|
||||
compute_response_delete (req, res)
|
||||
else
|
||||
--| FIXME: Here we need to define the Allow methods
|
||||
handle_method_not_allowed_response (orig_path + "%N There is conflict while trying to delete the order, the order could not be deleted in the current state", req, res)
|
||||
handle_method_not_allowed_response (l_path_info + "%N There is conflict while trying to delete the order, the order could not be deleted in the current state", req, res)
|
||||
end
|
||||
else
|
||||
handle_resource_not_found_response (orig_path + " not found in this server", req, res)
|
||||
handle_resource_not_found_response (l_path_info + " not found in this server", req, res)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -374,6 +394,6 @@ feature {NONE} -- Implementation Repository Layer
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2012, Javier Velilla and others"
|
||||
copyright: "2011-2013, Javier Velilla and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
end
|
||||
|
||||
@@ -37,7 +37,7 @@ feature {NONE} -- Initialization
|
||||
order_handler: ORDER_HANDLER
|
||||
doc: WSF_ROUTER_SELF_DOCUMENTATION_HANDLER
|
||||
do
|
||||
create order_handler
|
||||
create order_handler.make_with_router (router)
|
||||
router.handle_with_request_methods ("/order", order_handler, router.methods_POST)
|
||||
router.handle_with_request_methods ("/order/{orderid}", order_handler, router.methods_GET + router.methods_DELETE + router.methods_PUT)
|
||||
create doc.make_hidden (router)
|
||||
|
||||
@@ -350,7 +350,9 @@ feature -- Header change: general
|
||||
end
|
||||
|
||||
add_header_key_value (k,v: READABLE_STRING_8)
|
||||
-- Add header `k:v', or replace existing header of same header name/key
|
||||
-- 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
|
||||
@@ -509,6 +511,18 @@ feature -- Content related header
|
||||
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
|
||||
|
||||
@@ -54,6 +54,18 @@ feature -- Access
|
||||
|
||||
http_if_match: STRING = "HTTP_IF_MATCH"
|
||||
|
||||
http_if_modified_since: STRING = "HTTP_IF_MODIFIED_SINCE"
|
||||
|
||||
http_if_none_match: STRING = "HTTP_IF_NONE_MATCH"
|
||||
|
||||
http_if_range: STRING = "HTTP_IF_RANGE"
|
||||
|
||||
http_if_unmodified_since: STRING = "HTTP_IF_UNMODIFIED_SINCE"
|
||||
|
||||
http_last_modified: STRING = "HTTP_LAST_MODIFIED"
|
||||
|
||||
http_range: STRING = "HTTP_RANGE"
|
||||
|
||||
gateway_interface: STRING = "GATEWAY_INTERFACE"
|
||||
|
||||
auth_type: STRING = "AUTH_TYPE"
|
||||
|
||||
@@ -609,6 +609,36 @@ feature -- HTTP_*
|
||||
deferred
|
||||
end
|
||||
|
||||
http_if_modified_since: detachable READABLE_STRING_8
|
||||
-- Modification check on resource
|
||||
deferred
|
||||
end
|
||||
|
||||
http_if_none_match: detachable READABLE_STRING_8
|
||||
-- Existence check on resource
|
||||
deferred
|
||||
end
|
||||
|
||||
http_if_range: detachable READABLE_STRING_8
|
||||
-- Range check on resource
|
||||
deferred
|
||||
end
|
||||
|
||||
http_if_unmodified_since: detachable READABLE_STRING_8
|
||||
-- Modification check on resource
|
||||
deferred
|
||||
end
|
||||
|
||||
http_last_modified: detachable READABLE_STRING_8
|
||||
-- Modification time of resource
|
||||
deferred
|
||||
end
|
||||
|
||||
http_range: detachable READABLE_STRING_8
|
||||
-- Requested byte-range of resource
|
||||
deferred
|
||||
end
|
||||
|
||||
feature -- Extra CGI environment variables
|
||||
|
||||
request_uri: READABLE_STRING_8
|
||||
|
||||
@@ -253,6 +253,42 @@ feature -- Access: HTTP_* CGI meta parameters - 1.1
|
||||
do
|
||||
Result := meta_string_variable ({WGI_META_NAMES}.http_if_match)
|
||||
end
|
||||
|
||||
http_if_modified_since: detachable READABLE_STRING_8
|
||||
-- Modification check on resource
|
||||
do
|
||||
Result := meta_string_variable ({WGI_META_NAMES}.http_if_modified_since)
|
||||
end
|
||||
|
||||
http_if_none_match: detachable READABLE_STRING_8
|
||||
-- Existence check on resource
|
||||
do
|
||||
Result := meta_string_variable ({WGI_META_NAMES}.http_if_none_match)
|
||||
end
|
||||
|
||||
http_if_range: detachable READABLE_STRING_8
|
||||
-- Range check on resource
|
||||
do
|
||||
Result := meta_string_variable ({WGI_META_NAMES}.http_if_range)
|
||||
end
|
||||
|
||||
http_if_unmodified_since: detachable READABLE_STRING_8
|
||||
-- Modification check on resource
|
||||
do
|
||||
Result := meta_string_variable ({WGI_META_NAMES}.http_if_unmodified_since)
|
||||
end
|
||||
|
||||
http_last_modified: detachable READABLE_STRING_8
|
||||
-- Modification time of resource
|
||||
do
|
||||
Result := meta_string_variable ({WGI_META_NAMES}.http_last_modified)
|
||||
end
|
||||
|
||||
http_range: detachable READABLE_STRING_8
|
||||
-- Requested byte-range of resource
|
||||
do
|
||||
Result := meta_string_variable ({WGI_META_NAMES}.http_range)
|
||||
end
|
||||
|
||||
feature -- Access: Extension to CGI meta parameters - 1.1
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
== libFCGI for Eiffel libFCGI wrapper library ==
|
||||
# libFCGI for Eiffel libFCGI wrapper library
|
||||
|
||||
=== On Windows ===
|
||||
## On Windows
|
||||
|
||||
The Eiffel libFCGI wrapper needs a modified version of libFCGI (provided by http://www.fastcgi.com/devkit/libfcgi/)
|
||||
|
||||
@@ -12,10 +12,15 @@ And then to build the needed .dll and .lib file, use either:
|
||||
build_win32.bat
|
||||
or build_win64.bat
|
||||
|
||||
=== On other platorms ===
|
||||
## On other platorms
|
||||
|
||||
You can use the original version of libfcgi
|
||||
|
||||
For instance, on Ubuntu (or any debian):
|
||||
### Debian based system (Ubuntu, ...)
|
||||
On Ubuntu (or any Debian based system):
|
||||
> sudo apt-get install libfcgi-dev
|
||||
|
||||
|
||||
### Mac OS X
|
||||
On Mac OS X:
|
||||
> sudo port install fcgi
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
== Eiffel libFCGI wrapper library ==
|
||||
# Eiffel libFCGI wrapper library
|
||||
|
||||
This Eiffel library wraps the libFCGI devkit from http://www.fastcgi.com/devkit/libfcgi/
|
||||
|
||||
=== Windows ===
|
||||
## Windows
|
||||
To compile your own binaries .lib and .dll on __Windows__: [read more](Clib/README.md)
|
||||
|
||||
=== Others ===
|
||||
## Others
|
||||
|
||||
==== Debian based system (ubuntu, ...) ====
|
||||
To install the fcgi lib on debian, you can use
|
||||
### Debian based system (ubuntu, ...)
|
||||
To install the fcgi lib on Ubuntu (or any Debian based system), you can use
|
||||
> sudo apt-get install libfcgi-dev
|
||||
|
||||
### Mac OS X
|
||||
To install the fcgi lib on Mac OS X, you can use [MacPorts](http://www.macports.org/)
|
||||
> sudo port install fcgi
|
||||
|
||||
144
library/server/libfcgi/implementation/mac/fcgi_c_api.e
Normal file
144
library/server/libfcgi/implementation/mac/fcgi_c_api.e
Normal file
@@ -0,0 +1,144 @@
|
||||
note
|
||||
description: "Wrappers around FastCGI C API."
|
||||
legal: "See notice at end of class."
|
||||
status: "See notice at end of class."
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
FCGI_C_API
|
||||
|
||||
feature -- Connection
|
||||
|
||||
accept: INTEGER
|
||||
-- Accept a Fast CGI connection.
|
||||
-- Return 0 for successful calls, -1 otherwise.
|
||||
external
|
||||
"C inline use %"fcgi_stdio.h%""
|
||||
alias
|
||||
"return FCGI_Accept();"
|
||||
end
|
||||
|
||||
environ: POINTER
|
||||
-- Get the (char**) environ variable from the DLL.
|
||||
external
|
||||
"C inline use %"crt_externs.h%""
|
||||
alias
|
||||
"return (char*) _NSGetEnviron();"
|
||||
end
|
||||
|
||||
finish
|
||||
-- Finished current request from HTTP server started from
|
||||
-- the most recent call to `fcgi_accept'.
|
||||
external
|
||||
"C inline use %"fcgi_stdio.h%""
|
||||
alias
|
||||
"FCGI_Finish();"
|
||||
end
|
||||
|
||||
set_exit_status (v: INTEGER)
|
||||
-- Set the exit status for the most recent request
|
||||
external
|
||||
"C inline use %"fcgi_stdio.h%""
|
||||
alias
|
||||
"FCGI_SetExitStatus($v);"
|
||||
end
|
||||
|
||||
feature -- Input
|
||||
|
||||
read_content_into (a_buffer: POINTER; a_length: INTEGER): INTEGER
|
||||
-- Read content stream into `a_buffer' but no more than `a_length' character.
|
||||
external
|
||||
"C inline use %"fcgi_stdio.h%""
|
||||
alias
|
||||
"[
|
||||
{
|
||||
size_t n;
|
||||
if (! FCGI_feof(FCGI_stdin)) {
|
||||
n = FCGI_fread($a_buffer, 1, $a_length, FCGI_stdin);
|
||||
} else {
|
||||
n = 0;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
]"
|
||||
end
|
||||
|
||||
feature {FCGI_IMP} -- Internal
|
||||
|
||||
feof (v: POINTER): INTEGER
|
||||
-- FCGI_feof()
|
||||
external
|
||||
"C inline use %"fcgi_stdio.h%""
|
||||
alias
|
||||
"FCGI_feof"
|
||||
end
|
||||
|
||||
feature {NONE} -- Input
|
||||
|
||||
gets (s: POINTER): POINTER
|
||||
-- gets() reads a line from stdin into the buffer pointed to
|
||||
-- by s until either a terminating newline or EOF, which it
|
||||
-- replaces with '\0'
|
||||
-- No check for buffer overrun is performed
|
||||
external
|
||||
"C inline use %"fcgi_stdio.h%""
|
||||
alias
|
||||
"return FCGI_gets($s);"
|
||||
end
|
||||
|
||||
feature -- Output
|
||||
|
||||
put_string (v: POINTER; n: INTEGER)
|
||||
external
|
||||
"C inline use %"fcgi_stdio.h%""
|
||||
alias
|
||||
"FCGI_fwrite($v, 1, $n, FCGI_stdout);"
|
||||
end
|
||||
|
||||
feature -- Error
|
||||
|
||||
put_error (v: POINTER; n: INTEGER)
|
||||
external
|
||||
"C inline use %"fcgi_stdio.h%""
|
||||
alias
|
||||
"FCGI_fwrite($v, 1, $n, FCGI_stderr);"
|
||||
end
|
||||
|
||||
feature -- Access
|
||||
|
||||
stdout: POINTER
|
||||
-- FCGI_stdout() return pointer on output FCGI_FILE
|
||||
external
|
||||
"C inline use %"fcgi_stdio.h%""
|
||||
alias
|
||||
"FCGI_stdout"
|
||||
end
|
||||
|
||||
stdin: POINTER
|
||||
-- FCGI_stdin() return pointer on input FCGI_FILE
|
||||
external
|
||||
"C inline use %"fcgi_stdio.h%""
|
||||
alias
|
||||
"FCGI_stdin"
|
||||
end
|
||||
|
||||
stderr: POINTER
|
||||
-- FCGI_stderr() return pointer on error FCGI_FILE
|
||||
external
|
||||
"C inline use %"fcgi_stdio.h%""
|
||||
alias
|
||||
"FCGI_stderr"
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "Copyright (c) 1984-2011, 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
|
||||
179
library/server/libfcgi/implementation/mac/fcgi_imp.e
Normal file
179
library/server/libfcgi/implementation/mac/fcgi_imp.e
Normal file
@@ -0,0 +1,179 @@
|
||||
note
|
||||
description: "Implementation for the FCGI_I interface"
|
||||
legal: "See notice at end of class."
|
||||
status: "See notice at end of class."
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
deferred class FCGI_IMP
|
||||
|
||||
inherit
|
||||
FCGI_I
|
||||
STRING_HANDLER
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
make
|
||||
-- Initialize FCGI interface
|
||||
do
|
||||
create fcgi
|
||||
end
|
||||
|
||||
fcgi: FCGI_C_API
|
||||
-- FastCGI C API primitives
|
||||
|
||||
|
||||
feature -- Access
|
||||
|
||||
fcgi_environ: POINTER
|
||||
do
|
||||
Result := fcgi.environ
|
||||
end
|
||||
|
||||
fcgi_end_of_input: BOOLEAN
|
||||
do
|
||||
Result := fcgi.feof (fcgi.stdin) = 0
|
||||
end
|
||||
|
||||
feature -- FCGI Connection
|
||||
|
||||
fcgi_listen: INTEGER
|
||||
-- Listen to the FCGI input stream
|
||||
-- Return 0 for successful calls, -1 otherwise.
|
||||
do
|
||||
Result := {FCGI_C_API}.accept
|
||||
end
|
||||
|
||||
fcgi_finish
|
||||
-- Finish current request from HTTP server started from
|
||||
-- the most recent call to `fcgi_accept'.
|
||||
do
|
||||
{FCGI_C_API}.finish
|
||||
end
|
||||
|
||||
set_fcgi_exit_status (v: INTEGER)
|
||||
do
|
||||
{FCGI_C_API}.set_exit_status (-2)
|
||||
end
|
||||
|
||||
feature -- FCGI output
|
||||
|
||||
put_string (a_str: READABLE_STRING_8)
|
||||
-- Put `a_str' on the FastCGI stdout.
|
||||
local
|
||||
l_c_str: C_STRING
|
||||
do
|
||||
l_c_str := c_buffer
|
||||
l_c_str.set_string (a_str)
|
||||
{FCGI_C_API}.put_string (l_c_str.item, l_c_str.count)
|
||||
end
|
||||
|
||||
feature -- Error
|
||||
|
||||
put_error (a_message: READABLE_STRING_8)
|
||||
-- Put error message `a_message' on the FastCGI stderr
|
||||
local
|
||||
l_c_str: C_STRING
|
||||
do
|
||||
l_c_str := c_buffer
|
||||
l_c_str.set_string (a_message)
|
||||
fcgi.put_error (l_c_str.item, l_c_str.count)
|
||||
end
|
||||
|
||||
feature -- FCGI Input
|
||||
|
||||
copy_from_stdin (n: INTEGER; tf: FILE)
|
||||
-- Read up to n bytes from stdin and write to given file
|
||||
local
|
||||
l_c_str: C_STRING
|
||||
num, readsize, writecount: INTEGER
|
||||
done: BOOLEAN
|
||||
do
|
||||
readsize := n.min (K_input_bufsize)
|
||||
l_c_str := c_buffer
|
||||
from
|
||||
until done or writecount >= n
|
||||
loop
|
||||
num := {FCGI_C_API}.read_content_into (l_c_str.item, readsize)
|
||||
if num = 0 then
|
||||
-- EOF
|
||||
done := True
|
||||
else
|
||||
tf.put_managed_pointer (c_buffer.managed_data, 0, num)
|
||||
writecount := writecount + num
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
feature {NONE} -- Implementation: FCGI Input
|
||||
|
||||
fill_pointer_from_stdin (p: POINTER; n: INTEGER): INTEGER
|
||||
-- Read up to `n' bytes from stdin and store in pointer `p'
|
||||
-- and return number of bytes read.
|
||||
do
|
||||
Result := {FCGI_C_API}.read_content_into (p, n)
|
||||
end
|
||||
|
||||
feature -- I/O Routines
|
||||
|
||||
--RFO read_stdin_into (a_str: STRING)
|
||||
--RFO -- Read a string from the `stdin' into `a_str'.
|
||||
--RFO require
|
||||
--RFO a_str_not_void: a_str /= Void
|
||||
--RFO local
|
||||
--RFO l_c_str: C_STRING
|
||||
--RFO n: INTEGER
|
||||
--RFO do
|
||||
--RFO l_c_str := c_buffer
|
||||
--RFO n := {FCGI_C_API}.read_content_into (l_c_str.item, l_c_str.capacity)
|
||||
--RFO a_str.set_count (n)
|
||||
--RFO l_c_str.read_substring_into (a_str, 1, n)
|
||||
--RFO end
|
||||
|
||||
--RFO read_string_into (a_str: STRING)
|
||||
--RFO require
|
||||
--RFO exists: a_str /= Void
|
||||
--RFO local
|
||||
--RFO l_c_str: C_STRING
|
||||
--RFO p: POINTER
|
||||
--RFO do
|
||||
--RFO create l_c_str.make_empty (1024)
|
||||
--RFO p := {FCGI_C_API}.gets (l_c_str.item)
|
||||
--RFO-- if p /= default_pointer and p = l_c_str.item then
|
||||
--RFO a_str.resize (l_c_str.count)
|
||||
--RFO l_c_str.read_string_into (a_str)
|
||||
--RFO-- else
|
||||
--RFO-- put_error_line ("Bad pointer from gets")
|
||||
--RFO-- end
|
||||
--RFO end
|
||||
|
||||
--RFO read_line
|
||||
--RFO -- Read up to the next end of line, or end of input
|
||||
--RFO -- Leave result in last_string
|
||||
--RFO -- Newline character is absent from result
|
||||
--RFO do
|
||||
--RFO if last_string = Void then
|
||||
--RFO create Result.make (K_input_bufsize)
|
||||
--RFO else
|
||||
--RFO last_string.wipe_out
|
||||
--RFO end
|
||||
--RFO-- if input_filename /= Void then
|
||||
--RFO-- io.read_line
|
||||
--RFO-- last_string.append (io.last_string)
|
||||
--RFO-- else
|
||||
--RFO read_string_into (last_string)
|
||||
--RFO-- end
|
||||
--RFO end
|
||||
|
||||
note
|
||||
copyright: "Copyright (c) 1984-2011, 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
|
||||
@@ -26,7 +26,12 @@
|
||||
</external_library>
|
||||
<external_library location="/usr/lib/libfcgi.so">
|
||||
<condition>
|
||||
<platform excluded_value="windows"/>
|
||||
<platform value="unix"/>
|
||||
</condition>
|
||||
</external_library>
|
||||
<external_library location="/opt/local/lib/libfcgi.dylib">
|
||||
<condition>
|
||||
<platform value="macintosh"/>
|
||||
</condition>
|
||||
</external_library>
|
||||
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
|
||||
@@ -34,6 +39,7 @@
|
||||
<file_rule>
|
||||
<exclude>/linux$</exclude>
|
||||
<exclude>/fake$</exclude>
|
||||
<exclude>/mac$</exclude>
|
||||
<condition>
|
||||
<platform value="windows"/>
|
||||
</condition>
|
||||
@@ -41,8 +47,17 @@
|
||||
<file_rule>
|
||||
<exclude>/fake$</exclude>
|
||||
<exclude>/windows$</exclude>
|
||||
<exclude>/mac$</exclude>
|
||||
<condition>
|
||||
<platform excluded_value="windows"/>
|
||||
<platform value="unix"/>
|
||||
</condition>
|
||||
</file_rule>
|
||||
<file_rule>
|
||||
<exclude>/fake$</exclude>
|
||||
<exclude>/windows$</exclude>
|
||||
<exclude>/linux$</exclude>
|
||||
<condition>
|
||||
<platform value="macintosh"/>
|
||||
</condition>
|
||||
</file_rule>
|
||||
</cluster>
|
||||
|
||||
@@ -27,7 +27,12 @@
|
||||
</external_library>
|
||||
<external_library location="/usr/lib/libfcgi.so">
|
||||
<condition>
|
||||
<platform excluded_value="windows"/>
|
||||
<platform value="unix"/>
|
||||
</condition>
|
||||
</external_library>
|
||||
<external_library location="/opt/local/lib/libfcgi.dylib">
|
||||
<condition>
|
||||
<platform value="macintosh"/>
|
||||
</condition>
|
||||
</external_library>
|
||||
<library name="base" location="$ISE_LIBRARY\library\base\base.ecf"/>
|
||||
@@ -36,6 +41,7 @@
|
||||
<file_rule>
|
||||
<exclude>/linux$</exclude>
|
||||
<exclude>/fake$</exclude>
|
||||
<exclude>/mac$</exclude>
|
||||
<condition>
|
||||
<platform value="windows"/>
|
||||
</condition>
|
||||
@@ -43,8 +49,17 @@
|
||||
<file_rule>
|
||||
<exclude>/windows$</exclude>
|
||||
<exclude>/fake$</exclude>
|
||||
<exclude>/mac$</exclude>
|
||||
<condition>
|
||||
<platform excluded_value="windows"/>
|
||||
<platform value="unix"/>
|
||||
</condition>
|
||||
</file_rule>
|
||||
<file_rule>
|
||||
<exclude>/fake$</exclude>
|
||||
<exclude>/windows$</exclude>
|
||||
<exclude>/linux$</exclude>
|
||||
<condition>
|
||||
<platform value="macintosh"/>
|
||||
</condition>
|
||||
</file_rule>
|
||||
</cluster>
|
||||
|
||||
66
library/server/wsf/policy_driven/wsf_delete_helper.e
Normal file
66
library/server/wsf/policy_driven/wsf_delete_helper.e
Normal file
@@ -0,0 +1,66 @@
|
||||
note
|
||||
|
||||
description: "[
|
||||
Policy-driven helpers to implement the DELETE method.
|
||||
]"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class WSF_DELETE_HELPER
|
||||
|
||||
inherit
|
||||
|
||||
WSF_METHOD_HELPER
|
||||
|
||||
feature {NONE} -- Implementation
|
||||
|
||||
send_response (req: WSF_REQUEST; res: WSF_RESPONSE; a_handler: WSF_SKELETON_HANDLER; a_header: HTTP_HEADER; a_new_resource: BOOLEAN)
|
||||
-- Write response to deletion of resource named by `req' into `res'.
|
||||
-- Upto four execution variables may be set on `req':
|
||||
-- "NEGOTIATED_MEDIA_TYPE"
|
||||
-- "NEGOTIATED_LANGUAGE"
|
||||
-- "NEGOTIATED_CHARSET"
|
||||
-- "NEGOTIATED_ENCODING"
|
||||
local
|
||||
l_dt: STRING
|
||||
l_ok: BOOLEAN
|
||||
do
|
||||
a_handler.delete (req)
|
||||
l_ok := a_handler.response_ok (req)
|
||||
if l_ok then
|
||||
if a_handler.includes_response_entity (req) then
|
||||
a_handler.ensure_content_available (req)
|
||||
l_ok := a_handler.response_ok (req)
|
||||
if l_ok then
|
||||
a_header.put_content_length (a_handler.content_length (req).as_integer_32)
|
||||
end
|
||||
-- we don't bother supporting chunked responses for DELETE.
|
||||
else
|
||||
a_header.put_content_length (0)
|
||||
end
|
||||
if attached req.request_time as l_time then
|
||||
l_dt := (create {HTTP_DATE}.make_from_date_time (l_time)).rfc1123_string
|
||||
a_header.put_header_key_value ({HTTP_HEADER_NAMES}.header_date, l_dt)
|
||||
generate_cache_headers (req, a_handler, a_header, l_time)
|
||||
end
|
||||
if a_handler.delete_queued (req) then
|
||||
res.set_status_code ({HTTP_STATUS_CODE}.accepted)
|
||||
res.put_header_text (a_header.string)
|
||||
res.put_string (a_handler.content (req))
|
||||
elseif a_handler.deleted (req) then
|
||||
if a_handler.includes_response_entity (req) then
|
||||
res.set_status_code ({HTTP_STATUS_CODE}.ok)
|
||||
res.put_header_text (a_header.string)
|
||||
res.put_string (a_handler.content (req))
|
||||
else
|
||||
res.set_status_code ({HTTP_STATUS_CODE}.no_content)
|
||||
res.put_header_text (a_header.string)
|
||||
end
|
||||
end
|
||||
end
|
||||
if not l_ok then
|
||||
write_error_response (req, res)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
72
library/server/wsf/policy_driven/wsf_get_helper.e
Normal file
72
library/server/wsf/policy_driven/wsf_get_helper.e
Normal file
@@ -0,0 +1,72 @@
|
||||
note
|
||||
|
||||
description: "[
|
||||
Policy-driven helpers to implement processing of GET and HEAD requests.
|
||||
]"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class WSF_GET_HELPER
|
||||
|
||||
inherit
|
||||
|
||||
WSF_METHOD_HELPER
|
||||
|
||||
feature {NONE} -- Implementation
|
||||
|
||||
send_response (req: WSF_REQUEST; res: WSF_RESPONSE; a_handler: WSF_SKELETON_HANDLER; a_header: HTTP_HEADER; a_new_resource: BOOLEAN)
|
||||
-- Write response to `req' into `res' in accordance with `a_media_type' etc.
|
||||
-- Upto four execution variables may be set on `req':
|
||||
-- "NEGOTIATED_MEDIA_TYPE"
|
||||
-- "NEGOTIATED_LANGUAGE"
|
||||
-- "NEGOTIATED_CHARSET"
|
||||
-- "NEGOTIATED_ENCODING"
|
||||
local
|
||||
l_chunked, l_ok: BOOLEAN
|
||||
l_dt: STRING
|
||||
do
|
||||
a_handler.ensure_content_available (req)
|
||||
l_ok := a_handler.response_ok (req)
|
||||
if l_ok then
|
||||
l_chunked := a_handler.is_chunking (req)
|
||||
if l_chunked then
|
||||
a_header.put_transfer_encoding_chunked
|
||||
else
|
||||
a_header.put_content_length (a_handler.content_length (req).as_integer_32)
|
||||
end
|
||||
if attached req.request_time as l_time then
|
||||
l_dt := (create {HTTP_DATE}.make_from_date_time (l_time)).rfc1123_string
|
||||
a_header.put_header_key_value ({HTTP_HEADER_NAMES}.header_date, l_dt)
|
||||
generate_cache_headers (req, a_handler, a_header, l_time)
|
||||
end
|
||||
l_ok := a_handler.response_ok (req)
|
||||
end
|
||||
if l_ok then
|
||||
res.set_status_code ({HTTP_STATUS_CODE}.ok)
|
||||
else
|
||||
write_error_response (req, res)
|
||||
end
|
||||
if attached a_handler.etag (req) as l_etag then
|
||||
a_header.put_header_key_value ({HTTP_HEADER_NAMES}.header_etag, l_etag)
|
||||
end
|
||||
res.put_header_text (a_header.string)
|
||||
if l_ok then
|
||||
if l_chunked then
|
||||
send_chunked_response (req, res, a_handler, a_header)
|
||||
else
|
||||
res.put_string (a_handler.content (req))
|
||||
end
|
||||
end
|
||||
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
|
||||
598
library/server/wsf/policy_driven/wsf_method_helper.e
Normal file
598
library/server/wsf/policy_driven/wsf_method_helper.e
Normal file
@@ -0,0 +1,598 @@
|
||||
note
|
||||
|
||||
description: "[
|
||||
Policy-driven helpers to implement a method.
|
||||
|
||||
]"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
deferred class WSF_METHOD_HELPER
|
||||
|
||||
inherit
|
||||
|
||||
HTTP_STATUS_CODE_MESSAGES
|
||||
|
||||
SHARED_HTML_ENCODER
|
||||
export {NONE} all end
|
||||
|
||||
feature -- Access
|
||||
|
||||
resource_exists: BOOLEAN
|
||||
-- Does the requested resource (request URI) exist?
|
||||
|
||||
feature -- Setting
|
||||
|
||||
set_resource_exists
|
||||
-- Set `resource_exists' to `True'.
|
||||
do
|
||||
resource_exists := True
|
||||
ensure
|
||||
set: resource_exists
|
||||
end
|
||||
|
||||
feature -- Basic operations
|
||||
|
||||
execute_new_resource (req: WSF_REQUEST; res: WSF_RESPONSE; a_handler: WSF_SKELETON_HANDLER)
|
||||
-- Write response to non-existing resource requested by `req.' into `res'.
|
||||
-- Policy routines are available in `a_handler'.
|
||||
-- Upto four execution variables may be set on `req':
|
||||
-- "NEGOTIATED_MEDIA_TYPE"
|
||||
-- "NEGOTIATED_LANGUAGE"
|
||||
-- "NEGOTIATED_CHARSET"
|
||||
-- "NEGOTIATED_ENCODING"
|
||||
-- An HTTP_HEADER is also available as the execution variable "NEGOTIATED_HTTP_HEADER".
|
||||
-- It includes the Vary header (if any)
|
||||
require
|
||||
req_attached: req /= Void
|
||||
res_attached: res /= Void
|
||||
a_handler_attached: a_handler /= Void
|
||||
local
|
||||
l_locs: LIST [URI]
|
||||
do
|
||||
if a_handler.resource_previously_existed (req) then
|
||||
if a_handler.resource_moved_permanently (req) then
|
||||
l_locs := a_handler.previous_location (req)
|
||||
handle_redirection_error (req, res, l_locs, {HTTP_STATUS_CODE}.moved_permanently)
|
||||
elseif a_handler.resource_moved_temporarily (req) then
|
||||
l_locs := a_handler.previous_location (req)
|
||||
handle_redirection_error (req, res, l_locs, {HTTP_STATUS_CODE}.found)
|
||||
else
|
||||
check attached {HTTP_HEADER} req.execution_variable (a_handler.Negotiated_http_header_execution_variable) as h then
|
||||
-- postcondition header_attached of `handle_content_negotiation'
|
||||
h.put_content_type_text_plain
|
||||
h.put_current_date
|
||||
h.put_content_length (0)
|
||||
res.set_status_code ({HTTP_STATUS_CODE}.gone)
|
||||
res.put_header_lines (h)
|
||||
end
|
||||
end
|
||||
else
|
||||
check attached {HTTP_HEADER} req.execution_variable (a_handler.Negotiated_http_header_execution_variable) as h then
|
||||
-- postcondition header_attached of `handle_content_negotiation'
|
||||
h.put_content_type_text_plain
|
||||
h.put_current_date
|
||||
h.put_content_length (0)
|
||||
res.set_status_code ({HTTP_STATUS_CODE}.not_found)
|
||||
res.put_header_lines (h)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
execute_existing_resource (req: WSF_REQUEST; res: WSF_RESPONSE; a_handler: WSF_SKELETON_HANDLER)
|
||||
-- Write response to existing resource requested by `req' into `res'.
|
||||
-- Policy routines are available in `a_handler'.
|
||||
-- Upto four execution variables may be set on `req':
|
||||
-- "NEGOTIATED_MEDIA_TYPE"
|
||||
-- "NEGOTIATED_LANGUAGE"
|
||||
-- "NEGOTIATED_CHARSET"
|
||||
-- "NEGOTIATED_ENCODING"
|
||||
require
|
||||
req_attached: req /= Void
|
||||
res_attached: res /= Void
|
||||
a_handler_attached: a_handler /= Void
|
||||
not_if_match_star: attached req.http_if_match as l_if_match implies not l_if_match.same_string ("*")
|
||||
local
|
||||
l_etags: LIST [READABLE_STRING_8]
|
||||
l_failed: BOOLEAN
|
||||
l_date: HTTP_DATE
|
||||
do
|
||||
if attached req.http_if_match as l_if_match then
|
||||
-- also if-range when we add support for range requests
|
||||
if not l_if_match.same_string ("*") then
|
||||
l_etags := l_if_match.split (',')
|
||||
l_failed := not across l_etags as i_etags some a_handler.matching_etag (req, i_etags.item, True) end
|
||||
end
|
||||
end
|
||||
if l_failed then
|
||||
handle_precondition_failed (req, res)
|
||||
else
|
||||
if attached req.http_if_unmodified_since as l_if_unmodified_since then
|
||||
if l_if_unmodified_since.is_string_8 then
|
||||
create l_date.make_from_string (l_if_unmodified_since.as_string_8)
|
||||
if not l_date.has_error then
|
||||
if a_handler.modified_since (req, l_date.date_time) then
|
||||
handle_precondition_failed (req, res)
|
||||
l_failed := True
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
if not l_failed then
|
||||
if attached req.http_if_none_match as l_if_none_match then
|
||||
l_etags := l_if_none_match.split (',')
|
||||
l_failed := l_if_none_match.same_string ("*") or
|
||||
across l_etags as i_etags some a_handler.matching_etag (req, i_etags.item, False) end
|
||||
end
|
||||
if l_failed then
|
||||
handle_if_none_match_failed (req, res, a_handler)
|
||||
else
|
||||
if attached req.http_if_modified_since as l_if_modified_since then
|
||||
if l_if_modified_since.is_string_8 then
|
||||
create l_date.make_from_string (l_if_modified_since.as_string_8)
|
||||
if not l_date.has_error then
|
||||
if not a_handler.modified_since (req, l_date.date_time) then
|
||||
handle_not_modified (req, res, a_handler)
|
||||
l_failed := True
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
if not l_failed then
|
||||
check attached {HTTP_HEADER} req.execution_variable (a_handler.Negotiated_http_header_execution_variable) as h then
|
||||
-- postcondition header_attached of `handle_content_negotiation'
|
||||
send_response (req, res, a_handler, h, False)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
feature -- Content negotiation
|
||||
|
||||
handle_content_negotiation (req: WSF_REQUEST; res: WSF_RESPONSE; a_handler: WSF_SKELETON_HANDLER)
|
||||
-- Negotiate acceptable content for, then write, response requested by `req' into `res'.
|
||||
-- Policy routines are available in `a_handler'.
|
||||
--
|
||||
-- Either a 406 Not Acceptable error is sent, or upto four execution variables may be set on `req':
|
||||
-- "NEGOTIATED_MEDIA_TYPE"
|
||||
-- "NEGOTIATED_LANGUAGE"
|
||||
-- "NEGOTIATED_CHARSET"
|
||||
-- "NEGOTIATED_ENCODING"
|
||||
-- An HTTP_HEADER is also saved as the execution variable "NEGOTIATED_HTTP_HEADER".
|
||||
-- It includes the Vary header (if any)
|
||||
require
|
||||
req_attached: req /= Void
|
||||
res_attached: res /= Void
|
||||
a_handler_attached: a_handler /= Void
|
||||
local
|
||||
l_conneg: CONNEG_SERVER_SIDE
|
||||
h: HTTP_HEADER
|
||||
l_media: MEDIA_TYPE_VARIANT_RESULTS
|
||||
l_lang: LANGUAGE_VARIANT_RESULTS
|
||||
l_charset: CHARACTER_ENCODING_VARIANT_RESULTS
|
||||
l_encoding: COMPRESSION_VARIANT_RESULTS
|
||||
l_mime_types, l_langs, l_charsets, l_encodings: LIST [STRING]
|
||||
l_vary_star: BOOLEAN
|
||||
do
|
||||
create h.make
|
||||
l_vary_star := not a_handler.predictable_response (req)
|
||||
if l_vary_star then
|
||||
h.add_header_key_value ({HTTP_HEADER_NAMES}.header_vary, "*")
|
||||
elseif attached a_handler.additional_variant_headers (req) as l_additional then
|
||||
across l_additional as i_additional loop
|
||||
h.add_header_key_value ({HTTP_HEADER_NAMES}.header_vary, i_additional.item)
|
||||
end
|
||||
end
|
||||
l_conneg := a_handler.conneg (req)
|
||||
l_mime_types := a_handler.mime_types_supported (req)
|
||||
l_media := l_conneg.media_type_preference (l_mime_types, req.http_accept)
|
||||
if not l_vary_star and l_mime_types.count > 1 and attached l_media.variant_header as l_media_variant then
|
||||
h.add_header_key_value ({HTTP_HEADER_NAMES}.header_vary, l_media_variant)
|
||||
end
|
||||
if not l_media.is_acceptable then
|
||||
handle_not_acceptable ("None of the requested ContentTypes were acceptable", l_mime_types, req, res)
|
||||
else
|
||||
l_langs := a_handler.languages_supported (req)
|
||||
l_lang := l_conneg.language_preference (l_langs, req.http_accept_language)
|
||||
if not l_vary_star and l_langs.count > 1 and attached l_lang.variant_header as l_lang_variant then
|
||||
h.add_header_key_value ({HTTP_HEADER_NAMES}.header_vary, l_lang_variant)
|
||||
end
|
||||
if not l_lang.is_acceptable then
|
||||
handle_not_acceptable ("None of the requested languages were acceptable", l_langs, req, res)
|
||||
else
|
||||
if attached l_lang.language_type as l_language_type then
|
||||
h.put_content_language (l_language_type)
|
||||
req.set_execution_variable (a_handler.Negotiated_language_execution_variable, l_language_type)
|
||||
end
|
||||
l_charsets := a_handler.charsets_supported (req)
|
||||
l_charset := l_conneg.charset_preference (l_charsets, req.http_accept_charset)
|
||||
if not l_vary_star and l_charsets.count > 1 and attached l_charset.variant_header as l_charset_variant then
|
||||
h.add_header_key_value ({HTTP_HEADER_NAMES}.header_vary, l_charset_variant)
|
||||
end
|
||||
if not l_charset.is_acceptable then
|
||||
handle_not_acceptable ("None of the requested character encodings were acceptable", l_charsets, req, res)
|
||||
else
|
||||
if attached l_media.media_type as l_media_type then
|
||||
if attached l_charset.character_type as l_character_type then
|
||||
h.put_content_type (l_media_type + "; charset=" + l_character_type)
|
||||
req.set_execution_variable (a_handler.Negotiated_charset_execution_variable, l_charset)
|
||||
else
|
||||
h.put_content_type (l_media_type)
|
||||
end
|
||||
req.set_execution_variable (a_handler.Negotiated_media_type_execution_variable, l_media_type)
|
||||
end
|
||||
l_encodings := a_handler.encodings_supported (req)
|
||||
l_encoding := l_conneg.encoding_preference (l_encodings, req.http_accept_encoding)
|
||||
if not l_vary_star and l_encodings.count > 1 and attached l_encoding.variant_header as l_encoding_variant then
|
||||
h.add_header_key_value ({HTTP_HEADER_NAMES}.header_vary, l_encoding_variant)
|
||||
end
|
||||
if not l_encoding.is_acceptable then
|
||||
handle_not_acceptable ("None of the requested transfer encodings were acceptable", l_encodings, req, res)
|
||||
else
|
||||
if attached l_encoding.compression_type as l_compression_type then
|
||||
h.put_content_encoding (l_compression_type)
|
||||
req.set_execution_variable (a_handler.Negotiated_encoding_execution_variable, l_compression_type)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
req.set_execution_variable (a_handler.Negotiated_http_header_execution_variable, h)
|
||||
ensure
|
||||
header_attached: attached {HTTP_HEADER} req.execution_variable (a_handler.Negotiated_http_header_execution_variable)
|
||||
end
|
||||
|
||||
feature {NONE} -- Implementation
|
||||
|
||||
send_response (req: WSF_REQUEST; res: WSF_RESPONSE; a_handler: WSF_SKELETON_HANDLER; a_header: HTTP_HEADER; a_new_resource: BOOLEAN)
|
||||
-- Write response to `req' into `res'.
|
||||
-- Upto four execution variables may be set on `req':
|
||||
-- "NEGOTIATED_MEDIA_TYPE"
|
||||
-- "NEGOTIATED_LANGUAGE"
|
||||
-- "NEGOTIATED_CHARSET"
|
||||
-- "NEGOTIATED_ENCODING"
|
||||
require
|
||||
req_attached: req /= Void
|
||||
res_attached: res /= Void
|
||||
a_handler_attached: a_handler /= Void
|
||||
a_header_attached: a_header /= Void
|
||||
deferred
|
||||
end
|
||||
|
||||
send_chunked_response (req: WSF_REQUEST; res: WSF_RESPONSE; a_handler: WSF_SKELETON_HANDLER; a_header: HTTP_HEADER)
|
||||
-- Write response in chunks to `req'.
|
||||
-- Upto four execution variables may be set on `req':
|
||||
-- "NEGOTIATED_MEDIA_TYPE"
|
||||
-- "NEGOTIATED_LANGUAGE"
|
||||
-- "NEGOTIATED_CHARSET"
|
||||
-- "NEGOTIATED_ENCODING"
|
||||
require
|
||||
req_attached: req /= Void
|
||||
res_attached: res /= Void
|
||||
a_handler_attached: a_handler /= Void
|
||||
local
|
||||
l_chunk: TUPLE [a_chunk: READABLE_STRING_8; a_extension: detachable READABLE_STRING_8]
|
||||
do
|
||||
from
|
||||
if a_handler.response_ok (req) then
|
||||
l_chunk := a_handler.next_chunk (req)
|
||||
res.put_chunk (l_chunk.a_chunk, l_chunk.a_extension)
|
||||
else
|
||||
write_error_response (req, res)
|
||||
end
|
||||
until
|
||||
a_handler.finished (req) or not a_handler.response_ok (req)
|
||||
loop
|
||||
a_handler.generate_next_chunk (req)
|
||||
if a_handler.response_ok (req) then
|
||||
l_chunk := a_handler.next_chunk (req)
|
||||
res.put_chunk (l_chunk.a_chunk, l_chunk.a_extension)
|
||||
else
|
||||
write_error_response (req, res)
|
||||
end
|
||||
end
|
||||
if a_handler.finished (req) then
|
||||
-- In future, add support for trailers
|
||||
res.put_chunk_end
|
||||
end
|
||||
end
|
||||
|
||||
generate_cache_headers (req: WSF_REQUEST; a_handler: WSF_SKELETON_HANDLER; a_header: HTTP_HEADER; a_request_time: DATE_TIME)
|
||||
-- Write headers affecting caching for `req' into `a_header'.
|
||||
require
|
||||
req_attached: req /= Void
|
||||
a_handler_attached: a_handler /= Void
|
||||
a_header_attached: a_header /= Void
|
||||
a_request_time_attached: a_request_time /= Void
|
||||
local
|
||||
l_age, l_age_1, l_age_2: NATURAL
|
||||
l_dur: DATE_TIME_DURATION
|
||||
l_dt, l_field_names: STRING
|
||||
do
|
||||
l_age := a_handler.http_1_0_age (req)
|
||||
create l_dur.make (0, 0, 0, 0, 0, l_age.as_integer_32)
|
||||
l_dt := (create {HTTP_DATE}.make_from_date_time (a_request_time + l_dur)).rfc1123_string
|
||||
a_header.put_header_key_value ({HTTP_HEADER_NAMES}.header_expires, l_dt)
|
||||
l_age_1 := a_handler.max_age (req)
|
||||
if l_age_1 /= l_age then
|
||||
a_header.add_header_key_value ({HTTP_HEADER_NAMES}.header_cache_control, "max-age=" + l_age_1.out)
|
||||
end
|
||||
l_age_2 := a_handler.shared_age (req)
|
||||
if l_age_2 /= l_age_1 then
|
||||
a_header.add_header_key_value ({HTTP_HEADER_NAMES}.header_cache_control, "s-maxage=" + l_age_2.out)
|
||||
end
|
||||
if a_handler.is_freely_cacheable (req) then
|
||||
a_header.add_header_key_value ({HTTP_HEADER_NAMES}.header_cache_control, "public")
|
||||
elseif attached a_handler.private_headers (req) as l_fields then
|
||||
l_field_names := "="
|
||||
if not l_fields.is_empty then
|
||||
append_field_name (l_field_names, l_fields)
|
||||
end
|
||||
a_header.add_header_key_value ({HTTP_HEADER_NAMES}.header_cache_control, "private" + l_field_names)
|
||||
end
|
||||
if attached a_handler.non_cacheable_headers (req) as l_fields then
|
||||
l_field_names := "="
|
||||
if not l_fields.is_empty then
|
||||
append_field_name (l_field_names, l_fields)
|
||||
end
|
||||
a_header.add_header_key_value ({HTTP_HEADER_NAMES}.header_cache_control, "no-cache" + l_field_names)
|
||||
end
|
||||
if not a_handler.is_transformable (req) then
|
||||
a_header.add_header_key_value ({HTTP_HEADER_NAMES}.header_cache_control, "no-transform")
|
||||
end
|
||||
if a_handler.must_revalidate (req) then
|
||||
a_header.add_header_key_value ({HTTP_HEADER_NAMES}.header_cache_control, "must-revalidate")
|
||||
end
|
||||
if a_handler.must_proxy_revalidate (req) then
|
||||
a_header.add_header_key_value ({HTTP_HEADER_NAMES}.header_cache_control, "proxy-revalidate")
|
||||
end
|
||||
end
|
||||
|
||||
append_field_name (a_field_names: STRING; a_fields: LIST [READABLE_STRING_8])
|
||||
-- Append all of `a_fields' as a comma-separated list to `a_field_names'.
|
||||
require
|
||||
a_field_names_attached: a_field_names /= Void
|
||||
a_fields_attached: a_fields /= Void
|
||||
do
|
||||
across a_fields as i_fields loop
|
||||
a_field_names.append_string (i_fields.item)
|
||||
if not i_fields.is_last then
|
||||
a_field_names.append_character (',')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
feature -- Error reporting
|
||||
|
||||
write_error_response (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
-- Write an error response to `res' using `req.error_handler'.
|
||||
require
|
||||
req_attached: req /= Void
|
||||
res_attached: res /= Void
|
||||
req_has_error: req.has_error
|
||||
local
|
||||
h: HTTP_HEADER
|
||||
m: READABLE_STRING_8
|
||||
do
|
||||
m := req.error_handler.as_string_representation
|
||||
create h.make
|
||||
h.put_content_type_text_plain
|
||||
h.put_content_length (m.count)
|
||||
res.set_status_code (req.error_handler.primary_error_code)
|
||||
res.put_header_lines (h)
|
||||
res.put_string (m)
|
||||
end
|
||||
|
||||
handle_redirection_error (req: WSF_REQUEST; res: WSF_RESPONSE; a_locations: LIST [URI]; a_status_code: INTEGER)
|
||||
-- Write `a_status_code' error to `res'.
|
||||
-- Include all of `a_locations' in the headers, and hyperlink to the first one in the body.
|
||||
require
|
||||
res_attached: res /= Void
|
||||
req_attached: req /= Void
|
||||
a_locations_attached: a_locations /= Void
|
||||
a_location_not_empty: not a_locations.is_empty
|
||||
a_status_code_code: is_valid_http_status_code (a_status_code)
|
||||
local
|
||||
h: HTTP_HEADER
|
||||
s: STRING
|
||||
do
|
||||
if attached http_status_code_message (a_status_code) as l_msg then
|
||||
create h.make
|
||||
across a_locations as i_location loop
|
||||
h.add_header_key_value ({HTTP_HEADER_NAMES}.header_location, i_location.item.string)
|
||||
end
|
||||
if req.is_content_type_accepted ({HTTP_MIME_TYPES}.text_html) then
|
||||
s := "<html lang=%"en%"><head>"
|
||||
s.append ("<title>")
|
||||
s.append (html_encoder.encoded_string (req.request_uri))
|
||||
s.append ("Error " + a_status_code.out + " (" + l_msg + ")")
|
||||
s.append ("</title>%N")
|
||||
s.append ("[
|
||||
<style type="text/css">
|
||||
div#header {color: #fff; background-color: #000; padding: 20px; text-align: center; font-size: 2em; font-weight: bold;}
|
||||
div#message { margin: 40px; text-align: center; font-size: 1.5em; }
|
||||
div#suggestions { margin: auto; width: 60%;}
|
||||
div#suggestions ul { }
|
||||
div#footer {color: #999; background-color: #eee; padding: 10px; text-align: center; }
|
||||
div#logo { float: right; margin: 20px; width: 60px height: auto; font-size: 0.8em; text-align: center; }
|
||||
div#logo div.outer { padding: 6px; width: 60px; border: solid 3px #500; background-color: #b00;}
|
||||
div#logo div.outer div.inner1 { display: block; margin: 10px 15px; width: 30px; height: 50px; color: #fff; background-color: #fff; border: solid 2px #900; }
|
||||
div#logo div.outer div.inner2 { margin: 10px 15px; width: 30px; height: 15px; color: #fff; background-color: #fff; border: solid 2px #900; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
]")
|
||||
s.append ("<div id=%"header%">Error " + a_status_code.out + " (" + l_msg + ")</div>")
|
||||
s.append ("<div id=%"logo%">")
|
||||
s.append ("<div class=%"outer%"> ")
|
||||
s.append ("<div class=%"inner1%"></div>")
|
||||
s.append ("<div class=%"inner2%"></div>")
|
||||
s.append ("</div>")
|
||||
s.append ("The current location for this resource is <a href=%"" + a_locations.first.string + "%">here</a>")
|
||||
s.append ("Error " + a_status_code.out + " (" + l_msg + ")</div>")
|
||||
s.append ("<div id=%"message%">Error " + a_status_code.out + " (" + l_msg + "): <code>" + html_encoder.encoded_string (req.request_uri) + "</code></div>")
|
||||
s.append ("<div id=%"footer%"></div>")
|
||||
s.append ("</body>%N")
|
||||
s.append ("</html>%N")
|
||||
|
||||
h.put_content_type_text_html
|
||||
else
|
||||
s := "Error " + a_status_code.out + " (" + l_msg + "): "
|
||||
s.append (req.request_uri)
|
||||
s.append_character ('%N')
|
||||
s.append ("The current location for this resource is " + a_locations.first.string)
|
||||
h.put_content_type_text_plain
|
||||
end
|
||||
h.put_content_length (s.count)
|
||||
res.put_header_lines (h)
|
||||
res.put_string (s)
|
||||
res.flush
|
||||
end
|
||||
end
|
||||
|
||||
handle_not_acceptable (a_message: READABLE_STRING_8; a_supported: LIST [STRING]; req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
-- Write a Not Acceptable response to `res'.
|
||||
require
|
||||
req_attached: req /= Void
|
||||
res_attached: res /= Void
|
||||
a_message_attached: a_message /= Void
|
||||
a_supported_attached: a_supported /= Void
|
||||
local
|
||||
h: HTTP_HEADER
|
||||
s: STRING
|
||||
do
|
||||
create h.make
|
||||
h.put_content_type_text_plain
|
||||
h.put_current_date
|
||||
s := a_message
|
||||
s.append_character ('%N')
|
||||
s.append_character ('%N')
|
||||
s.append_string ("We accept the following:%N%N")
|
||||
across a_supported as i_supported loop
|
||||
s.append_string (i_supported.item)
|
||||
s.append_character ('%N')
|
||||
end
|
||||
h.put_content_length (s.count)
|
||||
res.set_status_code ({HTTP_STATUS_CODE}.not_acceptable)
|
||||
res.put_header_lines (h)
|
||||
res.put_string (s)
|
||||
res.flush
|
||||
end
|
||||
|
||||
handle_if_none_match_failed (req: WSF_REQUEST; res: WSF_RESPONSE; a_handler: WSF_SKELETON_HANDLER)
|
||||
-- Write a Not Modified response to `res'.
|
||||
require
|
||||
req_attached: req /= Void
|
||||
res_attached: res /= Void
|
||||
a_handler_attached: a_handler /= Void
|
||||
do
|
||||
handle_not_modified (req, res, a_handler)
|
||||
end
|
||||
|
||||
handle_not_modified (req: WSF_REQUEST; res: WSF_RESPONSE; a_handler: WSF_SKELETON_HANDLER)
|
||||
-- Write a Not Modified response to `res'.
|
||||
-- Upto four execution variables may be set on `req':
|
||||
-- "NEGOTIATED_MEDIA_TYPE"
|
||||
-- "NEGOTIATED_LANGUAGE"
|
||||
-- "NEGOTIATED_CHARSET"
|
||||
-- "NEGOTIATED_ENCODING"
|
||||
require
|
||||
req_attached: req /= Void
|
||||
res_attached: res /= Void
|
||||
a_handler_attached: a_handler /= Void
|
||||
local
|
||||
h: HTTP_HEADER
|
||||
do
|
||||
create h.make
|
||||
h.put_content_type_text_plain
|
||||
h.put_content_length (0)
|
||||
if attached a_handler.etag (req) as l_etag then
|
||||
h.put_header_key_value ({HTTP_HEADER_NAMES}.header_etag, l_etag)
|
||||
end
|
||||
generate_cache_headers (req, a_handler, h, create {DATE_TIME}.make_now_utc)
|
||||
res.set_status_code ({HTTP_STATUS_CODE}.not_modified)
|
||||
res.put_header_lines (h)
|
||||
end
|
||||
|
||||
handle_precondition_failed (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
-- Write a Precondition Failed response for `req' to `res'.
|
||||
require
|
||||
req_attached: req /= Void
|
||||
res_attached: res /= Void
|
||||
local
|
||||
h: HTTP_HEADER
|
||||
do
|
||||
create h.make
|
||||
h.put_content_type_text_plain
|
||||
h.put_current_date
|
||||
h.put_content_length (0)
|
||||
res.set_status_code ({HTTP_STATUS_CODE}.precondition_failed)
|
||||
res.put_header_lines (h)
|
||||
end
|
||||
|
||||
handle_unsupported_media_type (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
-- Write a Unsupported Media Type response for `req' to `res'.
|
||||
require
|
||||
req_attached: req /= Void
|
||||
res_attached: res /= Void
|
||||
local
|
||||
h: HTTP_HEADER
|
||||
do
|
||||
create h.make
|
||||
h.put_content_type_text_plain
|
||||
h.put_current_date
|
||||
h.put_content_length (0)
|
||||
res.set_status_code ({HTTP_STATUS_CODE}.unsupported_media_type)
|
||||
res.put_header_lines (h)
|
||||
end
|
||||
|
||||
handle_not_implemented (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
-- Write a Not Implemented response for `req' to `res'.
|
||||
require
|
||||
req_attached: req /= Void
|
||||
res_attached: res /= Void
|
||||
local
|
||||
h: HTTP_HEADER
|
||||
do
|
||||
create h.make
|
||||
h.put_content_type_text_plain
|
||||
h.put_current_date
|
||||
h.put_content_length (0)
|
||||
res.set_status_code ({HTTP_STATUS_CODE}.not_implemented)
|
||||
res.put_header_lines (h)
|
||||
end
|
||||
|
||||
handle_request_entity_too_large (req: WSF_REQUEST; res: WSF_RESPONSE; a_handler: WSF_SKELETON_HANDLER)
|
||||
-- Write a Request Entity Too Large response for `req' to `res'.
|
||||
require
|
||||
req_attached: req /= Void
|
||||
res_attached: res /= Void
|
||||
a_handler_attached: a_handler /= Void
|
||||
local
|
||||
h: HTTP_HEADER
|
||||
do
|
||||
create h.make
|
||||
h.put_content_type_text_plain
|
||||
h.put_current_date
|
||||
h.put_content_length (0)
|
||||
res.set_status_code ({HTTP_STATUS_CODE}.request_entity_too_large)
|
||||
res.put_header_lines (h)
|
||||
-- FIXME: Need to check if condition is temporary. This needs a new query
|
||||
-- on the handler. For now we can claim compliance by saying the condition
|
||||
-- is always permenent :-) - author's might not like this though.
|
||||
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
|
||||
42
library/server/wsf/policy_driven/wsf_method_helper_factory.e
Normal file
42
library/server/wsf/policy_driven/wsf_method_helper_factory.e
Normal file
@@ -0,0 +1,42 @@
|
||||
note
|
||||
|
||||
description: "[
|
||||
Default factory for policy-driven method helpers.
|
||||
Extension methods can be implemented here.
|
||||
]"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class WSF_METHOD_HELPER_FACTORY
|
||||
|
||||
feature -- Factory
|
||||
|
||||
new_method_helper (a_method: READABLE_STRING_8): detachable WSF_METHOD_HELPER
|
||||
-- New object for processing `a_method';
|
||||
-- Redefine this routine to implement extension methods.
|
||||
require
|
||||
a_method_attached: a_method /= Void
|
||||
do
|
||||
if a_method.is_case_insensitive_equal ({HTTP_REQUEST_METHODS}.method_get) or
|
||||
a_method.is_case_insensitive_equal ({HTTP_REQUEST_METHODS}.method_head) then
|
||||
create {WSF_GET_HELPER} Result
|
||||
elseif a_method.is_case_insensitive_equal ({HTTP_REQUEST_METHODS}.method_put) then
|
||||
create {WSF_PUT_HELPER} Result
|
||||
elseif a_method.is_case_insensitive_equal ({HTTP_REQUEST_METHODS}.method_post) then
|
||||
create {WSF_POST_HELPER} Result
|
||||
elseif a_method.is_case_insensitive_equal ({HTTP_REQUEST_METHODS}.method_delete) then
|
||||
create {WSF_DELETE_HELPER} Result
|
||||
end
|
||||
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
|
||||
80
library/server/wsf/policy_driven/wsf_post_helper.e
Normal file
80
library/server/wsf/policy_driven/wsf_post_helper.e
Normal file
@@ -0,0 +1,80 @@
|
||||
note
|
||||
|
||||
description: "[
|
||||
Policy-driven helpers to implement POST.
|
||||
]"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class WSF_POST_HELPER
|
||||
|
||||
inherit
|
||||
|
||||
WSF_METHOD_HELPER
|
||||
redefine
|
||||
execute_new_resource
|
||||
end
|
||||
|
||||
feature -- Basic operations
|
||||
|
||||
execute_new_resource (req: WSF_REQUEST; res: WSF_RESPONSE; a_handler: WSF_SKELETON_HANDLER)
|
||||
-- Write response to non-existing resource requested by `req.' into `res'.
|
||||
-- Policy routines are available in `a_handler'.
|
||||
do
|
||||
if a_handler.allow_post_to_missing_resource (req) then
|
||||
check attached {HTTP_HEADER} req.execution_variable (a_handler.Negotiated_http_header_execution_variable) as h then
|
||||
-- postcondition header_attached of `handle_content_negotiation'
|
||||
send_response (req, res, a_handler, h, True)
|
||||
end
|
||||
else
|
||||
res.send (create {WSF_NOT_FOUND_RESPONSE}.make(req))
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
feature {NONE} -- Implementation
|
||||
|
||||
send_response (req: WSF_REQUEST; res: WSF_RESPONSE; a_handler: WSF_SKELETON_HANDLER; a_header: HTTP_HEADER; a_new_resource: BOOLEAN)
|
||||
-- Write response to `req' into `res' in accordance with `a_media_type' etc. as a new URI.
|
||||
-- Upto four execution variables may be set on `req':
|
||||
-- "NEGOTIATED_MEDIA_TYPE"
|
||||
-- "NEGOTIATED_LANGUAGE"
|
||||
-- "NEGOTIATED_CHARSET"
|
||||
-- "NEGOTIATED_ENCODING"
|
||||
local
|
||||
l_code: NATURAL
|
||||
do
|
||||
a_handler.read_entity (req)
|
||||
if a_handler.is_entity_too_large (req) then
|
||||
handle_request_entity_too_large (req, res, a_handler)
|
||||
else
|
||||
a_handler.check_content_headers (req)
|
||||
l_code := a_handler.content_check_code (req)
|
||||
if l_code /= 0 then
|
||||
if l_code = 415 then
|
||||
handle_unsupported_media_type (req, res)
|
||||
else
|
||||
handle_not_implemented (req, res)
|
||||
end
|
||||
else
|
||||
a_handler.check_request (req, res)
|
||||
if a_handler.request_check_code (req) = 0 then
|
||||
a_handler.append_resource (req, res)
|
||||
-- 200 or 204 or 303 or 500 (add support for this?)
|
||||
-- FIXME: more support, such as includes_response_entity
|
||||
end
|
||||
end
|
||||
end
|
||||
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
|
||||
82
library/server/wsf/policy_driven/wsf_put_helper.e
Normal file
82
library/server/wsf/policy_driven/wsf_put_helper.e
Normal file
@@ -0,0 +1,82 @@
|
||||
note
|
||||
|
||||
description: "[
|
||||
Policy-driven helpers to implement PUT.
|
||||
]"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class WSF_PUT_HELPER
|
||||
|
||||
inherit
|
||||
|
||||
WSF_METHOD_HELPER
|
||||
redefine
|
||||
execute_new_resource
|
||||
end
|
||||
|
||||
feature -- Basic operations
|
||||
|
||||
execute_new_resource (req: WSF_REQUEST; res: WSF_RESPONSE; a_handler: WSF_SKELETON_HANDLER)
|
||||
-- Write response to non-existing resource requested by `req.' into `res'.
|
||||
-- Policy routines are available in `a_handler'.
|
||||
do
|
||||
if a_handler.treat_as_moved_permanently (req) then
|
||||
handle_redirection_error (req, res, a_handler.previous_location (req), {HTTP_STATUS_CODE}.moved_permanently)
|
||||
else
|
||||
check attached {HTTP_HEADER} req.execution_variable (a_handler.Negotiated_http_header_execution_variable) as h then
|
||||
-- postcondition header_attached of `handle_content_negotiation'
|
||||
send_response (req, res, a_handler, h, True)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
feature {NONE} -- Implementation
|
||||
|
||||
send_response (req: WSF_REQUEST; res: WSF_RESPONSE; a_handler: WSF_SKELETON_HANDLER; a_header: HTTP_HEADER; a_new_resource: BOOLEAN)
|
||||
-- Write response to `req' into `res' in accordance with `a_media_type' etc. as a new URI.
|
||||
local
|
||||
l_code: NATURAL
|
||||
do
|
||||
a_handler.read_entity (req)
|
||||
if a_handler.is_entity_too_large (req) then
|
||||
handle_request_entity_too_large (req, res, a_handler)
|
||||
else
|
||||
a_handler.check_content_headers (req)
|
||||
l_code := a_handler.content_check_code (req)
|
||||
if l_code /= 0 then
|
||||
if l_code = 415 then
|
||||
handle_unsupported_media_type (req, res)
|
||||
else
|
||||
handle_not_implemented (req, res)
|
||||
end
|
||||
else
|
||||
a_handler.check_request (req, res)
|
||||
if a_handler.request_check_code (req) = 0 then
|
||||
if a_new_resource then
|
||||
a_handler.create_resource (req, res)
|
||||
-- 201 or 500 (add support for this?)
|
||||
else
|
||||
a_handler.check_conflict (req, res)
|
||||
if a_handler.conflict_check_code (req) = 0 then
|
||||
a_handler.update_resource (req, res)
|
||||
-- 204 or 500 (add support for this?)
|
||||
-- FIXME: more support, such as includes_response_entity
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
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
|
||||
550
library/server/wsf/policy_driven/wsf_skeleton_handler.e
Normal file
550
library/server/wsf/policy_driven/wsf_skeleton_handler.e
Normal file
@@ -0,0 +1,550 @@
|
||||
note
|
||||
|
||||
description: "[
|
||||
Policy-driven handlers.
|
||||
Implementers only need to concentrate on creating content.
|
||||
]"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
deferred class WSF_SKELETON_HANDLER
|
||||
|
||||
inherit
|
||||
|
||||
WSF_URI_TEMPLATE_HANDLER
|
||||
redefine
|
||||
execute
|
||||
end
|
||||
|
||||
WSF_OPTIONS_POLICY
|
||||
|
||||
WSF_PREVIOUS_POLICY
|
||||
|
||||
WSF_CACHING_POLICY
|
||||
|
||||
WSF_METHOD_HELPER_FACTORY
|
||||
|
||||
WSF_SELF_DOCUMENTED_HANDLER
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
make_with_router (a_router: WSF_ROUTER)
|
||||
-- Initialize `router'.
|
||||
require
|
||||
a_router_attached: a_router /= Void
|
||||
do
|
||||
router := a_router
|
||||
ensure
|
||||
router_aliased: router = a_router
|
||||
end
|
||||
|
||||
feature -- Router
|
||||
|
||||
router: WSF_ROUTER
|
||||
-- So that WSF_OPTIONS_POLICY can find the allowed methods
|
||||
|
||||
feature -- Execution variables
|
||||
|
||||
Negotiated_language_execution_variable: STRING = "NEGOTIATED_LANGUAGE"
|
||||
-- Execution variable set by framework
|
||||
|
||||
Negotiated_charset_execution_variable: STRING = "NEGOTIATED_CHARSET"
|
||||
-- Execution variable set by framework
|
||||
|
||||
Negotiated_media_type_execution_variable: STRING = "NEGOTIATED_MEDIA_TYPE"
|
||||
-- Execution variable set by framework
|
||||
|
||||
Negotiated_encoding_execution_variable: STRING = "NEGOTIATED_ENCODING"
|
||||
-- Execution variable set by framework
|
||||
|
||||
Negotiated_http_header_execution_variable: STRING = "NEGOTIATED_HTTP_HEADER"
|
||||
-- Execution variable set by framework
|
||||
|
||||
Request_entity_execution_variable: STRING = "REQUEST_ENTITY"
|
||||
-- Execution variable set by framework
|
||||
|
||||
Conflict_check_code_execution_variable: STRING = "CONFLICT_CHECK_CODE"
|
||||
-- Execution variable set by framework
|
||||
|
||||
Content_check_code_execution_variable: STRING = "CONTENT_CHECK_CODE"
|
||||
-- Execution variable set by framework
|
||||
|
||||
Request_check_code_execution_variable: STRING = "REQUEST_CHECK_CODE"
|
||||
-- Execution variable set by framework
|
||||
|
||||
feature -- Access
|
||||
|
||||
is_chunking (req: WSF_REQUEST): BOOLEAN
|
||||
-- Will the response to `req' using chunked transfer encoding?
|
||||
require
|
||||
req_attached: req /= Void
|
||||
deferred
|
||||
end
|
||||
|
||||
includes_response_entity (req: WSF_REQUEST): BOOLEAN
|
||||
-- Does the response to `req' include an entity?
|
||||
-- Method will be DELETE, OUT, POST or an extension method.
|
||||
require
|
||||
req_attached: req /= Void
|
||||
deferred
|
||||
end
|
||||
|
||||
conneg (req: WSF_REQUEST): CONNEG_SERVER_SIDE
|
||||
-- Content negotiation for `req';
|
||||
-- This would normally be a once object, ignoring `req'.
|
||||
require
|
||||
req_attached: req /= Void
|
||||
deferred
|
||||
end
|
||||
|
||||
mime_types_supported (req: WSF_REQUEST): LIST [STRING]
|
||||
-- All values for Accept header that `Current' can serve
|
||||
require
|
||||
req_attached: req /= Void
|
||||
deferred
|
||||
ensure
|
||||
mime_types_supported_includes_default: Result.has (conneg (req).mime_default)
|
||||
end
|
||||
|
||||
languages_supported (req: WSF_REQUEST): LIST [STRING]
|
||||
-- All values for Accept-Language header that `Current' can serve
|
||||
require
|
||||
req_attached: req /= Void
|
||||
deferred
|
||||
ensure
|
||||
languages_supported_includes_default: Result.has (conneg (req).language_default)
|
||||
end
|
||||
|
||||
charsets_supported (req: WSF_REQUEST): LIST [STRING]
|
||||
-- All values for Accept-Charset header that `Current' can serve
|
||||
require
|
||||
req_attached: req /= Void
|
||||
deferred
|
||||
ensure
|
||||
charsets_supported_includes_default: Result.has (conneg (req).charset_default)
|
||||
end
|
||||
|
||||
encodings_supported (req: WSF_REQUEST): LIST [STRING]
|
||||
-- All values for Accept-Encoding header that `Current' can serve
|
||||
require
|
||||
req_attached: req /= Void
|
||||
deferred
|
||||
ensure
|
||||
encodings_supported_includes_default: Result.has (conneg (req).encoding_default)
|
||||
end
|
||||
|
||||
additional_variant_headers (req: WSF_REQUEST): detachable LIST [STRING]
|
||||
-- Header other than Accept, Accept-Language, Accept-Charset and Accept-Encoding,
|
||||
-- which might affect the response
|
||||
do
|
||||
end
|
||||
|
||||
predictable_response (req: WSF_REQUEST): BOOLEAN
|
||||
-- Does the response to `req' vary only on the dimensions of ContentType, Language, Charset and Transfer encoding,
|
||||
-- plus those named in `additional_variant_headers'?
|
||||
do
|
||||
Result := True
|
||||
-- redefine to return `False', so as to induce a Vary: * header
|
||||
end
|
||||
|
||||
matching_etag (req: WSF_REQUEST; a_etag: READABLE_STRING_32; a_strong: BOOLEAN): BOOLEAN
|
||||
-- Is `a_etag' a match for resource requested in `req'?
|
||||
-- If `a_strong' then the strong comparison function must be used.
|
||||
require
|
||||
req_attached: req /= Void
|
||||
deferred
|
||||
end
|
||||
|
||||
etag (req: WSF_REQUEST): detachable READABLE_STRING_8
|
||||
-- Optional Etag for response entity to `req';
|
||||
-- Upto four execution variables may be set on `req':
|
||||
-- "NEGOTIATED_MEDIA_TYPE"
|
||||
-- "NEGOTIATED_LANGUAGE"
|
||||
-- "NEGOTIATED_CHARSET"
|
||||
-- "NEGOTIATED_ENCODING"
|
||||
require
|
||||
req_attached: req /= Void
|
||||
deferred
|
||||
end
|
||||
|
||||
modified_since (req: WSF_REQUEST; a_date_time: DATE_TIME): BOOLEAN
|
||||
-- Has resource requested in `req' been modified since `a_date_time' (UTC)?
|
||||
require
|
||||
req_attached: req /= Void
|
||||
deferred
|
||||
end
|
||||
|
||||
treat_as_moved_permanently (req: WSF_REQUEST): BOOLEAN
|
||||
-- Rather than store as a new entity, do we treat it as an existing entity that has been moved?
|
||||
require
|
||||
req_attached: req /= Void
|
||||
put_request: req.is_request_method ({HTTP_REQUEST_METHODS}.method_put)
|
||||
do
|
||||
-- No. Redefine this if needed.
|
||||
end
|
||||
|
||||
allow_post_to_missing_resource (req: WSF_REQUEST): BOOLEAN
|
||||
-- The resource named in `req' does not exist, and this is a POST. Do we allow it?
|
||||
require
|
||||
req_attached: req /= Void
|
||||
deferred
|
||||
end
|
||||
|
||||
feature -- Measurement
|
||||
|
||||
content_length (req: WSF_REQUEST): NATURAL
|
||||
-- Length of entity-body of the response to `req'
|
||||
require
|
||||
req_attached: req /= Void
|
||||
not_chunked: not is_chunking (req)
|
||||
deferred
|
||||
end
|
||||
|
||||
feature -- Status report
|
||||
|
||||
finished (req: WSF_REQUEST): BOOLEAN
|
||||
-- Has the last chunk been generated for `req'?
|
||||
require
|
||||
req_attached: req /= Void
|
||||
chunked: is_chunking (req)
|
||||
deferred
|
||||
end
|
||||
|
||||
|
||||
feature -- Documentation
|
||||
|
||||
mapping_documentation (m: WSF_ROUTER_MAPPING; a_request_methods: detachable WSF_REQUEST_METHODS): WSF_ROUTER_MAPPING_DOCUMENTATION
|
||||
-- Documentation associated with Current handler, in the context of the mapping `m' and methods `a_request_methods'
|
||||
--| `m' and `a_request_methods' are useful to produce specific documentation when the handler is used for multiple mapping.
|
||||
do
|
||||
create Result.make (m)
|
||||
Result.add_description (description)
|
||||
end
|
||||
|
||||
description: READABLE_STRING_GENERAL
|
||||
-- General description for self-generated documentation;
|
||||
-- The specific URI templates supported will be described automatically
|
||||
deferred
|
||||
ensure
|
||||
description_attached: Result /= Void
|
||||
end
|
||||
|
||||
feature -- DELETE
|
||||
|
||||
delete (req: WSF_REQUEST)
|
||||
-- Delete resource named in `req' or set an error on `req.error_handler'.
|
||||
require
|
||||
req_attached: req /= Void
|
||||
deferred
|
||||
ensure
|
||||
error_or_queued_or_deleted: not req.error_handler.has_error implies (delete_queued (req) or deleted (req))
|
||||
end
|
||||
|
||||
deleted (req: WSF_REQUEST): BOOLEAN
|
||||
-- Has resource named by `req' been deleted?
|
||||
require
|
||||
req_attached: req /= Void
|
||||
do
|
||||
if not req.error_handler.has_error then
|
||||
Result := True
|
||||
end
|
||||
ensure
|
||||
negative_implication: not Result implies req.error_handler.has_error
|
||||
end
|
||||
|
||||
delete_queued (req: WSF_REQUEST): BOOLEAN
|
||||
-- Has resource named by `req' been queued for deletion?
|
||||
require
|
||||
req_attached: req /= Void
|
||||
deferred
|
||||
ensure
|
||||
entity_available: includes_response_entity (req)
|
||||
end
|
||||
|
||||
feature -- GET/HEAD content
|
||||
|
||||
ensure_content_available (req: WSF_REQUEST)
|
||||
-- Commence generation of response text (entity-body) (if not already done in `check_resource_exists').
|
||||
-- If not chunked, then this will create the entire entity-body so as to be available
|
||||
-- for a subsequent call to `content'.
|
||||
-- If chunked, only the first chunk will be made available to `next_chunk'. If chunk extensions
|
||||
-- are used, then this will also generate the chunk extension for the first chunk.
|
||||
-- Upto four execution variables may be set on `req':
|
||||
-- "NEGOTIATED_MEDIA_TYPE"
|
||||
-- "NEGOTIATED_LANGUAGE"
|
||||
-- "NEGOTIATED_CHARSET"
|
||||
-- "NEGOTIATED_ENCODING"
|
||||
-- If you support etags, and you have more than one possible representation
|
||||
-- for the resource (so that your etag depends upon the particular representation),
|
||||
-- then you will probably have already created the response entity in `check_resource_exists'.
|
||||
require
|
||||
req_attached: req /= Void
|
||||
get_or_head_or_delete: req.is_get_head_request_method or req.is_delete_request_method
|
||||
deferred
|
||||
end
|
||||
|
||||
response_ok (req: WSF_REQUEST): BOOLEAN
|
||||
-- Has generation of the response (so-far, if chunked) proceeded witout error?
|
||||
require
|
||||
req_attached: req /= Void
|
||||
do
|
||||
Result := not req.error_handler.has_error
|
||||
ensure
|
||||
last_error_set: Result = not req.error_handler.has_error
|
||||
end
|
||||
|
||||
content (req: WSF_REQUEST): READABLE_STRING_8
|
||||
-- Non-chunked entity body in response to `req';
|
||||
-- Upto four execution variables may be set on `req':
|
||||
-- "NEGOTIATED_MEDIA_TYPE"
|
||||
-- "NEGOTIATED_LANGUAGE"
|
||||
-- "NEGOTIATED_CHARSET"
|
||||
-- "NEGOTIATED_ENCODING"
|
||||
require
|
||||
req_attached: req /= Void
|
||||
head_get_or_delete: req.is_get_head_request_method or req.is_delete_request_method
|
||||
no_error: response_ok (req)
|
||||
not_chunked: not is_chunking (req)
|
||||
deferred
|
||||
end
|
||||
|
||||
generate_next_chunk (req: WSF_REQUEST)
|
||||
-- Prepare next chunk (including optional chunk extension) of entity body in response to `req'.
|
||||
-- This is not called for the first chunk.
|
||||
-- Upto four execution variables may be set on `req':
|
||||
-- "NEGOTIATED_MEDIA_TYPE"
|
||||
-- "NEGOTIATED_LANGUAGE"
|
||||
-- "NEGOTIATED_CHARSET"
|
||||
-- "NEGOTIATED_ENCODING"
|
||||
require
|
||||
req_attached: req /= Void
|
||||
no_error: response_ok (req)
|
||||
chunked: is_chunking (req)
|
||||
deferred
|
||||
end
|
||||
|
||||
next_chunk (req: WSF_REQUEST): TUPLE [a_check: READABLE_STRING_8; a_extension: detachable READABLE_STRING_8]
|
||||
-- Next chunk of entity body in response to `req';
|
||||
-- The second field of the result is an optional chunk extension.
|
||||
-- Four execution variables are set on `req':
|
||||
-- "NEGOTIATED_MEDIA_TYPE"
|
||||
-- "NEGOTIATED_LANGUAGE"
|
||||
-- "NEGOTIATED_CHARSET"
|
||||
-- "NEGOTIATED_ENCODING"
|
||||
require
|
||||
req_attached: req /= Void
|
||||
no_error: response_ok (req)
|
||||
chunked: is_chunking (req)
|
||||
deferred
|
||||
end
|
||||
|
||||
feature -- PUT/POST
|
||||
|
||||
read_entity (req: WSF_REQUEST)
|
||||
-- Read request body and set as `req.execution_variable (Request_entity_execution_variable)'.
|
||||
require
|
||||
req_attached: req /= Void
|
||||
local
|
||||
l_body: STRING
|
||||
do
|
||||
create l_body.make_empty
|
||||
req.read_input_data_into (l_body)
|
||||
if not l_body.is_empty then
|
||||
req.set_execution_variable (Request_entity_execution_variable, l_body)
|
||||
end
|
||||
end
|
||||
|
||||
is_entity_too_large (req: WSF_REQUEST): BOOLEAN
|
||||
-- Is the entity stored in `req.execution_variable (Request_entity_execution_variable)' too large for the application?
|
||||
require
|
||||
req_attached: req /= Void
|
||||
deferred
|
||||
end
|
||||
|
||||
check_content_headers (req: WSF_REQUEST)
|
||||
-- Check we can support all content headers on request entity.
|
||||
-- Set `req.execution_variable (Content_check_code_execution_variable)' to {NATURAL} zero if OK, or 415 or 501 if not.
|
||||
require
|
||||
req_attached: req /= Void
|
||||
deferred
|
||||
end
|
||||
|
||||
content_check_code (req: WSF_REQUEST): NATURAL
|
||||
-- Code set by `check_content_headers'.
|
||||
require
|
||||
req_attached: req /= Void
|
||||
do
|
||||
if attached {NATURAL} req.execution_variable (Content_check_code_execution_variable) as l_code then
|
||||
Result := l_code
|
||||
end
|
||||
end
|
||||
|
||||
create_resource (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
-- Create new resource in response to a PUT request when `check_resource_exists' returns `False'.
|
||||
-- Implementor must set error code of 200 OK or 500 Server Error.
|
||||
require
|
||||
req_attached: req /= Void
|
||||
res_attached: res /= Void
|
||||
put_request: req.is_put_request_method
|
||||
deferred
|
||||
end
|
||||
|
||||
append_resource (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
-- Create new resource in response to a POST request.
|
||||
-- Implementor must set error code of 200 OK or 204 No Content or 303 See Other or 500 Server Error.
|
||||
require
|
||||
req_attached: req /= Void
|
||||
res_attached: res /= Void
|
||||
post_request: req.is_post_request_method
|
||||
deferred
|
||||
end
|
||||
|
||||
check_conflict (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
-- Check to see if updating the resource is problematic due to the current state of the resource.
|
||||
-- Set `req.execution_variable (Conflict_check_code_execution_variable)' to {NATURAL} zero if OK, or 409 if not.
|
||||
-- In the latter case, write the full error response to `res'.
|
||||
require
|
||||
req_attached: req /= Void
|
||||
res_attached: res /= Void
|
||||
deferred
|
||||
end
|
||||
|
||||
conflict_check_code (req: WSF_REQUEST): NATURAL
|
||||
-- Code set by `check_conflict'.
|
||||
require
|
||||
req_attached: req /= Void
|
||||
do
|
||||
if attached {NATURAL} req.execution_variable (Conflict_check_code_execution_variable) as l_code then
|
||||
Result := l_code
|
||||
end
|
||||
end
|
||||
|
||||
check_request (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
-- Check that the request entity is a valid request.
|
||||
-- The entity is available as `req.execution_variable (Conflict_check_code_execution_variable)'.
|
||||
-- Set `req.execution_variable (Request_check_code_execution_variable)' to {NATURAL} zero if OK, or 400 if not.
|
||||
-- In the latter case, write the full error response to `res'.
|
||||
require
|
||||
req_attached: req /= Void
|
||||
res_attached: res /= Void
|
||||
put_or_post: req.is_put_post_request_method
|
||||
deferred
|
||||
end
|
||||
|
||||
request_check_code (req: WSF_REQUEST): NATURAL
|
||||
-- Code set by `check_request'.
|
||||
require
|
||||
req_attached: req /= Void
|
||||
do
|
||||
if attached {NATURAL} req.execution_variable (Request_check_code_execution_variable) as l_code then
|
||||
Result := l_code
|
||||
end
|
||||
end
|
||||
|
||||
update_resource (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
-- Perform the update requested in `req'.
|
||||
-- Write a response to `res' with a code of 204 or 500.
|
||||
require
|
||||
req_attached: req /= Void
|
||||
res_attached: res /= Void
|
||||
put_request: req.is_put_request_method
|
||||
deferred
|
||||
end
|
||||
|
||||
feature -- Execution
|
||||
|
||||
execute (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
-- <Precursor>
|
||||
do
|
||||
check
|
||||
known_method: router.allowed_methods_for_request (req).has (req.request_method)
|
||||
not_trace: not req.is_request_method ({HTTP_REQUEST_METHODS}.method_trace)
|
||||
not_connect: not req.is_request_method ({HTTP_REQUEST_METHODS}.method_connect)
|
||||
end
|
||||
if req.is_request_method ({HTTP_REQUEST_METHODS}.method_options) then
|
||||
execute_options (req, res, router)
|
||||
else
|
||||
if attached new_method_helper (req.request_method) as l_helper then
|
||||
execute_method (req, res, l_helper)
|
||||
else
|
||||
handle_internal_server_error (res)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
execute_method (req: WSF_REQUEST; res: WSF_RESPONSE; a_helper: WSF_METHOD_HELPER)
|
||||
-- Write response to `req' into `res', using `a_helper' as a logic helper.
|
||||
require
|
||||
req_attached: req /= Void
|
||||
res_attached: res /= Void
|
||||
a_helper_attached: a_helper /= Void
|
||||
do
|
||||
a_helper.handle_content_negotiation (req, res, Current)
|
||||
if not res.status_is_set or else res.status_code /= {HTTP_STATUS_CODE}.Not_acceptable then
|
||||
check_resource_exists (req, a_helper)
|
||||
if a_helper.resource_exists then
|
||||
a_helper.execute_existing_resource (req, res, Current)
|
||||
else
|
||||
if attached req.http_if_match as l_if_match and then l_if_match.same_string ("*") then
|
||||
a_helper.handle_precondition_failed (req, res)
|
||||
else
|
||||
a_helper.execute_new_resource (req, res, Current)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
check_resource_exists (req: WSF_REQUEST; a_helper: WSF_METHOD_HELPER)
|
||||
-- Call `a_helper.set_resource_exists' to indicate that `req.path_translated'
|
||||
-- is the name of an existing resource.
|
||||
-- Upto four execution variables may be set on `req':
|
||||
-- "NEGOTIATED_MEDIA_TYPE"
|
||||
-- "NEGOTIATED_LANGUAGE"
|
||||
-- "NEGOTIATED_CHARSET"
|
||||
-- "NEGOTIATED_ENCODING"
|
||||
-- If you support etags, and you have more than one possible representation
|
||||
-- for the resource (so that your etag depends upon the particular representation),
|
||||
-- then you will probably need to create the response entity at this point, rather
|
||||
-- than in `ensure_content_available'.
|
||||
require
|
||||
req_attached: req /= Void
|
||||
a_helper_attached: a_helper /= Void
|
||||
deferred
|
||||
end
|
||||
|
||||
feature {NONE} -- Implementation
|
||||
|
||||
handle_internal_server_error (res: WSF_RESPONSE)
|
||||
-- Write "Internal Server Error" response to `res'.
|
||||
require
|
||||
res_attached: res /= Void
|
||||
local
|
||||
h: HTTP_HEADER
|
||||
m: STRING_8
|
||||
do
|
||||
create h.make
|
||||
h.put_content_type_text_plain
|
||||
m := "Server failed to handle request properly"
|
||||
h.put_content_length (m.count)
|
||||
h.put_current_date
|
||||
res.set_status_code ({HTTP_STATUS_CODE}.internal_server_error)
|
||||
res.put_header_lines (h)
|
||||
res.put_string (m)
|
||||
ensure
|
||||
response_status_is_set: res.status_is_set
|
||||
status_is_service_unavailable: res.status_code = {HTTP_STATUS_CODE}.internal_server_error
|
||||
body_sent: res.message_committed and then res.transfered_content_length > 0
|
||||
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
|
||||
@@ -30,14 +30,14 @@ feature -- Execution
|
||||
handle_unavailable (res)
|
||||
elseif requires_proxy (req) then
|
||||
handle_use_proxy (req, res)
|
||||
elseif
|
||||
maximum_uri_length > 0 and then
|
||||
req.request_uri.count.to_natural_32 > maximum_uri_length
|
||||
elseif
|
||||
maximum_uri_length > 0 and then
|
||||
req.request_uri.count.to_natural_32 > maximum_uri_length
|
||||
then
|
||||
handle_request_uri_too_long (res)
|
||||
elseif
|
||||
req.is_request_method ({HTTP_REQUEST_METHODS}.method_options) and then
|
||||
req.request_uri.same_string ("*")
|
||||
elseif
|
||||
req.is_request_method ({HTTP_REQUEST_METHODS}.method_options) and then
|
||||
req.request_uri.same_string ("*")
|
||||
then
|
||||
handle_server_options (req, res)
|
||||
else
|
||||
137
library/server/wsf/router/policy/wsf_caching_policy.e
Normal file
137
library/server/wsf/router/policy/wsf_caching_policy.e
Normal file
@@ -0,0 +1,137 @@
|
||||
note
|
||||
|
||||
description: "[
|
||||
Policies for determing caching of responses.
|
||||
]"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
deferred class WSF_CACHING_POLICY
|
||||
|
||||
feature -- Access
|
||||
|
||||
Never_expires: NATURAL = 525600
|
||||
-- 525600 = 365 * 24 * 60 * 60 = (almost) 1 year;
|
||||
-- See http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.21 for an explanation of why this means never expire
|
||||
|
||||
max_age (req: WSF_REQUEST): NATURAL
|
||||
-- Maximum age in seconds before response to `req` is considered stale;
|
||||
-- This is used to generate a Cache-Control: max-age header.
|
||||
-- Return 0 to indicate already expired.
|
||||
-- Return `Never_expires' to indicate never expires.
|
||||
require
|
||||
req_attached: req /= Void
|
||||
deferred
|
||||
ensure
|
||||
not_more_than_1_year: Result <= Never_expires
|
||||
end
|
||||
|
||||
shared_age (req: WSF_REQUEST): NATURAL
|
||||
-- Maximum age in seconds before response to `req` is considered stale in a shared cache;
|
||||
-- This is used to generate a Cache-Control: s-maxage header.
|
||||
-- If you wish to have different expiry ages for shared and provate caches, redefine this routine.
|
||||
-- Return 0 to indicate already expired.
|
||||
-- Return `Never_expires' to indicate never expires.
|
||||
require
|
||||
req_attached: req /= Void
|
||||
do
|
||||
Result := max_age (req)
|
||||
ensure
|
||||
not_more_than_1_year: Result <= Never_expires
|
||||
end
|
||||
|
||||
http_1_0_age (req: WSF_REQUEST): NATURAL
|
||||
-- Maximum age in seconds before response to `req` is considered stale;
|
||||
-- This is used to generate an Expires header, which HTTP/1.0 caches understand.
|
||||
-- If you wish to generate a different age for HTTP/1.0 caches, then redefine this routine.
|
||||
-- Return 0 to indicate already expired.
|
||||
-- Return `Never_expires' to indicate never expires. Note this will
|
||||
-- make a result cachecable that would not normally be cacheable (such as as response
|
||||
-- to a POST), unless overriden by cache-control headers, so be sure to check `req.request_method'.
|
||||
require
|
||||
req_attached: req /= Void
|
||||
do
|
||||
Result := max_age (req)
|
||||
ensure
|
||||
not_more_than_1_year: Result <= Never_expires
|
||||
end
|
||||
|
||||
is_freely_cacheable (req: WSF_REQUEST): BOOLEAN
|
||||
-- Should the response to `req' be freely cachable in shared caches?
|
||||
-- If `True', then a Cache-Control: public header will be generated.
|
||||
require
|
||||
req_attached: req /= Void
|
||||
deferred
|
||||
end
|
||||
|
||||
is_transformable (req: WSF_REQUEST): BOOLEAN
|
||||
-- Should a non-transparent proxy be allowed to modify headers of response to `req`?
|
||||
-- The headers concerned are listed in http://www.w3.org/Protocols/rfc2616-sec14.html#sec14,9.
|
||||
-- If `False' then a Cache-Control: no-transorm header will be generated.
|
||||
require
|
||||
req_attached: req /= Void
|
||||
do
|
||||
-- We choose a conservative default. But most applications can
|
||||
-- redefine to return `True'.
|
||||
end
|
||||
|
||||
must_revalidate (req: WSF_REQUEST): BOOLEAN
|
||||
-- If a client has requested, or a cache is configured, to ignore server's expiration time,
|
||||
-- should we force revalidation anyway?
|
||||
-- If `True' then a Cache-Control: must-revalidate header will be generated.
|
||||
require
|
||||
req_attached: req /= Void
|
||||
do
|
||||
-- Redefine to force revalidation.
|
||||
end
|
||||
|
||||
must_proxy_revalidate (req: WSF_REQUEST): BOOLEAN
|
||||
-- If a shared cache is configured to ignore server's expiration time,
|
||||
-- should we force revalidation anyway?
|
||||
-- If `True' then a Cache-Control: proxy-revalidate header will be generated.
|
||||
require
|
||||
req_attached: req /= Void
|
||||
do
|
||||
-- Redefine to force revalidation.
|
||||
end
|
||||
|
||||
private_headers (req: WSF_REQUEST): detachable LIST [READABLE_STRING_8]
|
||||
-- Header names intended for a single user.
|
||||
-- If non-Void, then a Cache-Control: private header will be generated.
|
||||
-- Returning an empty list prevents the entire response from being served from a shared cache.
|
||||
require
|
||||
req_attached: req /= Void
|
||||
deferred
|
||||
ensure
|
||||
not_freely_cacheable: Result /= Void implies not is_freely_cacheable (req)
|
||||
end
|
||||
|
||||
non_cacheable_headers (req: WSF_REQUEST): detachable LIST [READABLE_STRING_8]
|
||||
-- Header names that will not be sent from a cache without revalidation;
|
||||
-- If non-Void, then a Cache-Control: no-cache header will be generated.
|
||||
-- Returning an empty list prevents the response being served from a cache
|
||||
-- without revalidation.
|
||||
require
|
||||
req_attached: req /= Void
|
||||
deferred
|
||||
end
|
||||
|
||||
is_sensitive (req: WSF_REQUEST): BOOLEAN
|
||||
-- Is the response to `req' of a sensitive nature?
|
||||
-- If `True' then a Cache-Control: no-store header will be generated.
|
||||
require
|
||||
req_attached: req /= Void
|
||||
deferred
|
||||
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
|
||||
37
library/server/wsf/router/policy/wsf_options_policy.e
Normal file
37
library/server/wsf/router/policy/wsf_options_policy.e
Normal file
@@ -0,0 +1,37 @@
|
||||
note
|
||||
|
||||
description: "[
|
||||
Default policy for responing to OPTIONS requests other than OPTIONS*
|
||||
By overriding `execute_options', clients can add a body, for example.
|
||||
]"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class WSF_OPTIONS_POLICY
|
||||
|
||||
feature -- Basic operations
|
||||
|
||||
execute_options (req: WSF_REQUEST; res: WSF_RESPONSE; a_router: WSF_ROUTER)
|
||||
-- Write response to `req' into `res'.
|
||||
require
|
||||
req_attached: req /= Void
|
||||
options_request: req.is_request_method ({HTTP_REQUEST_METHODS}.method_options)
|
||||
res_attached: res /= Void
|
||||
a_router_attached: a_router /= Void
|
||||
local
|
||||
l_methods: WSF_REQUEST_METHODS
|
||||
h: HTTP_HEADER
|
||||
do
|
||||
create h.make
|
||||
res.set_status_code ({HTTP_STATUS_CODE}.ok)
|
||||
h.put_content_type ({HTTP_MIME_TYPES}.text_plain)
|
||||
h.put_current_date
|
||||
h.put_content_length (0)
|
||||
l_methods := a_router.allowed_methods_for_request (req)
|
||||
if not l_methods.is_empty then
|
||||
h.put_allow (l_methods)
|
||||
end
|
||||
res.put_header_text (h.string)
|
||||
end
|
||||
|
||||
end
|
||||
63
library/server/wsf/router/policy/wsf_previous_policy.e
Normal file
63
library/server/wsf/router/policy/wsf_previous_policy.e
Normal file
@@ -0,0 +1,63 @@
|
||||
note
|
||||
|
||||
description: "[
|
||||
Policies for deciding if a resource that currently doesn't exist used to do so.
|
||||
This default implementation assumes that no resources used to exist.
|
||||
]"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
deferred class WSF_PREVIOUS_POLICY
|
||||
|
||||
feature -- Access
|
||||
|
||||
resource_previously_existed (req: WSF_REQUEST): BOOLEAN
|
||||
-- Did `req.path_translated' exist previously?
|
||||
require
|
||||
req_attached: req /= Void
|
||||
do
|
||||
-- No. Override if this is not want you want.
|
||||
end
|
||||
|
||||
resource_moved_permanently (req: WSF_REQUEST): BOOLEAN
|
||||
-- Was `req.path_translated' moved permanently?
|
||||
require
|
||||
req_attached: req /= Void
|
||||
previously_existed: resource_previously_existed (req)
|
||||
do
|
||||
-- No. Override if this is not want you want.
|
||||
end
|
||||
|
||||
resource_moved_temporarily (req: WSF_REQUEST): BOOLEAN
|
||||
-- Was `req.path_translated' moved temporarily?
|
||||
require
|
||||
req_attached: req /= Void
|
||||
previously_existed: resource_previously_existed (req)
|
||||
do
|
||||
-- No. Override if this is not want you want.
|
||||
end
|
||||
|
||||
previous_location (req: WSF_REQUEST): LIST [URI]
|
||||
-- Previous location(s) for resource named by `req';
|
||||
require
|
||||
req_attached: req /= Void
|
||||
previously_existed: resource_previously_existed (req)
|
||||
moved: resource_moved_permanently (req) or resource_moved_temporarily (req)
|
||||
do
|
||||
create {LINKED_LIST [URI]} Result.make
|
||||
ensure
|
||||
previous_location_attached: Result /= Void
|
||||
non_empty_list: not Result.is_empty
|
||||
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
|
||||
@@ -16,8 +16,7 @@ inherit
|
||||
end
|
||||
|
||||
create
|
||||
make,
|
||||
make_with_router
|
||||
make
|
||||
|
||||
feature -- Execution
|
||||
|
||||
@@ -29,7 +28,7 @@ feature -- Execution
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2012, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Eiffel Software and others"
|
||||
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
|
||||
|
||||
@@ -13,11 +13,10 @@ inherit
|
||||
WSF_URI_HANDLER
|
||||
|
||||
create
|
||||
make,
|
||||
make_with_router
|
||||
make
|
||||
|
||||
note
|
||||
copyright: "2011-2012, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Eiffel Software and others"
|
||||
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
|
||||
|
||||
@@ -13,11 +13,10 @@ inherit
|
||||
WSF_URI_TEMPLATE_HANDLER
|
||||
|
||||
create
|
||||
make,
|
||||
make_with_router
|
||||
make
|
||||
|
||||
note
|
||||
copyright: "2011-2012, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Eiffel Software and others"
|
||||
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
|
||||
|
||||
@@ -12,14 +12,9 @@ inherit
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
make_with_router (a_router: like router)
|
||||
do
|
||||
router := a_router
|
||||
end
|
||||
|
||||
make (n: INTEGER)
|
||||
do
|
||||
make_with_router (create {like router}.make (n))
|
||||
create router.make (n)
|
||||
end
|
||||
|
||||
feature -- Access
|
||||
|
||||
@@ -16,8 +16,7 @@ inherit
|
||||
end
|
||||
|
||||
create
|
||||
make,
|
||||
make_with_router
|
||||
make
|
||||
|
||||
feature -- Execution
|
||||
|
||||
@@ -27,7 +26,7 @@ feature -- Execution
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2012, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Eiffel Software and others"
|
||||
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
|
||||
|
||||
@@ -18,8 +18,7 @@ inherit
|
||||
end
|
||||
|
||||
create
|
||||
make,
|
||||
make_with_router
|
||||
make
|
||||
|
||||
feature -- Execution
|
||||
|
||||
@@ -31,7 +30,7 @@ feature -- Execution
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2012, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Eiffel Software and others"
|
||||
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
|
||||
|
||||
@@ -18,8 +18,7 @@ inherit
|
||||
end
|
||||
|
||||
create
|
||||
make,
|
||||
make_with_router
|
||||
make
|
||||
|
||||
feature -- Execution
|
||||
|
||||
@@ -31,7 +30,7 @@ feature -- Execution
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2012, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Eiffel Software and others"
|
||||
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
|
||||
|
||||
@@ -15,8 +15,7 @@ inherit
|
||||
end
|
||||
|
||||
create
|
||||
make,
|
||||
make_with_router
|
||||
make
|
||||
|
||||
feature -- Execution
|
||||
|
||||
@@ -26,7 +25,7 @@ feature -- Execution
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2012, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Eiffel Software and others"
|
||||
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
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
note
|
||||
description: "[
|
||||
Server request context of the httpd request
|
||||
|
||||
|
||||
It includes CGI interface and a few extra values that are usually valuable
|
||||
meta_variable (a_name: READABLE_STRING_GENERAL): detachable WSF_STRING
|
||||
meta_string_variable (a_name: READABLE_STRING_GENERAL): detachable READABLE_STRING_32
|
||||
|
||||
meta_variable (a_name: READABLE_STRING_GENERAL): detachable WSF_STRING
|
||||
meta_string_variable (a_name: READABLE_STRING_GENERAL): detachable READABLE_STRING_32
|
||||
|
||||
In addition it provides
|
||||
|
||||
|
||||
query_parameter (a_name: READABLE_STRING_GENERAL): detachable WSF_VALUE
|
||||
form_parameter (a_name: READABLE_STRING_GENERAL): detachable WSF_VALUE
|
||||
cookie (a_name: READABLE_STRING_GENERAL): detachable WSF_VALUE
|
||||
...
|
||||
|
||||
|
||||
And also has
|
||||
execution_variable (a_name: READABLE_STRING_GENERAL): detachable ANY
|
||||
--| to keep value attached to the request
|
||||
@@ -140,7 +140,6 @@ feature -- Destroy
|
||||
end
|
||||
|
||||
content_length_value := 0
|
||||
content_type := Void
|
||||
execution_variables_table.wipe_out
|
||||
internal_cookies_table := Void
|
||||
internal_form_data_parameters_table := Void
|
||||
@@ -157,7 +156,6 @@ feature -- Destroy
|
||||
raw_input_data_recorded := False
|
||||
request_method := empty_string_8
|
||||
set_uploaded_file_path (Void)
|
||||
-- wgi_request
|
||||
end
|
||||
|
||||
feature -- Status report
|
||||
@@ -208,7 +206,7 @@ feature -- Error handling
|
||||
|
||||
error_handler: ERROR_HANDLER
|
||||
-- Error handler
|
||||
-- By default initialized to new handler
|
||||
-- By default initialized to new handler
|
||||
|
||||
feature -- Access: Input
|
||||
|
||||
@@ -219,7 +217,7 @@ feature -- Access: Input
|
||||
end
|
||||
|
||||
is_chunked_input: BOOLEAN
|
||||
-- Is request using chunked transfer-encoding?
|
||||
-- Is request using chunked transfer-encoding?
|
||||
-- If True, the Content-Length has no meaning
|
||||
do
|
||||
Result := wgi_request.is_chunked_input
|
||||
@@ -311,18 +309,44 @@ feature -- Helper
|
||||
Result := request_method.is_case_insensitive_equal (m.as_string_8)
|
||||
end
|
||||
|
||||
is_put_request_method: BOOLEAN
|
||||
-- Is Current a PUT request method?
|
||||
do
|
||||
Result := request_method.is_case_insensitive_equal ({HTTP_REQUEST_METHODS}.method_put)
|
||||
end
|
||||
|
||||
is_post_request_method: BOOLEAN
|
||||
-- Is Current a POST request method?
|
||||
do
|
||||
Result := request_method.is_case_insensitive_equal ({HTTP_REQUEST_METHODS}.method_post)
|
||||
end
|
||||
|
||||
is_delete_request_method: BOOLEAN
|
||||
-- Is Current a DELETE request method?
|
||||
do
|
||||
Result := request_method.is_case_insensitive_equal ({HTTP_REQUEST_METHODS}.method_delete)
|
||||
end
|
||||
|
||||
is_get_request_method: BOOLEAN
|
||||
-- Is Current a GET request method?
|
||||
do
|
||||
Result := request_method.is_case_insensitive_equal ({HTTP_REQUEST_METHODS}.method_get)
|
||||
end
|
||||
|
||||
is_get_head_request_method: BOOLEAN
|
||||
-- Is Current a GET or a HEAD request method?
|
||||
do
|
||||
Result := request_method.is_case_insensitive_equal ({HTTP_REQUEST_METHODS}.method_get) or
|
||||
request_method.is_case_insensitive_equal ({HTTP_REQUEST_METHODS}.method_head)
|
||||
end
|
||||
|
||||
is_put_post_request_method: BOOLEAN
|
||||
-- Is Current a PUT or a POST request method?
|
||||
do
|
||||
Result := request_method.is_case_insensitive_equal ({HTTP_REQUEST_METHODS}.method_put) or
|
||||
request_method.is_case_insensitive_equal ({HTTP_REQUEST_METHODS}.method_post)
|
||||
end
|
||||
|
||||
is_content_type_accepted (a_content_type: READABLE_STRING_GENERAL): BOOLEAN
|
||||
-- Does client accepts content_type for the response?
|
||||
--| Based on header "Accept:" that can be for instance
|
||||
@@ -350,6 +374,8 @@ feature -- Helper
|
||||
end
|
||||
end
|
||||
Result := l_accept.has_substring (a_content_type)
|
||||
else
|
||||
Result := True
|
||||
end
|
||||
end
|
||||
|
||||
@@ -375,7 +401,7 @@ feature -- Eiffel WGI access
|
||||
Result := wgi_request.wgi_connector
|
||||
end
|
||||
|
||||
feature {WSF_REQUEST_EXPORTER} -- Override value
|
||||
feature {WSF_REQUEST_EXPORTER} -- Override value
|
||||
|
||||
set_request_method (a_request_method: like request_method)
|
||||
-- Set `request_method' to `a_request_method'
|
||||
@@ -414,7 +440,7 @@ feature {NONE} -- Access: global variable
|
||||
end
|
||||
end
|
||||
|
||||
feature -- Access: global variables
|
||||
feature -- Access: global variables
|
||||
|
||||
items: ITERABLE [WSF_VALUE]
|
||||
do
|
||||
@@ -654,9 +680,9 @@ feature -- Access: CGI Meta variables
|
||||
feature {NONE} -- Access: CGI meta parameters
|
||||
|
||||
meta_variables_table: STRING_TABLE [WSF_STRING]
|
||||
-- CGI Environment parameters
|
||||
-- CGI Environment parameters
|
||||
|
||||
feature -- Access: CGI meta parameters - 1.1
|
||||
feature -- Access: CGI meta parameters - 1.1
|
||||
|
||||
auth_type: detachable READABLE_STRING_8
|
||||
-- This variable is specific to requests made via the "http"
|
||||
@@ -1128,7 +1154,7 @@ feature -- HTTP_*
|
||||
|
||||
http_access_control_request_headers: detachable READABLE_STRING_8
|
||||
-- Indicates which headers will be used in the actual request
|
||||
-- as part of the preflight request
|
||||
-- as part of the preflight request
|
||||
do
|
||||
Result := wgi_request.http_access_control_request_headers
|
||||
end
|
||||
@@ -1139,6 +1165,42 @@ feature -- HTTP_*
|
||||
Result := wgi_request.http_if_match
|
||||
end
|
||||
|
||||
http_if_modified_since: detachable READABLE_STRING_8
|
||||
-- Modification check on resource
|
||||
do
|
||||
Result := wgi_request.http_if_modified_since
|
||||
end
|
||||
|
||||
http_if_none_match: detachable READABLE_STRING_8
|
||||
-- Existence check on resource
|
||||
do
|
||||
Result := wgi_request.http_if_none_match
|
||||
end
|
||||
|
||||
http_if_range: detachable READABLE_STRING_8
|
||||
-- Range check on resource
|
||||
do
|
||||
Result := wgi_request.http_if_range
|
||||
end
|
||||
|
||||
http_if_unmodified_since: detachable READABLE_STRING_8
|
||||
-- Modification check on resource
|
||||
do
|
||||
Result := wgi_request.http_if_unmodified_since
|
||||
end
|
||||
|
||||
http_last_modified: detachable READABLE_STRING_8
|
||||
-- Modification time of resource
|
||||
do
|
||||
Result := wgi_request.http_last_modified
|
||||
end
|
||||
|
||||
http_range: detachable READABLE_STRING_8
|
||||
-- Requested byte-range of resource
|
||||
do
|
||||
Result := wgi_request.http_range
|
||||
end
|
||||
|
||||
feature -- Extra CGI environment variables
|
||||
|
||||
request_uri: READABLE_STRING_8
|
||||
@@ -1319,7 +1381,7 @@ feature -- Query parameters
|
||||
feature {NONE} -- Query parameters: implementation
|
||||
|
||||
query_parameters_table: STRING_TABLE [WSF_VALUE]
|
||||
-- Parameters extracted from QUERY_STRING
|
||||
-- Parameters extracted from QUERY_STRING
|
||||
local
|
||||
vars: like internal_query_parameters_table
|
||||
p,e: INTEGER
|
||||
@@ -1496,7 +1558,7 @@ feature {NONE} -- Form fields and related
|
||||
uploaded_files_table: STRING_TABLE [WSF_UPLOADED_FILE]
|
||||
|
||||
get_form_parameters
|
||||
-- Variables sent by POST, ... request
|
||||
-- Variables sent by POST, ... request
|
||||
local
|
||||
vars: like internal_form_data_parameters_table
|
||||
l_raw_data_cell: detachable CELL [detachable STRING_8]
|
||||
@@ -1528,7 +1590,7 @@ feature {NONE} -- Form fields and related
|
||||
end
|
||||
|
||||
form_parameters_table: STRING_TABLE [WSF_VALUE]
|
||||
-- Variables sent by POST request
|
||||
-- Variables sent by POST request
|
||||
local
|
||||
vars: like internal_form_data_parameters_table
|
||||
do
|
||||
@@ -1541,7 +1603,7 @@ feature {NONE} -- Form fields and related
|
||||
Result := vars
|
||||
end
|
||||
|
||||
feature {NONE} -- Implementation: smart parameter identification
|
||||
feature {NONE} -- Implementation: smart parameter identification
|
||||
|
||||
add_value_to_table (a_name: READABLE_STRING_8; a_value: READABLE_STRING_8; a_table: STRING_TABLE [WSF_VALUE])
|
||||
-- Add urlencoded parameter `a_name'=`a_value' to `a_table'
|
||||
@@ -1749,7 +1811,7 @@ feature {NONE} -- Implementation: URL Utility
|
||||
-- Server url
|
||||
|
||||
internal_url_base: detachable STRING
|
||||
-- URL base of potential script
|
||||
-- URL base of potential script
|
||||
|
||||
feature -- Element change
|
||||
|
||||
@@ -1909,7 +1971,7 @@ feature {NONE} -- Implementation
|
||||
end
|
||||
end
|
||||
|
||||
feature {NONE} -- Implementation: utilities
|
||||
feature {NONE} -- Implementation: utilities
|
||||
|
||||
single_slash_starting_string (s: READABLE_STRING_32): STRING_32
|
||||
-- Return the string `s' (or twin) with one and only one starting slash
|
||||
@@ -1939,7 +2001,7 @@ feature {NONE} -- Implementation: utilities
|
||||
check i >= 2 and i <= n end
|
||||
Result := s.substring (i - 1, s.count)
|
||||
else
|
||||
--| starts with one '/' and only one
|
||||
--| starts with one '/' and only one
|
||||
Result := s
|
||||
end
|
||||
elseif n = 1 then
|
||||
|
||||
@@ -18,7 +18,11 @@
|
||||
<library name="time" location="$ISE_LIBRARY\library\time\time-safe.ecf"/>
|
||||
<library name="uri" location="$ISE_LIBRARY\library\text\uri\uri-safe.ecf"/>
|
||||
<library name="uri_template" location="..\..\text\parser\uri_template\uri_template-safe.ecf"/>
|
||||
<cluster name="router" location=".\router\" recursive="true"/>
|
||||
<cluster name="router" location=".\router\" recursive="true">
|
||||
<file_rule>
|
||||
<exclude>/policy_driven$</exclude>
|
||||
</file_rule>
|
||||
</cluster>
|
||||
<cluster name="src" location=".\src\" recursive="true"/>
|
||||
</target>
|
||||
</system>
|
||||
|
||||
@@ -15,11 +15,16 @@
|
||||
<library name="time" location="$ISE_LIBRARY\library\time\time.ecf"/>
|
||||
<library name="error" location="../../utility/general/error/error.ecf"/>
|
||||
<library name="http" location="../../network/protocol/http/http.ecf"/>
|
||||
<library name="uri_template" location="../../text/parser/uri_template/uri_template.ecf"/>
|
||||
<library name="uri_template"
|
||||
location="../../text/parser/uri_template/uri_template.ecf"/>
|
||||
<library name="encoder"
|
||||
location="..\..\text\encoder\encoder.ecf"/>
|
||||
<library name="uri" location="$ISE_LIBRARY\library\text\uri\uri.ecf" readonly="true"/>
|
||||
<cluster name="src" location=".\src" recursive="true"/>
|
||||
<cluster name="router" location=".\router" recursive="true"/>
|
||||
<cluster name="router" location=".\router" recursive="true">
|
||||
<file_rule>
|
||||
<exclude>/policy_driven$</exclude>
|
||||
</file_rule>
|
||||
</cluster>
|
||||
</target>
|
||||
</system>
|
||||
|
||||
22
library/server/wsf/wsf_policy_driven-safe.ecf
Normal file
22
library/server/wsf/wsf_policy_driven-safe.ecf
Normal file
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-11-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-11-0 http://www.eiffel.com/developers/xml/configuration-1-11-0.xsd" name="wsf_policy_driven" uuid="3FC00449-5101-461D-94C6-10920C30EBF4" library_target="wsf_policy_driven">
|
||||
<target name="wsf_policy_driven">
|
||||
<root all_classes="true"/>
|
||||
<file_rule>
|
||||
<exclude>/.git$</exclude>
|
||||
<exclude>/EIFGENs$</exclude>
|
||||
<exclude>/.svn$</exclude>
|
||||
</file_rule>
|
||||
<option warning="true" full_class_checking="true" is_attached_by_default="true" void_safety="all" syntax="standard">
|
||||
</option>
|
||||
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
|
||||
<library name="time" location="$ISE_LIBRARY\library\time\time-safe.ecf"/>
|
||||
<library name="uri" location="$ISE_LIBRARY\library\text\uri\uri-safe.ecf"/>
|
||||
<library name="ewsgi" location="..\ewsgi\ewsgi-safe.ecf"/>
|
||||
<library name="conneg" location="../../network/protocol/CONNEG/conneg-safe.ecf"/>
|
||||
<library name="encoder" location="..\..\text\encoder\encoder-safe.ecf"/>
|
||||
<library name="http" location="..\..\network\protocol\http\http-safe.ecf"/>
|
||||
<library name="wsf" location="wsf-safe.ecf"/>
|
||||
<cluster name="policy" location=".\policy_driven\" recursive="true"/>
|
||||
</target>
|
||||
</system>
|
||||
22
library/server/wsf/wsf_policy_driven.ecf
Normal file
22
library/server/wsf/wsf_policy_driven.ecf
Normal file
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-11-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-11-0 http://www.eiffel.com/developers/xml/configuration-1-11-0.xsd" name="wsf_policy_driven" uuid="3FC00449-5101-461D-94C6-10920C30EBF4" library_target="wsf_policy_driven">
|
||||
<target name="wsf_policy_driven">
|
||||
<root all_classes="true"/>
|
||||
<file_rule>
|
||||
<exclude>/.git$</exclude>
|
||||
<exclude>/EIFGENs$</exclude>
|
||||
<exclude>/.svn$</exclude>
|
||||
</file_rule>
|
||||
<option warning="true" full_class_checking="true" void_safety="none" syntax="standard">
|
||||
</option>
|
||||
<library name="base" location="$ISE_LIBRARY\library\base\base.ecf"/>
|
||||
<library name="time" location="$ISE_LIBRARY\library\time\time.ecf"/>
|
||||
<library name="uri" location="$ISE_LIBRARY\library\text\uri\uri.ecf"/>
|
||||
<library name="ewsgi" location="..\ewsgi\ewsgi.ecf"/>
|
||||
<library name="conneg" location="../../network/protocol/CONNEG/conneg.ecf"/>
|
||||
<library name="encoder" location="..\..\text\encoder\encoder.ecf"/>
|
||||
<library name="http" location="..\..\network\protocol\http\http.ecf"/>
|
||||
<library name="wsf" location="wsf.ecf"/>
|
||||
<cluster name="policy" location=".\policy_driven\" recursive="true"/>
|
||||
</target>
|
||||
</system>
|
||||
@@ -25,6 +25,16 @@ feature {NONE} -- Initialization
|
||||
create error_added_actions
|
||||
end
|
||||
|
||||
feature -- Access
|
||||
|
||||
primary_error_code: INTEGER
|
||||
-- Code of first error in `errors'
|
||||
require
|
||||
at_least_one_error: has_error
|
||||
do
|
||||
Result := errors.first.code
|
||||
end
|
||||
|
||||
feature -- Status
|
||||
|
||||
has_error: BOOLEAN
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
</option>
|
||||
<library name="atom" location="..\draft\library\protocol\syndication\atom\atom-safe.ecf" readonly="false"/>
|
||||
<library name="client" location="..\examples\restbucksCRUD\client\client-safe.ecf" readonly="false"/>
|
||||
<library name="cms" location="..\draft\application\cms\cms-safe.ecf" readonly="false"/>
|
||||
<library name="connector_cgi" location="..\library\server\ewsgi\connectors\cgi\cgi-safe.ecf" readonly="false"/>
|
||||
<library name="connector_libfcgi" location="..\library\server\ewsgi\connectors\libfcgi\libfcgi-safe.ecf" readonly="false"/>
|
||||
<library name="connector_nino" location="..\library\server\ewsgi\connectors\nino\nino-safe.ecf" readonly="false"/>
|
||||
@@ -22,7 +21,6 @@
|
||||
<library name="default_libfcgi" location="..\library\server\wsf\default\libfcgi-safe.ecf" readonly="false"/>
|
||||
<library name="default_nino" location="..\library\server\wsf\default\nino-safe.ecf" readonly="false"/>
|
||||
<library name="default_openshift" location="..\library\server\wsf\default\openshift-safe.ecf" readonly="false"/>
|
||||
<library name="demo" location="..\draft\application\cms\example\cms_demo-safe.ecf" readonly="false"/>
|
||||
<library name="demo-1" location="..\library\security\openid\consumer\demo\demo-safe.ecf" readonly="false"/>
|
||||
<library name="encoder" location="..\library\text\encoder\encoder-safe.ecf" readonly="false"/>
|
||||
<library name="error" location="..\library\utility\general\error\error-safe.ecf" readonly="false"/>
|
||||
@@ -48,6 +46,7 @@
|
||||
<library name="uri_template" location="..\library\text\parser\uri_template\uri_template-safe.ecf" readonly="false"/>
|
||||
<library name="wizard" location="..\tools\ise_wizard\ewf_ise_wizard-safe.ecf" readonly="false"/>
|
||||
<library name="wsf" location="..\library\server\wsf\wsf-safe.ecf" readonly="false"/>
|
||||
<library name="wsf_policy_driven" location="..\library\server\wsf\wsf_policy_driven-safe.ecf" readonly="false"/>
|
||||
<library name="wsf_all" location="..\library\server\wsf\connector\all-safe.ecf" readonly="false"/>
|
||||
<library name="wsf_cgi" location="..\library\server\wsf\connector\cgi-safe.ecf" readonly="false"/>
|
||||
<library name="wsf_extension" location="..\library\server\wsf\wsf_extension-safe.ecf" readonly="false"/>
|
||||
|
||||
@@ -10,8 +10,6 @@
|
||||
</file_rule>
|
||||
<option warning="true" full_class_checking="true" is_attached_by_default="true" void_safety="all" syntax="standard">
|
||||
</option>
|
||||
<library name="cms" location="..\draft\application\cms\cms-safe.ecf" readonly="false"/>
|
||||
<library name="demo" location="..\draft\application\cms\example\cms_demo-safe.ecf" readonly="false"/>
|
||||
<library name="restbucks" location="..\examples\restbucksCRUD\restbucks-safe.ecf" readonly="false"/>
|
||||
<library name="client" location="..\examples\restbucksCRUD\client\client-safe.ecf" readonly="false"/>
|
||||
<library name="upload_image" location="..\examples\upload_image\upload_image-safe.ecf" readonly="false"/>
|
||||
@@ -21,7 +19,6 @@
|
||||
<library name="openid" location="..\library\security\openid\consumer\openid-safe.ecf" readonly="false"/>
|
||||
<library name="demo-1" location="..\library\security\openid\consumer\demo\demo-safe.ecf" readonly="false"/>
|
||||
<library name="http_authorization" location="..\library\server\authentication\http_authorization\http_authorization-safe.ecf" readonly="false"/>
|
||||
<library name="ewf_support" location="..\library\server\ewf_support\ewf_support-safe.ecf" readonly="false"/>
|
||||
<library name="ewsgi" location="..\library\server\ewsgi\ewsgi-safe.ecf" readonly="false"/>
|
||||
<library name="ewsgi_spec" location="..\library\server\ewsgi\ewsgi_spec-safe.ecf" readonly="false"/>
|
||||
<library name="connector_cgi" location="..\library\server\ewsgi\connectors\cgi\cgi-safe.ecf" readonly="false"/>
|
||||
|
||||
Reference in New Issue
Block a user