package httpcats
sectionYPositions = computeSectionYPositions($el), 10)"
x-init="setTimeout(() => sectionYPositions = computeSectionYPositions($el), 10)"
>
A simple HTTP client / server using h1, h2, and miou
Install
dune-project
Dependency
Authors
Maintainers
Sources
httpcats-0.3.2.tbz
sha256=9f4227667fbb8da0db7f759a30009d8770cf31f91f7b09a471740055f47cd6b8
sha512=9119d5664e7c318f92e05980bf9f758421e1d9e7d6ae486e730e7ddb3f3a375bf33c468cca4287f02ebee4dc68f125f18c2aa10a7399f74fa273126571488623
doc/src/httpcats.runtime/flow.ml.html
Source file flow.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 121exception Closed_by_peer (* NOTE(dinosaure): it may happen that it is impossible to [write] to a peer. The standard error is [EPIPE] as well as a [SIGPIPE] signal that we ignore (at the application level). The user must transform this error by raising the [Closed_by_peer] exception. In this way, the "Runtime" is informed that the connection has been closed. *) (* NOTE(dinosaure): the HTTP state-machines (and [Faraday]) speak {!type:Bstr.t}, so this is what we require from a flow too. A stack which is able to fill and consume a bigstring directly then needs {b no} intermediate buffer at all: what the kernel (or [utcp]) writes is what the parser reads. A stack which can only speak [bytes]/[string] (typically [ocaml-tls], whose plaintext lives in a [string]) implements {!module-type:BYTES} instead and is adapted with {!module:Of_bytes}, which owns the scratch buffer needed to bridge the two. The buffer then belongs to the flow that actually needs it rather than to the [Runtime]. *) module type S = sig type t val read : t -> Bstr.t -> off:int -> len:int -> int (** [read flow bstr ~off ~len] reads at most [len] bytes from [flow] into [bstr] at [off] and returns how many bytes were actually read. [0] means end-of-input. *) val writev : t -> Bstr.t Faraday.iovec list -> unit (** [writev flow iovecs] writes all the given [iovecs] to [flow]. {b NOTE}: the buffers of [iovecs] belong to the caller and are reused as soon as [writev] returns. An implementation which defers the actual transmission (such as [utcp], which keeps the segment in its own state) must take a copy. @raise Closed_by_peer if the peer closed the connection on its side. *) val close : t -> unit val shutdown : t -> [ `read | `write | `read_write ] -> unit end (** A flow which can only speak [bytes]/[string]. Use {!module:Of_bytes} to obtain an {!module-type:S} out of it. *) module type BYTES = sig type t val read : t -> ?off:int -> ?len:int -> bytes -> int val write : t -> ?off:int -> ?len:int -> string -> unit val close : t -> unit val shutdown : t -> [ `read | `write | `read_write ] -> unit end let default_chunk_size = 0x10000 (* NOTE(dinosaure): the largest amount we ask the underlying flow for on a single [read], and the largest slice of a [Faraday] iovec we [write] in one go. Such a buffer lands in the major heap (a [Bytes.create] stays in the minor heap only up to 2047 bytes on a 64-bit machine: [Max_young_wosize] is 256 words, so [Bytes.create 2047] costs 257 minor words and [Bytes.create 2048] costs none), which is what we want: it is allocated once per connection and lives as long as it does, so we would rather not have it travel through the minor heap at all. HISTORY(dinosaure): this constant lived in the [Runtime] as [(Sys.word_size / 8 * 256) - 1] - precisely that 2047 bound - because a scratch buffer was then allocated on *every* [read], so keeping it young mattered. It later became [16384 - 1], which kept the [- 1] although the buffers had become per-connection and the bound meaningless. Down here an off-by-one below a power of two is in fact harmful: a TLS record carries at most [16384] bytes of plaintext, so asking for [16383] splits a full record into a [16383] read followed by a [1] byte one. *) (** The result of {!module:Of_bytes}: a bigstring flow which owns the scratch buffer needed to talk to the underlying [bytes]/[string] one. *) module type CHANNEL = sig include S type flow val make : ?chunk_size:int -> flow -> t val prj : t -> flow end module Of_bytes (Flow : BYTES) : CHANNEL with type flow = Flow.t = struct type flow = Flow.t type t = { flow: Flow.t; mutable rd: bytes; mutable wr: bytes; chunk: int } (* NOTE(dinosaure): the scratch buffers grow on demand (up to [chunk]) rather than being allocated at [chunk] straight away. A connection which only exchanges small messages keeps small buffers, while a connection streaming a large body converges to [chunk] after a couple of allocations. *) let make ?(chunk_size = default_chunk_size) flow = { flow; rd= Bytes.empty; wr= Bytes.empty; chunk= chunk_size } let prj { flow; _ } = flow let[@inline] reserve buf len = if Bytes.length buf >= len then buf else Bytes.create len let read t bstr ~off:dst_off ~len = let len = Int.min len t.chunk in t.rd <- reserve t.rd len; let len' = Flow.read t.flow t.rd ~off:0 ~len in Bstr.blit_from_bytes t.rd ~src_off:0 bstr ~dst_off ~len:len'; len' let writev t bstrs = let fn { Faraday.buffer; off; len } = let rec go src_off len = if len > 0 then begin let n = Int.min len t.chunk in t.wr <- reserve t.wr n; Bstr.blit_to_bytes buffer ~src_off t.wr ~dst_off:0 ~len:n; Flow.write t.flow ~off:0 ~len:n (Bytes.unsafe_to_string t.wr); go (src_off + n) (len - n) end in go off len in List.iter fn bstrs let close t = Flow.close t.flow let shutdown t cmd = Flow.shutdown t.flow cmd end
sectionYPositions = computeSectionYPositions($el), 10)"
x-init="setTimeout(() => sectionYPositions = computeSectionYPositions($el), 10)"
>