From a5a06f0159b29d2f8aecb83acacb423f96fc9121 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Mon, 2 Dec 2024 14:45:26 -0500 Subject: [PATCH] feat multipart: add helper to parse boundary --- .../tiny_httpd_multipart_form_data.ml | 24 ++++++++ .../tiny_httpd_multipart_form_data.mli | 3 + src/multipart_form/utils_.ml | 59 ++----------------- 3 files changed, 31 insertions(+), 55 deletions(-) diff --git a/src/multipart_form/tiny_httpd_multipart_form_data.ml b/src/multipart_form/tiny_httpd_multipart_form_data.ml index 6529f0e6..315b35e0 100644 --- a/src/multipart_form/tiny_httpd_multipart_form_data.ml +++ b/src/multipart_form/tiny_httpd_multipart_form_data.ml @@ -204,6 +204,30 @@ and parse_headers_rec (self : st) acc : Headers.t = ) ) +let parse_content_type (hs : Tiny_httpd.Headers.t) : _ option = + match Tiny_httpd.Headers.get "content-type" hs with + | None -> None + | Some s -> + (match String.split_on_char ';' s with + | "multipart/form-data" :: tl -> + let boundary = ref None in + List.iter + (fun s -> + match Utils_.split1_on ~c:'=' @@ String.trim s with + | Some ("boundary", "") -> () + | Some ("boundary", s) -> + let s = + if s.[0] = '"' && s.[String.length s - 1] = '"' then + String.sub s 1 (String.length s - 2) + else + s + in + boundary := Some (`boundary s) + | _ -> ()) + tl; + !boundary + | _ -> None) + module Private_ = struct type nonrec chunk = chunk = Delim | Eof | Read of int diff --git a/src/multipart_form/tiny_httpd_multipart_form_data.mli b/src/multipart_form/tiny_httpd_multipart_form_data.mli index 009dbc47..a1747e5d 100644 --- a/src/multipart_form/tiny_httpd_multipart_form_data.mli +++ b/src/multipart_form/tiny_httpd_multipart_form_data.mli @@ -5,6 +5,9 @@ type st val create : ?buf_size:int -> ?out_buf_size:int -> boundary:string -> #Iostream.In.t -> st +val parse_content_type : Tiny_httpd.Headers.t -> [ `boundary of string ] option +(** Parse headers for [content-type: multipart/form-data; boundary=…] *) + type slice = Iostream.Slice.t type event = Part of Tiny_httpd.Headers.t | Read of slice | End_of_input diff --git a/src/multipart_form/utils_.ml b/src/multipart_form/utils_.ml index 6d56e698..a1f993ce 100644 --- a/src/multipart_form/utils_.ml +++ b/src/multipart_form/utils_.ml @@ -1,5 +1,3 @@ -(* module StringMap = Map.Make (String) *) - let string_eq ~a ~a_start ~b ~len : bool = assert (len <= String.length b); if String.length a >= a_start + len then ( @@ -14,56 +12,7 @@ let string_eq ~a ~a_start ~b ~len : bool = ) else false -let ends_with ~suffix ~suffix_length s = - let s_length = String.length s in - s_length >= suffix_length - && string_eq ~a:s ~a_start:(s_length - suffix_length) ~b:suffix - ~len:suffix_length - -let rec first_matching p = function - | [] -> None - | x :: xs -> - (match p x with - | Some y -> Some y - | None -> first_matching p xs) - -let[@inline] option_map f = function - | None -> None - | Some x -> Some (f x) - -let find_common_idx a b = - let rec go i = - if i <= 0 then - None - else if ends_with ~suffix:b ~suffix_length:i a then - Some (String.length a - i) - else - go (i - 1) - in - go (String.length b) - -(* -let[@inline] word = function - | "" -> [] - | w -> [ Some w ] - -let split_on_string ~pattern s = - let pattern_length = String.length pattern in - let rec go start acc = - match Stringext.find_from ~start s ~pattern with - | Some match_start -> - let before = String.sub s start (match_start - start) in - let new_acc = (None :: word before) @ acc in - let new_start = match_start + pattern_length in - go new_start new_acc - | None -> word (Stringext.string_after s start) @ acc - in - List.rev (go 0 []) - -let split_and_process_string ~boundary s = - let f = function - | None -> `Delim - | Some w -> `Word w - in - List.map f @@ split_on_string ~pattern:boundary s - *) +let split1_on ~c s = + match String.index s c with + | exception Not_found -> None + | i -> Some (String.sub s 0 i, String.sub s (i + 1) (String.length s - i - 1))