
{0 Service handlers}



{1:predefined_outputs Output modules}

Once the {{!page-"server-services".identification}service identification mechanism}
identifies the service responsible for a given URL, it executes its
service handler. The service handler is a function taking the GET and
POST parameters as arguments and returning the content to be sent to
the client. The return type of the service handler depends on the
function used to register it. The most common case is HTML content
build with the TyXML library, but Eliom additionally provides a lot of
output modules to ease the implementation of common Web
interaction. See section {{!page-"server-outputs".predefined_outputs}Predefined output modules} for a
comprehensive list.

{2 List of predefined output modules}

Services can send several types of data, using a variety of predefined modules.
It is also possible to {{!page-"server-outputs".creating}create your own output modules}.
The main predefined output modules are:



{%wodoc:div class="paragraph"%} Generating content for the browser {%wodoc:end%}

;{!Eliom_registration.Html}
: Registration of functions that generate HTML pages statically checked
using polymorphic variant types. You may use constructor functions from {!Eliom_content.Html.D} or a syntax extension close to the standard HTML syntax.
;{!Eliom_registration.Flow5}
: Registration of functions that generate a portion of page using {!Eliom_content.Html.F} or the syntax extension (useful for [XMLHttpRequest] requests for example). Do not use with Eliom applications: you can instead use {!Eliom_client.server_function} (or the [let%rpc] syntax) to call server functions that produce HTML nodes.
;{!Eliom_registration.Html_text}
: Registration of functions that generate text HTML pages, without any validation of the content. The content type sent by the server is "[text/html]".
;{!Eliom_registration.CssText}
: Registration of functions that   generate CSS pages, without any validation of the content. The content type sent by the server is "[text/css]".
;{!Eliom_registration.String}
: Registration of functions that   generate text pages, without any validation of the content. The services return a pair of strings. The first string is the content   of the page, while the second string is the content type.
;{!Eliom_registration.File}
: Registration of services that send files. See {{!page-"server-outputs".eliomfiles}here} for an example of use.
;{!Eliom_registration.Streamlist}
: Registration of services that send "byte" contents. It is used when big content (that does not fit in memory) is generated.

{%wodoc:div class="paragraph"%} Generating content for client-server applications {%wodoc:end%}

;{!Eliom_registration.App}
: Functor that allows creation of services belonging to a client-server Eliom application (see {{!page-"clientserver-applications"}chapter client-server applications}).

{%wodoc:div class="paragraph"%} Special browser interraction {%wodoc:end%}

;{!Eliom_registration.Action}
: Registration of actions (functions that do not generate any page. See {{!page-"server-outputs".actions}Action}). The page corresponding to the URL (without the special parameter identifying the action) is reloaded after the action by default if possible.
;{!Eliom_registration.Unit}
: Like {!Eliom_registration.Action}, but the URL is not reloaded after the action. (Same as [Eliom_registration.Action] with [[`NoReload]] option).
;{!Eliom_registration.Redirection}
: Registration of HTTP redirections. The handler returns the service (without parameter) of the page you want to redirect to. The browser will get a 301 or 307 code in answer and redo the request to the new URL. To specify whether you want temporary (307) or permanent (301) redirections, use the {%wodoc:span class="code"%}?options{%wodoc:end%} parameter of registration functions. For example: {%wodoc:span class="code"%}register ~options:`Permanent ...{%wodoc:end%} or {%wodoc:span class="code"%}register ~options:`Temporary ...{%wodoc:end%}.
;{!Eliom_registration.String_redirection}
: Same but the ouput type is a string. Use with care! Warning: According to the RFC of the HTTP protocol, the URL must be absolute!

{%wodoc:div class="paragraph"%} Customization of other outputs {%wodoc:end%}


;{!Eliom_registration.Customize}
: Specialization of service registration functions by customizing the page type.

{%wodoc:div class="paragraph"%} Sending caml values to client side code {%wodoc:end%}

;{!Eliom_registration.Ocaml}
: Registration of services sending marshalled OCaml values. See the section on
{{!page-"clientserver-applications".communication}communications in the chapter about client-server applications}.

{%wodoc:div class="paragraph"%} Runtime choice of content {%wodoc:end%}

;{!Eliom_registration.Any}
: Registration of services that can choose what they send, for example an HTML page or a file, depending on some situation (parameter, user logged or not, page present in a cache ...). It is also possible to create your own modules for other types of pages. See {{!page-"server-services".any}here} for an example of use.

{2 Advanced output modules}



{3:eliomfiles Sending files}

You may want to register a service that sends files.  To do so,
use the {!Eliom_registration.File} module. Example:

{@ocaml[
let sendfile =
  Eliom_registration.File.create
    ~path:(Eliom_service.Path ["sendfile"])
    ~meth:(Eliom_service.Get Eliom_parameter.unit)
    (fun () () -> return "filename")
]}

Other example, with "suffix" services (see {{!page-"server-params".suffix}here}):
{@ocaml[
let sendfile2 =
  Eliom_registration.File.create
    ~path:(Eliom_service.Path ["files"])
    ~meth:
      (Eliom_service.Get
         Eliom_parameter.(suffix (all_suffix "filename")))
    (fun s () -> Lwt.return @@
      "//path//" ^
      Ocsigen_lib.Url.string_of_url_path ~encode:false s)
]}

The extension {{!page-"server-services".staticparts}Staticmod} is another way to
handle static files.



{3 Sending portions of pages}


The {!Eliom_registration.Flow5}
module allows you to register services that send portions of pages, of
any element type.  It is sometimes useful to create
AJAX pages (i.e. pages using the [XMLHttpRequest]
JavaScript object).  Note that the service returns a list of
blocks. For sending HTML to the client-side portion of an Eliom application,
server functions
({!Eliom_client.server_function}, [let%rpc]) are
better-suited.


{@ocaml[let divpage =
  Eliom_registration.Flow5.create
    ~path:(Eliom_service.Path ["div"])
    ~meth:(Eliom_service.Get Eliom_parameter.unit)
    (fun () () ->
      Lwt.return
        [div [h2 [txt "Hallo"];
              p [txt "Blablablabla"] ]])
]}


The
{!Eliom_registration.Make_typed_xml_registration}
module allows the creation of new modules for registering portions of
pages of other types.  For example, {!Eliom_registration.Flow5} is defined by:

{@ocaml[module Flow5 = Make_typed_xml_registration(Xml)(Html.F)(struct
  type content = Html_types.body_content
end)
]}


{3:redirections Redirections}

{4 Redirections to Eliom services}

The {!Eliom_registration.Redirection} module allows one to register HTTP redirections.{%html:<br/>%}
If a request is made for such a service, the server asks the browser
to retry with another URL.

Redirection services need to return a GET service without parameter at all.
Example:
{@ocaml[
let redir1 =
  Eliom_registration.Redirection.create
    ~options:`TemporaryRedirect
    ~path:(Eliom_service.Path ["redir"])
    ~meth:(Eliom_service.Get Eliom_parameter.uni)
    (fun () () -> Lwt.return someservice)
]}

If you want to give parameters to such services, use
{!Eliom_service.preapply} (see also in
{{!page-"server-services".preapplied}section about pre-applied services}).
Example:

{@ocaml[
let redir = Eliom_registration.Redirection.create
   ~options:`TemporaryRedirect
   ~path:(Eliom_service.Path ["redir"])
   ~meth:(Eliom_service.Get Eliom_parameter.(int "o"))
   (fun o () ->
      Lwt.return
        (Eliom_service.preapply service_with_params (o,(22,"ee"))))
]}

The [options] parameter may be either [`Temporary] or
[`Permanent].

Note that the cost of a redirection is one more request and
one more response.

{4 Redirections to generated URLs}

The
{!Eliom_registration.String_redirection}
module allows one to register HTTP redirections to custom URLs
provided as strings. In most cases, it is a better idea to use {!Eliom_registration.Redirection}, even for
external redirections (using
{!Eliom_service.extern}).
Use
{!Eliom_registration.String_redirection}
only when it is not possible to have a service corresponding to a URL.

Notice that the supplied URL must be absolute.

{3:actions Actions}

Actions are used to perform side effects before generating the
fallback of a service. When an action is called, the service handler is
executed, then the service handler of the fallback service is
executed.

Eliom references of scope {!Eliom_common.request_scope} set in
an action handler are still available in the service handler of the fallback.

A common use of actions and pathless services working synergistically
is the implementation of login/logout forms. Actions are well-suited
for the following reasons:
- Connection and disconnection can happen as the side-effect of the
  action, after which we stay on the same page; and
- The connection/disconnection form generally needs to be present on
  all pages. An action implemented as a pathless service will respond
  no matter what page it is called from, so there is no need to create
  a version with POST parameters of each service.
The implementation of the same behavior with standard Web programming
techniques is usually much more complicated.

{3:any Registering services that decide what they want to send}

You may want to register a service that will send, for instance,
sometimes an HTML page, sometimes a file, sometimes something else.
To do that, use the
{!Eliom_registration.Any} module,
together with the {%wodoc:span class="code"%}send{%wodoc:end%} function of the module
you want to use. Example:

{@ocaml[
let send_any =
  Eliom_registration.Any.create
    ~path:(Eliom_service.Path ["sendany"])
    ~meth:(Eliom_service.Get Eliom_parameter.(string "type"))
    (fun s () ->
       if s = "valid" then
         Eliom_registration.Html.send
           (html
              (head (title (txt "")) [])
              (body [p [txt "This page has been statically checked. ";
                        txt "If you change the parameter in the URL ";
                        txt "will get an unchecked text page"]]))
       else
         Eliom_registration.Html_text.send
           "<p>Not a valid page. Try with type=\"valid\" in the URL.</p>")
]}

{4 Dynamically modifying register options using Any}

You may also use
{!Eliom_registration.Any}
to dynamically modify the parameters usually set on the register
function. You can set the HTTP code, the charset, the content_type,
the HTTP headers and the specific option of the output module.

{@ocaml[
let change_option =
  Eliom_registration.Any.create
    ~path:(Eliom_service.Path ["change_option"])
    ~meth:(Eliom_service.Get Eliom_parameter.unit)
   (fun () () ->
     Eliom_registration.Html.send
       ~code:403
       (html
          (head (title (txt "forbidden")) [])
          (body [p [txt "forbidden"]])))
]}

{4 About kind type and how to serve application and other content with the same service}

In Eliom applications, changing the current page does not always do the
same thing. When going to a page inside the same application by
clicking on a link (or calling {!Eliom_client.change_page}) the client
application performs an [XmlHttpRequest] and modifies the displayed page
according to the result. When going to content outside the
application (another site, a static file, etc.) the client leaves the
application by changing the browser URL.

When using {!Eliom_registration.Any}, there is no way to know before the request whether the content is
from the same application or not. To this end, there are phantom type
annotations to the type of the [send] functions: {!Eliom_registration.kind}. The {!Eliom_registration.Any.register} takes a
service handler that can server only one kind of content: that way it
is not possible to inadvertently mix kinds. The different kinds of
content are:

- Browser content: everything that can't be handled by application
  directly, e.g., HTML pages, files
- Block content: subparts of pages sent as XML: e.g., [Flow5],
  [Block].
- Application content: pages of application.
- Ocaml content: marshalled OCaml values.
- Unknown content: content generated as text.

Yet sometimes you may want to mix the kinds of contents a service can
return. The function {!val
Eliom_registration.appl_self_redirect} allows one to cast browser
content to application content. When an application requests some
content cast through that function, the server sends some information
asking the client to exit to a specific address. You should not use
that on POST services: leaving the application sending POST parameters
is not always possible and the request will be performed two times.

For instance, you may want to serve a file if it exists, and generate
some error message with client-side code otherwise. You can achieve
this as follows.

{@ocaml[
let file_or_application =
  Eliom_registration.Any.create
    ~path:(Eliom_service.Path ["file_or_application"])
    ~meth:(Eliom_service.Get Eliom_parameter.(string "filename"))
   (fun filename () ->
     if Eliom_registration.File.check_file filename
     then
       Eliom_registration.appl_self_redirect
         Eliom_registration.File.send
         filename
     else
       My_application.send ~code:404
         (html
           (head (title (txt "no page")) [])
           (body [p [txt "the file does not exist"]])))
]}

Unknown content can be cast to browser content using {!val
Eliom_registration.cast_unknown_content_kind}.

{2:creating Creating your own output modules}

{3:customize By customizing an existing registration module}

Using {!Eliom_registration.Customize}, you can
specialize a registration function to avoid code duplication. You can
for instance add parameters before calling the service handler, or
modify the answer.

Importantly, you can use this customization mechanism to check whether
a user is logged-in before serving the content of a page. That way, we
don't need to do the check in every service handler, and we can get
the user information directly.

In this example, we check if we are in a session group telling that
the user is connected. If this not the case, we generate a login
page. When we are in a group, we retrieve the user information and
pass it to the service handler. It returns a title and the content of
the body. That way, it also always generate the same CSS without code
duplication.

{@ocaml[
module Connected_param = struct

  type page = user_info -> Eliom_registration.Html.page Lwt.t

  let translate page =
    match
      Eliom_state.get_volatile_data_session_group
        ~scope:Eliom_common.default_session_scope ()
    with
    | None ->
      login_page ()
    | Some group_name ->
      let%lwt user_info = get_user_info group_name in
      let%lwt page_title, content = page user_info in
      Lwt.return
        Eliom_content.Html.D.(
          html (head (title (txt page_title)) [])
            (body content)
        )

end


module Connected =
  Eliom_registration.Customize
    (Eliom_registration.Html)
    (Connected_param)
]}

{3 By building the HTTP frame}

For defining more sophisticated kinds of custom services, you may need
to create your own registration module via the
{!Eliom_mkreg.Make} functor.
As an example, we define a module serving
integers as text. Notice that for this particular example you can (and
should) use
{!Eliom_registration.Customize}
instead.

{@ocaml[
module Int_reg_base = struct

  type page = int

  type options = unit

  type return =
    Eliom_service.non_ocaml

  type result =
    Eliom_registration.browser_content Eliom_registration.kind

  let result_of_http_result = Eliom_registration.cast_http_result

  let send_appl_content = Eliom_service.XNever

  let send ?options ?charset ?code ?content_type ?headers content =
    let content = string_of_int content in
    let%lwt r =
      Ocsigen_senders.Text_content.result_of_content
        (content, "text/plain")
    in
    Lwt.return @@ Ocsigen_http_frame.Result.update r ()
      ~code:(match code with Some c -> c | None -> 200)
      ~charset:
        (match charset with
         | None ->  Some (Eliom_config.get_config_default_charset ())
         | _ -> charset)
      ~content_type:
        (match content_type with
         | None -> Ocsigen_http_frame.Result.content_type r
         | _ -> content_type)
      ~headers:
        (match headers with
         | None ->
           Ocsigen_http_frame.Result.headers r
         | Some headers ->
           Http_headers.with_defaults
             headers
             (Ocsigen_http_frame.Result.headers r))

end

module Int = Eliom_mkreg.Make(Int_reg_base)
]}

If your [page] type has parameters you should use
{!Eliom_mkreg.Make_poly} instead.

{1 Considerations for implementing services}

{2:lwt Implementing cooperative service handlers with Lwt}

Remember that a Web site written with Eliom is an OCaml application.
This application must be able to handle several requests at the same
time, in order to prevent a single request from making the whole
server hang.  To make this possible, Ocsigen uses {e cooperative
threads}.  The monadic style followed by
{!Lwt} makes such threads easy to use.

Below is an example of a page written in a non-cooperative way, that
has the effect of stopping the entire server for 5 seconds. No one
will be able to query the server during this period:

{%wodoc:@ class=donotrun%}
{@ocaml[
let looong =
  Eliom_registration.Html.create
    ~path:(Eliom_service.Path ["looong"])
    ~meth:(Eliom_service.Get Eliom_parameter.unit)
    (fun () () ->
       Unix.sleep 5;
       Lwt.return
         Eliom_content.Html.D.(
           html
             (head (title (txt "")) [])
             (body [h1 [txt "Ok now, you can read the page."]])
         ))
]}

To solve this problem, use a cooperative version of [Unix.sleep]:
{!Lwt_unix.sleep}:

{@ocaml[open Eliom_content.Html.D
let looong =
  Eliom_registration.Html.create
    ~path:(Eliom_service.Path ["looong"])
    ~meth:(Eliom_service.Get Eliom_parameter.unit)
    (fun () () ->
       let%lwt () = Lwt_unix.sleep 5.0 in
       Lwt.return
         Eliom_content.Html.D.(
           html
             (head (title (txt "")) [])
             (body [h1 [txt "Ok now, you can read the page."]])
         ))
]}

If you want to use, say, a database library that is not written in a
cooperative way, but which is thread-safe for preemptive threads, use
the {!Lwt_preemptive} module to detach
the computation. In the following example, we simulate the request by
making a call to {%wodoc:span class="code"%}Unix.sleep{%wodoc:end%}:

{@ocaml[
let looong2 =
  Eliom_registration.Html.create
    ~path:(Eliom_service.Path ["looong2"])
    ~meth:(Eliom_service.Get Eliom_parameter.unit)
    (fun () () ->
       let%lwt () = Lwt_preemptive.detach Unix.sleep 5 in
       Lwt.return
         Eliom_content.Html.D.(
           html
             (head (title (txt "")) [])
             (body [h1 [txt "Ok now, you can read the page."]])
         ))
]}

{2:error_handling Error handling}

{3 Exception handling}

You can catch exceptions raised during page generation in two places:

- add an exception handler to services using the [?error_handler]
  parameter of the registration functions.
- add a global exception handler using {!val
  Eliom_registration.set_exn_handler}

You can use it to catch exception {!exception
Eliom_common.Eliom_404} and generate a custom 404 page.

{@ocaml[
let () =
  Eliom_registration.set_exn_handler @@ function
  | Eliom_common.Eliom_404 ->
    Eliom_registration.Html.send ~code:404
      Eliom_content.Html.D.(
        html
          (head (title (txt "")) [])
          (body [h1 [txt "Eliom tutorial"];
                 p [txt "Page not found"]])
      )
  | Eliom_common.Eliom_Wrong_parameter ->
    Eliom_registration.Html.send
      Eliom_content.Html.D.(
        html
          (head (title (txt "")) [])
          (body [h1 [txt "Eliom tutorial"];
                 p [txt "Wrong parameters"]])
      )
  | e ->
    Lwt.fail e
]}

{3 Fallback services}

You can check whether a service was directly called or if it was used
as a fallback using the {!val
Eliom_request_info.get_link_too_old} function. In case of services
registered with a restricted scope, you can find out which state was
closed using {!val
Eliom_request_info.get_expired_service_sessions}

{3 Error in service handlers of actions}

If something wrong happens during an action, it is possible to inform
the service generating the page. For instance, you may want to display a
"wrong password" message after an aborted connection.  To transmit
this kind of information, use Eliom references (see module {!Eliom_reference}) created using scope {!Eliom_common.request_scope}. The value will be available to the service
generating the page.

Other example: creating user accounts using actions. If the
creation fails, you may want to display some message to the user, like
"password too weak" or "name already used".
