Skip to content
Open
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: 3 additions & 0 deletions CHANGES
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
"ocsigenserver --serve ./public --port 8000 --directory-listing") now
serves a directory of static files without any configuration file or
pre-created log directory. The Staticmod extension is loaded on demand.
* One-command reverse proxy: "ocsigenserver --reverse-proxy URL" forwards
every request to the base URL given, with no configuration file. The
Revproxy extension is loaded on demand.
* Logging: when no <logdir> (and no <syslog>) is given in the configuration
file, logs now go to the standard output and standard error instead of to
files, so that no log directory is required.
Expand Down
10 changes: 8 additions & 2 deletions doc/index.mld
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,15 @@ terminal. A few options are available:
{v
ocsigenserver --serve ./public --port 8000 --directory-listing
v}
You can also start a reverse proxy that forwards every request to another
server, again without any configuration file:
{v
ocsigenserver --reverse-proxy http://localhost:9000 --port 8080
v}

This is the simplest way to get started. For anything more elaborate (several
hosts, extensions, reverse proxy, etc.) use a configuration file or the library
API, as described below.
hosts, extensions, fine-grained reverse proxy rules, etc.) use a configuration
file or the library API, as described below.

{1 Using Ocsigen Server as an executable}

Expand Down
4 changes: 4 additions & 0 deletions doc/launching.mld
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ following options:
file (logs go to the terminal). DIR can also be given as a plain
positional argument: "ocsigenserver ./public".

--reverse-proxy URL
Forward every request to the base URL given (for example
http://localhost:9000), without a configuration file.

-P, --port
Port to listen on in serve mode (default 8080).

Expand Down
11 changes: 11 additions & 0 deletions src/extensions/revproxy.ml
Original file line number Diff line number Diff line change
Expand Up @@ -192,3 +192,14 @@ let () =
()

let run ~redirection () _ _ _ = gen redirection

(* Publish a "forward everything to TARGET" function so that the one-command
reverse-proxy mode can use it after loading this extension dynamically,
without a static dependency. *)
let () =
Ocsigen.Server.register_reverse_proxy_server (fun ~target ->
let target = Ocsigen_base.Lib.Url.remove_end_slash target in
run
~redirection:
(create_redirection ~full_url:false ~regexp:"(.*)" (target ^ "/\\1"))
())
27 changes: 22 additions & 5 deletions src/ocsigenserver.ml
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
let usage = "usage: ocsigenserver [-c configfile | [--serve] DIR [options]]"
let usage =
"usage: ocsigenserver [-c configfile | [--serve] DIR | --reverse-proxy URL [options]]"

let section = Logs.Src.create "ocsigen:main"

(* Report command-line errors through Logs. A stderr reporter is installed
Expand All @@ -19,6 +21,7 @@ let fatal fmt =
let serve_dir = ref None
let serve_port = ref None
let directory_listing = ref false
let reverse_proxy = ref None
let config_given = ref false

let set_serve_dir d =
Expand All @@ -35,6 +38,8 @@ let () =
let alt_msg =
"Alternate config file (default " ^ Ocsigen.Config.get_config_file () ^ ")"
and serve_msg = "Serve the static files of DIR without a configuration file"
and proxy_msg =
"Forward every request to the base URL given, without a configuration file"
and port_msg = "Port to listen on in serve mode (default 8080)"
and listing_msg = "List directory contents in serve mode (when no index file)"
and silent_msg = "Silent mode (error messages in errors.log only)"
Expand All @@ -49,6 +54,9 @@ let () =
[ "-c", Arg.String set_configfile, alt_msg
; "--config", Arg.String set_configfile, alt_msg
; "--serve", Arg.String set_serve_dir, serve_msg
; ( "--reverse-proxy"
, Arg.String (fun u -> reverse_proxy := Some u)
, proxy_msg )
; "-P", Arg.Int (fun p -> serve_port := Some p), port_msg
; "--port", Arg.Int (fun p -> serve_port := Some p), port_msg
; "--directory-listing", Arg.Set directory_listing, listing_msg
Expand Down Expand Up @@ -86,14 +94,23 @@ let check_serve_dir dir =
| exception Sys_error _ -> fatal "no such directory: %s" dir

let () =
match !serve_dir with
| Some dir ->
match !serve_dir, !reverse_proxy with
| Some _, Some _ -> fatal "--serve and --reverse-proxy cannot be combined"
| Some dir, None ->
if !config_given
then fatal "-c/--config cannot be combined with serve mode";
let dir = check_serve_dir dir in
Ocsigen.Server.serve ~dir ?port:!serve_port
~directory_listing:!directory_listing ()
| None ->
| None, Some target ->
if !config_given
then fatal "-c/--config cannot be combined with --reverse-proxy";
if !directory_listing
then fatal "--directory-listing is only valid when serving a directory";
Ocsigen.Server.reverse_proxy ~target ?port:!serve_port ()
| None, None ->
if !serve_port <> None || !directory_listing
then fatal "--port and --directory-listing require a directory to serve";
then
fatal
"--port and --directory-listing require a directory to serve or --reverse-proxy";
Ocsigen.Server.exec (Ocsigen.Parseconfig.parse_config ())
54 changes: 35 additions & 19 deletions src/server/Ocsigen/server.ml
Original file line number Diff line number Diff line change
Expand Up @@ -475,39 +475,55 @@ let start
Extensions.start_initialisation ();
Extensions.set_hosts instructions)

(* Registry through which the Staticmod extension, once loaded, publishes its
static-file serving function. This lets the one-command serve mode use the
extension without statically linking it (which would clash with the
configuration-file path, where the extension is loaded dynamically). *)
(* Registries through which the Staticmod and Revproxy extensions, once loaded,
publish their serving functions. This lets the one-command serve mode use
them without statically linking them (which would clash with the
configuration-file path, where the extensions are loaded dynamically). *)
let static_server : (dir:string -> instruction) option ref = ref None
let register_static_server f = static_server := Some f
let reverse_proxy_server : (target:string -> instruction) option ref = ref None
let register_reverse_proxy_server f = reverse_proxy_server := Some f

let serve ?(port = 8080) ?(directory_listing = false) ~dir () =
(* One-command serve mode: no configuration file and no log directory are
required. Logs go to stderr and the command pipe is placed in a temporary
location so that the usual control commands remain available. *)
(* Common setup of the one-command modes: no configuration file and no log
directory are required. Logs go to stderr and the command pipe is placed in a
temporary location so that the usual control commands remain available. *)
let one_command_setup () =
Config.set_log_to_stderr true;
Config.set_command_pipe
(Filename.concat
(Filename.get_temp_dir_name ())
(Printf.sprintf "ocsigenserver-%d.cmd" (Unix.getpid ())));
(* Load the Staticmod extension on demand. It registers its serving function
through [register_static_server]. *)
(Printf.sprintf "ocsigenserver-%d.cmd" (Unix.getpid ())))

(* Load the extension [package] on demand and return the serving function it
published in [registry]. *)
let load_one_command_extension package registry =
(try
Ocsigen_base.Loader.loadfiles
(fun () -> ())
(fun () -> ())
false
(Ocsigen_base.Loader.findfiles "ocsigenserver.ext.staticmod")
(Ocsigen_base.Loader.findfiles package)
with e ->
let msg, errno = errmsg e in
Messages.errlog msg; exit errno);
match !static_server with
match !registry with
| Some f -> f
| None ->
Messages.errlog
"The Staticmod extension did not register its serving function";
Messages.errlog (package ^ " did not register its serving function");
exit 1
| Some static ->
start
~ports:[`All, port]
[host ~list_directory_content:directory_listing [static ~dir]]

let serve ?(port = 8080) ?(directory_listing = false) ~dir () =
one_command_setup ();
let static =
load_one_command_extension "ocsigenserver.ext.staticmod" static_server
in
start
~ports:[`All, port]
[host ~list_directory_content:directory_listing [static ~dir]]

let reverse_proxy ?(port = 8080) ~target () =
one_command_setup ();
let proxy =
load_one_command_extension "ocsigenserver.ext.revproxy" reverse_proxy_server
in
start ~ports:[`All, port] [host [proxy ~target]]
12 changes: 12 additions & 0 deletions src/server/Ocsigen/server.mli
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,13 @@ val serve : ?port:int -> ?directory_listing:bool -> dir:string -> unit -> unit
into the program. [port] defaults to 8080. When [directory_listing] is
[true], directories without an index file are listed. Never returns. *)

val reverse_proxy : ?port:int -> target:string -> unit -> unit
(** [reverse_proxy ~target ()] starts a server that forwards every request to
the [target] base URL (for example [http://localhost:9000]), with no
configuration file and no log directory required. This backs
[ocsigenserver --reverse-proxy URL]. The Revproxy extension is loaded
dynamically on demand. [port] defaults to 8080. Never returns. *)

val start :
?ports:(Config.Socket_type.t * int) list
-> ?ssl_ports:(Config.Socket_type.t * int) list
Expand Down Expand Up @@ -94,6 +101,11 @@ val register_static_server : (dir:string -> instruction) -> unit
static-file serving function, so that {!serve} can use it without a static
dependency on the extension. *)

val register_reverse_proxy_server : (target:string -> instruction) -> unit
(** Called by the Revproxy extension when it is loaded to publish its
request-forwarding function, so that {!reverse_proxy} can use it without a
static dependency on the extension. *)

val host :
?regexp:string
-> ?port:int
Expand Down
28 changes: 28 additions & 0 deletions test/reverse-proxy.t/run.t
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
Forward every request to another server with a single command, with no
configuration file.

Start an upstream server that serves a directory:

$ mkdir upstream
$ printf 'hello from upstream' > upstream/hello.txt
$ ocsigenserver --serve upstream --port 8071 >upstream.log 2>&1 &
$ UPSTREAM_PID=$!
$ trap 'kill $UPSTREAM_PID $PROXY_PID 2>/dev/null' EXIT

Wait for the upstream to answer:

$ curl -s --retry 20 --retry-delay 1 --retry-connrefused \
> http://127.0.0.1:8071/hello.txt
hello from upstream (no-eol)

Start a reverse proxy in front of it:

$ ocsigenserver --reverse-proxy http://127.0.0.1:8071 --port 8072 \
> >proxy.log 2>&1 &
$ PROXY_PID=$!

A request to the proxy is forwarded to the upstream and its answer is relayed:

$ curl -s --retry 20 --retry-delay 1 --retry-connrefused \
> http://127.0.0.1:8072/hello.txt
hello from upstream (no-eol)
Loading