The web pod defines the standard APIs used to handle both client and server side HTTP requests.
Client side HTTP requests:
WebClient: manages client side of the HTTP protocol
Server side web APIs are organized into the primary classes:
WebReq: models an incoming web request such as the method, uri, request headers, and input stream.
WebRes: models the outgoing web response such as the status code, response headers, and output stream.
Weblet: an entity which processes a web request.
WebMod: a web module which may be composed with other modules to build up a web solution.
WebClient class is used to manage client side HTTP requests and responses. The basic lifecycle of WebClient:
- configure request fields such as
- send request headers via
- optionally write request body via
- read response status and headers via
- process response fields such as
- optionally read response body via
Using the low level methods
readRes enables HTTP pipelining (multiple requests and responses on the same TCP socket connection). There are also a series of convenience methods which make common cases easier.
See examples for sample code.
Pretty much anything that touches a HTTP request should be implement
Weblet. The lifecycle of a Weblet is quite simple:
- all web requests are guaranteed to be called on their own actor/thread with the actor locals "web.req" and "web.res"
res: the current actor's
WebRes are available with these methods - so there no need to pass the request and response around
onService method can be overridden directly to handle the request, or the default implementation will route to the
doPost, etc methods
WebReq class models the request side of a HTTP request. Common methods you will use include:
method: HTTP method such as "GET" or "POST"
uri: the request URI parsed into a
Uri which allows you access the parsed path and query segments.
mod: web module currently responsible for request
modBase: URI used to route to current module
modRel: module relative URI used for module internal processing
headers: a case insensitive
Str:Str map of the request HTTP headers
in: access to the raw input stream of the request
form: access to the parsed form data
Str:Str map of cookies
Str:Obj map used to stash stuff for the browser "connection" between HTTP requests
Str:Obj map used to stash stuff only for the life of request
WebRes class models the response side of a HTTP request. A
WebRes has the following lifecycle:
- Uncommitted: at this point nothing has been written back on the TCP socket and
cookies are still configurable
- Committed: at this point the HTTP response headers have been written, and you can write the response content via the out stream. Once a response is committed, attempts to access
sendErr will raise an exception
- Done: at this point the response is complete - for example once the
sendErr method is called, the response is done
Common methods you will use include:
statusCode: sets the HTTP status code - must be set before commit
Str:Str map of HTTP headers - must be set before commit
cookies: used to set the cookie header - must be set before commit
out: the output stream for writing the content - first call commits the response
isCommitted: check commit state
isDone: check done state
sendErr: used to send an error status code
redirect: used to send a redirect status code
WebRes is a fairly low level API which requires the commit state model to avoid buffering the content.
WebSession class models the client session which allows you to persist state between HTTP requests. WebSessions in Fantom are cookie based using the cookie name "fanws". The default session implementation stores sessions in memory for up to 24 hours, then clears them from the cache - session state is not persisted between VM restarts.
WebSession provides a
Str:Obj? map to store arbitrary name/value pairs. You can use the
set methods to manage session state. You can use
delete to explicitly delete the session cookie and server side state. The values stored in a WebSession should always be serializable objects.
WebSessions are created and accessed via the
WebReq.session method. The first time a session is accessed it sets the cookie header in the response - therefore sessions should always be accessed before the response is committed. Deleting a session also requires setting the cookie header and must done before the response is committed.
Example of storing a counter in a session:
override Void doGet()
Int count := req.session.get("counter", 0)
req.session["counter"] = count + 1
res.headers["Content-Type"] = "text/plain"
res.statusCode = 200
WebMod class is the base class for plugging in web server modules. WebMods are immutable Weblets which may be composed together to build higher level modules or to configure the entire web server.
During processing of a given web request, there is always exactly one WebMod responsible for the request which is available via the
WebReq.mod method. The URI used to route to the module is accessed by
WebReq.modBase, and the remainder of the URI which is internal to the module via
WebReq.modRel. Using these methods you can write modules which can be freely plugged anywhere into a server's URI namespace.
WebMods receive the
onStop callbacks when the web server is started and stopped. These callbacks can be used to perform initialization and cleanup such as managing actors.
webmod pod includes a library of modules which are designed to handle common tasks such publishing static files, routing, and pipelining.
Using "Expect: 100-continue" allows the server to fail-fast and report an error to the client before the client sends the request body. This technique is often used before posting large files to verify preconditions.
WebClient API does not provide automatic support for using the Expect header. You must manually handle flow control yourself. Here is a simple example showing how to post a file using the Expect header:
c := WebClient(`http://example.com/post-file`)
c.reqMethod = "POST"
c.reqHeaders["Content-Type"] = file.mimeType.toStr
c.reqHeaders["Content-Length"] = file.size.toStr
c.reqHeaders["Expect"] = "100-continue"
if (c.resCode != 100) throw IOErr("Expecting 100, not $c.resCode")
if (c.resCode != 200) throw IOErr("Expecting 200, not $c.resCode")
Server side processing of the Expect header is automatic. When a Weblet acquires the
WebReq.in stream for the first time, the request is checked for the "Expect: 100-continue" header and if specified, then a 100 Continue is automatically sent.