Current section

34 Versions

Jump to

Compare versions

8 files changed
+694 additions
-698 deletions
  @@ -1,5 +1,5 @@
1 1 name = "gleam_http"
2 - version = "4.2.0"
2 + version = "4.3.0"
3 3 licences = ["Apache-2.0"]
4 4 description = "Types and functions for Gleam HTTP clients and servers"
5 5 gleam = ">= 1.11.0"
  @@ -1,6 +1,6 @@
1 1 {<<"name">>, <<"gleam_http">>}.
2 2 {<<"app">>, <<"gleam_http">>}.
3 - {<<"version">>, <<"4.2.0">>}.
3 + {<<"version">>, <<"4.3.0">>}.
4 4 {<<"description">>, <<"Types and functions for Gleam HTTP clients and servers"/utf8>>}.
5 5 {<<"licenses">>, [<<"Apache-2.0">>]}.
6 6 {<<"build_tools">>, [<<"gleam">>]}.
  @@ -273,14 +273,169 @@ pub fn parse_multipart_headers(
273 273 boundary: String,
274 274 ) -> Result(MultipartHeaders, Nil) {
275 275 let boundary = bit_array.from_string(boundary)
276 - // TODO: rewrite this to use a bit pattern once JavaScript supports
277 - // the `b:binary-size(bsize)` pattern.
278 - let prefix = <<45, 45, boundary:bits>>
279 - case bit_array.slice(data, 0, bit_array.byte_size(prefix)) == Ok(prefix) {
280 - // There is no preamble, parse the headers.
281 - True -> parse_headers_after_prelude(data, boundary)
282 - // There is a preamble, skip it before parsing.
283 - False -> skip_preamble(data, boundary)
276 + let boundary_bytes = bit_array.byte_size(boundary)
277 + do_parse_multipart_headers(data, boundary, boundary_bytes)
278 + }
279 +
280 + fn do_parse_multipart_headers(
281 + data: BitArray,
282 + boundary: BitArray,
283 + boundary_bytes: Int,
284 + ) -> Result(MultipartHeaders, Nil) {
285 + case data {
286 + // The headers start right away with a boundary.
287 + <<"--", found:size(boundary_bytes)-bytes, rest:bits>> if found == boundary ->
288 + case rest {
289 + // Final boundary `--boundary--`.
290 + <<"--", rest:bits>> -> Ok(MultipartHeaders([], remaining: rest))
291 +
292 + // Regular boundary `--boundary`.
293 + <<_, _, _:bits>> -> do_parse_headers(rest)
294 +
295 + // Not enough bytes to make a choice, we have to wait for more.
296 + _ ->
297 + more_please_headers(data, fn(data) {
298 + do_parse_multipart_headers(data, boundary, boundary_bytes)
299 + })
300 + }
301 +
302 + // The headers start with a preamble we need to skip.
303 + _ -> skip_preamble(data, boundary, boundary_bytes)
304 + }
305 + }
306 +
307 + fn skip_preamble(
308 + data: BitArray,
309 + boundary: BitArray,
310 + boundary_bytes: Int,
311 + ) -> Result(MultipartHeaders, Nil) {
312 + case data {
313 + // We might have found the preamble end!
314 + <<"\r\n--", rest:bytes>> -> {
315 + case rest {
316 + // It is indeed the end, we go on parsing the headers that come after
317 + // the boundary.
318 + <<found:size(boundary_bytes)-bytes, rest:bits>> if found == boundary ->
319 + do_parse_headers(rest)
320 +
321 + // This was not the end because `\r\n--` is not followed by the expected
322 + // boundary, so we keep ignoring bytes.
323 + <<_found:size(boundary_bytes)-bytes, _:bits>> ->
324 + skip_preamble(rest, boundary, boundary_bytes)
325 +
326 + // There's not enough bytes to make sure the `\r\n--` is not followed
327 + // by the closing boundary so we have to wait for more to come.
328 + _ ->
329 + more_please_headers(data, skip_preamble(_, boundary, boundary_bytes))
330 + }
331 + }
332 +
333 + // We don't have enough bytes to be sure this is not the end of the preamble
334 + // so we have to wait for more to come.
335 + <<>> | <<"\r">> | <<"\r\n">> | <<"\r\n-">> ->
336 + more_please_headers(data, skip_preamble(_, boundary, boundary_bytes))
337 +
338 + // The byte is surely part of the preamble and can be skipped.
339 + <<_, data:bytes>> -> skip_preamble(data, boundary, boundary_bytes)
340 +
341 + _ -> panic as "unreachable"
342 + }
343 + }
344 +
345 + fn do_parse_headers(data: BitArray) -> Result(MultipartHeaders, Nil) {
346 + case data {
347 + // We've reached the end, there are no headers.
348 + <<"\r\n\r\n", data:bytes>> -> Ok(MultipartHeaders([], remaining: data))
349 +
350 + // Skip the line break after the boundary.
351 + <<"\r\n", data:bytes>> -> parse_header_name(data, [])
352 +
353 + <<"\r">> | <<>> -> more_please_headers(data, do_parse_headers)
354 +
355 + _ -> Error(Nil)
356 + }
357 + }
358 +
359 + fn parse_header_name(
360 + data: BitArray,
361 + headers: List(Header),
362 + ) -> Result(MultipartHeaders, Nil) {
363 + case data {
364 + // We first have to skip all whitespace preceding the name.
365 + <<" ", rest:bits>> | <<"\t", rest:bits>> -> parse_header_name(rest, headers)
366 + <<_, _:bits>> -> parse_header_name_loop(data, headers, <<>>)
367 +
368 + // We don't have enough bits to make a choice and will have to wait for more
369 + // to come.
370 + _ -> more_please_headers(data, parse_header_name(_, headers))
371 + }
372 + }
373 +
374 + fn parse_header_name_loop(data: BitArray, headers: List(Header), name: BitArray) {
375 + case data {
376 + // We've found the end of the header, we can now start parsing its value.
377 + <<":", data:bits>> ->
378 + case bit_array.to_string(name) {
379 + Ok(name) -> parse_header_value(data, headers, name)
380 + Error(Nil) -> Error(Nil)
381 + }
382 +
383 + // Otherwise the character belongs to the header.
384 + <<char, data:bits>> ->
385 + parse_header_name_loop(data, headers, <<name:bits, char>>)
386 +
387 + // We don't have enough bits to make a choice and have to wait for more to
388 + // come.
389 + _ -> more_please_headers(data, parse_header_name_loop(_, headers, name))
390 + }
391 + }
392 +
393 + fn parse_header_value(data: BitArray, headers: List(Header), name: String) {
394 + case data {
395 + // We first have to skip all whitespace preceding the value.
396 + <<" ", rest:bits>> | <<"\t", rest:bits>> ->
397 + parse_header_value(rest, headers, name)
398 +
399 + <<_, _:bits>> -> parse_header_value_loop(data, headers, name, <<>>)
400 +
401 + // We don't have enough bits to make a choice and will have to wait for more
402 + // to come.
403 + _ -> more_please_headers(data, parse_header_value(_, headers, name))
404 + }
405 + }
406 +
407 + fn parse_header_value_loop(
408 + data: BitArray,
409 + headers: List(Header),
410 + name: String,
411 + value: BitArray,
412 + ) -> Result(MultipartHeaders, Nil) {
413 + case data {
414 + // We need at least 4 bytes to check for the end of the headers.
415 + <<>> | <<_>> | <<_, _>> | <<_, _, _>> ->
416 + more_please_headers(data, fn(data) {
417 + parse_header_value_loop(data, headers, name, value)
418 + })
419 +
420 + <<"\r\n\r\n", data:bytes>> -> {
421 + use value <- result.map(bit_array.to_string(value))
422 + let headers = list.reverse([#(string.lowercase(name), value), ..headers])
423 + MultipartHeaders(headers:, remaining: data)
424 + }
425 +
426 + <<"\r\n ", data:bytes>> | <<"\r\n\t", data:bytes>> ->
427 + parse_header_value_loop(data, headers, name, value)
428 +
429 + <<"\r\n", data:bytes>> -> {
430 + use value <- result.try(bit_array.to_string(value))
431 + let headers = [#(string.lowercase(name), value), ..headers]
432 + parse_header_name(data, headers)
433 + }
434 +
435 + <<char, rest:bytes>> ->
436 + parse_header_value_loop(rest, headers, name, <<value:bits, char>>)
437 +
438 + _ -> Error(Nil)
284 439 }
285 440 }
286 441
  @@ -300,237 +455,75 @@ pub fn parse_multipart_body(
300 455 data: BitArray,
301 456 boundary: String,
302 457 ) -> Result(MultipartBody, Nil) {
303 - boundary
304 - |> bit_array.from_string
305 - |> parse_body_with_bit_array(data, _)
458 + let boundary = bit_array.from_string(boundary)
459 + let boundary_bytes = bit_array.byte_size(boundary)
460 + do_parse_multipart_body(data, boundary, boundary_bytes)
306 461 }
307 462
308 - fn parse_body_with_bit_array(
463 + fn do_parse_multipart_body(
309 464 data: BitArray,
310 465 boundary: BitArray,
466 + boundary_bytes: Int,
311 467 ) -> Result(MultipartBody, Nil) {
312 - let bsize = bit_array.byte_size(boundary)
313 - let prefix = bit_array.slice(data, 0, 2 + bsize)
314 - case prefix == Ok(<<45, 45, boundary:bits>>) {
315 - True -> Ok(MultipartBody(<<>>, done: False, remaining: data))
316 - False -> parse_body_loop(data, boundary, <<>>)
468 + case data {
469 + <<"--", found:size(boundary_bytes)-bytes, _:bytes>> if found == boundary ->
470 + Ok(MultipartBody(<<>>, done: False, remaining: data))
471 + _ -> parse_body_loop(data, boundary, boundary_bytes, <<>>)
317 472 }
318 473 }
319 474
320 475 fn parse_body_loop(
321 476 data: BitArray,
322 477 boundary: BitArray,
478 + boundary_bytes: Int,
323 479 body: BitArray,
324 480 ) -> Result(MultipartBody, Nil) {
325 - let dsize = bit_array.byte_size(data)
326 - let bsize = bit_array.byte_size(boundary)
327 - let required = 6 + bsize
328 481 case data {
329 - _ if dsize < required -> {
330 - more_please_body(parse_body_loop(_, boundary, <<>>), body, data)
331 - }
482 + <<>> | <<"\r">> ->
483 + more_please_body(body, data, fn(data) {
484 + parse_body_loop(data, boundary, boundary_bytes, <<>>)
485 + })
332 486
333 - // TODO: flatten this into a single case expression once JavaScript supports
334 - // the `b:binary-size(bsize)` pattern.
335 - //
336 - // \r\n
337 - <<13, 10, data:bytes>> -> {
338 - let desired = <<45, 45, boundary:bits>>
339 - let size = bit_array.byte_size(desired)
340 - let dsize = bit_array.byte_size(data)
341 - let prefix = bit_array.slice(data, 0, size)
342 - let rest = bit_array.slice(data, size, dsize - size)
343 - case prefix == Ok(desired), rest {
344 - // --boundary\r\n
345 - True, Ok(<<13, 10, _:bytes>>) ->
346 - Ok(MultipartBody(body, done: False, remaining: data))
487 + <<"\r\n", rest:bits>> ->
488 + case rest {
489 + // This string match is written as `"\r", "\n"` (instead of just
490 + // `"\r\n"`) to work around this compiler bug:
491 + // https://github.com/gleam-lang/gleam/issues/4993
492 + //
493 + // Even after that is fixed want to keep it like this to make sure the
494 + // http library will work with older gleam versions on the js target.
495 + // So please don't change it!
496 + //
497 + // vvvvvvvvvv This one right here!
498 + <<"--", found:size(boundary_bytes)-bytes, "\r", "\n", _:bits>>
499 + if found == boundary
500 + -> Ok(MultipartBody(body, done: False, remaining: rest))
347 501
348 - // --boundary--
349 - True, Ok(<<45, 45, data:bytes>>) ->
350 - Ok(MultipartBody(body, done: True, remaining: data))
502 + // Same goes for this match as well!
503 + // vvvvvvvvvv This one right here!
504 + <<"--", found:size(boundary_bytes)-bytes, "-", "-", rest:bits>>
505 + if found == boundary
506 + -> Ok(MultipartBody(body, done: True, remaining: rest))
351 507
352 - False, _ -> parse_body_loop(data, boundary, <<body:bits, 13, 10>>)
353 - _, _ -> Error(Nil)
508 + <<_, _, _:size(boundary_bytes)-bytes, _, _, _:bits>> ->
509 + parse_body_loop(rest, boundary, boundary_bytes, <<body:bits, "\r\n">>)
510 +
511 + _ ->
512 + more_please_body(body, data, fn(data) {
513 + parse_body_loop(data, boundary, boundary_bytes, <<>>)
514 + })
354 515 }
355 - }
356 516
357 - <<char, data:bytes>> -> {
358 - parse_body_loop(data, boundary, <<body:bits, char>>)
359 - }
517 + <<char, data:bits>> ->
518 + parse_body_loop(data, boundary, boundary_bytes, <<body:bits, char>>)
360 519
361 520 _ -> panic as "unreachable"
362 521 }
363 522 }
364 523
365 - fn parse_headers_after_prelude(
366 - data: BitArray,
367 - boundary: BitArray,
368 - ) -> Result(MultipartHeaders, Nil) {
369 - let dsize = bit_array.byte_size(data)
370 - let bsize = bit_array.byte_size(boundary)
371 - let required_size = bsize + 4
372 -
373 - // TODO: this could be written as a single case expression if JavaScript had
374 - // support for the `b:binary-size(bsize)` pattern. Rewrite this once the
375 - // compiler support this.
376 -
377 - use <- bool.guard(
378 - when: dsize < required_size,
379 - return: more_please_headers(parse_headers_after_prelude(_, boundary), data),
380 - )
381 -
382 - use prefix <- result.try(bit_array.slice(data, 0, required_size - 2))
383 - use second <- result.try(bit_array.slice(data, 2 + bsize, 2))
384 - let desired = <<45, 45, boundary:bits>>
385 -
386 - use <- bool.guard(prefix != desired, return: Error(Nil))
387 -
388 - case second == <<45, 45>> {
389 - // --boundary--
390 - // The last boundary. Return the epilogue.
391 - True -> {
392 - let rest_size = dsize - required_size
393 - use data <- result.map(bit_array.slice(data, required_size, rest_size))
394 - MultipartHeaders([], remaining: data)
395 - }
396 -
397 - // --boundary
398 - False -> {
399 - let start = required_size - 2
400 - let rest_size = dsize - required_size + 2
401 - use data <- result.try(bit_array.slice(data, start, rest_size))
402 - do_parse_headers(data)
403 - }
404 - }
405 - }
406 -
407 - fn skip_preamble(
408 - data: BitArray,
409 - boundary: BitArray,
410 - ) -> Result(MultipartHeaders, Nil) {
411 - let data_size = bit_array.byte_size(data)
412 - let boundary_size = bit_array.byte_size(boundary)
413 - let required = boundary_size + 4
414 - case data {
415 - _ if data_size < required ->
416 - more_please_headers(skip_preamble(_, boundary), data)
417 -
418 - // TODO: change this to use one non-nested case expression once the compiler
419 - // supports the `b:binary-size(bsize)` pattern on JS.
420 - // \r\n--
421 - <<13, 10, 45, 45, data:bytes>> -> {
422 - case bit_array.slice(data, 0, boundary_size) {
423 - // --boundary
424 - Ok(prefix) if prefix == boundary -> {
425 - let start = boundary_size
426 - let length = bit_array.byte_size(data) - boundary_size
427 - use rest <- result.try(bit_array.slice(data, start, length))
428 - do_parse_headers(rest)
429 - }
430 - Ok(_) -> skip_preamble(data, boundary)
431 - Error(_) -> Error(Nil)
432 - }
433 - }
434 -
435 - <<_, data:bytes>> -> skip_preamble(data, boundary)
436 -
437 - _ -> panic as "unreachable"
438 - }
439 - }
440 -
441 - fn skip_whitespace(data: BitArray) -> BitArray {
442 - case data {
443 - // Space or tab.
444 - <<32, data:bytes>> | <<9, data:bytes>> -> skip_whitespace(data)
445 - _ -> data
446 - }
447 - }
448 -
449 - fn do_parse_headers(data: BitArray) -> Result(MultipartHeaders, Nil) {
450 - case data {
451 - // \r\n\r\n
452 - // We've reached the end, there are no headers.
453 - <<13, 10, 13, 10, data:bytes>> -> Ok(MultipartHeaders([], remaining: data))
454 -
455 - // \r\n
456 - // Skip the line break after the boundary.
457 - <<13, 10, data:bytes>> -> parse_header_name(data, [], <<>>)
458 -
459 - <<13>> | <<>> -> more_please_headers(do_parse_headers, data)
460 -
461 - _ -> Error(Nil)
462 - }
463 - }
464 -
465 - fn parse_header_name(
466 - data: BitArray,
467 - headers: List(Header),
468 - name: BitArray,
469 - ) -> Result(MultipartHeaders, Nil) {
470 - case skip_whitespace(data) {
471 - // :
472 - <<58, data:bytes>> ->
473 - data
474 - |> skip_whitespace
475 - |> parse_header_value(headers, name, <<>>)
476 -
477 - <<char, data:bytes>> ->
478 - parse_header_name(data, headers, <<name:bits, char>>)
479 -
480 - _ -> more_please_headers(parse_header_name(_, headers, name), data)
481 - }
482 - }
483 -
484 - fn parse_header_value(
485 - data: BitArray,
486 - headers: List(Header),
487 - name: BitArray,
488 - value: BitArray,
489 - ) -> Result(MultipartHeaders, Nil) {
490 - let size = bit_array.byte_size(data)
491 - case data {
492 - // We need at least 4 bytes to check for the end of the headers.
493 - _ if size < 4 ->
494 - fn(data) {
495 - data
496 - |> skip_whitespace
497 - |> parse_header_value(headers, name, value)
498 - }
499 - |> more_please_headers(data)
500 -
501 - // \r\n\r\n
502 - <<13, 10, 13, 10, data:bytes>> -> {
503 - use name <- result.try(bit_array.to_string(name))
504 - use value <- result.map(bit_array.to_string(value))
505 - let headers = list.reverse([#(string.lowercase(name), value), ..headers])
506 - MultipartHeaders(headers, data)
507 - }
508 -
509 - // \r\n\s
510 - // \r\n\t
511 - <<13, 10, 32, data:bytes>> | <<13, 10, 9, data:bytes>> ->
512 - parse_header_value(data, headers, name, value)
513 -
514 - // \r\n
515 - <<13, 10, data:bytes>> -> {
516 - use name <- result.try(bit_array.to_string(name))
517 - use value <- result.try(bit_array.to_string(value))
518 - let headers = [#(string.lowercase(name), value), ..headers]
519 - parse_header_name(data, headers, <<>>)
520 - }
521 -
522 - <<char, rest:bytes>> -> {
523 - let value = <<value:bits, char>>
524 - parse_header_value(rest, headers, name, value)
525 - }
526 -
527 - _ -> Error(Nil)
528 - }
529 - }
530 -
531 524 fn more_please_headers(
532 - continuation: fn(BitArray) -> Result(MultipartHeaders, Nil),
533 525 existing: BitArray,
526 + continuation: fn(BitArray) -> Result(MultipartHeaders, Nil),
534 527 ) -> Result(MultipartHeaders, Nil) {
535 528 Ok(
536 529 MoreRequiredForHeaders(fn(more) {
  @@ -540,6 +533,19 @@ fn more_please_headers(
540 533 )
541 534 }
542 535
536 + fn more_please_body(
537 + chunk: BitArray,
538 + existing: BitArray,
539 + continuation: fn(BitArray) -> Result(MultipartBody, Nil),
540 + ) -> Result(MultipartBody, Nil) {
541 + Ok(
542 + MoreRequiredForBody(chunk, fn(more) {
543 + use <- bool.guard(more == <<>>, return: Error(Nil))
544 + continuation(<<existing:bits, more:bits>>)
545 + }),
546 + )
547 + }
548 +
543 549 pub type ContentDisposition {
544 550 ContentDisposition(String, parameters: List(#(String, String)))
545 551 }
  @@ -644,19 +650,6 @@ fn parse_rfc_2045_parameter_unquoted_value(
644 650 }
645 651 }
646 652
647 - fn more_please_body(
648 - continuation: fn(BitArray) -> Result(MultipartBody, Nil),
649 - chunk: BitArray,
650 - existing: BitArray,
651 - ) -> Result(MultipartBody, Nil) {
652 - fn(more) {
653 - use <- bool.guard(more == <<>>, return: Error(Nil))
654 - continuation(<<existing:bits, more:bits>>)
655 - }
656 - |> MoreRequiredForBody(chunk, _)
657 - |> Ok
658 - }
659 -
660 653 /// A HTTP header is a key-value pair. Header keys must be all lowercase
661 654 /// characters.
662 655 pub type Header =
  @@ -1,7 +1,7 @@
1 1 -module(gleam@http).
2 2 -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
3 3 -define(FILEPATH, "src/gleam/http.gleam").
4 - -export([parse_method/1, method_to_string/1, scheme_to_string/1, scheme_from_string/1, parse_content_disposition/1, parse_multipart_body/2, parse_multipart_headers/2]).
4 + -export([parse_method/1, method_to_string/1, scheme_to_string/1, scheme_from_string/1, parse_multipart_body/2, parse_content_disposition/1, parse_multipart_headers/2]).
5 5 -export_type([method/0, scheme/0, multipart_headers/0, multipart_body/0, content_disposition/0]).
6 6
7 7 -if(?OTP_RELEASE >= 27).
  @@ -423,26 +423,12 @@ scheme_from_string(Scheme) ->
423 423 {error, nil}
424 424 end.
425 425
426 - -file("src/gleam/http.gleam", 441).
427 - -spec skip_whitespace(bitstring()) -> bitstring().
428 - skip_whitespace(Data) ->
429 - case Data of
430 - <<32, Data@1/binary>> ->
431 - skip_whitespace(Data@1);
432 -
433 - <<9, Data@1/binary>> ->
434 - skip_whitespace(Data@1);
435 -
436 - _ ->
437 - Data
438 - end.
439 -
440 - -file("src/gleam/http.gleam", 531).
426 + -file("src/gleam/http.gleam", 524).
441 427 -spec more_please_headers(
442 - fun((bitstring()) -> {ok, multipart_headers()} | {error, nil}),
443 - bitstring()
428 + bitstring(),
429 + fun((bitstring()) -> {ok, multipart_headers()} | {error, nil})
444 430 ) -> {ok, multipart_headers()} | {error, nil}.
445 - more_please_headers(Continuation, Existing) ->
431 + more_please_headers(Existing, Continuation) ->
446 432 {ok,
447 433 {more_required_for_headers,
448 434 fun(More) ->
  @@ -455,7 +441,141 @@ more_please_headers(Continuation, Existing) ->
455 441 )
456 442 end}}.
457 443
458 - -file("src/gleam/http.gleam", 612).
444 + -file("src/gleam/http.gleam", 536).
445 + -spec more_please_body(
446 + bitstring(),
447 + bitstring(),
448 + fun((bitstring()) -> {ok, multipart_body()} | {error, nil})
449 + ) -> {ok, multipart_body()} | {error, nil}.
450 + more_please_body(Chunk, Existing, Continuation) ->
451 + {ok,
452 + {more_required_for_body,
453 + Chunk,
454 + fun(More) ->
455 + gleam@bool:guard(
456 + More =:= <<>>,
457 + {error, nil},
458 + fun() ->
459 + Continuation(<<Existing/bitstring, More/bitstring>>)
460 + end
461 + )
462 + end}}.
463 +
464 + -file("src/gleam/http.gleam", 475).
465 + -spec parse_body_loop(bitstring(), bitstring(), integer(), bitstring()) -> {ok,
466 + multipart_body()} |
467 + {error, nil}.
468 + parse_body_loop(Data, Boundary, Boundary_bytes, Body) ->
469 + case Data of
470 + <<>> ->
471 + more_please_body(
472 + Body,
473 + Data,
474 + fun(Data@1) ->
475 + parse_body_loop(Data@1, Boundary, Boundary_bytes, <<>>)
476 + end
477 + );
478 +
479 + <<"\r"/utf8>> ->
480 + more_please_body(
481 + Body,
482 + Data,
483 + fun(Data@1) ->
484 + parse_body_loop(Data@1, Boundary, Boundary_bytes, <<>>)
485 + end
486 + );
487 +
488 + <<"\r\n"/utf8, Rest/bitstring>> ->
489 + case Rest of
490 + <<"--"/utf8,
491 + Found:Boundary_bytes/binary,
492 + "\r"/utf8,
493 + "\n"/utf8,
494 + _/bitstring>> when Found =:= Boundary ->
495 + {ok, {multipart_body, Body, false, Rest}};
496 +
497 + <<"--"/utf8,
498 + Found@1:Boundary_bytes/binary,
499 + "-"/utf8,
500 + "-"/utf8,
501 + Rest@1/bitstring>> when Found@1 =:= Boundary ->
502 + {ok, {multipart_body, Body, true, Rest@1}};
503 +
504 + <<_, _, _:Boundary_bytes/binary, _, _, _/bitstring>> ->
505 + parse_body_loop(
506 + Rest,
507 + Boundary,
508 + Boundary_bytes,
509 + <<Body/bitstring, "\r\n"/utf8>>
510 + );
511 +
512 + _ ->
513 + more_please_body(
514 + Body,
515 + Data,
516 + fun(Data@2) ->
517 + parse_body_loop(
518 + Data@2,
519 + Boundary,
520 + Boundary_bytes,
521 + <<>>
522 + )
523 + end
524 + )
525 + end;
526 +
527 + <<Char, Data@3/bitstring>> ->
528 + parse_body_loop(
529 + Data@3,
530 + Boundary,
531 + Boundary_bytes,
532 + <<Body/bitstring, Char>>
533 + );
534 +
535 + _ ->
536 + erlang:error(#{gleam_error => panic,
537 + message => <<"unreachable"/utf8>>,
538 + file => <<?FILEPATH/utf8>>,
539 + module => <<"gleam/http"/utf8>>,
540 + function => <<"parse_body_loop"/utf8>>,
541 + line => 520})
542 + end.
543 +
544 + -file("src/gleam/http.gleam", 463).
545 + -spec do_parse_multipart_body(bitstring(), bitstring(), integer()) -> {ok,
546 + multipart_body()} |
547 + {error, nil}.
548 + do_parse_multipart_body(Data, Boundary, Boundary_bytes) ->
549 + case Data of
550 + <<"--"/utf8, Found:Boundary_bytes/binary, _/binary>> when Found =:= Boundary ->
551 + {ok, {multipart_body, <<>>, false, Data}};
552 +
553 + _ ->
554 + parse_body_loop(Data, Boundary, Boundary_bytes, <<>>)
555 + end.
556 +
557 + -file("src/gleam/http.gleam", 454).
558 + ?DOC(
559 + " Parse the body for part of a multipart message, as defined in RFC 2045. The\n"
560 + " body is everything until the next boundary. This function is generally to be\n"
561 + " called after calling `parse_multipart_headers` for a given part.\n"
562 + "\n"
563 + " This function will accept input of any size, it is up to the caller to limit\n"
564 + " it if needed.\n"
565 + "\n"
566 + " To enable streaming parsing of multipart messages, this function will return\n"
567 + " a continuation if there is not enough data to fully parse the body, along\n"
568 + " with the data that has been parsed so far. Further information is available\n"
569 + " in the documentation for `MultipartBody`.\n"
570 + ).
571 + -spec parse_multipart_body(bitstring(), binary()) -> {ok, multipart_body()} |
572 + {error, nil}.
573 + parse_multipart_body(Data, Boundary) ->
574 + Boundary@1 = gleam_stdlib:identity(Boundary),
575 + Boundary_bytes = erlang:byte_size(Boundary@1),
576 + do_parse_multipart_body(Data, Boundary@1, Boundary_bytes).
577 +
578 + -file("src/gleam/http.gleam", 618).
459 579 -spec parse_rfc_2045_parameter_quoted_value(binary(), binary(), binary()) -> {ok,
460 580 {{binary(), binary()}, binary()}} |
461 581 {error, nil}.
  @@ -468,18 +588,17 @@ parse_rfc_2045_parameter_quoted_value(Header, Name, Value) ->
468 588 {ok, {{Name, Value}, Rest}};
469 589
470 590 {ok, {<<"\\"/utf8>>, Rest@1}} ->
471 - case gleam_stdlib:string_pop_grapheme(Rest@1) of
472 - {ok, X} ->
473 - {Grapheme, Rest@2} = X,
591 + gleam@result:'try'(
592 + gleam_stdlib:string_pop_grapheme(Rest@1),
593 + fun(_use0) ->
594 + {Grapheme, Rest@2} = _use0,
474 595 parse_rfc_2045_parameter_quoted_value(
475 596 Rest@2,
476 597 Name,
477 598 <<Value/binary, Grapheme/binary>>
478 - );
479 -
480 - {error, E} ->
481 - {error, E}
482 - end;
599 + )
600 + end
601 + );
483 602
484 603 {ok, {Grapheme@1, Rest@3}} ->
485 604 parse_rfc_2045_parameter_quoted_value(
  @@ -489,7 +608,7 @@ parse_rfc_2045_parameter_quoted_value(Header, Name, Value) ->
489 608 )
490 609 end.
491 610
492 - -file("src/gleam/http.gleam", 629).
611 + -file("src/gleam/http.gleam", 635).
493 612 -spec parse_rfc_2045_parameter_unquoted_value(binary(), binary(), binary()) -> {{binary(),
494 613 binary()},
495 614 binary()}.
  @@ -515,7 +634,7 @@ parse_rfc_2045_parameter_unquoted_value(Header, Name, Value) ->
515 634 )
516 635 end.
517 636
518 - -file("src/gleam/http.gleam", 600).
637 + -file("src/gleam/http.gleam", 606).
519 638 -spec parse_rfc_2045_parameter_value(binary(), binary()) -> {ok,
520 639 {{binary(), binary()}, binary()}} |
521 640 {error, nil}.
  @@ -532,14 +651,15 @@ parse_rfc_2045_parameter_value(Header, Name) ->
532 651 parse_rfc_2045_parameter_unquoted_value(Rest@1, Name, Grapheme)}
533 652 end.
534 653
535 - -file("src/gleam/http.gleam", 589).
654 + -file("src/gleam/http.gleam", 595).
536 655 -spec parse_rfc_2045_parameter(binary(), binary()) -> {ok,
537 656 {{binary(), binary()}, binary()}} |
538 657 {error, nil}.
539 658 parse_rfc_2045_parameter(Header, Name) ->
540 - case gleam_stdlib:string_pop_grapheme(Header) of
541 - {ok, X} ->
542 - {Grapheme, Rest} = X,
659 + gleam@result:'try'(
660 + gleam_stdlib:string_pop_grapheme(Header),
661 + fun(_use0) ->
662 + {Grapheme, Rest} = _use0,
543 663 case Grapheme of
544 664 <<"="/utf8>> ->
545 665 parse_rfc_2045_parameter_value(Rest, Name);
  @@ -549,13 +669,11 @@ parse_rfc_2045_parameter(Header, Name) ->
549 669 Rest,
550 670 <<Name/binary, (string:lowercase(Grapheme))/binary>>
551 671 )
552 - end;
672 + end
673 + end
674 + ).
553 675
554 - {error, E} ->
555 - {error, E}
556 - end.
557 -
558 - -file("src/gleam/http.gleam", 571).
676 + -file("src/gleam/http.gleam", 577).
559 677 -spec parse_rfc_2045_parameters(binary(), list({binary(), binary()})) -> {ok,
560 678 list({binary(), binary()})} |
561 679 {error, nil}.
  @@ -575,23 +693,16 @@ parse_rfc_2045_parameters(Header, Parameters) ->
575 693
576 694 {ok, {Grapheme, Rest@1}} ->
577 695 Acc = string:lowercase(Grapheme),
578 - begin
579 - Result = parse_rfc_2045_parameter(Rest@1, Acc),
580 - case Result of
581 - {ok, X} ->
582 - {Parameter, Rest@2} = X,
583 - parse_rfc_2045_parameters(
584 - Rest@2,
585 - [Parameter | Parameters]
586 - );
587 -
588 - {error, E} ->
589 - {error, E}
696 + gleam@result:'try'(
697 + parse_rfc_2045_parameter(Rest@1, Acc),
698 + fun(_use0) ->
699 + {Parameter, Rest@2} = _use0,
700 + parse_rfc_2045_parameters(Rest@2, [Parameter | Parameters])
590 701 end
591 - end
702 + )
592 703 end.
593 704
594 - -file("src/gleam/http.gleam", 553).
705 + -file("src/gleam/http.gleam", 559).
595 706 -spec parse_content_disposition_type(binary(), binary()) -> {ok,
596 707 content_disposition()} |
597 708 {error, nil}.
  @@ -602,33 +713,24 @@ parse_content_disposition_type(Header, Name) ->
602 713
603 714 {ok, {<<" "/utf8>>, Rest}} ->
604 715 Result = parse_rfc_2045_parameters(Rest, []),
605 - case Result of
606 - {ok, X} ->
607 - {ok, {content_disposition, Name, X}};
608 -
609 - {error, E} ->
610 - {error, E}
611 - end;
716 + gleam@result:map(
717 + Result,
718 + fun(Parameters) -> {content_disposition, Name, Parameters} end
719 + );
612 720
613 721 {ok, {<<"\t"/utf8>>, Rest}} ->
614 722 Result = parse_rfc_2045_parameters(Rest, []),
615 - case Result of
616 - {ok, X} ->
617 - {ok, {content_disposition, Name, X}};
618 -
619 - {error, E} ->
620 - {error, E}
621 - end;
723 + gleam@result:map(
724 + Result,
725 + fun(Parameters) -> {content_disposition, Name, Parameters} end
726 + );
622 727
623 728 {ok, {<<";"/utf8>>, Rest}} ->
624 729 Result = parse_rfc_2045_parameters(Rest, []),
625 - case Result of
626 - {ok, X} ->
627 - {ok, {content_disposition, Name, X}};
628 -
629 - {error, E} ->
630 - {error, E}
631 - end;
730 + gleam@result:map(
731 + Result,
732 + fun(Parameters) -> {content_disposition, Name, Parameters} end
733 + );
632 734
633 735 {ok, {Grapheme, Rest@1}} ->
634 736 parse_content_disposition_type(
  @@ -637,340 +739,241 @@ parse_content_disposition_type(Header, Name) ->
637 739 )
638 740 end.
639 741
640 - -file("src/gleam/http.gleam", 547).
742 + -file("src/gleam/http.gleam", 553).
641 743 -spec parse_content_disposition(binary()) -> {ok, content_disposition()} |
642 744 {error, nil}.
643 745 parse_content_disposition(Header) ->
644 746 parse_content_disposition_type(Header, <<""/utf8>>).
645 747
646 - -file("src/gleam/http.gleam", 647).
647 - -spec more_please_body(
648 - fun((bitstring()) -> {ok, multipart_body()} | {error, nil}),
649 - bitstring(),
650 - bitstring()
651 - ) -> {ok, multipart_body()} | {error, nil}.
652 - more_please_body(Continuation, Chunk, Existing) ->
653 - _pipe = fun(More) ->
654 - gleam@bool:guard(
655 - More =:= <<>>,
656 - {error, nil},
657 - fun() -> Continuation(<<Existing/bitstring, More/bitstring>>) end
658 - )
659 - end,
660 - _pipe@1 = {more_required_for_body, Chunk, _pipe},
661 - {ok, _pipe@1}.
662 -
663 - -file("src/gleam/http.gleam", 320).
664 - -spec parse_body_loop(bitstring(), bitstring(), bitstring()) -> {ok,
665 - multipart_body()} |
666 - {error, nil}.
667 - parse_body_loop(Data, Boundary, Body) ->
668 - Dsize = erlang:byte_size(Data),
669 - Bsize = erlang:byte_size(Boundary),
670 - Required = 6 + Bsize,
671 - case Data of
672 - _ when Dsize < Required ->
673 - more_please_body(
674 - fun(_capture) -> parse_body_loop(_capture, Boundary, <<>>) end,
675 - Body,
676 - Data
677 - );
678 -
679 - <<13, 10, Data@1/binary>> ->
680 - Desired = <<45, 45, Boundary/bitstring>>,
681 - Size = erlang:byte_size(Desired),
682 - Dsize@1 = erlang:byte_size(Data@1),
683 - Prefix = gleam_stdlib:bit_array_slice(Data@1, 0, Size),
684 - Rest = gleam_stdlib:bit_array_slice(Data@1, Size, Dsize@1 - Size),
685 - case {Prefix =:= {ok, Desired}, Rest} of
686 - {true, {ok, <<13, 10, _/binary>>}} ->
687 - {ok, {multipart_body, Body, false, Data@1}};
688 -
689 - {true, {ok, <<45, 45, Data@2/binary>>}} ->
690 - {ok, {multipart_body, Body, true, Data@2}};
691 -
692 - {false, _} ->
693 - parse_body_loop(
694 - Data@1,
695 - Boundary,
696 - <<Body/bitstring, 13, 10>>
697 - );
698 -
699 - {_, _} ->
700 - {error, nil}
701 - end;
702 -
703 - <<Char, Data@3/binary>> ->
704 - parse_body_loop(Data@3, Boundary, <<Body/bitstring, Char>>);
705 -
706 - _ ->
707 - erlang:error(#{gleam_error => panic,
708 - message => <<"unreachable"/utf8>>,
709 - file => <<?FILEPATH/utf8>>,
710 - module => <<"gleam/http"/utf8>>,
711 - function => <<"parse_body_loop"/utf8>>,
712 - line => 361})
713 - end.
714 -
715 - -file("src/gleam/http.gleam", 308).
716 - -spec parse_body_with_bit_array(bitstring(), bitstring()) -> {ok,
717 - multipart_body()} |
718 - {error, nil}.
719 - parse_body_with_bit_array(Data, Boundary) ->
720 - Bsize = erlang:byte_size(Boundary),
721 - Prefix = gleam_stdlib:bit_array_slice(Data, 0, 2 + Bsize),
722 - case Prefix =:= {ok, <<45, 45, Boundary/bitstring>>} of
723 - true ->
724 - {ok, {multipart_body, <<>>, false, Data}};
725 -
726 - false ->
727 - parse_body_loop(Data, Boundary, <<>>)
728 - end.
729 -
730 - -file("src/gleam/http.gleam", 299).
731 - ?DOC(
732 - " Parse the body for part of a multipart message, as defined in RFC 2045. The\n"
733 - " body is everything until the next boundary. This function is generally to be\n"
734 - " called after calling `parse_multipart_headers` for a given part.\n"
735 - "\n"
736 - " This function will accept input of any size, it is up to the caller to limit\n"
737 - " it if needed.\n"
738 - "\n"
739 - " To enable streaming parsing of multipart messages, this function will return\n"
740 - " a continuation if there is not enough data to fully parse the body, along\n"
741 - " with the data that has been parsed so far. Further information is available\n"
742 - " in the documentation for `MultipartBody`.\n"
743 - ).
744 - -spec parse_multipart_body(bitstring(), binary()) -> {ok, multipart_body()} |
745 - {error, nil}.
746 - parse_multipart_body(Data, Boundary) ->
747 - _pipe = Boundary,
748 - _pipe@1 = gleam_stdlib:identity(_pipe),
749 - parse_body_with_bit_array(Data, _pipe@1).
750 -
751 - -file("src/gleam/http.gleam", 484).
752 - -spec parse_header_value(
748 + -file("src/gleam/http.gleam", 407).
749 + -spec parse_header_value_loop(
753 750 bitstring(),
754 751 list({binary(), binary()}),
755 - bitstring(),
752 + binary(),
756 753 bitstring()
757 754 ) -> {ok, multipart_headers()} | {error, nil}.
758 - parse_header_value(Data, Headers, Name, Value) ->
759 - Size = erlang:byte_size(Data),
755 + parse_header_value_loop(Data, Headers, Name, Value) ->
760 756 case Data of
761 - _ when Size < 4 ->
762 - _pipe@2 = fun(Data@1) -> _pipe = Data@1,
763 - _pipe@1 = skip_whitespace(_pipe),
764 - parse_header_value(_pipe@1, Headers, Name, Value) end,
765 - more_please_headers(_pipe@2, Data);
757 + <<>> ->
758 + more_please_headers(
759 + Data,
760 + fun(Data@1) ->
761 + parse_header_value_loop(Data@1, Headers, Name, Value)
762 + end
763 + );
766 764
767 - <<13, 10, 13, 10, Data@2/binary>> ->
768 - case gleam@bit_array:to_string(Name) of
769 - {ok, X} ->
770 - Name@1 = X,
771 - case gleam@bit_array:to_string(Value) of
772 - {ok, X@1} ->
773 - {ok,
774 - begin
775 - Headers@1 = lists:reverse(
776 - [{string:lowercase(Name@1), X@1} |
777 - Headers]
778 - ),
779 - {multipart_headers, Headers@1, Data@2}
780 - end};
765 + <<_>> ->
766 + more_please_headers(
767 + Data,
768 + fun(Data@1) ->
769 + parse_header_value_loop(Data@1, Headers, Name, Value)
770 + end
771 + );
781 772
782 - {error, E} ->
783 - {error, E}
784 - end;
773 + <<_, _>> ->
774 + more_please_headers(
775 + Data,
776 + fun(Data@1) ->
777 + parse_header_value_loop(Data@1, Headers, Name, Value)
778 + end
779 + );
785 780
786 - {error, E@1} ->
787 - {error, E@1}
788 - end;
781 + <<_, _, _>> ->
782 + more_please_headers(
783 + Data,
784 + fun(Data@1) ->
785 + parse_header_value_loop(Data@1, Headers, Name, Value)
786 + end
787 + );
789 788
790 - <<13, 10, 32, Data@3/binary>> ->
791 - parse_header_value(Data@3, Headers, Name, Value);
789 + <<"\r\n\r\n"/utf8, Data@2/binary>> ->
790 + gleam@result:map(
791 + gleam@bit_array:to_string(Value),
792 + fun(Value@1) ->
793 + Headers@1 = lists:reverse(
794 + [{string:lowercase(Name), Value@1} | Headers]
795 + ),
796 + {multipart_headers, Headers@1, Data@2}
797 + end
798 + );
792 799
793 - <<13, 10, 9, Data@3/binary>> ->
794 - parse_header_value(Data@3, Headers, Name, Value);
800 + <<"\r\n "/utf8, Data@3/binary>> ->
801 + parse_header_value_loop(Data@3, Headers, Name, Value);
795 802
796 - <<13, 10, Data@4/binary>> ->
797 - case gleam@bit_array:to_string(Name) of
798 - {ok, X@2} ->
799 - Name@2 = X@2,
800 - case gleam@bit_array:to_string(Value) of
801 - {ok, X@3} ->
802 - Headers@2 = [{string:lowercase(Name@2), X@3} |
803 - Headers],
804 - parse_header_name(Data@4, Headers@2, <<>>);
803 + <<"\r\n\t"/utf8, Data@3/binary>> ->
804 + parse_header_value_loop(Data@3, Headers, Name, Value);
805 805
806 - {error, E@2} ->
807 - {error, E@2}
808 - end;
809 -
810 - {error, E@3} ->
811 - {error, E@3}
812 - end;
806 + <<"\r\n"/utf8, Data@4/binary>> ->
807 + gleam@result:'try'(
808 + gleam@bit_array:to_string(Value),
809 + fun(Value@2) ->
810 + Headers@2 = [{string:lowercase(Name), Value@2} | Headers],
811 + parse_header_name(Data@4, Headers@2)
812 + end
813 + );
813 814
814 815 <<Char, Rest/binary>> ->
815 - Value@1 = <<Value/bitstring, Char>>,
816 - parse_header_value(Rest, Headers, Name, Value@1);
816 + parse_header_value_loop(
817 + Rest,
818 + Headers,
819 + Name,
820 + <<Value/bitstring, Char>>
821 + );
817 822
818 823 _ ->
819 824 {error, nil}
820 825 end.
821 826
822 - -file("src/gleam/http.gleam", 465).
823 - -spec parse_header_name(bitstring(), list({binary(), binary()}), bitstring()) -> {ok,
827 + -file("src/gleam/http.gleam", 359).
828 + -spec parse_header_name(bitstring(), list({binary(), binary()})) -> {ok,
824 829 multipart_headers()} |
825 830 {error, nil}.
826 - parse_header_name(Data, Headers, Name) ->
827 - case skip_whitespace(Data) of
828 - <<58, Data@1/binary>> ->
829 - _pipe = Data@1,
830 - _pipe@1 = skip_whitespace(_pipe),
831 - parse_header_value(_pipe@1, Headers, Name, <<>>);
831 + parse_header_name(Data, Headers) ->
832 + case Data of
833 + <<" "/utf8, Rest/bitstring>> ->
834 + parse_header_name(Rest, Headers);
832 835
833 - <<Char, Data@2/binary>> ->
834 - parse_header_name(Data@2, Headers, <<Name/bitstring, Char>>);
836 + <<"\t"/utf8, Rest/bitstring>> ->
837 + parse_header_name(Rest, Headers);
838 +
839 + <<_, _/bitstring>> ->
840 + parse_header_name_loop(Data, Headers, <<>>);
835 841
836 842 _ ->
837 843 more_please_headers(
838 - fun(_capture) -> parse_header_name(_capture, Headers, Name) end,
839 - Data
844 + Data,
845 + fun(_capture) -> parse_header_name(_capture, Headers) end
840 846 )
841 847 end.
842 848
843 - -file("src/gleam/http.gleam", 449).
849 + -file("src/gleam/http.gleam", 374).
850 + -spec parse_header_name_loop(
851 + bitstring(),
852 + list({binary(), binary()}),
853 + bitstring()
854 + ) -> {ok, multipart_headers()} | {error, nil}.
855 + parse_header_name_loop(Data, Headers, Name) ->
856 + case Data of
857 + <<":"/utf8, Data@1/bitstring>> ->
858 + case gleam@bit_array:to_string(Name) of
859 + {ok, Name@1} ->
860 + parse_header_value(Data@1, Headers, Name@1);
861 +
862 + {error, nil} ->
863 + {error, nil}
864 + end;
865 +
866 + <<Char, Data@2/bitstring>> ->
867 + parse_header_name_loop(Data@2, Headers, <<Name/bitstring, Char>>);
868 +
869 + _ ->
870 + more_please_headers(
871 + Data,
872 + fun(_capture) ->
873 + parse_header_name_loop(_capture, Headers, Name)
874 + end
875 + )
876 + end.
877 +
878 + -file("src/gleam/http.gleam", 393).
879 + -spec parse_header_value(bitstring(), list({binary(), binary()}), binary()) -> {ok,
880 + multipart_headers()} |
881 + {error, nil}.
882 + parse_header_value(Data, Headers, Name) ->
883 + case Data of
884 + <<" "/utf8, Rest/bitstring>> ->
885 + parse_header_value(Rest, Headers, Name);
886 +
887 + <<"\t"/utf8, Rest/bitstring>> ->
888 + parse_header_value(Rest, Headers, Name);
889 +
890 + <<_, _/bitstring>> ->
891 + parse_header_value_loop(Data, Headers, Name, <<>>);
892 +
893 + _ ->
894 + more_please_headers(
895 + Data,
896 + fun(_capture) -> parse_header_value(_capture, Headers, Name) end
897 + )
898 + end.
899 +
900 + -file("src/gleam/http.gleam", 345).
844 901 -spec do_parse_headers(bitstring()) -> {ok, multipart_headers()} | {error, nil}.
845 902 do_parse_headers(Data) ->
846 903 case Data of
847 - <<13, 10, 13, 10, Data@1/binary>> ->
904 + <<"\r\n\r\n"/utf8, Data@1/binary>> ->
848 905 {ok, {multipart_headers, [], Data@1}};
849 906
850 - <<13, 10, Data@2/binary>> ->
851 - parse_header_name(Data@2, [], <<>>);
907 + <<"\r\n"/utf8, Data@2/binary>> ->
908 + parse_header_name(Data@2, []);
852 909
853 - <<13>> ->
854 - more_please_headers(fun do_parse_headers/1, Data);
910 + <<"\r"/utf8>> ->
911 + more_please_headers(Data, fun do_parse_headers/1);
855 912
856 913 <<>> ->
857 - more_please_headers(fun do_parse_headers/1, Data);
914 + more_please_headers(Data, fun do_parse_headers/1);
858 915
859 916 _ ->
860 917 {error, nil}
861 918 end.
862 919
863 - -file("src/gleam/http.gleam", 365).
864 - -spec parse_headers_after_prelude(bitstring(), bitstring()) -> {ok,
920 + -file("src/gleam/http.gleam", 307).
921 + -spec skip_preamble(bitstring(), bitstring(), integer()) -> {ok,
865 922 multipart_headers()} |
866 923 {error, nil}.
867 - parse_headers_after_prelude(Data, Boundary) ->
868 - Dsize = erlang:byte_size(Data),
869 - Bsize = erlang:byte_size(Boundary),
870 - Required_size = Bsize + 4,
871 - case Dsize < Required_size of
872 - true ->
873 - more_please_headers(
874 - fun(_capture) ->
875 - parse_headers_after_prelude(_capture, Boundary)
876 - end,
877 - Data
878 - );
879 -
880 - false ->
881 - case gleam_stdlib:bit_array_slice(Data, 0, Required_size - 2) of
882 - {ok, X} ->
883 - Prefix = X,
884 - case gleam_stdlib:bit_array_slice(Data, 2 + Bsize, 2) of
885 - {ok, X@1} ->
886 - Second = X@1,
887 - Desired = <<45, 45, Boundary/bitstring>>,
888 - case Prefix /= Desired of
889 - true ->
890 - {error, nil};
891 -
892 - false ->
893 - case Second =:= <<45, 45>> of
894 - true ->
895 - Rest_size = Dsize - Required_size,
896 - case gleam_stdlib:bit_array_slice(
897 - Data,
898 - Required_size,
899 - Rest_size
900 - ) of
901 - {ok, X@2} ->
902 - {ok,
903 - {multipart_headers,
904 - [],
905 - X@2}};
906 -
907 - {error, E} ->
908 - {error, E}
909 - end;
910 -
911 - false ->
912 - Start = Required_size - 2,
913 - Rest_size@1 = (Dsize - Required_size)
914 - + 2,
915 - case gleam_stdlib:bit_array_slice(
916 - Data,
917 - Start,
918 - Rest_size@1
919 - ) of
920 - {ok, X@3} ->
921 - do_parse_headers(X@3);
922 -
923 - {error, E@1} ->
924 - {error, E@1}
925 - end
926 - end
927 - end;
928 -
929 - {error, E@2} ->
930 - {error, E@2}
931 - end;
932 -
933 - {error, E@3} ->
934 - {error, E@3}
935 - end
936 - end.
937 -
938 - -file("src/gleam/http.gleam", 407).
939 - -spec skip_preamble(bitstring(), bitstring()) -> {ok, multipart_headers()} |
940 - {error, nil}.
941 - skip_preamble(Data, Boundary) ->
942 - Data_size = erlang:byte_size(Data),
943 - Boundary_size = erlang:byte_size(Boundary),
944 - Required = Boundary_size + 4,
924 + skip_preamble(Data, Boundary, Boundary_bytes) ->
945 925 case Data of
946 - _ when Data_size < Required ->
947 - more_please_headers(
948 - fun(_capture) -> skip_preamble(_capture, Boundary) end,
949 - Data
950 - );
926 + <<"\r\n--"/utf8, Rest/binary>> ->
927 + case Rest of
928 + <<Found:Boundary_bytes/binary, Rest@1/bitstring>> when Found =:= Boundary ->
929 + do_parse_headers(Rest@1);
951 930
952 - <<13, 10, 45, 45, Data@1/binary>> ->
953 - case gleam_stdlib:bit_array_slice(Data@1, 0, Boundary_size) of
954 - {ok, Prefix} when Prefix =:= Boundary ->
955 - Start = Boundary_size,
956 - Length = erlang:byte_size(Data@1) - Boundary_size,
957 - case gleam_stdlib:bit_array_slice(Data@1, Start, Length) of
958 - {ok, X} ->
959 - do_parse_headers(X);
931 + <<_:Boundary_bytes/binary, _/bitstring>> ->
932 + skip_preamble(Rest, Boundary, Boundary_bytes);
960 933
961 - {error, E} ->
962 - {error, E}
963 - end;
964 -
965 - {ok, _} ->
966 - skip_preamble(Data@1, Boundary);
967 -
968 - {error, _} ->
969 - {error, nil}
934 + _ ->
935 + more_please_headers(
936 + Data,
937 + fun(_capture) ->
938 + skip_preamble(_capture, Boundary, Boundary_bytes)
939 + end
940 + )
970 941 end;
971 942
972 - <<_, Data@2/binary>> ->
973 - skip_preamble(Data@2, Boundary);
943 + <<>> ->
944 + more_please_headers(
945 + Data,
946 + fun(_capture@1) ->
947 + skip_preamble(_capture@1, Boundary, Boundary_bytes)
948 + end
949 + );
950 +
951 + <<"\r"/utf8>> ->
952 + more_please_headers(
953 + Data,
954 + fun(_capture@1) ->
955 + skip_preamble(_capture@1, Boundary, Boundary_bytes)
956 + end
957 + );
958 +
959 + <<"\r\n"/utf8>> ->
960 + more_please_headers(
961 + Data,
962 + fun(_capture@1) ->
963 + skip_preamble(_capture@1, Boundary, Boundary_bytes)
964 + end
965 + );
966 +
967 + <<"\r\n-"/utf8>> ->
968 + more_please_headers(
969 + Data,
970 + fun(_capture@1) ->
971 + skip_preamble(_capture@1, Boundary, Boundary_bytes)
972 + end
973 + );
974 +
975 + <<_, Data@1/binary>> ->
976 + skip_preamble(Data@1, Boundary, Boundary_bytes);
974 977
975 978 _ ->
976 979 erlang:error(#{gleam_error => panic,
  @@ -978,7 +981,38 @@ skip_preamble(Data, Boundary) ->
978 981 file => <<?FILEPATH/utf8>>,
979 982 module => <<"gleam/http"/utf8>>,
980 983 function => <<"skip_preamble"/utf8>>,
981 - line => 437})
984 + line => 341})
985 + end.
986 +
987 + -file("src/gleam/http.gleam", 280).
988 + -spec do_parse_multipart_headers(bitstring(), bitstring(), integer()) -> {ok,
989 + multipart_headers()} |
990 + {error, nil}.
991 + do_parse_multipart_headers(Data, Boundary, Boundary_bytes) ->
992 + case Data of
993 + <<"--"/utf8, Found:Boundary_bytes/binary, Rest/bitstring>> when Found =:= Boundary ->
994 + case Rest of
995 + <<"--"/utf8, Rest@1/bitstring>> ->
996 + {ok, {multipart_headers, [], Rest@1}};
997 +
998 + <<_, _, _/bitstring>> ->
999 + do_parse_headers(Rest);
1000 +
1001 + _ ->
1002 + more_please_headers(
1003 + Data,
1004 + fun(Data@1) ->
1005 + do_parse_multipart_headers(
1006 + Data@1,
1007 + Boundary,
1008 + Boundary_bytes
1009 + )
1010 + end
1011 + )
1012 + end;
1013 +
1014 + _ ->
1015 + skip_preamble(Data, Boundary, Boundary_bytes)
982 1016 end.
983 1017
984 1018 -file("src/gleam/http.gleam", 271).
  @@ -1000,12 +1034,5 @@ skip_preamble(Data, Boundary) ->
1000 1034 {error, nil}.
1001 1035 parse_multipart_headers(Data, Boundary) ->
1002 1036 Boundary@1 = gleam_stdlib:identity(Boundary),
1003 - Prefix = <<45, 45, Boundary@1/bitstring>>,
1004 - case gleam_stdlib:bit_array_slice(Data, 0, erlang:byte_size(Prefix)) =:= {ok,
1005 - Prefix} of
1006 - true ->
1007 - parse_headers_after_prelude(Data, Boundary@1);
1008 -
1009 - false ->
1010 - skip_preamble(Data, Boundary@1)
1011 - end.
1037 + Boundary_bytes = erlang:byte_size(Boundary@1),
1038 + do_parse_multipart_headers(Data, Boundary@1, Boundary_bytes).
  @@ -12,10 +12,10 @@
12 12 -define(DOC(Str), -compile([])).
13 13 -endif.
14 14
15 - -type request(EHR) :: {request,
15 + -type request(ECH) :: {request,
16 16 gleam@http:method(),
17 17 list({binary(), binary()}),
18 - EHR,
18 + ECH,
19 19 gleam@http:scheme(),
20 20 binary(),
21 21 gleam@option:option(integer()),
  @@ -39,36 +39,33 @@ to_uri(Request) ->
39 39 ?DOC(" Construct a request from a URI.\n").
40 40 -spec from_uri(gleam@uri:uri()) -> {ok, request(binary())} | {error, nil}.
41 41 from_uri(Uri) ->
42 - case begin
43 - _pipe = erlang:element(2, Uri),
44 - _pipe@1 = gleam@option:unwrap(_pipe, <<""/utf8>>),
45 - gleam@http:scheme_from_string(_pipe@1)
46 - end of
47 - {ok, X} ->
48 - Scheme = X,
49 - case begin
50 - _pipe@2 = erlang:element(4, Uri),
51 - gleam@option:to_result(_pipe@2, nil)
52 - end of
53 - {ok, X@1} ->
42 + gleam@result:'try'(
43 + begin
44 + _pipe = erlang:element(2, Uri),
45 + _pipe@1 = gleam@option:unwrap(_pipe, <<""/utf8>>),
46 + gleam@http:scheme_from_string(_pipe@1)
47 + end,
48 + fun(Scheme) ->
49 + gleam@result:'try'(
50 + begin
51 + _pipe@2 = erlang:element(4, Uri),
52 + gleam@option:to_result(_pipe@2, nil)
53 + end,
54 + fun(Host) ->
54 55 Req = {request,
55 56 get,
56 57 [],
57 58 <<""/utf8>>,
58 59 Scheme,
59 - X@1,
60 + Host,
60 61 erlang:element(5, Uri),
61 62 erlang:element(6, Uri),
62 63 erlang:element(7, Uri)},
63 - {ok, Req};
64 -
65 - {error, E} ->
66 - {error, E}
67 - end;
68 -
69 - {error, E@1} ->
70 - {error, E@1}
71 - end.
64 + {ok, Req}
65 + end
66 + )
67 + end
68 + ).
72 69
73 70 -file("src/gleam/http/request.gleam", 75).
74 71 ?DOC(
  @@ -92,7 +89,7 @@ get_header(Request, Key) ->
92 89 " Header keys are always lowercase in `gleam_http`. To use any uppercase\n"
93 90 " letter is invalid.\n"
94 91 ).
95 - -spec set_header(request(EIB), binary(), binary()) -> request(EIB).
92 + -spec set_header(request(ECR), binary(), binary()) -> request(ECR).
96 93 set_header(Request, Key, Value) ->
97 94 Headers = gleam@list:key_set(
98 95 erlang:element(3, Request),
  @@ -119,7 +116,7 @@ set_header(Request, Key, Value) ->
119 116 " Header keys are always lowercase in `gleam_http`. To use any uppercase\n"
120 117 " letter is invalid.\n"
121 118 ).
122 - -spec prepend_header(request(EIE), binary(), binary()) -> request(EIE).
119 + -spec prepend_header(request(ECU), binary(), binary()) -> request(ECU).
123 120 prepend_header(Request, Key, Value) ->
124 121 Headers = [{string:lowercase(Key), Value} | erlang:element(3, Request)],
125 122 {request,
  @@ -134,7 +131,7 @@ prepend_header(Request, Key, Value) ->
134 131
135 132 -file("src/gleam/http/request.gleam", 114).
136 133 ?DOC(" Set the body of the request, overwriting any existing body.\n").
137 - -spec set_body(request(any()), EIJ) -> request(EIJ).
134 + -spec set_body(request(any()), ECZ) -> request(ECZ).
138 135 set_body(Req, Body) ->
139 136 {request,
140 137 erlang:element(2, Req),
  @@ -148,7 +145,7 @@ set_body(Req, Body) ->
148 145
149 146 -file("src/gleam/http/request.gleam", 120).
150 147 ?DOC(" Update the body of a request using a given function.\n").
151 - -spec map(request(EIL), fun((EIL) -> EIN)) -> request(EIN).
148 + -spec map(request(EDB), fun((EDB) -> EDD)) -> request(EDD).
152 149 map(Request, Transform) ->
153 150 _pipe = erlang:element(4, Request),
154 151 _pipe@1 = Transform(_pipe),
  @@ -190,7 +187,7 @@ get_query(Request) ->
190 187 " Set the query of the request.\n"
191 188 " Query params will be percent encoded before being added to the Request.\n"
192 189 ).
193 - -spec set_query(request(EIX), list({binary(), binary()})) -> request(EIX).
190 + -spec set_query(request(EDN), list({binary(), binary()})) -> request(EDN).
194 191 set_query(Req, Query) ->
195 192 Query@1 = begin
196 193 _pipe = gleam@list:map(
  @@ -216,7 +213,7 @@ set_query(Req, Query) ->
216 213
217 214 -file("src/gleam/http/request.gleam", 173).
218 215 ?DOC(" Set the method of the request.\n").
219 - -spec set_method(request(EJB), gleam@http:method()) -> request(EJB).
216 + -spec set_method(request(EDR), gleam@http:method()) -> request(EDR).
220 217 set_method(Req, Method) ->
221 218 {request,
222 219 Method,
  @@ -251,17 +248,11 @@ new() ->
251 248 to(Url) ->
252 249 _pipe = Url,
253 250 _pipe@1 = gleam_stdlib:uri_parse(_pipe),
254 - case _pipe@1 of
255 - {ok, X} ->
256 - from_uri(X);
257 -
258 - {error, E} ->
259 - {error, E}
260 - end.
251 + gleam@result:'try'(_pipe@1, fun from_uri/1).
261 252
262 253 -file("src/gleam/http/request.gleam", 203).
263 254 ?DOC(" Set the scheme (protocol) of the request.\n").
264 - -spec set_scheme(request(EJI), gleam@http:scheme()) -> request(EJI).
255 + -spec set_scheme(request(EDY), gleam@http:scheme()) -> request(EDY).
265 256 set_scheme(Req, Scheme) ->
266 257 {request,
267 258 erlang:element(2, Req),
  @@ -275,7 +266,7 @@ set_scheme(Req, Scheme) ->
275 266
276 267 -file("src/gleam/http/request.gleam", 209).
277 268 ?DOC(" Set the host of the request.\n").
278 - -spec set_host(request(EJL), binary()) -> request(EJL).
269 + -spec set_host(request(EEB), binary()) -> request(EEB).
279 270 set_host(Req, Host) ->
280 271 {request,
281 272 erlang:element(2, Req),
  @@ -289,7 +280,7 @@ set_host(Req, Host) ->
289 280
290 281 -file("src/gleam/http/request.gleam", 215).
291 282 ?DOC(" Set the port of the request.\n").
292 - -spec set_port(request(EJO), integer()) -> request(EJO).
283 + -spec set_port(request(EEE), integer()) -> request(EEE).
293 284 set_port(Req, Port) ->
294 285 {request,
295 286 erlang:element(2, Req),
  @@ -303,7 +294,7 @@ set_port(Req, Port) ->
303 294
304 295 -file("src/gleam/http/request.gleam", 221).
305 296 ?DOC(" Set the path of the request.\n").
306 - -spec set_path(request(EJR), binary()) -> request(EJR).
297 + -spec set_path(request(EEH), binary()) -> request(EEH).
307 298 set_path(Req, Path) ->
308 299 {request,
309 300 erlang:element(2, Req),
  @@ -324,7 +315,7 @@ set_path(Req, Path) ->
324 315 " function cannot guarentee that previous cookies with the same name are\n"
325 316 " replaced.\n"
326 317 ).
327 - -spec set_cookie(request(EJU), binary(), binary()) -> request(EJU).
318 + -spec set_cookie(request(EEK), binary(), binary()) -> request(EEK).
328 319 set_cookie(Req, Name, Value) ->
329 320 {Cookies, Headers} = begin
330 321 _pipe = gleam@list:key_pop(erlang:element(3, Req), <<"cookie"/utf8>>),
  @@ -377,7 +368,7 @@ get_cookies(Req) ->
377 368 " Remove a cookie from the request. If no cookie is found return the request\n"
378 369 " unchanged. This will not remove the cookie from the client.\n"
379 370 ).
380 - -spec remove_cookie(request(EKA), binary()) -> request(EKA).
371 + -spec remove_cookie(request(EEQ), binary()) -> request(EEQ).
381 372 remove_cookie(Req, Name) ->
382 373 case gleam@list:key_pop(erlang:element(3, Req), <<"cookie"/utf8>>) of
383 374 {ok, {Cookies_string, Headers}} ->
Loading more files…