eliom-3.0.3/0000755000000000000000000000000012062377521011015 5ustar0000000000000000eliom-3.0.3/Makefile0000644000000000000000000000321512062377521012456 0ustar0000000000000000include Makefile.config ### Building .PHONY: all byte opt doc all: ${MAKE} -C src all byte: ${MAKE} -C src byte opt: ${MAKE} -C src opt odoc: $(MAKE) -C src odoc doc: $(MAKE) -C doc doc ### Testing ### .PHONY: run.local run.opt.local top run.local: tests.byte fifo ocsigenserver -c local/etc/${PROJECTNAME}.conf tests.byte: byte ${MAKE} -C tests byte run.opt.local: tests.opt fifo ocsigenserver.opt -c local/etc/${PROJECTNAME}.conf tests.opt: opt ${MAKE} -C tests opt fifo: [ -p local/var/run/${PROJECTNAME}_command ] || \ { mkfifo local/var/run/${PROJECTNAME}_command; \ chmod 660 local/var/run/${PROJECTNAME}_command; } ### Cleaning ### clean: clean.local ${MAKE} -C src clean ${MAKE} -C tests clean clean.local: -rm -f $(PROJECTNAME)-*.tar.gz distclean: clean.local ${MAKE} -C src distclean ${MAKE} -C tests distclean -${MAKE} -C doc clean -rm Makefile.config -rm -f *~ \#* .\#* ### Installation #### .PHONY: install uninstall reinstall install: $(MAKE) -C src install @echo @echo "## Run \"make doc\" and \"make install.doc\" to build and install the ocamldoc." install.byte: $(MAKE) -C src install.byte install.opt: $(MAKE) -C src install.opt uninstall: -$(MAKE) -C src uninstall reinstall: $(MAKE) -C src reinstall reinstall.byte: $(MAKE) -C src reinstall.byte reinstall.opt: $(MAKE) -C src reinstall.opt install.doc: ${MAKE} -C doc install ### .PHONY: dist VERSION := $(shell head -n 1 VERSION) dist: DARCS_REPO=$(PWD) darcs dist -d $(PROJECTNAME)-$(VERSION) ### .PHONY: depend depend: ${MAKE} -C src/syntax ${MAKE} -C src/tools ${MAKE} -C src files/META.${PROJECTNAME} depend ${MAKE} -C tests depend eliom-3.0.3/LICENSE0000644000000000000000000000041312062377521012020 0ustar0000000000000000The Ocsigen application core, and other portions of the official Ocsigen distribution not explicitly licensed otherwise, are licensed under the GNU LESSER GENERAL PUBLIC LICENSE with openssl linking exception -- see the 'COPYING' file in this directory for details. eliom-3.0.3/CHANGES0000644000000000000000000011230112062377521012006 0ustar0000000000000000 ===== 3.0.0 (2012-12-10) ===== * Language ** Generalized client values in server code ** Injections into client sections * Tools ** Added eliom-destillery for generating project scaffolds ** Support Eliom interface files (.eliomi) in eliomc, eliomdep ** eliomdep: Generate dependencies between eliom-modules ** eliomc: infer only with option -infer, drop option -noinfer ** eliomc: Basic support for -i on eliom-files ** eliom{c,dep,opt},js_of_eliom: -dump to output the intermediate code ** eliomc,js_of_eliom: always open Eliom_pervasives in eliom files * API ** Eliom_pervasives.server_function to easily access the from the client ** Get current state of a scope ** Module to access values of Eliom references in external states (Eliom_state.Ext) ** Scope names are now named scope hierarchies ** Iterate the scope hierarchy (group > session > client > request) ** Adding Eliom_parameter.(type_checker, neopt) ** Add functions to insert html5 in atom feeds ** Eliom_tools.{F,D}.html to ease creation of head-tag ** Eliom_tools.wrap_handler as an easy alernative to Eliom_registration.Customize ** Test for initial request of a client on the server * Changed server behaviour ** Eliom_state.discard_everything now also discards request state ** Don't send nodes as data when they are part of the document * Changed client behaviour ** Show progress cursor during navigation with change_page ** Improved error messages ** Fail on the client when a [server_function] or call_caml_service fails on the server * Bugfixes ** Allow % for injections directly after parentheses * Support dropped for ** Xhtml ** OCamlDuce ** Eliom_compatibility_2_1 * A myriade of bugfixes ===== 2.2.2 (2012-06-11) ===== * Fix (re-disable) generation of API doc for ocamlduce modules ===== 2.2.1 (2012-06-07) ===== Addendum to the great renaming * Eliom_cookies -> Eliom_cookie * Eliom_comet.Channels -> Eliom_comet.Channel ===== 2.2 (2012-06-02) ===== The great renaming: Make Eliom's API more coherent * Dropped Eliom_pervasives, split into ** Eliom_lib (extension of stdlib) ** Eliom_content (content creation) * Moved all Html5,Xhtml content function to Eliom_content.{Html5,Xhtml} ** {HTML5,SVG,XHTML}.M -> Eliom_content.{Html5,Svg,Xhtml}.F ** {HTML5,SVG,XHTML}.DOM -> Eliom_content.{Html5,Svg,Xhtml}.D ** Eliom_output.{Html5,Xhtml}: form functions to Eliom_content.{Html5,Xhtml} ** Eliom_client.Html5 -> Eliom_content.Html5.To_dom ** Eliom_dom -> Eliom_content.Html5.Manip ** HTML5.of_element -> Eliom_content.Html5.Of_dom.of_element * Removed default implementations in Eliom_content.{Html5,Svg,Xhtml} (was inclusion of DOM) * Rename Eliom_output to Eliom_registration * Rename Eliom_registration.Eliom_appl to Eliom_registration.App * Conform naming conventions for modules (singular, lower-case) ** XML -> Xml ** SVG -> Svg ** XML_types -> Xml_types ** XHTML_types -> Xhtml_types ** HTML5_types -> Html5_types ** Eliom_services -> Eliom_service ** Eliom_parameters -> Eliom_parameter ** Eliom_references -> Eliom_reference ** Eliom_extensions -> Eliom_extension * Approximating compatibility module for Eliom 2.1: Eliom_compatibility_2_1 ===== 2.1.1 (2012-03-20) ===== * Server: Add Eliom references of scope site * Server: Add an non-Lwt interface to volatile Eliom references * Client: Add an option to restrict usage of XHR. * Bugfixes: ** Client: More robust and portable history handling ** Client: More robust CSS preloading (change_page) ** Client: Better compatibility with Opera ===== 2.1.0 (2012-02-24) ===== * Page loading rewriten. ** We do not traverse the HTML tree anymore for retrieving links and putting events handlers, it use querySelectorAll instead. (with a fallback for older browsers) *** We do not send a sparse tree along the page anymore. *** It is faster and more resilient to ugly browser plugins ** Add css loading optimisations: It fetch the css included using @include with XHR before changing page and construct only one concatenated css. It avoids page blinking when the css change and is faster (browsers tend to render the page once per css). ** Unwrapping hand rewritten in js. ** And a lot of other optimisations. * Rework of unique nodes: ** Introduce new lightweight 'unique' node of scope request, a.k.a. DOM nodes: use it through HTML5.DOM.{div,...} or its alias HTML5.{div,...}. ** To create unique node of scope application, the function 'HTML5.M.unique ?copy' has been renamed 'HTML5.create_global_elt ?id' ** Abstract id for application node are created with "HTML.new_global_elt_id" ** Node representation now differs between the client and the server. They are unwrapped on the client to simplify the XML representation: "unique" node that have already a "Javascript DOM representation" only contains a reference to the DOM representation. * Server API Changes: ** Add Eliom_state.get_*_session_group_size to know the number of session in a session group ** Parameters of type any can't be suffixes (semantic problem) ** Add Eliom_comet.wait_timeout to notify when a client isn't active ** Allow services to setup "Cache-policy: max-age" with Text, CssText and Files modules. ** Eliom_state.discard and discard_data now accept scope request. * Client API Changes: ** add an Eliom_dom module provinding imperative function on unique HTML5.elt. (for instance {{{appendChild: ?before:'a elt -> 'b elt -> 'c elt -> unit}}}). ** HTML5.M.a_on{click,...} functions directly expect a function as parameter (XML.event_handler_of_function isn't required anymore). ** Eliom_comet: configuration option for time until timeout after the focus is lost ** Handling wrapping of int32 type. ** Onload event handlers on HTML elements are executed when they are converted to DOM (using Eliom_client.of_ functions). This allows them to be added to nodes not sent in the page. ** Calls to {{{Eliom_services.onload}}} are taken into account for services sending caml values. The handlers are executed when the call to {{{Eliom_client.caml_call_service}}} terminates. * Eliom syntax: ** Event handlers can now access the current event using the _ev variable inside {{ ... }} typed according to the attribute. ** Allow modules to be used inside client expr {{ ... }} and client_expr {{ ... }} to be used inside modules ** Add -ppopt option to eliomc * And a lot of bugfixes. ===== 2.0.2 (2001-11-30) ===== * Compatibility with js_of_ocaml-1.0.8 and tyxml-2.0.1 * API Changes: ** Eliom_output.Redirection: use 302 as default and introduce abstract nome for all the 30x HTTP code ** Eliom_output.Customize: remove unrequired Functor parameters. * Delayed URl computation: this allows relative links to be created outside of service handler. * Client: do not ignore "onload" events attached on Dom elements other than window. * Bugfixes: ** Client: do not execute change_page_uri when the middle button or any modifier key is pressed. ** Client: update correctly the location when changing page with forms. ** Server: displaying better dynlink error messages. ** Syntax extension: avoid infinite loop while parsing first-order module expression ** Eliom_parameters: fixing wrong parameter names for list of list ** Eliom_parameters: fixing bug causing stack overflow when lists parameter names were wrong ** Fixing bug with non-localized parameters and service not keeping non-attached params ** Eliom_comet: stateless channels can be registered multiple times (close #220) ===== 2.0.1 (2011-09-28) ===== * Bugfixes in eliom appl.: ** Preload css when changing page to avoid a disgraceful "flash" effect ** Use to preserve links in unique node when changing page ** Fragment in URL aren't ignored anymore ** Proper exception handling in eliomc, js_of_eliom, ... ** Display a correct URL after submitting a
===== 2.0 (2011-09-20) ===== * Bugfixes: ** Correct handling of HTTP redirections hidden by XHR ** Fix links from http to https on client application ===== 2.0-rc2 (2011-09-06) ===== * Feature: ** api history: when the api history is available, application URLs are more readable (no more {{{#!}}}) * Bugfixes: ** fix browser compatibility: it should work with Firefox (>= 3.0), Chrome (all recent version we tested), Safari 5, Opera (tested 11.51), Internet explorer (9, ie 8 almost work) ** in forms declared outside of the sp's scope, hrefs were incorrect: url calculation is now done for each request ** the function {{{Eliom_client.caml_call_service}}} was broken with some kind of services ** application cookies are now always sent ** fix incorrect sharing of type variables in the syntax extension (thanks to William Le Ferrand) ** 404 and 403 errors are not dropped by applications anymore (This fix is partial: when the page content is application content, this still does not work when an exception handler returns something else) ** Fix problems with url generation for non-attached coservices inside an application ** tail recursive marshalling and demarshalling: no more limit on the depth of data sent along the page ** Fix problems with redirection and action with `NoReload exiting the application ** Handle correctly 204 code in application (do nothing with change page, fail with caml_call_service) ** Fix escape character '>': problem with strings containing "]]>" ===== 2.0-rc1 (2011-07-15) ===== * Eliom_client.change_page type now correctly forbid to call it on non-eliom application services * Stateless implementation of comet channels * Scope and state names parameters of functions are now fused in one parameter (called scope) * Change the type of 'send' functions: forbid use of incompatible output with Eliom_output.Any together. * Change the format of page loaded by an application: all pages are sent in HTML5 format. (first answers were in HTML5 subsequent were marshalled caml tree) * XML representation is now shared by client and server: conversion to Dom nodes is made by Eliom_client.Html5.of_ functions * Remove the need for an application container: application pages are now classical HTML5 pages. * Add Eliom_pervasives.{HTML5/XML/SVG}.unique function to declare XML node handled "by reference" * Fix ocamldoc generation with Eliom_output * Eliom_appl are now stateless if no data is stored in session * Allow dynamic wrapping with values to be sent to client with caml services and comet channel * Add Eliom_react.S: server to client React signals * Add lazy site values: lazy evaluated one time per site * Option to compile without preemptive threads * And lots of simplifications and bug fixes ===== 1.91 (2011-04-08) ===== * Split the ocsigen package in three : tyxml, ocsigenserver and eliom * Rename into eliom * It's now possible to set a //priority// on services registered on the same path, to choose in which order Eliom will try them. * New implementation of internal application forms with formData when available * New build system for eliom application * No type restriction in value sent from the server to the client ===== 1.90 ===== * New module {{{Eliom_client}}} for client/server Eliom programs using js_of_ocaml. * Eliom client: calling a service from client side code. * Eliom: Services taking Caml values as parameters. * Eliom: services sending Caml values. * Eliom: new implementation of forms and links to be compatible with client side programs. * Eliom: sp parameter has been removed (now using Lwt's thread storage) * Eliom: {{{Eliom_predefmod}}} renamed {{{Eliom_output}}} * Eliom: New module {{{Eliom_output.Customize}}} to create your own register functions from another registration module * Eliom: Module {{{Eliom_sessions}}} replaced by {{{Eliom_state}}}, {{{Eliom_request_info}}} and {{{Eliom_config}}}. * Eliom: new implementation of user cookies. * Eliom: Client process cookies. Like usual browser cookies but for one client side process. * Eliom: Client process server side state data. Like session data but for one client side process. * Eliom: Client process session services. * Eliom: Session group data. Like session data but for all sessions in a session group. * Eliom: Session group services. * Eliom: session replaced by a more general concept of "server side state". States have a scope: client process, session or group of sessions. * Eliom: session tables and request cache now deprecated, replaced by //Eliom references// * Eliom client: Possible to call another service without stopping the client side program, with bookmark support and back button support. * New extension Comet to allow server -> client communication. * Eliom: client/server communication channels. * Eliom: client/server reactive programming using React. * Eliom client: syntax extension for separating client and server code. * Eliom: New module Eliom_output.Eliom_appl for registering pages that belong to the same Eliom application. * Eliom client: syntax extension and wrapping/unwrapping mechanism to access server side values in client side code. * Eliom client: Relinking the DOM on client side after loading a (portion of) page. This allows nodes created on server side to be directly used in client side code. * Eliom client: XHR redirections for Eliom applications. * Eliom: safe unmarshaling of caml values sent by client side programs * Xhtml: Xhtml5 support * Atom module and Pubsubhubbub * OpenID support * SVG module * Documentation: New tutorial * Documentation: New Eliom manual * //and many other things ...// ===== 1.3.4 ===== * Eliom: Now supporting list of lists in service parameters ===== 1.3.3 (2010-06-13) ===== * Eliom: Fix some typos in Eliom's tutorial stylesheet * Server: Fix usage of {{{accept_n}}} to avoid file descriptor leakage * XHTML: Adding missing elements and attributes in XHTML.M * Cleaning Ocsigen_cache * Eliom: Fixing actions with uploads ===== 1.3.2 (2010-04-30) ===== * Add dummy findlib packages ocsigen.xhtml*, that depend on ocsigen_xhtml*, for compatibility with previous versions. These packages might be removed in a future (major) release. * Port to Lwt 2.1.0 ===== 1.3.1 (2010-04-23) ===== * Split out ocsigen_xhtml findlib package * Configuration file: when no protocol is specified in {{{}}}, listen on IPv6 (if possible) and IPv4 (always) ===== 1.3.0 (2010-01-22) ===== * Server: Each request now has a polymorphic data table (called //request cache//), where you can store the data you want to keep during the whole page generation. * Eliom: actions now return {{{()}}}. Use the request cache to send information to fallbacks. * Eliom: CSRF-safe coservices * Eliom: the number of sessions without group by IP address is now limited * Eliom: efficient implementation of limitation of sessions by groups (or now IP) for large limits * Eliom: the number of anonymous coservices by session is now limited * Eliom: the number of anonymous coservices without session by IP address is now limited * Eliom: now possible to unregister services * Eliom: New (very) experimental module {{{Eliom_obrowser}}} to use Eliom with Obrowser * Eliom: Now possible to have constant parts in suffixes to allow URLS like {{{/param1/something/param2}}} * Eliom: services with optional suffixes * Eliom: form towards a service with suffix: it is now possible to choose whether you want the redirection towards the suffix form or not * Eliom: lists and sets in suffixes * Eliom: Now possible to create services sending custom HTTP header values or customizing the content-type * Eliom: New notion: "Non localized parameters". Can be sent to any service. * Eliom: changing the order of parameters for user type form widgets * Eliom: changing the order of parameters for user type form widgets * Eliom: Eliom_tools.menu and hierarchical_menu now compare the URL strings (was: compare the service) * Eliom: textarea now take a string (was pcdata) * Eliom: The type of the iterator for lists in parameters has changed * Eliom: New options in configuration file to set session timeouts * Server: now possible to give the config file name to reload server command * Server: now possible to do a "graceful shutdown" of the server using the "shutdown" server command * Server: now possible to add custom commands for the command pipe * Server: EXPERIMENTAL now possible to observe HTTP headers before sending the result * Xhtmlpp: the parsing now fails if a quotation for an Xhtml element contains superfluous elements. (This can cause the parsing of previously incorrect code to fail) * Staticmod/Eliom: attempting to access a file whose name contains a NULL character will result in a 403. * Server: HTTP headers containing newlines are now properly escaped. * Server: correct missing xmlns in Xhtml DTD * Server: now send last-modified and etag headers when returning a 403 * Server: Now accepting several requests at a time (as explained in "Accept()able strategies" by Tim Brecht & all) * Rewritemod: more rewriting possibilities (still very basic) * Eliom menus are now more robust when finding which item is active * Fixed handling of incorrectly-sized multipart requests. Thanks to Mauricio Fernandez for noticing the bug * Upload directory and maximum file size can now be configured on a per-site basis * Renamed the field of Ocsigen_http_frame.t * Javascript events support in Xhtml.M ; Thanks to john @ 5070.info for the patch ===== 1.2.2 (2009-10-17) ===== * Add react and lwt.unix to the list of built-in findlib packages ===== 1.2.1 (2009-09-26) ===== * Lwt 2.0 compatibility: ** Adapt to Lwt.t/Lwt.u splitting ** fix Makefile to deal with lwt.unix findlib package * Fix ocsipersist-dbm Makefile * Fix for pcre-ocaml 6.0.0 * Fix typo regarding --stubdir in configure script ===== 1.2.0 (2009-03-25) ===== * Native code version now compiled by default * Now possible to link extensions and Eliom modules statically, for example to use a native code server on platforms where native dynlink is not supported * Server: Partial requests implemented (Range HTTP header) * Build C stubs into a shared library so that bytecode executables may be not linked in custom mode; new {{{--stubdir}}} option in {{{configure}}} script * Eliom: non-attached services now called "named non-attached coservices" and created using {{{Eliom_services.new_coservice'}}} with the optional {{{name}}} parameter * Eliom: now possible to create named attached coservices using the optional {{{name}}} parameter * Eliom: now possible to write libraries for Eliom sites, loaded inside {{{}}}, but not generating any page * Eliom and server: EXPERIMENTAL now possible to make extensions that can use Eliom's data * XHTML.M's pretty printer: now possible to set the DOCTYPE manually * Eliom: now possible to set manually the DOCTYPE when registering an XHTML.M service * Redirectmod and Revproxy: now possible to do more complex rewriting * Accesscontrol: add support for {{{}}} and {{{}}} conditions * Config file: {{{aliases}}} attribute now called {{{hostfilter}}} * Revproxy and Redirectmod: now possible to filter on server, port and protocol * New extension extendconfiguration to allow dynamic changes in the configuration (mimetypes, charsets, followsymnlink, directory listing, ...) * Server: new module {{{LocalFiles}}} factoring the common parts for sending static files (with Eliom and staticmod for example), while checking that the files can safely be sent. * Now possible to use XHTML pretty printers without Ocsigen, using the {{{xhtmlpretty.cma}}} library * Add {{{Ocsigen_lib.register_exn_printer}}}, better error messages * Now possible to use the same configuration file in native code and in bytecode (.cmo/.cma filenames are internally translated to .cmxs) * Signature of Ocsigen_extensions.register_extension is now more complete and more lightweight * Userconf: the options set in the local .userconf file are kept in the enclosing {{{}}} tag * Server: possibility to ignore or to supply an alternative command-line * Ocsigen_http_client: timeout when the distant server does not exists * OCaml versions < 3.10 are not supported anymore * Extensions are now much more strict w.r.t. the syntax of configuration files * Staticmod: accessing a directory for which indexing is disallowed returns an error 404 (instead of a 403 previously) ===== 1.1.0 (2008-07-15) ===== * Lwt removed (now distributed separately) * {{{XHTML.M}}} pretty printer: fixing pretty printing of empty tags (for browser compatibility) * Eliom_duce: New pretty printer for XHTML fixing pretty printing of empty tags * Eliom: secure sessions, secure services, (absolute) https links/forms, and using secure cookies * Eliom: Adding special "void action", without any parameters * Eliom: {{{Eliom_predefmod.Redirections}}} now called {{{Eliom_predefmod.String_redirection}}}, and adding new module {{{Eliom_predefmod.Redirection}}} that use GET services without parameters as data type. * Eliom and XHTML.M: Small changes of types in interfaces * Eliom: New session ID generator * Eliom: Adding types {{{int32}}} and {{{int64}}} for parameters and forms * Eliom: Adding functions {{{lwt_get_form}}} and {{{lwt_post_form}}} for creating forms using cooperative functions * Eliom and Staticmod: now possible to give GET parameters to static pages * Eliom: Bugfix in Makefiles for native code version * Eliom forms: Adding missing types in interfaces * Eliom_tools: current page is now optional in menus * Userconf and Eliom: there was a bug when loading both Eliom and Userconf together * Reverse Proxy: Now sending content length when available * Web server: The default content-type is now {{{application/octet-stream}}} * Creating and installing a cma file for all Ocsigen libraries not installed elsewhere * Ocsipersist-dbm: fixing bug when removing data * Deflatemod: fixing memory leak * And small bugfixes in XHTML.M, Eliom, ... ===== 1.0.0 (2008-04-01) ===== * Config file: findlib integration * Eliom and Ocsigen: changing namespace convention for modules * Access control: simplification of config file syntax * Eliom: Allowing (module dependent) parameters for registration functions * New xhtml output without pretty printing * Web server: Bugfix in HTTP/1.0 with keep-alive * Reverse proxy: Bugfix GET parameters were wrong * Reverse proxy: Bugfix memory consumption when the connection was closed by client ===== 0.99.5 (2008-01-11) ===== * Revproxy: pipelining of requests * Access control: simplification, generalization of filters and syntax changes in config file * Eliom: EXPERIMENTAL session groups * Eliom: non-attached services * Eliomduce: new functor {{{SubXhtml}}} for creating registration modules * Eliomduce: new module Eliomducetools with same features as Eliomtools, but for Eliomduce * Web server: now possible to split the configuration file into several files using the {{{}}} option. * Web server: now possible to have {{{}}} option inside another {{{}}} in configuration files, and the the first one is optional * Web server: EXPERIMENTAL user configuration files, with restricted possibilities (for security reasons) * Web server: IPv6 support * Deflatemod: now possible to filter on file extensions * Eliom: new option to keep GET non-attached parameters or not when doing a POST form towards a non-attached coservice. * Eliom: bugfix path of session cookies * Eliom: bugfix POST non-attached coservices called from a page with non-attached GET parameters were not found. * Lwt: now catching exceptions raised after timeouts * Cgimod: bufixes in path handling * Web server: bugfix - some files were not closed ===== 0.99.4 (2007-11-21) ===== * Ocsigen: Changes in the extension mechanism. The extensions are not tried in the order in which they are loaded any more, but in the order in which the options are specified for each site in the configuration file. * New experimental extension: access control * A few small enhancements ** Eliom: internal cookie management rewritten (+ bugfix) ** Eliom: Small changes in function names ** Eliom: now retry all extensions after actions (not only Eliom) ** Eliom: cleaning {{{Eliommod}}} interface ** Ocsigen server: Internal changes in server (removing "send" functions, debug messages lazier) ** Lwt: Adding a few functions in {{{Lwt_chan}}} interface ** Staticmod: Allowing default error pages for HTTP errors to be customized ** Ocsipersist (dbm and sqlite): better handling of database errors ** XHTML: New pretty printer for xhtml using streams (up to 15% speedup on requests) ** XHTML: Allowing any value for {{{}}} rel attribute (for example {{{shortcut icon}}}). ===== 0.99.3 (2007-11-07) ===== * Ocsigen: New module Deflatemod to compress data before sending to the browser. * Ocsigen: EXPERIMENTAL - New module Revproxy (reverse proxy). * Eliom: New session mechanism, with the ability to name the sessions and thus have several sessions for the same site. * Eliom: Now possible to have one site with session inside a subdirectory of another one. * Lwt: New module {{{Lwt_timeout}}} to implement timeouts, new module {{{Lwt_chan}}}, new module {{{Lwt_mutex}}}, new function {{{Lwt_unix.abort}}} to make all threads waiting on a file descriptor abort with an exception. * Ocsigen: New implementation of lots of Web server internals. Better management of Ocsigen's streams, file descriptors, exceptions, timeouts ... * A lot of enhancements and bug fixes: ** Eliom: Single {{{}}} in forms, by Stéphane Dalmas * EXPERIMENTAL: The Web server is now extensible. It means that you can add modules (like Apache modules) for generating pages, filters of requests, extensions of config files. For now there are two modules, one for static pages, and one for dynamic pages. The only changes for users is that they need to dynlink staticmod.cmo and ocsigenmod.cma from the configuration file. The syntax of config file for modules and staticdir also changed. * It is now possible to specify the encoding of characters for each sub-site. * Now usable with Ocamlnet 2.2 or 1.1.2. * EXPERIMENTAL: If OCamlDuce is installed on your system, you can now use it to do the type-checking of your pages (see the documentation). Warning: This is broken with OCamlDuce 3.09.2 patch level 2. You need at least OCamlDuce 3.09.3 patch level 1. * Removing Ocsimore from the default distribution. That version of Ocsimore is not supported anymore. Ocsimore has been rewritten from scratch by Piero Furiesi. ===== 0.5.1 (2006-12-14) ===== * Bugfix Konqueror: Multipart forms with now work correctly with Konqueror * Bugfix Internet Explorer: getting around a bug of Internet Explorer while displaying page * Bugfix NetBSD: Makefile * Bugfix Debian for HPPA, Mips, Alpha: Makefile * Bugfix: preemptive.cmi was missing in the installation directory * Adding manpage (S. Mimram) * Adding logrotate configuration * Daemon mode: adding setsid and redirect stdout/stderr to /dev/null. Closing stdin. ===== 0.5.0 (2006-11-23) ===== * HTTP 1.1 improved (HEAD, ETag, keep-alive implemented, If-Modified-Since, ...) * HTTPS support * Pipelining of requests * Server can listen on several ports * Multiple servers: you can now define several servers in the config file. * Virtual hosts: filtering on hostnames/ports (with wildcards) * Asynchronous file upload with multipart support * Large file transfer improved. * MIME types are now parsed from a file * Changes in the syntax of config file * Accessors for server parameters. Use ({{{get_user_agent sp}}}) instead of {{{sp.user_agent}}}. * Page generation is now using {{{Lwt}}} cooperative threads, to make it possible the generation of several pages at the same time. Practically, add {{{Lwt.return}}} before the page you want to send, and use cooperative input/output functions. * Typing errors of parameters are now catchable. * {{{static_dir}}} is now a function * Most of global references have been removed. You now need to give sp as parameter to {{{register_for_session}}}, {{{static_dir}}}, {{{close_session}}}, etc. * Links and forms now take {{{server_params}}} instead of {{{current_url}}} ({{{sp}}} is shorter than {{{sp.current_url}}}) * EXPERIMENTAL: Use of preemptive threads for non cooperative libraries ({{{detach}}} function). * EXPERIMENTAL: The {{{Ocsigen}}} module now contains a functor {{{Make}}} to allows the use of several ways of generating XHTML. The default way (using {{{XHTML.M}}} or the syntax extension) can be used by doing {{{open Ocsigen.Xhtml}}}. There is also an untyped xhtml generation module called {{{Ocsigen.Text}}}. * EXPERIMENTAL: extension of forms. * Reorganisation of the code * Bugfixes in makefiles * Bugfix: escaping of strings in xhtml with syntax extension (thanks to David Mentre) ===== 0.4.0 (2006-06-06) ===== * Full reimplementation of the core using Generalized Algebraic Data Types, * {{{_int}}}, {{{_string}}}, etc. are now called {{{int}}}, {{{string}}}, etc. * The type {{{_http_params}}} is now called {{{server_params}}}, * Services functions now all take 3 parameters, one for server parameters, one for GET parameters, and one for POST parameters. Note that {{{**}}} is used to create **pairs** and not tuples. * The {{{a}}} and {{{post_form}}} functions now take a fix number of parameters, correponding to GET parameters. * //URLs// are now called //services//, * //state URLs// are now called //auxiliary services//, * {{{register_post_url}}} does not exist anymore. use {{{register_service}}} instead (idem for all other {{{register_post_*}}} functions). * Changes for prefix URLs * Small changes for actions * EXPERIMENTAL: sums, bool and list types for services and forms * small bugfixes ===== 0.3.27 (2006-04-27) ===== * Change the way to get server parameters ===== 0.3.26 ===== * Load unsafe modules * Other small changes ===== 0.3.25-2 (2006-02-24) ===== * Small bugfix for 64 bits processors * bugfix for static files * {{{action_link}}} is now called {{{action_a}}} ===== 0.3.24 (2006-02-07) ===== * More documentation * Change types {{{internal_url}}} and {{{external_service}}} to use polymorphic variants ===== 0.3.23 (2006-02-07) ===== * Better handling of static files and "403 Forbidden" message eliom-3.0.3/README0000644000000000000000000000724312062377521011703 0ustar0000000000000000Eliom ------------------------------------------------------------------ Requirements: ============= Compilers: * ocaml and camlp4 (need version >= 3.12.1) * js_of_ocaml (need version >= 1.3.2, with deriving-ocsigen support) * a C compiler (tested with gcc-4.4.5) Libraries: * findlib * ocsigenserver (need version >= 2.2) * tyxml (need version >= 2.2) * react (tested with 0.9.3) * deriving-ocsigen (need version >= 0.3-ocsigen) See: https://github.com/hnrgrgr/deriving * ocamlssl (tested with 0.4.6) * calendar (tested with 2.03.1) If OCaml 3.12 and the needed libraries (findlib/react/lwt...) are not installed on your computer and not available on your favorite linux distribution, you may use the Ocsigen bunble GODI to install them automatically, see: http://ocsigen.org/install#bundle http://godi.camlcity.org/godi/index.html To run the native code version of ocsigen server, you may need to generate cmxs files for the libraries you need, if they are not included in your distribution, see: http://ocsigen.org/ocsigenserver/manual/misc ------------------------------------------------------------------ Build instructions: =================== * run "sh configure [options]" to generate 'Makefile.config' - For the full list of options, run "sh configure --help". * verify that 'Makefile.config' suits to your needs. * run "make" to compile * run "make install" as root to install * [optional] run "make doc" to build the ocamldoc * [optional] run "make install.doc" as root to install the ocamldoc * run "make uninstall" to uninstall (almost) everything ------------------------------------------------------------------ Local testings: =============== * run "make run.local" of "make run.opt.local" in the ocsigen source directory. * open http://localhost:8080/miniwiki in your browser * if it does not work, look at the logs (see 'local/var/log/' in the ocsgigen source directory) or run ocsigen with options -v or -V (verbose and debug mode). * sources for this example may be found in the directory 'examples/miniwiki'. For a full tutorial, see: http://ocsigen.org/tutorial For testsuite, see: http://localhost:8080/ ------------------------------------------------------------------ Authors: ======== Vincent Balat (project leader, Web server, Ocsigenmod, Eliom, Eliom client, Staticmod, XHTML syntax extension, documentation, Ocsimore, extension mechanism, Ocsidbm, Ocsipersist with DBM, ...) Jérôme Vouillon (Lwt, Web server, js_of_ocaml, O'Closure, ...) Boris Yakobowski (Ocsimore, module Extendconfiguration, Ocsigen server...) Benjamin Canou (O'Browser) Jérémie Dimino (Lwt) Raphaël Proust (Ocsforge, Eliom client, Comet) Stéphane Glondu (Configuration file, Findlib integration, access control, HTTP authentication, Debian package, ...) Gabriel Kerneis (XHTML syntax extension for OCaml 3.10, Ocsipersist with SQLite, CGI module, forms in Eliom, deflatemod, ...) Denis Berthod (HTTP protocol, Web server) Grégoire Henry (safe unmarshalling of client data) Pierre Chambart (Comet) Jaap Boender (Ocsimore, NetBSD and Godi packages) Gabriel Scherer (Macaque) Gabriel Cardoso (O'Closure) Jean-Henri Granarolo (Ocsforge) Simon Castellan (HTML5, OpenID, SVG) Piero Furiesi (Ocsimore) Thorsten Ohl (most of the functions generating XHTML (xML and xHTML modules)) Mauricio Fernandez (Xhtmlcompact, static linking of extensions and Eliom modules) Nataliya Guts (Web server, HTTPS) Archibald Pontier (Atom, Pubsubhubbub) Jérôme Velleine (CGI module) Charles Oran (O'Closure) Pierre Clairambault (Lwt_lib, Gentoo package, configure script, ...) Cécile Herbelin (HTML5, Benchmarks) eliom-3.0.3/COPYING0000644000000000000000000006543712062377521012067 0ustar0000000000000000This program is released under the LGPL version 2.1 (see the text below) with the additional exemption that compiling, linking, and/or using OpenSSL is allowed. As a special exception to the GNU Library General Public License, you may also link, statically or dynamically, a "work that uses the Library" with a publicly distributed version of the Library to produce an executable file containing portions of the Library, and distribute that executable file under terms of your choice, without any of the additional requirements listed in clause 6 of the GNU Library General Public License. By "a publicly distributed version of the Library", we mean either the unmodified Library, or a modified version of the Library that is distributed under the conditions defined in clause 3 of the GNU Library General Public License. This exception does not however invalidate any other reasons why the executable file might be covered by the GNU Library General Public License. GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. , 1 April 1990 Ty Coon, President of Vice That's all there is to it! eliom-3.0.3/Makefile.options0000644000000000000000000000240112062377521014144 0ustar0000000000000000 BYTEDBG := OPTDBG := THREAD := ifeq "$(DEBUG)" "YES" BYTEDBG += -g OPTDBG += -g endif ifeq "$(ANNOT)" "YES" BYTEDBG += -dtypes OPTDBG += -dtypes endif ifeq "$(PROFILING)" "YES" BYTEDBG :=p ${BYTEDBG} OPTDBG += -p endif ifeq "$(PREEMPTIVE)" "YES" THREAD += -thread endif SERVERDIR := ${TEMPROOT}${LIBDIR}/${PROJECTNAME}/server CLIENTDIR := ${TEMPROOT}${LIBDIR}/${PROJECTNAME}/client SYNTAXDIR := ${TEMPROOT}${LIBDIR}/${PROJECTNAME}/syntax SERVER_PACKAGE := ocsigenserver \ ocsigenserver.ext \ js_of_ocaml.deriving \ react \ calendar \ tyxml \ lwt.react \ cryptokit \ SERVER_SYNTAX := js_of_ocaml.deriving.syntax \ lwt.syntax \ tyxml.syntax \ CLIENT_PACKAGE := ocsigenserver.cookies \ ocsigenserver.polytables \ ocsigenserver.baselib.base \ js_of_ocaml \ js_of_ocaml.deriving \ lwt.react \ tyxml.functor \ CLIENT_SYNTAX := lwt.syntax \ js_of_ocaml.syntax \ js_of_ocaml.deriving.syntax \ tyxml.syntax \ eliom-3.0.3/VERSION0000644000000000000000000000000612062377521012061 0ustar00000000000000003.0.3 eliom-3.0.3/boring0000644000000000000000000000102112062377521012212 0ustar0000000000000000~$ \.cmi$ \.cmx$ \.cmo$ \.cma$ \.cmxs$ \.cmxa$ \.o$ \.a$ \.so$ \.sw[po]$ \.odoc$ ^Makefile.config$ ^baselib/ocsigen_config.ml$ ^doc/client/api-html ^doc/server/api-html ^local/etc/eliom.conf ^local/var/lib/miniwiki/* ^local/var/lib/ocsidb ^local/var/log/ ^local/var/www/tests/eliom_testsuite.js$ ^src/files/META$ ^src/files/META.eliom$ ^src/tools/eliomc$ ^src/tools/eliomcp$ ^src/tools/eliomdep$ ^src/tools/eliomopt$ ^src/tools/js_of_eliom$ \.type_mli$ \.deps$ ^tests/_client/ ^tests/_server/ ^doc/client/__tmp ^doc/server/__tmp eliom-3.0.3/configure0000644000000000000000000002475312062377521012734 0ustar0000000000000000#! /bin/sh # Adapted from the ocamlnet configure script. ####################################################################### # Helpers: test_binary () { # $1: the name of the binary echo -n "Checking for $1 ... " if which "$1" >/dev/null 2>/dev/null; then echo "found" return 0 else echo "not found" return 1 fi } fail_binary () { echo echo "Required command '$1' not found!" [ -z "$2" ] || echo "===> $2" exit 1 } check_binary () { # $1: the name of the binary # $2: an URL if test_binary "$1"; then return else fail_binary "$1" "$2" fi } test_library () { # $1: the name of the library (findlib) echo -n "Checking for $1 ... " if ocamlfind query $1 >/dev/null 2>/dev/null; then echo "found" return 0 else echo "not found" return 1 fi } fail_library () { echo echo "Required library $1 not found!" [ -z "$2" ] || echo "===> $2" exit 1 } check_library () { # $1: the name of the library (findlib) # $2: an URL if test_library "$1"; then return else fail_library "$1" "$2" fi } ####################################################################### # Defaults #--- Options --- # value 0: off # value 1: on # defaults: set_defaults () { name="eliom" enable_natdynlink=1 enable_debug=0 enable_annot=0 with_preempt=1 root="" temproot="" prefix="/usr/local" bindir="" mandir="" libdir="" docdir="" datadir="" } set_defaults my_pwd=$(dirname $0) version=$(head -n 1 $my_pwd/VERSION) full_pwd=$(pwd) ######################################################################## ## Option parsing # # ehelp_debug="Enable/disable debug output" ehelp_annot="Enable/disable .annot files" ehelp_natdynlink="Enable/disable nativecode dynamic linking" ## Which options exist? eoptions for enable/disable, woptions for with/without: eoptions="debug annot natdynlink" woptions="preempt" print_options () { for opt in $eoptions; do e="o=\$enable_$opt" eval "$e" uopt=$(echo $opt | sed -e 's/_/-/g') if [ $o -gt 0 ]; then echo " --enable-$uopt" else echo " --disable-$uopt" fi done for opt in $woptions; do e="o=\$with_$opt" eval "$e" uopt=$(echo $opt | sed -e 's/_/-/g') if [ $o -gt 0 ]; then echo " --with-$uopt" else echo " --without-$uopt" fi done case "$bindir" in "") bindir2="/bin";; *) bindir2=$bindir;; esac case "$mandir" in "") mandir2="/man";; *) mandir2=$mandir;; esac case "$libdir" in "") libdir2="\$(shell \${OCAMLFIND} printconf destdir)";; *) libdir2=$libdir;; esac case "$docdir" in "") docdir2="/share/doc/\$(PROJECTNAME)";; *) docdir2=$docdir;; esac case "$datadir" in "") datadir2="/share/\$(PROJECTNAME)";; *) datadir2=$datadir;; esac echo " --name $name" echo " --root $root" echo " --temproot $temproot" echo " --prefix $prefix" echo " --bindir $bindir2" echo " --mandir $mandir2" echo " --libdir $libdir2" echo " --docdir $docdir2" echo " --datadir $datadir2" } usage () { set_defaults cat <<_EOF_ >&2 usage: ./configure [ options ] --enable-debug, --disable-debug Enable/disable debug output --enable-annot, --disable-annot Enable/disable .annot files generation --enable-natdynlink, --disable-natdynlink Enable/disable nativecode dynamic linking --with-preempt, --without-preempt compile with preemptive threads support. --name=NAME The name of the library and ocamlfind package. --root=DIR Root directory to install the package, every other options are relatives to this path. (usually /) --temproot=DIR Temporary root directory to install the package (usually always "" but for package makers) --prefix=DIR Subdirectory where to install binaries and libs (usually /usr or /usr/local) --bindir=DIR Install binaries into this directory --mandir=DIR Install manpages into this directory --libdir=DIR Common directory for Ocsigen server's libraries --docdir=DIR Install documentation in this directory --datadir=DIR Install additional data files in this directory Defaults are: _EOF_ print_options >&2 exit 1 } check_eopt () { for x in $eoptions; do if [ "$x" = "$1" ]; then return 0 fi done echo "Unknown option: $1" >&2 exit 1 } check_wopt () { for x in $woptions; do if [ "$x" = "$1" ]; then return 0 fi done echo "Unknown option: $1" >&2 exit 1 } echo "Welcome to Eliom version $version" >&2 while [ "$#" -gt 0 ]; do case "$1" in --enable-*) opt=$(echo "$1" | sed -e 's/--enable-//' -e 's/-/_/g') check_eopt "$opt" eval "enable_$opt=2" shift ;; --disable-*) opt=$(echo "$1" | sed -e 's/--disable-//' -e 's/-/_/g') check_eopt "$opt" eval "enable_$opt=-1" shift ;; --with-*) opt=$(echo "$1" | sed -e 's/--with-//' -e 's/-/_/g') check_wopt "$opt" eval "with_$opt=2" shift ;; --without-*) opt=$(echo "$1" | sed -e 's/--without-//' -e 's/-/_/g') check_wopt "$opt" eval "with_$opt=-1" shift ;; --root) root="$2" shift shift ;; --temproot) temproot="$2" shift shift ;; --prefix) prefix="$2" shift shift ;; --name) name="$2" shift shift ;; --bindir) bindir="$2" shift shift ;; --mandir) mandir="$2" shift shift ;; --libdir) libdir="$2" shift shift ;; --docdir) docdir="$2" shift shift ;; --datadir) datadir="$2" shift shift ;; *) echo "Unknown option: $1" >&2 usage esac done case "$bindir" in "") bindir="$prefix/bin";; esac case "$mandir" in "") mandir="$prefix/man";; esac case "$libdir" in "") libdir="\$(shell \${OCAMLFIND} printconf destdir)";; *) libdir=$root$libdir esac case "$docdir" in "") docdir="$prefix/share/doc/\$(PROJECTNAME)";; esac case "$datadir" in "") datadir="$prefix/share/\$(PROJECTNAME)";; esac check_binary ocamlc "See: http://www.ocaml.org/" check_ocamlversion () { echo -n "Checking for OCaml version... " version=$(ocamlc -version) echo $version n1=$(echo $version | sed 's/^\([0-9][0-9]*\)\..*$/\1/') n2=$(echo $version | sed 's/^[0-9][0-9]*\.\([0-9][0-9]*\)\..*$/\1/') # n3=`echo $version | sed 's/^[0-9][0-9]*\.[0-9][0-9]*\.\([0-9][0-9]*\)\..*$/\1/'` if [ $n1 -eq 3 ] && [ $n2 -lt 12 ]; then echo; echo "OCaml >= 3.12 is required. Aborting."; exit 1; fi } check_ocsigenserverversion () { echo -n "Checking for ocsigenserver version... " version=$(ocamlfind query ocsigenserver -l | grep version: | awk '{ print $2 }') echo $version n1=$(echo $version | sed 's/^\([0-9][0-9]*\)\..*$/\1/') n2=$(echo $version | sed 's/^[0-9][0-9]*\.\([0-9][0-9]*\).*$/\1/') # n3=`echo $version | sed 's/^[0-9][0-9]*\.[0-9][0-9]*\.\([0-9][0-9]*\)\..*$/\1/'` if [ $n1 -eq 1 ] && [ $n2 -lt 91 ]; then echo; echo "Ocsigenserver >= 1.91 is required. Aborting."; exit 1; fi } check_ocamlversion check_binary ocamlfind "See: http://projects.camlcity.org/projects/findlib.html" check_library ocsigenserver "See: http://ocsigen.org/ocsigenserver/" check_ocsigenserverversion check_library deriving-ocsigen "See: https://github.com/hnrgrgr/deriving" check_binary js_of_ocaml "See: http://ocsigen.org/js_of_ocaml" check_library js_of_ocaml.deriving "Missing support for deriving-ocsigen in js_of_ocaml" check_library react "See: http://erratique.ch/software/react" check_library lwt "See: http://ocsigen.org/lwt" check_library lwt.unix "Missing support for 'unix' in lwt." check_library lwt.react "Missing support for 'react' in lwt." check_library calendar "See: http://calendar.forge.ocamlcore.org/" check_library cryptokit "See: http://pauillac.inria.fr/~xleroy/software.html#cryptokit" ###################################################################### # Summary echo echo "Effective options:" print_options echo #Convert 0/1 values to YES/NO if [ $enable_debug -gt 0 ] ; then enable_debug="YES" else enable_debug="NO" fi if [ $enable_annot -gt 0 ] ; then enable_annot="YES" else enable_annot="NO" fi if [ $enable_natdynlink -gt 0 ] ; then enable_natdynlink="YES" else enable_natdynlink="NO" fi if [ $with_preempt -gt 0 ] ; then with_preempt="YES" else with_preempt="NO" fi ###################################################################### # Write Makefile.conf echo "Writing Makefile.config" cat <<_EOF_ > $my_pwd/Makefile.config # The name of the library, ocamlfind package, etc. PROJECTNAME := $name #### External binaries #### OCAMLFIND := ocamlfind OCAMLMKLIB := ocamlmklib JS_OF_OCAML := js_of_ocaml CC := gcc INSTALL := install ### Options ### # Do you want preemptive threads ? YES/NO PREEMPTIVE:=$with_preempt # Do you want to use dynamic linking for native code? YES/NO NATDYNLINK:=$enable_natdynlink # Do you want debugging information (-g) ? YES/NO DEBUG:=$enable_debug # Do you want annot files (-dtypes) ? YES/NO ANNOT:=$enable_annot # Profiling (always put NO here - but if you want to debug eliom): PROFILING:=NO ### Paths ### # Temporary root directory to install the package (usually always "" but for package makers) TEMPROOT = $temproot # The directory for eliom compiler (binary): BINDIR := $root$bindir # Where to install Eliom tools manpages: MANDIR := $root$mandir # Where to install Eliom libraries: LIBDIR := $libdir # Where to put Eliom documentation: DOCDIR := $root$docdir # Where to install additional data: DATADIR := $datadir # The source directory (needed for local testing) SRC := $full_pwd include \$(SRC)/Makefile.options _EOF_ ###################################################################### # Finish echo echo echo "Please check Makefile.config." echo echo "You can now compile Eliom by invoking:" echo echo " make" echo " make doc" echo echo "You may want to test the extension before installation:" echo echo " make run.local" echo " make run.opt.local" echo echo "Finally, if you want system-wide install, (become root if needed and) do" echo echo " make install" echo " make install.doc" echo eliom-3.0.3/predist0000644000000000000000000000027712062377521012420 0ustar0000000000000000#!/bin/sh chmod a+rx configure if [ -d "$DARCS_REPO" ]; then darcs changes --last 100 --repodir "$DARCS_REPO" > CHANGES.darcs fi touch src/server/.depend src/client/.depend tests/.depend eliom-3.0.3/tests/0000755000000000000000000000000012062377521012157 5ustar0000000000000000eliom-3.0.3/tests/tutomake.sh0000644000000000000000000000052012062377521014341 0ustar0000000000000000#!/bin/sh sed "s/>>/~>>/g" | sed "s/%%/>>/g" | sed "s/%:xmllist>/g' eliom-3.0.3/tests/eliom_testsuite_global.eliom0000644000000000000000000000035612062377521017750 0ustar0000000000000000 let eref : string option Eliom_reference.eref = Eliom_reference.eref ~scope:Eliom_common.site_scope None let eref' : string option Eliom_reference.eref = Eliom_reference.eref ~scope:Eliom_common.site_scope ~persistent:"eref2" None eliom-3.0.3/tests/Makefile0000644000000000000000000001117512062377521013624 0ustar0000000000000000include ../Makefile.config ## Use local files ## (tests do not require global installation of Eliom) export OCAMLPATH := ${SRC}/src/files:${OCAMLPATH} export PATH := ${SRC}/src/tools:${PATH} export ELIOM_DATA_DIR := ${SRC}/src/files ifeq "${DEBUG}" "YES" DEBUG_OPTION := -g else DEBUG_OPTION := endif ELIOMC := eliomc -I ${SRC}/src/server/extensions ${DEBUG_OPTION} ELIOMOPT := eliomopt -I ${SRC}/src/server/extensions ${DEBUG_OPTION} ELIOMDEP := eliomdep -I ${SRC}/src/server/extensions JS_OF_ELIOM := js_of_eliom ${DEBUG_OPTION} ifeq "${NATDYNLINK}" "YES" all: byte opt else all: byte endif #### Main site : eliom_testsuite #### SERVER_PACKAGE := CLIENT_PACKAGE := SERVER_FILES := eliom_testsuite_base.eliom \ eliom_testsuite1.ml \ eliom_testsuite2.ml \ eliom_testsuite3.eliom \ eliom_testsuite4.eliom \ eliom_testsuite.ml \ atom_example.ml \ CLIENT_FILES := eliom_testsuite_base.eliom \ eliom_testsuite3.eliom \ eliom_testsuite4.eliom OTHER_FILES := eliom_testsuite_global.eliom eliom_testsuite_site.eliom STATICDIR := ../local/var/www/tests TYPE_DIR = DESTILLERY_FILES := $(addprefix $(STATICDIR)/destillery/basic/,test.cma test.cmxa test.js) byte:: _server/eliom_testsuite_global.cmo _server/eliom_testsuite_site.cmo eliom_testsuite.cma ${STATICDIR}/eliom_testsuite.js destillery-basic-byte opt:: _server/eliom_testsuite_global.cmxs _server/eliom_testsuite_site.cmxs eliom_testsuite.cmxs ${STATICDIR}/eliom_testsuite.js destillery-basic-opt #### Destillery #### .PHONY: destillery-basic-byte destillery-basic-opt destillery/basic: mkdir -p $@ eliom-destillery -name destillery_basic -destination $@ echo 'PREFIX=$(CURDIR)/../local/' >> $@/Makefile.options echo 'STATICDIR=var/www/destillery-basic/static' >> $@/Makefile.options echo 'ELIOMSTATICDIR=var/www/destillery-basic/eliomstatic' >> $@/Makefile.options echo 'LIBDIR=var/www/destillery-basic/lib' >> $@/Makefile.options echo 'WWWUSER=' >> $@/Makefile.options echo 'WWWGROUP=' >> $@/Makefile.options make -C $@ byte install.static destillery-basic-byte: | destillery/basic make -C destillery/basic byte install.lib.byte destillery-basic-opt: | destillery/basic make -C destillery/basic opt install.lib.opt #### Server side ####### SERVER_INC := ${addprefix -package ,${SERVER_PACKAGE}} SERVER_OBJS := $(patsubst %.eliom,_server/%.cmo, \ $(patsubst %.ml,_server/%.cmo,${SERVER_FILES})) eliom_testsuite.cma: ${SERVER_OBJS} ${ELIOMC} -a -o $@ $^ eliom_testsuite.cmxa: ${SERVER_OBJS:.cmo=.cmx} ${ELIOMOPT} -a -o $@ $^ ${or ${TYPE_DIR},_server}/%.type_mli: %.eliom ${ELIOMC} -infer ${SERVER_INC} -o $@ $< _server/%.cmi: %.mli ${ELIOMC} -c ${SERVER_INC} $< _server/%.cmo: %.ml ${ELIOMC} -c ${SERVER_INC} $< _server/%.cmo: %.eliom ${ELIOMC} -c ${addprefix -type-dir ,${TYPE_DIR}} ${SERVER_INC} $< _server/%.cmx: %.ml ${ELIOMOPT} -c ${SERVER_INC} $< _server/%.cmx: %.eliom ${ELIOMOPT} -c ${addprefix -type-dir ,${TYPE_DIR}} ${SERVER_INC} $< %.cmxs: %.cmxa $(ELIOMOPT) -shared -linkall -o $@ $< %.cmxs: %.cmx $(ELIOMOPT) -shared -linkall -o $@ $< ##### Client side #### CLIENT_LIBS := ${addprefix -package ,${CLIENT_PACKAGE}} CLIENT_INC := ${addprefix -package ,${CLIENT_PACKAGE}} CLIENT_OBJS := $(patsubst %.eliom,_client/%.cmo, \ $(patsubst %.ml,_client/%.cmo,${CLIENT_FILES})) ${STATICDIR}/eliom_testsuite.js: ${CLIENT_OBJS} ${JS_OF_ELIOM} -o $@ -jsopt -pretty -jsopt -noinline ${CLIENT_LIBS} $^ _client/%.cmi: %.mli ${JS_OF_ELIOM} -c ${CLIENT_INC} $< _client/%.cmo: %.eliom ${JS_OF_ELIOM} -c ${addprefix -type-dir ,${TYPE_DIR}} ${CLIENT_INC} $< _client/%.cmo: %.ml ${JS_OF_ELIOM} -c ${CLIENT_INC} $< ####### Aux site: miniwiki ####### byte:: miniwiki.byte opt:: miniwiki.opt miniwiki.byte: $(MAKE) --no-print-directory -C miniwiki byte miniwiki.opt: $(MAKE) --no-print-directory -C miniwiki opt ############ ## Clean up clean: clean.local ${MAKE} -C miniwiki clean clean.local: -rm -f *.cm[ioax] *.cmxa *.cmxs *.o *.a *.annot -rm -f ${or ${TYPE_DIR},_server}/*.type_mli -rm -f ${STATICDIR}/eliom_testsuite.js -rm -rf _client/* _server/* -rm -rf destillery distclean: clean.local -rm -f *~ \#* .\#* ${MAKE} -C distclean ${MAKE} -C miniwiki distclean ## Dependencies depend: ${MAKE} -C miniwiki depend $(ELIOMDEP) -server ${SERVER_INC} ${addprefix -type-dir ,${TYPE_DIR}} ${SERVER_FILES} ${OTHER_FILES} > .depend $(ELIOMDEP) -client ${CLIENT_INC} ${addprefix -type-dir ,${TYPE_DIR}} ${CLIENT_FILES} $(OTHER_FILES) >> .depend include .depend ## TODO # %.wiki: %.ml # cat $< | sed '1,/(\*wiki\*/d' | sed '/%<||2>%/,$$ d' | /bin/sh ./tutomake.sh > $@ eliom-3.0.3/tests/eliom_testsuite_base.eliom0000644000000000000000000001246412062377521017425 0ustar0000000000000000 {shared{ open Eliom_content open Eliom_lib }} {client{ let () = if Js.to_string Dom_html.window##location##hash = "#__trace" then Eliom_config.set_tracing true; if Js.to_string Dom_html.window##location##hash = "#__timings" then Eliom_config.debug_timings := true }} module My_appl = Eliom_registration.App ( struct let application_name = "eliom_testsuite" end) let main = Eliom_service.service [] Eliom_parameter.unit () let tests description services = Html5.F.( div [ h4 [pcdata description]; ul (List.map (fun (description, service) -> li [a ~service [pcdata description] ()]) services); ] ) let testsuite ~name testsuite_tests = Html5.F.( div (h3 [pcdata name] :: List.map (uncurry tests) testsuite_tests) ) let test_logger = Html5.Id.create_global_elt (Html5.D.(div ~a:[a_class ["test_logger"]] [h4 [pcdata "Client logger"]])) let test ~path ~title:ttl ~description f = ttl, My_appl.register_service ~path ~get_params:Eliom_parameter.unit (fun () () -> lwt content = f () in let toggle_tracing = {{ fun _ -> Eliom_config.set_tracing (not (Eliom_config.get_tracing ())); alert "%s tracing" (if Eliom_config.get_tracing () then "Enabled" else "Disabled") }} in Lwt.return Html5.F.(html (Eliom_tools.F.head ~title:(String.concat "/" path) ~css:[["style.css"]] ()) (body (div [ a ~xhr:false ~service:main [pcdata "Home and break app"] () ; pcdata " - " ; a ~service:Eliom_service.void_coservice' [pcdata "Reload in running app"] () ; pcdata " - " ; Raw.a ~a:[a_id "toggle"; a_onclick toggle_tracing] [pcdata "Toggle tracing (or append #__trace to the URL)"] ; ] :: h1 ~a:[a_class ["test_title"]] [pcdata ttl] :: div ~a:[a_class ["test_description"]] description :: hr () :: content @ [ test_logger ])))) let thebutton ?(msg="THE BUTTON") onclick : [> Html5_types.button ] Html5.elt = Html5.F.( button ~button_type:`Submit ~a:[a_class ["thebutton"]; a_onclick onclick] [ pcdata msg ]) let monospace fmt = Printf.ksprintf (fun str -> Html5.F.(span ~a:[a_class ["monospace"]] [pcdata str])) fmt {client{ let buffer = ref [] let append_log_message msg = Html5.Manip.appendChild %test_logger Html5.D.(div ~a:[a_class ["logging_line"]] [pcdata msg]) let () = let rec flush () = Eliom_client.onload (fun () -> List.iter append_log_message (List.rev !buffer); buffer := []; Lwt.ignore_result (lwt () = Lwt_js.sleep 0.01 in Lwt.return (flush ()))) in flush () let log : 'a . ('a, unit, string, unit) format4 -> 'a = fun fmt -> Printf.ksprintf (fun msg -> if Eliom_client.in_onload () then buffer := msg :: !buffer else append_log_message msg) fmt }} {shared{ let report_flush_assertions' name output ~ran ~failed = Printf.ksprintf output "Eliom_testsuite %S:" name; Printf.ksprintf output " * Ran %d assertions (%s)" (List.length ran) (String.concat ", " ran); if failed = [] then Printf.ksprintf output " * All tests succeeded" else Printf.ksprintf output " * %d tests failed: %s" (List.length failed) (String.concat ", " failed); }} {server{ let failed_assertions : string list Eliom_reference.Volatile.eref = Eliom_reference.Volatile.eref ~scope:Eliom_common.request_scope [] let ran_assertions : string list Eliom_reference.Volatile.eref = Eliom_reference.Volatile.eref ~scope:Eliom_common.request_scope [] let report_flush_assertions name = report_flush_assertions' name (debug "%s") ~ran:(Eliom_reference.Volatile.get ran_assertions) ~failed:(Eliom_reference.Volatile.get failed_assertions); Eliom_reference.Volatile.set ran_assertions []; Eliom_reference.Volatile.set failed_assertions [] let assert_equal ?(eq=(=)) ~name value should_be = Eliom_reference.Volatile.modify ran_assertions (fun names -> name :: names); if not (eq value should_be) then Eliom_reference.Volatile.modify failed_assertions (fun names -> name :: names) }} {client{ let ran_assertions = ref [] let failed_assertions = ref [] let report_flush_assertions name = report_flush_assertions' name (log "%s") ~ran:!ran_assertions ~failed:!failed_assertions; ran_assertions := []; failed_assertions := [] let assert_equal ?(eq=(=)) ~name value should_be = ran_assertions := name :: !ran_assertions; if not (eq value should_be) then failed_assertions := name :: !failed_assertions }} eliom-3.0.3/tests/eliom_testsuite.ml0000644000000000000000000005433112062377521015735 0ustar0000000000000000 let (>>=) = Lwt.(>>=) let (>|=) = Lwt.(>|=) open Eliom_lib open Eliom_content open Html5.D open Eliom_testsuite1 open Eliom_testsuite2 open Eliom_testsuite3 (* Main page for the test suite *) let _ = Eliom_registration.Html5.register Eliom_testsuite_base.main (fun () () -> Lwt.return (html (head (title (pcdata "Examples from the manual")) [Html5.D.css_link (Html5.D.make_uri ~service:(Eliom_service.static_dir ()) ["style.css"]) ()]) (body [ h1 [img ~alt:"Ocsigen" ~src:(Html5.D.make_uri ~service:(Eliom_service.static_dir ()) ["ocsigen5.png"]) ()]; Eliom_testsuite_base.testsuite ~name:"Test suite 4" Eliom_testsuite4.tests; h3 [pcdata "Eliom examples"]; h4 [pcdata "Simple pages"]; p [ pcdata "A simple page: "; a coucou [code [pcdata "coucou"]] (); br (); pcdata "A page with a counter: "; a count [code [pcdata "count"]] (); br (); pcdata "A page in a directory: "; a hello [code [pcdata "dir/hello"]] (); br (); pcdata "Default page of a directory: "; a default [code [pcdata "rep/"]] () ]; h4 [pcdata "Destillery"]; p [ Raw.a ~a:[a_href (Raw.uri_of_string "destillery/basic")] [pcdata "Basic destillery"]; ]; h4 [pcdata "Parameters"]; p [ pcdata "A page with GET parameters: "; a coucou_params [code [pcdata "coucou"]; pcdata " with params"] (45,(22,"krokodile")); pcdata "(what if the first parameter is not an integer?)"; br (); pcdata "A page with \"suffix\" URL that knows the IP and user-agent of the client: "; a uasuffix [code [pcdata "uasuffix"]] (2007,6); br (); pcdata "A page with \"suffix\" URL and GET parameters: "; a isuffix [code [pcdata "isuffix"]] ((111, ["OO";"II";"OO"]), 333); br (); pcdata "A page with constants in suffix: "; a constfix [pcdata "Page with constants in suffix"] ("aa", ((), "bb")); br (); pcdata "Form towards page with suffix: "; a suffixform [pcdata "formsuffix"] (); br (); pcdata "A page with a parameter of user-defined type : "; a mytype [code [pcdata "mytype"]] A; ]; h4 [pcdata "Links and Forms"]; p [ pcdata "A page with links: "; a links [code [pcdata "links"]] (); br (); pcdata "A page with a link towards itself: "; a linkrec [code [pcdata "linkrec"]] (); br (); pcdata "The "; a Eliom_testsuite_base.main [pcdata "default page"] (); pcdata "of this directory (myself)"; br (); pcdata "A page with a GET form that leads to the \"coucou\" page with parameters: "; a form [code [pcdata "form"]] (); br (); pcdata "A POST form towards the \"post\" page: "; a form2 [code [pcdata "form2"]] (); br (); pcdata "The \"post\" page, when it does not receive parameters: "; a no_post_param_service [code [pcdata "post"]; pcdata " without post_params"] (); br (); pcdata "A POST form towards a service with GET parameters: "; a form3 [code [pcdata "form3"]] (); br (); pcdata "A POST form towards an external page: "; a form4 [code [pcdata "form4"]] (); ]; h4 [pcdata "Sessions"]; p [ pcdata "Coservices: "; a coservices_example [code [pcdata "coservice"]] (); br (); pcdata "A session based on cookies, implemented with session data: "; a session_data_example [code [pcdata "sessdata"]] (); br (); pcdata "A session based on cookies, implemented with actions: "; a connect_example3 [code [pcdata "actions"]] (); br (); pcdata "A session based on cookies, with session services: "; a session_services_example [code [pcdata "sessionservices"]] (); br (); pcdata "A session based on cookies, implemented with actions, with session groups: "; a connect_example5 [code [pcdata "groups"]] (); br (); pcdata "The same with wrong user if not \"toto\": "; a connect_example6 [code [pcdata "actions2"]] (); br (); pcdata "A session based on cookies, implemented with actions, with session groups, and using a group table: "; a group_tables_example [code [pcdata "grouptables"]] (); br (); pcdata "A session based on cookies, implemented with actions, with session groups, and using a persistent group table: "; a pgroup_tables_example [code [pcdata "pgrouptables"]] (); br (); pcdata "Coservices in the session table: "; a calc [code [pcdata "calc"]] (); br (); pcdata "Persistent sessions: "; a persist_session_example [code [pcdata "persist"]] (); br (); pcdata "Volatile group data: "; a connect_example_gd [code [pcdata "sessgrpdata"]] (); br (); pcdata "Persistent group data: "; a connect_example_pgd [code [pcdata "psessgrpdata"]] (); br (); ]; h4 [pcdata "Other"]; p [ pcdata "A page that is very slow, implemented in cooperative way: "; a looong [code [pcdata "looong"]] (); br (); pcdata "A page that is very slow, using preemptive threads: "; a looong2 [code [pcdata "looong2"]] (); br (); pcdata "Catching errors: "; a catch [code [pcdata "catch"]] 22; pcdata " (change the value in the URL)"; br (); pcdata "Redirection: "; a redir [code [pcdata "redir"]] 11; br (); pcdata "Cookies: "; a cookies [code [pcdata "cookies"]] (); br (); pcdata "Disposable coservices: "; a disposable [code [pcdata "disposable"]] (); br (); pcdata "Coservice with timeout: "; a timeout [code [pcdata "timeout"]] (); br (); pcdata "Public coservice created after initialization (with timeout): "; a publiccoduringsess [code [pcdata "publiccoduringsess"]] (); br (); pcdata "The following URL send either a statically checked page, or a text page: "; a send_any [code [pcdata "send_any"]] "valid"; br (); pcdata "A page with a persistent counter: "; a count2 [code [pcdata "count2"]] (); br (); pcdata "A page with a persistent counter with persitent Eliom ref: "; a persref [code [pcdata "persref"]] (); br (); a hier1 [pcdata "Hierarchical menu"] (); br (); a divpage [code [pcdata "a link sending a <div> page"]] (); br (); a tonlparams [pcdata "Non localized parameters"] (); br (); a nlparams [pcdata "Non localized parameters (absent)"] 4; br (); a nlparams_with_nlp [pcdata "Non localized parameters (present)"] (22, (11, "aa")); br (); a nlpost_entry [pcdata "Non localized parameters on post service"] (); br (); a csrfsafe_example [pcdata "CSRF safe services"] (); br (); a volatile_references [pcdata "Volatile references"] (); br (); a reference_from_fun [pcdata "References from fun"] (); br (); a Eliom_testsuite_site.reference_scope_site [pcdata "References of scope site"] (); br (); ]; h4 [pcdata "Advanced forms"]; p [ pcdata "A page that parses a parameter using a regular expression: "; a regexpserv [code [pcdata "regexpserv"]] "[toto]"; br (); pcdata "A form with a checkbox: "; a form_bool [pcdata "Try it"] (); br (); pcdata "A page that takes a set of parameters: "; a set [code [pcdata "set"]] ["Ciao";"bello";"ciao"]; br (); pcdata "A form to the previous one: "; a setform [code [pcdata "setform"]] (); br (); pcdata "A page that takes any parameter: "; a raw_serv [code [pcdata "raw_serv"]] [("a","hello"); ("b","ciao")]; br (); pcdata "A form to the previous one: "; a raw_form [code [pcdata "raw_form"]] (); br (); pcdata "A form for a list of parameters: "; a listform [pcdata "Try it"] (); br (); ]; h3 [pcdata "js_of_ocaml events"]; p [ a event_service [code [pcdata "With arrows"]] (); br (); a event2_service [code [pcdata "With Lwt"]] (); br (); ]; h3 [pcdata "Other tests"]; p [ a coucou [pcdata "coucou"] (); br (); a sumform [pcdata "alternative parameters"] (); br (); a sumform2 [pcdata "alternative parameters with POST"] (); br (); a optform [pcdata "Optional parameters"] (); br (); a main_neopt_service [pcdata "Non-empty optional parameters"] (); br (); a sfail [pcdata "Service raising an exception"] (); br (); a sraise [pcdata "Wrong use of exceptions during service"] (); br (); a getcoex [pcdata "GET coservice with preapplied fallback, etc"] (); br (); a postcoex [pcdata "POST service with coservice fallback"] (); br (); a su [pcdata "Suffix and other service at same URL"] (); br (); a suffixform_su2 [pcdata "Suffix and other service at same URL: a form towards the suffix service"] (); br (); a su4 [pcdata "Suffix service with constant part and priority"] ("aa", ((), "bb")); br (); a preappliedsuffix [pcdata "Preapplied suffix"] (); br (); a constform [pcdata "Form towards suffix service with constants"] (); br (); a getact [pcdata "action on GET attached coservice, etc"] 127; br (); a noreload [pcdata "action with `NoReload option"] (); br (); a cookies2 [pcdata "Many cookies"] "le suffixe de l'URL"; br (); a headers [pcdata "Customizing HTTP headers"] (); br (); a sendfileex [pcdata "Send file"] (); br (); a sendfile2 [pcdata "Send file 2"] "style.css"; br (); a sendfileexception [pcdata "Do not send file"] (); br (); a sendfileregexp [pcdata "Send file with regexp"] (); br (); a suffixform2 [pcdata "Suffix 2"] (); br (); a suffixform3 [pcdata "Suffix 3"] (); br (); a suffixform4 [pcdata "Suffix 4"] (); br (); a nosuffix [pcdata "Page without suffix on the same URL of a page with suffix"] (); br (); a anypostform [pcdata "POST form to any parameters"] (); br (); a any2 [pcdata "int + any parameters"] (3, [("Ciao","bel"); ("ragazzo","!")]); br (); a any3 [pcdata "any parameters broken (s after any)"] (4, ([("Thierry","Richard");("Sébastien","Stéphane")], "s")); br (); (* broken a any4 [pcdata "Any in suffix"] [("bo","ba");("bi","bu")]; br (); *) a any5 [pcdata "Suffix + any parameters"] ("ee", [("bo","ba");("bi","bu")]); br (); a uploadgetform [pcdata "Upload with GET"] (); br (); a sufli [pcdata "List in suffix"] [("bo", 4);("ba", 3);("bi", 2);("bu", 1)]; br (); a sufliform [pcdata "Form to list in suffix"] (); br (); a sufliopt [pcdata "List of optional values in suffix"] [None; Some "j"]; br (); a sufliopt2 [pcdata "List of optional pairs in suffix"] [None; Some ("j", "ee")]; br (); a sufset [pcdata "Set in suffix"] ["bo";"ba";"bi";"bu"]; br (); (* a sufli2 [pcdata "List not in the end of in suffix"] ([1; 2; 3], 4); br (); *) a boollistform [pcdata "Bool list"] (); br (); a lilists [pcdata "List of lists in parameters"] (); br (); a wlf_lists [pcdata "List of lists in parameters - 2nd example"] [[333]]; br (); a preappmenu [pcdata "Menu with pre-applied services"] (); br (); a exn_act_main [pcdata "Actions that raises an exception"] (); br (); a close_from_outside [pcdata "Closing sessions from outside"] (); br (); a set_timeout_form [pcdata "Setting timeouts from outside sessions"] (); br (); a ~fragment:"a-- ---++&é/@" ~service:url_encoding [pcdata "Urls with strange characters inside"] (["l/l%l &l=l+l)l@";"m\\m\"m";"n?èn~n"], [("po?po&po~po/po+po", "lo?\"l o#lo'lo lo=lo&l o/lo+lo"); ("bo=mo@co:ro", "zo^zo%zo$zo:zo?aaa")]); br (); a ~service:(Eliom_service.static_dir_with_params ~get_params:Eliom_parameter.any ()) [pcdata "Static file with GET parameters"] (["ocsigen5.png"], [("aa", "lmk"); ("bb", "4")]); br (); a extreq [pcdata "External request"] (); br (); a servreq [pcdata "Server request"] (); br (); a servreqloop [pcdata "Looping server request"] (); br (); a nlparams2 [pcdata "nl params and suffix, on void coservice"] ((3, 5), 222); br (); a optsuf [pcdata "optional suffix"] None; br (); a optsuf [pcdata "optional suffix"] (Some ("", None)); br (); a optsuf [pcdata "optional suffix"] (Some ("toto", Some 2)); br (); a optsuf2 [pcdata "optional suffix 2"] (Some "un", Some 2); br (); a optsuf2 [pcdata "optional suffix 2"] (None, Some 2); br (); a optsuf2 [pcdata "optional suffix 2"] (Some "un", None); br (); a optsuf2 [pcdata "optional suffix 2"] (None, None); br (); a csrfsafe_get_example [pcdata "GET CSRF safe service"] (); br (); a csrfsafe_postget_example [pcdata "POST CSRF safe service on GET CSRF safe service"] (); br (); a csrfsafe_session_example [pcdata "POST non attached CSRF safe service in session table"] (); br (); a unregister_example [pcdata "Unregistering services"] (); br (); a raw_post_example [pcdata "Raw POST data"] (); br (); ]; h3 [pcdata "Eliom Client"]; h4 [pcdata "Interaction"]; p [ a eliomclient1 [pcdata "Simple example of client side code"] (); br (); a eliomclient2 [pcdata "Nodes with various onclick features"] (); br (); a eliomclient3 [pcdata "Caml values in service parameters"] (); br (); a eliomclient4 [pcdata "A service sending a Caml value"] (); br (); a uri_test [pcdata "Simple test of URL generation"] (); br (); a formc [pcdata "Links and forms"] (); br (); a ~fragment:"id40" ~service:long_page [pcdata "Fragment scrolling"] (); br (); a live1 [ pcdata "History handling and page load/unload events." ] (); br (); a relink_test [pcdata "Global elements"] (); br (); a unique1 [pcdata "Onload event on element and global element"] (); br (); a body_onload [pcdata "Onload event on the body element"] (); br (); (* a xhr_form_with_file [pcdata "xhr forms with file"] (); *) (* br (); *) a caml_service_cookies [pcdata "Client process cookies with caml service"] (); br (); a gotowithoutclient [pcdata "A page that links to a service that belongs to the application but do not launch the application if it is already launched"] (); br (); a default_no_appl [pcdata "Toggle the default value of no_appl"] (); br (); a wrapping1 [pcdata "wrapping test 1"] (); br (); a wrapping_big_values [pcdata "wrapping test: big values"] 200000; br (); a caml_service_wrapping [pcdata "wrapping for caml call service"] (); br (); a caml_service_with_onload [pcdata "onload with caml call service"] (); br (); a service_style1 [pcdata "test header modifications"] (); br (); a any_service [pcdata "Eliom_output.Any with Eliom_appl"] 1; br (); a domnodes_timings [pcdata "Speed test for TyXMl nodes with dom semantics (previously known as unique node of scope request)"] (2,10); br (); a shared_dom_nodes [pcdata "Multiple occurences of unique nodes."] (); br (); a nl_serv [pcdata "Non localised parameters and eliom appl"] (); br (); a nlpost_entry [pcdata "Non localised parameters with forms in eliom appl"] (); br (); ]; h4 [pcdata "Templates"]; p [ a tmpl1_page1 [pcdata "Multiple template switching."] (); br (); a hist_page1 [pcdata "Browser history and templates."] (); br (); ]; h4 [pcdata "Comet"]; p [ a comet1 [pcdata "A really simple comet example"] (); br (); a comet2 [pcdata "A comet example with server to client and client to server asynchronous events"] (); br (); a comet3 [pcdata "Server simultaneous events, transmitted together"] (); br (); a comet_wrapping [pcdata "sent wrapped values"] (); br (); a comet_signal [pcdata "Signal"] (); br (); a comet_message_board [pcdata "Minimalistic message board"] (); br (); ]; h5 [pcdata "stateless"]; p [ a comet_stateless [pcdata "simple stateless comet"] (); br (); a comet_signal_stateless [pcdata "Signal"] (); br (); a comet_message_board_stateless [pcdata "Minimalistic stateless message board"] (); br (); a bus_multiple_times [pcdata "Bus stream used multiple times"] (); br (); ]; h5 [pcdata "external"]; p [ a comet_stateless_external [pcdata "comet channel on another server"] (); a external_xhr [pcdata "request and external service with xhr"] (); ]; h4 [pcdata "More tests"]; p [ a appl_redir [pcdata "Eliom applications and redirections"] (); br (); a noreload_appl [pcdata "Eliom applications and actions with `NoReload option"] (); br (); a nonapplprocessservice [pcdata "Client process service not registered with Eliom_appl"] (); br (); a gracefull_fail_with_file [ pcdata "link to a service hidden by a file" ] (); br (); a appl_with_redirect_service [ pcdata "link to a service hidden by a redirection" ] (); br (); a big_service [ pcdata "loading a big page" ] (); br (); ]; h4 [pcdata "Process states"]; p [ a states_test [pcdata "Extensive test of Eliom references of different scopes, accessed from inside or outside the state itself"] (); br (); pcdata "Coservices: "; a tcoservices_example [code [pcdata "tcoservice"]] (); br (); pcdata "Coservice with timeout: "; a ttimeout [code [pcdata "timeout"]] (); br (); pcdata "A session based on cookies, implemented with session data: "; a tsession_data_example [code [pcdata "tsessdata"]] (); br (); pcdata "A session based on cookies, implemented with actions: "; a tconnect_example3 [code [pcdata "tactions"]] (); br (); pcdata "A session based on cookies, with session services: "; a tsession_services_example [code [pcdata "tsessionservices"]] (); br (); pcdata "A session based on cookies, implemented with actions, with session groups: "; a connect_example5 [code [pcdata "groups"]] (); br (); pcdata "Session and client process: "; a connect_example789 [code [pcdata "session_appl"]] (); br (); (* pcdata "The same with wrong user if not \"toto\": "; a tconnect_example6 [code [pcdata "tactions2"]] (); br (); *) pcdata "Coservices in the session table: "; a tcalc [code [pcdata "tcalc"]] (); br (); pcdata "Persistent sessions: "; a tpersist_session_example [code [pcdata "tpersist"]] (); br (); a tcsrfsafe_example [pcdata "CSRF safe services"] (); br () ]; h4 [ pcdata "Other" ]; p [ pcdata "User tab cookies: "; a tcookies [ code [ pcdata "tcookies" ] ] (); br (); pcdata "A link inside the application that ascks for an action outside the application. Eliom will ask the client side program to so a redirection: "; a actionoutside [ code [ pcdata "actionoutside" ] ] (); br (); ] ]))) (* *zap*) eliom-3.0.3/tests/eliom_testsuite4.eliom0000644000000000000000000014623112062377521016517 0ustar0000000000000000 {shared{ open Eliom_content open Eliom_lib }} {client{ let () = (* Eliom_lib.set_tracing true; *) () }} (******************************************************************************) let the_number = 100 let ocaml_service = Eliom_registration.Ocaml.register_coservice' ~get_params:Eliom_parameter.unit (let rec counter = ref 0 in fun () () -> ignore {unit{ Eliom_testsuite_base.log "From ocaml service 1"; }}; incr counter; if !counter mod 3 = 0 then Lwt.fail (Failure "Fails each 3rd time") else (ignore {unit{ Eliom_testsuite_base.log "From ocaml service 2"; }}; Lwt.return the_number)) let test_client_value_on_caml_service = Eliom_testsuite_base.test ~title:"Client values in Ocaml-services" ~path:["holes"; "caml_service"] ~description:Html5.F.([ pcdata "On loading: \"From main service\""; br (); pcdata "On clicking button"; ul [ li [pcdata "\"From ocaml service 1\""]; li [pcdata "\"From ocaml service 2\""]; li [Printf.ksprintf pcdata "\"number: %d\"" the_number]; ]; pcdata "Each time clicking the button"; ul [ li [pcdata "\"From ocaml service 1\""]; li [pcdata "Exception on server: Failure(\"Fails each 3rd time\")"]; ] ]) (fun () -> ignore {unit{ Eliom_testsuite_base.log "From main service"; }}; let onclick = {{ fun _ -> Lwt.ignore_result (try_lwt lwt number = Eliom_client.call_caml_service %ocaml_service () () in Eliom_testsuite_base.log "number: %d" number; Lwt.return () with Exception_on_server msg -> Eliom_testsuite_base.log "Exception on server: %s" msg; Lwt.return ()) }} in Lwt.return Html5.F.([ button ~a:[a_onclick onclick ] ~button_type:`Submit [ pcdata "Click to get ocaml service"; ] ])) (******************************************************************************) (* Binding of escaped nodes *) let free_global = Html5.(Id.create_global_elt (D.div F.([b [pcdata "Global (free)"]]))) let bound_global = Html5.(Id.create_global_elt (D.div F.([b [pcdata "Global (bound)"]]))) let free_request = Html5.(D.div F.([b [pcdata "Request (free)"]])) let bound_request = Html5.(D.div F.([b [pcdata "Request (bound)"]])) let other_service = Eliom_registration.Ocaml.register_coservice' ~get_params:Eliom_parameter.unit (fun () () -> ignore {unit{ debug "on other service"; Html5.Manip.appendChild %free_request Html5.F.(div [pcdata "from ocaml service"]); Html5.Manip.appendChild %free_global Html5.F.(div [pcdata "from ocaml service"]); Html5.Manip.appendChild %bound_request Html5.F.(div [pcdata "from ocaml service"]); Html5.Manip.appendChild %bound_global Html5.F.(div [pcdata "from ocaml service"]); () }}; Lwt.return ()) {client{ debug "toplevel"; Eliom_client.onload (fun () -> debug "onload"; Html5.Manip.appendChild %free_request Html5.F.(div [pcdata "from client init"]); Html5.Manip.appendChild %free_global Html5.F.(div [pcdata "from client init"]); Html5.Manip.appendChild %bound_request Html5.F.(div [pcdata "from client init"]); Html5.Manip.appendChild %bound_global Html5.F.(div [pcdata "from client init"]); ()) }} let addenda = Html5.D.div [] let node_bindings_local_global_id = Html5.Id.new_elt_id ~global:true () let node_bindings_local_request_id = Html5.Id.new_elt_id ~global:false () let node_bindings = Eliom_testsuite_base.test ~title:"Binding of nodes" ~path:["holes"; "node_binding"] ~description:Html5.F.([ p [pcdata "Observe when HTML5 elements with DOM semantics are reused."]; p [pcdata "Bound nodes are sent in the page; free nodes are added by client value side effect after loading the page."]; ul [ li [pcdata "Initially, every node receives an \"from client\""]; li [pcdata "All four nodes should receive an \"onclick\" line when \"Add onclick lines\" is clicked."]; li [pcdata "The free ones should reset if you visit the empty service and go back in history"]; li [pcdata "The bound and free global nodes should receive a \"from ocaml service\" when \"Run Ocaml service\" is clicked"]; ]; ]) (fun () -> let local_bound_global = Html5.Id.create_named_elt ~id:node_bindings_local_global_id Html5.(D.div [F.(b [pcdata "Global (bound, local)"])]) in let local_bound_request = Html5.Id.create_named_elt ~id:node_bindings_local_request_id Html5.(D.div [F.(b [pcdata "Request (bound, local)"])]) in ignore {unit{ debug "Adding free"; Html5.Manip.appendChild %addenda %free_request; Html5.Manip.appendChild %addenda %free_global; ignore %bound_global; ignore %bound_request; ignore %local_bound_global; ignore %local_bound_request; () }}; let add_onclick = {{ fun _ -> debug "onclick"; List.iter (fun node -> Html5.Manip.appendChild node Html5.F.(div [pcdata "onclick"])) [%free_request; %free_global; %bound_request; %bound_global; %local_bound_global; %local_bound_request]; }} in let run_ocaml_service = {{ fun _ -> debug "run_ocaml_service"; Lwt.ignore_result (Eliom_client.call_caml_service ~service: %other_service () ()) }} in Lwt.return Html5.F.([ Eliom_testsuite_base.thebutton ~msg:"Add onclick lines" add_onclick; Eliom_testsuite_base.thebutton ~msg:"Run ocaml service" run_ocaml_service; local_bound_global; local_bound_request; bound_request; bound_global; addenda; ])) (******************************************************************************) (* Data sharing *) let data_sharing_elt1 = Html5.D.(div ~a:[a_class ["monospace"]] [pcdata "VVVVVVVVVVV"]) let data_sharing_elt2 = Html5.D.(div ~a:[a_class ["monospace"]] [pcdata "WWWWWWWWWWW"]) let data_sharing_elt3 = Html5.D.(div ~a:[a_class ["monospace"]] [pcdata "XXXXXXXXXXX"]) let data_sharing_addenda = Html5.D.div [] let data_sharing = Eliom_testsuite_base.test ~title:"Data sharing" ~path:["holes"; "data_sharing"] ~description:Html5.F.([ p [pcdata "Checks wheather data in the eliom request data is shared"]; p [pcdata "The string of request data is given below."]; p [pcdata "There are three elements on the server, which contain the strings \"VVVVVVVVVVV\", \"WWWWWWWWWWW\", and \"XXXXXXXXXXX\" respectively. All three elements are added to the DOM under \"Added from client\"."]; p [pcdata "The string \"VVVVVVVVVVV\" should not appear in the request data, because the corresponding element is sent as part of the DOM. The string \"WWWWWWWWWWW\" and \"XXXXXXXXXXX\" should appear in the request data exactly once."] ]) (fun () -> let data_sharing_data = Html5.D.div [] in ignore {unit{ Html5.Manip.appendChild %data_sharing_addenda %data_sharing_elt1; Html5.Manip.appendChild %data_sharing_addenda %data_sharing_elt2; }}; ignore {unit{ Html5.Manip.appendChild %data_sharing_data (Html5.F.pcdata (Js.to_string (Js.Unsafe.variable "__eliom_request_data"))) }}; Lwt.return Html5.F.([ data_sharing_elt1; section [ h4 [ pcdata "Added from client" ]; data_sharing_addenda ]; section [ h4 [ pcdata "Request data" ]; data_sharing_data ]; ])) {client{ let () = ignore (%data_sharing_elt1, %data_sharing_elt2); Eliom_client.onload (fun () -> Html5.Manip.appendChild %data_sharing_addenda %data_sharing_elt3) }} (******************************************************************************) (* Custom data *) {shared{ type my_data = { x : int; y : int } deriving (Json) let my_data = Eliom_content.Html5.Custom_data.create_json ~name:"my_int" ~default:{x=0;y=0;} Json.t }} {client{ let show_my_data (ev : Dom_html.mouseEvent Js.t) = let elt = Js.Optdef.get (ev##target) (fun () -> failwith "show_my_data") in let i = Html5.Custom_data.get_dom elt my_data in alert "custom_data : {x=%d;y=%d}" i.x i.y let change_data container = let element = Html5.To_dom.of_element container in let i = Html5.Custom_data.get_dom element my_data in let i' = { x = succ i.x; y = pred i.y } in Html5.Custom_data.set_dom element my_data i' }} let test_custom_data = let description = "Custom data: modification and defaults" in let path = ["custom_data"] in description, Eliom_testsuite_base.My_appl.register_service ~path ~get_params:Eliom_parameter.unit (fun () () -> let open Html5.F in let container = Html5.D.div ~a:[Html5.Custom_data.attrib my_data { x = 100; y = 100 }] [pcdata "A: click me (my_data is originally {x=100;y=100})"] in let change_button = button ~a:[a_onclick {{ fun _ -> change_data %container }}] ~button_type:`Submit [pcdata "In-/decrement my_data"] in Lwt.return (html (head (title (pcdata (String.concat "/" path))) []) (body [ h1 [pcdata description]; div ~a:[a_onclick {{ show_my_data }}] [ ul [ li [pcdata "The following div \"click me ...\" contains a custom data for my_data."]; li [pcdata "The value of the div may be changed by the button below."]; li [pcdata "Click any of these lines or the div, to alert it's my_data custom data."]; ]; container; ]; change_button; ]))) (******************************************************************************) (* (******************************************************************************) (* Client values: espaping *) let client_values_injection = let v_a = "a" in let v_b = {string{ "b" }} in let v_c = {string{ Printf.sprintf "(c a:%s)" %v_a }} in let v_d = {string{ Printf.sprintf "(d b:%s)" %v_b }} in let v_f = {string->string{ fun arg -> Printf.sprintf "(f arg:%s)" arg }} in let onclick str cstr = {{ fun ev -> alert "onclick\n \ a:%s b:%s c:%s d:%s\n \ str:%s cstr:%s farg:%s" %v_a %v_b %v_c %v_d %str %cstr ( %v_f "arg") }} in let description = "Nested escaping of (client-) values into holes" in let path = ["client_values"; "injection"] in description, Eliom_testsuite_base.My_appl.register_service ~path ~get_params:Eliom_parameter.unit (fun () () -> let cstr = {string{ "cstr" }} in Lwt.return Html5.F.( html (Eliom_tools.F.head ~title:(String.concat "/" path) ~css:[["style.css"]] ()) (body [ h1 [pcdata description]; div ~a:[a_class ["thebutton"]; a_onclick (onclick "str" cstr)] [ pcdata "Click me" ]; ]) )) (******************************************************************************) (******************************************************************************) (* Client values: mutability *) let client_values_mutability = let server_ref = ref 0 in let client_ref = {int ref{ ref 0 }} in let fresh_ref_client = {unit -> int ref{ fun () -> ref 0}} in let id = "fresh_client_ref" in let fresh_client_ref s = {int ref{ ignore %id; debug "fresh_client_ref: %s" %s; ref 0 }} in let s = "server" in let onclick = {{ fun _ -> let s = "client" in let fresh_ref_client = %fresh_ref_client () in let fresh_client_ref = %(fresh_client_ref s) in incr %server_ref; incr %client_ref; incr fresh_ref_client; incr fresh_client_ref; debug "injected server reference: %d (increments)\n\ injected client reference: %d (increments)\n\ fresh reference client: %d (resets)\n\ fresh client reference: %d (increments)" ! %server_ref ! %client_ref !fresh_ref_client !fresh_client_ref; incr %server_ref; incr %client_ref; incr fresh_ref_client; incr fresh_client_ref; debug "server reference_r: %d (increments)\n\ injected client reference_r: %d (increments)\n\ fresh reference client: %d (increments)\n\ fresh client reference: %d (increments)" ! %server_ref ! %client_ref !fresh_ref_client !fresh_client_ref }} in let description = "Mutability of server values and injected holes" in let path = ["client_values"; "mutability"] in description, Eliom_testsuite_base.My_appl.register_service ~path ~get_params:Eliom_parameter.unit (fun () () -> Lwt.return Html5.F.( html (Eliom_tools.F.head ~title:(String.concat "/" path) ~css:[["style.css"]] ()) (body [ h1 [pcdata description]; div ~a:[a_class ["thebutton"]; a_onclick onclick] [ pcdata "Click me" ]; a ~service:Eliom_service.void_coservice' [pcdata "self"] (); ]) )) (******************************************************************************) (******************************************************************************) (* Client values: changing context *) let client_values_changing_context = let client_ref = {int ref{ ref 100 }} in let client_f = {string->string{ debug "client_f once"; fun str -> Printf.sprintf "(client_f %s)" str }} in let client_hold str = {string{ debug "client_hold once: %s" %str; Printf.sprintf "(client_hold %s)" %str }} in let client_value = {string{ debug "client value, once"; "client_value" }} in let other_client_value = {string{ Printf.sprintf "--%s--" %client_value }} in let handler ix = {{ debug "handler, once per page: %d" %ix; fun _ -> incr %client_ref; debug "handler, on click: ix:%d client_ref:%d client_value:%s other_client_value:%s (client_f ix):%s" %ix ! %client_ref %client_value %other_client_value ( %client_f (string_of_int %ix)) }} in let description = "Changing context of client values" in let path = ["client_values"; "changing_context"] in let ix = ref 0 in description, Eliom_testsuite_base.My_appl.register_service ~path ~get_params:Eliom_parameter.unit (fun () () -> incr ix; debug "ix: %d" !ix; Lwt.return Html5.F.( html (Eliom_tools.F.head ~title:(String.concat "/" path) ~css:[["style.css"]] ()) (body [ h2 [pcdata description]; div ~a:[a_class ["thebutton"]; a_onclick (handler !ix)] [ pcdata "Click me" ]; a ~service:Eliom_service.void_coservice' [ pcdata "Reload, keep application running" ] (); ]) )) (******************************************************************************) (******************************************************************************) (* Client values: client value initialization *) {client{ }} let client_values_initialization = let name = "Observe when client values created by server holes are initialized" in let description = "" in let client_value_1 = {string{ debug "init: client_value_1 (after each reload)"; "client_value_1" }} in let client_value_2 = {string{ debug "init: client_value_2 (after each reload)"; Printf.sprintf "(client_value_2 %s)" %client_value_1 }} in let client_function = {string->string{ debug "init: client_function (after each reload)"; fun str -> Printf.sprintf "(client_function %s)" str }} in let client_value_onhold () = {string{ debug "init: client_value_onhold (after each click)"; "client_value_onhold" }} in let onclick ix = {{ debug "init: onclick"; fun _ -> debug "client_value_1: %s\n \ client_value_2: %s\n \ (client_function %d): %s\n \ (client_value_onhold ()): %s" %client_value_1 %client_value_2 %ix ( %client_function (string_of_int %ix)) %(client_value_onhold ()) }} in let path = ["client_values"; "initialization"] in name, Eliom_testsuite_base.My_appl.register_service ~path ~get_params:Eliom_parameter.unit (let ix = ref 0 in fun () () -> incr ix; Lwt.return Html5.F.( html (Eliom_tools.F.head ~title:(String.concat "/" path) ~css:[["style.css"]] ()) (body [ h2 [pcdata name]; div ~a:[a_class ["description"]] [pcdata description]; div ~a:[a_class ["thebutton"]; a_onclick (onclick !ix)] [ pcdata (Printf.sprintf "Click me (ix:%d)" !ix) ]; a ~service:Eliom_service.void_coservice' ~xhr:true [ pcdata "Reload, keep application running" ] (); ]) )) (******************************************************************************) (******************************************************************************) (* Client value custom data * {shared{ let my_unit_unit = Html5.Custom_data.create_client_value ~name:"my_unit_unit" () }} {client{ let run_element : _ Eliom_content.Html5.elt -> unit -> unit = let f = Html5.Custom_data.get_dom (To_dom.of_element trg) my_unit_unit in f () let f () = debug "ffffffff" let g () = debug "ggggggg" }} {server{ open Eliom_content let a = {string{ "a" }} let b = {string->string{ fun x -> "(b" ^ " " ^ %a ^ " " ^ x ^ ")" }} let my_div label cv = Html5.F.( let onclick = {{ fun ev -> run_element ev##target () }} in div ~a:[a_onclick onclick; Html5.Custom_data.attrib my_unit_unit cv] [ pcdata label ] ) let client_values = My_appl.register_service ~path:["client_values"] ~get_params:Eliom_parameter.unit (fun () () -> (* let w = 1 in *) Eliom_service.onload' {{ fun () -> debug "hic %s" ( %b "x") }}; Lwt.return Html5.F.( html (Eliom_tools.F.head ~title:"client_values" ()) (body [ ]) )) }} ******************************************************************************) {shared{ let elt src = ignore {unit{ debug "creating elt from %s" %src }}; Html5.F.(div ~a:[a_onclick {{ fun _ -> debug "click!"}}] [pcdata ("click ("^src^")")]) }} let test_simple = let description = "test" in let path = ["client_values"; "simple"] in let v1 = "v1" in let v2 = {string{ debug "init v2"; "v2" }} in let v3 = {string{ Printf.sprintf "(v3 v1:%s)" %v1 }} in let v4 = {string{ Printf.sprintf "(v4 v2:%s)" %v2 }} in let v5 = {string{ Printf.sprintf "(v5 v4:%s)" %v4 }} in let v6 = {unit->string{ debug "init v6"; fun () -> Printf.sprintf "arg: %s %s %s %s %s" %v1 %v2 %v3 %v4 %v5 }} in description, Eliom_testsuite_base.My_appl.register_service ~path ~get_params:Eliom_parameter.unit (fun () () -> let () = Eliom_service.onload {{ fun _ -> Dom.appendChild (Dom_html.document##body) (Eliom_content.Html5.To_dom.of_div (elt "client")) }} in Lwt.return Html5.F.( html (Eliom_tools.F.head ~title:(String.concat "/" path) ~css:[["style.css"]] ()) (body [ h2 [pcdata description]; div ~a:[a_class ["thebutton"]; a_onclick {{ debug "init handler"; fun _ -> debug "%s" ( %v6 ()) }}] [ pcdata "Click me" ]; a ~service:Eliom_service.void_coservice' [pcdata "self"] (); elt "server"; ]) )) (******************************************************************************) (* Client event handler syntax *) {shared{ let wrap str = "(wrap "^str^")" let shared_value = "shared_value" let shared_onclick context = {{ debug "init shared_onclick from %s" %context; fun _ -> let shared_value = "shared_inner_value" in ignore shared_value; let str = "shared_onclick from " ^ %context ^ ": \n" ^ " %shared_value=\"" ^ %shared_value ^ "\"\n" ^ " %(wrap shared_value)=\"" ^ %(wrap shared_value) ^ "\"" in Dom_html.window##alert(Js.string str) }} let unused_handler = {unit->unit{ fun () -> (assert false) }} }} {client{ let client_value = "client_value" let client_tests = [ ( let client_onclick = {{ debug "init client_onclick"; fun _ -> let client_value = "client_inner_value" in ignore client_value; let str = "client_onclick: \n" ^ " %shared_value=\"" ^ %shared_value ^ "\"\n" ^ " %client_value=\"" ^ %client_value ^ "\"\n" ^ " %(wrap shared_value)=\"" ^ %(wrap shared_value) ^ "\"\n" ^ " %(wrap client_value)=\"" ^ %(wrap client_value) ^ "\"\n" in Dom_html.window##alert(Js.string str) }} in Html5.F.( li ~a:[a_onclick client_onclick] [ pcdata "client_onclick: %shared_value=\"shared_value\" %client_value=\"client_value\""] ) ); Html5.F.( li ~a:[a_onclick (shared_onclick "client")] [pcdata "shared_onclick from client: %shared_onclick=\"shared_value\""] ); ] let unused_handler_client = {unit->unit{ fun () -> (assert false) }} }} {server{ let server_value = "server_value" let server_tests = [ ( let server_onclick = {{ debug "init server_onclick"; fun _ -> let shared_value = "shared_inner_server_value" in let server_value = "server_inner_value" in ignore (shared_value, server_value); let str = "server_onclick: \n" ^ " %shared_value=\"" ^ %shared_value ^ "\"\n" ^ " %server_value=\"" ^ %server_value ^ "\"\n" ^ " %(wrap shared_value)=\"" ^ %(wrap shared_value) ^ "\"\n" ^ " %(wrap server_value)=\"" ^ %(wrap server_value) ^ "\"\n" in Dom_html.window##alert(Js.string str) }} in Html5.F.( li ~a:[a_onclick server_onclick] [pcdata "server_onclick: %shared_value=\"shared_value\" %server_value=\"server_value\""] ) ); Html5.F.( li ~a:[a_onclick (shared_onclick "server")] [pcdata "shared_onclick from server: %shared_value=\"shared_value\""] ); ] let unused_handler_server = {unit->unit{ fun () -> (assert false) }} }} {shared{ let shared_tests context = [ Html5.F.( li ~a:[a_onclick (shared_onclick (context^" via shared"))] [ pcdata ("shared_onclick from " ^ context ^ " via shared: " ^ "%shared_value=\"shared_value\"") ] ) ] }} let client_handler_syntax = let description = "Client event handler syntax" in let path = ["client_handler_syntax"] in description, Eliom_testsuite_base.My_appl.register_service ~path ~get_params:Eliom_parameter.unit (fun () () -> let open Html5.F in let tests_id = Html5.Id.new_elt_id ~global:false () in Eliom_service.onload {{ fun _ -> List.iter (Html5.Manip.Named.appendChild %tests_id) Html5.F.([ div [pcdata "Client elements"]; ul (client_tests); ]); List.iter (Html5.Manip.Named.appendChild %tests_id) Html5.F.([ div [pcdata "Shared elements used from client"]; ul (shared_tests "client"); ]) }}; Lwt.return (html (Eliom_tools.F.head ~title:(String.concat "/" path) ()) (body [ h1 [pcdata description]; Html5.Id.create_named_elt ~id:tests_id (div [ div [pcdata "Server elements"]; ul server_tests; div [pcdata "Shared elements from server"]; ul (shared_tests "server") ]) ]))) (******************************************************************************) (******************************************************************************) (* Client event handler syntax 2 *) {shared{ type hidden_widget = { content : Html5_types.div_content Html5.elt list; widget_id : Html5_types.flow5 Html5.Id.id; overlay_id : Html5_types.div Html5.Id.id; container_id : Html5_types.div Html5.Id.id; mutable show_callback : (unit -> unit) option; mutable content_getter : (unit -> Html5_types.div_content Html5.elt list Lwt.t) option; mutable set_content_thread : unit Lwt.t option; } }} {shared{ let hidden_widget content = { content = (content :> Html5_types.div_content Html5.elt list); widget_id = Html5.Id.new_elt_id (); overlay_id = Html5.Id.new_elt_id (); container_id = Html5.Id.new_elt_id (); show_callback = None; content_getter = None; set_content_thread = None; } let hidden_widget_html w = let open Html5.F in let container = Html5.Id.create_named_elt ~id:w.container_id (div ~a:[a_class ["container"]; a_style "display: none"] w.content) in let onclick_overlay = {{ fun _ -> Html5.(Manip.SetCss.display (Id.get_element %w.overlay_id) "none"); Html5.(Manip.SetCss.display (Id.get_element %w.container_id) "block"); Option.iter (fun f -> f ()) %w.show_callback; Option.iter (fun f -> let waiter, wakener = Lwt.task () in let t = lwt () = waiter in lwt content = f () in Html5.Manip.Named.replaceAllChild %w.container_id content; Lwt.return () in Lwt.wakeup wakener (); %w.set_content_thread <- Some t) %w.content_getter; () }} in let overlay = Html5.Id.create_named_elt ~id:w.overlay_id (div ~a:[ a_class ["overlay"]; a_onclick onclick_overlay; ] [ pcdata "click to show"; ]) in Html5.Id.create_named_elt ~id:w.widget_id (div ~a:[a_class ["hidden_widget"]] [ overlay; container; ]) }} {client{ let hidden_widget_hide w _ = Html5.Manip.Named.replaceAllChild w.container_id w.content; Html5.(Manip.SetCss.display (Id.get_element w.overlay_id) "block"); Html5.(Manip.SetCss.display (Id.get_element w.container_id) "none"); Option.iter (fun t -> Lwt.cancel t; w.set_content_thread <- None) w.set_content_thread; () let hidden_widget_set_show_callback w f = w.show_callback <- Some f let hidden_widget_set_content_getter w f = w.content_getter <- Some f }} let get_slow_content = Eliom_registration.Ocaml.register_coservice' ~get_params:Eliom_parameter.unit (fun () () -> let sleep_time = 1.0 +. Random.float 4.0 in debug "Sleep %f" sleep_time; lwt () = Lwt_unix.sleep sleep_time in Lwt.return Html5.F.([ (h2 [pcdata "Slow content"] :> Html5_types.div_content Html5.elt); p [pcdata (Printf.sprintf "Had to sleep %f seconds for this" sleep_time)]; ])) let client_handler_syntax_2 = Eliom_testsuite_base.My_appl.register_service ~path:["client_handler_syntax_2"] ~get_params:Eliom_parameter.unit (fun () () -> let w = hidden_widget [Html5.F.pcdata "waiting ..."] in let hide_button = Html5.D.( button ~a:[a_onclick {{ hidden_widget_hide %w }} ] ~button_type:`Submit [ pcdata "Hide again"; ]) in let add_another_waiter_button = let onclick = {{ fun _ -> let w = hidden_widget [Html5.F.pcdata "Incredible content!"] in ignore (Dom.appendChild (Dom_html.document##body) (Html5.To_dom.of_element (hidden_widget_html w))) }} in Html5.D.( button ~a:[a_onclick onclick ] ~button_type:`Submit [ pcdata "Add another waiter widget"; ]) in Eliom_service.onload {{ fun _ -> hidden_widget_set_show_callback %w (fun () -> ignore (Dom.insertBefore (Dom_html.document##body) (Html5.To_dom.of_element %hide_button) Js.null)); hidden_widget_set_content_getter %w (fun () -> Eliom_client.call_caml_service ~service: %get_slow_content () ()) }}; Lwt.return Html5.F.( html (Eliom_tools.F.head ~title:"client_handler_syntax_2" ~css:[["style.css"]] ()) (body [ h1 [pcdata "Real world shared event handler"]; hidden_widget_html w; add_another_waiter_button; ]))) (******************************************************************************) {shared{ let shared_onclick source = {{ debug "init shared_onclick from %s" %source; fun _ -> debug "shared_onclick from %s" %source }} }} {client{ }} let client_values_shared = let description = "shared client values" in let path = ["client_values"; "shared"] in description, Eliom_testsuite_base.My_appl.register_service ~path ~get_params:Eliom_parameter.unit (fun () () -> Lwt.return Html5.F.( html (Eliom_tools.F.head ~title:(String.concat "/" path) ~css:[["style.css"]] ()) (body [ h2 [pcdata description]; div ~a:[a_class ["thebutton"]; a_onclick (shared_onclick "server")] [ pcdata "Click me" ]; a ~service:Eliom_service.void_coservice' [pcdata "self"] (); ]) )) (******************************************************************************) (* Client values onload *) {shared{ let shared_elt src = ignore {unit{ debug "init shared onload from ??" (* %src *); Lwt.ignore_result (lwt () = Eliom_client.wait_load_end () in debug "shared onload from %s" %src; Lwt.return ()) }}; Html5.F.(div [pcdata ("shared elt from "^src)]) }} {client{ let client_elt = ignore {unit{ debug "init client onload" ; Lwt.ignore_result (lwt () = Eliom_client.wait_load_end () in debug "client onload"; Lwt.return ()) }}; Html5.F.(div [pcdata "client elt"]) }} {server{ let server_elt = ignore {unit{ debug "init server onload"; Lwt.ignore_result (lwt () = Eliom_client.wait_load_end () in debug "server onload"; Lwt.return ()) }}; Html5.F.(div [pcdata "server elt"]) }} let client_values_onload = let description = "client values onload" in let path = ["client_values"; "onload"] in description, Eliom_testsuite_base.My_appl.register_service ~path ~get_params:Eliom_parameter.unit (fun () () -> ignore {unit{ Lwt.ignore_result (lwt () = Eliom_client.wait_load_end () in Dom.appendChild (Dom_html.document##body) (Html5.To_dom.of_div (shared_elt "client")); Dom.appendChild (Dom_html.document##body) (Html5.To_dom.of_div client_elt); Lwt.return ()) }}; Lwt.return Html5.F.( html (Eliom_tools.F.head ~title:(String.concat "/" path) ~css:[["style.css"]] ()) (body [ h2 [pcdata description]; div ~a:[a_class ["thebutton"]; a_onclick (shared_onclick "server")] [ pcdata "Click me" ]; a ~service:Eliom_service.void_coservice' [pcdata "self"] (); server_elt; shared_elt "server"; ]) )) (******************************************************************************) (******************************************************************************) (* Escaped in client *) let time () = Int64.of_float (Unix.gettimeofday () *. 1000.0) let global_value = time () let client_process, request = let client_process_counter = ref 0 in let request_counter = ref 0 in Eliom_reference.eref_from_fun ~scope:Eliom_common.client_process (fun () -> request_counter := 0; incr client_process_counter; !client_process_counter), Eliom_reference.Volatile.eref_from_fun ~scope:Eliom_common.request (fun () -> incr request_counter; !request_counter) let s = Eliom_registration.Action.register_coservice' ~get_params:Eliom_parameter.unit (fun () () -> debug "escaped_in_client: the action"; Lwt.return ()) {client{ let show_server_injections _ = debug "global_value: %Ld\n\ client_process: %d\n\ request: %d\n\ time: %Ld" %global_value %client_process %request %(time ()) let () = debug "global_value: %Ld" %global_value let link () = Html5.F.(div [(*a ~service: %s [pcdata "Action! (on server)"] ()*)]) }} let escaped_in_client = let description = "Escaping server values on the client" in let path = ["escape"; "on_client"] in description, Eliom_testsuite_base.My_appl.register_service ~path ~get_params:Eliom_parameter.unit (fun () () -> lwt cp = Eliom_reference.get client_process in debug "global_value: %Ld\n\ client_process: %d\n\ request: %d\n\ time: %Ld" global_value cp (Eliom_reference.Volatile.get request) (time ()); ignore {unit{ Lwt.ignore_result (lwt () = Eliom_client.wait_load_end () in show_server_injections (); Dom.appendChild (Dom_html.document##body) (Html5.To_dom.of_element (link ())); Lwt.return ()) }}; Lwt.return Html5.F.( html (Eliom_tools.F.head ~title:(String.concat "/" path) ~css:[["style.css"]] ()) (body [ h2 [pcdata description]; div ~a:[a_class ["thebutton"]; a_onclick {{ show_server_injections }}] [ pcdata "Click me" ]; div [ a ~service:Eliom_service.void_coservice' [pcdata "reload in app"] () ]; ]) )) (******************************************************************************) (******************************************************************************) (* Client value injection *) {client{ let log = Eliom_testsuite_base.log }} let server_constant = Random.int 100 let request_reference = Eliom_reference.Volatile.eref_from_fun ~scope:Eliom_common.request (fun () -> Random.int 100) let deep_client_value = Some {string{ "client value" }} let an_elt = Html5.D.div [] {client{ let () = Lwt.ignore_result (lwt () = Eliom_client.wait_load_end () in debug "---> !!! 2"; Html5.Manip.appendChild %an_elt Html5.F.(pcdata "!!! 2"); Lwt.return ()); log "client TOP" let f () = log "server_constant: %d" %server_constant; log "request_reference: %d" %request_reference; match %deep_client_value with | Some s -> log "deep client string: %s" s | None -> () let () = f () }} let deep_client_values = let counter = ref 0 in Eliom_testsuite_base.test ~title:"Client value injections" ~path:["client"; "injections"] ~description:Html5.F.([ pcdata "" ]) (fun () -> debug "server_constant: %d" server_constant; debug "request_reference: %d" (Eliom_reference.Volatile.get request_reference); let request_counter = incr counter; Printf.sprintf "--%d--" !counter in let onclick = {{ (* FIXME BB enable escaping of Eliom_references *) (* debug "----> request_reference': %d" %request_reference; *) begin match %deep_client_value with | Some client_value -> log "deep client string: %s" client_value | None -> assert false end; f (); fun _ -> log "request_counter: %s" %request_counter }} in Eliom_service.onload {{ fun _ -> debug "---> !!! 1"; Html5.Manip.appendChild %an_elt Html5.F.(pcdata "!!! 1") }}; Lwt.return Html5.F.([ div [a ~service:Eliom_service.void_coservice' [pcdata "reload in app"] ()]; div [ button ~a:[a_onclick onclick ] ~button_type:`Submit [ pcdata "Click"; ] ]; an_elt; ])) (******************************************************************************) *) (* XXX Two times the same code, once in shared (with variable names postix _shared and once in client (with variable names postfix _client). *) {server{ let injection_scoping_shared_v1 = "server1" }} {shared{ let injection_scoping_shared_v1 = "shared1" let injection_scoping_shared () = Eliom_testsuite_base.assert_equal ~name:"injection_scoping_shared_v1" injection_scoping_shared_v1 "shared1"; debug "%%injection_scoping_shared_v1=%s (server1)" %injection_scoping_shared_v1; Eliom_testsuite_base.assert_equal ~name:"%injection_scoping_shared_v1" %injection_scoping_shared_v1 "server1"; () }} {server{ let injection_scoping_client_v1 = "server1" }} {client{ let injection_scoping_client_v1 = "client1" let injection_scoping_client () = Eliom_testsuite_base.assert_equal ~name:"injection_scoping_client_v1" injection_scoping_client_v1 "client1"; Eliom_testsuite_base.assert_equal ~name:"%injection_scoping_client_v1" %injection_scoping_client_v1 "server1"; () }} let test_injection_scoping = let title = "Scoping of injections in client/shared-section" in Eliom_testsuite_base.test ~title ~path:["holes"; "injection_scoping"] ~description:Html5.F.([ p [pcdata "Test, which value is referenced by "; Eliom_testsuite_base.monospace "v"; pcdata " and an injection "; Eliom_testsuite_base.monospace "%%v"; pcdata " in cases like:"]; pre [pcdata "\ {server{\n let v = ...\n }}\n {client/shared{\n let v = ...\n let _ = v, %v (* <-- here *) }}"]; p [pcdata "There must be 4 tests mentioned in the client logger (two each \ from testing shared and client), and 2 in the server output."]; ]) (fun () -> injection_scoping_shared (); Eliom_testsuite_base.report_flush_assertions title; ignore {unit{ injection_scoping_shared (); injection_scoping_client (); Eliom_testsuite_base.report_flush_assertions %title; }}; Lwt.return Html5.F.([])) (******************************************************************************) (* XXX Two times the same code, once in shared (with variable names postix _shared and once in server (with variable names prefixed by escaping_scoping_server_). *) {client{ let escaping_scoping_server_v1 = "client1" }} {server{ let escaping_scoping_server_v1 = "server1" let escaping_scoping_server () = ignore {unit{ Eliom_testsuite_base.assert_equal ~name:"escaping_scoping_server_v1" escaping_scoping_server_v1 "client1"; Eliom_testsuite_base.assert_equal ~name:"%escaping_scoping_server_v1" %escaping_scoping_server_v1 "server1"; let escaping_scoping_server_v1 = "inner1" in Eliom_testsuite_base.assert_equal ~name:"escaping_scoping_server_v1 (with inner)" escaping_scoping_server_v1 "inner1"; Eliom_testsuite_base.assert_equal ~name:"%escaping_scoping_server_v1 (with inner)" %escaping_scoping_server_v1 "server1"; () }} }} {client{ let escaping_scoping_shared_v1 = "client1" }} {shared{ let escaping_scoping_shared_v1 = "shared1" let escaping_scoping_shared () = ignore {unit{ Eliom_testsuite_base.assert_equal ~name:"escaping_scoping_shared_v1" escaping_scoping_shared_v1 "client1"; Eliom_testsuite_base.assert_equal ~name:"%escaping_scoping_shared_v1" %escaping_scoping_shared_v1 "shared1"; let escaping_scoping_shared_v1 = "inner1" in Eliom_testsuite_base.assert_equal ~name:"escaping_scoping_shared_v1 (with inner)" escaping_scoping_shared_v1 "inner1"; Eliom_testsuite_base.assert_equal ~name:"%escaping_scoping_shared_v1 (with inner)" %escaping_scoping_shared_v1 "shared1"; () }} }} let test_escaping_scoping = let title = "Scoping of escaped variables in server/shared-section" in Eliom_testsuite_base.test ~title ~path:["holes"; "escaping_scoping"] ~description:Html5.F.([ p [pcdata "Test, which value is referenced by "; Eliom_testsuite_base.monospace "v"; pcdata " and an escaped variable "; Eliom_testsuite_base.monospace "%%v"; pcdata " in cases like:"]; pre [pcdata "\ {client{\n let v = ...\n }}\n {server/shared{\n let v = ...\n let _ = {{ v, %v (* <-- here *) }} }}"]; p [pcdata "There must be 12 tests mentioned in the client logger (4 from \ testing the server-section, and each 4 from testing the shared-section \ in client and server), and 0 in the server output."]; ]) (fun () -> escaping_scoping_shared (); escaping_scoping_server (); Eliom_testsuite_base.report_flush_assertions title; ignore {unit{ escaping_scoping_shared (); Eliom_testsuite_base.report_flush_assertions %title; }}; Lwt.return Html5.F.([])) (******************************************************************************) let test_server_function = let f str = if str = "" then Lwt.fail (Failure "Empty string") else let strstr = str ^ str in debug "test_server_function: received %S sending %S" str strstr; Lwt.return (str ^ str) in let rpc_f = server_function Json.t f in Eliom_testsuite_base.test ~title:"RPC / server functions" ~path:["mixed"; "server_function"] ~description:Html5.F.([ pcdata "Server functions make functions from the server available on the client."; br (); pcdata "Click the button to send the content of the field to the server, where it \ logged to the console, and sent back doubled (in the client logger)"; pcdata "If you send the empty string, however, an exception is raised on the server."; ]) (fun () -> let field = Html5.D.raw_input ~input_type:`Text () in let onclick = {{ fun _ -> let field_dom = Html5.To_dom.of_input %field in let str = Js.to_string field_dom##value in field_dom##value <- Js.string ""; Lwt.async (fun () -> try_lwt lwt strstr = %rpc_f str in Eliom_testsuite_base.log "Sent %S received %S" str strstr; Lwt.return () with Exception_on_server str -> Eliom_testsuite_base.log "Exception on server: %s" str; Lwt.return ()) }} in Lwt.return Html5.F.([ Eliom_testsuite_base.thebutton ~msg:"send" onclick; br (); field; ])) (******************************************************************************) {server{ let () = ignore {unit{ Eliom_testsuite_base.log "STEP 0" }} let client_value_initialization_a = "1" let client_value_initialization_b = "2" }} {client{ let client_value_initialization_x1 = 2 let () = Eliom_testsuite_base.log "STEP %s" %client_value_initialization_a let () = Eliom_testsuite_base.log "STEP %s" %client_value_initialization_b }} {server{ let () = ignore {unit{ Eliom_testsuite_base.log "STEP 3" }} let client_value_initialization_f (x : int client_value) : unit client_value = {{ Eliom_testsuite_base.log "STEP %d" %x }} let client_value_initialization_y1 = client_value_initialization_f {{ client_value_initialization_x1 }} }} {client{ let client_value_initialization_x2 = 5 let client_value_initialization_f2 () = let () = ignore %client_value_initialization_a in let () = ignore %client_value_initialization_a in let () = ignore %(debug "STEP 0") in let () = ignore %(debug "STEP 1") in () }} {server{ let client_value_initialization_y2 = client_value_initialization_f {{ client_value_initialization_x2 }} let () = ignore {unit{ Eliom_testsuite_base.log "STEP 6" }} }} let client_value_initialization = Eliom_testsuite_base.test ~title:"Order of initializations of client values" ~path:["holes"; "client_value_initialization"] ~description:Html5.F.([ p [pcdata "The client logger should show the STEPs 0-6"]; p [pcdata "The server output should show STEPs 0-1"]; ]) (fun () -> ignore {unit{ client_value_initialization_f2 () }}; Lwt.return []) let late_unwrap_unwrap_id = 1001 {server{ type 'a late_unwrap_marked_for_unwrap = 'a * Eliom_wrap.unwrapper }} {client{ type 'a late_unwrap_marked_for_unwrap = 'a }} {shared{ type late_unwrap_base = string type late_unwrap_t = late_unwrap_base late_unwrap_marked_for_unwrap }} let late_unwrap_unwrapper = Eliom_wrap.create_unwrapper (Eliom_wrap.id_of_int late_unwrap_unwrap_id) let late_unwrap_value : late_unwrap_t = "one", late_unwrap_unwrapper let late_unwrap_values : late_unwrap_t list = [late_unwrap_value; late_unwrap_value] let late_unwrap_other_value : late_unwrap_t = "other", late_unwrap_unwrapper {client{ let () = Eliom_unwrap.register_unwrapper (Eliom_unwrap.id_of_int %late_unwrap_unwrap_id) (fun (v, _) -> Eliom_testsuite_base.log "Called unwrapper for %S" v; "!" ^ v ^ "!") }} {client{ let () = Eliom_testsuite_base.log "Value %s" %late_unwrap_value }} {client{ let () = Eliom_testsuite_base.log "Value %s" %late_unwrap_value; Eliom_testsuite_base.log "Values %s" (String.concat ", " (List.map String.escaped %late_unwrap_values)); Eliom_testsuite_base.log "Other value %s" %late_unwrap_other_value }} let late_unwrap = Eliom_testsuite_base.test ~title:"Late unwrapping" ~path:["holes"; "late_unwrap"] ~description:Html5.F.([ p [pcdata "On the server, two values, \"one\" and \"other\" are created and furnished with an unwrapper. The former is also shared in a list."]; p [pcdata "Values with the respective unwrapper are unwrapped on the client side by adding an exclamation mark at the start and end of the string"]; p [pcdata "The following result should be observed (in logging output)"]; ol [ li [pcdata "Called unwrapper for \"fresh\""]; li [pcdata "Called unwrapper for \"other\""]; li [pcdata "Called unwrapper for \"one\""]; li [pcdata "Value \"!one!\""]; li [pcdata "Value \"!one!\""]; li [pcdata "Values \"!one\", \"!one!\""]; li [pcdata "Other value \"!other!\""]; ]; p [pcdata "And 'Fresh \"!fresh!\"' for every request"]; ]) (fun () -> let fresh : late_unwrap_t = "fresh", late_unwrap_unwrapper in ignore {unit{ Eliom_testsuite_base.log "Fresh %s" %fresh }}; Lwt.return []) (******************************************************************************) let wrap_handler = let state = Eliom_reference.eref ~scope:Eliom_common.default_session_scope None in let service = Eliom_testsuite_base.My_appl.register_coservice' ~get_params:Eliom_parameter.unit (Eliom_tools.wrap_handler (fun () -> Eliom_reference.get state) (fun () () -> Lwt.return Html5.F.(html (head (title (pcdata "not set")) []) (body [pcdata "not set"]))) (fun value () () -> Lwt.return Html5.F.(html (head (title (pcdata "set")) []) (body [Printf.ksprintf pcdata "set to %d." value])))) in let set_state = let counter = ref 0 in Eliom_registration.Unit.register_coservice' ~get_params:Eliom_parameter.unit (fun () () -> lwt () = Eliom_reference.set state (incr counter; Some !counter) in Lwt.return ()) in let unset_state = Eliom_registration.Unit.register_coservice' ~get_params:Eliom_parameter.unit (fun () () -> lwt () = Eliom_reference.set state None in Lwt.return ()) in Eliom_testsuite_base.test ~title:"Wrap handler" ~path:["mixed"; "wrap_handler"] ~description:Html5.F.([ pcdata "The links 'set state' and 'unset state' allow to modify a state."; pcdata "The link 'test state' show whether the state is set or not."; ]) (fun () -> Lwt.return Html5.F.([ ul [ li [a ~service [pcdata "test state"] ()]; li [a ~service:set_state [pcdata "set state"] ()]; li [a ~service:unset_state [pcdata "unset state"] ()]; ]])) (******************************************************************************) {client{ Eliom_config.set_tracing true }} let cross_change_page_client_values = let global_client_ref = {string ref{ ref "initial" }} in Eliom_testsuite_base.test ~title:"Cross change page client values" ~path:["holes";"cross_change_page"] ~description:Html5.F.([ ul [ li [pcdata "The server keeps a global client value with a client reference. \ It is logged on each change_page (reload within application). \ The server adds an prime to that string on each reload"] ] ]) (fun () -> ignore {unit{ Eliom_testsuite_base.log "Global client reference is %S" ! %global_client_ref; %global_client_ref := ! %global_client_ref ^ "'" }}; Lwt.return Html5.F.([ ])) (******************************************************************************) let tests = [ "Mixed", [ test_custom_data; test_server_function; wrap_handler; ]; "Holes", [ test_injection_scoping; test_escaping_scoping; test_client_value_on_caml_service; node_bindings; data_sharing; client_value_initialization; late_unwrap; cross_change_page_client_values; (* test_simple; client_values_injection; client_values_mutability; client_values_changing_context; client_values_initialization; client_values_shared; client_handler_syntax; client_values_onload; escaped_in_client; deep_client_values; *) ]; ] eliom-3.0.3/tests/eliom_testsuite3.eliom0000644000000000000000000044032512062377521016517 0ustar0000000000000000(*zap* *) {shared{ open Eliom_lib open Eliom_content open Ocsigen_cookies }} (* *zap*) (*zap* This is the Eliom documentation. You can find a more readable version of comments on http://www.ocsigen.org *zap*) (*wiki* %<||1>% %
%>% %
Lwt.return (make_page [p ~a:[(*zap* *)a_class ["clickable"];(* *zap*) (* with {{ expr }}, the expression is executed by the client. *) a_onclick {{ fun _ -> Dom_html.window##alert(Js.string "clicked!") }} ] [pcdata "I am a clickable paragraph"]; ])) (*wiki* All services belonging to the application will be entry points to the application. It means that if you call such a service, the client side code will be sent to the browser, and the client side execution will start, //and will not stop if you go to another service belonging to the same application!// ====Compiling //soon (have a look at Ocsigen source for now -- //examples// directory)// ====Using a distant Eliom service in client side code For now, the syntax extension has not been implemented, thus the syntax is somewhat more complicated. Here are some examples of what you can do: *wiki*) let eliomclient2 = service ~path:["plip"; "eliomclient2"] ~get_params:unit () let myblockservice = Eliom_registration.Flow5.register_post_coservice ~fallback:eliomclient2 ~post_params:unit (fun () () -> Lwt.return [p [pcdata ("I come from a distant service! Here is a random value: "^ string_of_int (Random.int 100))]]) let eliom_caml_tree = Eliom_registration.Ocaml.register_post_coservice' ~post_params:unit (fun () () -> Lwt.return Html5.F.(([div [p [pcdata "Coucou, voici un Div construit avec TyXML sur le serveur"]; ul [li [pcdata "item1"]; li [pcdata "item2"]; li [pcdata "item3"]; ]; p [a ~service:eliomclient1 [pcdata "Lien"] ()]; p ~a:[a_onclick {{ fun _ -> Dom_html.window##alert(Js.string "clicked!") }}] [pcdata "I am a clickable paragraph"]; ]] : Html5_types.div elt list))) ;; (* This ";;" is necessary in order to have the "shared" following entry being parsed as "str_item" (instead of "expr"). This is Camlp4 related, it may evolve. *) {shared{ let item () = li [pcdata Sys.ocaml_version] }} ;; let _ = My_appl.register eliomclient2 (fun () () -> Lwt.return (make_page ([ (*wiki* The following example shows how to go to another service, exactly like pressing a link (here a service that do not belong to the application): *wiki*) p ~a:[(*zap* *)a_class ["clickable"];(* *zap*) a_onclick {{ fun _ -> Eliom_client.exit_to ~service: %Eliom_testsuite1.coucou (* just as [coucou] *) () () }} ] [pcdata "Link to a service outside the Eliom application, with exit_to"]; (*wiki* To use server values inside client code one should use the syntax {{{ %id }}} where and {{{id}}} an identifier for the value. *wiki*) (*zap* *) (*wiki* The following examples shows how to do a request to a service, and use the content: *wiki*) (* p ~a:[(*zap* *)a_class ["clickable"];(* *zap*) a_onclick ((fun.client (myblockservice : (unit, unit, 'c, 'd, 'e, 'f, 'g, Eliom_service.http) Eliom_service.service) -> let body = Dom_html.document##body in (*Js_old.get_element_by_id "bodyid"*) Eliom_client.call_service ~service:myblockservice () () >>= fun s -> (try let l = Js_old.Node.children (Js_old.dom_of_xml s) in List.iter (Js_old.Node.append body) l with e -> Js_old.alert (Printexc.to_string e)); (* does not work with chrome. A solution is probably to use set "innerHTML". *) Lwt.return () ) myblockservice) ] [pcdata "Click here to add content from the server."]; *) (* *zap*) (*wiki* The following examples shows how to change the current page, without stopping the client side program. *wiki*) p ~a:[(*zap* *)a_class ["clickable"];(* *zap*) a_onclick {{ fun _ -> ignore(Eliom_client.change_page ~service:%eliomclient1 () ()) }} ] [pcdata "Click here to change the page without stopping the program (with change_page)."]; p ~a:[(*zap* *)a_class ["clickable"];(* *zap*) a_onclick {{ fun _ -> Eliom_client.exit_to ~service:%eliomclient2 () () }} ] [pcdata "Click here to relaunch the program by reloading the page (with exit_to)."]; p ~a:[(*zap* *)a_class ["clickable"];(* *zap*) a_onclick {{ fun _ -> ignore(Eliom_client.change_page ~service:%eliomclient1 () ()) }} ] [pcdata "A generic client-side function for calling "; code [pcdata "change_page"]; pcdata "."]; (*wiki* The following examples shows how to get a subpage from the server, and put it inside your page. *wiki*) p ~a:[(*zap* *)a_class ["clickable"];(* *zap*) a_onclick {{ fun _ -> ignore (try_lwt Eliom_client.call_caml_service ~service:%eliom_caml_tree () () >|= fun blocks -> List.iter (Dom.appendChild Dom_html.document##body) (List.map Html5.To_dom.of_element blocks) with | e -> Dom_html.window##alert(Js.string (Printexc.to_string e)); Lwt.return () ) }} ] [pcdata "Click here to get a subpage from server."]; (*wiki* ====Refering to parts of the page in client side code *wiki*) (let container = Html5.D.ul [ item () ; item () ; item ()] in div [p ~a:[(*zap* *)a_class ["clickable"];(* *zap*) a_onclick {{ fun _ -> Dom.appendChild (Html5.To_dom.of_ul %container) (* node is the wrapper keyword for HTML5.M nodes. *) (Html5.To_dom.of_li (item ())) }} ] [pcdata "Click here to add an item below with the current version of OCaml."]; container]); (*wiki* ====Refering to server side data in client side code In the case you want to send some server side value with your page, just do: *wiki*) (let my_value = 1.12345 in p ~a:[(*zap* *)a_class ["clickable"];(* *zap*)a_onclick {{ fun _ -> Dom_html.window##alert (Js.string (string_of_float %my_value)) }} ] [pcdata "Click here to see a server side value sent with the page."]); (*wiki* ====Other tests *wiki*) p ~a:[(*zap* *)a_class ["clickable"];(* *zap*) a_onclick {{ fun _ -> let coucou = %Eliom_testsuite1.coucou in let eliomclient1 = %eliomclient1 in (Dom.appendChild (Dom_html.document##body) (Html5.To_dom.of_p (p [Html5.D.a ~service:coucou [pcdata "An external link generated client side"] (); pcdata ", "; Html5.D.a (*zap* *)~a:[a_class ["clickable"]](* *zap*) ~service:eliomclient1 [pcdata "another, inside the application, "] (); pcdata " and "; span ~a:[a_class ["clickable"]; a_onclick (fun _ -> Dom_html.window##alert(Js.string "clicked!"))] [pcdata "Here a client-side span with onclick"] ] )) ) }} ] [pcdata "Click here to add client side generated links."]; ]))) (*wiki* ====Using OCaml values as service parameters It is now possible to send OCaml values to services. To do that, use the {{{Eliom_parameter.caml}}} function: *wiki*) {shared{ type ec3 = (int * string * string list) deriving (Json) }} let caml_value : ec3 = (299, "oo", ["a";"b";"c"]) let eliomclient3' = My_appl.register_post_coservice' ~post_params:(caml "isb" Json.t) (fun () (i, s, l as v) -> Lwt.return (make_page [p [if v = caml_value then pcdata "The expected data were correctly received." else pcdata "Do not received the expected data."]; p (pcdata (Printf.sprintf "i = %d, s = %s" i s):: List.map (fun a -> pcdata a) l )])) let eliomclient3 = My_appl.register_service ~path:["eliomclient3"] ~get_params:unit (fun () () -> Lwt.return (make_page [p ~a:[(*zap* *)a_class ["clickable"];(* *zap*)a_onclick {{ fun _ -> ignore (Eliom_client.change_page ~service:%eliomclient3' () %caml_value) }} ] [pcdata "Click to send Ocaml data as Post parameter"] ])) (*wiki* ====Sending OCaml values using services It is possible to do services that send any caml value. For example: *wiki*) let eliomclient4' = Eliom_registration.Ocaml.register_post_coservice' ~post_params:unit (fun () () -> Lwt.return [1; 2; 3]) let eliomclient4 = My_appl.register_service ~path:["eliomclient4"] ~get_params:unit (fun () () -> Lwt.return (make_page [p ~a:[(*zap* *)a_class ["clickable"];(* *zap*)a_onclick {{ fun _ -> lwt_ignore (let body = Dom_html.document##body in lwt l = Eliom_client.call_caml_service ~service:%eliomclient4' () () in List.iter (fun i -> Dom.appendChild body (Dom_html.document##createTextNode (Js.string (string_of_int i)))) l; Lwt.return ()) }} ] [pcdata "Click to receive Ocaml data and append them below (Should append \"123\")."] ])) (******************************) (* caml service set reference *) (******************************) let ref_caml_service = Eliom_reference.eref ~scope:Eliom_common.default_process_scope None let caml_incr_service = Eliom_registration.Ocaml.register_service ~path:["caml_service_cookies_request"] ~get_params:unit (fun () () -> lwt i = match_lwt Eliom_reference.get ref_caml_service with | None -> Lwt.return 0 | Some i -> Lwt.return i in lwt () = Eliom_reference.set ref_caml_service (Some (succ i)) in Lwt.return i) let text_incr_service = Eliom_registration.String.register_service ~path:["text_service_cookies_request"] ~get_params:unit (fun () () -> lwt i = match_lwt Eliom_reference.get ref_caml_service with | None -> Lwt.return 0 | Some i -> Lwt.return i in lwt () = Eliom_reference.set ref_caml_service (Some (succ i)) in Lwt.return ((string_of_int i),"text/plain")) let caml_service_cookies = My_appl.register_service ~path:["caml_service_cookies"] ~get_params:unit (fun () () -> Lwt.return (make_page [ div ~a:[a_onclick {{ fun _ -> ignore ( lwt i = try_lwt debug "caml_call_service"; Eliom_client.call_caml_service ~service:%caml_incr_service () () with | e -> debug_exn "caml_call_service exception: " e; Lwt.fail e in Dom.appendChild (Dom_html.document##body) (Dom_html.document##createTextNode (Js.string ("ref: "^ string_of_int i ^"; "))); Lwt.return ()) }}] [pcdata "click: caml_service"]; div ~a:[a_onclick {{ fun _ -> ignore ( lwt i = try_lwt debug "call_service"; Eliom_client.call_service ~service:%text_incr_service () () with | e -> debug_exn "call_service exception: " e; Lwt.fail e in Dom.appendChild (Dom_html.document##body) (Dom_html.document##createTextNode (Js.string ("ref: "^ i ^"; "))); Lwt.return ()) }}] [pcdata "click: text service"]; pcdata "when clicking on this div, it should print a value incremented each time"; br (); pcdata "this test verifies that client process cookies are correctly sent with caml value services"; br (); ]) ) let default_no_appl = let module App = Eliom_registration.App (struct let application_name = "eliom_testsuite" end) in let open Html5.D in let id = Html5.Id.new_elt_id ~global:true () in let unique_content = let counter = ref 0 in fun () -> incr counter; pcdata (string_of_int !counter) in let get_service = Eliom_service.service ~path:["no-xhr"] ~get_params:Eliom_parameter.unit () in let post_service = Eliom_service.post_service ~fallback:get_service ~post_params:Eliom_parameter.unit () in let toggle_default_no_appl = Eliom_registration.Action.register_post_coservice' ~post_params:Eliom_parameter.unit (fun () () -> Eliom_config.(set_default_links_xhr (not (get_default_links_xhr ()))); Lwt.return ()) in let handler () () = let global_elt = Html5.Id.create_named_elt ~id (div [pcdata "Named unique content: "; unique_content ()]) in Lwt.return (html (head (title (pcdata "default_link_xhr")) []) (body [ global_elt; div [pcdata "Unique content: "; unique_content ()]; div Html5.D.([ a ~service:get_service [pcdata "Link to self"] (); get_form ~service:get_service (fun () -> [ string_input ~input_type:`Submit ~value:"Get to self" () ]); post_form ~service:post_service (fun () -> [ string_input ~input_type:`Submit ~value:"Post to self" () ]) (); post_form ~service:toggle_default_no_appl (fun () -> [ string_input ~input_type:`Submit ~value:"Toggle" (); pcdata " value of "; code [pcdata "sitedata.default_link_xhr"]; pcdata (Printf.sprintf " (is %b)" (Eliom_config.get_default_links_xhr ())) ]) (); p [ pcdata "You may also try to add the attribute "; code [pcdata "xhr-link='yes'"]; pcdata " or "; code [pcdata "'no'"]; pcdata " into the configuration of your Eliom module."; ] ]) ])) in App.register ~service:get_service handler; App.register ~service:post_service handler; Eliom_service.((get_service : (_, _, get_service_kind, _, _, _, registrable, unit) service)) (*wiki* ====Other tests: *wiki*) let withoutclient = Eliom_service.service ~path:["withoutclient"] ~get_params:unit () let gotowithoutclient = Eliom_service.service ~path:["gotowithoutclient"] ~get_params:unit () let _ = My_appl.register ~options:{Eliom_registration.do_not_launch = true} ~service:withoutclient (fun () () -> Lwt.return (make_page [p [pcdata "If the application was not launched before coming here (or if you reload), this page will not launch it. But if it was launched before, it is still running."]; p ~a:[(*zap* *)a_class ["clickable"];(* *zap*) a_onclick {{ fun _ -> ignore (Eliom_client.change_page ~service:%gotowithoutclient () ()) }} ] [pcdata "Click here to go to a page that launches the application every time (this link does not work if the appl is not launched)."]; p [a (*zap* *)~a:[a_class ["clickable"]](* *zap*) ~service:gotowithoutclient [pcdata "Same link with "; code [pcdata "a"]; pcdata "."] ()]; ])); My_appl.register ~service:gotowithoutclient (fun () () -> Lwt.return (make_page [p [pcdata "The application is launched."]; p ~a:[(*zap* *)a_class ["clickable"];(* *zap*) a_onclick {{ fun _ -> ignore (Eliom_client.change_page ~service:%withoutclient () ()) }} ] [pcdata "Click here to see the page that does not launch the application."]; p [a (*zap* *)~a:[a_class ["clickable"]](* *zap*) ~service:withoutclient [pcdata "Same link with "; code [pcdata "a"]; pcdata "."] ()]; ])) let uri_test = My_appl.register_service ~path:["uritest"] ~get_params:unit (fun () () -> let div = Html5.D.div [ p [pcdata "The following URLs are computed either on server or client side. They should be equal."]; p [pcdata (Eliom_uri.make_string_uri ~service:eliomclient1 ())]; ] in ignore {unit{ Eliom_client.onload (fun _-> Dom.appendChild (Html5.To_dom.of_div %div) (Html5.To_dom.of_p (p [pcdata (Eliom_uri.make_string_uri ~service:%eliomclient1 ())]))) }}; Lwt.return (make_page [div]) ) {client{ let put n f = Printf.ksprintf (fun s -> Dom.appendChild (Html5.To_dom.of_element n) (Html5.To_dom.of_p (p [pcdata s; br ()]))) f let put_li n f = Printf.ksprintf (fun s -> Dom.appendChild (Html5.To_dom.of_element n) (Html5.To_dom.of_li (li [pcdata s]))) f }} let wrapping_big_values = My_appl.register_service ~path:["wrapping_big_values"] ~get_params:(int "size") (fun size () -> let div = Html5.D.div [pcdata (Printf.sprintf "there should be a line with: list length: %i" size); br ()] in let list = Array.to_list (Array.init size (fun i -> i)) in ignore {unit{ Eliom_client.onload (fun _-> put %div "list length: %i" (List.length %list);) }}; Lwt.return (make_page [div])) {shared{ module Wrapping_test = struct type 'a t1 = { v_int : int; v_float : float; v_string : string; (* v_int64 : int64; *) v_service : 'a } end }} let v1 = { Wrapping_test.v_int = 42; v_float = 42.42; v_string = "fourty two"; (* v_int64 = 0x4200000000000000L; *) v_service = eliom_caml_tree } let rec rec_list = 1::2::3::rec_list let react_up = Eliom_react.Up.create (Eliom_parameter.caml "react param" Json.t) let e = Eliom_react.Up.to_react react_up let e' = React.E.map (fun i -> Printf.printf "event: %i\n%!" i) e let rec rec_list_react = (react_up,42)::rec_list_react let global_div = Html5.Id.create_global_elt (div [pcdata "global div"]) let other_global_div = Html5.Id.create_global_elt (div [pcdata "other global div"]) let wrapping1 = Eliom_service.service ~path:["wrapping1"] ~get_params:unit () let gc_service = Eliom_registration.Redirection.register_service ~path:["gc_wrapping1"] ~get_params:Eliom_parameter.unit (fun () () -> Gc.full_major (); Lwt.return wrapping1) let () = My_appl.register wrapping1 (fun () () -> let list = Html5.D.ul [] in (* Simple unwrapping *) ignore {unit{ Eliom_client.onload (fun _ -> let v = %v1 in put_li %list "The following item must be: \"42=42 42.42=42.420000 fourty two=fourty two\""; put_li %list "42=%i 42.42=%f fourty two=%s" v.Wrapping_test.v_int v.Wrapping_test.v_float v.Wrapping_test.v_string; put_li %list "The following line must be: \"1::2::3::1::2::3::1::...\""; ( match %rec_list with | a::b::c::d::e::f::g::_ -> put_li %list "%i::%i::%i::%i::%i::%i::%i::..." a b c d e f g; | _ -> put_li %list "problem with recursive list"; )) }}; (* Node unwrapping and Caml servive *) let content = Html5.D.div [] in let unwrapping_div = div ~a:[ a_onclick {{ fun _ -> let v = %v1 in lwt_ignore (lwt blocks = Eliom_client.call_caml_service ~service:v.Wrapping_test.v_service () () in List.iter (Html5.Manip.appendChild %content) blocks; Lwt.return ()) }} ] [pcdata "Click here to append some content "; pcdata "(received through an caml_service) "; pcdata "at the bottom of the page (test service unwrapping).";] in (* React *) let react_div = p ~a:[ a_onclick {{ fun _ -> let f_react = fst (List.hd %rec_list_react) in ignore (f_react 42) }} ] [pcdata "The string \"event: 42\" should appear on stdout"; pcdata "(of the server) when this is clicked."; pcdata "(test unwrapping of react service)"] in Lwt.return (make_page [ list; unwrapping_div; react_div; content; ] ) ) (*wiki* ====Implicit registration of services to implement distant function calls *wiki*) (*wiki* *wiki*) (*wiki* *wiki*) (*wiki* >% <<|onecol>> >% <<|colprincipale>> *wiki*) (*wiki* %<||2>% *wiki*) (*wiki* ====Comet programming The first example demonstrate server-to-client channel communication. Channels are wrapped and sent to the client. A second example uses channels to transmit occurrences of an event. *wiki*) (* random wait *) let rand_tick = let i = ref 0 in fun () -> Lwt_unix.sleep (float_of_int (2 + (Random.int 2))) >>= fun () -> incr i; Lwt.return (Some !i) let stream1 = Lwt_stream.from rand_tick let _ = Lwt_stream.iter (fun _ -> ()) stream1 let comet1 = My_appl.register_service ~path:["comet1"] ~get_params:unit (fun () () -> let c1 = Eliom_comet.Channel.create (Lwt_stream.clone stream1) in let tick2 = let i = ref 0 in fun () -> Lwt_unix.sleep (float_of_int (6 + (Random.int 6))) >>= fun () -> incr i; Lwt.return (Some !i) in let stream2 = Lwt_stream.from tick2 in let c2 = Eliom_comet.Channel.create stream2 in ignore {unit{ Eliom_client.onload (fun _ -> let _ = Lwt_stream.iter_s (fun i -> Dom.appendChild (Dom_html.document##body) (Dom_html.document##createTextNode (Js.string ("public: "^ string_of_int i ^"; "))) ; Lwt.return () ) %c1 in let _ = Lwt_stream.iter_s (fun i -> Dom.appendChild (Dom_html.document##body) (Dom_html.document##createTextNode (Js.string ("private: "^ string_of_int i ^"; "))) ; Lwt.return () ) %c2 in ()) }}; Lwt.return (make_page [ div [pcdata "To fully understand the meaning of the public channel, \ use a couple browsers on this page."; br (); Html5.D.a ~service:Eliom_testsuite_base.main [pcdata "go outside of application"] ()] ; ]) ) let caml_wrapping_service = Eliom_registration.Ocaml.register_post_coservice' ~post_params:(Eliom_parameter.unit) (fun () () -> Lwt.return (Eliom_comet.Channel.create (Lwt_stream.clone stream1))) let keep_ref v t = ignore (lwt () = Lwt_unix.sleep t in ignore v; Lwt.return ()) let global_channel_wrapping_service = Eliom_registration.Ocaml.register_post_coservice' ~post_params:(Eliom_parameter.unit) (fun () () -> let channel = Eliom_comet.Channel.create ~scope:`Site (Lwt_stream.clone stream1) in keep_ref channel 3.; Lwt.return channel) {client{ let iter_stream_append f c = Lwt_stream.iter_s (fun i -> Dom.appendChild (Dom_html.document##body) (Dom_html.document##createTextNode (Js.string (f i))); (* let some time for the server to gc the channel: if the client is always requesting data on it, there will always be a reference: this allow us to call a Gc.full_major by hand, during that 0.5 seconds, to test the collection.*) lwt () = Lwt_js.sleep 0.5 in Lwt.return () ) c }} let caml_service_wrapping = My_appl.register_service ~path:["caml_service_wrapping"] ~get_params:unit (fun () () -> ignore {unit{ Eliom_client.onload (fun _ -> let c = Eliom_comet.Configuration.new_configuration () in Eliom_comet.Configuration.set_always_active c true) }}; Lwt.return (make_page [ div ~a:[a_class ["clickable"]; a_onclick {{ fun _ -> ignore ( lwt c = Eliom_client.call_caml_service ~service:%caml_wrapping_service () () in try_lwt iter_stream_append (Printf.sprintf "message: %i; ") c with | e -> debug_exn "caml_service_wrapping: exception: " e; Lwt.fail e ) }}] [pcdata "click to create a channel with scope client_process"]; div ~a:[a_class ["clickable"]; a_onclick {{ fun _ -> ignore ( lwt c = Eliom_client.call_caml_service ~service:%global_channel_wrapping_service () () in try_lwt iter_stream_append (Printf.sprintf "site message: %i; ") c with | Eliom_comet.Channel_closed -> Dom.appendChild (Dom_html.document##body) (Dom_html.document##createTextNode (Js.string ("channel closed"))); Lwt.return () | e -> debug_exn "global_channel_wrapping_service: exception: " e; Lwt.fail e ) }}] [pcdata "click to create a channel with scope site: it has a lifetime of 3 seconds: after 3 seconds, there is no garanty on availability of this channel"]; pcdata "when clicking on this link, messages should be received every 1 second"; ]) ) (*wiki* This second example involves client-to-server and server to client event propagation. There is no manual handling of channel, only events are used. *wiki*) let comet2 = My_appl.register_service ~path:["comet2"] ~get_params:unit (fun () () -> (* First create a server-readable client-writable event AKA up event AKA client-to-server asynchronous edge *) let e_up = Eliom_react.Up.create (Eliom_parameter.caml "letter" Json.t) in let e_up_react = Eliom_react.Up.to_react e_up in let e_down = Eliom_react.Down.of_react (React.E.map (function "A" -> "alpha" | "B" -> "beta" | _ -> "what ?") e_up_react ) in let `R _ = React.E.retain e_up_react (fun () -> ignore e_down) in ignore {unit{ Eliom_client.onload (fun _ -> ignore (React.E.map (fun s -> Dom_html.window##alert (Js.string s)) %e_down)) }}; (* We can send the page *) Lwt.return (make_page [ h2 [pcdata "Dual events"] ; div (* This div is for pushing "A" to the server side event *) ~a:[(*zap* *)a_class ["clickable"];(* *zap*)a_onclick {{ fun _-> ignore ( %e_up "A") }} ] [pcdata "Push A"] ; div (* This one is for pushing "B" *) ~a:[(*zap* *)a_class ["clickable"];(* *zap*)a_onclick {{ fun _-> ignore ( %e_up "B") }} ] [pcdata "Push B"] ; ]) ) (*wiki* This third example demonstrates the capacity for simultaneous server push. *wiki*) let comet3 = My_appl.register_service ~path:["comet3"] ~get_params:unit (fun () () -> (* First create a server-readable client-writable event AKA up event AKA client-to-server asynchronous edge *) let e_up = Eliom_react.Up.create (Eliom_parameter.caml "double" Json.t) in let e_up_react = Eliom_react.Up.to_react e_up in let e_down_1 = Eliom_react.Down.of_react (React.E.map (let i = ref 0 in fun _ -> incr i ; !i) e_up_react ) in let e_down_2 = Eliom_react.Down.of_react (React.E.map (fun _ -> "haha") e_up_react) in let `R _ = React.E.retain e_up_react (fun () -> ignore e_down_1 ; ignore e_down_2) in ignore {unit{ Eliom_client.onload (fun _ -> ignore (React.E.map (fun s -> Dom_html.window##alert (Js.string s)) (React.E.merge (^) "" [ React.E.map string_of_int %e_down_1 ; %e_down_2 ; ] ))) }}; (* We can send the page *) Lwt.return (make_page [ h2 [pcdata "Simultaneous events"] ; div ~a:[(*zap* *)a_class ["clickable"];(* *zap*)a_onclick {{ fun _ -> ignore ( %e_up "") }} ] [pcdata "Send me two values from different events !"] ; ]) ) let comet_wrapping = My_appl.register_service ~path:["comet_wrapping"] ~get_params:unit (fun () () -> let node = Html5.D.div [pcdata "node created on server side"] in let service_stream,push_service = Lwt_stream.create () in push_service (Some Eliom_testsuite1.coucou); let c_service = Eliom_comet.Channel.create service_stream in let xml_stream,push_xml = Lwt_stream.create () in push_xml (Some (div [pcdata "basic xml wrapping";node])); push_xml (Some (div [Html5.D.a ~service:Eliom_testsuite1.coucou [pcdata "xml external link wrapping"] ()])); push_xml (Some (div [Html5.D.a ~service:comet1 [pcdata "xml internal link wrapping"] (); br (); pcdata "this link must not stop the process! (same random number in the container)."])); let c_xml = Eliom_comet.Channel.create xml_stream in let div_link = Html5.D.div [] in ignore {unit{ Eliom_client.onload (fun _ -> ignore (Lwt_stream.iter (fun service -> Dom.appendChild (Html5.To_dom.of_element %div_link) (Html5.To_dom.of_element ( Html5.D.a ~service [pcdata "service wrapping"] ())) ) %c_service); ignore (Lwt_stream.iter (fun xml -> Dom.appendChild (Html5.To_dom.of_element %div_link) (Html5.To_dom.of_element xml) ) %c_xml)) }}; Lwt.return (make_page [ div [pcdata "there should be a working links below"]; node; div_link; ]) ) let comet_signal_maker name time = My_appl.register_service ~path:[name] ~get_params:unit (fun () () -> let time_div = Html5.D.div [] in ignore {unit{ Eliom_client.onload (fun _ -> Lwt_react.S.keep (React.S.map (fun t -> (Html5.To_dom.of_div %time_div)##innerHTML <- Js.string (string_of_float t)) %time)) }}; Lwt.return (make_page [ h2 [pcdata "Signal"] ; time_div; br (); a ~service:Eliom_service.void_coservice' [pcdata "reload"] (); ]) ) let time = let t = Unix.gettimeofday () in let e = Lwt_react.E.from (fun () -> Lwt_unix.sleep 0.1 >>= (fun () -> Lwt.return (Unix.gettimeofday ()))) in Eliom_react.S.Down.of_react (Lwt_react.S.hold t e) let comet_signal = comet_signal_maker "comet_signal" time (*wiki* Here is the code for a minimalistic message board. *wiki*) let comet_message_board_maker name message_bus cb = My_appl.register_service ~path:[name] ~get_params:unit (fun () () -> cb (); Lwt.return ( let container = Html5.D.ul [li [em [pcdata "This is the message board"]]] in let field = Html5.D.raw_input ~a:[a_id "msg"; a_name "message"] ~input_type:`Text () in ignore {unit{ Eliom_client.onload (fun _ -> let c = Eliom_comet.Configuration.new_configuration () in Eliom_comet.Configuration.set_timeout c 3.; let _ = Lwt.catch (fun () -> Lwt_stream.iter_s (fun msg -> Dom.appendChild (Html5.To_dom.of_element %container) (Html5.To_dom.of_li (li [pcdata msg])); Lwt.return ()) (Eliom_bus.stream %message_bus)) (function | Eliom_comet.Channel_full -> Dom.appendChild (Html5.To_dom.of_element %container) (Html5.To_dom.of_li (li [pcdata "channel full, no more messages"])); Lwt.return () | e -> debug_exn "comet exception: " e; Lwt.fail e); in ()) }} ; let go = div ~a:[(*zap* *)a_class ["clickable"];(* *zap*) a_onclick {{ fun _ -> let field = (Js.Opt.get (Dom_html.CoerceTo.input (Js.Opt.get (Dom_html.document##getElementById (Js.string "msg")) (fun () -> failwith "No field found") ) ) (fun () -> failwith "No field found") ) in let v = Js.to_string field##value in field##value <- Js.string "" ; ignore (Eliom_bus.write %message_bus v) }} ] [pcdata "send"] in (make_page [ h2 [pcdata "Message board"]; raw_form ~a:[a_action (Xml.uri_of_string "")] (div [field; go]) []; container; br (); Html5.D.a ~service:Eliom_testsuite_base.main [pcdata "go outside of application"] (); ])) ) let message_bus = Eliom_bus.create ~scope:Eliom_common.default_process_scope ~size:10 Json.t let _ = Lwt_stream.iter (fun msg -> Printf.printf "msg: %s\n%!" msg) (Eliom_bus.stream message_bus) let message_board_callback () = Eliom_bus.write message_bus "a user joined in"; let _ = lwt () = Eliom_comet.Channel.wait_timeout ~scope:Eliom_common.default_process_scope 1. in Eliom_bus.write message_bus "a user went away"; Lwt.return () in () let comet_message_board = comet_message_board_maker "message_board" message_bus message_board_callback (* bus stream received multiple times *) let multiple_bus = Eliom_bus.create ~scope:Eliom_common.default_process_scope ~name:"multiple_bus" ~size:10 Json.t let _ = Lwt_stream.iter (fun _ -> ()) (Eliom_bus.stream multiple_bus) let multiple_bus_stateless = Eliom_bus.create ~name:"multiple_bus_stateless" ~scope:`Site ~size:10 Json.t let multiple_bus_position = ref 0 let _ = let rec tick () = lwt () = Lwt_unix.sleep 1. in Eliom_bus.write multiple_bus !multiple_bus_position; Eliom_bus.write multiple_bus_stateless !multiple_bus_position; incr multiple_bus_position; tick () in tick () let bus_multiple_times = My_appl.register_service ~path:["multiple_bus"] ~get_params:unit (fun () () -> let container = Html5.D.ul [li [em [pcdata "there will be lines"]]] in let onload s message_bus = ignore {unit{ Eliom_client.onload (fun _ -> let _ = try_lwt Lwt_stream.iter_s (fun msg -> Dom.appendChild (Html5.To_dom.of_element %container) (Html5.To_dom.of_li (li [pcdata (Printf.sprintf "stream %s: %i" %s msg)])); Lwt.return ()) (Eliom_bus.stream %message_bus) with | Eliom_comet.Channel_full -> Dom.appendChild (Html5.To_dom.of_element %container) (Html5.To_dom.of_li (li [pcdata "channel full, no more messages"])); Lwt.return () | e -> Lwt.fail e; in ()) }} in onload "statefull 1" multiple_bus; onload "statefull 2" multiple_bus; onload "statefull 3" multiple_bus; onload "stateless 1" multiple_bus_stateless; onload "stateless 2" multiple_bus_stateless; onload "stateless 3" multiple_bus_stateless; Lwt.return (make_page [ h2 [pcdata "Multiple streams from one bus"]; br (); a ~service:Eliom_service.void_coservice' [pcdata "reload"] (); br (); pcdata (Printf.sprintf "original position: %i" !multiple_bus_position); br (); container;]) ) (*wiki* ===Stateless comet channels *wiki*) (* random wait *) let rand_tick = let i = ref 0 in fun () -> Lwt_unix.sleep (float_of_int (2 + (Random.int 2))) >>= fun () -> incr i; Lwt.return (Some !i) let stream_sl = Lwt_stream.from rand_tick let stateless_channel = Eliom_comet.Channel.create ~scope:`Site ~name:"stateless" stream_sl let _ = Eliom_comet.Channel.get_wrapped stateless_channel let external_stateless_channel : int Eliom_comet.Channel.t = Eliom_comet.Channel.external_channel ~prefix:"http://localhost:8080" ~name:"stateless" () let comet_stateless = My_appl.register_service ~path:["comet_stateless"] ~get_params:unit (fun () () -> ignore {unit{ Eliom_client.onload (fun _-> let _ = Lwt_stream.iter_s (fun i -> Dom.appendChild (Dom_html.document##body) (Dom_html.document##createTextNode (Js.string ("msg: "^ string_of_int i ^"; "))) ; Lwt.return () ) %stateless_channel in ()) }}; Lwt.return (make_page [ div [pcdata "Comet channel with no client specific server side state."] ; ]) ) let comet_stateless_external = My_appl.register_service ~path:["comet_stateless_external"] ~get_params:unit (fun () () -> ignore {unit{ Eliom_client.onload (fun _ -> let _ = Lwt_stream.iter_s (fun i -> Dom.appendChild (Dom_html.document##body) (Dom_html.document##createTextNode (Js.string ("msg: "^ string_of_int i ^"; "))) ; Lwt.return () ) %external_stateless_channel in ()) }}; Lwt.return (make_page [ div [pcdata "External Comet channel: access the channel at http://localhost:8080."; br (); pcdata "If it is another server, that server must run the Cross-Origin Resource Sharing extension of ocsigenserver to allow requests from this page."]; ]) ) let time = let t = Unix.gettimeofday () in let e = Lwt_react.E.from (fun () -> Lwt_unix.sleep 0.1 >>= (fun () -> Lwt.return (Unix.gettimeofday ()))) in Eliom_react.S.Down.of_react ~scope:`Site ~name:"time" (Lwt_react.S.hold t e) let comet_signal_stateless = comet_signal_maker "comet_signal_stateless" time let message_bus_site = Eliom_bus.create ~scope:`Site ~size:10 Json.t let _ = Lwt_stream.iter (fun msg -> Printf.printf "msg site: %s\n%!" msg) (Eliom_bus.stream message_bus_site) let comet_message_board_stateless = comet_message_board_maker "message_board_stateless" message_bus_site (fun () -> ()) (*wiki* ===Header manipulation with eliom client *wiki*) let service_style1 = Eliom_service.service ~path:["css"; "style1"] ~get_params:unit () let service_style2 = Eliom_service.service ~path:["css"; "style2"] ~get_params:unit () let service_no_style = Eliom_service.service ~path:["css"; "no_style"] ~get_params:unit () let page_css_test () = [Html5.D.a ~service:service_style1 [pcdata "same page with style 1"] (); br (); Html5.D.a ~service:service_style2 [pcdata "same page with style 2"] (); br (); Html5.D.a ~service:service_no_style [pcdata "same page with no style"] (); br (); div ~a:[a_class ["some_class"];] [pcdata "div with style"]] let make_css_link file = Html5.D.css_link (Html5.D.make_uri ~service:(Eliom_service.static_dir ()) [file]) () let () = My_appl.register ~service:service_style1 (fun () () -> Lwt.return (make_page ~css:[make_css_link "test_style1.css"] (page_css_test ()))); My_appl.register ~service:service_style2 (fun () () -> Lwt.return (make_page ~css:[make_css_link "test_style2.css"] (page_css_test ()))); My_appl.register ~service:service_no_style (fun () () -> Lwt.return (make_page (page_css_test ()))) (*wiki* ===Events with arrows *wiki*) {client{ open Event_arrows }} let event_service = My_appl.register_service ~path:["events"] ~get_params:Eliom_parameter.unit (fun () () -> let make_target s = Html5.D.p [Html5.F.Raw.a [pcdata s]] in let target1 = make_target "Un seul clic" in let target2 = make_target "Annuler le précédent" in let target3 = make_target "Drag vers la ligne au dessus une seule fois" in let target4 = make_target "Plein de clics seq" in let target5 = make_target "Annuler le précédent" in let target6 = make_target "Deux clics" in let target7 = make_target "Un clic sur deux" in let target8 = make_target "All clicks but the first" in let target9 = make_target "Un des deux, premier" in let target10 = make_target "Un des deux, deuxieme" in let target11 = make_target "Annuler les deux précédents" in let target12 = make_target "Drag" in let target13 = make_target "Annuler le précédent" in let target14 = make_target "Drag with long handler" in let target15 = make_target "Annuler le précédent" in let target16 = make_target "Mouse over change color" in let targetresult = Html5.D.p [] in ignore {unit{ Eliom_client.onload (fun _ -> let targetresult = (Html5.To_dom.of_p %targetresult) in let handler = lwt_arr (fun ev -> ignore (targetresult##appendChild ((Html5.To_dom.of_element (Html5.F.pcdata " plip") :> Dom.node Js.t))); Lwt.return ()) in let handler_long = lwt_arr (fun ev -> Lwt_js.sleep 0.7 >>= fun () -> ignore (targetresult##appendChild ((Html5.To_dom.of_element (Html5.F.pcdata " plop") :> Dom.node Js.t))); Lwt.return () ) in let cancel c = arr (fun _ -> cancel c) in let c = run (click (Html5.To_dom.of_p %target1) >>> handler) () in let _ = run (click (Html5.To_dom.of_p %target2) >>> cancel c) () in let _ = run (mousedown (Html5.To_dom.of_p %target3) >>> mouseup (Html5.To_dom.of_p %target2) >>> handler) () in let c = run (clicks (Html5.To_dom.of_p %target4) handler_long) () in let _ = run (click (Html5.To_dom.of_p %target5) >>> cancel c) () in let _ = run (click (Html5.To_dom.of_p %target6) >>> handler >>> click (Html5.To_dom.of_p %target6) >>> handler) () in let _ = run (clicks (Html5.To_dom.of_p %target7) (click (Html5.To_dom.of_p %target7) >>> handler)) () in let _ = run (click (Html5.To_dom.of_p %target8) >>> clicks (Html5.To_dom.of_p %target8) handler) () in let c = run (first [click (Html5.To_dom.of_p %target9) >>> handler; click (Html5.To_dom.of_p %target10) >>> handler]) () in let _ = run (click (Html5.To_dom.of_p %target11) >>> cancel c) () in let c = run (mousedowns (Html5.To_dom.of_p %target12) (first [mouseup Dom_html.document; mousemoves Dom_html.document handler])) () in let _ = run (click (Html5.To_dom.of_p %target13) >>> cancel c) () in let c = run (mousedowns (Html5.To_dom.of_p %target14) (first [mouseup Dom_html.document; mousemoves Dom_html.document handler_long])) () in let _ = run (click (Html5.To_dom.of_p %target15) >>> cancel c) () in let t16 = Html5.To_dom.of_p %target16 in let _ = run (mouseovers t16 (arr (fun _ -> t16##style##backgroundColor <- Js.string "red")) ) () in let _ = run (mouseouts t16 (arr (fun _ -> t16##style##backgroundColor <- Js.string "")) ) () in ()) }}; Lwt.return (make_page [target1; target2; target3; target4; target5; target6; target7; target8; target9; target10; target11; target12; target13; target14; target15; target16; targetresult]) ) (*wiki* ===Events with lwt with cancellers *wiki*) {client{ open Lwt_js_events }} let event2_service = My_appl.register_service ~path:["events2"] ~get_params:Eliom_parameter.unit (fun () () -> let make_target s = Html5.D.p [Html5.F.Raw.a [pcdata s]] in let target1 = make_target "Un seul clic" in let target2 = make_target "Annuler le précédent" in let target3 = make_target "Drag vers la ligne au dessus une seule fois" in let target4 = make_target "Plein de clics seq" in let target5 = make_target "Annuler le précédent" in let target6 = make_target "Deux clics" in let target7 = make_target "Un clic sur deux" in let target8 = make_target "All clicks but the first" in let target9 = make_target "Un des deux, premier" in let target10 = make_target "Un des deux, deuxieme" in let target11 = make_target "Annuler les deux précédents" in let target12 = make_target "Drag" in let target13 = make_target "Annuler le précédent" in let target14 = make_target "Drag with long handler" in let target15 = make_target "Annuler le précédent" in let target16 = make_target "Mouse over change color" in let target17 = make_target "Mouse wheel (browser dependant - test in several browsers)" in let target18 = Html5.D.raw_textarea ~name:"a" () in let target19 = make_target "If you click very quickly after having entered a letter below, my handler (short) will occure, and the long handler for the keypress will be cancelled (event if it already started)." in let target20 = Html5.D.raw_textarea ~name:"b" () in let target21 = make_target "If you click very quickly after having entered a letter below, my handler will not occure because the long handler for the keypress below is detached." in let targetresult = Html5.D.p [] in ignore {unit{ Eliom_client.onload (fun _ -> let targetresult = (Html5.To_dom.of_p %targetresult) in let handler ev = ignore (targetresult##appendChild ((Html5.To_dom.of_element (Html5.F.pcdata " plip") :> Dom.node Js.t))); Lwt.return () in let handler_long ev = Lwt_js.sleep 0.7 >>= fun () -> ignore (targetresult##appendChild ((Html5.To_dom.of_element (Html5.F.pcdata " plop") :> Dom.node Js.t))); Lwt.return () in let handler1 ev _ = handler ev in let handler_long1 ev _ = handler_long ev in let c = click (Html5.To_dom.of_p %target1) >>= handler in ignore (click (Html5.To_dom.of_p %target2) >|= fun _ -> Lwt.cancel c); ignore (mousedown (Html5.To_dom.of_p %target3) >>= fun ev -> preventDefault ev; mouseup (Html5.To_dom.of_p %target2) >>= handler); let c = clicks (Html5.To_dom.of_p %target4) handler_long1 in ignore (click (Html5.To_dom.of_p %target5) >|= fun _ -> Lwt.cancel c); ignore (click (Html5.To_dom.of_p %target6) >>= handler >>= fun () -> click (Html5.To_dom.of_p %target6) >>= handler); ignore (clicks (Html5.To_dom.of_p %target7) (fun _ _ -> click (Html5.To_dom.of_p %target7) >>= handler)); ignore (click (Html5.To_dom.of_p %target8) >>= fun _ -> clicks (Html5.To_dom.of_p %target8) handler1); let c = Lwt.pick [click (Html5.To_dom.of_p %target9) >>= handler; click (Html5.To_dom.of_p %target10) >>= handler] in ignore (click (Html5.To_dom.of_p %target11) >|= fun _ -> Lwt.cancel c); let c = mousedowns (Html5.To_dom.of_p %target12) (fun _ _ -> Lwt.pick [(mouseup Dom_html.document >|= fun _ -> ()); mousemoves Dom_html.document handler1]) in ignore (click (Html5.To_dom.of_p %target13) >|= fun _ -> Lwt.cancel c); let c = mousedowns (Html5.To_dom.of_p %target14) (fun _ _ -> Lwt.pick [(mouseup Dom_html.document >|= fun _ -> ()); mousemoves Dom_html.document handler_long1]) in ignore (click (Html5.To_dom.of_p %target15) >|= fun _ -> Lwt.cancel c); let t16 = Html5.To_dom.of_p %target16 in ignore (mouseovers t16 (fun _ _ -> t16##style##backgroundColor <- Js.string "red"; Lwt.return ())); ignore (mouseouts t16 (fun _ _ -> t16##style##backgroundColor <- Js.string ""; Lwt.return ())); ignore (mousewheels (Html5.To_dom.of_p %target17) (fun (_, (dx, dy)) _ -> ignore (targetresult##appendChild ((Html5.To_dom.of_element (Html5.F.pcdata (Printf.sprintf "(%d, %d)" dx dy)) :> Dom.node Js.t))); Lwt.return ())); ignore (Lwt.pick [(keypress (Html5.To_dom.of_textarea %target18) >>= handler_long); click (Html5.To_dom.of_p %target19) >>= handler ]); ignore (Lwt.pick [(keypress (Html5.To_dom.of_textarea %target20) >>= fun _ -> ignore (handler_long ()); Lwt.return () ); click (Html5.To_dom.of_p %target21) >>= handler ])) }}; Lwt.return (make_page [target1; target2; target3; target4; target5; target6; target7; target8; target9; target10; target11; target12; target13; target14; target15; target16; target17; target18; target19; target20; target21; targetresult]) ) (*wiki* ===Tab sessions *wiki*) open Lwt (************************************************************) (************ Connection of users, version 1 ****************) (************************************************************) (*zap* *) let scope_hierarchy = Eliom_common.create_scope_hierarchy "tsession_data" let scope = `Client_process scope_hierarchy (* *zap*) (* "my_table" will be the structure used to store the session data (namely the login name): *) let my_table = Eliom_state.create_volatile_table ~scope () (* -------------------------------------------------------- *) (* Create services, but do not register them yet: *) let tsession_data_example = Eliom_service.service ~path:["tsessdata"] ~get_params:Eliom_parameter.unit () let tsession_data_example_with_post_params = Eliom_service.post_service ~fallback:tsession_data_example ~post_params:(Eliom_parameter.string "login") () let tsession_data_example_close = Eliom_service.service ~path:["tclose"] ~get_params:Eliom_parameter.unit () (* -------------------------------------------------------- *) (* Handler for the "tsession_data_example" service: *) let tsession_data_example_handler _ _ = let sessdat = Eliom_state.get_volatile_data ~table:my_table () in return (make_page [ match sessdat with | Eliom_state.Data name -> p [pcdata ("Hello "^name); br (); Html5.D.a tsession_data_example_close [pcdata "close session"] ()] | Eliom_state.Data_session_expired | Eliom_state.No_data -> Html5.D.post_form tsession_data_example_with_post_params (fun login -> [p [pcdata "login: "; Html5.D.string_input ~input_type:`Text ~name:login ()]]) () ]) (* -------------------------------------------------------- *) (* Handler for the "tsession_data_example_with_post_params" *) (* service with POST params: *) let tsession_data_example_with_post_params_handler _ login = lwt () = Eliom_state.discard ~scope () in Eliom_state.set_volatile_data ~table:my_table login; return (make_page [p [pcdata ("Welcome " ^ login ^ ". You are now connected."); br (); Html5.D.a tsession_data_example [pcdata "Try again"] () ]]) (* -------------------------------------------------------- *) (* Handler for the "tsession_data_example_close" service: *) let tsession_data_example_close_handler () () = let sessdat = Eliom_state.get_volatile_data ~table:my_table () in lwt () = Eliom_state.discard ~scope () in return (make_page [ (match sessdat with | Eliom_state.Data_session_expired -> p [pcdata "Your session has expired."] | Eliom_state.No_data -> p [pcdata "You were not connected."] | Eliom_state.Data _ -> p [pcdata "You have been disconnected."]); p [Html5.D.a tsession_data_example [pcdata "Retry"] () ]]) (* -------------------------------------------------------- *) (* Registration of main services: *) let () = My_appl.register tsession_data_example_close tsession_data_example_close_handler; My_appl.register tsession_data_example tsession_data_example_handler; My_appl.register tsession_data_example_with_post_params tsession_data_example_with_post_params_handler (************************************************************) (************ Connection of users, version 2 ****************) (************************************************************) (*zap* *) let scope_hierarchy = Eliom_common.create_scope_hierarchy "tsession_services" let scope = `Client_process scope_hierarchy (* *zap*) (* -------------------------------------------------------- *) (* Create services, but do not register them yet: *) let tsession_services_example = Eliom_service.service ~path:["tsessionservices"] ~get_params:Eliom_parameter.unit () let tsession_services_example_with_post_params = Eliom_service.post_service ~fallback:tsession_services_example ~post_params:(Eliom_parameter.string "login") () let tsession_services_example_close = Eliom_service.service ~path:["tclose2"] ~get_params:Eliom_parameter.unit () (* ------------------------------------------------------------- *) (* Handler for the "tsession_services_example" service: *) (* It displays the main page of our site, with a login form. *) let tsession_services_example_handler () () = let f = Html5.D.post_form tsession_services_example_with_post_params (fun login -> [p [pcdata "login: "; string_input ~input_type:`Text ~name:login ()]]) () in return (make_page [f]) (* ------------------------------------------------------------- *) (* Handler for the "tsession_services_example_close" service: *) let tsession_services_example_close_handler () () = lwt () = Eliom_state.discard ~scope () in Lwt.return (make_page [p [pcdata "You have been disconnected. "; a tsession_services_example [pcdata "Retry"] () ]]) (* ------------------------------------------------------------- *) (* Handler for the "session_services_example_with_post_params" *) (* service: *) let tlaunch_session () login = (* New handler for the main page: *) let new_main_page () () = return (make_page [p [pcdata "Welcome "; pcdata login; pcdata "!"; br (); a eliomclient1 [pcdata "coucou"] (); br (); a tsession_services_example_close [pcdata "close session"] ()]]) in (* If a session was opened, we close it first! *) lwt () = Eliom_state.discard ~scope () in (* Now we register new versions of main services in the session service table: *) My_appl.register ~scope ~service:tsession_services_example (* service is any public service already registered, here the main page of our site *) new_main_page; My_appl.register ~scope ~service:eliomclient1 (fun () () -> return (make_page [p [pcdata "Coucou "; pcdata login; pcdata "!"]])); new_main_page () () (* -------------------------------------------------------- *) (* Registration of main services: *) let () = My_appl.register ~service:tsession_services_example tsession_services_example_handler; My_appl.register ~service:tsession_services_example_close tsession_services_example_close_handler; My_appl.register ~service:tsession_services_example_with_post_params tlaunch_session (************************************************************) (************** Coservices. Basic examples ******************) (************************************************************) (* -------------------------------------------------------- *) (* We create one main service and two coservices: *) let tcoservices_example = Eliom_service.service ~path:["tcoserv"] ~get_params:Eliom_parameter.unit () let tcoservices_example_post = Eliom_service.post_coservice ~fallback:tcoservices_example ~post_params:Eliom_parameter.unit () let tcoservices_example_get = Eliom_service.coservice ~fallback:tcoservices_example ~get_params:Eliom_parameter.unit () (* -------------------------------------------------------- *) (* The three of them display the same page, *) (* but the coservices change the counter. *) let _ = let c = ref 0 in let page () () = let l3 = Html5.D.post_form tcoservices_example_post (fun _ -> [p [Html5.D.string_input ~input_type:`Submit ~value:"incr i (post)" ()]]) () in let l4 = Html5.D.get_form tcoservices_example_get (fun _ -> [p [Html5.D.string_input ~input_type:`Submit ~value:"incr i (get)" ()]]) in return (make_page [p [pcdata "The random number in the container must not change!"; br (); pcdata "i is equal to "; pcdata (string_of_int !c); br (); a tcoservices_example [pcdata "internal application link to myself"] (); br (); a tcoservices_example_get [pcdata "incr i"] ()]; l3; l4]) in My_appl.register tcoservices_example page; let f () () = c := !c + 1; page () () in My_appl.register tcoservices_example_post f; My_appl.register tcoservices_example_get f (************************************************************) (*************** calc: sum of two integers ******************) (************************************************************) (*zap* *) let scope = `Client_process Eliom_testsuite1.calc_example_scope_hierarchy (* *zap*) (* -------------------------------------------------------- *) (* We create two main services on the same URL, *) (* one with a GET integer parameter: *) let tcalc = service ~path:["tcalc"] ~get_params:unit () let tcalc_i = service ~path:["tcalc"] ~get_params:(int "i") () (* -------------------------------------------------------- *) (* The handler for the service without parameter. *) (* It displays a form where you can write an integer value: *) let tcalc_handler () () = let create_form intname = [p [pcdata "Write a number: "; Html5.D.int_input ~input_type:`Text ~name:intname (); br (); Html5.D.string_input ~input_type:`Submit ~value:"Send" ()]] in let f = Html5.D.get_form tcalc_i create_form in return (make_page [f]) (* -------------------------------------------------------- *) (* The handler for the service with parameter. *) (* It creates dynamically and registers a new coservice *) (* with one GET integer parameter. *) (* This new coservice depends on the first value (i) *) (* entered by the user. *) let tcalc_i_handler i () = let create_form is = (fun entier -> [p [pcdata (is^" + "); int_input ~input_type:`Text ~name:entier (); br (); string_input ~input_type:`Submit ~value:"Sum" ()]]) in let is = string_of_int i in let tcalc_result = My_appl.register_coservice ~scope:Eliom_common.default_process_scope ~fallback:tcalc ~get_params:(int "j") (fun j () -> let js = string_of_int j in let ijs = string_of_int (i+j) in return (make_page [p [pcdata (is^" + "^js^" = "^ijs)]])) in let f = get_form tcalc_result (create_form is) in return (make_page [f]) (* -------------------------------------------------------- *) (* Registration of main services: *) let () = My_appl.register tcalc tcalc_handler; My_appl.register tcalc_i tcalc_i_handler (************************************************************) (************ Connection of users, version 3 ****************) (************************************************************) (*zap* *) let scope = `Client_process Eliom_testsuite1.connect_example3_scope_hierarchy let my_table = Eliom_state.create_volatile_table ~scope () (* *zap*) (* -------------------------------------------------------- *) (* We create one main service and two (POST) actions *) (* (for connection and disconnection) *) let tconnect_example3 = Eliom_service.service ~path:["taction"] ~get_params:Eliom_parameter.unit () let tconnect_action = Eliom_service.post_coservice' ~name:"tconnect3" ~post_params:(Eliom_parameter.string "login") () (* As the handler is very simple, we register it now: *) let tdisconnect_action = Eliom_registration.Action.register_post_coservice' ~name:"tdisconnect3" ~post_params:Eliom_parameter.unit (fun () () -> Eliom_state.discard ~scope ()) (* -------------------------------------------------------- *) (* login ang logout boxes: *) let tdisconnect_box s = Html5.D.post_form tdisconnect_action (fun _ -> [p [Html5.D.string_input ~input_type:`Submit ~value:s ()]]) () let tlogin_box () = Html5.D.post_form tconnect_action (fun loginname -> [p (let l = [pcdata "login: "; Html5.D.string_input ~input_type:`Text ~name:loginname ()] in l) ]) () (* -------------------------------------------------------- *) (* Handler for the "connect_example3" service (main page): *) let tconnect_example3_handler () () = let sessdat = Eliom_state.get_volatile_data ~table:my_table () in return (make_page (match sessdat with | Eliom_state.Data name -> [p [pcdata ("Hello "^name); br ()]; Html5.D.a ~service:tconnect_example3 [pcdata "Try again to check whether you are still connected"] (); tdisconnect_box "Close session"] | Eliom_state.Data_session_expired | Eliom_state.No_data -> [tlogin_box ()] )) (* -------------------------------------------------------- *) (* Handler for connect_action (user logs in): *) let tconnect_action_handler () login = lwt () = Eliom_state.discard ~scope () in Eliom_state.set_volatile_data ~table:my_table login; return () (* -------------------------------------------------------- *) (* Registration of main services: *) let () = My_appl.register ~service:tconnect_example3 tconnect_example3_handler; Eliom_registration.Action.register ~service:tconnect_action tconnect_action_handler (************************************************************) (************ Connection of users, version 4 ****************) (**************** (persistent sessions) *********************) (************************************************************) (*zap* *) let scope = `Client_process Eliom_testsuite1.persistent_sessions_scope_hierarchy (* *zap*) let tmy_persistent_table = Eliom_state.create_persistent_table ~scope "teliom_example_table" (* -------------------------------------------------------- *) (* We create one main service and two (POST) actions *) (* (for connection and disconnection) *) let tpersist_session_example = Eliom_service.service ~path:["tpersist"] ~get_params:unit () let tpersist_session_connect_action = Eliom_service.post_coservice' ~name:"tconnect4" ~post_params:(string "login") () (* disconnect_action, login_box and disconnect_box have been defined in the section about actions *) (*zap* *) (* -------------------------------------------------------- *) (* Actually, no. It's a lie because we don't use the same session name :-) *) (* new disconnect action and box: *) let tdisconnect_action = Eliom_registration.Action.register_post_coservice' ~name:"tdisconnect4" ~post_params:Eliom_parameter.unit (fun () () -> Eliom_state.discard ~scope ()) let tdisconnect_box s = Html5.D.post_form tdisconnect_action (fun _ -> [p [Html5.D.string_input ~input_type:`Submit ~value:s ()]]) () let bad_user_key = Polytables.make_key () let get_bad_user table = try Polytables.get ~table ~key:bad_user_key with Not_found -> false (* -------------------------------------------------------- *) (* new login box: *) let tlogin_box session_expired action = Html5.D.post_form action (fun loginname -> let l = [pcdata "login: "; string_input ~input_type:`Text ~name:loginname ()] in [p (if get_bad_user (Eliom_request_info.get_request_cache ()) then (pcdata "Wrong user")::(br ())::l else if session_expired then (pcdata "Session expired")::(br ())::l else l) ]) () (* *zap*) (* ----------------------------------------------------------- *) (* Handler for "persist_session_example" service (main page): *) let tpersist_session_example_handler () () = let timeoutcoserv = Eliom_service.coservice ~fallback:tpersist_session_example ~get_params:unit ~timeout:5. () in let _ = Eliom_registration.Html5.register ~service:timeoutcoserv ~scope:Eliom_common.default_process_scope (fun _ _ -> return (html (head (title (pcdata "Cooooooooservices with timeouts")) []) (body [p [pcdata "I am a coservice with timeout."; br (); pcdata "Try to reload the page!"; br (); pcdata "I will disappear after 5 seconds of inactivity."; pcdata "Pour l'instant c'est un Eliom_output.Html5 au lieu de My_appl parce qu'il y a un bug à corriger dans ce cas. Remettre My_appl ici et ajouter un test pour ce bug." ]; ]))) (* [p [pcdata "I am a coservice with timeout."; br (); a timeoutcoserv [pcdata "Try again"] (); br (); pcdata "I will disappear after 5 seconds of inactivity." ]; ]) *) in Eliom_state.get_persistent_data ~table:tmy_persistent_table () >>= fun sessdat -> Lwt.return (make_page (match sessdat with | Eliom_state.Data name -> [p [pcdata ("Hello "^name); br ()]; tdisconnect_box "Close session"] | Eliom_state.Data_session_expired -> [tlogin_box true tpersist_session_connect_action; p [em [pcdata "The only user is 'toto'."]]] | Eliom_state.No_data -> [tlogin_box false tpersist_session_connect_action; p [em [pcdata "The only user is 'toto'."]]] )) (* ----------------------------------------------------------- *) (* Handler for persist_session_connect_action (user logs in): *) let tpersist_session_connect_action_handler () login = lwt () = Eliom_state.discard ~scope () in if login = "toto" (* Check user and password :-) *) then Eliom_state.set_persistent_data ~table:tmy_persistent_table login else ((*zap* *)Polytables.set (Eliom_request_info.get_request_cache ()) bad_user_key true;(* *zap*)return ()) (* -------------------------------------------------------- *) (* Registration of main services: *) let () = My_appl.register ~service:tpersist_session_example tpersist_session_example_handler; Eliom_registration.Action.register ~service:tpersist_session_connect_action tpersist_session_connect_action_handler (* (************************************************************) (************ Connection of users, version 6 ****************) (************************************************************) (*zap* *) let scope_hierarchy = Eliom_common.create_scope_hierarchy "connect_example6" let scope = `Client_process scope_hierarchy (* *zap*) (* -------------------------------------------------------- *) (* We create one main service and two (POST) actions *) (* (for connection and disconnection) *) let tconnect_example6 = Eliom_service.service ~path:["taction2"] ~get_params:unit () let tconnect_action = Eliom_service.post_coservice' ~name:"tconnect6" ~post_params:(string "login") () (* new disconnect action and box: *) let tdisconnect_action = Eliom_registration.Action.register_post_coservice' ~name:"tdisconnect6" ~post_params:Eliom_parameter.unit (fun () () -> Eliom_state.discard (*zap* *) ~state_name (* *zap*) ~scope ()) let tdisconnect_box s = Eliom_registration.Html5.post_form tdisconnect_action (fun _ -> [p [Html5.D.string_input ~input_type:`Submit ~value:s ()]]) () let bad_user_key = Polytables.make_key () let get_bad_user table = try Polytables.get ~table ~key:bad_user_key with Not_found -> false (* -------------------------------------------------------- *) (* new login box: *) let tlogin_box session_expired action = Html5.D.post_form action (fun loginname -> let l = [pcdata "login: "; string_input ~input_type:`Text ~name:loginname ()] in [p (if get_bad_user (Eliom_request_info.get_request_cache ()) then (pcdata "Wrong user")::(br ())::l else if session_expired then (pcdata "Session expired")::(br ())::l else l) ]) () (* -------------------------------------------------------- *) (* Handler for the "connect_example6" service (main page): *) let tconnect_example6_handler () () = let status = Eliom_state.volatile_data_state_status (*zap* *) ~state_name (* *zap*) () in let group = Eliom_state.get_volatile_data_session_group (*zap* *) ~state_name (* *zap*) () in return (match group, status with | Some name, _ -> [p [pcdata ("Hello "^name); br ()]; tdisconnect_box "Close session"] | None, Eliom_state.Expired_state -> [tlogin_box true tconnect_action; p [em [pcdata "The only user is 'toto'."]]] | _ -> [tlogin_box false tconnect_action; p [em [pcdata "The only user is 'toto'."]]] ) (* -------------------------------------------------------- *) (* New handler for connect_action (user logs in): *) let tconnect_action_handler () login = Eliom_state.discard (*zap* *) ~state_name (* *zap*) ~scope () >>= fun () -> if login = "toto" (* Check user and password :-) *) then begin Eliom_state.set_volatile_data_session_group ~set_max:4 (*zap* *) ~state_name (* *zap*) login; return () end else begin Polytables.set (Eliom_request_info.get_request_cache ()) bad_user_key true; return () end (* -------------------------------------------------------- *) (* Registration of main services: *) let () = My_appl.register ~service:tconnect_example6 tconnect_example6_handler; Eliom_registration.Action.register ~service:tconnect_action tconnect_action_handler *) let csrf_scope = `Client_process Eliom_testsuite1.csrf_scope_hierarchy let tcsrfsafe_example = Eliom_service.service ~path:["tcsrf"] ~get_params:Eliom_parameter.unit () let tcsrfsafe_example_post = Eliom_service.post_coservice ~csrf_safe:true ~csrf_scope ~csrf_secure:true ~timeout:10. ~max_use:1 ~https:true ~fallback:tcsrfsafe_example ~post_params:Eliom_parameter.unit () let _ = let page () () = let l3 = Html5.D.post_form tcsrfsafe_example_post (fun _ -> [p [Html5.D.string_input ~input_type:`Submit ~value:"Click" ()]]) () in Lwt.return (make_page [p [pcdata "A new coservice will be created each time this form is displayed. Your server must be running with HTTPS enabled. Clicking on the button should go to a new page (but this is probably broken for client process scope)."]; l3]) in My_appl.register tcsrfsafe_example page; My_appl.register tcsrfsafe_example_post (fun () () -> Lwt.return (make_page [p [pcdata "This is a CSRF safe service"]])) (***** User cookies *****) let cookiename = "mycookie" let tcookies = service ["tcookies"] unit () let _ = My_appl.register tcookies (fun () () -> Eliom_state.set_cookie ~cookie_level:`Client_process ~name:cookiename ~value:(string_of_int (Random.int 100)) (); Lwt.return (make_page [p [pcdata "A new tab cookie is sent each time you load this page. If you reload, the cookie will be sent by the browser and you can observe the value changing."]; p [pcdata (try "cookie value: "^ (CookiesTable.find cookiename (Eliom_request_info.get_cookies ~cookie_level:`Client_process ())) with _ -> ""); br (); a tcookies [pcdata "send other cookie"] ()]])) (***** Action outside the application: will ask the client program to do a redirection *****) let coucouaction = Eliom_registration.Action.register_coservice ~fallback:Eliom_testsuite1.coucou ~get_params:unit (fun () () -> Lwt.return ()) let coucouaction2 = Eliom_registration.Action.register_coservice' ~get_params:unit (fun () () -> Lwt.return ()) let actionoutside = My_appl.register_service ~path:["actionoutside"] ~get_params:unit (fun () () -> Lwt.return (make_page [p [a ~service:coucouaction [ pcdata "Click to do an action outside the application"] () ]; p [a ~service:coucouaction2 [ pcdata "Click to do an action outside the application (with non-attached coservice)"] () ]; ])) (*****************************************************************************) (* persistent references *) let persref = service ["persref"] unit () let _ = let next = let pr = Eliom_reference.eref ~scope:`Global ~persistent:"__eliom_example_persref" 0 in let mutex = Lwt_mutex.create () in fun () -> Lwt_mutex.lock mutex >>= fun () -> Eliom_reference.get pr >>= fun v -> let v = v+1 in Eliom_reference.set pr v >>= fun () -> Lwt_mutex.unlock mutex; Lwt.return v in Eliom_registration.Html5.register persref (fun () () -> next () >>= fun v -> Lwt.return (html (head (title (pcdata "Persistent references")) []) (body [pcdata "This page has been viewed "; pcdata (string_of_int v); pcdata " times."] ) ) ) (*********) let ttimeout = service ["ttimeout"] unit () let _ = let page () () = let timeoutcoserv = Eliom_service.coservice ~fallback:ttimeout ~get_params:unit ~timeout:5. () in let _ = My_appl.register ~service:timeoutcoserv ~scope:Eliom_common.default_process_scope (fun _ _ -> Lwt.return (make_page [p [pcdata "I am a coservice with timeout."; br (); a timeoutcoserv [pcdata "Try again"] (); br (); pcdata "I will disappear after 5 seconds of inactivity." ]; ])) in return (make_page [h2 [pcdata "Client process coservices with timeouts"]; p [pcdata "I just created a coservice with 5 seconds timeout."; br (); a timeoutcoserv [pcdata "Try it"] (); ]; ]) in My_appl.register ttimeout page (*****************************************************************************) let nonapplprocessservice = service ["nonapplprocessservice"] unit () let _ = let page () () = let serv = Eliom_registration.Ocaml.register_post_coservice' ~scope:Eliom_common.default_process_scope ~post_params:unit (fun () () -> Lwt.return [1; 2; 3]) in let serv2 = Eliom_registration.Html5.register_coservice' ~scope:Eliom_common.default_process_scope ~get_params:unit (fun () () -> Lwt.return (html (head (title (pcdata "mmmh")) []) (body [p [pcdata "It works"]]))) in Lwt.return (make_page [h2 [pcdata "Client process service not registered with My_appl"]; p [pcdata "I just created two coservices with scope `Client_process but not registered with My_appl."; br (); span ~a:[a_class ["clickable"]; a_onclick {{ fun _ -> let body = Dom_html.document##body in ignore (Eliom_client.call_caml_service ~service:%serv () () >|= List.iter (fun i -> Dom.appendChild body (Dom_html.document##createTextNode (Js.string (string_of_int i))))) }} ] [pcdata "Click to call it and receive Ocaml data (service registered with Eliom_registration.Ocaml)."]; br (); pcdata "It works, because we send tab cookies with Eliom_client.call_service or Eliom_client.call_caml_service."; br (); Html5.D.a ~service:serv2 [pcdata "Here a link to an client process service outside the application."] (); pcdata " For now it does not work, because we do not send tab cookies for non My_appl services ... How to solve this?"; br (); pcdata "Add a test of link to another application." ] ]) in My_appl.register nonapplprocessservice page (*****************************************************************************) (* Session + My_appl *) let scope_hierarchy = Eliom_common.create_scope_hierarchy "session_appl" let session = `Session scope_hierarchy let connect_example789 = Eliom_service.service ~path:["session_appl"] ~get_params:unit () let connect_action789 = Eliom_service.post_coservice' ~name:"connection789" ~post_params:(string "login") () let disconnect_action789 = Eliom_registration.Action.register_post_coservice' ~name:"disconnection789" ~post_params:Eliom_parameter.unit (fun () () -> Eliom_state.discard ~scope:session ()) let disconnect_box s = Html5.D.post_form disconnect_action789 (fun _ -> [p [Html5.D.string_input ~input_type:`Submit ~value:s ()]]) () let bad_user = Eliom_reference.eref ~scope:Eliom_common.request_scope false let user = Eliom_reference.eref ~scope:session None let login_box session_expired bad_u action = Html5.D.post_form action (fun loginname -> let l = [pcdata "login: "; Html5.D.string_input ~input_type:`Text ~name:loginname ()] in [p (if bad_u then (pcdata "Wrong user")::(br ())::l else if session_expired then (pcdata "Session expired")::(br ())::l else l) ]) () let connect_example_handler () () = let status = Eliom_state.volatile_data_state_status ~scope:session () in lwt bad_u = Eliom_reference.get bad_user in lwt u = Eliom_reference.get user in Lwt.return (make_page (match u, status with | Some name, _ -> [p [pcdata ("Hello "^name); br ()]; disconnect_box "Close session"] | None, Eliom_state.Expired_state -> [login_box true bad_u connect_action789; p [em [pcdata "The only user is 'toto'."]]] | _ -> [login_box false bad_u connect_action789; p [em [pcdata "The only user is 'toto'."]]] )) let connect_action_handler () login = lwt () = Eliom_state.discard ~scope:session () in if login = "toto" then Eliom_reference.set user (Some login) else Eliom_reference.set bad_user true let () = My_appl.register ~service:connect_example789 connect_example_handler; Eliom_registration.Action.register ~service:connect_action789 connect_action_handler (*****************************************************************************) (* Form towards internal suffix service *) let isuffixc = My_appl.register_service ~path:["isuffixc"] ~get_params:(suffix_prod (int "suff" ** all_suffix_string "endsuff") (int "i")) (fun ((suff, endsuff), i) () -> Lwt.return (make_page [p [pcdata "The suffix of the url is "; strong [pcdata (string_of_int suff)]; pcdata " followed by "; strong [pcdata endsuff]; pcdata " and i is equal to "; strong [pcdata (string_of_int i)]]])) {shared{ let create_suffixformc ((suff, endsuff),i) = [pcdata "Form to an (internal appl) suffix service."; pcdata "Write an int for the suffix:"; Html5.D.int_input ~input_type:`Text ~name:suff (); pcdata "Write a string: "; Html5.D.string_input ~input_type:`Text ~name:endsuff (); pcdata "Write an int: "; Html5.D.int_input ~input_type:`Text ~name:i (); Html5.D.string_input ~input_type:`Submit ~value:"Click" () ] }} (*****************************************************************************) (* Redirections and Eliom applications: *) let appl_redir1 = Eliom_registration.Redirection.register_service ~path:["internalredir"] ~get_params:Eliom_parameter.unit (fun () () -> Lwt.return eliomclient2) let appl_redir2 = Eliom_registration.Redirection.register_service ~path:["externalredir"] ~get_params:Eliom_parameter.unit (fun () () -> Lwt.return Eliom_testsuite1.coucou) let appl_redir = My_appl.register_service ~path:["applredir"] ~get_params:unit (fun () () -> Lwt.return (make_page [p [ a ~service:appl_redir1 [ pcdata "Link to a redirection inside the Eliom application"] (); br (); a ~service:appl_redir2 [ pcdata "Link to a redirection outside the Eliom application"] (); ]; Html5.D.get_form ~service:appl_redir1 (fun () -> [Html5.D.string_input ~input_type:`Submit ~value:"Form to a redirection inside the Eliom application" ()] ); Html5.D.get_form ~service:appl_redir2 (fun () -> [Html5.D.string_input ~input_type:`Submit ~value:"Form to a redirection outside the Eliom application" ()] ) ])) (*****************************************************************************) (* Void coservices with Eliom applications: *) let applvoid_redir = Eliom_registration.Redirection.register_post_coservice' ~name:"applvoidcoserv" ~post_params:Eliom_parameter.unit (fun () () -> Lwt.return Eliom_service.void_hidden_coservice') (*****************************************************************************) (* Form examples: *) let postformc = My_appl.register_post_service ~fallback:Eliom_testsuite1.coucou ~post_params:(Eliom_parameter.string "zzz") (fun () s -> Lwt.return (make_page [p [pcdata "Yo man. "; pcdata s]])) module Another_appl = Eliom_registration.App ( struct let application_name = "eliom_testsuite3bis" end) let make_page_bis ?(css = []) content = html (head (title (pcdata "Eliom application example (bis)")) ([ style [pcdata "a,.clickable {color: #111188; cursor: pointer;}"]; My_appl.application_script (); ] @ css)) (body [h1 [pcdata "Eliom application"]; header (); div content ] ) {client{ module Another_appl = Eliom_registration.Html5 }} let otherappl = Another_appl.register_service ~path:["other"; "appl"] ~get_params:unit (fun () () -> Lwt.return (make_page_bis [p [pcdata "I am another application"] ])) let long_page = Eliom_service.service ~path:["fragment";"main"] ~get_params:unit () let _ = My_appl.register long_page (fun () () -> let rec list i n = if i >= n then [Html5.F.li [Html5.D.a ~fragment:"" ~service:long_page [pcdata ("Goto TOP")] ()]] else Html5.F.li ~a:[Html5.F.a_id ("id" ^string_of_int i)] [Html5.F.pcdata ("Item #" ^ string_of_int i); Html5.F.pcdata "; "; Html5.D.a ~fragment:("id" ^ string_of_int (n-i)) ~service:long_page [pcdata ("Goto #" ^ string_of_int (n-i))] ();] :: list (i+1) n in Lwt.return (make_page [Html5.F.ul (list 1 100)])) let service_with_get_params = Eliom_service.service ~path:["intgp"] ~get_params:(suffix_prod (string "s") (string "t")) () let _ = My_appl.register service_with_get_params (fun (s, t) () -> Lwt.return (make_page [p [pcdata "Check that spaces and accents in parameters are ok"]; p [pcdata s]; p [pcdata t]])) {client{ let pinger : unit Lwt.t option ref = ref None let rec loop t i r = debug "Ping %d %d" i !r; incr r; try_lwt Lwt_js.sleep t >> loop t (i+1) r with _ -> debug "Pinger cancelled"; Lwt.return () let loop_counter = ref 0 let () = debug "Application loading" }} let live1 = Eliom_service.service ["live";"one"] unit () let live2 = Eliom_service.service ["live";"two"] unit () let live3 = Eliom_service.service ["live";"three"] unit () let live_description = div [pcdata "This is an application with three pages. "; pcdata "When loading the application shows a message in the console. "; pcdata "When loading each page show a message in the console."; pcdata "The first page display \"Ping\" every 2 seconds in the console."; br (); pcdata "Try to navigate between page. Try to leave the application and to get back.";] let live_links = div [ ul [li [Html5.D.a ~service:live1 [pcdata "Page one"] ()]; li [Html5.D.a ~service:live2 [pcdata "Page two"] ()]; li [Html5.D.a ~service:live3 [pcdata "Page threee"] ()]]] let dead_links = div [ ul [li [Html5.D.a ~service:Eliom_testsuite1.coucou [pcdata "Link to a service outside the application."] ()]; li [Html5.D.a ~service:otherappl [pcdata "Link to another application."] ()];]] let () = My_appl.register ~service:live1 (fun () () -> ignore {unit{ Eliom_client.onload (fun _ -> debug "Page 1 loading"; pinger := Some (loop 2. 0 loop_counter)) }}; ignore {unit{ Eliom_client.onunload (fun _ -> debug "Page 1 unloading"; Option.iter Lwt.cancel !pinger) }}; Lwt.return (make_page [h1 [pcdata "Page one"]; live_description; live_links; dead_links])) let () = My_appl.register ~service:live2 (fun () () -> ignore {unit{ Eliom_client.onload (fun _ -> debug "Page 2 loading"); Eliom_client.onunload (fun _ -> debug "Page 2 unloading") }}; Lwt.return (make_page [h1 [pcdata "Page two"];live_description; live_links; dead_links])) let () = My_appl.register ~service:live3 (fun () () -> ignore {unit{ Eliom_client.onload (fun _ -> debug "Page 3 loading"); Eliom_client.onunload (fun _ -> debug "Page 3 unloading") }}; Lwt.return (make_page [h1 [pcdata "Page threee"]; live_description; live_links; dead_links])) let formc = My_appl.register_service ["formc"] unit (fun () () -> let div = Html5.D.div [h3 [pcdata "Forms and links created on client side:"]] in ignore {unit{ Eliom_client.onload (fun _ -> let l = [ h4 [pcdata "to outside the application:"]; p [Html5.D.a ~service:%Eliom_testsuite1.coucou [pcdata "Link to a service outside the application."] ()]; p [Html5.D.a ~service:%Eliom_testsuite1.coucou_params [pcdata "Link to a service outside the application, with params (unicode)"] (1, (2, "tutu cccéccc+ccc"))]; Html5.D.get_form ~service:%Eliom_testsuite1.coucou (fun () -> [Html5.D.string_input ~input_type:`Submit ~value:"GET form to a service outside the Eliom application" ()] ); Html5.D.post_form ~service:%Eliom_testsuite1.my_service_with_post_params (fun s -> [Html5.D.string_input ~input_type:`Hidden ~name:s ~value:"plop" (); Html5.D.string_input ~input_type:`Submit ~value:"POST form to a service outside the Eliom application" ()] ) (); p [Html5.D.a ~service:%otherappl [pcdata "Link to another application."] (); ]; Html5.D.get_form ~service:%otherappl (fun () -> [Html5.D.string_input ~input_type:`Submit ~value:"GET form to another application" ();]; ); h4 [pcdata "inside the application — must not stop the process! (same random number in the container)."]; p [Html5.D.a ~service:%long_page [pcdata "Link to a service inside the application."] ()]; p [Html5.D.a ~service:%long_page ~fragment:"id40" [pcdata "Link to a service inside the application (fragment)."] ()]; p [Html5.D.a ~https:false ~service:%long_page [pcdata "Link to a service inside the application (force http)."] ()]; p [Html5.D.a ~https:true ~service:%long_page [pcdata "Link to a service inside the application (force https)."] ()]; p [Html5.D.a ~service:%service_with_get_params [pcdata "Link to a service inside the application (GET parameters, with spaces and Unicode)."] ("toto aaaéaaa+aaa", "tata oooéooo+ooo")]; p ~a:[(*zap* *)a_class ["clickable"];(* *zap*) a_onclick ( fun _ -> ignore(Eliom_client.change_page ~service:%service_with_get_params ("toto aaaéaaa+aaa", "tata oooéooo+ooo") ()) ) ] [pcdata "Change page to a service inside the application (GET parameters, with spaces and Unicode)."]; Html5.D.get_form ~service:%eliomclient1 (fun () -> [Html5.D.string_input ~input_type:`Submit ~value:"GET form to a service inside the Eliom application" ()] ); Html5.D.post_form ~service:%postformc (fun s -> [Html5.D.string_input ~input_type:`Submit ~name:s ~value:"POST form to a service inside the Eliom application" ()] ) (); Html5.D.get_form %isuffixc create_suffixformc; Html5.D.post_form ~service:%applvoid_redir (fun () -> [pcdata "POST form towards action with void service redirection. This must not stop the application (same random number in the container but not in the here: "; pcdata (string_of_int (Random.int 1000)); pcdata ") "; Html5.D.string_input ~input_type:`Submit ~value:"Click to send POST form to myself." ()] ) (); ] in List.iter (fun e -> Dom.appendChild (Html5.To_dom.of_div %div) (Html5.To_dom.of_element e)) l) }}; Lwt.return (make_page [ h3 [pcdata "Forms and links created on server side:"]; h4 [pcdata "to outside the application:"]; p [Html5.D.a ~service:Eliom_testsuite1.coucou [pcdata "Link to a service outside the application."] ()]; p [Html5.D.a ~service:Eliom_testsuite1.coucou_params [pcdata "Link to a service outside the application, with params (unicode)"] (1, (2, "tutu cccéccc+ccc"))]; Html5.D.get_form ~service:Eliom_testsuite1.coucou (fun () -> [Html5.D.string_input ~input_type:`Submit ~value:"GET form to a service outside the Eliom application" ()] ); Html5.D.post_form ~service:Eliom_testsuite1.my_service_with_post_params (fun s -> [Html5.D.string_input ~input_type:`Hidden ~name:s ~value:"plop" (); Html5.D.string_input ~input_type:`Submit ~value:"POST form to a service outside the Eliom application" ()] ) (); p [Html5.D.a ~service:otherappl [pcdata "Link to another application."] (); pcdata " (The other appl won't work as it is not compiled)"]; Html5.D.get_form ~service:otherappl (fun () -> [Html5.D.string_input ~input_type:`Submit ~value:"GET form to another application" ();] ); h4 [pcdata "inside the application — must not stop the process! (same random number in the container)."]; p [Html5.D.a ~service:long_page [pcdata "Link to a service inside the application."] ()]; p [Html5.D.a ~service:long_page ~fragment:"id40" [pcdata "Link to a service inside the application (fragment)."] ()]; p [Html5.D.a ~https:false ~service:long_page [pcdata "Link to a service inside the application (force http)."] ()]; p [Html5.D.a ~https:true ~service:long_page [pcdata "Link to a service inside the application (force https)."] ()]; p [Html5.D.a ~service:service_with_get_params [pcdata "Link to a service inside the application (GET parameters, with spaces and Unicode)."] ("toto aaaéaaa+aaa", "tata oooéooo+ooo")]; Html5.D.get_form ~service:eliomclient1 (fun () -> [Html5.D.string_input ~input_type:`Submit ~value:"GET form to a service inside the Eliom application" ()] ); Html5.D.post_form ~service:postformc (fun s -> [Html5.D.string_input ~input_type:`Submit ~name:s ~value:"POST form to a service inside the Eliom application" ()] ) (); Html5.D.get_form isuffixc create_suffixformc; Html5.D.post_form ~service:applvoid_redir (fun () -> [pcdata "POST form towards action with void service redirection. This must not stop the application (same random number in the container but not in the here: "; pcdata (string_of_int (Random.int 1000)); pcdata ") "; Html5.D.string_input ~input_type:`Submit ~value:"Click to send POST form to myself." ()] ) (); div; ])) (*****************************************************************************) (* Any with Eliom applications: *) let any_service = Eliom_service.service ~path:["appl_any"] ~get_params:(Eliom_parameter.int "with_eliom_appl") () let any_service_fallback = My_appl.register_service ~path:["appl_any_"] ~get_params:Eliom_parameter.unit (fun () () -> Lwt.return (make_page [p [pcdata "any_appl_ fallback"; br (); pcdata "it should never be called"]])) let () = let make_content name = let sp = Eliom_common.get_sp () in let appl_name = sp.Eliom_common.sp_client_appl_name in let links f = span [f "version generated by Eliom_registration.Html5" 0; br (); f "version generated by My_appl" 1; br (); f "version generated by Another_appl" 2; br (); f "a file" 3; br ()] in [p [pcdata ("this page was generated by " ^ name); br (); links (fun text i -> Html5.D.a ~service:any_service [pcdata text] i); br (); pcdata "back button do not work after exiting application (with link to Eliom_registration.Html5) when the last request had post parameters: the post parameters does not appear in the url: it will lead to the fallback"; br (); match appl_name with | None -> pcdata "last request was not from an application" | Some an -> pcdata ("last request was from application "^an) ] ] in let make_any = function | 0 -> Printf.printf "html5 case\n%!"; Eliom_registration.appl_self_redirect Eliom_registration.Html5.send (html (head (title (pcdata "html5 content")) []) (body (make_content "Eliom_registration.Html5.send"))) | 1 -> Printf.printf "my appl case\n%!"; My_appl.send (make_page (make_content "My_appl.send")) | 2 -> Printf.printf "another appl case\n%!"; Eliom_registration.appl_self_redirect Another_appl.send (make_page (make_content "Another_appl.send")) | _ -> Printf.printf "Files case\n%!"; Eliom_registration.appl_self_redirect Eliom_registration.File.send "/var/www/ocsigen/tutorial/ocsigen5.png" in Eliom_registration.Any.register ~service:any_service (fun choice () -> make_any choice) (*****************************************************************************) (* Gracefull fail to external content *) let never_shown_service = My_appl.register_service ~path:["service_hidden_by_a_file.html"] ~get_params:(Eliom_parameter.unit) (fun () () -> return (make_page [pcdata "this page should never appear: a file with the same name is hidding it";])) let gracefull_fail_with_file = My_appl.register_service ~path:["gracefull_fail_with_file"] ~get_params:unit (fun () () -> return (make_page [Html5.D.a ~service:never_shown_service [pcdata "link to a service hidden by a file"] ();])) (*****************************************************************************) (* correct url with redirections *) let redirected_src_service = My_appl.register_service ~path:["redirect_src"] ~get_params:(Eliom_parameter.unit) (fun () () -> return (make_page [pcdata "this page should never appear: a redirection happen before";])) let redirected_dst_service = My_appl.register_service ~path:["redirect_dst"] ~get_params:(Eliom_parameter.unit) (fun () () -> return (make_page [pcdata "the url in the browser bar should contain redirect_dst and not redirect_src";])) let appl_with_redirect_service = My_appl.register_service ~path:["appl_with_redirect"] ~get_params:unit (fun () () -> return (make_page [Html5.D.a ~service:redirected_src_service [pcdata "link to a service hidden by a redirection"] (); br (); pcdata "there should be a line like: in the configuration file"; ])) (*****************************************************************************) (* Actions with `NoReload option *) let noreload_ref = ref 0 let noreload_action = Eliom_registration.Action.register_coservice' ~options:`NoReload ~get_params:unit (fun () () -> noreload_ref := !noreload_ref + 1; Lwt.return ()) let noreload_appl = My_appl.register_service ~path:["noreloadappl"] ~get_params:unit (fun () () -> return (html (head (title (pcdata "counter")) []) (body [p [pcdata (string_of_int (!noreload_ref)); br (); Html5.D.a ~service:noreload_action [pcdata "Click to increment the counter."] (); br (); pcdata "You should not see the result if you do not reload the page." ]]))) (*****************************************************************************) (* XHR form with files: *) (* TODO: to be fixed in 2.1: need a new parameter for caml_call_service to send files or formulary. Now it uses Eliom_request.send_post_form which is low level and should not be exported let page_content () ((((((case,radio),select),multi),text),pass),file) = Lwt_io.with_file ~mode:Lwt_io.input file.Ocsigen_extensions.tmp_filename Lwt_io.read >|= (fun contents -> [ p [pcdata (if case then "checked" else "not checked")]; p [pcdata (match radio with | None -> "no choice" | Some radio -> Printf.sprintf "radio = %i" radio)]; p [pcdata (Printf.sprintf "select: %s" select)]; p [pcdata (Printf.sprintf "selects: %s" (String.concat ", " multi))]; p [pcdata (Printf.sprintf "text: %s" text)]; p [pcdata (Printf.sprintf "pass: %s" pass)]; p [pcdata (Printf.sprintf "file: name %s length %Li hash: %s" file.Ocsigen_extensions.tmp_filename file.Ocsigen_extensions.filesize (Digest.to_hex (Digest.string contents)))]; ]) let block_form_fallback = Eliom_registration.Blocks5.register_service ~path:["resultblocks"] ~get_params:unit (fun () () -> return [pcdata "nothing"]) let block_form_result = Eliom_registration.Blocks5.register_post_service ~post_params:((((((bool "case" ** (radio (int "radio"))) ** string "select") ** set string "multi") ** string "text") ** string "password") ** file "file") ~fallback:block_form_fallback page_content let make_xhr_form ((((((casename,radio),select),multi),text),pass),file) = [p [pcdata "check ?"; bool_checkbox ~name:casename ()]; p [pcdata "choose ?"; int_radio ~name:radio ~value:1 (); int_radio ~name:radio ~value:42 ()]; p [pcdata "select:"; string_select ~name:select (Option ([],"toto",None,false)) [Option ([],"tutu",Some (pcdata "tutu ?"),true); Optgroup ([],"machin",([],"chose",None,true), [([],"chose2",None,false); ([],"chose3",None,false)]); ]; ]; p [pcdata "multi:"; string_multiple_select ~name:multi (Option ([],"toto",None,false)) [Option ([],"tutu",Some (pcdata "tutu ?"),true); Optgroup ([],"machin",([],"chose",None,true), [([],"chose2",None,false); ([],"chose3",None,false)]); ]; ]; p [string_input ~name:text ~input_type:`Text ~value:"text" ()]; p [string_input ~name:pass ~input_type:`Password ~value:"pass" ()]; p [file_input ~name:file ()]] let xhr_form_with_file = My_appl.register_service ["xhr_form_with_file"] unit (fun () () -> let form = Html5.D.(post_form block_form_result make_xhr_form ()) in let subpage = Html5.D.(div []) in let launch = p ~a:[(*zap* *)a_class ["clickable"];(* *zap*) a_onclick {{ fun _ -> let uri = Eliom_uri.make_string_uri ~service:%block_form_result () in ignore (Eliom_request.send_post_form (Html5.To_dom.of_form %form) uri >|= (fun contents -> ( Html5.To_dom.of_div %subpage )##innerHTML <- (Js.string contents))) }}] [pcdata "send form with an xhr"] in Lwt.return (make_page [ pcdata "this test need upload: add /tmp/upload to the configuration file"; form; launch; subpage])) *) let global_div = Html5.Id.create_global_elt (p ~a:[a_onload {{ fun _ -> debug "Div1: plop once." }}] [pcdata "Div: "; span ~a:[a_onload {{ fun _ -> debug "Span inside Div1: plop once"}}] [pcdata "global"]]) let local_div = Html5.D.p ~a:[a_onload {{ fun _ -> debug "Div2: always plop." }}] [pcdata "Div2: "; span ~a:[a_onload {{ fun _ -> debug "Span inside Div2: always plop";}}] [pcdata "local"]] let simple_div = p ~a:[a_onload {{ fun _ -> debug "Div3: always plop." }}] [pcdata "Div3: "; span ~a:[a_onload {{ fun _ -> debug "Span inside Div3: always plop";}}] [pcdata "classical"]] let unique1 = Eliom_service.service ~path:["appl";"unique1"] ~get_params:Eliom_parameter.unit () let unique2 = Eliom_service.service ~path:["appl";"unique2"] ~get_params:Eliom_parameter.unit () let _ = My_appl.register unique1 (fun () () -> ignore {unit{Eliom_client.onload (fun _ -> debug "Load page 1") }}; return (make_page [h1 [pcdata "Page 1"]; p [pcdata "This page contains three div with attached onload event."; pcdata "Those events display debug messages in the console. "; pcdata "One div is a global element, the other one is not. "; Html5.D.a ~service:unique2 [pcdata "Follow this link"] (); pcdata ", and then press the back button: "; pcdata "only the onload event of the non-global div should be executed."; pcdata "Each div contains a span with an attached onload event. They should be fired whenever the onload event is fired in their fathers." ]; global_div; local_div; simple_div; ])) let body_onload = My_appl.register_service ["body_onload"] Eliom_parameter.unit (fun () () -> return (html (head (title (pcdata "body onload")) []) (body ~a:[a_onload {{ fun _ ->debug "it works"}}] [ p [pcdata "onload on the body element.\n There should be \"it works\" in the console"]; br (); p [pcdata "there will also probably be an error message (caml_closure_id... is not defined). It is not a problem, and we can't simply avoid it"]; ]))) let _ = My_appl.register unique2 (fun () () -> ignore {unit{Eliom_client.onload (fun _ -> debug "Load page 2") }}; return (make_page [h1 [pcdata "Page 2"]; Html5.D.a ~service:unique1 [pcdata "Get back to Page 1."] (); p [pcdata "Try also to reload this page before switching back to Page 1:"; pcdata "The onload event of the unique div should be executed "; pcdata "(when it is appears for the first time)"]; ])) let big_service = Eliom_service.service ~path:["big_page"] ~get_params:Eliom_parameter.unit () let rec big_page n = let link = Html5.D.a ~service:big_service [pcdata "same page"] () in (*let link = p ~a:[a_class ["toto"]] [span [pcdata "rien"]] in*) if n = 0 then link else div [big_page (n-1); big_page (n-1);] let _ = My_appl.register big_service (fun () () -> return (make_page [h1 [pcdata "Big page"]; div [big_page 12]])) let relink_test = Eliom_service.service ~path:["relink_test"] ~get_params:Eliom_parameter.unit () let global_list = Html5.Id.create_global_elt (ul [li [pcdata "First element"]]) let local_list = Html5.D.ul [li [pcdata "First element"]] let relink_page () = ignore {unit{ Eliom_client.onload (fun _ -> debug "onload"; put_li %global_list "Global."; put_li %local_list "Request.") }}; [ div [pcdata "This div contains a global list sent by reference. While the application runs, there should be one new item in the list each time the page is loaded."; global_list]; div [pcdata "This div contains a request list sent by reference. There should be only one item in the list."; local_list]; div [Html5.D.a ~service:relink_test [pcdata "Same page"] (); pcdata " (This should add an item in the global list)"; br (); Html5.D.a ~service:eliomclient1 [pcdata "Another page inside the application"] (); pcdata " (If you use the back button to redisplay this page, there should be a new item in the global list)"; br (); Html5.D.a ~service:Eliom_testsuite_base.main [pcdata "Outside application"] (); pcdata " (If you use the back button to redisplay this page, there should be only only item in the global list)"; br (); ] ] let _ = My_appl.register ~content_type:"text/html" ~service:relink_test (fun () () -> return (make_page (relink_page ()))) {client{ let react_div ?a r = let init = React.S.value r in let node = Html5.D.div ?a init in let node_dom = Html5.To_dom.of_element node in let s = React.S.map (fun sons -> List.iter (fun n -> ignore (node_dom##removeChild((n:> Dom.node Js.t)))) (Dom.list_of_nodeList (node_dom##childNodes)); List.iter (fun n -> let n = Html5.To_dom.of_element n in ignore (node_dom##appendChild((n:> Dom.node Js.t)))) sons) r in Lwt_react.S.keep s; node let count = ref 0 let r,push = React.S.create 0 let react_node node r = let _init = React.S.value r in let node_dom = Html5.To_dom.of_element node in let s = React.S.map (fun sons -> List.iter (fun n -> ignore (node_dom##removeChild((n:> Dom.node Js.t)))) (Dom.list_of_nodeList (node_dom##childNodes)); List.iter (fun n -> let n = Html5.To_dom.of_element n in ignore (node_dom##appendChild((n:> Dom.node Js.t)))) sons) r in Lwt_react.S.keep s }} let react_example = Eliom_service.service ~path:["react_example"] ~get_params:Eliom_parameter.unit () let () = My_appl.register react_example (fun () () -> let click_div = Html5.D.div ~a:[a_onclick {{ fun _ -> push (incr count; !count) }}] [] in ignore {unit{ Eliom_client.onload (fun _ -> react_node %click_div (React.S.map (fun i -> [pcdata (Printf.sprintf "value: %i" i)]) r)) }}; Lwt.return (make_page [click_div])) (************ onload with caml service ***********) let caml_service_with_onload' = Eliom_registration.Ocaml.register_service ~path:["caml_service_with_onload'"] ~get_params:Eliom_parameter.unit (fun () () -> let node = Html5.D.div [pcdata "new div"] in ignore {unit{ Eliom_client.onload (fun _ -> let node = Html5.To_dom.of_div %node in ignore (Dom_html.addEventListener node Dom_html.Event.click (Dom_html.handler (fun _ -> Dom_html.window##alert(Js.string "clicked!"); Js._true)) Js._true); ()) }}; Lwt.return (node : Html5_types.div Html5.F.elt)) let caml_service_with_onload = My_appl.register_service ~path:["caml_service_with_onload"] ~get_params:Eliom_parameter.unit (fun () () -> let click_div = div ~a:[a_onclick {{ fun _ -> ignore ( lwt node = Eliom_client.call_caml_service ~service:( %caml_service_with_onload' ) () () in let node = Html5.To_dom.of_div node in ignore (Dom_html.document##body##appendChild( (node:> Dom.node Js.t) )); Lwt.return () ) }}] [pcdata "click"] in Lwt.return (make_page [ pcdata "onload with caml call service. A node should appear when clicking. An alert should be displayed when clicking the new nodes."; click_div])) (***********************) (* Request unique node *) let rec dom_div_tree v width height = if height = 0 then (incr v; [pcdata (string_of_int !v ^ " - ")]) else Array.to_list (Array.init width (fun i -> Html5.D.div (dom_div_tree v width (height-1)))) let dom_div_tree w h = dom_div_tree (ref 0) w h let rec div_tree v width height = if height = 0 then (incr v; [pcdata (string_of_int !v ^ " - ")]) else Array.to_list (Array.init width (fun i -> Html5.F.div (div_tree v width (height-1)))) let div_tree w h = div_tree (ref 0) w h let domnodes_timings = Eliom_service.service ~path:["domnodes_timings"] ~get_params:(int "widht" ** int "height") () let nodes_timings = Eliom_service.service ~path:["nodes_timings"] ~get_params:(int "widht" ** int "height") () let rec power n m = if m = 0 then 1 else let h = power n (m/2) in if m mod 2 = 1 then h * h * n else h * h {client{ let change_target_value ev = let v = if !Eliom_config.debug_timings then "Deactivate timings" else "Activate timings" in Js.Optdef.case (ev##target) (fun () -> assert false) (fun elt -> Js.Opt.case (Dom_html.CoerceTo.input (elt)) (fun () -> assert false) (fun input -> input##value <- Js.string v)); }} let activate_timings_button = Html5.Id.create_global_elt (Html5.F.string_input ~a:[ Html5.F.a_onclick {{ fun ev -> Eliom_config.debug_timings := not (!Eliom_config.debug_timings); change_target_value ev; }}] ~input_type:`Submit ~value:"Activate timings" ()) let update_tree service w h = Html5.F.get_form ~service (fun (wn,hn) -> [ Html5.F.fieldset [ Html5.F.label ~a:[Html5.D.a_for wn] [pcdata "Tree width: "]; Html5.F.int_input ~name:wn ~input_type:`Text ~value:w (); Html5.F.label ~a:[Html5.D.a_for wn] [pcdata "and height: "]; Html5.F.int_input ~name:hn ~input_type:`Text ~value:h (); Html5.F.string_input ~input_type:`Submit ~value:"Update" (); ] ]) let _ = My_appl.register ~service:domnodes_timings (fun (w,h) () -> let div = Html5.F.div ~a:[a_style "display:none;"] (dom_div_tree w h) in Lwt.return (make_page [Html5.F.h2 [pcdata (Printf.sprintf "Huge tree of dom nodes (%d^%d = %d)" w h (power w h))]; Html5.F.p [pcdata "This page contains a hidden tree of dom nodes (a.k.a unique nodes of scope request). "; pcdata "Activate timings and look into the console how the 'relink_request_nodes' value "; pcdata "evolves when the number of unique nodes increase. Then compare timings on the "; Html5.D.a nodes_timings [pcdata "same page with non-unique nodes"] (w,h); pcdata "."]; activate_timings_button; update_tree domnodes_timings w h; div])) let _ = My_appl.register ~service:nodes_timings (fun (w,h) () -> let div = Html5.F.div ~a:[a_style "display:none;"] (div_tree w h) in Lwt.return (make_page [Html5.F.h2 [pcdata (Printf.sprintf "Huge tree of classical nodes (%d^%d = %d)" w h (power w h))]; Html5.F.p [Html5.D.a domnodes_timings [pcdata "Back to unique nodes."] (w,h)]; activate_timings_button; update_tree domnodes_timings w h; div])) let shared_dom_nodes = My_appl.register_service ~path:["shared_dom_nodes"] ~get_params:unit (fun () () -> let li = Html5.D.li [pcdata "Shared item"] in let li_appl = Html5.Id.create_global_elt (Html5.F.li [pcdata "Shared item"]) in Lwt.return (make_page [Html5.F.h2 [pcdata "Multiple occurences of a unique node"]; Html5.F.p [pcdata "The following list contains two occurences of a unique node items (of scope request). "; pcdata "One between item A and item B ; one between B and C. "; pcdata "Only the second one should be displayed."]; Html5.F.ul [ Html5.F.li [pcdata "Non-shared item A"]; li; Html5.F.li [pcdata "Non-shared item B"]; li; Html5.F.li [pcdata "Non-shared item C"];]; Html5.F.p [pcdata "It is possible that for a very short period of time the first one appears. "; pcdata "However, programmer probably do not want to use multiple occurences of a unique node "; pcdata "and this \"blink\" will be a good reminder of unique node misuse..."]; Html5.F.p [pcdata "Same game with scope application."]; Html5.F.ul [ Html5.F.li [pcdata "Non-shared item A"]; li_appl; Html5.F.li [pcdata "Non-shared item B"]; li_appl; Html5.F.li [pcdata "Non-shared item C"];]; ])) (**** TEMPLATE ****) let tmpl1_page1 = Eliom_service.service ~path:["tmpl1";"page1"] ~get_params:unit () let tmpl1_page2 = Eliom_service.service ~path:["tmpl1";"page2"] ~get_params:unit () let tmpl1_page3 = Eliom_service.service ~path:["tmpl1";"page3"] ~get_params:unit () let tmpl2_page1 = Eliom_service.service ~path:["tmpl2";"page1"] ~get_params:unit () let tmpl2_page2 = Eliom_service.service ~path:["tmpl2";"page2"] ~get_params:unit () let tmpl1_update id contents = {{ Eliom_client.onload (fun () -> debug "Update"; Html5.Manip.Named.replaceAllChild %id %contents) }} module Tmpl_1 = Eliom_registration.Eliom_tmpl(My_appl)(struct type t = Html5_types.flow5 Html5.elt list let name = "template_one" let content_id = Html5.Id.new_elt_id () let make_page contents = Lwt.return Html5.F.(make_page [h2 [pcdata "Template #1"]; ul [li [Html5.D.a ~service:tmpl1_page1 [pcdata "Page 1"] ()]; li [Html5.D.a ~service:tmpl1_page2 [pcdata "Page 2"] ()]; li [Html5.D.a ~service:tmpl1_page3 [pcdata "Page 3"] ()]; li [Html5.D.a ~service:tmpl2_page1 [pcdata "Page 1 (tmpl2)"] ()]; li [Html5.D.a ~service:tmpl2_page2 [pcdata "Page 2 (tmpl2)"] ()]; li ~a:[a_onclick {{ fun _ -> lwt_ignore(Eliom_client.change_page ~service:%tmpl1_page1 () ())}}] [pcdata "Click me 1 (change_page)"]; li ~a:[a_onclick {{ fun _ -> lwt_ignore(Eliom_client.change_page ~service:%tmpl1_page2 () ())}}] [pcdata "Click me 2 (change_page)"]; li ~a:[a_onclick {{ fun _ -> lwt_ignore(Eliom_client.change_page ~service:%tmpl1_page3 () ())}}] [pcdata "Click me 3 (change_page)"]; li ~a:[a_onclick {{ fun _ -> lwt_ignore(Eliom_client.change_page ~service:%tmpl2_page1 () ())}}] [pcdata "Click me 1 (change_page, tmpl2)"]; li ~a:[a_onclick {{ fun _ -> lwt_ignore(Eliom_client.change_page ~service:%tmpl2_page2 () ())}}] [pcdata "Click me 2 (change_page, tmpl2)"]; ]; Html5.Id.create_named_elt ~id:content_id (div contents)]) let update = tmpl1_update content_id end) module Tmpl_2 = Eliom_registration.Eliom_tmpl(My_appl)(struct type t = Html5_types.flow5 Html5.elt list let name = "template_two" let content_id = Html5.Id.new_elt_id () let make_page contents = Lwt.return Html5.F.(make_page [h2 [pcdata "Template #2"]; ul [li [Html5.D.a ~service:tmpl1_page1 [pcdata "Page 1 (tmpl1)"] ()]; li [Html5.D.a ~service:tmpl1_page2 [pcdata "Page 2 (tmpl1)"] ()]; li [Html5.D.a ~service:tmpl1_page3 [pcdata "Page 3 (tmpl1)"] ()]; li [Html5.D.a ~service:tmpl2_page1 [pcdata "Page 1"] ()]; li [Html5.D.a ~service:tmpl2_page2 [pcdata "Page 2"] ()]; li ~a:[a_onclick {{ fun _ -> lwt_ignore(Eliom_client.change_page ~service:%tmpl1_page1 () ())}}] [pcdata "Click me 1 (change_page, tmpl1)"]; li ~a:[a_onclick {{ fun _ -> lwt_ignore(Eliom_client.change_page ~service:%tmpl1_page2 () ())}}] [pcdata "Click me 2 (change_page, tmpl1)"]; li ~a:[a_onclick {{ fun _ -> lwt_ignore(Eliom_client.change_page ~service:%tmpl1_page3 () ())}}] [pcdata "Click me 3 (change_page, tmpl1)"]; li ~a:[a_onclick {{ fun _ -> lwt_ignore(Eliom_client.change_page ~service:%tmpl2_page1 () ())}}] [pcdata "Click me 1 (change_page)"]; li ~a:[a_onclick {{ fun _ -> lwt_ignore(Eliom_client.change_page ~service:%tmpl2_page2 () ())}}] [pcdata "Click me 2 (change_page)"]; ]; Html5.Id.create_named_elt ~id:content_id (div contents)]) let update = tmpl1_update content_id end) let () = Tmpl_1.register ~service:tmpl1_page1 (fun () () -> Lwt.return [h3 [pcdata "Page 1"]]) let () = Tmpl_1.register ~service:tmpl1_page2 (fun () () -> Lwt.return [h3 [pcdata "Page 2"]]) let () = Tmpl_1.register ~service:tmpl1_page3 (fun () () -> Lwt.return [h3 [pcdata "Page 3"]]) let () = Tmpl_2.register ~service:tmpl2_page1 (fun () () -> Lwt.return [h3 [pcdata "Page 1"]]) let () = Tmpl_2.register ~service:tmpl2_page2 (fun () () -> Lwt.return [h3 [pcdata "Page 2"]]) (**** HISTORY ****) let hist_page1 = Eliom_service.service ~path:["hist";"page1"] ~get_params:unit () let hist_page2 = Eliom_service.service ~path:["hist";"page2"] ~get_params:unit () let hist_page3 = Eliom_service.service ~path:["hist";"page3"] ~get_params:unit () let hist_page4 = Eliom_service.service ~path:["hist";"page4"] ~get_params:unit () let hist_page5 = Eliom_service.service ~path:["hist";"page5"] ~get_params:unit () let make_hist_page contents = Html5.F.(make_page [h2 [pcdata "Test History"]; ul [li [Html5.D.a ~service:hist_page1 [pcdata "Page 1"] ()]; li [Html5.D.a ~service:hist_page2 [pcdata "Page 2"] ()]; li [Html5.D.a ~service:hist_page3 [pcdata "Page 3"] ()]; li [Html5.D.a ~service:hist_page4 [pcdata "Page 4"] ()]; li [Html5.D.a ~service:hist_page5 [pcdata "Page 5"] ()]; ]; div contents]) let () = My_appl.register ~service:hist_page1 (fun () () -> Lwt.return (make_hist_page [h3 [pcdata "Page 1"]])) let () = My_appl.register ~service:hist_page2 (fun () () -> Lwt.return (make_hist_page [h3 [pcdata "Page 2"]])) let () = My_appl.register ~service:hist_page3 (fun () () -> Lwt.return (make_hist_page [h3 [pcdata "Page 3"]])) let () = My_appl.register ~service:hist_page4 (fun () () -> Lwt.return (make_hist_page [h3 [pcdata "Page 4"]])) let () = My_appl.register ~service:hist_page5 (fun () () -> Lwt.return (make_hist_page [h3 [pcdata "Page 5"]])) (**************************************************************) let nl_params = Eliom_parameter.make_non_localized_parameters ~prefix:"tutoeliom" ~name:"mynlparams" (Eliom_parameter.int "a" ** Eliom_parameter.string "s") let nl_serv = service ~path:["appl_nlparams"] ~get_params:(unit) () let _ = My_appl.register ~service:nl_serv (fun () () -> Lwt.return ( make_page [ p [a ~service:nl_serv ~nl_params:(Eliom_parameter.add_nl_parameter Eliom_parameter.empty_nl_params_set nl_params (22, "oh") ) [pcdata "with nl params"] (); br (); a ~service:Eliom_service.void_hidden_coservice' [pcdata "without nl params"] (); pcdata "there is a problem here: click many times on \"witout nl params\" and inspect it"; ]; ])) (***********) let nlpost_entry = Eliom_service.service ~path:["appl_nlpost"] ~get_params:(Eliom_parameter.unit) () let nlpost = Eliom_service.post_coservice ~fallback:nlpost_entry ~name:"appl_nlpost" ~post_params:(Eliom_parameter.unit) () let nlpost_with_nlp = Eliom_service.add_non_localized_get_parameters nl_params nlpost let create_form_nl s = (fun () -> [Html5.F.p [Html5.D.string_input ~input_type:`Submit ~value:s ()]]) let () = My_appl.register nlpost (fun () () -> let nlp = match Eliom_parameter.get_non_localized_get_parameters nl_params with | None -> "no non localised parameter" | Some _ -> "some non localised parameter" in Lwt.return Html5.F.(html (head (title (pcdata "")) []) (body [div [ pcdata nlp; br(); Html5.D.post_form nlpost_with_nlp (create_form_nl "with nl param") ((),(12, "ab")); br (); Html5.D.post_form nlpost (create_form_nl "without nl param") (); ]]))) let () = My_appl.register nlpost_entry (fun () () -> Lwt.return Html5.F.(html (head (title (pcdata "")) []) (body [div [ Html5.D.post_form nlpost_with_nlp (create_form_nl "with nl param") ((),(12, "ab")); br (); Html5.D.post_form nlpost (create_form_nl "without nl param") (); ]]))) (********************************************************) (* test external xhr ( and see if cookies are sent ) *) let some_external_service = Eliom_service.external_service ~prefix:"http://remysharp.com" ~path:["demo";"cors.php"] ~get_params:(Eliom_parameter.unit) () let external_xhr = service ~path:["external_xhr"] ~get_params:(unit) () let _ = My_appl.register ~service:external_xhr (fun () () -> Lwt.return ( make_page [ p ~a:[a_class ["clickable"]; a_onclick {{ fun _ -> debug "click"; ignore ( lwt r = Eliom_client.call_service ~service:%some_external_service () () in debug "result: %s" r; Lwt.return ()) }}] [pcdata "click to do an external xhr"] ])) (********************************************************) (* Test Eliom_config.parse_config *) let () = let elt1_a1 = ref None in let elt1_a2 = ref None in let elt1_init_called = ref false in let elt1_e1_pcdata = ref None in let elt2_other_attributes = ref [] in let elt2_other_elements = ref [] in let x1 = ref None in let x2 = ref None in Eliom_config.parse_config Ocsigen_extensions.Configuration.([ element ~name:"optional-elt" ~init:(fun () -> elt1_init_called := true) ~attributes:[ attribute ~name:"optional-attr" (fun a -> elt1_a1 := Some a); attribute ~name:"obligatory-attr" ~obligatory:true (fun a -> elt1_a2 := Some a); ] ~elements:[ element ~name:"inner-obligatory-elt" ~obligatory:true ~pcdata:(fun str -> elt1_e1_pcdata := Some str) () ] (); element ~name:"obligatory-elt" ~obligatory:true ~attributes:[ attribute ~name:"optional-attr" (fun v -> x1 := Some v); attribute ~name:"obligatory-attr" ~obligatory:true (fun v -> x2 := Some v); ] ~other_attributes:(fun name value -> elt2_other_attributes := (name^"="^value) :: !elt2_other_attributes) ~other_elements:(fun name _ _ -> elt2_other_elements := name :: !elt2_other_elements) () ]); Printf.printf "*************************************\n"; Printf.printf "* Eliom_config.parse_config results *\n%!"; Printf.printf "optional-elt@optional-attr: %s\n%!" (Option.get (fun () -> "---") !elt1_a1); Printf.printf "optional-elt@obligatory-attr: %s\n%!" (Option.get (fun () -> "---") !elt1_a2); Printf.printf "optional-elt init called: %b\n%!" !elt1_init_called; Printf.printf "optional-elt > obligatory-elt PCDATA: %s\n%!" (Option.get (fun () -> "---") !elt1_e1_pcdata); Printf.printf "obligatory-elt@optional-attr-b: %s\n%!" (Option.get (fun () -> "---") !x1); Printf.printf "obligatory-elt@obligatory-attr-b: %s\n%!" (Option.get (fun () -> "---") !x2); Printf.printf "obligatory-elt ATTRIBUTES: %s\n%!" (String.concat " " !elt2_other_attributes); Printf.printf "obligatory-elt ELEMENTS: %s\n%!" (String.concat " " !elt2_other_elements); Printf.printf "*************************************\n%!"; () (********************************************************) (* Test Eliom_service.static_dir *) {shared{ let image : _ Html5.elt = Html5.D.( img ~alt:"some static file" ~src:(Html5.D.make_uri ~service:(Eliom_service.static_dir ()) ["some static file"]) () ) }} (********************************************************) (* Extensive test of states *) let states_test = Eliom_service.service ~path:["states"; ""] ~get_params:(Eliom_parameter.unit) () let states_test_bis = Eliom_service.service ~path:["states"] ~get_params:(Eliom_parameter.suffix (string "group")) () let next = let c = ref 0 in (fun () -> c := !c + 1; !c) let nexti _ = next () let () = My_appl.register states_test (fun () () -> Lwt.return Html5.F.( html (head (title (pcdata "States test")) []) (body [ h1 [pcdata "Testing states for different scopes"]; p [ pcdata "This test is an extensive test of Eliom references of different scopes, accessed from inside or outside the state itself. To run this test, you need at least two different browsers, one with several tabs on "; Html5.D.a states_test_bis [pcdata "this page"] "A"; pcdata " and another with several tabs on "; Html5.D.a states_test_bis [pcdata "this other page"] "B"; pcdata ". All tabs of a same browser must be one the same page, because loading the page sets the session group. Read the instructions on these pages."; ]]))) let vgr = Eliom_reference.Volatile.eref_from_fun ~scope:Eliom_common.default_group_scope next let vsr = Eliom_reference.Volatile.eref_from_fun ~scope:Eliom_common.default_session_scope next let vpr = Eliom_reference.Volatile.eref_from_fun ~scope:Eliom_common.default_process_scope next let pgr = Eliom_reference.eref_from_fun ~scope:Eliom_common.default_group_scope ~persistent:"pgr" next let psr = Eliom_reference.eref_from_fun ~scope:Eliom_common.default_session_scope ~persistent:"psr" next let ppr = Eliom_reference.eref_from_fun ~scope:Eliom_common.default_process_scope ~persistent:"ppr" next let change_gr = Eliom_registration.Ocaml.register_post_coservice' ~post_params:unit (fun () () -> let () = Eliom_reference.Volatile.modify vgr nexti in Eliom_reference.modify pgr nexti) let change_sr = Eliom_registration.Ocaml.register_post_coservice' ~post_params:unit (fun () () -> let () = Eliom_reference.Volatile.modify vsr nexti in Eliom_reference.modify psr nexti) let change_pr = Eliom_registration.Ocaml.register_post_coservice' ~post_params:unit (fun () () -> let () = Eliom_reference.Volatile.modify vpr nexti in Eliom_reference.modify ppr nexti) let change_other_gr = Eliom_registration.Ocaml.register_post_coservice' ~post_params:(string "g") (fun () g -> let vstate = Eliom_state.Ext.volatile_data_group_state g in let pstate = Eliom_state.Ext.persistent_data_group_state g in Eliom_reference.Volatile.Ext.modify vstate vgr nexti; Eliom_reference.Ext.modify pstate pgr nexti) let change_other_sr = Eliom_registration.Ocaml.register_post_coservice' ~post_params:(string "g") (fun () g -> let vstate = Eliom_state.Ext.volatile_data_group_state g in lwt () = Eliom_state.Ext.iter_sub_states vstate (fun state -> Eliom_reference.Volatile.Ext.modify state vsr nexti; Lwt.return ()) in let pstate = Eliom_state.Ext.persistent_data_group_state g in Eliom_state.Ext.iter_sub_states pstate (fun state -> Eliom_reference.Ext.modify state psr nexti)) let change_other_pr = Eliom_registration.Ocaml.register_post_coservice' ~post_params:(string "g") (fun () g -> let vstate = Eliom_state.Ext.volatile_data_group_state g in lwt () = Eliom_state.Ext.iter_sub_states vstate (fun state -> Eliom_state.Ext.iter_sub_states state (fun state -> Eliom_reference.Volatile.Ext.modify state vpr nexti; Lwt.return ())) in let pstate = Eliom_state.Ext.persistent_data_group_state g in Eliom_state.Ext.iter_sub_states pstate (fun state -> Eliom_state.Ext.iter_sub_states state (fun state -> Eliom_reference.Ext.modify state ppr nexti))) let () = My_appl.register states_test_bis (fun group () -> let other_group = if group = "A" then "B" else "A" in Eliom_state.set_volatile_data_session_group ~set_max:4 group; lwt () = Eliom_state.set_persistent_data_session_group ~set_max:(Some 4) group in let vgr = Eliom_reference.Volatile.get vgr in let vsr = Eliom_reference.Volatile.get vsr in let vpr = Eliom_reference.Volatile.get vpr in lwt pgr = Eliom_reference.get pgr in lwt psr = Eliom_reference.get psr in lwt ppr = Eliom_reference.get ppr in Lwt.return Html5.F.( html (head (title (pcdata ("States test — group "^group))) []) (body [ h1 [pcdata ("Testing states for different scopes — This browser session belongs to group "^group)]; p [ pcdata ("These (persistent and data) sessions belongs to a group called \""^group^"\".")]; p [ pcdata "Here are the values of differents Eliom references. To update the values after a test, "; strong [Raw.a ~a:[a_class ["clickable"]; a_onclick {{ fun _ -> ignore(Eliom_client.change_page ~service:%Eliom_service.void_coservice' () ()) }}] [pcdata "click here"]]; pcdata " (change_page to myself)." ]; p [pcdata "(volatile/persistent) group references: "; strong [pcdata (string_of_int vgr)]; pcdata "/"; strong [pcdata (string_of_int pgr)]; em [pcdata " — check that they are the same on all tabs, all browsers in this group"]; br (); strong [Raw.a ~a:[a_class ["clickable"]; a_onclick {{ fun _ -> ignore(Eliom_client.call_caml_service ~service:%change_gr () ()) }}] [pcdata "Click here to change the values on server side"]]; pcdata ", then update and check."; br (); strong [Raw.a ~a:[a_class ["clickable"]; a_onclick {{ fun _ -> ignore(Eliom_client.call_caml_service ~service:%change_other_gr () %other_group) }}] [pcdata "Click here to change the values on server side for the other group"]]; pcdata ", then update and check group references on browsers belonging to the other group."; ]; p [pcdata "(volatile/persistent) session references: "; strong [pcdata (string_of_int vsr)]; pcdata "/"; strong [pcdata (string_of_int psr)]; em [pcdata " — check that they are the same on all tabs of this browser, but not other browsers."]; br (); strong [Raw.a ~a:[a_class ["clickable"]; a_onclick {{ fun _ -> ignore(Eliom_client.call_caml_service ~service:%change_sr () ()) }}] [pcdata "Click here to change the values on server side"]]; pcdata ", then update and check."; br (); strong [Raw.a ~a:[a_class ["clickable"]; a_onclick {{ fun _ -> ignore(Eliom_client.call_caml_service ~service:%change_other_sr () %other_group) }}] [pcdata "Click here to change the values on server side for the other group"]]; pcdata ", then update and check session references on browsers belonging to the other group."; ]; p [pcdata "(volatile/persistent) process references: "; strong [pcdata (string_of_int vpr)]; pcdata "/"; strong [pcdata (string_of_int ppr)]; em [pcdata " — check that they are different on all tabs"]; br (); strong [Raw.a ~a:[a_class ["clickable"]; a_onclick {{ fun _ -> ignore(Eliom_client.call_caml_service ~service:%change_pr () ()) }}] [pcdata "Click here to change the values on server side"]]; pcdata ", then update and check."; br (); strong [Raw.a ~a:[a_class ["clickable"]; a_onclick {{ fun _ -> ignore(Eliom_client.call_caml_service ~service:%change_other_pr () %other_group) }}] [pcdata "Click here to change the values on server side for the other group"]]; pcdata ", then update and check on browsers belonging to the other group."; ]; ]))) eliom-3.0.3/tests/monitoring.ml0000644000000000000000000001554712062377521014712 0ustar0000000000000000(* Ocsigen * Copyright (C) 2005 Vincent Balat * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, with linking exception; * either version 2.1 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *) (* A page providing infos about the server (number of sessions, uptime...) *) open Ocsigen_extensions (* for profiling info *) open Eliom_output.Xhtml open Eliom_output open Eliom_service open Eliom_parameter open Eliom_state open Unix open Lwt let launchtime = Unix.time () let _ = register_service ~path:[] ~get_params:unit (fun () () -> let tm = Unix.gmtime ((Unix.time ()) -. launchtime) in let year = if tm.tm_year>0 then (string_of_int (tm.tm_year - 70))^" years, " else "" in let days = (string_of_int (tm.tm_yday))^" days, " in let hour = (string_of_int (tm.tm_hour))^" hours, " in let min = (string_of_int (tm.tm_min))^" min, " in let sec = (string_of_int (tm.tm_sec))^" seconds" in let uptime = year^days^hour^min^sec in let stat = Gc.quick_stat () in let size = string_of_int (stat.Gc.heap_words) in let maxsize = string_of_int (stat.Gc.top_heap_words) in let compactions = string_of_int stat.Gc.compactions in let mincol = string_of_int stat.Gc.minor_collections in let majcol = string_of_int stat.Gc.major_collections in let top_heap_words = string_of_int stat.Gc.top_heap_words in let pid = string_of_int (Unix.getpid ()) in let fd = try let dir = Unix.opendir ("/proc/"^pid^"/fd") in let rec aux v = try ignore ((* print_endline *) (readdir dir)); aux (v+1) with End_of_file -> v in let r = try string_of_int ((aux 0) - 2) with e -> ("(Error: "^(Printexc.to_string e)^")") in Unix.closedir dir; Some r with _ -> None in let nssess = Eliom_state.number_of_service_sessions () in let ndsess = Eliom_state.number_of_volatile_data_sessions () in let ntables = Eliom_state.number_of_tables () in let ntableselts = Eliom_state.number_of_table_elements () in Eliom_state.number_of_persistent_data_sessions () >>= (fun nbperssess -> let nbperstab = Eliom_state.number_of_persistent_tables () in Eliom_state.number_of_persistent_table_elements () >>= (fun nbperstabel -> let dot = <:xhtmllist< . >> in let list1 a l = <:xhtmllist< with, respectively $str:List.fold_left (fun deb i -> deb^", "^(string_of_int i)) (string_of_int a) l $ elements inside. >> in let list2 (n,a) l = <:xhtmllist< with, respectively $str:List.fold_left (fun deb (s, i) -> deb^", "^s^" : "^(string_of_int i)) (n^" : "^(string_of_int a)) l$ elements inside. >> in Eliommod_sessiongroups.Pers.nb_of_groups () >>= fun persgrplength -> Lwt.return <<

Ocsigen server monitoring

Version of Ocsigen: $str:Ocsigen_config.version_number$

Uptime: $str:uptime$.

The number of sessions is not available in this version of Eliom.

Number of clients connected: $str:(string_of_int (get_number_of_connected ()))$.

PID : $str:pid$

$str:match fd with None -> "Information on file descriptors not accessible in /proc." | Some fd -> fd^" file descriptors opened."$

GC

Size of major heap: $str:size$ words (max: $str:maxsize$).

Since the beginning:

  • $str:mincol$ minor garbage collections,
  • $str:majcol$ major collections,
  • $str:compactions$ compactions of the heap.
  • Maximum size reached by the major heap: $str:top_heap_words$ words.

Lwt threads

removed

Preemptive threads

There are currently $str:(string_of_int (Lwt_preemptive.nbthreads ()))$ detached threads running (min $str:(string_of_int (Ocsigen_config.get_minthreads ()))$, max $str:(string_of_int (Ocsigen_config.get_maxthreads ()))$), from which $str:(string_of_int (Lwt_preemptive.nbthreadsbusy ()))$ are busy. $str:(string_of_int (Lwt_preemptive.nbthreadsqueued ()))$ computations queued (max $str:(string_of_int (Ocsigen_config.get_max_number_of_threads_queued ()))$).

HTTP connexions

Number of $str:"" (* string_of_int (Ocsigen_http_com.Timeout.nb_threads_waiting_timeout ()) *)$ connexions waiting for timeout: not implemented in that version

Eliom sessions

There are $str:string_of_int nssess$ Eliom service sessions opened, $str:string_of_int ndsess$ Eliom in memory data sessions opened.
There are $str:string_of_int ntables$ Eliom in memory tables created $list: (match ntableselts with | [] -> dot | a::l -> list1 a l) $

There are $str:string_of_int nbperssess$ Eliom persistent sessions opened.
and $str:string_of_int nbperstab$ Eliom persistent tables created $list: (match nbperstabel with | [] -> dot | a::l -> list2 a l) $

Number of session groups:

  • Service sessions: $str: string_of_int (Eliommod_sessiongroups.Serv.nb_of_groups ())$
  • Volatile data sessions: $str: string_of_int (Eliommod_sessiongroups.Data.nb_of_groups ())$
  • Persistent data sessions: $str: string_of_int persgrplength$
>>))) (* Lwt threads: $str:(string_of_int (Lwt_unix.inputs_length ()))$ lwt threads waiting for inputs
$str:(string_of_int (Lwt_unix.outputs_length ()))$ lwt threads waiting for outputs
$str:(string_of_int (Lwt_unix.wait_children_length ()))$ lwt threads waiting for children
$str:(string_of_int (Lwt_unix.sleep_queue_size ()))$ sleeping lwt threads
$str:(string_of_int (Lwt_unix.get_new_sleeps ()))$ new sleeps.
*) eliom-3.0.3/tests/eliom_testsuite_site.eliom0000644000000000000000000000315312062377521017452 0ustar0000000000000000 (*****************************************************************************) (** References of scope site *) open Eliom_content open Eliom_lib let reference_scope_site = let action = Eliom_registration.Action.register_post_coservice' ~post_params:(Eliom_parameter.string "v") (fun () v -> lwt () = Eliom_reference.set Eliom_testsuite_global.eref (Some v) in Eliom_reference.set Eliom_testsuite_global.eref' (Some v)) in Eliom_registration.Html5.register_service ~path:["reference_scope_site"] ~get_params:Eliom_parameter.unit (fun () () -> let show = function None -> Html5.D.entity "#x2012" | Some str -> Html5.D.pcdata str in lwt v = Lwt.map show (Eliom_reference.get Eliom_testsuite_global.eref) in lwt v' = Lwt.map show (Eliom_reference.get Eliom_testsuite_global.eref') in Lwt.return Html5.D.( html (head (title (pcdata "")) []) (body [ p [ pcdata "This is site "; em [pcdata (Eliom_common.((get_sp ()).sp_sitedata.config_info).Ocsigen_extensions.default_hostname)]; ]; p [pcdata "Open other site (substitute localhost by 127.0.0.1 in the URL or vice verse)."]; p [ pcdata "Current value "; i [v]; pcdata ", persistent "; i [v']; ]; pcdata "Enter a new string for both references"; Html5.D.post_form ~service:action (fun name -> [Html5.D.string_input ~input_type:`Text ~name ()]) () ]) )) eliom-3.0.3/tests/.depend0000644000000000000000000000532412062377521013423 0ustar0000000000000000_server/eliom_testsuite_base.cmo : _server/eliom_testsuite_base.type_mli _server/eliom_testsuite_base.cmx : _server/eliom_testsuite_base.type_mli _server/eliom_testsuite_base.cmo : _server/eliom_testsuite_base.cmx : _server/eliom_testsuite1.cmo : _server/eliom_testsuite1.cmx : _server/eliom_testsuite2.cmo : _server/eliom_testsuite1.cmo _server/eliom_testsuite2.cmx : _server/eliom_testsuite1.cmx _server/eliom_testsuite3.cmo : _server/eliom_testsuite3.type_mli _server/eliom_testsuite3.cmx : _server/eliom_testsuite3.type_mli _server/eliom_testsuite3.cmo : _server/eliom_testsuite_base.cmo _server/eliom_testsuite1.cmo _server/eliom_testsuite3.cmx : _server/eliom_testsuite_base.cmx _server/eliom_testsuite1.cmx _server/eliom_testsuite4.cmo : _server/eliom_testsuite4.type_mli _server/eliom_testsuite4.cmx : _server/eliom_testsuite4.type_mli _server/eliom_testsuite4.cmo : _server/eliom_testsuite_base.cmo _server/eliom_testsuite4.cmx : _server/eliom_testsuite_base.cmx _server/eliom_testsuite.cmo : _server/eliom_testsuite2.cmo _server/eliom_testsuite1.cmo _server/eliom_testsuite.cmx : _server/eliom_testsuite2.cmx _server/eliom_testsuite1.cmx _server/eliom_testsuite_global.cmo : _server/eliom_testsuite_global.type_mli _server/eliom_testsuite_global.cmx : _server/eliom_testsuite_global.type_mli _server/eliom_testsuite_global.cmo : _server/eliom_testsuite_global.cmx : _server/eliom_testsuite_site.cmo : _server/eliom_testsuite_site.type_mli _server/eliom_testsuite_site.cmx : _server/eliom_testsuite_site.type_mli _server/eliom_testsuite_site.cmo : _server/eliom_testsuite_global.cmo _server/eliom_testsuite_site.cmx : _server/eliom_testsuite_global.cmx _client/eliom_testsuite_base.cmo : _server/eliom_testsuite_base.type_mli _client/eliom_testsuite_base.cmx : _server/eliom_testsuite_base.type_mli _client/eliom_testsuite_base.cmo : _client/eliom_testsuite_base.cmx : _client/eliom_testsuite3.cmo : _server/eliom_testsuite3.type_mli _client/eliom_testsuite3.cmx : _server/eliom_testsuite3.type_mli _client/eliom_testsuite3.cmo : _client/eliom_testsuite3.cmx : _client/eliom_testsuite4.cmo : _server/eliom_testsuite4.type_mli _client/eliom_testsuite4.cmx : _server/eliom_testsuite4.type_mli _client/eliom_testsuite4.cmo : _client/eliom_testsuite_base.cmo _client/eliom_testsuite4.cmx : _client/eliom_testsuite_base.cmx _client/eliom_testsuite_global.cmo : _server/eliom_testsuite_global.type_mli _client/eliom_testsuite_global.cmx : _server/eliom_testsuite_global.type_mli _client/eliom_testsuite_global.cmo : _client/eliom_testsuite_global.cmx : _client/eliom_testsuite_site.cmo : _server/eliom_testsuite_site.type_mli _client/eliom_testsuite_site.cmx : _server/eliom_testsuite_site.type_mli _client/eliom_testsuite_site.cmo : _client/eliom_testsuite_site.cmx : eliom-3.0.3/tests/eliom_testsuite2.ml0000644000000000000000000022734612062377521016027 0ustar0000000000000000(* Ocsigen * http://www.ocsigen.org * Module eliomexamples.ml * Copyright (C) 2007 Vincent Balat * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, with linking exception; * either version 2.1 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *) (* Other examples for Eliom, and various tests *) open Eliom_lib open Eliom_content open Html5.F open Lwt open Eliom_parameter open Ocsigen_cookies (*****************************************************************************) (* Test for raw_post_data *) let raw_post_example = Eliom_registration.Html5.register_service ~path:["rawpost"] ~get_params:unit (fun () () -> Lwt.return (html (head (title (pcdata "raw post data")) []) (body [p [pcdata "It is possible to send POST data to this URL, using any content-type other than form data or multipart. Try it with telnet. Cut and paste in a terminal:"]; pre [pcdata "telnet localhost 8080 POST /rawpost HTTP/1.0 Content-type: plop/plop Content-length: 124"]; ])) ) let raw_post_service = Eliom_registration.Html5.register_post_service ~fallback:raw_post_example ~post_params:raw_post_data (fun () (ct, stream) -> let ct = match ct with | None -> "" | Some ((content_type1, content_type2), _) -> content_type1^"/"^content_type2 in (match stream with | None -> Lwt.return "" | Some stream -> Ocsigen_stream.string_of_stream 1000 (Ocsigen_stream.get stream)) >>= fun s -> Lwt.return (html (head (title (pcdata "raw post data")) []) (body [p [pcdata "I received POST data, with content-type = "; pcdata ct; pcdata ", and the first 1000 bytes of the content are:"]; p [pcdata s]]) ) ) (*****************************************************************************) (************************************************************) (****************** Connection of users *********************) (************************************************************) (*zap* *) let scope_hierarchy = Eliom_common.create_scope_hierarchy "connect_example_state" let session = `Session scope_hierarchy (* *zap*) (* -------------------------------------------------------- *) (* We create one main service and two (POST) actions *) (* (for connection and disconnection) *) let connect_example = Eliom_service.service ~path:["connect_example"] ~get_params:unit () let connect_action = Eliom_service.post_coservice' ~name:"connection" ~post_params:(string "login") () (* disconnect action and box: *) let disconnect_action = Eliom_registration.Action.register_post_coservice' ~name:"disconnection" ~post_params:Eliom_parameter.unit (fun () () -> Eliom_state.discard ~scope:session ()) let disconnect_box s = Html5.D.post_form disconnect_action (fun _ -> [p [Html5.D.string_input ~input_type:`Submit ~value:s ()]]) () (* The following eref is true if the connection has action failed: *) let bad_user = Eliom_reference.eref ~scope:Eliom_common.request_scope false (* The following eref is the name of the user, when connected *) let user = Eliom_reference.eref ~scope:session None (* -------------------------------------------------------- *) (* new login box: *) let login_box session_expired bad_u action = Html5.D.post_form action (fun loginname -> let l = [pcdata "login: "; Html5.D.string_input ~input_type:`Text ~name:loginname ()] in [p (if bad_u then (pcdata "Wrong user")::(br ())::l else if session_expired then (pcdata "Session expired")::(br ())::l else l) ]) () (* -------------------------------------------------------- *) (* Handler for the "connect_example" service (main page): *) let connect_example_handler () () = (* The following function tests whether the session has expired: *) let status = Eliom_state.volatile_data_state_status (*zap* *) ~scope:session (* *zap*) () in Eliom_reference.get bad_user >>= fun bad_u -> Eliom_reference.get user >>= fun u -> Lwt.return (html (head (title (pcdata "")) []) (body (match u, status with | Some name, _ -> [p [pcdata ("Hello "^name); br ()]; disconnect_box "Close session"] | None, Eliom_state.Expired_state -> [login_box true bad_u connect_action; p [em [pcdata "The only user is 'toto'."]]] | _ -> [login_box false bad_u connect_action; p [em [pcdata "The only user is 'toto'."]]] ))) (* -------------------------------------------------------- *) (* Handler for connect_action (user logs in): *) let connect_action_handler () login = lwt () = Eliom_state.discard ~scope:session () in if login = "toto" (* Check user and password :-) *) then Eliom_reference.set user (Some login) else Eliom_reference.set bad_user true (* -------------------------------------------------------- *) (* Registration of main services: *) let () = Eliom_registration.Html5.register ~service:connect_example connect_example_handler; Eliom_registration.Action.register ~service:connect_action connect_action_handler (*****************************************************************************) (************************************************************) (********* Connection of users with session groups **********) (************************************************************) (*zap* *) let scope_hierarchy = Eliom_common.create_scope_hierarchy "session_group_example_state" let session = `Session scope_hierarchy (* *zap*) (* -------------------------------------------------------- *) (* We create one main service and two (POST) actions *) (* (for connection and disconnection) *) let connect_example = Eliom_service.service ~path:["sessgrp"] ~get_params:unit () let connect_action = Eliom_service.post_coservice' ~name:"connection2" ~post_params:(string "login") () (* disconnect action and box: *) let disconnect_action = Eliom_registration.Action.register_post_coservice' ~name:"disconnection2" ~post_params:Eliom_parameter.unit (fun () () -> Eliom_state.discard ~scope:session ()) let disconnect_box s = Html5.D.post_form disconnect_action (fun _ -> [p [Html5.D.string_input ~input_type:`Submit ~value:s ()]]) () (* The following eref is true if the connection has action failed: *) let bad_user = Eliom_reference.eref ~scope:Eliom_common.request_scope false (* -------------------------------------------------------- *) (* new login box: *) let login_box session_expired bad_u action = Html5.D.post_form action (fun loginname -> let l = [pcdata "login: "; Html5.D.string_input ~input_type:`Text ~name:loginname ()] in [p (if bad_u then (pcdata "Wrong user")::(br ())::l else if session_expired then (pcdata "Session expired")::(br ())::l else l) ]) () (* -------------------------------------------------------- *) (* Handler for the "connect_example" service (main page): *) let connect_example_handler () () = (* The following function tests whether the session has expired: *) let status = Eliom_state.volatile_data_state_status (*zap* *) ~scope:session (* *zap*) () in let group = Eliom_state.get_volatile_data_session_group (*zap* *) ~scope:session (* *zap*) () in let group_size = Eliom_state.get_volatile_data_session_group_size ~scope:session () in Eliom_reference.get bad_user >>= fun bad_u -> Lwt.return (html (head (title (pcdata "")) []) (body (match group, group_size, status with | Some name, Some size , _ -> [p [pcdata ("Hello "^name); br (); pcdata (Printf.sprintf "There are %i session in this group" size); br ()]; disconnect_box "Close session"] | None, _, Eliom_state.Expired_state -> [login_box true bad_u connect_action; p [em [pcdata "The only user is 'toto'."]]] | _ -> [login_box false bad_u connect_action; p [em [pcdata "The only user is 'toto'."]]] ))) (* -------------------------------------------------------- *) (* Handler for connect_action (user logs in): *) let connect_action_handler () login = lwt () = Eliom_state.discard ~scope:session () in if login = "toto" (* Check user and password :-) *) then begin Eliom_state.set_volatile_data_session_group ~set_max:4 (*zap* *) ~scope:session (* *zap*) login; Eliom_registration.Redirection.send Eliom_service.void_hidden_coservice' end else Eliom_reference.set bad_user true >>= fun () -> Eliom_registration.Action.send () (* -------------------------------------------------------- *) (* Registration of main services: *) let () = Eliom_registration.Html5.register ~service:connect_example connect_example_handler; Eliom_registration.Any.register ~service:connect_action connect_action_handler (*****************************************************************************) let myeref = Eliom_reference.eref ~scope:`Global ~persistent:"perscount" 0 let count3 = let next = let mutex = Lwt_mutex.create () in (fun () -> Lwt_mutex.lock mutex >>= fun () -> Eliom_reference.get myeref >>= fun oldc -> let newc = oldc + 1 in Eliom_reference.set myeref newc >>= fun () -> Lwt_mutex.unlock mutex; Lwt.return newc) in Eliom_registration.Html5.register_service ~path:["count3"] ~get_params:unit (fun () () -> next () >>= (fun n -> Lwt.return (Html5.F.html (Html5.F.head (Html5.F.title (Html5.F.pcdata "counter")) []) (Html5.F.body [Html5.F.p [Html5.F.pcdata (string_of_int n)]])))) (*****************************) (* Volatile eliom references *) let volatile_references = let page elts = Html5.D.( html (head (title (pcdata "Volatile reference")) []) (body elts) ) in let eref = Eliom_reference.Volatile.eref ~scope:Eliom_common.default_session_scope 10 in let service = Eliom_service.service ~path:["volatile_reference"] ~get_params:Eliom_parameter.unit () in let set_service = Eliom_registration.Html5.register_post_coservice ~fallback:service ~post_params:(Eliom_parameter.int "n") (fun () n -> lwt () = Eliom_reference.set (eref :> _ Eliom_reference.eref) n in Lwt.return (page Html5.D.([ pcdata "Reference was set."; Html5.D.a ~service [pcdata "back"] (); ]))) in let unset_service = Eliom_registration.Html5.register_post_coservice ~fallback:service ~post_params:Eliom_parameter.unit (fun () () -> let () = Eliom_reference.Volatile.unset eref in Lwt.return (page Html5.F.([ pcdata "Reference was unset."; Html5.D.a ~service [pcdata "back"] (); ]))) in Eliom_registration.Html5.register ~service (fun () () -> let v = Eliom_reference.Volatile.get eref in Lwt.return (page Html5.D.([ h2 [pcdata "Volatile reference"]; p [pcdata "Value is "; pcdata (string_of_int v)]; Html5.D.( post_form ~service:set_service (fun name -> [ int_input ~input_type:`Text ~name (); string_input ~input_type:`Submit ~value:"Set" (); ]) () ); Html5.D.( post_form ~service:unset_service (fun () -> [ string_input ~input_type:`Submit ~value:"Unset" (); ]) () ); ]))); service (*****************************) (* Eliom references from fun *) let reference_from_fun = let page elts = Html5.D.( html (head (title (pcdata "Reference from fun")) []) (body elts) ) in let eref = Eliom_reference.eref_from_fun ~scope:Eliom_common.default_session_scope (fun () -> print_endline "Eliom references from fun: init value"; Random.int 100) in let service = Eliom_service.service ~path:["reference_from_fun"] ~get_params:Eliom_parameter.unit () in let set_service = Eliom_registration.Html5.register_post_coservice ~fallback:service ~post_params:(Eliom_parameter.int "n") (fun () n -> lwt () = Eliom_reference.set eref n in Lwt.return (page Html5.D.([ pcdata "Reference was set."; Html5.D.a ~service [pcdata "back"] (); ]))) in let unset_service = Eliom_registration.Html5.register_post_coservice ~fallback:service ~post_params:Eliom_parameter.unit (fun () () -> lwt () = Eliom_reference.unset eref in Lwt.return (page Html5.D.([ pcdata "Reference was unset."; Html5.D.a ~service [pcdata "back"] (); ]))) in Eliom_registration.Html5.register ~service (fun () () -> lwt v = Eliom_reference.get eref in Lwt.return (page Html5.D.([ h2 [pcdata "Reference from fun"]; p [pcdata "Value is "; pcdata (string_of_int v)]; Html5.D.( post_form ~service:set_service (fun name -> [ int_input ~input_type:`Text ~name (); string_input ~input_type:`Submit ~value:"Set" (); ]) () ); Html5.D.( post_form ~service:unset_service (fun () -> [ string_input ~input_type:`Submit ~value:"Unset" (); ]) () ); ]))); service (*****************************************************************************) open Eliom_testsuite1 open Eliom_registration.Html5 open Eliom_service open Eliom_state (* Lists of lists *) let lilists = service [ "lilists" ] unit () let lilists2 = service ["lilists2"] (list "l" (string "title" ** (list "il" (int "i")))) () let create_form f = let l = f.it (fun (sn, l2) v init -> (tr ((td [pcdata ("Write a string: ")]) ::(td [string_input ~input_type:`Text ~name:sn ()]) ::(td [pcdata ("Write integers: ")]) ::(l2.it (fun iname v init -> (td [int_input ~input_type:`Text ~name:iname ()])::init) ["A"; "B"] [])) )::init) ["one";"two";"three"] [] in [table (List.hd l) (List.tl l); p [string_input ~input_type:`Submit ~value:"Click" ()]] let () = register lilists (fun () () -> let f = Html5.D.get_form lilists2 create_form in return (html (head (title (pcdata "")) []) (body [f]))) let () = register lilists2 (fun ll () -> return (html (head (title (pcdata "")) []) (body (List.map (fun (s, il) -> p (pcdata s:: List.map (fun i -> pcdata (string_of_int i)) il)) ll)))) (* other example of list of list (mail W. Le Ferrant 2011/11/24) *) let wlf_lists = service [ "wlflists" ] (list "items" (list "followers" (int "follower"))) () let handler elements _ = Ocsigen_messages.debug (fun () -> "> nb of elements: "^ string_of_int (List.length elements)) ; Lwt.return "ok" let _ = Eliom_registration.Html_text.register wlf_lists handler (* sums in parameters types *) let sumserv = register_service ~path:["sum"] ~get_params:(sum (int "i") (sum (int "ii") (string "s"))) (fun g () -> return (html (head (title (pcdata "")) []) (body [p [pcdata "You sent: "; strong [pcdata (match g with | Inj1 i | Inj2 (Inj1 i) -> string_of_int i | Inj2 (Inj2 s) -> s) ]]]))) let create_form = (fun (name1, (name2, name3)) -> [p [ Html5.D.int_input ~name:name1 ~input_type:`Submit ~value:48 (); Html5.D.int_input ~name:name2 ~input_type:`Submit ~value:55 (); Html5.D.string_input ~name:name3 ~input_type:`Submit ~value:"plop" (); ]]) let sumform = register_service ["sumform"] unit (fun () () -> let f = Html5.D.get_form sumserv create_form in return (html (head (title (pcdata "")) []) (body [f]))) let sumform2 = service ~path:["sumform2"] ~get_params:unit () let sumserv = register_post_service ~fallback:sumform2 ~post_params:(sum (int "i") (sum (int "ii") (string "s"))) (fun () post -> return (html (head (title (pcdata "")) []) (body [p [pcdata "You sent: "; strong [pcdata (match post with | Inj1 i | Inj2 (Inj1 i) -> string_of_int i | Inj2 (Inj2 s) -> s) ]]]))) let () = register sumform2 (fun () () -> let f = Html5.D.post_form sumserv create_form () in return (html (head (title (pcdata "")) []) (body [f]))) (******) (* unregistering services *) let unregister_example = Eliom_registration.Html5.register_service ~path:["unregister"] ~get_params:Eliom_parameter.unit (fun () () -> let s1 = Eliom_registration.Html5.register_service ~path:["unregister1"] ~get_params:Eliom_parameter.unit (fun () () -> failwith "s1") in let s2 = Eliom_registration.Html5.register_coservice ~fallback:s1 ~get_params:Eliom_parameter.unit (fun () () -> failwith "s2") in let s3 = Eliom_registration.Html5.register_coservice' ~get_params:Eliom_parameter.unit (fun () () -> failwith "s3") in Eliom_registration.Html5.register ~scope:Eliom_common.default_session_scope ~service:s1 (fun () () -> failwith "s4"); Eliom_service.unregister s1; Eliom_service.unregister s2; Eliom_service.unregister s3; Eliom_service.unregister ~scope:Eliom_common.default_session_scope s1; Lwt.return (html (head (title (pcdata "Unregistering services")) []) (body [p [pcdata "These services have been registered and unregistered"]; p [a s1 [pcdata "regular service"] (); pcdata ", "; a s2 [pcdata "coservice"] (); pcdata ", "; a s3 [pcdata "non attached coservice"] (); pcdata ", "; a s1 [pcdata "session service"] (); ]])) ) (******) (* CSRF GET *) let csrfsafe_get_example = Eliom_service.service ~path:["csrfget"] ~get_params:Eliom_parameter.unit () let csrfsafe_example_get = Eliom_service.coservice ~csrf_safe:true ~timeout:10. ~fallback:csrfsafe_get_example ~get_params:Eliom_parameter.unit () let _ = let page () () = let l3 = Html5.D.get_form csrfsafe_example_get (fun _ -> [p [Html5.D.string_input ~input_type:`Submit ~value:"Click" ()]]) in return (html (head (title (pcdata "CSRF safe service example")) []) (body [p [pcdata "A new coservice will be created each time this form is displayed. Your server must be running with HTTPS enabled."]; l3])) in Eliom_registration.Html5.register csrfsafe_get_example page; Eliom_registration.Html5.register csrfsafe_example_get (fun () () -> Lwt.return (html (head (title (pcdata "CSRF safe service")) []) (body [p [pcdata "This is a GET CSRF safe service"]]))) (******) (* CSRF POST on CSRF GET coservice *) let csrfsafe_postget_example = Eliom_service.service ~path:["csrfpostget"] ~get_params:Eliom_parameter.unit () let csrfsafe_example_post = Eliom_service.post_coservice ~csrf_safe:true ~timeout:10. ~fallback:csrfsafe_example_get (* !!! *) ~post_params:Eliom_parameter.unit () let _ = let page () () = let l3 = Html5.D.post_form csrfsafe_example_post (fun _ -> [p [Html5.D.string_input ~input_type:`Submit ~value:"Click" ()]]) () in return (html (head (title (pcdata "CSRF safe service example")) []) (body [p [pcdata "A new coservice will be created each time this form is displayed"]; l3])) in Eliom_registration.Html5.register csrfsafe_postget_example page; Eliom_registration.Html5.register csrfsafe_example_post (fun () () -> Lwt.return (html (head (title (pcdata "CSRF safe service")) []) (body [p [pcdata "This is a POST CSRF safe service, combined with a GET CSRF safe service"]]))) (******) (* CSRF for_session *) let csrfsafe_session_example = Eliom_service.service ~path:["csrfsession"] ~get_params:Eliom_parameter.unit () let myscope = (`Session (Eliom_common.create_scope_hierarchy "plop")) let csrfsafe_example_session = Eliom_service.post_coservice' ~csrf_safe:true ~csrf_scope:myscope ~csrf_secure:true ~timeout:10. ~post_params:Eliom_parameter.unit () let _ = let page () () = Eliom_registration.Html5.register ~scope:myscope ~secure_session:true ~service:csrfsafe_example_session (fun () () -> Lwt.return (html (head (title (pcdata "CSRF safe service")) []) (body [p [pcdata "This is a POST CSRF safe service"]]))); let l3 = Html5.D.post_form csrfsafe_example_session (fun _ -> [p [Html5.D.string_input ~input_type:`Submit ~value:"Click" ()]]) () in return (html (head (title (pcdata "CSRF safe service example")) []) (body [p [pcdata "A new coservice will be created each time this form is displayed"]; l3])) in Eliom_registration.Html5.register csrfsafe_session_example page (******) (* optional suffix parameters *) let optsuf = register_service ~path:["optsuf"] ~get_params:(suffix(opt(string "q" ** (opt (int "i"))))) (fun o () -> Lwt.return (html (head (title (pcdata "")) []) (body [p [pcdata (match o with | None -> "" | Some (s, o) -> s^(match o with | None -> "" | Some i -> string_of_int i)); ]]))) let optsuf2 = register_service ~path:["optsuf2"] ~get_params:(suffix(opt(string "q") ** (opt (int "i")))) (fun (s, i) () -> Lwt.return (html (head (title (pcdata "")) []) (body [p [pcdata (match s with | None -> "" | Some s -> s); pcdata (match i with | None -> "" | Some i -> string_of_int i)]; ]))) (*******) let my_nl_params = Eliom_parameter.make_non_localized_parameters ~prefix:"tutoeliom" ~name:"mynlp" (Eliom_parameter.int "a" ** Eliom_parameter.string "s") let void_with_nlp = Eliom_service.add_non_localized_get_parameters my_nl_params Eliom_service.void_coservice' let hidden_void_with_nlp = Eliom_service.add_non_localized_get_parameters my_nl_params Eliom_service.void_hidden_coservice' let nlparams2 = service ~path:["voidnl"] ~get_params:(suffix_prod (int "year" ** int "month") (int "w" )) () let nlparams2_with_nlp = Eliom_service.add_non_localized_get_parameters my_nl_params nlparams2 let () = register nlparams2 (fun ((aa, bb), w) () -> Lwt.return (html (head (title (pcdata "")) []) (body [p [ a void_with_nlp [pcdata "void coservice with non loc param"] ((), (11, "aa")); br (); a hidden_void_with_nlp [pcdata "void hidden coservice with non loc param"] ((), (22, "bb")); br (); a nlparams2_with_nlp [pcdata "myself with non loc param"] (((4, 5), 777), (12, "ab"))]; p [pcdata "I have my suffix, "; pcdata ("with values year = "^string_of_int aa^ " and month = "^string_of_int bb^ ". w = "^string_of_int w^".")]; (match Eliom_parameter.get_non_localized_get_parameters my_nl_params with | None -> p [pcdata "I do not have my non localized parameters"] | Some (a, s) -> p [pcdata "I have my non localized parameters, "; pcdata ("with values a = "^string_of_int a^ " and s = "^s^".")] )])) ) (***********) let nlpost_entry = Eliom_service.service ~path:["nlpost"] ~get_params:(Eliom_parameter.unit) () let nlpost = Eliom_service.post_coservice ~fallback:nlpost_entry ~name:"nlpost" ~post_params:(Eliom_parameter.unit) () let nlpost_with_nlp = Eliom_service.add_non_localized_get_parameters my_nl_params nlpost let create_form_nl s = (fun () -> [Html5.F.p [Html5.D.string_input ~input_type:`Submit ~value:s ()]]) let () = Eliom_registration.Html5.register nlpost (fun () () -> let nlp = match Eliom_parameter.get_non_localized_get_parameters my_nl_params with | None -> "no non localised parameter" | Some _ -> "some non localised parameter" in Lwt.return Html5.F.(html (head (title (pcdata "")) []) (body [div [ pcdata nlp; br(); Html5.D.post_form nlpost_with_nlp (create_form_nl "with nl param") ((),(12, "ab")); Html5.D.post_form nlpost (create_form_nl "without nl param") (); ]]))) let () = Eliom_registration.Html5.register nlpost_entry (fun () () -> Lwt.return Html5.F.(html (head (title (pcdata "")) []) (body [div [ Html5.D.post_form nlpost_with_nlp (create_form_nl "with nl param") ((),(12, "ab")); Html5.D.post_form nlpost (create_form_nl "without nl param") (); ]]))) (*******) (* doing requests *) (* Warning: compute_result may return an deflated result! *) (* Check! (see for example Eliom_registration.Action) *) let extreq = register_service ~path:["extreq"] ~get_params:unit (fun () () -> Ocsigen_http_client.get "ocsigen.org" "/ocsimoreadmin/static/ocsiwikistyle.css" () >>= fun frame -> (match frame.Ocsigen_http_frame.frame_content with | None -> Lwt.return "" | Some stream -> Ocsigen_stream.string_of_stream (Ocsigen_config.get_maxrequestbodysizeinmemory ()) (Ocsigen_stream.get stream)) >>= fun s -> (* Here use an XML parser, or send the stream directly using an appropriate Eliom_mkreg module *) return (html (head (title (pcdata "")) []) (body [p [pcdata s]]))) let servreq = register_service ~path:["servreq"] ~get_params:unit (fun () () -> let ri = Eliom_request_info.get_ri () in let ri = Ocsigen_extensions.ri_of_url "tuto/" ri in Ocsigen_extensions.compute_result ri >>= fun result -> let stream = fst result.Ocsigen_http_frame.res_stream in Ocsigen_stream.string_of_stream (Ocsigen_config.get_maxrequestbodysizeinmemory ()) (Ocsigen_stream.get stream) >>= fun s -> (* Here use an XML parser, or send the stream directly using an appropriate Eliom_mkreg module *) return (html (head (title (pcdata "")) []) (body [p [pcdata s]]))) let servreqloop = register_service ~path:["servreqloop"] ~get_params:unit (fun () () -> let ri = Eliom_request_info.get_ri () in Ocsigen_extensions.compute_result ri >>= fun result -> let stream = fst result.Ocsigen_http_frame.res_stream in Ocsigen_stream.string_of_stream (Ocsigen_config.get_maxrequestbodysizeinmemory ()) (Ocsigen_stream.get stream) >>= fun s -> (* Here use an XML parser, or send the stream directly using an appropriate Eliom_mkreg module *) return (html (head (title (pcdata "")) []) (body [p [pcdata s]]))) (* Customizing HTTP headers *) let headers = register_service ~code:666 ~charset:"plopcharset" (* ~content_type:"custom/contenttype" *) ~headers:(Http_headers.add (Http_headers.name "XCustom-header") "This is an example" Http_headers.empty) ~path:["httpheaders"] ~get_params:unit (fun () () -> Eliom_state.set_cookie ~path:[] ~name:"Customcookie" ~value:"Value" ~secure:true (); Eliom_state.set_cookie ~path:[] ~name:"Customcookie2" ~value:"Value2" (); Lwt.return (html (head (title (pcdata "")) []) (body [h1 [pcdata "Look at my HTTP headers"]]))) (* form towards a suffix service with constants *) let create_form (n1, (_, n2)) = let module Html5 = Eliom_content.Html5.F in <:html5list<

$string_input ~input_type:`Text ~name:n1 ()$ $string_input ~input_type:`Text ~name:n2 ()$ $string_input ~input_type:`Submit ~value:"Click" ()$

>> let constform = register_service ["constform"] unit (fun () () -> let f = get_form Eliom_testsuite1.constfix create_form in return (html (head (title (pcdata "")) []) (body [h1 [pcdata "Hallo"]; f ]))) (* Suffix and other service at same URL *) let su2 = register_service ~path:["fuffix";""] ~get_params:(suffix (all_suffix_string "s")) (fun s () -> return (html (head (title (pcdata "")) []) (body [h1 [pcdata s]; p [pcdata "Try page fuffix/a/b"]]))) let su = register_service ~path:["fuffix";"a";"b"] ~get_params:unit (fun () () -> return (html (head (title (pcdata "")) []) (body [h1 [pcdata "Try another suffix"]]))) let su3 = register_service ~path:["fuffix";""] ~get_params:unit (fun () () -> return (html (head (title (pcdata "")) []) (body [h1 [pcdata "Try another suffix"]]))) let su4 = register_service ~path:["fuffix";""] ~get_params:(suffix (string "s" ** suffix_const "CONST" ** string "ss")) ~priority:1 (fun (s, ((), ss)) () -> return (html (head (title (pcdata "")) []) (body [h1 [pcdata s]; p [pcdata "I am a suffix service with a constant part, registered after the generic suffix service, but I have a priority, so that you can see me!"]]))) let create_suffixform_su2 s = let module Html5 = Eliom_content.Html5.F in <:html5list<

Write a string: $string_input ~input_type:`Text ~name:s ()$
$string_input ~input_type:`Submit ~value:"Click" ()$

>> let suffixform_su2 = register_service ["suffixform_su2"] unit (fun () () -> let f = get_form su2 create_suffixform_su2 in return (html (head (title (pcdata "")) []) (body [h1 [pcdata "Hallo"]; f ]))) (* optional parameters *) let optparam = register_service ~path:["opt"] ~get_params:(Eliom_parameter.opt (Eliom_parameter.string "a" ** Eliom_parameter.string "b")) (fun o () -> Lwt.return (html (head (title (pcdata "")) []) (body [h1 [pcdata "Hallo!"]; match o with | None -> p [pcdata "no parameters"] | Some (a, b) -> p [pcdata a; pcdata ", "; pcdata b] ])) ) let optform = register_service ~path:["optform"] ~get_params:unit (fun () () -> (* testing lwt_get_form *) Html5.D.lwt_get_form ~service:optparam (fun (an, bn) -> Lwt.return [p [ string_input ~input_type:`Text ~name:an (); string_input ~input_type:`Text ~name:bn (); Html5.D.string_input ~input_type:`Submit ~value:"Click" ()]]) >>= fun form -> return (html (head (title (pcdata "")) []) (body [h1 [pcdata "Hallo!"]; form ])) ) (* Preapplied service with suffix parameters *) let presu_service = register_service ~path: ["preappliedsuffix2"] ~get_params: (suffix (int "i")) (fun i () -> Lwt.return (html (head (title (pcdata "")) []) (body [p [ pcdata ("You sent: " ^ (string_of_int i))]]))) let creator_handler () () = let create_form () = [fieldset [string_input ~input_type:`Submit ~value:"Click" ()]] in let myservice = preapply presu_service 10 in let myform = get_form myservice create_form in Lwt.return (html (head (title (pcdata "")) []) (body [ p [pcdata "Form with preapplied parameter:"]; myform; p [a myservice [pcdata "Link with preapplied parameter"] ()] ])) let preappliedsuffix = register_service ~path: ["preappliedsuffix"] ~get_params: unit creator_handler (* URL with ? or / in data or paths *) let url_encoding = let module Html5 = Eliom_content.Html5.F in register_service ~path:["urlencoding&à/=é?abl let ll = List.map (fun (a,s) -> <:html5< ($str:a$, $str:s$) >>) l in let sl = List.map (fun s -> <:html5< $str:s$ >>) suf in return (html (head (title (pcdata "")) []) (body [p [pcdata "All characters must be displayed correctly, including ampersand or unicode"]; p sl; p ll ]))) (* menu with preapplied services *) let preappl = preapply coucou_params (3,(4,"cinq")) let preappl2 = preapply uasuffix (1999,01) let mymenu current = let module Html5 = Eliom_content.Html5.F in Eliom_tools.F.menu ~classe:["menuprincipal"] [(coucou, <:html5list< coucou >>); (preappl, <:html5list< params >>); (preappl2, <:html5list< params and suffix >>); ] ~service:current () let preappmenu = register_service ~path:["menu"] ~get_params:unit (fun () () -> return (html (head (title (pcdata "")) []) (body [h1 [pcdata "Hallo"]; mymenu coucou ]))) (* GET Non-attached coservice *) let nonatt = coservice' ~get_params:(string "e") () (* GET coservice with preapplied fallback *) (* + Non-attached coservice on a pre-applied coservice *) (* + Non-attached coservice on a non-attached coservice *) let f s = (html (head (title (pcdata "")) []) (body [h1 [pcdata s]; p [a nonatt [pcdata "clic"] "nonon"]; get_form nonatt (fun string_name -> [p [pcdata "Non attached coservice: "; string_input ~input_type:`Text ~name:string_name (); string_input ~input_type:`Submit ~value:"Click" ()]]) ])) let getco = register_coservice ~fallback:preappl ~get_params:(int "i" ** string "s") (fun (i,s) () -> return (f s)) let _ = register nonatt (fun s () -> return (f s)) let getcoex = register_service ~path:["getco"] ~get_params:unit (fun () () -> return (html (head (title (pcdata "")) []) (body [p [a getco [pcdata "clic"] (22,"eee") ]; get_form getco (fun (number_name,string_name) -> [p [pcdata "Write an int: "; int_input ~input_type:`Text ~name:number_name (); pcdata "Write a string: "; string_input ~input_type:`Text ~name:string_name (); string_input ~input_type:`Submit ~value:"Click" ()]]) ]))) (* POST service with preapplied fallback are not possible: *) (* let my_service_with_post_params = register_post_service ~fallback:preappl ~post_params:(string "value") (fun () value -> return (html (head (title (pcdata "")) []) (body [h1 [pcdata value]]))) *) (* GET coservice with coservice fallback: not possible *) (* let preappl3 = preapply getco (777,"ooo") let getco2 = register_coservice ~fallback:preappl3 ~get_params:(int "i2" ** string "s2") (fun (i,s) () -> return (html (head (title (pcdata "")) []) (body [h1 [pcdata s]]))) *) (* POST service with coservice fallback *) let my_service_with_post_params = register_post_service ~fallback:getco ~post_params:(string "value") (fun (i,s) value -> return (html (head (title (pcdata "")) []) (body [h1 [pcdata (s^" "^value)]]))) let postcoex = register_service ["postco"] unit (fun () () -> let f = (post_form my_service_with_post_params (fun chaine -> [p [pcdata "Write a string: "; string_input ~input_type:`Text ~name:chaine ()]]) (222,"ooo")) in return (html (head (title (pcdata "form")) []) (body [f]))) (* action on GET attached coservice *) let v = ref 0 let getact = service ~path:["getact"] ~get_params:(int "p") () let act = Eliom_registration.Action.register_coservice ~fallback:(preapply getact 22) ~get_params:(int "bip") (fun g p -> v := g; return ()) (* action on GET non-attached coservice on GET coservice page *) let naact = Eliom_registration.Action.register_coservice' ~get_params:(int "bop") (fun g p -> v := g; return ()) let naunit = Eliom_registration.Unit.register_coservice' ~get_params:(int "bap") (fun g p -> v := g; return ()) let _ = register getact (fun aa () -> return (html (head (title (pcdata "getact")) []) (body [h1 [pcdata ("v = "^(string_of_int !v))]; p [pcdata ("p = "^(string_of_int aa))]; p [a getact [pcdata "link to myself"] 0; br (); a act [pcdata "an attached action to change v"] (Random.int 100); br (); a naact [pcdata "a non attached action to change v"] (100 + Random.int 100); pcdata " (Actually if called after the previous one, v won't change. More precisely, it will change and turn back to the former value because the attached coservice is reloaded after action)"; br (); a naunit [pcdata "a non attached \"Unit\" page to change v"] (200 + Random.int 100); pcdata " (Reload after clicking here)" ]]))) (* Many cookies *) let cookiename = "c" let cookies2 = service ["c";""] (suffix (all_suffix_string "s")) () let _ = Eliom_registration.Html5.register cookies2 (fun s () -> let now = Unix.time () in Eliom_state.set_cookie ~path:[] ~exp:(now +. 10.) ~name:(cookiename^"6") ~value:(string_of_int (Random.int 100)) ~secure:true (); Eliom_state.set_cookie ~path:[] ~exp:(now +. 10.) ~name:(cookiename^"7") ~value:(string_of_int (Random.int 100)) ~secure:true (); Eliom_state.set_cookie ~path:["c";"plop"] ~name:(cookiename^"8") ~value:(string_of_int (Random.int 100)) (); Eliom_state.set_cookie ~path:["c";"plop"] ~name:(cookiename^"9") ~value:(string_of_int (Random.int 100)) (); Eliom_state.set_cookie ~path:["c";"plop"] ~name:(cookiename^"10") ~value:(string_of_int (Random.int 100)) ~secure:true (); Eliom_state.set_cookie ~path:["c";"plop"] ~name:(cookiename^"11") ~value:(string_of_int (Random.int 100)) ~secure:true (); Eliom_state.set_cookie ~path:["c";"plop"] ~name:(cookiename^"12") ~value:(string_of_int (Random.int 100)) ~secure:true (); if CookiesTable.mem (cookiename^"1") (Eliom_request_info.get_cookies ()) then (Eliom_state.unset_cookie ~name:(cookiename^"1") (); Eliom_state.unset_cookie ~name:(cookiename^"2") ()) else begin Eliom_state.set_cookie ~name:(cookiename^"1") ~value:(string_of_int (Random.int 100)) ~secure:true (); Eliom_state.set_cookie ~name:(cookiename^"2") ~value:(string_of_int (Random.int 100)) (); Eliom_state.set_cookie ~name:(cookiename^"3") ~value:(string_of_int (Random.int 100)) () end; Lwt.return (html (head (title (pcdata "")) []) (body [p (CookiesTable.fold (fun n v l -> (pcdata (n^"="^v)):: (br ())::l ) (Eliom_request_info.get_cookies ()) [a cookies2 [pcdata "send other cookies"] ""; br (); a cookies2 [pcdata "send other cookies and see the url /c/plop"] "plop"] )])) ) (* Send file *) let sendfileex = register_service ~path:["files";""] ~get_params:unit (fun () () -> return (html (head (title (pcdata "")) []) (body [h1 [pcdata "With a suffix, that page will send a file"]]))) let sendfile2 = Eliom_registration.File.register_service ~path:["files";""] ~get_params:(suffix (all_suffix "filename")) (fun s () -> return ("/var/www/ocsigen/"^(Url.string_of_url_path ~encode:false s))) let sendfileexception = register_service ~path:["files";"exception"] ~get_params:unit (fun () () -> return (html (head (title (pcdata "")) []) (body [h1 [pcdata "With another suffix, that page will send a file"]]))) (* Complex suffixes *) let suffix2 = service ~path:["suffix2";""] ~get_params:(suffix (string "suff1" ** int "ii" ** all_suffix "ee")) () let _ = register suffix2 (fun (suf1, (ii, ee)) () -> return (html (head (title (pcdata "")) []) (body [p [pcdata "The suffix of the url is "; strong [pcdata (suf1^", "^(string_of_int ii)^", "^ (Url.string_of_url_path ~encode:false ee))]]; p [a suffix2 [pcdata "link to myself"] ("a", (2, []))]]))) let suffix3 = register_service ~path:["suffix3";""] ~get_params:(suffix_prod (string "suff1" ** int "ii" ** all_suffix_user int_of_string string_of_int "ee") (string "a" ** int "b")) (fun ((suf1, (ii, ee)), (a, b)) () -> return (html (head (title (pcdata "")) []) (body [p [pcdata "The parameters in the url are "; strong [pcdata (suf1^", "^(string_of_int ii)^", "^ (string_of_int ee)^", "^ a^", "^(string_of_int b))]]]))) let create_suffixform2 (suf1, (ii, ee)) = let module Html5 = Eliom_content.Html5.F in <:html5list<

Write a string: $string_input ~input_type:`Text ~name:suf1 ()$
Write an int: $int_input ~input_type:`Text ~name:ii ()$
Write a string: $user_type_input (Url.string_of_url_path ~encode:false) ~input_type:`Text ~name:ee ()$
$string_input ~input_type:`Submit ~value:"Click" ()$

>> let suffixform2 = register_service ["suffixform2"] unit (fun () () -> let f = get_form suffix2 create_suffixform2 in return (html (head (title (pcdata "")) []) (body [h1 [pcdata "Hallo"]; f ]))) let create_suffixform3 ((suf1, (ii, ee)), (a, b)) = let module Html5 = Eliom_content.Html5.F in <:html5list<

Write a string: $string_input ~input_type:`Text ~name:suf1 ()$
Write an int: $int_input ~input_type:`Text ~name:ii ()$
Write an int: $int_input ~input_type:`Text ~name:ee ()$
Write a string: $string_input ~input_type:`Text ~name:a ()$
Write an int: $int_input ~input_type:`Text ~name:b ()$
$string_input ~input_type:`Submit ~value:"Click" ()$

>> let suffixform3 = register_service ["suffixform3"] unit (fun () () -> let f = get_form suffix3 create_suffixform3 in return (html (head (title (pcdata "")) []) (body [h1 [pcdata "Hallo"]; f ]))) let suffix5 = register_service ~path:["suffix5"] ~get_params:(suffix (all_suffix "s")) (fun s () -> return (html (head (title (pcdata "")) []) (body [p [pcdata "This is a page with suffix "; strong [pcdata (Url.string_of_url_path ~encode:false s)]]]))) let nosuffix = register_service ~path:["suffix5";"notasuffix"] ~get_params:unit (fun () () -> return (html (head (title (pcdata "")) []) (body [p [pcdata "This is a page without suffix. Replace "; code [pcdata "notasuffix"]; pcdata " in the URL by something else." ]]))) (* Send file with regexp *) let sendfileregexp = register_service ~path:["files2";""] ~get_params:unit (fun () () -> return (html (head (title (pcdata "")) []) (body [h1 [pcdata "With a suffix, that page will send a file"]]))) let r = Netstring_pcre.regexp "~([^/]*)(.*)" let sendfile2 = Eliom_registration.File.register_service ~path:["files2";""] (* ~get_params:(regexp r "/home/$1/public_html$2" "filename") *) ~get_params:(suffix ~redirect_if_not_suffix:false (all_suffix_regexp r "$u($1)/public_html$2" ~to_string:(fun s -> s) "filename")) (fun s () -> return s) (* Here I am using redirect_if_not_suffix:false because otherwise I would need to write a more sophisticated to_string function *) (* let sendfile2 = Files.register_service ~path:["files2";""] ~get_params:(suffix (all_suffix_regexp r "/home/$1/public_html$2" "filename")) (* ~get_params:(suffix (all_suffix_regexp r "$$u($1)$2" "filename")) *) (fun s () -> return s) *) let create_suffixform4 n = let module Html5 = Eliom_content.Html5.F in <:html5list<

Write the name of the file: $string_input ~input_type:`Text ~name:n ()$ $string_input ~input_type:`Submit ~value:"Click" ()$

>> let suffixform4 = register_service ["suffixform4"] unit (fun () () -> let f = get_form sendfile2 create_suffixform4 in return (html (head (title (pcdata "")) []) (body [h1 [pcdata "Hallo"]; f ]))) (* Advanced use of any *) let any2 = register_service ~path:["any2"] ~get_params:(int "i" ** any) (fun (i,l) () -> let module Html5 = Eliom_content.Html5.F in let ll = List.map (fun (a,s) -> <:html5< ($str:a$, $str:s$) >>) l in return <:html5<

You sent: $list:ll$
i = $str:(string_of_int i)$

>>) (* the following will not work because s is taken in any. (not checked) *) let any3 = register_service ~path:["any3"] ~get_params:(int "i" ** any ** string "s") (fun (i,(l,s)) () -> let module Html5 = Eliom_content.Html5.F in let ll = List.map (fun (a,s) -> <:html5< ($str:a$, $str:s$) >>) l in return <:html5<

You sent: $list:ll$
i = $str:(string_of_int i)$
s = $str:s$

>>) (* any cannot be in suffix: (not checked) *) let any4 = register_service ~path:["any4"] ~get_params:(suffix any) (fun l () -> let module Html5 = Eliom_content.Html5.F in let ll = List.map (fun (a,s) -> <:html5< ($str:a$, $str:s$) >>) l in return <:html5<

You sent: $list:ll$

>>) let any5 = register_service ~path:["any5"] ~get_params:(suffix_prod (string "s") any) (fun (s, l) () -> let module Html5 = Eliom_content.Html5.F in let ll = List.map (fun (a,s) -> <:html5< ($str:a$, $str:s$) >>) l in return <:html5<

You sent $str:s$ and : $list:ll$

>>) (* list in suffix *) let sufli = service ~path:["sufli"] ~get_params:(suffix (list "l" (string "s" ** int "i"))) () let _ = register sufli (fun l () -> let module Html5 = Eliom_content.Html5.F in let ll = List.map (fun (s, i) -> <:html5< $str:(s^string_of_int i)$ >>) l in return <:html5<

You sent: $list:ll$

$a sufli [pcdata "myself"] [("a", 2)]$, $a sufli [pcdata "myself (empty list)"] []$

>>) let create_sufliform f = let l = f.it (fun (sn, iname) v init -> (tr [td [pcdata ("Write a string: ")]; td [string_input ~input_type:`Text ~name:sn ()]; td [pcdata ("Write an integer: ")]; td [int_input ~input_type:`Text ~name:iname ()]; ])::init) ["one";"two";"three"] [] in [table (List.hd l) (List.tl l); p [string_input ~input_type:`Submit ~value:"Click" ()]] let sufliform = register_service ["sufliform"] unit (fun () () -> let f = get_form sufli create_sufliform in return (html (head (title (pcdata "")) []) (body [h1 [pcdata "Hallo"]; f ]))) (* (* mmmh ... disabled dynamically for now *) let sufli2 = service ~path:["sufli2"] ~get_params:(suffix ((list "l" (int "i")) ** int "j")) () let _ = register sufli2 (fun (l, j) () -> let ll = List.map (fun i -> <:html5< $str:(string_of_int i)$ >>) l in return <:html5<

You sent: $list:ll$, and j=$str:string_of_int j$.

$a sufli2 [pcdata "myself"] ([1; 2], 3)$, $a sufli2 [pcdata "myself (empty list)"] ([], 1)$

>>) *) let sufliopt = service ~path:["sufliopt"] ~get_params:(suffix (list "l" (opt (string "s")))) () let _ = register sufliopt (fun l () -> let module Html5 = Eliom_content.Html5.F in let ll = List.map (function None -> pcdata "" | Some s -> <:html5< $str:s$ >>) l in return <:html5<

You sent: $list:ll$

$a sufliopt [pcdata "myself"] [Some "a"; None; Some "po"; None; None; Some "k"; None]$, $a sufliopt [pcdata "myself (empty list)"] []$ $a sufliopt [pcdata "myself (list [None; None])"] [None; None]$ $a sufliopt [pcdata "myself (list [None])"] [None]$

>>) let sufliopt2 = service ~path:["sufliopt2"] ~get_params:(suffix (list "l" (opt (string "s" ** string "ss")))) () let _ = register sufliopt2 (fun l () -> let module Html5 = Eliom_content.Html5.F in let ll = List.map (function None -> pcdata "" | Some (s, ss) -> <:html5< ($str:s$, $str:ss$) >>) l in return <:html5<

You sent: $list:ll$

$a sufliopt2 [pcdata "myself"] [Some ("a", "jj"); None; Some ("po", "jjj"); None; None; Some ("k", "pp"); None]$, $a sufliopt2 [pcdata "myself (empty list)"] []$ $a sufliopt2 [pcdata "myself (list [None; None])"] [None; None]$ $a sufliopt2 [pcdata "myself (list [None])"] [None]$

>>) (* set in suffix *) let sufset = register_service ~path:["sufset"] ~get_params:(suffix (Eliom_parameter.set string "s")) (fun l () -> let module Html5 = Eliom_content.Html5.F in let ll = List.map (fun s -> <:html5< $str:s$ >>) l in return <:html5<

You sent: $list:ll$

>>) (* form to any2 *) let any2form = register_service ~path:["any2form"] ~get_params:unit (fun () () -> return (html (head (title (pcdata "")) []) (body [h1 [pcdata "Any Form"]; get_form any2 (fun (iname,grr) -> [p [pcdata "Form to any2: "; int_input ~input_type:`Text ~name:iname (); raw_input ~input_type:`Text ~name:"plop" (); raw_input ~input_type:`Text ~name:"plip" (); raw_input ~input_type:`Text ~name:"plap" (); string_input ~input_type:`Submit ~value:"Click" ()]]) ]))) (* bool list *) let boollist = register_service ~path:["boollist"] ~get_params:(list "a" (bool "b")) (fun l () -> let ll = List.map (fun b -> (strong [pcdata (if b then "true" else "false")])) l in return (html (head (title (pcdata "")) []) (body [p ((pcdata "You sent: ")::ll)] ))) let create_listform f = (* Here, f.it is an iterator like List.map, but it must be applied to a function taking 2 arguments (and not 1 as in map), the first one being the name of the parameter. The last parameter of f.it is the code that must be appended at the end of the list created *) let l = f.it (fun boolname v init -> (tr[td [pcdata ("Write the value for "^v^": ")]; td [bool_checkbox ~name:boolname ()]])::init) ["one";"two";"three"] [] in [table (List.hd l) (List.tl l); p [raw_input ~input_type:`Submit ~value:"Click" ()]] let boollistform = register_service ["boolform"] unit (fun () () -> let f = get_form boollist create_listform in return (html (head (title (pcdata "")) []) (body [f]))) (********) let coucoucou = register_service ~path:["coucoucou"] ~get_params:unit (fun () () -> return (html (head (title (pcdata "")) []) (body [h1 [pcdata "Hallo!"]]))) (* any with POST *) let any = register_post_service ~fallback:coucoucou ~post_params:any (fun () l -> let module Html5 = Eliom_content.Html5.F in let ll = List.map (fun (a,s) -> <:html5< ($str:a$, $str:s$) >>) l in return <:html5<

You sent: $list:ll$

>>) (* form to any *) let anypostform = register_service ~path:["anypostform"] ~get_params:unit (fun () () -> return (html (head (title (pcdata "")) []) (body [h1 [pcdata "Any Form"]; post_form any (fun () -> [p [pcdata "Empty form to any: "; string_input ~input_type:`Submit ~value:"Click" ()]]) () ]))) (**********) (* upload *) (* ce qui suit ne doit pas fonctionner. Mais il faudrait l'interdire *) let get_param_service = register_service ~path:["uploadget"] ~get_params:(string "name" ** file "file") (fun (name,file) () -> let to_display = let newname = "/tmp/fichier" in (try Unix.unlink newname; with _ -> ()); Unix.link (Eliom_request_info.get_tmp_filename file) newname; let fd_in = open_in newname in try let line = input_line fd_in in close_in fd_in; line (*end*) with End_of_file -> close_in fd_in; "vide" in return (html (head (title (pcdata name)) []) (body [h1 [pcdata to_display]]))) let uploadgetform = register_service ["uploadget"] unit (fun () () -> let f = (* ARG (post_form ~a:[(Html5.F.a_enctype "multipart/form-data")] fichier2 *) (get_form ~a:[(Html5.F.a_enctype "multipart/form-data")] ~service:get_param_service (*post_form my_service_with_post_params *) (fun (str, file) -> [p [pcdata "Write a string: "; string_input ~input_type:`Text ~name:str (); br (); file_input ~name:file ()]])) in return (html (head (title (pcdata "form")) []) (body [f]))) (*******) (* Actions that raises an exception *) let exn_act = Eliom_registration.Action.register_coservice' ~get_params:unit (fun g p -> fail Not_found) let exn_act_main = register_service ~path:["exnact"] ~get_params:unit (fun () () -> return (html (head (title (pcdata "exnact")) []) (body [h1 [pcdata "Hello"]; p [a exn_act [pcdata "Do the action"] (); pcdata "It will raise an exception, and you will receive an error 500." ]]))) let action_example2_scope = `Session (Eliom_common.create_scope_hierarchy "action_example2") (* close sessions from outside *) let close_from_outside = register_service ~path:["close_from_outside"] ~get_params:unit (fun () () -> lwt () = discard_all ~scope:persistent_session_scope () in lwt () = discard_all ~scope:action_example2_scope () in return (html (head (title (pcdata "")) []) (body [h1 [pcdata "all sessions called \"persistent_sessions\" and \"action_example2\" closed"]; p [a persist_session_example [pcdata "try"] ()]]))) (* setting timeouts *) let set_timeout = register_service ~path:["set_timeout"] ~get_params:(int "t" ** (bool "recompute" ** bool "overrideconfig")) (fun (t, (recompute, override_configfile)) () -> set_global_persistent_data_state_timeout ~override_configfile ~cookie_scope:persistent_session_scope ~recompute_expdates:recompute (Some (float_of_int t)); set_global_volatile_state_timeout ~override_configfile ~cookie_scope:action_example2_scope ~recompute_expdates:recompute (Some (float_of_int t)); return (html (head (title (pcdata "")) []) (body [h1 [pcdata "Setting timeout"]; p [ if recompute then pcdata ("The timeout for sessions called \"persistent_sessions\" and \"action_example2\" has been set to "^(string_of_int t)^" seconds (all expiration dates updated).") else pcdata ("From now, the timeout for sessions called \"persistent_sessions\" and \"action_example2\" will be "^(string_of_int t)^" seconds (expiration dates not updated)."); br (); a persist_session_example [pcdata "Try"] ()]]))) let create_form = (fun (number_name, (bool1name, bool2name)) -> [p [pcdata "New timeout: "; Html5.D.int_input ~input_type:`Text ~name:number_name (); br (); pcdata "Check the box if you want to recompute all timeouts: "; Html5.D.bool_checkbox ~name:bool1name (); br (); pcdata "Check the box if you want to override configuration file: "; Html5.D.bool_checkbox ~name:bool2name (); Html5.D.string_input ~input_type:`Submit ~value:"Submit" ()]]) let set_timeout_form = register_service ["set_timeout"] unit (fun () () -> let f = Html5.D.get_form set_timeout create_form in return (html (head (title (pcdata "")) []) (body [f]))) (******************************************************************) let sraise = register_service ~path:["raise"] ~get_params:unit (fun () () -> failwith "Bad use of exceptions") let sfail = register_service ~path:["fail"] ~get_params:unit (fun () () -> Lwt.fail (Failure "Service raising an exception")) (*****************************************************************************) (* 2011/08/02 Vincent - Volatile group data removing group data or not when no session in the group?*) (*zap* *) open Html5.F (*zap* *) let scope_hierarchy = Eliom_common.create_scope_hierarchy "session_group_data_example_state" let session = `Session scope_hierarchy let group = `Session_group scope_hierarchy (* *zap*) (* -------------------------------------------------------- *) (* We create one main service and two (POST) actions *) (* (for connection and disconnection) *) let connect_example_gd = Eliom_service.service ~path:["sessgrpdata"] ~get_params:unit () let connect_action = Eliom_service.post_coservice' ~name:"connectiongd" ~post_params:(string "login") () (* disconnect action and box: *) let disconnect_action = Eliom_registration.Action.register_post_coservice' ~name:"disconnectiongd" ~post_params:Eliom_parameter.unit (fun () () -> Eliom_state.discard ~scope:session ()) let disconnect_box s = Html5.D.post_form disconnect_action (fun _ -> [p [Html5.D.string_input ~input_type:`Submit ~value:s ()]]) () (* The following eref is true if the connection has action failed: *) let bad_user = Eliom_reference.eref ~scope:Eliom_common.request_scope false let my_group_data = Eliom_reference.eref ~scope:group None let change_gd = Eliom_registration.Action.register_post_coservice' ~name:"changegd" ~post_params:Eliom_parameter.unit (fun () () -> Eliom_reference.set my_group_data (Some (1000 + Random.int 1000))) (* -------------------------------------------------------- *) (* new login box: *) let login_box session_expired bad_u action = Html5.D.post_form action (fun loginname -> let l = [pcdata "login: "; Html5.D.string_input ~input_type:`Text ~name:loginname ()] in [p (if bad_u then (pcdata "Wrong user")::(br ())::l else if session_expired then (pcdata "Session expired")::(br ())::l else l) ]) () (* -------------------------------------------------------- *) (* Handler for the "connect_example" service (main page): *) let connect_example_handler () () = (* The following function tests whether the session has expired: *) let status = Eliom_state.volatile_data_state_status (*zap* *) ~scope:session (* *zap*) () in let group = Eliom_state.get_volatile_data_session_group (*zap* *) ~scope:session (* *zap*) () in Eliom_reference.get bad_user >>= fun bad_u -> Eliom_reference.get my_group_data >>= fun my_group_data -> Lwt.return (html (head (title (pcdata "")) []) (body (match group, status with | Some name, _ -> [p [pcdata ("Hello "^name); br (); (match my_group_data with | None -> pcdata "You have no group data." | Some i -> pcdata ("Your group data is "^string_of_int i^"."))]; Html5.D.post_form change_gd (fun () -> [p [Html5.D.string_input ~input_type:`Submit ~value:"Change group data" ()]]) (); p [pcdata "Check that several sessions have the same group data."]; p [pcdata "Volatile group data are currently discarded when all group disappear. This is weird and not coherent with persistent group data. But I don't really see a correct use of volatile group data. Is there any? And there is a risk of memory leak if we keep them. Besides, volatile sessions are (hopefully) going to disappear soon."]; disconnect_box "Close session"] | None, Eliom_state.Expired_state -> [login_box true bad_u connect_action; p [em [pcdata "The only user is 'toto'."]]] | _ -> [login_box false bad_u connect_action; p [em [pcdata "The only user is 'toto'."]]] ))) (* -------------------------------------------------------- *) (* Handler for connect_action (user logs in): *) let connect_action_handler () login = lwt () = Eliom_state.discard ~scope:session () in if login = "toto" (* Check user and password :-) *) then begin Eliom_state.set_volatile_data_session_group ~set_max:4 (*zap* *) ~scope:session (* *zap*) login; Eliom_reference.get my_group_data >>= fun mgd -> (if mgd = None then Eliom_reference.set my_group_data (Some (Random.int 1000)) else Lwt.return ()) >>= fun () -> Eliom_registration.Redirection.send Eliom_service.void_hidden_coservice' end else Eliom_reference.set bad_user true >>= fun () -> Eliom_registration.Action.send () (* -------------------------------------------------------- *) (* Registration of main services: *) let () = Eliom_registration.Html5.register ~service:connect_example_gd connect_example_handler; Eliom_registration.Any.register ~service:connect_action connect_action_handler (*****************************************************************************) (* 2011/08/02 Vincent - Persistent group data removing group data or not when no session in the group? *) (*zap* *) open Html5.F (*zap* *) let scope_hierarchy = Eliom_common.create_scope_hierarchy "pers_session_group_data_example_state" let session = `Session scope_hierarchy let group = `Session_group scope_hierarchy (* *zap*) (* -------------------------------------------------------- *) (* We create one main service and two (POST) actions *) (* (for connection and disconnection) *) let connect_example_pgd = Eliom_service.service ~path:["psessgrpdata"] ~get_params:unit () let connect_action = Eliom_service.post_coservice' ~name:"connectionpgd" ~post_params:(string "login") () (* disconnect action and box: *) let disconnect_action = Eliom_registration.Action.register_post_coservice' ~name:"disconnectionpgd" ~post_params:Eliom_parameter.unit (fun () () -> Eliom_state.discard ~scope:session ()) let disconnect_box s = Html5.D.post_form disconnect_action (fun _ -> [p [Html5.D.string_input ~input_type:`Submit ~value:s ()]]) () (* The following eref is true if the connection has action failed: *) let bad_user = Eliom_reference.eref ~scope:Eliom_common.request_scope false let my_group_data = Eliom_reference.eref ~persistent:"pgd" ~scope:group None let change_gd = Eliom_registration.Action.register_post_coservice' ~name:"changepgd" ~post_params:Eliom_parameter.unit (fun () () -> Eliom_reference.set my_group_data (Some (1000 + Random.int 1000))) (* -------------------------------------------------------- *) (* new login box: *) let login_box session_expired bad_u action = Html5.D.post_form action (fun loginname -> let l = [pcdata "login: "; Html5.D.string_input ~input_type:`Text ~name:loginname ()] in [p (if bad_u then (pcdata "Wrong user")::(br ())::l else if session_expired then (pcdata "Session expired")::(br ())::l else l) ]) () (* -------------------------------------------------------- *) (* Handler for the "connect_example" service (main page): *) let connect_example_handler () () = (* The following function tests whether the session has expired: *) let status = Eliom_state.volatile_data_state_status (*zap* *) ~scope:session (* *zap*) () in let group = Eliom_state.get_volatile_data_session_group (*zap* *) ~scope:session (* *zap*) () in Eliom_reference.get bad_user >>= fun bad_u -> Eliom_reference.get my_group_data >>= fun my_group_data -> Lwt.return (html (head (title (pcdata "")) []) (body (match group, status with | Some name, _ -> [p [pcdata ("Hello "^name); br (); (match my_group_data with | None -> pcdata "You have no group data." | Some i -> pcdata ("Your group data is "^string_of_int i^".")); ]; Html5.D.post_form change_gd (fun () -> [p [Html5.D.string_input ~input_type:`Submit ~value:"Change group data" ()]]) (); p [pcdata "Check that several sessions have the same group data."]; p [pcdata "Check that persistent group data do not disappear when all sessions from the group are closed."]; p [pcdata "Persistent group data are used as a basic database, for example to store user information (email, etc)."]; disconnect_box "Close session"] | None, Eliom_state.Expired_state -> [login_box true bad_u connect_action; p [em [pcdata "The only user is 'toto'."]]] | _ -> [login_box false bad_u connect_action; p [em [pcdata "The only user is 'toto'."]]] ))) (* -------------------------------------------------------- *) (* Handler for connect_action (user logs in): *) let connect_action_handler () login = lwt () = Eliom_state.discard ~scope:session () in if login = "toto" (* Check user and password :-) *) then begin Eliom_state.set_volatile_data_session_group ~set_max:4 (*zap* *) ~scope:session (* *zap*) login; Eliom_reference.get my_group_data >>= fun mgd -> (if mgd = None then Eliom_reference.set my_group_data (Some (Random.int 1000)) else Lwt.return ()) >>= fun () -> Eliom_registration.Redirection.send Eliom_service.void_hidden_coservice' end else Eliom_reference.set bad_user true >>= fun () -> Eliom_registration.Action.send () (* -------------------------------------------------------- *) (* Registration of main services: *) let () = Eliom_registration.Html5.register ~service:connect_example_pgd connect_example_handler; Eliom_registration.Any.register ~service:connect_action connect_action_handler (*****************************************************************************) (* Actions with `NoReload option *) let noreload_ref = ref 0 let noreload_action = Eliom_registration.Action.register_coservice' ~options:`NoReload ~get_params:unit (fun () () -> noreload_ref := !noreload_ref + 1; Lwt.return ()) let noreload = register_service ~path:["noreload"] ~get_params:unit (fun () () -> return (html (head (title (pcdata "counter")) []) (body [p [pcdata (string_of_int (!noreload_ref)); br (); Html5.D.a ~service:noreload_action [pcdata "Click to increment the counter."] (); br (); pcdata "You should not see the result if you do not reload the page." ]]))) (*****************************************************************************) (* neopt, by Dario Teixeira *) let neopt_handler ((a, b), (c, d)) () = Lwt.return (html (head (title (pcdata "Coucou")) []) (body [ p [pcdata "Coucou:"]; p [pcdata (Printf.sprintf "a: %s" a)]; p [pcdata (Printf.sprintf "b: %s" (match b with Some b -> string_of_int b | None -> "(none)"))]; p [pcdata (Printf.sprintf "c: %s" (match c with Some c -> string_of_float c | None -> "(none)"))]; p [pcdata (Printf.sprintf "d: %s" (match d with Some d -> d | None -> "(none)"))]; ])) let neopt_service = Eliom_registration.Html5.register_service ~path: ["neopt"] ~get_params: (suffix_prod (Eliom_parameter.string "a" ** neopt (Eliom_parameter.int "b")) (neopt (Eliom_parameter.float "c") ** neopt (Eliom_parameter.string "d"))) neopt_handler let neopt_form ((e_a, e_b), (e_c, e_d)) = [ fieldset [ label ~a:[a_for e_a] [pcdata "Enter string 'a':"]; Html5.D.string_input ~a:[a_id "e_a"] ~input_type:`Text ~name:e_a (); br (); label ~a:[a_for e_b] [pcdata "Enter int 'b' (neopt):"]; Html5.D.int_input ~a:[a_id "e_b"] ~input_type:`Text ~name:e_b (); br (); label ~a:[a_for e_c] [pcdata "Enter float 'c' (neopt):"]; Html5.D.float_input ~a:[a_id "e_c"] ~input_type:`Text ~name:e_c (); br (); label ~a:[a_for e_d] [pcdata "Enter string 'd' (neopt):"]; Html5.D.string_input ~a:[a_id "e_d"] ~input_type:`Text ~name:e_d (); br (); Html5.D.button ~button_type:`Submit [pcdata "Apply"]; ] ] let main_neopt_handler () () = Lwt.return (html (head (title (pcdata "Main")) []) (body [ p [ pcdata "Here's a "; Html5.D.a neopt_service [pcdata "link"] (("foo", None), (None, None)); pcdata " to the neopt service" ]; p [ pcdata "Here's another "; Html5.D.a neopt_service [pcdata "link"] (("foo", Some 1), (None, None)); pcdata " to the neopt service" ]; p [ pcdata "Here's yet another "; Html5.D.a neopt_service [pcdata "link"] (("foo", None), (Some 2.0, Some "Olá!")); pcdata " to the neopt service" ]; p [ pcdata "Here's the final "; Html5.D.a neopt_service [pcdata "link"] (("foo", Some 1), (Some 2.0, Some "Olá!")); pcdata " to the neopt service" ]; Html5.D.get_form neopt_service neopt_form; ])) let main_neopt_service = Eliom_registration.Html5.register_service ~path: ["neopt0"] ~get_params: Eliom_parameter.unit main_neopt_handler eliom-3.0.3/tests/eliom_testsuite1.ml0000644000000000000000000021766012062377521016024 0ustar0000000000000000(* Eliom test suite, part 1 *) (* TODO: extract the tests from the manual or vice versa. Take the code in the manual, not here! (and remove duplicates here) *) (* TODO: include some missing parts in the manual *) open Eliom_lib open Eliom_content open Lwt open Html5.F open Ocsigen_cookies open Eliom_service open Eliom_parameter open Eliom_state open Eliom_registration.Html5 let coucou = register_service ~path:["coucou"] ~get_params:unit (fun () () -> return (html (head (title (pcdata "")) []) (body [h1 [pcdata "Hallo!"]]))) let coucou1 = register_service ~path:["coucou1"] ~get_params:Eliom_parameter.unit (fun () () -> let module Html5 = Eliom_content.Html5.F in return <<

Coucou

>>) (* let coucou_xhtml = let open XHTML.M in Eliom_output.Xhtml.register_service ~path:["coucou_xhtml"] ~get_params:unit (fun () () -> return (html (head (title (pcdata "")) []) (body [h1 [pcdata "Hallo!"]]))) *) (* let coucou1_xthml = Eliom_output.Html5.register_service ~path:["coucou1_xhtml"] ~get_params:Eliom_parameter.unit (fun () () -> let module Html5 = Eliom_content.Html5.F in return <:xhtml<

Coucou

>>) *) let coucoutext = Eliom_registration.Html_text.register_service ~path:["coucoutext"] ~get_params:Eliom_parameter.unit (fun () () -> return ("n'importe quoi "^ (Eliom_content.Html_text.a coucou "clic" ())^ "")) (*wiki* Page generation may have side-effects: *wiki*) let count = let next = let c = ref 0 in (fun () -> c := !c + 1; !c) in register_service ~path:["count"] ~get_params:unit (fun () () -> return (html (head (title (pcdata "counter")) []) (body [p [pcdata (string_of_int (next ()))]]))) (*wiki* As usual in OCaml, you can forget labels when the application is total: *wiki*) let hello = register_service ["dir";"hello"] (* the url dir/hello *) unit (fun () () -> return (html (head (title (pcdata "Hello")) []) (body [h1 [pcdata "Hello"]]))) (*wiki* The following example shows how to define the default page for a directory. (Note that %% means the default page of the directory %%) *wiki*) let default = register_service ["rep";""] unit (fun () () -> return (html (head (title (pcdata "")) []) (body [p [pcdata "default page. rep is redirected to rep/"]]))) let writeparams (i1, (i2, s1)) () = return (html (head (title (pcdata "")) []) (body [p [pcdata "You sent: "; strong [pcdata (string_of_int i1)]; pcdata ", "; strong [pcdata (string_of_int i2)]; pcdata " and "; strong [pcdata s1]]])) (*zap* you can register twice the same service, with different parameter names *zap*) let coucou_params = register_service ~path:["coucou"] ~get_params:(int "i" ** (int "ii" ** string "s")) writeparams (*zap* If you register twice exactly the same URL, the server won't start *zap*) (*wiki* *wiki*) let uasuffix = register_service ~path:["uasuffix"] ~get_params:(suffix (int "year" ** int "month")) (fun (year, month) () -> return (html (head (title (pcdata "")) []) (body [p [pcdata "The suffix of the url is "; strong [pcdata ((string_of_int year)^"/" ^(string_of_int month))]; pcdata ", your user-agent is "; strong [pcdata (Eliom_request_info.get_user_agent ())]; pcdata ", your IP is "; strong [pcdata (Eliom_request_info.get_remote_ip ())]]]))) (*wiki* *wiki*) let isuffix = register_service ~path:["isuffix"] ~get_params:(suffix_prod (int "suff" ** all_suffix "endsuff") (int "i")) (fun ((suff, endsuff), i) () -> return (html (head (title (pcdata "")) []) (body [p [pcdata "The suffix of the url is "; strong [pcdata (string_of_int suff)]; pcdata " followed by "; strong [pcdata (Url.string_of_url_path ~encode:false endsuff)]; pcdata " and i is equal to "; strong [pcdata (string_of_int i)]]]))) (*wiki* *wiki*) let constfix = register_service ~path:["constfix"] ~get_params:(suffix (string "s1" ** (Eliom_parameter.suffix_const "toto" ** string "s2"))) (fun (s1, ((), s2)) () -> return (html (head (title (pcdata "")) []) (body [h1 [pcdata "Suffix with constants"]; p [pcdata ("Parameters are "^s1^" and "^s2)]]))) (*wiki* *wiki*) type mysum = A | B let mysum_of_string = function | "A" -> A | "B" -> B | _ -> raise (Failure "mysum_of_string") let string_of_mysum = function | A -> "A" | B -> "B" let mytype = Eliom_registration.Html5.register_service ~path:["mytype"] ~get_params: (Eliom_parameter.user_type mysum_of_string string_of_mysum "valeur") (fun x () -> let v = string_of_mysum x in return (html (head (title (pcdata "")) []) (body [p [pcdata (v^" is valid. Now try with another value.")]]))) (*wiki* *wiki*) let raw_serv = register_service ~path:["any"] ~get_params:Eliom_parameter.any (fun l () -> let module Html5 = Eliom_content.Html5.F in let ll = List.map (fun (a,s) -> <:html5< ($str:a$, $str:s$) >>) l in return <:html5<

You sent: $list:ll$

>>) (*wiki* *wiki*) let catch = register_service ~path:["catch"] ~get_params:(int "i") ~error_handler:(fun l -> return (html (head (title (pcdata "")) []) (body [p [pcdata ("i is not an integer.")]]))) (fun i () -> let v = string_of_int i in return (html (head (title (pcdata "")) []) (body [p [pcdata ("i is an integer: "^v)]]))) (*wiki* *wiki*) let links = register_service ["rep";"links"] unit (fun () () -> return (html (head (title (pcdata "Links")) []) (body [p [Html5.D.a coucou [pcdata "coucou"] (); br (); Html5.D.a hello [pcdata "hello"] (); br (); Html5.D.a default [pcdata "default page of the dir"] (); br (); Html5.D.a uasuffix [pcdata "uasuffix"] (2007,06); br (); Html5.D.a coucou_params [pcdata "coucou_params"] (42,(22,"ciao")); br (); Html5.D.a raw_serv [pcdata "raw_serv"] [("sun","yellow");("sea","blue and pink")]; br (); Html5.D.a (external_service ~prefix:"http://fr.wikipedia.org" ~path:["wiki";""] ~get_params:(suffix (all_suffix "suff")) ()) [pcdata "OCaml on wikipedia"] ["OCaml"]; br (); Html5.F.Raw.a ~a:[a_href (Xml.uri_of_string "http://en.wikipedia.org/wiki/OCaml")] [pcdata "OCaml on wikipedia"] ]]))) (*zap* Note that to create a link we need to know the current url, because: the link from toto/titi to toto/tata is "tata" and not "toto/tata" *zap*) (*wiki* *wiki*) let linkrec = Eliom_service.service ["linkrec"] unit () let _ = Eliom_registration.Html5.register linkrec (fun () () -> return (html (head (title (pcdata "")) []) (body [p [a linkrec [pcdata "click"] ()]]))) (*zap* If some url are not registered, the server will not start: let essai = new_url ~path:["essai"] ~server_params:no_server_param ~get_params:no_get_param () *zap*) (*zap* pour les reload : le serveur ne s'éteint pas mais ajoute un message sur les services non enregistrés dans son log *zap*) (*wiki* *wiki*) let create_form = (fun (number_name, (number2_name, string_name)) -> [p [pcdata "Write an int: "; Html5.D.int_input ~input_type:`Text ~name:number_name (); pcdata "Write another int: "; Html5.D.int_input ~input_type:`Text ~name:number2_name (); pcdata "Write a string: "; Html5.D.string_input ~input_type:`Text ~name:string_name (); Html5.D.string_input ~input_type:`Submit ~value:"Click" ()]]) let form = register_service ["form"] unit (fun () () -> let f = Html5.D.get_form coucou_params create_form in return (html (head (title (pcdata "")) []) (body [f]))) (*wiki* *wiki*) let raw_form = register_service ~path:["anyform"] ~get_params:unit (fun () () -> return (html (head (title (pcdata "")) []) (body [h1 [pcdata "Any Form"]; Html5.D.get_form raw_serv (fun () -> [p [pcdata "Form to raw_serv: "; Html5.D.raw_input ~input_type:`Text ~name:"plop" (); Html5.D.raw_input ~input_type:`Text ~name:"plip" (); Html5.D.raw_input ~input_type:`Text ~name:"plap" (); Html5.D.string_input ~input_type:`Submit ~value:"Click" ()]]) ]))) (*wiki* *wiki*) let no_post_param_service = register_service ~path:["post"] ~get_params:unit (fun () () -> return (html (head (title (pcdata "")) []) (body [h1 [pcdata "Version of the page without POST parameters"]]))) let my_service_with_post_params = register_post_service ~fallback:no_post_param_service ~post_params:(string "value") (fun () value -> return (html (head (title (pcdata "")) []) (body [h1 [pcdata value]]))) (*wiki* Services may take both GET and POST parameters: *wiki*) let get_no_post_param_service = register_service ~path:["post2"] ~get_params:(int "i") (fun i () -> return (html (head (title (pcdata "")) []) (body [p [pcdata "No POST parameter, i:"; em [pcdata (string_of_int i)]]]))) let my_service_with_get_and_post = register_post_service ~fallback:get_no_post_param_service ~post_params:(string "value") (fun i value -> return (html (head (title (pcdata "")) []) (body [p [pcdata "Value: "; em [pcdata value]; pcdata ", i: "; em [pcdata (string_of_int i)]]]))) (*wiki* POST forms *wiki*) let form2 = register_service ["form2"] unit (fun () () -> let f = (Html5.D.post_form my_service_with_post_params (fun chaine -> [p [pcdata "Write a string: "; string_input ~input_type:`Text ~name:chaine ()]]) ()) in return (html (head (title (pcdata "form")) []) (body [f]))) let form3 = register_service ["form3"] unit (fun () () -> let module Html5 = Eliom_content.Html5.F in let f = (Eliom_content.Html5.D.post_form my_service_with_get_and_post (fun chaine -> <:html5list<

Write a string: $string_input ~input_type:`Text ~name:chaine ()$

>>) 222) in return <:html5< $f$ >>) let form4 = register_service ["form4"] unit (fun () () -> let module Html5 = Eliom_content.Html5.F in let f = (Eliom_content.Html5.D.post_form (external_post_service ~prefix:"http://www.petizomverts.com" ~path:["zebulon"] ~get_params:(int "i") ~post_params:(string "chaine") ()) (fun chaine -> <:html5list<

Write a string: $string_input ~input_type:`Text ~name:chaine ()$

>>) 222) in return (html (head (title (pcdata "form")) []) (body [f]))) (*wiki* Lwt % Unix.sleep 5; return (html (head (title (pcdata "")) []) (body [h1 [pcdata "Ok now, you can read the page."]]))) >% *wiki*) let looong = register_service ~path:["looong"] ~get_params:unit (fun () () -> Lwt_unix.sleep 5.0 >>= fun () -> return (html (head (title (pcdata "")) []) (body [h1 [pcdata "Ok now, you can read the page."]]))) (*wiki* *wiki*) let looong2 = register_service ~path:["looong2"] ~get_params:unit (fun () () -> Lwt_preemptive.detach Unix.sleep 5 >>= fun () -> return (html (head (title (pcdata "")) []) (body [h1 [pcdata "Ok now, you can read the page."]]))) (*wiki* *wiki*) (************************************************************) (************ Connection of users, version 1 ****************) (************************************************************) (*zap* *) let scope_hierarchy = Eliom_common.create_scope_hierarchy "session_data" let session = `Session scope_hierarchy (* *zap*) (* "my_table" will be the structure used to store the session data (namely the login name): *) let my_table = Eliom_state.create_volatile_table (*zap* *) ~scope:session (* *zap*) () (* -------------------------------------------------------- *) (* Create services, but do not register them yet: *) let session_data_example = Eliom_service.service ~path:["sessdata"] ~get_params:Eliom_parameter.unit () let session_data_example_with_post_params = Eliom_service.post_service ~fallback:session_data_example ~post_params:(Eliom_parameter.string "login") () let session_data_example_close = Eliom_service.service ~path:["close"] ~get_params:Eliom_parameter.unit () (* -------------------------------------------------------- *) (* Handler for the "session_data_example" service: *) let session_data_example_handler _ _ = let sessdat = Eliom_state.get_volatile_data ~table:my_table () in return (html (head (title (pcdata "")) []) (body [ match sessdat with | Eliom_state.Data name -> p [pcdata ("Hello "^name); br (); Html5.D.a session_data_example_close [pcdata "close session"] ()] | Eliom_state.Data_session_expired | Eliom_state.No_data -> Html5.D.post_form session_data_example_with_post_params (fun login -> [p [pcdata "login: "; Html5.D.string_input ~input_type:`Text ~name:login ()]]) () ])) (* -------------------------------------------------------- *) (* Handler for the "session_data_example_with_post_params" *) (* service with POST params: *) let session_data_example_with_post_params_handler _ login = lwt () = Eliom_state.discard (*zap* *) ~scope:session (* *zap*) () in Eliom_state.set_volatile_data ~table:my_table login; return (html (head (title (pcdata "")) []) (body [p [pcdata ("Welcome " ^ login ^ ". You are now connected."); br (); Html5.D.a session_data_example [pcdata "Try again"] () ]])) (* -------------------------------------------------------- *) (* Handler for the "session_data_example_close" service: *) let session_data_example_close_handler () () = let sessdat = Eliom_state.get_volatile_data ~table:my_table () in lwt () = Eliom_state.discard (*zap* *) ~scope:session (* *zap*) () in return (html (head (title (pcdata "Disconnect")) []) (body [ (match sessdat with | Eliom_state.Data_session_expired -> p [pcdata "Your session has expired."] | Eliom_state.No_data -> p [pcdata "You were not connected."] | Eliom_state.Data _ -> p [pcdata "You have been disconnected."]); p [Html5.D.a session_data_example [pcdata "Retry"] () ]])) (* -------------------------------------------------------- *) (* Registration of main services: *) let () = Eliom_registration.Html5.register session_data_example_close session_data_example_close_handler; Eliom_registration.Html5.register session_data_example session_data_example_handler; Eliom_registration.Html5.register session_data_example_with_post_params session_data_example_with_post_params_handler (*zap* *) let () = set_default_global_service_state_timeout ~cookie_level:`Session (Some 600.) let () = set_default_global_persistent_data_state_timeout ~cookie_level:`Session (Some 3600.) (* *zap*) (************************************************************) (************ Connection of users, version 2 ****************) (************************************************************) (*zap* *) let scope_hierarchy = Eliom_common.create_scope_hierarchy "session_services" let session = `Session scope_hierarchy (* *zap*) (* -------------------------------------------------------- *) (* Create services, but do not register them yet: *) let session_services_example = Eliom_service.service ~path:["sessionservices"] ~get_params:Eliom_parameter.unit () let session_services_example_with_post_params = Eliom_service.post_service ~fallback:session_services_example ~post_params:(Eliom_parameter.string "login") () let session_services_example_close = Eliom_service.service ~path:["close2"] ~get_params:Eliom_parameter.unit () (* ------------------------------------------------------------- *) (* Handler for the "session_services_example" service: *) (* It displays the main page of our site, with a login form. *) let session_services_example_handler () () = let f = Html5.D.post_form session_services_example_with_post_params (fun login -> [p [pcdata "login: "; string_input ~input_type:`Text ~name:login ()]]) () in return (html (head (title (pcdata "")) []) (body [f])) (* ------------------------------------------------------------- *) (* Handler for the "session_services_example_close" service: *) let session_services_example_close_handler () () = lwt () = Eliom_state.discard (*zap* *) ~scope:session (* *zap*) () in return (html (head (title (pcdata "Disconnect")) []) (body [p [pcdata "You have been disconnected. "; a session_services_example [pcdata "Retry"] () ]])) (*wiki* When the page is called with login parameters, it runs the function %% that replaces some services already defined by new ones: *wiki*) (* ------------------------------------------------------------- *) (* Handler for the "session_services_example_with_post_params" *) (* service: *) let launch_session () login = (* New handler for the main page: *) let new_main_page () () = return (html (head (title (pcdata "")) []) (body [p [pcdata "Welcome "; pcdata login; pcdata "!"; br (); a coucou [pcdata "coucou"] (); br (); a hello [pcdata "hello"] (); br (); a links [pcdata "links"] (); br (); a session_services_example_close [pcdata "close session"] ()]])) in (* If a session was opened, we close it first! *) lwt () = Eliom_state.discard ~scope:session () in (* Now we register new versions of main services in the session service table: *) Eliom_registration.Html5.register ~scope:session ~service:session_services_example (* service is any public service already registered, here the main page of our site *) new_main_page; Eliom_registration.Html5.register ~scope:session ~service:coucou (fun () () -> return (html (head (title (pcdata "")) []) (body [p [pcdata "Coucou "; pcdata login; pcdata "!"]]))); Eliom_registration.Html5.register ~scope:session ~service:hello (fun () () -> return (html (head (title (pcdata "")) []) (body [p [pcdata "Ciao "; pcdata login; pcdata "!"]]))); new_main_page () () (* -------------------------------------------------------- *) (* Registration of main services: *) let () = Eliom_registration.Html5.register ~service:session_services_example session_services_example_handler; Eliom_registration.Html5.register ~service:session_services_example_close session_services_example_close_handler; Eliom_registration.Html5.register ~service:session_services_example_with_post_params launch_session (*zap* Registering for session during initialisation is forbidden: let _ = register ~scope:`Session ~path:coucou1 %<

humhum

>% *zap*) (*wiki* *wiki*) (************************************************************) (************** Coservices. Basic examples ******************) (************************************************************) (* -------------------------------------------------------- *) (* We create one main service and two coservices: *) let coservices_example = Eliom_service.service ~path:["coserv"] ~get_params:Eliom_parameter.unit () let coservices_example_post = Eliom_service.post_coservice ~fallback:coservices_example ~post_params:Eliom_parameter.unit () let coservices_example_get = Eliom_service.coservice ~fallback:coservices_example ~get_params:Eliom_parameter.unit () (* -------------------------------------------------------- *) (* The three of them display the same page, *) (* but the coservices change the counter. *) let _ = let c = ref 0 in let page () () = let l3 = Html5.D.post_form coservices_example_post (fun _ -> [p [Html5.D.string_input ~input_type:`Submit ~value:"incr i (post)" ()]]) () in let l4 = Html5.D.get_form coservices_example_get (fun _ -> [p [Html5.D.string_input ~input_type:`Submit ~value:"incr i (get)" ()]]) in return (html (head (title (pcdata "")) []) (body [p [pcdata "i is equal to "; pcdata (string_of_int !c); br (); a coservices_example [pcdata "reload"] (); br (); a coservices_example_get [pcdata "incr i"] ()]; l3; l4])) in Eliom_registration.Html5.register coservices_example page; let f () () = c := !c + 1; page () () in Eliom_registration.Html5.register coservices_example_post f; Eliom_registration.Html5.register coservices_example_get f (*wiki* %
% %
%). The style of programming used by Eliom is closer to //Continuation Passing Style// (CPS), and has the advantage that it does not need control operators, and fits very well Web programming. Coservices allow to create dynamically new continuations that depend on previous interactions with users ([[manual/dev/2#p2calc|See the %% example below]]). Such a behaviour is difficult to simulate with traditional Web programming. >% *wiki*) (*zap* Queinnec example: *zap*) (************************************************************) (*************** calc: sum of two integers ******************) (************************************************************) (*zap* *) let calc_example_scope_hierarchy = Eliom_common.create_scope_hierarchy "calc_example" let session = `Session calc_example_scope_hierarchy let session_group = `Session_group calc_example_scope_hierarchy (* *zap*) (* -------------------------------------------------------- *) (* We create two main services on the same URL, *) (* one with a GET integer parameter: *) let calc = service ~path:["calc"] ~get_params:unit () let calc_i = service ~path:["calc"] ~get_params:(int "i") () (* -------------------------------------------------------- *) (* The handler for the service without parameter. *) (* It displays a form where you can write an integer value: *) let calc_handler () () = let create_form intname = [p [pcdata "Write a number: "; Html5.D.int_input ~input_type:`Text ~name:intname (); br (); Html5.D.string_input ~input_type:`Submit ~value:"Send" ()]] in let f = Html5.D.get_form calc_i create_form in return (html (head (title (pcdata "")) []) (body [f])) (* -------------------------------------------------------- *) (* The handler for the service with parameter. *) (* It creates dynamically and registers a new coservice *) (* with one GET integer parameter. *) (* This new coservice depends on the first value (i) *) (* entered by the user. *) let calc_i_handler i () = let create_form is = (fun entier -> [p [pcdata (is^" + "); int_input ~input_type:`Text ~name:entier (); br (); string_input ~input_type:`Submit ~value:"Sum" ()]]) in let is = string_of_int i in let calc_result = register_coservice ~scope:Eliom_common.default_session_scope ~fallback:calc ~get_params:(int "j") (fun j () -> let js = string_of_int j in let ijs = string_of_int (i+j) in return (html (head (title (pcdata "")) []) (body [p [pcdata (is^" + "^js^" = "^ijs)]]))) in let f = get_form calc_result (create_form is) in return (html (head (title (pcdata "")) []) (body [f])) (* -------------------------------------------------------- *) (* Registration of main services: *) let () = Eliom_registration.Html5.register calc calc_handler; Eliom_registration.Html5.register calc_i calc_i_handler (*wiki* *wiki*) (************************************************************) (************ Connection of users, version 3 ****************) (************************************************************) (*zap* *) let connect_example3_scope_hierarchy = Eliom_common.create_scope_hierarchy "connect_example3" let session = `Session connect_example3_scope_hierarchy let session_group = `Session_group connect_example3_scope_hierarchy let my_table = Eliom_state.create_volatile_table (*zap* *) ~scope:session (* *zap*) () (* *zap*) (* -------------------------------------------------------- *) (* We create one main service and two (POST) actions *) (* (for connection and disconnection) *) let connect_example3 = Eliom_service.service ~path:["action"] ~get_params:Eliom_parameter.unit () let connect_action = Eliom_service.post_coservice' ~name:"connect3" ~post_params:(Eliom_parameter.string "login") () (* As the handler is very simple, we register it now: *) let disconnect_action = Eliom_registration.Action.register_post_coservice' ~name:"disconnect3" ~post_params:Eliom_parameter.unit (fun () () -> Eliom_state.discard (*zap* *) ~scope:session (* *zap*) ()) (* -------------------------------------------------------- *) (* login ang logout boxes: *) let disconnect_box s = Html5.D.post_form disconnect_action (fun _ -> [p [Html5.D.string_input ~input_type:`Submit ~value:s ()]]) () let login_box () = Html5.D.post_form connect_action (fun loginname -> [p (let l = [pcdata "login: "; Html5.D.string_input ~input_type:`Text ~name:loginname ()] in l) ]) () (* -------------------------------------------------------- *) (* Handler for the "connect_example3" service (main page): *) let connect_example3_handler () () = let sessdat = Eliom_state.get_volatile_data ~table:my_table () in return (html (head (title (pcdata "")) []) (body (match sessdat with | Eliom_state.Data name -> [p [pcdata ("Hello "^name); br ()]; disconnect_box "Close session"] | Eliom_state.Data_session_expired | Eliom_state.No_data -> [login_box ()] ))) (* -------------------------------------------------------- *) (* Handler for connect_action (user logs in): *) let connect_action_handler () login = lwt () = Eliom_state.discard (*zap* *) ~scope:session (* *zap*) () in Eliom_state.set_volatile_data ~table:my_table login; return () (* -------------------------------------------------------- *) (* Registration of main services: *) let () = Eliom_registration.Html5.register ~service:connect_example3 connect_example3_handler; Eliom_registration.Action.register ~service:connect_action connect_action_handler (*wiki* *wiki*) let divpage = Eliom_registration.Flow5.register_service ~path:["div"] ~get_params:unit (fun () () -> return [div [h2 [pcdata "Hallo"]; p [pcdata "Blablablabla"] ]]) (*wiki* *wiki*) let redir1 = Eliom_registration.Redirection.register_service ~path:["redir"] ~get_params:Eliom_parameter.unit (fun () () -> Lwt.return coucou) (*wiki* *wiki*) let redir = Eliom_registration.Redirection.register_service ~path:["redir"] ~get_params:(int "o") (fun o () -> Lwt.return (Eliom_service.preapply coucou_params (o,(22,"ee")))) (*wiki* *wiki*) let send_any = Eliom_registration.Any.register_service ~path:["sendany"] ~get_params:(string "type") (fun s () -> if s = "valid" then Eliom_registration.Html5.send (html (head (title (pcdata "")) []) (body [p [pcdata "This page has been statically typechecked. If you change the parameter in the URL you will get an unchecked text page"]])) else Eliom_registration.Html_text.send "

It is not a valid page. Put type=\"valid\" in the URL to get a typechecked page.

" ) (*wiki* Cookies *wiki*) let cookiename = "mycookie" let cookies = service ["cookies"] unit () let _ = Eliom_registration.Html5.register cookies (fun () () -> Eliom_state.set_cookie ~name:cookiename ~value:(string_of_int (Random.int 100)) (); Lwt.return (html (head (title (pcdata "")) []) (body [p [pcdata (try "cookie value: "^ (CookiesTable.find cookiename (Eliom_request_info.get_cookies ())) with _ -> ""); br (); a cookies [pcdata "send other cookie"] ()]]))) (*wiki* *wiki*) let mystore = Ocsipersist.open_store "eliomexamplestore2" let count2 = let next = let cthr = Ocsipersist.make_persistent mystore "countpage" 0 in let mutex = Lwt_mutex.create () in (fun () -> cthr >>= fun c -> Lwt_mutex.lock mutex >>= fun () -> Ocsipersist.get c >>= fun oldc -> let newc = oldc + 1 in Ocsipersist.set c newc >>= fun () -> Lwt_mutex.unlock mutex; Lwt.return newc) in register_service ~path:["count2"] ~get_params:unit (fun () () -> next () >>= (fun n -> return (html (head (title (pcdata "counter")) []) (body [p [pcdata (string_of_int n)]])))) (*wiki* *wiki*) (************************************************************) (************ Connection of users, version 4 ****************) (**************** (persistent sessions) *********************) (************************************************************) (*zap* *) let persistent_sessions_scope_hierarchy = Eliom_common.create_scope_hierarchy "persistent_sessions" let session = `Session persistent_sessions_scope_hierarchy let session_group = `Session_group persistent_sessions_scope_hierarchy let persistent_session_scope = session (* *zap*) let my_persistent_table = create_persistent_table (*zap* *) ~scope:session (* *zap*) "eliom_example_table" (* -------------------------------------------------------- *) (* We create one main service and two (POST) actions *) (* (for connection and disconnection) *) let persist_session_example = Eliom_service.service ~path:["persist"] ~get_params:unit () let persist_session_connect_action = Eliom_service.post_coservice' ~name:"connect4" ~post_params:(string "login") () (* disconnect_action, login_box and disconnect_box have been defined in the section about actions *) (*zap* *) (* -------------------------------------------------------- *) (* Actually, no. It's a lie because we don't use the same session name :-) *) (* new disconnect action and box: *) let disconnect_action = Eliom_registration.Action.register_post_coservice' ~name:"disconnect4" ~post_params:Eliom_parameter.unit (fun () () -> Eliom_state.discard ~scope:session ()) let disconnect_box s = Html5.D.post_form disconnect_action (fun _ -> [p [Html5.D.string_input ~input_type:`Submit ~value:s ()]]) () let bad_user_key = Polytables.make_key () let get_bad_user table = try Polytables.get ~table ~key:bad_user_key with Not_found -> false (* -------------------------------------------------------- *) (* new login box: *) let login_box session_expired action = Html5.D.post_form action (fun loginname -> let l = [pcdata "login: "; string_input ~input_type:`Text ~name:loginname ()] in [p (if get_bad_user (Eliom_request_info.get_request_cache ()) then (pcdata "Wrong user")::(br ())::l else if session_expired then (pcdata "Session expired")::(br ())::l else l) ]) () (* *zap*) (* ----------------------------------------------------------- *) (* Handler for "persist_session_example" service (main page): *) let persist_session_example_handler () () = Eliom_state.get_persistent_data ~table:my_persistent_table () >>= fun sessdat -> return (html (head (title (pcdata "")) []) (body (match sessdat with | Eliom_state.Data name -> [p [pcdata ("Hello "^name); br ()]; disconnect_box "Close session"] | Eliom_state.Data_session_expired -> [login_box true persist_session_connect_action; p [em [pcdata "The only user is 'toto'."]]] | Eliom_state.No_data -> [login_box false persist_session_connect_action; p [em [pcdata "The only user is 'toto'."]]] ))) (* ----------------------------------------------------------- *) (* Handler for persist_session_connect_action (user logs in): *) let persist_session_connect_action_handler () login = lwt () = Eliom_state.discard (*zap* *) ~scope:session (* *zap*) () in if login = "toto" (* Check user and password :-) *) then Eliom_state.set_persistent_data ~table:my_persistent_table login else ((*zap* *)Polytables.set (Eliom_request_info.get_request_cache ()) bad_user_key true;(* *zap*)return ()) (* -------------------------------------------------------- *) (* Registration of main services: *) let () = Eliom_registration.Html5.register ~service:persist_session_example persist_session_example_handler; Eliom_registration.Action.register ~service:persist_session_connect_action persist_session_connect_action_handler (*wiki* *wiki*) (************************************************************) (************ Connection of users, version 6 ****************) (************************************************************) (*zap* *) let scope_hierarchy = Eliom_common.create_scope_hierarchy "connect_example6" let session = `Session scope_hierarchy let session_group = `Session_group scope_hierarchy (* *zap*) (* -------------------------------------------------------- *) (* We create one main service and two (POST) actions *) (* (for connection and disconnection) *) let connect_example6 = Eliom_service.service ~path:["action2"] ~get_params:unit () let connect_action = Eliom_service.post_coservice' ~name:"connect6" ~post_params:(string "login") () (* new disconnect action and box: *) let disconnect_action = Eliom_registration.Action.register_post_coservice' ~name:"disconnect6" ~post_params:Eliom_parameter.unit (fun () () -> Eliom_state.discard (*zap* *) ~scope:session (* *zap*) ()) let disconnect_box s = Html5.D.post_form disconnect_action (fun _ -> [p [Html5.D.string_input ~input_type:`Submit ~value:s ()]]) () let bad_user_key = Polytables.make_key () let get_bad_user table = try Polytables.get ~table ~key:bad_user_key with Not_found -> false (* -------------------------------------------------------- *) (* new login box: *) let login_box session_expired action = Html5.D.post_form action (fun loginname -> let l = [pcdata "login: "; string_input ~input_type:`Text ~name:loginname ()] in [p (if get_bad_user (Eliom_request_info.get_request_cache ()) then (pcdata "Wrong user")::(br ())::l else if session_expired then (pcdata "Session expired")::(br ())::l else l) ]) () (* -------------------------------------------------------- *) (* Handler for the "connect_example6" service (main page): *) let connect_example6_handler () () = let status = Eliom_state.volatile_data_state_status (*zap* *) ~scope:session (* *zap*) () in let group = Eliom_state.get_volatile_data_session_group (*zap* *) ~scope:session (* *zap*) () in return (html (head (title (pcdata "")) []) (body (match group, status with | Some name, _ -> [p [pcdata ("Hello "^name); br ()]; disconnect_box "Close session"] | None, Eliom_state.Expired_state -> [login_box true connect_action; p [em [pcdata "The only user is 'toto'."]]] | _ -> [login_box false connect_action; p [em [pcdata "The only user is 'toto'."]]] ))) (* -------------------------------------------------------- *) (* New handler for connect_action (user logs in): *) let connect_action_handler () login = lwt () = Eliom_state.discard (*zap* *) ~scope:session (* *zap*) () in if login = "toto" (* Check user and password :-) *) then begin Eliom_state.set_volatile_data_session_group ~set_max:4 (*zap* *) ~scope:session (* *zap*) login; return () end else begin Polytables.set (Eliom_request_info.get_request_cache ()) bad_user_key true; return () end (* -------------------------------------------------------- *) (* Registration of main services: *) let () = Eliom_registration.Html5.register ~service:connect_example6 connect_example6_handler; Eliom_registration.Action.register ~service:connect_action connect_action_handler (*wiki* *wiki*) let disposable = service ["disposable"] unit () let _ = register disposable (fun () () -> let disp_coservice = coservice ~max_use:2 ~fallback:disposable ~get_params:unit () in register ~scope:Eliom_common.default_session_scope ~service:disp_coservice (fun () () -> return (html (head (title (pcdata "")) []) (body [p [pcdata "I am a disposable coservice"; br (); a disp_coservice [pcdata "Try me once again"] ()]])) ); return (html (head (title (pcdata "")) []) (body [p [(if Eliom_request_info.get_link_too_old () then pcdata "Your link was outdated. I am the fallback. I just created a new disposable coservice. You can use it only twice." else pcdata "I just created a disposable coservice. You can use it only twice."); br (); a disp_coservice [pcdata "Try it!"] ()]]))) (*wiki* *wiki*) let timeout = service ["timeout"] unit () let _ = let page () () = let timeoutcoserv = register_coservice ~scope:session ~fallback:timeout ~get_params:unit ~timeout:5. (fun _ _ -> return (html (head (title (pcdata "Coservices with timeouts")) []) (body [p [pcdata "I am a coservice with timeout."; br (); pcdata "Try to reload the page!"; br (); pcdata "I will disappear after 5 seconds of inactivity." ]; ]))) in return (html (head (title (pcdata "Coservices with timeouts")) []) (body [p [pcdata "I just created a coservice with 5 seconds timeout."; br (); a timeoutcoserv [pcdata "Try it"] (); ]; ])) in register timeout page (*wiki* *wiki*) let publiccoduringsess = service ["publiccoduringsess"] unit () let _ = let page () () = let timeoutcoserv = register_coservice ~fallback:publiccoduringsess ~get_params:unit ~timeout:5. (fun _ _ -> return (html (head (title (pcdata "Coservices with timeouts")) []) (body [p [pcdata "I am a public coservice with timeout."; br (); pcdata "I will disappear after 5 seconds of inactivity." ]; ]))) in return (html (head (title (pcdata "Public coservices with timeouts")) []) (body [p [pcdata "I just created a public coservice with 5 seconds timeout."; br (); a timeoutcoserv [pcdata "Try it"] (); ]; ])) in register publiccoduringsess page (*wiki* *wiki*) let _ = Eliom_registration.set_exn_handler (fun e -> match e with | Eliom_common.Eliom_404 -> Eliom_registration.Html5.send ~code:404 (html (head (title (pcdata "")) []) (body [h1 [pcdata "Eliom tutorial"]; p [pcdata "Page not found"]])) (* | Eliom_common.Eliom_Wrong_parameter -> Eliom_registration.Html5.send (html (head (title (pcdata "")) []) (body [h1 [pcdata "Eliom tutorial"]; p [pcdata "Wrong parameters"]])) *) | e -> fail e) (*wiki* *wiki*) let my_nl_params = Eliom_parameter.make_non_localized_parameters ~prefix:"tutoeliom" ~name:"mynlparams" (Eliom_parameter.int "a" ** Eliom_parameter.string "s") let nlparams = service ~path:["nlparams"] ~get_params:(int "i") () let make_body () = [p [a ~service:nlparams [pcdata "without nl params"] 4]; p [a ~service:nlparams ~nl_params:(Eliom_parameter.add_nl_parameter Eliom_parameter.empty_nl_params_set my_nl_params (22, "oh") ) [pcdata "with nl params"] 5]; get_form ~service:nlparams ~nl_params:(Eliom_parameter.add_nl_parameter Eliom_parameter.empty_nl_params_set my_nl_params (22, "oh") ) (fun iname -> [p [pcdata "form with hidden nl params"; Html5.D.int_input ~input_type:`Text ~name:iname (); Html5.D.string_input ~input_type:`Submit ~value:"Send" ()]]); get_form ~service:nlparams (fun iname -> let (aname, sname) = Eliom_parameter.get_nl_params_names my_nl_params in [p [pcdata "form with nl params fiels"; Html5.D.int_input ~input_type:`Text ~name:iname (); Html5.D.int_input ~input_type:`Text ~name:aname (); Html5.D.string_input ~input_type:`Text ~name:sname (); Html5.D.string_input ~input_type:`Submit ~value:"Send" ()]]); ] let _ = register nlparams (fun i () -> Lwt.return (html (head (title (pcdata "")) []) (body ((p [pcdata "i = "; strong [pcdata (string_of_int i)]]):: (match Eliom_parameter.get_non_localized_get_parameters my_nl_params with | None -> p [pcdata "I do not have my non localized parameters"] | Some (a, s) -> p [pcdata "I have my non localized parameters, "; pcdata ("with values a = "^string_of_int a^ " and s = "^s^".")] )::make_body ()))) ) (*wiki* *wiki*) let tonlparams = register_service ~path:["nlparams"] ~get_params:unit (fun () () -> Lwt.return (html (head (title (pcdata "")) []) (body (make_body ())))) (*wiki* *wiki*) let nlparams_with_nlp = Eliom_service.add_non_localized_get_parameters my_nl_params nlparams (*wiki* *wiki*) (************************************************************) (************ Connection of users, version 5 ****************) (************************************************************) (*zap* *) let scope_hierarchy = Eliom_common.create_scope_hierarchy "connect_example5" let session = `Session scope_hierarchy let session_group = `Session_group scope_hierarchy (* *zap*) (* -------------------------------------------------------- *) (* We create one main service and two (POST) actions *) (* (for connection and disconnection) *) let connect_example5 = Eliom_service.service ~path:["groups"] ~get_params:Eliom_parameter.unit () let connect_action = Eliom_service.post_coservice' ~name:"connect5" ~post_params:(Eliom_parameter.string "login") () (* As the handler is very simple, we register it now: *) let disconnect_action = Eliom_registration.Action.register_post_coservice' ~name:"disconnect5" ~post_params:Eliom_parameter.unit (fun () () -> Eliom_state.discard (*zap* *) ~scope:session (* *zap*) ()) (* -------------------------------------------------------- *) (* login ang logout boxes: *) let disconnect_box s = Html5.D.post_form disconnect_action (fun _ -> [p [Html5.D.string_input ~input_type:`Submit ~value:s ()]]) () let login_box () = Html5.D.post_form connect_action (fun loginname -> [p (let l = [pcdata "login: "; Html5.D.string_input ~input_type:`Text ~name:loginname ()] in l) ]) () (* -------------------------------------------------------- *) (* Handler for the "connect_example5" service (main page): *) let connect_example5_handler () () = let sessdat = Eliom_state.get_volatile_data_session_group (*zap* *) ~scope:session (* *zap*) () in return (html (head (title (pcdata "")) []) (body (match sessdat with | Some name -> [p [pcdata ("Hello "^name); br ()]; disconnect_box "Close session"] | None -> [login_box ()] ))) (* -------------------------------------------------------- *) (* Handler for connect_action (user logs in): *) let connect_action_handler () login = Eliom_state.discard (*zap* *) ~scope:session (* *zap*) () >>= fun () -> Eliom_state.set_volatile_data_session_group ~set_max:4 (*zap* *) ~scope:session (* *zap*) login; return () (* -------------------------------------------------------- *) (* Registration of main services: *) let () = Eliom_registration.Html5.register ~service:connect_example5 connect_example5_handler; Eliom_registration.Action.register ~service:connect_action connect_action_handler (*wiki* *wiki*) (************************************************************) (********************* Group tables *************************) (************************************************************) (*zap* *) let scope_hierarchy = Eliom_common.create_scope_hierarchy "group_tables" let session = `Session scope_hierarchy let session_group = `Session_group scope_hierarchy (* *zap*) let my_table = Eliom_state.create_volatile_table ~scope:session_group () (* -------------------------------------------------------- *) (* We create one main service and two (POST) actions *) (* (for connection and disconnection) *) let group_tables_example = Eliom_service.service ~path:["grouptables"] ~get_params:Eliom_parameter.unit () let connect_action = Eliom_service.post_coservice' ~name:"connect7" ~post_params:(Eliom_parameter.string "login") () let disconnect_action = Eliom_registration.Action.register_post_coservice' ~name:"disconnectgt" ~post_params:Eliom_parameter.unit (fun () () -> Eliom_state.discard ~scope:session ()) let disconnect_g_action = Eliom_registration.Action.register_post_coservice' ~name:"disconnectgtg" ~post_params:Eliom_parameter.unit (fun () () -> Eliom_state.discard ~scope:session_group ()) (* -------------------------------------------------------- *) (* login ang logout boxes: *) let disconnect_box () = div [ Html5.D.post_form disconnect_action (fun _ -> [p [Html5.D.string_input ~input_type:`Submit ~value:"Close session" ()]]) (); Html5.D.post_form disconnect_g_action (fun _ -> [p [Html5.D.string_input ~input_type:`Submit ~value:"Close group" ()]]) () ] let login_box () = Html5.D.post_form connect_action (fun loginname -> [p (let l = [pcdata "login: "; Html5.D.string_input ~input_type:`Text ~name:loginname ()] in l) ]) () (* -------------------------------------------------------- *) (* Handler for the "group_tables_example" service (main page): *) let group_tables_example_handler () () = let sessdat = Eliom_state.get_volatile_data_session_group (*zap* *) ~scope:session (* *zap*) () in let groupdata = Eliom_state.get_volatile_data ~table:my_table () in let group_info name = match groupdata with | Eliom_state.Data_session_expired | Eliom_state.No_data -> let d = string_of_int (Random.int 1000) in Eliom_state.set_volatile_data ~table:my_table d; d | Eliom_state.Data d -> d in return (html (head (title (pcdata "")) []) (body (match sessdat with | Some name -> [p [pcdata ("Hello "^name); br ()]; (let d = group_info name in p [pcdata "Your group data is: "; pcdata d; pcdata ". It is common to all the sessions for the same user "; pcdata name; pcdata ". Try with another browser!" ]); p [pcdata "Check that all sessions with same user name share the value."]; p [pcdata "Check that the value disappears when all sessions from the group are closed."]; p [pcdata "Check that the all sessions are closed when clicking on \"close group\" button."]; disconnect_box ()] | None -> [login_box ()] ))) (* -------------------------------------------------------- *) (* Handler for connect_action (user logs in): *) let connect_action_handler () login = lwt () = Eliom_state.discard (*zap* *) ~scope:session (* *zap*) () in Eliom_state.set_volatile_data_session_group ~set_max:4 (*zap* *) ~scope:session (* *zap*) login; return () (* -------------------------------------------------------- *) (* Registration of main services: *) let () = Eliom_registration.Html5.register ~service:group_tables_example group_tables_example_handler; Eliom_registration.Action.register ~service:connect_action connect_action_handler (*zap* *) (************************************************************) (**************** Persistent group tables *******************) (************************************************************) let scope_hierarchy = Eliom_common.create_scope_hierarchy "pgroup_tables" let session = `Session scope_hierarchy let session_group = `Session_group scope_hierarchy let my_table = Eliom_state.create_persistent_table ~scope:session_group "pgroup_table" (* -------------------------------------------------------- *) (* We create one main service and two (POST) actions *) (* (for connection and disconnection) *) let pgroup_tables_example = Eliom_service.service ~path:["pgrouptables"] ~get_params:Eliom_parameter.unit () let connect_action = Eliom_service.post_coservice' ~name:"connect8" ~post_params:(Eliom_parameter.string "login") () let disconnect_action = Eliom_registration.Action.register_post_coservice' ~name:"pdisconnectgt" ~post_params:Eliom_parameter.unit (fun () () -> Eliom_state.discard ~scope:session ()) let disconnect_g_action = Eliom_registration.Action.register_post_coservice' ~name:"pdisconnectgtg" ~post_params:Eliom_parameter.unit (fun () () -> Eliom_state.discard ~scope:session_group ()) (* -------------------------------------------------------- *) (* login ang logout boxes: *) let disconnect_box () = div [ Html5.D.post_form disconnect_action (fun _ -> [p [Html5.D.string_input ~input_type:`Submit ~value:"Close session" ()]]) (); Html5.D.post_form disconnect_g_action (fun _ -> [p [Html5.D.string_input ~input_type:`Submit ~value:"Close group" ()]]) () ] let login_box () = Html5.D.post_form connect_action (fun loginname -> [p (let l = [pcdata "login: "; Html5.D.string_input ~input_type:`Text ~name:loginname ()] in l) ]) () (* -------------------------------------------------------- *) (* Handler for the "group_tables_example" service (main page): *) let group_tables_example_handler () () = Eliom_state.get_persistent_data_session_group ~scope:session () >>= fun sessdat -> Eliom_state.get_persistent_data ~table:my_table () >>= fun groupdata -> let group_info name = match groupdata with | Eliom_state.Data_session_expired | Eliom_state.No_data -> let d = string_of_int (Random.int 1000) in Eliom_state.set_persistent_data ~table:my_table d >>= fun r -> Lwt.return d | Eliom_state.Data d -> Lwt.return d in (match sessdat with | Some name -> (group_info name >>= fun d -> Lwt.return [p [pcdata ("Hello "^name); br ()]; (p [pcdata "Your persistent group data is: "; pcdata d; pcdata ". It is common to all the sessions for the same user "; pcdata name; pcdata ". Try with another browser!" ]); p [pcdata "Check that all sessions with same user name share the value."]; p [pcdata "Check that the value disappears when all sessions from the group are closed."]; p [pcdata "Check that the all sessions are closed when clicking on \"close group\" button."]; p [pcdata "Check that the value is preserved after relaunching the server."]; disconnect_box ()]) | None -> Lwt.return [login_box ()]) >>= fun l -> Lwt.return (html (head (title (pcdata "")) []) (body l)) (* -------------------------------------------------------- *) (* Handler for connect_action (user logs in): *) let connect_action_handler () login = lwt () = Eliom_state.discard ~scope:session () in Eliom_state.set_persistent_data_session_group ~set_max:(Some 4) ~scope:session login (* -------------------------------------------------------- *) (* Registration of main services: *) let () = Eliom_registration.Html5.register ~service:pgroup_tables_example group_tables_example_handler; Eliom_registration.Action.register ~service:connect_action connect_action_handler (* *zap*) (*wiki* *wiki*) let csrf_scope_hierarchy = Eliom_common.create_scope_hierarchy "csrf" let csrf_scope = `Session csrf_scope_hierarchy let csrfsafe_example = Eliom_service.service ~path:["csrf"] ~get_params:Eliom_parameter.unit () let csrfsafe_example_post = Eliom_service.post_coservice ~csrf_safe:true ~csrf_scope ~csrf_secure:true ~timeout:10. ~max_use:1 ~https:true ~fallback:csrfsafe_example ~post_params:Eliom_parameter.unit () let _ = let page () () = let l3 = Html5.D.post_form csrfsafe_example_post (fun _ -> [p [Html5.D.string_input ~input_type:`Submit ~value:"Click" ()]]) () in return (html (head (title (pcdata "CSRF safe service example")) []) (body [p [pcdata "A new coservice will be created each time this form is displayed"]; l3])) in Eliom_registration.Html5.register csrfsafe_example page; Eliom_registration.Html5.register csrfsafe_example_post (fun () () -> Lwt.return (html (head (title (pcdata "CSRF safe service")) []) (body [p [pcdata "This is a CSRF safe service"]]))) (*wiki* % return (html (head (title (pcdata "")) []) (body [p [pcdata g]]))) >% *wiki*) (*zap* *) let myregexp = Netstring_pcre.regexp "\\[(.*)\\]" let regexpserv = Eliom_registration.Html5.register_service ~path:["regexp"] ~get_params:(regexp myregexp "\\1" (fun s -> s) "myparam") (fun g () -> return (html (head (title (pcdata "")) []) (body [p [pcdata g]]))) (* *zap*) (*wiki* *wiki*) (* Form with bool checkbox: *) let bool_params = register_service ~path:["bool"] ~get_params:(bool "case") (fun case () -> let module Html5 = Eliom_content.Html5.F in return <:html5<

$pcdata (if case then "checked" else "not checked")$

>>) let create_form_bool casename = let module Html5 = Eliom_content.Html5.F in <:html5list<

check? $bool_checkbox ~name:casename ()$
$string_input ~input_type:`Submit ~value:"Click" ()$

>> let form_bool = register_service ["formbool"] unit (fun () () -> let module Html5 = Eliom_content.Html5.F in let f = get_form bool_params create_form_bool in return <:html5< $f$ >>) (*wiki* *wiki*) let set = register_service ~path:["set"] ~get_params:(set string "s") (fun l () -> let module Html5 = Eliom_content.Html5.F in let ll = List.map (fun s -> <:html5< $str:s$ >>) l in let module Html5 = Eliom_content.Html5.F in return <:html5<

You sent: $list:ll$

>>) (*wiki* *wiki*) (* form to set *) let setform = register_service ~path:["setform"] ~get_params:unit (fun () () -> return (html (head (title (pcdata "")) []) (body [h1 [pcdata "Set Form"]; get_form set (fun n -> [p [pcdata "Form to set: "; string_checkbox ~name:n ~value:"box1" (); string_checkbox ~name:n ~value:"box2" ~checked:true (); string_checkbox ~name:n ~value:"box3" (); string_checkbox ~name:n ~value:"box4" (); string_input ~input_type:`Submit ~value:"Click" ()]]) ]))) (*wiki* *wiki*) let select_example_result = register_service ~path:["select"] ~get_params:(string "s") (fun g () -> return (html (head (title (pcdata "")) []) (body [p [pcdata "You selected: "; strong [pcdata g]]]))) let create_select_form = (fun select_name -> [p [pcdata "Select something: "; Html5.D.string_select ~name:select_name (Html5.D.Option ([] (* attributes *), "Bob" (* value *), None (* Content, if different from value *), false (* not selected *))) (* first line *) [Html5.D.Option ([], "Marc", None, false); (Html5.D.Optgroup ([], "Girls", ([], "Karin", None, false), [([a_disabled `Disabled], "Juliette", None, false); ([], "Alice", None, true); ([], "Germaine", Some (pcdata "Bob's mother"), false)]))] ; Html5.D.string_input ~input_type:`Submit ~value:"Send" ()]]) let select_example = register_service ["select"] unit (fun () () -> let f = Html5.D.get_form select_example_result create_select_form in return (html (head (title (pcdata "")) []) (body [f]))) (*wiki* *wiki*) let coord = register_service ~path:["coord"] ~get_params:(coordinates "coord") (fun c () -> let module Html5 = Eliom_content.Html5.F in return <:html5<

You clicked on coordinates: ($str:(string_of_int c.abscissa)$, $str:(string_of_int c.ordinate)$)

>>) (* form to image *) let imageform = register_service ~path:["imageform"] ~get_params:unit (fun () () -> return (html (head (title (pcdata "")) []) (body [h1 [pcdata "Image Form"]; get_form coord (fun n -> [p [image_input ~src:(make_uri ~service:(static_dir ()) ["ocsigen5.png"]) ~name:n ()]]) ]))) (*wiki* *wiki*) let coord2 = register_service ~path:["coord2"] ~get_params:(int_coordinates "coord") (fun (i, c) () -> let module Html5 = Eliom_content.Html5.F in return <:html5<

You clicked on coordinates: ($str:(string_of_int c.abscissa)$, $str:(string_of_int c.ordinate)$)

>>) (* form to image *) let imageform2 = register_service ~path:["imageform2"] ~get_params:unit (fun () () -> return (html (head (title (pcdata "")) []) (body [h1 [pcdata "Image Form"]; get_form coord2 (fun n -> [p [int_image_input ~src:(make_uri ~service:(static_dir ()) ["ocsigen5.png"]) ~name:n ~value:3 ()]]) ]))) (*wiki* *wiki*) (* lists *) let coucou_list = register_service ~path:["coucou"] ~get_params:(list "a" (string "str")) (fun l () -> let module Html5 = Eliom_content.Html5.F in let ll = List.map (fun s -> <:html5< $str:s$ >>) l in return <:html5<

You sent: $list:ll$

>>) (*wiki* *wiki*) (*zap* Note: Actually almost all services will be overwritten by new versions, but not those with user_type parameters for example (because the type description contains functions) *zap*) (* Form with list: *) let create_listform f = (* Here, f.it is an iterator like List.map, but it must be applied to a function taking 3 arguments (unlike 1 in map), the first one being the name of the parameter, and the second one the element of list. The last parameter of f.it is the code that must be appended at the end of the list created *) let module Html5 = Eliom_content.Html5.F in f.it (fun stringname v init -> <:html5list<

Write the value for $str:v$: $string_input ~input_type:`Text ~name:stringname ()$

>>@init) ["one";"two";"three";"four"] <:html5list<

$string_input ~input_type:`Submit ~value:"Click" ()$

>> let listform = register_service ["listform"] unit (fun () () -> let module Html5 = Eliom_content.Html5.F in let f = get_form coucou_list create_listform in return <:html5< $f$ >>) (*wiki* *wiki*) (* Form for service with suffix: *) let create_suffixform ((suff, endsuff),i) = let module Html5 = Eliom_content.Html5.F in <:html5list<

Write the suffix (integer): $int_input ~input_type:`Text ~name:suff ()$
Write a string: $user_type_input (Url.string_of_url_path ~encode:false) ~input_type:`Text ~name:endsuff ()$
Write an int: $int_input ~input_type:`Text ~name:i ()$
$string_input ~input_type:`Submit ~value:"Click" ()$

>> let suffixform = register_service ["suffixform"] unit (fun () () -> let f = get_form isuffix create_suffixform in let module Html5 = Eliom_content.Html5.F in return <:html5< $f$ >>) (*wiki* *wiki*) let upload = service ~path:["upload"] ~get_params:unit () let upload2 = register_post_service ~fallback:upload ~post_params:(file "file") (fun () file -> let to_display = let newname = "/tmp/thefile" in (try Unix.unlink newname; with _ -> ()); Ocsigen_messages.console2 (Eliom_request_info.get_tmp_filename file); Unix.link (Eliom_request_info.get_tmp_filename file) newname; let fd_in = open_in newname in try let line = input_line fd_in in close_in fd_in; line (*end*) with End_of_file -> close_in fd_in; "vide" in return (html (head (title (pcdata "Upload")) []) (body [h1 [pcdata to_display]]))) let uploadform = register upload (fun () () -> let f = (post_form upload2 (fun file -> [p [file_input ~name:file (); br (); string_input ~input_type:`Submit ~value:"Send" () ]]) ()) in return (html (head (title (pcdata "form")) []) (body [f]))) (*wiki* *wiki*) (* Hierarchical menu *) open Eliom_tools let hier1 = service ~path:["hier1"] ~get_params:unit () let hier2 = service ~path:["hier2"] ~get_params:unit () let hier3 = service ~path:["hier3"] ~get_params:unit () let hier4 = service ~path:["hier4"] ~get_params:unit () let hier5 = service ~path:["hier5"] ~get_params:unit () let hier6 = service ~path:["hier6"] ~get_params:unit () let hier7 = service ~path:["hier7"] ~get_params:unit () let hier8 = service ~path:["hier8"] ~get_params:unit () let hier9 = service ~path:["hier9"] ~get_params:unit () let hier10 = service ~path:["hier10"] ~get_params:unit () let mymenu : (_, Eliom_service.registrable, _) hierarchical_site = ( (Main_page hier1), [([pcdata "page 1"], Site_tree (Main_page hier1, [])); ([pcdata "page 2"], Site_tree (Main_page hier2, [])); ([pcdata "submenu 4"], Site_tree (Default_page hier4, [([pcdata "submenu 3"], Site_tree (Not_clickable, [([pcdata "page 3"], Site_tree (Main_page hier3, [])); ([pcdata "page 4"], Site_tree (Main_page hier4, [])); ([pcdata "page 5"], Site_tree (Main_page hier5, []))] ) ); ([pcdata "page 6"], Site_tree (Main_page hier6, []))] ) ); ([pcdata "page 7"], Site_tree (Main_page hier7, [])); ([pcdata "disabled"], Disabled); ([pcdata "submenu 8"], Site_tree (Main_page hier8, [([pcdata "page 9"], Site_tree (Main_page hier9, [])); ([pcdata "page 10"], Site_tree (Main_page hier10, []))] ) ) ] ) let f i s () () = return (html (head (title (pcdata "")) ((style ~a:[a_mime_type "text/css"] [cdata_style "a {color: red;}\n li.eliomtools_current > a {color: blue;}\n .breadthmenu li {\n display: inline;\n padding: 0px 1em;\n margin: 0px;\n border-right: solid 1px black;}\n .breadthmenu li.eliomtools_last {border: none;}\n "]):: Eliom_tools.F.structure_links mymenu ~service:s ()) ) (body [h1 [pcdata ("Page "^string_of_int i)]; h2 [pcdata "Depth first, whole tree:"]; div (Eliom_tools.F.hierarchical_menu_depth_first ~whole_tree:true mymenu ~service:s ()); h2 [pcdata "Depth first, only current submenu:"]; div (Eliom_tools.F.hierarchical_menu_depth_first mymenu ~service:s ()); h2 [pcdata "Breadth first:"]; div (Eliom_tools.F.hierarchical_menu_breadth_first ~classe:["breadthmenu"] mymenu ~service:s ())])) let _ = register hier1 (f 1 hier1); register hier2 (f 2 hier2); register hier3 (f 3 hier3); register hier4 (f 4 hier4); register hier5 (f 5 hier5); register hier6 (f 6 hier6); register hier7 (f 7 hier7); register hier8 (f 8 hier8); register hier9 (f 9 hier9); register hier10 (f 10 hier10) eliom-3.0.3/tests/atom_example.ml0000644000000000000000000000557512062377521015200 0ustar0000000000000000(* * Copyright (C) 2010 Archibald Pontier * * This source file is part of Ocsigen < http://ocsigen.org/ > * * atom is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * atom is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with atom; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *) module M = Xhtml.M module Html5 = Eliom_content.Html5.F open Atom_feed open CalendarLib let f () = Lwt.return ( let r2 = Calendar.make 2009 11 22 13 54 21 in let d2 = Calendar.make 2010 7 1 18 12 1 in let d3 = Calendar.make 2012 12 11 16 14 36 in (* let's build the feed *) feed ~updated:r2 ~id:"http://test.org" ~title:(plain "Un flux Atom") (* the optional fields *) ~fields:[ authors [author "Tyruiop"]; subtitle (xhtml [M.pcdata "Voilà un exemple du flux atom généré avec Ocsigen !"]); links [link ~elt:[`Rel ("alternate"); `Type ("text/html") ] "http://test.org"]] (* the entry list *) [entry ~updated:r2 ~id:"http://test.org/1" ~title:(plain "Article 1") (* the entry optional fields *) [authors [author ~elt:[uri "http://tyruiop.org"] "Tyruiop"]; links [link "http://test.org/1"]; summary (plain "Un petit résumé de l'article 1, avec un lien."); inlineC ["Un exemple de text content"]]; entry ~updated:d2 ~id:"http://test.org/2" ~title:(plain "Article 2") [authors [author "Tyruiop"]; summary (plain "Un petit résumé de l'article 2"); published d2; xhtmlC [M.pcdata "un exemple de content en xHTML !"]]; entry ~updated:d3 ~id:"http://test.org/3" ~title:(plain "Article 3") [authors [author "bépo"]; summary (html5 [Html5.pcdata "Un petit résumé de l'article 3 en "; Html5.a ~service:Eliom_testsuite_base.main [Html5.pcdata "Html5"] (); ] ); published d3; html5C [Html5.pcdata "un exemple de content en HTML5 !"]]; ] ) (*(* let's register the feed *) let s = Eliom_atom.Reg.register_new_service ~path:["test"] ~get_params:Eliom_parameter.unit f *) let s = Eliom_atom.register_feed ~path:["test"] ~hubs:["http://tyruiop.org:8888"; "http://pubsubhubbub.appspot.com"] "http://tyruiop.org:8080/test/test" f eliom-3.0.3/tests/_client/0000755000000000000000000000000012062377521013574 5ustar0000000000000000eliom-3.0.3/tests/_server/0000755000000000000000000000000012062377521013624 5ustar0000000000000000eliom-3.0.3/tests/miniforum/0000755000000000000000000000000012062377521014164 5ustar0000000000000000eliom-3.0.3/tests/miniforum/Makefile0000644000000000000000000000254212062377521015627 0ustar0000000000000000# Write here all the findlib packages you need, for example: # PACKAGES= ,extlib,netstring # Write here all your .ml files, in dependency order (default: all) FILES=services.ml mylib.ml forum0.ml forum1.ml forum2.ml CAMLC = ocamlfind ocamlc -g -thread $(LIB) CAMLOPT = ocamlfind ocamlopt -thread $(LIB) CAMLDOC = ocamlfind ocamldoc $(LIB) CAMLDEP = ocamlfind ocamldep OCSIGENREP = `ocamlfind query ocsigen` #OCSIGENREP = ../ocsigen/lib LIB = -package lwt.unix,ocsigen$(PACKAGES) -I $(OCSIGENREP) # If you use the syntax extension: # PP = -pp "camlp4o $(OCSIGENREP)/xhtmlsyntax.cma" # otherwise PP = OBJS = $(FILES:.ml=.cmo) CMA = v0/site0.cma v1/site1.cma v2/site2.cma .PHONY: $(CMA) all: depend $(CMA) install #$(CMA): $(OBJS) # $(CAMLC) -a -o $(CMA) $(OBJS) v0/site0.cma: $(MAKE) -C v0 site0.cma v1/site1.cma: $(MAKE) -C v1 site1.cma v2/site2.cma: $(MAKE) -C v2 site2.cma install: chmod a+r $(CMA) .SUFFIXES: .SUFFIXES: .ml .mli .cmo .cmi .cmx .PHONY: doc .ml.cmo: $(CAMLC) $(PP) -c $< .mli.cmi: $(CAMLC) -c $< .ml.cmx: $(CAMLOPT) $(PP) -c $< doc: # $(CAMLDOC) -d doc -html db.mli clean: -rm -f *.cm[ioxa] *~ $(NAME) $(MAKE) -C v0 clean $(MAKE) -C v1 clean $(MAKE) -C v2 clean depend: $(CAMLDEP) $(PP) $(LIB) $(FILES:.ml=.mli) $(FILES) > .depend $(MAKE) -C v0 depend $(MAKE) -C v1 depend $(MAKE) -C v2 depend FORCE: -include .depend eliom-3.0.3/tests/miniforum/ocsigen.conf0000644000000000000000000000144612062377521016467 0ustar0000000000000000 8081 /tmp /tmp /tmp/ocsigen_command eliom-3.0.3/tests/miniforum/v0/0000755000000000000000000000000012062377521014511 5ustar0000000000000000eliom-3.0.3/tests/miniforum/v0/Makefile0000644000000000000000000000237512062377521016160 0ustar0000000000000000# Write here all the findlib packages you need, for example: # PACKAGES= ,extlib,netstring # Write here all your .ml files, in dependency order (default: all) FILES=services.ml mylib.ml forum0.ml forum1.ml forum2.ml CAMLC = ocamlfind ocamlc -g -thread $(LIB) CAMLOPT = ocamlfind ocamlopt -thread $(LIB) CAMLDOC = ocamlfind ocamldoc $(LIB) CAMLDEP = ocamlfind ocamldep OCSIGENREP = `ocamlfind query ocsigen` #OCSIGENREP = ../ocsigen/lib LIB = -package lwt.unix,ocsigen$(PACKAGES) -I $(OCSIGENREP) # If you use the syntax extension: PP = -pp "camlp4o $(OCSIGENREP)/xhtmlsyntax.cma" # otherwise #PP = OBJS = $(FILES:.ml=.cmo) CMA = site0.cma site1.cma site2.cma all: depend $(CMA) install #$(CMA): $(OBJS) # $(CAMLC) -a -o $(CMA) $(OBJS) site0.cma: mylib.cmo forum.cmo $(CAMLC) -a -o $@ $^ site1.cma: mylib.cmo forum.cmo $(CAMLC) -a -o $@ $^ site2.cma: services.cmo mylib.cmo forum.cmo $(CAMLC) -a -o $@ $^ install: chmod a+r $(CMA) .SUFFIXES: .SUFFIXES: .ml .mli .cmo .cmi .cmx .PHONY: doc .ml.cmo: $(CAMLC) $(PP) -c $< .mli.cmi: $(CAMLC) -c $< .ml.cmx: $(CAMLOPT) $(PP) -c $< doc: # $(CAMLDOC) -d doc -html db.mli clean: -rm -f *.cm[ioxa] *~ $(NAME) depend: $(CAMLDEP) $(PP) $(LIB) $(FILES:.ml=.mli) $(FILES) > .depend FORCE: -include .depend eliom-3.0.3/tests/miniforum/v0/mylib.ml0000644000000000000000000000214412062377521016160 0ustar0000000000000000open Eliom_predefmod.Xhtml open XHTML.M open Eliom_service let create_page sp mytitle mycontent = Lwt.return << $str:mytitle$

$str:mytitle$

$list:mycontent$ >> let create_page2 sp mytitle mycontent = Lwt.return (html (head (title (pcdata mytitle)) []) (body ((h1 [pcdata mytitle])::mycontent))) (* Messages database *) (* For the example, I'm storing messages in memory. I should use a database instead. Here are some predefined messages: *) let table = ref ["Welcome to Eliom's world."; "Hello! This is the second message."; "I am the third message of the forum."] let display_message_list () = match !table with | [] -> p [em [pcdata "No message"]] | m::l -> ul (li [pcdata m]) (List.map (fun m -> li [pcdata m]) l) let display_message n = try let m = List.nth !table n in p [pcdata m] with | Failure _ | Invalid_argument _ -> p [em [pcdata "no such message"]] let register_message msg = table := !table@[msg] eliom-3.0.3/tests/miniforum/v0/forum.ml0000644000000000000000000000075412062377521016201 0ustar0000000000000000open XHTML.M open Eliom_service open Eliom_parameter open Eliom_predefmod let mainpage = new_service ~path:[] ~get_params:unit () let msgpage = new_service ~path:[] ~get_params:(int "n") () let () = Xhtml.register mainpage (fun sp () () -> Mylib.create_page sp "Messages" [Mylib.display_message_list ()]) let () = Xhtml.register msgpage (fun sp n () -> Mylib.create_page sp ("Message "^(string_of_int n)) [Mylib.display_message n]) eliom-3.0.3/tests/miniforum/v0/forum0.ml0000644000000000000000000000044612062377521016257 0ustar0000000000000000open XHTML.M open Eliomservices open Eliomparameters open Eliompredefmod open Eliompredefmod.Xhtml let mainpage = new_service ~path:[] ~get_params:unit () let () = register mainpage (fun sp () () -> Mylib.create_page sp "Messages" [Mylib.display_message_list ()]) eliom-3.0.3/tests/miniforum/static/0000755000000000000000000000000012062377521015453 5ustar0000000000000000eliom-3.0.3/tests/miniforum/static/style.css0000644000000000000000000000061712062377521017331 0ustar0000000000000000h1{ background-color: #2233aa; text-align: center; color: white; margin: 2px; } body{ margin: 0px; padding: 10px; border: 5px solid #333333; } div.box{ border: 1px solid #555555; } div.colonnegauche{ float: left; width: 200px; } div.colonnedroite{ margin-left: 210px; } li{ border: 2px solid white; padding: 4px; background-color: #99ccff; list-style: none; } eliom-3.0.3/tests/miniforum/v2/0000755000000000000000000000000012062377521014513 5ustar0000000000000000eliom-3.0.3/tests/miniforum/v2/Makefile0000644000000000000000000000237612062377521016163 0ustar0000000000000000# Write here all the findlib packages you need, for example: # PACKAGES= ,extlib,netstring # Write here all your .ml files, in dependency order (default: all) FILES=services.ml mylib.ml forum0.ml forum1.ml forum2.ml CAMLC = ocamlfind ocamlc -g -thread $(LIB) CAMLOPT = ocamlfind ocamlopt -thread $(LIB) CAMLDOC = ocamlfind ocamldoc $(LIB) CAMLDEP = ocamlfind ocamldep OCSIGENREP = `ocamlfind query ocsigen` #OCSIGENREP = ../ocsigen/lib LIB = -package lwt.unix,ocsigen$(PACKAGES) -I $(OCSIGENREP) # If you use the syntax extension: # PP = -pp "camlp4o $(OCSIGENREP)/xhtmlsyntax.cma" # otherwise PP = OBJS = $(FILES:.ml=.cmo) CMA = site0.cma site1.cma site2.cma all: depend $(CMA) install #$(CMA): $(OBJS) # $(CAMLC) -a -o $(CMA) $(OBJS) site0.cma: mylib.cmo forum.cmo $(CAMLC) -a -o $@ $^ site1.cma: mylib.cmo forum.cmo $(CAMLC) -a -o $@ $^ site2.cma: services.cmo mylib.cmo forum.cmo $(CAMLC) -a -o $@ $^ install: chmod a+r $(CMA) .SUFFIXES: .SUFFIXES: .ml .mli .cmo .cmi .cmx .PHONY: doc .ml.cmo: $(CAMLC) $(PP) -c $< .mli.cmi: $(CAMLC) -c $< .ml.cmx: $(CAMLOPT) $(PP) -c $< doc: # $(CAMLDOC) -d doc -html db.mli clean: -rm -f *.cm[ioxa] *~ $(NAME) depend: $(CAMLDEP) $(PP) $(LIB) $(FILES:.ml=.mli) $(FILES) > .depend FORCE: -include .depend eliom-3.0.3/tests/miniforum/v2/mylib.ml0000644000000000000000000000377312062377521016173 0ustar0000000000000000open XHTML.M open Eliom_predefmod.Xhtml open Eliom_service (* Messages database *) (* For the example, I'm storing messages in memory. I should use a database instead. Here are some predefined messages: *) let table = ref ["Welcome to Eliom's world."; "Hello! This is the second message."; "I am the third message of the forum."] let display_message_list sp = let f m i = [pcdata m; pcdata " "; a Services.msgpage sp [pcdata "read"] i] in match !table with | [] -> p [em [pcdata "No message"]] | m::l -> ul (li (f m 0)) (snd (List.fold_right (fun m (i, l) -> (i+1, (li (f m i))::l)) l (0, []))) let display_message n = try let m = List.nth !table n in p [pcdata m] with | Failure _ | Invalid_argument _ -> p [em [pcdata "no such message"]] let register_message msg = table := !table@[msg] let disconnect_box sp s = post_form Services.disconnect_action sp (fun _ -> [p [Eliom_predefmod.Xhtml.string_input ~input_type:`Submit ~value:s ()]]) () let login_box sp = post_form Services.connect_action sp (fun loginname -> [p (let l = [pcdata "login: "; Eliom_predefmod.Xhtml.string_input ~input_type:`Text ~name:loginname ()] in l) ]) () let userbox ~sp = let sessdat = Eliom_sessions.get_volatile_data_session_group ~sp () in (match sessdat with | Eliom_sessions.Data name -> div [p [pcdata ("Hello "^name); br ()]; disconnect_box sp "Close session"] | Eliom_sessions.Data_session_expired | Eliom_sessions.No_data -> login_box sp ) (*****) let create_page sp mytitle mycontent = Lwt.return (html (head (title (pcdata mytitle)) [css_link (make_uri ~service:(static_dir sp) ~sp ["style.css"]) ()]) (body ((h1 [pcdata mytitle])::(userbox sp)::mycontent))) eliom-3.0.3/tests/miniforum/v2/services.ml0000644000000000000000000000076212062377521016675 0ustar0000000000000000open Eliom_service open Eliom_parameter let mainpage = new_service ~path:[] ~get_params:unit () let msgpage = new_service ~path:[] ~get_params:(int "n") () let addmsgpage = new_post_service ~fallback:mainpage ~post_params:(string "msg") () let newmsgpage = new_service ~path:["newmessage"] ~get_params:unit () let connect_action = new_post_coservice' ~name:"connect" ~post_params:(string "login") () let disconnect_action = new_post_coservice' ~name:"disconnect" ~post_params:unit () eliom-3.0.3/tests/miniforum/v2/forum.ml0000644000000000000000000000324712062377521016203 0ustar0000000000000000open Lwt open XHTML.M open Eliom_predefmod open Eliom_service open Eliom_parameter open Eliom_predefmod.Xhtml open Services let () = register mainpage (fun sp () () -> Mylib.create_page sp "Messages" [Mylib.display_message_list sp; p [a newmsgpage sp [pcdata "Create a new message"] ()]]) let () = register msgpage (fun sp n () -> Mylib.create_page sp ("Message "^(string_of_int n)) [Mylib.display_message n]) let () = register addmsgpage (fun sp () msg -> let ok = new_coservice ~max_use:1 ~fallback:mainpage ~get_params:unit () in Actions.register ~sp ~service:ok (fun sp () () -> Mylib.register_message msg; Lwt.return []); Mylib.create_page sp "Confirm this Message?" [p [pcdata msg]; p [ a ok sp [pcdata "Yes"] (); pcdata " "; a mainpage sp [pcdata "Cancel"] ()] ] ) let () = register newmsgpage (fun sp () () -> Mylib.create_page sp "New Message" [post_form addmsgpage sp (fun fieldname -> [p [pcdata "Write your message: "; br (); textarea ~name:fieldname ~rows:10 ~cols:80 (); br (); string_input ~input_type:`Submit ~value:"Enter" ()]]) ()]) let () = Actions.register disconnect_action (fun sp () () -> Eliom_sessions.close_session ~sp () >>= fun () -> Lwt.return []) let () = Actions.register connect_action (fun sp () login -> Eliom_sessions.close_session ~sp () >>= fun () -> Eliom_sessions.set_volatile_data_session_group ~set_max:(Some 10) ~sp login; Lwt.return []) eliom-3.0.3/tests/miniforum/v1/0000755000000000000000000000000012062377521014512 5ustar0000000000000000eliom-3.0.3/tests/miniforum/v1/Makefile0000644000000000000000000000237612062377521016162 0ustar0000000000000000# Write here all the findlib packages you need, for example: # PACKAGES= ,extlib,netstring # Write here all your .ml files, in dependency order (default: all) FILES=services.ml mylib.ml forum0.ml forum1.ml forum2.ml CAMLC = ocamlfind ocamlc -g -thread $(LIB) CAMLOPT = ocamlfind ocamlopt -thread $(LIB) CAMLDOC = ocamlfind ocamldoc $(LIB) CAMLDEP = ocamlfind ocamldep OCSIGENREP = `ocamlfind query ocsigen` #OCSIGENREP = ../ocsigen/lib LIB = -package lwt.unix,ocsigen$(PACKAGES) -I $(OCSIGENREP) # If you use the syntax extension: # PP = -pp "camlp4o $(OCSIGENREP)/xhtmlsyntax.cma" # otherwise PP = OBJS = $(FILES:.ml=.cmo) CMA = site0.cma site1.cma site2.cma all: depend $(CMA) install #$(CMA): $(OBJS) # $(CAMLC) -a -o $(CMA) $(OBJS) site0.cma: mylib.cmo forum.cmo $(CAMLC) -a -o $@ $^ site1.cma: mylib.cmo forum.cmo $(CAMLC) -a -o $@ $^ site2.cma: services.cmo mylib.cmo forum.cmo $(CAMLC) -a -o $@ $^ install: chmod a+r $(CMA) .SUFFIXES: .SUFFIXES: .ml .mli .cmo .cmi .cmx .PHONY: doc .ml.cmo: $(CAMLC) $(PP) -c $< .mli.cmi: $(CAMLC) -c $< .ml.cmx: $(CAMLOPT) $(PP) -c $< doc: # $(CAMLDOC) -d doc -html db.mli clean: -rm -f *.cm[ioxa] *~ $(NAME) depend: $(CAMLDEP) $(PP) $(LIB) $(FILES:.ml=.mli) $(FILES) > .depend FORCE: -include .depend eliom-3.0.3/tests/miniforum/v1/mylib.ml0000644000000000000000000000176312062377521016167 0ustar0000000000000000open Eliom_predefmod.Xhtml open XHTML.M open Eliom_service let create_page sp mytitle mycontent = Lwt.return (html (head (title (pcdata mytitle)) [css_link (make_uri ~service:(static_dir sp) ~sp ["style.css"]) ()]) (body ((h1 [pcdata mytitle])::mycontent))) (* Messages database *) (* For the example, I'm storing messages in memory. I should use a database instead. Here are some predefined messages: *) let table = ref ["Welcome to Eliom's world."; "Hello! This is the second message."; "I am the third message of the forum."] let display_message_list () = match !table with | [] -> p [em [pcdata "No message"]] | m::l -> ul (li [pcdata m]) (List.map (fun m -> li [pcdata m]) l) let display_message n = try let m = List.nth !table n in p [pcdata m] with | Failure _ | Invalid_argument _ -> p [em [pcdata "no such message"]] let register_message msg = table := !table@[msg] eliom-3.0.3/tests/miniforum/v1/forum.ml0000644000000000000000000000315412062377521016177 0ustar0000000000000000open XHTML.M open Eliom_service open Eliom_parameter open Eliom_predefmod open Eliom_predefmod.Xhtml let mainpage = new_service ~path:[] ~get_params:unit () let msgpage = new_service ~path:[] ~get_params:(int "n") () let addmsgpage = new_post_service ~fallback:mainpage ~post_params:(string "msg") () let newmsgpage = new_service ~path:["newmessage"] ~get_params:unit () let () = register mainpage (fun sp () () -> Mylib.create_page sp "Messages" [Mylib.display_message_list (); p [a newmsgpage sp [pcdata "Create a new message"] ()]]) let () = register msgpage (fun sp n () -> Mylib.create_page sp ("Message "^(string_of_int n)) [Mylib.display_message n]) let () = register addmsgpage (fun sp () msg -> let ok = new_coservice ~max_use:1 ~fallback:mainpage ~get_params:unit () in register ~sp ~service:ok (fun sp () () -> Mylib.register_message msg; Mylib.create_page sp "Message added" [Mylib.display_message_list ()]); Mylib.create_page sp "Confirm this Message?" [p [pcdata msg]; p [ a ok sp [pcdata "Yes"] (); pcdata " "; a mainpage sp [pcdata "Cancel"] ()] ] ) let () = register newmsgpage (fun sp () () -> Mylib.create_page sp "New Message" [post_form addmsgpage sp (fun fieldname -> [p [pcdata "Write your message: "; br (); textarea ~name:fieldname ~rows:10 ~cols:80 (); br (); string_input ~input_type:`Submit ~value:"Enter" ()]]) ()]) eliom-3.0.3/tests/miniwiki/0000755000000000000000000000000012062377521013777 5ustar0000000000000000eliom-3.0.3/tests/miniwiki/Makefile0000644000000000000000000000167212062377521015445 0ustar0000000000000000include ../../Makefile.config ## Use local files ## (tests do not require global installation of Eliom) export OCAMLPATH := ${SRC}/src/files:${OCAMLPATH} export PATH := ${SRC}/src/tools:${PATH} LIBS := -I .. ELIOMC := eliomc${BYTEDBG} ELIOMOPT := eliomopt ${OPTDBG} ELIOMDEP := eliomdep ifeq "${NATDYNLINK}" "YES" all: byte opt else all: byte endif ### Library FILES := miniwiki.ml byte:: miniwiki.cma opt:: miniwiki.cmxs miniwiki.cma: ${FILES:.ml=.cmo} ${ELIOMC} -a -o $@ $^ miniwiki.cmxa: ${FILES:.ml=.cmx} ${ELIOMOPT} -a -o $@ $^ ############ %.cmi: %.mli $(ELIOMC) ${LIBS} -c $< %.cmo: %.ml $(ELIOMC) ${LIBS} -c $< %.cmx: %.ml $(ELIOMOPT) ${LIBS} -c $< %.cmxs: %.cmxa $(ELIOMOPT) -shared -linkall -o $@ $< ## Clean up clean: -rm -f *.cm[ioax] *.cmxa *.cmxs *.o *.a *.annot -rm -f _server/* distclean: clean -rm -f *~ \#* .\#* ## Dependencies depend: $(ELIOMDEP) -server ${LIBS} *.ml *.mli > .depend FORCE: -include .dependeliom-3.0.3/tests/miniwiki/README0000644000000000000000000000204012062377521014653 0ustar0000000000000000Miniwiki --------- Miniwiki is a simple wiki written for Ocsigen by Janne Hellsten (jjhellst@gmail.com). It's primary purpose is to server as a code example for Ocsigen module developers. Compiling & running ------------------- Miniwiki should be compiled and install with Ocsigen. The default config file usually contains miniwiki preconfigured. If not, adapt files/miniwiki.conf and start Ocsigen with Miniwiki by running ocsigen -c examples/miniwiki/files/miniwiki.conf 4. Point your browser to localhost:9999/ Implementation -------------- Wiki page storage Each wiki page is a file in the wiki storage directory (/var/lib/ocsigen/miniwiki/wikidata/ is the default). A wiki page "Foo" corresponds to a file called "Foo.wiki" in the "wikidata" directory. Character encoding Wiki pages are stored as UTF-8 text files. If the site is properly configured, the wiki properly allows the use of UTF-8 content in wiki pages. Since wiki page names map directly to filenames on disk, page names containing non 7-bit ASCII might not work well. eliom-3.0.3/tests/miniwiki/.depend0000644000000000000000000000005612062377521015240 0ustar0000000000000000_server/miniwiki.cmo : _server/miniwiki.cmx : eliom-3.0.3/tests/miniwiki/miniwiki.ml0000644000000000000000000002613512062377521016160 0ustar0000000000000000(* Ocsigen * http://www.ocsigen.org * Module miniwiki.ml * Copyright (C) 2007 Janne Hellsten * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, with linking exception; * either version 2.1 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *) (* open Eliom_pervasives *) open Eliom_lib open Eliom_content open Eliom_content.Html5.F open Eliom_service open Eliom_parameter open Eliom_state open Simplexmlparser open Lwt open Lwt_chan module P = Printf let (>>) f g = g f let wiki_view_page = service [] (suffix (string "p")) () let wiki_edit_page = service ["edit"] (string "p") () let wiki_start = Eliom_registration.Redirection.register_service [] unit (fun _ _ -> Lwt.return (Eliom_service.preapply wiki_view_page "WikiStart")) let finally_ handler f x = catch (fun () -> f x) (fun e -> handler() >>= fun () -> fail e) >>= fun r -> handler () >>= fun () -> return r let fold_read_lines f accum inchnl = let line () = catch (fun () -> Lwt_chan.input_line inchnl >>= fun line -> return (Some line)) (function End_of_file -> return None | e -> fail e) in let rec loop accum = line () >>= fun l -> match l with | Some e -> loop (f accum e) | None -> return accum in loop accum let with_open_out fname f = Lwt_chan.open_out fname >>= fun oc -> finally_ (fun () -> Lwt_chan.flush oc >>= (fun () -> Lwt_chan.close_out oc)) f oc let with_open_in fname f = Lwt_chan.open_in fname >>= fun ic -> finally_ (fun () -> Lwt_chan.close_in ic) f ic let wiki_file_dir = let rec find_wikidata = function [Element ("wikidata", [("dir", s)],_)] -> s | _ -> raise (Ocsigen_extensions.Error_in_config_file ("Unexpected content inside Miniwiki config")) in let c = Eliom_config.get_config () in find_wikidata c let wiki_page_filename page = wiki_file_dir ^ "/" ^ page ^ ".wiki" let wiki_page_exists page = Sys.file_exists (wiki_page_filename page) let save_wiki_page page text = with_open_out (wiki_page_filename page) (fun chnl -> output_string chnl text) let load_wiki_page page = with_open_in (wiki_page_filename page) (fun chnl -> fold_read_lines (fun acc line -> line::acc) [] chnl >>= fun l -> return (List.rev l)) let h1_re = Pcre.regexp "^=(.*)=([ \n\r]*)?$" let h2_re = Pcre.regexp "^==(.*)==([ \n\r]*)?$" let h3_re = Pcre.regexp "^===(.*)===([ \n\r]*)?$" let list_re = Pcre.regexp "^[ ]?([*]+) (.*)([ \n\r]*)?$" let match_pcre_option rex s = try Some (Pcre.extract ~rex s) with Not_found -> None let is_list s = match_pcre_option list_re s let open_pre_re = Pcre.regexp "^(
|{{{)[ \n\r]+$"
let close_pre_re = Pcre.regexp "^(
|}}})[ \n\r]+$" let take_while pred lines = let rec loop acc = function (x::xs) as lst -> if pred x then loop (x::acc) xs else (lst, List.rev acc) | [] -> ([], List.rev acc) in loop [] lines let comp_re = Pcre.regexp ~flags:[`ANCHORED] let accepted_chars_ = "a-zA-Z\128-\2550-9_!\"§°#%&/\\(\\)=\\?\\+\\.,;:{}'@\\$\\^\\*`´<>" let accepted_chars_sans_ws = "["^accepted_chars_^"-]+" let accepted_chars = "["^accepted_chars_^" -]+" let text_re = comp_re ("("^accepted_chars_sans_ws^")") let wikilink_re = comp_re "([A-Z][a-z]+([A-Z][a-z]+)+)" let wikilinkanum_re = comp_re ("(\\[(wiki|file|http):("^accepted_chars_sans_ws^")[ ]+("^accepted_chars^")\\])") let wikilinkanum_no_text_re = comp_re ("(\\[(wiki|file|http):("^accepted_chars_sans_ws^")\\])") let translate_list items = let add_ul t lst = t @ [ul lst] in let rec loop = function ((nesting1,text1)::(nesting2,text2)::xs) as lst -> if nesting1 = nesting2 then (li text1)::loop (List.tl lst) else if nesting1 < nesting2 then (* enter *) let (next_same_level,same_or_higher) = take_while (fun (n,_) -> n >= nesting2) (List.tl lst) in (li (add_ul text1 (loop same_or_higher)))::loop next_same_level else (* leave *) loop (List.tl lst) | (nesting,text)::[] -> [(li text)] | [] -> [] in let list_items = loop items in ul list_items let parse_lines lines = let wikilink scheme page text = if scheme = "wiki" || scheme = "" then let t = if text = "" then page else text in if wiki_page_exists page then a wiki_view_page [pcdata t] page else a ~a:[a_class ["missing_page"]] ~service:wiki_view_page [pcdata t] page else (* External link *) let url = scheme^":"^page in let t = if text = "" then url else text in Html5.F.Raw.a ~a:[a_href (Html5.F.uri_of_string (fun () -> url))] [pcdata t] in let rec pcre_first_match str pos = let rec loop = function (rex,f)::xs -> (try Some (Pcre.extract ~rex ~pos str, f) with Not_found -> loop xs) | [] -> None in loop in (* Parse a line of text *) let rec parse_text acc s = let len = String.length s in let add_html html_acc html = html::html_acc in let parse_wikilink acc r charpos = (add_html acc (wikilink "" r.(1) r.(1)), charpos+(String.length r.(0))) in let parse_wikilinkanum acc r charpos = let scheme = r.(2) in let page = r.(3) in let text = r.(4) in let fm_len = String.length r.(0) in (add_html acc (wikilink scheme page text), charpos+fm_len) in let parse_wikilinkanum_no_text acc r charpos = let scheme = r.(2) in let page = r.(3) in let text = "" in let fm_len = String.length r.(0) in (add_html acc (wikilink scheme page text), charpos+fm_len) in let parse_text acc r charpos = (add_html acc (pcdata r.(1)), charpos+(String.length r.(0))) in let text_patterns = [(wikilink_re, parse_wikilink); (wikilinkanum_re, parse_wikilinkanum); (wikilinkanum_no_text_re, parse_wikilinkanum_no_text); (text_re, parse_text)] in let rec loop acc charpos = if charpos >= len then acc else if s.[charpos] = '\t' then let m = "\t" in loop (add_html acc (pcdata m)) (charpos+1) else if s.[charpos] = ' ' then let m = " " in loop (add_html acc (pcdata m)) (charpos+1) else if s.[charpos] = '\r' || s.[charpos] = '\n' then acc else begin match pcre_first_match s charpos text_patterns with Some (r,f) -> let (acc',charpos') = f acc r charpos in loop acc' charpos' | None -> let s = (String.sub s charpos ((String.length s)-charpos)) in add_html acc (span [span ~a:[a_class ["error"]] [pcdata "WIKI SYNTAX ERROR IN INPUT: "]; pcdata s]) end in List.rev (loop acc 0) in (* Line-by-line wiki parser *) let rec loop acc = function (x::xs) as lst -> let parse_list r = (* Grab all lines starting with '*': *) let (after_bullets,bullets) = take_while (fun e -> is_list e <> None) lst in let list_items = List.map (fun e -> match is_list e with Some r -> let n_stars = String.length r.(1) in (n_stars, parse_text [] r.(2)) | None -> assert false) bullets in loop ((translate_list list_items)::acc) after_bullets in let parse_verbatim r = (* Handle
..
, {{{..}}} *) let (after_pre,contents) = take_while (fun x -> match_pcre_option close_pre_re x = None) lst in let p = (pre [pcdata (String.concat "\n" (List.tl contents))]) in loop (p::acc) (List.tl after_pre) in let wiki_pats = [(h3_re, (fun r -> loop ((h3 [pcdata r.(1)])::acc) xs)); (h2_re, (fun r -> loop ((h2 [pcdata r.(1)])::acc) xs)); (h1_re, (fun r -> loop ((h1 [pcdata r.(1)])::acc) xs)); (list_re, parse_list); (open_pre_re, parse_verbatim)] in begin match pcre_first_match x 0 wiki_pats with Some (res, action) -> action res | None -> loop ((p (parse_text [] x))::acc) xs end | [] -> List.rev acc in return (loop [] lines) let wikiml_to_html page = if wiki_page_exists page then load_wiki_page page >>= parse_lines else return [] (* Use this as the basis for all pages. Includes CSS etc. *) let html_stub body_html = return (html (head (title (pcdata "")) [css_link (make_uri ~service:(static_dir ()) ["style.css"]) ()]) (body body_html)) let wiki_page_menu_html page content = [div ~a:[a_id "navbar"] [div ~a:[a_id "akmenu"] [p [span ~a:[a_class ["nwikilogo"]] [(pcdata "MiniWiki")]; a ~service:wiki_view_page ~a:[a_accesskey 'h'; a_class ["ak"]] [pcdata "Home"] "WikiStart"; a ~service:wiki_edit_page ~a:[a_accesskey 'e'; a_class ["ak"]] [pcdata "Edit page"] page; br ()]]]; div ~a:[a_id "content"] content] let wiki_page_contents_html page ?(content=[]) () = wikiml_to_html page >>= fun p -> return (wiki_page_menu_html page (content @ p)) let view_page page = wiki_page_contents_html page () >>= fun p -> html_stub p (* Save page as a result of /edit?p=Page *) let service_save_page_post = Eliom_registration.Html5.register_post_service ~fallback:wiki_view_page ~post_params:(string "value") (fun page value -> (* Save wiki page from POST value: *) save_wiki_page page value >>= fun () -> view_page page) (* /edit?p=Page *) let _ = Eliom_registration.Html5.register wiki_edit_page (fun page () -> (if wiki_page_exists page then load_wiki_page page >>= fun s -> return (String.concat "\n" s) else return "") >>= fun wikitext -> let f = post_form service_save_page_post (fun chain -> [(p [string_input ~input_type:`Submit ~value:"Save" (); br (); textarea ~name:chain ~value:wikitext ()])]) page in wiki_page_contents_html page ~content:[f] () >>= fun c -> html_stub c) (* /view?p=Page *) let _ = Eliom_registration.Html5.register wiki_view_page (fun page () -> if not (wiki_page_exists page) then let f = a wiki_edit_page [pcdata "Create new page"] page in html_stub (wiki_page_menu_html page [f]) else view_page page) eliom-3.0.3/tests/miniwiki/wikidata/0000755000000000000000000000000012062377521015574 5ustar0000000000000000eliom-3.0.3/tests/miniwiki/wikidata/TestPage.wiki0000644000000000000000000000164512062377521020203 0ustar0000000000000000= Test page = == Lists == * Test links [http://www.google.fi Google] and [http://www.google.fi] ** Nested under Foo ** Still nested under Foo * Top-level * TodoListForNwiki ** Foobar ** Barfoo *** sdf * Hoax * Poax === Heading 3 === One item: * List Two items: * Item 1 * Item 2 Three items, one nested * Item 1 * Item 2 ** Nested Item 1 Four items, one nested * Item 1 * Item 2 ** Nested Item 1 * Item 3
foo
Another line of verbatim text
   foo
== Paragraphs of text == Paragraphs of text. Paragraphs: My habanero started to flower a few weeks ago, and now, November 1st, it has produced a couple of pods as well. Quite surprising given the amount of light we have. Paragraphs of text. Paragraphs: My habanero started to flower a few weeks ago, and now, November 1st, it has produced a couple of pods as well. Quite surprising given the amount of light we have. eliom-3.0.3/tests/miniwiki/wikidata/WikiStart.wiki0000644000000000000000000000025412062377521020403 0ustar0000000000000000= Start Page of Miniwiki = == What is Miniwiki? == Miniwiki is a very simple wiki written as an example for Ocsigen. == Testing == See TestPage coucou tata eliom-3.0.3/tests/miniwiki/_server/0000755000000000000000000000000012062377521015444 5ustar0000000000000000eliom-3.0.3/doc/0000755000000000000000000000000012062377521011562 5ustar0000000000000000eliom-3.0.3/doc/Makefile0000644000000000000000000000035712062377521013227 0ustar0000000000000000 doc: server.doc client.doc server.doc: ${MAKE} -C server doc client.doc: ${MAKE} -C client doc clean: ${MAKE} -C server clean ${MAKE} -C client clean -rm -f *~ \#* .\#* install: ${MAKE} -C server install ${MAKE} -C client installeliom-3.0.3/doc/index.wiki0000644000000000000000000000041312062377521013554 0ustar0000000000000000= Eliom -- API reference [[wiki:mindmap.pdf|A mindmap to get an overview on the most important modules of Eliom]] <<| in /var/www/data/site-ocsimore/eliom >> <> <> eliom-3.0.3/doc/server/0000755000000000000000000000000012062377521013070 5ustar0000000000000000eliom-3.0.3/doc/server/Makefile0000644000000000000000000000220312062377521014525 0ustar0000000000000000include ../../Makefile.config include ../../src/server/Makefile.filelist OCAMLDOC := ${OCAMLFIND} ocamldoc ODOC := $(addprefix ../../src/server/,$(DOC:.mli=.odoc)) doc: odoc api-html/index.html api-man/Eliom_lib.server.3o wikidoc: odoc api-wiki/index.wiki odoc: ${MAKE} -C ../../src/server odoc api-html/index.html: indexdoc ${ODOC} mkdir -p api-html $(OCAMLDOC) ${LIBS} -d api-html -intro indexdoc $(addprefix -load ,${ODOC}) -html api-man/Eliom_lib.server.3o: ${ODOC} mkdir -p api-man $(OCAMLDOC) ${LIBS} -d api-man -man-mini $(addprefix -load ,${ODOC}) -man \ -man-section 3o -man-suffix server.3o api-wiki/index.wiki: indexdoc ${ODOC} mkdir -p api-wiki ODOC_WIKI_SUBPROJECT=server $(OCAMLDOC) -d api-wiki -intro indexdoc \ -i $(shell ocamlfind query wikidoc) -g odoc_wiki.cma \ -colorize-code $(addprefix -load ,${ODOC}) install: ${INSTALL} -d -m 755 $(TEMPROOT)$(DOCDIR)/server $(INSTALL) -m 644 api-html/* $(TEMPROOT)$(DOCDIR)/server $(INSTALL) -m 755 -d $(TEMPROOT)$(MANDIR)/man3 $(INSTALL) -m 755 api-man/* $(TEMPROOT)$(MANDIR)/man3 clean: -rm -f api-html/* -rm -f api-man/* -rm -f api-wiki/* -rm -f *~ \#* .\#* eliom-3.0.3/doc/server/indexdoc0000644000000000000000000000127312062377521014613 0ustar0000000000000000{1 Server API} {!modules: Eliom_pervasives Eliom_lib Eliom_common Eliom_config Eliom_request_info Eliom_reference Eliom_state } {2 Content and form creation} {!modules: Eliom_content Eliom_content.Html5 Eliom_content.Svg Eliom_content.Xml Eliom_tools } {2 Services creation} {!modules: Eliom_service Eliom_parameter Eliom_registration Eliom_registration.Html5 Eliom_registration.Action Eliom_registration.Ocaml Eliom_registration.App Eliom_registration.File Eliom_registration.Any Eliom_registration.Redirection } {2 Client/server communication} {!modules: Eliom_bus Eliom_comet Eliom_react } {2 Extensions} {!modules: Eliom_atom Atom_feed Eliom_openid Eliom_s2s } {2 Index} {!indexlist} eliom-3.0.3/doc/manual-wiki/0000755000000000000000000000000012062377521014000 5ustar0000000000000000eliom-3.0.3/doc/manual-wiki/server-security.wiki0000644000000000000000000001554312062377521020050 0ustar0000000000000000==How to write secure applications with Eliom== Eliom and Ocsigen server are taking in charge a lot of security issues automatically. This unburdens the programmer from having to think about most of security problems. This page details various possible designs flaws of web applications, how Eliom and Ocsigen server (possibly) protects you against a possible exploitation of these flaws, and where you should be careful. //Please help us maintaining this page, by sending us any comments.// <<|For more details on the various flaws, see e.g. [[http://www.amazon.fr/Web-Application-Hackers-Handbook-Discovering/dp/0470170778/|this book]].>> === The application only does client-side verification This is probably the biggest (and most dangerous) possible mistake. As the user has an entire control over the data sent to the server, **never** assume that the data sent by the client has been verified (even if there some checking function in Javascript or O'Browser). Instead, reimplement all verifications server side. * As a (small) mitigating factor, Eliom automatically checks that the type of the parameters is correct. * Suggested approach: use permissions to control if the user is allowed to perform an action. Then, ** when creating a form, check if the user has the required permission (if not, you will usually display a message //insufficient permissions// instead of the form) ** in the service that answers to the form, perform the exact same check However, note that incorrect data will never crash the server. === Incorrect access controls This typically happens if authentication is badly implemented, or altogether missing in some places. * each URL should implement user verification. Never assume that the user comes from a trusted url, for example because the url is secret. * use a programmatic, layered model, which encodes the various user rights (for ex: admin/moderators/editors/connected users/anonymous). Thus a compromised login will not compromise the entire application * Do not leak information through erroneous login (such as //this login does not exist//). Always answer //Bad login or password//, using always the same string * Do not permit too many near-simultaneous login attempts, either for the same login, or from the same IP. You can use the module Lwt_throttle to delay login, if too many connections are started. === [[http://en.wikipedia.org/wiki/Code_injection|Code injection]] * Eliom modules are written in OCaml, which is a compiled language. This prevents all code injection possible with script languages. * No sql injection is possible if you use Macaque or PGOCaml, which use PostgreSQL prepared statements. * No Html injection is possible, as unsafe HTML characters are always escaped before being written. === [[http://en.wikipedia.org/wiki/Directory_traversal|Path traversal]] * The module <> can be used to prevent access to files or entire directories, from both Staticmod and Eliom. * Even if <> is activated, users can only expose their own files; see the {{{localpath}}} option for details. * Occurrences of the {{{'..'}}} pattern appearing in Urls are automatically removed by the server * Ocsigen automatically decode HTML entities in URLs, which protects against attacks based on quoted characters === [[http://en.wikipedia.org/wiki/Session_fixation|Session fixation]] * Ocsigen cookies are cryptographically generated, and cannot be guessed by the attacker. Thus the attacker cannot control, or supply, the cookie of the client. * To be completely safe, do not issue tokens to anonymous users, or issue a new token as soon as a login/logout takes place <<|Vincent : le dernier truc est mal expliqué et je ne comprends pas trop ce qu'il a voulu dire. Il faut expliquer pourquoi les cookies de Eliom sont sûrs de ce point de vue, et dire comment éviter un pb avec des cookies que l'on pose soi-même. token = ?>> === [[http://en.wikipedia.org/wiki/Cross-site_scripting|Cross-site scripting (XSS)]] * most XSS attacks require code injection, from which you are automatically protected (see above) * However, avoid at all costs to put user-supplied text in sensitive tags, including: ** {{{" type for_attrib = string let make_for_attrib name = "for=\""^name^"\"" end include Eliom_mkforms.MakeForms(Forms_base) end eliom-3.0.3/src/server/Makefile.filelist0000644000000000000000000000256612062377521016375 0ustar0000000000000000 INTF := eliom_lib.cmi \ eliom_content_core.cmi \ eliom_content.cmi \ eliom_cookie.cmi \ eliom_common.cmi \ eliom_types.cmi \ eliom_parameter.cmi \ eliom_service.cmi \ eliom_config.cmi \ eliom_request_info.cmi \ eliom_state.cmi \ eliom_uri.cmi \ eliom_mkforms.cmi \ eliom_registration.cmi \ eliom_comet.cmi \ eliom_react.cmi \ eliom_bus.cmi \ eliom_wrap.cmi \ \ eliom_tools_common.cmi \ eliom_tools.cmi \ eliom_mkreg.cmi \ eliom_reference.cmi \ eliom_extension.cmi \ eliom_pervasives.cmi \ \ extensions/eliom_s2s.cmi \ extensions/eliom_openid.cmi \ extensions/atom_feed.cmi \ extensions/eliom_atom.cmi \ NO_MLI := eliom_pervasives_base.cmi IMPL := eliom.cma \ NATIMPL := eliom.cmxa eliom.a ifeq "$(NATDYNLINK)" "YES" NATIMPL += eliom.cmxs endif NOP4 := eliom_lib.mli DOC := ${subst .cmi,.mli, ${filter-out ${NO_MLI}, ${INTF}}} \ eliom_types_base.mli \ eliom_comet_base.mli \ eliom-3.0.3/src/server/eliom_comet.ml0000644000000000000000000006267112062377521015754 0ustar0000000000000000(* Ocsigen * http://www.ocsigen.org * Copyright (C) 2010-2011 * Raphaël Proust * Pierre Chambart * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, with linking exception; * either version 2.1 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *) (* TODO: handle ended stream ( and on client side too ) *) open Eliom_lib (* Shortening names of modules *) module OFrame = Ocsigen_http_frame module OStream = Ocsigen_stream module OMsg = Ocsigen_messages module Ecb = Eliom_comet_base type chan_id = string let encode_downgoing s = Eliom_comet_base.Json_answer.to_string (Eliom_comet_base.Stateful_messages (Array.of_list s)) let encode_global_downgoing s = Eliom_comet_base.Json_answer.to_string (Eliom_comet_base.Stateless_messages (Array.of_list s)) let timeout_msg = Eliom_comet_base.Json_answer.to_string Eliom_comet_base.Timeout let process_closed_msg = Eliom_comet_base.Json_answer.to_string Eliom_comet_base.Process_closed let error_msg s = Eliom_comet_base.Json_answer.to_string (Eliom_comet_base.Comet_error s) let json_content_type = "application/json" exception New_connection module Comet_param = struct type page = string let translate content = Lwt.return (content,json_content_type) end module Comet = Eliom_registration.Customize ( Eliom_registration.String ) ( Comet_param ) let comet_path = ["__eliom_comet__"] let comet_global_path = ["__eliom_comet_global__"] let fallback_service = Eliom_common.lazy_site_value_from_fun (fun () -> Comet.register_service ~path:comet_path ~get_params:Eliom_parameter.unit (fun () () -> Lwt.return process_closed_msg)) let fallback_global_service = Eliom_common.lazy_site_value_from_fun (fun () -> Comet.register_service ~path:comet_global_path ~get_params:Eliom_parameter.unit (fun () () -> Lwt.return (error_msg "request with no post parameters or there isn't any registered site comet channel"))) let new_id = make_cryptographic_safe_string (* ocsigenserver needs to be modified for this to be configurable: the connection is closed after a fixed time if the server does not send anything. By default it is 30 seconds *) let timeout = 20. module Stateless : sig type channel val create : ?name:string -> size:int -> string Lwt_stream.t -> channel val get_id : channel -> string val get_service : unit -> Ecb.comet_service val get_kind : newest:bool -> channel -> Ecb.stateless_kind val chan_id_of_string : string -> 'a Ecb.chan_id end = struct type channel_id = string module Dlist = Ocsigen_cache.Dlist type channel = { ch_id : channel_id; mutable ch_index : int; (* the number of messages already added to the channel *) ch_content : (string * int) Dlist.t; ch_wakeup : unit Lwt_condition.t; (* condition broadcasted when there is a new message *) } module Channel_hash = struct type t = channel let equal c1 c2 = c1.ch_id = c2.ch_id let hash c = Hashtbl.hash c.ch_id end module Weak_channel_table = Weak.Make(Channel_hash) let channels = Weak_channel_table.create 0 let find_channel = let dummy_channel = { ch_id = ""; ch_index = 0; ch_content = Dlist.create 1; ch_wakeup = Lwt_condition.create (); } in fun ch_id -> let dummy = { dummy_channel with ch_id = ch_id } in try Some (Weak_channel_table.find channels dummy) with | Not_found -> None let wakeup_waiters channel = Lwt_condition.broadcast channel.ch_wakeup () (* fill the channel with messages from the stream *) let run_channel channel stream = let channel' = Weak.create 1 in Weak.set channel' 0 (Some channel); let channel = channel' in (* hide non weak reference to be sure not to keep a strong reference *) let f msg = match Weak.get channel 0 with | None -> raise_lwt Not_found (* terminates the loop: remove reference on the stream, etc ... *) | Some channel -> channel.ch_index <- succ channel.ch_index; ignore (Dlist.add (msg,channel.ch_index) channel.ch_content: 'a option); wakeup_waiters channel; Lwt.return () in ignore (Lwt_stream.iter_s f stream:unit Lwt.t) let make_name name = "stateless:"^name let chan_id_of_string name = Ecb.chan_id_of_string (make_name name) let create ?(name=new_id ()) ~size stream = let name = make_name name in let channel = { ch_id = name; ch_index = 0; ch_content = Dlist.create size; ch_wakeup = Lwt_condition.create () } in run_channel channel stream; match find_channel name with | Some _ -> failwith (Printf.sprintf "can't create channel %s: a channel with the same name already exists" name) | None -> Weak_channel_table.add channels channel; channel let get_channel (ch_id,position) = match find_channel ch_id with | Some channel -> Left (channel,position) | None -> Right ch_id exception Finished of (channel_id * (string * int) Ecb.channel_data) list let queue_take channel last = try Dlist.fold (fun l (v,index) -> if index >= last then (channel.ch_id,Ecb.Data (v,index))::l else raise (Finished l)) [] channel.ch_content with | Finished l -> l let get_available_data = function | Right ch_id -> [ch_id,Ecb.Closed] | Left (channel,position) -> match position with (* the first request of the client should be with i = 1 *) (* when the client is requesting the newest data, only return one if he don't already have it *) | Ecb.Newest i when i > channel.ch_index -> [] | Ecb.Newest _ | Ecb.Last None -> (* initialisation of external newest channels *) (match Dlist.newest channel.ch_content with | None -> [] (* should not happen *) | Some node -> [channel.ch_id,Ecb.Data (Dlist.value node)]) (* when the client is requesting the data after index i return all data with index gretter or equal to i*) | Ecb.After i when i > channel.ch_index -> [] (* if the requested value is not in the queue anymore, tell the client that its request was dropped *) | Ecb.After i when i <= channel.ch_index - (Dlist.size channel.ch_content) -> [channel.ch_id,Ecb.Full] | Ecb.After i -> queue_take channel i | Ecb.Last (Some n) -> let i = channel.ch_index - (min (Dlist.size channel.ch_content) n) in queue_take channel i let has_data = function | Right _ -> true (* a channel was closed: need to tell it to the client *) | Left (channel,position) -> match position with | Ecb.Newest i when i > channel.ch_index -> false | Ecb.Newest i -> true | Ecb.After i when i > channel.ch_index -> false | Ecb.After i -> true | Ecb.Last n when (Dlist.size channel.ch_content) > 0 -> true | Ecb.Last n -> false let really_wait_data requests = let rec make_list = function | [] -> [] | (Left (channel,_))::q -> (Lwt_condition.wait channel.ch_wakeup)::(make_list q) | (Right _)::q -> assert false (* closed channels are considered to have data *) in Lwt.pick (make_list requests) let wait_data requests = if List.exists has_data requests then Lwt.return () else Lwt_unix.with_timeout timeout (fun () -> really_wait_data requests) let handle_request () = function | Ecb.Stateful _ -> failwith "attempting to request data on stateless service with a stateful request" | Ecb.Stateless requests -> let requests = List.map get_channel (Array.to_list requests) in lwt res = try_lwt lwt () = wait_data requests in Lwt.return (List.flatten (List.map get_available_data requests)) with | Lwt_unix.Timeout -> Lwt.return [] in Lwt.return (encode_global_downgoing res) let global_service = Eliom_common.lazy_site_value_from_fun (fun () -> Comet.register_post_service ~fallback:(Eliom_common.force_lazy_site_value fallback_global_service) ~post_params:Ecb.comet_request_param handle_request) let get_service () = Eliom_common.force_lazy_site_value global_service let get_id {ch_id} = ch_id let get_kind ~newest {ch_index} = if newest then Ecb.Newest_kind (ch_index + 1) else Ecb.After_kind (ch_index + 1) end module Stateful : (** String channels on wich is build the module Channel *) sig type t val create : ?scope:Eliom_common.client_process_scope -> ?name:chan_id -> string Ecb.channel_data Lwt_stream.t -> t val get_id : t -> string type comet_service = Ecb.comet_service val get_service : t -> comet_service val close_channel : t -> unit val wait_timeout : ?scope:Eliom_common.client_process_scope -> float -> unit Lwt.t end = struct type chan_id = string type comet_service = Ecb.comet_service type internal_comet_service = Ecb.internal_comet_service type end_request_waiters = unit Lwt.u type activity = | Active of end_request_waiters list (** There is currently a request from the client *) | Inactive of float (** The last request from the client completed at that time *) type handler = { hd_scope : Eliom_common.client_process_scope; (* id : int; pour tester que ce sont des service differents... *) mutable hd_active_streams : ( chan_id * ( string Ecb.channel_data Lwt_stream.t ) ) list; (** streams that are currently sent to client *) mutable hd_unregistered_streams : ( chan_id * ( string Ecb.channel_data Lwt_stream.t ) ) list; (** streams that are created on the server side, but client did not register *) mutable hd_registered_chan_id : chan_id list; (** the fusion of all the streams from hd_active_streams *) mutable hd_update_streams : unit Lwt.t; (** thread that wakeup when there are new active streams. *) mutable hd_update_streams_w : unit Lwt.u; hd_service : internal_comet_service; mutable hd_last : string * int; (** the last message sent to the client, if he sends a request with the same number, this message is immediately sent back.*) mutable hd_activity : activity; } exception Connection_closed let set_active handler = match handler.hd_activity with | Active _ -> () | Inactive _ -> handler.hd_activity <- Active [] let set_inactive handler = match handler.hd_activity with | Active l -> handler.hd_activity <- Inactive (Unix.gettimeofday ()); List.iter (fun waiter -> Lwt.wakeup waiter ()) l | Inactive _ -> () let update_inactive handler = match handler.hd_activity with | Active _ -> () | Inactive _ -> handler.hd_activity <- Inactive (Unix.gettimeofday ()) let wait_handler_timeout handler t = let rec run () = match handler.hd_activity with | Active l -> let waiter,waker = Lwt.task () in let t = lwt () = waiter in lwt () = Lwt_unix.sleep t in run () in handler.hd_activity <- Active (waker::l); t | Inactive inactive_time -> let now = Unix.gettimeofday () in if now -. inactive_time > t then Lwt.return () else lwt () = Lwt_unix.sleep (t -. (now -. inactive_time)) in run () in run () (** called when a connection is opened, it makes the other connection terminate with no data. That way there is at most one opened connection to the service. There are new connection opened when the client wants to listen to new channels for instance. *) let new_connection handler = let t,w = Lwt.task () in let wakener = handler.hd_update_streams_w in handler.hd_update_streams <- t; handler.hd_update_streams_w <- w; set_active handler; Lwt.wakeup_exn wakener New_connection (** called when a new channel is made active. It restarts the thread wainting for inputs ( wait_data ) such that it can receive the messages from the new channel *) let signal_update handler = let t,w = Lwt.task () in let wakener = handler.hd_update_streams_w in handler.hd_update_streams <- t; handler.hd_update_streams_w <- w; Lwt.wakeup wakener () let wait_streams streams = Lwt.pick (List.map (fun (_,s) -> Lwt_stream.peek s) streams) (** read up to [n] messages in the list of streams [streams] without blocking. *) let read_streams n streams = let rec aux acc n streams = match streams with | [] -> acc | (id,stream)::other_streams -> match n with | 0 -> acc | _ -> let l = Lwt_stream.get_available_up_to n stream in let l' = List.map (fun v -> id,v) l in let rest = n - (List.length l) in aux (l'@acc) rest other_streams in aux [] n streams (** wait for data on any channel that the client asks. It correcly handles new channels the server creates after that the client registered them *) let rec wait_data handler = Lwt.choose [ Lwt.protected (wait_streams handler.hd_active_streams) >>= ( fun _ -> Lwt.return `Data ); Lwt.protected (handler.hd_update_streams) >>= ( fun _ -> Lwt.return `Update ) ] >>= ( function | `Data -> Lwt.return () | `Update -> wait_data handler ) let launch_stream handler (chan_id,stream) = handler.hd_active_streams <- (chan_id,stream)::handler.hd_active_streams; signal_update handler let register_channel handler chan_id = OMsg.debug2 (Printf.sprintf "eliom: comet: register channel %s" chan_id); if not (List.mem_assoc chan_id handler.hd_active_streams) then try let stream = List.assoc chan_id handler.hd_unregistered_streams in handler.hd_unregistered_streams <- List.remove_assoc chan_id handler.hd_unregistered_streams; launch_stream handler (chan_id,stream) with | Not_found -> handler.hd_registered_chan_id <- chan_id::handler.hd_registered_chan_id let close_channel' handler chan_id = OMsg.debug2 (Printf.sprintf "eliom: comet: close channel %s" chan_id); handler.hd_active_streams <- List.remove_assoc chan_id handler.hd_active_streams; handler.hd_unregistered_streams <- List.remove_assoc chan_id handler.hd_unregistered_streams; handler.hd_registered_chan_id <- List.filter ((<>) chan_id) handler.hd_registered_chan_id; signal_update handler let wait_closed_connection () = let ri = Eliom_request_info.get_ri () in lwt () = ri.Ocsigen_extensions.ri_connection_closed in raise_lwt Connection_closed (* register the service handler.hd_service *) let run_handler handler = let f () req = match req with | Ecb.Stateless _ -> failwith "attempting to request data on stateful service with a stateless request" | Ecb.Stateful (Ecb.Request_data number) -> OMsg.debug2 (Printf.sprintf "eliom: comet: received request %i" number); (* if a new connection occurs for a service, we reply immediately to the previous with no data. *) new_connection handler; if snd handler.hd_last = number then Lwt.return (fst handler.hd_last) else Lwt.catch ( fun () -> Lwt_unix.with_timeout timeout (fun () -> lwt () = Lwt.choose [ wait_closed_connection (); wait_data handler ] in let messages = read_streams 100 handler.hd_active_streams in let message = encode_downgoing messages in handler.hd_last <- (message,number); set_inactive handler; Lwt.return message ) ) ( function | New_connection -> Lwt.return (encode_downgoing []) (* happens if an other connection has been opened on that service *) (* CCC in this case, it would be beter to return code 204: no content *) | Lwt_unix.Timeout -> set_inactive handler; Lwt.return timeout_msg | Connection_closed -> set_inactive handler; (* it doesn't matter what we do here *) raise_lwt Connection_closed | e -> set_inactive handler; Lwt.fail e ) | Ecb.Stateful (Ecb.Commands commands) -> update_inactive handler; List.iter (function | Ecb.Register channel -> register_channel handler channel | Ecb.Close channel -> close_channel' handler channel) (Array.to_list commands); (* command connections are replied immediately by an empty answer *) Lwt.return (encode_downgoing []) in Comet.register ~scope:handler.hd_scope ~service:handler.hd_service f (** For each scope there is a reference containing the handler. The reference itself are stocked in [handler_ref_table]. This table is never cleaned, but it is supposed that this won't be a problem as scope should be used in limited number *) (* as of now only `Client_process scope are handled: so we only stock scope_hierarchy *) type handler_ref_table = (Eliom_common.scope_hierarchy,handler option Eliom_reference.eref) Hashtbl.t let handler_ref_table : handler_ref_table = Hashtbl.create 1 (* this is a hack for the create function not to return 'a Lwt.t type: This is needed because bus and react create the channel at wrapping time, where it is impossible to block *) let get_ref eref = match Lwt.state (Eliom_reference.get eref) with | Lwt.Return v -> v | _ -> failwith "Eliom_comet: accessing channel references should not be blocking: this is an eliom bug" let set_ref eref v = match Lwt.state (Eliom_reference.set eref v) with | Lwt.Return () -> () | _ -> failwith "Eliom_comet: accessing channel references should not be blocking: this is an eliom bug" let get_handler_eref scope = let scope_hierarchy = Eliom_common_base.scope_hierarchy_of_scope scope in try Hashtbl.find handler_ref_table scope_hierarchy with | Not_found -> let eref = Eliom_reference.eref ~scope:(`Client_process scope_hierarchy) None in Hashtbl.add handler_ref_table scope_hierarchy eref; eref let get_handler scope = let eref = get_handler_eref scope in match get_ref eref with | Some t -> t | None -> begin let hd_service = (* CCC ajouter possibilité d'https *) Eliom_service.post_coservice ~fallback:(Eliom_common.force_lazy_site_value fallback_service) (*~name:"comet" (* CCC faut il mettre un nom ? *)*) ~post_params:Ecb.comet_request_param () in let hd_update_streams,hd_update_streams_w = Lwt.task () in let handler = { hd_scope = scope; hd_active_streams = []; hd_unregistered_streams = []; hd_registered_chan_id = []; hd_service; hd_update_streams; hd_update_streams_w; hd_last = "", -1; hd_activity = Inactive (Unix.gettimeofday ()); } in set_ref eref (Some handler); run_handler handler; handler end let wait_timeout ?(scope=Eliom_common.comet_client_process_scope) t = let hd = get_handler scope in wait_handler_timeout hd t type t = { ch_handler : handler; ch_id : chan_id; ch_stream : string Ecb.channel_data Lwt_stream.t; } let close_channel chan = close_channel' chan.ch_handler chan.ch_id let name_of_scope (scope:Eliom_common.user_scope) = let sp = Eliom_common.get_sp () in let name = Eliom_common.make_full_state_name ~sp ~secure:false (*VVV secure? *) ~scope in let pref = match scope with | `Session_group _ -> "sessiongroup:" | `Session _ -> "session:" | `Client_process _ -> "clientprocess:" in Eliom_common.make_full_cookie_name pref name let create ?(scope=Eliom_common.comet_client_process_scope) ?(name=new_id ()) stream = let name = (name_of_scope (scope:>Eliom_common.user_scope)) ^ name in let handler = get_handler scope in OMsg.debug2 (Printf.sprintf "eliom: comet: create channel %s" name); if List.mem name handler.hd_registered_chan_id then begin handler.hd_registered_chan_id <- List.filter ((<>) name) handler.hd_registered_chan_id; launch_stream handler (name,stream) end else handler.hd_unregistered_streams <- (name,stream)::handler.hd_unregistered_streams; { ch_handler = handler; ch_stream = stream; ch_id = name; } let get_id {ch_id} = ch_id let get_service chan = (chan.ch_handler.hd_service:>comet_service) end module Channel : sig type 'a t type comet_scope = [ Eliom_common.site_scope | Eliom_common.client_process_scope ] val create : ?scope:[< comet_scope ] -> ?name:string -> ?size:int -> 'a Lwt_stream.t -> 'a t val create_unlimited : ?scope:Eliom_common.client_process_scope -> ?name:string -> 'a Lwt_stream.t -> 'a t val create_newest : ?name:string -> 'a Lwt_stream.t -> 'a t val get_wrapped : 'a t -> 'a Ecb.wrapped_channel val external_channel : ?history:int -> ?newest:bool -> prefix:string -> name:string -> unit -> 'a t val wait_timeout : ?scope:Eliom_common.client_process_scope -> float -> unit Lwt.t end = struct type 'a channel = | Stateless of Stateless.channel | Stateless_newest of Stateless.channel | Stateful of Stateful.t | External of 'a Ecb.wrapped_channel type 'a t = { channel : 'a channel; channel_mark : 'a t Eliom_common.wrapper; } let get_wrapped t = match t.channel with | Stateful channel -> Ecb.Stateful_channel (Stateful.get_service channel, Ecb.chan_id_of_string (Stateful.get_id channel)) | Stateless channel -> Ecb.Stateless_channel (Stateless.get_service (), Ecb.chan_id_of_string (Stateless.get_id channel), Stateless.get_kind ~newest:false channel) | Stateless_newest channel -> Ecb.Stateless_channel (Stateless.get_service (), Ecb.chan_id_of_string (Stateless.get_id channel), Stateless.get_kind ~newest:true channel) | External wrapped -> wrapped let internal_wrap c = (get_wrapped c,Eliom_common.make_unwrapper Eliom_common.comet_channel_unwrap_id) let channel_mark () = Eliom_common.make_wrapper internal_wrap exception Halt (* TODO close on full *) let limit_stream ~size s = let open Lwt in let full = ref false in let closed = ref false in let count = ref 0 in let str, push = Lwt_stream.create () in let stopper,wake_stopper = wait () in let rec loop () = ( Lwt_stream.get s stopper ) >>= function | Some x -> if !count >= size then (full := true; ignore (Lwt_stream.get_available str); (* flush the channel *) return ()) else (incr count; push (Some ( Ecb.Data x )); loop ()) | None -> return () in ignore (loop ():'a Lwt.t); let res = Lwt_stream.from (fun () -> if !full then if !closed then return None else ( closed := true; return (Some Ecb.Full) ) else (decr count; Lwt_stream.get str)) in Gc.finalise (fun _ -> wakeup_exn wake_stopper Halt) res; res let marshal (v:'a) = let wrapped = Eliom_wrap.wrap v in let value : 'a Eliom_types.eliom_comet_data_type = wrapped in (Url.encode ~plus:false (Marshal.to_string value [])) let create_stateful_channel ?scope ?name stream = Stateful (Stateful.create ?scope ?name (Lwt_stream.map (function | Ecb.Closed -> OMsg.debug2 (Printf.sprintf "eliom: closed in stateful channels: this is an error: this should not be possible"); Ecb.Closed | Ecb.Full -> Ecb.Full | Ecb.Data s -> Ecb.Data (marshal s)) stream)) let create_stateless_channel ?name ~size stream = Stateless (Stateless.create ?name ~size (Lwt_stream.map marshal stream)) let create_stateless_newest_channel ?name stream = Stateless_newest (Stateless.create ?name ~size:1 (Lwt_stream.map marshal stream)) let create_stateful ?scope ?name ?(size=1000) stream = let stream = limit_stream ~size stream in { channel = create_stateful_channel ?scope ?name stream; channel_mark = channel_mark () } let create_unlimited ?scope ?name stream = let stream = Lwt_stream.map (fun x -> Ecb.Data x) stream in { channel = create_stateful_channel ?scope ?name stream; channel_mark = channel_mark () } let create_stateless ?name ?(size=1000) stream = { channel = create_stateless_channel ?name ~size stream; channel_mark = channel_mark () } let create_newest ?name stream = { channel = create_stateless_newest_channel ?name stream; channel_mark = channel_mark () } type comet_scope = [ Eliom_common.site_scope | Eliom_common.client_process_scope ] let create ?scope ?name ?(size=1000) stream = match scope with | None -> create_stateful ?name ~size stream | Some ((`Client_process n) as scope) -> create_stateful ~scope ?name ~size stream | Some `Site -> create_stateless ?name ~size stream let external_channel ?(history=1) ?(newest=false) ~prefix ~name () = let service = Eliom_service.external_post_service ~prefix ~path:comet_global_path ~get_params:Eliom_parameter.unit ~post_params:Ecb.comet_request_param () in let last = if newest then None else Some history in { channel = External (Ecb.Stateless_channel (service, Stateless.chan_id_of_string name, Ecb.Last_kind last)); channel_mark = channel_mark () } let wait_timeout = Stateful.wait_timeout end eliom-3.0.3/src/server/eliom_extension.ml0000644000000000000000000000327612062377521016655 0ustar0000000000000000(* Ocsigen * http://www.ocsigen.org * Copyright (C) 2008 Vincent Balat * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, with linking exception; * either version 2.1 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *) (*****************************************************************************) (*****************************************************************************) (** Run Ocsigen extensions that can access Eliom data *) (*****************************************************************************) (*****************************************************************************) let (>>=) = Lwt.bind type eliom_extension_sig = unit -> Ocsigen_extensions.answer Lwt.t let module_action : eliom_extension_sig ref = ref (fun _ -> failwith "Eliommod_extension") let register_eliom_extension f = module_action := f let get_eliom_extension () = !module_action let run_eliom_extension (fext : eliom_extension_sig) now info sitedata = let sp = Eliom_common.make_server_params sitedata info None None in Lwt.with_value Eliom_common.sp_key (Some sp) fext eliom-3.0.3/src/server/eliom_tools_common.mli0000644000000000000000000000555612062377521017525 0ustar0000000000000000(* Ocsigen * Copyright (C) 2005 Vincent Balat * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, with linking exception; * either version 2.1 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *) open Eliom_service open Eliom_parameter open Eliom_state (** {2 Menus } *) type ('a, 'b, 'c) one_page = (unit, unit, 'a, [ `WithoutSuffix ], unit, unit, 'b, 'c) service constraint 'c = [< Eliom_registration.non_caml_service ] type get_page = (Eliom_service.get_service_kind, Eliom_service.registrable, Eliom_registration.non_caml_service) one_page (** {2 Hierchical sites } *) type ('a, 'b, 'c) hierarchical_site_item = | Disabled | Site_tree of ('a, 'b, 'c) hierarchical_site constraint 'b = [< Eliom_service.registrable ] and ('a, 'b) main_page = | Main_page of ('a, 'b, Eliom_registration.non_caml_service) one_page | Default_page of ('a, 'b, Eliom_registration.non_caml_service) one_page | Not_clickable constraint 'b = [< Eliom_service.registrable ] and ('a, 'b, 'c) hierarchical_site = (('a, 'b) main_page * ('c * ('a, 'b, 'c) hierarchical_site_item) list) constraint 'b = [< Eliom_service.registrable ] (** The type of hierarchical sites. A hierarchical site is a pair (main page, subpages). The difference between [Main_page], [Default_page] and [Not_clickable] is a bit subtle: - [Main_page] is when you want to create a main page for your subsite. All the subpages are subsections of that page. - [Default_page] is like [Main_page] but is not taken into account for computing which is the current page in the menu. Use it for example when there is no main page, but you want one of the subpages to be the default page for your subsite. The service you use as default page must appear another time in the subtree! - [Not_clickable] is when you do not want the menu entry to be a link but you want subpages. Each subpage is defined by the text to be displayed in menus and a [hierarchical_site_item]. If the latter is [Disabled], the menu entry is disabled. *) (**/**) val menu_class : string val last_class : string val current_class : string val current_path_class : string val disabled_class : string val first_class : string val level_class : string eliom-3.0.3/src/server/eliom_mkreg.mli0000644000000000000000000000435212062377521016113 0ustar0000000000000000(* Ocsigen * http://www.ocsigen.org * Module Eliom_mkreg * Copyright (C) 2007 Vincent Balat * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, with linking exception; * either version 2.1 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *) (** This module defines the functor to use to creates modules generating functions to register services for your own types of pages. It is used for example in {!Eliom_registration}. *) open Eliom_lib open Ocsigen_extensions open Eliom_state open Eliom_parameter open Eliom_service (** {2 Creating modules to register services for one type of pages} *) module type REG_PARAM = "sigs/eliom_reg_param.mli" module MakeRegister(Pages: REG_PARAM) : sig include "sigs/eliom_reg.mli" subst type page := Pages.page and type options := Pages.options and type return := Pages.return and type result := Pages.result end (** {2 Creating modules to register services for one type of parametrised pages} *) module type REG_PARAM_ALPHA_RETURN = sig type ('a, 'b) page type 'a return type ('a, 'b) result include "sigs/eliom_reg_param.mli" subst type page := ('a, 'b) page and type return := 'b return and type result := ('a, 'b) result end module MakeRegister_AlphaReturn(Pages: REG_PARAM_ALPHA_RETURN) : sig include "sigs/eliom_reg_alpha_return.mli" subst type page := ('a, 'b) Pages.page and type options := Pages.options and type return := 'b Pages.return and type result := ('a, 'b) Pages.result end (**/**) val suffix_redir_uri_key : string Polytables.key eliom-3.0.3/src/server/eliom_parameter.ml0000644000000000000000000004406212062377521016617 0ustar0000000000000000(* Ocsigen * http://www.ocsigen.org * Module eliom_parameter.ml * Copyright (C) 2007 Vincent Balat * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, with linking exception; * either version 2.1 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *) open Eliom_lib include Eliom_parameter_base type suff = [ `WithoutSuffix | `WithSuffix | `Endsuffix ] open Ocsigen_extensions type anon_params_type = int let anonymise_params_type (t : ('a, 'b, 'c) params_type) : anon_params_type = Hashtbl.hash_param 1000 1000 t (*****************************************************************************) (* types available only on server side (no pcre on browser) *) let regexp reg dest ~to_string n = user_type (fun s -> match Netstring_pcre.string_match reg s 0 with | Some _ -> begin try Ocsigen_extensions.replace_user_dir reg (Ocsigen_extensions.parse_user_dir dest) s with Ocsigen_extensions.NoSuchUser -> raise (Failure "User does not exist") end | _ -> raise (Failure "Regexp not matching")) to_string n let all_suffix_regexp reg dest ~(to_string : 'a -> string) (n : string) : (string, [`Endsuffix], [ `One of string ] param_name) params_type = all_suffix_user (fun s -> match Netstring_pcre.string_match reg s 0 with | Some _ -> begin try Ocsigen_extensions.replace_user_dir reg (Ocsigen_extensions.parse_user_dir dest) s with Ocsigen_extensions.NoSuchUser -> raise (Failure "User does not exist") end | _ -> raise (Failure "Regexp not matching")) to_string n (******************************************************************) (* The following function reconstructs the value of parameters from expected type and GET or POST parameters *) type 'a res_reconstr_param = | Res_ of ('a * (string * string) list * (string * file_info) list) | Errors_ of ((string * string * exn) list * (string * string) list * (string * file_info) list) let reconstruct_params_ req (typ : ('a, [<`WithSuffix|`WithoutSuffix], 'b) params_type) params files nosuffixversion urlsuffix : 'a = let rec parse_suffix typ suff = match (typ, suff) with | TESuffix _, l -> Obj.magic l, [] (*VVV encode=false? *) | TESuffixs _, l -> Obj.magic (Url.string_of_url_path ~encode:false l), [] | TESuffixu (_, of_string, to_string), l -> (try (*VVV encode=false? *) Obj.magic (of_string (Url.string_of_url_path ~encode:false l)), [] with e -> raise (Eliom_common.Eliom_Typing_Error [("", e)])) | TOption t, [] -> Obj.magic None, [] | TOption t, ""::l -> Obj.magic None, l | TOption t, l -> let r, ll = parse_suffix t l in Obj.magic (Some r), ll | TNEOption t, [] -> Obj.magic None, [] | TNEOption t, ""::l -> Obj.magic None, l | TNEOption t, l -> let r, ll = parse_suffix t l in Obj.magic (Some r), ll | TList _, [] | TSet _, [] -> Obj.magic [], [] | TList (_, t), l | TSet t, l -> let b, l = Obj.magic (parse_suffix t l) in (match l with | [] -> raise Eliom_common.Eliom_Wrong_parameter | [""] -> Obj.magic [b], [] | _ -> let c, l = Obj.magic (parse_suffix typ l) in Obj.magic (b::c), l) | TProd (TList _, _), _ | TProd (TSet _, _), _ -> failwith "Lists or sets in suffixes must be last parameters" | TProd (t1, t2), l -> (match parse_suffix t1 l with | _, [] -> raise Eliom_common.Eliom_Wrong_parameter | r, l -> let rr, ll = parse_suffix t2 l in Obj.magic (r, rr), ll) | TString _, v::l -> Obj.magic v, l | TInt name, v::l -> (try Obj.magic (int_of_string v), l with e -> raise (Eliom_common.Eliom_Typing_Error [("", e)])) | TInt32 name, v::l -> (try Obj.magic (Int32.of_string v), l with e -> raise (Eliom_common.Eliom_Typing_Error [("", e)])) | TInt64 name, v::l -> (try Obj.magic (Int64.of_string v), l with e -> raise (Eliom_common.Eliom_Typing_Error [("", e)])) | TFloat name, v::l -> (try Obj.magic (float_of_string v), l with e -> raise (Eliom_common.Eliom_Typing_Error [("", e)])) | TUnit, v::l -> (if v="" then Obj.magic (), l else raise Eliom_common.Eliom_Wrong_parameter) | TBool name, v::l -> (try Obj.magic (bool_of_string v), l with e -> raise (Eliom_common.Eliom_Typing_Error [("", e)])) | TUserType (name, of_string, string_of), v::l -> (try Obj.magic (of_string v), l with e -> raise (Eliom_common.Eliom_Typing_Error [("", e)])) | TTypeFilter (t, None), _ -> failwith "Type filter without filter" | TTypeFilter (t, Some check), l -> let (v, _) as a = parse_suffix t l in check v; a | TConst value, v::l -> if v = value then Obj.magic (), l else raise Eliom_common.Eliom_Wrong_parameter | TSum (t1, t2), l -> (try parse_suffix t1 l with Eliom_common.Eliom_Wrong_parameter -> parse_suffix t2 l) | TCoord _, l -> (match Obj.magic (parse_suffix (TInt "") l) with | _, [] -> raise Eliom_common.Eliom_Wrong_parameter | r, l -> let rr, ll = Obj.magic (parse_suffix (TInt "") l) in Obj.magic {abscissa = r; ordinate=rr}, ll) | TCoordv (t, _), l -> let a, l = parse_suffix t l in (match Obj.magic (parse_suffix (TInt "") l) with | _, [] -> raise Eliom_common.Eliom_Wrong_parameter | r, l -> let rr, ll = Obj.magic (parse_suffix (TInt "") l) in Obj.magic (a, {abscissa = r; ordinate=rr}), ll) | TNLParams _, _ -> failwith "It is not possible to have non localized parameters in suffix" | TJson (_, Some typ), v::l -> Deriving_Json.from_string typ v, l | TJson (_, None), v::l -> assert false (* client side only *) | TAny _, _ -> failwith "It is not possible to use any in suffix. May be try with all_suffix ?" | _ -> raise Eliom_common.Eliom_Wrong_parameter in let aux2 typ params = let rec aux_list t params files name pref suff = let rec aa i lp fl pref = let rec end_of_list len = function | [] -> true | (a, _)::_ when (try (String.sub a 0 len) = pref with Invalid_argument _ -> false) -> false | _::l -> end_of_list len l in if end_of_list (String.length pref) lp then Res_ ((Obj.magic []), lp, fl) else match aux t lp fl pref (make_list_suffix i) with | Res_ (v, lp2, f) -> (match aa (i+1) lp2 f pref with | Res_ (v2,lp3,f2) -> Res_ ((Obj.magic (v::v2)),lp3,f2) | err -> err) | Errors_ (errs, l, f) -> Errors_ (errs, l, f) in aa 0 params files (pref^name^suff^".") and aux (typ : ('a, [<`WithSuffix|`WithoutSuffix|`Endsuffix], 'b) params_type) params files pref suff : 'a res_reconstr_param = match typ with | TNLParams (_, _, _, t) -> aux t params files pref suff | TProd (t1, t2) -> (match aux t1 params files pref suff with | Res_ (v1, l1, f) -> (match aux t2 l1 f pref suff with | Res_ (v2, l2, f2) -> Res_ ((Obj.magic (v1, v2)), l2, f2) | err -> err) | Errors_ (errs, l, f) -> (match aux t2 l f pref suff with | Res_ (_, ll, ff) -> Errors_ (errs, ll, ff) | Errors_ (errs2, ll, ff) -> Errors_ ((errs2@errs), ll, ff))) | TOption t -> (try (match aux t params files pref suff with | Res_ (v, l, f) -> Res_ ((Obj.magic (Some v)), l, f) | err -> err) with Not_found -> Res_ ((Obj.magic None), params, files)) | TNEOption t -> (try (match aux t params files pref suff with | Res_ (v, l, f) -> if (Obj.tag (Obj.repr v) = Obj.string_tag) && (String.length (Obj.magic v : string) = 0) (* Is the value an empty string? *) then Res_ ((Obj.magic None), l, f) else Res_ ((Obj.magic (Some v)), l, f) | Errors_ ([(_,"",_)], ll, ff) -> Res_ ((Obj.magic None), ll, ff) | err -> err) with Not_found -> Res_ ((Obj.magic None), params, files)) | TBool name -> (try let v,l = (List.assoc_remove (pref^name^suff) params) in Res_ ((Obj.magic true),l,files) with Not_found -> Res_ ((Obj.magic false), params, files)) | TList (n, t) -> Obj.magic (aux_list t params files n pref suff) | TSet t -> let rec aux_set params files = try match aux t params files pref suff with | Res_ (vv, ll, ff) -> (match aux_set ll ff with | Res_ (vv2, ll2, ff2) -> Res_ (Obj.magic (vv::vv2), ll2, ff2) | err -> err) | Errors_ (errs, ll, ff) -> (match aux_set ll ff with | Res_ (_, ll2, ff2) -> Errors_ (errs, ll2, ff2) | Errors_ (errs2, ll2, ff2) -> Errors_ (errs@errs2, ll2, ff2)) with Not_found -> Res_ (Obj.magic [], params, files) in Obj.magic (aux_set params files) | TSum (t1, t2) -> (try match aux t1 params files pref suff with | Res_ (v,l,files) -> Res_ ((Obj.magic (Inj1 v)),l,files) | err -> err with Not_found -> (match aux t2 params files pref suff with | Res_ (v,l,files) -> Res_ ((Obj.magic (Inj2 v)),l,files) | err -> err)) | TString name -> let v,l = List.assoc_remove (pref^name^suff) params in Res_ ((Obj.magic v),l,files) | TInt name -> let v,l = (List.assoc_remove (pref^name^suff) params) in (try (Res_ ((Obj.magic (int_of_string v)), l, files)) with e -> Errors_ ([(pref^name^suff),v,e], l, files)) | TInt32 name -> let v,l = (List.assoc_remove (pref^name^suff) params) in (try (Res_ ((Obj.magic (Int32.of_string v)),l,files)) with e -> Errors_ ([(pref^name^suff),v,e], l, files)) | TInt64 name -> let v,l = (List.assoc_remove (pref^name^suff) params) in (try (Res_ ((Obj.magic (Int64.of_string v)),l,files)) with e -> Errors_ ([(pref^name^suff),v,e], l, files)) | TFloat name -> let v,l = (List.assoc_remove (pref^name^suff) params) in (try (Res_ ((Obj.magic (float_of_string v)),l,files)) with e -> Errors_ ([(pref^name^suff),v,e], l, files)) | TFile name -> let v,f = List.assoc_remove (pref^name^suff) files in Res_ ((Obj.magic v), params, f) | TCoord name -> let r1 = let v, l = (List.assoc_remove (pref^name^suff^".x") params) in (try (Res_ ((int_of_string v), l, files)) with e -> Errors_ ([(pref^name^suff^".x"), v, e], l, files)) in (match r1 with | Res_ (x1, l1, f) -> let v, l = (List.assoc_remove (pref^name^suff^".y") l1) in (try (Res_ ( (Obj.magic {abscissa= x1; ordinate= int_of_string v}), l, f)) with e -> Errors_ ([(pref^name^suff^".y"), v, e], l, f)) | Errors_ (errs, l1, f) -> let v, l = (List.assoc_remove (pref^name^suff^".y") l1) in (try ignore (int_of_string v); Errors_ (errs, l, f) with e -> Errors_ (((pref^name^suff^".y"), v, e)::errs, l, f))) | TCoordv (t, name) -> aux (TProd (t, TCoord name)) params files pref suff | TUserType (name, of_string, string_of) -> let v,l = (List.assoc_remove (pref^name^suff) params) in (try (Res_ ((Obj.magic (of_string v)),l,files)) with e -> Errors_ ([(pref^name^suff),v,e], l, files)) | TTypeFilter (t, None) -> failwith "Type filter without filter" | TTypeFilter (t, Some check) -> (match aux t params files pref suff with | Res_ (v, l, files) as a -> (try check v; a with e -> Errors_ (["","<>",e], l, files)) | a -> a) | TUnit -> Res_ ((Obj.magic ()), params, files) | TAny -> Res_ ((Obj.magic params), [], files) | TConst _ -> Res_ ((Obj.magic ()), params, files) | TESuffix n -> let v,l = List.assoc_remove n params in (* cannot have prefix or suffix *) Res_ ((Obj.magic (Neturl.split_path v)), l, files) | TESuffixs n -> let v,l = List.assoc_remove n params in (* cannot have prefix or suffix *) Res_ ((Obj.magic v), l, files) | TESuffixu (n, of_string, to_string) -> let v,l = List.assoc_remove n params in (* cannot have prefix or suffix *) (try Res_ ((Obj.magic (of_string v)), l, files) with e -> Errors_ ([(pref^n^suff), v, e], l, files)) | TSuffix (_, s) -> (match urlsuffix with | None -> if nosuffixversion (* the special page name "nosuffix" is present *) then aux s params files pref suff else raise Eliom_common.Eliom_Wrong_parameter | Some urlsuffix -> (match parse_suffix s urlsuffix with | p, [] -> Res_ (p, params, files) | _ -> raise Eliom_common.Eliom_Wrong_parameter)) | TJson (name, Some typ) -> let v,l = List.assoc_remove (pref^name^suff) params in Res_ ((of_json ~typ v),l,files) | TJson (name, None) -> assert false (* Never unmarshal server side without type! *) | TRaw_post_data -> raise Eliom_common.Eliom_Wrong_parameter in match Obj.magic (aux typ params files "" "") with | Res_ (v, l, files) -> if (l, files) = ([], []) then v else raise Eliom_common.Eliom_Wrong_parameter | Errors_ (errs, l, files) -> if (l, files) = ([], []) then raise (Eliom_common.Eliom_Typing_Error (List.map (fun (v,l,e) -> (v,e)) errs)) else raise Eliom_common.Eliom_Wrong_parameter in try Obj.magic (aux2 typ params) with | Not_found -> raise Eliom_common.Eliom_Wrong_parameter let reconstruct_params ~sp typ params files nosuffixversion urlsuffix = match typ, params, files with | TRaw_post_data, None, None -> let ri = Eliom_request_info.get_ri_sp sp in Lwt.return (Obj.magic (ri.Ocsigen_extensions.ri_content_type, ri.Ocsigen_extensions.ri_http_frame.Ocsigen_http_frame.frame_content)) | _, None, None -> (try Lwt.return (reconstruct_params_ sp.Eliom_common.sp_request typ [] [] nosuffixversion urlsuffix) with e -> Lwt.fail e) | _, Some params, Some files -> params >>= fun params -> files >>= fun files -> (try Lwt.return (reconstruct_params_ sp.Eliom_common.sp_request typ params files nosuffixversion urlsuffix) with e -> Lwt.fail e) | _ -> Lwt.fail Eliom_common.Eliom_Wrong_parameter (*****************************************************************************) (* Non localized parameters *) let get_non_localized_parameters params getorpost ~sp (name, _, keys, paramtype) = (* non localized parameters are parsed only once, and cached in request_cache *) let key = getorpost keys in (try (* first, look in cache: *) Polytables.get ~table:sp.Eliom_common.sp_request.Ocsigen_extensions.request_info.Ocsigen_extensions.ri_request_cache ~key with Not_found -> let p = try Some (let params = String.Table.find name params in reconstruct_params_ sp.Eliom_common.sp_request paramtype params [] false None) with Eliom_common.Eliom_Wrong_parameter | Not_found -> None in (* add in cache: *) Polytables.set ~table:sp.Eliom_common.sp_request.Ocsigen_extensions.request_info.Ocsigen_extensions.ri_request_cache ~key ~value:p; p) let get_non_localized_get_parameters p = let sp = Eliom_common.get_sp () in get_non_localized_parameters sp.Eliom_common.sp_si.Eliom_common.si_nl_get_params fst ~sp p let get_non_localized_post_parameters p = let sp = Eliom_common.get_sp () in get_non_localized_parameters sp.Eliom_common.sp_si.Eliom_common.si_nl_post_params snd ~sp p eliom-3.0.3/src/server/eliom_content_core.ml0000644000000000000000000005745112062377521017327 0ustar0000000000000000(* Ocsigen * http://www.ocsigen.org * Copyright (C) 2012 Vincent Balat, Benedikt Becker * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, with linking exception; * either version 2.1 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *) open Eliom_lib (* This the core of [Eliom_content] without its dependencies to [Eliom_service] et al. Its name is not [Eliom_content_base] because this would suggest the sharing between server and client. *) (*****************************************************************************) module Xml = struct include RawXML type econtent = | Empty | Comment of string | EncodedPCDATA of string | PCDATA of string | Entity of string | Leaf of ename * attrib list | Node of ename * attrib list * elt list and recontent = | RELazy of econtent Eliom_lazy.request | RE of econtent and elt' = { recontent : recontent; node_id : node_id; unwrapper_mark: Eliom_wrap.unwrapper; } (** Values of type [elt] are wrapped values of type [elt']. *) and elt = { elt : elt'; wrapper_mark : elt Eliom_wrap.wrapper } let content { elt } = match elt.recontent with | RE e -> e | RELazy e -> Eliom_lazy.force e module Node_id_set = Set.Make (struct type t = node_id let compare : t -> t -> int = compare end) let node_ids_in_content = ref Node_id_set.empty let wrapper_mark = Eliom_wrap.create_wrapper (fun { elt } -> if Node_id_set.mem elt.node_id !node_ids_in_content then { elt with recontent = RE Empty } else elt) let wrap page value = let node_ids = ref [] in let rec collect_node_ids ({ elt = { node_id }} as elt) = if node_id <> NoId then node_ids := node_id :: !node_ids; match content elt with | Empty | Comment _ | EncodedPCDATA _ | PCDATA _ | Entity _ | Leaf _ -> () | Node (_, _, children) -> List.iter collect_node_ids children in collect_node_ids page; node_ids_in_content := List.fold_right Node_id_set.add !node_ids Node_id_set.empty; let res = Eliom_wrap.wrap value in node_ids_in_content := Node_id_set.empty; res let rcontent { elt } = elt.recontent let get_node_id { elt } = elt.node_id let tyxml_unwrap_id = Eliom_wrap.id_of_int tyxml_unwrap_id_int let make elt = { elt = { recontent = RE elt; node_id = NoId; unwrapper_mark = Eliom_wrap.create_unwrapper tyxml_unwrap_id }; wrapper_mark } let make_lazy elt = { elt = { recontent = RELazy elt; node_id = NoId; unwrapper_mark = Eliom_wrap.create_unwrapper tyxml_unwrap_id }; wrapper_mark } let empty () = make Empty let comment c = make (Comment c) let pcdata d = make (PCDATA d) let encodedpcdata d = make (EncodedPCDATA d) let entity e = make (Entity e) let leaf ?(a = []) name = make (Leaf (name, a)) let node ?(a = []) name children = make (Node (name, a, children)) let lazy_node ?(a = []) name children = make_lazy (Eliom_lazy.from_fun (fun () -> (Node (name, a, Eliom_lazy.force children)))) let caml_event_handler cf = let crypto = make_cryptographic_safe_string () in CE_registered_closure (crypto, Eliom_lib.client_value_server_repr cf) let event_handler cf = Caml (caml_event_handler cf) let cdata s = (* GK *) (* For security reasons, we do not allow "]]>" inside CDATA (as this string is to be considered as the end of the cdata) *) let s' = "\n") "" s) ^"\n]]>\n" in encodedpcdata s' let cdata_script s = (* GK *) (* For security reasons, we do not allow "]]>" inside CDATA (as this string is to be considered as the end of the cdata) *) let s' = "\n//") "" s) ^"\n//]]>\n" in encodedpcdata s' let cdata_style s = (* GK *) (* For security reasons, we do not allow "]]>" inside CDATA (as this string is to be considered as the end of the cdata) *) let s' = "\n/* ") "" s) ^"\n/* ]]> */\n" in encodedpcdata s' let make_node_name ~global () = (if global then "global_" else "") ^ "server_" ^ make_cryptographic_safe_string () let make_process_node ?(id = make_node_name ~global:true ()) elt' = { elt' with elt = { elt'.elt with node_id = ProcessId id } } let make_request_node elt' = { elt' with elt = { elt'.elt with node_id = RequestId (make_node_name ~global:false ()) } } (** Ref tree *) let cons_attrib att acc = match racontent att with | RACamlEventHandler (CE_registered_closure (closure_id, cv)) -> ClosureMap.add closure_id cv acc | _ -> acc let make_event_handler_table elt = let rec aux closure_acc elt = let make attribs = List.fold_right cons_attrib attribs closure_acc in match content elt with | Empty | EncodedPCDATA _ | PCDATA _ | Entity _ | Comment _ -> closure_acc | Leaf (_, attribs) -> make attribs | Node (_, attribs, elts) -> List.fold_left aux (make attribs) elts in aux ClosureMap.empty elt let set_classes node_id = function | Empty | Comment _ | EncodedPCDATA _ | PCDATA _ | Entity _ as e -> e | Leaf (ename, attribs) -> Leaf (ename, filter_class_attribs node_id attribs) | Node (ename, attribs, sons) -> Node (ename, filter_class_attribs node_id attribs, sons) let content { elt } = let c = match elt.recontent with | RE e -> e | RELazy e -> Eliom_lazy.force e in set_classes elt.node_id c end module Eliom_xml = Xml module Svg = struct module D = Svg_f.Make(struct include Xml let make elt = make_request_node (make elt) let make_lazy elt = make_request_node (make_lazy elt) let empty () = make Empty let comment c = make (Comment c) let pcdata d = make (PCDATA d) let encodedpcdata d = make (EncodedPCDATA d) let entity e = make (Entity e) let leaf ?(a = []) name = make (Leaf (name, a)) let node ?(a = []) name children = make (Node (name, a, children)) let lazy_node ?(a = []) name children = make_lazy (Eliom_lazy.from_fun (fun () -> (Node (name, a, Eliom_lazy.force children)))) end) module F = Svg_f.Make(Xml) type +'a elt = 'a F.elt type +'a attrib = 'a F.attrib type uri = F.uri module Id = struct type 'a id = string (* FIXME invariant type parameter ? *) let new_elt_id: ?global:bool -> unit -> 'a id = fun ?(global=true) () -> Xml.make_node_name ~global () let create_named_elt ~(id : 'a id) elt = D.tot (Xml.make_process_node ~id (D.toelt elt)) let create_global_elt elt = D.tot (Xml.make_process_node (D.toelt elt)) end module Printer = Xml_print.Make_typed_simple(Xml)(F) end module Html5 = struct module D = struct (* This is [Eliom_content.Xml] adapted such that request nodes are produced *) module Xml' = struct include Eliom_xml let make elt = make_request_node (make elt) let make_lazy elt = make_request_node (make_lazy elt) let empty () = make Empty let comment c = make (Comment c) let pcdata d = make (PCDATA d) let encodedpcdata d = make (EncodedPCDATA d) let entity e = make (Entity e) let leaf ?(a = []) name = make (Leaf (name, a)) let node ?(a = []) name children = make (Node (name, a, children)) let lazy_node ?(a = []) name children = make_lazy (Eliom_lazy.from_fun (fun () -> (Node (name, a, Eliom_lazy.force children)))) end module Raw = Html5_f.Make(Xml')(Svg.D) include Raw type ('a, 'b, 'c) lazy_plus = ?a: (('a attrib) list) -> 'b elt Eliom_lazy.request -> ('b elt) list Eliom_lazy.request -> 'c elt let lazy_form ?(a = []) elt1 elts = tot (Xml'.lazy_node ~a:(to_xmlattribs a) "form" (Eliom_lazy.from_fun (fun () -> toelt (Eliom_lazy.force elt1) :: toeltl (Eliom_lazy.force elts)))) let a_onabort ev = Raw.a_onabort (Eliom_xml.event_handler ev) let a_onafterprint ev = Raw.a_onafterprint (Eliom_xml.event_handler ev) let a_onbeforeprint ev = Raw.a_onbeforeprint (Eliom_xml.event_handler ev) let a_onbeforeunload ev = Raw.a_onbeforeunload (Eliom_xml.event_handler ev) let a_onblur ev = Raw.a_onblur (Eliom_xml.event_handler ev) let a_oncanplay ev = Raw.a_oncanplay (Eliom_xml.event_handler ev) let a_oncanplaythrough ev = Raw.a_oncanplaythrough (Eliom_xml.event_handler ev) let a_onchange ev = Raw.a_onchange (Eliom_xml.event_handler ev) let a_onclick (ev : (Dom_html.mouseEvent Js.t -> unit) Eliom_lib.client_value) = Raw.a_onclick (Eliom_xml.event_handler (Obj.magic ev)) (* Typed by the syntax extension. *) let a_oncontextmenu (ev : (Dom_html.mouseEvent Js.t -> unit) Eliom_lib.client_value) = Raw.a_oncontextmenu (Eliom_xml.event_handler (Obj.magic ev)) (* Typed with the syntax extension *) let a_ondblclick (ev : (Dom_html.mouseEvent Js.t -> unit) Eliom_lib.client_value) = Raw.a_ondblclick (Eliom_xml.event_handler (Obj.magic ev)) (* Typed with the syntax extension *) let a_ondrag (ev : (Dom_html.mouseEvent Js.t -> unit) Eliom_lib.client_value) = Raw.a_ondrag (Eliom_xml.event_handler (Obj.magic ev)) (* Typed with the syntax extension *) let a_ondragend (ev : (Dom_html.mouseEvent Js.t -> unit) Eliom_lib.client_value) = Raw.a_ondragend (Eliom_xml.event_handler (Obj.magic ev)) (* Typed with the syntax extension *) let a_ondragenter (ev : (Dom_html.mouseEvent Js.t -> unit) Eliom_lib.client_value) = Raw.a_ondragenter (Eliom_xml.event_handler (Obj.magic ev)) (* Typed with the syntax extension *) let a_ondragleave (ev : (Dom_html.mouseEvent Js.t -> unit) Eliom_lib.client_value) = Raw.a_ondragleave (Eliom_xml.event_handler (Obj.magic ev)) (* Typed with the syntax extension *) let a_ondragover (ev : (Dom_html.mouseEvent Js.t -> unit) Eliom_lib.client_value) = Raw.a_ondragover (Eliom_xml.event_handler (Obj.magic ev)) (* Typed with the syntax extension *) let a_ondragstart (ev : (Dom_html.mouseEvent Js.t -> unit) Eliom_lib.client_value) = Raw.a_ondragstart (Eliom_xml.event_handler (Obj.magic ev)) (* Typed with the syntax extension *) let a_ondrop (ev : (Dom_html.mouseEvent Js.t -> unit) Eliom_lib.client_value) = Raw.a_ondrop (Eliom_xml.event_handler (Obj.magic ev)) (* Typed with the syntax extension *) let a_ondurationchange ev = Raw.a_ondurationchange (Eliom_xml.event_handler ev) let a_onemptied ev = Raw.a_onemptied (Eliom_xml.event_handler ev) let a_onended ev = Raw.a_onended (Eliom_xml.event_handler ev) let a_onerror ev = Raw.a_onerror (Eliom_xml.event_handler ev) let a_onfocus ev = Raw.a_onfocus (Eliom_xml.event_handler ev) let a_onformchange ev = Raw.a_onformchange (Eliom_xml.event_handler ev) let a_onforminput ev = Raw.a_onforminput (Eliom_xml.event_handler ev) let a_onhashchange ev = Raw.a_onhashchange (Eliom_xml.event_handler ev) let a_oninput ev = Raw.a_oninput (Eliom_xml.event_handler ev) let a_oninvalid ev = Raw.a_oninvalid (Eliom_xml.event_handler ev) let a_onmousedown (ev : (Dom_html.mouseEvent Js.t -> unit) Eliom_lib.client_value) = Raw.a_onmousedown (Eliom_xml.event_handler (Obj.magic ev)) (* Typed with the syntax extension *) let a_onmouseup (ev : (Dom_html.mouseEvent Js.t -> unit) Eliom_lib.client_value) = Raw.a_onmouseup (Eliom_xml.event_handler (Obj.magic ev)) (* Typed with the syntax extension *) let a_onmouseover (ev : (Dom_html.mouseEvent Js.t -> unit) Eliom_lib.client_value) = Raw.a_onmouseover (Eliom_xml.event_handler (Obj.magic ev)) (* Typed with the syntax extension *) let a_onmousemove (ev : (Dom_html.mouseEvent Js.t -> unit) Eliom_lib.client_value) = Raw.a_onmousemove (Eliom_xml.event_handler (Obj.magic ev)) (* Typed with the syntax extension *) let a_onmouseout (ev : (Dom_html.mouseEvent Js.t -> unit) Eliom_lib.client_value) = Raw.a_onmouseout (Eliom_xml.event_handler (Obj.magic ev)) (* Typed with the syntax extension *) let a_onmousewheel ev = Raw.a_onmousewheel (Eliom_xml.event_handler ev) let a_onoffline ev = Raw.a_onoffline (Eliom_xml.event_handler ev) let a_ononline ev = Raw.a_ononline (Eliom_xml.event_handler ev) let a_onpause ev = Raw.a_onpause (Eliom_xml.event_handler ev) let a_onplay ev = Raw.a_onplay (Eliom_xml.event_handler ev) let a_onplaying ev = Raw.a_onplaying (Eliom_xml.event_handler ev) let a_onpagehide ev = Raw.a_onpagehide (Eliom_xml.event_handler ev) let a_onpageshow ev = Raw.a_onpageshow (Eliom_xml.event_handler ev) let a_onpopstate ev = Raw.a_onpopstate (Eliom_xml.event_handler ev) let a_onprogress ev = Raw.a_onprogress (Eliom_xml.event_handler ev) let a_onratechange ev = Raw.a_onratechange (Eliom_xml.event_handler ev) let a_onreadystatechange ev = Raw.a_onreadystatechange (Eliom_xml.event_handler ev) let a_onredo ev = Raw.a_onredo (Eliom_xml.event_handler ev) let a_onresize ev = Raw.a_onresize (Eliom_xml.event_handler ev) let a_onscroll ev = Raw.a_onscroll (Eliom_xml.event_handler ev) let a_onseeked ev = Raw.a_onseeked (Eliom_xml.event_handler ev) let a_onseeking ev = Raw.a_onseeking (Eliom_xml.event_handler ev) let a_onselect ev = Raw.a_onselect (Eliom_xml.event_handler ev) let a_onshow ev = Raw.a_onshow (Eliom_xml.event_handler ev) let a_onstalled ev = Raw.a_onstalled (Eliom_xml.event_handler ev) let a_onstorage ev = Raw.a_onstorage (Eliom_xml.event_handler ev) let a_onsubmit ev = Raw.a_onsubmit (Eliom_xml.event_handler ev) let a_onsuspend ev = Raw.a_onsuspend (Eliom_xml.event_handler ev) let a_ontimeupdate ev = Raw.a_ontimeupdate (Eliom_xml.event_handler ev) let a_onundo ev = Raw.a_onundo (Eliom_xml.event_handler ev) let a_onunload ev = Raw.a_onunload (Eliom_xml.event_handler ev) let a_onvolumechange ev = Raw.a_onvolumechange (Eliom_xml.event_handler ev) let a_onwaiting ev = Raw.a_onwaiting (Eliom_xml.event_handler ev) let a_onkeypress (ev : (Dom_html.keyboardEvent Js.t -> unit) Eliom_lib.client_value) = Raw.a_onkeypress (Eliom_xml.event_handler (Obj.magic ev)) (* Typed with the syntax extension *) let a_onkeydown (ev : (Dom_html.keyboardEvent Js.t -> unit) Eliom_lib.client_value) = Raw.a_onkeydown (Eliom_xml.event_handler (Obj.magic ev)) (* Typed with the syntax extension *) let a_onkeyup (ev : (Dom_html.keyboardEvent Js.t -> unit) Eliom_lib.client_value) = Raw.a_onkeyup (Eliom_xml.event_handler (Obj.magic ev)) (* Typed with the syntax extension *) let a_onload ev = Raw.a_onload (Eliom_xml.event_handler ev) let a_onloadeddata ev = Raw.a_onloadeddata (Eliom_xml.event_handler ev) let a_onloadedmetadata ev = Raw.a_onloadedmetadata (Eliom_xml.event_handler ev) let a_onloadstart ev = Raw.a_onloadstart (Eliom_xml.event_handler ev) let a_onmessage ev = Raw.a_onmessage (Eliom_xml.event_handler ev) end module F = struct module Raw = Html5_f.Make(Xml)(Svg.F) include Raw type ('a, 'b, 'c) lazy_plus = ?a: (('a attrib) list) -> 'b elt Eliom_lazy.request -> ('b elt) list Eliom_lazy.request -> 'c elt let lazy_form ?(a = []) elt1 elts = tot (Eliom_xml.lazy_node ~a:(to_xmlattribs a) "form" (Eliom_lazy.from_fun (fun () -> toelt (Eliom_lazy.force elt1) :: toeltl (Eliom_lazy.force elts)))) let a_onabort ev = a_onabort (Eliom_xml.event_handler ev) let a_onafterprint ev = a_onafterprint (Eliom_xml.event_handler ev) let a_onbeforeprint ev = a_onbeforeprint (Eliom_xml.event_handler ev) let a_onbeforeunload ev = a_onbeforeunload (Eliom_xml.event_handler ev) let a_onblur ev = a_onblur (Eliom_xml.event_handler ev) let a_oncanplay ev = a_oncanplay (Eliom_xml.event_handler ev) let a_oncanplaythrough ev = a_oncanplaythrough (Eliom_xml.event_handler ev) let a_onchange ev = a_onchange (Eliom_xml.event_handler ev) let a_onclick (ev : (Dom_html.mouseEvent Js.t -> unit) Eliom_lib.client_value) = a_onclick (Eliom_xml.event_handler (Obj.magic ev)) (* Typed by the syntax extension. *) let a_oncontextmenu (ev : (Dom_html.mouseEvent Js.t -> unit) Eliom_lib.client_value) = a_oncontextmenu (Eliom_xml.event_handler (Obj.magic ev)) (* Typed with the syntax extension *) let a_ondblclick (ev : (Dom_html.mouseEvent Js.t -> unit) Eliom_lib.client_value) = a_ondblclick (Eliom_xml.event_handler (Obj.magic ev)) (* Typed with the syntax extension *) let a_ondrag (ev : (Dom_html.mouseEvent Js.t -> unit) Eliom_lib.client_value) = a_ondrag (Eliom_xml.event_handler (Obj.magic ev)) (* Typed with the syntax extension *) let a_ondragend (ev : (Dom_html.mouseEvent Js.t -> unit) Eliom_lib.client_value) = a_ondragend (Eliom_xml.event_handler (Obj.magic ev)) (* Typed with the syntax extension *) let a_ondragenter (ev : (Dom_html.mouseEvent Js.t -> unit) Eliom_lib.client_value) = a_ondragenter (Eliom_xml.event_handler (Obj.magic ev)) (* Typed with the syntax extension *) let a_ondragleave (ev : (Dom_html.mouseEvent Js.t -> unit) Eliom_lib.client_value) = a_ondragleave (Eliom_xml.event_handler (Obj.magic ev)) (* Typed with the syntax extension *) let a_ondragover (ev : (Dom_html.mouseEvent Js.t -> unit) Eliom_lib.client_value) = a_ondragover (Eliom_xml.event_handler (Obj.magic ev)) (* Typed with the syntax extension *) let a_ondragstart (ev : (Dom_html.mouseEvent Js.t -> unit) Eliom_lib.client_value) = a_ondragstart (Eliom_xml.event_handler (Obj.magic ev)) (* Typed with the syntax extension *) let a_ondrop (ev : (Dom_html.mouseEvent Js.t -> unit) Eliom_lib.client_value) = a_ondrop (Eliom_xml.event_handler (Obj.magic ev)) (* Typed with the syntax extension *) let a_ondurationchange ev = a_ondurationchange (Eliom_xml.event_handler ev) let a_onemptied ev = a_onemptied (Eliom_xml.event_handler ev) let a_onended ev = a_onended (Eliom_xml.event_handler ev) let a_onerror ev = a_onerror (Eliom_xml.event_handler ev) let a_onfocus ev = a_onfocus (Eliom_xml.event_handler ev) let a_onformchange ev = a_onformchange (Eliom_xml.event_handler ev) let a_onforminput ev = a_onforminput (Eliom_xml.event_handler ev) let a_onhashchange ev = a_onhashchange (Eliom_xml.event_handler ev) let a_oninput ev = a_oninput (Eliom_xml.event_handler ev) let a_oninvalid ev = a_oninvalid (Eliom_xml.event_handler ev) let a_onmousedown (ev : (Dom_html.mouseEvent Js.t -> unit) Eliom_lib.client_value) = a_onmousedown (Eliom_xml.event_handler (Obj.magic ev)) (* Typed with the syntax extension *) let a_onmouseup (ev : (Dom_html.mouseEvent Js.t -> unit) Eliom_lib.client_value) = a_onmouseup (Eliom_xml.event_handler (Obj.magic ev)) (* Typed with the syntax extension *) let a_onmouseover (ev : (Dom_html.mouseEvent Js.t -> unit) Eliom_lib.client_value) = a_onmouseover (Eliom_xml.event_handler (Obj.magic ev)) (* Typed with the syntax extension *) let a_onmousemove (ev : (Dom_html.mouseEvent Js.t -> unit) Eliom_lib.client_value) = a_onmousemove (Eliom_xml.event_handler (Obj.magic ev)) (* Typed with the syntax extension *) let a_onmouseout (ev : (Dom_html.mouseEvent Js.t -> unit) Eliom_lib.client_value) = a_onmouseout (Eliom_xml.event_handler (Obj.magic ev)) (* Typed with the syntax extension *) let a_onmousewheel ev = a_onmousewheel (Eliom_xml.event_handler ev) let a_onoffline ev = a_onoffline (Eliom_xml.event_handler ev) let a_ononline ev = a_ononline (Eliom_xml.event_handler ev) let a_onpause ev = a_onpause (Eliom_xml.event_handler ev) let a_onplay ev = a_onplay (Eliom_xml.event_handler ev) let a_onplaying ev = a_onplaying (Eliom_xml.event_handler ev) let a_onpagehide ev = a_onpagehide (Eliom_xml.event_handler ev) let a_onpageshow ev = a_onpageshow (Eliom_xml.event_handler ev) let a_onpopstate ev = a_onpopstate (Eliom_xml.event_handler ev) let a_onprogress ev = a_onprogress (Eliom_xml.event_handler ev) let a_onratechange ev = a_onratechange (Eliom_xml.event_handler ev) let a_onreadystatechange ev = a_onreadystatechange (Eliom_xml.event_handler ev) let a_onredo ev = a_onredo (Eliom_xml.event_handler ev) let a_onresize ev = a_onresize (Eliom_xml.event_handler ev) let a_onscroll ev = a_onscroll (Eliom_xml.event_handler ev) let a_onseeked ev = a_onseeked (Eliom_xml.event_handler ev) let a_onseeking ev = a_onseeking (Eliom_xml.event_handler ev) let a_onselect ev = a_onselect (Eliom_xml.event_handler ev) let a_onshow ev = a_onshow (Eliom_xml.event_handler ev) let a_onstalled ev = a_onstalled (Eliom_xml.event_handler ev) let a_onstorage ev = a_onstorage (Eliom_xml.event_handler ev) let a_onsubmit ev = a_onsubmit (Eliom_xml.event_handler ev) let a_onsuspend ev = a_onsuspend (Eliom_xml.event_handler ev) let a_ontimeupdate ev = a_ontimeupdate (Eliom_xml.event_handler ev) let a_onundo ev = a_onundo (Eliom_xml.event_handler ev) let a_onunload ev = a_onunload (Eliom_xml.event_handler ev) let a_onvolumechange ev = a_onvolumechange (Eliom_xml.event_handler ev) let a_onwaiting ev = a_onwaiting (Eliom_xml.event_handler ev) let a_onkeypress (ev : (Dom_html.keyboardEvent Js.t -> unit) Eliom_lib.client_value) = a_onkeypress (Eliom_xml.event_handler (Obj.magic ev)) (* Typed with the syntax extension *) let a_onkeydown (ev : (Dom_html.keyboardEvent Js.t -> unit) Eliom_lib.client_value) = a_onkeydown (Eliom_xml.event_handler (Obj.magic ev)) (* Typed with the syntax extension *) let a_onkeyup (ev : (Dom_html.keyboardEvent Js.t -> unit) Eliom_lib.client_value) = a_onkeyup (Eliom_xml.event_handler (Obj.magic ev)) (* Typed with the syntax extension *) let a_onload ev = a_onload (Eliom_xml.event_handler ev) let a_onloadeddata ev = a_onloadeddata (Eliom_xml.event_handler ev) let a_onloadedmetadata ev = a_onloadedmetadata (Eliom_xml.event_handler ev) let a_onloadstart ev = a_onloadstart (Eliom_xml.event_handler ev) let a_onmessage ev = a_onmessage (Eliom_xml.event_handler ev) end type +'a elt = 'a F.elt type +'a attrib = 'a F.attrib type uri = F.uri module Id = struct type 'a id = string (* FIXME invariant type parameter ? *) let new_elt_id: ?global:bool -> unit -> 'a id = fun ?(global=true) () -> Xml.make_node_name ~global () let create_named_elt ~(id : 'a id) elt = D.tot (Xml.make_process_node ~id (D.toelt elt)) let create_global_elt elt = D.tot (Xml.make_process_node (D.toelt elt)) let have_id name elt = Xml.get_node_id (D.toelt elt) = Xml.ProcessId name end module Custom_data = struct type 'a t = { name : string; to_string : 'a -> string; of_string : string -> 'a; default : 'a option; } let create ~name ?default ~to_string ~of_string () = { name ; of_string ; to_string; default } let create_json ~name ?default typ = { name ; of_string = of_json ~typ ; to_string = to_json ~typ; default } let attrib custom_data value = F.a_user_data custom_data.name (custom_data.to_string value) end module Printer = Xml_print.Make_typed_simple(Xml)(F) end eliom-3.0.3/src/server/eliom_comet.mli0000644000000000000000000001142512062377521016114 0ustar0000000000000000(* Ocsigen * http://www.ocsigen.org * Copyright (C) 2010-2011 * Raphaël Proust * Pierre Chambart * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, with linking exception; * either version 2.1 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *) (** Primitives to push data to the client, without explicit request. *) (** Basic primitives needed for server push. *) module Channel : sig (** [v t] is the type of server-to-client communication channels transporting data of type [v] *) type 'a t type comet_scope = [ Eliom_common.site_scope | Eliom_common.client_process_scope ] (** [create s] returns a channel sending values from [s]. There are two kind of channels created depending on the given scope (defaults to [Eliom_common.comet_client_process]). With scope {!Eliom_common.site} all users knowing the name of the channel can access it. Only one message queue is created: it is what we call a stateless channel in the sense that the memory used by the channel does not depend on the number of users. The channel can be reclaimed by the GC when there is no more reference to it. The buffer channel has a limited buffer of size [size] (default: 1000). If the client requests too old messages, exception [Eliom_coment.Channel_full] will be raised (on client side). With a scope of level {!Eliom_common.client_process_scope} the channel can only be accessed by the user who created it. It can only be created when client process data is available (that is: during a request). The eliom service created to communicate with the client is only available in the scope of the client process. To avoid memory leak when the client do not read sent data, the channel has a limited [size]. When a channel is full, no data can be read from it anymore. A channel can be used only once on client side. To be able to receive the same data multiple times on client side, use [create (Lwt_stream.clone s)] every time. To enforce the limit on the buffer size, the data is read into [stream] as soon as possible: If you want a channel that reads data on the stream only when the client requests it, use [create_unlimited] instead, but be careful of memory leaks. *) val create : ?scope:[< comet_scope ] -> ?name:string -> ?size:int -> 'a Lwt_stream.t -> 'a t (** [create_unlimited s] creates a channel which does not read immediately on the stream. It is read only when the client requests it: use it if the data you send depends on the time of the request (for instance the number of unread mails). Be careful, the size of this stream is not limited: if the size of the stream increases and your clients don't read it, you may have memory leaks. *) val create_unlimited : ?scope:Eliom_common.client_process_scope -> ?name:string -> 'a Lwt_stream.t -> 'a t (** [create_newest s] is similar to [create ~scope:Eliom_common.site s] but only the last message is returned to the client. *) val create_newest : ?name:string -> 'a Lwt_stream.t -> 'a t (** [external_channel ~prefix ~name ()] declares an external channel. The channel was created by an instance of Eliom serving the prefix [prefix] (the prefix configured in the tag of the configuration file). The channel was named by [name]. Both servers must run the exact same version of Eliom. The optional [newest] parameters tells whether the channel is a new one. If the channel is not new, [history] is the maximum number of messages retrieved at the first request. The default is [1]. *) val external_channel : ?history:int -> ?newest:bool -> prefix:string -> name:string -> unit -> 'a t (** [wait_timeout ~scope time] waits for a period of inactivity of length [time] in the [scope]. Only activity on stateful channels is taken into accounts. The default [scope] is [Eliom_common.comet_client_process]. *) val wait_timeout : ?scope:Eliom_common.client_process_scope -> float -> unit Lwt.t (**/**) val get_wrapped : 'a t -> 'a Eliom_comet_base.wrapped_channel end eliom-3.0.3/src/server/eliom_config.ml0000644000000000000000000000500112062377521016072 0ustar0000000000000000(* Ocsigen * http://www.ocsigen.org * Copyright (C) 2010 Vincent Balat * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, with linking exception; * either version 2.1 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *) let get_default_hostname () = let sitedata = Eliom_request_info.find_sitedata "get_default_hostname" in sitedata.Eliom_common.config_info.Ocsigen_extensions.default_hostname let get_default_port () = let sitedata = Eliom_request_info.find_sitedata "get_default_port" in sitedata.Eliom_common.config_info.Ocsigen_extensions.default_httpport let get_default_sslport () = let sitedata = Eliom_request_info.find_sitedata "get_default_sslport" in sitedata.Eliom_common.config_info.Ocsigen_extensions.default_httpsport let get_default_links_xhr () = let sitedata = Eliom_request_info.find_sitedata "get_default_links_xhr" in sitedata.Eliom_common.default_links_xhr#get let set_default_links_xhr ?override_configfile v = let sitedata = Eliom_request_info.find_sitedata "set_default_links_xhr" in sitedata.Eliom_common.default_links_xhr#set v let get_config_default_charset_sp sp = Ocsigen_charset_mime.default_charset sp.Eliom_common.sp_request.Ocsigen_extensions.request_config.Ocsigen_extensions.charset_assoc let get_config_default_charset () = let sp = Eliom_common.get_sp () in get_config_default_charset_sp sp let get_config_info_sp sp = sp.Eliom_common.sp_request.Ocsigen_extensions.request_config let get_config_info () = let sp = Eliom_common.get_sp () in get_config_info_sp sp let get_config () = match Eliom_common.global_register_allowed () with | Some _ -> !Eliommod.config | None -> raise (Eliom_common.Eliom_site_information_not_available "Eliom_config.get_config") let parse_config ?pcdata ?other_elements elements = Ocsigen_extensions.Configuration.process_elements ~in_tag:"eliom" ?pcdata ?other_elements ~elements (get_config ()) eliom-3.0.3/src/server/eliom_config.mli0000644000000000000000000000661412062377521016256 0ustar0000000000000000(* Ocsigen * http://www.ocsigen.org * Copyright (C) 2010 Vincent Balat * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, with linking exception; * either version 2.1 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *) (** The function [get_default_hostname ()]returns the hostname declared in the config file ([]) or the default machine hostname. *) val get_default_hostname : unit -> string (** The function [get_default_port ()] returns the port number declared in the config file ([]) or 80 if undeclared. *) val get_default_port : unit -> int (** The function [get_default_sslport ()] returns the https port number declared in the config file ([]) or 443 if undeclared. *) val get_default_sslport : unit -> int (** The function [get_config_default_charset ()] returns the default charset for this site. *) val get_config_default_charset : unit -> string (** The provided value serves as a default value for the optional parameter [~xhr] in the functions [Eliom_registration.*.{a, get_form, post_form, lwt_get_form, lwt_post_form}] (cf. {!Eliom_registration.Html5.a} et al.). This value can also be set in the {{:http://ocsigen.org/eliom/dev/manual/config#h5o-25}config file}. *) val set_default_links_xhr : ?override_configfile:bool -> bool -> unit (**/**) val get_default_links_xhr : unit -> bool (**/**) (** The function [get_config ()] returns the information of the configuration file concerning that site (between [] and [] or [] and []). {e Warning: You must call that function during the initialisation of your module (not during a Lwt thread or a service) otherwise it will raise the exception {!Eliom_common.Eliom_site_information_not_available}. If you want to build a statically linkable module, you must call this function inside the initialisation function given to {!Eliom_service.register_eliom_module}.} *) val get_config : unit -> Simplexmlparser.xml list (** Process the configuration {% <> %} by a give specification (cf. {% <> %}) *) val parse_config : ?pcdata:(string -> unit) -> ?other_elements:(string -> (string * string) list -> Simplexmlparser.xml list -> unit) -> Ocsigen_extensions.Configuration.element list -> unit (** The function [get_config_info ()] returns the information concerning the request from the configuration files. *) val get_config_info : unit -> Ocsigen_extensions.config_info (**/**) val get_config_info_sp : Eliom_common.server_params -> Ocsigen_extensions.config_info val get_config_default_charset_sp : Eliom_common.server_params -> string eliom-3.0.3/src/server/eliom_react.ml0000644000000000000000000001731612062377521015737 0ustar0000000000000000(* Ocsigen * http://www.ocsigen.org * Copyright (C) 2010 * Raphaël Proust * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, with linking exception; * either version 2.1 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *) (* Module for event wrapping and related functions *) open Lwt_react module Down = struct type 'a stateful = {throttling: float option; scope: Eliom_common.client_process_scope option; react: 'a E.t; name: string option; size: int option;} type 'a stateless = 'a Eliom_comet.Channel.t type 'a t' = | Stateful of 'a stateful | Stateless of 'a stateless type 'a t = {t : 'a t'; react_down_mark: 'a t Eliom_common.wrapper;} let wrap_stateful {throttling=t; scope; react=e; name; size} = let ee = (match t with | None -> e | Some t -> E.limit (fun () -> Lwt_unix.sleep t) e) in let stream = E.to_stream ee in let channel = Eliom_comet.Channel.create ?scope ?name ?size stream in (channel,Eliom_common.make_unwrapper Eliom_common.react_down_unwrap_id) let wrap_stateless channel = (channel,Eliom_common.make_unwrapper Eliom_common.react_down_unwrap_id) let internal_wrap = function | { t = Stateful v } -> wrap_stateful v | { t = Stateless v } -> wrap_stateless v let react_down_mark () = Eliom_common.make_wrapper internal_wrap let stateful ?scope ?throttling ?name ?size (e : 'a E.t) = Stateful {throttling=throttling; scope; react=e; name=name; size=size; } let stateless ?throttling ?name ?size (e : 'a E.t) = let ee = (match throttling with | None -> e | Some t -> E.limit (fun () -> Lwt_unix.sleep t) e) in let stream = E.to_stream ee in Stateless (Eliom_comet.Channel.create ~scope:`Site ?name ?size stream) let of_react ?scope ?throttling ?name ?size (e : 'a E.t) = let t = match scope with | Some `Site -> stateless ?throttling ?name ?size e | None -> stateful ?throttling ?name ?size e | Some ((`Client_process n) as scope) -> stateful ~scope ?throttling ?name ?size e in { t; react_down_mark=react_down_mark () } end module Up = struct type 'a t = { event : 'a E.t; service : (unit, 'a, [ `Nonattached of [ `Post ] Eliom_service.na_s ], [ `WithoutSuffix ], unit, [ `One of 'a Eliom_parameter.caml ] Eliom_parameter.param_name, [ `Registrable ], Eliom_registration.Action.return) Eliom_service.service; wrapper : 'a t Eliom_common.wrapper } let to_react t = t.event let internal_wrap t = (t.service, Eliom_common.make_unwrapper Eliom_common.react_up_unwrap_id) let up_event_wrapper () = Eliom_common.make_wrapper internal_wrap (* An event is created along with a service responsible for it's occurences. * function takes a param_type *) let create ?scope ?name post_params = let (e, push) = E.create () in let sp = Eliom_common.get_sp_option () in let scope = match sp, scope with | _, Some l -> l | None, _ -> `Site | _ -> (Eliom_common.comet_client_process_scope :> Eliom_common.scope) in let e_writer = Eliom_service.post_coservice' ?name ~post_params () in Eliom_registration.Action.register ~scope ~options:`NoReload ~service:e_writer (fun () value -> push value ; Lwt.return ()); { event = e; service = e_writer; wrapper = up_event_wrapper () } end module S = struct module Down = struct type 'a stateful = {throttling: float option; scope: Eliom_common.client_process_scope option; signal: 'a S.t; name: string option;} type 'a stateless = {channel: 'a Eliom_comet.Channel.t; stream: 'a Lwt_stream.t; (* avoid garbage collection *) sl_signal: 'a S.t} type 'a t' = | Stateful of 'a stateful | Stateless of 'a stateless type 'a t = { t : 'a t'; signal_down_mark: 'a t Eliom_common.wrapper; } type 'a store = { s : unit S.t Lazy.t; (* to avoid signal GC *) mutable value : 'a; mutable read : bool; condition : unit Lwt_condition.t; } let make_store signal = let rec store = { s = s'; value = S.value signal; read = false; condition = Lwt_condition.create (); } and s' = lazy ( S.map (fun v -> store.read <- false; store.value <- v; Lwt_condition.broadcast store.condition (); ()) signal) in ignore (Lazy.force store.s); store let read_store store = let rec aux () = if store.read then begin lwt () = Lwt_condition.wait store.condition in aux () end else begin store.read <- true; Lwt.return (Some store.value) end in aux let wrap_stateful {throttling=t; scope; signal=s; name=name} = let s : 'a S.t = (match t with | None -> s | Some t -> S.limit (fun () -> Lwt_unix.sleep t) s) in let store = make_store s in let stream = Lwt_stream.from (read_store store) in let channel = Eliom_comet.Channel.create_unlimited ?scope ?name stream in let value : 'a = S.value s in (channel,value,Eliom_common.make_unwrapper Eliom_common.signal_down_unwrap_id) let wrap_stateful {throttling=t; signal=s; name=name} = let s : 'a S.t = (match t with | None -> s | Some t -> S.limit (fun () -> Lwt_unix.sleep t) s) in let store = make_store s in let stream = Lwt_stream.from (read_store store) in let channel = Eliom_comet.Channel.create_unlimited ?name stream in let value : 'a = S.value s in (channel,value,Eliom_common.make_unwrapper Eliom_common.signal_down_unwrap_id) let wrap_stateless {sl_signal=s; channel} = let value : 'a = S.value s in (channel,value,Eliom_common.make_unwrapper Eliom_common.signal_down_unwrap_id) let internal_wrap = function | { t = Stateful v } -> wrap_stateful v | { t = Stateless v } -> wrap_stateless v let signal_down_mark () = Eliom_common.make_wrapper internal_wrap let stateful ?scope ?throttling ?name (s : 'a S.t) = Stateful {throttling=throttling; scope; signal=s; name=name;} let stateless ?throttling ?name (s : 'a S.t) = let s = match throttling with | None -> s | Some t -> S.limit (fun () -> Lwt_unix.sleep t) s in let e = S.changes s in let stream = E.to_stream e in Stateless {channel = Eliom_comet.Channel.create_newest ?name stream; stream; sl_signal = s} let of_react ?scope ?throttling ?name (s : 'a S.t) = let t = match scope with | Some `Site -> stateless ?throttling ?name s | None -> stateful ?throttling ?name s | Some ((`Client_process n) as scope) -> stateful ~scope ?throttling ?name s in { t; signal_down_mark=signal_down_mark () } end end eliom-3.0.3/src/server/eliom_bus.ml0000644000000000000000000000746412062377521015435 0ustar0000000000000000(* Ocsigen * http://www.ocsigen.org * Copyright (C) 2010 * Raphaël Proust * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, with linking exception; * either version 2.1 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *) module Ecb = Eliom_comet_base type 'a t = { stream : 'a Lwt_stream.t; scope : Eliom_comet.Channel.comet_scope; name : string option; channel : 'a Eliom_comet.Channel.t option; write : ('a -> unit); service : 'a Ecb.bus_send_service; service_registered : bool Eliom_state.volatile_table option; size : int option; bus_mark : 'a t Eliom_common.wrapper; (* must be the last field ! *) } let register_sender scope service write = Eliom_registration.Action.register ~scope ~options:`NoReload ~service (fun () x -> List.iter write x ; Lwt.return ()) let internal_wrap (bus: 'a t) : 'a Ecb.wrapped_bus * Eliom_common.unwrapper = let channel = match bus.channel with | None -> Eliom_comet.Channel.create ~scope:bus.scope ?name:bus.name ?size:bus.size (Lwt_stream.clone bus.stream) | Some c -> c in begin match bus.service_registered with | None -> () | Some table -> match Eliom_state.get_volatile_data ~table () with | Eliom_state.Data true -> () | _ -> register_sender bus.scope (bus.service:> ('h, 'a list, [ Eliom_service.internal_service_kind ], 'f, 'c, 'd, 'e, 'g) Eliom_service.service) bus.write; Eliom_state.set_volatile_data ~table true end; ( ( Eliom_comet.Channel.get_wrapped channel, bus.service ), Eliom_common.make_unwrapper Eliom_common.bus_unwrap_id ) let bus_mark () = Eliom_common.make_wrapper internal_wrap let deriving_to_list : 'a Deriving_Json.t -> 'a list Deriving_Json.t = fun (type typ) typ -> let (typ_list:typ list Deriving_Json.t) = let module M = Deriving_Json.Json_list(Deriving_Json.Defaults''(struct type a = typ let t = typ end)) in M.t in typ_list let create ?scope ?name ?size typ = (*The stream*) let (stream, push) = Lwt_stream.create () in let push x = push (Some x) in let scope = match scope with | None | Some `Site -> `Site | Some `Client_process n -> `Client_process n in let channel = match scope with | `Site -> Some (Eliom_comet.Channel.create ~scope ?name ?size (Lwt_stream.clone stream)) | `Client_process _ -> None in let typ_list = deriving_to_list typ in (*The service*) let post_params = (Eliom_parameter.caml "bus_write" typ_list : ('a, 'aa, 'aaa) Eliom_parameter.params_type) in let distant_write = Eliom_service.post_coservice' ?name ~post_params () in let service_registered = match scope with | `Site -> register_sender scope distant_write push; None | `Client_process _ as scope -> Some (Eliom_state.create_volatile_table ~scope ()) in (*The bus*) let bus = { stream; channel; scope; name; write = push; service = distant_write; service_registered; bus_mark = bus_mark (); size = size } in bus let stream bus = match bus.scope with | `Site -> Lwt_stream.clone bus.stream | `Client_process _ -> bus.stream let write bus x = bus.write x eliom-3.0.3/src/server/eliom_common.ml0000644000000000000000000014444712062377521016137 0ustar0000000000000000(* Ocsigen * http://www.ocsigen.org * Copyright (C) 2007 Vincent Balat * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, with linking exception; * either version 2.1 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *) open Eliom_lib open Ocsigen_cookies include Eliom_common_base exception Eliom_Wrong_parameter (** Service called with wrong parameter names *) exception Eliom_Session_expired exception Eliom_Typing_Error of (string * exn) list exception Eliom_duplicate_registration of string exception Eliom_there_are_unregistered_services of (string list * string list list * na_key_serv list) exception Eliom_site_information_not_available of string exception Eliom_page_erasing of string exception Eliom_error_while_loading_site of string exception Eliom_404 exception Eliom_do_redirection of string exception Eliom_do_half_xhr_redirection of string type 'a tenable_value = < get : 'a ; set : ?override_tenable:bool -> 'a -> unit > let tenable_value ~name v = object val mutable value = v val mutable tenable = false method get = value method set ?(override_tenable=false) v = if not tenable || override_tenable then ( value <- v; tenable <- override_tenable ) else Ocsigen_messages.warning ("Ignored setting tenable value \""^name^"\".") end (*****************************************************************************) (*VVV Do not forget to change the version number when the internal format change!!! *) let persistent_cookie_table_version = "_v5" (* v2 introduces session groups *) (* v3 introduces tab sessions *) (* v4 introduces group tables *) (* v5 removes secure scopes *) let eliom_persistent_cookie_table = "eliom_persist_cookies"^persistent_cookie_table_version let datacookiename = "eliomdatasession|" let servicecookiename = "eliomservicesession|" (* must not be a prefix of the following and vice versa (idem for data) *) let persistentcookiename = "eliompersistentsession|" (*****************************************************************************) let eliom_link_too_old : bool Polytables.key = Polytables.make_key () (** The coservice does not exist any more *) let eliom_service_session_expired : (full_state_name list * full_state_name list) Polytables.key = Polytables.make_key () (** If present in request data, means that the service session cookies does not exist any more. The string lists are the list of names of expired sessions *) let found_stop_key = Polytables.make_key () (*****************************************************************************) type 'a session_cookie = | SCNo_data | SCData_session_expired | SC of 'a type cookie_exp = | CENothing (** nothing to set (keep current value) *) | CEBrowser (** expires at browser close *) | CESome of float (** expiration date *) type timeout = | TGlobal (** see global setting *) | TNone (** explicitely set no timeout *) | TSome of float (** timeout duration in seconds *) (* The table of tables for each session. Keys are cookies *) module SessionCookies = Hashtbl.Make(struct type t = string let equal = (=) let hash = Hashtbl.hash end) (* session groups *) type 'a sessgrp = (string * cookie_level * (string, Ip_address.t) leftright) (* The full session group is the triple (site_dir_string, scope, session group name). The scope is the scope of group members (`Session by default). If there is no session group, we limit the number of sessions by IP address. *) type perssessgrp = string (* same triple, marshaled *) let make_persistent_full_group_name ~cookie_level site_dir_string = function | None -> None | Some g -> Some (Marshal.to_string (site_dir_string, cookie_level, Left g) []) let getperssessgrp a = Marshal.from_string a 0 let string_of_perssessgrp = id (* cookies information during page generation: *) type 'a one_service_cookie_info = (* service sessions: *) {sc_value:string (* current value *); sc_table:'a ref (* service session table ref towards cookie table *); sc_timeout:timeout ref (* user timeout - ref towards cookie table *); sc_exp:float option ref (* expiration date ref (server side) - None = never ref towards cookie table *); sc_cookie_exp:cookie_exp ref (* cookie expiration date to set *); sc_session_group: cookie_level sessgrp ref (* session group *); mutable sc_session_group_node:string Ocsigen_cache.Dlist.node; } type one_data_cookie_info = (* in memory data sessions: *) {dc_value:string (* current value *); dc_timeout:timeout ref (* user timeout - ref towards cookie table *); dc_exp:float option ref (* expiration date ref (server side) - None = never ref towards cookie table *); dc_cookie_exp:cookie_exp ref (* cookie expiration date to set *); dc_session_group: cookie_level sessgrp ref (* session group *); mutable dc_session_group_node:string Ocsigen_cache.Dlist.node; } type one_persistent_cookie_info = {pc_value:string (* current value *); pc_timeout:timeout ref (* user timeout *); pc_cookie_exp:cookie_exp ref (* cookie expiration date to set *); pc_session_group:perssessgrp option ref (* session group *) } (*VVV heavy *) type 'a cookie_info1 = (* service sessions: *) (string option (* value sent by the browser *) (* None = new cookie (not sent by the browser) *) * 'a one_service_cookie_info session_cookie ref (* SCNo_data = the session has been closed SCData_session_expired = the cookie has not been found in the table. For both of them, ask the browser to remove the cookie. *) ) (* This one is not lazy because we must check all service sessions at each request to find the services *) Full_state_name_table.t ref (* The key is the full session name *) * (* in memory data sessions: *) (string option (* value sent by the browser *) (* None = new cookie (not sent by the browser) *) * one_data_cookie_info session_cookie ref (* SCNo_data = the session has been closed SCData_session_expired = the cookie has not been found in the table. For both of them, ask the browser to remove the cookie. *) ) Lazy.t (* Lazy because we do not want to ask the browser to unset the cookie if the cookie has not been used, otherwise it is impossible to write a message "Your session has expired" *) Full_state_name_table.t ref (* The key is the full session name *) * (* persistent sessions: *) ((string (* value sent by the browser *) * timeout (* timeout at the beginning of the request *) * float option (* (server side) expdate at the beginning of the request None = no exp *) * perssessgrp option (* session group at beginning of request *)) option (* None = new cookie (not sent by the browser) *) * one_persistent_cookie_info session_cookie ref (* SCNo_data = the session has been closed SCData_session_expired = the cookie has not been found in the table. For both of them, ask the browser to remove the cookie. *) ) Lwt.t Lazy.t Full_state_name_table.t ref type 'a cookie_info = 'a cookie_info1 (* unsecure *) * 'a cookie_info1 option (* secure, if https *) (* non persistent cookies for services *) type 'a servicecookiestablecontent = (full_state_name * 'a (* session table *) * float option ref (* expiration date by timeout (server side) *) * timeout ref (* user timeout *) * cookie_level sessgrp ref (* session group *) * string Ocsigen_cache.Dlist.node (* session group node *)) type 'a servicecookiestable = 'a servicecookiestablecontent SessionCookies.t (* the table contains: - the table of services - the expiration date (by timeout), changed at each access to the table (float option) None -> no expiration - the timeout for the user (float option option) None -> see global config Some None -> no timeout - the group to which belongs the session *) (* non persistent cookies for in memory data *) type datacookiestablecontent = (full_state_name * float option ref (* expiration date by timeout (server side) *) * timeout ref (* user timeout *) * cookie_level sessgrp ref (* session group *) * string Ocsigen_cache.Dlist.node (* session group node *)) type datacookiestable = datacookiestablecontent SessionCookies.t (*****************************************************************************) let ipv4mask = ref 0b11111111111111110000000000000000l (* /16 *) let ipv6mask = ref (0b1111111111111111111111111111111111111111111111111111111100000000L, 0L) (* /56 (???) *) let get_mask4 m = match fst m with | Some m -> m | None -> !ipv4mask let get_mask6 m = match fst m with | Some m -> m | None -> !ipv6mask module Net_addr_Hashtbl = (* keys are IP address modulo "network equivalence" *) (struct include Hashtbl.Make(struct type t = Ip_address.t let equal = (=) let hash = Hashtbl.hash end) let add m4 m6 t k v = add t (Ip_address.network_of_ip k (get_mask4 m4) (get_mask6 m6)) v let remove m4 m6 t k = remove t (Ip_address.network_of_ip k (get_mask4 m4) (get_mask6 m6)) let find m4 m6 t k = find t (Ip_address.network_of_ip k (get_mask4 m4) (get_mask6 m6)) let find_all m4 m6 t k = find_all t (Ip_address.network_of_ip k (get_mask4 m4) (get_mask6 m6)) let replace m4 m6 t k v = replace t (Ip_address.network_of_ip k (get_mask4 m4) (get_mask6 m6)) v let mem m4 m6 t k = mem t (Ip_address.network_of_ip k (get_mask4 m4) (get_mask6 m6)) end : sig type key = Ip_address.t type 'a t val create : int -> 'a t val clear : 'a t -> unit val copy : 'a t -> 'a t val add : int32 option * 'bb -> (int64 * int64) option * 'bb -> 'a t -> key -> 'a -> unit val remove : int32 option * 'bb -> (int64 * int64) option * 'bb -> 'a t -> key -> unit val find : int32 option * 'bb -> (int64 * int64) option * 'bb -> 'a t -> key -> 'a val find_all : int32 option * 'bb -> (int64 * int64) option * 'bb -> 'a t -> key -> 'a list val replace : int32 option * 'bb -> (int64 * int64) option * 'bb -> 'a t -> key -> 'a -> unit val mem : int32 option * 'bb -> (int64 * int64) option * 'bb -> 'a t -> key -> bool val iter : (key -> 'a -> unit) -> 'a t -> unit val fold : (key -> 'a -> 'b -> 'b) -> 'a t -> 'b -> 'b val length : 'a t -> int end) let create_dlist_ip_table = Net_addr_Hashtbl.create let find_dlist_ip_table = Net_addr_Hashtbl.find (*****************************************************************************) type page_table_key = {key_state : att_key_serv * att_key_serv; key_kind: Ocsigen_http_frame.Http_header.http_method} module Serv_Table = Map.Make(struct type t = page_table_key let compare = compare end) module NAserv_Table = Map.Make(struct type t = na_key_serv let compare = compare end) type anon_params_type = int type node_info = { ni_id : node_ref; mutable ni_sent : bool; } module Hier_set = String.Set type server_params = {sp_request: Ocsigen_extensions.request; sp_si: sess_info; sp_sitedata: sitedata (* data for the whole site *); sp_cookie_info: tables cookie_info; sp_tab_cookie_info: tables cookie_info; mutable sp_user_cookies: Ocsigen_cookies.cookieset; (* cookies (un)set by the user during service *) mutable sp_user_tab_cookies: Ocsigen_cookies.cookieset; mutable sp_client_appl_name: string option; (* The application name, as sent by the browser *) sp_suffix: Url.path option (* suffix *); sp_full_state_name: full_state_name option (* the name of the session to which belong the service that answered (if it is a session service) *); sp_client_process_info: client_process_info; } and page_table = page_table_content Serv_Table.t and page_table_content = Ptc of (page_table ref * page_table_key, na_key_serv) leftright Ocsigen_cache.Dlist.node option (* for limitation of number of dynamic anonymous coservices *) * ((anon_params_type * anon_params_type) (* unique_id, computed from parameters type. must be the same even if the actual service reference is different (after reloading the site) so that it replaces the former one *) * (int ref option (* max_use *) * (float * float ref) option (* timeout and expiration date for the service *) * (bool -> server_params -> Ocsigen_http_frame.result Lwt.t) )) list and naservice_table_content = (int (* generation (= number of reloads of sites after which that service has been created) *) * int ref option (* max_use *) * (float * float ref) option (* timeout and expiration date *) * (server_params -> Ocsigen_http_frame.result Lwt.t) * (page_table ref * page_table_key, na_key_serv) leftright Ocsigen_cache.Dlist.node option (* for limitation of number of dynamic coservices *) ) and naservice_table = | AVide | ATable of naservice_table_content NAserv_Table.t and dircontent = | Vide | Table of direlt ref String.Table.t and direlt = | Dir of dircontent ref | File of page_table ref and tables = {mutable table_services : (int (* generation *) * int (* priority *) * dircontent ref) list; table_naservices : naservice_table ref; (* ref, and not mutable field because it simpler to use recursively with Dir of dircontent ref *) (* Information for the GC: *) mutable table_contains_services_with_timeout : bool; (* true if dircontent contains services with timeout *) mutable table_contains_naservices_with_timeout : bool; (* true if naservice_table contains services with timeout *) mutable csrf_get_or_na_registration_functions : (sp:server_params -> string) Int.Table.t; mutable csrf_post_registration_functions : (sp:server_params -> att_key_serv -> string) Int.Table.t; (* These two table are used for CSRF safe services: We associate to each service unique id the function that will register a new anonymous coservice each time we create a link or form. Attached POST coservices may have both a GET and POST registration function. That's why there are two tables. The functions associated to each service may be different for each session. That's why we use these table, and not a field in the service record. *) service_dlist_add : ?sp:server_params -> (page_table ref * page_table_key, na_key_serv) leftright -> (page_table ref * page_table_key, na_key_serv) leftright Ocsigen_cache.Dlist.node (* Add in a dlist for limiting the number of dynamic anonymous coservices in each table (and avoid DoS). There is one dlist for each session, and one for each IP in global tables. The dlist parameter is the table and coservice number for attached coservices, and the coservice number for non-attached ones. *) } and sitedata = {site_dir: Url.path; site_dir_string: string; config_info: Ocsigen_extensions.config_info; default_links_xhr : bool tenable_value; (* Timeouts: - default for site (browser sessions) - default for site (tab sessions) - then default for each full session name The booleans means "has been set from config file" *) mutable servtimeout: (float option * bool) option * (float option * bool) option * ((full_state_name * (float option * bool)) list); mutable datatimeout: (float option * bool) option * (float option * bool) option * ((full_state_name * (float option * bool)) list); mutable perstimeout: (float option * bool) option * (float option * bool) option * ((full_state_name * (float option * bool)) list); site_value_table : Polytables.t; (* table containing evaluated lazy site values *) mutable registered_scope_hierarchies: Hier_set.t; (* All services, and state data are stored in these tables, for scopes session and client process. The scope is registered in the full session name. *) global_services: tables; (* global service table *) session_services: tables servicecookiestable; (* cookie table for services (tab and browser sessions) *) session_data: datacookiestable; (* cookie table for in memory session data (tab and browser sessions) contains the information about the cookie (expiration, group ...). *) group_of_groups: [ `Session_group ] sessgrp Ocsigen_cache.Dlist.t; (* Limitation of the number of groups per site *) mutable remove_session_data: string -> unit; mutable not_bound_in_data_tables: string -> bool; mutable exn_handler: exn -> Ocsigen_http_frame.result Lwt.t; mutable unregistered_services: Url.path list; mutable unregistered_na_services: na_key_serv list; mutable max_volatile_data_sessions_per_group : int * bool; mutable max_volatile_data_sessions_per_subnet : int * bool; mutable max_volatile_data_tab_sessions_per_group : int * bool; mutable max_service_sessions_per_group : int * bool; mutable max_service_sessions_per_subnet : int * bool; mutable max_service_tab_sessions_per_group : int * bool; mutable max_persistent_data_sessions_per_group : int option * bool; mutable max_persistent_data_tab_sessions_per_group : int option * bool; mutable max_anonymous_services_per_session : int * bool; mutable max_anonymous_services_per_subnet : int * bool; dlist_ip_table : dlist_ip_table; mutable ipv4mask : int32 option * bool; mutable ipv6mask : (int64 * int64) option * bool; } and dlist_ip_table = (page_table ref * page_table_key, na_key_serv) leftright Ocsigen_cache.Dlist.t Net_addr_Hashtbl.t (*****************************************************************************) let make_full_cookie_name cookieprefix (cookie_scope, secure, site_dir_string) = let scope_hier = scope_hierarchy_of_scope cookie_scope in let secure = if secure then "S|" else "|" in let hier1, hiername = match scope_hier with | User_hier hiername -> "||", hiername | Default_ref_hier -> "|ref|", "" | Default_comet_hier -> "|comet|", "" in let s = String.concat "" [cookieprefix; secure; site_dir_string; hier1; hiername] in s let make_full_state_name2 site_dir_string secure ~(scope:[< user_scope ]) : full_state_name = (* The information in the cookie name, without the kind of session *) ((scope :> user_scope), secure, site_dir_string) let make_full_state_name ~sp ~secure ~(scope:[< user_scope ]) = make_full_state_name2 sp.sp_sitedata.site_dir_string secure scope let get_cookie_info sp = function | `Session -> sp.sp_cookie_info | `Client_process -> sp.sp_tab_cookie_info (*****************************************************************************) (** Create server parameters record *) let make_server_params sitedata (ri, si, all_cookie_info, all_tab_cookie_info, user_tab_cookies) suffix full_state_name = let appl_name = try Some (CookiesTable.find appl_name_cookie_name si.si_tab_cookies) (* It is an XHR from the client application, or an internal form *) with Not_found -> None in let cpi = match si.si_client_process_info with | Some cpi -> cpi | None -> let request_info = ri.Ocsigen_extensions.request_info in { cpi_ssl = request_info.Ocsigen_extensions.ri_ssl; cpi_hostname = Ocsigen_extensions.get_hostname ri; cpi_server_port = Ocsigen_extensions.get_port ri; cpi_original_full_path = request_info.Ocsigen_extensions.ri_original_full_path; } in { sp_request = ri; sp_si = si; sp_sitedata = sitedata; sp_cookie_info = all_cookie_info; sp_tab_cookie_info = all_tab_cookie_info; sp_user_cookies = Ocsigen_cookies.empty_cookieset; sp_user_tab_cookies = user_tab_cookies; sp_client_appl_name = appl_name; sp_suffix = suffix; sp_full_state_name = full_state_name; sp_client_process_info = cpi; } let sp_key = Lwt.new_key () let get_sp_option () = Lwt.get sp_key let get_sp () = match Lwt.get sp_key with | Some sp -> sp | None -> failwith "That function cannot be called here because it needs information about the request or the site." let sp_of_option sp = match sp with | None -> get_sp () | Some sp -> sp (*****************************************************************************) (* Scope registration *) (*****************************************************************************) let global_scope : global_scope = `Global let site_scope : site_scope = `Site let default_group_scope : session_group_scope = `Session_group Default_ref_hier let default_session_scope : session_scope = `Session Default_ref_hier let default_process_scope : client_process_scope = `Client_process Default_ref_hier let comet_client_process_scope : client_process_scope = `Client_process Default_comet_hier let request_scope : request_scope = `Request let registered_scope_hierarchies = ref Hier_set.empty let register_scope_hierarchy (name:string) = match get_sp_option () with | None -> if Hier_set.mem name !registered_scope_hierarchies then failwith (Printf.sprintf "the scope hierarchy %s has already been registered" name) else registered_scope_hierarchies := Hier_set.add name !registered_scope_hierarchies | Some sp -> if Hier_set.mem name !registered_scope_hierarchies || Hier_set.mem name sp.sp_sitedata.registered_scope_hierarchies then failwith (Printf.sprintf "the scope hierarchy %s has already been registered" name) else sp.sp_sitedata.registered_scope_hierarchies <- Hier_set.add name sp.sp_sitedata.registered_scope_hierarchies let create_scope_hierarchy name : scope_hierarchy = register_scope_hierarchy name; User_hier name let list_scope_hierarchies () = let sp = get_sp () in Default_comet_hier::Default_ref_hier:: (List.map (fun s -> User_hier s) (Hier_set.elements !registered_scope_hierarchies) @ List.map (fun s -> User_hier s) (Hier_set.elements sp.sp_sitedata.registered_scope_hierarchies) ) (*****************************************************************************) (* The current registration directory *) let absolute_change_sitedata, get_current_sitedata, end_current_sitedata = let f2 : sitedata list ref = ref [] in let popf2 () = match !f2 with | _::t -> f2 := t | [] -> f2 := [] in ((fun sitedata -> f2 := sitedata::!f2) (* absolute_change_sitedata *), (fun () -> match !f2 with | [] -> raise (Eliom_site_information_not_available "get_current_sitedata") | sd::_ -> sd) (* get_current_sitedata *), (fun () -> popf2 ()) (* end_current_sitedata *)) (* Warning: these functions are used only during the initialisation phase, which is not threaded ... That's why it works, but ... it is not really clean ... public registration relies on this directory (defined for each site in the config file) *) (*****************************************************************************) let add_unregistered sitedata a = sitedata.unregistered_services <- a::sitedata.unregistered_services let add_unregistered_na sitedata a = sitedata.unregistered_na_services <- a::sitedata.unregistered_na_services let remove_unregistered sitedata a = sitedata.unregistered_services <- List.remove_first_if_any a sitedata.unregistered_services let remove_unregistered_na sitedata a = sitedata.unregistered_na_services <- List.remove_first_if_any a sitedata.unregistered_na_services let verify_all_registered sitedata = match sitedata.unregistered_services, sitedata.unregistered_na_services with | [], [] -> () | l1, l2 -> raise (Eliom_there_are_unregistered_services (sitedata.site_dir, l1, l2)) let during_eliom_module_loading, begin_load_eliom_module, end_load_eliom_module = let during_eliom_module_loading_ = ref false in ((fun () -> !during_eliom_module_loading_), (fun () -> during_eliom_module_loading_ := true), (fun () -> during_eliom_module_loading_ := false)) let global_register_allowed () = if (Ocsigen_extensions.during_initialisation ()) && (during_eliom_module_loading ()) then Some get_current_sitedata else None let get_site_data () = match get_sp_option () with | Some sp -> sp.sp_sitedata | None -> if during_eliom_module_loading () then get_current_sitedata () else failwith "get_site_data" (*****************************************************************************) (* Lazy site value: each site have a different value *) (* Evaluated values are never collected by the GC, the table always keeps a reference on it. *) (* there is no test for cycles *) type 'a lazy_site_value = { lazy_sv_fun : unit -> 'a; lazy_sv_key : 'a Polytables.key } let force_lazy_site_value v = let sitedata = match get_sp_option () with | Some sp -> sp.sp_sitedata | None -> match global_register_allowed () with | Some f -> f () | None -> raise (Eliom_site_information_not_available "force_lazy_site_value") in try Polytables.get ~table:sitedata.site_value_table ~key:v.lazy_sv_key with | Not_found -> let value = v.lazy_sv_fun () in Polytables.set ~table:sitedata.site_value_table ~key:v.lazy_sv_key ~value; value let lazy_site_value_from_fun f = { lazy_sv_key = Polytables.make_key (); lazy_sv_fun = f } (*****************************************************************************) (*****************************************************************************) (* The table of dynamic pages for each virtual server, and naservices *) (* Each node contains either a list of nodes (case directory) or a table of "answers" (functions that will generate the page) *) let empty_page_table () = Serv_Table.empty let empty_dircontent () = Vide let empty_naservice_table () = AVide let service_tables_are_empty t = !(t.table_naservices) = AVide && ((* !(t.table_services) = [] <---- probably enough? *) List.for_all (fun (_, _, r) -> !r = Vide) t.table_services) let remove_naservice_table at k = match at with | AVide -> AVide | ATable t -> ATable (NAserv_Table.remove k t) let dlist_finaliser na_table_ref node = (* If the node disappears from the dlist, we remove the service from the service table *) match Ocsigen_cache.Dlist.value node with | Left (page_table_ref, page_table_key) -> page_table_ref := Serv_Table.remove page_table_key !page_table_ref | Right na_key_serv -> na_table_ref := remove_naservice_table !na_table_ref na_key_serv let dlist_finaliser_ip sitedata ip na_table_ref node = dlist_finaliser na_table_ref node; match Ocsigen_cache.Dlist.list_of node with | Some cl -> if Ocsigen_cache.Dlist.size cl = 1 then (try Net_addr_Hashtbl.remove sitedata.ipv4mask sitedata.ipv6mask sitedata.dlist_ip_table ip with Not_found -> ()) | None -> () let add_dlist_ dlist v = ignore (Ocsigen_cache.Dlist.add v dlist); match Ocsigen_cache.Dlist.newest dlist with | Some a -> a | None -> assert false let empty_tables max forsession = let t1 = [] in let t2 = ref (empty_naservice_table ()) in {table_services = t1; table_naservices = t2; table_contains_services_with_timeout = false; table_contains_naservices_with_timeout = false; csrf_get_or_na_registration_functions = Int.Table.empty; csrf_post_registration_functions = Int.Table.empty; service_dlist_add = if forsession then let dlist = Ocsigen_cache.Dlist.create max in Ocsigen_cache.Dlist.set_finaliser_before (dlist_finaliser t2) dlist; fun ?sp v -> add_dlist_ dlist v else fun ?sp v -> let ip, max, sitedata = match sp with | None -> Ip_address.inet6_addr_loopback, max, (match global_register_allowed () with | None -> failwith "global tables created outside initialisation" | Some get -> get ()) | Some sp -> ((Lazy.force sp.sp_request.Ocsigen_extensions.request_info.Ocsigen_extensions.ri_remote_ip_parsed), (fst sp.sp_sitedata.max_anonymous_services_per_subnet), sp.sp_sitedata ) in let dlist = try Net_addr_Hashtbl.find sitedata.ipv4mask sitedata.ipv6mask sitedata.dlist_ip_table ip with Not_found -> let dlist = Ocsigen_cache.Dlist.create max in Net_addr_Hashtbl.add sitedata.ipv4mask sitedata.ipv6mask sitedata.dlist_ip_table ip dlist; Ocsigen_cache.Dlist.set_finaliser_before (dlist_finaliser_ip sitedata ip t2) dlist; dlist in add_dlist_ dlist v } let new_service_session_tables sitedata = empty_tables (fst sitedata.max_anonymous_services_per_session) true let get_mask4 sitedata = get_mask4 sitedata.ipv4mask let get_mask6 sitedata = get_mask6 sitedata.ipv6mask (*****************************************************************************) open Lwt (* Split parameter list, removing those whose name starts with pref *) let split_prefix_param pref l = let len = String.length pref in List.partition (fun (n,_) -> try (String.sub n 0 len) = pref with Invalid_argument _ -> false) l (* Special version for non localized parameters *) let split_nl_prefix_param = let prefixlength = String.length nl_param_prefix in let prefixlengthminusone = prefixlength - 1 in fun l -> let rec aux other map = function | [] -> (map, other) | ((n, v) as a)::l -> if String.first_diff n nl_param_prefix 0 prefixlengthminusone = prefixlength then try let last = String.index_from n prefixlength '.' in let nl_param_name = String.sub n prefixlength (last - prefixlength) in let previous = try String.Table.find nl_param_name map with Not_found -> [] in aux other (String.Table.add nl_param_name (a::previous) map) l with Invalid_argument _ | Not_found -> aux (a::other) map l else aux (a::other) map l in aux [] String.Table.empty l (* The cookie name is sessionkind|S?|sitedirstring|"ref" ou "comet" ou ""|hiername *) let full_state_name_of_cookie_name cookie_level cookiename = let pref, cookiename = Ocsigen_lib.String.sep '|' cookiename in let secure, cookiename = Ocsigen_lib.String.sep '|' cookiename in let sitedirstring, cookiename = Ocsigen_lib.String.sep '|' cookiename in let hier1, hiername = Ocsigen_lib.String.sep '|' cookiename in let secure = secure = "S" in let sc_hier = match hier1 with | "" -> Eliom_common_base.User_hier hiername | "ref" -> Eliom_common_base.Default_ref_hier | "comet" -> Eliom_common_base.Default_comet_hier | _ -> raise Not_found in match cookie_level with | `Session -> (`Session sc_hier, secure, sitedirstring) | `Client_process -> (`Client_process sc_hier, secure, sitedirstring) let getcookies secure cookie_level cookienamepref cookies = let length = String.length cookienamepref in let last = length - 1 in CookiesTable.fold (fun name value beg -> if String.first_diff cookienamepref name 0 last = length then try let (_, sec, _) as expcn = full_state_name_of_cookie_name cookie_level name in if sec = secure then Full_state_name_table.add expcn value beg else beg with Not_found -> beg else beg ) cookies Full_state_name_table.empty (* Remove all parameters whose name starts with pref *) let remove_prefixed_param pref l = let len = String.length pref in let rec aux = function | [] -> [] | ((n,v) as a)::l -> try if (String.sub n 0 len) = pref then aux l else a::(aux l) with Invalid_argument _ -> a::(aux l) in aux l (* After an action, we do not take into account actual get params, but these ones: *) let eliom_params_after_action = Polytables.make_key () (* After an ction, we get tab_cookies info from rc: *) let tab_cookie_action_info_key = Polytables.make_key () type cpi = client_process_info = { cpi_ssl : bool; cpi_hostname : string; cpi_server_port : int; cpi_original_full_path : string list; } deriving (Json) let get_session_info req previous_extension_err = let req_whole = req and ri = req.Ocsigen_extensions.request_info and ci = req.Ocsigen_extensions.request_config in (* *) let rc = ri.Ocsigen_extensions.ri_request_cache in let no_post_param, p = match ri.Ocsigen_extensions.ri_post_params with | None -> true, Lwt.return [] | Some f -> false, f ci in p >>= fun post_params -> let (previous_tab_cookies_info, tab_cookies, post_params) = try let (tci, utc, tc) = Polytables.get ~table:rc ~key:tab_cookie_action_info_key in Polytables.remove ~table:rc ~key:tab_cookie_action_info_key; (Some (tci, utc), tc, post_params) with Not_found -> let tab_cookies, post_params = try (* Tab cookies are found in HTTP headers, but also sometimes in POST params (when we do not want to do an XHR because we want to stop the client side process). It should never be both. *) let (tc, pp) = List.assoc_remove tab_cookies_param_name post_params in let tc = Json.from_string<(string * string) list> tc in (List.fold_left (fun t (k,v) -> CookiesTable.add k v t) CookiesTable.empty tc, pp) (*Marshal.from_string (Ocsigen_lib.decode tc) 0, pp*) with Not_found -> try (* looking for tab cookies in headers *) let tc = Ocsigen_headers.find tab_cookies_header_name ri.Ocsigen_extensions.ri_http_frame in let tc = Json.from_string<(string * string) list> tc in (List.fold_left (fun t (k,v) -> CookiesTable.add k v t) CookiesTable.empty tc, post_params) with Not_found -> CookiesTable.empty, post_params in (None, tab_cookies, post_params) in let cpi = try (* looking for client process info in headers *) let cpi = Ocsigen_headers.find tab_cpi_header_name ri.Ocsigen_extensions.ri_http_frame in Some (Json.from_string cpi) with Not_found -> None in let epd = lazy (try (* looking in headers *) let epd = Ocsigen_headers.find expecting_process_page_name ri.Ocsigen_extensions.ri_http_frame in Json.from_string epd with Not_found -> false) in let post_params, get_params, to_be_considered_as_get = try ([], Lazy.force ri.Ocsigen_extensions.ri_get_params @snd (List.assoc_remove to_be_considered_as_get_param_name post_params), true) (* It was a POST request to be considered as GET *) with Not_found -> (post_params, Lazy.force ri.Ocsigen_extensions.ri_get_params, false) in (*204FORMS* old implementation of forms with 204 and change_page_event let get_params, internal_form = try (snd (List.assoc_remove internal_form_full_name get_params), true) with Not_found -> (get_params, false) in *) let get_params0 = get_params in let post_params0 = post_params in let get_params, post_params, (all_get_params, all_post_params, nl_get_params, nl_post_params, all_get_but_nl (*204FORMS*, internal_form *)) = try (get_params, post_params, Polytables.get ~table:ri.Ocsigen_extensions.ri_request_cache ~key:eliom_params_after_action) with Not_found -> let nl_get_params, get_params = split_nl_prefix_param get_params0 in let nl_post_params, post_params = split_nl_prefix_param post_params0 in let all_get_but_nl = get_params in get_params, post_params, (get_params0, (if no_post_param then None else Some post_params0), nl_get_params, nl_post_params, all_get_but_nl (*204FORMS*, internal_form *)) in let browser_cookies = Lazy.force ri.Ocsigen_extensions.ri_cookies in let data_cookies = getcookies false `Session datacookiename browser_cookies in let service_cookies = getcookies false `Session servicecookiename browser_cookies in let persistent_cookies = getcookies false `Session persistentcookiename browser_cookies in let secure_cookie_info = if ri.Ocsigen_extensions.ri_ssl then let sdata_cookies = getcookies true `Session datacookiename browser_cookies in let sservice_cookies = getcookies true `Session servicecookiename browser_cookies in let spersistent_cookies = getcookies true `Session persistentcookiename browser_cookies in Some (sservice_cookies, sdata_cookies, spersistent_cookies) else None in let naservice_info, (get_state, post_state), (get_params, other_get_params), na_get_params, post_params = let post_naservice_name, na_post_params = try let n, pp = List.assoc_remove naservice_num post_params in (RNa_post' n, pp) with Not_found -> try let n, pp = List.assoc_remove naservice_name post_params in (RNa_post_ n, pp) with Not_found -> (RNa_no, []) in match post_naservice_name with | RNa_post_ _ | RNa_post' _ -> (* POST non attached coservice *) (post_naservice_name, (RAtt_no, RAtt_no), ([], get_params), (lazy (try (try (naservice_name, List.assoc naservice_name get_params) with Not_found -> (naservice_num, List.assoc naservice_num get_params)) ::(fst (split_prefix_param na_co_param_prefix get_params)) with Not_found -> []) ), na_post_params) | _ -> let get_naservice_name, na_name_num, (na_get_params, other_get_params) = try let n, gp = List.assoc_remove naservice_num get_params in (RNa_get' n, [(naservice_num, n)], (split_prefix_param na_co_param_prefix gp)) with Not_found -> try let n, gp = List.assoc_remove naservice_name get_params in (RNa_get_ n, [(naservice_name, n)], (split_prefix_param na_co_param_prefix gp)) with Not_found -> (RNa_no, [], ([], get_params)) in match get_naservice_name with | RNa_get_ _ | RNa_get' _ -> (* GET non attached coservice *) (get_naservice_name, (RAtt_no, RAtt_no), (na_get_params, other_get_params), (lazy (na_name_num@na_get_params)), []) (* Not possible to have POST parameters without naservice_num if there is a GET naservice_num *) | _ -> let post_state, post_params = try let s, pp = List.assoc_remove post_numstate_param_name post_params in (RAtt_anon s, pp) with Not_found -> try let s, pp = List.assoc_remove post_state_param_name post_params in (RAtt_named s, pp) with Not_found -> (RAtt_no, post_params) in let get_state, (get_params, other_get_params) = try let s, gp = List.assoc_remove get_numstate_param_name get_params in ((RAtt_anon s), (split_prefix_param co_param_prefix gp)) with Not_found -> try let s, gp = List.assoc_remove get_state_param_name get_params in ((RAtt_named s), (split_prefix_param co_param_prefix gp)) with Not_found -> (RAtt_no, (get_params, [])) in (RNa_no, (get_state, post_state), (get_params, other_get_params), (lazy (na_name_num@na_get_params)), post_params) in let persistent_nl_get_params = lazy (String.Table.fold (fun k a t -> if nl_is_persistent k then String.Table.add k a t else t) nl_get_params String.Table.empty) in let data_cookies_tab = getcookies false `Client_process datacookiename tab_cookies in let service_cookies_tab = getcookies false `Client_process servicecookiename tab_cookies in let persistent_cookies_tab = getcookies false `Client_process persistentcookiename tab_cookies in let secure_cookie_info_tab = if ri.Ocsigen_extensions.ri_ssl then let sdata_cookies = getcookies true `Client_process datacookiename tab_cookies in let sservice_cookies = getcookies true `Client_process servicecookiename tab_cookies in let spersistent_cookies = getcookies true `Client_process persistentcookiename tab_cookies in Some (sservice_cookies, sdata_cookies, spersistent_cookies) else None in let get_params_string, url_string = (*204FORMS* old implementation of forms with 204 and change_page_event if internal_form then let gps = Url.make_encoded_parameters all_get_params in let uri = ri.Ocsigen_extensions.ri_full_path_string in ((if gps = "" then None else Some gps), String.may_append uri ~sep:"?" gps) else *) (ri.Ocsigen_extensions.ri_get_params_string, ri.Ocsigen_extensions.ri_url_string) in let ri', sess = (*VVV 2011/02/15 TODO: I think we'd better not change ri here. Keep ri for original values and use si for Eliom's values? *) {ri with Ocsigen_extensions.ri_url_string = url_string; Ocsigen_extensions.ri_get_params_string = get_params_string; Ocsigen_extensions.ri_method = (if (ri.Ocsigen_extensions.ri_method = Ocsigen_http_frame.Http_header.HEAD) || to_be_considered_as_get then Ocsigen_http_frame.Http_header.GET else ri.Ocsigen_extensions.ri_method); (* Here we modify ri, instead of putting service parameters in si. Thus it works better after actions: the request can be taken by other extensions, with new parameters. Initial parameters are kept in si. *) Ocsigen_extensions.ri_get_params = lazy get_params; Ocsigen_extensions.ri_post_params = if no_post_param then None else Some (fun _ -> Lwt.return post_params)}, {si_service_session_cookies= service_cookies; si_data_session_cookies= data_cookies; si_persistent_session_cookies= persistent_cookies; si_secure_cookie_info= secure_cookie_info; si_service_session_cookies_tab= service_cookies_tab; si_data_session_cookies_tab= data_cookies_tab; si_persistent_session_cookies_tab= persistent_cookies_tab; si_secure_cookie_info_tab= secure_cookie_info_tab; si_tab_cookies= tab_cookies; si_nonatt_info= naservice_info; si_state_info= (get_state, post_state); si_other_get_params= other_get_params; si_all_get_params= all_get_params; si_all_post_params= all_post_params; si_previous_extension_error= previous_extension_err; si_na_get_params= na_get_params; si_nl_get_params= nl_get_params; si_nl_post_params= nl_post_params; si_persistent_nl_get_params= persistent_nl_get_params; si_all_get_but_nl= all_get_but_nl; si_all_get_but_na_nl= lazy (List.remove_assoc naservice_name (List.remove_assoc naservice_num (remove_prefixed_param na_co_param_prefix all_get_but_nl))); si_client_process_info= cpi; si_expect_process_data= epd; (*204FORMS* si_internal_form= internal_form; *) } in Lwt.return ({ req_whole with Ocsigen_extensions.request_info = ri' }, sess, previous_tab_cookies_info) type ('a, 'b) foundornot = Found of 'a | Notfound of 'b (*****************************************************************************) type info = (Ocsigen_extensions.request * sess_info * tables cookie_info (* current browser cookie info *) * tables cookie_info (* current tab cookie info *) * Ocsigen_cookies.cookieset (* current user tab cookies *)) exception Eliom_retry_with of info (*****************************************************************************) (* Each persistent table created by sites correspond to a file on the disk. We save the names of the currently opened tables in this table: *) module Perstables = struct let empty = [] let add v t = v::t let fold = List.fold_left end let perstables = ref Perstables.empty let create_persistent_table name = perstables := Perstables.add name !perstables; Ocsipersist.open_table name let persistent_cookies_table : (full_state_name * float option * timeout * perssessgrp option) Ocsipersist.table Lazy.t = lazy (create_persistent_table eliom_persistent_cookie_table) (* Another tables, containing the session info for each cookie *) (* the table contains: - the expiration date (by timeout), changed at each access to the table (float option) None -> no expiration - the timeout for the user (float option option) None -> see global config Some None -> no timeout *) (* It is lazy, because we must delay the creation of the table until the initialization of eliom in case we use static linking with sqlite backend ... *) (** removes the entry from all opened tables *) let remove_from_all_persistent_tables key = Perstables.fold (* could be replaced by a parallel map *) (fun thr t -> thr >>= fun () -> Ocsipersist.remove (Ocsipersist.open_table t) key >>= Lwt_unix.yield) (return ()) !perstables (**** Wrapper type shared by client/server side ***) type 'a wrapper = 'a Eliom_wrap.wrapper let make_wrapper f = Eliom_wrap.create_wrapper f let empty_wrapper () = Eliom_wrap.empty_wrapper type unwrap_id = Eliom_wrap.unwrap_id type unwrapper = Eliom_wrap.unwrapper let make_unwrapper = Eliom_wrap.create_unwrapper let empty_unwrapper = Eliom_wrap.empty_unwrapper let react_up_unwrap_id : unwrap_id = Eliom_wrap.id_of_int react_up_unwrap_id_int let react_down_unwrap_id : unwrap_id = Eliom_wrap.id_of_int react_down_unwrap_id_int let signal_down_unwrap_id : unwrap_id = Eliom_wrap.id_of_int signal_down_unwrap_id_int let comet_channel_unwrap_id : unwrap_id = Eliom_wrap.id_of_int comet_channel_unwrap_id_int let bus_unwrap_id : unwrap_id = Eliom_wrap.id_of_int bus_unwrap_id_int (* HACK: Remove the 'nl_get_appl_parameter' used to avoid confusion between XHR and classical request in App. *) let patch_request_info req = if List.mem_assoc nl_get_appl_parameter (Lazy.force req.Ocsigen_extensions.request_info.Ocsigen_extensions.ri_get_params) then { req with Ocsigen_extensions.request_info = let get_params = List.remove_assoc nl_get_appl_parameter (Lazy.force req.Ocsigen_extensions.request_info.Ocsigen_extensions.ri_get_params) in { req.Ocsigen_extensions.request_info with Ocsigen_extensions. ri_get_params = lazy get_params; ri_get_params_string = if get_params = [] then None else Some (Url.make_encoded_parameters get_params); } } else req eliom-3.0.3/src/server/eliom_wrap.ml0000644000000000000000000001237512062377521015612 0ustar0000000000000000(* Ocsigen * Copyright (C) 2011 Pierre Chambart * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, with linking exception; * either version 2.1 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *) type poly type 'a wrapped_value = poly * 'a module AddrType = struct type t = Obj.t let hash v = let v = Obj.repr v in if Obj.is_block v (* The returned hash must contain the 'int' bit. The division enforces that without loosing too much information. *) then (Obj.obj v / 2) else failwith ("not a block "^(string_of_int (Obj.obj v))) let equal = (==) end module T = Hashtbl.Make(AddrType) let with_no_heap_move f v = let gc_control = Gc.get () in (* disable heap compaction *) Gc.set { gc_control with Gc.max_overhead = max_int }; (* promote all remaining parts of v to the major heap *) Gc.minor (); (* from now on, memory addresses of parts of v won't change *) let res = try `Data (f v) with e -> `Exn e in (* reset gc settings *) Gc.set gc_control; match res with | `Data v -> v | `Exn e -> raise e module Mark : sig type t val wrap_mark : t val do_nothing_mark: t val unwrap_mark : t end = struct type t = string let wrap_mark = "wrap_mark" let do_nothing_mark = "do_nothing_mark" let unwrap_mark = "unwrap_mark" end type marked_value = { mark : Mark.t; f : ( Obj.t -> Obj.t ) option; } let make_mark f mark = { mark; f } let is_marked (mark:Mark.t) o = let is_mark o = if (Obj.tag o = 0 && Obj.size o = 2 && Obj.field o 0 == (Obj.repr mark)) then (let f = (Obj.field o 1) in assert (Obj.tag f = 0); (* The case None should not happen here *) assert (Obj.size f = 1); assert (let tag = Obj.tag (Obj.field f 0) in tag = Obj.infix_tag || tag = Obj.closure_tag); true) else false in if (Obj.tag o = 0 && Obj.size o >= 2) (* WARNING: we only allow block values with tag = 0 to be wrapped. It is easier: we do not have to do another test to know if the value is a function *) then begin let potential_mark = (Obj.field o (Obj.size o - 1)) in if is_mark potential_mark then Some (Obj.obj potential_mark:marked_value) else None end else None type action = | Set_field of ( Obj.t * int ) | Replace of Obj.t | Return type stack = | Do of (Obj.t * action) | Wrap of ((Obj.t -> Obj.t) * Obj.t) let find t v = if Obj.tag v < Obj.no_scan_tag then try Some (T.find t v) with | Not_found -> None else Some v let search_and_replace v = let t = T.create 1 in let rec loop = function | [] -> assert false | (Wrap (f,v))::q -> let new_v = f v in (* f v is not guaranted to be in the major head: we need to move it before adding to the table *) Gc.minor (); loop ((Do (new_v,Replace v))::q) | (Do (v,action))::q as s -> match find t v with | Some r -> (match action with | Set_field (o,i) -> Obj.set_field o i r; loop q | Replace o -> T.replace t o r; loop q | Return -> r) | None -> match is_marked Mark.wrap_mark v with | Some { f = Some f } -> let stack = (Wrap (f,v))::s in loop stack | Some { f = None } -> assert false | None -> let tag = Obj.tag v in if tag = Obj.closure_tag || tag = Obj.infix_tag || tag = Obj.lazy_tag || tag = Obj.object_tag then ( if tag = Obj.lazy_tag then failwith "lazy values must be forced before wrapping"; if tag = Obj.object_tag then failwith "cannot wrap object values"; if tag = Obj.closure_tag then failwith "cannot wrap functional values"; failwith "cannot wrap functional values: infix tag" ) else begin let size = Obj.size v in let new_v = Obj.new_block tag size in T.add t v new_v; (* It is ok to do this because tag < no_scan_tag and it is not a closure ( either infix, normal or lazy ) *) let stack = ref s in for i = 0 to size - 1 do stack := (Do ((Obj.field v i),Set_field (new_v,i))) :: !stack; done; loop !stack end in with_no_heap_move loop [Do (v,Return)] type +'a wrapper = marked_value let create_wrapper (f: 'a -> 'b) : 'a wrapper = make_mark (Some (fun x -> Obj.repr (f (Obj.obj x)))) Mark.wrap_mark let empty_wrapper : 'a wrapper = make_mark None Mark.do_nothing_mark type unwrap_id = int let id_of_int x = x type unwrapper = (* WARNING Must be the same as Eliom_unwrap.unwrapper *) { id : unwrap_id; umark : Mark.t; } let create_unwrapper id = { id = id; umark = Mark.unwrap_mark } let empty_unwrapper = { id = -1; umark = Mark.do_nothing_mark } let wrap v = Obj.magic Mark.unwrap_mark, Obj.obj (search_and_replace (Obj.repr v)) eliom-3.0.3/src/server/eliom_mkreg.ml0000644000000000000000000006506412062377521015751 0ustar0000000000000000(* Ocsigen * http://www.ocsigen.org * Module Eliom_mkreg * Copyright (C) 2007 Vincent Balat * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, with linking exception; * either version 2.1 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *) open Eliom_lib open Lwt open Ocsigen_extensions open Eliom_state open Eliom_parameter open Eliom_service open Lazy let suffix_redir_uri_key = Polytables.make_key () (****************************************************************************) type ('options,'page,'result) param = { send : ?options:'options -> ?charset:string -> ?code: int -> ?content_type:string -> ?headers: Http_headers.t -> 'page -> Ocsigen_http_frame.result Lwt.t; send_appl_content : Eliom_service.send_appl_content; (** Whether the service is capable to send application content when required. This field is usually [Eliom_service.XNever]. This value is recorded inside each service just after registration. *) result_of_http_result : Ocsigen_http_frame.result -> 'result; } (* If it is an xmlHTTPrequest who asked for an internal application service but the current service does not belong to the same application, we ask the browser to stop the program and do a redirection. This can happen for example after an action, when the fallback service does not belong to the application. We can not do a regular redirection because it is an XHR. We use our own redirections. *) (*VVV An alternative, to avoid the redirection with rc, would be to answer the full page and to detect on client side that it is not the answer of an XRH (using content-type) and ask the browser to act as if it were a regular request. Is it possible to do that? Drawback: The URL will be wrong Other solution: send the page and ask the browser to put it in the cache during a few seconds. Then redirect. But can we trust the browser cache? *) (* the test to know before page generation if the page can contain application data. This test is not exhaustif: services declared as XAlways can contain classical content, but we can't know it at this point: we must wait for the page to be generated and then see if it is effectively application content. *) let check_before name service = match Eliom_service.get_send_appl_content service (* the appl name of the service *) with | Eliom_service.XSame_appl (an, _) when (an = name) -> (* Same appl, it is ok *) false | Eliom_service.XAlways -> (* It is an action *) false | _ -> true (* This test check if there is a header set only by Eliom_registration.App. This test is sufficient, but it is better to stop page generation as soon as we know that the content won't be needed: hence we test what we can before page generation. *) let check_after name result = try let appl_name = Http_headers.find (Http_headers.name Eliom_common_base.appl_name_header_name) result.Ocsigen_http_frame.res_headers in not (appl_name = name) with (* not an application content *) | Not_found -> true let check_process_redir sp f param = let redir = if Eliom_request_info.expecting_process_page () then match sp.Eliom_common.sp_client_appl_name with (* the appl name as sent by browser *) | None -> false (* should not happen *) | Some anr -> f anr param (* the browser asked application eliom data (content only) for application anr *) else false in if redir then let ri = Eliom_request_info.get_ri_sp sp in raise_lwt (* we answer to the xhr by asking an HTTP redirection *) (Eliom_common.Eliom_do_half_xhr_redirection ("/"^ String.may_concat ri.Ocsigen_extensions.ri_original_full_path_string ~sep:"?" (Eliom_parameter.construct_params_string (Lazy.force ri.Ocsigen_extensions.ri_get_params) ))) (* We do not put hostname and port. It is ok with half or full xhr redirections. *) (* If an action occured before, it may have removed some get params form ri *) else Lwt.return () let send_with_cookies sp pages ?options ?charset ?code ?content_type ?headers content = lwt result = pages.send ?options ?charset ?code ?content_type ?headers content in lwt () = check_process_redir sp check_after result in lwt tab_cookies = Eliommod_cookies.compute_cookies_to_send sp.Eliom_common.sp_sitedata sp.Eliom_common.sp_tab_cookie_info sp.Eliom_common.sp_user_tab_cookies in (* TODO: do not add header when no cookies *) let tab_cookies = Eliommod_cookies.cookieset_to_json tab_cookies in Lwt.return { result with Ocsigen_http_frame.res_cookies = Ocsigen_cookies.add_cookies (Eliom_request_info.get_user_cookies ()) result.Ocsigen_http_frame.res_cookies; res_headers = Http_headers.add (Http_headers.name Eliom_common_base.set_tab_cookies_header_name) tab_cookies result.Ocsigen_http_frame.res_headers; } let register_aux pages ?options ?charset ?code ?content_type ?headers table ~service ?(error_handler = fun l -> raise (Eliom_common.Eliom_Typing_Error l)) page_generator = Eliom_service.set_send_appl_content service (pages.send_appl_content); begin match get_kind_ service with | `Attached attser -> let key_kind = get_or_post_ attser in let attserget = get_get_name_ attser in let attserpost = get_post_name_ attser in let suffix_with_redirect = get_redirect_suffix_ attser in let priority = get_priority_ attser in let sgpt = get_get_params_type_ service in let sppt = get_post_params_type_ service in let f table ((attserget, attserpost) as attsernames) = Eliommod_services.add_service priority table (get_sub_path_ attser) {Eliom_common.key_state = attsernames; Eliom_common.key_kind = key_kind} ((if attserget = Eliom_common.SAtt_no || attserpost = Eliom_common.SAtt_no then (anonymise_params_type sgpt, anonymise_params_type sppt) else (0, 0)), ((match get_max_use_ service with | None -> None | Some i -> Some (ref i)), (match get_timeout_ service with | None -> None | Some t -> Some (t, ref (t +. Unix.time ()))), (fun nosuffixversion sp -> Lwt.with_value Eliom_common.sp_key (Some sp) (fun () -> let ri = Eliom_request_info.get_ri_sp sp and suff = Eliom_request_info.get_suffix_sp sp in (catch (fun () -> reconstruct_params ~sp sgpt (Some (Lwt.return (force ri.ri_get_params))) (Some (Lwt.return [])) nosuffixversion suff >>= fun g -> let post_params = Eliom_request_info.get_post_params_sp sp in let files = Eliom_request_info.get_files_sp sp in reconstruct_params ~sp sppt post_params files false None >>= fun p -> (* GRGR TODO: avoid Eliom_uri.make_string_uri_. But we need to "downcast" the type of service to the correct "get service". *) (if Eliom_request_info.get_http_method () = Ocsigen_http_frame.Http_header.GET && nosuffixversion && suffix_with_redirect then (* it is a suffix service in version without suffix. We redirect. *) if not (Eliom_request_info.expecting_process_page ()) then let redir_uri = Eliom_uri.make_string_uri_ ~absolute:true ~service: (service : ('a, 'b, [< Eliom_service.internal_service_kind ], [< Eliom_service.suff ], 'c, 'd, [ `Registrable ], 'return) Eliom_service.service :> ('a, 'b, Eliom_service.service_kind, [< Eliom_service.suff ], 'c, 'd, [< Eliom_service.registrable ], 'return) Eliom_service.service) g in Lwt.fail (Eliom_common.Eliom_do_redirection redir_uri) else begin (* It is an internal application form. We don't redirect but we set this special information for url to be displayed by the browser (see Eliom_request_info.rebuild_uri_without_iternal_form_info_) *) let redir_uri = Eliom_uri.make_string_uri_ ~absolute:false ~absolute_path:true ~service: (service : ('a, 'b, [< Eliom_service.internal_service_kind ], [< Eliom_service.suff ], 'c, 'd, [ `Registrable ], 'return) Eliom_service.service :> ('a, 'b, Eliom_service.service_kind, [< Eliom_service.suff ], 'c, 'd, [< Eliom_service.registrable ], 'return) Eliom_service.service) g in let redir_uri = if String.length redir_uri > 0 then String.sub redir_uri 1 (String.length redir_uri - 1) else redir_uri in let rc = Eliom_request_info.get_request_cache_sp sp in Polytables.set ~table:rc ~key:suffix_redir_uri_key ~value:redir_uri; Lwt.return () end else Lwt.return ()) >>= fun () -> check_process_redir sp check_before service >>= fun () -> page_generator g p) (function | Eliom_common.Eliom_Typing_Error l -> error_handler l | e -> fail e) >>= fun content -> send_with_cookies sp pages ?options ?charset ?code ?content_type ?headers content))))) in (match (key_kind, attserget, attserpost) with | (Ocsigen_http_frame.Http_header.POST, _, Eliom_common.SAtt_csrf_safe (id, scope, secure_session)) -> let tablereg, forsession = match table with | Left globtbl -> globtbl, false | Right (sp, ct, sec) -> if secure_session <> sec || scope <> ct then raise Wrong_session_table_for_CSRF_safe_coservice; !(Eliom_state.get_session_service_table ?secure:secure_session ~scope ~sp ()), true in Eliom_service.set_delayed_post_registration_function tablereg id (fun ~sp attserget -> let n = Eliom_service.new_state () in let attserpost = Eliom_common.SAtt_anon n in let table = if forsession then tablereg else (* we do not register in global table, but in the table specified while creating the csrf safe service *) !(Eliom_state.get_session_service_table ?secure:secure_session ~scope ~sp ()) in f table (attserget, attserpost); n) | (Ocsigen_http_frame.Http_header.GET, Eliom_common.SAtt_csrf_safe (id, scope, secure_session), _) -> let tablereg, forsession = match table with | Left globtbl -> globtbl, false | Right (sp, ct, sec) -> if secure_session <> sec || ct <> scope then raise Wrong_session_table_for_CSRF_safe_coservice; !(Eliom_state.get_session_service_table ?secure:secure_session ~scope ~sp ()), true in Eliom_service.set_delayed_get_or_na_registration_function tablereg id (fun ~sp -> let n = Eliom_service.new_state () in let attserget = Eliom_common.SAtt_anon n in let table = if forsession then tablereg else (* we do not register in global table, but in the table specified while creating the csrf safe service *) !(Eliom_state.get_session_service_table ?secure:secure_session ~scope ~sp ()) in f table (attserget, attserpost); n) | _ -> let tablereg = match table with | Left globtbl -> globtbl | Right (sp, scope, secure_session) -> !(Eliom_state.get_session_service_table ?secure:secure_session ~scope ~sp ()) in f tablereg (attserget, attserpost)) | `Nonattached naser -> let na_name = get_na_name_ naser in let f table na_name = Eliommod_naservices.add_naservice table na_name ((match get_max_use_ service with | None -> None | Some i -> Some (ref i)), (match get_timeout_ service with | None -> None | Some t -> Some (t, ref (t +. Unix.time ()))), (fun sp -> Lwt.with_value Eliom_common.sp_key (Some sp) (fun () -> let ri = Eliom_request_info.get_ri_sp sp in catch (fun () -> reconstruct_params ~sp (get_get_params_type_ service) (Some (Lwt.return (force ri.ri_get_params))) (Some (Lwt.return [])) false None >>= fun g -> let post_params = Eliom_request_info.get_post_params_sp sp in let files = Eliom_request_info.get_files_sp sp in reconstruct_params ~sp (get_post_params_type_ service) post_params files false None >>= fun p -> check_process_redir sp check_before service >>= fun () -> page_generator g p) (function | Eliom_common.Eliom_Typing_Error l -> error_handler l | e -> fail e) >>= fun content -> send_with_cookies sp pages ?options ?charset ?code ?content_type ?headers content) )) in match na_name with | Eliom_common.SNa_get_csrf_safe (id, scope, secure_session) -> (* CSRF safe coservice: we'll do the registration later *) let tablereg, forsession = match table with | Left globtbl -> globtbl, false | Right (sp, ct, sec) -> if secure_session <> sec || ct <> scope then raise Wrong_session_table_for_CSRF_safe_coservice; !(Eliom_state.get_session_service_table ?secure:secure_session ~scope ~sp ()), true in set_delayed_get_or_na_registration_function tablereg id (fun ~sp -> let n = Eliom_service.new_state () in let na_name = Eliom_common.SNa_get' n in let table = if forsession then tablereg else (* we do not register in global table, but in the table specified while creating the csrf safe service *) !(Eliom_state.get_session_service_table ?secure:secure_session ~scope ~sp ()) in f table na_name; n) | Eliom_common.SNa_post_csrf_safe (id, scope, secure_session) -> (* CSRF safe coservice: we'll do the registration later *) let tablereg, forsession = match table with | Left globtbl -> globtbl, false | Right (sp, ct, sec) -> if secure_session <> sec || ct <> scope then raise Wrong_session_table_for_CSRF_safe_coservice; !(Eliom_state.get_session_service_table ?secure:secure_session ~scope ~sp ()), true in set_delayed_get_or_na_registration_function tablereg id (fun ~sp -> let n = Eliom_service.new_state () in let na_name = Eliom_common.SNa_post' n in let table = if forsession then tablereg else (* we do not register in global table, but in the table specified while creating the csrf safe service *) !(Eliom_state.get_session_service_table ?secure:secure_session ~scope ~sp ()) in f table na_name; n) | _ -> let tablereg = match table with | Left globtbl -> globtbl | Right (sp, scope, secure_session) -> !(Eliom_state.get_session_service_table ?secure:secure_session ~scope ~sp ()) in f tablereg na_name end let send pages ?options ?charset ?code ?content_type ?headers content = lwt result = pages.send ?options ?charset ?code ?content_type ?headers content in Lwt.return (pages.result_of_http_result result) let register pages ?scope ?options ?charset ?code ?content_type ?headers ?secure_session ~service ?error_handler page_gen = let sp = Eliom_common.get_sp_option () in match scope, sp with | None, None | Some `Site, None -> (match Eliom_common.global_register_allowed () with | Some get_current_sitedata -> let sitedata = get_current_sitedata () in (match get_kind_ service with | `Attached attser -> Eliom_common.remove_unregistered sitedata (get_sub_path_ attser) | `Nonattached naser -> Eliom_common.remove_unregistered_na sitedata (get_na_name_ naser)); register_aux pages ?options ?charset ?code ?content_type ?headers (Left sitedata.Eliom_common.global_services) ~service ?error_handler page_gen | _ -> raise (Eliom_common.Eliom_site_information_not_available "register")) | None, Some sp | Some `Site, Some sp -> register_aux pages ?options ?charset ?code ?content_type ?headers ?error_handler (Left (get_global_table ())) ~service page_gen | _, None -> raise (failwith "Missing sp while registering service") | Some (#Eliom_common.user_scope as scope), Some sp -> register_aux pages ?options ?charset ?code ?content_type ?headers ?error_handler (Right (sp, scope, secure_session)) ~service page_gen (* WARNING: if we create a new service without registering it, we can have a link towards a page that does not exist!!! :-( That's why I impose to register all service during init. The only other way I see to avoid this is to impose a syntax extension like "let rec" for service... *) let register_service pages ?scope ?options ?charset ?code ?content_type ?headers ?secure_session ?https ?priority ~path ~get_params ?error_handler page = let u = service ?https ?priority ~path ~get_params () in register pages ?scope ?options ?charset ?code ?content_type ?headers ?secure_session ~service:u ?error_handler page; u let register_coservice pages ?scope ?options ?charset ?code ?content_type ?headers ?secure_session ?name ?csrf_safe ?csrf_scope ?csrf_secure ?max_use ?timeout ?https ~fallback ~get_params ?error_handler page = let u = coservice ?name ?csrf_safe ?csrf_scope:(csrf_scope:>Eliom_common.user_scope option) ?csrf_secure ?max_use ?timeout ?https ~fallback ~get_params () in register pages ?scope ?options ?charset ?code ?content_type ?headers ?secure_session ~service:u ?error_handler page; u let register_coservice' pages ?scope ?options ?charset ?code ?content_type ?headers ?secure_session ?name ?csrf_safe ?csrf_scope ?csrf_secure ?max_use ?timeout ?https ~get_params ?error_handler page = let u = coservice' ?name ?csrf_safe ?csrf_scope:(csrf_scope:>Eliom_common.user_scope option) ?csrf_secure ?max_use ?timeout ?https ~get_params () in register pages ?scope ?options ?charset ?code ?content_type ?headers ?secure_session ~service:u ?error_handler page; u let register_post_service pages ?scope ?options ?charset ?code ?content_type ?headers ?secure_session ?https ?priority ~fallback ~post_params ?error_handler page_gen = let u = post_service ?https ?priority ~fallback:fallback ~post_params:post_params () in register pages ?scope ?options ?charset ?code ?content_type ?headers ?secure_session ~service:u ?error_handler page_gen; u let register_post_coservice pages ?scope ?options ?charset ?code ?content_type ?headers ?secure_session ?name ?csrf_safe ?csrf_scope ?csrf_secure ?max_use ?timeout ?https ~fallback ~post_params ?error_handler page_gen = let u = post_coservice ?name ?csrf_safe ?csrf_scope:(csrf_scope:>Eliom_common.user_scope option) ?csrf_secure ?max_use ?timeout ?https ~fallback ~post_params () in register pages ?scope ?options ?charset ?code ?content_type ?headers ?secure_session ~service:u ?error_handler page_gen; u let register_post_coservice' pages ?scope ?options ?charset ?code ?content_type ?headers ?secure_session ?name ?csrf_safe ?csrf_scope ?csrf_secure ?max_use ?timeout ?keep_get_na_params ?https ~post_params ?error_handler page_gen = let u = post_coservice' ?name ?csrf_safe ?csrf_scope:(csrf_scope:>Eliom_common.user_scope option) ?csrf_secure ?keep_get_na_params ?max_use ?timeout ?https ~post_params () in register pages ?scope ?options ?charset ?code ?content_type ?headers ?secure_session ~service:u ?error_handler page_gen; u module type REG_PARAM = "sigs/eliom_reg_param.mli" module MakeRegister(Pages : REG_PARAM) = struct type page = Pages.page type options = Pages.options type return = Pages.return type result = Pages.result let pages = { send = Pages.send; send_appl_content = Pages.send_appl_content; result_of_http_result = Pages.result_of_http_result; } let send ?options = send pages ?options let register ?scope = register pages ?scope let register_service ?scope = register_service pages ?scope let register_coservice ?scope = register_coservice pages ?scope let register_coservice' ?scope = register_coservice' pages ?scope let register_post_service ?scope = register_post_service pages ?scope let register_post_coservice ?scope = register_post_coservice pages ?scope let register_post_coservice' ?scope = register_post_coservice' pages ?scope end module type REG_PARAM_ALPHA_RETURN = sig type ('a, 'b) page type 'a return type ('a, 'b) result include "sigs/eliom_reg_param.mli" subst type page := ('a, 'b) page and type return := 'b return and type result := ('a, 'b) result end module MakeRegister_AlphaReturn(Pages : REG_PARAM_ALPHA_RETURN) = struct type ('a, 'b) page = ('a, 'b) Pages.page type options = Pages.options type 'b return = 'b Pages.return type ('a, 'b) result = ('a, 'b) Pages.result let pages = { send = Pages.send; send_appl_content = Pages.send_appl_content; result_of_http_result = Pages.result_of_http_result; } let send ?options = send pages ?options let register ?scope = register pages ?scope let register_service ?scope = register_service pages ?scope let register_coservice ?scope = register_coservice pages ?scope let register_coservice' ?scope = register_coservice' pages ?scope let register_post_service ?scope = register_post_service pages ?scope let register_post_coservice ?scope = register_post_coservice pages ?scope let register_post_coservice' ?scope = register_post_coservice' pages ?scope end eliom-3.0.3/src/server/eliom_reference.mli0000644000000000000000000002051312062377521016741 0ustar0000000000000000(* Ocsigen * http://www.ocsigen.org * Copyright (C) 2010 Vincent Balat * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, with linking exception; * either version 2.1 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *) (** {2 Server side state data, a.k.a Eliom references} *) (** Eliom references come in two flavors: they may be stored persistently or the may be volatile. The module [Volatile] allows creation of references which can be, get, set, modify, and unset volatile references through {e non-Lwt} functions. *) type ('a, +'storage) eref' (** The type of Eliom references whose content is of type ['a]. *) type 'a eref = ('a, [ `Volatile | `Persistent ]) eref' (** Exception raised when trying to access an eref that has not been initaliazed, when we don't want to initialize it. *) exception Eref_not_intialized (** The function [eref ~scope value] creates an Eliom reference for the given [scope] and initialize it with [value]. See the Eliom manual for more information about {% <>%}. Use the optional parameter [?persistent] if you want the data to survive after relaunching the server. You must give an unique name to the table in which it will be stored on the hard disk (using Ocsipersist). Be very careful to use unique names, and to change the name if you change the type of the data, otherwise the server may crash (unsafe unmarshaling). This parameter has no effect for scope {!Eliom_common.request}. Use the optional parameter [~secure:true] if you want the data to be available only using HTTPS. This parameter has no effect for scopes {!Eliom_common.global}, {!Eliom_common.site}, and {!Eliom_common.request}. {e Warning: Eliom references of scope {!Eliom_common.global}, {!Eliom_common.site} or {!Eliom_common.request} may be created at any time ; but for other scopes, they must be created when the site information is available to Eliom, that is, either during the initialization phase of the server (while reading the configuration file) or during a request. Otherwise, it will raise the exception {!Eliom_common.Eliom_site_information_not_available}. If you are using static linking, you must delay the call to this function until the configuration file is read, using {!Eliom_service.register_eliom_module}. Otherwise you will also get this exception.} *) val eref : scope:[< Eliom_common.all_scope ] -> ?secure:bool -> ?persistent:string -> 'a -> 'a eref (** The function [eref_from_fun] works like the above {!Eliom_reference.eref}, but instead of providing a value for the initial content, a function [f] for {e creating the initial content} is provided (cf. also {!Lazy.lazy_from_fun}). In each scope, the function [f] is called for creating the value of the reference the first time the reference is read (by {!Eliom_reference.get}), if the value has not been set explicitly before (by {!Eliom_reference.set}); or if the reference was reset (by {!Eliom_reference.reset}) before. *) val eref_from_fun : scope:[< Eliom_common.all_scope ] -> ?secure:bool -> ?persistent:string -> (unit -> 'a) -> 'a eref (** The function [get eref] returns the current value of the Eliom reference [eref]. {e Warning: this function cannot be used outside of a service handler when [eref] has been created with a scope different of {!Eliom_common.global}; it can neither be used outside of an Eliom module when [eref] has been created with scope {!Eliom_common.site}} *) val get : 'a eref -> 'a Lwt.t (* That function introduces a Lwt cooperation point only for persistent references. *) (** The function [set eref v] set [v] as current value of the Eliom reference [eref]. {e Warning: this function could not be used outside af a service handler when [eref] has been created with a scope different of {!Eliom_common.global}; it can neither be used outside of an Eliom module when [eref] has been created with scope {!Eliom_common.site}} *) val set : 'a eref -> 'a -> unit Lwt.t (* That function introduces a Lwt cooperation point only for persistent references. *) (** The function [modify eref f] modifies the content of the Eliom reference [eref] by applying the function [f] on it. {e Warning: this function could not be used outside af a service handler when [eref] has been created with a scope different of {!Eliom_common.global}; it can neither be used outside of an Eliom module when [eref] has been created with scope {!Eliom_common.site}} *) val modify : 'a eref -> ('a -> 'a) -> unit Lwt.t (* That function introduces a Lwt cooperation point only for persistent references. *) (** The function [unset eref] reset the content of the Eliom reference [eref] to its initial value. {e Warning: this function could not be used outside af a service handler when [eref] has been created with a scope different of {!Eliom_common.global}; it can neither be used outside of an Eliom module when [eref] has been created with scope {!Eliom_common.site}} *) val unset : 'a eref -> unit Lwt.t (* That function introduces a Lwt cooperation point only for persistent references. *) (** Same functions as in [Eliom_reference] but a non-Lwt interface for non-persistent Eliom references. *) module Volatile : sig (** The type of volatile Eliom references. Note that [('a Eliom_reference.Volatile.eref :> 'a Eliom_reference.eref)], i.e. whereever you can use an ['a Eliom_reference.eref] you can also use an ['a Eliom_reference.Volatile.eref :> 'a Eliom_reference.eref]. *) type 'a eref = ('a, [`Volatile]) eref' val eref : scope:[< Eliom_common.all_scope] -> ?secure:bool -> 'a -> 'a eref val eref_from_fun : scope:[< Eliom_common.all_scope] -> ?secure:bool -> (unit -> 'a) -> 'a eref val get : 'a eref -> 'a val set : 'a eref -> 'a -> unit val modify : 'a eref -> ('a -> 'a) -> unit val unset : 'a eref -> unit module Ext : sig (** This module allows access to volatile references for other groups, sessions, or client processes. Use it in conjunction with functions like {!Eliom_state.Ext.iter_on_all_volatile_data_sessions_from_group} to get the sessions from a group (or the processes from a session). *) (** get the value of a reference from outside the state. If the value has not been set yet for this state, it will raise exception [Eref_not_intialized]. *) val get : ([< `Session_group | `Session | `Client_process ], [< `Data ]) Eliom_state.Ext.state -> 'a eref -> 'a val set : ([< `Session_group | `Session | `Client_process ], [< `Data ]) Eliom_state.Ext.state -> 'a eref -> 'a -> unit (** Warning: the function will be executed with the current context *) val modify : ([< `Session_group | `Session | `Client_process ], [< `Data ]) Eliom_state.Ext.state -> 'a eref -> ('a -> 'a) -> unit val unset : ([< `Session_group | `Session | `Client_process ], [< `Data ]) Eliom_state.Ext.state -> 'a eref -> unit end end module Ext : sig val get : ([< `Session_group | `Session | `Client_process ], [< `Data | `Pers ]) Eliom_state.Ext.state -> 'a eref -> 'a Lwt.t val set : ([< `Session_group | `Session | `Client_process ], [< `Data | `Pers ]) Eliom_state.Ext.state -> 'a eref -> 'a -> unit Lwt.t val modify : ([< `Session_group | `Session | `Client_process ], [< `Data | `Pers ]) Eliom_state.Ext.state -> 'a eref -> ('a -> 'a) -> unit Lwt.t val unset : ([< `Session_group | `Session | `Client_process ], [< `Data | `Pers ]) Eliom_state.Ext.state -> 'a eref -> unit Lwt.t end eliom-3.0.3/src/server/eliom_service.ml0000644000000000000000000005225212062377521016277 0ustar0000000000000000(* Ocsigen * http://www.ocsigen.org * Copyright (C) 2007 Vincent Balat * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, with linking exception; * either version 2.1 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *) open Eliom_lib open Eliom_content_core open Eliom_state open Eliom_parameter open Lwt open Lazy (* Manipulation of services - this code can be use only on server side. *) include Eliom_service_base exception Wrong_session_table_for_CSRF_safe_coservice (*********) (* If there is a client side process, we do an XHR with tab cookies *) let xhr_with_cookies s = if is_external s then None else match s.send_appl_content with | XAlways -> Some None | XNever -> None (* actually this will be tested again later in get_onload_form_creators *) | XSame_appl (_, tmpl) -> Some tmpl (* Some an = current_page_appl_name *) (* for now we do not know the current_page_appl_name. We will know it only after calling send. In case it is not the same name, we will not send the onload_form_creator_info. *) (**********) let new_state = Eliommod_cookies.make_new_session_id (* WAS: (* This does not need to be cryptographickly robust. We just want to avoid the same values when the server is relaunched. *) let c = ref (Int64.bits_of_float (Unix.gettimeofday ())) in fun () -> c := Int64.add !c Int64.one ; (Printf.sprintf "%x" (Random.int 0xFFFF))^(Printf.sprintf "%Lx" !c) But I turned this into cryptographickly robust version to implement CSRF-safe services. *) let get_or_post_ s = match s.get_or_post with | `Get -> Ocsigen_http_frame.Http_header.GET | `Post -> Ocsigen_http_frame.Http_header.POST (*****************************************************************************) (*****************************************************************************) (* Registration of static module initialization functions *) (*****************************************************************************) (*****************************************************************************) let register_eliom_module name f = Ocsigen_loader.set_module_init_function name f (*****************************************************************************) (*****************************************************************************) (* Page registration, handling of links and forms *) (*****************************************************************************) (*****************************************************************************) let uniqueid = let r = ref (-1) in fun () -> r := !r + 1; !r (****************************************************************************) (****************************************************************************) (** Definition of services *) let service_aux ~https ~path ?redirect_suffix ?keep_nl_params ?priority ~get_params = let sp = Eliom_common.get_sp_option () in match sp with | None -> (match Eliom_common.global_register_allowed () with | Some get_current_sitedata -> let sitedata = get_current_sitedata () in let path = Url.remove_internal_slash (Url.change_empty_list (Url.remove_slash_at_beginning path)) in let u = service_aux_aux ~https ~prefix:"" ~path ~site_dir: sitedata.Eliom_common.site_dir ~kind:(`Internal `Service) ~getorpost:`Get ?redirect_suffix ?keep_nl_params ?priority ~get_params ~post_params:unit () in Eliom_common.add_unregistered sitedata path; u | None -> raise (Eliom_common.Eliom_site_information_not_available "service")) | Some sp -> let path = Url.remove_internal_slash (Url.change_empty_list (Url.remove_slash_at_beginning path)) in service_aux_aux ~https ~prefix:"" ~path:path ~site_dir:(Eliom_request_info.get_site_dir_sp sp) ~kind:(`Internal `Service) ~getorpost:`Get ?redirect_suffix ?keep_nl_params ?priority ~get_params ~post_params:unit () let service ?(https = false) ~path ?keep_nl_params ?priority ~get_params () = let suffix = contains_suffix get_params in service_aux ~https ~path:(match suffix with | None -> path | _ -> path@[Eliom_common.eliom_suffix_internal_name]) ?keep_nl_params ?redirect_suffix:suffix ?priority ~get_params let default_csrf_scope = function (* We do not use the classical syntax for default value. Otherwise, the type for csrf_scope was: [< Eliom_common.user_scope > `Session] *) | None -> `Session Eliom_common_base.Default_ref_hier | Some c -> (c :> [Eliom_common.user_scope]) let coservice ?name ?(csrf_safe = false) ?csrf_scope ?csrf_secure ?max_use ?timeout ?(https = false) ~fallback ?keep_nl_params ~get_params () = let csrf_scope = default_csrf_scope csrf_scope in let `Attached k = fallback.kind in (* (match Eliom_common.global_register_allowed () with | Some _ -> Eliom_common.add_unregistered k.path; | _ -> ()); *) {fallback with max_use= max_use; timeout= timeout; get_params_type = add_pref_params Eliom_common.co_param_prefix get_params; kind = `Attached {k with get_name = (if csrf_safe then Eliom_common.SAtt_csrf_safe (uniqueid (), (csrf_scope:>Eliom_common.user_scope), csrf_secure) else (match name with | None -> Eliom_common.SAtt_anon (new_state ()) | Some name -> Eliom_common.SAtt_named name)); att_kind = `Internal `Coservice; get_or_post = `Get; }; https = https || fallback.https; keep_nl_params = match keep_nl_params with | None -> fallback.keep_nl_params | Some k -> k; } (* Warning: here no GET parameters for the fallback. Preapply services if you want fallbacks with GET parameters *) let coservice' ?name ?(csrf_safe = false) ?csrf_scope ?csrf_secure ?max_use ?timeout ?(https = false) ?(keep_nl_params = `Persistent) ~get_params () = let csrf_scope = default_csrf_scope csrf_scope in (* (match Eliom_common.global_register_allowed () with | Some _ -> Eliom_common.add_unregistered_na n; | _ -> () (* Do we accept unregistered non-attached coservices? *)); *) (* (* Do we accept unregistered non-attached named coservices? *) match sp with | None -> ... *) { (*VVV allow timeout and max_use for named coservices? *) max_use= max_use; timeout= timeout; pre_applied_parameters = String.Table.empty, []; get_params_type = add_pref_params Eliom_common.na_co_param_prefix get_params; post_params_type = unit; kind = `Nonattached {na_name = (if csrf_safe then Eliom_common.SNa_get_csrf_safe (uniqueid (), (csrf_scope:>Eliom_common.user_scope), csrf_secure) else match name with | None -> Eliom_common.SNa_get' (new_state ()) | Some name -> Eliom_common.SNa_get_ name); na_kind = `Get; }; https = https; keep_nl_params = keep_nl_params; send_appl_content = XNever; service_mark = service_mark (); } (****************************************************************************) (* Create a service with post parameters in the server *) let post_service_aux ~https ~fallback ?(keep_nl_params = `None) ?(priority = default_priority) ~post_params = (* Create a main service (not a coservice) internal, post only *) (* ici faire une vérification "duplicate parameter" ? *) let `Attached k1 = fallback.kind in let `Internal k = k1.att_kind in { pre_applied_parameters = fallback.pre_applied_parameters; get_params_type = fallback.get_params_type; post_params_type = post_params; max_use= None; timeout= None; kind = `Attached {prefix = k1.prefix; subpath = k1.subpath; fullpath = k1.fullpath; att_kind = `Internal k; get_or_post = `Post; get_name = k1.get_name; post_name = Eliom_common.SAtt_no; redirect_suffix = false; priority; }; https = https; keep_nl_params = keep_nl_params; send_appl_content = XNever; service_mark = service_mark (); } let post_service ?(https = false) ~fallback ?keep_nl_params ?priority ~post_params () = (* POST service without POST parameters means that the service will answer to a POST request only. *) let `Attached k1 = fallback.kind in let `Internal kind = k1.att_kind in let path = k1.subpath in let sp = Eliom_common.get_sp_option () in let u = post_service_aux ~https ~fallback ?keep_nl_params ?priority ~post_params in match sp with | None -> (match Eliom_common.global_register_allowed () with | Some get_current_sitedata -> Eliom_common.add_unregistered (get_current_sitedata ()) path; u | None -> if kind = `Service then raise (Eliom_common.Eliom_site_information_not_available "post_service") else u) | _ -> u (* if the fallback is a coservice, do we get a coservice or a service? *) let post_coservice ?name ?(csrf_safe = false) ?csrf_scope ?csrf_secure ?max_use ?timeout ?(https = false) ~fallback ?keep_nl_params ~post_params () = let csrf_scope = default_csrf_scope csrf_scope in let `Attached k1 = fallback.kind in (* (match Eliom_common.global_register_allowed () with | Some _ -> Eliom_common.add_unregistered k1.path; | _ -> ()); *) {fallback with post_params_type = post_params; max_use= max_use; timeout= timeout; kind = `Attached {k1 with att_kind = `Internal `Coservice; get_or_post = `Post; post_name = (if csrf_safe then Eliom_common.SAtt_csrf_safe (uniqueid (), (csrf_scope:>Eliom_common.user_scope), csrf_secure) else (match name with | None -> Eliom_common.SAtt_anon (new_state ()) | Some name -> Eliom_common.SAtt_named name)); }; https = https; keep_nl_params = match keep_nl_params with | None -> fallback.keep_nl_params | Some k -> k; } (* It is not possible to make a post_coservice function with an optional ?fallback parameter because the type 'get of the result depends on the 'get of the fallback. Or we must impose 'get = unit ... *) let post_coservice' ?name ?(csrf_safe = false) ?csrf_scope ?csrf_secure ?max_use ?timeout ?(https = false) ?(keep_nl_params = `All) ?(keep_get_na_params = true) ~post_params () = let csrf_scope = default_csrf_scope csrf_scope in (* match Eliom_common.global_register_allowed () with | Some _ -> Eliom_common.add_unregistered None | _ -> () *) { (*VVV allow timeout and max_use for named coservices? *) max_use= max_use; timeout= timeout; pre_applied_parameters = String.Table.empty, []; get_params_type = unit; post_params_type = post_params; kind = `Nonattached {na_name = (if csrf_safe then Eliom_common.SNa_post_csrf_safe (uniqueid (), (csrf_scope:>Eliom_common.user_scope), csrf_secure) else (match name with | None -> Eliom_common.SNa_post' (new_state ()) | Some name -> Eliom_common.SNa_post_ name)); na_kind = `Post keep_get_na_params; }; https = https; keep_nl_params = keep_nl_params; send_appl_content = XNever; service_mark = service_mark (); } (*****************************************************************************) let add_service = Eliommod_services.add_service let add_naservice = Eliommod_naservices.add_naservice (*****************************************************************************) exception Unregistered_CSRF_safe_coservice let register_delayed_get_or_na_coservice ~sp (k, scope, secure) = let f = try let table = !(Eliom_state.get_session_service_table_if_exists ~sp ~scope:(scope:>Eliom_common.user_scope) ?secure ()) in Int.Table.find k table.Eliom_common.csrf_get_or_na_registration_functions with Not_found -> let table = Eliom_state.get_global_table () in try Int.Table.find k table.Eliom_common.csrf_get_or_na_registration_functions with Not_found -> raise Unregistered_CSRF_safe_coservice in f ~sp let register_delayed_post_coservice ~sp (k, scope, secure) getname = let f = try let table = !(Eliom_state.get_session_service_table_if_exists ~sp ~scope:(scope:>Eliom_common.user_scope) ?secure ()) in Int.Table.find k table.Eliom_common.csrf_post_registration_functions with Not_found -> let table = Eliom_state.get_global_table () in try Int.Table.find k table.Eliom_common.csrf_post_registration_functions with Not_found -> raise Unregistered_CSRF_safe_coservice in f ~sp getname let set_delayed_get_or_na_registration_function tables k f = tables.Eliom_common.csrf_get_or_na_registration_functions <- Int.Table.add k f tables.Eliom_common.csrf_get_or_na_registration_functions let set_delayed_post_registration_function tables k f = tables.Eliom_common.csrf_post_registration_functions <- Int.Table.add k f tables.Eliom_common.csrf_post_registration_functions (*****************************************************************************) let remove_service table service = match get_kind_ service with | `Attached attser -> let key_kind = get_or_post_ attser in let attserget = get_get_name_ attser in let attserpost = get_post_name_ attser in let sgpt = get_get_params_type_ service in let sppt = get_post_params_type_ service in Eliommod_services.remove_service table (get_sub_path_ attser) {Eliom_common.key_state = (attserget, attserpost); Eliom_common.key_kind = key_kind} (if attserget = Eliom_common.SAtt_no || attserpost = Eliom_common.SAtt_no then (anonymise_params_type sgpt, anonymise_params_type sppt) else (0, 0)) | `Nonattached naser -> let na_name = get_na_name_ naser in Eliommod_naservices.remove_naservice table na_name let unregister ?scope ?secure service = let sp = Eliom_common.get_sp_option () in match scope with | None | Some `Site -> let table = match sp with | None -> (match Eliom_common.global_register_allowed () with | Some get_current_sitedata -> let sitedata = get_current_sitedata () in sitedata.Eliom_common.global_services | _ -> raise (Eliom_common.Eliom_site_information_not_available "unregister")) | Some sp -> get_global_table () in remove_service table service | Some (#Eliom_common.user_scope as scope) -> match sp with | None -> raise (failwith "Unregistering service for non global scope must be done during a request") | Some sp -> let table = !(Eliom_state.get_session_service_table ~sp ?secure ~scope ()) in remove_service table service (*****************************************************************************) let pre_wrap s = {s with get_params_type = Eliom_parameter.wrap_param_type s.get_params_type; post_params_type = Eliom_parameter.wrap_param_type s.post_params_type; } (* let wrap s = Eliom_types.wrap_parameters (pre_wrap s) *) (******************************************************************************) (* Global data *) let get_global_data, modify_global_data = (* We have to classify global data from ocsigen extensions (no site available) and eliommodules (site data available). Furthermore, the Eliom services must only send global data from ocsigen extensions and their own site. *) let global_data = ref String_map.empty in let site_data = Eliom_reference.Volatile.eref ~scope:Eliom_common.site_scope String_map.empty in let is_site_available () = (* Matches valid states for Eliom_common.get_site_data *) Eliom_common.(get_sp_option () <> None || during_eliom_module_loading ()) in let get () = if is_site_available () then String_map.merge (fun compilation_unit_id global site -> match global, site with | None, None -> assert false | Some data, None | None, Some data -> Some data | Some _, Some site_data -> Printf.ksprintf Ocsigen_messages.errlog "Compilation unit %s linked globally AND as Eliom module" compilation_unit_id; Some site_data) !global_data (Eliom_reference.Volatile.get site_data) else !global_data in let modify f = if is_site_available () then Eliom_reference.Volatile.modify site_data f else global_data := f !global_data in get, modify let current_server_section_data = ref [] let get_compilation_unit_global_data compilation_unit_id = if not (String_map.mem compilation_unit_id (get_global_data ())) then ( let data = { server_sections_data = Queue.create (); client_sections_data = Queue.create () } in ignore (modify_global_data (String_map.add compilation_unit_id data)) ); String_map.find compilation_unit_id (get_global_data ()) let close_server_section ~compilation_unit_id = let { server_sections_data } = get_compilation_unit_global_data compilation_unit_id in Queue.push (List.rev !current_server_section_data) server_sections_data; current_server_section_data := [] let close_client_section ~compilation_unit_id injection_data = let { client_sections_data } = get_compilation_unit_global_data compilation_unit_id in let injection_datum (injection_id, injection_value) = { injection_id; injection_value } in Queue.push (List.map injection_datum injection_data) client_sections_data let get_global_data () = let on_injection_datum injection_datum = let injection_value = injection_datum.injection_value () in { injection_datum with injection_value } in String_map.map (fun compilation_unit_global_data -> let client_sections_data = Queue.create () in Queue.iter (fun injection_data -> Queue.push (List.map on_injection_datum injection_data) client_sections_data) compilation_unit_global_data.client_sections_data; { compilation_unit_global_data with client_sections_data }) (get_global_data ()) (* Request data *) let request_data : request_data Eliom_reference.Volatile.eref = Eliom_reference.Volatile.eref ~scope:Eliom_common.request_scope [] let get_request_data () = List.rev (Eliom_reference.Volatile.get request_data) (* Register data *) let is_global = ref false let register_client_value_data ~closure_id ~instance_id ~args = let client_value_datum = { closure_id; instance_id; args } in if !is_global then if Eliom_common.get_sp_option () = None then current_server_section_data := client_value_datum :: !current_server_section_data else raise (Client_value_creation_invalid_context closure_id) else Eliom_reference.Volatile.modify request_data (fun sofar -> client_value_datum :: sofar) (* Syntax helpers *) module Syntax_helpers = struct let escaped_value = Eliom_lib.escaped_value let client_value closure_id args = let instance_id = Eliom_lib.fresh_ix () in register_client_value_data ~closure_id ~instance_id ~args:(to_poly args); create_client_value (Client_value_server_repr.create closure_id instance_id) let close_server_section compilation_unit_id = close_server_section ~compilation_unit_id let close_client_section compilation_unit_id = close_client_section ~compilation_unit_id let set_global b = is_global := b end eliom-3.0.3/src/server/eliom_types.mli0000644000000000000000000000402012062377521016142 0ustar0000000000000000(* Ocsigen * http://www.ocsigen.org * Module eliom_client_types.ml * Copyright (C) 2010 Vincent Balat * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, with linking exception; * either version 2.1 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *) (** Types shared by client and server. *) open Eliom_lib open Eliom_content_core type sitedata = { site_dir: string list; site_dir_string: string; } type server_params val sp : server_params (**/**) type eliom_js_page_data = { ejs_global_data: global_data option; ejs_request_data: request_data; (* Event handlers *) ejs_event_handler_table: Xml.event_handler_table; (* Session info *) ejs_sess_info: Eliom_common.sess_info; } type 'a eliom_caml_service_data = { ecs_request_data: request_data; ecs_data: 'a; } (* the data sent on channels *) type 'a eliom_comet_data_type = 'a Eliom_wrap.wrapped_value (*SGO* Server generated onclicks/onsubmits val a_closure_id : int val a_closure_id_string : string val get_closure_id : int val get_closure_id_string : string val post_closure_id : int val post_closure_id_string : string val eliom_temporary_form_node_name : string *) (*POSTtabcookies* forms with tab cookies in POST params: val add_tab_cookies_to_get_form_id : int val add_tab_cookies_to_get_form_id_string : string val add_tab_cookies_to_post_form_id : int val add_tab_cookies_to_post_form_id_string : string *) val encode_eliom_data : 'a -> string eliom-3.0.3/src/server/eliom_state.mli0000644000000000000000000011020412062377521016120 0ustar0000000000000000(* Ocsigen * http://www.ocsigen.org * Copyright (C) 2007 Vincent Balat * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, with linking exception; * either version 2.1 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *) (** Manage server-side state. {% <>%} *) open Eliom_lib open Ocsigen_extensions (*****************************************************************************) (** {2 Managing the state of an application} *) (** {3 Closing sessions, removing state data and services} *) (** Delete server-side (vatile and persistent) state data and services for a session, a group of sessions, a client process or a request. Use that function to close a session (using scope [Eliom_common.session]). Closing a group of sessions will close all sessions in the group. By default will remove both secure and unsecure data and services, but if [~secure] is present. {e Warning: you may also want to unset some request-scoped Eliom references when discarding a state.} *) val discard : scope:[< Eliom_common.user_scope | Eliom_common.request_scope ] -> ?secure:bool -> unit -> unit Lwt.t (* Discard services and (volatile and persistent) data for all user and request scopes *) val discard_all_scopes : ?secure:bool -> unit -> unit Lwt.t (** Remove current state data. If the optional parameter [?persistent] is not present, will remove both volatile and persistent data. Otherwise only volatile or persistent data. *) val discard_data : ?persistent:bool -> scope:[< Eliom_common.user_scope | Eliom_common.request_scope ] -> ?secure:bool -> unit -> unit Lwt.t (** Remove all services registered for the given scope (the default beeing [`Session]). *) val discard_services : scope:[< Eliom_common.user_scope ] -> ?secure:bool -> unit -> unit (*****************************************************************************) (** {3 State status} *) (** The following functions return the current state of the state for a given scope: - [Alive_state] means that data has been recorded for this scope - [Empty_state] means that there is no data for this scope - [Expired_state] means that data for this scope has been removed because the timeout has been reached. The default scope is [`Session]. *) type state_status = Alive_state | Empty_state | Expired_state val service_state_status : scope:[< Eliom_common.user_scope ] -> ?secure:bool -> unit -> state_status val volatile_data_state_status : scope:[< Eliom_common.user_scope ] -> ?secure:bool -> unit -> state_status val persistent_data_state_status : scope:[< Eliom_common.user_scope ] -> ?secure:bool -> unit -> state_status Lwt.t (*****************************************************************************) (** {3 User cookies} If you want to store a client-side state, and ask the browser to send it back with each request, you can set manually your own cookies. Usual cookies correspond to scope [`Session] (that is, one browser). The browser send them with each request to the same Web site. But Eliom also implements client-side process cookies (scope [`Client_process]), that behave in the same way, but for one instance of the client-side Eliom program (if there is one). Cookies can be limited to a subsite using the [?path] optional parameter. This path is relative to the main path of your Web site. (It is not possible to set a cookie for a subsite larger than your current Web site). Cookies can have an expiration date, specified (in seconds since the 1st of January 1970) in the optional parameter [?exp]. If the parameter is not set, the expiration date will be when the browser is closed. Secure cookies are sent by the browser only with HTTPS (default: [false]). *) (** Ask the browser to record a cookie. *) val set_cookie : ?cookie_level:Eliom_common.cookie_level -> ?path:string list -> ?exp:float -> ?secure:bool -> name:string -> value:string -> unit -> unit (** Ask the browser to remove a cookie. *) val unset_cookie : ?cookie_level:Eliom_common.cookie_level -> ?path:string list -> name:string -> unit -> unit (*****************************************************************************) (** {2 Session groups} *) (** If your Web site has users, it is a good idea to group together all the sessions for one user. Otherwise, you may want to group sessions according to another criterion. Session groups may be used for example to limit the number of sessions one user can open at the same time, or to implement a "close all your sessions" feature. Usually, the group is the user name. *) (** {3 Putting a session in a group, removing a session from a group} *) (** sets the group to which belong the service session. If the optional [?set_max] parameter is present, also sets the maximum number of sessions in the group. Default: follow current configuration for the group or default configuration if the group does not exist. If [~secure] is false when the protocol is https, it will affect the unsecure session. Otherwise, il will affect the secure session in https, the unsecure one in http. *) val set_service_session_group : ?set_max: int -> ?scope:Eliom_common.session_scope -> ?secure:bool -> string -> unit (** Remove the session from its group. Will not close the session if it contains data. *) val unset_service_session_group : ?set_max: int -> ?scope:Eliom_common.session_scope -> ?secure:bool -> unit -> unit (** returns the group to which belong the service session. If the session does not belong to any group, or if no session is opened, return [None]. *) val get_service_session_group : ?scope:Eliom_common.session_scope -> ?secure:bool -> unit -> string option (** returns the number of sessions in the group. If he session does not belong to any group or if no session is opened, returns [None] *) val get_service_session_group_size : ?scope:Eliom_common.session_scope -> ?secure:bool -> unit -> int option (** sets the group to which belong the volatile data session. If the optional [?set_max] parameter is present, also sets the maximum number of sessions in the group. Default: follow current configuration for the group or default configuration if the group does not exist. *) val set_volatile_data_session_group : ?set_max: int -> ?scope:Eliom_common.session_scope -> ?secure:bool -> string -> unit (** Remove the session from its group. Will not close the session if it contains data. *) val unset_volatile_data_session_group : ?set_max: int -> ?scope:Eliom_common.session_scope -> ?secure:bool -> unit -> unit (** returns the group to which belong the data session. If the session does not belong to any group, or if no session is opened, return [None]. *) val get_volatile_data_session_group : ?scope:Eliom_common.session_scope -> ?secure:bool -> unit -> string option (** returns the number of sessions in the group. If he session does not belong to any group or if no session is opened, returns [None] *) val get_volatile_data_session_group_size : ?scope:Eliom_common.session_scope -> ?secure:bool -> unit -> int option (** sets the group to which belong the persistent session. If the optional [?set_max] parameter is present, also sets the maximum number of sessions in the group. When [~set_max:None] is present, the number of session is unlimited. Default: follow current configuration for the group or default configuration if the group does not exist. *) val set_persistent_data_session_group : ?set_max: int option -> ?scope:Eliom_common.session_scope -> ?secure:bool -> string -> unit Lwt.t (** Remove the session from its group. Will not close the session if it contains data. *) val unset_persistent_data_session_group : ?scope:Eliom_common.session_scope -> ?secure:bool -> unit -> unit Lwt.t (** returns the group to which belong the persistent session. If the session does not belong to any group, or if no session is opened, return [None]. *) val get_persistent_data_session_group : ?scope:Eliom_common.session_scope -> ?secure:bool -> unit -> string option Lwt.t (** {3 Maximum group size} *) (** The following functions of this section set the maximum number of sessions in a session group, for the different kinds of session. This won't modify existing groups. That value will be used only as default value if you do not specify the optional parameter [?set_max] of function {!Eliom_state.set_volatile_data_session_group}. If there is no group, the number of sessions is limitated by sub network (which can be a problem for example if the server is behind a reverse proxy). It is highly recommended to use session groups! - Default number of sessions in a group: 5 - Default number of sessions in a sub network: 1000000 - Default IPV4 sub network: /16 - Default IPV6 sub network: /56 These default can be changed from configuration file and/or using these functions. If [~override_configfile] is [true] (default ([false]), then the function will set the value even if it has been modified in the configuration file. It means that by default, these functions have no effect if there is a value in the configuration file. This gives the ability to override the values chosen by the module in the configuration file. Use [~override_configfile:true] for example if your Eliom module wants to change the values afterwards (for example in the site configuration Web interface). *) (** Sets the maximum number of service sessions in a session group (see above). *) val set_default_max_service_sessions_per_group : ?override_configfile:bool -> int -> unit (** Sets the maximum number of volatile data sessions in a session group (see above). *) val set_default_max_volatile_data_sessions_per_group : ?override_configfile:bool -> int -> unit (** Sets the maximum number of persistent data sessions in a session group (see above). [None] means "no limitation". *) val set_default_max_persistent_data_sessions_per_group : ?override_configfile:bool -> int option -> unit (** Sets the maximum number of volatile sessions (data and service) in a session group (see above). *) val set_default_max_volatile_sessions_per_group : ?override_configfile:bool -> int -> unit (** Sets the maximum number of service sessions in a subnet (see above). *) val set_default_max_service_sessions_per_subnet : ?override_configfile:bool -> int -> unit (** Sets the maximum number of volatile data sessions in a subnet (see above). *) val set_default_max_volatile_data_sessions_per_subnet : ?override_configfile:bool -> int -> unit (** Sets the maximum number of volatile sessions (data and service) in a subnet (see above). *) val set_default_max_volatile_sessions_per_subnet : ?override_configfile:bool -> int -> unit (** Sets the maximum number of tab service sessions in a session group (see above). *) val set_default_max_service_tab_sessions_per_group : ?override_configfile:bool -> int -> unit (** Sets the maximum number of volatile data tab sessions in a session group (see above). *) val set_default_max_volatile_data_tab_sessions_per_group : ?override_configfile:bool -> int -> unit (** Sets the maximum number of persistent data tab sessions in a session group (see above). *) val set_default_max_persistent_data_tab_sessions_per_group : ?override_configfile:bool -> int option -> unit (** Sets the maximum number of volatile tab sessions (data and service) in a session group (see above). *) val set_default_max_volatile_tab_sessions_per_group : ?override_configfile:bool -> int -> unit (** Sets the mask for subnet (IPV4). *) val set_ipv4_subnet_mask : ?override_configfile:bool -> int32 -> unit (** Sets the mask for subnet (IPV6). *) val set_ipv6_subnet_mask : ?override_configfile:bool -> int64 * int64 -> unit (** Sets the maximum number of service sessions in the current session group (or for the client sub network, if there is no group). *) val set_max_service_states_for_group_or_subnet : scope:Eliom_common.user_scope -> ?secure:bool -> int -> unit (** Sets the maximum number of volatile data sessions in the current session group (or for the client sub network, if there is no group). *) val set_max_volatile_data_states_for_group_or_subnet : scope:Eliom_common.user_scope -> ?secure:bool -> int -> unit (** Sets the maximum number of volatile sessions (both data and service sessions) in the current group (or for the client sub network, if there is no group). *) val set_max_volatile_states_for_group_or_subnet : scope:Eliom_common.user_scope -> ?secure:bool -> int -> unit (** {2 Expiration of cookies and timeouts} *) (** {3 Cookie expiration} *) (** The functions in this section ask the browser to set the state cookie expiration date, for the different kinds of session, in seconds, since the 1st of January 1970. [None] means the cookie will expire when the browser is closed. Note: there is no way to set cookies for an infinite time on browsers. By default, it will affect regular browser cookies (sessions). But if you set [~cookie_level:`Client_process], it will only affect the client-side Eliom process (if there is one), which simulates some kind of "tab cookies". *) (** Sets the cookie expiration date for the current service state (see above). *) val set_service_cookie_exp_date : cookie_scope:Eliom_common.cookie_scope -> ?secure:bool -> float option -> unit (** Sets the cookie expiration date for the current data state (see above). *) val set_volatile_data_cookie_exp_date : cookie_scope:Eliom_common.cookie_scope -> ?secure:bool -> float option -> unit (** Sets the cookie expiration date for the persistent state (see above). *) val set_persistent_data_cookie_exp_date : cookie_scope:Eliom_common.cookie_scope -> ?secure:bool -> float option -> unit Lwt.t (** {3 Global configuration of state timeouts} *) (** The following functions set the timeout for states, for the different kinds of states. States will be closed after this amount of time of inactivity from the user. [None] means no timeout. The optional parameter [?recompute_expdates] is [false] by default. If you set it to [true], the expiration dates for all states in the table will be recomputed with the new timeout. That is, the difference between the new timeout and the old one will be added to their expiration dates (asynchronously, by another Lwt thread, as this can take a long time). States whose timeout has been set individually with functions like {!Eliom_state.set_volatile_data_state_timeout} won't be affected. If [~scope_hierarchy] is not present, it is the default for all scope hierarchies, and in that case [recompute_expdates] is ignored. [~scope_hierarchy:None] means the default scope hierarchy. If [~override_configfile] is [true] (default ([false]), then the function will set the timeout even if it has been modified in the configuration file. It means that by default, these functions have no effect if there is a value in the configuration file. This gives the ability to override the values chosen by the module in the configuration file. Use [~override_configfile:true] for example if your Eliom module wants to change the values afterwards (for example in the site configuration Web interface). *) (** Sets the (server side) timeout for volatile (= "in memory") sessions (both service session and volatile data session). *) val set_global_volatile_state_timeout : cookie_scope:[< Eliom_common.cookie_scope ] -> ?secure: bool -> ?recompute_expdates:bool -> ?override_configfile:bool -> float option -> unit val set_default_global_service_state_timeout : cookie_level:[< Eliom_common.cookie_level ] -> ?override_configfile:bool -> float option -> unit (** Sets the (server side) timeout for service states. *) val set_global_service_state_timeout : cookie_scope:[< Eliom_common.cookie_scope ] -> ?secure: bool -> ?recompute_expdates:bool -> ?override_configfile:bool -> float option -> unit val set_default_global_service_state_timeout : cookie_level:[< Eliom_common.cookie_level ] -> ?override_configfile:bool -> float option -> unit (** Sets the (server side) timeout for volatile (= "in memory") data states. *) val set_global_volatile_data_state_timeout : cookie_scope:[< Eliom_common.cookie_scope ] -> ?secure: bool -> ?recompute_expdates:bool -> ?override_configfile:bool -> float option -> unit val set_default_global_volatile_data_state_timeout : cookie_level:[< Eliom_common.cookie_level ] -> ?override_configfile:bool -> float option -> unit (** Sets the (server side) timeout for persistent states. *) val set_global_persistent_data_state_timeout : cookie_scope:[< Eliom_common.cookie_scope ] -> ?secure: bool -> ?recompute_expdates:bool -> ?override_configfile:bool -> float option -> unit val set_default_global_persistent_data_state_timeout : cookie_level:[< Eliom_common.cookie_level ] -> ?override_configfile:bool -> float option -> unit (** Returns the (server side) timeout for service states. *) val get_global_service_state_timeout : ?secure: bool -> cookie_scope:[< Eliom_common.cookie_scope ] -> unit -> float option (** Returns the (server side) timeout for "volatile data" states. *) val get_global_volatile_data_state_timeout : ?secure: bool -> cookie_scope:[< Eliom_common.cookie_scope ] -> unit -> float option (** Returns the (server side) timeout for persistent states. *) val get_global_persistent_data_state_timeout : ?secure: bool -> cookie_scope:[< Eliom_common.cookie_scope ] -> unit -> float option (** {3 Personalizing timeouts for current state} *) (** sets the timeout for service state (server side) for current user, in seconds. [None] = no timeout *) val set_service_state_timeout : cookie_scope:Eliom_common.cookie_scope -> ?secure:bool -> float option -> unit (** remove the service state timeout for current user (and turn back to the default). *) val unset_service_state_timeout : cookie_scope:[< Eliom_common.cookie_scope ] -> ?secure:bool -> unit -> unit (** returns the timeout for current service state. [None] = no timeout *) val get_service_state_timeout : cookie_scope:[< Eliom_common.cookie_scope ] -> ?secure:bool -> unit -> float option (** sets the (server side) timeout for volatile data state for current user, in seconds. [None] = no timeout *) val set_volatile_data_state_timeout : cookie_scope:[< Eliom_common.cookie_scope ] -> ?secure:bool -> float option -> unit (** remove the "volatile data" state timeout for current user (and turn back to the default). *) val unset_volatile_data_state_timeout : cookie_scope:[< Eliom_common.cookie_scope ] -> ?secure:bool -> unit -> unit (** returns the timeout for current volatile data state. [None] = no timeout *) val get_volatile_data_state_timeout : cookie_scope:[< Eliom_common.cookie_scope ] -> ?secure:bool -> unit -> float option (** sets the (server side) timeout for persistent state for current user, in seconds. [None] = no timeout *) val set_persistent_data_state_timeout : cookie_scope:[< Eliom_common.cookie_scope ] -> ?secure:bool -> float option -> unit Lwt.t (** remove the persistent state timeout for current user (and turn back to the default). *) val unset_persistent_data_state_timeout : cookie_scope:[< Eliom_common.cookie_scope ] -> ?secure:bool -> unit -> unit Lwt.t (** returns the persistent state timeout for current user. [None] = no timeout *) val get_persistent_data_state_timeout : cookie_scope:[< Eliom_common.cookie_scope ] -> ?secure:bool -> unit -> float option Lwt.t (*****************************************************************************) (** {2 Administrating server side state} *) (** {e Warning: Most these functions must be called when the site information is available, that is, either during a request or during the initialisation phase of the site. Otherwise, it will raise the exception {!Eliom_common.Eliom_site_information_not_available}. If you are using static linking, you must delay the call to this function until the configuration file is read, using {!Eliom_service.register_eliom_module}. Otherwise you will also get this exception.} *) (** The type of (volatile) state data tables. *) type 'a volatile_table (** The type of persistent state data tables. *) type 'a persistent_table (** Discard all services and persistent and volatile data for every scopes. *) val discard_everything : unit -> unit Lwt.t (*CCC missing ~secure? *) (** Discard all services and persistent and volatile data for one scope. *) val discard_all : scope:Eliom_common.user_scope -> ?secure:bool -> unit -> unit Lwt.t (*VVV missing: scope group *) (*VVV missing ~secure? *) (** Discard server side data for all clients, for the given scope. If the optional parameter [?persistent] is not present, both the persistent and volatile data will be removed. *) val discard_all_data : ?persistent:bool -> scope:Eliom_common.user_scope -> ?secure:bool -> unit -> unit Lwt.t (*VVV missing: scope group *) (*VVV missing ~secure? *) (** Remove all services registered for clients for the given scope. *) val discard_all_services : scope:Eliom_common.user_scope -> ?secure:bool -> unit -> unit Lwt.t (*VVV missing: scope group *) (*VVV missing ~secure? *) module Ext : sig (** Type used to describe session timeouts *) type timeout = | TGlobal (** see global setting *) | TNone (** explicitly set no timeout *) | TSome of float (** timeout duration in seconds *) (** These types are used to get or set information about browser or process cookies (like timeouts). *) type service_cookie_info type data_cookie_info type persistent_cookie_info (** The type of states. The first parameter corresponds to the scope level and the second one to the kind of state (volatile or persistent data, or service state) *) type (+'a, +'b) state (** [volatile_data_group_state ~scope n] returns the state corresponding to the group named [n] in scope [scope]. *) val volatile_data_group_state : ?scope:Eliom_common.session_group_scope -> string -> ([> `Session_group ], [> `Data ]) state (** Same for persistent data *) val persistent_data_group_state : ?scope:Eliom_common.session_group_scope -> string -> ([> `Session_group ], [> `Pers ]) state (** Same for services *) val service_group_state : ?scope:Eliom_common.session_group_scope -> string -> ([> `Session_group ], [> `Service ]) state (** [current_volatile_session_state ~scope] returns the state corresponding to current session in scope [scope]. *) val current_volatile_session_state : ?secure:bool -> ?scope:Eliom_common.session_scope -> unit -> ([< `Session ], [< `Data ]) state (** Same for persistent data *) val current_persistent_session_state : ?secure:bool -> ?scope:Eliom_common.session_scope -> unit -> ([< `Session ], [< `Pers ]) state Lwt.t (** Same for services *) val current_service_session_state : ?secure:bool -> ?scope:Eliom_common.session_scope -> unit -> ([< `Session ], [< `Service ]) state (** Discard external states *) val discard_state : state : ('a, 'b) state -> unit Lwt.t (** Fold all sessions in a groups, or all client processes in a session. *) val fold_volatile_sub_states : state : ([< `Session_group | `Session ], [< `Data | `Service ] as 'k) state -> ('a -> ([< `Session | `Client_process ], 'k) state -> 'a) -> 'a -> 'a (** Iter on all sessions in a groups, or all client processes in a session. *) val iter_volatile_sub_states : state: ([< `Session_group | `Session ], [< `Data | `Service ] as 'k) state -> (([< `Session | `Client_process ], 'k) state -> unit) -> unit (** Fold all sessions in a groups, or all client processes in a session. *) val fold_sub_states : state : ([< `Session_group | `Session ], [< `Data | `Pers | `Service ] as 'k) state -> ('a -> ([< `Session | `Client_process ], 'k) state -> 'a Lwt.t) -> 'a -> 'a Lwt.t (** Iter on all sessions in a groups, or all client processes in a session. *) val iter_sub_states : state: ([< `Session_group | `Session ], 'k) state -> (([< `Session | `Client_process ], 'k) state -> unit Lwt.t) -> unit Lwt.t module Low_level : sig (** Functions to access table data. Prefer using Eliom references. *) (** Raises [Not_found] if no data in the table for the cookie. *) val get_volatile_data : state:([< `Session_group | `Session | `Client_process ], [< `Data ]) state -> table:'a volatile_table -> 'a (** Fails with lwt exception [Not_found] if no data in the table for the cookie. *) val get_persistent_data : state:([< `Session_group | `Session | `Client_process ], [< `Pers ]) state -> table:'a persistent_table -> 'a Lwt.t val set_volatile_data : state:([< `Session_group | `Session | `Client_process ], [< `Data ]) state -> table:'a volatile_table -> 'a -> unit (** Fails with lwt exception [Not_found] if no data in the table for the cookie. *) val set_persistent_data : state:([< `Session_group | `Session | `Client_process ], [< `Pers ]) state -> table:'a persistent_table -> 'a -> unit Lwt.t val remove_volatile_data : state:([< `Session_group | `Session | `Client_process ], [< `Data ]) state -> table:'a volatile_table -> unit val remove_persistent_data : state:([< `Session_group | `Session | `Client_process ], [< `Pers ]) state -> table:'a persistent_table -> unit Lwt.t end (** Get the infomration about cookies (timeouts, etc.) *) val get_service_cookie_info : ([< Eliom_common.cookie_level ], [ `Service ]) state -> service_cookie_info val get_volatile_data_cookie_info : ([< Eliom_common.cookie_level ], [ `Data ]) state -> data_cookie_info val get_persistent_cookie_info : ([< Eliom_common.cookie_level ], [ `Pers ]) state -> persistent_cookie_info Lwt.t val get_service_cookie_scope : cookie:service_cookie_info -> Eliom_common.user_scope val get_volatile_data_cookie_scope : cookie:data_cookie_info -> Eliom_common.user_scope val get_persistent_data_cookie_scope : cookie:persistent_cookie_info -> Eliom_common.user_scope val set_service_cookie_timeout : cookie:service_cookie_info -> float option -> unit val set_volatile_data_cookie_timeout : cookie:data_cookie_info -> float option -> unit val set_persistent_data_cookie_timeout : cookie:persistent_cookie_info -> float option -> unit Lwt.t val get_service_cookie_timeout : cookie:service_cookie_info -> timeout val get_volatile_data_cookie_timeout : cookie:data_cookie_info -> timeout val get_persistent_data_cookie_timeout : cookie:persistent_cookie_info -> timeout val unset_service_cookie_timeout : cookie:service_cookie_info -> unit val unset_volatile_data_cookie_timeout : cookie:data_cookie_info -> unit val unset_persistent_data_cookie_timeout : cookie:persistent_cookie_info -> unit Lwt.t (** Returns a list containing the names of all session group that are available for this site. *) val get_session_group_list : unit -> string list (** Iterator on all active service cookies. [Lwt_unix.yield] is called automatically after each iteration. *) val iter_service_cookies : (service_cookie_info -> unit Lwt.t) -> unit Lwt.t (** Iterator on data cookies. [Lwt_unix.yield] is called automatically after each iteration. *) val iter_volatile_data_cookies : (data_cookie_info -> unit Lwt.t) -> unit Lwt.t (** Iterator on persistent cookies. [Lwt_unix.yield] is called automatically after each iteration. *) val iter_persistent_data_cookies : (persistent_cookie_info -> unit Lwt.t) -> unit Lwt.t (** Iterator on service cookies. [Lwt_unix.yield] is called automatically after each iteration. *) val fold_service_cookies : (service_cookie_info -> 'b -> 'b Lwt.t) -> 'b -> 'b Lwt.t (** Iterator on data cookies. [Lwt_unix.yield] is called automatically after each iteration. *) val fold_volatile_data_cookies : (data_cookie_info -> 'b -> 'b Lwt.t) -> 'b -> 'b Lwt.t (** Iterator on persistent cookies. [Lwt_unix.yield] is called automatically after each iteration. *) val fold_persistent_data_cookies : (persistent_cookie_info -> 'b -> 'b Lwt.t) -> 'b -> 'b Lwt.t (**/**) val untype_state : ('a, 'b) state -> ('c, 'd) state end (*****************************************************************************) (**/**) (** {3 Session data (deprecated interface)} *) (** This is the low level interface (deprecated). Use now Eliom references. *) (** The type used for getting data from a state. *) type 'a state_data = | No_data | Data_session_expired | Data of 'a (** {4 In memory state data} *) (** creates a table in memory where you can store the session data for all users. (low level) {e Warning: This functions must be called when the site information is available, that is, either during a request or during the initialisation phase of the site. Otherwise, it will raise the exception {!Eliom_common.Eliom_site_information_not_available}. If you are using static linking, you must delay the call to this function until the configuration file is read, using {!Eliom_service.register_eliom_module}. Otherwise you will also get this exception.} *) val create_volatile_table : scope:Eliom_common.user_scope -> ?secure:bool -> unit -> 'a volatile_table (** gets session data for the current session (if any). (low level) *) val get_volatile_data : table:'a volatile_table -> unit -> 'a state_data (** sets session data for the current session. (low level) *) val set_volatile_data : table:'a volatile_table -> 'a -> unit (** removes session data for the current session (but does not close the session). If the session does not exist, does nothing. (low level) *) val remove_volatile_data : table:'a volatile_table -> unit -> unit (**/**) (**/**) (** {4 Persistent state data} *) (** creates a table on hard disk where you can store the session data for all users. It uses {!Ocsipersist}. (low level) *) val create_persistent_table : scope:Eliom_common.user_scope -> ?secure:bool -> string -> 'a persistent_table (** gets persistent session data for the current persistent session (if any). (low level) *) val get_persistent_data : table:'a persistent_table -> unit -> 'a state_data Lwt.t (** sets persistent session data for the current persistent session. (low level) *) val set_persistent_data : table:'a persistent_table -> 'a -> unit Lwt.t (** removes session data for the current persistent session (but does not close the session). If the session does not exist, does nothing. (low level) *) val remove_persistent_data : table:'a persistent_table -> unit -> unit Lwt.t (**/**) (* (** {3 Default timeouts} *) (** returns the default timeout for service sessions (server side). The default timeout is common for all sessions for which no other value has been set. At the beginning of the server, it is taken from the configuration file, (or set to default value). [None] = no timeout. *) val get_default_service_session_timeout : unit -> float option (** returns the default timeout for "volatile data" sessions (server side). The default timeout is common for all sessions for which no other value has been set. At the beginning of the server, it is taken from the configuration file, (or set to default value). [None] = no timeout. *) val get_default_volatile_data_session_timeout : unit -> float option (** returns the default timeout for sessions (server side). The default timeout is common for all sessions for which no other value has been set. At the beginning of the server, it is taken from the configuration file, (or set to default value). [None] = no timeout. *) val get_default_persistent_data_session_timeout : unit -> float option (** sets the default timeout for volatile (= "in memory") sessions (i.e. both service session and volatile data session) (server side). [None] = no timeout. Warning: this function sets the default for all sites. You should probably use [set_global_volatile_session_timeout] instead. *) val set_default_volatile_session_timeout : float option -> unit (** sets the default timeout for service sessions. [None] = no timeout. Warning: this function sets the default for all sites. You should probably use [set_global_service_session_timeout] instead. *) val set_default_service_session_timeout : float option -> unit (** sets the default timeout for "volatile data" sessions (server side). [None] = no timeout. Warning: this function sets the default for all sites. You should probably use [set_global_volatile_data_session_timeout] instead. *) val set_default_volatile_data_session_timeout : float option -> unit (** sets the default timeout for sessions (server side). [None] = no timeout. Warning: this function sets the default for all sites. You should probably use [set_global_persistent_data_session_timeout] instead. *) val set_default_persistent_data_session_timeout : float option -> unit *) (*****************************************************************************) (**/**) (** {3 Other low level functions} You probably don't need these functions. *) (** returns the value of the Eliom's cookies for one persistent session. Returns [None] is no session is active. *) val get_persistent_data_cookie : cookie_scope:Eliom_common.cookie_scope -> ?secure:bool -> unit -> string option Lwt.t (** returns the value of Eliom's cookies for one service session. Returns [None] is no session is active. *) val get_service_cookie : cookie_scope:Eliom_common.cookie_scope -> ?secure:bool -> unit -> string option (** returns the value of Eliom's cookies for one "volatile data" session. Returns [None] is no session is active. *) val get_volatile_data_cookie : cookie_scope:Eliom_common.cookie_scope -> ?secure:bool -> unit -> string option (**/**) (**/**) (*****************************************************************************) val number_of_service_cookies : unit -> int val number_of_volatile_data_cookies : unit -> int val number_of_tables : unit -> int val number_of_table_elements : unit -> int list val number_of_persistent_data_cookies : unit -> int Lwt.t val number_of_persistent_tables : unit -> int val number_of_persistent_table_elements : unit -> (string * int) list Lwt.t (* Because of Dbm implementation, the result may be less than the expected result in some case (with a version of ocsipersist based on Dbm) *) val get_global_table : unit -> Eliom_common.tables val get_session_service_table : sp:Eliom_common.server_params -> scope:Eliom_common.user_scope -> ?secure:bool -> unit -> Eliom_common.tables ref val get_session_service_table_if_exists : sp:Eliom_common.server_params -> scope:Eliom_common.user_scope -> ?secure:bool -> unit -> Eliom_common.tables ref val create_volatile_table_during_session_ : scope:Eliom_common.user_scope -> secure:bool -> Eliom_common.sitedata -> 'a volatile_table eliom-3.0.3/src/server/eliom_lib.ml0000644000000000000000000000622612062377521015405 0ustar0000000000000000 include Ocsigen_lib include (Eliom_lib_base : module type of Eliom_lib_base with type 'a Int64_map.t = 'a Eliom_lib_base.Int64_map.t with type 'a String_map.t = 'a Eliom_lib_base.String_map.t with type 'a Int_map.t = 'a Eliom_lib_base.Int_map.t with type escaped_value = Eliom_lib_base.escaped_value with type +'a Client_value_server_repr.t = 'a Eliom_lib_base.Client_value_server_repr.t with type client_value_datum = Eliom_lib_base.client_value_datum with type 'a injection_datum = 'a Eliom_lib_base.injection_datum with type 'a compilation_unit_global_data = 'a Eliom_lib_base.compilation_unit_global_data with type 'a global_data := 'a Eliom_lib_base.global_data with type request_data = Eliom_lib_base.request_data) let escaped_value_escaped_value = fst let debug f = Printf.ksprintf (fun s -> Printf.eprintf "%s\n%!" s) f let to_json ?typ v = match typ with | Some typ -> Deriving_Json.to_string typ v | None -> assert false (* implemented only client side *) let of_json ?typ s = match typ with | Some typ -> Deriving_Json.from_string typ s | None -> assert false (* implemented only client side *) type file_info = Ocsigen_extensions.file_info let string_escape s = let l = String.length s in let b = Buffer.create (4 * l) in let conv = "0123456789abcdef" in for i = 0 to l - 1 do let c = s.[i] in match c with '\000' when i = l - 1 || s.[i + 1] < '0' || s.[i + 1] > '9' -> Buffer.add_string b "\\0" | '\b' -> Buffer.add_string b "\\b" | '\t' -> Buffer.add_string b "\\t" | '\n' -> Buffer.add_string b "\\n" (*| '\011' -> (* IE<9 doesn't like vertical tab \v *) Buffer.add_string b "\\v"*) | '\012' -> Buffer.add_string b "\\f" | '\r' -> Buffer.add_string b "\\r" | '\'' -> Buffer.add_string b "\\'" | '\\' -> Buffer.add_string b "\\\\" | '\000' .. '\031' | '\127' .. '\255' | '&' | '<' | '>' -> let c = Char.code c in Buffer.add_string b "\\x"; Buffer.add_char b conv.[c lsr 4]; Buffer.add_char b conv.[c land 0xf] | _ -> Buffer.add_char b c done; Buffer.contents b let jsmarshal v = string_escape (Marshal.to_string v []) let wrap_and_marshall_poly : poly -> string = fun poly -> string_escape (Marshal.to_string (Eliom_wrap.wrap poly) []) type 'a client_value = 'a Client_value_server_repr.t * Eliom_wrap.unwrapper let create_client_value cv = cv, Eliom_wrap.create_unwrapper (Eliom_wrap.id_of_int Eliom_lib_base.client_value_unwrap_id_int) let client_value_server_repr = fst exception Client_value_creation_invalid_context of int64 let escaped_value value : escaped_value (* * Eliom_wrap.unwrapper *) = to_poly value type global_data = poly Eliom_lib_base.global_data * Eliom_wrap.unwrapper let global_data_unwrapper = Eliom_wrap.create_unwrapper (Eliom_wrap.id_of_int global_data_unwrap_id_int) eliom-3.0.3/src/server/eliom_extension_template.ml0000644000000000000000000000322212062377521020537 0ustar0000000000000000(* Ocsigen * http://www.ocsigen.org * Module extensiontemplate.ml * Copyright (C) 2007 Vincent Balat * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, with linking exception; * either version 2.1 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *) (*****************************************************************************) (*****************************************************************************) (* This is an example of extension for Ocsigen *) (* Take this as a template for writing your own Eliom based extensions to the Web server *) (*****************************************************************************) (*****************************************************************************) let _ = Eliom_extension.register_eliom_extension (fun sp -> Lwt.return (Ocsigen_extensions.Ext_found (fun () -> let content = "Eliom Extension template page" in Ocsigen_senders.Text_content.result_of_content (content, "text/plain")))) eliom-3.0.3/src/server/eliom_content.mli0000644000000000000000000002401012062377521016451 0ustar0000000000000000(* Ocsigen * http://www.ocsigen.org * Copyright (C) 2012 Vincent Balat, Benedikt Becker * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, with linking exception; * either version 2.1 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *) (** This module provides the creation of valid XML content, i.e. XML, SVG, and (X)HTML5. XML tree manipulation within Eliom is based on the TyXML library but use a custom representation for XML values (see {!Xml}). Then, [Eliom_content] redefines the three high level interfaces ({!Svg}, {!Html5}) that are provided by TyXML for valid XML tree creation and printing. Modules {!Eliom_content.Html5}, {!Eliom_content.Svg} contain two implementing sub-modules: {!Eliom_content.Html5.F} and {!Eliom_content.Html5.D}. {5 Functional semantics} The [F] modules provide functions to create elements with {e f}unctional semantics: On the one hand side, those values do not have an identifier, which means utilizations of those values are independent of each other. On the other hand side, they cannot be referred to, neither by client code when created on the server, nor for usage in the functions of {% <> %} and {% <> %}. {5 DOM semantics} The [D] modules provide functions to create elements with {e D}OM semantics: Firstly, they behave like DOM nodes, e.g. they can only be added once to the DOM tree even when appended several times. Secondly, those values have an identifier, which means they can be referred to on the client side (by [%variable]) or used with the functions in {% <> %} and {% <> %}. In case of doubt, use the modules with DOM-like semantics {!Eliom_content.Html5.D}. *) (** Abstract signature for links and forms creation functions. For concrete instance see {!Html5}, or {!Html_text}. *) module type Forms = "sigs/eliom_forms.mli" (** Low-level XML manipulation. *) module Xml : module type of Eliom_content_core.Xml with type uri = Eliom_content_core.Xml.uri and type attrib = Eliom_content_core.Xml.attrib and type elt = Eliom_content_core.Xml.elt and type event_handler = Eliom_content_core.Xml.event_handler and type event_handler_table = Eliom_content_core.Xml.event_handler_table and type -'a caml_event_handler = 'a Eliom_content_core.Xml.caml_event_handler (** Building and pretty-printing valid SVG tree. *) module Svg : module type of Eliom_content_core.Svg with type uri = Eliom_content_core.Svg.uri and type 'a attrib = 'a Eliom_content_core.Svg.attrib and type +'a elt = 'a Eliom_content_core.Svg.elt (** Building and printing valid (X)HTML5 tree. *) module Html5 : sig (** See the Eliom manual for more information on {% <> %} for HTML5 tree manipulated by client/server application. *) type +'a elt = 'a Eliom_content_core.Html5.elt type +'a attrib = 'a Eliom_content_core.Html5.attrib type uri = Eliom_content_core.Html5.uri (** Creation of {b F}unctional HTML5 content (copy-able but not referable, see also {% <> %}). *) module F : sig (** {2 Content creation} See {% <> %} *) open Pervasives include module type of Eliom_content_core.Html5.F with type Xml.uri = Xml.uri and type Xml.event_handler = Xml.event_handler and type Xml.attrib = Xml.attrib and type Xml.elt = Xml.elt with type +'a elt = 'a elt and type 'a attrib = 'a attrib and type uri = uri include "sigs/eliom_html5_forms.mli" (** Creates an untyped form. *) val raw_form : ([< Html5_types.form_attrib ], [< Html5_types.form_content_fun ], [> Html5_types.form ]) plus (** This is an alias to {% <> %} to avoid the untyped [Eliom_content_core.Html5.F.form]. *) val form : ?absolute:bool -> ?absolute_path:bool -> ?https:bool -> ?a:Html5_types.form_attrib attrib list -> service:('get, unit, [< get_service_kind ], [ ?hostname:string -> ?port:int -> ?fragment:string -> ?keep_nl_params:[ `All | `Persistent | `None ] -> ?nl_params: Eliom_parameter.nl_params_set -> ?xhr:bool -> ('gn -> Html5_types.form_content elt list) -> [> Html5_types.form ] elt (** This is an alias to {% <> %} to avoid the untyped [Eliom_content_core.Html5.F.input]. *) val input : ?a:Html5_types.input_attrib attrib list -> input_type:[< | `Url | `Tel | `Text | `Time | `Search | `Password | `Checkbox | `Range | `Radio | `Submit | `Reset | `Number | `Hidden | `Month | `Week | `File | `Email | `Image | `Datetime_local | `Datetime | `Date | `Color | `Button] -> ?name:[< string setoneradio ] param_name -> ?value:string -> unit -> [> Html5_types.input ] elt (** This is an alias to {% <> %} to avoid the untyped [Eliom_content_core.Html5.F.select]. *) val select : ?a:Html5_types.select_attrib attrib list -> name:[< `One of string ] param_name -> string select_opt -> string select_opt list -> [> Html5_types.select ] elt end (** Creation of HTML5 content with {b D}OM semantics (referable, see also {% <> %}). *) module D : sig (** {2 Content creation} See {% <> %} *) open Pervasives include module type of Eliom_content_core.Html5.D with type Xml.uri = Xml.uri and type Xml.event_handler = Xml.event_handler and type Xml.attrib = Xml.attrib and type Xml.elt = Xml.elt with type +'a elt = 'a elt and type 'a attrib = 'a attrib and type uri = uri include "sigs/eliom_html5_forms.mli" (** Creates an untyped form. *) val raw_form : ([< Html5_types.form_attrib ], [< Html5_types.form_content_fun ], [> Html5_types.form ]) plus (** This is an alias to {% <> %} to avoid the untyped [Eliom_content_core.Html5.D.form]. *) val form : ?absolute:bool -> ?absolute_path:bool -> ?https:bool -> ?a:Html5_types.form_attrib attrib list -> service:('get, unit, [< get_service_kind ], [ ?hostname:string -> ?port:int -> ?fragment:string -> ?keep_nl_params:[ `All | `Persistent | `None ] -> ?nl_params: Eliom_parameter.nl_params_set -> ?xhr:bool -> ('gn -> Html5_types.form_content elt list) -> [> Html5_types.form ] elt (** This is an alias to {% <> %} to avoid the untyped [Eliom_content_core.Html5.D.input]. *) val input : ?a:Html5_types.input_attrib attrib list -> input_type:[< | `Url | `Tel | `Text | `Time | `Search | `Password | `Checkbox | `Range | `Radio | `Submit | `Reset | `Number | `Hidden | `Month | `Week | `File | `Email | `Image | `Datetime_local | `Datetime | `Date | `Color | `Button] -> ?name:[< string setoneradio ] param_name -> ?value:string -> unit -> [> Html5_types.input ] elt (** This is an alias to {% <> %} to avoid the untyped [Eliom_content_core.Html5.D.select]. *) val select : ?a:Html5_types.select_attrib attrib list -> name:[< `One of string ] param_name -> string select_opt -> string select_opt list -> [> Html5_types.select ] elt end (** Node identifiers *) module Id : module type of Eliom_content_core.Html5.Id with type +'a id = 'a Eliom_content_core.Html5.Id.id module Custom_data : module type of Eliom_content_core.Html5.Custom_data with type 'a t = 'a Eliom_content_core.Html5.Custom_data.t module Printer : module type of Eliom_content_core.Html5.Printer end module Html_text : sig include "sigs/eliom_forms.mli" subst type uri := string and type pcdata_elt := string and type form_elt := string and type form_content_elt := string and type form_content_elt_list := string and type form_attrib_t := string and type 'a a_elt := string and type 'a a_content_elt := string and type 'a a_content_elt_list := string and type a_attrib_t := string and type link_elt := string and type link_attrib_t := string and type script_elt := string and type script_attrib_t := string and type textarea_elt := string and type textarea_attrib_t := string and type input_elt := string and type input_attrib_t := string and type select_elt := string and type select_attrib_t := string and type button_elt := string and type button_content_elt := string and type button_content_elt_list := string and type button_attrib_t := string and type optgroup_attrib_t := string and type option_attrib_t := string and type input_type_t := string and type button_type_t := string and type for_attrib := string end eliom-3.0.3/src/server/eliom_pervasives.mli0000644000000000000000000000436612062377521017202 0ustar0000000000000000 open Eliom_pervasives_base (** This module is automatically open by {v eliomc v} and {v js_of_eliom v}. *) (** {2 Client values} See the {% <> %}. *) (** Client values on the server are created by the syntax [{typ{ expr }}] in the server section (cf. {% <> %}). They are abstract, but become concrete once sent to the client. See also {% <> %}. *) type 'a client_value = 'a Eliom_lib.client_value (** {2 RPC / Server functions} See the {% <> %}.*) (** A value of type [('a, 'b) server_function] is created on the server from a function ['a -> 'b Lwt.t] and provides a given function on the client side. See also {% <> %}. *) type ('a, 'b) server_function (** [server_function argument_type f] creates a value of type {% <> %}. This allows to call [f] from the client. The first argument [argument_type] is an instance of [Deriving_Json] for the type of the argument. It is used to safely encode and decode the argument sent to the server. The optional parameters correspond directly to the optional parameters of {% <> %}. See also the {% <> %}. *) (* BBB This is not in Eliom_service because it depends on Eliom_registration *) val server_function : ?scope:[< Eliom_common.scope ] -> ?options:unit -> ?charset:string -> ?code:int -> ?content_type:string -> ?headers:Http_headers.t -> ?secure_session:bool -> ?name:string -> ?csrf_safe:bool -> ?csrf_scope:[< Eliom_common.user_scope ] -> ?csrf_secure:bool -> ?max_use:int -> ?timeout:float -> ?https:bool -> ?error_handler:((string * exn) list -> 'b Lwt.t) -> 'a Deriving_Json.t -> ('a -> 'b Lwt.t) -> ('a, 'b) server_function eliom-3.0.3/src/server/eliom_wrap.mli0000644000000000000000000000334512062377521015760 0ustar0000000000000000(* Ocsigen * Copyright (C) 2011 Pierre Chambart * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, with linking exception; * either version 2.1 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *) type 'a wrapped_value (** ['a wrapper] is the type of values to include into a value of type 'a for it to be wraped specificaly *) type +'a wrapper (** [create f] create a new tag that can be included into a value. if [wrap] is called on a father of a value [v] containing a tag, the value [v] will be replaced by [f v] before marshaling. *) val create_wrapper : ( 'a -> 'b ) -> 'a wrapper (** marshal a value, taking into account the tags. *) (* == Internals [wrap v] traverses the OCaml structure of the value [v], replacing all included values [w] whose last object field (cf. [Obj.field]) is a wrapper created by [create_wrapper f] by [f w]. *) val wrap : 'a -> 'a wrapped_value (** a wrapper that do not change the value *) val empty_wrapper : 'a wrapper (** unwrap **) type unwrap_id type unwrapper val create_unwrapper : unwrap_id -> unwrapper val empty_unwrapper : unwrapper val id_of_int : int -> unwrap_id eliom-3.0.3/src/server/eliom_content_core.mli0000644000000000000000000002575512062377521017502 0ustar0000000000000000(* Ocsigen * http://www.ocsigen.org * Copyright (C) 2012 Vincent Balat, Benedikt Becker * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, with linking exception; * either version 2.1 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *) (** See {% <> %} for complete module. *) (** Low-level XML manipulation. *) module Xml : sig (** {2 Base functions} Cf. {% <> %}. *) include Xml_sigs.Iterable (** {2 Unique nodes } *) (** Unique nodes are XML nodes that are manipulated 'by reference' when sent to the client part of an Eliom-application: the created element is allocated only one time in each instance of an application. See {% <>%} for more details. *) (** Event handlers *) (** Values of type ['a caml_event_handler] represents event handler build with the [{{ ... }}] syntax (see the Eliom manual for more information on {% <>%}). Such values are expected by functions like {!Eliom_service.on_load} or {!Eliom_content.Html5.a_onclick}. The type parameter is the type of the javascript event expected by the handler, for example {% <>%} or {% <>%}. *) type -'a caml_event_handler constraint 'a = #Dom_html.event (**/**) val make_process_node : ?id:string -> elt -> elt val make_request_node : elt -> elt val uri_of_fun: (unit -> string) -> uri (* Building ref tree. *) type event_handler_table (* Concrete on client-side only. *) type node_id val get_node_id : elt -> node_id val make_event_handler_table : elt -> event_handler_table val event_handler_of_string : string -> event_handler val string_of_event_handler : event_handler -> string val event_handler_of_service : ( [ `A | `Form_get | `Form_post ] * (bool * string list) option * string option) option Eliom_lazy.request -> event_handler val caml_event_handler : ((#Dom_html.event as 'a) Js.t -> unit) Eliom_lib.client_value -> 'a caml_event_handler val event_handler : (Dom_html.event Js.t -> unit) Eliom_lib.client_value -> event_handler type racontent = | RA of acontent | RACamlEventHandler of Dom_html.event caml_event_handler | RALazyStr of string Eliom_lazy.request | RALazyStrL of separator * string Eliom_lazy.request list val racontent : attrib -> racontent val lazy_node : ?a:(attrib list) -> ename -> elt list Eliom_lazy.request -> elt (**/**) (** [Eliom_content.Xml.wrap page v] is like [Eliom_wrap.wrap v] but it makes sure that all [elt]s in [v] which are included in [page] are sent with empty content. This is safe because such elements will be taken from the DOM on the client either ways. *) val wrap : elt -> 'a -> 'a Eliom_wrap.wrapped_value end (**/**) module Eliom_xml : module type of Xml with type uri = Xml.uri and type separator = Xml.separator and type acontent = Xml.acontent and type attrib = Xml.attrib and type elt = Xml.elt and type -'a caml_event_handler = 'a Xml.caml_event_handler (**/**) (** Building and pretty-printing valid SVG tree. *) module Svg : sig (** See the Eliom manual for more information on{% <> %} for SVG tree manipulated by client/server application. *) type +'a elt type 'a attrib type uri = Xml.uri (** Typed interface for building valid SVG tree (functional semantics). See {% <> %}. *) module F : Svg_sigs.T with type Xml.uri = Xml.uri and type Xml.event_handler = Xml.event_handler and type Xml.attrib = Xml.attrib and type Xml.elt = Xml.elt with type 'a elt = 'a elt and type 'a attrib = 'a attrib and type uri = uri (** Typed interface for building valid SVG tree (DOM semantics). See {% <> %}. *) module D : Svg_sigs.T with type Xml.uri = Xml.uri and type Xml.event_handler = Xml.event_handler and type Xml.attrib = Xml.attrib and type Xml.elt = Xml.elt with type 'a elt = 'a elt and type 'a attrib = 'a attrib and type uri = uri (** Node identifiers. *) module Id : sig (** The type of global SVG element identifier. *) type +'a id (** The function [new_elt_id ()] creates a new HTML5 element identifier. (see the Eliom manual for more information on {% <>%}).*) val new_elt_id: ?global:bool -> unit -> 'a id (** The function [create_named_elt ~id elt] create a copy of the element [elt] that will be accessible through the name [id]. *) val create_named_elt: id:'a id -> 'a elt -> 'a elt (** The function [create_named_elt elt] is equivalent to [create_named_elt ~id:(new_elt_id ()) elt]. *) val create_global_elt: 'a elt -> 'a elt end (** SVG printer. See {% <> %}. *) module Printer : Xml_sigs.Typed_simple_printer with type 'a elt := 'a F.elt and type doc := F.doc end (** Building and printing valid (X)HTML5 tree. *) module Html5 : sig (** See the Eliom manual for more information on {% <> %} for HTML5 tree manipulated by client/server application. *) type +'a elt type +'a attrib type uri = Xml.uri (** Typed interface for building valid HTML5 tree (functional semantics). *) module F : sig (** Cf. {% <> %}. *) module Raw : Html5_sigs.T with type Xml.uri = Xml.uri and type Xml.event_handler = Xml.event_handler and type Xml.attrib = Xml.attrib and type Xml.elt = Xml.elt with module Svg := Svg.F with type +'a elt = 'a elt and type 'a attrib = 'a attrib and type uri = uri include module type of Raw (*BB TODO Hide untyped [input]. *) (** {2 Event handlers} *) (** Redefine event handler attributes to simplify their usage. *) include "sigs/eliom_html5_event_handler.mli" (**/**) type ('a, 'b, 'c) lazy_plus = ?a: (('a attrib) list) -> 'b elt Eliom_lazy.request -> ('b elt) list Eliom_lazy.request -> 'c elt val lazy_form: ([< Html5_types.form_attrib ], [< Html5_types.form_content_fun ], [> Html5_types.form ]) lazy_plus end (** Typed interface for building valid HTML5 tree (DOM semantics). *) module D : sig (** Cf. {% <> %}. *) module Raw : Html5_sigs.T with type Xml.uri = Xml.uri and type Xml.event_handler = Xml.event_handler and type Xml.attrib = Xml.attrib and type Xml.elt = Xml.elt with module Svg := Svg.D with type +'a elt = 'a elt and type 'a attrib = 'a attrib and type uri = uri include module type of Raw (*BB TODO Hide untyped [input]. *) (** {2 Event handlers} *) (** Redefine event handler attributes to simplify their usage. *) include "sigs/eliom_html5_event_handler.mli" (**/**) type ('a, 'b, 'c) lazy_plus = ?a: (('a attrib) list) -> 'b elt Eliom_lazy.request -> ('b elt) list Eliom_lazy.request -> 'c elt val lazy_form: ([< Html5_types.form_attrib ], [< Html5_types.form_content_fun ], [> Html5_types.form ]) lazy_plus end (** Node identifiers *) module Id : sig (** The type of global HTML5 element identifier. *) type +'a id (** The function [new_elt_id ()] creates a new global HTML5 element identifier (see the Eliom manual for more information on {% <>%}).*) val new_elt_id: ?global:bool -> unit -> 'a id (** The function [create_named_elt ~id elt] create a copy of the element [elt] that will be sent to client with the reference [id]. *) val create_named_elt: id:'a id -> 'a elt -> 'a elt (** The function [create_named_elt elt] is equivalent to [create_named_elt ~id:(new_elt_id ()) elt]. *) val create_global_elt: 'a elt -> 'a elt (**/**) val have_id: 'a id -> 'b elt -> bool end (** Type-safe custom data for HTML5. See the {% <> %}. *) module Custom_data : sig (** Custom data with values of type ['a]. *) type 'a t (** Create a custom data field by providing string conversion functions. If the [default] is provided, calls to {% <> %} return that instead of throwing an exception [Not_found]. *) val create : name:string -> ?default:'a -> to_string:('a -> string) -> of_string:(string -> 'a) -> unit -> 'a t (** Create a custom data from a Json-deriving type. *) val create_json : name:string -> ?default:'a -> 'a Deriving_Json.t -> 'a t (** [attrib my_data value ] creates a HTML5 attribute for the custom-data type [my_data] with value [value] for injecting it into an a HTML5 tree ({% <> %}). *) val attrib : 'a t -> 'a -> [> | `User_data ] attrib end (** {{:http://dev.w3.org/html5/html-xhtml-author-guide/}"Polyglot"} HTML5 printer. See {% <> %}. *) module Printer : Xml_sigs.Typed_simple_printer with type 'a elt := 'a F.elt and type doc := F.doc end eliom-3.0.3/src/server/eliom_validate_forms.eliom0000644000000000000000000002712712062377521020336 0ustar0000000000000000(* Eliom_validate_forms * Copyright (C) 20010 Simon Castellan * For ocsigen * http://www.ocsigen.org * Laboratoire PPS - CNRS Université Paris Diderot * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, with linking exception; * either version 2.1 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *) (* Eliom_validate_forms Automatic validation of forms. This modules provides a form validation system client-side. It does not replace a check on the server, as data coming from the client should not be strusted. The idea is to reuse the information parameters (Eliom_parameter.params_type) to check whether a form's value is "valid". So you should enforce verification on the parameters via the user_type parameter. *) (* XX: maybe some things should be factorized out of the example. *) (** Here is a small example : a login box {[ open Eliom_validate_forms module App = Eliom_predefmod.App ( struct let application_name = "eliom_validate_forms_example" let params = {Eliom_predefmod.default_appl_params with Eliom_predefmod.ap_title = "eliom_validate_forms_example"; Eliom_predefmod.ap_headers = [XHTML.M.link ~a:[a_href (uri_of_string "style.css"); a_rel [`Stylesheet]] ()]; Eliom_predefmod.ap_container = Some (None, fun div -> [div]) } end) ;; module Forms = ValidateForms (App) let login_form = new_service ~path:["test"] ~get_params: unit () let string_guard f = user_type ~to_string:(fun s -> s) ~of_string: (fun s -> if not (f s) then failwith "invalid"; s) let args = (string_guard ((=) "test") "nick" ** string_guard (fun s -> String.length s >= 8) "password") let validate = App.register_new_service ~path:["validate"] ~get_params: args (fun sp _ number -> return [div [pcdata "You passed!"]]) ;; {client{ let replace_child node new_children = let rec remove_children () = match Js.Opt.to_option (node##firstChild) with | Some child -> Dom.removeChild node child; remove_children () | None -> () in remove_children (); List.iter (Dom.appendChild node) new_children }} ;; let popup ~sp cl content container = {{ let container = lookup $ magic: container $ in Lwt.return (replace_child container (XHTML.M.toeltl [span ~a:[a_class $ magic : cl $] [pcdata $ magic : content $]])) }} let entry gen_input ?value ?delay ?a ?server_listen ?server_check ~prompt ~sp ~input_type ~name ~id f = label [pcdata prompt; gen_input ?value ?delay ?a ~input_type ?server_listen ?server_check (fun x -> f x id) name; span ~a:[a_id id] []; br ()] let _ = App.register ~service:login_form (fun sp () () -> let form = Forms.gen_form (fun ~service ~sp f -> App.get_form ~service ~sp f) (validate, (args, unit)) ~sp ~on_fail: (popup ~sp ["bad"] "Invalid form" "result") (fun gen_input (nick, password) -> let entry = entry gen_input ~server_listen: [a_onchange] in [fieldset ~a:[a_id "fieldset"] [div ~a:[a_id "result"][]; entry ~prompt: "Nickname:" ~sp ~input_type: `Text ~name: nick ~id: "nick" ~server_check: (fun _ param -> Lwt_unix.sleep 2. >>= (fun () -> return (param = "asmanur"))) (function | `Success -> popup ~sp ["ok"] "Nickname correct" | `Failure -> popup ~sp ["bad"] "Invalid nickname" | `Loading -> popup ~sp [] "Waiting…"); entry ~prompt: "Password:" ~sp ~input_type: `Password ~name: password ~id: "password" (function | `Success -> popup ~sp ["ok"] "valid" | `Failure -> popup ~sp ["bad"] "Invalid password (must be >= 8 characters long)" | `Loading -> popup ~sp [] ""); App.string_input ~input_type: `Submit ~value: "Submit" ()]]) in return [div [form]]) ]} *) open Lwt open XHTML.M open Eliom_parameter {client{ open Lwt open Dom_html open Eliom_client let lookup ?error name = Js.Opt.get (document##getElementById (Js.string name)) (fun () -> failwith (match error with Some s -> s | None -> name)) let coerce f arg = Js.Opt.get (f arg) (fun () -> failwith "Invalid element") (* We store some data about the forms in the page, client-side. *) (* For each form, we have a list of (param_name, validation_code), that is used when we check that the form is valid before submission. *) let inputs = ref [] (* Adds a field to the input. Called in [gen_input] *) let set_field (form: string) (field: string) valid = let found = ref false in inputs := List.map (fun (form', l) -> if form' = form then (found := true; (form', (field, valid) :: l)) else (form', l)) !inputs; if not !found then inputs := (form, [field, valid]) :: !inputs (* For a given form, returns the list of the inputs that are not valid *) let get_invalids form_name = Lwt_list.filter_p (fun (name, valid) -> Js.Unsafe.variable valid >|= not) (try List.assoc form_name !inputs with Not_found -> []) >|= List.map fst }} module ValidateForms (Appl : Eliom_predefmod.XHTMLFORMSSIG ) = struct (** Status of a validation : - Loading : used to display a nice message for validation that may take time : should be empty for fast checks. - Success : when the input is valid - Failure : when the input is invalid *) module Appl = struct include Appl end type status = [ `Loading | `Success | `Failure ] (** Generate a form with automatic validation. Arguments : - on_fail : an optional javascript code (of type unit Lwt.t) that is executed when the whole form is checked but that there is some invalid inputs. - form_name : an optional name to distinguish between several forms in the same page. You should specify one if there is more that one form in your page ! - create : the function from Appl to build the form (post_form or get_form) - a pair (service, args) where service is an eliom service and args are the post parameters of the service - the server params - a function that is called to build the form. In the same way than Eliom_predefmod.X.post_form takes a function that builds the form gen_form does the same, except that the function does take on top of the parameter names, a function, [gen_input] used to generate an input checking its contents. gen_input takes several arguments : - the optional value of the input - delay : optional time (defaults to 100 ms) to wait before checking an input's validity. - an optional list of attributes - a mandatory input_type - an optional list of events to listen on for server-side check (eg. a_onchange, etc.) - an optional function, checking the contents to see if it is valid. It default to the function specified in the arguments of the service. But sometimes, you want to do something that takes time : for instance checking that an user exists in database. - A function f (server-side) that takes a status and should generate the corresponding javascript code (of type unit Lwt.t). - name : the parameter name. *) (* TODO: - provides support for client-side check. We need to wait for a better support of the inline javascript code - for now the form checking is done by clicking on a link because we can't listen on the onsubmit event of a form thanks to eliom - We use Eliom_service.set_on_load that means we can be replaced by any user script and that we may erase one of his, eliom doesn't provide a add_on_load function - We use some getElementById to get an input's contents. This generates htmlcode with a lot of useless id's. This should be changed as we can express things like that {{ fun evt self -> ... }} - Do some wrappers : gen_get_form, gen_lwt_get_form, ... *) let get_id = let k = ref 0 in fun () -> incr k; !k let gen_form ?on_fail ?(form_name = "form") create (service, (get, post)) ~sp form_contents = let args = get ** post in let submit = {{ get_invalids $ magic : form_name $ >>= fun invalids -> if invalids <> [] then (match $ magic : on_fail $ with | Some s -> Firebug.console##log (String.concat " " invalids); Js.Unsafe.variable s | None -> return ()) else return () }} in (* List of javascript code to execute on startup. Basically it fills the inputs list below *) let scripts = ref [] in let gen_input ?value ?(delay = 0.1) ?(a = []) ~input_type ?(server_listen = []) ?server_check f name = let id = "__eliom_form_" ^ form_name ^ "__" ^ string_of_int (get_id ()) in let from_string, to_string = match walk_parameter_tree (Obj.magic name) args with | Some a -> a | None -> failwith "Invalid name" in let check = match server_check with | Some f -> f | None -> fun _ arg -> return (try ignore (from_string arg); true with _ -> false) in (* We generate a service dedicated to checking that this parameter is valid. It may not be the better way to do this, but eh. *) let service = Eliom_predefmod.Ocaml.register_new_post_coservice' ~post_params: (string "params") ~sp (fun sp _ arg -> check sp arg) in (* We use a trick to provide client-side functions in javascript code. As we need to evaluate [f] on only three values, we do this on the server and send the resulting javacsript code to the client. We should use client functions as they are available *) let handler = let onloading, onsuccess, onfailure = f `Loading, f `Success, f `Failure in {{ let elem = coerce CoerceTo.input (lookup $ magic : id $) in let exec s = Js.Unsafe.variable s in exec $ magic:onloading $ >>= fun () -> Lwt_js.sleep $ magic: delay $ >>= (fun () -> call_caml_service ~service: $ magic:service $ ~sp: $ sp:sp $ () (Js.to_string elem##value) >>= (fun b -> Lwt.catch (fun () -> if b then exec $ magic : onsuccess $ else exec $ magic : onfailure $) (fun e -> return ()) >>= (fun _ -> return b))) }} in let () = scripts := {{ set_field $ magic : form_name $ $ magic : name $ $ magic : handler $ }} :: !scripts in let a = a_id id :: List.map (fun x -> x handler) server_listen @ a in Appl.user_type_input to_string ?value ~input_type ~a ~name () in let form_contents = form_contents gen_input in let form = create ~service ~sp form_contents in (* so that side-effects (scripts-filling) take place *) let _ = Eliom_service.set_on_load ~sp (String.concat ";\n" !scripts) in div [XHTML.M.a ~a:[a_href (uri_of_string "#"); a_onclick submit] [pcdata "Check the form"] ; form] ;; end eliom-3.0.3/src/server/eliom_react.mli0000644000000000000000000000771512062377521016112 0ustar0000000000000000(* Ocsigen * http://www.ocsigen.org * Copyright (C) 2010 * Raphaël Proust * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, with linking exception; * either version 2.1 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *) (** Propagate events occurrences from the server to the client and the other way around. Occurrence propagation is done asynchronously. The use of this module is pretty much useless without it's client counter part. *) (* These two dual files are to be modified together with compatibility issues in mind. *) (** Event from server to client. *) module Down : sig (** A "Down event" (AKA down-going event) is an event which occurrences are transmitted asynchronously to the client. Even if they are named "events", it might be better to consider them as asynchronous server-to-client edges in the react events dependency graph. To use this, call function [of_react] on server side, and just use the returned value as a react event on client side. Example: [let e = of_react ... in ... {{ ... React.E.map f %e; ... }}] *) (** The abstract type of down events. *) type 'a t (** [of_react ?scope ?throttling ?name e] create an asynchronous edge originating from [e]. The parameters are: [throttling] for the limit to event propagation rate, [name] for named edges, [size] for the size of the server side buffer. [scope] tell which kind of channel this rely on (See [Eliom_comet.create]). *) val of_react : ?scope:[ ?throttling:float -> ?name:string -> ?size:int -> 'a React.E.t -> 'a t end (** Event from client to server. *) module Up : sig (** Up events are quite different from Down events. Because of the asymmetrical nature of web programming and because of the reactive model used, an Up event must be created on the server and wrapped into a callback (or something the client can build a callback with). Example of use: [let e_up = Eliom_react.Up.create (Eliom_parameter.caml "a" Json.t) in ... {{ ignore ( %e_up "A") }} ... ] *) (** The type of events that, while being "on the server", are triggered by clients. On the server such an event is /primitive/ (hence the [create] function) whereas it is /effect-full/ on the client. *) type 'a t (** [to_react e] injects the up events [e] into react events so that it can be manipulated as a standard event. *) val to_react : 'a t -> 'a React.E.t (** [create param] creates an Up event. If [~name] is present, the coservice used to transmit the event will always have the same name, even if the server is restarted. [~scope] describes the visibility of the event. By default, it is [`Site] if it is called during initialisation, [`Client_process] otherwise. *) val create : ?scope:Eliom_common.scope -> ?name:string -> ('a, [ `WithoutSuffix ], [ `One of 'a Eliom_parameter.caml ] Eliom_parameter.param_name) Eliom_parameter.params_type -> 'a t end module S : sig module Down : sig (** The abstract type of down signals. *) type 'a t val of_react : ?scope:[ ?throttling:float -> ?name:string -> 'a React.S.t -> 'a t end end eliom-3.0.3/src/server/.depend0000644000000000000000000004424312062377521014361 0ustar0000000000000000eliom_bus.cmo : eliom_state.cmi eliom_service.cmi eliom_registration.cmi \ eliom_parameter.cmi eliom_common.cmi eliom_comet_base.cmi eliom_comet.cmi \ eliom_bus.cmi eliom_bus.cmx : eliom_state.cmx eliom_service.cmx eliom_registration.cmx \ eliom_parameter.cmx eliom_common.cmx eliom_comet_base.cmx eliom_comet.cmx \ eliom_bus.cmi eliom_bus.cmi : eliom_comet.cmi eliom_comet_base.cmo : eliom_service.cmi eliom_registration.cmi \ eliom_parameter.cmi eliom_comet_base.cmi eliom_comet_base.cmx : eliom_service.cmx eliom_registration.cmx \ eliom_parameter.cmx eliom_comet_base.cmi eliom_comet_base.cmi : eliom_service.cmi eliom_registration.cmi \ eliom_parameter.cmi eliom_comet.cmo : eliom_wrap.cmi eliom_types.cmi eliom_service.cmi \ eliom_request_info.cmi eliom_registration.cmi eliom_reference.cmi \ eliom_parameter.cmi eliom_lib.cmi eliom_common_base.cmo eliom_common.cmi \ eliom_comet_base.cmi eliom_comet.cmi eliom_comet.cmx : eliom_wrap.cmx eliom_types.cmx eliom_service.cmx \ eliom_request_info.cmx eliom_registration.cmx eliom_reference.cmx \ eliom_parameter.cmx eliom_lib.cmx eliom_common_base.cmx eliom_common.cmx \ eliom_comet_base.cmx eliom_comet.cmi eliom_comet.cmi : eliom_common.cmi eliom_comet_base.cmi eliom_common_base.cmo : eliom_lib_base.cmi eliom_lib.cmi eliom_common_base.cmx : eliom_lib_base.cmx eliom_lib.cmx eliom_common.cmo : eliom_wrap.cmi eliom_lib.cmi eliom_common_base.cmo \ eliom_common.cmi eliom_common.cmx : eliom_wrap.cmx eliom_lib.cmx eliom_common_base.cmx \ eliom_common.cmi eliom_common.cmi : eliom_wrap.cmi eliom_lib.cmi eliom_common_base.cmo eliom_config.cmo : private/eliommod.cmo eliom_request_info.cmi \ eliom_common.cmi eliom_config.cmi eliom_config.cmx : private/eliommod.cmx eliom_request_info.cmx \ eliom_common.cmx eliom_config.cmi eliom_config.cmi : eliom_common.cmi eliom_content_core.cmo : eliom_wrap.cmi eliom_lib.cmi eliom_lazy.cmi \ eliom_content_core.cmi eliom_content_core.cmx : eliom_wrap.cmx eliom_lib.cmx eliom_lazy.cmx \ eliom_content_core.cmi eliom_content_core.cmi : eliom_wrap.cmi eliom_lib.cmi eliom_lazy.cmi eliom_content.cmo : eliom_service.cmi eliom_registration_base.cmi \ eliom_parameter.cmi eliom_mkforms.cmi eliom_lib.cmi eliom_lazy.cmi \ eliom_content_core.cmi eliom_content.cmi eliom_content.cmx : eliom_service.cmx eliom_registration_base.cmx \ eliom_parameter.cmx eliom_mkforms.cmx eliom_lib.cmx eliom_lazy.cmx \ eliom_content_core.cmx eliom_content.cmi eliom_content.cmi : eliom_service.cmi eliom_parameter.cmi eliom_lib.cmi \ eliom_content_core.cmi eliom_cookie.cmo : eliom_lib.cmi eliom_cookie.cmi eliom_cookie.cmx : eliom_lib.cmx eliom_cookie.cmi eliom_cookie.cmi : eliom_lib.cmi eliom_cookies_base.cmo : eliom_lib.cmi eliom_cookies_base.cmx : eliom_lib.cmx eliom_error_pages.cmo : eliom_lib.cmi eliom_content_core.cmi eliom_error_pages.cmx : eliom_lib.cmx eliom_content_core.cmx eliom_extension.cmo : eliom_common.cmi eliom_extension.cmi eliom_extension.cmx : eliom_common.cmx eliom_extension.cmi eliom_extension.cmi : eliom_common.cmi eliom_extension_template.cmo : eliom_extension.cmi eliom_extension_template.cmx : eliom_extension.cmx eliom_lazy.cmo : eliom_wrap.cmi eliom_lazy.cmi eliom_lazy.cmx : eliom_wrap.cmx eliom_lazy.cmi eliom_lazy.cmi : eliom_lib_base.cmo : eliom_lazy.cmi eliom_lib_base.cmi eliom_lib_base.cmx : eliom_lazy.cmx eliom_lib_base.cmi eliom_lib_base.cmi : eliom_lazy.cmi eliom_lib.cmo : eliom_wrap.cmi eliom_lib_base.cmi eliom_lib.cmi eliom_lib.cmx : eliom_wrap.cmx eliom_lib_base.cmx eliom_lib.cmi eliom_lib.cmi : eliom_wrap.cmi eliom_lib_base.cmi eliom_mkforms.cmo : eliom_uri.cmi eliom_service.cmi eliom_parameter.cmi \ eliom_lib.cmi eliom_lazy.cmi eliom_common.cmi eliom_mkforms.cmi eliom_mkforms.cmx : eliom_uri.cmx eliom_service.cmx eliom_parameter.cmx \ eliom_lib.cmx eliom_lazy.cmx eliom_common.cmx eliom_mkforms.cmi eliom_mkforms.cmi : eliom_service.cmi eliom_parameter.cmi eliom_lib.cmi \ eliom_lazy.cmi eliom_mkreg.cmo : private/eliommod_services.cmi \ private/eliommod_naservices.cmi private/eliommod_cookies.cmi \ eliom_uri.cmi eliom_state.cmi eliom_service.cmi eliom_request_info.cmi \ eliom_parameter.cmi eliom_lib.cmi eliom_common_base.cmo eliom_common.cmi \ eliom_mkreg.cmi eliom_mkreg.cmx : private/eliommod_services.cmx \ private/eliommod_naservices.cmx private/eliommod_cookies.cmx \ eliom_uri.cmx eliom_state.cmx eliom_service.cmx eliom_request_info.cmx \ eliom_parameter.cmx eliom_lib.cmx eliom_common_base.cmx eliom_common.cmx \ eliom_mkreg.cmi eliom_mkreg.cmi : eliom_state.cmi eliom_service.cmi eliom_parameter.cmi \ eliom_lib.cmi eliom_common.cmi eliom_parameter_base.cmo : eliom_lib.cmi eliom_common.cmi eliom_parameter_base.cmx : eliom_lib.cmx eliom_common.cmx eliom_parameter.cmo : eliom_request_info.cmi eliom_parameter_base.cmo \ eliom_lib.cmi eliom_common.cmi eliom_parameter.cmi eliom_parameter.cmx : eliom_request_info.cmx eliom_parameter_base.cmx \ eliom_lib.cmx eliom_common.cmx eliom_parameter.cmi eliom_parameter.cmi : eliom_lib.cmi eliom_common.cmi eliom_pervasives_base.cmo : eliom_service.cmi eliom_parameter.cmi eliom_pervasives_base.cmx : eliom_service.cmx eliom_parameter.cmx eliom_pervasives.cmo : eliom_wrap.cmi eliom_registration.cmi \ eliom_pervasives_base.cmo eliom_parameter.cmi eliom_lib.cmi \ eliom_common_base.cmo eliom_pervasives.cmi eliom_pervasives.cmx : eliom_wrap.cmx eliom_registration.cmx \ eliom_pervasives_base.cmx eliom_parameter.cmx eliom_lib.cmx \ eliom_common_base.cmx eliom_pervasives.cmi eliom_pervasives.cmi : eliom_pervasives_base.cmo eliom_lib.cmi \ eliom_common.cmi eliom_process.cmo : eliom_process.cmx : eliom_react.cmo : eliom_service.cmi eliom_registration.cmi \ eliom_parameter.cmi eliom_common.cmi eliom_comet.cmi eliom_react.cmi eliom_react.cmx : eliom_service.cmx eliom_registration.cmx \ eliom_parameter.cmx eliom_common.cmx eliom_comet.cmx eliom_react.cmi eliom_react.cmi : eliom_parameter.cmi eliom_common.cmi eliom_comet.cmi eliom_reference.cmo : eliom_state.cmi eliom_request_info.cmi \ eliom_common.cmi eliom_reference.cmi eliom_reference.cmx : eliom_state.cmx eliom_request_info.cmx \ eliom_common.cmx eliom_reference.cmi eliom_reference.cmi : eliom_state.cmi eliom_common.cmi eliom_registration_base.cmo : eliom_uri.cmi eliom_service.cmi \ eliom_parameter.cmi eliom_mkforms.cmi eliom_lib.cmi eliom_lazy.cmi \ eliom_content_core.cmi eliom_config.cmi eliom_registration_base.cmi eliom_registration_base.cmx : eliom_uri.cmx eliom_service.cmx \ eliom_parameter.cmx eliom_mkforms.cmx eliom_lib.cmx eliom_lazy.cmx \ eliom_content_core.cmx eliom_config.cmx eliom_registration_base.cmi eliom_registration_base.cmi : eliom_service.cmi eliom_parameter.cmi \ eliom_lib.cmi eliom_content_core.cmi eliom_registration.cmo : private/eliommod_pagegen.cmi \ private/eliommod_cookies.cmi private/eliommod_cli.cmi eliom_types.cmi \ eliom_state.cmi eliom_service.cmi eliom_request_info.cmi \ eliom_registration_base.cmi eliom_reference.cmi eliom_parameter.cmi \ eliom_mkreg.cmi eliom_lib.cmi eliom_content_core.cmi eliom_content.cmi \ eliom_config.cmi eliom_common_base.cmo eliom_common.cmi \ eliom_registration.cmi eliom_registration.cmx : private/eliommod_pagegen.cmx \ private/eliommod_cookies.cmx private/eliommod_cli.cmx eliom_types.cmx \ eliom_state.cmx eliom_service.cmx eliom_request_info.cmx \ eliom_registration_base.cmx eliom_reference.cmx eliom_parameter.cmx \ eliom_mkreg.cmx eliom_lib.cmx eliom_content_core.cmx eliom_content.cmx \ eliom_config.cmx eliom_common_base.cmx eliom_common.cmx \ eliom_registration.cmi eliom_registration.cmi : eliom_service.cmi eliom_parameter.cmi eliom_lib.cmi \ eliom_content_core.cmi eliom_content.cmi eliom_common.cmi eliom_request_info.cmo : eliom_lib.cmi eliom_common.cmi \ eliom_request_info.cmi eliom_request_info.cmx : eliom_lib.cmx eliom_common.cmx \ eliom_request_info.cmi eliom_request_info.cmi : eliom_lib.cmi eliom_common.cmi eliom_service_base.cmo : eliom_request_info.cmi eliom_parameter.cmi \ eliom_lib.cmi eliom_common.cmi eliom_service_base.cmx : eliom_request_info.cmx eliom_parameter.cmx \ eliom_lib.cmx eliom_common.cmx eliom_service.cmo : private/eliommod_services.cmi \ private/eliommod_naservices.cmi private/eliommod_cookies.cmi \ eliom_state.cmi eliom_service_base.cmo eliom_request_info.cmi \ eliom_reference.cmi eliom_parameter.cmi eliom_lib.cmi \ eliom_content_core.cmi eliom_common_base.cmo eliom_common.cmi \ eliom_service.cmi eliom_service.cmx : private/eliommod_services.cmx \ private/eliommod_naservices.cmx private/eliommod_cookies.cmx \ eliom_state.cmx eliom_service_base.cmx eliom_request_info.cmx \ eliom_reference.cmx eliom_parameter.cmx eliom_lib.cmx \ eliom_content_core.cmx eliom_common_base.cmx eliom_common.cmx \ eliom_service.cmi eliom_service.cmi : eliom_parameter.cmi eliom_lib_base.cmi eliom_lib.cmi \ eliom_content_core.cmi eliom_common.cmi eliom_state.cmo : private/eliommod_timeouts.cmi \ private/eliommod_sessiongroups.cmi private/eliommod_sessexpl.cmi \ private/eliommod_sessadmin.cmi private/eliommod_sersess.cmi \ private/eliommod_persess.cmi private/eliommod_datasess.cmi \ eliom_request_info.cmi eliom_lib.cmi eliom_common.cmi eliom_state.cmi eliom_state.cmx : private/eliommod_timeouts.cmx \ private/eliommod_sessiongroups.cmx private/eliommod_sessexpl.cmx \ private/eliommod_sessadmin.cmx private/eliommod_sersess.cmx \ private/eliommod_persess.cmx private/eliommod_datasess.cmx \ eliom_request_info.cmx eliom_lib.cmx eliom_common.cmx eliom_state.cmi eliom_state.cmi : eliom_lib.cmi eliom_common.cmi eliom_tools_common.cmo : eliom_service.cmi eliom_registration.cmi \ eliom_tools_common.cmi eliom_tools_common.cmx : eliom_service.cmx eliom_registration.cmx \ eliom_tools_common.cmi eliom_tools_common.cmi : eliom_state.cmi eliom_service.cmi \ eliom_registration.cmi eliom_parameter.cmi eliom_tools.cmo : eliom_tools_common.cmi eliom_service.cmi \ eliom_request_info.cmi eliom_registration.cmi eliom_reference.cmi \ eliom_lib.cmi eliom_content.cmi eliom_common.cmi eliom_tools.cmi eliom_tools.cmx : eliom_tools_common.cmx eliom_service.cmx \ eliom_request_info.cmx eliom_registration.cmx eliom_reference.cmx \ eliom_lib.cmx eliom_content.cmx eliom_common.cmx eliom_tools.cmi eliom_tools.cmi : eliom_tools_common.cmi eliom_state.cmi eliom_service.cmi \ eliom_registration.cmi eliom_parameter.cmi eliom_lib.cmi \ eliom_content.cmi eliom_types_base.cmo : eliom_wrap.cmi eliom_lib.cmi eliom_content_core.cmi \ eliom_common.cmi eliom_types_base.cmi eliom_types_base.cmx : eliom_wrap.cmx eliom_lib.cmx eliom_content_core.cmx \ eliom_common.cmx eliom_types_base.cmi eliom_types_base.cmi : eliom_wrap.cmi eliom_lib.cmi eliom_content_core.cmi \ eliom_common.cmi eliom_types.cmo : eliom_wrap.cmi eliom_types_base.cmi eliom_lib.cmi \ eliom_types.cmi eliom_types.cmx : eliom_wrap.cmx eliom_types_base.cmx eliom_lib.cmx \ eliom_types.cmi eliom_types.cmi : eliom_wrap.cmi eliom_lib.cmi eliom_content_core.cmi \ eliom_common.cmi eliom_uri.cmo : eliom_service.cmi eliom_request_info.cmi eliom_parameter.cmi \ eliom_lib.cmi eliom_config.cmi eliom_common.cmi eliom_uri.cmi eliom_uri.cmx : eliom_service.cmx eliom_request_info.cmx eliom_parameter.cmx \ eliom_lib.cmx eliom_config.cmx eliom_common.cmx eliom_uri.cmi eliom_uri.cmi : eliom_service.cmi eliom_parameter.cmi eliom_lib.cmi \ eliom_common.cmi eliom_wrap.cmo : eliom_wrap.cmi eliom_wrap.cmx : eliom_wrap.cmi eliom_wrap.cmi : extensions/atom_feed.cmo : eliom_lib.cmi eliom_content.cmi \ extensions/atom_feed.cmi extensions/atom_feed.cmx : eliom_lib.cmx eliom_content.cmx \ extensions/atom_feed.cmi extensions/atom_feed.cmi : eliom_lib.cmi eliom_content.cmi extensions/eliom_atom.cmo : eliom_service.cmi eliom_registration.cmi \ eliom_parameter.cmi eliom_mkreg.cmi eliom_lib.cmi \ extensions/atom_feed.cmi extensions/eliom_atom.cmi extensions/eliom_atom.cmx : eliom_service.cmx eliom_registration.cmx \ eliom_parameter.cmx eliom_mkreg.cmx eliom_lib.cmx \ extensions/atom_feed.cmx extensions/eliom_atom.cmi extensions/eliom_atom.cmi : eliom_service.cmi eliom_registration.cmi \ eliom_parameter.cmi eliom_lib.cmi eliom_common.cmi \ extensions/atom_feed.cmi extensions/eliom_openid.cmo : eliom_state.cmi eliom_service.cmi \ extensions/eliom_s2s.cmi eliom_request_info.cmi eliom_registration.cmi \ eliom_parameter.cmi eliom_lib.cmi eliom_content.cmi eliom_common.cmi \ extensions/eliom_openid.cmi extensions/eliom_openid.cmx : eliom_state.cmx eliom_service.cmx \ extensions/eliom_s2s.cmx eliom_request_info.cmx eliom_registration.cmx \ eliom_parameter.cmx eliom_lib.cmx eliom_content.cmx eliom_common.cmx \ extensions/eliom_openid.cmi extensions/eliom_openid.cmi : eliom_registration.cmi eliom_lib.cmi extensions/eliom_s2s.cmo : eliom_lib.cmi extensions/eliom_s2s.cmi extensions/eliom_s2s.cmx : eliom_lib.cmx extensions/eliom_s2s.cmi extensions/eliom_s2s.cmi : private/eliommod_cli.cmo : eliom_types.cmi eliom_request_info.cmi \ eliom_lib.cmi eliom_common.cmi private/eliommod_cli.cmi private/eliommod_cli.cmx : eliom_types.cmx eliom_request_info.cmx \ eliom_lib.cmx eliom_common.cmx private/eliommod_cli.cmi private/eliommod_cli.cmi : eliom_types.cmi eliom_lib.cmi eliom_common.cmi private/eliommod_cookies.cmo : private/eliommod_sessiongroups.cmi \ eliom_lib.cmi eliom_cookies_base.cmo eliom_common.cmi \ private/eliommod_cookies.cmi private/eliommod_cookies.cmx : private/eliommod_sessiongroups.cmx \ eliom_lib.cmx eliom_cookies_base.cmx eliom_common.cmx \ private/eliommod_cookies.cmi private/eliommod_cookies.cmi : eliom_lib.cmi eliom_common.cmi private/eliommod_datasess.cmo : private/eliommod_sessiongroups.cmi \ private/eliommod_cookies.cmi eliom_request_info.cmi eliom_common.cmi \ private/eliommod_datasess.cmi private/eliommod_datasess.cmx : private/eliommod_sessiongroups.cmx \ private/eliommod_cookies.cmx eliom_request_info.cmx eliom_common.cmx \ private/eliommod_datasess.cmi private/eliommod_datasess.cmi : eliom_common.cmi private/eliommod_gc.cmo : private/eliommod_sessiongroups.cmi \ private/eliommod_persess.cmi eliom_lib.cmi eliom_common.cmi \ private/eliommod_gc.cmi private/eliommod_gc.cmx : private/eliommod_sessiongroups.cmx \ private/eliommod_persess.cmx eliom_lib.cmx eliom_common.cmx \ private/eliommod_gc.cmi private/eliommod_gc.cmi : eliom_common.cmi private/eliommod.cmo : private/eliommod_timeouts.cmi \ private/eliommod_sessiongroups.cmi private/eliommod_pagegen.cmi \ private/eliommod_gc.cmi private/eliommod_cookies.cmi eliom_lib.cmi \ eliom_extension.cmi eliom_common_base.cmo eliom_common.cmi private/eliommod.cmx : private/eliommod_timeouts.cmx \ private/eliommod_sessiongroups.cmx private/eliommod_pagegen.cmx \ private/eliommod_gc.cmx private/eliommod_cookies.cmx eliom_lib.cmx \ eliom_extension.cmx eliom_common_base.cmx eliom_common.cmx private/eliommod_naservices.cmo : eliom_lib.cmi eliom_common.cmi \ private/eliommod_naservices.cmi private/eliommod_naservices.cmx : eliom_lib.cmx eliom_common.cmx \ private/eliommod_naservices.cmi private/eliommod_naservices.cmi : eliom_common.cmi private/eliommod_pagegen.cmo : private/eliommod_timeouts.cmi \ private/eliommod_services.cmi private/eliommod_persess.cmi \ private/eliommod_naservices.cmi private/eliommod_cookies.cmi \ eliom_lib.cmi eliom_extension.cmi eliom_error_pages.cmo \ eliom_content_core.cmi eliom_common.cmi private/eliommod_pagegen.cmi private/eliommod_pagegen.cmx : private/eliommod_timeouts.cmx \ private/eliommod_services.cmx private/eliommod_persess.cmx \ private/eliommod_naservices.cmx private/eliommod_cookies.cmx \ eliom_lib.cmx eliom_extension.cmx eliom_error_pages.cmx \ eliom_content_core.cmx eliom_common.cmx private/eliommod_pagegen.cmi private/eliommod_pagegen.cmi : eliom_extension.cmi eliom_common.cmi private/eliommod_persess.cmo : private/eliommod_sessiongroups.cmi \ private/eliommod_cookies.cmi eliom_common.cmi \ private/eliommod_persess.cmi private/eliommod_persess.cmx : private/eliommod_sessiongroups.cmx \ private/eliommod_cookies.cmx eliom_common.cmx \ private/eliommod_persess.cmi private/eliommod_persess.cmi : eliom_common.cmi private/eliommod_sersess.cmo : private/eliommod_sessiongroups.cmi \ private/eliommod_cookies.cmi eliom_request_info.cmi eliom_common.cmi \ private/eliommod_sersess.cmi private/eliommod_sersess.cmx : private/eliommod_sessiongroups.cmx \ private/eliommod_cookies.cmx eliom_request_info.cmx eliom_common.cmx \ private/eliommod_sersess.cmi private/eliommod_sersess.cmi : eliom_common.cmi private/eliommod_services.cmo : eliom_lib.cmi eliom_common.cmi \ private/eliommod_services.cmi private/eliommod_services.cmx : eliom_lib.cmx eliom_common.cmx \ private/eliommod_services.cmi private/eliommod_services.cmi : eliom_lib.cmi eliom_common.cmi private/eliommod_sessadmin.cmo : private/eliommod_sessiongroups.cmi \ private/eliommod_persess.cmi eliom_common.cmi \ private/eliommod_sessadmin.cmi private/eliommod_sessadmin.cmx : private/eliommod_sessiongroups.cmx \ private/eliommod_persess.cmx eliom_common.cmx \ private/eliommod_sessadmin.cmi private/eliommod_sessadmin.cmi : eliom_common.cmi private/eliommod_sessexpl.cmo : private/eliommod_persess.cmi \ private/eliommod_datasess.cmi eliom_request_info.cmi eliom_common.cmi \ private/eliommod_sessexpl.cmi private/eliommod_sessexpl.cmx : private/eliommod_persess.cmx \ private/eliommod_datasess.cmx eliom_request_info.cmx eliom_common.cmx \ private/eliommod_sessexpl.cmi private/eliommod_sessexpl.cmi : eliom_common.cmi private/eliommod_sessiongroups.cmo : eliom_lib.cmi eliom_common.cmi \ private/eliommod_sessiongroups.cmi private/eliommod_sessiongroups.cmx : eliom_lib.cmx eliom_common.cmx \ private/eliommod_sessiongroups.cmi private/eliommod_sessiongroups.cmi : eliom_lib.cmi eliom_common.cmi private/eliommod_timeouts.cmo : private/eliommod_sessadmin.cmi eliom_lib.cmi \ eliom_common.cmi private/eliommod_timeouts.cmi private/eliommod_timeouts.cmx : private/eliommod_sessadmin.cmx eliom_lib.cmx \ eliom_common.cmx private/eliommod_timeouts.cmi private/eliommod_timeouts.cmi : eliom_common.cmi eliom-3.0.3/src/server/eliom_lib.mli0000644000000000000000000000603312062377521015552 0ustar0000000000000000(* Ocsigen * http://www.ocsigen.org * Copyright (C) 2011 Grégoire Henry * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, with linking exception; * either version 2.1 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *) (** See {% <> %} *) include module type of Ocsigen_lib with type poly = Ocsigen_lib.poly and type yesnomaybe = Ocsigen_lib.yesnomaybe and type ('a, 'b) leftright = ('a, 'b) Ocsigen_lib.leftright and type 'a Clist.t = 'a Ocsigen_lib.Clist.t and type 'a Clist.node = 'a Ocsigen_lib.Clist.node and type Ip_address.t = Ocsigen_lib.Ip_address.t include module type of Eliom_lib_base with type 'a Int64_map.t = 'a Eliom_lib_base.Int64_map.t with type 'a String_map.t = 'a Eliom_lib_base.String_map.t with type 'a Int_map.t = 'a Eliom_lib_base.Int_map.t with type escaped_value = Eliom_lib_base.escaped_value with type +'a Client_value_server_repr.t = 'a Eliom_lib_base.Client_value_server_repr.t with type client_value_datum = Eliom_lib_base.client_value_datum with type 'a injection_datum = 'a Eliom_lib_base.injection_datum with type 'a compilation_unit_global_data = 'a Eliom_lib_base.compilation_unit_global_data with type 'a global_data := 'a Eliom_lib_base.global_data with type request_data = Eliom_lib_base.request_data (** See {% <> %}. *) type 'a client_value (** Raised if a client value of the given closure ID is created at a point in time where it is neither global (i.e. during the initialization of the server program), nor request (i.e. during the processing of a request). *) exception Client_value_creation_invalid_context of int64 exception Eliom_Internal_Error of string type file_info = Ocsigen_extensions.file_info val to_json : ?typ:'a Deriving_Json.t -> 'a -> string val of_json : ?typ:'a Deriving_Json.t -> string -> 'a val debug: ('a, unit, string, unit) format4 -> 'a (** Marshal an OCaml value into a string. All characters are escaped *) val jsmarshal : 'a -> string (**/**) val create_client_value : 'a Client_value_server_repr.t -> 'a client_value val client_value_server_repr : 'a client_value -> 'a Client_value_server_repr.t val escaped_value : 'a -> escaped_value (* * Eliom_wrap.unwrapper *) val string_escape : string -> string type global_data = poly Eliom_lib_base.global_data * Eliom_wrap.unwrapper val global_data_unwrapper : Eliom_wrap.unwrapper (**/**) eliom-3.0.3/src/server/eliom_request_info.mli0000644000000000000000000003214712062377521017514 0ustar0000000000000000(* Ocsigen * http://www.ocsigen.org * Module eliomsessions.mli * Copyright (C) 2007 Vincent Balat * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, with linking exception; * either version 2.1 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *) (** This module contains the functions you need to get (or set) information about current request. *) open Eliom_lib open Ocsigen_extensions open Ocsigen_cookies (** {2 Getting information about the request} *) (** returns the HTTP method used for the request (usually GET or POST). *) val get_http_method : unit -> Ocsigen_http_frame.Http_header.http_method (** returns the name of the user agent that did the request (usually the name of the browser). *) val get_user_agent : unit -> string (** returns the full URL as a string *) val get_full_url : unit -> string (** returns the internet address of the client as a string *) val get_remote_ip : unit -> string (** returns the internet address of the client, using the type [Unix.inet_addr] (defined in OCaml's standard library). *) val get_remote_inet_addr : unit -> Unix.inet_addr (** returns the full path of the URL as a string. *) val get_current_full_path_string : unit -> string (** returns the full path of the URL using the type [Url.path] *) val get_current_full_path : unit -> Url.path (** returns the full path of the URL as first sent by the browser (not changed by previous extensions like rewritemod) *) val get_original_full_path_string : unit -> string (** returns the full path of the URL as first sent by the browser (not changed by previous extensions like rewritemod) *) val get_original_full_path : unit -> Url.path (** returns the sub path of the URL as a string. The sub-path is the full path without the path of the site (set in the configuration file). *) val get_current_sub_path_string : unit -> string (** returns the sub path of the URL using the type [Url.path]. The sub-path is the full path without the path of the site (set in the configuration file). *) val get_current_sub_path : unit -> Url.path (** returns the hostname that has been sent by the user agent. For HTTP/1.0, the Host field is not mandatory in the request. *) val get_header_hostname : unit -> string option (** returns the hostname used for absolute links. It is either the [Host] header sent by the browser or the default hostname set in the configuration file, depending on server configuration ([] option). *) val get_hostname : unit -> string (** returns the port of the server. It is either the default port in the configuration file (if [] is present is the configuration file), or the port in the Host header of the request (if present), or the port on which the request has been done (otherwise). *) val get_server_port : unit -> int (** returns true if https is used, false if http. *) val get_ssl : unit -> bool (** returns the suffix of the current URL *) val get_suffix : unit -> Url.path option (** returns the cookies sent by the browser *) val get_cookies : ?cookie_level:Eliom_common.cookie_level -> unit -> string CookiesTable.t (** returns an Unix timestamp associated to the request *) val get_timeofday : unit -> float (** returns an unique id associated to the request *) val get_request_id : unit -> int64 (** {3 Exceptions and fallbacks} *) (** returns a table in which you can store all the data you want during a request. It can also be used to send information after an action. Keep an eye on this information to know what succeeded before the current service was called (failed connection, timeout ...) The table is created at the beginning of the request. *) val get_request_cache : unit -> Polytables.t (** Remove all data from the request cache *) val clean_request_cache : unit -> unit (** returns [true] if the coservice called has not been found. In that case, the current service is the fallback. *) val get_link_too_old : unit -> bool (** returns the list of names of service sessions expired for the current request, for browser sessions and tab sessions. *) val get_expired_service_sessions : unit -> (Eliom_common.full_state_name list * Eliom_common.full_state_name list) (** returns the HTTP error code sent by the Ocsigen extension that tried to answer to the request before Eliom. It is 404 by default. *) val get_previous_extension_error_code : unit -> int (*****************************************************************************) (** {2 Getting information about files uploaded} *) (** Warning: The files uploaded are automatically erased by Ocsigen just after the request has been fulfilled. If you want to keep them, create a new hard link yourself during the service (or make a copy). *) (** returns the filename used by Ocsigen for the uploaded file. *) val get_tmp_filename : file_info -> string (** returns the size of the file. *) val get_filesize : file_info -> int64 (** returns the name the file had on the client when it has been sent. *) val get_original_filename : file_info -> string (** returns the root of the site. *) val get_site_dir : unit -> Url.path (*****************************************************************************) (** {2 Getting parameters (low level)} *) (** The usual way to get parameters with Eliom is to use the second and third parameters of the service handlers. These are low level functions you may need for more advanced use. *) (** returns the parameters of the URL (GET parameters) that concern the running service. For example in the case of a non-attached coservice called from a page with GET parameters, only the parameters of that non-attached coservice are returned (even if the other are still in the URL). *) val get_get_params : unit -> (string * string) list (** returns current parameters of the URL (GET parameters) (even those that are for subsequent services, but not previous actions) *) val get_all_current_get_params : unit -> (string * string) list (** returns all parameters of the URL (GET parameters) as sent initially by the browser *) val get_initial_get_params : unit -> (string * string) list (** returns the parameters of the URL (GET parameters) that do not concern the running service. *) val get_other_get_params : unit -> (string * string) list (** returns non localized parameters in the URL. *) val get_nl_get_params : unit -> (string * string) list String.Table.t (** returns persistent non localized parameters in the URL. *) val get_persistent_nl_get_params : unit -> (string * string) list String.Table.t (** returns non localized POST parameters. *) val get_nl_post_params : unit -> (string * string) list String.Table.t (** returns the parameters in the body of the HTTP request (POST parameters) that concern the running service. None means that POST data where neither urlencoded form data or multipart data. *) val get_post_params : unit -> (string * string) list Lwt.t option (** returns all parameters in the body of the HTTP request (POST parameters) (even those that are for another service) *) val get_all_post_params : unit -> (string * string) list option (*****************************************************************************) (** {2 Other low level functions} *) (** You probably don't need these functions. *) (** returns all the information about the request. *) val get_ri : unit -> Ocsigen_extensions.request_info (** returns all the information about the request and config. *) val get_request : unit -> request (** returns the name of the sessions to which belongs the running service ([None] if it is not a session service) *) val get_state_name : unit -> Eliom_common.full_state_name option (** returns the values of the Eliom's cookies for persistent sessions sent by the browser. *) val get_persistent_cookies : unit -> string Eliom_common.Full_state_name_table.t (** returns the values of Eliom's cookies for non persistent sessions sent by the browser. *) val get_data_cookies : unit -> string Eliom_common.Full_state_name_table.t (** Returns the http error code of the request before Eliom was called *) val get_previous_extension_error_code :unit -> int (** Returns [true] if the request was done by a client side Eliom program, which was expecting to receive a new HTML page to display inside the process. *) val expecting_process_page : unit -> bool (*****************************************************************************) (** {3 Getting information about the URL of the client side process (csp)} Warning: it is different from the URL to which the request has been made. *) (** returns the full path of the URL where the client-side process is running. If there is no client side process, same as {!get_original_full_path}. *) val get_csp_original_full_path : unit -> Url.path (** returns the hostname used for absolute links, computed when launching the client side process for the first time. If there is no client side process, same as {!get_hostname}. It is either the [Host] header sent by the browser or the default hostname set in the configuration file, depending on server configuration ([] option). *) val get_csp_hostname : unit -> string (** returns the port of the server, used when launching the client side process (not the current request). It corresponds to the port in the URL of the browser. If there is no client side process, same as {!get_server_port}. *) val get_csp_server_port : unit -> int (** returns true if https is used in the URL of the browser, false if http. If there is no client side process, same as {!get_ssl}. *) val get_csp_ssl : unit -> bool (**/**) val get_csp_original_full_path_sp : Eliom_common.server_params -> Url.path val get_csp_hostname_sp : Eliom_common.server_params -> string val get_csp_server_port_sp : Eliom_common.server_params -> int val get_csp_ssl_sp : Eliom_common.server_params -> bool (*****************************************************************************) val get_sitedata_sp : sp:Eliom_common.server_params -> Eliom_common.sitedata val get_sitedata : unit -> Eliom_common.sitedata (* (** returns the cookie expiration date for the session, in seconds, since the 1st of january 1970. must have been set just before (not saved server side). *) val get_cookie_exp_date : ?state_name:string -> unit -> unit -> float option (** returns the cookie expiration date for the persistent session, in seconds, since the 1st of january 1970. must have been set just before (not saved server side). *) val get_persistent_cookie_exp_date : ?state_name:string -> unit -> unit -> float option *) val find_sitedata : string -> Eliom_common.sitedata val get_si : Eliom_common.server_params -> Eliom_common.sess_info val get_user_cookies : unit -> Ocsigen_cookies.cookieset val get_user_tab_cookies : unit -> Ocsigen_cookies.cookieset val get_sp_client_appl_name : unit -> string option val get_sp_client_process_info_sp : Eliom_common.server_params -> Eliom_common.client_process_info val get_sp_client_process_info : unit -> Eliom_common.client_process_info val set_site_handler : Eliom_common.sitedata -> (exn -> Ocsigen_http_frame.result Lwt.t) -> unit val get_request_sp : Eliom_common.server_params -> request val get_site_dir_sp : Eliom_common.server_params -> Url.path val get_hostname_sp : Eliom_common.server_params -> string val get_full_url_sp : Eliom_common.server_params -> string val get_other_get_params_sp : Eliom_common.server_params -> (string * string) list val get_nl_get_params_sp : Eliom_common.server_params -> (string * string) list String.Table.t val get_persistent_nl_get_params_sp : Eliom_common.server_params -> (string * string) list String.Table.t val get_nl_post_params_sp : Eliom_common.server_params -> (string * string) list String.Table.t val get_original_full_path_sp : Eliom_common.server_params -> Url.path val get_original_full_path_string_sp : Eliom_common.server_params -> string val get_server_port_sp : Eliom_common.server_params -> int val get_ssl_sp : Eliom_common.server_params -> bool val get_ri_sp : Eliom_common.server_params -> Ocsigen_extensions.request_info val get_post_params_sp : Eliom_common.server_params -> (string * string) list Lwt.t option val get_files_sp : Eliom_common.server_params -> (string * file_info) list Lwt.t option val get_suffix_sp : Eliom_common.server_params -> Url.path option val get_request_cache_sp : Eliom_common.server_params -> Polytables.t val get_request_id_sp : Eliom_common.server_params -> int64 eliom-3.0.3/src/server/eliom_tools_common.ml0000644000000000000000000000374612062377521017353 0ustar0000000000000000(* Ocsigen * Copyright (C) 2005 Vincent Balat * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, with linking exception; * either version 2.1 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *) open Eliom_service type ('a, 'b, 'c) one_page = (unit, unit, 'a, [ `WithoutSuffix ], unit, unit, 'b, 'c) service constraint 'c = [< Eliom_registration.non_caml_service ] type get_page = (Eliom_service.get_service_kind, Eliom_service.registrable, Eliom_registration.non_caml_service) one_page (* constraint 'c = [ ] *) let menu_class = "eliomtools_menu" let last_class = "eliomtools_last" let current_class = "eliomtools_current" let current_path_class = "eliomtools_current_path" let disabled_class = "eliomtools_disabled" let first_class = "eliomtools_first" let level_class = "eliomtools_level" type ('a, 'b, 'c) hierarchical_site_item = | Disabled | Site_tree of ('a, 'b, 'c) hierarchical_site constraint 'b = [< Eliom_service.registrable ] and ('a, 'b) main_page = | Main_page of ('a, 'b, Eliom_registration.non_caml_service) one_page | Default_page of ('a, 'b, Eliom_registration.non_caml_service) one_page | Not_clickable constraint 'b = [< Eliom_service.registrable ] and ('a, 'b, 'c) hierarchical_site = (('a, 'b) main_page * ('c * ('a, 'b, 'c) hierarchical_site_item) list) constraint 'b = [< Eliom_service.registrable ] eliom-3.0.3/src/server/eliom_reference.ml0000644000000000000000000002127212062377521016573 0ustar0000000000000000 (* Ocsigen * http://www.ocsigen.org * Copyright (C) 2010 Vincent Balat * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, with linking exception; * either version 2.1 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *) (*****************************************************************************) (** {2 Eliom references} *) open Eliom_state let (>>=) = Lwt.bind let pers_ref_store = Ocsipersist.open_store "eliom__persistent_refs" type 'a eref_kind = | Req of 'a Polytables.key | Sit of 'a Polytables.key | Ref of 'a lazy_t ref (* Ocaml reference *) | Vol of 'a volatile_table Lazy.t (* Vol. table (group, session, process) *) | Ocsiper of 'a option Ocsipersist.t Lwt.t (* Global persist. table *) | Ocsiper_sit of 'a Ocsipersist.table (* Persist. table for site *) | Per of 'a persistent_table (* Persist. table for group session or process *) type volatile = [ `Volatile ] type persistent = [ `Persistent ] type ('a, 'storage) eref' = (unit -> 'a) * 'a eref_kind type 'a eref = ('a, [ volatile | persistent ]) eref' exception Eref_not_intialized module Volatile = struct type 'a eref = ('a, volatile) eref' (* TODO With GADTs, drop the [assert false] and [failwith] statements below! *) let eref_from_fun ~scope ?secure f : 'a eref = f, match scope with | `Request -> Req (Polytables.make_key ()) | `Global -> Ref (ref (Lazy.lazy_from_fun f)) | `Site -> Sit (Polytables.make_key ()) | (#Eliom_common.user_scope as scope) -> Vol (lazy (create_volatile_table ~scope ?secure ())) let eref ~scope ?secure v = eref_from_fun ~scope ?secure (fun () -> v) let get (f, table : _ eref) = match table with | Req key -> let table = Eliom_request_info.get_request_cache () in (try Polytables.get ~table ~key with Not_found -> let value = f () in Polytables.set ~table ~key ~value; value) | Sit key -> let table = Eliom_common.((get_site_data ()).site_value_table) in (try Polytables.get ~table ~key with Not_found -> let value = f () in Polytables.set ~table ~key ~value; value) | Vol t -> (match get_volatile_data ~table:(Lazy.force t) () with | Data d -> d | _ -> (let value = f () in set_volatile_data ~table:(Lazy.force t) value; value)) | Ref r -> Lazy.force !r | _ -> assert false let set (_, table : _ eref) value = match table with | Req key -> let table = Eliom_request_info.get_request_cache () in Polytables.set ~table ~key ~value; | Sit key -> let table = Eliom_common.((get_site_data ()).site_value_table) in Polytables.set ~table ~key ~value | Vol t -> set_volatile_data ~table:(Lazy.force t) value; | Ref r -> r := Lazy.lazy_from_val value | _ -> assert false let modify eref f = set eref (f (get eref)) let unset (f, table : _ eref) = match table with | Req key -> let table = Eliom_request_info.get_request_cache () in Polytables.remove ~table ~key; | Sit key -> let table = Eliom_common.((get_site_data ()).site_value_table) in Polytables.remove ~table ~key | Vol t -> remove_volatile_data ~table:(Lazy.force t) (); | Ref r -> r := Lazy.lazy_from_fun f | _ -> assert false module Ext = struct let get state (f, table) = match table with | Vol t -> (try Eliom_state.Ext.Low_level.get_volatile_data ~state ~table:(Lazy.force t) with Not_found -> (* I don't want to run f in the wrong context -> I fail *) raise Eref_not_intialized) | _ -> failwith "wrong eref for this function" let set state (_, table) value = match table with | Vol t -> Eliom_state.Ext.Low_level.set_volatile_data ~state ~table:(Lazy.force t) value | _ -> failwith "wrong eref for this function" let modify state eref f = set state eref (f (get state eref)) let unset state (f, table : _ eref) = match table with | Vol t -> Eliom_state.Ext.Low_level.remove_volatile_data ~state ~table:(Lazy.force t); | _ -> failwith "wrong eref for this function" end end let eref_from_fun ~scope ?secure ?persistent f : 'a eref = match (scope:[ (Volatile.eref_from_fun ~scope ?secure f :> _ eref) | `Global -> begin match persistent with | None -> (Volatile.eref_from_fun ~scope ?secure f :> _ eref) | Some name -> (f, Ocsiper (Ocsipersist.make_persistent ~store:pers_ref_store ~name ~default:None)) end | `Site -> begin match persistent with | None -> (Volatile.eref_from_fun ~scope ?secure f :> _ eref) | Some name -> (*VVV!!! ??? CHECK! *) (f, Ocsiper_sit (Ocsipersist.open_table name)) end | (#Eliom_common.user_scope as scope) -> match persistent with | None -> (Volatile.eref_from_fun ~scope ?secure f :> _ eref) | Some name -> (f, Per (create_persistent_table ~scope ?secure name)) let eref ~scope ?secure ?persistent v = eref_from_fun ~scope ?secure ?persistent (fun () -> v) let get_site_id () = let sd = Eliom_common.get_site_data () in sd.Eliom_common.config_info.Ocsigen_extensions.default_hostname ^ ":" ^ sd.Eliom_common.site_dir_string let get (f, table as eref) = match table with | Per t -> (get_persistent_data ~table:t () >>= function | Data d -> Lwt.return d | _ -> let value = f () in set_persistent_data ~table:t value >>= fun () -> Lwt.return value) | Ocsiper r -> (r >>= fun r -> Ocsipersist.get r >>= function | Some v -> Lwt.return v | None -> let value = f () in Ocsipersist.set r (Some value) >>= fun () -> Lwt.return value) | Ocsiper_sit t -> (let site_id = get_site_id () in try_lwt Ocsipersist.find t site_id with Not_found -> let value = f () in Ocsipersist.add t site_id value >>= fun () -> Lwt.return value) | _ -> Lwt.return (Volatile.get eref) let set (_, table as eref) value = match table with | Per t -> set_persistent_data ~table:t value | Ocsiper r -> r >>= fun r -> Ocsipersist.set r (Some value) | Ocsiper_sit t -> Ocsipersist.add t (get_site_id ()) value | _ -> Lwt.return (Volatile.set eref value) let modify eref f = get eref >>= fun x -> set eref (f x) let unset (f, table as eref) = match table with | Per t -> remove_persistent_data ~table:t () | Ocsiper r -> r >>= fun r -> Ocsipersist.set r None | Ocsiper_sit t -> Ocsipersist.remove t (get_site_id ()) | _ -> Lwt.return (Volatile.unset eref) module Ext = struct let get state ((_, table) as r) = let state = Eliom_state.Ext.untype_state state in match table with | Vol _ -> Lwt.return (Volatile.Ext.get state r) | Per t -> (Lwt.catch (fun () -> Eliom_state.Ext.Low_level.get_persistent_data ~state ~table:t) (function | Not_found -> Lwt.fail Eref_not_intialized | e -> Lwt.fail e)) | _ -> failwith "wrong eref for this function" let set state ((_, table) as r) value = let state = Eliom_state.Ext.untype_state state in match table with | Vol _ -> Lwt.return (Volatile.Ext.set state r value) | Per t -> Eliom_state.Ext.Low_level.set_persistent_data ~state ~table:t value | _ -> Lwt.fail (Failure "wrong eref for this function") let modify state eref f = get state eref >>= fun v -> set state eref (f v) let unset state ((_, table) as r) = let state = Eliom_state.Ext.untype_state state in match table with | Vol _ -> Lwt.return (Volatile.Ext.unset state r) | Per t -> Eliom_state.Ext.Low_level.remove_persistent_data ~state ~table:t | _ -> failwith "wrong eref for this function" end eliom-3.0.3/src/server/eliom_request_info.ml0000644000000000000000000002405312062377521017340 0ustar0000000000000000(* Ocsigen * http://www.ocsigen.org * Copyright (C) 2010 Vincent Balat * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, with linking exception; * either version 2.1 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *) open Eliom_lib open Lwt open Ocsigen_extensions (*****************************************************************************) let find_sitedata fun_name = match Eliom_common.get_sp_option () with | Some sp -> sp.Eliom_common.sp_sitedata | None -> match Eliom_common.global_register_allowed () with | Some get_current_sitedata -> get_current_sitedata () | _ -> raise (Eliom_common.Eliom_site_information_not_available fun_name) (*****************************************************************************) let get_http_method () = let sp = Eliom_common.get_sp () in sp.Eliom_common.sp_request.request_info.ri_method let get_user_agent () = let sp = Eliom_common.get_sp () in sp.Eliom_common.sp_request.request_info.ri_user_agent let get_full_url_sp sp = sp.Eliom_common.sp_request.request_info.ri_url_string let get_full_url () = let sp = Eliom_common.get_sp () in get_full_url_sp sp let get_remote_ip () = let sp = Eliom_common.get_sp () in sp.Eliom_common.sp_request.request_info.ri_remote_ip let get_remote_inet_addr () = let sp = Eliom_common.get_sp () in sp.Eliom_common.sp_request.request_info.ri_remote_inet_addr let get_get_params () = let sp = Eliom_common.get_sp () in Lazy.force sp.Eliom_common.sp_request.request_info.ri_get_params let get_all_current_get_params_sp sp = sp.Eliom_common.sp_si.Eliom_common.si_all_get_params let get_all_current_get_params () = let sp = Eliom_common.get_sp () in get_all_current_get_params_sp sp let get_initial_get_params () = let sp = Eliom_common.get_sp () in Lazy.force sp.Eliom_common.sp_request.request_info.ri_initial_get_params let get_get_params_string () = let sp = Eliom_common.get_sp () in sp.Eliom_common.sp_request.request_info.ri_get_params_string let get_post_params_sp sp = match sp.Eliom_common.sp_request.request_info.ri_post_params with | None -> None | Some f -> Some (f sp.Eliom_common.sp_request.request_config) let get_post_params () = let sp = Eliom_common.get_sp () in get_post_params_sp sp let get_files_sp sp = match sp.Eliom_common.sp_request.request_info.ri_files with | None -> None | Some f -> Some (f sp.Eliom_common.sp_request.request_config) let get_all_post_params () = let sp = Eliom_common.get_sp () in sp.Eliom_common.sp_si.Eliom_common.si_all_post_params let get_original_full_path_string_sp sp = sp.Eliom_common.sp_request.request_info.ri_original_full_path_string let get_original_full_path_string () = let sp = Eliom_common.get_sp () in get_original_full_path_string_sp sp let get_original_full_path_sp sp = sp.Eliom_common.sp_request.request_info.ri_original_full_path let get_original_full_path () = let sp = Eliom_common.get_sp () in get_original_full_path_sp sp let get_current_full_path () = let sp = Eliom_common.get_sp () in sp.Eliom_common.sp_request.request_info.ri_full_path let get_current_full_path_string () = let sp = Eliom_common.get_sp () in sp.Eliom_common.sp_request.request_info.ri_full_path_string let get_current_sub_path () = let sp = Eliom_common.get_sp () in sp.Eliom_common.sp_request.request_info.ri_sub_path let get_current_sub_path_string () = let sp = Eliom_common.get_sp () in sp.Eliom_common.sp_request.request_info.ri_sub_path_string let get_header_hostname () = let sp = Eliom_common.get_sp () in sp.Eliom_common.sp_request.request_info.ri_host let get_timeofday_sp sp = sp.Eliom_common.sp_request.request_info.ri_timeofday let get_request_id_sp sp = Int64.bits_of_float (get_timeofday_sp sp) let get_timeofday () = let sp = Eliom_common.get_sp () in get_timeofday_sp sp let get_request_id () = Int64.bits_of_float (get_timeofday ()) let get_hostname_sp sp = Ocsigen_extensions.get_hostname sp.Eliom_common.sp_request let get_hostname () = let sp = Eliom_common.get_sp () in get_hostname_sp sp let get_server_port_sp sp = Ocsigen_extensions.get_port sp.Eliom_common.sp_request let get_server_port () = let sp = Eliom_common.get_sp () in get_server_port_sp sp let get_ssl_sp sp = sp.Eliom_common.sp_request.request_info.ri_ssl let get_ssl () = let sp = Eliom_common.get_sp () in get_ssl_sp sp let get_other_get_params () = let sp = Eliom_common.get_sp () in sp.Eliom_common.sp_si.Eliom_common.si_other_get_params let get_nl_get_params () = let sp = Eliom_common.get_sp () in sp.Eliom_common.sp_si.Eliom_common.si_nl_get_params let get_persistent_nl_get_params () = let sp = Eliom_common.get_sp () in Lazy.force sp.Eliom_common.sp_si.Eliom_common.si_persistent_nl_get_params let get_nl_post_params () = let sp = Eliom_common.get_sp () in sp.Eliom_common.sp_si.Eliom_common.si_nl_post_params let get_other_get_params_sp sp = sp.Eliom_common.sp_si.Eliom_common.si_other_get_params let get_nl_get_params_sp sp = sp.Eliom_common.sp_si.Eliom_common.si_nl_get_params let get_persistent_nl_get_params_sp sp = Lazy.force sp.Eliom_common.sp_si.Eliom_common.si_persistent_nl_get_params let get_nl_post_params_sp sp = sp.Eliom_common.sp_si.Eliom_common.si_nl_post_params let get_suffix_sp sp = sp.Eliom_common.sp_suffix let get_suffix () = let sp = Eliom_common.get_sp () in get_suffix_sp sp let get_state_name () = let sp = Eliom_common.get_sp () in sp.Eliom_common.sp_full_state_name let get_request_cache_sp sp = sp.Eliom_common.sp_request.request_info.ri_request_cache let get_request_cache () = let sp = Eliom_common.get_sp () in get_request_cache_sp sp let clean_request_cache () = let sp = Eliom_common.get_sp () in sp.Eliom_common.sp_request.request_info.ri_request_cache <- Polytables.create () let get_link_too_old () = let sp = Eliom_common.get_sp () in try Polytables.get ~table:sp.Eliom_common.sp_request.request_info.ri_request_cache ~key:Eliom_common.eliom_link_too_old with Not_found -> false let get_expired_service_sessions () = let sp = Eliom_common.get_sp () in try Polytables.get ~table:sp.Eliom_common.sp_request.request_info.ri_request_cache ~key:Eliom_common.eliom_service_session_expired with Not_found -> ([], []) let get_cookies ?(cookie_level = `Session) () = let sp = Eliom_common.get_sp () in match cookie_level with | `Session -> Lazy.force sp.Eliom_common.sp_request.request_info.ri_cookies | `Client_process -> sp.Eliom_common.sp_si.Eliom_common.si_tab_cookies let get_data_cookies () = let sp = Eliom_common.get_sp () in sp.Eliom_common.sp_si.Eliom_common.si_data_session_cookies let get_persistent_cookies () = let sp = Eliom_common.get_sp () in sp.Eliom_common.sp_si.Eliom_common.si_persistent_session_cookies let get_previous_extension_error_code () = let sp = Eliom_common.get_sp () in sp.Eliom_common.sp_si.Eliom_common.si_previous_extension_error let get_si sp = sp.Eliom_common.sp_si let get_user_cookies () = let sp = Eliom_common.get_sp () in sp.Eliom_common.sp_user_cookies let get_user_tab_cookies () = let sp = Eliom_common.get_sp () in sp.Eliom_common.sp_user_tab_cookies (****) let get_sp_client_appl_name () = let sp = Eliom_common.get_sp () in sp.Eliom_common.sp_client_appl_name let get_sp_client_process_info_sp sp = sp.Eliom_common.sp_client_process_info let get_sp_client_process_info () = let sp = Eliom_common.get_sp () in get_sp_client_process_info_sp sp let expecting_process_page () = let sp = Eliom_common.get_sp () in Lazy.force sp.Eliom_common.sp_si.Eliom_common.si_expect_process_data let get_csp_original_full_path () = let cpi = get_sp_client_process_info () in cpi.Eliom_common.cpi_original_full_path let get_csp_hostname () = let cpi = get_sp_client_process_info () in cpi.Eliom_common.cpi_hostname let get_csp_server_port () = let cpi = get_sp_client_process_info () in cpi.Eliom_common.cpi_server_port let get_csp_ssl () = let cpi = get_sp_client_process_info () in cpi.Eliom_common.cpi_ssl let get_csp_original_full_path_sp sp = let cpi = get_sp_client_process_info_sp sp in cpi.Eliom_common.cpi_original_full_path let get_csp_hostname_sp sp = let cpi = get_sp_client_process_info_sp sp in cpi.Eliom_common.cpi_hostname let get_csp_server_port_sp sp = let cpi = get_sp_client_process_info_sp sp in cpi.Eliom_common.cpi_server_port let get_csp_ssl_sp sp = let cpi = get_sp_client_process_info_sp sp in cpi.Eliom_common.cpi_ssl (* *) let get_site_dir () = let sitedata = find_sitedata "Eliom_request_info.get_site_dir" in sitedata.Eliom_common.site_dir let get_site_dir_sp sp = sp.Eliom_common.sp_sitedata.Eliom_common.site_dir let get_site_dir_string () = let sitedata = find_sitedata "Eliom_request_info.get_site_dir_string" in sitedata.Eliom_common.site_dir_string let get_request () = let sp = Eliom_common.get_sp () in sp.Eliom_common.sp_request let get_request_sp sp = sp.Eliom_common.sp_request let get_ri_sp sp = sp.Eliom_common.sp_request.Ocsigen_extensions.request_info let get_ri () = let sp = Eliom_common.get_sp () in get_ri_sp sp let get_tmp_filename fi = fi.tmp_filename let get_filesize fi = fi.filesize let get_original_filename fi = fi.original_basename let get_sitedata () = let sp = Eliom_common.get_sp () in sp.Eliom_common.sp_sitedata let get_sitedata_sp ~sp = sp.Eliom_common.sp_sitedata (***) (*VVV ici ? pour des raisons de typage... *) let set_site_handler sitedata handler = sitedata.Eliom_common.exn_handler <- handler eliom-3.0.3/src/server/eliom_process.ml0000644000000000000000000000175512062377521016317 0ustar0000000000000000(* Ocsigen * http://www.ocsigen.org * Copyright (C) 2010 Vincent Balat * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, with linking exception; * either version 2.1 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *) exception Server_side_process_closed (** returns None on server side *) let get_application_name () = None (** false on server side *) let client_side = false eliom-3.0.3/src/server/eliom_error_pages.ml0000644000000000000000000000601212062377521017140 0ustar0000000000000000(* Ocsigen * Copyright (C) 2005 Vincent Balat * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, with linking exception; * either version 2.1 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *) open Eliom_lib open Eliom_content_core open Html5.F let page_error_param_type l = let s = match l with [] -> [pcdata "Wrong type for parameter"] | [(n,_)] -> [pcdata "Wrong type for parameter ";em [pcdata n];pcdata "."] | (n,_)::ll -> (pcdata "Wrong type for parameters "):: (List.fold_left (fun deb (n,_) -> (em [pcdata n])::(pcdata ", ")::deb) [em [pcdata n];pcdata "."] ll) in html (head (title (pcdata "")) []) (body [h1 s] ) let page_bad_param after_action gl pl = let s = "Wrong parameters" in html (head (title (pcdata s)) []) (body ((h1 [pcdata s]):: (if Ocsigen_config.get_debugmode () then [h2 [pcdata "Debugging information:"]; (if after_action then (p [pcdata "An action occurred successfully. But Eliom was unable to find the service for displaying the page."]) else (p [pcdata "Eliom was unable to find a service matching these parameters."])); (match gl with | [] -> p [pcdata "No GET parameters have been given to services."] | (n, a)::l -> p ((pcdata "GET parameters given to services: "):: [em ((pcdata n)::(pcdata "=")::(pcdata a):: (List.fold_right (fun (n, a) b -> (pcdata "&"):: (pcdata n)::(pcdata "=")::(pcdata a)::b) l [pcdata "."]))])); (match pl with | [] -> p [pcdata "No POST parameters have been given to services."] | a::l -> p ((pcdata "Names of POST parameters given to services: "):: (em [pcdata a]):: (List.fold_right (fun n b -> (pcdata ", ")::(em [pcdata n])::b) l [pcdata "."])))] else []) ) ) let page_session_expired = let s = "Session expired" in html (head (title (pcdata s)) []) (body [h1 [pcdata s]] ) eliom-3.0.3/src/server/eliom_bus.mli0000644000000000000000000000445612062377521015604 0ustar0000000000000000(* Ocsigen * http://www.ocsigen.org * Copyright (C) 2010-2011 * Raphaël Proust * Pierre Chambart * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, with linking exception; * either version 2.1 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *) (** Broadcasting facilities between clients and server *) (** The type of bus's carrying values of type ['a]. Bus's are values that can be easily shared among clients. Each of these clients along with the server can send a value on the bus. Values can be received by each of the participants as a stream. Note that no effort is put to order message receptions on the different participants. *) type 'a t (** [create ?scope ?name ?size] makes a fresh bus. The [name] optional parameter can be used to make persistent (as in server restart persistent) bus's. The [scope] parameter is used to chose the kind of channel on which the bus rely (See [Eliom_comet.create] for more information). The [?name] argument allow one to make bus's persistent over server restart. The [size] argument behaves like the one on {!Eliom_comet.Channel.create} *) val create : ?scope:[< Eliom_comet.Channel.comet_scope ] -> ?name:string -> ?size:int -> 'a Deriving_Json.t -> 'a t (** [stream b] returns the stream of datas sent to bus [b]. Notice you sould not use that function multiple times on the same bus, it will return the same stream. If you want to receive multiple times the same datas, you sould copy the stream with [Lwt_stream.clone] *) val stream : 'a t -> 'a Lwt_stream.t (** [write b x] sends the value [x] on the bus [b]. Every participant, including the server, will receive [x]. *) val write : 'a t -> 'a -> unit eliom-3.0.3/src/server/eliom_extension.mli0000644000000000000000000000316012062377521017016 0ustar0000000000000000(* Ocsigen * http://www.ocsigen.org * Copyright (C) 2008 Vincent Balat * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, with linking exception; * either version 2.1 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *) (** Allows Ocsigen's extension to access Eliom data. See the Eliom manual for more information about {% <>%} *) (** Type of the function that must be registered to declare an eliom extension. *) type eliom_extension_sig = unit -> Ocsigen_extensions.answer Lwt.t val register_eliom_extension : eliom_extension_sig -> unit (**/**) val get_eliom_extension : unit -> eliom_extension_sig val run_eliom_extension : eliom_extension_sig -> float -> (Ocsigen_extensions.request * Eliom_common.sess_info * Eliom_common.tables Eliom_common.cookie_info * Eliom_common.tables Eliom_common.cookie_info * Ocsigen_cookies.cookieset) -> Eliom_common.sitedata -> Ocsigen_extensions.answer Lwt.t eliom-3.0.3/src/server/eliom_common.mli0000644000000000000000000006116512062377521016303 0ustar0000000000000000(* Ocsigen * http://www.ocsigen.org * Module eliom_common.mli * Copyright (C) 2005 Vincent Balat * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, with linking exception; * either version 2.1 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *) (** Low level functions for Eliom, exceptions and types. *) open Ocsigen_extensions open Ocsigen_cookies open Eliom_lib (** {2 Scopes} *) (* those types are not available to the user, a scope must be created using create_..._scope functions *) type scope_hierarchy = Eliom_common_base.scope_hierarchy type cookie_scope = [ `Session of scope_hierarchy | `Client_process of scope_hierarchy ] type user_scope = [ `Session_group of scope_hierarchy | cookie_scope ] type scope = [ `Site | user_scope ] type all_scope = [ scope | `Global | `Request ] type cookie_level = [ `Session | `Client_process ] type user_level = [ `Session_group | cookie_level ] val cookie_scope_of_user_scope : [< user_scope ] -> [> cookie_scope ] val cookie_level_of_user_scope : [< user_scope ] -> [> cookie_level ] val level_of_user_scope : [< user_scope ] -> [> user_level ] (** Eliom is using regular (browser) cookies but can also use its own browser tab cookies (only if you are using a client side Eliom application). It is possible to define Eliom references or services for one (browser) session, for one tab, or for one group of sessions. Using [`Global] scope means you want the data or service to be available to any client. [`Site] is limited to current sub-site (if you have several sites on the same server). If you want to restrict the visibility of an Eliom reference or a service: * to a browser session, use [~scope:Eliom_common.default_session_scope], * to a group of sessions, use [~scope:Eliom_common.default_group_scope], * to a client process, use [~scope:Eliom_common.default_process_scope]. If you have a client side Eliom program running, and you want to restrict the visibility of the service to this instance of the program, use [~scope:Eliom_common.default_process_scope]. You can create new scope hierachies with {!Eliom_common.create_scope_hierarchy}. Thus it is possible to have for example several sessions that can be opened or closed independently. They use different cookies. Secure scopes are associated to secure cookies (that is, cookies sent by browsers only if the protocol is https). *) type global_scope = [`Global] type site_scope = [`Site] type session_group_scope = [`Session_group of scope_hierarchy] type session_scope = [`Session of scope_hierarchy] type client_process_scope = [`Client_process of scope_hierarchy] type request_scope = [`Request] val global_scope : global_scope val site_scope : site_scope val default_group_scope : session_group_scope val default_session_scope : session_scope val default_process_scope : client_process_scope val comet_client_process_scope : client_process_scope val request_scope : request_scope val create_scope_hierarchy : string -> scope_hierarchy val list_scope_hierarchies : unit -> scope_hierarchy list (** {2 Exception and error handling} *) (** Page not found *) exception Eliom_404 (** Service called with wrong parameter names *) exception Eliom_Wrong_parameter exception Eliom_Session_expired (** The service (GET or POST) parameters do not match expected type *) exception Eliom_Typing_Error of (string * exn) list (** That function cannot be used when the site information is not available, that is, outside a request or the initialisation phase of your Eliom module (while reading the configuration file). In particular, you cannot use the function before the configuration file is read for example when you are using {e static linking}. In that case you must delay the function call using {!Eliom_service.register_eliom_module}. *) exception Eliom_site_information_not_available of string type full_state_name = user_scope * bool (* secure *) * string (* site_dir_string *) module Full_state_name_table : Map.S with type key = full_state_name (** If present and true in request data, it means that the previous coservice does not exist any more *) val eliom_link_too_old : bool Polytables.key (** If present in request data, means that the service session cookies does not exist any more. The string lists are the list of names of expired sessions *) val eliom_service_session_expired : (full_state_name list * full_state_name list) Polytables.key (**/**) (*VVV Warning: raising these exceptions will NOT send cookies! Do not use them inside services! *) exception Eliom_do_redirection of string (* Used to redirect to the suffix version of the service *) exception Eliom_do_half_xhr_redirection of string (** A [(v:tenable_value)] captures a value, which is available through [v#get]. The value can be be set to by [v#set]. However, once set by [v#set ~override_tenable:true] it can only be overridden by further calls to [v#set ~override_tenable:true]. Other attempts will be ignored. *) type 'a tenable_value = < get : 'a ; set : ?override_tenable:bool -> 'a -> unit > (** Create a named {!type:Eliom_common.tenable_value} with the given initial value. The name will only be used for warnings when setting a strong value isn't possible. *) val tenable_value : name:string -> 'a -> 'a tenable_value (* Service kinds: *) type att_key_serv = | SAtt_no (* regular service *) | SAtt_named of string (* named coservice *) | SAtt_anon of string (* anonymous coservice *) | SAtt_csrf_safe of (int * user_scope * bool option) (* CSRF safe anonymous coservice *) (* CSRF safe service registration delayed until form/link creation *) (* the int is an unique id, the user_scope is used for delayed registration (if the service is registered in the global table), the bool option is the ?secure parameter for delayed registration (if the service is registered in the global table) *) type na_key_serv = | SNa_no (* no na information *) | SNa_void_keep (* void coservice that keeps GET na parameters *) | SNa_void_dontkeep (* void coservice that does not keep GET na parameters *) | SNa_get_ of string (* named *) | SNa_post_ of string (* named *) | SNa_get' of string (* anonymous *) | SNa_post' of string (* anonymous *) | SNa_get_csrf_safe of (int * user_scope * bool option) (* CSRF safe anonymous coservice *) | SNa_post_csrf_safe of (int * user_scope * bool option) (* CSRF safe anonymous coservice *) (* the same, for incoming requests: *) type att_key_req = | RAtt_no (* no coservice information *) | RAtt_named of string (* named coservice *) | RAtt_anon of string (* anonymous coservice *) type na_key_req = | RNa_no (* no na information *) | RNa_get_ of string (* named *) | RNa_post_ of string (* named *) | RNa_get' of string (* anonymous *) | RNa_post' of string (* anonymous *) exception Eliom_duplicate_registration of string exception Eliom_there_are_unregistered_services of (string list * string list list * na_key_serv list) exception Eliom_page_erasing of string exception Eliom_error_while_loading_site of string val defaultpagename : string val eliom_suffix_name : string val eliom_suffix_internal_name : string val eliom_nosuffix_page : string val naservice_num : string val naservice_name : string val get_state_param_name : string val post_state_param_name : string val get_numstate_param_name : string val post_numstate_param_name : string val co_param_prefix : string val na_co_param_prefix : string val nl_param_prefix : string val eliom_internal_nlp_prefix : string val pnl_param_prefix : string val npnl_param_prefix : string (*204FORMS* old implementation of forms with 204 and change_page_event val internal_form_name : string val internal_form_bool_name : string *) val datacookiename : string val servicecookiename : string val persistentcookiename : string val persistent_cookie_table_version : string val eliom_persistent_cookie_table : string val inline_class_name : string val nodisplay_class_name : string val appl_name_cookie_name : string val tab_cookies_param_name : string val to_be_considered_as_get_param_name : string val full_xhr_redir_header : string val half_xhr_redir_header : string val default_group_name : string type client_process_info = { cpi_ssl : bool; cpi_hostname : string; cpi_server_port : int; cpi_original_full_path : Url.path; } type sess_info = { si_other_get_params : (string * string) list; si_all_get_params : (string * string) list; si_all_post_params : (string * string) list option; si_service_session_cookies : string Full_state_name_table.t; si_data_session_cookies : string Full_state_name_table.t; si_persistent_session_cookies : string Full_state_name_table.t; si_secure_cookie_info: (string Full_state_name_table.t * string Full_state_name_table.t * string Full_state_name_table.t) option; si_service_session_cookies_tab: string Full_state_name_table.t; si_data_session_cookies_tab: string Full_state_name_table.t; si_persistent_session_cookies_tab: string Full_state_name_table.t; si_secure_cookie_info_tab: (string Full_state_name_table.t * string Full_state_name_table.t * string Full_state_name_table.t) option; si_tab_cookies: string CookiesTable.t; si_nonatt_info : na_key_req; si_state_info: (att_key_req * att_key_req); si_previous_extension_error : int; si_na_get_params: (string * string) list Lazy.t; si_nl_get_params: (string * string) list String.Table.t; si_nl_post_params: (string * string) list String.Table.t; si_persistent_nl_get_params: (string * string) list String.Table.t Lazy.t; si_all_get_but_na_nl: (string * string) list Lazy.t; si_all_get_but_nl: (string * string) list; si_client_process_info: client_process_info option; si_expect_process_data : bool Lazy.t; (*204FORMS* si_internal_form: bool; *) } module SessionCookies : Hashtbl.S with type key = string (* session groups *) type 'a sessgrp = (string * cookie_level * (string, Ip_address.t) leftright) (* The full session group is the triple (site_dir_string, scope, session group name). The scope is the scope of group members (`Session by default). If there is no session group, we limit the number of sessions by IP address. *) type perssessgrp (* the same triple, marshaled *) val make_persistent_full_group_name : cookie_level:cookie_level -> string -> string option -> perssessgrp option val getperssessgrp : perssessgrp -> (string * cookie_level * (string, Ip_address.t) leftright) val string_of_perssessgrp : perssessgrp -> string type 'a session_cookie = SCNo_data | SCData_session_expired | SC of 'a type cookie_exp = | CENothing (* keep current browser value *) | CEBrowser (* ask to remove the cookie when the browser is closed *) | CESome of float (* date (not duration!) *) type timeout = TGlobal | TNone | TSome of float type 'a one_service_cookie_info = { sc_value : string; sc_table : 'a ref; sc_timeout : timeout ref; sc_exp : float option ref; sc_cookie_exp : cookie_exp ref; sc_session_group: cookie_level sessgrp ref (* session group *); mutable sc_session_group_node:string Ocsigen_cache.Dlist.node; } type one_data_cookie_info = { dc_value : string; dc_timeout : timeout ref; dc_exp : float option ref; dc_cookie_exp : cookie_exp ref; dc_session_group: cookie_level sessgrp ref (* session group *); mutable dc_session_group_node:string Ocsigen_cache.Dlist.node; } type one_persistent_cookie_info = { pc_value : string; pc_timeout : timeout ref; pc_cookie_exp : cookie_exp ref; pc_session_group : perssessgrp option ref; } type 'a cookie_info1 = (string option * 'a one_service_cookie_info session_cookie ref) Full_state_name_table.t ref * (string option * one_data_cookie_info session_cookie ref) Lazy.t Full_state_name_table.t ref * ((string * timeout * float option * perssessgrp option) option * one_persistent_cookie_info session_cookie ref) Lwt.t Lazy.t Full_state_name_table.t ref type 'a cookie_info = 'a cookie_info1 (* unsecure *) * 'a cookie_info1 option (* secure, if https *) type 'a servicecookiestablecontent = full_state_name * 'a * float option ref * timeout ref * cookie_level sessgrp ref * string Ocsigen_cache.Dlist.node type 'a servicecookiestable = 'a servicecookiestablecontent SessionCookies.t type datacookiestablecontent = full_state_name * float option ref * timeout ref * cookie_level sessgrp ref * string Ocsigen_cache.Dlist.node type datacookiestable = datacookiestablecontent SessionCookies.t type page_table_key = { key_state : att_key_serv * att_key_serv; key_kind : Ocsigen_http_frame.Http_header.http_method; } module NAserv_Table : Map.S with type key = na_key_serv module Serv_Table : Map.S with type key = page_table_key type dlist_ip_table type anon_params_type = int type node_ref = string type node_info = { ni_id : node_ref; mutable ni_sent : bool; } module Hier_set : Set.S type server_params = { sp_request : Ocsigen_extensions.request; sp_si : sess_info; sp_sitedata : sitedata; sp_cookie_info : tables cookie_info; sp_tab_cookie_info : tables cookie_info; mutable sp_user_cookies: Ocsigen_cookies.cookieset; (* cookies (un)set by the user during service *) mutable sp_user_tab_cookies: Ocsigen_cookies.cookieset; mutable sp_client_appl_name: string option; (* The application name, as sent by the browser *) sp_suffix : Url.path option; sp_full_state_name : full_state_name option; sp_client_process_info: client_process_info; (* Contains the base URL information from which the client process has been launched (if any). All relative links and forms will be created with respect to this information (if present - from current URL otherwise). It is taken form a client process state if the application has been launched before (and not timeouted on server side). Otherwise, it is created and registered in a server side state the first time we need it. *) } and page_table = page_table_content Serv_Table.t and page_table_content = Ptc of (page_table ref * page_table_key, na_key_serv) leftright Ocsigen_cache.Dlist.node option (* for limitation of number of dynamic anonymous coservices *) * ((anon_params_type * anon_params_type) (* unique_id, computed from parameters type. must be the same even if the actual service reference is different (after reloading the site) so that it replaces the former one *) * (int ref option (* max_use *) * (float * float ref) option (* timeout and expiration date for the service *) * (bool -> server_params -> Ocsigen_http_frame.result Lwt.t) )) list and naservice_table_content = (int (* generation (= number of reloads of sites after which that service has been created) *) * int ref option (* max_use *) * (float * float ref) option (* timeout and expiration date *) * (server_params -> Ocsigen_http_frame.result Lwt.t) * (page_table ref * page_table_key, na_key_serv) leftright Ocsigen_cache.Dlist.node option (* for limitation of number of dynamic coservices *) ) and naservice_table = | AVide | ATable of naservice_table_content NAserv_Table.t and dircontent = Vide | Table of direlt ref String.Table.t and direlt = Dir of dircontent ref | File of page_table ref and tables = {mutable table_services : (int (* generation *) * int (* priority *) * dircontent ref) list; table_naservices : naservice_table ref; (* Information for the GC: *) mutable table_contains_services_with_timeout : bool; (* true if dircontent contains services with timeout *) mutable table_contains_naservices_with_timeout : bool; (* true if naservice_table contains services with timeout *) mutable csrf_get_or_na_registration_functions : (sp:server_params -> string) Int.Table.t; mutable csrf_post_registration_functions : (sp:server_params -> att_key_serv -> string) Int.Table.t; (* These two table are used for CSRF safe services: We associate to each service unique id the function that will register a new anonymous coservice each time we create a link or form. Attached POST coservices may have both a GET and POST registration function. That's why there are two tables. The functions associated to each service may be different for each session. That's why we use these table, and not a field in the service record. *) service_dlist_add : ?sp:server_params -> (page_table ref * page_table_key, na_key_serv) leftright -> (page_table ref * page_table_key, na_key_serv) leftright Ocsigen_cache.Dlist.node (* Add in a dlist for limiting the number of dynamic anonymous coservices in each table (and avoid DoS). There is one dlist for each session, and one for each IP in global tables. The dlist parameter is the table and coservice number for attached coservices, and the coservice number for non-attached ones. *) } and sitedata = { site_dir : Url.path; site_dir_string : string; config_info: Ocsigen_extensions.config_info; default_links_xhr : bool tenable_value; (* Timeouts: - default for site (browser sessions) - default for site (tab sessions) - then default for each full state name The booleans means "has been set from config file" *) mutable servtimeout: (float option * bool) option * (float option * bool) option * ((full_state_name * (float option * bool)) list); mutable datatimeout: (float option * bool) option * (float option * bool) option * ((full_state_name * (float option * bool)) list); mutable perstimeout: (float option * bool) option * (float option * bool) option * ((full_state_name * (float option * bool)) list); site_value_table : Polytables.t; (* table containing evaluated lazy site values *) mutable registered_scope_hierarchies: Hier_set.t; global_services : tables; session_services : tables servicecookiestable; session_data : datacookiestable; group_of_groups: [ `Session_group ] sessgrp Ocsigen_cache.Dlist.t; (* Limitation of the number of groups per site *) mutable remove_session_data : string -> unit; mutable not_bound_in_data_tables : string -> bool; mutable exn_handler : exn -> Ocsigen_http_frame.result Lwt.t; mutable unregistered_services : Url.path list; mutable unregistered_na_services : na_key_serv list; mutable max_volatile_data_sessions_per_group : int * bool; mutable max_volatile_data_sessions_per_subnet : int * bool; mutable max_volatile_data_tab_sessions_per_group : int * bool; mutable max_service_sessions_per_group : int * bool; mutable max_service_sessions_per_subnet : int * bool; mutable max_service_tab_sessions_per_group : int * bool; mutable max_persistent_data_sessions_per_group : int option * bool; mutable max_persistent_data_tab_sessions_per_group : int option * bool; mutable max_anonymous_services_per_session : int * bool; mutable max_anonymous_services_per_subnet : int * bool; dlist_ip_table : dlist_ip_table; mutable ipv4mask : int32 option * bool; mutable ipv6mask : (int64 * int64) option * bool; } type 'a lazy_site_value (** lazy site values, are lazy values with content available only in the context of a site: the closure one time for each site ( requesting it ) *) val force_lazy_site_value : 'a lazy_site_value -> 'a val lazy_site_value_from_fun : ( unit -> 'a ) -> 'a lazy_site_value type info = (Ocsigen_extensions.request * sess_info * tables cookie_info * tables cookie_info * Ocsigen_cookies.cookieset) exception Eliom_retry_with of info val make_server_params : sitedata -> info -> Url.path option -> full_state_name option -> server_params val empty_page_table : unit -> page_table val empty_dircontent : unit -> dircontent val empty_naservice_table : unit -> naservice_table val service_tables_are_empty : tables -> bool val empty_tables : int -> bool -> tables val new_service_session_tables : sitedata -> tables val split_prefix_param : string -> (string * 'a) list -> (string * 'a) list * (string * 'a) list val get_session_info : Ocsigen_extensions.request -> int -> (Ocsigen_extensions.request * sess_info * (tables cookie_info * Ocsigen_cookies.cookieset) option) Lwt.t type ('a, 'b) foundornot = Found of 'a | Notfound of 'b val make_full_cookie_name : string -> full_state_name -> string val make_full_state_name : sp:server_params -> secure:bool -> scope:[< user_scope ] -> full_state_name val make_full_state_name2 : string -> bool -> scope:[< user_scope ] -> full_state_name module Perstables : sig val empty : 'a list val add : 'a -> 'a list -> 'a list val fold : ('a -> 'b -> 'a) -> 'a -> 'b list -> 'a end val perstables : string list ref val create_persistent_table : string -> 'a Ocsipersist.table val persistent_cookies_table : (full_state_name * float option * timeout * perssessgrp option) Ocsipersist.table Lazy.t val remove_from_all_persistent_tables : string -> unit Lwt.t val absolute_change_sitedata : sitedata -> unit val get_current_sitedata : unit -> sitedata val end_current_sitedata : unit -> unit val add_unregistered : sitedata -> Url.path -> unit val add_unregistered_na : sitedata -> na_key_serv -> unit val remove_unregistered : sitedata -> Url.path -> unit val remove_unregistered_na : sitedata -> na_key_serv -> unit val verify_all_registered : sitedata -> unit val during_eliom_module_loading : unit -> bool val begin_load_eliom_module : unit -> unit val end_load_eliom_module : unit -> unit val global_register_allowed : unit -> (unit -> sitedata) option (** Get the site data, which is only available {e during the loading of eliom modules, and during a request.} *) val get_site_data : unit -> sitedata val eliom_params_after_action : ((string * string) list * (string * string) list option * (string * string) list String.Table.t * (string * string) list String.Table.t * (string * string) list (*204FORMS* * bool *)) Polytables.key val att_key_serv_of_req : att_key_req -> att_key_serv val na_key_serv_of_req : na_key_req -> na_key_serv val remove_naservice_table : naservice_table -> NAserv_Table.key -> naservice_table val get_mask4 : sitedata -> int32 val get_mask6 : sitedata -> (int64 * int64) val ipv4mask : int32 ref val ipv6mask : (int64 * int64) ref val create_dlist_ip_table : int -> dlist_ip_table val find_dlist_ip_table : int32 option * 'a -> (int64 * int64) option * 'a -> dlist_ip_table -> Ip_address.t -> (page_table ref * page_table_key, na_key_serv) leftright Ocsigen_cache.Dlist.t val get_cookie_info : server_params -> [< cookie_level ] -> tables cookie_info val tab_cookie_action_info_key : (tables cookie_info * Ocsigen_cookies.cookieset * string CookiesTable.t) Polytables.key val sp_key : server_params Lwt.key val get_sp_option : unit -> server_params option val get_sp : unit -> server_params val sp_of_option : server_params option -> server_params val found_stop_key : unit Polytables.key (**** Wrapper type shared by client/server side ***) type 'a wrapper = 'a Eliom_wrap.wrapper val make_wrapper : ('a -> 'b) -> 'a wrapper val empty_wrapper : unit -> 'a wrapper type unwrapper = Eliom_wrap.unwrapper type unwrap_id = Eliom_wrap.unwrap_id val make_unwrapper : unwrap_id -> unwrapper val empty_unwrapper : unwrapper val react_up_unwrap_id : unwrap_id val react_down_unwrap_id : unwrap_id val signal_down_unwrap_id : unwrap_id val comet_channel_unwrap_id : unwrap_id val bus_unwrap_id : unwrap_id val nl_get_appl_parameter: string val patch_request_info: Ocsigen_extensions.request -> Ocsigen_extensions.request eliom-3.0.3/src/server/eliom_registration.mli0000644000000000000000000004570012062377521017522 0ustar0000000000000000(* Ocsigen * http://www.ocsigen.org * Module Eliom_registration * Copyright (C) 2007 Vincent Balat * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, with linking exception; * either version 2.1 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *) (** Eliom services registration for various kinds of page content: Eliom application, valid {!Html5}, actions, redirections, static files, … *) (** See the Eliom manual for more information on {% <>%} and {% <>%}. *) (** {% <> %}*) open Eliom_lib open Eliom_content_core (** {2 Type definitions} *) (** The type [kind] is an abstract type for the HTTP frame returned by a service. The type parameters are phantom types describing the content of the frame: - The second parameter is the same as the last type parameters of the corresponding {!type:Eliom_service.service}. Currently, one of the following types: {ul {- {!Eliom_registration.appl_service}} {- {!Eliom_registration.http_service}} {- {!Eliom_parameter.caml}} } - The first parameter is a refinement of the second parameter. Currently, one of the following types: {ul {- {!application_content}} {- {!browser_content}} {- {!block_content}} {- {!unknown_content}} {- {!caml_content}}} *) type ('a, 'b) kind (** {3 Return types for {!type:Eliom_service.service} } *) (** {4 Classical content} *) (** The type [http_service] is used as a phantom type parameters for {!Eliom_service.service} and {!Eliom_registration.kind}. It means the returned content is classical HTTP content described by the content type header. See {!Eliom_registration.kind} for a list of others return types. *) type http_service = [ `Http ] (** The type [browser_content] is a refinement of {!http_service} to be used as a phantom type parameters for {!Eliom_registration.kind}. It means the returned content must be interpreted in the browser as stated by the content-type header. This is most common return type for an eliom service, see for example {!Html5}, {!CssText}, {!File}, {!Redirection}, ….*) type browser_content = [ `Browser ] (** The type [block_content] is a refinement of {!http_service} to be used as a phantom type parameters for {!Eliom_registration.kind}. It means the returned content is a subtree of an XML value. See for example {!Block5} or {!Make_typed_xml_registration}. *) type block_content (** The type [unknown_content] is a refinement of {!http_service} to be used as a phantom type parameters for {!Eliom_registration.kind} when the content-type can't be determined staticaly. See {!Text} or {!Any}. *) type unknown_content (** {4 Application content} *) (** The type [appl_service] is used as a phantom type parameters for {!Eliom_service.service} and {!Eliom_registration.kind}. It means the service is part of an Eliom application. See {!Eliom_registration.kind} for a list of others return types. *) type appl_service = [ `Appl ] (** The type [application_content] is a refinement of {!appl_service} to be used as a phantom type parameters for {!Eliom_registration.kind}. The parameter ['a] is phantom type that is unique for a given application. *) type 'a application_content = [ `Appl of 'a ] (**/**) type 'a application_name (**/**) (** {4 OCaml content} *) (** The type [caml_content] is an synomyn for {!Eliom_parameter.caml} to be used as a phantom type parameters for {!Eliom_registration.kind}. See {!Ocaml}. *) type 'a caml_content (** The type [non_caml_service] is used as phantom type parameters for the {!Eliom_registration.kind}. It used to type functions that operates over service that do not returns OCaml values, like {!appl_self_redirect}. *) type non_caml_service = [ appl_service | http_service ] (** {3 Module signature} *) (** Abstract signature for service registration functions. For concrete instance see {!Html5}, {!CssText}, {!File}, {!Redirection}, … *) module type Registration = sig type page type options type return type result include "sigs/eliom_reg_simpl.mli" end (** {2 Using HTML5 with services } *) (** Eliom service registration for services that returns HTML5 page. This is a subset of the {!Html5} module and an instance of the {!Registration} abstract signature. *) module Html5_registration : "sigs/eliom_html5_reg.mli" (** Eliom service registration for HTML5 page. This an instance the {!modtype:Registration} abstract signatures. *) module Html5 : sig include "sigs/eliom_html5_reg.mli" end (** {2 Eliom client/server applications} *) (** Signature for application creation. *) module type APPL_PARAMS = sig (** Name of the application. Two distincts applications must have distincts names. *) val application_name : string end (** Type for the options of an Eliom application service. If you set [do_not_launch] to [true] when creating an application service, it will send the page without launching the client side program. However, if the program is already lanched, the client side process won't be stopped. Use this if some of your pages are not using the client side program and you want to make them load faster. *) type appl_service_options = { do_not_launch : bool; (** Do not launch the client side program if it is not already launched. Default: [false]. *) } (** The default options record for an eliom service. See {!appl_service_options}. *) val default_appl_service_options : appl_service_options module type ELIOM_APPL = sig (** The function [application_name ()] returns a [" end (****************************************************************************) (****************************************************************************) module Textforms = MakeForms(Textforms_) module Textreg = MakeRegister(Textreg_) module Text = struct include Textforms include Textreg end eliom-3.0.3/src/legacy/oldocsigenmod/ocsigenboxes.mli0000644000000000000000000000230612062377521021072 0ustar0000000000000000(** Predefined boxes for Ocsigenmod *) val menu : ?classe:XHTML.F.nmtoken list -> ((unit,unit, [<`Internal_Service of [<`Public_Service | `Local_Service] | `External_Service],[<`WithSuffix|`WithoutSuffix] as 'tipo,unit Ocsigen.param_name, unit Ocsigen.param_name) Ocsigen.service * Xhtmltypes.a_content XHTML.F.elt list) -> ((unit,unit, [<`Internal_Service of [<`Public_Service | `Local_Service] | `External_Service],[<`WithSuffix|`WithoutSuffix] as 'tipo,unit Ocsigen.param_name, unit Ocsigen.param_name) Ocsigen.service * Xhtmltypes.a_content XHTML.F.elt list) list -> (unit,unit, [<`Internal_Service of [<`Public_Service | `Local_Service] | `External_Service],'tipo, unit Ocsigen.param_name, unit Ocsigen.param_name) Ocsigen.service -> Ocsigen.server_params -> [> `Ul ] XHTML.F.elt (** Creates a menu Example: [menu ~classe:["mainmenu"] [ (home, <:xmllist< Home >>); (infos, <:xmllist< More infos >>) ] current sp] Tip: How to make a menu with different kinds of services (external, internal...)? You need to coerce each of them. For example [(home :> (('a, 'b, 'c, 'd, 'e, 'f, 'g, 'h, Ocsigen.service_kind) service))] *) eliom-3.0.3/src/legacy/oldocsigenmod/ocsigenduce.ml0000644000000000000000000001606212062377521020525 0ustar0000000000000000(* Ocsigen * http://www.ocsigen.org * Module ocsigenduce.ml * Copyright (C) 2007 Vincent Balat, Alain Frisch * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, with linking exception; * either version 2.1 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *) open Ocsigen_http_frame open Ocsigen_http_com open Lwt open Ocsigen_senders open Xhtmltypes_duce let add_css (a : html) : html = let css = {{