1
1
type t =
2
- { payload_length : int
3
- ; is_fin : bool
4
- ; mask : int32 option
5
- ; payload : Payload .t
6
- ; opcode : Websocket.Opcode .t
7
- }
2
+ { payload_length : int
3
+ ; is_fin : bool
4
+ ; mask : int32 option
5
+ ; opcode : Websocket.Opcode .t
6
+ }
8
7
9
8
let is_fin headers =
10
9
let bits = Bigstringaf. unsafe_get headers 0 |> Char. code in
@@ -87,7 +86,7 @@ let parse_headers =
87
86
>> = fun headers_len -> Unsafe. take headers_len Bigstringaf. sub
88
87
;;
89
88
90
- let payload_parser t =
89
+ let payload_parser t payload =
91
90
let open Angstrom in
92
91
let unmask t bs ~src_off =
93
92
match t.mask with
@@ -120,28 +119,24 @@ let payload_parser t =
120
119
available >> = fun m ->
121
120
let m' = (min m n) in
122
121
let n' = n - m' in
123
- schedule_size ~src_off t. payload m'
122
+ schedule_size ~src_off payload m'
124
123
>> = fun () -> read_exact (src_off + m') n'
125
124
in
126
125
fun n -> read_exact 0 n
127
126
in
128
127
read_exact t.payload_length
129
- >> = fun () -> finish t. payload
128
+ >> = fun () -> finish payload
130
129
;;
131
130
132
- let frame ~ buf =
131
+ let frame =
133
132
let open Angstrom in
134
133
parse_headers
135
134
>> | fun headers ->
136
135
let payload_length = payload_length_of_headers headers
137
136
and is_fin = is_fin headers
138
137
and opcode = opcode headers
139
138
and mask = mask headers in
140
- let payload = match payload_length with
141
- | 0 -> Payload. empty
142
- | _ -> Payload. create buf
143
- in
144
- { is_fin; opcode; mask; payload_length; payload }
139
+ { is_fin; opcode; mask; payload_length }
145
140
;;
146
141
147
142
module Reader = struct
@@ -155,25 +150,53 @@ module Reader = struct
155
150
type 'error t =
156
151
{ parser : unit Angstrom .t
157
152
; mutable parse_state : 'error parse_state
158
- ; mutable closed : bool }
153
+ ; mutable closed : bool
154
+ ; mutable wakeup : Optional_thunk .t
155
+ }
156
+
157
+ let wakeup t =
158
+ let f = t.wakeup in
159
+ t.wakeup < - Optional_thunk. none;
160
+ Optional_thunk. call_if_some f
159
161
160
162
let create frame_handler =
161
- let parser =
163
+ let rec parser t =
162
164
let open Angstrom in
163
165
let buf = Bigstringaf. create 0x1000 in
164
166
skip_many
165
- (frame ~buf < * commit >> = fun frame ->
166
- let payload = frame.payload in
167
- let { is_fin; opcode; payload_length = len; _ } = frame in
168
- frame_handler ~opcode ~is_fin ~len payload;
169
- payload_parser frame)
167
+ (frame < * commit >> = fun frame ->
168
+ let { payload_length; _ } = frame in
169
+ let payload =
170
+ match payload_length with
171
+ | 0 -> Payload. create_empty ()
172
+ | _ ->
173
+ Payload. create buf
174
+ ~when_ready_to_read: (Optional_thunk. some (fun () ->
175
+ wakeup (Lazy. force t)))
176
+ in
177
+ frame_handler frame payload;
178
+ payload_parser frame payload)
179
+ and t = lazy (
180
+ { parser = parser t
181
+ ; parse_state = Done
182
+ ; closed = false
183
+ ; wakeup = Optional_thunk. none
184
+ }
185
+ )
170
186
in
171
- { parser
172
- ; parse_state = Done
173
- ; closed = false
174
- }
187
+ Lazy. force t
175
188
;;
176
189
190
+ let is_closed t =
191
+ t.closed
192
+
193
+ let on_wakeup t k =
194
+ if is_closed t
195
+ then failwith " on_wakeup on closed reader"
196
+ else if Optional_thunk. is_some t.wakeup
197
+ then failwith " on_wakeup: only one callback can be registered at a time"
198
+ else t.wakeup < - Optional_thunk. some k
199
+
177
200
let transition t state =
178
201
match state with
179
202
| AU. Done (consumed, () )
@@ -195,28 +218,48 @@ module Reader = struct
195
218
t.parse_state < - Partial continue
196
219
| _ -> assert false
197
220
198
- let rec read_with_more t bs ~off ~len more =
221
+ let rec _read_with_more t bs ~off ~len more =
222
+ let initial = match t.parse_state with Done -> true | _ -> false in
199
223
let consumed =
200
224
match t.parse_state with
201
225
| Fail _ -> 0
226
+ (* Don't feed empty input when we're at a request boundary *)
227
+ | Done when len = 0 -> 0
202
228
| Done ->
203
229
start t (AU. parse t.parser);
204
- read_with_more t bs ~off ~len more;
230
+ _read_with_more t bs ~off ~len more;
205
231
| Partial continue ->
206
232
transition t (continue bs more ~off ~len )
207
233
in
208
- begin match more with
209
- | Complete -> t.closed < - true ;
210
- | Incomplete -> ()
211
- end ;
234
+ (* Special case where the parser just started and was fed a zero-length
235
+ * bigstring. Avoid putting them parser in an error state in this scenario.
236
+ * If we were already in a `Partial` state, return the error. *)
237
+ if initial && len = 0 then t.parse_state < - Done ;
238
+ match t.parse_state with
239
+ | Done when consumed < len ->
240
+ let off = off + consumed
241
+ and len = len - consumed in
242
+ consumed + _read_with_more t bs ~off ~len more
243
+ | _ -> consumed
244
+ ;;
245
+
246
+ let read_with_more t bs ~off ~len more =
247
+ let consumed = _read_with_more t bs ~off ~len more in
248
+ (match more with
249
+ | Complete ->
250
+ t.closed < - true
251
+ | Incomplete -> () );
212
252
consumed
213
253
254
+ let force_close t =
255
+ t.closed < - true ;
256
+ ;;
257
+
214
258
let next t =
215
259
match t.parse_state with
216
- | Done ->
217
- if t.closed
218
- then `Close
219
- else `Read
220
260
| Fail failure -> `Error failure
261
+ | _ when t.closed -> `Close
262
+ | Done -> `Read
221
263
| Partial _ -> `Read
264
+ ;;
222
265
end
0 commit comments