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