; HOW TO RUN: ; Call broken? with a port number (greater than 1024 unless you're root) ; i.e. (broken? 8080) ; It should return #f if it's not broken, otherwise it returns a string explaining the brokenness. ; note - should be run on multiple platforms ; more-here - test flushing pending requests to a terminated servlet (module suite mzscheme (provide broken? broken?/remote start-server files-broken? authentication-broken? normal-servlets-broken? errors-broken?) (require (lib "process.ss") (lib "etc.ss") "my-url.ss" (lib "base64.ss" "net") (lib "xml.ss" "xml") (lib "channel.ss" "web-server")) (define myprint void) ; header-pattern = (listof (cons sym (+ regexp str))) (error-print-width 800) (define test-directory (build-path (collection-path "tests") "web-server")) (define web-root (build-path test-directory "web-root")) ;(define web-root (collection-path "web-server" "default-web-root")) (define answers-directory (build-path test-directory "answers")) (define TEST-IP "127.0.0.1") (print-struct #t) ;; broken?/remote: nat -> (union falses str) ;; same as broken? but connect to a running web-server ;; rather than starting a new one (define (broken?/remote port) (or (files-broken? port) (authentication-broken? port) (normal-servlets-broken? port) (extended-servlets-broken? port) (errors-broken? port) (timeouts-broken? port))) ; broken? : nat -> (+ false str) ;; the cadilac broken? function, starts the server automatically. (define (broken? port) (or (channels-broken?) (let ([server (start-server port)]) (dynamic-wind void (lambda () (or (and (not (subprocess? server)) server) (files-broken? port) (authentication-broken? port) (normal-servlets-broken? port) (extended-servlets-broken? port) (errors-broken? port) (timeouts-broken? port))) (lambda () (kill-subprocess server)))))) ; channels-broken? : -> (U str #f) ; more here - stress test for synchronization defects (define (channels-broken?) (let ([c (make-async-channel)] [v (gensym)]) (async-channel-put c v) (if (eq? v (async-channel-get c)) (or (let ([*x* #f]) (async-channel-get-available c (lambda (y) (set! *x* "channel: not empty"))) *x*) (let ([*x* "channel: nothing available"]) (async-channel-put c v) (async-channel-get-available c (lambda (y) (set! *x* #f))) *x*) (let/ec k (async-channel-try-get c (lambda () (k #f))) "channel: removed item from empty channel") (let/ec k (let ([x (gensym)]) (async-channel-put c x) (async-channel-try-get c (lambda () (k "channel: fail to remove x"))) #f))) "channel: put/get mismatch"))) ; start-server : nat -> (U str subprocess) (define (start-server port) (let-values ([(mz-subprocess mz-out mz-in mz-err) (subprocess #f #f #f (find-executable-path "web-server" #f) "-p" (number->string port) "-f" (path->string (build-path test-directory "configuration-table")) )]) (sleep 5) (if (not (eq? 'running (subprocess-status mz-subprocess))) (format "server mzscheme isn't running:~n~s" ; just print the first 20 lines of junk or so (read-string 1600 mz-err)) (if (char-ready? mz-err) (format "server printed error: ~s" (read-line mz-err)) mz-subprocess)))) ; kill-subprocess : subprocess -> void (define (kill-subprocess p) (let ([pid (subprocess-pid p)] [kill-path (find-executable-path "kill" #f)]) (unless (or (zero? pid) (not kill-path)) (let-values ([(kill-subprocess out in err) (subprocess #f #f #f kill-path (number->string pid))]) (close-input-port out) (close-output-port in) (close-input-port err) (subprocess-wait kill-subprocess))))) ; wont-start? : nat -> (+ false str) ; effect: tries to start the server '(define (wont-start? port) (with-handlers ([void (lambda (exn) (format "start-server: exn = ~a" exn))]) (let*-values ([(mz-subprocess mz-out mz-in mz-err) (subprocess #f #f #f (find-executable-path "web-server" #f) "-p" (number->string port))] [(send-command) ; String -> Void (lambda (command) (write command mz-in) (newline mz-in))]) ;(send-command `(current-directory ,web-root)) (send-command `(serve ,port)) (sleep 5) (if (not (eq? 'running (subprocess-status mz-subprocess))) (format "server mzscheme isn't running:~n~s" ; just print the first 20 lines of junk or so (read-string 1600 mz-err)) (if (char-ready? mz-err) (format "server printed error: ~s" (read-line mz-err)) #f))))) (define date-regexp "^(Sun|Mon|Tue|Wed|Thu|Fri|Sat), [0-9]* (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) [0-9]* [0-9][0-9]:[0-9][0-9]:[0-9][0-9] GMT$") (define usual-headers `((date ,date-regexp) (Last-Modified ,date-regexp) (server "^PLT Scheme$") (content-type "^text/html$") (connection "^close$"))) (define jpeg-headers `((date ,date-regexp) (Last-Modified ,date-regexp) (server "^PLT Scheme$") (content-type "^image/jpeg$") (connection "^close$"))) ; content-length-header : str -> (list sym str) (define (content-length-header file-path) (myprint " file-path = ~s~n" file-path) `(content-length ,(format "^~a$" (file-size file-path)))) ; files-broken? : nat -> (+ false string) (define (files-broken? port) (myprint "files-broken?~n") (with-handlers ([void (lambda (exn) (format "test-files: error starting up ~a" exn))]) (let* ([file-path (build-path web-root "htdocs" "index.html")] [implicit-url (local-url port "")] [explicit-url (combine-url/relative implicit-url "index.html")] [pattern (append usual-headers (list (content-length-header file-path)))] ;; jpeg tests ;; for PR#6302 [picture1-path (build-path web-root "htdocs" "me.jpg")] [picture1-url (combine-url/relative implicit-url "me.jpg")] [picture1-pattern (append jpeg-headers (list (content-length-header picture1-path)))] ;; reproduces PR#6302 b-cuz me2.JPG has upper-case extension [picture2-path (build-path web-root "htdocs" "me2.JPG")] [picture2-url (combine-url/relative implicit-url "me2.JPG")] [picture2-pattern (append jpeg-headers (list (content-length-header picture2-path)))] ) (or (problem-with-url? file-path pattern explicit-url) (problem-with-url? file-path pattern implicit-url) (problem-with-url? picture1-path picture1-pattern picture1-url) (problem-with-url? picture2-path picture2-pattern picture2-url) )))) ; problem-with-url? : str header-pattern url [(listof str)] -> (U false str) (define problem-with-url? (opt-lambda (file-path header-match url [extra-headers null]) (delimit-resources (lambda () (with-handlers ([void (lambda (exn) (format "test-url: url = ~a~n ~a~n" url exn))]) (call-with-input-file file-path (lambda (file-input) (let* ([http-port (get-impure-port url extra-headers)] [headers (purify-port http-port)]) (error-add (format "problem-with-url: ~s: " (url->string url)) (or (mime-headers-problem? headers header-match) (input-port-diff http-port file-input))))))))))) (define (print-port i-port) (printf "inside print-port~n") (let loop ([l (read-line i-port)]) (unless (eof-object? l) (printf "~a~n" l) (loop (read-line i-port))))) ; : url header-pattern regexp -> (U false str) (define (broken-url-regexp? to-test header-match expected) (delimit-resources (lambda () (with-handlers ([void (lambda (exn) (format "exception for url ~e~n~e" to-test exn))]) (let* ([http-port (get-impure-port to-test)] [headers (purify-port http-port)]) (error-add (format "url did not match: ~e: " (url->string to-test)) (or (mime-headers-problem? headers header-match) (regexp-match expected http-port)))))))) ; delimit-resources : (-> a) -> a (define (delimit-resources thunk) (let ([cust (make-custodian)] [old-cust (current-custodian)]) (dynamic-wind (lambda () (current-custodian cust)) thunk (lambda () (current-custodian old-cust) (custodian-shutdown-all cust))))) ; authentication-broken? : nat -> (+ false str) (define (authentication-broken? port) (myprint "authentication-broken?~n") (let* ([forbidden-file-path (build-path web-root "conf" "forbidden.html")] [okay-file-path (build-path web-root "htdocs" "secret" "index.html")] [forbidden-content-length (content-length-header forbidden-file-path)] [okay-content-length (content-length-header okay-file-path)] [auth-header `(www-authenticate "^Basic realm=\"secret stuff\"$")] [forbidden-headers (append usual-headers (list forbidden-content-length auth-header))] [okay-headers (append usual-headers (list okay-content-length))] ; [authorization (list (format "authorization: Basic ~a" (base64-encode "bubba:bbq")))] [authorization (list (bytes->string/utf-8 (bytes-append #"authorization: Basic " (base64-encode #"bubba:bbq"))))] ) (or (problem-with-url? forbidden-file-path forbidden-headers (local-url port "secret/")) (problem-with-url? okay-file-path okay-headers (local-url port "secret/") authorization) (problem-with-url? forbidden-file-path forbidden-headers (local-url port "secret/index.html")) (problem-with-url? okay-file-path okay-headers (local-url port "secret/index.html") authorization)))) ; normal-servlets-broken? : Nat -> (+ false str) (define (normal-servlets-broken? port) (myprint "normal-servlets-broken?~n") (let* ([local-test-url (lambda (path) (local-url port (string-append "servlets/tests/" path)))] [broken-url? (lambda (answer prog) (let ([answer-1 (build-path answers-directory answer)]) (problem-with-url? answer-1 (append usual-headers (list (content-length-header answer-1))) (local-test-url prog))))]) (or (broken-url? "test.servlet-1" "test.ss") (broken-url? "test.servlet-2" "test.ss?a=b&see=def") (problem-with-url? (build-path answers-directory "incremental") usual-headers (local-url port "servlets/tests/incremental.ss")) ; more here - test chunked version ;(problem-with-url? (build-path answers-directory "incremental") ; `(,(car usual-headers) ; ,(cadr usual-headers) ; ,(caddr usual-headers) ; (Transfer-Encoding "^chunked$")) ; (local-url port "servlets/tests/incremental.ss")) (let ([string-answer (build-path answers-directory "mime-servlet")]) (problem-with-url? string-answer `(,(car usual-headers) ,(cadr usual-headers) ,(caddr usual-headers) (content-type "^text/uber-format$") (connection "^close$") ,(content-length-header string-answer)) (local-url port "servlets/tests/mime.ss"))) (broken-url? "a-module" "a-module.ss") (broken-url? "b-module" "b-module.ss?texan=big-hat") ;(broken-url? "suspended-module" "suspended-module.ss") ; k-id is random (broken-url-regexp? (local-test-url "suspended-module.ss") (append usual-headers (list `(content-length "[0-9]*"))) suspended-module-regexp)))) (define suspended-module-regexp (regexp "
pair of graph
")) #f) |# )