Welcome to the Simple HTTP Test Container for use by test applications that need to test aspects of their HTTP protocol. This container allows you to PUT resources to file names and to GET them later. This could be used by unit test frameworks that need to be able to PUT and GET resources.
The container is released under a BSD license:
* Copyright (c) 2007, Escenic AS * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of Escenic AS nor the names of its contributors may * be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY ESCENIC AS ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL ESCENIC AS BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
The container has the notion of a flat file system. Files are not structured in directories; they are ignored. So you can PUT to /somefile or /my/directory/somefile and you would actually be putting the same resource. Directories are used for something completely different; namely enabling parts of HTTP.
For example if you want to enable use of the "Last-Modified" header you can GET your resource from the "lm" directory: GET /lm/somefile will give you a "Last-Modified" header. Also if you send in the "If-Modified-Since" header with the same date as "Last-Modified" then you will get a HTTP 304 NOT MODIFIED response as specified by HTTP.
Likewise if you GET your resource from the "etag" directory you will get ETag responses based on the checksum of the file, and the server will perform If-Match and If-None-Match requests accordingly.
The following list of directories are supported:
Here is an example conversation between a user agent and this web application. First we PUT some text to the file "hello.txt"
PUT /hello.txt Host: localhost Hello World! ------------------------------------ 204 No Content Date: Fri, 09 Nov 2007 12:37:45 GMT
We can now GET it if we like:
GET /hello.txt ---------8<--------------------- 200 OK Connection: close Date: Fri, 09 Nov 2007 12:40:45 GMT Content-Length: 12 Content-Type: text/plain Hello World!
The actual headers will probably vary based on your application server and user agent.
The GET request can now be enhanced with a "Last-Modified" date by accessing the "hello.txt" file in the "lm" directory:
GET /lm/hello.txt ---------8<--------------------- 200 OK Connection: close Date: Fri, 09 Nov 2007 12:40:45 GMT Content-Length: 12 Content-Type: text/plain Last-Modified: Fri, 09 Nov 2007 12:37:45 GMT Hello World!
It is now of course possible to construct a Conditional GET which will trigger the server 304 response. We take the Last-Modified header of the response and put it into the If-Modified-Since header of the request:
GET /lm/hello.txt If-Modified-Since: Fri, 09 Nov 2007 12:37:45 GMT ---------8<--------------------- 304 NOT MODIFIED Connection: close Date: Fri, 09 Nov 2007 12:45:12 GMT Last-Modified: Fri, 09 Nov 2007 12:37:45 GMT
Let's add entity tags by GETting the response from the "etag" directory. It's the same resource that's why I can reuse the If-Modified-Since from the previous example even though it looks as if I'm accessing a "different" resource. Both /lm/hello.txt and /lm/etag/hello.txt are the same "thing" and have the same modification dates etc.
GET /lm/etag/hello.txt If-Modified-Since: Fri, 09 Nov 2007 12:37:45 GMT ---------8<--------------------- 304 NOT MODIFIED Connection: close Date: Fri, 09 Nov 2007 12:45:12 GMT ETag: "ffffffffc63cb61d" Last-Modified: Fri, 09 Nov 2007 12:37:45 GMT
Since it was a conditional GET (If-Modified-Since) I didn't get any content but only updated headers: ETag. The ETag is simply a hashcode of the content, and the string "Hello World!" gets the ETag "ffffffffc63cb61d". We can now construct a conditional GET which relies on ETags instead:
GET /lm/etag/hello.txt If-None-Match: "ffffffffc63cb61d" ---------8<--------------------- 304 NOT MODIFIED Connection: close Date: Fri, 09 Nov 2007 12:45:12 GMT ETag: "ffffffffc63cb61d" Last-Modified: Fri, 09 Nov 2007 12:37:45 GMT
Conditional ETag matching is quite cool. The cache can keep the last 10 representations in the hopes that one of them might go back in style. If so it sends the ETags of all of the cached responses as follows
GET /lm/etag/hello.txt If-None-Match: "782f2dcc2" "ffffffffc63cb61d" "ffdb49dc" "a3cdbeef6" ---------8<--------------------- 304 NOT MODIFIED Connection: close Date: Fri, 09 Nov 2007 12:45:12 GMT ETag: "ffffffffc63cb61d" Last-Modified: Fri, 09 Nov 2007 12:37:45 GMT
The response from the server indicates which of them to use to the client by setting the ETag header just as it would given the full entity.
The "cc" directory enables a simple cache control which gives you 18 seconds of cache. It only affects responses:
GET /cc/hello.txt ---------8<--------------------- 200 OK Connection: close Date: Fri, 09 Nov 2007 12:45:12 GMT Cache-Control: max-age=18 Hello World!
You can also specify the max-age parameter as follows:
GET /cc,100/hello.txt ---------8<--------------------- 200 OK Connection: close Date: Fri, 09 Nov 2007 12:45:12 GMT Cache-Control: max-age=100 Hello World!
There is preliminary support for surrogates by accessing any resource from a 'surrogate' directory. Surrogate checking is only possible if you actually send in the "Surrogate-Capability" header. Typically proxies use this to tell origin servers that they are there and that they can do post processing. If an origin server finds out that it wants to use this functionality it adds a "Surrogate-Control" header in the response to tell it that it should activate one or more transformations on the response before it is passed back to the client.
The surrogate support in the web application simply looks for a surrogate with the capability of processing ESI/1.0 and returns all of that surrogate's capabilities in the Surrogate-Control header. Let's just take a look at an example!
GET /surrogate/hello.txt Surrogate-Capability: varnish="ESI/1.0 Joe/2.0" ---------8<--------------------- 200 OK Connection: close Date: Fri, 09 Nov 2007 12:45:12 GMT Surrogate-Control: content="ESI/1.0 Joe/2.0" Hello World!
As you can see the entire set of capabilities (ESI/1.0 and Joe/2.0) are echoed back to the server. If the surrogate does not support ESI/1.0 then we don't consider it. Here we have a surrogate which doesn't support ESI and the response does not ask for any processing.
GET /surrogate/hello.txt Surrogate-Capability: varnish="VARNISH/1.0 Joe/2.0" ---------8<--------------------- 200 OK Connection: close Date: Fri, 09 Nov 2007 12:46:34 GMT Hello World!
Finally, you can of course combine these things by adding stuff to the path. The order of the path is not important, as the order is strictly defined by the application itself (it checks for ETags before it checks for Last Modified and so on. Here's a request with all of the options enabled:
GET /surrogate/cc,180/etag/lm/hello.txt Surrogate-Capability: foo="ESI/1.0" If-Modified-Since: Fri, 09 Nov 2007 12:37:45 GMT If-None-Match: "ffffffffc63cb61d" ---------8<--------------------- 304 NOT MODIFIED Connection: close Cache-Control: max-age=180 Date: Fri, 09 Nov 2007 14:27:28 GMT ETag: "ffffffffc63cb61d" Surrogate-Control: content="ESI/1.0"
As you can see the 304 response correctly includes the Surrogate-Control header indicating that the origin server still wants surrogates to do ESI processing.
It is possible to simulate a slow origin server by accessing anything from within the /sleep/ directory. The sleeping server is configurable; it is possible to tell the origin server to sleep for a specified number of seconds. By default the server will sleep for five seconds. Here are some example URIs to help you get going:
Slow origin servers can come in handy when testing for timeout and stale/freshness. By default the slowness does not affect conditional methods where a HTTP 304 is returned. If you want HTTP 304 to be slow as well, use the unconditional-sleep keyword which is the same as sleep except that it always happens.
It is possible to generate arbitrary error codes on the server by accessing the /error/ directory. You can also add the HTTP response code you want as a parameter:
Errors can be useful when testing how a surrogate reacts to different types of errors.
It is possible to specify the failure rate in percent when GETing a resource: /error,rate=10/ will return HTTP 500 error codes ten percent of the times. The rest of the times the GET will proceed as usual.
It is possible to specify a binary pattern which describes the error response rate specifically: You can tell the resource to work once and fail several times, to allow for populating caches. This is useful if you want to test how errors are handled when there is stale content in the cache.
There are three different keywords available for errors:
It is possible to combine them into quite erratic origin server behaviour: GET /lm/sleep,3/unconditional-sleep,1/error,503,rate=10/slow-error,404,rate=10/conditional-error,rate=50/ This means:
If you need to debug, simply replace rate= with pattern= and give all the error paths different patterns (011, 101, 110) so each error condition happens once in every three requests.