ocaml http/1, http/2 and websocket client and server library
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