http2.bend

bend-kit-http2@0.1.0.0 · 311 lines · 0 definitions

1# RFC 9113 HTTP/2 frame codec over packed Bytes.
2import Base
3import bend-kit-bytes@0.3.1.0/bytes.bend as Bytes
4
5type Frame is Type:
6  Frame{kind: U32, flags: U32, stream: U32, payload: Bytes.Bytes}
7
8type Error is Data:
9  Error{code: U32, connection: Bool, stream: U32}
10
11type Decode is Type:
12  Need{input: Bytes.Bytes}
13  Got{frame: Frame, rest: Bytes.Bytes}
14  Bad{error: Error}
15
16type Checked is Type:
17  Valid{frame: Frame}
18  Invalid{error: Error}
19
20# Error codes from RFC 9113 §7: 1=PROTOCOL_ERROR, 3=FLOW_CONTROL_ERROR,
21# 6=FRAME_SIZE_ERROR. A connection error uses stream 0.
22def fail(+code: U32, +connection: Bool, +stream: U32) -> Maybe<&2, Error>:
23  Some{Error{code, connection, Bool.pick(U32, connection, 0, stream)}}
24
25def reject(bad: Bool, +code: U32, +connection: Bool, +stream: U32) -> Maybe<&2, Error>:
26  match bad:
27    case True{}:
28      fail(code, connection, stream)
29    case False{}:
30      None{}
31
32def first(m: Maybe<&2, Error>, n: Maybe<&2, Error>) -> Maybe<&2, Error>:
33  match m:
34    case Some{e}:
35      Some{e}
36    case None{}:
37      n
38
39def number(m: Maybe<&2, U32>) -> U32:
40  match m:
41    case Some{x}:
42      x
43    case None{}:
44      0
45
46def with_number(-R: Type, r: Bytes.Bytes & Maybe<&2, U32>, k: Bytes.Bytes -> U32 -> R) -> R:
47  (b, m) = r
48  k(b, number(m))
49
50def with_slice(-R: Type, r: Bytes.Bytes & Bytes.Bytes, k: Bytes.Bytes -> Bytes.Bytes -> R) -> R:
51  (b, part) = r
52  k(b, part)
53
54def has(+flags: U32, +bit: U32) -> Bool:
55  U32.is_ne((flags .&. bit : U32), 0)
56
57# Field blocks, SETTINGS, and stream 0 make frame-size faults connection-wide.
58def size.scope(+kind: U32, +stream: U32) -> Bool:
59  match kind:
60    case 1:
61      True{}
62    case 4:
63      True{}
64    case 5:
65      True{}
66    case 9:
67      True{}
68    case _:
69      U32.is_eq(stream, 0)
70
71def size.error(+kind: U32, +stream: U32) -> Error:
72  +connection = size.scope(kind, stream)
73  Error{6, connection, Bool.pick(U32, connection, 0, stream)}
74
75def size.reject(oversized: Bool, +kind: U32, +stream: U32) -> Maybe<&2, Error>:
76  match oversized:
77    case True{}:
78      Some{size.error(kind, stream)}
79    case False{}:
80      None{}
81
82# A PADDED payload must contain the pad-length octet and all the padding.
83def padding(+flags: U32, +len: U32, +fixed: U32, +pad: U32) -> Bool:
84  Bool.and(has(flags, 8), Bool.or(U32.is_le(len, fixed), U32.is_gt(pad, (len - fixed - 1 : U32))))
85
86def setting(+id: U32, +value: U32) -> Maybe<&2, Error>:
87  match id:
88    case 2:
89      reject(U32.is_gt(value, 1), 1, True{}, 0)
90    case 4:
91      reject(U32.is_gt(value, 2147483647), 3, True{}, 0)
92    case 5:
93      reject(Bool.or(U32.is_lt(value, 16384), U32.is_gt(value, 16777215)), 1, True{}, 0)
94    case _:
95      None{}
96
97def settings.go(n: Nat, b: Bytes.Bytes, +at: U32, m: Maybe<&2, Error>) -> Bytes.Bytes & Maybe<&2, Error>:
98  match n:
99    case 0n:
100      (b, m)
101    case 1n+p:
102      with_number(Bytes.Bytes & Maybe<&2, Error>, Bytes.get.u16be(b, at), b => id =>
103        with_number(Bytes.Bytes & Maybe<&2, Error>, Bytes.get.u32be(b, (at + 2 : U32)), b => value =>
104          settings.go(p, b, (at + 6 : U32), first(m, setting(id, value)))))
105
106def rules(+kind: U32, +flags: U32, +stream: U32, +len: U32, +pad: U32, +promised: U32, +value: U32, m: Maybe<&2, Error>) -> Maybe<&2, Error>:
107  match kind:
108    case 0:
109      first(reject(U32.is_eq(stream, 0), 1, True{}, stream),
110        first(reject(Bool.and(has(flags, 8), U32.is_eq(len, 0)), 6, False{}, stream),
111          reject(padding(flags, len, 0, pad), 1, True{}, stream)))
112    case 1:
113      +fixed = Bool.pick(U32, has(flags, 32), 5, 0)
114      +prefix = Bool.pick(U32, has(flags, 8), 1, 0)
115      first(reject(U32.is_eq(stream, 0), 1, True{}, stream),
116        first(reject(U32.is_lt(len, (fixed + prefix : U32)), 6, True{}, stream),
117          reject(padding(flags, len, fixed, pad), 1, True{}, stream)))
118    case 2:
119      first(reject(U32.is_ne(len, 5), 6, U32.is_eq(stream, 0), stream),
120        reject(U32.is_eq(stream, 0), 1, True{}, stream))
121    case 3:
122      first(reject(U32.is_ne(len, 4), 6, True{}, stream),
123        reject(U32.is_eq(stream, 0), 1, True{}, stream))
124    case 4:
125      first(reject(U32.is_ne(stream, 0), 1, True{}, stream),
126        first(reject(Bool.and(has(flags, 1), U32.is_ne(len, 0)), 6, True{}, stream),
127          first(reject(U32.is_ne((len % 6 : U32), 0), 6, True{}, stream), m)))
128    case 5:
129      +prefix = Bool.pick(U32, has(flags, 8), 1, 0)
130      first(reject(U32.is_eq(stream, 0), 1, True{}, stream),
131        first(reject(U32.is_lt(len, (prefix + 4 : U32)), 6, True{}, stream),
132          first(reject(padding(flags, len, 4, pad), 1, True{}, stream),
133            reject(U32.is_eq(promised, 0), 1, True{}, stream))))
134    case 6:
135      first(reject(U32.is_ne(len, 8), 6, True{}, stream),
136        reject(U32.is_ne(stream, 0), 1, True{}, stream))
137    case 7:
138      first(reject(U32.is_lt(len, 8), 6, True{}, stream),
139        reject(U32.is_ne(stream, 0), 1, True{}, stream))
140    case 8:
141      first(reject(U32.is_ne(len, 4), 6, U32.is_eq(stream, 0), stream),
142        reject(U32.is_eq((value .&. 2147483647 : U32), 0), 1, U32.is_eq(stream, 0), stream))
143    case 9:
144      reject(U32.is_eq(stream, 0), 1, True{}, stream)
145    case _:
146      None{}
147
148def checked(m: Maybe<&2, Error>, +kind: U32, +flags: U32, +stream: U32, b: Bytes.Bytes) -> Checked:
149  match m:
150    case Some{e}:
151      Invalid{e}
152    case None{}:
153      Valid{Frame{kind, flags, stream, b}}
154
155def with_settings(r: Bytes.Bytes & Maybe<&2, Error>, +flags: U32, +stream: U32) -> Checked:
156  (b, m) = r
157  checked(m, 4, flags, stream, b)
158
159def inspect.settings(m: Maybe<&2, Error>, +flags: U32, +stream: U32, +len: U32, b: Bytes.Bytes) -> Checked:
160  match m:
161    case Some{e}:
162      Invalid{e}
163    case None{}:
164      with_settings(settings.go(U32.to_nat((len / 6 : U32)), b, 0, None{}), flags, stream)
165
166def inspect.promised(+kind: U32, +flags: U32, +stream: U32, +len: U32, +pad: U32, b: Bytes.Bytes) -> Checked:
167  match kind:
168    case 5:
169      +prefix = Bool.pick(U32, has(flags, 8), 1, 0)
170      with_number(Checked, Bytes.get.u32be(b, prefix), b => promised =>
171        checked(rules(kind, flags, stream, len, pad, (promised .&. 2147483647 : U32), 0, None{}), kind, flags, stream, b))
172    case _:
173      checked(rules(kind, flags, stream, len, pad, 0, 0, None{}), kind, flags, stream, b)
174
175def inspect.padded.if(padded: Bool, +kind: U32, +flags: U32, +stream: U32, +len: U32, b: Bytes.Bytes) -> Checked:
176  match padded:
177    case True{}:
178      with_number(Checked, Bytes.get(b, 0), b => pad =>
179        inspect.promised(kind, flags, stream, len, pad, b))
180    case False{}:
181      inspect.promised(kind, flags, stream, len, 0, b)
182
183def inspect.padded(+kind: U32, +flags: U32, +stream: U32, +len: U32, b: Bytes.Bytes) -> Checked:
184  inspect.padded.if(has(flags, 8), kind, flags, stream, len, b)
185
186def inspect.detail(+kind: U32, +flags: U32, +stream: U32, +len: U32, b: Bytes.Bytes) -> Checked:
187  match kind:
188    case 0:
189      inspect.padded(kind, flags, stream, len, b)
190    case 1:
191      inspect.padded(kind, flags, stream, len, b)
192    case 4:
193      inspect.settings(rules(kind, flags, stream, len, 0, 0, 0, None{}), flags, stream, len, b)
194    case 5:
195      inspect.padded(kind, flags, stream, len, b)
196    case 8:
197      with_number(Checked, Bytes.get.u32be(b, 0), b => value =>
198        checked(rules(kind, flags, stream, len, 0, 0, value, None{}), kind, flags, stream, b))
199    case _:
200      checked(rules(kind, flags, stream, len, 0, 0, 0, None{}), kind, flags, stream, b)
201
202
203def inspect.basic(m: Maybe<&2, Error>, +kind: U32, +flags: U32, +stream: U32, +len: U32, b: Bytes.Bytes) -> Checked:
204  match m:
205    case Some{e}:
206      Invalid{e}
207    case None{}:
208      inspect.detail(kind, flags, stream, len, b)
209
210def inspect(max: U32, +kind: U32, +flags: U32, +stream: U32, b: Bytes.Bytes) -> Checked:
211  Bytes.Bytes{+len, buf} = b
212  +basic = first(size.reject(Bool.or(U32.is_gt(len, max), U32.is_gt(len, 16777215)), kind, stream),
213    first(reject(U32.is_gt(kind, 255), 1, True{}, stream),
214      first(reject(U32.is_gt(flags, 255), 1, True{}, stream),
215        reject(U32.is_gt(stream, 2147483647), 1, True{}, stream))))
216  inspect.basic(basic, kind, flags, stream, len, Bytes.Bytes{len, buf})
217
218# max is the peer's SETTINGS_MAX_FRAME_SIZE, initially 16384.
219def allowed(+kind: U32) -> U32:
220  match kind:
221    case 0:
222      9
223    case 1:
224      45
225    case 2:
226      0
227    case 3:
228      0
229    case 4:
230      1
231    case 5:
232      12
233    case 6:
234      1
235    case 7:
236      0
237    case 8:
238      0
239    case 9:
240      4
241    case _:
242      255
243
244def encode.buffer(b: Bytes.Bytes, src: Array<U32>, +len: U32) -> Result<&1, &1, Error, Bytes.Bytes>:
245  Bytes.Bytes{_, dst} = b
246  Done{Bytes.Bytes{(len + 9 : U32), Bytes.dst(Bytes.copy(len, src, dst, 0, 9))}}
247
248def encode.flags(ok: Bool, +kind: U32, +flags: U32, +stream: U32, payload: Bytes.Bytes) -> Result<&1, &1, Error, Bytes.Bytes>:
249  match ok:
250    case False{}:
251      Fail{Error{1, True{}, 0}}
252    case True{}:
253      Bytes.Bytes{+len, src} = payload
254      encode.buffer(Bytes.set.u32be(Bytes.set(Bytes.set(Bytes.put(Bytes.new((len + 9 : U32)), 0, 3, True{}, len), 3, kind), 4, flags), 5, stream), src, len)
255
256def encode.checked(c: Checked) -> Result<&1, &1, Error, Bytes.Bytes>:
257  match c:
258    case Invalid{e}:
259      Fail{e}
260    case Valid{Frame{+kind, +flags, +stream, payload}}:
261      encode.flags(U32.is_eq((flags .&. U32.not(allowed(kind)) : U32), 0), kind, flags, stream, payload)
262def encode(max: U32, f: Frame) -> Result<&1, &1, Error, Bytes.Bytes>:
263  Frame{+kind, +flags, +stream, payload} = f
264  encode.checked(inspect(max, kind, flags, stream, payload))
265
266def parse.result(c: Checked, b: Bytes.Bytes, +len: U32) -> Decode:
267  match c:
268    case Invalid{e}:
269      Bad{e}
270    case Valid{f}:
271      Bytes.Bytes{+n, buf} = b
272      with_slice(Decode, Bytes.slice(Bytes.Bytes{n, buf}, (len + 9 : U32), (n - len - 9 : U32)),
273        unused => rest => Got{f, rest})
274
275def parse.payload(max: U32, +len: U32, +kind: U32, +flags: U32, +stream: U32, b: Bytes.Bytes) -> Decode:
276  with_slice(Decode, Bytes.slice(b, 9, len), b => payload =>
277    parse.result(inspect(max, kind, flags, stream, payload), b, len))
278
279def parse.full(enough: Bool, max: U32, +len: U32, +kind: U32, +flags: U32, +stream: U32, b: Bytes.Bytes) -> Decode:
280  match enough:
281    case True{}:
282      parse.payload(max, len, kind, flags, stream, b)
283    case False{}:
284      Need{b}
285
286def parse.size(oversized: Bool, max: U32, +len: U32, +kind: U32, +flags: U32, +stream: U32, b: Bytes.Bytes) -> Decode:
287  match oversized:
288    case True{}:
289      Bad{size.error(kind, stream)}
290    case False{}:
291      Bytes.Bytes{+n, buf} = b
292      parse.full(U32.is_le(len, (n - 9 : U32)), max, len, kind, flags, stream, Bytes.Bytes{n, buf})
293
294def parse.header(+max: U32, b: Bytes.Bytes) -> Decode:
295  with_number(Decode, Bytes.uint(b, 0, 3, True{}), b => +len =>
296    with_number(Decode, Bytes.get(b, 3), b => kind =>
297      with_number(Decode, Bytes.get(b, 4), b => flags =>
298        with_number(Decode, Bytes.get.u32be(b, 5), b => stream =>
299          parse.size(U32.is_gt(len, max), max, len, kind, flags, (stream .&. 2147483647 : U32), b)))))
300
301def parse.start(short: Bool, max: U32, b: Bytes.Bytes) -> Decode:
302  match short:
303    case True{}:
304      Need{b}
305    case False{}:
306      parse.header(max, b)
307
308# A partial header or payload returns Need with the original input intact.
309def parse(max: U32, b: Bytes.Bytes) -> Decode:
310  Bytes.Bytes{+n, buf} = b
311  parse.start(U32.is_lt(n, 9), max, Bytes.Bytes{n, buf})