Sessions and server side state
Differents scopes for data and services
The server-side state of an application refers to data stored on server side that could be shared by all clients, or that could be specific to a limited scope such as:
- a session,
- a group of sessions (for example all sessions of the same user),
- a client-side process (when programming with App), or
- the current request.
For a given scope, server-side state regroups:
- services registered with this specific scope, and
- data encapsulated within an Eliom reference created with this specific scope.
States disappear either when the associated scope is discarded explicitely or by a timeout.
Scope and cookies
On a technical point of view, sessions and groups of sessions are implemented automatically within Eliom by asking the browser to send a session identifier in a cookie. Client-side processes also send an identifier in each request, using some kind of "client-side process-cookie".
It is possible to create different scopes of the same level if you want several states for the same application (advanced use). Each scope uses its own cookies and you can discard data for one scope only.
States can be secure or not. Secure means that the state data or service will be associated to a secure cookie, that is a cookie that is sent by the browser only if the protocol is HTTPS. Use secure states to access confidential data, that you do not want to send through HTTP.
Three kind of states
In the current implementation, because of some limitation in OCaml's serialization mechanism, there are three kinds of states (for each scope):
- volatile data states,
- volatile service states,
- persistent data states.
Volatile states will not survive after relaunching the server. There is no persistent service state. Be very careful if you use both persistent state data and service state, as your session may become inconsistent (use service states only for volatile services, like coservices with timeouts).
We hope to simplify this when OCaml's serialization mechanism evolves. In the meantime, be very careful where you store your data. To avoid shutting down the server, note that it is possible to ask the server to load dynamically new versions of your site (see command reload).
Scopes, cookies and sessions ¶
Eliom use a notion of scopes to restrict the visibility of server-side data to some clients. Together with Eliom references, this is a very convenient way to implement a mechanism of session for your website.
There is two category of scopes:
- Eliom scopes, that regroups:
- User scopes: used to restrict the accessibility of a data or a service to subset of the clients.
Within the users scopes, Eliom distinguish three scopes that differs on how Eliom associates clients and data. Data and dynamic services created with scope:
- Eliom_common.session are only visible to client where specific cookies has been set. i.e. a session is associated to a single browser.
- Eliom_common.session_group are only visible to client who belongs to one of the session attached to the group. See section Session groups for more information.
- Eliom_common.client_process are only visible to a specific instance of an Eliom application. See section Eliom applications for more information. i.e. a client_process is associated to a single tab of a browser.
If you want to handle multiple sessions for the same site –e.g. several different data sessions that could be created and discarded independently– you can create new users scope that will use different cookies, see section Named users scopes for more information.
Creating sessions and scopes
Automatic session creation
Eliom automatically creates a session –and setup the corresponding cookie on the client– when you first modify an Eliom reference of scope session, when you register a service with this scope or when you enter a session group.
By default, Eliom is using three cookies for sessions (and session groups):
- one for session services,
- one for volatile session data,
- one for persistent session data.
For client side process, it uses the same three kinds of client side process cookies.
Named users scopes and multiple sessions ¶
If you want to handle multiple sessions for the same site that could be created and discarded independently, you can create new "named" users scope that will use different cookies, with the function Eliom_common.create_scope_name.
let custom_session_name = Eliom_common.create_scope_name "custom" let custom_session = `Session custom_session_name
Then, the value custom_session could replace the usual Eliom_common.session for the ~scope parameter of functions Eliom_reference.eref, Eliom_registration.Html5.register, Eliom_state.discard, ...
You can also create named client_process scope and session_group by replacing `Session by `Client_process or `Session_group. In all case, the Eliom_common.create_scope_name function will prevent you to create two identical scope name.
Closing sessions ¶
To discard a state, use the Eliom_states.discard function. It will remove all server-side services and data (persistent or not) for the given scope. Used with ~scope:Eliom_common.session, this will close a session. Used with ~scope:Eliom_common.session_group, this will close all sessions in the group.
It is also possible to discard selectively only services, persistent data or volatile data (see the Eliom_state module). But this may be periculous. Be very careful when doing this, as you are desynchronizing the three kinds of sessions.
- It is a good idea to close the session when a user tries to connect, even if it is already connected,
- You may also want to unset some request-scoped Eliom references when discarding a state,
- If your state data contains opened file descriptors, they won't be closed by OCaml's garbage collector. Close it yourself! (for example using Gc.finalise).
Timeouts and session duration ¶
The default timeout for sessions in one hour. Sessions will be automatically closed after that amount of time of inactivity from the user. You can change that value for your whole site using the Eliom_state.set_global_volatile_state_timeout.
There is also a possibility to change the default value for Eliom in the configuration file like this:
<extension findlib-package="ocsigenserver.ext.eliom"> <volatiletimeout value="7200"/> </extension>
In the configuration files the value "infinity" would mean no timeout.
That default may be overriden by each site using Eliom_state.set_global_volatile_timeout or Eliom_state.set_default_volatile_timeout. If you want your user to be able to set the default in the configuration file for your site (between <site> and </site>), you must parse the configuration ( using Eliom_config.get_config function). You can also change the timeout for a specific user only with the following functions: Eliom_state.set_volatile_data_state_timeout. For more details, see the Eliom_state module's interface.
Secure session ¶
By default, data and services saved in a session are shared between client connecting with the http and https protocols. If you want to keep some state in a secure session that are visible only to client accessing with the https protocol, you may give the optionnal parameter ~secure:true when calling functions like Eliom_reference.eref, Eliom_registration.Html5.register, etc.
Secure sessions are using secure cookies (i.e. Ocsigen server will ask the browsers to send the cookie only if the protocol is HTTPS). Thus it is not possible to access secure references and services if the user is using http.
Session groups ¶
Sharing data between a group of session
Session group is a kind of scope that allows to share data or services between a set of sessions, e.g. all sessions for given user. For example, using persistant Eliom references with scope Eliom_common.session_group is a convenient way to store data about a user without having to explicitly use an external database.
A session group is identified by a name. The current session could be attached to a group of session using one of the following functions –depending on the nature of data you want to share– that take the session group name as parameter:
A session could be only attached to one group at a time, but it is possible to create multiple sessions for a same client attached to different groups, see section Named users scopes for more information.
It's possible to fetch the current session group name of a session, if any, or to detach a session from a group. See the module Eliom_state for more information.
Limit the number of session within a group
The number of sessions in a group is limited. If all sessions are in a group you will avoid some malicious user to open too many sessions. If you do not use session groups, the number of sessions is limitated by IP address, which can be a problem for example if the server is behind a reverse proxy or if many user are using the same NAT.
Close all session of a group
Session groups allows to implement features like "close all sessions" for one user (even those opened on other browsers). Consider the following scenario: a user logs in from home using a "Remember me on this computer" feature, which sets a (almost) no-expiration cookie on his browser and session timeouts of infinity on the server. The user goes on vacation, and while logging from a cyber-café, he also sets the "Remember me" option. Back home he realises the mistake, and wishes to do a "global logout", i.e., closing all existing sessions associated with his user name.
For those kind of usage, it is highly recommended to set a group for each of the three kinds of states you use (services, volatile data and persistent data).
See section Closing sessions for more information.
Eliom references (erefs) are some kind of references with a (possibly) limited scope. You declare a reference with an initial value and a scope. Then, when you change the associated value, it actually changes only for the scope you specified.
Eliom references can be persistent or not (that is: can survive after relaunching the server or not).
Eliom references are used for example:
- to store session data, server side data for a client process, or user data (scope: session, client process, session group),
- or to keep some information about the current request (scope: request), for example to give information to the service taking in charge the request after an action,
- to implement persistent references (scope: global)
- for caching functions (Eliom_reference.eref_from_fun).
Non persistent global Eliom references are equivalent to regular OCaml references.
Eliom references are either created using the function Eliom_reference.eref, that works like the usual Ocaml ref function, but with at least one additional scope parameter. Or they may be created by the fucntion Eliom_reference.eref_from_fun: Its argument function is evaluated the first time the reference is accessed (through Eliom_reference.get), within one scope or after the reference has been reset.
type 'a eref val eref : scope:[< Eliom_common.all_scope ] -> ?secure:bool -> ?persistent:string -> 'a -> 'a eref val eref_from_fun : scope:[< Eliom_common.all_scope ] -> ?secure:bool -> ?persistent:string -> (unit -> 'a) -> 'a eref val get : 'a eref -> 'a Lwt.t val set : 'a eref -> 'a -> unit Lwt.t val modify : 'a eref -> ('a -> 'a) -> unit Lwt.t val unset : 'a eref -> unit Lwt.t
Persistent references ¶
Persistent references are Eliom references that survives after relaunching the server. They are implemented using the Ocsipersist module for which Ocsigenserver provides two implementations, one based on SQLite, the other one based on DBM.
Persistent references are created by adding the ~persistent parameter to the Eliom_reference.eref function call. The value of this parameter is the name of the reference in the database.
The module Eliom_reference.Volatile allows the creation of non-persistent Eliom references, which can then be used through a non-Lwt interface.
Low-level cookies manipulation
Eliom references are used to store data on the server-side. It is also possible to ask the browser or the client-side process to record some piece of data and send it back to the server with each request.
This is implemented using the usual browser cookies for sessions –and a simulation of browser cookies by Eliom client side processes– with the functions Eliom_State.set_cookie and Eliom_State.get_cookie.