tricu

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

01fde1b298c8920fe2116d3763ea2e4cc865d0752c25325587289dc72f999d68.tri (26065B)


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