1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131
|
;; -*- mode: common-lisp; package: net.aserve -*-
;;
;; headers.cl
;;
;; copyright (c) 1986-2005 Franz Inc, Berkeley, CA - All rights reserved.
;; copyright (c) 2000-2007 Franz Inc, Oakland, CA - All rights reserved.
;;
;; This code is free software; you can redistribute it and/or
;; modify it under the terms of the version 2.1 of
;; the GNU Lesser General Public License as published by
;; the Free Software Foundation, as clarified by the AllegroServe
;; prequel found in license-allegroserve.txt.
;;
;; This code is distributed in the hope that it will be useful,
;; but without any warranty; without even the implied warranty of
;; merchantability or fitness for a particular purpose. See the GNU
;; Lesser General Public License for more details.
;;
;; Version 2.1 of the GNU Lesser General Public License is in the file
;; license-lgpl.txt that was distributed with this file.
;; If it is not present, you can access it from
;; http://www.gnu.org/copyleft/lesser.txt (until superseded by a newer
;; version) or write to the Free Software Foundation, Inc., 59 Temple Place,
;; Suite 330, Boston, MA 02111-1307 USA
;;
;;
;; $Id: headers.cl,v 1.31 2008/01/28 17:52:21 jkf Exp $
;; Description:
;; header parsing
;;- This code in this file obeys the Lisp Coding Standard found in
;;- http://www.franz.com/~jkf/coding_standards.html
;;-
(in-package :net.aserve)
(defvar *header-byte-array*
;; unsigned-byte 8 vector contains the characters referenced by
;; the *header-lookup-array* . All characters are downcased.
)
(defvar *header-lookup-array*
;; indexed by the length of the header name.
;; value is list of (hba-index header-number) for all headers
;; that have a name this length
;; hba index is the index in the *header-byte-array* where the header
;; name begins.
;; the header-number is in index into..
)
;; *header-keyword-array* defvar'ed in main.cl
;; indexed by header-number, holds the keyword naming this header
(defvar *header-name-array*
;; indexed by header-number, holds the string containing the 'pretty'
;; version of this header
)
(defvar *header-client-array*
;; indexed by header number, contains a kwd symbol specifying how
;; to treat his header when proxying a client request to a server
)
(defvar *header-server-array*
;; indexed by header number, contains a kwd symbol specifying how
;; to treat his header when proxying a server response to a client
)
#+ignore
(defvar *header-cache-match-array*
;; indexed by header number. contains a kwd symbol or nil
;; describing how matching should be done
)
(defvar *header-count*
;; the number of headers we're tracking
)
(defconstant *header-block-size* 4096) ; bytes in a header block
(defconstant *header-block-used-size-index*
;; where the size of lower part of the buffer is kept
(- *header-block-size* 2))
(defconstant *header-block-data-start-index*
;; where the start of the lowest data block is stored
(- *header-block-size* 4))
(defmacro header-block-header-index (index)
;; where in the buffer the 2byte entry for header 'index' is located
`(- *header-block-size* 6 (ash ,index 1)))
(eval-when (compile eval)
;; the headers from the http spec
;; Following the header name we specify how to
;; 1. transfer client request headers and
;; 2 server response headers
;; (header-name client server)
;; client/server
;; :p - pass this header on
;; :np - don't pass this header on verbatim
;; :nf - this header won't be found
;; 3. how the proxy-cache compares a new request-header against
;; the request header stored with a cached response.
;; :mx - need exact match
;; :mp - either exact match or no value for new request-header
;; nil - no match needed
;;
;;***** note well: adding something to this table must be accompanied
;; by modifying *headers-count* above and then removing all caches
;; if we're using a proxy cache.
(defparameter *http-headers*
'(("Accept" :p :nf :mp)
("Accept-Charset" :p :nf :mp)
("Accept-Encoding" :p :nf :mp)
("Accept-Language" :p :nf :mp)
("Accept-Ranges" :p :nf :mx)
("Age" :nf :p nil)
("Allow" :nf :p nil)
("Authorization" :p :nf :mx)
("Cache-control" :p :p :mp)
("Connection" :np :np nil)
("Content-Disposition" :nf :nf nil) ; in multipart/form-data bodies
("Content-Encoding" :p :p :mx)
("Content-Language" :p :p :mx)
("Content-Length" :np :np nil)
("Content-Location" :p :p :mx)
("Content-Md5" :p :p :mx)
("Content-Range" :p :p :mx)
("Content-Type" :p :p :mx)
("Cookie" :p :p :mx)
("Date" :p :p nil)
("Etag" :nf :p nil)
("Expect" :p :nf :mx)
("Expires" :nf :p nil)
("From" :p :nf :mp) ; mp?
("Host" :np :nf :mx)
("If-Match" :p :nf :mx)
("If-Modified-Since" :p :n nil)
("If-None-Match" :p :nf :mx)
("If-Range" :p :nf :mx)
("If-Unmodified-Since" :p :nf nil)
("Last-Modified" :nf :p nil)
("Location" :nf :p nil)
("Max-Forwards" :np :nf nil)
("Pragma" :p :p nil) ; on reloads browsers add pragms
("Proxy-Authenticate" :nf :p nil)
("Proxy-Authorization" :p :nf :mx)
("Range" :p :nf :mx)
("Referer" :p :nf nil) ; should we match? who cares..
("Retry-After" :nf :p nil )
("Server" :nf :p nil)
("Set-Cookie" :nf :p nil)
("Status" :nf :nf nil) ; not real header but found in cgi responses
("TE" :p :nf :mx)
("Trailer" :np :np nil)
("Transfer-Encoding" :np :np nil)
("Upgrade" :np :nf nil)
("User-Agent" :p :nf :mp)
("Vary" :nf :p nil)
("Via" :np :np nil) ; modified by proxy both dierctions
("Warning" :p :p :mx)
("WWW-Authenticate" :nf :p nil)
)))
;; number of headers.
;; we take advantage of this being a constant in the code below and
;; in the proxy caches. If this number should change all proxy caches
;; should be removed.
(defconstant *headers-count* #.(length *http-headers*))
(defmacro header-block-data-start ()
;; return index right above the first data index object stored
`(- *header-block-size* 4 (* *headers-count* 2)))
(eval-when (compile eval)
(defmacro build-header-lookup-table ()
(let ((max-length 0)
(total-length 0))
; compute max and total length
(dolist (header *http-headers*)
(setq header (car header))
(let ((len (length header)))
(setq max-length (max max-length len))
(incf total-length len)))
(let ((header-byte-array (make-array total-length
:element-type '(unsigned-byte 8)))
(header-lookup-array (make-array (1+ max-length) :initial-element nil)))
(let ((hba -1)
(header-number -1)
(header-kwds)
(plists)
)
(dolist (header *http-headers*)
(setq header (car header))
(let ((len (length header)))
(setq header (string-downcase header))
(let ((header-keyword (read-from-string
(format nil ":~a"
header))))
(push header-keyword header-kwds)
(push (list (1+ hba)
(incf header-number))
(aref header-lookup-array len))
(push (cons header-keyword header-number) plists)
)
(dotimes (i len)
(setf (aref header-byte-array (incf hba))
(char-code (schar header i))))))
`(progn (setq *header-byte-array* ',header-byte-array)
(setq *header-lookup-array* ',header-lookup-array)
(setq *header-keyword-array*
',(make-array (length header-kwds)
:initial-contents
(reverse header-kwds)))
(setq *header-name-array*
',(make-array (length *http-headers*)
:initial-contents
(mapcar #'first *http-headers*)))
(setq *header-client-array*
',(make-array (length *http-headers*)
:initial-contents
(mapcar #'second *http-headers*)))
(setq *header-server-array*
',(make-array (length *http-headers*)
:initial-contents
(mapcar #'third *http-headers*)))
#+ignore
(setq *header-cache-match-array*
',(make-array (length *http-headers*)
:initial-contents
(mapcar #'fourth *http-headers*)))
(setq *header-count*
;; number of distinct headers
,(1+ header-number))
(if* (not (eql *header-count* *headers-count*))
then (error "setq *headers-count* to ~d in headers.cl"
*header-count*))
(dolist (hkw ',plists)
(setf (get (car hkw) 'kwdi) (cdr hkw)))
))))))
(build-header-lookup-table)
(defparameter *header-block-sresource*
;; 4096 element usb8 arrays
;; used to hold header contents with index at the end
(create-sresource
:create #'(lambda (sresource &optional size)
(declare (ignore sresource))
(if* size
then (error "size can't be specifed for header blocks"))
(make-array *header-block-size*
:element-type '(unsigned-byte 8)))))
(defparameter *header-block-plus-sresource*
;; (+ 4096 100) element usb8 arrays
;; used to hold things slight larger than a header block will hold
(create-sresource
:create #'(lambda (sresource &optional size)
(declare (ignore sresource))
(if* size
then (error "size can't be specifed for header blocks"))
(make-array (+ *header-block-size* 100)
:element-type '(unsigned-byte 8)))))
(defparameter *header-index-sresource*
;; used in parsing to hold location of header info in header-block
(create-sresource
:create #'(lambda (sresource &optional size)
(declare (ignore sresource))
(if* size
then (error "size can't be specifed for header index"))
(make-array *header-count* :initial-element nil))
:init #'(lambda (sresource buffer)
(declare (ignore sresource))
(dotimes (i (length buffer))
(setf (svref buffer i) nil)))))
(defun get-header-block ()
(get-sresource *header-block-sresource*))
(defun free-header-blocks (blocks)
;; free a list of blocks
(dolist (block blocks)
(free-sresource *header-block-sresource* block)))
(defun free-header-block (block)
(if* (and block (atom block))
then (free-sresource *header-block-sresource* block)
elseif block
then (error "bad value passed to free-header-block ~s" block)))
(defun get-header-plus-block ()
(get-sresource *header-block-plus-sresource*))
(defun free-header-plus-block (block)
(if* block then (free-sresource *header-block-plus-sresource* block)))
;; parsed header array
;; We have to work with headers and to reduce consing we work with
;; them in place in a structure called a parsed header block
;; This is stored in a 4096 byte usb8 vector.
;; The layout is:
;; headers and values .. empty .. data-block header-index-block min-db size
;;
;; The headers and values are exactly what's read from the web client/server.
;; they end in a crlf after the last value.
;;
;; The size is a 2 byte value specifying the index after the last
;; header value. It says where we can add new headers if we want.
;; All values greater than 2 bytes are stored in big endian form.
;;
;; min-db is a 2 byte value telling where the lowest data-block entry starts
;;
;; The header-index-block is 2 bytes per entry and specifies an index
;; into the data-block where a descriptor for the value associated with
;; this header are located. This file lists the headers we know about
;; and each is given an index. The header-index-block is stored
;; so that index 0 is closest to the end of the array
;; The data blocks have the format
;; count(1) start1(2) end1(2) ... ... startN(2) endN(2)
;; which describes how many header values there are and where they
;; are. end is one byte beyond the header value.
(defmacro unsigned-16-value (array index)
(let ((gindex (gensym)))
`(let ((,gindex ,index))
(declare (fixnum ,gindex))
(the fixnum
(+ (the fixnum (ash (aref ,array ,gindex) 8))
(aref ,array (1+ ,gindex)))))))
(defsetf unsigned-16-value (array index) (value)
(let ((gindex (gensym))
(gvalue (gensym)))
`(let ((,gindex ,index)
(,gvalue ,value))
(setf (aref ,array ,gindex) (hipart ,gvalue))
(setf (aref ,array (1+ ,gindex)) (lopart ,gvalue))
,gvalue)))
(defmacro hipart (x)
`(the fixnum (logand #xff (ash (the fixnum ,x) -8))))
(defmacro lopart (x)
`(the fixnum (logand #xff (the fixnum ,x))))
(defun parse-header-block-internal (buff start end ans)
;; the buff is an (unsigned-byte 8) array containing headers
;; and their values from start to just before end.
;; ans is a simple-vector large enough to hold *header-count* values.
;;
;; modify ans to store by header-number showing what, if any, values
;; are associated with this header. The values are a list
;; of cons (start . end) meaning the value is from start to end-1
;; spaces are trimmed from the sides
;;
(let ((i start)
(state 0)
beginhv
beginh
hnum
ch
otherheaders
otherheadername
)
(macrolet ((tolower-set (loc)
;; return the value at loc, convert it to lower
;; case and store back if the conversion was done
(let ((var (gensym)))
`(let ((,var ,loc))
(if* (<= #.(char-int #\A) ,var #.(char-int #\Z))
then ; must lower case
(incf ,var #.(- (char-int #\a) (char-int #\A)))
(setf ,loc ,var))
,var))))
(block done
(loop
;(format t "i: ~d, st ~d ch ~s~%" i state (code-char (aref buff i)))
(case state
(0 ; beginning a header
(if* (>= i end)
then (return))
; starting out look for something in the character range
(setq ch (tolower-set (aref buff i)))
(if* (not (<= #.(char-int #\a) ch #.(char-int #\z)))
then ; this can't be a header start
; skip to the eol
(setq state 1)
else ; got a header start, skip to the next colon
(setq beginh i)
(incf i)
(loop
(if* (>= i end) then (return-from done))
(setq ch (tolower-set (aref buff i)))
(if* (eq ch #.(char-int #\:))
then ; found a header
(setq hnum
(locate-header buff beginh i))
(incf i)
(if* (null hnum)
then ; unknown header, save specially
(setq otherheadername
(buffer-subseq-to-string
buff beginh (1- i))))
(setq state 2) ; skip to value
(return)
else (incf i)))))
(1 ; skip to eol ( a linefeed in this case)
(if* (>= i end) then (return))
(loop
(setq ch (aref buff i))
(if* (eq ch #.(char-int #\linefeed))
then (setq state 0)
(incf i)
(return)
else (incf i))
(if* (>= i end) then (return-from done))
))
(2 ; accumulate a header value
(if* (>= i end) then (return))
(if* (null beginhv) then (setq beginhv i))
(loop
(setq ch (aref buff i))
(if* (eq ch #.(char-int #\linefeed))
then (incf i)
(return))
(incf i)
(if* (>= i end) then (return-from done)))
; hit eol, but there still could be a continuation
(setq state 3))
(3 ; read a header line, now this could be a continuation
; or a new header or eo headers
(if* (or (>= i end)
(not (eq (aref buff i) #.(char-int #\space))))
then ; end of one header's value
; backup and ignore cr lf
(let ((back (1- i)))
(loop
(let ((ch (aref buff back)))
(if* (or (eq ch #.(char-code #\return))
(eq ch #.(char-code #\linefeed)))
then (decf back)
else (return))))
(incf back)
; now strip spaces from beginning
(loop
(if* (>= beginhv back)
then (return)
elseif (eq (aref buff beginhv) #.(char-code #\space))
then (incf beginhv)
else (return)))
; strip from end
(loop
(if* (>= beginhv back)
then (return)
elseif (eq (aref buff (1- back))
#.(char-code #\space))
then (decf back)
else (return)))
; must keep the header items in the same order
; they were received (according to the http spec)
(if* hnum
then ; known header
(let ((cur (svref ans hnum))
(new (list (cons beginhv back))))
(if* cur
then (setq cur (append cur new))
else (setq cur new))
(setf (svref ans hnum) cur))
else ; unknown header
(push (cons otherheadername
(buffer-subseq-to-string
buff beginhv back))
otherheaders))
(setq beginhv nil)
(setq state 0))
else (setq state 2))))))
otherheaders)))
(defun parse-header-block (buff start end)
(let ((ans (get-sresource *header-index-sresource*))
(otherheaders))
(setq otherheaders (parse-header-block-internal buff start end ans))
; store the info in ans into the buffer at the end
(let* ((table-index (header-block-header-index 0))
(data-index (header-block-data-start)))
(dotimes (i (length ans))
(let ((data (svref ans i)))
(if* data
then ; must store data and an index to it
(let* ((data-len (length data))
(size (+ 1 ; count
(ash data-len 2) ; 4 bytes per data entry
)))
(decf data-index size)
(setf (unsigned-16-value buff table-index) data-index)
(setf (aref buff data-index) data-len)
(let ((i (1+ data-index)))
(dolist (datum data)
(setf (unsigned-16-value buff i) (car datum))
(incf i 2)
(setf (unsigned-16-value buff i) (cdr datum))
(incf i 2))))
else ; nothing there, zero it out
(setf (aref buff table-index) 0)
(setf (aref buff (1+ table-index)) 0)))
(decf table-index 2))
(setf (unsigned-16-value buff *header-block-used-size-index*) end)
(setf (unsigned-16-value buff *header-block-data-start-index* )
data-index)
(if* (> end data-index)
then (error "header is too large")))
(free-sresource *header-index-sresource* ans)
otherheaders))
(defun free-req-header-block (req)
;; if the req has an associated header block, give it back
(free-sresource *header-block-sresource* (request-header-block req))
(setf (request-header-block req) nil))
(defun header-buffer-values (buff header-index)
;; the buff is a usb8 array that has been built by parse-header-block
;; we are asked to return the location of the value(s) for the header
;; with the given index
;; we return nil if the header has no value
;; otherwise we return values
;; start index
;; end index
;; list of (start-index . end-index) for the rest of the values, if any
;; be a nice guy and handle a symbolic header keyword name
(if* (symbolp header-index)
then (let ((ans (get header-index 'kwdi)))
(if* (null ans)
then (error "no such header as ~s" header-index))
(setq header-index ans)))
(let ((table-index (header-block-header-index header-index))
(data-index))
(setq data-index (unsigned-16-value buff table-index))
(if* (< 0 data-index (length buff))
then ; get values
(let ((count (aref buff data-index))
(first-start (unsigned-16-value buff (+ 1 data-index)))
(first-end (unsigned-16-value buff (+ 3 data-index))))
(if* (> count 1)
then ; must get a list of the rest
(incf data-index 5)
(let (res)
(dotimes (i (1- count))
(push (cons (unsigned-16-value buff data-index)
(unsigned-16-value buff
(+ 2 data-index)))
res)
(incf data-index 4))
(values first-start
first-end
(nreverse res)))
else (values first-start first-end))))))
(defun buffer-subseq-to-string (buff start end)
;; extract a subsequence of the usb8 buff and return it as a string
(let ((str (make-string (- end start))))
(do ((i start (1+ i))
(ii 0 (1+ ii)))
((>= i end))
(setf (schar str ii)
(code-char (aref buff i))))
str))
(defun header-buffer-req-header-value (req header)
;; see header-buffer-header-value for what this does.
(let ((buff (request-header-block req)))
; there will be no buffer for http/0.9 requests
(and buff
(header-buffer-header-value (request-header-block req) header))))
(defun header-buffer-header-value (buff header)
;; header is a number or keyword symbol.
;; return nil or the header value as a string
;;
;; according to the http spec, multiple headers with the same name
;; is only allowed when the header value is a comma separated list
;; of items, and the sequence of header values can be considered
;; as one big value separated by commas
;;
(if* (symbolp header)
then (setq header (get header 'kwdi)))
(if* (fixnump header)
then
(multiple-value-bind (start end others)
(header-buffer-values buff header)
; we only get the first value
(if* start
then (let ((ans (buffer-subseq-to-string buff start end)))
(if* others
then ; must concatente the others as well
(let (res)
(dolist (oth others)
(push (buffer-subseq-to-string buff
(car oth)
(cdr oth))
res)
(push ", " res))
(apply #'concatenate 'string ans res))
else ans))))))
(defun locate-header (buff start end)
;; find the header-index of the header between start and end in buff.
;; buffer is an usb8 array.
;; return nil if no match
(let ((size (- end start))
(hba *header-byte-array*))
(if* (< 0 size (length *header-lookup-array*))
then (dolist (header (svref *header-lookup-array* size))
(let ((begin (car header)))
(if* (dotimes (i size t)
(if* (not (eq (aref buff (+ start i))
(aref hba (+ begin i))))
then (return nil)))
then ; match
(return (cadr header))))))))
(defun compute-client-request-headers (sock)
;; for the client code we return a list of headers or signal
;; an error.
;;
(let* ((buff (get-sresource *header-block-sresource*))
(end (read-headers-into-buffer sock buff)))
(if* end
then (prog1 (parse-and-listify-header-block buff end)
(free-sresource *header-block-sresource* buff))
else (free-sresource *header-block-sresource* buff)
(error "Incomplete headers sent by server"))))
(defun parse-and-listify-header-block (buff end)
;; buff is a header-block
;; parse the headers in the block and then extract the info
;; in assoc list form
(let ((ans (get-sresource *header-index-sresource*))
(headers))
; store the non-standard headers in the header array
(setq headers (parse-header-block-internal buff 0 end ans))
; now cons up the headers
(dotimes (i *header-count*)
(let ((res (svref ans i)))
(if* res
then (let ((kwd (svref *header-keyword-array* i)))
(dolist (ent res)
(let ((start (car ent))
(end (cdr ent)))
(let ((str (make-string (- end start))))
(do ((i start (1+ i))
(ii 0 (1+ ii)))
((>= i end))
(setf (schar str ii)
(code-char
(aref buff i))))
(push (cons kwd str) headers))))))))
(free-sresource *header-index-sresource* ans)
(nreverse headers)))
(defun listify-parsed-header-block (buff)
;; the header block buff has been parsed.
;; we just extract all headers in conses
(let (res)
(dotimes (i *headers-count*)
(let ((val (header-buffer-header-value buff i)))
(if* val
then (push (cons (aref *header-keyword-array* i) val) res))))
(nreverse res)))
(defun initialize-header-block (buf)
;; set the parsed header block buf to the empty state
; clear out the indicies pointing to the values
(let ((index (header-block-header-index 0)))
(dotimes (i *header-count*)
(setf (unsigned-16-value buf index) 0)
(decf index 2)))
; no headers yet
(setf (unsigned-16-value buf *header-block-used-size-index*) 0)
; start of where to put data
(setf (unsigned-16-value buf *header-block-data-start-index*)
(header-block-data-start))
buf)
(defun copy-headers (frombuf tobuf header-array)
;; copy the headers denoted as :p (pass) in header array
;; in frombuf to the tobuf
;;
;; return the index after the last header stored.
(let ((toi 0)
(data-index (header-block-data-start))
(this-data-index)
(header-index (header-block-header-index 0)))
(dotimes (i (length header-array))
(if* (eq :p (svref header-array i))
then ; passed intact
(multiple-value-bind (start end others)
(header-buffer-values frombuf i)
(if* start
then (let ((items (1+ (length others))))
(decf data-index (1+ (* items 4)))
(setf (aref tobuf data-index) items)
(setf (unsigned-16-value tobuf header-index)
data-index)
(setq this-data-index (1+ data-index)))
(loop
(if* (null start) then (return))
(let ((name (svref *header-name-array* i)))
; copy in header name
(dotimes (j (length name))
(setf (aref tobuf toi) (char-code (schar name j)))
(incf toi))
(setf (aref tobuf toi) #.(char-code #\:))
(incf toi)
(setf (aref tobuf toi) #.(char-code #\space))
(incf toi)
; set the start address
(setf (unsigned-16-value tobuf this-data-index)
toi)
(incf this-data-index 2)
; copy in the header value
(do ((j start (1+ j)))
((>= j end))
(setf (aref tobuf toi) (aref frombuf j))
(incf toi))
; set the end address
(setf (unsigned-16-value tobuf this-data-index)
toi)
(incf this-data-index 2)
; add the obligatory crlf
(setf (aref tobuf toi) #.(char-code #\return))
(incf toi)
(setf (aref tobuf toi) #.(char-code #\linefeed))
(incf toi))
(let ((next (pop others)))
(if* next
then (setq start (car next)
end (cdr next))
else (return))))
else (setf (unsigned-16-value tobuf header-index) 0)))
else ; clear out the header index
(setf (unsigned-16-value tobuf header-index) 0))
(decf header-index 2))
(setf (unsigned-16-value tobuf *header-block-used-size-index*) toi)
(setf (unsigned-16-value tobuf *header-block-data-start-index* )
data-index)
toi))
(defun insert-header (buff header value)
;; insert the header (kwd symbol or integer) at the end of the current buffer
;; end is the index of the next buffer position to fill
;; return the index of the first unfilled spot of the buffer
;;
(if* (symbolp header)
then (let ((val (get header 'kwdi)))
(if* (null val)
then (error "no such header as ~s" header))
(setq header val)))
(let ((end (unsigned-16-value buff *header-block-used-size-index*))
(starth)
(endh))
(let ((name (svref *header-name-array* header)))
(dotimes (j (length name))
(setf (aref buff end) (char-code (schar name j)))
(incf end))
(setf (aref buff end) #.(char-code #\:))
(incf end)
(setf (aref buff end) #.(char-code #\space))
(incf end)
(setq starth end)
(dotimes (j (length value))
(setf (aref buff end) (char-code (schar value j)))
(incf end))
(setq endh end)
(setf (aref buff end) #.(char-code #\return))
(incf end)
(setf (aref buff end) #.(char-code #\linefeed))
(incf end))
; now insert the information about this header in the data list
(let ((this-data-index (unsigned-16-value buff (header-block-header-index
header)))
(data-start (unsigned-16-value buff *header-block-data-start-index*)))
(let ((count 0))
(if* (not (zerop this-data-index))
then ; must copy this one down and add to it
(setq count (aref buff this-data-index)))
(incf count) ; for our new one
(decf data-start (+ 1 (* count 4)))
(setf (unsigned-16-value buff (header-block-header-index header))
data-start)
(setf (unsigned-16-value buff *header-block-data-start-index*)
data-start)
(setf (aref buff data-start) count)
; copy in old stuff
(incf this-data-index)
(incf data-start)
(dotimes (i (* 4 (1- count)))
(setf (aref buff data-start) (aref buff this-data-index))
(incf data-start)
(incf this-data-index))
; store in new info
(setf (unsigned-16-value buff data-start) starth)
(setf (unsigned-16-value buff (+ 2 data-start)) endh)))
; new end of headers
(setf (unsigned-16-value buff *header-block-used-size-index*) end)))
(defun insert-non-standard-header (buff name value)
;; insert a header that's not known by index into the buffer
;;
(setq name (string name))
(let ((end (unsigned-16-value buff *header-block-used-size-index*)))
(if* (> (+ end (length name) (length value) 4)
(header-block-data-start))
then ; no room
(return-from insert-non-standard-header nil))
(dotimes (i (length name))
(setf (aref buff end) (char-code (aref name i)))
(incf end))
(setf (aref buff end) #.(char-code #\:))
(incf end)
(setf (aref buff end) #.(char-code #\space))
(incf end)
(dotimes (i (length value))
(setf (aref buff end) (char-code (aref value i)))
(incf end))
(setf (aref buff end) #.(char-code #\return))
(incf end)
(setf (aref buff end) #.(char-code #\linefeed))
(incf end)
(setf (unsigned-16-value buff *header-block-used-size-index*) end)))
#+ignore
(defun insert-end-of-headers (buff end)
;; put in the final crlf
(setf (aref buff end) #.(char-code #\return))
(incf end)
(setf (aref buff end) #.(char-code #\linefeed))
(incf end)
end)
(defun header-match-values (request-block cache-block i exactp)
;; compare the header value for the current request vrs the cache-block
;; they match if they are identical
;; or if (not exact) and the request-block value is not given.
;;
(multiple-value-bind (rstart rend rest)
(header-buffer-values request-block i)
(multiple-value-bind (cstart cend cest)
(header-buffer-values cache-block i)
(or (and (null rstart) (null cstart)) ; both not present
(and (null rstart) (not exactp)) ; not given in request and !exact
; check for both present and identical
(and rstart cstart
(eql (- rend rstart) (- cend cstart)) ; same size
(equal rest cest)
(loop
(do ((rr rstart (1+ rr))
(cc cstart (1+ cc)))
((>= rr rend))
(if* (not (eq (aref request-block rr)
(aref cache-block cc)))
then (return-from header-match-values nil)))
(if* rest
then (setq rstart (caar rest)
rend (cdar rest)
cstart (caar cest)
cend (cdar cest))
(pop rest)
(pop cest)
else (return t))))))))
(defun header-match-prefix-string (buff header string)
;; match the prefix of the header vlue against the given string
(if* (symbolp header)
then (let ((val (get header 'kwdi)))
(if* (null val)
then (error "no such header as ~s" header))
(setq header val)))
(multiple-value-bind (rstart rend)
(header-buffer-values buff header)
(if* (and rstart (>= (- rend rstart) (length string)))
then ; compare byte by byte
(dotimes (i (length string) t)
(if* (not (eql (ausb8 buff rstart)
(char-code (schar string i))))
then (return nil))
(incf rstart)))))
(defun header-empty-p (buff header)
;; test to see if there is no value for this header
(if* (symbolp header)
then (let ((val (get header 'kwdi)))
(if* (null val)
then (error "no such header as ~s" header))
(setq header val)))
(header-buffer-values buff header))
(defun add-trailing-crlf (buff xx)
;; buff is a parsed header block.
;; find the end of the data and add a crlf and then return the
;; index right after the linefeed
(declare (ignore xx))
(let ((size (unsigned-16-value buff *header-block-used-size-index*)))
(if* (not (<= 0 size (header-block-data-start)))
then (error "buffer likely isn't a parsed header block"))
(setf (aref buff size) #.(char-code #\return))
(setf (aref buff (1+ size)) #.(char-code #\linefeed))
(+ size 2)))
#+ignore (defun testit ()
;; test the parsing
(let ((str (format nil "Date: 12 jul 2000 ~c~%User-Agent: zipp ~c~%Host: foo.com ~c~%Via: foo~c~% bar~c~% baz~c~%User-Agent: snorter~c~%"
#\return
#\return
#\return
#\return
#\return
#\return
#\return)))
(let ((thebuf (get-sresource *header-block-sresource*)))
(dotimes (i (length str))
(setf (aref thebuf i)
(char-int (schar str i))))
(parse-header-block thebuf 0 (length str))
(setq *xx* thebuf)
(format t "original~%")
(dump-header-block thebuf)
(insert-header thebuf :last-modified "dec 11, 1945")
(insert-header thebuf :user-agent "new agent")
(format t "after mod~%")
(dump-header-block thebuf)
(let ((newbuf (get-sresource *header-block-sresource*)))
(copy-headers thebuf newbuf *header-client-array*)
(format t "after copy ~%")
(dump-header-block newbuf))
)))
(defun dump-header-block (thebuf &optional (str t))
;; debugging function to print out the contents of a block
;; buffer that has been parsed and the parse table stored in it.
(dotimes (i *header-count*)
(multiple-value-bind (start end rest)
(header-buffer-values thebuf i)
(if* start
then (push (cons start end) rest))
(dolist (res rest)
(if* res
then (format str "~d: ~a ~s '"
i
(svref *header-name-array* i)
res)
(do ((ind (car res) (1+ ind)))
((>= ind (cdr res)))
(write-char (code-char (aref thebuf ind)) str))
(format str "'")
(terpri str))))))
(defun compute-request-headers (req)
;; compute an alist of all headers from the request
;; This is slow so it's meant to be used during debugging only.
;;
(let (res)
(dotimes (i *header-count*)
(let ((val (header-buffer-req-header-value req i)))
(if* val
then (push (cons (aref *header-keyword-array* i)
val)
res))))
(nreverse res)))
|