Files

68 lines
2.5 KiB
OCaml
Raw Permalink Normal View History

2023-09-20 20:45:45 +03:00
(*
OSmol: simple static Gopher server in OCaml
Uses the same logic as Gofor-LX in Python
Created by Luxferre in 2023, released into public domain
*)
let listen_port = ref "70" (* server port *)
let root_dir = ref "." (* content root directory *)
let index_map = ref "index.map" (* index map file name *)
let posargs = ref []
let usage_msg = "Usage: " ^ Sys.argv.(0) ^ " [-p 70] [-d .] [-i index.map]"
let speclist = [
("-p", Arg.Set_string listen_port, "Port to listen on (default 70)");
("-d", Arg.Set_string root_dir, "Content root directory (default .)");
("-i", Arg.Set_string index_map, "Index map file name (default index.map)")
]
let anon_fun parg = posargs := parg :: !posargs
let () = Arg.parse speclist anon_fun usage_msg
(* get the resolved root directory with no trailing slashes *)
let real_root_dir = try Unix.realpath !root_dir
with _ -> failwith "No root directory!"
(* request processing *)
(* server error message formatter *)
let errmsg msg =
Printf.sprintf "3%s\t\terror.host\t1\r\n.\r\n\r\n" msg |> Bytes.of_string
(* read a file as bytes sequence for Gopher output *)
let readfile path =
if Sys.file_exists path then (* read the file *)
let ch = open_in_bin path in
let len = in_channel_length ch in
let buf = Bytes.create len in
try (really_input ch buf 0 len; close_in_noerr ch; buf)
with _ -> errmsg "Error reading the selector, try again later!"
else errmsg "Selector not found!"
(* find content by selector and return it as bytes sequence *)
let findsel sel =
let selpath = (try Unix.realpath (real_root_dir ^ "/" ^ sel)
with _ -> "###") in
(* check if selpath starts with rootdir *)
if String.starts_with ~prefix:real_root_dir selpath then
if Sys.is_directory selpath then (* resolve to index *)
readfile (selpath ^ "/" ^ !index_map)
else readfile selpath (* normal file *)
else errmsg "Selector not found!"
(* inch is line-based, outch is binary *)
let reqproc inch outch =
let reqparts = input_line inch |> String.split_on_char '\t' in
output_bytes outch (match reqparts with
| sel :: [] -> findsel (String.trim sel) (* valid selector with no tab *)
| [] -> findsel "/" (* empty root selector *)
| _ :: _ -> errmsg "Search selectors unsupported!"
);;
(* start the server *)
let lport = int_of_string(!listen_port) in
if lport > 0 && lport < 65536 then (
print_endline ("Starting server on port " ^ (string_of_int lport));
Unix.establish_server reqproc (Unix.ADDR_INET (Unix.inet_addr_any, lport))
) else failwith "Invalid port!"