tricu

An interpreted language for exploring Tree Calculus
Log | Files | Refs | README | LICENSE

0e9b65a67a02911e0fc4e6e4fed4275207d60af7ec236bae97a59b16d86df066.tri (23863B)


      1 !import prelude *
      2 !import io *
      3 !import patterns *
      4 !import socket *
      5 !import parser *
      6 !import native_math as Nat
      7 !export *
      8 
      9 -- ---------------------------------------------------------------------------
     10 --  Constants
     11 -- ---------------------------------------------------------------------------
     12 
     13 maxHeaderBytes = 65536
     14 maxBodyBytes = 1048576
     15 maxUriBytes = 8192
     16 
     17 crlf = pair 13 (pair 10 t)
     18 crlfcrlf = pair 13 (pair 10 (pair 13 (pair 10 t)))
     19 
     20 -- ---------------------------------------------------------------------------
     21 --  Response construction
     22 -- ---------------------------------------------------------------------------
     23 
     24 statusPhrases =
     25   [(pair 200 "OK")
     26    (pair 201 "Created")
     27    (pair 204 "No Content")
     28    (pair 400 "Bad Request")
     29    (pair 404 "Not Found")
     30    (pair 405 "Method Not Allowed")
     31    (pair 431 "Request Header Fields Too Large")
     32    (pair 501 "Not Implemented")
     33    (pair 505 "HTTP Version Not Supported")]
     34 
     35 lookupStatusPhrase_ self code phrases =
     36   lazyList
     37     (_ : "Internal Server Error")
     38     (h r :
     39       lazyBool
     40         (_ : snd h)
     41         (_ : self code r)
     42         (equal? code (fst h)))
     43     phrases
     44 
     45 statusPhrase = (code :
     46   y lookupStatusPhrase_ code statusPhrases)
     47 
     48 statusLine = (code phrase :
     49   append "HTTP/1.1 " (append (showNumber code) (append " " (append phrase "\r\n"))))
     50 
     51 headerLine = (key value :
     52   append key (append ": " (append value "\r\n")))
     53 
     54 buildResponse = (status headers body :
     55   append
     56     (statusLine status (statusPhrase status))
     57     (append
     58       (foldl (acc h : append acc (headerLine (fst h) (snd h))) "" headers)
     59       (append "\r\n" body)))
     60 
     61 response = (status contentType body :
     62   buildResponse status
     63     [(pair "Content-Type" contentType)
     64      (pair "Content-Length" (showNumber (length body)))
     65      (pair "Connection" "close")]
     66     body)
     67 
     68 emptyResponse = (status :
     69   buildResponse status
     70     [(pair "Content-Length" "0")
     71      (pair "Connection" "close")]
     72     "")
     73 
     74 okResponse = (body :
     75   response 200 "text/plain; charset=utf-8" body)
     76 
     77 textResponse = (body :
     78   response 200 "text/plain; charset=utf-8" body)
     79 
     80 jsonResponse = (body :
     81   response 200 "application/json" body)
     82 
     83 htmlResponse = (body :
     84   response 200 "text/html; charset=utf-8" body)
     85 
     86 createdResponse = (body :
     87   response 201 "text/plain; charset=utf-8" body)
     88 
     89 notFoundResponse = (
     90   response 404 "text/plain; charset=utf-8" "Not found\n")
     91 
     92 badRequestResponse = (msg :
     93   response 400 "text/plain; charset=utf-8" msg)
     94 
     95 errorResponse = (status msg :
     96   response status "text/plain; charset=utf-8" msg)
     97 
     98 headerEndState state h =
     99   lazyBool
    100     (_ :
    101       lazyBool
    102         (_ : 3)
    103         (_ : 1)
    104         (equal? state 2))
    105     (_ :
    106       lazyBool
    107         (_ :
    108           lazyBool
    109             (_ : 4)
    110             (_ : 2)
    111             (equal? state 3))
    112         (_ : 0)
    113         (boolAnd?
    114           (equal? h 10)
    115           (boolOr? (equal? state 1) (equal? state 3))))
    116     (equal? h 13)
    117 
    118 headersOnly_ self bs state acc =
    119   lazyList
    120     (_ : reverse acc)
    121     (h r :
    122       let nextAcc = pair h acc in
    123       let nextState = headerEndState state h in
    124       lazyBool
    125         (_ : reverse nextAcc)
    126         (_ : self r nextState nextAcc)
    127         (equal? nextState 4))
    128     bs
    129 
    130 headersOnly = (response :
    131   y headersOnly_ response 0 t)
    132 
    133 responseForMethod = (method resp :
    134   lazyBool
    135     (_ : headersOnly resp)
    136     (_ : resp)
    137     (equal? method "HEAD"))
    138 
    139 -- ---------------------------------------------------------------------------
    140 --  Header receive / framing
    141 -- ---------------------------------------------------------------------------
    142 
    143 recvUntilMax_ = (y (self sock pattern maxBytes acc accLen :
    144   onResult_ (recv sock 4096)
    145     (err :
    146       pure (err 400 acc))
    147     (chunk :
    148       lazyBool
    149         (_ : pure (err 400 acc))
    150         (_ :
    151           let chunkLen = length chunk in
    152           let nextLen = add accLen chunkLen in
    153           let next = append acc chunk in
    154           lazyBool
    155             (_ :
    156               lazyBool
    157                 (_ : pure (ok next t))
    158                 (_ : self sock pattern maxBytes next nextLen)
    159                 (contains? pattern next))
    160             (_ : pure (err 431 next))
    161             (lte? nextLen maxBytes))
    162         (emptyList? chunk))))
    163 
    164 recvUntilMax = (sock pattern maxBytes :
    165   recvUntilMax_ sock pattern maxBytes t 0)
    166 
    167 recvUntil = (sock pattern :
    168   recvUntilMax sock pattern maxHeaderBytes)
    169 
    170 recvHeaders = (sock :
    171   recvUntilMax sock crlfcrlf maxHeaderBytes)
    172 
    173 -- ---------------------------------------------------------------------------
    174 --  Request line parsing
    175 -- ---------------------------------------------------------------------------
    176 
    177 -- HTTP/1.1 lines require CRLF. A bare CR/LF is an error, never a byte to
    178 -- discard. Failure retains the offending suffix and prior consumption; EOF
    179 -- cannot masquerade as a successfully parsed empty line.
    180 httpLineError code found rest consumed = (parserReplyFail
    181   (parserDiagnostic code "CRLF" found t ["http-line"])
    182   rest consumed false)
    183 
    184 httpScanLine [13 10 | rest] reversed = parserReplyOk (reverse reversed) rest true false
    185 httpScanLine input{[13 | _]} reversed = httpLineError 2 13 input (not? (emptyList? reversed))
    186 httpScanLine input{[10 | _]} reversed = httpLineError 2 10 input (not? (emptyList? reversed))
    187 httpScanLine [byte | rest] reversed = httpScanLine rest (pair byte reversed)
    188 httpScanLine [] reversed = httpLineError 1 t [] (not? (emptyList? reversed))
    189 httpScanLine input reversed = httpLineError 2 input input (not? (emptyList? reversed))
    190 
    191 httpLineReply input = httpScanLine input []
    192 httpBadRequest = err 400 "Bad Request\n"
    193 
    194 -- Unlike the old pair-only helper, this Result distinguishes a blank CRLF
    195 -- line from truncated/malformed input. Both callers propagate the failure.
    196 readLineBytes input = (parserMatchReply
    197   (_ _ _ _ : httpBadRequest)
    198   (line rest _ _ : ok line rest)
    199   (httpLineReply input))
    200 
    201 -- The finite request line is already framed by readLineBytes. Deliberately
    202 -- retain the historical three-fields grammar: empty fields are permitted,
    203 -- but a third separating space is an error. The parser reply exposes byte
    204 -- consumption to new clients, while parseThreeWords preserves HTTP's public
    205 -- Result/400 boundary and the original "Bad Request" remainder.
    206 httpNonSpace? byte = not? (equal? byte 32)
    207 
    208 httpThreeWordsReply =
    209   parserContext "request-line"
    210     (parserBind (parserTakeWhile httpNonSpace?) (method :
    211       parserThen (parserExpect 32)
    212         (parserBind (parserTakeWhile httpNonSpace?) (target :
    213           parserThen (parserExpect 32)
    214             (parserBind (parserTakeWhile httpNonSpace?) (version :
    215               parserMap (_ : pair method (pair target version))
    216                 parserEof))))))
    217 
    218 parseThreeWords = (bs :
    219   parserMatchReply
    220     (diagnostic rest consumed committed : err 400 "Bad Request\n")
    221     (words rest consumed committed : ok words rest)
    222     (httpThreeWordsReply bs))
    223 
    224 parseRequestLine input = (let
    225   parseLine ((t t), (line, rest)) = (lazyResult
    226     (_ _ : httpBadRequest)
    227     (request _ : ok request rest)
    228     (parseThreeWords line))
    229   parseLine _ = httpBadRequest
    230   in parseLine (readLineBytes input))
    231 
    232 -- ---------------------------------------------------------------------------
    233 --  Header parsing
    234 -- ---------------------------------------------------------------------------
    235 
    236 
    237 boolOr? = (a b :
    238   matchBool true b a)
    239 
    240 boolAnd? = (a b :
    241   matchBool b false a)
    242 
    243 -- Preserve the old bit predicate's truthiness, even for malformed bit trees.
    244 httpAsciiBit bit = matchBool true false (bit1? bit)
    245 
    246 -- Uppercase ASCII is 65..90. For byte inputs the normalized code below is
    247 -- the input itself once the high-bit checks pass. Keep original low-bit trees
    248 -- in the result: the historical helper also accepts some malformed bit trees,
    249 -- and this readability rewrite does not change that policy.
    250 toLowerAsciiByte byte{[b0 b1 b2 b3 b4 b5 b6 | rest]} = (let
    251   code = [(httpAsciiBit b0) (httpAsciiBit b1) (httpAsciiBit b2)
    252           (httpAsciiBit b3) (httpAsciiBit b4) false true]
    253   lower ^true = [b0 b1 b2 b3 b4 true true]
    254   lower _ = byte
    255   in lower (and? (isZero? rest)
    256     (and? (bit1? b6) (and? (bit0? b5)
    257       (Nat.inRange code 65 90)))))
    258 toLowerAsciiByte byte = byte
    259 
    260 -- RFC 9110 token names, OWS, and field-value bytes. obs-text (128..255)
    261 -- stays opaque; only the ASCII header name is lowercased. Reject obs-fold.
    262 httpTokenByte? byte = (or? (Nat.letter byte)
    263   (or? (Nat.digit byte) (any? (equal? byte) "!#$%&'*+-.^_`|~")))
    264 httpOws? byte = or? (equal? byte 32) (equal? byte 9)
    265 httpFieldByte? byte = (or? (equal? byte 9)
    266   (or? (Nat.printable byte) (Nat.inRange byte 128 255)))
    267 httpTrimOws value = reverse (dropWhile httpOws? (reverse (dropWhile httpOws? value)))
    268 
    269 -- Parse exactly one finite line, excluding CRLF. Preserve parser diagnostics
    270 -- and consumption, but never accept a valid prefix followed by invalid bytes.
    271 httpHeaderLineReply = (parserContext "header-line"
    272     (parserBind
    273       (parserLabel "header-name"
    274         (parserSome (parserSatisfy httpTokenByte?)))
    275       (name : parserThen (parserLabel "colon" (parserExpect 58))
    276         (parserMap (value : pair (map toLowerAsciiByte name)
    277           (httpTrimOws value))
    278           (parserLabel "header-value"
    279             (parserBefore (parserTakeWhile httpFieldByte?) parserEof))))))
    280 
    281 -- Only an actual empty CRLF line ends the headers. The returned suffix is
    282 -- opaque body data, even if it contains CR, LF, NUL or header-looking bytes.
    283 httpParseHeaderLines input reversedHeaders = (let
    284   parseLine ((t t), ([], rest)) = ok (reverse reversedHeaders) rest
    285   parseLine ((t t), (line, rest)) = (let
    286     continue ((t t), (header, _)) = httpParseHeaderLines rest (pair header reversedHeaders)
    287     continue _ = httpBadRequest
    288     in continue (httpHeaderLineReply line))
    289   parseLine _ = httpBadRequest
    290   in parseLine (readLineBytes input))
    291 
    292 parseHeaders input = httpParseHeaderLines input []
    293 
    294 -- ---------------------------------------------------------------------------
    295 --  Content-Length parsing
    296 -- ---------------------------------------------------------------------------
    297 
    298 bit0? = (x :
    299   isZero? x)
    300 
    301 bit1? = (x :
    302   triage
    303     false
    304     (a : isZero? a)
    305     (_ _ : false)
    306     x)
    307 
    308 low3 = (b0 b1 b2 :
    309   matchBool
    310     (matchBool
    311       (matchBool 7 6 (bit1? b0))
    312       (matchBool 5 4 (bit1? b0))
    313       (bit1? b1))
    314     (matchBool
    315       (matchBool 3 2 (bit1? b0))
    316       (matchBool 1 0 (bit1? b0))
    317       (bit1? b1))
    318     (bit1? b2))
    319 
    320 decimalDigit = (c :
    321   triage
    322     nothing
    323     (_ : nothing)
    324     (b0 r0 :
    325       triage
    326         nothing
    327         (_ : nothing)
    328         (b1 r1 :
    329           triage
    330             nothing
    331             (_ : nothing)
    332             (b2 r2 :
    333               triage
    334                 nothing
    335                 (_ : nothing)
    336                 (b3 r3 :
    337                   triage
    338                     nothing
    339                     (_ : nothing)
    340                     (b4 r4 :
    341                       triage
    342                         nothing
    343                         (_ : nothing)
    344                         (b5 r5 :
    345                           matchBool
    346                             (matchBool
    347                               (matchBool
    348                                 (matchBool
    349                                   (matchBool
    350                                     (just (low3 b0 b1 b2))
    351                                     (matchBool
    352                                       (matchBool
    353                                         (just (matchBool 9 8 (bit1? b0)))
    354                                         nothing
    355                                         (bit0? b2))
    356                                       nothing
    357                                       (bit0? b1))
    358                                     (bit0? b3))
    359                                   nothing
    360                                   (bit1? b5))
    361                                 nothing
    362                                 (bit1? b4))
    363                               nothing
    364                               (isZero? r5))
    365                             nothing
    366                             true)
    367                         r4)
    368                     r3)
    369                 r2)
    370             r1)
    371         r0)
    372     c)
    373 
    374 readDecimal_ = (self bytes acc :
    375   matchList
    376     (just acc)
    377     (h r :
    378       matchMaybe
    379         nothing
    380         (d : self r (add (mul acc 10) d))
    381         (decimalDigit h))
    382     bytes)
    383 
    384 readDecimal = (bytes :
    385   matchBool
    386     nothing
    387     (y readDecimal_ bytes 0)
    388     (emptyList? bytes))
    389 
    390 maxBodyBytesDecimal = "1048576"
    391 
    392 byte0? b = equal? b 48
    393 digitLtMax? maxDigit digit = lt? digit maxDigit
    394 
    395 stripLeadingZeros_ self raw =
    396   lazyList
    397     (_ : t)
    398     (c r :
    399       lazyBool
    400         (_ : self r)
    401         (_ : raw)
    402         (byte0? c))
    403     raw
    404 
    405 decimalLengthLte_ self max raw =
    406   lazyList
    407     (_ : true)
    408     (_ rest :
    409       lazyList
    410         (_ : false)
    411         (_ maxRest : self maxRest rest)
    412         max)
    413     raw
    414 
    415 decimalSameLength_ self max raw =
    416   lazyList
    417     (_ :
    418       lazyList
    419         (_ : true)
    420         (_ _ : false)
    421         max)
    422     (_ rest :
    423       lazyList
    424         (_ : false)
    425         (_ maxRest : self maxRest rest)
    426         max)
    427     raw
    428 
    429 sameLengthDecimalLte_ self max raw less =
    430   lazyList
    431     (_ : true)
    432     (digit rest :
    433       lazyList
    434         (_ : false)
    435         (maxDigit maxRest :
    436           lazyBool
    437             (_ : self maxRest rest true)
    438             (_ :
    439               lazyBool
    440                 (_ : self maxRest rest true)
    441                 (_ :
    442                   lazyBool
    443                     (_ : self maxRest rest false)
    444                     (_ : false)
    445                     (equal? digit maxDigit))
    446                 (digitLtMax? maxDigit digit))
    447             less)
    448         max)
    449     raw
    450 
    451 decimalLengthLte? max raw = y decimalLengthLte_ max raw
    452 
    453 decimalSameLength? max raw = y decimalSameLength_ max raw
    454 
    455 decimalBytesLte? max raw =
    456   let trimmed = y stripLeadingZeros_ raw in
    457   lazyBool
    458     (_ : y sameLengthDecimalLte_ max trimmed false)
    459     (_ : decimalLengthLte? max trimmed)
    460     (decimalSameLength? max trimmed)
    461 
    462 -- This digit predicate reuses the existing structural byte decoder: asking
    463 -- generic numeric comparisons about abstract byte trees would make importing
    464 -- this module hang before any request has been received.
    465 httpDecimalDigit? byte =
    466   lazyMaybe
    467     (_ : false)
    468     (digit : true)
    469     (decimalDigit byte)
    470 
    471 httpDecimalReply =
    472   parserContext "content-length"
    473     (parserBind
    474       (parserLabel "decimal-digit"
    475         (parserSome (parserSatisfy httpDecimalDigit?)))
    476       (digits : parserThen
    477         (parserLabel "end-of-decimal" parserEof)
    478         (lazyMaybe
    479           (_ : parserFailure
    480             (parserDiagnostic 2 "decimal-digit" t t t))
    481           (number : parserPure number)
    482           (readDecimal digits))))
    483 
    484 -- Retain the earlier validator as an oracle until all header consumers have
    485 -- been exercised with the generic decimal reply.
    486 parseContentLengthValueLegacy = (raw :
    487   matchMaybe
    488     (err 400 "Bad Request\n")
    489     (n :
    490       lazyBool
    491         (_ : ok (just n) t)
    492         (_ : err 413 "Request body too large\n")
    493         (decimalBytesLte? maxBodyBytesDecimal raw))
    494     (readDecimal raw))
    495 
    496 parseContentLengthValue = (raw :
    497   parserMatchReply
    498     (diagnostic rest consumed committed : err 400 "Bad Request\n")
    499     (n rest consumed committed :
    500       lazyBool
    501         (_ : ok (just n) t)
    502         (_ : err 413 "Request body too large\n")
    503         (decimalBytesLte? maxBodyBytesDecimal raw))
    504     (httpDecimalReply raw))
    505 
    506 contentLength_ = (self headers :
    507   matchList
    508     (ok nothing t)
    509     (h r :
    510       matchBool
    511         (parseContentLengthValue (snd h))
    512         (self r)
    513         (equal? "content-length" (fst h)))
    514     headers)
    515 
    516 contentLength = (headers :
    517   y contentLength_ headers)
    518 
    519 -- ---------------------------------------------------------------------------
    520 --  Body reading
    521 -- ---------------------------------------------------------------------------
    522 
    523 bodyReadState = (remaining accRev rest :
    524   pair remaining (pair accRev rest))
    525 
    526 bodyReadRemaining = (state :
    527   fst state)
    528 
    529 bodyReadAccRev = (state :
    530   fst (snd state))
    531 
    532 bodyReadRest = (state :
    533   snd (snd state))
    534 
    535 takeBodyBytes_ = (self bytes remaining accRev :
    536   lazyBool
    537     (_ : bodyReadState 0 accRev bytes)
    538     (_ :
    539       lazyList
    540         (_ : bodyReadState remaining accRev t)
    541         (h r :
    542           self r (pred remaining) (pair h accRev))
    543         bytes)
    544     (isZero? remaining))
    545 
    546 takeBodyBytes = (bytes remaining accRev :
    547   y takeBodyBytes_ bytes remaining accRev)
    548 
    549 shiftRight1 n = triage 0 (_ : 0) (_ rest : rest) n
    550 
    551 shiftRight2 n = shiftRight1 (shiftRight1 n)
    552 shiftRight4 n = shiftRight2 (shiftRight2 n)
    553 shiftRight8 n = shiftRight4 (shiftRight4 n)
    554 shiftRight12 n = shiftRight4 (shiftRight8 n)
    555 
    556 shiftRight6 n = shiftRight2 (shiftRight4 n)
    557 
    558 atLeast16? n = not? (isZero? (shiftRight4 n))
    559 atLeast64? n = not? (isZero? (shiftRight6 n))
    560 atLeast256? n = not? (isZero? (shiftRight8 n))
    561 atLeast1024? n = not? (isZero? (shiftRight2 (shiftRight8 n)))
    562 atLeast4096? n = not? (isZero? (shiftRight12 n))
    563 
    564 recvChunkMax4096 remaining =
    565   lazyBool
    566     (_ : 4096)
    567     (_ :
    568       lazyBool
    569         (_ : 1024)
    570         (_ :
    571           lazyBool
    572             (_ : 256)
    573             (_ :
    574               lazyBool
    575                 (_ : 64)
    576                 (_ :
    577                   lazyBool
    578                     (_ : 16)
    579                     (_ : 1)
    580                     (atLeast16? remaining))
    581                 (atLeast64? remaining))
    582             (atLeast256? remaining))
    583         (atLeast1024? remaining))
    584     (atLeast4096? remaining)
    585 
    586 readBodyRecv = (self sock remaining accRev recvBytes :
    587   onResult_ (recv sock recvBytes)
    588     (errMsg :
    589       pure
    590         (err
    591           400
    592           (append "recv failed while reading body: " errMsg)))
    593     (chunk :
    594       let state = takeBodyBytes chunk remaining accRev in
    595       let nextRemaining = bodyReadRemaining state in
    596       let nextAccRev = bodyReadAccRev state in
    597       lazyBool
    598         (_ : pure (ok (reverse nextAccRev) (bodyReadRest state)))
    599         (_ : self sock nextRemaining nextAccRev)
    600         (isZero? nextRemaining)))
    601 
    602 readBodyMore_ = (self sock remaining accRev :
    603   lazyBool
    604     (_ : pure (ok (reverse accRev) t))
    605     (_ : readBodyRecv self sock remaining accRev (recvChunkMax4096 remaining))
    606     (isZero? remaining))
    607 
    608 readBodyMore = (sock remaining accRev :
    609   y readBodyMore_ sock remaining accRev)
    610 
    611 readBodyExact = (sock expected initialBytes :
    612   let state = takeBodyBytes initialBytes expected t in
    613   let remaining = bodyReadRemaining state in
    614   let accRev = bodyReadAccRev state in
    615   lazyBool
    616     (_ : pure (ok (reverse accRev) (bodyReadRest state)))
    617     (_ : readBodyMore sock remaining accRev)
    618     (isZero? remaining))
    619 
    620 validateBodyLength = (expected body rest :
    621   let actual = length body in
    622   lazyBool
    623     (_ : pure (ok body rest))
    624     (_ :
    625       pure
    626         (err
    627           400
    628           (append
    629             "body length mismatch expected="
    630             (append
    631               (showNumber expected)
    632               (append
    633                 " actual="
    634                 (showNumber actual))))))
    635     (equal? actual expected))
    636 
    637 readBody = (sock headers initialBytes :
    638   matchResult
    639     (status msg :
    640       pure (err status msg))
    641     (maybeLen rest :
    642       lazyMaybe
    643         (_ : pure (ok t initialBytes))
    644         (n :
    645           onOk (readBodyExact sock n initialBytes)
    646             (body rest :
    647               validateBodyLength n body rest))
    648         maybeLen)
    649     (contentLength headers))
    650 
    651 -- ---------------------------------------------------------------------------
    652 -- Request validation
    653 -- ---------------------------------------------------------------------------
    654 
    655 validMethod? = (method :
    656   lazyBool
    657     (_ : true)
    658     (_ :
    659       lazyBool
    660         (_ : true)
    661         (_ :
    662           lazyBool
    663             (_ : true)
    664             (_ : false)
    665             (equal? method "HEAD"))
    666         (equal? method "POST"))
    667     (equal? method "GET"))
    668 
    669 validVersion? = (version :
    670   lazyBool
    671     (_ : true)
    672     (_ : equal? version "HTTP/1.0")
    673     (equal? version "HTTP/1.1"))
    674 
    675 validTarget? = (target :
    676   startsWith? "/" target)
    677 
    678 validateRequest = (method target version headers :
    679   lazyBool
    680     (_ :
    681       lazyBool
    682         (_ :
    683           lazyBool
    684             (_ : ok t t)
    685             (_ : err 400 "Bad Request\n")
    686             (validTarget? target))
    687         (_ : err 505 "HTTP Version Not Supported\n")
    688         (validVersion? version))
    689     (_ : err 400 "Bad Request\n")
    690     (validMethod? method))
    691 
    692 -- ---------------------------------------------------------------------------
    693 -- 11. Handler pipeline
    694 -- ---------------------------------------------------------------------------
    695 
    696 routerMethod = (method :
    697   lazyBool
    698     (_ : "GET")
    699     (_ : method)
    700     (equal? method "HEAD"))
    701 
    702 respondAndClose = (sock resp :
    703   onOk_ (finally (send sock resp) (closeSocket_ sock)) (_ :
    704     pure (ok t t)))
    705 
    706 handleReadableRequest = (router client method target headers rest3 :
    707   onResult (readBody client headers rest3)
    708     (status msg :
    709       respondAndClose client
    710         (responseForMethod method
    711           (errorResponse status msg)))
    712     (body rest :
    713       respondAndClose client
    714         (responseForMethod method
    715           (router (routerMethod method) target headers body))))
    716 
    717 handleParsedHeaders = (router client method target version rest2 :
    718   matchResult
    719     (code bad :
    720       respondAndClose client (badRequestResponse "Bad Request\n"))
    721     (headers rest3 :
    722       matchResult
    723         (status msg :
    724           respondAndClose client
    725             (responseForMethod method (errorResponse status msg)))
    726         (ignored rest :
    727           handleReadableRequest router client method target headers rest3)
    728         (validateRequest method target version headers))
    729     (parseHeaders rest2))
    730 
    731 handleParsedRequest = (router client req rest2 :
    732   ((method :
    733     ((target :
    734       ((version :
    735         handleParsedHeaders router client method target version rest2)
    736        (snd (snd req))))
    737      (fst (snd req))))
    738    (fst req)))
    739 
    740 httpHandler = (router client peer :
    741   onResult_ (recvHeaders client)
    742     (status :
    743       respondAndClose client
    744         (badRequestResponse "Bad Request\n"))
    745     (raw :
    746       matchResult
    747         (code bad :
    748           respondAndClose client (badRequestResponse "Bad Request\n"))
    749         (req rest2 :
    750           handleParsedRequest router client req rest2)
    751         (parseRequestLine raw)))
    752 
    753 -- ---------------------------------------------------------------------------
    754 -- 12. IO-aware handler pipeline
    755 -- ---------------------------------------------------------------------------
    756 
    757 handleReadableRequestIO = (routerIO client method target headers rest3 :
    758   onResult (readBody client headers rest3)
    759     (status msg :
    760       respondAndClose client
    761         (responseForMethod method
    762           (errorResponse status msg)))
    763     (body rest :
    764       bind (routerIO (routerMethod method) target headers body) (resp :
    765         respondAndClose client (responseForMethod method resp))))
    766 
    767 handleParsedHeadersIO = (routerIO client method target version rest2 :
    768   matchResult
    769     (code bad :
    770       respondAndClose client (badRequestResponse "Bad Request\n"))
    771     (headers rest3 :
    772       matchResult
    773         (status msg :
    774           respondAndClose client
    775             (responseForMethod method (errorResponse status msg)))
    776         (ignored rest :
    777           handleReadableRequestIO routerIO client method target headers rest3)
    778         (validateRequest method target version headers))
    779     (parseHeaders rest2))
    780 
    781 handleParsedRequestIO = (routerIO client req rest2 :
    782   ((method :
    783     ((target :
    784       ((version :
    785         handleParsedHeadersIO routerIO client method target version rest2)
    786        (snd (snd req))))
    787      (fst (snd req))))
    788    (fst req)))
    789 
    790 httpHandlerIO = (routerIO client peer :
    791   onResult_ (recvHeaders client)
    792     (status :
    793       respondAndClose client
    794         (badRequestResponse "Bad Request\n"))
    795     (raw :
    796       matchResult
    797         (code bad :
    798           respondAndClose client (badRequestResponse "Bad Request\n"))
    799         (req rest2 :
    800           handleParsedRequestIO routerIO client req rest2)
    801         (parseRequestLine raw)))