1 ;==============================================================================
3 ; File: "http.scm", Time-stamp: <2009-03-13 12:05:07 feeley>
5 ; Copyright (c) 2005-2008 by Marc Feeley, All Rights Reserved.
7 ;==============================================================================
9 (##namespace ("http#"))
11 (##include "~~lib/gambit#.scm")
12 (##include "html#.scm")
14 (##include "http#.scm")
23 ;==============================================================================
27 (define hash-substring
28 (lambda (str start end)
33 (loop (modulo (+ (* h 5063) (char->integer (string-ref str i)))
40 (define-macro make-token-table
43 ; "alist" is a list of lists of the form "(string expression)"
45 ; The result is a perfect hash-table represented as a vector of
46 ; length 2*N, where N is the hash modulus. If the string S is in
47 ; the hash-table it is at index
49 ; X = (* 2 (modulo (hash-substring S 0 (string-length S)) N))
51 ; and the associated expression is at index X+1.
53 (define hash-substring ; repeated from above to be
54 (lambda (str start end) ; available for macro expansion
59 (loop (modulo (+ (* h 5063) (char->integer (string-ref str i)))
66 (define make-perfect-hash-table
68 (let loop1 ((n (length alist)))
69 (let ((v (make-vector (* 2 n) #f)))
70 (let loop2 ((lst alist))
76 (modulo (hash-substring str 0 (string-length str))
82 (vector-set! v (+ h 1) (cadr x))
86 (cons 'vector (vector->list (make-perfect-hash-table alist)))))
88 (define token-table-lookup-substring
89 (lambda (table str start end)
90 (let* ((n (quotient (vector-length table) 2))
91 (h (* 2 (modulo (hash-substring str start end) n)))
92 (x (vector-ref table h)))
97 (if (char=? (string-ref str i) (string-ref x j))
98 (loop (+ i 1) (+ j 1))
103 (= (string-length x) (- end start))
106 (define token-table-lookup-string
108 (token-table-lookup-substring table str 0 (string-length str))))
110 ;==============================================================================
116 (let ((n (char->integer (string-ref str i))))
117 (cond ((and (>= n 48) (<= n 57))
119 ((and (>= n 65) (<= n 70))
121 ((and (>= n 97) (<= n 102))
128 (let ((n1 (hex-digit str i)))
130 (let ((n2 (hex-digit str (+ i 1))))
132 (+ (* n1 16) n2)))))))
134 (define plausible-hex-escape?
137 (not (control-or-space-char? (string-ref str (+ j 1))))
138 (not (control-or-space-char? (string-ref str (+ j 2)))))))
140 (define control-or-space-char?
142 (or (not (char<? #\space c))
143 (not (char<? c #\x7f)))))
145 (define excluded-char?
147 (or (not (char<? #\space c))
148 (not (char<? c #\x7f))
163 (define extract-escaped
164 (lambda (str start n)
165 (let ((result (make-string n)))
166 (let loop ((i start) (j 0))
168 (let ((c (string-ref str i)))
170 (let ((n (hex-octet str (+ i 1))))
173 (string-set! result j (integer->char n))
177 (string-set! result j (if (char=? c #\+) #\space c))
183 id: 62788556-c247-11d9-9598-00039301ba52
193 (lambda (str start end decode? cont)
194 (let ((uri (make-uri #f #f "" #f #f)))
196 (define extract-string
199 (extract-escaped str i n)
200 (substring str i j))))
202 (define extract-query
210 (lambda (bindings end)
212 (substring str i j))))
214 (define state0 ; possibly inside the "scheme" part
217 (let ((c (string-ref str j)))
218 (cond ((char=? c #\:)
220 (state2 j (+ j 1) 1) ; the ":" is in the "path" part
221 (let ((scheme (extract-string i j n)))
224 (uri-scheme-set! uri scheme)
225 (if (and (< (+ j 2) end)
226 (char=? (string-ref str (+ j 1))
228 (char=? (string-ref str (+ j 2))
230 (state1 (+ j 3) (+ j 3) 0)
231 (state2 (+ j 1) (+ j 1) 0)))))))
235 (char=? (string-ref str (+ j 1)) #\/))
236 (state1 (+ j 2) (+ j 2) 0)
237 (state2 i (+ j 1) (+ n 1))))
239 (let ((path (extract-string i j n)))
242 (uri-path-set! uri path)
243 (state3 (+ j 1) (+ j 1) 0)))))
245 (let ((path (extract-string i j n)))
248 (uri-path-set! uri path)
249 (state4 (+ j 1) (+ j 1) 0)))))
251 (and (plausible-hex-escape? str end j)
252 (state0 i (+ j 3) (+ n 1))))
253 ((control-or-space-char? c)
254 (let ((path (extract-string i j n)))
257 (uri-path-set! uri path)
260 (state0 i (+ j 1) (+ n 1)))))
261 (let ((path (extract-string i j n)))
264 (uri-path-set! uri path)
267 (define state1 ; inside the "authority" part
270 (let ((c (string-ref str j)))
271 (cond ((char=? c #\/)
272 (let ((authority (extract-string i j n)))
275 (uri-authority-set! uri authority)
276 (state2 j (+ j 1) 1)))))
278 (let ((authority (extract-string i j n)))
281 (uri-authority-set! uri authority)
282 (state3 (+ j 1) (+ j 1) 0)))))
284 (let ((authority (extract-string i j n)))
287 (uri-authority-set! uri authority)
288 (state4 (+ j 1) (+ j 1) 0)))))
290 (and (plausible-hex-escape? str end j)
291 (state1 i (+ j 3) (+ n 1))))
292 ((control-or-space-char? c)
293 (let ((authority (extract-string i j n)))
296 (uri-authority-set! uri authority)
299 (state1 i (+ j 1) (+ n 1)))))
300 (let ((authority (extract-string i j n)))
303 (uri-authority-set! uri authority)
306 (define state2 ; inside the "path" part
309 (let ((c (string-ref str j)))
310 (cond ((char=? c #\?)
311 (let ((path (extract-string i j n)))
314 (uri-path-set! uri path)
315 (state3 (+ j 1) (+ j 1) 0)))))
317 (let ((path (extract-string i j n)))
320 (uri-path-set! uri path)
321 (state4 (+ j 1) (+ j 1) 0)))))
323 (and (plausible-hex-escape? str end j)
324 (state2 i (+ j 3) (+ n 1))))
325 ((control-or-space-char? c)
326 (let ((path (extract-string i j n)))
329 (uri-path-set! uri path)
332 (state2 i (+ j 1) (+ n 1)))))
333 (let ((path (extract-string i j n)))
336 (uri-path-set! uri path)
339 (define state3 ; inside the "query" part
342 (let ((c (string-ref str j)))
343 (cond ((char=? c #\#)
344 (let ((query (extract-query i j n)))
347 (uri-query-set! uri query)
348 (state4 (+ j 1) (+ j 1) 0)))))
350 (and (plausible-hex-escape? str end j)
351 (state3 i (+ j 3) (+ n 1))))
352 ((control-or-space-char? c)
353 (let ((query (extract-query i j n)))
356 (uri-query-set! uri query)
359 (state3 i (+ j 1) (+ n 1)))))
360 (let ((query (extract-query i j n)))
363 (uri-query-set! uri query)
366 (define state4 ; inside the "fragment" part
369 (let ((c (string-ref str j)))
370 (cond ((char=? c #\%)
371 (and (plausible-hex-escape? str end j)
372 (state4 i (+ j 3) (+ n 1))))
373 ((control-or-space-char? c)
374 (let ((fragment (extract-string i j n)))
377 (uri-fragment-set! uri fragment)
380 (state4 i (+ j 1) (+ n 1)))))
381 (let ((fragment (extract-string i j n)))
384 (uri-fragment-set! uri fragment)
387 (let ((i (state0 start start 0)))
391 (define parse-uri-query
392 (lambda (str start end decode? cont)
393 (let ((rev-bindings '()))
395 (define extract-string
398 (extract-escaped str i n)
399 (substring str i j))))
404 (let ((c (string-ref str j)))
405 (cond ((char=? c #\%)
406 (and (plausible-hex-escape? str end j)
411 (let ((name (extract-string i j n)))
435 (let ((c (string-ref str j)))
436 (cond ((char=? c #\%)
437 (and (plausible-hex-escape? str end j)
443 (let ((val (extract-string i j n)))
447 (cons (cons name val) rev-bindings))
455 (let ((val (extract-string i j n)))
459 (cons (cons name val) rev-bindings))
466 (let ((val (extract-string i j n)))
470 (cons (cons name val) rev-bindings))
473 (let ((i (state0 start start 0)))
474 (cont (and i (reverse rev-bindings))
478 (lambda (str decode?)
484 (and (= end (string-length str))
487 (define string->uri-query
488 (lambda (str decode?)
494 (and (= end (string-length str))
497 (define encode-for-uri
499 (let ((end (string-length str)))
502 (lambda (result i j n)
504 (let ((new-j (- j 1))
506 (string-set! result new-n (string-ref str new-j))
507 (copy result i new-j new-n))
512 (string-ref "0123456789ABCDEF" (bitwise-and x 15))))
517 (let ((c (string-ref str j)))
518 (cond ((char=? c #\space)
519 (let ((result (encode (+ j 1) (+ j 1) (+ n 1))))
520 (string-set! result n #\+)
521 (copy result i j n)))
524 (let ((result (encode (+ j 1) (+ j 1) (+ n 3))))
525 (let* ((x (char->integer c))
526 (hi (hex (arithmetic-shift x -4)))
528 (string-set! result n #\%)
529 (string-set! result (+ n 1) hi)
530 (string-set! result (+ n 2) lo))
531 (copy result i j n)))
533 (encode i (+ j 1) (+ n 1)))))
534 (let ((result (make-string n)))
535 (copy result i j n)))))
539 ;==============================================================================
541 ; x-www-form-urlencoded encoding and decoding.
543 (define encode-x-www-form-urlencoded
546 (define write-urlencoded
551 (write-char (string-ref "0123456789ABCDEF" n))))
554 (if (< i (string-length str))
555 (let ((c (string-ref str i)))
556 (cond ((or (and (char>=? c #\a) (char<=? c #\z))
557 (and (char>=? c #\A) (char<=? c #\Z))
558 (and (char>=? c #\0) (char<=? c #\9)))
563 (let ((n (char->integer c)))
566 (bitwise-and (arithmetic-shift n -4) 15))
567 (write-nibble (bitwise-and n 15)))))
572 (write-urlencoded (car field))
574 (write-urlencoded (cdr field))))
578 (with-output-to-string
581 (let ((field1 (car fields)))
583 (for-each (lambda (field)
588 (define decode-x-www-form-urlencoded
590 (let ((n (string-length str)))
594 (let ((s (make-string len)))
595 (let loop ((i start) (j 0))
597 (let ((c (string-ref str i)))
598 (cond ((char=? c #\%)
602 (string-set! s j (integer->char x))
603 (loop (+ i 3) (+ j 1))))
607 (string-set! s j #\space)
608 (loop (+ i 1) (+ j 1)))
611 (loop (+ i 1) (+ j 1)))))
617 (let ((h1 (nibble i))
618 (h2 (nibble (+ i 1))))
619 (and h1 h2 (+ (* h1 16) h2)))
624 (let ((c (string-ref str i)))
625 (cond ((and (char>=? c #\0) (char<=? c #\9))
626 (- (char->integer c) (char->integer #\0)))
627 ((and (char>=? c #\a) (char<=? c #\f))
628 (+ 10 (- (char->integer c) (char->integer #\a))))
629 ((and (char>=? c #\A) (char<=? c #\F))
630 (+ 10 (- (char->integer c) (char->integer #\A))))
634 (define state0 ; at beginning of string
635 (lambda (i rev-fields)
641 (reverse rev-fields))))
643 (define state1 ; in field name
644 (lambda (i start len rev-fields)
646 (let ((c (string-ref str i)))
647 (cond ((char=? c #\=)
666 (define state2 ; in field value
667 (lambda (i start len name rev-fields)
671 (cons (cons name (extract start len))
675 (let ((c (string-ref str i)))
676 (cond ((char=? c #\&)
694 (reverse (end-of-field)))))
698 ;==============================================================================
703 id: c69165bd-c13f-11d9-830f-00039301ba52
712 id: 8e66862f-c143-11d9-9f4e-00039301ba52
714 (server unprintable:)
723 (define make-http-server
728 (OPTIONS unimplemented-method)
729 (GET unimplemented-method)
730 (HEAD unimplemented-method)
731 (POST unimplemented-method)
732 (PUT unimplemented-method)
733 (DELETE unimplemented-method)
734 (TRACE unimplemented-method)
735 (CONNECT unimplemented-method))
748 ("CONNECT" CONNECT)))))
750 (define http-server-start!
754 (list server-address: '#u8(127 0 0 1) ; on localhost interface only
755 port-number: (server-port-number hs)
758 char-encoding: 'ISO-8859-1))))
759 (accept-connections hs server-port))))
761 (define accept-connections
762 (lambda (hs server-port)
766 (if (server-threaded? hs)
767 (let ((dummy-port (open-dummy)))
768 (parameterize ((current-input-port dummy-port)
769 (current-output-port dummy-port))
773 (serve-connection hs connection))))))
774 (serve-connection hs connection)))
778 (lambda (connection html)
779 (write-html html connection)
780 (close-port connection)))
782 (define method-not-implemented-error
786 (<html> (<head> (<title> "501 Method Not Implemented"))
788 (<h1> "Method Not Implemented"))))))
790 (define unimplemented-method
792 (let* ((request (current-request))
793 (connection (request-connection request)))
794 (method-not-implemented-error connection))))
796 (define bad-request-error
800 (<html> (<head> (<title> "400 Bad Request"))
803 (<p> "Your browser sent a request that this server could "
812 (request-connection request))
814 (request-version request)))
816 (define generate-reply
818 (if (or (eq? version 'HTTP/1.0)
819 (eq? version 'HTTP/1.1))
821 (with-output-to-u8vector
822 '(char-encoding: ISO-8859-1
828 (list version " 200 OK" eol
829 "Content-Length: " (u8vector-length message) eol
830 "Content-Type: text/html; charset=ISO-8859-1" eol
831 "Connection: close" eol
837 (u8vector-length message)
839 (with-output-to-port port thunk))))
844 (generate-reply connection)
846 (call-with-output-u8vector
848 (lambda (port) (generate-reply port)))))
849 (write-subu8vector output 0 (u8vector-length output) ##stdout-port)
850 (force-output ##stdout-port)
851 (write-subu8vector output 0 (u8vector-length output) connection)))
853 (close-port connection))))
857 (reply (lambda () (write-html html)))))
859 (define current-request
861 (thread-specific (current-thread)))) ; request is stored in thread
863 ;------------------------------------------------------------------------------
865 (define serve-connection
866 (lambda (hs connection)
868 ; Configure the connection with the client so that if we can't
869 ; read the request after 300 seconds, the read operation will fail
870 ; (and the thread will terminate).
872 (input-port-timeout-set! connection 300)
874 ; Configure the connection with the client so that if we can't
875 ; write the response after 300 seconds, the write operation will
876 ; fail (and the thread will terminate).
878 (output-port-timeout-set! connection 300)
880 (let ((req (permissive-read-line connection)))
881 (if (not (string? req))
882 (bad-request-error connection)
885 (cond ((= i (string-length req))
887 ((char=? (string-ref req i) #\space)
893 (token-table-lookup-substring
894 (server-method-table hs)
907 (define handle-version
911 (let ((attributes (read-header connection)))
913 (handle-request version attributes)
914 (bad-request-error connection))))
916 ; this is an HTTP/0.9 request
917 (handle-request 'HTTP/0.9 '()))
919 (bad-request-error connection)))))
921 (define handle-request
922 (lambda (version attributes)
924 (server-method-table hs))
926 (vector-ref method-table method-index))
928 (vector-ref method-table (+ method-index 1)))
930 (read-content connection attributes))
932 (let ((x (assoc "Content-Type" attributes)))
936 "application/x-www-form-urlencoded"))
937 (decode-x-www-form-urlencoded content)
948 (thread-specific-set! (current-thread) request))
952 (bad-request-error connection))
953 ((not (< i (string-length req)))
955 ((not (char=? (string-ref req i) #\space))
956 (bad-request-error connection))
959 (token-table-lookup-substring
963 (string-length req))))
966 (vector-ref version-table
967 (+ version-index 1)))
968 (bad-request-error connection)))))))
970 (method-not-implemented-error connection)))))))
972 (define version-table
974 ("HTTP/1.0" 'HTTP/1.0)
975 ("HTTP/1.1" 'HTTP/1.1)))
979 (let loop ((attributes '()))
980 (let ((line (permissive-read-line connection)))
983 ((= (string-length line) 0)
986 (let ((attribute (split-attribute-line line)))
988 (loop (cons attribute attributes))
992 (lambda (connection attributes)
994 (cond ((assoc "Content-Length" attributes)
997 (let ((n (string->number (cdr x))))
998 (and n (integer? n) (exact? n) n))))
1002 (let ((str (make-string cl)))
1003 (let ((n (read-substring str 0 cl connection)))
1009 (define permissive-read-line
1011 (let ((s (read-line port)))
1012 (if (and (string? s)
1013 (> (string-length s) 0)
1014 (char=? (string-ref s (- (string-length s) 1)) #\return))
1015 ; efficient version of (substring s 0 (- (string-length s) 1))
1016 (begin (##string-shrink! s (- (string-length s) 1)) s)
1019 (define find-char-pos
1022 (if (< i (string-length str))
1023 (if (char=? char (string-ref str i))
1028 (define split-attribute-line
1030 (let ((pos (find-char-pos line #\:)))
1032 (< (+ pos 1) (string-length line))
1033 (char=? #\space (string-ref line (+ pos 1)))
1034 (cons (substring line 0 pos)
1035 (substring line (+ pos 2) (string-length line)))))))
1037 ;==============================================================================