112 lines
4.0 KiB
Scheme
112 lines
4.0 KiB
Scheme
;; Copyright 2002, 2003 Andreas Bernauer
|
|
|
|
;; Bindings of POST requests can be read only once, since they are
|
|
;; read from an input port. So we have to cache them, for the case of
|
|
;; a later GET-BINDINGS call on the same POST request. The requests
|
|
;; are referenced by a weak pointer. Thread-safe as all threads use
|
|
;; the same lock.
|
|
(define *POST-bindings-cache* '())
|
|
(define *cache-lock* (make-lock))
|
|
|
|
(define (get-bindings surflet-request)
|
|
(let ((request-method (surflet-request-method surflet-request))
|
|
(content-type (assoc 'content-type
|
|
(surflet-request-headers surflet-request))))
|
|
|
|
;; Check if we the content-type is the one we support. If there's
|
|
;; no content-type, assume the default (this is the one we
|
|
;; support).
|
|
(if (and content-type
|
|
;; Have to string-trim now, because the (buggy?) rfc822
|
|
;; implementation leaves the leading whitespace of the
|
|
;; header value.
|
|
(not (string=? (string-trim (cdr content-type))
|
|
"application/x-www-form-urlencoded")))
|
|
(error "get-bindings currently only supports
|
|
'application/x-www-form-urlencoded' as content-type"))
|
|
(cond
|
|
((string=? request-method "GET")
|
|
(form-query-list (http-url-search
|
|
(surflet-request-url surflet-request))))
|
|
((string=? request-method "POST")
|
|
(or (cached-bindings surflet-request)
|
|
(let* ((content-length (get-content-length
|
|
(surflet-request-headers surflet-request)))
|
|
(input-port (surflet-request-input-port surflet-request))
|
|
(form-data (read-string content-length input-port))
|
|
(form-bindings (form-query-list form-data)))
|
|
(obtain-lock *cache-lock*)
|
|
(set! *POST-bindings-cache*
|
|
(cons (cons (make-weak-pointer surflet-request)
|
|
form-bindings)
|
|
*POST-bindings-cache*))
|
|
(release-lock *cache-lock*)
|
|
form-bindings)))
|
|
(else
|
|
(error "unsupported request type")))))
|
|
|
|
;; Looking up, if we have cached this request. While going through the
|
|
;; list, we remove entries to request objects, that are no longer
|
|
;; valid. Expecting a call for an uncached request every now and then,
|
|
;; it is guaranteed, that the list is cleaned up every now and
|
|
;; then. The cache is a list of pairs
|
|
;;; (surflet-request . computed-binding)
|
|
(define (cached-bindings surflet-request)
|
|
(obtain-lock *cache-lock*)
|
|
(let ((result
|
|
(let loop ((predecessor #f)
|
|
(cache *POST-bindings-cache*))
|
|
(if (null? cache)
|
|
#f ; no such request cached
|
|
(let* ((head (car cache))
|
|
(s-req (weak-pointer-ref (car head))))
|
|
(if s-req
|
|
(if (eq? s-req surflet-request)
|
|
(cdr head) ; request is cached
|
|
(loop (if predecessor
|
|
(cdr predecessor)
|
|
cache)
|
|
(cdr cache))) ; request isn't cached
|
|
(begin ;; request object is gone ==> remove
|
|
;; it from list
|
|
(if predecessor
|
|
(set-cdr! predecessor (cdr cache))
|
|
(set! *POST-bindings-cache* (cdr cache)))
|
|
(loop predecessor
|
|
(cdr predecessor)))))))))
|
|
(release-lock *cache-lock*)
|
|
result))
|
|
|
|
|
|
;; Will be needed when we handle POST requests.
|
|
(define (get-content-length headers)
|
|
(cond ((get-header headers 'content-length) =>
|
|
;; adopted from httpd/cgi-server.scm
|
|
(lambda (content-length) ; Skip initial whitespace (& other non-digits).
|
|
(let ((first-digit (string-index content-length char-set:digit))
|
|
(content-length-len (string-length content-length)))
|
|
(if first-digit
|
|
(string->number (substring content-length first-digit
|
|
content-length-len))
|
|
;; (status-code bad-request) req
|
|
(error "Illegal `Content-length:' header.")))))
|
|
(else
|
|
(error "No Content-length specified for POST data."))))
|
|
|
|
(define (extract-bindings key bindings)
|
|
(let ((key (if (symbol? key) (symbol->string key) key)))
|
|
(map cdr
|
|
(filter (lambda (binding)
|
|
(equal? (car binding) key))
|
|
bindings))))
|
|
|
|
(define (extract-single-binding key bindings)
|
|
(let ((key-bindings (extract-bindings key bindings)))
|
|
(if (= 1 (length key-bindings))
|
|
(car key-bindings)
|
|
(error "extract-one-binding: more than one or zero bindings found"
|
|
(length key-bindings)
|
|
key bindings))))
|
|
|
|
|