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