Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .merlin
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ PKG cryptokit
PKG netstring
PKG netstring-pcre
PKG ipaddr
PKG tyxml tyxml.parser
PKG tyxml
PKG xml-light
PKG camlzip
PKG dynlink

Expand Down
17 changes: 5 additions & 12 deletions Makefile.options
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
BYTEDBG :=
OPTDBG :=
THREAD :=
THREAD := -thread

ifeq "$(DEBUG)" "YES"
BYTEDBG += -g
Expand All @@ -17,29 +17,22 @@ BYTEDBG := -p ${BYTEDBG}
OPTDBG += -p
endif

ifeq "$(PREEMPTIVE)" "YES"
THREAD += -thread
endif

## ${SERVER_PACKAGE} is not only used to build the 'ocsigenserver' executable
## but also to generate src/baselib/ocsigen_config.ml and src/files/META

ifeq "$(PREEMPTIVE)" "YES"
LWT_PREEMPTIVE_PACKAGE:=lwt.preemptive
endif

BASE_PACKAGE := lwt ipaddr bytes

SERVER_PACKAGE := lwt_ssl \
bytes \
${LWT_PREEMPTIVE_PACKAGE} \
bytes \
lwt.unix \
lwt_log \
ipaddr \
netstring \
netstring-pcre \
findlib \
cryptokit \
tyxml \
tyxml.parser \
xml-light \
dynlink \

INITPACKAGE := \"$(shell ${OCAMLFIND} query -p-format -recursive \
Expand Down
30 changes: 3 additions & 27 deletions configure
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,6 @@ set_defaults () {
with_pgsql=1
with_camlzip=1
with_dbm=1
with_preempt=1
prefix="/usr/local"
bindir=""
logdir=""
Expand All @@ -108,7 +107,7 @@ full_pwd=`pwd`

## Which options exist? eoptions for enable/disable, woptions for with/without:
eoptions="debug annot natdynlink"
woptions="pgsql sqlite dbm camlzip preempt"
woptions="pgsql sqlite dbm camlzip"

print_options () {
for opt in $eoptions; do
Expand Down Expand Up @@ -412,7 +411,7 @@ check_library lwt "See: http://ocsigen.org/lwt"
check_library lwt.unix "Missing support for 'unix' in lwt."
check_library lwt_react "See: http://ocsigen.org/lwt"
check_library lwt_ssl "See: http://ocsigen.org/lwt"
check_library lwt.preemptive "Missing support for 'preemptive' in lwt."
check_library lwt_log "See: http://ocsigen.org/lwt"

check_library netstring \
"See ocamlnet: http://projects.camlcity.org/projects/ocamlnet.html"
Expand All @@ -425,6 +424,7 @@ check_library pcre "See: http://ocaml.info/home/ocaml_sources.html"
check_library cryptokit "See: http://pauillac.inria.fr/~xleroy/software.html#cryptokit"

check_library tyxml "See: http://ocsigen.org/tyxml/"
check_library xml-light "See: https://github.com/ncannasse/xml-light"

# Check PostgreSQL
case "$with_pgsql" in
Expand Down Expand Up @@ -462,22 +462,6 @@ if [ "$with_camlzip" -gt 0 ]; then
fi
fi

# Check Lwt.preemptive
if [ "$with_preempt" -gt 0 ]; then
if test_library lwt.preemptive; then
echo -n
elif [ "$with_preempt" -gt 1 ]; then
fail_library lwt.preemptive "Missing support for 'preemptive' in lwt."
else
with_preempt=0
fi
fi

if [ "$with_sqlite" -eq 1 ] && [ "$with_preempt" -eq -1 ]; then
echo "preemptive threads are needed by sqlite, enable it with -with-preempt"
exit 1
fi

# Check rlwrap or ledit
if test_binary rlwrap; then
rlwrap=rlwrap
Expand Down Expand Up @@ -532,11 +516,6 @@ if [ $with_camlzip -gt 0 ] ; then
else
with_camlzip="NO"
fi
if [ $with_preempt -gt 0 ] ; then
with_preempt="YES"
else
with_preempt="NO"
fi

ocamlinclude=`ocamlfind printconf stdlib`

Expand Down Expand Up @@ -588,9 +567,6 @@ OCSIPERSISTPGSQL:=$with_pgsql
# Do you want ocsipersist with dbm? YES/NO
OCSIPERSISTDBM:=$with_dbm

# Do you want preemptive threads ? YES/NO
PREEMPTIVE:=$with_preempt

# Do you want debugging information (-g) ? YES/NO
DEBUG:=$enable_debug

Expand Down
63 changes: 31 additions & 32 deletions doc/manual-wiki/extend.wiki
Original file line number Diff line number Diff line change
Expand Up @@ -4,79 +4,79 @@

This page describes how to extend Ocsigen's server. This can be used to create new ways to generate pages (like Apache modules), to filter and change the requests (for example, rewriting of URLS), to extend the syntax of the configuration file.

Remember to program your extensions in cooperative way using Lwt!
Remember to program your extensions in cooperative way using Lwt!

These features have been introduced in Ocsigen 0.6.0. The extension mechanism will be improved in the future to fit better the needs of developers. As very extensions have been written for now, all this is somewhat experimental, and we look forward to feedback from developers about missing features or problems.
These features have been introduced in Ocsigen 0.6.0. The extension mechanism will be improved in the future to fit better the needs of developers. As very extensions have been written for now, all this is somewhat experimental, and we look forward to feedback from developers about missing features or problems.

//Ocsigen is a collaborative project. Contributions are welcome. For example a proxy, a fastCGI module~ ...//

===Filtering the requests or writing a module to generate pages
===Filtering the requests or writing a module to generate pages

You can take as example the files {{{extensiontemplate.ml}}} or {{{staticmod.ml}}} from Ocsigen's distribution.
You can take as example the files {{{extensiontemplate.ml}}} or {{{staticmod.ml}}} from Ocsigen's distribution.

The type of request is {{{Extensions.request_info}}} (have a look at it in the interface of the module {{{Extensions}}}).
The type of request is {{{Extensions.request_info}}} (have a look at it in the interface of the module {{{Extensions}}}).

Each extensions loaded in the configuration file tries to handle the request and returns something of type {{{Extensions.answer}}}. If the page is not found by the extension ({{{Ext_not_found}}}), the following one will try to handle the request. If the page is found, the answer is {{{Ext_found r}}} where {{{r}}} has type {{{Extensions.result}}}. An extension can also modify the request before giving it to the next one (answer {{{Ext_continue_with of Extensions.request_info}}}).
Each extensions loaded in the configuration file tries to handle the request and returns something of type {{{Extensions.answer}}}. If the page is not found by the extension ({{{Ext_not_found}}}), the following one will try to handle the request. If the page is found, the answer is {{{Ext_found r}}} where {{{r}}} has type {{{Extensions.result}}}. An extension can also modify the request before giving it to the next one (answer {{{Ext_continue_with of Extensions.request_info}}}).

To write such an extension, just write a cmo or cma, and use the function Extensions.register_extension to register you extension. This function takes four functions as parameters:
* a function that will be called for each virtual server, generating two functions:
**one that will be called to generate the pages (it has type {{{string option -> request_info -> answer Lwt.t}}}), where the {{{string option}}} is the encoding for characters possibly specified in the configuration file,
**one to parse the configuration file (see next section).
*a function that will be called at the beginning of the initialisation phase (if you need to initialize your extension, otherwise, put the identity).
*a function that will be called at the end of the initialisation phase of the server (if you need to do something here, otherwise the identity function).
*a function that will create an error message from the exceptions that may be raised during the initialisation phase, and raise again all other exceptions. That function has type {{{exn -> string}}}. Use the raise function if you don't need any.
To write such an extension, just write a cmo or cma, and use the function Extensions.register_extension to register you extension. This function takes four functions as parameters:
* a function that will be called for each virtual server, generating two functions:
**one that will be called to generate the pages (it has type {{{string option -> request_info -> answer Lwt.t}}}), where the {{{string option}}} is the encoding for characters possibly specified in the configuration file,
**one to parse the configuration file (see next section).
*a function that will be called at the beginning of the initialisation phase (if you need to initialize your extension, otherwise, put the identity).
*a function that will be called at the end of the initialisation phase of the server (if you need to do something here, otherwise the identity function).
*a function that will create an error message from the exceptions that may be raised during the initialisation phase, and raise again all other exceptions. That function has type {{{exn -> string}}}. Use the raise function if you don't need any.

Example (from {{{staticmod.ml}}}):
Example (from {{{staticmod.ml}}}):

{{{
let _ = register_extension
((fun _ ->
let page_tree =
try
((fun _ ->
let page_tree =
try
find hostpattern
with Not_found ->
let n = new_pages_tree () in
add hostpattern n;
n
in
(gen page_tree,
(gen page_tree,
parse_config page_tree)),
start_init,
end_init,
raise)
}}}

While writing extensions, be very careful about site reloading. The initialisation phase will start again at each reloading, and the function you register will be called for each virtual at each reloading.
While writing extensions, be very careful about site reloading. The initialisation phase will start again at each reloading, and the function you register will be called for each virtual at each reloading.

===Filtering the outputs
===Filtering the outputs
It is also possible to create extensions that will filter the output of the server (for example to compress it). It is very similar to the previous one. Basically, use {{{Extensions.register_output_filter}}} instead of {{{Extensions.register_extension}}}. Have a look at the file {{{deflatemod.ml}}} for an example.

===Extending the configuration file
===Extending the configuration file

====Extending the configuration file for an extension
====Extending the configuration file for an extension

The parsing of Ocsigen's configuration file is using a very basic xml parser (module {{{Simplexmlparser}}}). The function to be registered by the {{{Extensions.register_extension}}} function takes two parameters: the path of the web site and the xml subtree.
The parsing of Ocsigen's configuration file is using a very basic xml parser (package {{{xml-light}}}). The function to be registered by the {{{Extensions.register_extension}}} function takes two parameters: the path of the web site and the xml subtree.

{{{
let parse_config path = function
Simplexmlparser.Element ("tag", attr, content) -> ...
Xml.Element ("tag", attr, content) -> ...
(* Do what you want here *)
| Simplexmlparser.Element _ ->
| Xml.Element _ ->
raise (Extensions.Bad_config_tag_for_extension t)
| _ -> raise (Extensions.Error_in_config_file "(my extension)")
}}}

The module {{{Parseconfig}}} defines functions to parse strings or sizes (in bytes, GB etc).
The module {{{Parseconfig}}} defines functions to parse strings or sizes (in bytes, GB etc).

====Giving parameters to an extension
====Giving parameters to an extension

//Warning: This is experimental. Please report your experience if you use it.//

Extensions may take parameters in the configuration file. During the loading of the extension, the function {{{Extensions.get_config ()}}} returns the xml tree between {{{<extension>}}} and {{{</extension>}}} (or {{{<library>}}} and {{{</library>}}}). Write a parser for that tree.
Extensions may take parameters in the configuration file. During the loading of the extension, the function {{{Extensions.get_config ()}}} returns the xml tree between {{{<extension>}}} and {{{</extension>}}} (or {{{<library>}}} and {{{</library>}}}). Write a parser for that tree.

===Catching the request before it is fully read
===Catching the request before it is fully read

For some extensions of the Web server, it is necessary to catch the request before it has been fully read (especially before the body of the request has been read). For example it is the case if you want to write a (reverse) proxy.
For some extensions of the Web server, it is necessary to catch the request before it has been fully read (especially before the body of the request has been read). For example it is the case if you want to write a (reverse) proxy.

//Warning: This is experimental. Please report your experience if you use it.//

Expand All @@ -94,10 +94,9 @@ It takes as parameter a function of type
If you want to add your own commands for the server command pipe, do something like:

{{{
let () =
let () =
Ocsigen_extensions.register_command_function ~prefix:"yourextensionname"
(fun s c -> match c with
| ["mycommand"] -> ...
| _ -> raise Ocsigen_extensions.Unknown_command)
}}}

5 changes: 3 additions & 2 deletions opam
Original file line number Diff line number Diff line change
Expand Up @@ -48,16 +48,17 @@ depends: [
"base-threads"
"react"
"ssl"
"lwt" {>= "3.0.0" & < "4.0.0"}
"lwt" {>= "3.0.0"}
"lwt_ssl"
"lwt_react"
"lwt_log"
"ocamlnet" {>= "4.0.2"}
"pcre"
"cryptokit"
"tyxml" {>= "4.0.0"}
("dbm" | "sqlite3" | "pgocaml")
"ipaddr" {>= "2.1"}
"camlp4" # to force building tyxml.parser
"xml-light"
]
depopts: "camlzip"
conflicts: [
Expand Down
8 changes: 1 addition & 7 deletions src/baselib/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ include ../../Makefile.config
PACKAGE := \
bytes \
lwt.unix \
lwt_log \
netstring \
netstring-pcre \
cryptokit \
Expand Down Expand Up @@ -57,12 +58,6 @@ else
NATIVECODE_RUNTIME_DETECT=false
endif

ifeq "$(PREEMPTIVE)" "YES"
PREEMTIMPLEM=Lwt_preemptive
else
PREEMTIMPLEM=Fake_preempt
endif

VERSION := $(shell head -n 1 ../../VERSION)

ocsigen_config.ml: ocsigen_config.ml.in ../../Makefile.config ../../Makefile.options ../../VERSION
Expand All @@ -80,7 +75,6 @@ ocsigen_config.ml: ocsigen_config.ml.in ../../Makefile.config ../../Makefile.opt
| sed s%_PROJECTNAME_%$(PROJECTNAME)%g \
| sed s%_COMMANDPIPE_%$(COMMANDPIPE)%g \
| sed s%_CONFIGDIR_%$(CONFIGDIR)% \
| sed s%_PREEMTIMPLEM_%$(PREEMTIMPLEM)% \
| sed s%_ISNATIVE_%$(NATIVECODE_RUNTIME_DETECT)%g \
| sed "s%_DEPS_%$(INITPACKAGE)%g" \
> ocsigen_config.ml
Expand Down
14 changes: 3 additions & 11 deletions src/baselib/ocsigen_config.ml.in
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,6 @@ let native_ext = if is_native then ".opt" else ""
let builtin_packages =
List.fold_left (fun a s -> String.Set.add s a) String.Set.empty [_DEPS_]

module Fake_preempt =
struct
let set_max_number_of_threads_queued : int -> unit = fun _ -> ()
let get_max_number_of_threads_queued : unit -> int = fun () -> 0
let init : int -> int -> (string -> unit) -> unit = fun _ _ _ -> ()
end

(* Server config: *)
let (uploaddir : string option ref) = ref None
let logdir = ref (Some ("_LOGDIR_"))
Expand Down Expand Up @@ -97,7 +90,7 @@ let set_veryverbose () =
let set_minthreads i = minthreads := i
let set_maxthreads i = maxthreads := i
let set_max_number_of_threads_queued =
_PREEMTIMPLEM_.set_max_number_of_threads_queued
Lwt_preemptive.set_max_number_of_threads_queued
let set_max_number_of_connections i = max_number_of_connections := i
let set_client_timeout i = silent_client_timeout := i
let set_server_timeout i = silent_server_timeout := i
Expand Down Expand Up @@ -143,7 +136,7 @@ let get_default_group () = !default_group
let get_minthreads () = !minthreads
let get_maxthreads () = !maxthreads
let get_max_number_of_threads_queued =
_PREEMTIMPLEM_.get_max_number_of_threads_queued
Lwt_preemptive.get_max_number_of_threads_queued
let get_max_number_of_connections () = !max_number_of_connections
let get_client_timeout () = !silent_client_timeout
let get_server_timeout () = !silent_server_timeout
Expand Down Expand Up @@ -175,5 +168,4 @@ let display_version () =
print_newline ();
exit 0

let init_preempt =
_PREEMTIMPLEM_.init
let init_preempt = Lwt_preemptive.init
4 changes: 2 additions & 2 deletions src/baselib/ocsigen_stream.ml
Original file line number Diff line number Diff line change
Expand Up @@ -229,10 +229,10 @@ let of_file filename =
let fd = Lwt_unix.of_unix_file_descr
(Unix.openfile filename [Unix.O_RDONLY;Unix.O_NONBLOCK] 0o666)
in
let ch = Lwt_chan.in_channel_of_descr fd in
let ch = Lwt_io.of_fd ~mode:Lwt_io.input fd in
let buf = Bytes.create 1024 in
let rec aux () =
Lwt_chan.input ch buf 0 1024 >>= fun n ->
Lwt_io.read_into ch buf 0 1024 >>= fun n ->
if n = 0 then empty None else
(* Streams should be immutable, thus we always make a copy
of the buffer *)
Expand Down
3 changes: 2 additions & 1 deletion src/extensions/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@ include ../../Makefile.config
PACKAGE := \
bytes \
lwt.unix \
lwt_log \
ipaddr \
lwt_ssl \
lwt_react \
netstring \
netstring-pcre \
tyxml.parser
xml-light

LIBS := -I ../baselib -I ../http -I ../server ${addprefix -package ,${PACKAGE}}
OCAMLC := $(OCAMLFIND) ocamlc ${BYTEDBG} ${THREAD}
Expand Down
Loading