124 lines
4.5 KiB
Scheme
124 lines
4.5 KiB
Scheme
; some useful utilities
|
|
|
|
;;; This file is part of the Scheme Untergrund Networking package.
|
|
|
|
;;; Copyright (c) 2002 by Andreas Bernauer.
|
|
;;; For copyright information, see the file COPYING which comes with
|
|
;;; the distribution.
|
|
|
|
(define (host-name-or-ip addr)
|
|
(with-fatal-error-handler
|
|
(lambda (condition more)
|
|
(call-with-values
|
|
(lambda () (socket-address->internet-address addr))
|
|
(lambda (ip port)
|
|
(format-internet-host-address ip))))
|
|
(host-info:name (host-info addr))))
|
|
|
|
(define (on-interrupt interrupt thunk)
|
|
(let lp ((event (most-recent-sigevent)))
|
|
(let ((next (next-sigevent event interrupt)))
|
|
(thunk)
|
|
(lp next))))
|
|
|
|
(define (socket-address->string socket-address . with-port?)
|
|
(let ((with-port? (:optional with-port? #t)))
|
|
(receive (host-address service-port)
|
|
(socket-address->internet-address socket-address)
|
|
(if with-port?
|
|
(format #f "~A:~A"
|
|
(format-internet-host-address host-address)
|
|
(format-port service-port))
|
|
(format #f "~A"
|
|
(format-internet-host-address host-address))))))
|
|
|
|
|
|
;;; Assemble a filename from ROOT and the elts of PATH-LIST.
|
|
;;; If the assembled filename contains a .. subdirectory, return #f,
|
|
;;; otw return the filename.
|
|
|
|
(define dotdot-check
|
|
(let ((dotdot-re (make-regexp "(^|/)\\.\\.($|/)"))) ; Matches a .. subdir.
|
|
(lambda (root path-list)
|
|
(let ((fname (if (null? path-list) root ; Bogus hack.
|
|
(string-append (file-name-as-directory root)
|
|
(string-join path-list "/")))))
|
|
(and (not (regexp-exec dotdot-re fname)) ; Check for .. subdir.
|
|
fname)))))
|
|
|
|
;;; Timeout on network writes?
|
|
|
|
(define (copy-inport->outport in out . maybe-buffer-size)
|
|
(let* ((buffer-size (:optional maybe-buffer-size 1024))
|
|
(buf (make-string buffer-size)))
|
|
(let loop ()
|
|
(cond ((read-string! buf in) => (lambda (nchars)
|
|
(write-string buf out 0 nchars)
|
|
(loop)))))
|
|
(force-output out)))
|
|
|
|
(define (dump fd)
|
|
(copy-inport->outport fd (current-output-port)))
|
|
|
|
(define (with-lock lock thunk)
|
|
(dynamic-wind
|
|
(lambda ()
|
|
(obtain-lock lock))
|
|
thunk
|
|
(lambda ()
|
|
(release-lock lock))))
|
|
|
|
;; Get Header from (RFC822 like) header alist
|
|
(define (get-header headers tag)
|
|
(cond ((assq tag headers) => cdr)
|
|
(else #f)))
|
|
|
|
|
|
;; GET-NUMERIC-FIELD-VALUE
|
|
;; generalized function to get a field value of the form 1*DIGIT
|
|
;; req is a request record, field-name a symbol
|
|
|
|
;; check wether a header-field with name field-name is contained in req;
|
|
;; if not, return #f,
|
|
;; if there is one, check wether its field-content conforms to
|
|
;; field-content = *LWS 1*DIGIT *LWS
|
|
;; (i.e. optional leading whitespaces, at least one digit, optional trailing whitespace);
|
|
;; if so, return digit as a number
|
|
|
|
(define (get-numeric-field-value req field-name)
|
|
(let
|
|
;;take first Content-length: header (RFC 2616 allows only one Content-length: header)
|
|
((field-content (get-header (request-headers req) field-name)))
|
|
(if field-content
|
|
(let*
|
|
((field-value-start (string-skip field-content char-set:whitespace));; skip whitespace, ;;char-set:whitespace = LWS from RFC2616?
|
|
(field-value (if field-value-start ;;yes, field content contained non-whitespace chars
|
|
(string->number (substring field-content
|
|
field-value-start
|
|
(string-length field-content))) ;;trailing whitespace? RFC allows it! ->
|
|
;; probably read-rfc822-headers in rfc822.scm should do the job of skipping leading and trailing whitespace?*
|
|
(http-error (status-code bad-request) req
|
|
(format #f "~A header contained only whitespace" field-name)))))
|
|
(if (and (integer? field-value) (>= field-value 0)) ;;yes, field value contained only digits
|
|
field-value
|
|
(http-error (status-code bad-request) req
|
|
(format #f "~A header contained characters other than digits or whitespace between digits" field-name))))
|
|
#f)))
|
|
|
|
|
|
;;* RFC 2616, 4.2: The field-content does not include any leading or
|
|
;;trailing LWS: linear white space occurring before the first
|
|
;;non-whitespace character of the field-value or after the last
|
|
;;non-whitespace character of the field-value. Such leading or
|
|
;;trailing LWS MAY be removed without changing the semantics of the
|
|
;;field value.
|
|
|
|
|
|
;;get request's message-body length from Content-length: header or
|
|
;;throw http-error if no such header
|
|
(define (get-body-length-from-content-length req)
|
|
(let ((maybe-length (get-numeric-field-value req 'content-length)))
|
|
(or maybe-length
|
|
(http-error (status-code bad-request) req "No Content-Length header in request"))))
|
|
|