Add static handler
This commit is contained in:
parent
95ebaee99a
commit
71ab60be82
24 changed files with 763477 additions and 43 deletions
|
@ -2,10 +2,17 @@
|
||||||
|
|
||||||
open Vif ;;
|
open Vif ;;
|
||||||
|
|
||||||
let default req target _server () =
|
let default req _server () =
|
||||||
let* () = Response.with_string req "Hello World!\n" in
|
let* () = Response.with_string req "Hello World!\n" in
|
||||||
Response.respond `OK
|
Response.respond `OK
|
||||||
;;
|
;;
|
||||||
|
|
||||||
|
let routes =
|
||||||
|
let open Vif.U in
|
||||||
|
let open Vif.R in
|
||||||
|
let open Vif.Content_type in
|
||||||
|
[ get (rel /?? nil) --> default ]
|
||||||
|
;;
|
||||||
|
|
||||||
let () = Miou_unix.run @@ fun () ->
|
let () = Miou_unix.run @@ fun () ->
|
||||||
Vif.run ~default [] () ;;
|
Vif.run routes () ;;
|
||||||
|
|
|
@ -5,11 +5,18 @@ let counter = Atomic.make 0 ;;
|
||||||
|
|
||||||
open Vif ;;
|
open Vif ;;
|
||||||
|
|
||||||
let default req target _server () =
|
let default req _server () =
|
||||||
let v = Atomic.fetch_and_add counter 1 in
|
let v = Atomic.fetch_and_add counter 1 in
|
||||||
let str = Fmt.str "%d request(s)\n" (succ v) in
|
let str = Fmt.str "%d request(s)\n" (succ v) in
|
||||||
let* () = Response.with_string req str in
|
let* () = Response.with_string req str in
|
||||||
Response.respond `OK
|
Response.respond `OK
|
||||||
;;
|
;;
|
||||||
|
|
||||||
let () = Miou_unix.run @@ fun () -> Vif.run ~default [] () ;;
|
let routes =
|
||||||
|
let open Vif.U in
|
||||||
|
let open Vif.R in
|
||||||
|
let open Vif.Content_type in
|
||||||
|
[ get (rel /?? nil) --> default ]
|
||||||
|
|
||||||
|
let () = Miou_unix.run @@ fun () ->
|
||||||
|
Vif.run routes () ;;
|
||||||
|
|
763135
examples/03-fail/log.txt
Normal file
763135
examples/03-fail/log.txt
Normal file
File diff suppressed because it is too large
Load diff
|
@ -2,6 +2,19 @@
|
||||||
|
|
||||||
exception Foo ;;
|
exception Foo ;;
|
||||||
|
|
||||||
let () = Printexc.register_printer @@ function Foo -> Some "Foo" | _ -> None ;;
|
let () = Printexc.register_printer @@ function
|
||||||
let default req target server () = raise Foo ;;
|
| Foo -> Some "Foo"
|
||||||
let () = Miou_unix.run @@ fun () -> Vif.run ~default [] () ;;
|
| _ -> None ;;
|
||||||
|
|
||||||
|
let default req server () = raise Foo ;;
|
||||||
|
|
||||||
|
let routes =
|
||||||
|
let open Vif.U in
|
||||||
|
let open Vif.R in
|
||||||
|
let open Vif.Content_type in
|
||||||
|
[ get (rel /?? nil) --> default ]
|
||||||
|
;;
|
||||||
|
|
||||||
|
let () = Miou_unix.run @@ fun () ->
|
||||||
|
Vif.run routes ()
|
||||||
|
;;
|
||||||
|
|
|
@ -9,12 +9,19 @@ let foo =
|
||||||
|
|
||||||
open Vif ;;
|
open Vif ;;
|
||||||
|
|
||||||
let default req target server () =
|
let default req server () =
|
||||||
let Foo = Vif.S.device foo server in
|
let Foo = Vif.S.device foo server in
|
||||||
let* () = Response.with_string req "ok\n" in
|
let* () = Response.with_string req "ok\n" in
|
||||||
Response.respond `OK
|
Response.respond `OK
|
||||||
;;
|
;;
|
||||||
|
|
||||||
|
let routes =
|
||||||
|
let open Vif.U in
|
||||||
|
let open Vif.R in
|
||||||
|
let open Vif.Content_type in
|
||||||
|
[ get (rel /?? nil) --> default ]
|
||||||
|
|
||||||
let () =
|
let () =
|
||||||
Miou_unix.run @@ fun () -> Vif.run ~default ~devices:Vif.Ds.[ foo ] [] ()
|
Miou_unix.run @@ fun () ->
|
||||||
|
Vif.run ~devices:Vif.Ds.[ foo ] routes ()
|
||||||
;;
|
;;
|
||||||
|
|
|
@ -52,10 +52,4 @@ let routes =
|
||||||
[ post (json_encoding foo) (rel /?? nil) --> deserialize ]
|
[ post (json_encoding foo) (rel /?? nil) --> deserialize ]
|
||||||
;;
|
;;
|
||||||
|
|
||||||
let default req target _server () =
|
let () = Miou_unix.run @@ fun () -> Vif.run routes () ;;
|
||||||
let str = Fmt.str "%s not found\n" target in
|
|
||||||
let* () = Response.with_string req str in
|
|
||||||
Response.respond `Not_found
|
|
||||||
;;
|
|
||||||
|
|
||||||
let () = Miou_unix.run @@ fun () -> Vif.run ~default routes () ;;
|
|
||||||
|
|
|
@ -53,12 +53,6 @@ let list req server _cfg =
|
||||||
Response.respond `Internal_server_error
|
Response.respond `Internal_server_error
|
||||||
;;
|
;;
|
||||||
|
|
||||||
let default req target server _ =
|
|
||||||
let str = Fmt.str "%s not found\n" target in
|
|
||||||
let* () = Response.with_string req str in
|
|
||||||
Response.respond `Not_found
|
|
||||||
;;
|
|
||||||
|
|
||||||
let routes =
|
let routes =
|
||||||
let open Vif.U in
|
let open Vif.U in
|
||||||
let open Vif.R in
|
let open Vif.R in
|
||||||
|
@ -71,5 +65,5 @@ let () =
|
||||||
Caqti_miou.Switch.run @@ fun sw ->
|
Caqti_miou.Switch.run @@ fun sw ->
|
||||||
let uri = Uri.of_string "sqlite3:foo.sqlite?create=false" in
|
let uri = Uri.of_string "sqlite3:foo.sqlite?create=false" in
|
||||||
let cfg = { sw; uri } in
|
let cfg = { sw; uri } in
|
||||||
Vif.run ~default ~devices:Vif.Ds.[ caqti ] routes cfg
|
Vif.run ~devices:Vif.Ds.[ caqti ] routes cfg
|
||||||
;;
|
;;
|
||||||
|
|
|
@ -2,12 +2,19 @@
|
||||||
|
|
||||||
open Vif ;;
|
open Vif ;;
|
||||||
|
|
||||||
let default req target server () =
|
let default req server () =
|
||||||
let* () = Response.with_string ~compression:`DEFLATE req "Hello World!\n" in
|
let* () = Response.with_string ~compression:`DEFLATE req "Hello World!\n" in
|
||||||
let field = "content-type" in
|
let field = "content-type" in
|
||||||
let* () = Response.add ~field "text/plain; charset=utf-8" in
|
let* () = Response.add ~field "text/plain; charset=utf-8" in
|
||||||
Response.respond `OK
|
Response.respond `OK
|
||||||
;;
|
;;
|
||||||
|
|
||||||
|
let routes =
|
||||||
|
let open Vif.U in
|
||||||
|
let open Vif.R in
|
||||||
|
let open Vif.Content_type in
|
||||||
|
[ get (rel /?? nil) --> default ]
|
||||||
|
|
||||||
let () = Miou_unix.run @@ fun () ->
|
let () = Miou_unix.run @@ fun () ->
|
||||||
Vif.run ~default [] () ;;
|
Vif.run routes ()
|
||||||
|
;;
|
||||||
|
|
|
@ -12,7 +12,7 @@ let sha1 =
|
||||||
Sink { init; push; full; stop }
|
Sink { init; push; full; stop }
|
||||||
;;
|
;;
|
||||||
|
|
||||||
let default req target server () =
|
let default req server () =
|
||||||
let stream = Request.stream req in
|
let stream = Request.stream req in
|
||||||
let hash = Stream.Stream.into sha1 stream in
|
let hash = Stream.Stream.into sha1 stream in
|
||||||
let field = "content-type" in
|
let field = "content-type" in
|
||||||
|
@ -21,5 +21,12 @@ let default req target server () =
|
||||||
Response.respond `OK
|
Response.respond `OK
|
||||||
;;
|
;;
|
||||||
|
|
||||||
|
let routes =
|
||||||
|
let open Vif.U in
|
||||||
|
let open Vif.R in
|
||||||
|
let open Vif.Content_type in
|
||||||
|
[ post any (rel /?? nil) --> default ]
|
||||||
|
|
||||||
let () = Miou_unix.run @@ fun () ->
|
let () = Miou_unix.run @@ fun () ->
|
||||||
Vif.run ~default [] () ;;
|
Vif.run routes ()
|
||||||
|
;;
|
||||||
|
|
|
@ -70,7 +70,7 @@ let login req server { secret }=
|
||||||
Response.respond (`Code 422)
|
Response.respond (`Code 422)
|
||||||
;;
|
;;
|
||||||
|
|
||||||
let default req target server _cfg =
|
let default req server _cfg =
|
||||||
match Request.get jwt req with
|
match Request.get jwt req with
|
||||||
| None ->
|
| None ->
|
||||||
let field = "content-type" in
|
let field = "content-type" in
|
||||||
|
@ -89,9 +89,11 @@ let routes =
|
||||||
let open Vif.U in
|
let open Vif.U in
|
||||||
let open Vif.R in
|
let open Vif.R in
|
||||||
let open Vif.Content_type in
|
let open Vif.Content_type in
|
||||||
[ post (json_encoding credential) (rel / "login" /?? nil) --> login ]
|
[ post (json_encoding credential) (rel / "login" /?? nil) --> login
|
||||||
|
; get (rel /?? nil) --> default ]
|
||||||
;;
|
;;
|
||||||
|
|
||||||
let () = Miou_unix.run @@ fun () ->
|
let () = Miou_unix.run @@ fun () ->
|
||||||
let secret = "deadbeef" in
|
let secret = "deadbeef" in
|
||||||
Vif.run ~default ~middlewares:Ms.[ jwt ] routes { secret } ;;
|
Vif.run ~middlewares:Ms.[ jwt ] routes { secret }
|
||||||
|
;;
|
||||||
|
|
31
examples/10-route/main.ml
Normal file
31
examples/10-route/main.ml
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
#require "vif" ;;
|
||||||
|
|
||||||
|
open Vif ;;
|
||||||
|
|
||||||
|
let hello req name server _ =
|
||||||
|
let str = Fmt.str "Hello, %S!\n" name in
|
||||||
|
let field = "content-type" in
|
||||||
|
let* () = Response.add ~field "text/plain; charset=utf-8" in
|
||||||
|
let* () = Response.with_string req str in
|
||||||
|
Response.respond `OK
|
||||||
|
;;
|
||||||
|
|
||||||
|
let default req server _cfg =
|
||||||
|
let str = Fmt.str "Hello World!\n" in
|
||||||
|
let field = "content-type" in
|
||||||
|
let* () = Response.add ~field "text/plain; charset=utf-8" in
|
||||||
|
let* () = Response.with_string req str in
|
||||||
|
Response.respond `OK
|
||||||
|
;;
|
||||||
|
|
||||||
|
let routes =
|
||||||
|
let open Vif.U in
|
||||||
|
let open Vif.R in
|
||||||
|
let open Vif.Content_type in
|
||||||
|
[ get (rel / "echo" /% string /?? nil) --> hello
|
||||||
|
; get (rel /?? nil) --> default ]
|
||||||
|
;;
|
||||||
|
|
||||||
|
let () = Miou_unix.run @@ fun () ->
|
||||||
|
Vif.run routes ()
|
||||||
|
;;
|
22
examples/11-stream/main.ml
Normal file
22
examples/11-stream/main.ml
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
#require "vif" ;;
|
||||||
|
|
||||||
|
open Vif ;;
|
||||||
|
|
||||||
|
let cat req server _ =
|
||||||
|
let src = Request.stream req in
|
||||||
|
let field = "content-type" in
|
||||||
|
let* () = Response.add ~field "application/octet-stream" in
|
||||||
|
let* () = Response.with_stream req src in
|
||||||
|
Response.respond `OK
|
||||||
|
;;
|
||||||
|
|
||||||
|
let routes =
|
||||||
|
let open Vif.U in
|
||||||
|
let open Vif.R in
|
||||||
|
let open Vif.Content_type in
|
||||||
|
[ post any (rel /?? nil) --> cat ]
|
||||||
|
;;
|
||||||
|
|
||||||
|
let () = Miou_unix.run @@ fun () ->
|
||||||
|
Vif.run routes ()
|
||||||
|
;;
|
1
examples/12-static/index.html
Normal file
1
examples/12-static/index.html
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<h1>Hello from Vif!</h1>
|
7
examples/12-static/main.ml
Normal file
7
examples/12-static/main.ml
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
#require "vif" ;;
|
||||||
|
|
||||||
|
open Vif ;;
|
||||||
|
|
||||||
|
let () = Miou_unix.run @@ fun () ->
|
||||||
|
Vif.run ~handlers:[ Handler.static ] [] ()
|
||||||
|
;;
|
|
@ -9,6 +9,9 @@
|
||||||
jsonm
|
jsonm
|
||||||
cmdliner
|
cmdliner
|
||||||
hmap
|
hmap
|
||||||
|
fpath
|
||||||
|
conan-database.light
|
||||||
|
conan-unix
|
||||||
decompress.zl
|
decompress.zl
|
||||||
decompress.gz
|
decompress.gz
|
||||||
mirage-crypto-rng-miou-unix
|
mirage-crypto-rng-miou-unix
|
||||||
|
|
|
@ -78,6 +78,7 @@ module Headers = Vif_headers
|
||||||
module Request = Vif_request
|
module Request = Vif_request
|
||||||
module Response = Vif_response
|
module Response = Vif_response
|
||||||
module Cookie = Vif_cookie
|
module Cookie = Vif_cookie
|
||||||
|
module Handler = Vif_handler
|
||||||
|
|
||||||
type e = Response.e = Empty
|
type e = Response.e = Empty
|
||||||
type f = Response.f = Filled
|
type f = Response.f = Filled
|
||||||
|
@ -96,6 +97,8 @@ let content_type req0 =
|
||||||
let c = Option.to_result ~none:`Not_found c in
|
let c = Option.to_result ~none:`Not_found c in
|
||||||
Result.bind c Multipart_form.Content_type.of_string
|
Result.bind c Multipart_form.Content_type.of_string
|
||||||
|
|
||||||
|
[@@@warning "-8"]
|
||||||
|
|
||||||
let recognize_request ~env req0 =
|
let recognize_request ~env req0 =
|
||||||
let extract : type c a.
|
let extract : type c a.
|
||||||
Vif_method.t option
|
Vif_method.t option
|
||||||
|
@ -190,9 +193,11 @@ let rec user's_functions daemon =
|
||||||
let fn (User's_task (req0, fn)) =
|
let fn (User's_task (req0, fn)) =
|
||||||
let _prm =
|
let _prm =
|
||||||
Miou.call ~orphans:daemon.orphans @@ fun () ->
|
Miou.call ~orphans:daemon.orphans @@ fun () ->
|
||||||
let response = fn daemon.server daemon.user's_value in
|
match
|
||||||
match Vif_response.(run req0 empty) response with
|
Vif_response.(run req0 empty) (fn daemon.server daemon.user's_value)
|
||||||
|
with
|
||||||
| Vif_response.Sent, () -> Vif_request0.close req0
|
| Vif_response.Sent, () -> Vif_request0.close req0
|
||||||
|
| exception exn -> Vif_request0.report_exn req0 exn
|
||||||
in
|
in
|
||||||
()
|
()
|
||||||
in
|
in
|
||||||
|
@ -240,8 +245,40 @@ let store_pid = function
|
||||||
output_string oc (string_of_int (Unix.getpid ()));
|
output_string oc (string_of_int (Unix.getpid ()));
|
||||||
close_out oc
|
close_out oc
|
||||||
|
|
||||||
|
let default req target _server _user's_value =
|
||||||
|
let pp_field ppf (k, v) =
|
||||||
|
let v = String.split_on_char ' ' v in
|
||||||
|
let v = List.map (String.split_on_char '\t') v in
|
||||||
|
let v = List.flatten v in
|
||||||
|
let v = List.filter_map (function "" -> None | v -> Some v) v in
|
||||||
|
Fmt.pf ppf "%s: @[<hov>%a@]%!" k Fmt.(list ~sep:(any "@ ") string) v
|
||||||
|
in
|
||||||
|
let str =
|
||||||
|
Fmt.str "Unspecified destination %s (%a):\n%a\n" target Vif_method.pp
|
||||||
|
(Vif_request.meth req)
|
||||||
|
Fmt.(list ~sep:(any "\n") pp_field)
|
||||||
|
(Vif_request.headers req)
|
||||||
|
in
|
||||||
|
let len = String.length str in
|
||||||
|
let field = "content-type" in
|
||||||
|
let* () = Vif_response.add ~field "text/plain; charset=utf-8" in
|
||||||
|
let field = "content-length" in
|
||||||
|
let* () = Vif_response.add ~field (string_of_int len) in
|
||||||
|
let* () = Vif_response.with_string req str in
|
||||||
|
Vif_response.respond `Not_found
|
||||||
|
|
||||||
|
let default_from_handlers handlers req target server user's_value =
|
||||||
|
let fn acc handler =
|
||||||
|
match acc with
|
||||||
|
| Some _ as acc -> acc
|
||||||
|
| None -> handler req target server user's_value
|
||||||
|
in
|
||||||
|
match List.fold_left fn None handlers with
|
||||||
|
| Some p -> p
|
||||||
|
| None -> default req target server user's_value
|
||||||
|
|
||||||
let run ?(cfg = Vif_options.config_from_globals ()) ?(devices = Ds.[])
|
let run ?(cfg = Vif_options.config_from_globals ()) ?(devices = Ds.[])
|
||||||
?(middlewares = Ms.[]) ~default routes user's_value =
|
?(middlewares = Ms.[]) ?(handlers = []) routes user's_value =
|
||||||
let rng = Mirage_crypto_rng_miou_unix.(initialize (module Pfortuna)) in
|
let rng = Mirage_crypto_rng_miou_unix.(initialize (module Pfortuna)) in
|
||||||
let finally () = Mirage_crypto_rng_miou_unix.kill rng in
|
let finally () = Mirage_crypto_rng_miou_unix.kill rng in
|
||||||
Fun.protect ~finally @@ fun () ->
|
Fun.protect ~finally @@ fun () ->
|
||||||
|
@ -275,6 +312,7 @@ let run ?(cfg = Vif_options.config_from_globals ()) ?(devices = Ds.[])
|
||||||
; server
|
; server
|
||||||
}
|
}
|
||||||
in
|
in
|
||||||
|
let default = default_from_handlers handlers in
|
||||||
let user's_tasks = Miou.call @@ fun () -> user's_functions daemon in
|
let user's_tasks = Miou.call @@ fun () -> user's_functions daemon in
|
||||||
let fn0 = handler cfg ~default ~middlewares routes daemon in
|
let fn0 = handler cfg ~default ~middlewares routes daemon in
|
||||||
let prm0 = Miou.async @@ fun () -> handle stop cfg fn0 in
|
let prm0 = Miou.async @@ fun () -> handle stop cfg fn0 in
|
||||||
|
|
|
@ -1,5 +1,11 @@
|
||||||
module U : sig
|
module U : sig
|
||||||
type 'a atom = 'a Tyre.t
|
type 'a atom = 'a Tyre.t
|
||||||
|
|
||||||
|
val int : int atom
|
||||||
|
val string : string atom
|
||||||
|
val option : 'a atom -> 'a option atom
|
||||||
|
val conv : ('a -> 'b) -> ('b -> 'a) -> 'a atom -> 'b atom
|
||||||
|
|
||||||
type ('f, 'r) path
|
type ('f, 'r) path
|
||||||
|
|
||||||
val rel : ('r, 'r) path
|
val rel : ('r, 'r) path
|
||||||
|
@ -300,6 +306,17 @@ module Cookie : sig
|
||||||
-> ('p, 'p, unit) Response.t
|
-> ('p, 'p, unit) Response.t
|
||||||
end
|
end
|
||||||
|
|
||||||
|
module Handler : sig
|
||||||
|
type ('c, 'value) t =
|
||||||
|
('c, string) Request.t
|
||||||
|
-> string
|
||||||
|
-> S.t
|
||||||
|
-> 'value
|
||||||
|
-> (Response.e, Response.s, unit) Response.t option
|
||||||
|
|
||||||
|
val static : ?top:Fpath.t -> ('c, 'value) t
|
||||||
|
end
|
||||||
|
|
||||||
type config
|
type config
|
||||||
type e = Response.e
|
type e = Response.e
|
||||||
type f = Response.f
|
type f = Response.f
|
||||||
|
@ -328,12 +345,7 @@ val run :
|
||||||
?cfg:config
|
?cfg:config
|
||||||
-> ?devices:'value Ds.t
|
-> ?devices:'value Ds.t
|
||||||
-> ?middlewares:'value Ms.t
|
-> ?middlewares:'value Ms.t
|
||||||
-> default:
|
-> ?handlers:('c, 'value) Handler.t list
|
||||||
( ('c, string) Request.t
|
|
||||||
-> string
|
|
||||||
-> S.t
|
|
||||||
-> 'value
|
|
||||||
-> (e, s, unit) Response.t)
|
|
||||||
-> (S.t -> 'value -> (e, s, unit) Response.t) R.route list
|
-> (S.t -> 'value -> (e, s, unit) Response.t) R.route list
|
||||||
-> 'value
|
-> 'value
|
||||||
-> unit
|
-> unit
|
||||||
|
|
|
@ -1,9 +1,13 @@
|
||||||
type null = Null
|
type null = Null
|
||||||
and json = Json
|
type json = Json
|
||||||
|
type multipart_form = Multipart_form
|
||||||
|
type stream = string Stream.source Multipart_form.elt Stream.source
|
||||||
|
|
||||||
type ('c, 'a) t =
|
type ('c, 'a) t =
|
||||||
| Null : (null, unit) t
|
| Null : (null, unit) t
|
||||||
| Json_encoding : 'a Json_encoding.encoding -> (json, 'a) t
|
| Json_encoding : 'a Json_encoding.encoding -> (json, 'a) t
|
||||||
|
| Multipart_form_encoding : 'a Multipart_form.t -> (multipart_form, 'a) t
|
||||||
|
| Multipart_form : (multipart_form, stream) t
|
||||||
| Json : (json, Json.t) t
|
| Json : (json, Json.t) t
|
||||||
| Any : ('c, string) t
|
| Any : ('c, string) t
|
||||||
|
|
||||||
|
@ -11,3 +15,41 @@ let null = Null
|
||||||
let json_encoding e = Json_encoding e
|
let json_encoding e = Json_encoding e
|
||||||
let json = Json
|
let json = Json
|
||||||
let any = Any
|
let any = Any
|
||||||
|
|
||||||
|
module Witness = struct
|
||||||
|
type (_, _) eq = Refl : ('a, 'a) eq
|
||||||
|
type _ equality = ..
|
||||||
|
|
||||||
|
module type Inst = sig
|
||||||
|
type t
|
||||||
|
type _ equality += Eq : t equality
|
||||||
|
end
|
||||||
|
|
||||||
|
type 'a t = (module Inst with type t = 'a)
|
||||||
|
|
||||||
|
let make : type a. unit -> a t =
|
||||||
|
fun () ->
|
||||||
|
let module Inst = struct
|
||||||
|
type t = a
|
||||||
|
type _ equality += Eq : t equality
|
||||||
|
end in
|
||||||
|
(module Inst)
|
||||||
|
|
||||||
|
let _eq : type a b. a t -> b t -> (a, b) eq option =
|
||||||
|
fun (module A) (module B) -> match A.Eq with B.Eq -> Some Refl | _ -> None
|
||||||
|
end
|
||||||
|
|
||||||
|
module Multipart = struct
|
||||||
|
type 'a t = { rwit: 'a Witness.t; rfields: 'a fields_and_constr }
|
||||||
|
|
||||||
|
and 'a fields_and_constr =
|
||||||
|
| Fields : ('a, 'b) fields * 'b -> 'a fields_and_constr
|
||||||
|
|
||||||
|
and ('a, 'b) fields =
|
||||||
|
| F0 : ('a, 'a) fields
|
||||||
|
| F1 : ('a, 'b) field * ('a, 'c) fields -> ('a, 'b -> 'c) fields
|
||||||
|
|
||||||
|
and ('a, 'b) field = { name: string; ftype: 'b ty; fget: 'a -> 'b }
|
||||||
|
and 'a ty = Primary : 'a primary -> 'a ty | Record : 'a t -> 'a ty
|
||||||
|
and 'a primary = String : string primary
|
||||||
|
end
|
||||||
|
|
84
lib/vif/vif_handler.ml
Normal file
84
lib/vif/vif_handler.ml
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
type ('c, 'value) t =
|
||||||
|
('c, string) Vif_request.t
|
||||||
|
-> string
|
||||||
|
-> Vif_s.t
|
||||||
|
-> 'value
|
||||||
|
-> (Vif_response.e, Vif_response.s, unit) Vif_response.t option
|
||||||
|
|
||||||
|
let pwd = Fpath.v (Unix.getcwd ())
|
||||||
|
let tree = Conan_light.tree
|
||||||
|
|
||||||
|
let sha256sum path =
|
||||||
|
let path = Fpath.to_string path in
|
||||||
|
if Sys.file_exists path = false || Sys.is_directory path then
|
||||||
|
invalid_arg "sha256sum";
|
||||||
|
let fd = Unix.openfile path Unix.[ O_RDONLY ] 0o644 in
|
||||||
|
let finally () = Unix.close fd in
|
||||||
|
Fun.protect ~finally @@ fun () ->
|
||||||
|
let stat = Unix.fstat fd in
|
||||||
|
let ba =
|
||||||
|
Unix.map_file fd Bigarray.char Bigarray.c_layout false
|
||||||
|
[| stat.Unix.st_size |]
|
||||||
|
in
|
||||||
|
let ba = Bigarray.array1_of_genarray ba in
|
||||||
|
let hash = Digestif.SHA256.digest_bigstring ba in
|
||||||
|
Digestif.SHA256.to_hex hash
|
||||||
|
|
||||||
|
let mime_type path =
|
||||||
|
match Conan_unix.run_with_tree Conan_light.tree (Fpath.to_string path) with
|
||||||
|
| Ok m ->
|
||||||
|
Option.value ~default:"application/octet-stream" (Conan.Metadata.mime m)
|
||||||
|
| Error _ -> "application/octet-stream"
|
||||||
|
| exception _ -> "application/octet-stream"
|
||||||
|
|
||||||
|
let cache req target =
|
||||||
|
let hdrs = Vif_request.headers req in
|
||||||
|
let hash = sha256sum target in
|
||||||
|
match Vif_headers.get hdrs "if-none-match" with
|
||||||
|
| Some hash' -> String.equal hash hash'
|
||||||
|
| None -> false
|
||||||
|
|
||||||
|
let valid ~top target =
|
||||||
|
Fpath.is_prefix top target
|
||||||
|
&& Sys.file_exists (Fpath.to_string target)
|
||||||
|
&& Sys.is_directory (Fpath.to_string target) = false
|
||||||
|
|
||||||
|
let pp_msg ppf (`Msg msg) = Fmt.string ppf msg
|
||||||
|
|
||||||
|
let trim lst =
|
||||||
|
let lst = List.drop_while (( = ) "") lst in
|
||||||
|
let lst = List.drop_while (( = ) "") (List.rev lst) in
|
||||||
|
List.rev lst
|
||||||
|
|
||||||
|
let static ?(top = pwd) req target _server _ =
|
||||||
|
let target = String.split_on_char '/' target in
|
||||||
|
let target = trim target in
|
||||||
|
let target = String.concat "/" target in
|
||||||
|
let abs_path =
|
||||||
|
let ( let* ) = Result.bind in
|
||||||
|
let* x = Fpath.of_string target in
|
||||||
|
Ok Fpath.(normalize (top // x))
|
||||||
|
in
|
||||||
|
match (Vif_request.meth req, abs_path) with
|
||||||
|
| `GET, Ok abs_path when valid ~top abs_path -> begin
|
||||||
|
let ( let* ) = Vif_response.bind in
|
||||||
|
let process =
|
||||||
|
if cache req abs_path then
|
||||||
|
let* () = Vif_response.with_string req "" in
|
||||||
|
Vif_response.respond `Not_modified
|
||||||
|
else
|
||||||
|
let src = Stream.Source.file (Fpath.to_string abs_path) in
|
||||||
|
let src = Stream.Stream.from src in
|
||||||
|
let field = "content-type" in
|
||||||
|
let* () = Vif_response.add ~field (mime_type abs_path) in
|
||||||
|
let stat = Unix.stat (Fpath.to_string abs_path) in
|
||||||
|
let field = "content-length" in
|
||||||
|
let* () = Vif_response.add ~field (string_of_int stat.Unix.st_size) in
|
||||||
|
let field = "etag" in
|
||||||
|
let* () = Vif_response.add ~field (sha256sum abs_path) in
|
||||||
|
let* () = Vif_response.with_stream req src in
|
||||||
|
Vif_response.respond `OK
|
||||||
|
in
|
||||||
|
Some process
|
||||||
|
end
|
||||||
|
| _ -> None
|
|
@ -8,3 +8,14 @@ type t =
|
||||||
| `PUT
|
| `PUT
|
||||||
| `TRACE
|
| `TRACE
|
||||||
| `Other of string ]
|
| `Other of string ]
|
||||||
|
|
||||||
|
let pp ppf = function
|
||||||
|
| `CONNECT -> Fmt.string ppf "CONNECT"
|
||||||
|
| `DELETE -> Fmt.string ppf "DELETE"
|
||||||
|
| `GET -> Fmt.string ppf "GET"
|
||||||
|
| `HEAD -> Fmt.string ppf "HEAD"
|
||||||
|
| `OPTIONS -> Fmt.string ppf "OPTIONS"
|
||||||
|
| `POST -> Fmt.string ppf "POST"
|
||||||
|
| `PUT -> Fmt.string ppf "PUT"
|
||||||
|
| `TRACE -> Fmt.string ppf "TRACE"
|
||||||
|
| `Other str -> Fmt.string ppf (String.uppercase_ascii str)
|
||||||
|
|
|
@ -261,7 +261,7 @@ let dispatch : type r c.
|
||||||
default request target
|
default request target
|
||||||
| Some subs -> begin
|
| Some subs -> begin
|
||||||
try find_and_trigger ~original:target ~request subs wl
|
try find_and_trigger ~original:target ~request subs wl
|
||||||
with exn ->
|
with Not_found as exn ->
|
||||||
Log.debug (fun m ->
|
Log.debug (fun m ->
|
||||||
m "Fallback to the default route (exn: %s)"
|
m "Fallback to the default route (exn: %s)"
|
||||||
(Printexc.to_string exn));
|
(Printexc.to_string exn));
|
||||||
|
|
|
@ -81,6 +81,11 @@ let request_body { reqd; _ } =
|
||||||
| `V1 reqd -> `V1 (H1.Reqd.request_body reqd)
|
| `V1 reqd -> `V1 (H1.Reqd.request_body reqd)
|
||||||
| `V2 reqd -> `V2 (H2.Reqd.request_body reqd)
|
| `V2 reqd -> `V2 (H2.Reqd.request_body reqd)
|
||||||
|
|
||||||
|
let report_exn { reqd; _ } exn =
|
||||||
|
match reqd with
|
||||||
|
| `V1 reqd -> H1.Reqd.report_exn reqd exn
|
||||||
|
| `V2 reqd -> H2.Reqd.report_exn reqd exn
|
||||||
|
|
||||||
let version { request; _ } = match request with V1 _ -> 1 | V2 _ -> 2
|
let version { request; _ } = match request with V1 _ -> 1 | V2 _ -> 2
|
||||||
let tls { tls; _ } = tls
|
let tls { tls; _ } = tls
|
||||||
let on_localhost { on_localhost; _ } = on_localhost
|
let on_localhost { on_localhost; _ } = on_localhost
|
||||||
|
|
|
@ -162,8 +162,8 @@ let run : type a p q. Vif_request0.t -> p state -> (p, q, a) t -> q state * a =
|
||||||
(state, ())
|
(state, ())
|
||||||
| Empty, Stream stream -> (Filled stream, ())
|
| Empty, Stream stream -> (Filled stream, ())
|
||||||
| Empty, String str ->
|
| Empty, String str ->
|
||||||
if Vif_request0.version req = 1
|
if Vif_request0.version req = 1 then
|
||||||
then headers := Vif_headers.add_unless_exists !headers "connection" "close";
|
headers := Vif_headers.add_unless_exists !headers "connection" "close";
|
||||||
(Filled (Stream.Stream.singleton str), ())
|
(Filled (Stream.Stream.singleton str), ())
|
||||||
| Filled stream, Respond status ->
|
| Filled stream, Respond status ->
|
||||||
let headers = !headers in
|
let headers = !headers in
|
||||||
|
|
|
@ -61,6 +61,11 @@ let ( /% ) = Path.add_atom
|
||||||
let ( /? ) path query = Url.make ~slash:No_slash path query
|
let ( /? ) path query = Url.make ~slash:No_slash path query
|
||||||
let ( //? ) path query = Url.make ~slash:Slash path query
|
let ( //? ) path query = Url.make ~slash:Slash path query
|
||||||
let ( /?? ) path query = Url.make ~slash:Maybe_slash path query
|
let ( /?? ) path query = Url.make ~slash:Maybe_slash path query
|
||||||
|
let int = Tyre.int
|
||||||
|
let string = Tyre.(regex Re.(rep1 @@ compl [ char '/' ]))
|
||||||
|
let bool = Tyre.bool
|
||||||
|
let option = Tyre.opt
|
||||||
|
let conv = Tyre.conv
|
||||||
let eval_atom p x = Tyre.(eval (Internal.to_t p) x)
|
let eval_atom p x = Tyre.(eval (Internal.to_t p) x)
|
||||||
|
|
||||||
let eval_top_atom : type a. a Tyre.Internal.raw -> a -> string list = function
|
let eval_top_atom : type a. a Tyre.Internal.raw -> a -> string list = function
|
||||||
|
|
Loading…
Reference in a new issue