package tiny_httpd
Minimal HTTP server using threads
Install
Dune Dependency
Authors
Maintainers
Sources
tiny_httpd-0.19.tbz
sha256=55b6eed7d2e41b9b2a282b344e01ca010abdba66cbbbbe5adab18303b9ff8360
sha512=98c599627b9cd98f641d389e022f9ddc025bfba838793267904b94a01b0b40b3b4cfd8f5a8d8214aaa947958e45cecbe1df751692a461f929802714d7af50e06
doc/src/tiny_httpd.multipart-form-data/tiny_httpd_multipart_form_data.ml.html
Source file tiny_httpd_multipart_form_data.ml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250
(* ported from https://github.com/cryptosense/multipart-form-data . *) open Tiny_httpd module Slice = Iostream.Slice module Content_disposition = Content_disposition let spf = Printf.sprintf type buf = { bs: bytes; mutable len: int } let shift_left_ (self : buf) n = if n = self.len then self.len <- 0 else ( assert (n < self.len); Bytes.blit self.bs n self.bs 0 (self.len - n); self.len <- self.len - n ) let[@inline] buf_full (self : buf) : bool = self.len >= Bytes.length self.bs type slice = Iostream.Slice.t type event = Part of Tiny_httpd.Headers.t | Read of slice | End_of_input type out_state = Begin | Inside_part | Eof type st = { boundary: string; ic: Iostream.In.t; buf: buf; (** Used to split on the boundary *) mutable first: bool; (** Are we parsing the first boundary? *) mutable eof_split: bool; buf_out: buf; (** Used to return output slices *) mutable st_out: out_state; } let create ?(buf_size = 64 * 1024) ?(out_buf_size = 8 * 1024) ~boundary ic : st = let ic = (ic : #Iostream.In.t :> Iostream.In.t) in { boundary; first = true; ic; buf = { bs = Bytes.create buf_size; len = 0 }; eof_split = false; buf_out = { bs = Bytes.create out_buf_size; len = 0 }; st_out = Begin; } type chunk = Delim | Eof | Read of int let[@inline] prefix_size_ (self : st) : int = if self.first then 2 else 4 let[@inline] min_len_ (self : st) : int = prefix_size_ self + String.length self.boundary exception Found_boundary of int let rec read_chunk_ (self : st) buf i_buf len : chunk = if self.eof_split then Eof else if self.buf.len < min_len_ self then ( (* try to refill buffer *) let n = Iostream.In.input self.ic self.buf.bs self.buf.len (Bytes.length self.buf.bs - self.buf.len) in if n = 0 && self.buf.len = 0 then ( self.eof_split <- true; Eof ) else if n = 0 then ( let n_read = min len self.buf.len in Bytes.blit self.buf.bs 0 buf i_buf n_read; shift_left_ self.buf n_read; Read n_read ) else ( self.buf.len <- self.buf.len + n; read_chunk_ self buf i_buf len ) ) else ( try let i = ref 0 in let end_pos = min len self.buf.len - prefix_size_ self - String.length self.boundary in while !i <= end_pos do if self.first && Bytes.unsafe_get self.buf.bs !i = '-' && Bytes.unsafe_get self.buf.bs (!i + 1) = '-' && Utils_.string_eq ~a:(Bytes.unsafe_to_string self.buf.bs) ~a_start:(!i + 2) ~b:self.boundary ~len:(String.length self.boundary) || (not self.first) && Bytes.unsafe_get self.buf.bs !i = '\r' && Bytes.unsafe_get self.buf.bs (!i + 1) = '\n' && Bytes.unsafe_get self.buf.bs (!i + 2) = '-' && Bytes.unsafe_get self.buf.bs (!i + 3) = '-' && Utils_.string_eq ~a:(Bytes.unsafe_to_string self.buf.bs) ~a_start:(!i + 4) ~b:self.boundary ~len:(String.length self.boundary) then raise_notrace (Found_boundary !i); incr i done; let n_read = min !i len in Bytes.blit self.buf.bs 0 buf i_buf n_read; shift_left_ self.buf n_read; Read n_read with | Found_boundary 0 -> shift_left_ self.buf (prefix_size_ self + String.length self.boundary); self.first <- false; Delim | Found_boundary n -> let n_read = min n len in Bytes.blit self.buf.bs 0 buf i_buf n_read; shift_left_ self.buf n_read; Read n_read ) exception Found of int (** Find \r\n *) let find_crlf_exn (buf : buf) : int = try for i = 0 to buf.len - 2 do if Bytes.unsafe_get buf.bs i = '\r' && Bytes.unsafe_get buf.bs (i + 1) = '\n' then raise_notrace (Found i) done; raise Not_found with Found i -> i let[@inline] read_to_buf_out_ (self : st) = assert (not (buf_full self.buf_out)); read_chunk_ self self.buf_out.bs self.buf_out.len (Bytes.length self.buf_out.bs - self.buf_out.len) let read_data_or_fail_ (self : st) : unit = match read_to_buf_out_ self with | Delim -> failwith "multipart: unexpected boundary while parsing headers" | Eof -> failwith "multipart: unexpected EOF while parsing headers" | Read n -> self.buf_out.len <- self.buf_out.len + n let rec next (self : st) : event = match self.st_out with | Eof -> End_of_input | Inside_part when self.buf_out.len > 0 -> (* there's data to return *) let sl = { Slice.bytes = self.buf_out.bs; off = 0; len = self.buf_out.len } in self.buf_out.len <- 0; Read sl | Inside_part -> (* refill or reach boundary *) (match read_to_buf_out_ self with | Eof -> self.st_out <- Eof; End_of_input | Delim -> parse_after_boundary self | Read n -> self.buf_out.len <- n; next self) | Begin -> (match read_to_buf_out_ self with | Delim -> parse_after_boundary self | Eof -> self.st_out <- Eof; End_of_input | Read _ -> failwith "multipart: expected boundary, got data") and parse_after_boundary (self : st) : event = while self.buf_out.len < 2 do read_data_or_fail_ self done; let after_boundary = Bytes.sub_string self.buf_out.bs 0 2 in shift_left_ self.buf_out 2; match after_boundary with | "--" -> self.st_out <- Eof; End_of_input | "\r\n" -> let headers = parse_headers_rec self [] in self.st_out <- Inside_part; Part headers | s -> failwith (spf "multipart: expect '--' or '\r\n' after boundary, got %S" s) and parse_headers_rec (self : st) acc : Headers.t = if self.buf_out.len = 0 then ( read_data_or_fail_ self; parse_headers_rec self acc ) else ( match find_crlf_exn self.buf_out with | exception Not_found -> if buf_full self.buf_out then failwith "multipart: header line is too long" else ( read_data_or_fail_ self; parse_headers_rec self acc ) | i -> let line = Bytes.sub_string self.buf_out.bs 0 i in shift_left_ self.buf_out (i + 2); if line = "" then List.rev acc else ( match Tiny_httpd.Headers.parse_line_ line with | Ok (k, v) -> parse_headers_rec self ((String.lowercase_ascii k, v) :: acc) | Error msg -> failwith (spf "multipart: failed to parser header: %s\nline: %S" msg line) ) ) 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 = Utils_.remove_quotes s in boundary := Some (`boundary s) | _ -> ()) tl; !boundary | _ -> None) module Private_ = struct type nonrec chunk = chunk = Delim | Eof | Read of int let read_chunk_ = read_chunk_ end
sectionYPositions = computeSectionYPositions($el), 10)"
x-init="setTimeout(() => sectionYPositions = computeSectionYPositions($el), 10)"
>