070b602212eeba4693107cd145eae0ec07a9d4db68c4a635676c08707f8fcc3e.tri (3322B)
1 !import base as B 2 !import native_math as N 3 !import http as HTTP 4 !import io_v2 as IO 5 !export recvHeaders 6 !export readBody 7 !export respond 8 !export handle 9 !export handleWith 10 11 -- Reuse pure HTTP grammar/limits. The header cap covers the entire accumulated 12 -- receive buffer, including prefetched body bytes, exactly as in the v1 reader. 13 receiveHeaders client buffer count = IO.bind (IO.readSome client 4096) (reply : case reply of 14 ((t t), (("data", chunk), t)) = let 15 next = B.append buffer chunk 16 size = N.add count (HTTP.httpBodyLength chunk) 17 in case N.lte size HTTP.maxHeaderBytes of 18 (t t) = case B.contains? HTTP.crlfcrlf next of 19 (t t) = IO.pure (B.ok next t) 20 _ = receiveHeaders client next size 21 _ = IO.pure (B.err 431 t) 22 _ = IO.pure (B.err 400 t)) 23 recvHeaders client = receiveHeaders client [] 0 24 25 readState _ (0, (reversed, rest)) = IO.pure (B.ok (B.reverse reversed) rest) 26 readState client (remaining, (reversed, _)) = IO.bind 27 (IO.readSome client (HTTP.recvChunkMax4096 remaining)) (reply : case reply of 28 ((t t), (("data", chunk), t)) = readState client (HTTP.takeBodyBytes chunk remaining reversed) 29 _ = IO.pure (B.err 400 "Unexpected end of request body\n")) 30 readState _ _ = IO.pure HTTP.httpBadRequest 31 readBody client fields initial = case HTTP.contentLength fields of 32 ((t t), (t, _)) = IO.pure (B.ok [] initial) 33 ((t t), ((t count), _)) = readState client (HTTP.takeBodyBytes initial count []) 34 failure = IO.pure failure 35 36 -- The connection's owning scope closes it even on partial write/failure. Do not 37 -- retry uncertain writes or let a cleanup failure turn into a second response. 38 send client bytes = case HTTP.takeBodyBytes bytes 4096 [] of 39 (_, ([], _)) = IO.pure t 40 (_, (reversed, rest)) = IO.require (IO.writeAll client (B.reverse reversed)) (_ : send client rest) 41 _ = IO.fail "Invalid response byte list" 42 respond client method response = send client (HTTP.responseForMethod method response) 43 body router client method target fields ((t t), (bytes, _)) = 44 router client method target fields bytes 45 body _ client method _ _ (t, (status, message)) = respond client method (HTTP.errorResponse status message) 46 body _ client method _ _ _ = respond client method (HTTP.badRequestResponse "Bad Request\n") 47 fields router client method target version raw = case HTTP.parseHeaders raw of 48 ((t t), (headers, initial)) = case HTTP.validateRequest method target version headers of 49 ((t t), _) = IO.bind (readBody client headers initial) (body router client method target headers) 50 (t, (status, message)) = respond client method (HTTP.errorResponse status message) 51 _ = respond client method (HTTP.badRequestResponse "Bad Request\n") 52 _ = respond client "" (HTTP.badRequestResponse "Bad Request\n") 53 handleWith router client ((t t), ((method, (target, version)), raw)) = fields router client method target version raw 54 handleWith _ client _ = respond client "" (HTTP.badRequestResponse "Bad Request\n") 55 56 -- Response callbacks run only after the same request grammar/body checks. They 57 -- may send a bounded native body; framing errors use the ordinary byte response. 58 handle router client parsed = handleWith 59 (socket method target headers bytes : IO.bind 60 (router (HTTP.routerMethod method) target headers bytes) (respond socket method)) 61 client parsed