forked from franzinc/aserve
-
Notifications
You must be signed in to change notification settings - Fork 0
/
headers.cl
1134 lines (919 loc) · 33.9 KB
/
headers.cl
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
;; -*- mode: common-lisp; package: net.aserve -*-
;;
;; headers.cl
;;
;; copyright (c) 1986-2005 Franz Inc, Berkeley, CA - All rights reserved.
;; copyright (c) 2000-2013 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))))
(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*))
: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 (debug-format :xmit-client-response-headers "~a"
(octets-to-string buff :end end
:external-format :octets))
(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)