racket/collects/web-server/servlet-env.ss
Jay McCarthy fe078ee54b stuffers
svn: r13474
2009-02-06 23:23:21 +00:00

291 lines
11 KiB
Scheme

; Derived from plai/web/server, which was based on an older version of this
; Also derived from planet/untyped/instaservlet
#lang scheme
(require (prefix-in net: net/sendurl)
scheme/contract
scheme/list
scheme/unit
scheme/serialize
net/tcp-unit
net/tcp-sig
scheme/runtime-path
net/ssl-tcp-unit)
(require web-server/web-server
web-server/managers/lru
web-server/managers/manager
web-server/configuration/namespace
web-server/http
web-server/stuffers
web-server/configuration/responders
web-server/private/mime-types
web-server/servlet/setup
web-server/dispatchers/dispatch
(prefix-in lift: web-server/dispatchers/dispatch-lift)
(prefix-in fsmap: web-server/dispatchers/filesystem-map)
(prefix-in sequencer: web-server/dispatchers/dispatch-sequencer)
(prefix-in files: web-server/dispatchers/dispatch-files)
(prefix-in filter: web-server/dispatchers/dispatch-filter)
(prefix-in servlets: web-server/dispatchers/dispatch-servlets)
(prefix-in log: web-server/dispatchers/dispatch-log))
(define send-url (make-parameter net:send-url))
(define (quit-server sema)
(lift:make
(lambda (request)
(thread (lambda () (sleep 2) (semaphore-post sema)))
`(html (head (title "Server Stopped")
(link ([rel "stylesheet"] [href "/error.css"])))
(body (div ([class "section"])
(div ([class "title"]) "Server Stopped")
(p "Return to DrScheme.")))))))
(define-runtime-path default-web-root
(list 'lib
"web-server/default-web-root"))
(provide/contract
[dispatch/servlet (((request? . -> . response/c))
(#:regexp regexp?
#:current-directory path-string?
#:namespace (listof module-path?)
#:stateless? boolean?
#:stuffer (stuffer/c serializable? bytes?)
#:manager manager?)
. ->* .
dispatcher/c)]
[serve/launch/wait (((semaphore? . -> . dispatcher/c))
(#:launch-path (or/c false/c string?)
#:banner? boolean?
#:listen-ip (or/c false/c string?)
#:port number?
#:ssl-keys (or/c false/c (cons/c path-string? path-string?)))
. ->* .
void)]
[serve/servlet (((request? . -> . response/c))
(#:command-line? boolean?
#:launch-browser? boolean?
#:quit? boolean?
#:banner? boolean?
#:listen-ip (or/c false/c string?)
#:port number?
#:ssl? boolean?
#:manager manager?
#:servlet-namespace (listof module-path?)
#:server-root-path path-string?
#:stateless? boolean?
#:stuffer (stuffer/c serializable? bytes?)
#:extra-files-paths (listof path-string?)
#:servlets-root path-string?
#:servlet-current-directory path-string?
#:file-not-found-responder (request? . -> . response/c)
#:mime-types-path path-string?
#:servlet-path string?
#:servlet-regexp regexp?
#:log-file (or/c false/c path-string?)
#:log-format log:log-format/c)
. ->* .
void)])
;; utility for conveniently chaining dispatchers
(define (dispatcher-sequence . dispatchers)
(let loop ([ds dispatchers] [r '()])
(cond [(null? ds) (apply sequencer:make (reverse r))]
[(not (car ds)) (loop (cdr ds) r)]
[(list? (car ds)) (loop (append (car ds) (cdr ds)) r)]
[else (loop (cdr ds) (cons (car ds) r))])))
(define (dispatch/servlet
start
#:regexp
[servlet-regexp #rx""]
#:current-directory
[servlet-current-directory (current-directory)]
#:namespace
[servlet-namespace empty]
#:stateless?
[stateless? #f]
#:stuffer
[stuffer default-stuffer]
#:manager
[manager
(make-threshold-LRU-manager
(lambda (request)
`(html (head (title "Page Has Expired."))
(body (p "Sorry, this page has expired. Please go back."))))
(* 64 1024 1024))])
(define servlet-box (box #f))
(define make-servlet-namespace
(make-make-servlet-namespace #:to-be-copied-module-specs servlet-namespace))
(filter:make
servlet-regexp
(servlets:make
(lambda (url)
(or (unbox servlet-box)
(let ([servlet
(parameterize ([current-custodian (make-custodian)]
[current-namespace
(make-servlet-namespace
#:additional-specs
default-module-specs)])
(if stateless?
(make-stateless.servlet servlet-current-directory stuffer start)
(make-v2.servlet servlet-current-directory manager start)))])
(set-box! servlet-box servlet)
servlet))))))
(define (serve/launch/wait
dispatcher
#:launch-path
[launch-path #f]
#:banner?
[banner? #t]
#:listen-ip
[listen-ip "127.0.0.1"]
#:port
[port 8000]
#:ssl-keys
[ssl-keys #f])
(define ssl? (pair? ssl-keys))
(define server-url
(string-append (if ssl? "https" "http")
"://localhost"
(if (and (not ssl?) (= port 80))
"" (format ":~a" port))))
(define sema (make-semaphore 0))
(define shutdown-server
(serve #:dispatch (dispatcher sema)
#:listen-ip listen-ip
#:port port
#:tcp@ (if ssl?
(let ()
(define-unit-binding ssl-tcp@
(make-ssl-tcp@
(car ssl-keys) (cdr ssl-keys)
#f #f #f #f #f)
(import) (export tcp^))
ssl-tcp@)
tcp@)))
(when launch-path
((send-url) (string-append server-url launch-path) #t))
(when banner?
(printf "Your Web application is running at ~a.\n"
(if launch-path
(string-append server-url launch-path)
server-url))
(printf "Click 'Stop' at any time to terminate the Web Server.\n"))
(let ([bye (lambda ()
(when banner? (printf "\nWeb Server stopped.\n"))
(shutdown-server))])
(with-handlers ([exn:break? (lambda (exn) (bye))])
(semaphore-wait/enable-break sema)
;; We can get here if a /quit url is visited
(bye))))
(define (serve/servlet
start
#:command-line?
[command-line? #f]
#:launch-browser?
[launch-browser? (not command-line?)]
#:quit?
[quit? (not command-line?)]
#:banner?
[banner? (not command-line?)]
#:listen-ip
[listen-ip "127.0.0.1"]
#:port
[the-port 8000]
#:ssl?
[ssl? #f]
#:manager
[manager
(make-threshold-LRU-manager
(lambda (request)
`(html (head (title "Page Has Expired."))
(body (p "Sorry, this page has expired. Please go back."))))
(* 64 1024 1024))]
#:servlet-path
[servlet-path "/servlets/standalone.ss"]
#:servlet-regexp
[servlet-regexp (regexp (format "^~a$" (regexp-quote servlet-path)))]
#:stateless?
[stateless? #f]
#:stuffer
[stuffer default-stuffer]
#:servlet-namespace
[servlet-namespace empty]
#:server-root-path
[server-root-path default-web-root]
#:extra-files-paths
[extra-files-paths (list (build-path server-root-path "htdocs"))]
#:servlets-root
[servlets-root (build-path server-root-path "htdocs")]
#:servlet-current-directory
[servlet-current-directory servlets-root]
#:file-not-found-responder
[file-not-found-responder
(gen-file-not-found-responder
(build-path server-root-path "conf" "not-found.html"))]
#:mime-types-path
[mime-types-path (let ([p (build-path server-root-path "mime.types")])
(if (file-exists? p)
p
(build-path default-web-root "mime.types")))]
#:log-file
[log-file #f]
#:log-format
[log-format 'apache-default])
(define (dispatcher sema)
(dispatcher-sequence
(and log-file (log:make #:format (log:log-format->format log-format)
#:log-path log-file))
(and quit? (filter:make #rx"^/quit$" (quit-server sema)))
(dispatch/servlet
start
#:regexp servlet-regexp
#:namespace servlet-namespace
#:stateless? stateless?
#:stuffer stuffer
#:current-directory servlet-current-directory
#:manager manager)
(let-values ([(clear-cache! url->servlet)
(servlets:make-cached-url->servlet
(fsmap:filter-url->path
#rx"\\.(ss|scm)$"
(fsmap:make-url->valid-path
(fsmap:make-url->path servlets-root)))
(make-default-path->servlet
#:make-servlet-namespace
(make-make-servlet-namespace #:to-be-copied-module-specs servlet-namespace)))])
(servlets:make url->servlet))
(map (lambda (extra-files-path)
(files:make
#:url->path (fsmap:make-url->path extra-files-path)
#:path->mime-type (make-path->mime-type mime-types-path)
#:indices (list "index.html" "index.htm")))
extra-files-paths)
(files:make
#:url->path (fsmap:make-url->path (build-path server-root-path "htdocs"))
#:path->mime-type (make-path->mime-type mime-types-path)
#:indices (list "index.html" "index.htm"))
(lift:make file-not-found-responder)))
(serve/launch/wait
dispatcher
#:launch-path (if launch-browser? servlet-path #f)
#:banner? banner?
#:listen-ip listen-ip
#:port the-port
#:ssl-keys
(if ssl?
(cons (build-path server-root-path "server-cert.pem")
(build-path server-root-path "private-key.pem"))
#f)))