tricu

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

d3a1a09472ab1c40cca5f42c782aa559fcf9bf6d6d8d6c08f6102cb52bd4c6f2.tri (25252B)


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