ocaml http/1, http/2 and websocket client and server library
at main 6.5 kB view raw
1(** CODEC module for type-safe serialization/deserialization. 2 3 This module provides a signature for codecs that can encode and decode 4 values to/from binary buffers. Users implement this signature with their 5 preferred serialization library (yojson, jsonm, msgpck, etc.). 6 7 The library itself does not depend on any specific serialization library. *) 8 9(** {1 CODEC Signature} *) 10 11(** Module signature for codecs. 12 13 Codecs provide encoding/decoding between OCaml values and binary buffers. 14 The Cstruct.t type is used for efficient zero-copy binary handling. *) 15module type CODEC = sig 16 type 'a encoder 17 (** Encoder for type 'a - converts values to binary format *) 18 19 type 'a decoder 20 (** Decoder for type 'a - parses values from binary format *) 21 22 val content_type : string 23 (** Content-Type header value for this codec. Examples: "application/json", 24 "application/msgpack", "application/cbor" *) 25 26 val encode : 'a encoder -> 'a -> (Cstruct.t, string) result 27 (** Encode a value to a buffer. Returns Error with message on encoding 28 failure. *) 29 30 val decode : 'a decoder -> Cstruct.t -> ('a, string) result 31 (** Decode a value from a buffer. Returns Error with message on decoding 32 failure. *) 33 34 val encode_stream : 'a encoder -> 'a -> Cstruct.t Seq.t option 35 (** Optional: Streaming encode for large payloads. Returns None if streaming 36 is not supported. *) 37 38 val decode_stream : 39 'a decoder -> Cstruct.t Seq.t -> ('a, string) result option 40 (** Optional: Streaming decode for large payloads. Returns None if streaming 41 is not supported. *) 42end 43 44(** {1 With_codec Functor} *) 45 46(** Error type for codec operations *) 47type codec_error = 48 | Encode_error of string 49 | Decode_error of string 50 | Unsupported_body_type 51 52let codec_error_to_string = function 53 | Encode_error msg -> "Encode error: " ^ msg 54 | Decode_error msg -> "Decode error: " ^ msg 55 | Unsupported_body_type -> "Unsupported body type for codec operation" 56 57(** Functor that provides helpers for working with a specific codec. 58 59 This functor generates request/response helpers that automatically set 60 Content-Type headers and handle encoding/decoding. *) 61module With_codec (C : CODEC) = struct 62 (** Encode a value to a string body *) 63 let encode_body encoder value = 64 match C.encode encoder value with 65 | Ok buf -> Ok (Cstruct.to_string buf) 66 | Error msg -> Error (Encode_error msg) 67 68 (** Decode a string body to a value *) 69 let decode_body decoder body_str = 70 let buf = Cstruct.of_string body_str in 71 match C.decode decoder buf with 72 | Ok value -> Ok value 73 | Error msg -> Error (Decode_error msg) 74 75 (** Encode a value directly to Cstruct *) 76 let encode encoder value = 77 match C.encode encoder value with 78 | Ok buf -> Ok buf 79 | Error msg -> Error (Encode_error msg) 80 81 (** Decode a Cstruct to a value *) 82 let decode decoder buf = 83 match C.decode decoder buf with 84 | Ok value -> Ok value 85 | Error msg -> Error (Decode_error msg) 86 87 (** Get the content type for this codec *) 88 let content_type = C.content_type 89end 90 91(** {1 Example Implementations} 92 93 These are examples showing how to implement codecs. Users should implement 94 their own codecs with their preferred libraries. 95 96 {2 JSON Example} 97 98 {[ 99 module Json_codec : Hcs.Codec.CODEC = struct 100 (* Using yojson as an example *) 101 type 'a encoder = 'a -> Yojson.Safe.t 102 type 'a decoder = Yojson.Safe.t -> ('a, string) result 103 104 let content_type = "application/json" 105 106 let encode enc value = 107 try Ok (Cstruct.of_string (Yojson.Safe.to_string (enc value))) 108 with exn -> Error (Printexc.to_string exn) 109 110 let decode dec buf = 111 try 112 let json = Yojson.Safe.from_string (Cstruct.to_string buf) in 113 dec json 114 with exn -> Error (Printexc.to_string exn) 115 116 let encode_stream _ _ = None 117 let decode_stream _ _ = None 118 end 119 ]} 120 121 {2 MessagePack Example} 122 123 {[ 124 module Msgpack_codec : Hcs.Codec.CODEC = struct 125 type 'a encoder = 'a -> Msgpck.t 126 type 'a decoder = Msgpck.t -> ('a, string) result 127 128 let content_type = "application/msgpack" 129 130 let encode enc value = 131 try 132 let packed = 133 Msgpck.Bytes.to_string (Msgpck.Bytes.of_msgpck (enc value)) 134 in 135 Ok (Cstruct.of_string packed) 136 with exn -> Error (Printexc.to_string exn) 137 138 let decode dec buf = 139 try 140 match 141 Msgpck.Bytes.read (Bytes.of_string (Cstruct.to_string buf)) 142 with 143 | Some (msgpack, _) -> dec msgpack 144 | None -> Error "Failed to parse msgpack" 145 with exn -> Error (Printexc.to_string exn) 146 147 let encode_stream _ _ = None 148 let decode_stream _ _ = None 149 end 150 ]} 151 152 {2 Plain Text Codec} 153 154 {[ 155 module Text_codec : Hcs.Codec.CODEC = struct 156 type 'a encoder = 'a -> string 157 type 'a decoder = string -> ('a, string) result 158 159 let content_type = "text/plain; charset=utf-8" 160 let encode enc value = Ok (Cstruct.of_string (enc value)) 161 let decode dec buf = dec (Cstruct.to_string buf) 162 let encode_stream _ _ = None 163 let decode_stream _ _ = None 164 end 165 ]} *) 166 167(** {1 Built-in Identity Codec} 168 169 A simple pass-through codec for raw binary data. *) 170module Identity_codec : 171 CODEC 172 with type 'a encoder = 'a -> Cstruct.t 173 and type 'a decoder = Cstruct.t -> ('a, string) result = struct 174 type 'a encoder = 'a -> Cstruct.t 175 type 'a decoder = Cstruct.t -> ('a, string) result 176 177 let content_type = "application/octet-stream" 178 179 let encode enc value = 180 try Ok (enc value) with exn -> Error (Printexc.to_string exn) 181 182 let decode dec buf = dec buf 183 let encode_stream _ _ = None 184 let decode_stream _ _ = None 185end 186 187(** {1 Built-in String Codec} 188 189 A simple codec for UTF-8 text. *) 190module String_codec : 191 CODEC 192 with type 'a encoder = 'a -> string 193 and type 'a decoder = string -> ('a, string) result = struct 194 type 'a encoder = 'a -> string 195 type 'a decoder = string -> ('a, string) result 196 197 let content_type = "text/plain; charset=utf-8" 198 199 let encode enc value = 200 try Ok (Cstruct.of_string (enc value)) 201 with exn -> Error (Printexc.to_string exn) 202 203 let decode dec buf = dec (Cstruct.to_string buf) 204 let encode_stream _ _ = None 205 let decode_stream _ _ = None 206end