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)))