Updating docs
This commit is contained in:
parent
9b066d0f04
commit
a28cf7df10
|
@ -2,6 +2,7 @@
|
|||
(require racket/runtime-path
|
||||
net/url
|
||||
web-server/private/xexpr
|
||||
web-server/http/xexpr
|
||||
web-server/http/response-structs
|
||||
web-server/http/request-structs)
|
||||
|
||||
|
@ -24,22 +25,23 @@
|
|||
"web-server/default-web-root/htdocs/error.css"))
|
||||
|
||||
(define (pretty-exception-response url exn)
|
||||
`(html
|
||||
(head
|
||||
(title "Servlet Error")
|
||||
(link ([rel "stylesheet"] [href "/error.css"])))
|
||||
(body
|
||||
(div ([class "section"])
|
||||
(div ([class "title"]) "Exception")
|
||||
(p
|
||||
"The application raised an exception with the message:"
|
||||
(pre ,(if (exn:pretty? exn)
|
||||
(exn:pretty-xexpr exn)
|
||||
(exn-message exn))))
|
||||
(p
|
||||
"Stack trace:"
|
||||
,(format-stack-trace
|
||||
(continuation-mark-set->context (exn-continuation-marks exn))))))))
|
||||
(response/xexpr
|
||||
`(html
|
||||
(head
|
||||
(title "Servlet Error")
|
||||
(link ([rel "stylesheet"] [href "/error.css"])))
|
||||
(body
|
||||
(div ([class "section"])
|
||||
(div ([class "title"]) "Exception")
|
||||
(p
|
||||
"The application raised an exception with the message:"
|
||||
(pre ,(if (exn:pretty? exn)
|
||||
(exn:pretty-xexpr exn)
|
||||
(exn-message exn))))
|
||||
(p
|
||||
"Stack trace:"
|
||||
,(format-stack-trace
|
||||
(continuation-mark-set->context (exn-continuation-marks exn)))))))))
|
||||
|
||||
|
||||
; file-response : nat str str [(cons sym str) ...] -> response
|
||||
|
|
|
@ -10,4 +10,4 @@
|
|||
200 #"Okay"
|
||||
(current-seconds) TEXT/HTML-MIME-TYPE
|
||||
empty
|
||||
(list (include-template "static.html"))))
|
||||
(list (string->bytes/utf-8 (include-template "static.html")))))
|
||||
|
|
|
@ -1,8 +1,13 @@
|
|||
#lang racket
|
||||
(require web-server/templates)
|
||||
(require web-server/templates
|
||||
web-server/http)
|
||||
(provide (all-defined-out))
|
||||
(define interface-version 'v1)
|
||||
(define timeout +inf.0)
|
||||
|
||||
(define (start initial-request)
|
||||
(response/template (include-template "static.html")))
|
||||
(response/full
|
||||
200 #"Okay"
|
||||
(current-seconds) TEXT/HTML-MIME-TYPE
|
||||
empty
|
||||
(list (string->bytes/utf-8 (include-template "static.html")))))
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#lang racket
|
||||
(require web-server/templates
|
||||
web-server/http
|
||||
xml)
|
||||
(provide (all-defined-out))
|
||||
(define interface-version 'v1)
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
(require "util.rkt"
|
||||
web-server/http)
|
||||
(provide/contract
|
||||
[read-mime-types (path-string? . -> . hash?)]
|
||||
[read-mime-types (path-string? . -> . (hash/c symbol? bytes?))]
|
||||
[make-path->mime-type (path-string? . -> . (path? . -> . bytes?))])
|
||||
|
||||
; read-mime-types : path? -> hash-table?
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
@title[#:tag "servlet-structs"]{Common Contracts}
|
||||
@(require (for-label web-server/servlet/servlet-structs
|
||||
web-server/http
|
||||
web-server/servlet))
|
||||
|
||||
@defmodule[web-server/servlet/servlet-structs]{
|
||||
|
@ -19,10 +20,11 @@ Example: @racket["http://localhost:8080/servlets;1*1*20131636/examples/add.rkt"]
|
|||
Equivalent to @racket[(k-url? . -> . response?)].
|
||||
|
||||
Example: @racketblock[(lambda (k-url)
|
||||
`(html
|
||||
(body
|
||||
(a ([href ,k-url])
|
||||
"Click Me to Invoke the Continuation!"))))]
|
||||
(response/xexpr
|
||||
`(html
|
||||
(body
|
||||
(a ([href ,k-url])
|
||||
"Click Me to Invoke the Continuation!")))))]
|
||||
}
|
||||
|
||||
@defthing[expiration-handler/c contract?]{
|
||||
|
@ -31,10 +33,11 @@ Equivalent to @racket[(or/c false/c (request? . -> . response?))].
|
|||
Typically @racket[#f] uses the default expiration handler, which displays an error message.
|
||||
|
||||
Example: @racketblock[(lambda (req)
|
||||
`(html (head (title "Expired"))
|
||||
(body (h1 "Expired")
|
||||
(p "This URL has expired. "
|
||||
"Please return to the home page."))))]
|
||||
(response/xexpr
|
||||
`(html (head (title "Expired"))
|
||||
(body (h1 "Expired")
|
||||
(p "This URL has expired. "
|
||||
"Please return to the home page.")))))]
|
||||
}
|
||||
|
||||
@defthing[embed/url/c contract?]{
|
||||
|
|
|
@ -1,5 +1,12 @@
|
|||
#lang scribble/doc
|
||||
@(require "web-server.rkt")
|
||||
@(require "web-server.rkt"
|
||||
(for-label web-server/http
|
||||
web-server/private/servlet
|
||||
web-server/configuration/responders
|
||||
web-server/dispatchers/filesystem-map
|
||||
web-server/servlet/setup
|
||||
net/url
|
||||
web-server/managers/manager))
|
||||
|
||||
@title[#:tag "dispatch-servlets"]{Serving Servlets}
|
||||
@a-dispatcher[web-server/dispatchers/dispatch-servlets
|
||||
|
|
|
@ -431,19 +431,20 @@ These @tech{formlet}s are the main combinators for form input.
|
|||
|
||||
@section{Utilities}
|
||||
|
||||
@(require (for-label web-server/formlets/servlet))
|
||||
@(require (for-label web-server/formlets/servlet
|
||||
web-server/http))
|
||||
@defmodule[web-server/formlets/servlet]{
|
||||
|
||||
A few utilities are provided for using @tech{formlet}s in Web applications.
|
||||
|
||||
@defproc[(send/formlet [f (formlet/c any/c ...)]
|
||||
[#:wrap wrapper
|
||||
(xexpr/c . -> . response?)
|
||||
(xexpr/c . -> . xexpr/c)
|
||||
(lambda (form-xexpr)
|
||||
`(html (head (title "Form Entry"))
|
||||
(body ,form-xexpr)))])
|
||||
(values any/c ...)]{
|
||||
Uses @racket[send/suspend] to send @racket[f]'s rendering (wrapped in a FORM tag whose action is
|
||||
Uses @racket[send/suspend] and @racket[response/xexpr] to send @racket[f]'s rendering (wrapped in a FORM tag whose action is
|
||||
the continuation URL (wrapped again by @racket[wrapper])) to the client.
|
||||
When the form is submitted, the request is passed to the
|
||||
processing stage of @racket[f].
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
@defmodule[web-server/http]
|
||||
|
||||
The @web-server implements many HTTP RFCs that are provided by this module.
|
||||
The @web-server implements many HTTP libraries that are provided by this module.
|
||||
|
||||
@; ------------------------------------------------------------
|
||||
@section[#:tag "request-structs"]{Requests}
|
||||
|
@ -159,35 +159,37 @@ Here is an example typical of what you will find in many applications:
|
|||
|
||||
@defmodule[web-server/http/response-structs]{
|
||||
|
||||
@defstruct[response/basic
|
||||
@defstruct*[response
|
||||
([code number?]
|
||||
[message bytes?]
|
||||
[seconds number?]
|
||||
[mime bytes?]
|
||||
[headers (listof header?)])]{
|
||||
A basic HTTP response containing no body. @racket[code] is the response code,
|
||||
[headers (listof header?)]
|
||||
[output (output-port? . -> . void)])]{
|
||||
An HTTP response where @racket[output] produces the body. @racket[code] is the response code,
|
||||
@racket[message] the message, @racket[seconds] the generation time, @racket[mime]
|
||||
the MIME type of the file, and @racket[extras] are the extra headers, in addition
|
||||
to those produced by the server.
|
||||
|
||||
Example:
|
||||
@racketblock[
|
||||
(make-response/basic
|
||||
(response
|
||||
301 #"Moved Permanently"
|
||||
(current-seconds) TEXT/HTML-MIME-TYPE
|
||||
(list (make-header #"Location"
|
||||
#"http://racket-lang.org/downloads")))
|
||||
#"http://racket-lang.org/downloads"))
|
||||
(λ (op) (write-bytes #"Moved" op)))
|
||||
]
|
||||
}
|
||||
|
||||
@defstruct[(response/full response/basic)
|
||||
([body (listof bytes?)])]{
|
||||
As with @racket[response/basic], except with @racket[body] as the response
|
||||
body.
|
||||
|
||||
|
||||
@defproc[(response/full [code number?] [message bytes?] [seconds number?] [mime bytes?]
|
||||
[headers (listof header?)] [body (listof bytes?)])
|
||||
response?]{
|
||||
A constructor for responses where @racket[body] is the response body.
|
||||
|
||||
Example:
|
||||
@racketblock[
|
||||
(make-response/full
|
||||
(response/full
|
||||
301 #"Moved Permanently"
|
||||
(current-seconds) TEXT/HTML-MIME-TYPE
|
||||
(list (make-header #"Location"
|
||||
|
@ -199,83 +201,6 @@ Here is an example typical of what you will find in many applications:
|
|||
#"</p></body></html>"))
|
||||
]
|
||||
}
|
||||
|
||||
@defstruct[(response/port response/basic)
|
||||
([output (output-port? . -> . void)])]{
|
||||
As with @racket[response/basic], except where @racket[output] generates the response
|
||||
body. This response type is not as safe and efficient for clients as @racket[response/incremental],
|
||||
but can be convenient on the server side.
|
||||
|
||||
Example:
|
||||
@racketblock[
|
||||
(make-response/full
|
||||
301 #"Moved Permanently"
|
||||
(current-seconds) TEXT/HTML-MIME-TYPE
|
||||
(list (make-header #"Location"
|
||||
#"http://racket-lang.org/downloads"))
|
||||
(λ (op)
|
||||
(write-bytes #"<html><body><p>" op)
|
||||
(write-bytes #"Please go to <a href=\"" op)
|
||||
(write-bytes #"http://racket-lang.org/downloads" op)
|
||||
(write-bytes #"\">here</a> instead." op)
|
||||
(write-bytes #"</p></body></html>" op)))
|
||||
]
|
||||
}
|
||||
|
||||
@defstruct[(response/incremental response/basic)
|
||||
([generator ((() () #:rest (listof bytes?) . ->* . any) . -> . any)])]{
|
||||
As with @racket[response/basic], except with @racket[generator] as a function that is
|
||||
called to generate the response body, by being given an @racket[output-response] function
|
||||
that outputs the content it is called with. If the @racket[output-response] function is called
|
||||
with arguments of zero length (when concatenated), then the output port is flushed with
|
||||
@racket[flush-output].
|
||||
|
||||
Here is a short example:
|
||||
@racketblock[
|
||||
(make-response/incremental
|
||||
200 #"OK" (current-seconds)
|
||||
#"application/octet-stream"
|
||||
(list (make-header #"Content-Disposition"
|
||||
#"attachment; filename=\"file\""))
|
||||
(lambda (output-response)
|
||||
(output-response #"Some content")
|
||||
(output-response)
|
||||
(output-response #"Even" #"more" #"content!")
|
||||
(output-response #"Now we're done")))
|
||||
]
|
||||
}
|
||||
|
||||
@defthing[response/c contract?]{
|
||||
Equivalent to
|
||||
@racketblock[
|
||||
(or/c response/basic?
|
||||
(cons/c bytes? (listof (or/c string? bytes?)))
|
||||
xexpr/c)
|
||||
]
|
||||
}
|
||||
|
||||
@defproc[(make-xexpr-response [xexpr xexpr/c]
|
||||
[#:code code number? 200]
|
||||
[#:message message bytes? #"Okay"]
|
||||
[#:seconds seconds number? (current-seconds)]
|
||||
[#:mime-type mime-type bytes? TEXT/HTML-MIME-TYPE]
|
||||
[#:headers headers (listof header?) empty]
|
||||
[#:preamble preamble bytes? #""])
|
||||
response/full?]{
|
||||
Equivalent to
|
||||
@racketblock[
|
||||
(make-response/full
|
||||
code message seconds mime-type headers
|
||||
(list preamble (string->bytes/utf-8 (xexpr->string xexpr))))
|
||||
]}
|
||||
|
||||
@defproc[(normalize-response [response response/c] [close? boolean? #f])
|
||||
(or/c response/full? response/incremental? response/port?)]{
|
||||
Coerces @racket[response] into a full response, filling in additional details where appropriate.
|
||||
|
||||
@racket[close?] represents whether the connection will be closed after the response is sent (i.e. if HTTP 1.0 is being used.) The accuracy of this only matters if
|
||||
@racket[response] is a @racket[response/incremental?].
|
||||
}
|
||||
|
||||
@defthing[TEXT/HTML-MIME-TYPE bytes?]{Equivalent to @racket[#"text/html; charset=utf-8"].}
|
||||
|
||||
|
@ -289,6 +214,7 @@ transmission that the server @bold{will not catch}.}
|
|||
|
||||
@(require (for-label net/cookie
|
||||
web-server/servlet
|
||||
web-server/http/xexpr
|
||||
web-server/http/redirect
|
||||
web-server/http/request-structs
|
||||
web-server/http/response-structs
|
||||
|
@ -311,12 +237,6 @@ transmission that the server @bold{will not catch}.}
|
|||
Constructs a header that sets the cookie.
|
||||
}
|
||||
|
||||
@defproc[(xexpr-response/cookies [cookies (listof cookie?)]
|
||||
[xexpr xexpr/c])
|
||||
response/full?]{
|
||||
Constructs a response using @racket[xexpr] that sets all the cookies in @racket[cookies].
|
||||
}
|
||||
|
||||
Examples:
|
||||
@racketblock[
|
||||
(define time-cookie
|
||||
|
@ -332,11 +252,11 @@ transmission that the server @bold{will not catch}.}
|
|||
(list time-cookie id-cookie)))
|
||||
|
||||
(send/suspend
|
||||
(lambda (k-url)
|
||||
(xexpr-response/cookies
|
||||
(list time-cookie id-cookie)
|
||||
`(html (head (title "Cookie Example"))
|
||||
(body (h1 "You're cookie'd!"))))))
|
||||
(lambda (k-url)
|
||||
(response/xexpr
|
||||
#:cookies (list time-cookie id-cookie)
|
||||
`(html (head (title "Cookie Example"))
|
||||
(body (h1 "You're cookie'd!"))))))
|
||||
]
|
||||
|
||||
@warning{When using cookies, make sure you follow the advice of the @link["http://cookies.lcs.mit.edu/"]{MIT Cookie Eaters},
|
||||
|
@ -347,6 +267,7 @@ transmission that the server @bold{will not catch}.}
|
|||
@section[#:tag "cookie-parse"]{Extracting Cookies}
|
||||
|
||||
@(require (for-label web-server/http/cookie-parse
|
||||
web-server/http/xexpr
|
||||
net/cookie
|
||||
net/url
|
||||
racket/list))
|
||||
|
@ -384,10 +305,11 @@ transmission that the server @bold{will not catch}.}
|
|||
(cookie->header (make-cookie "id" "joseph"))))))
|
||||
|
||||
(define (hello who)
|
||||
`(html (head (title "Hello!"))
|
||||
(body
|
||||
(h1 "Hello "
|
||||
,who))))
|
||||
(response/xexpr
|
||||
`(html (head (title "Hello!"))
|
||||
(body
|
||||
(h1 "Hello "
|
||||
,who)))))
|
||||
]
|
||||
}
|
||||
|
||||
|
@ -424,7 +346,8 @@ transmission that the server @bold{will not catch}.}
|
|||
|
||||
@; ------------------------------------------------------------
|
||||
@section[#:tag "basic-auth"]{Basic Authentication}
|
||||
@(require (for-label web-server/http/basic-auth))
|
||||
@(require (for-label web-server/http/response-structs
|
||||
web-server/http/basic-auth))
|
||||
|
||||
@defmodule[web-server/http/basic-auth]{
|
||||
|
||||
|
@ -449,21 +372,25 @@ web-server/insta
|
|||
(define (start req)
|
||||
(match (request->basic-credentials req)
|
||||
[(cons user pass)
|
||||
`(html (head (title "Basic Auth Test"))
|
||||
(body (h1 "User: " ,(bytes->string/utf-8 user))
|
||||
(h1 "Pass: " ,(bytes->string/utf-8 pass))))]
|
||||
(response/xexpr
|
||||
`(html (head (title "Basic Auth Test"))
|
||||
(body (h1 "User: " ,(bytes->string/utf-8 user))
|
||||
(h1 "Pass: " ,(bytes->string/utf-8 pass)))))]
|
||||
[else
|
||||
(make-response/basic
|
||||
(response
|
||||
401 #"Unauthorized" (current-seconds) TEXT/HTML-MIME-TYPE
|
||||
(list
|
||||
(make-basic-auth-header
|
||||
(format "Basic Auth Test: ~a" (gensym)))))]))
|
||||
(format "Basic Auth Test: ~a" (gensym))))
|
||||
void)]))
|
||||
]
|
||||
}
|
||||
|
||||
@; ------------------------------------------------------------
|
||||
@section[#:tag "digest-auth"]{Digest Authentication}
|
||||
@(require (for-label web-server/http/digest-auth
|
||||
web-server/http/xexpr
|
||||
web-server/http/response-structs
|
||||
racket/pretty))
|
||||
|
||||
@defmodule[web-server/http/digest-auth]{
|
||||
|
@ -518,20 +445,48 @@ web-server/insta
|
|||
(define (start req)
|
||||
(match (request->digest-credentials req)
|
||||
[#f
|
||||
(make-response/basic
|
||||
(response
|
||||
401 #"Unauthorized" (current-seconds) TEXT/HTML-MIME-TYPE
|
||||
(list (make-digest-auth-header
|
||||
(format "Digest Auth Test: ~a" (gensym))
|
||||
private-key opaque)))]
|
||||
private-key opaque))
|
||||
void)]
|
||||
[alist
|
||||
(define check
|
||||
(make-check-digest-credentials
|
||||
(password->digest-HA1 (lambda (username realm) "pass"))))
|
||||
(define pass?
|
||||
(check "GET" alist))
|
||||
`(html (head (title "Digest Auth Test"))
|
||||
(body
|
||||
(h1 ,(if pass? "Pass!" "No Pass!"))
|
||||
(pre ,(pretty-format alist))))]))
|
||||
(response/xexpr
|
||||
`(html (head (title "Digest Auth Test"))
|
||||
(body
|
||||
(h1 ,(if pass? "Pass!" "No Pass!"))
|
||||
(pre ,(pretty-format alist)))))]))
|
||||
]
|
||||
}
|
||||
|
||||
@; ------------------------------------------------------------
|
||||
@section[#:tag "xexpr"]{X-expression Support}
|
||||
@(require (for-label web-server/http/xexpr
|
||||
xml))
|
||||
|
||||
@defmodule[web-server/http/xexpr]{
|
||||
|
||||
@defproc[(response/xexpr [xexpr xexpr/c]
|
||||
[#:code code number? 200]
|
||||
[#:message message bytes? #"Okay"]
|
||||
[#:seconds seconds number? (current-seconds)]
|
||||
[#:mime-type mime-type bytes? TEXT/HTML-MIME-TYPE]
|
||||
[#:headers headers (listof header?) empty]
|
||||
[#:cookies cookies (listof cookie?) empty]
|
||||
[#:preamble preamble bytes? #""])
|
||||
response?]{
|
||||
Equivalent to
|
||||
@racketblock[
|
||||
(response/full
|
||||
code message seconds mime-type
|
||||
(append headers (map cookie->header cookies))
|
||||
(list preamble (string->bytes/utf-8 (xexpr->string xexpr))))
|
||||
]}
|
||||
|
||||
}
|
|
@ -49,7 +49,8 @@ An example @racket['stateless] servlet module:
|
|||
serialize-stuffer
|
||||
(md5-stuffer (build-path (find-system-path 'home-dir) ".urls"))))
|
||||
(define (start req)
|
||||
`(html (body (h2 "Look ma, no state!"))))
|
||||
(response/xexpr
|
||||
`(html (body (h2 "Look ma, no state!")))))
|
||||
]
|
||||
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
web-server/private/dispatch-server-unit
|
||||
web-server/private/dispatch-server-sig
|
||||
web-server/dispatchers/dispatch
|
||||
net/tcp-sig
|
||||
racket/async-channel
|
||||
unstable/contract
|
||||
web-server/configuration/configuration-table)
|
||||
|
|
|
@ -2,7 +2,8 @@
|
|||
@(require "web-server.rkt")
|
||||
|
||||
@title[#:tag "mime-types"]{MIME Types}
|
||||
@(require (for-label web-server/private/mime-types))
|
||||
@(require (for-label web-server/private/mime-types
|
||||
racket/contract))
|
||||
|
||||
@defmodule[web-server/private/mime-types]{
|
||||
|
||||
|
@ -10,7 +11,7 @@ This module provides function for dealing with @filepath{mime.types}
|
|||
files.
|
||||
|
||||
@defproc[(read-mime-types [p path-string?])
|
||||
(hash-table/c symbol? bytes?)]{
|
||||
(hash/c symbol? bytes?)]{
|
||||
Reads the @filepath{mime.types} file from @racket[p] and constructs a
|
||||
hash table mapping extensions to MIME types.
|
||||
}
|
||||
|
|
|
@ -30,13 +30,14 @@ When used inside @racket[page] syntactically, a rename transformer for the proce
|
|||
A simple example:
|
||||
@racketblock[
|
||||
(page
|
||||
`(html
|
||||
(body
|
||||
(a ([href
|
||||
,(embed/url
|
||||
(λ (req)
|
||||
"You clicked!"))])
|
||||
"Click me"))))]
|
||||
(response/xexpr
|
||||
`(html
|
||||
(body
|
||||
(a ([href
|
||||
,(embed/url
|
||||
(λ (req)
|
||||
"You clicked!"))])
|
||||
"Click me")))))]
|
||||
|
||||
Similarly, many Web applications make use almost exclusively of functions that are arguments to @racket[embed/url] and immediately invoke @racket[send/suspend/dispatch].
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@ There are a number of ways to run Web servlets.
|
|||
@section[#:tag "insta"]{Instant Servlets}
|
||||
@(require (for-label (only-in web-server/insta/insta
|
||||
no-web-browser static-files-path)
|
||||
web-server/http
|
||||
web-server/servlet-env))
|
||||
|
||||
@defmodulelang[web-server/insta #:use-sources (web-server/insta/insta)]
|
||||
|
@ -19,9 +20,10 @@ The fastest way to get a servlet running in the Web server is to use the
|
|||
@racketmod[
|
||||
web-server/insta
|
||||
|
||||
(define (start request)
|
||||
`(html (head (title "Hello world!"))
|
||||
(body (p "Hey out there!"))))
|
||||
(define (start req)
|
||||
(response/xexpr
|
||||
`(html (head (title "Hello world!"))
|
||||
(body (p "Hey out there!")))))
|
||||
]
|
||||
|
||||
And press @onscreen["Run"]. A Web browser will open up showing your new servlet.
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
@(require (for-label web-server/servlet-env
|
||||
web-server/servlet-dispatch
|
||||
web-server/http
|
||||
web-server/managers/manager
|
||||
web-server/managers/lru
|
||||
web-server/private/util
|
||||
web-server/dispatchers/dispatch
|
||||
|
|
|
@ -28,9 +28,10 @@ racket
|
|||
(require web-server/servlet
|
||||
web-server/servlet-env)
|
||||
|
||||
(define (start request)
|
||||
`(html (head (title "Hello world!"))
|
||||
(body (p "Hey out there!"))))
|
||||
(define (start req)
|
||||
(response/xexpr
|
||||
`(html (head (title "Hello world!"))
|
||||
(body (p "Hey out there!")))))
|
||||
|
||||
(serve/servlet start)
|
||||
]
|
||||
|
@ -41,9 +42,10 @@ racket
|
|||
(require web-server/servlet
|
||||
web-server/servlet-env)
|
||||
|
||||
(define (my-app request)
|
||||
`(html (head (title "Hello world!"))
|
||||
(body (p "Hey out there!"))))
|
||||
(define (my-app req)
|
||||
(response/xexpr
|
||||
`(html (head (title "Hello world!"))
|
||||
(body (p "Hey out there!")))))
|
||||
|
||||
(serve/servlet my-app)
|
||||
]
|
||||
|
@ -117,7 +119,8 @@ You can also put the call to @racket[serve/servlet] in the @racketmodname[web-se
|
|||
(start
|
||||
(send/suspend
|
||||
(lambda (k-url)
|
||||
`(html (body (a ([href ,k-url]) "Hello world!")))))))
|
||||
(response/xexpr
|
||||
`(html (body (a ([href ,k-url]) "Hello world!"))))))))
|
||||
|
||||
(serve/servlet start #:stateless? #t)
|
||||
]
|
||||
|
|
|
@ -6,7 +6,9 @@
|
|||
web-server/http
|
||||
web-server/private/servlet
|
||||
web-server/managers/manager
|
||||
web-server/configuration/namespace))
|
||||
web-server/configuration/namespace
|
||||
racket/serialize
|
||||
web-server/stuffers/stuffer))
|
||||
|
||||
@defmodule[web-server/servlet/setup]{
|
||||
|
||||
|
|
|
@ -39,11 +39,13 @@ An example version 2 module:
|
|||
(define manager
|
||||
(create-none-manager
|
||||
(lambda (req)
|
||||
`(html (head (title "No Continuations Here!"))
|
||||
(body (h1 "No Continuations Here!"))))))
|
||||
(response/xexpr
|
||||
`(html (head (title "No Continuations Here!"))
|
||||
(body (h1 "No Continuations Here!")))))))
|
||||
(define (start req)
|
||||
`(html (head (title "Hello World!"))
|
||||
(body (h1 "Hi Mom!"))))
|
||||
(response/xexpr
|
||||
`(html (head (title "Hello World!"))
|
||||
(body (h1 "Hi Mom!")))))
|
||||
]
|
||||
|
||||
These servlets have an extensive API available to them: @racketmodname[net/url], @racketmodname[web-server/http],
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#lang scribble/doc
|
||||
@(require "web-server.rkt"
|
||||
(for-label web-server/lang/soft
|
||||
(for-label web-server/http
|
||||
web-server/lang/soft
|
||||
web-server/lang/web))
|
||||
|
||||
@title[]{Soft State}
|
||||
|
@ -47,7 +48,8 @@ Here's an example servlet that uses soft state:
|
|||
(start
|
||||
(send/suspend
|
||||
(lambda (k-url)
|
||||
`(html (body (a ([href ,k-url]) "Done")))))))
|
||||
(response/xexpr
|
||||
`(html (body (a ([href ,k-url]) "Done"))))))))
|
||||
]
|
||||
|
||||
When this is run and the link is clicked a few times, the output is:
|
||||
|
|
|
@ -256,18 +256,13 @@ Notice how it also avoids the absurd amount of punctuation on line two.
|
|||
@section{HTTP Responses}
|
||||
|
||||
The quickest way to generate an HTTP response from a template is using
|
||||
the @racket[list] response type:
|
||||
@racketblock[
|
||||
(list #"text/html" (include-template "static.html"))
|
||||
]
|
||||
|
||||
If you want more control then you can generate a @racket[response?] struct:
|
||||
a @racket[response?] struct:
|
||||
@racketblock[
|
||||
(response/full
|
||||
200 #"Okay"
|
||||
(current-seconds) TEXT/HTML-MIME-TYPE
|
||||
empty
|
||||
(list (include-template "static.html")))
|
||||
(list (string->bytes/utf-8 (include-template "static.html"))))
|
||||
]
|
||||
|
||||
Finally, if you want to include the contents of a template inside a larger @xexpr :
|
||||
|
@ -335,12 +330,13 @@ He has divided his code into presentation functions and logic functions. We'll l
|
|||
The first presentation function defines the common layout of all pages.
|
||||
@racketblock[
|
||||
(define (template section body)
|
||||
`(html
|
||||
(head (title "Al's Church: " ,section))
|
||||
(body
|
||||
(h1 "Al's Church: " ,section)
|
||||
(div ([id "main"])
|
||||
,@body))))
|
||||
(response/xexpr
|
||||
`(html
|
||||
(head (title "Al's Church: " ,section))
|
||||
(body
|
||||
(h1 "Al's Church: " ,section)
|
||||
(div ([id "main"])
|
||||
,@body)))))
|
||||
]
|
||||
|
||||
One of the things to notice here is the @racket[unquote-splicing] on the @racket[body] argument.
|
||||
|
@ -379,32 +375,33 @@ his blog. He changes the @racket[template] function to:
|
|||
|
||||
@racketblock[
|
||||
(define (template section body)
|
||||
`(html
|
||||
(head
|
||||
(title "Al's Church: " ,section)
|
||||
(style ([type "text/css"])
|
||||
"body {margin: 0px; padding: 10px;}"
|
||||
"#main {background: #dddddd;}"))
|
||||
(body
|
||||
(script
|
||||
([type "text/javascript"])
|
||||
,(make-cdata
|
||||
#f #f
|
||||
"var gaJsHost = ((\"https:\" =="
|
||||
"document.location.protocol)"
|
||||
"? \"https://ssl.\" : \"http://www.\");"
|
||||
"document.write(unescape(\"%3Cscript src='\" + gaJsHost"
|
||||
"+ \"google-analytics.com/ga.js' "
|
||||
"type='text/javascript'%3E%3C/script%3E\"));"))
|
||||
(script
|
||||
([type "text/javascript"])
|
||||
,(make-cdata
|
||||
#f #f
|
||||
"var pageTracker = _gat._getTracker(\"UA-YYYYYYY-Y\");"
|
||||
"pageTracker._trackPageview();"))
|
||||
(h1 "Al's Church: " ,section)
|
||||
(div ([id "main"])
|
||||
,@body))))
|
||||
(response/xexpr
|
||||
`(html
|
||||
(head
|
||||
(title "Al's Church: " ,section)
|
||||
(style ([type "text/css"])
|
||||
"body {margin: 0px; padding: 10px;}"
|
||||
"#main {background: #dddddd;}"))
|
||||
(body
|
||||
(script
|
||||
([type "text/javascript"])
|
||||
,(make-cdata
|
||||
#f #f
|
||||
"var gaJsHost = ((\"https:\" =="
|
||||
"document.location.protocol)"
|
||||
"? \"https://ssl.\" : \"http://www.\");"
|
||||
"document.write(unescape(\"%3Cscript src='\" + gaJsHost"
|
||||
"+ \"google-analytics.com/ga.js' "
|
||||
"type='text/javascript'%3E%3C/script%3E\"));"))
|
||||
(script
|
||||
([type "text/javascript"])
|
||||
,(make-cdata
|
||||
#f #f
|
||||
"var pageTracker = _gat._getTracker(\"UA-YYYYYYY-Y\");"
|
||||
"pageTracker._trackPageview();"))
|
||||
(h1 "Al's Church: " ,section)
|
||||
(div ([id "main"])
|
||||
,@body)))))
|
||||
]
|
||||
|
||||
@margin-note{Some of these problems go away by using here strings, as described in the documentation on
|
||||
|
@ -447,8 +444,11 @@ To use templates, we need only change @racket[template], @racket[blog-posted], a
|
|||
|
||||
@racketblock[
|
||||
(define (template section body)
|
||||
(list TEXT/HTML-MIME-TYPE
|
||||
(include-template "blog.html")))
|
||||
(response/full
|
||||
200 #"Okay"
|
||||
(current-seconds) TEXT/HTML-MIME-TYPE
|
||||
empty
|
||||
(list (string->bytes/utf-8 (include-template "blog.html")))))
|
||||
|
||||
(define (blog-posted title body k-url)
|
||||
(include-template "blog-posted.html"))
|
||||
|
|
|
@ -2,7 +2,8 @@
|
|||
@(require "web-server.rkt")
|
||||
|
||||
@title[#:tag "web-cells"]{Web Cells}
|
||||
@(require (for-label web-server/servlet/web-cells
|
||||
@(require (for-label web-server/http
|
||||
web-server/servlet/web-cells
|
||||
web-server/servlet/web))
|
||||
|
||||
@defmodule[web-server/servlet/web-cells]{The
|
||||
|
@ -56,12 +57,13 @@ transformations of the program into continuation or store passing style.
|
|||
(define include2 (include-counter counter2))
|
||||
(send/suspend/dispatch
|
||||
(lambda (embed/url)
|
||||
`(html
|
||||
(body (h2 "Double Counters")
|
||||
(div (h3 "First")
|
||||
,(include1 embed/url))
|
||||
(div (h3 "Second")
|
||||
,(include2 embed/url)))))))
|
||||
(response/xexpr
|
||||
`(html
|
||||
(body (h2 "Double Counters")
|
||||
(div (h3 "First")
|
||||
,(include1 embed/url))
|
||||
(div (h3 "Second")
|
||||
,(include2 embed/url))))))))
|
||||
|
||||
(define (make-counter)
|
||||
(make-web-cell 0))
|
||||
|
|
|
@ -19,11 +19,12 @@ functions of interest for the servlet developer.
|
|||
Example:
|
||||
@racketblock[
|
||||
(send/back
|
||||
`(html
|
||||
(body
|
||||
(h1 "The sum is: "
|
||||
,(+ first-number
|
||||
second-number)))))
|
||||
(response/xexpr
|
||||
`(html
|
||||
(body
|
||||
(h1 "The sum is: "
|
||||
,(+ first-number
|
||||
second-number))))))
|
||||
]
|
||||
}
|
||||
|
||||
|
@ -39,12 +40,13 @@ functions of interest for the servlet developer.
|
|||
@racketblock[
|
||||
(send/suspend
|
||||
(lambda (k-url)
|
||||
`(html (head (title "Enter a number"))
|
||||
(body
|
||||
(form ([action ,k-url])
|
||||
"Enter a number: "
|
||||
(input ([name "number"]))
|
||||
(input ([type "submit"])))))))
|
||||
(response/xexpr
|
||||
`(html (head (title "Enter a number"))
|
||||
(body
|
||||
(form ([action ,k-url])
|
||||
"Enter a number: "
|
||||
(input ([name "number"]))
|
||||
(input ([type "submit"]))))))))
|
||||
]
|
||||
|
||||
When this form is submitted by the browser, the request will be sent to the URL generated by @racket[send/suspend].
|
||||
|
@ -78,20 +80,21 @@ functions of interest for the servlet developer.
|
|||
(count-dot-com
|
||||
(send/suspend/dispatch
|
||||
(lambda (embed/url)
|
||||
`(html
|
||||
(head (title "Count!"))
|
||||
(body
|
||||
(h2 (a ([href
|
||||
,(embed/url
|
||||
(lambda (req)
|
||||
(sub1 i)))])
|
||||
"-"))
|
||||
(h1 ,(number->string i))
|
||||
(h2 (a ([href
|
||||
,(embed/url
|
||||
(lambda (req)
|
||||
(add1 i)))])
|
||||
"+"))))))))
|
||||
(response/xexpr
|
||||
`(html
|
||||
(head (title "Count!"))
|
||||
(body
|
||||
(h2 (a ([href
|
||||
,(embed/url
|
||||
(lambda (req)
|
||||
(sub1 i)))])
|
||||
"-"))
|
||||
(h1 ,(number->string i))
|
||||
(h2 (a ([href
|
||||
,(embed/url
|
||||
(lambda (req)
|
||||
(add1 i)))])
|
||||
"+")))))))))
|
||||
]
|
||||
Notice that in this example the result of the handlers are returned to the continuation of @racket[send/suspend/dispatch].
|
||||
However, it is very common that the return value of @racket[send/suspend/dispatch] is irrevelant in
|
||||
|
@ -100,20 +103,21 @@ functions of interest for the servlet developer.
|
|||
(define (count-dot-com i)
|
||||
(send/suspend/dispatch
|
||||
(lambda (embed/url)
|
||||
`(html
|
||||
(head (title "Count!"))
|
||||
(body
|
||||
(h2 (a ([href
|
||||
,(embed/url
|
||||
(lambda (req)
|
||||
(count-dot-com (sub1 i))))])
|
||||
"-"))
|
||||
(h1 ,(number->string i))
|
||||
(h2 (a ([href
|
||||
,(embed/url
|
||||
(lambda (req)
|
||||
(count-dot-com (add1 i))))])
|
||||
"+")))))))
|
||||
(response/xexpr
|
||||
`(html
|
||||
(head (title "Count!"))
|
||||
(body
|
||||
(h2 (a ([href
|
||||
,(embed/url
|
||||
(lambda (req)
|
||||
(count-dot-com (sub1 i))))])
|
||||
"-"))
|
||||
(h1 ,(number->string i))
|
||||
(h2 (a ([href
|
||||
,(embed/url
|
||||
(lambda (req)
|
||||
(count-dot-com (add1 i))))])
|
||||
"+"))))))))
|
||||
]
|
||||
}
|
||||
|
||||
|
@ -136,9 +140,10 @@ functions of interest for the servlet developer.
|
|||
Use this if the user is truly `done' with your application. For example, it may be used to display the post-logout page:
|
||||
@racketblock[
|
||||
(send/finish
|
||||
`(html (head (title "Logged out"))
|
||||
(body (p "Thank you for using the services "
|
||||
"of the Add Two Numbers, Inc."))))
|
||||
(response/xexpr
|
||||
`(html (head (title "Logged out"))
|
||||
(body (p "Thank you for using the services "
|
||||
"of the Add Two Numbers, Inc.")))))
|
||||
]
|
||||
}
|
||||
|
||||
|
@ -164,7 +169,8 @@ functions of interest for the servlet developer.
|
|||
(parameterize
|
||||
([current-servlet-continuation-expiration-handler
|
||||
(lambda (req)
|
||||
`(html (head (title "Custom Expiration!"))))])
|
||||
(response/xexpr
|
||||
`(html (head (title "Custom Expiration!")))))])
|
||||
(send/suspend
|
||||
....))
|
||||
]
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#lang racket
|
||||
#lang racket/base
|
||||
(require xml
|
||||
scribble/text
|
||||
racket/port)
|
||||
|
|
Loading…
Reference in New Issue
Block a user