From 9baec4fbf00decdcf29d1425de1b33ff760bfc1c Mon Sep 17 00:00:00 2001 From: interp Date: Mon, 2 Sep 2002 13:43:03 +0000 Subject: [PATCH] Handle `Location:' and `Status:' headers of CGI program's output correctly. --- scheme/httpd/cgi-server.scm | 68 ++++++++++++++++++++++++------------- 1 file changed, 45 insertions(+), 23 deletions(-) diff --git a/scheme/httpd/cgi-server.scm b/scheme/httpd/cgi-server.scm index b3985b2..aef32e7 100644 --- a/scheme/httpd/cgi-server.scm +++ b/scheme/httpd/cgi-server.scm @@ -76,7 +76,7 @@ (define cgi-default-bin-path "/bin:/usr/bin:/usr/ucb:/usr/bsd:/usr/local/bin") ;;; The path handler for CGI scripts. (car path) is the script to run. -;;; cgi-bin-path is used, if no PATH-variable isn't defined +;;; cgi-bin-path is used, if PATH-variable isn't defined (define (cgi-handler bin-dir . maybe-cgi-bin-path) (let-optionals @@ -136,7 +136,7 @@ (make-http-error-response http-status/not-found req "File or directory doesn't exist.")) (else - (cgi-make-response (run/port* doit) req))))) + (cgi-make-response (run/port* doit) path req))))) (else (make-http-error-response http-status/method-not-allowed req)))))) @@ -241,19 +241,12 @@ ;;; The script isn't an "nph-" script, so we read the response, and mutate ;;; it into a real HTTP response, which we then send back to the HTTP client. -(define (cgi-make-response script-port req) +(define (cgi-make-response script-port path req) (let* ((headers (read-rfc822-headers script-port)) (ctype (get-header headers 'content-type)) ; The script headers (loc (get-header headers 'location)) - (stat (let ((stat-lines (get-header-lines headers 'status))) - (cond - ((not (pair? stat-lines)) ; No status header. - "200 The idiot CGI script left out the status line.") - ((null? (cdr stat-lines)) ; One line status header. - (car stat-lines)) - (else ; Vas ist das? - (http-error http-status/internal-error req - "CGI script generated multi-line status header."))))) + (stat (extract-status-code-and-text + (get-header-lines headers 'status) req)) (extra-headers (delete-headers (delete-headers (delete-headers headers 'content-type) 'location) @@ -262,16 +255,45 @@ (http-syslog (syslog-level debug) "[cgi-server] headers: ~s~%" headers) (http-syslog (syslog-level debug) "[cgi-server] request:method=~a~%" (request:method req)) - ;; Send the response header back to the client - (make-response ;code message seconds mime extras body - http-status/ok - (status-code->text http-status/ok) - (time) - ctype - extra-headers - (make-writer-body - (lambda (out options) ; what about loc&status? - (copy-inport->outport script-port out) - (close-input-port script-port)))))) + (write (list 'status-code stat)) + (if loc + (if (uri-like? (string-trim loc)) + (make-http-error-response http-status/moved-perm req + loc loc) + (make-redirect-response (string-trim loc))) + ;; Send the response header back to the client + (make-response ;code message seconds mime extras body + (car stat) ; code + (cdr stat) ; text + (time) + ctype + extra-headers + (make-writer-body + (lambda (out options) ; what about status? + (copy-inport->outport script-port out) + (close-input-port script-port))))))) +;; shouldn't this be in uri? +(define (uri-like? loc) + (receive (proto path search frag) + (parse-uri loc) + (if proto #t #f))) + +(define (extract-status-code-and-text stat-lines req) + (cond + ((not (pair? stat-lines)) ; No status header. + (cons 200 "The idiot CGI script left out the status line.")) + ((null? (cdr stat-lines)) ; One line status header. + (with-fatal-error-handler* + (lambda (c d) + (http-error http-status/internal-error req + "CGI script generated an invalid status header." + (car stat-lines) c)) + (lambda () + (let ((status (string-trim (car stat-lines)))) + (cons (string->number (substring status 0 3)) ; number + (substring/shared status 4)))))) ; text + (else ; Vas ist das? + (http-error http-status/internal-error req + "CGI script generated multi-line status header.")))) \ No newline at end of file