diff --git a/src/cipher/cipher.ml b/src/cipher/cipher.ml index 4d963bd8..75577d3f 100644 --- a/src/cipher/cipher.ml +++ b/src/cipher/cipher.ml @@ -118,8 +118,8 @@ struct | Some plaintext -> Some (Cstruct.to_string plaintext) end -let secrets_variable = - Dream.new_local +let secrets_field = + Dream.new_field ~name:"dream.secret" ~show_value:(fun _secrets -> "[redacted]") () @@ -131,19 +131,19 @@ let secrets_variable = let set_secret ?(old_secrets = []) secret = let value = secret::old_secrets in fun next_handler request -> - Dream.set_local request secrets_variable value; + Dream.set_field request secrets_field value; next_handler request let fallback_secrets = lazy [Random.random 32] let encryption_secret request = - match Dream.local request secrets_variable with + match Dream.field request secrets_field with | Some secrets -> List.hd secrets | None -> List.hd (Lazy.force fallback_secrets) let decryption_secrets request = - match Dream.local request secrets_variable with + match Dream.field request secrets_field with | Some secrets -> secrets | None -> Lazy.force fallback_secrets diff --git a/src/dream.ml b/src/dream.ml index 1cd455c7..0cb5be04 100644 --- a/src/dream.ml +++ b/src/dream.ml @@ -182,8 +182,12 @@ let with_stream message = set_stream message; message +type 'a local = 'a field +let new_local = new_field +let local = field + let with_local key value message = - set_local message key value; + set_field message key value; message let first message = diff --git a/src/dream.mli b/src/dream.mli index 27748882..cfb8101e 100644 --- a/src/dream.mli +++ b/src/dream.mli @@ -2349,20 +2349,40 @@ val decrypt : Dream supports user-defined per-message variables for use by middlewares. *) -type 'a local +type 'a field (** Per-message variable. *) -val new_local : ?name:string -> ?show_value:('a -> string) -> unit -> 'a local +(**/**) +type 'a local = 'a field +[@@ocaml.deprecated " Renamed to type Dream.field."] +(**/**) + +val new_field : ?name:string -> ?show_value:('a -> string) -> unit -> 'a field (** Declares a variable of type ['a] in all messages. The variable is initially unset in each message. The optional [~name] and [~show_value] are used by {!Dream.run} [~debug] to show the variable in debug dumps. *) -val local : 'b message -> 'a local -> 'a option +(**/**) +val new_local : ?name:string -> ?show_value:('a -> string) -> unit -> 'a field +[@@ocaml.deprecated " Renamed to Dream.new_field."] +(**/**) + +val field : 'b message -> 'a field -> 'a option (** Retrieves the value of the per-message variable. *) -val set_local : 'b message -> 'a local -> 'a -> unit +(**/**) +val local : 'b message -> 'a field -> 'a option +[@@ocaml.deprecated " Renamed to Dream.field."] +(**/**) + +val set_field : 'b message -> 'a field -> 'a -> unit (** Sets the per-message variable to the value. *) +(**/**) +val set_field : 'b message -> 'a field -> 'a -> unit +[@@ocaml.deprecated " Renamed to Dream.set_field."] +(**/**) + (**/**) val with_local : 'a local -> 'a -> 'b message -> 'b message [@@ocaml.deprecated diff --git a/src/http/error_handler.ml b/src/http/error_handler.ml index 1ec3f2f5..d1ed9611 100644 --- a/src/http/error_handler.ml +++ b/src/http/error_handler.ml @@ -97,7 +97,7 @@ let dump (error : Catch.error) = Dream.all_headers request |> List.iter (fun (name, value) -> p "\n%s: %s" name value); - Dream.fold_locals (fun name value first -> + Dream.fold_fields (fun name value first -> if first then p "\n"; p "\n%s: %s" name value; diff --git a/src/http/http.ml b/src/http/http.ml index 0a88e8b1..09dd274f 100644 --- a/src/http/http.ml +++ b/src/http/http.ml @@ -406,7 +406,7 @@ let wrap_handler request_id field in requests. *) let user's_websocket_handler websocket = Lwt.with_value - Dream__middleware.Log.lwt_key + Dream__middleware.Log.id_lwt_key (Dream__middleware.Log.get_request_id ~request ()) (fun () -> user's_websocket_handler websocket) in diff --git a/src/middleware/flash.ml b/src/middleware/flash.ml index 22d5e78e..17e0640c 100644 --- a/src/middleware/flash.ml +++ b/src/middleware/flash.ml @@ -15,8 +15,8 @@ let log = let five_minutes = 5. *. 60. -let storage = - Dream.new_local ~name:"dream.flash" () +let storage_field = + Dream.new_field ~name:"dream.flash" () let flash_cookie = "dream.flash" @@ -53,7 +53,7 @@ let flash request = let put_flash request category message = let outbox = - match Dream.local request storage with + match Dream.field request storage_field with | Some outbox -> outbox | None -> let message = "Missing flash message middleware" in @@ -75,7 +75,7 @@ let flash_messages inner_handler request = else log ~request "%s" "No flash messages."); let outbox = ref [] in - Dream.set_local request storage outbox; + Dream.set_field request storage_field outbox; let existing = Cookie.cookie request flash_cookie in let%lwt response = inner_handler request in let entries = List.rev !outbox in diff --git a/src/middleware/log.ml b/src/middleware/log.ml index e7290602..f488e9ab 100644 --- a/src/middleware/log.ml +++ b/src/middleware/log.ml @@ -70,13 +70,13 @@ let logs_lib_tag : string Logs.Tag.def = (* Lwt sequence-associated storage key used to pass request ids for use when ~request is not provided. *) -let lwt_key : string Lwt.key = +let id_lwt_key : string Lwt.key = Lwt.new_key () (* The actual request id "field" associated with each request by the logger. If this field is missing, the logger assigns the request a fresh id. *) -let id = - Dream.new_local +let id_field = + Dream.new_field ~name:request_id_label ~show_value:(fun id -> id) () @@ -86,11 +86,11 @@ let get_request_id ?request () = let request_id = match request with | None -> None - | Some request -> Dream.local request id + | Some request -> Dream.field request id_field in match request_id with | Some _ -> request_id - | None -> Lwt.get lwt_key + | None -> Lwt.get id_lwt_key (* The current state of the request id sequence. *) let last_id = @@ -470,13 +470,13 @@ struct (* Get the requwst's id or assign a new one. *) let id = - match Dream.local request id with + match Dream.field request id_field with | Some id -> id | None -> last_id := !last_id + 1; - let new_id = string_of_int !last_id in - Dream.set_local request id new_id; - new_id + let id = string_of_int !last_id in + Dream.set_field request id_field id; + id in (* Identify the request in the log. *) @@ -495,7 +495,7 @@ struct (* Call the rest of the app. *) Lwt.try_bind (fun () -> - Lwt.with_value lwt_key (Some id) (fun () -> + Lwt.with_value id_lwt_key (Some id) (fun () -> next_handler request)) (fun response -> (* Log the elapsed time. If the response is a redirection, log the diff --git a/src/middleware/router.ml b/src/middleware/router.ml index e188716f..108837ea 100644 --- a/src/middleware/router.ml +++ b/src/middleware/router.ml @@ -165,8 +165,8 @@ let scope prefix middlewares routes = -let path_variable : string list Dream.local = - Dream.new_local +let path_field : string list Dream.field = + Dream.new_field ~name:"dream.path" ~show_value:(fun path -> String.concat "/" path) () @@ -175,24 +175,24 @@ let path_variable : string list Dream.local = string. *) (* TODO Remove this from the API. *) let path the_request = - match Dream.local the_request path_variable with + match Dream.field the_request path_field with | Some path -> path | None -> Dream.(Formats.(the_request |> target |> split_target |> fst |> from_path)) (* TODO Move site_prefix into this file and remove with_path from the API. *) let set_path request path = - Dream.set_local request path_variable path + Dream.set_field request path_field path (* Prefix is stored backwards. *) -let prefix_variable : string list Dream.local = - Dream.new_local +let prefix_field : string list Dream.field = + Dream.new_field ~name:"dream.prefix" ~show_value:(fun prefix -> String.concat "/" (List.rev prefix)) () let internal_prefix request = - match Dream.local request prefix_variable with + match Dream.field request prefix_field with | Some prefix -> prefix | None -> [] @@ -200,10 +200,10 @@ let prefix request = Formats.make_path (List.rev (internal_prefix request)) let set_prefix request prefix = - Dream.set_local request prefix_variable prefix + Dream.set_field request prefix_field prefix -let params_variable : (string * string) list Dream.local = - Dream.new_local +let params_field : (string * string) list Dream.field = + Dream.new_field ~name:"dream.params" ~show_value:(fun params -> params @@ -222,7 +222,7 @@ let missing_param request name = failwith message let param request name = - match Dream.local request params_variable with + match Dream.field request params_field with | None -> missing_param request name | Some params -> try List.assoc name params @@ -261,7 +261,7 @@ let router routes = match node with | Handler (method_, handler) when method_matches method_ (Dream.method_ request) -> - Dream.set_local request params_variable bindings; + Dream.set_field request params_field bindings; if is_wildcard then begin set_prefix request prefix; set_path request path; @@ -279,7 +279,7 @@ let router routes = in let params = - match Dream.local request params_variable with + match Dream.field request params_field with | Some params -> params | None -> [] in diff --git a/src/middleware/server.ml b/src/middleware/server.ml index df027256..89a4977d 100644 --- a/src/middleware/server.ml +++ b/src/middleware/server.ml @@ -11,8 +11,8 @@ module Stream = Dream_pure.Stream -let client_variable = - Dream.new_local +let client_field = + Dream.new_field ~name:"dream.client" ~show_value:(fun client -> client) () @@ -20,28 +20,28 @@ let client_variable = (* TODO What should be reported when the client address is missing? This is a sign of local testing. *) let client request = - match Dream.local request client_variable with + match Dream.field request client_field with | None -> "127.0.0.1:0" | Some client -> client let set_client request client = - Dream.set_local request client_variable client + Dream.set_field request client_field client -let https_variable = - Dream.new_local +let https_field = + Dream.new_field ~name:"dream.https" ~show_value:string_of_bool () let https request = - match Dream.local request https_variable with + match Dream.field request https_field with | Some true -> true | _ -> false let set_https request https = - Dream.set_local request https_variable https + Dream.set_field request https_field https diff --git a/src/middleware/session.ml b/src/middleware/session.ml index df09c3c2..d7169597 100644 --- a/src/middleware/session.ml +++ b/src/middleware/session.ml @@ -19,14 +19,14 @@ type 'a back_end = { send : 'a -> Dream.request -> Dream.response -> Dream.response Lwt.t; } -let middleware local back_end = fun inner_handler request -> +let middleware field back_end = fun inner_handler request -> let%lwt session = back_end.load request in - Dream.set_local request local session; + Dream.set_field request field session; let%lwt response = inner_handler request in back_end.send session request response -let getter local request = - match Dream.local request local with +let getter field request = + match Dream.field request field with | Some session -> session | None -> @@ -40,10 +40,10 @@ type 'a typed_middleware = { } let typed_middleware ?show_value () = - let local = Dream.new_local ~name:"dream.session" ?show_value () in + let field = Dream.new_field ~name:"dream.session" ?show_value () in { - middleware = middleware local; - getter = getter local; + middleware = middleware field; + getter = getter field; } diff --git a/src/middleware/upload.ml b/src/middleware/upload.ml index 15def736..9417c255 100644 --- a/src/middleware/upload.ml +++ b/src/middleware/upload.ml @@ -30,8 +30,8 @@ let initial_multipart_state () = { } (* TODO Dump the value of the multipart state somehow? *) -let multipart_state_variable : multipart_state Dream.local = - Dream.new_local +let multipart_state_field : multipart_state Dream.field = + Dream.new_field ~name:"dream.multipart" () diff --git a/src/pure/inmost.ml b/src/pure/inmost.ml index d54aa98c..24ef3019 100644 --- a/src/pure/inmost.ml +++ b/src/pure/inmost.ml @@ -11,14 +11,11 @@ type status = Status.status type stream = Stream.stream type buffer = Stream.buffer -module Scope_variable_metadata = +module Custom_field_metadata = struct type 'a t = string option * ('a -> string) option end -module Scope = Hmap.Make (Scope_variable_metadata) -(* TODO Rename Scope, because there is now only one scope. *) -(* TODO Given there are now only locals, maybe it's worth renaming them to - something else - there is now only one concept of variables. *) +module Fields = Hmap.Make (Custom_field_metadata) type websocket = Stream.stream @@ -30,7 +27,7 @@ and 'a message = { mutable headers : (string * string) list; mutable client_stream : Stream.stream; mutable server_stream : Stream.stream; - mutable locals : Scope.t; + mutable fields : Fields.t; } and client = { @@ -223,29 +220,28 @@ let close_stream message = let is_websocket response = response.specific.websocket -let fold_scope f initial scope = - Scope.fold (fun (B (key, value)) accumulator -> - match Scope.Key.info key with - | Some name, Some show_value -> f name (show_value value) accumulator - | _ -> accumulator) - scope - initial -type 'a local = 'a Scope.key -let new_local ?name ?show_value () = - Scope.Key.create (name, show_value) +type 'a field = 'a Fields.key + +let new_field ?name ?show_value () = + Fields.Key.create (name, show_value) + +let field message key = + Fields.find key message.fields -(* TODO Tension between "t-first" and not, because typically, for a getter, the - "index" parameter could be partially applied. *) -let local message key = - Scope.find key message.locals +let set_field message key value = + message.fields <- Fields.add key value message.fields + +let fold_fields f initial message = + Fields.fold (fun (B (key, value)) accumulator -> + match Fields.Key.info key with + | Some name, Some show_value -> f name (show_value value) accumulator + | _ -> accumulator) + message.fields + initial -let set_local message key value = - message.locals <- Scope.add key value message.locals -let fold_locals f initial message = - fold_scope f initial message.locals let request ?method_ @@ -275,7 +271,7 @@ let request headers; client_stream; server_stream; - locals = Scope.empty; + fields = Fields.empty; } in request @@ -299,7 +295,7 @@ let response client_stream; server_stream; (* TODO This fully dead stream should be preallocated. *) - locals = Scope.empty; + fields = Fields.empty; } in response diff --git a/src/pure/inmost.mli b/src/pure/inmost.mli index 56cae5f3..d57b0b9b 100644 --- a/src/pure/inmost.mli +++ b/src/pure/inmost.mli @@ -111,8 +111,8 @@ val is_websocket : response -> (websocket -> unit promise) option -type 'a local -val new_local : ?name:string -> ?show_value:('a -> string) -> unit -> 'a local -val local : 'b message -> 'a local -> 'a option -val set_local : 'b message -> 'a local -> 'a -> unit -val fold_locals : (string -> string -> 'a -> 'a) -> 'a -> 'b message -> 'a +type 'a field +val new_field : ?name:string -> ?show_value:('a -> string) -> unit -> 'a field +val field : 'b message -> 'a field -> 'a option +val set_field : 'b message -> 'a field -> 'a -> unit +val fold_fields : (string -> string -> 'a -> 'a) -> 'a -> 'b message -> 'a diff --git a/src/sql/sql.ml b/src/sql/sql.ml index 69a667ad..af20da00 100644 --- a/src/sql/sql.ml +++ b/src/sql/sql.ml @@ -13,8 +13,8 @@ let log = Dream__middleware.Log.sub_log "dream.sql" (* TODO Debug metadata for the pools. *) -let pool_variable : (_, Caqti_error.t) Caqti_lwt.Pool.t Dream.local = - Dream.new_local () +let pool_field : (_, Caqti_error.t) Caqti_lwt.Pool.t Dream.field = + Dream.new_field () let foreign_keys_on = Caqti_request.exec Caqti_type.unit "PRAGMA foreign_keys = ON" @@ -30,7 +30,7 @@ let sql_pool ?size uri = begin match !pool_cell with | Some pool -> - Dream.set_local request pool_variable pool; + Dream.set_field request pool_field pool; inner_handler request | None -> (* The correctness of this code is subtle. There is no race condition with @@ -46,7 +46,7 @@ let sql_pool ?size uri = match pool with | Ok pool -> pool_cell := Some pool; - Dream.set_local request pool_variable pool; + Dream.set_field request pool_field pool; inner_handler request | Error error -> (* Deliberately raise an exception so that it can be communicated to any @@ -59,7 +59,7 @@ let sql_pool ?size uri = end let sql request callback = - match Dream.local request pool_variable with + match Dream.field request pool_field with | None -> let message = "Dream.sql: no pool; did you apply Dream.sql_pool?" in log.error (fun log -> log ~request "%s" message);