okami.mom/albedo

git clone https://git.nkp.pet/okami.mom/albedo

Aly Raffaufrefactor(sse): own field parsing and fragment assembly in Gleamfe3b8a3

main
4.8 KiB147 linesraw
1//// Catches framing bugs at exact byte splits that HTTP E2E cannot control.
2
3import albedo/openai_api/sse
4import gleam/bit_array
5import gleam/int
6import gleam/list
7import gleam/result
8import gleam/string
9
10pub fn split_utf8_bom_and_crlf_test() -> Nil {
11  let input = <<
12    0xEF,
13    0xBB,
14    0xBF,
15    ": heartbeat\r\nevent: update\r\ndata: hé\r\ndata: world\r\n\r\n":utf8,
16  >>
17  let chunks = bytes(input)
18  let assert Ok(#(_, events)) =
19    list.try_fold(chunks, #(sse.new(1024), []), fn(acc, chunk) {
20      let #(parser, events) = acc
21      use #(parser, next) <- result.try(sse.feed(parser, chunk))
22      Ok(#(parser, list.append(events, next)))
23    })
24  assert events == [sse.Event("update", "hé\nworld")]
25}
26
27pub fn configured_limits_reject_incomplete_lines_test() -> Nil {
28  assert sse.feed(sse.new(7), <<"data: xx">>) == Error(sse.EventTooLarge)
29  assert sse.feed(sse.new(7), <<":1234567">>) == Error(sse.EventTooLarge)
30}
31
32pub fn invalid_utf8_is_a_typed_error_test() -> Nil {
33  assert sse.feed(sse.new(1024), <<"data: ":utf8, 0xFF, "\n\n":utf8>>)
34    == Error(sse.InvalidUtf8)
35}
36
37pub fn non_byte_aligned_input_is_a_typed_error_test() -> Nil {
38  assert sse.feed(sse.new(1024), <<1:size(1)>>) == Error(sse.InvalidUtf8)
39  let assert Ok(#(parser, [])) = sse.feed(sse.new(1024), <<"data: ">>)
40  assert sse.feed(parser, <<1:size(1)>>) == Error(sse.InvalidUtf8)
41}
42
43pub fn flushes_unterminated_event_test() -> Nil {
44  let assert Ok(#(parser, [])) = sse.feed(sse.new(1024), <<"data: [DONE]">>)
45  assert sse.finish(parser) == Ok([sse.Event("", "[DONE]")])
46}
47
48fn bytes(input: BitArray) -> List(BitArray) {
49  case input {
50    <<byte, rest:bytes>> -> [<<byte>>, ..bytes(rest)]
51    _ -> []
52  }
53}
54
55pub fn every_two_chunk_partition_preserves_events_test() -> Nil {
56  let input = <<
57    0xEF,
58    0xBB,
59    0xBF,
60    "event: update\rdata:  \u{0301}hé\r\ndata: two\nignored: value\nid: bad\u{0000}\nretry: invalid\n\rdata\r\rdata: last":utf8,
61  >>
62  let expected = [
63    sse.Event("update", " \u{0301}hé\ntwo"),
64    sse.Event("", ""),
65    sse.Event("", "last"),
66  ]
67  int.range(0, bit_array.byte_size(input), with: Nil, run: fn(_, offset) {
68    let assert Ok(first) = bit_array.slice(input, 0, offset)
69    let assert Ok(second) =
70      bit_array.slice(input, offset, bit_array.byte_size(input) - offset)
71    assert decode_chunks([first, <<>>, second], 1024) == Ok(expected)
72  })
73  assert decode_chunks(bytes(input), 1024) == Ok(expected)
74}
75
76pub fn large_fragmented_line_is_assembled_without_changing_data_test() -> Nil {
77  let payload = string.repeat("hé", 30_000)
78  let input = bit_array.from_string("data: " <> payload <> "\n\n")
79  assert decode_chunks(bytes(input), 100_000) == Ok([sse.Event("", payload)])
80}
81
82pub fn event_budget_counts_unknown_fields_but_excludes_comments_test() -> Nil {
83  assert decode_chunks([<<": long comment\ndata: x\n\n">>], 14)
84    == Ok([sse.Event("", "x")])
85  assert decode_chunks([<<"data: x\nunknown\n\n">>], 13)
86    == Error(sse.EventTooLarge)
87}
88
89pub fn malformed_event_name_and_split_invalid_utf8_test() -> Nil {
90  let assert Error(sse.Malformed(_)) =
91    decode_chunks([<<"event: bad":utf8, 0, "\ndata: x\n\n":utf8>>], 1024)
92  assert decode_chunks([<<"data: ":utf8, 0xC3>>, <<"\n\n">>], 1024)
93    == Error(sse.InvalidUtf8)
94}
95
96pub fn trailing_cr_and_exact_line_limit_test() -> Nil {
97  assert decode_chunks([<<"data: x\r">>], 7) == Ok([sse.Event("", "x")])
98  assert decode_chunks([<<"data: x">>, <<"x">>], 7) == Error(sse.EventTooLarge)
99}
100
101fn decode_chunks(
102  chunks: List(BitArray),
103  limit: Int,
104) -> Result(List(sse.Event), sse.Error) {
105  use #(parser, events) <- result.try(
106    list.try_fold(chunks, #(sse.new(limit), []), fn(acc, chunk) {
107      use #(parser, next) <- result.try(sse.feed(acc.0, chunk))
108      Ok(#(parser, list.append(acc.1, next)))
109    }),
110  )
111  use trailing <- result.try(sse.finish(parser))
112  Ok(list.append(events, trailing))
113}
114
115pub fn bom_is_only_stripped_at_stream_start_test() -> Nil {
116  assert decode_chunks(
117      [
118        <<>>,
119        <<0xEF>>,
120        <<>>,
121        <<0xBB>>,
122        <<0xBF>>,
123        <<"data: ":utf8, 0xEF, 0xBB, 0xBF, "x\n\n":utf8>>,
124      ],
125      1024,
126    )
127    == Ok([sse.Event("", "\u{FEFF}x")])
128  assert decode_chunks([<<>>, <<0xEF>>, <<0xBB>>, <<>>], 1024)
129    == Error(sse.InvalidUtf8)
130  assert decode_chunks([<<>>, <<>>], 1024) == Ok([])
131}
132
133pub fn fields_split_only_the_first_colon_and_strip_one_ascii_space_test() -> Nil {
134  let assert Ok(#(_, events)) =
135    sse.feed(sse.new(1024), <<
136      "data:  first: second\ndata:\tthird\ndata\n\n":utf8,
137    >>)
138  assert events == [sse.Event("", " first: second\n\tthird\n")]
139}
140
141pub fn a_combining_mark_after_the_colon_is_field_data_test() -> Nil {
142  let assert Ok(#(_, events)) =
143    sse.feed(sse.new(1024), <<
144      "event:\u{0301}update\ndata:\u{0301}: value\n\n":utf8,
145    >>)
146  assert events == [sse.Event("\u{0301}update", "\u{0301}: value")]
147}