diff --git a/collects/web-server/configuration/configuration-table-structs.ss b/collects/web-server/configuration/configuration-table-structs.ss index 98c56ae66d..bdc0a22bed 100644 --- a/collects/web-server/configuration/configuration-table-structs.ss +++ b/collects/web-server/configuration/configuration-table-structs.ss @@ -1,7 +1,8 @@ (module configuration-table-structs mzscheme (require (lib "contract.ss") (lib "url.ss" "net")) - (require "../response-structs.ss") + (require "../response-structs.ss" + "../request-structs.ss") ; configuration-table = (make-configuration-table nat nat num host-table (listof (cons str host-table))) (define-struct configuration-table @@ -52,7 +53,7 @@ [authentication (url? (cons/c symbol? string?) . -> . response?)] [servlets-refreshed (-> response?)] [passwords-refreshed (-> response?)] - [file-not-found (url? . -> . response?)] + [file-not-found (request? . -> . response?)] [protocol (url? . -> . response?)] [collect-garbage (-> response?)])] [struct messages diff --git a/collects/web-server/configuration/responders.ss b/collects/web-server/configuration/responders.ss index 427a3990e1..383728d7e7 100644 --- a/collects/web-server/configuration/responders.ss +++ b/collects/web-server/configuration/responders.ss @@ -1,10 +1,11 @@ (module responders mzscheme (require (lib "contract.ss") (lib "url.ss" "net")) - (require "../response-structs.ss") + (require "../response-structs.ss" + "../request-structs.ss") ; error-response : nat str str [(cons sym str) ...] -> response - ; more here - cache files with a refresh option. + ; XXX - cache files with a refresh option. ; The server should still start without the files there, so the ; configuration tool still runs. (Alternatively, find an work around.) (define (error-response code short text-file . extra-headers) @@ -14,7 +15,6 @@ (list (read-file text-file)))) ; servlet-loading-responder : url tst -> response - ; more here - parameterize error based on a configurable file, perhaps? ; This is slightly tricky since the (interesting) content comes from the exception. (define (servlet-loading-responder url exn) ((error-display-handler) @@ -61,9 +61,9 @@ (lambda (error-message) (error-response 400 "Malformed Request" protocol-file))) - ; gen-file-not-found-responder : str -> url -> response + ; gen-file-not-found-responder : str -> req -> response (define (gen-file-not-found-responder file-not-found-file) - (lambda (url) + (lambda (req) (error-response 404 "File not found" file-not-found-file))) ; gen-collect-garbage-responder : str -> -> response @@ -78,12 +78,12 @@ (provide/contract [error-response ((natural-number/c string? string?) (listof (cons/c symbol? string?)) . ->* . (response?))] - [servlet-loading-responder (string? any/c . -> . response?)] + [servlet-loading-responder (url? any/c . -> . response?)] [gen-servlet-not-found (path-string? . -> . (url? . -> . response?))] [gen-servlet-responder (path-string? . -> . (url? any/c . -> . response?))] [gen-servlets-refreshed (path-string? . -> . (-> response?))] [gen-passwords-refreshed (path-string? . -> . (-> response?))] [gen-authentication-responder (path-string? . -> . (url? (cons/c symbol? string?) . -> . response?))] - [gen-protocol-responder (path-string? . -> . (string? . -> . response?))] - [gen-file-not-found-responder (path-string? . -> . (url? . -> . response?))] + [gen-protocol-responder (path-string? . -> . (url? . -> . response?))] + [gen-file-not-found-responder (path-string? . -> . (request? . -> . response?))] [gen-collect-garbage-responder (path-string? . -> . (-> response?))])) \ No newline at end of file diff --git a/collects/web-server/dispatchers/dispatch-const.ss b/collects/web-server/dispatchers/dispatch-const.ss new file mode 100644 index 0000000000..a0a7357228 --- /dev/null +++ b/collects/web-server/dispatchers/dispatch-const.ss @@ -0,0 +1,16 @@ +(module dispatch-const mzscheme + (require (lib "contract.ss")) + (require "dispatch.ss" + "../private/response.ss" + "../request-structs.ss" + "../response-structs.ss") + (provide/contract + [interface-version dispatcher-interface-version?] + [make ((request? . -> . response?) . -> . dispatcher?)]) + + (define interface-version 'v1) + (define ((make procedure) conn req) + (output-response/method + conn + (procedure req) + (request-method req)))) \ No newline at end of file diff --git a/collects/web-server/dispatchers/dispatch-files.ss b/collects/web-server/dispatchers/dispatch-files.ss index e90786c995..988f3beac0 100644 --- a/collects/web-server/dispatchers/dispatch-files.ss +++ b/collects/web-server/dispatchers/dispatch-files.ss @@ -1,9 +1,6 @@ (module dispatch-files mzscheme (require (lib "url.ss" "net") - (lib "xml.ss" "xml") (lib "kw.ss") - (lib "list.ss") - (lib "string.ss") (lib "plt-match.ss") (lib "contract.ss")) (require "dispatch.ss" @@ -11,47 +8,26 @@ "../private/util.ss" "../private/mime-types.ss" "../request-structs.ss" - "../private/response.ss" - "../response-structs.ss") + "../private/response.ss") (provide/contract [interface-version dispatcher-interface-version?]) (provide make) - - ; more here - ".." should probably raise an error instead of disappearing. - (define (url-path->path base p) - (path->complete-path - (apply build-path base - (reverse! - (foldl (lambda (x acc) - (cond - [(string=? x "") acc] - [(string=? x ".") acc] - [(string=? x "..") (if (pair? acc) (cdr acc) acc)] - [else (cons x acc)])) - null - (regexp-split #rx"/" p)))))) + + ;; looks-like-directory : str -> bool + ;; to determine if is url style path looks like it refers to a directory + (define (looks-like-directory? path) + (eq? #\/ (string-ref path (sub1 (string-length path))))) (define interface-version 'v1) (define/kw (make #:key - [htdocs-path "htdocs"] + url->path [mime-types-path "mime.types"] - [indices (list "index.html" "index.htm")] - [file-not-found-responder - (gen-file-not-found-responder "not-found.html")]) + [indices (list "index.html" "index.htm")]) (define get-mime-type (make-get-mime-type mime-types-path)) (lambda (conn req) (define uri (request-uri req)) (define method (request-method req)) - ;; ************************************************************ - ;; ************************************************************ - ;; SERVING FILES - - ;; serve-file : connection symbol uri host -> void - ;; to find the file, including searching for implicit index files, and serve it out - (define path - ; XXX Abstract this - (url-path->path htdocs-path - (url-path->string (url-path uri)))) + (define-values (path _) (url->path uri)) (cond [(file-exists? path) (match (headers-assq* #"Range" (request-headers/raw req)) @@ -74,43 +50,13 @@ ; XXX: Unhandled range: r (output-file conn path method (get-mime-type path))])])] [(directory-exists? path) - (let loop ([dir-defaults indices]) - (cond - [(pair? dir-defaults) - (let ([full-name (build-path path (first dir-defaults))]) - (if (file-exists? full-name) - (cond - [(looks-like-directory? (url-path->string (url-path uri))) - (output-file conn full-name method (get-mime-type full-name))] - [else - (output-slash-message conn method (url-path->string (url-path uri)))]) - (loop (rest dir-defaults))))] - [else - (output-response/method conn (file-not-found-responder uri) method)]))] + (let/ec esc + (for-each (lambda (dir-default) + (define full-name (build-path path dir-default)) + (when (and (file-exists? full-name) + (looks-like-directory? (url-path->string (url-path uri)))) + (esc (output-file conn full-name method (get-mime-type full-name))))) + indices) + (next-dispatcher))] [else - (output-response/method conn (file-not-found-responder uri) method)]))) - - ;; looks-like-directory : str -> bool - ;; to determine if is url style path looks like it refers to a directory - (define (looks-like-directory? path) - (eq? #\/ (string-ref path (sub1 (string-length path))))) - - ;; output-slash-message: connection symbol string -> void - ;; basically this is just a special error response - (define (output-slash-message conn method url-path-str) - (output-response/method - conn - (make-response/full - 301 "Moved Permanently" - (current-seconds) - TEXT/HTML-MIME-TYPE - `([Location . ,(string-append url-path-str "/")]) - (list - (xexpr->string - `(html - (head (title "Add a Slash")) - (body "Please use " - (a ([href ,(string-append - url-path-str "/")]) - "this url") " instead."))))) - method))) \ No newline at end of file + (next-dispatcher)])))) \ No newline at end of file diff --git a/collects/web-server/dispatchers/dispatch-filter.ss b/collects/web-server/dispatchers/dispatch-filter.ss index bc436b238c..810348f081 100644 --- a/collects/web-server/dispatchers/dispatch-filter.ss +++ b/collects/web-server/dispatchers/dispatch-filter.ss @@ -11,6 +11,7 @@ (define interface-version 'v1) (define ((make regex inner) conn req) (define path (url-path->string (url-path (request-uri req)))) + #;(printf "~S~n" `(filter ,regex ,(url->string (request-uri req)) ,path ,(regexp-match regex path))) (if (regexp-match regex path) (inner conn req) (next-dispatcher)))) \ No newline at end of file diff --git a/collects/web-server/dispatchers/dispatch-passwords.ss b/collects/web-server/dispatchers/dispatch-passwords.ss index 0176ac2f30..d3dfa6b6bc 100644 --- a/collects/web-server/dispatchers/dispatch-passwords.ss +++ b/collects/web-server/dispatchers/dispatch-passwords.ss @@ -18,9 +18,7 @@ [password-file "passwords"] [password-connection-timeout 300] [authentication-responder - (gen-authentication-responder "forbidden.html")] - [passwords-refresh-responder - (gen-passwords-refreshed "passwords-refresh.html")]) + (gen-authentication-responder "forbidden.html")]) (define last-read-time (box #f)) (define password-cache (box #f)) (define (update-password-cache!) @@ -30,12 +28,11 @@ (cur-mtime . > . (unbox last-read-time)) (not (unbox password-cache))) (set-box! last-read-time cur-mtime) - ; more here - a malformed password file will kill the connection (set-box! password-cache (read-passwords password-file)))))) (define (read-password-cache) (update-password-cache!) (unbox password-cache)) - (lambda (conn req) + (define (dispatch conn req) (define uri (request-uri req)) (define path (url-path->string (url-path uri))) (define method (request-method req)) @@ -48,15 +45,10 @@ (request-authentication conn method uri authentication-responder realm))] - [(string=? "/conf/refresh-passwords" path) - ;; more here - send a nice error page - (update-password-cache!) - (output-response/method - conn - (passwords-refresh-responder) - method)] [else - (next-dispatcher)]))) + (next-dispatcher)])) + (values update-password-cache! + dispatch)) ;; **************************************** ;; **************************************** @@ -133,4 +125,4 @@ (authentication-responder uri `(WWW-Authenticate . ,(format " Basic realm=\"~a\"" realm))) - method))) + method))) \ No newline at end of file diff --git a/collects/web-server/dispatchers/dispatch-pathprocedure.ss b/collects/web-server/dispatchers/dispatch-pathprocedure.ss index cb3e61a1ce..9930ab8172 100644 --- a/collects/web-server/dispatchers/dispatch-pathprocedure.ss +++ b/collects/web-server/dispatchers/dispatch-pathprocedure.ss @@ -8,7 +8,7 @@ "../response-structs.ss") (provide/contract [interface-version dispatcher-interface-version?] - [make (string? (-> response?) . -> . dispatcher?)]) + [make (string? (request? . -> . response?) . -> . dispatcher?)]) (define interface-version 'v1) (define ((make the-path procedure) conn req) @@ -16,6 +16,6 @@ (if (string=? the-path path) (output-response/method conn - (procedure) + (procedure req) (request-method req)) (next-dispatcher)))) \ No newline at end of file diff --git a/collects/web-server/dispatchers/dispatch-sequencer.ss b/collects/web-server/dispatchers/dispatch-sequencer.ss index ec445745e4..2525a9ee26 100644 --- a/collects/web-server/dispatchers/dispatch-sequencer.ss +++ b/collects/web-server/dispatchers/dispatch-sequencer.ss @@ -9,7 +9,8 @@ (define interface-version 'v1) (define ((make . dispatchers) conn req) (let loop ([dispatchers dispatchers]) - (let ([c (first dispatchers)]) - (with-handlers ([exn:dispatcher? - (lambda (e) (loop (rest dispatchers)))]) - (c conn req)))))) \ No newline at end of file + (if (empty? dispatchers) + (next-dispatcher) + (with-handlers ([exn:dispatcher? + (lambda (e) (loop (rest dispatchers)))]) + ((first dispatchers) conn req)))))) \ No newline at end of file diff --git a/collects/web-server/dispatchers/dispatch-servlets.ss b/collects/web-server/dispatchers/dispatch-servlets.ss index b7040e5dc1..3eea6ee342 100644 --- a/collects/web-server/dispatchers/dispatch-servlets.ss +++ b/collects/web-server/dispatchers/dispatch-servlets.ss @@ -1,8 +1,6 @@ (module dispatch-servlets mzscheme - (require (lib "url.ss" "net") - (lib "kw.ss") + (require (lib "kw.ss") (lib "plt-match.ss") - (lib "string.ss") (lib "contract.ss")) (require "dispatch.ss" "../private/web-server-structs.ss" @@ -14,7 +12,6 @@ "../servlet/web-cells.ss" "../servlet/web.ss" "../configuration/responders.ss" - "../private/util.ss" "../managers/manager.ss" "../managers/timeouts.ss" "../managers/lru.ss" @@ -25,46 +22,14 @@ [interface-version dispatcher-interface-version?]) (provide make) - (define (url-path->path base p) - (path->complete-path - (let ([path-elems (regexp-split #rx"/" p)]) - ;; Servlets can have extra stuff after them - (let ([build-path - (lambda (b p) - (if (string=? p "") - b - (build-path b p)))]) - (let loop - ([p-e (if (string=? (car path-elems) "") - (cddr path-elems) - (cdr path-elems))] - [f (build-path base - (if (string=? (car path-elems) "") - (cadr path-elems) - (car path-elems)))]) - (cond - [(null? p-e) - f] - [(directory-exists? f) - (loop (cdr p-e) (build-path f (car p-e)))] - [(file-exists? f) - f] - [else - ;; Don't worry about e.g. links for now - f])))))) - (define interface-version 'v1) (define/kw (make config:instances config:scripts config:make-servlet-namespace #:key - [servlet-root "servlets"] - [responders-servlets-refreshed - (gen-servlets-refreshed "servlet-refresh.html")] + url->path [responders-servlet-loading servlet-loading-responder] [responders-servlet (gen-servlet-responder "servlet-error.html")] - [responders-file-not-found - (gen-file-not-found-responder "not-found.html")] [timeouts-servlet-connection (* 60 60 24)] [timeouts-default-servlet 30]) ;; ************************************************************ @@ -75,6 +40,10 @@ (define (servlet-content-producer conn req) (define meth (request-method req)) (define uri (request-uri req)) + ;; XXX - make timeouts proportional to size of bindings + (adjust-connection-timeout! + conn + timeouts-servlet-connection) (case meth [(head) (output-response/method @@ -99,7 +68,7 @@ (with-handlers (;; couldn't find the servlet [exn:fail:filesystem:exists:servlet? (lambda (the-exn) - (output-response/method conn (responders-file-not-found (request-uri req)) (request-method req)))] + (next-dispatcher))] ;; servlet won't load (e.g. syntax error) [(lambda (x) #t) (lambda (the-exn) @@ -110,16 +79,13 @@ ; Create the session frame (with-frame (define instance-custodian (make-servlet-custodian)) - (define servlet-path + (define-values (servlet-path _) (with-handlers ([void (lambda (e) (raise (make-exn:fail:filesystem:exists:servlet (exn-message e) (exn-continuation-marks e))))]) - ; XXX Abstract this - (url-path->path - servlet-root - (url-path->string (url-path uri))))) + (url->path uri))) (parameterize ([current-directory (get-servlet-base-dir servlet-path)] [current-custodian instance-custodian] [exit-handler @@ -157,14 +123,6 @@ (the-exit-handler x))]) (with-handlers ([(lambda (x) #t) (make-servlet-exception-handler)]) - ;; Two possibilities: - ;; - module servlet. start : Request -> Void handles - ;; output-response via send/finish, etc. - ;; - unit/sig or simple xexpr servlet. These must produce a - ;; response, which is then output by the server. - ;; Here, we do not know if the servlet was a module, - ;; unit/sig, or Xexpr; we do know whether it produces a - ;; response. (send/back ((servlet-handler the-servlet) req))) ((manager-instance-unlock! manager) instance-id)))))))) (output-response conn response) @@ -175,8 +133,7 @@ ;; default-server-instance-expiration-handler : (request -> response) (define (default-servlet-instance-expiration-handler req) - (responders-file-not-found - (request-uri req))) + (next-dispatcher)) ;; make-servlet-exception-handler: servlet-instance -> exn -> void ;; This exception handler traps all unhandled servlet exceptions @@ -214,10 +171,7 @@ ;; pull the continuation out of the table and apply it (define (invoke-servlet-continuation conn req instance-id k-id salt) (define uri (request-uri req)) - (define servlet-path - (url-path->path - servlet-root - (url-path->string (url-path uri)))) + (define-values (servlet-path _) (url->path uri)) (define the-servlet (cached-load servlet-path)) (define manager (servlet-manager the-servlet)) (thread-cell-set! current-servlet the-servlet) @@ -267,10 +221,6 @@ (thread-cell-set! current-servlet-instance-id #f) (thread-cell-set! current-servlet #f)) - ;; ************************************************************ - ;; ************************************************************ - ;; Paul's ugly loading code: - ;; cached-load : path -> script, namespace ;; timestamps are no longer checked for performance. The cache must be explicitly ;; refreshed (see dispatch). @@ -304,7 +254,6 @@ ;; A servlet-file will contain either ;;;; A signed-unit-servlet ;;;; A module servlet, currently only 'v1 - ;;;;;; (XXX: I don't know what 'typed-model-split-store0 was, so it was removed.) ;;;; A response (define (load-servlet/path a-path) (define (v0.response->v1.lambda response-path response) @@ -376,26 +325,8 @@ [else (error 'load-servlet/path "Loading ~e produced ~n~e~n instead of a servlet." a-path s)]))) - (define svt-bin-re (regexp "^/servlets(;.*\\*.*\\*.*)?/.*")) - (define (servlet-bin? str) - (regexp-match svt-bin-re str)) - - ;; return dispatcher - (lambda (conn req) - (define path (url-path->string (url-path (request-uri req)))) - (cond [(string=? "/conf/refresh-servlets" path) - ;; more here - this is broken - only out of date or specifically mentioned - ;; scripts should be flushed. This destroys persistent state! - (cache-table-clear! (unbox config:scripts)) - (output-response/method - conn - (responders-servlets-refreshed) - (request-method req))] - [(servlet-bin? path) - (adjust-connection-timeout! - conn - timeouts-servlet-connection) - ;; more here - make timeouts proportional to size of bindings - (servlet-content-producer conn req)] - [else - (next-dispatcher)])))) \ No newline at end of file + (values (lambda () + ;; XXX - this is broken - only out of date or specifically mentioned + ;; scripts should be flushed. This destroys persistent state! + (cache-table-clear! (unbox config:scripts))) + servlet-content-producer))) \ No newline at end of file diff --git a/collects/web-server/dispatchers/filesystem-map.ss b/collects/web-server/dispatchers/filesystem-map.ss new file mode 100644 index 0000000000..4f1ff6c15a --- /dev/null +++ b/collects/web-server/dispatchers/filesystem-map.ss @@ -0,0 +1,50 @@ +(module filesystem-map mzscheme + (require (lib "url.ss" "net") + (lib "list.ss") + (lib "contract.ss")) + (require "../private/util.ss") + (define url-path? + ((url?) . ->* . (path? list?))) + + (provide/contract + [make-url->path (path? . -> . url-path?)] + [make-url->path/optimism (url-path? . -> . url-path?)]) + + (define (build-path* . l) + (if (empty? l) + (build-path 'same) + (apply build-path l))) + + (define ((make-url->path base) u) + (define nbase (path->complete-path base)) + (define the-path + ; Complete it against the base + (path->complete-path + ; Build a path + (apply build-path* + ; Remove all ".."s + ; XXX Should error? + (strip-prefix-ups + (map (lambda (p) + (if (string=? "" p) 'same p)) + ; Extract the paths from the url-path + (map path/param-path + (url-path u))))) + nbase)) + (define w/o-base (path-without-base nbase the-path)) + #;(printf "~S~n" `(url->path ,base ,nbase ,(url->string u) ,the-path ,w/o-base)) + (values the-path w/o-base)) + + (define ((make-url->path/optimism url->path) u) + (let loop ([up (url-path u)]) + #;(printf "~S~n" `(url->path/optimism ,(url->string u) ,up)) + (with-handlers ([exn? (lambda (exn) + #;((error-display-handler) (exn-message exn) exn) + (if (empty? up) + (raise exn) + (loop (reverse (rest (reverse up))))))]) + (define-values (p w/o-base) + (url->path (url-replace-path (lambda _ up) u))) + (unless (or (file-exists? p) (link-exists? p)) + (raise (make-exn:fail:filesystem:exists (string->immutable-string "No valid path") (current-continuation-marks)))) + (values p w/o-base))))) \ No newline at end of file diff --git a/collects/web-server/private/configure.ss b/collects/web-server/private/configure.ss index a4768f8b5e..a6656279d0 100644 --- a/collects/web-server/private/configure.ss +++ b/collects/web-server/private/configure.ss @@ -29,7 +29,18 @@ ; - change all configuration paths (in the configure servlet and in the server) to ; use a platform independent representation (i.e. a listof strings) - ; build-suspender : (listof html) (listof html) [(listof (cons sym str))] [(listof (cons sym str))] -> str -> response + ; to convert a platform dependent path into a listof path parts such that + ; (forall x (equal? (path->list x) (path->list (apply build-path (path->list x))))) + (define (path->list p) + (let loop ([p p] [acc null]) + (let-values ([(base name must-be-dir?) (split-path p)]) + (let ([new-acc (cons name acc)]) + (cond + [(string? base) (loop base new-acc)] + [else ; conflate 'relative and #f + new-acc]))))) + + ; build-suspender : (listof html) (listof html) [(listof (cons sym str))] [(listof (cons sym str))] -> str -> response (define build-suspender (opt-lambda (title content [body-attributes '([bgcolor "white"])] [head-attributes null]) (lambda (k-url) @@ -41,7 +52,7 @@ (body ,body-attributes (form ([action ,k-url] [method "post"]) ,@content)))))) - + ; write-to-file : str TST -> void (define (write-to-file file-name x) (call-with-output-file file-name @@ -96,7 +107,7 @@ (unless (string=? "127.0.0.1" (request-host-ip request)) (send/finish access-error-page)) request) - + (define web-base (directory-part default-configuration-path)) ; more here - abstract with static pages? @@ -194,14 +205,14 @@ ; doesn't work - the browser doesn't send the port and it wouldn't be reliable anyway ; perhaps the server could include it? #;(define (switch-to-current-port old) - (let ([current-port (url-port (request-uri initial-request))]) - (and (not (= current-port (configuration-table-port old))) - (make-configuration-table - current-port - (configuration-table-max-waiting old) - (configuration-table-initial-connection-timeout old) - (configuration-table-default-host old) - (configuration-table-virtual-hosts old))))) + (let ([current-port (url-port (request-uri initial-request))]) + (and (not (= current-port (configuration-table-port old))) + (make-configuration-table + current-port + (configuration-table-max-waiting old) + (configuration-table-initial-connection-timeout old) + (configuration-table-default-host old) + (configuration-table-virtual-hosts old))))) ; send-exn : tst -> doesn't (define (send-exn exn) diff --git a/collects/web-server/private/response.ss b/collects/web-server/private/response.ss index 156c57395f..d6dd7ea700 100644 --- a/collects/web-server/private/response.ss +++ b/collects/web-server/private/response.ss @@ -180,6 +180,7 @@ data-length (response/full-body resp/f)))) + ; XXX Get rid of method restriction ;; ************************************************** ;; output-file: connection path symbol bytes -> void (define (output-file conn file-path method mime-type) diff --git a/collects/web-server/private/util.ss b/collects/web-server/private/util.ss index ba7a4bcc2c..0142f85a4e 100644 --- a/collects/web-server/private/util.ss +++ b/collects/web-server/private/util.ss @@ -1,21 +1,68 @@ (module util mzscheme (require (lib "list.ss") + (lib "plt-match.ss") (lib "contract.ss") (lib "string.ss") (lib "url.ss" "net")) (provide url-replace-path) (provide/contract + [path-without-base (path? path? . -> . list?)] + [list-prefix (list? list? . -> . (or/c list? false/c))] + [strip-prefix-ups (list? . -> . list?)] ; XXX need path-element? [url-path->string ((listof (or/c string? path/param?)) . -> . string?)] [extract-flag (symbol? (listof (cons/c symbol? any/c)) any/c . -> . any/c)] [network-error ((symbol? string?) (listof any/c) . ->* . (void))] - [path->list (path? . -> . (cons/c (or/c path? (symbols 'up 'same)) - (listof (or/c path? (symbols 'up 'same)))))] [directory-part (path? . -> . path?)] [lowercase-symbol! ((or/c string? bytes?) . -> . symbol?)] [exn->string ((or/c exn? any/c) . -> . string?)] [build-path-unless-absolute (path-string? path-string? . -> . path?)]) + ; explode-path* : path? -> (listof path?) + (define (explode-path* p) + (let loop ([p p] [r empty]) + (cond + [(eq? 'relative p) r] + [(not p) r] + [else + (let-values ([(base name dir?) (split-path p)]) + (loop base (list* name r)))]))) + + ; strip-prefix-ups : (listof path-element?) -> (listof path-element?) + (define (strip-prefix-ups l) + (define prefix? (box #t)) + (filter (lambda (p) + (if (unbox prefix?) + (if (eq? 'up p) + #f + (begin #t + (set-box! prefix? #f))) + #t)) + l)) + + ; list-prefix : list? list? -> (or/c list? false/c) + ; Is l a prefix or r?, and what is that prefix? + (define (list-prefix ls rs) + (match ls + [(list) + (list)] + [(list-rest l0 ls) + (match rs + [(list) + #f] + [(list-rest r0 rs) + (if (equal? l0 r0) + (let ([ps (list-prefix ls rs)]) + (if ps (list* l0 ps) (list l0))) + #f)])])) + + ; path-without-base : path? path? -> (listof path-element?) + ; Expects paths in normal form + (define (path-without-base base path) + (define b (explode-path* base)) + (define p (explode-path* path)) + (list-tail p (length (list-prefix b p)))) + ;; replace-path: (url-path -> url-path) url -> url ;; make a new url by replacing the path part of a url with a function ;; of the url's old path @@ -86,18 +133,7 @@ [(eq? 'relative base) (current-directory)] [(not base) (error 'directory-part "~a is a top-level directory" path)] [(path? base) base]))) - - ; to convert a platform dependent path into a listof path parts such that - ; (forall x (equal? (path->list x) (path->list (apply build-path (path->list x))))) - (define (path->list p) - (let loop ([p p] [acc null]) - (let-values ([(base name must-be-dir?) (split-path p)]) - (let ([new-acc (cons name acc)]) - (cond - [(string? base) (loop base new-acc)] - [else ; conflate 'relative and #f - new-acc]))))) - + ; this is used by launchers ; extract-flag : sym (listof (cons sym alpha)) alpha -> alpha ; XXX remove diff --git a/collects/web-server/prototype-web-server/dispatch-servlets2.ss b/collects/web-server/prototype-web-server/dispatch-servlets2.ss index 8543ad774e..8c891b80e7 100644 --- a/collects/web-server/prototype-web-server/dispatch-servlets2.ss +++ b/collects/web-server/prototype-web-server/dispatch-servlets2.ss @@ -2,7 +2,6 @@ (require (lib "kw.ss") (lib "contract.ss") (lib "url.ss" "net") - (lib "plt-match.ss") (lib "request-structs.ss" "web-server") (lib "session.ss" "web-server" "prototype-web-server" "private") (only "private/web.ss" @@ -24,44 +23,40 @@ (define interface-version 'v1) (define/kw (make #:key - [htdocs-path "servlets"] + url->path [make-servlet-namespace (make-make-servlet-namespace)] [timeouts-servlet-connection (* 60 60 24)] [responders-servlet-loading servlet-loading-responder] [responders-servlet - (gen-servlet-responder "servlet-error.html")] - [responders-file-not-found - (gen-file-not-found-responder "not-found.html")]) + (gen-servlet-responder "servlet-error.html")]) ;; dispatch : connection request -> void (define (dispatch conn req) (define uri (request-uri req)) (adjust-connection-timeout! conn timeouts-servlet-connection) ;; XXX - make timeouts proportional to size of bindings - (with-handlers ([void - (lambda (the-exn) - (output-response/method - conn - (responders-servlet-loading uri the-exn) - (request-method req)))]) - (cond - [(extract-session uri) - => (lambda (session-id) - (resume-session session-id conn req))] - [else - (begin-session conn req)]))) + (cond + [(extract-session uri) + => (lambda (session-id) + (resume-session session-id conn req))] + [else + (begin-session conn req)])) ;; XXX Currently there are just sessions, should be servlets and sessions ;; begin-session: connection request (define (begin-session conn req) (define uri (request-uri req)) - (define-values (a-path url-servlet-path url-path-suffix) - ; XXX Abstract this, so they don't need to live on disk. - (url->servlet-path htdocs-path uri)) - (if a-path + (with-handlers ([void (lambda (exn) (next-dispatcher))]) + (define-values (a-path url-servlet-path) (url->path uri)) + (with-handlers ([void + (lambda (the-exn) + (output-response/method + conn + (responders-servlet-loading uri the-exn) + (request-method req)))]) (parameterize ([current-directory (directory-part a-path)]) (define cust (make-custodian top-cust)) (define ns (make-servlet-namespace @@ -71,7 +66,7 @@ (lib "abort-resume.ss" "web-server" "prototype-web-server" "private") (lib "session.ss" "web-server" "prototype-web-server" "private") (lib "request.ss" "web-server" "private")))) - (define ses (new-session cust ns (make-session-url uri url-servlet-path))) + (define ses (new-session cust ns (make-session-url uri (map path->string url-servlet-path)))) (parameterize ([current-custodian cust] [current-namespace ns] [current-session ses]) @@ -80,33 +75,16 @@ 'start)) (set-session-servlet! ses (initialize-servlet start))) (resume-session (session-id ses) - conn req)) - (output-response/method - conn - (responders-file-not-found uri) - (request-method req)))) + conn req))))) ; same-servlet? : url? url? -> boolean? (define (same-servlet? req ses) (define (abstract-url u) (map path/param-path (url-path u))) - (define ans - (let loop ([rp (abstract-url req)] - [sp (abstract-url ses)]) - (match sp - [(list) - #t] - [(list-rest s sp) - (match rp - [(list) - #f] - [(list-rest r rp) - (if (string=? s r) - (loop rp sp) - #f)])]))) + (define ans (list-prefix (abstract-url ses) (abstract-url req))) #;(printf "~S => ~S~n" `(same-servlet? ,(url->string req) ,(url->string ses)) ans) - ans) + (and ans #t)) ;; resume-session: number connection request (define (resume-session ses-id conn req) diff --git a/collects/web-server/prototype-web-server/private/utils.ss b/collects/web-server/prototype-web-server/private/utils.ss index e936777c72..1df80e0684 100644 --- a/collects/web-server/prototype-web-server/private/utils.ss +++ b/collects/web-server/prototype-web-server/private/utils.ss @@ -1,16 +1,13 @@ (module utils mzscheme (require (lib "contract.ss") (lib "url.ss" "net") - (lib "plt-match.ss") (lib "list.ss") (lib "serialize.ss")) (provide/contract [read/string (string? . -> . serializable?)] [write/string (serializable? . -> . string?)] - [url->servlet-path ((path? url?) . ->* . ((or/c path? false/c) (or/c (listof string?) false/c) (or/c (listof string?) false/c)))] - [make-session-url (url? (listof string?) . -> . url?)] - [split-url-path (url? url? . -> . (or/c (listof string?) false/c))]) + [make-session-url (url? (listof string?) . -> . url?)]) (define (read/string str) (read (open-input-string str))) @@ -35,91 +32,4 @@ new-path) '() #f - )) - - ;; build-root-path: -> path - ;; build the root path for whatever this OS is - (define (build-root-path) - (let loop ([prev (simplify-path (build-path 'same))] - [next (simplify-path (build-path 'up))]) - (if (equal? prev next) - prev - (loop next - (simplify-path (build-path next 'up)))))) - - (define the-root-path (build-root-path)) - - ;; simplify-url-path: url -> (listof string) - ;; take the dots out of the url-path - ;; Note: we simplify the url path relative to a hypothetical root, - ;; so that a malicious url can't cause the server to chase ".." - ;; up beyond the legitimate servlet root. - (define (simplify-url-path uri) - (path->list - (simplify-path - (apply build-path - (cons the-root-path - (map - (lambda (str) - (if (string=? str "") - 'same - str)) - (map - (lambda (path-elt) - (if (path/param? path-elt) - (path/param-path path-elt) - path-elt)) - (url-path uri)))))))) - - ;; path->list pth - ;; convert an absolute path to a list of strings - (define (path->list pth) - (reverse - (let path->list ([pth pth]) - (let-values ([(base name must-be-dir?) (split-path pth)]) - (if base - (cons (path->string name) (path->list base)) - '()))))) - - - ;; url->servlet-path: path url -> (values (union path #f) - ;; (union (listof url->string) #f) - ;; (union (listof string) #f)) - ;; Given a servlet directory and url, find a servlet. - ;; The first value is the servlet path. - ;; The second value is the prefix of the url-path used to find the servlet. - ;; The third value is the remaining suffix of the url-path. - (define (url->servlet-path servlet-dir uri) - #;(printf "~S~n" `(url->servlet-path ,servlet-dir ,uri)) - #;(printf " current-directory = ~s~n" (current-directory)) - (let loop ([base-path servlet-dir] - [servlet-path '()] - [path-list (simplify-url-path uri)]) - #;(printf "~S~n" `(loop ,base-path ,servlet-path ,path-list)) - (match path-list - [(list) - (values #f #f #f)] - [(list-rest next-path-segment rest-of-path) - (let ([new-base (build-path base-path next-path-segment)]) - #;(printf " new-base = ~s~n" new-base) - (cond - [(file-exists? new-base) - (values new-base - (reverse (list* next-path-segment servlet-path)) - rest-of-path)] - [else (loop new-base - (list* next-path-segment servlet-path) - rest-of-path)]))]))) - - ;; split-url-path: url url -> (union (listof string) #f) - ;; the first url's path is a prefix of the path of the second - ;; find the suffix and return it as a list of strings - (define (split-url-path pref-url suff-url) - (let loop ([pref-path (simplify-url-path pref-url)] - [suff-path (simplify-url-path suff-url)]) - (cond - [(null? pref-path) suff-path] - [(string=? (car pref-path) (car suff-path)) - (loop (cdr pref-path) (cdr suff-path))] - [else - (error "split-url-path: first path is not a preffix of the second")])))) \ No newline at end of file + ))) \ No newline at end of file diff --git a/collects/web-server/prototype-web-server/run.ss b/collects/web-server/prototype-web-server/run.ss index 2f5937629f..b5a80f4cd8 100644 --- a/collects/web-server/prototype-web-server/run.ss +++ b/collects/web-server/prototype-web-server/run.ss @@ -1,30 +1,34 @@ (module run mzscheme (require (lib "web-server.ss" "web-server") (lib "responders.ss" "web-server" "configuration") + (prefix fsmap: (lib "filesystem-map.ss" "web-server" "dispatchers")) (prefix files: (lib "dispatch-files.ss" "web-server" "dispatchers")) (prefix filter: (lib "dispatch-filter.ss" "web-server" "dispatchers")) + (prefix const: (lib "dispatch-const.ss" "web-server" "dispatchers")) (prefix sequencer: (lib "dispatch-sequencer.ss" "web-server" "dispatchers")) (prefix servlets2: (lib "dispatch-servlets2.ss" "web-server" "prototype-web-server"))) (define server-root-path (build-path "~" "Development" "plt" "default-web-root")) (define default-host-path (build-path server-root-path "conf")) - (define htdocs-path (build-path server-root-path "htdocs")) (define file-not-found-file (build-path default-host-path "not-found.html")) (define servlet-error-file (build-path default-host-path "servlet-error.html")) - + + (define url->path + (fsmap:make-url->path + (build-path server-root-path "htdocs"))) + (serve #:port 8080 #:dispatch (sequencer:make (filter:make #rx"\\.ss" - (servlets2:make #:htdocs-path htdocs-path + (servlets2:make #:url->path (fsmap:make-url->path/optimism url->path) #:timeouts-servlet-connection 86400 #:responders-servlet-loading (gen-servlet-responder servlet-error-file) - #:responders-servlet (gen-servlet-responder servlet-error-file) - #:responders-file-not-found (gen-file-not-found-responder file-not-found-file))) - (files:make #:htdocs-path htdocs-path + #:responders-servlet (gen-servlet-responder servlet-error-file))) + (files:make #:url->path url->path #:mime-types-path (build-path server-root-path "mime.types") - #:indices (list "index.html" "index.htm") - #:file-not-found-responder (gen-file-not-found-responder file-not-found-file)))) + #:indices (list "index.html" "index.htm")) + (const:make (gen-file-not-found-responder file-not-found-file)))) (do-not-return)) \ No newline at end of file diff --git a/collects/web-server/web-server-unit.ss b/collects/web-server/web-server-unit.ss index 2a069ab5a0..d7cc47f1bc 100644 --- a/collects/web-server/web-server-unit.ss +++ b/collects/web-server/web-server-unit.ss @@ -8,23 +8,25 @@ "private/web-server-structs.ss" "configuration/configuration-table-structs.ss" "private/cache-table.ss" - (rename "private/request.ss" - the-read-request read-request)) - (require (prefix sequencer: "dispatchers/dispatch-sequencer.ss") + (prefix http: "private/request.ss")) + (require (prefix fsmap: "dispatchers/filesystem-map.ss") + (prefix sequencer: "dispatchers/dispatch-sequencer.ss") (prefix passwords: "dispatchers/dispatch-passwords.ss") (prefix files: "dispatchers/dispatch-files.ss") (prefix servlets: "dispatchers/dispatch-servlets.ss") (prefix path-procedure: "dispatchers/dispatch-pathprocedure.ss") (prefix log: "dispatchers/dispatch-log.ss") - (prefix host: "dispatchers/dispatch-host.ss")) + (prefix host: "dispatchers/dispatch-host.ss") + (prefix filter: "dispatchers/dispatch-filter.ss") + (prefix const: "dispatchers/dispatch-const.ss")) (provide web-server@) - + (define-unit web-config@->dispatch-server-config@ (import (prefix config: web-config^)) (export dispatch-server-config^) (init-depend web-config^) - (define read-request the-read-request) + (define read-request http:read-request) (define port config:port) (define listen-ip config:listen-ip) @@ -48,26 +50,41 @@ (sequencer:make (log:make #:log-format (host-log-format host-info) #:log-path (host-log-path host-info)) - (passwords:make #:password-file (host-passwords host-info) - #:password-connection-timeout (timeouts-password (host-timeouts host-info)) - #:authentication-responder (responders-authentication (host-responders host-info)) - #:passwords-refresh-responder (responders-passwords-refreshed (host-responders host-info))) + (let-values ([(update-password-cache! password-check) + (passwords:make #:password-file (host-passwords host-info) + #:password-connection-timeout (timeouts-password (host-timeouts host-info)) + #:authentication-responder (responders-authentication (host-responders host-info)))]) + (sequencer:make + password-check + (path-procedure:make "/conf/refresh-passwords" + (lambda _ + (update-password-cache!) + ((responders-passwords-refreshed (host-responders host-info))))))) (path-procedure:make "/conf/collect-garbage" - (lambda () + (lambda _ (collect-garbage) ((responders-collect-garbage (host-responders host-info))))) - (servlets:make config:instances config:scripts config:make-servlet-namespace - #:servlet-root (paths-servlet (host-paths host-info)) - #:responders-servlets-refreshed (responders-servlets-refreshed (host-responders host-info)) - #:responders-servlet-loading (responders-servlet-loading (host-responders host-info)) - #:responders-servlet (responders-servlet (host-responders host-info)) - #:responders-file-not-found (responders-file-not-found (host-responders host-info)) - #:timeouts-servlet-connection (timeouts-servlet-connection (host-timeouts host-info)) - #:timeouts-default-servlet (timeouts-default-servlet (host-timeouts host-info))) - (files:make #:htdocs-path (paths-htdocs (host-paths host-info)) + (let-values ([(clear-cache! servlet-dispatch) + (servlets:make config:instances config:scripts config:make-servlet-namespace + #:url->path + (fsmap:make-url->path/optimism + (fsmap:make-url->path (paths-servlet (host-paths host-info)))) + #:responders-servlet-loading (responders-servlet-loading (host-responders host-info)) + #:responders-servlet (responders-servlet (host-responders host-info)) + #:timeouts-servlet-connection (timeouts-servlet-connection (host-timeouts host-info)) + #:timeouts-default-servlet (timeouts-default-servlet (host-timeouts host-info)))]) + (sequencer:make + (path-procedure:make "/conf/refresh-servlets" + (lambda _ + (clear-cache!) + ((responders-servlets-refreshed (host-responders host-info))))) + (filter:make + #rx"^/servlets" + servlet-dispatch))) + (files:make #:url->path (fsmap:make-url->path (paths-htdocs (host-paths host-info))) #:mime-types-path (paths-mime-types (host-paths host-info)) - #:indices (host-indices host-info) - #:file-not-found-responder (responders-file-not-found (host-responders host-info)))))) + #:indices (host-indices host-info)) + (const:make (responders-file-not-found (host-responders host-info)))))) (define-compound-unit/infer web-server@ (import tcp^ web-config^)