Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source
Page
Library
Module
Module type
Parameter
Class
Class type
Source
http.ml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139
open Sihl_contract.Http let log_src = Logs.Src.create ("sihl.service." ^ Sihl_contract.Http.name) module Logs = (val Logs.src_log log_src : Logs.LOG) let get path handler = Get, path, handler let post path handler = Post, path, handler let put path handler = Put, path, handler let delete path handler = Delete, path, handler let any path handler = Any, path, handler let router ?(scope = "/") ?(middlewares = []) routes = { scope; routes; middlewares } ;; let trailing_char s = let length = String.length s in try Some (String.sub s (length - 1) 1) with | _ -> None ;; let tail s = try String.sub s 1 (String.length s - 1) with | _ -> "" ;; let prefix prefix (meth, path, handler) = let path = match trailing_char prefix, Astring.String.head path with | Some "/", Some '/' -> Printf.sprintf "%s%s" prefix (tail path) | _, _ -> Printf.sprintf "%s%s" prefix path in meth, path, handler ;; let apply_middleware_stack middleware_stack (meth, path, handler) = (* The request goes through the middleware stack from top to bottom, so we have to reverse the middleware stack *) let middleware_stack = List.rev middleware_stack in let wrapped_handler = List.fold_left (fun handler middleware -> Rock.Middleware.apply middleware handler) handler middleware_stack in meth, path, wrapped_handler ;; let router_to_routes { scope; routes; middlewares } = routes |> List.map (prefix scope) |> List.map (apply_middleware_stack middlewares) ;; let externalize_path ?prefix path = let prefix = match prefix, Sihl_core.Configuration.read_string "PREFIX_PATH" with | Some prefix, _ -> prefix | _, Some prefix -> prefix | _ -> "" in path |> String.split_on_char '/' |> List.cons prefix |> String.concat "/" |> Stringext.replace_all ~pattern:"//" ~with_:"/" ;; let to_opium_builder (meth, path, handler) = let open Sihl_contract.Http in match meth with | Get -> Opium.App.get path handler | Post -> Opium.App.post path handler | Put -> Opium.App.put path handler | Delete -> Opium.App.delete path handler | Any -> Opium.App.all path handler ;; let routers_to_opium_builders routers = routers |> List.map (fun router -> let routes = router_to_routes router in routes |> List.map to_opium_builder |> List.rev) |> List.concat ;; type config = { port : int option } let config port = { port } let schema = let open Conformist in make [ optional (int ~default:3000 "PORT") ] config ;; let registered_routers = ref [] let start_server () = let open Lwt.Syntax in Logs.debug (fun m -> m "Starting HTTP server"); let port_nr = Option.value (Sihl_core.Configuration.read schema).port ~default:33000 in let app = Opium.App.(empty |> port port_nr |> cmd_name "Sihl App") in let builders = routers_to_opium_builders !registered_routers in let app = List.fold_left (fun app builder -> builder app) app builders in (* We don't want to block here, the returned Lwt.t will never resolve *) let* _ = Opium.App.start app in Lwt.return () ;; let start_cmd = Sihl_core.Command.make ~name:"start-http" ~description:"Start the HTTP server" (fun _ -> start_server ()) ;; (* Lifecycle *) let start () = (* Make sure that configuration is valid *) Sihl_core.Configuration.require schema; start_server () ;; let stop () = Lwt.return () let lifecycle = Sihl_core.Container.Lifecycle.create "http" ~start ~stop let register ?(routers = []) () = registered_routers := routers; let configuration = Sihl_core.Configuration.make ~schema () in Sihl_core.Container.Service.create ~configuration ~commands:[ start_cmd ] ~server:true lifecycle ;;