Add function binding-assq-all to request-structs.rkt
This commit is contained in:
parent
89234bde9e
commit
14084d560d
|
@ -22,7 +22,7 @@
|
||||||
[(list-rest (and h (struct header (af av))) hs)
|
[(list-rest (and h (struct header (af av))) hs)
|
||||||
(if (bytes=? af f)
|
(if (bytes=? af f)
|
||||||
h
|
h
|
||||||
(headers-assq f hs))]))
|
(headers-assq f hs))]))
|
||||||
(provide/contract
|
(provide/contract
|
||||||
[headers-assq (bytes? (listof header?) . -> . (or/c false/c header?))]
|
[headers-assq (bytes? (listof header?) . -> . (or/c false/c header?))]
|
||||||
[headers-assq* (bytes? (listof header?) . -> . (or/c false/c header?))]
|
[headers-assq* (bytes? (listof header?) . -> . (or/c false/c header?))]
|
||||||
|
@ -43,8 +43,15 @@
|
||||||
(if (equal? ti i)
|
(if (equal? ti i)
|
||||||
b
|
b
|
||||||
(bindings-assq ti bs))]))
|
(bindings-assq ti bs))]))
|
||||||
|
|
||||||
|
(define (bindings-assq-all ti bs)
|
||||||
|
(for/list ([b (in-list bs)]
|
||||||
|
#:when (and (binding? b) (equal? ti (binding-id b))))
|
||||||
|
b))
|
||||||
|
|
||||||
(provide/contract
|
(provide/contract
|
||||||
[bindings-assq (bytes? (listof binding?) . -> . (or/c false/c binding?))]
|
[bindings-assq (bytes? (listof binding?) . -> . (or/c false/c binding?))]
|
||||||
|
[bindings-assq-all (bytes? (listof binding?) . -> . (listof binding?))]
|
||||||
[struct binding ([id bytes?])]
|
[struct binding ([id bytes?])]
|
||||||
[struct (binding:form binding) ([id bytes?]
|
[struct (binding:form binding) ([id bytes?]
|
||||||
[value bytes?])]
|
[value bytes?])]
|
||||||
|
@ -64,7 +71,7 @@
|
||||||
(provide/contract
|
(provide/contract
|
||||||
[request-bindings/raw (request? . -> . (listof binding?))]
|
[request-bindings/raw (request? . -> . (listof binding?))]
|
||||||
[struct request ([method bytes?]
|
[struct request ([method bytes?]
|
||||||
[uri url?]
|
[uri url?]
|
||||||
[headers/raw (listof header?)]
|
[headers/raw (listof header?)]
|
||||||
[bindings/raw-promise (promise/c (listof binding?))]
|
[bindings/raw-promise (promise/c (listof binding?))]
|
||||||
[post-data/raw (or/c false/c bytes?)]
|
[post-data/raw (or/c false/c bytes?)]
|
||||||
|
|
|
@ -29,7 +29,7 @@ The @web-server implements many HTTP libraries that are provided by this module.
|
||||||
@defproc[(headers-assq* [id bytes?] [heads (listof header?)])
|
@defproc[(headers-assq* [id bytes?] [heads (listof header?)])
|
||||||
(or/c false/c header?)]{
|
(or/c false/c header?)]{
|
||||||
Returns the header with a field case-insensitively equal to @racket[id] from @racket[heads] or @racket[#f].
|
Returns the header with a field case-insensitively equal to @racket[id] from @racket[heads] or @racket[#f].
|
||||||
|
|
||||||
You almost @bold{always} want to use this, rather than @racket[headers-assq] because Web browsers may send headers with arbitrary casing.
|
You almost @bold{always} want to use this, rather than @racket[headers-assq] because Web browsers may send headers with arbitrary casing.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,6 +54,13 @@ The @web-server implements many HTTP libraries that are provided by this module.
|
||||||
Returns the binding with an id equal to @racket[id] from @racket[binds] or @racket[#f].
|
Returns the binding with an id equal to @racket[id] from @racket[binds] or @racket[#f].
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@defproc[(bindings-assq-all [id bytes?]
|
||||||
|
[binds (listof binding?)])
|
||||||
|
(listof binding?)]{
|
||||||
|
Like @racket[bindings-assq], but returns a list of all bindings matching @racket[id].
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@defstruct[request ([method bytes?]
|
@defstruct[request ([method bytes?]
|
||||||
[uri url?]
|
[uri url?]
|
||||||
[headers/raw (listof header?)]
|
[headers/raw (listof header?)]
|
||||||
|
@ -66,7 +73,7 @@ The @web-server implements many HTTP libraries that are provided by this module.
|
||||||
to the server at @racket[host-ip]:@racket[host-port] with @racket[headers/raw]
|
to the server at @racket[host-ip]:@racket[host-port] with @racket[headers/raw]
|
||||||
headers, @racket[bindings/raw] GET and POST queries and @racket[post-data/raw]
|
headers, @racket[bindings/raw] GET and POST queries and @racket[post-data/raw]
|
||||||
POST data.
|
POST data.
|
||||||
|
|
||||||
You are @bold{unlikely to need to construct} a request struct.
|
You are @bold{unlikely to need to construct} a request struct.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -79,7 +86,7 @@ Here is an example typical of what you will find in many applications:
|
||||||
@racketblock[
|
@racketblock[
|
||||||
(define (get-number req)
|
(define (get-number req)
|
||||||
(match
|
(match
|
||||||
(bindings-assq
|
(bindings-assq
|
||||||
#"number"
|
#"number"
|
||||||
(request-bindings/raw req))
|
(request-bindings/raw req))
|
||||||
[(? binding:form? b)
|
[(? binding:form? b)
|
||||||
|
@ -142,7 +149,7 @@ they are provided for compatibility with old code.}
|
||||||
Returns @racket[#t] if @racket[binds] contains a binding for @racket[id].
|
Returns @racket[#t] if @racket[binds] contains a binding for @racket[id].
|
||||||
Otherwise, @racket[#f].
|
Otherwise, @racket[#f].
|
||||||
}
|
}
|
||||||
|
|
||||||
Here is an example typical of what you will find in many applications:
|
Here is an example typical of what you will find in many applications:
|
||||||
@racketblock[
|
@racketblock[
|
||||||
(define (get-number req)
|
(define (get-number req)
|
||||||
|
@ -170,7 +177,7 @@ Here is an example typical of what you will find in many applications:
|
||||||
An HTTP response where @racket[output] produces the body. @racket[code] is the response code,
|
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]
|
@racket[message] the message, @racket[seconds] the generation time, @racket[mime]
|
||||||
the MIME type of the file, and @racket[headers] are the headers. If @racket[headers] does not include @litchar{Date}, @litchar{Last-Modified}, @litchar{Server}, or @litchar{Content-Type} headers, then the server will automatically add them. The server will always replace your @litchar{Connection} header if it needs to ensure the connection will be closed. (Typically with an HTTP/1.0 client.)
|
the MIME type of the file, and @racket[headers] are the headers. If @racket[headers] does not include @litchar{Date}, @litchar{Last-Modified}, @litchar{Server}, or @litchar{Content-Type} headers, then the server will automatically add them. The server will always replace your @litchar{Connection} header if it needs to ensure the connection will be closed. (Typically with an HTTP/1.0 client.)
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
@racketblock[
|
@racketblock[
|
||||||
(response
|
(response
|
||||||
|
@ -222,7 +229,7 @@ transmission that the server @bold{will not catch}.}
|
||||||
|
|
||||||
@defmodule[web-server/http/cookie]{
|
@defmodule[web-server/http/cookie]{
|
||||||
This module provides functions to create cookies and responses that set them.
|
This module provides functions to create cookies and responses that set them.
|
||||||
|
|
||||||
@defproc[(make-cookie [name cookie-name?] [value cookie-value?]
|
@defproc[(make-cookie [name cookie-name?] [value cookie-value?]
|
||||||
[#:comment comment (or/c false/c string?) #f]
|
[#:comment comment (or/c false/c string?) #f]
|
||||||
[#:domain domain (or/c false/c valid-domain?) #f]
|
[#:domain domain (or/c false/c valid-domain?) #f]
|
||||||
|
@ -232,25 +239,25 @@ transmission that the server @bold{will not catch}.}
|
||||||
cookie?]{
|
cookie?]{
|
||||||
Constructs a cookie with the appropriate fields.
|
Constructs a cookie with the appropriate fields.
|
||||||
}
|
}
|
||||||
|
|
||||||
@defproc[(cookie->header [c cookie?]) header?]{
|
@defproc[(cookie->header [c cookie?]) header?]{
|
||||||
Constructs a header that sets the cookie.
|
Constructs a header that sets the cookie.
|
||||||
}
|
}
|
||||||
|
|
||||||
Examples:
|
Examples:
|
||||||
@racketblock[
|
@racketblock[
|
||||||
(define time-cookie
|
(define time-cookie
|
||||||
(make-cookie "time" (number->string (current-seconds))))
|
(make-cookie "time" (number->string (current-seconds))))
|
||||||
(define id-cookie
|
(define id-cookie
|
||||||
(make-cookie "id" "joseph" #:secure? #t))
|
(make-cookie "id" "joseph" #:secure? #t))
|
||||||
|
|
||||||
(redirect-to
|
(redirect-to
|
||||||
"http://localhost/logged-in"
|
"http://localhost/logged-in"
|
||||||
see-other
|
see-other
|
||||||
#:headers
|
#:headers
|
||||||
(map cookie->header
|
(map cookie->header
|
||||||
(list time-cookie id-cookie)))
|
(list time-cookie id-cookie)))
|
||||||
|
|
||||||
(send/suspend
|
(send/suspend
|
||||||
(lambda (k-url)
|
(lambda (k-url)
|
||||||
(response/xexpr
|
(response/xexpr
|
||||||
|
@ -258,9 +265,9 @@ transmission that the server @bold{will not catch}.}
|
||||||
`(html (head (title "Cookie Example"))
|
`(html (head (title "Cookie Example"))
|
||||||
(body (h1 "You're cookie'd!"))))))
|
(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},
|
@warning{When using cookies, make sure you follow the advice of the @link["http://cookies.lcs.mit.edu/"]{MIT Cookie Eaters},
|
||||||
or you will be susceptible to dangerous attacks.}
|
or you will be susceptible to dangerous attacks.}
|
||||||
}
|
}
|
||||||
|
|
||||||
@; ------------------------------------------------------------
|
@; ------------------------------------------------------------
|
||||||
|
@ -272,7 +279,7 @@ transmission that the server @bold{will not catch}.}
|
||||||
net/url
|
net/url
|
||||||
racket/list))
|
racket/list))
|
||||||
@defmodule[web-server/http/cookie-parse]{
|
@defmodule[web-server/http/cookie-parse]{
|
||||||
@defstruct[client-cookie
|
@defstruct[client-cookie
|
||||||
([name string?]
|
([name string?]
|
||||||
[value string?]
|
[value string?]
|
||||||
[domain (or/c false/c valid-domain?)]
|
[domain (or/c false/c valid-domain?)]
|
||||||
|
@ -282,7 +289,7 @@ transmission that the server @bold{will not catch}.}
|
||||||
that come from the client are represented with a
|
that come from the client are represented with a
|
||||||
@racket[client-cookie] structure.
|
@racket[client-cookie] structure.
|
||||||
}
|
}
|
||||||
|
|
||||||
@defproc[(request-cookies [req request?])
|
@defproc[(request-cookies [req request?])
|
||||||
(listof client-cookie?)]{
|
(listof client-cookie?)]{
|
||||||
Extracts the cookies from @racket[req]'s headers.
|
Extracts the cookies from @racket[req]'s headers.
|
||||||
|
@ -292,24 +299,24 @@ transmission that the server @bold{will not catch}.}
|
||||||
@racketblock[
|
@racketblock[
|
||||||
(define (start req)
|
(define (start req)
|
||||||
(define cookies (request-cookies req))
|
(define cookies (request-cookies req))
|
||||||
(define id-cookie
|
(define id-cookie
|
||||||
(findf (lambda (c)
|
(findf (lambda (c)
|
||||||
(string=? "id" (client-cookie-name c)))
|
(string=? "id" (client-cookie-name c)))
|
||||||
cookies))
|
cookies))
|
||||||
(if id-cookie
|
(if id-cookie
|
||||||
(hello (client-cookie-value id-cookie))
|
(hello (client-cookie-value id-cookie))
|
||||||
(redirect-to
|
(redirect-to
|
||||||
(url->string (request-uri req))
|
(url->string (request-uri req))
|
||||||
see-other
|
see-other
|
||||||
#:headers
|
#:headers
|
||||||
(list
|
(list
|
||||||
(cookie->header (make-cookie "id" "joseph"))))))
|
(cookie->header (make-cookie "id" "joseph"))))))
|
||||||
|
|
||||||
(define (hello who)
|
(define (hello who)
|
||||||
(response/xexpr
|
(response/xexpr
|
||||||
`(html (head (title "Hello!"))
|
`(html (head (title "Hello!"))
|
||||||
(body
|
(body
|
||||||
(h1 "Hello "
|
(h1 "Hello "
|
||||||
,who)))))
|
,who)))))
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -327,7 +334,7 @@ transmission that the server @bold{will not catch}.}
|
||||||
response?]{
|
response?]{
|
||||||
Generates an HTTP response that redirects the browser to @racket[uri],
|
Generates an HTTP response that redirects the browser to @racket[uri],
|
||||||
while including the @racket[headers] in the response.
|
while including the @racket[headers] in the response.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
@racket[(redirect-to "http://www.add-three-numbers.com" permanently)]
|
@racket[(redirect-to "http://www.add-three-numbers.com" permanently)]
|
||||||
}
|
}
|
||||||
|
@ -353,23 +360,23 @@ transmission that the server @bold{will not catch}.}
|
||||||
@defmodule[web-server/http/basic-auth]{
|
@defmodule[web-server/http/basic-auth]{
|
||||||
|
|
||||||
An implementation of HTTP Basic Authentication.
|
An implementation of HTTP Basic Authentication.
|
||||||
|
|
||||||
@defproc[(make-basic-auth-header [realm string?])
|
@defproc[(make-basic-auth-header [realm string?])
|
||||||
header?]{
|
header?]{
|
||||||
Returns a header that instructs the Web browser to request a username and password from the client using
|
Returns a header that instructs the Web browser to request a username and password from the client using
|
||||||
Basic authentication with @racket[realm] as the realm.
|
Basic authentication with @racket[realm] as the realm.
|
||||||
}
|
}
|
||||||
|
|
||||||
@defproc[(request->basic-credentials [req request?])
|
@defproc[(request->basic-credentials [req request?])
|
||||||
(or/c false/c (cons/c bytes? bytes?))]{
|
(or/c false/c (cons/c bytes? bytes?))]{
|
||||||
Returns a pair of the username and password from the authentication
|
Returns a pair of the username and password from the authentication
|
||||||
header in @racket[req] if they are present, or @racket[#f].
|
header in @racket[req] if they are present, or @racket[#f].
|
||||||
}
|
}
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
@racketmod[
|
@racketmod[
|
||||||
web-server/insta
|
web-server/insta
|
||||||
|
|
||||||
(define (start req)
|
(define (start req)
|
||||||
(match (request->basic-credentials req)
|
(match (request->basic-credentials req)
|
||||||
[(cons user pass)
|
[(cons user pass)
|
||||||
|
@ -380,10 +387,10 @@ web-server/insta
|
||||||
[else
|
[else
|
||||||
(response
|
(response
|
||||||
401 #"Unauthorized" (current-seconds) TEXT/HTML-MIME-TYPE
|
401 #"Unauthorized" (current-seconds) TEXT/HTML-MIME-TYPE
|
||||||
(list
|
(list
|
||||||
(make-basic-auth-header
|
(make-basic-auth-header
|
||||||
(format "Basic Auth Test: ~a" (gensym))))
|
(format "Basic Auth Test: ~a" (gensym))))
|
||||||
void)]))
|
void)]))
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -397,18 +404,18 @@ web-server/insta
|
||||||
@defmodule[web-server/http/digest-auth]{
|
@defmodule[web-server/http/digest-auth]{
|
||||||
|
|
||||||
An implementation of HTTP Digest Authentication.
|
An implementation of HTTP Digest Authentication.
|
||||||
|
|
||||||
@defproc[(make-digest-auth-header [realm string?] [private-key string?] [opaque string?])
|
@defproc[(make-digest-auth-header [realm string?] [private-key string?] [opaque string?])
|
||||||
header?]{
|
header?]{
|
||||||
Returns a header that instructs the Web browser to request a username and password from the client
|
Returns a header that instructs the Web browser to request a username and password from the client
|
||||||
using Digest authentication with @racket[realm] as the realm, @racket[private-key] as the server's
|
using Digest authentication with @racket[realm] as the realm, @racket[private-key] as the server's
|
||||||
contribution to the nonce, and @racket[opaque] as the opaque data passed through the client.
|
contribution to the nonce, and @racket[opaque] as the opaque data passed through the client.
|
||||||
}
|
}
|
||||||
|
|
||||||
@defproc[(request->digest-credentials [req request?])
|
@defproc[(request->digest-credentials [req request?])
|
||||||
(or/c false/c (listof (cons/c symbol? string?)))]{
|
(or/c false/c (listof (cons/c symbol? string?)))]{
|
||||||
Returns the Digest credentials from @racket[req] (if they appear) as an association list.
|
Returns the Digest credentials from @racket[req] (if they appear) as an association list.
|
||||||
}
|
}
|
||||||
|
|
||||||
@defthing[username*realm->password/c contract?]{
|
@defthing[username*realm->password/c contract?]{
|
||||||
Used to look up the password for a user is a realm.
|
Used to look up the password for a user is a realm.
|
||||||
|
@ -464,9 +471,9 @@ web-server/insta
|
||||||
(check "GET" alist))
|
(check "GET" alist))
|
||||||
(response/xexpr
|
(response/xexpr
|
||||||
`(html (head (title "Digest Auth Test"))
|
`(html (head (title "Digest Auth Test"))
|
||||||
(body
|
(body
|
||||||
(h1 ,(if pass? "Pass!" "No Pass!"))
|
(h1 ,(if pass? "Pass!" "No Pass!"))
|
||||||
(pre ,(pretty-format alist)))))]))
|
(pre ,(pretty-format alist)))))]))
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -489,11 +496,11 @@ web-server/insta
|
||||||
Equivalent to
|
Equivalent to
|
||||||
@racketblock[
|
@racketblock[
|
||||||
(response/full
|
(response/full
|
||||||
code message seconds mime-type
|
code message seconds mime-type
|
||||||
(append headers (map cookie->header cookies))
|
(append headers (map cookie->header cookies))
|
||||||
(list preamble (string->bytes/utf-8 (xexpr->string xexpr))))
|
(list preamble (string->bytes/utf-8 (xexpr->string xexpr))))
|
||||||
]
|
]
|
||||||
|
|
||||||
This is a viable function to pass to @racket[set-any->response!].
|
This is a viable function to pass to @racket[set-any->response!].
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user