okami.mom/albedo
git clone https://git.nkp.pet/okami.mom/albedo
fe3b8a3
main
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 { 11let input = << 120xEF , 130xBB , 140xBF , 15": heartbeat\r\nevent: update\r\ndata: hé\r\ndata: world\r\n\r\n" : utf8 , 16>> 17let chunks = bytes ( input ) 18let assert Ok (#( _ , events )) = 19list . try_fold ( chunks , #( sse . new ( 1024 ), []), fn ( acc , chunk ) { 20let #( parser , events ) = acc 21use #( parser , next ) <- result . try ( sse . feed ( parser , chunk )) 22Ok (#( parser , list . append ( events , next ))) 23}) 24assert events == [ sse . Event ( "update" , "hé\nworld" )] 25} 26 27pub fn configured_limits_reject_incomplete_lines_test () -> Nil { 28assert sse . feed ( sse . new ( 7 ), << "data: xx" >>) == Error ( sse . EventTooLarge ) 29assert sse . feed ( sse . new ( 7 ), << ":1234567" >>) == Error ( sse . EventTooLarge ) 30} 31 32pub fn invalid_utf8_is_a_typed_error_test () -> Nil { 33assert 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 { 38assert sse . feed ( sse . new ( 1024 ), << 1 : size ( 1 )>>) == Error ( sse . InvalidUtf8 ) 39let assert Ok (#( parser , [])) = sse . feed ( sse . new ( 1024 ), << "data: " >>) 40assert sse . feed ( parser , << 1 : size ( 1 )>>) == Error ( sse . InvalidUtf8 ) 41} 42 43pub fn flushes_unterminated_event_test () -> Nil { 44let assert Ok (#( parser , [])) = sse . feed ( sse . new ( 1024 ), << "data: [DONE]" >>) 45assert sse . finish ( parser ) == Ok ([ sse . Event ( "" , "[DONE]" )]) 46} 47 48fn bytes ( input : BitArray ) -> List ( BitArray ) { 49case input { 50<< byte , rest : bytes >> -> [<< byte >>, .. bytes ( rest )] 51_ -> [] 52} 53} 54 55pub fn every_two_chunk_partition_preserves_events_test () -> Nil { 56let input = << 570xEF , 580xBB , 590xBF , 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>> 62let expected = [ 63sse . Event ( "update" , " \u{0301}hé\ntwo" ), 64sse . Event ( "" , "" ), 65sse . Event ( "" , "last" ), 66] 67int . range ( 0 , bit_array . byte_size ( input ), with : Nil , run : fn ( _ , offset ) { 68let assert Ok ( first ) = bit_array . slice ( input , 0 , offset ) 69let assert Ok ( second ) = 70bit_array . slice ( input , offset , bit_array . byte_size ( input ) - offset ) 71assert decode_chunks ([ first , <<>>, second ], 1024 ) == Ok ( expected ) 72}) 73assert decode_chunks ( bytes ( input ), 1024 ) == Ok ( expected ) 74} 75 76pub fn large_fragmented_line_is_assembled_without_changing_data_test () -> Nil { 77let payload = string . repeat ( "hé" , 30_000 ) 78let input = bit_array . from_string ( "data: " <> payload <> "\n\n" ) 79assert decode_chunks ( bytes ( input ), 100_000 ) == Ok ([ sse . Event ( "" , payload )]) 80} 81 82pub fn event_budget_counts_unknown_fields_but_excludes_comments_test () -> Nil { 83assert decode_chunks ([<< ": long comment\ndata: x\n\n" >>], 14 ) 84== Ok ([ sse . Event ( "" , "x" )]) 85assert 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 { 90let assert Error ( sse . Malformed ( _ )) = 91decode_chunks ([<< "event: bad" : utf8 , 0 , "\ndata: x\n\n" : utf8 >>], 1024 ) 92assert decode_chunks ([<< "data: " : utf8 , 0xC3 >>, << "\n\n" >>], 1024 ) 93== Error ( sse . InvalidUtf8 ) 94} 95 96pub fn trailing_cr_and_exact_line_limit_test () -> Nil { 97assert decode_chunks ([<< "data: x\r" >>], 7 ) == Ok ([ sse . Event ( "" , "x" )]) 98assert decode_chunks ([<< "data: x" >>, << "x" >>], 7 ) == Error ( sse . EventTooLarge ) 99} 100 101fn decode_chunks ( 102chunks : List ( BitArray ), 103limit : Int , 104) -> Result ( List ( sse . Event ), sse . Error ) { 105use #( parser , events ) <- result . try ( 106list . try_fold ( chunks , #( sse . new ( limit ), []), fn ( acc , chunk ) { 107use #( parser , next ) <- result . try ( sse . feed ( acc . 0 , chunk )) 108Ok (#( parser , list . append ( acc . 1 , next ))) 109}), 110) 111use trailing <- result . try ( sse . finish ( parser )) 112Ok ( list . append ( events , trailing )) 113} 114 115pub fn bom_is_only_stripped_at_stream_start_test () -> Nil { 116assert decode_chunks ( 117[ 118<<>>, 119<< 0xEF >>, 120<<>>, 121<< 0xBB >>, 122<< 0xBF >>, 123<< "data: " : utf8 , 0xEF , 0xBB , 0xBF , "x\n\n" : utf8 >>, 124], 1251024 , 126) 127== Ok ([ sse . Event ( "" , "\u{FEFF}x" )]) 128assert decode_chunks ([<<>>, << 0xEF >>, << 0xBB >>, <<>>], 1024 ) 129== Error ( sse . InvalidUtf8 ) 130assert decode_chunks ([<<>>, <<>>], 1024 ) == Ok ([]) 131} 132 133pub fn fields_split_only_the_first_colon_and_strip_one_ascii_space_test () -> Nil { 134let assert Ok (#( _ , events )) = 135sse . feed ( sse . new ( 1024 ), << 136"data: first: second\ndata:\tthird\ndata\n\n" : utf8 , 137>>) 138assert events == [ sse . Event ( "" , " first: second\n\tthird\n" )] 139} 140 141pub fn a_combining_mark_after_the_colon_is_field_data_test () -> Nil { 142let assert Ok (#( _ , events )) = 143sse . feed ( sse . new ( 1024 ), << 144"event:\u{0301}update\ndata:\u{0301}: value\n\n" : utf8 , 145>>) 146assert events == [ sse . Event ( "\u{0301}update" , "\u{0301}: value" )] 147}