Fixing pr11125

This commit is contained in:
Jay McCarthy 2010-08-26 09:52:33 -06:00
parent d4e3946ba2
commit 7190d423f7
8 changed files with 165 additions and 52 deletions

View File

@ -0,0 +1,66 @@
#lang racket
(require net/url
web-server/http
web-server/http/request
web-server/servlet-env
tests/eli-tester)
(define PORT 8999)
(define (do-the-test PORT #:connection-close? connection-close?)
(define tc (make-thread-cell 0))
(define (start req)
(thread-cell-set! tc (add1 (thread-cell-ref tc)))
(number->string (thread-cell-ref tc)))
(define-values (pipe-read-p pipe-write-p)
(make-pipe))
(define server-t
(thread
(λ ()
(parameterize ([current-output-port pipe-write-p])
(serve/servlet start
#:launch-browser? #f
#:connection-close? connection-close?
#:quit? #f
#:listen-ip #f
#:port PORT
#:servlet-path "/")))))
; Wait for server to start
(void (read-line pipe-read-p)
(read-line pipe-read-p))
(define-values (http-read-p http-write-p)
(tcp-connect "localhost" PORT))
(define (get-tc/err http-read-p http-write-p)
(with-handlers
([exn?
(λ (x)
(define-values (new-http-read-p new-http-write-p)
(tcp-connect "localhost" PORT))
(set! http-read-p new-http-read-p)
(set! http-write-p new-http-write-p)
(get-tc http-read-p http-write-p))])
(get-tc http-read-p http-write-p)))
(define (get-tc http-read-p http-write-p)
(fprintf http-write-p "GET / HTTP/1.1\r\n\r\n")
(flush-output http-write-p)
(read-line http-read-p)
(read-headers http-read-p)
(string->number (string (read-char http-read-p))))
(begin0
(list (get-tc/err http-read-p http-write-p)
(get-tc/err http-read-p http-write-p))
(kill-thread server-t)))
(test
(do-the-test PORT #:connection-close? #f) => (list 1 2)
(do-the-test (add1 PORT) #:connection-close? #t) => (list 1 1))

View File

@ -6,26 +6,27 @@
web-server/private/connection-manager web-server/private/connection-manager
web-server/http/request-structs) web-server/http/request-structs)
(define read-request/c
(connection?
tcp-listen-port?
(input-port? . -> . (values string? string?))
. -> .
(values request? boolean?)))
(provide/contract (provide/contract
[read-headers (-> input-port? (listof header?))] [read-headers (-> input-port? (listof header?))]
[rename make-ext:read-request make-read-request
(->* () (#:connection-close? boolean?) read-request/c)]
[rename ext:read-request read-request [rename ext:read-request read-request
(connection? read-request/c])
tcp-listen-port?
(input-port? . -> . (values string? string?))
. -> .
(values request? boolean?))])
(define (ext:read-request conn host-port port-addresses)
(with-handlers ([exn:fail? (lambda (exn)
(kill-connection! conn)
(raise exn))])
(read-request conn host-port port-addresses)))
;; ************************************************** ;; **************************************************
;; read-request: connection number (input-port -> string string) -> request boolean? ;; read-request: connection number (input-port -> string string) -> request boolean?
;; read the request line, and the headers, determine if the connection should ;; read the request line, and the headers, determine if the connection should
;; be closed after servicing the request and build a request structure ;; be closed after servicing the request and build a request structure
(define (read-request conn host-port port-addresses) (define ((make-read-request
#:connection-close? [connection-close? #f])
conn host-port port-addresses)
(define ip (define ip
(connection-i-port conn)) (connection-i-port conn))
(define-values (method uri major minor) (define-values (method uri major minor)
@ -46,8 +47,23 @@
(values (values
(make-request method uri headers bindings/raw-promise raw-post-data (make-request method uri headers bindings/raw-promise raw-post-data
host-ip host-port client-ip) host-ip host-port client-ip)
(close-connection? headers major minor (or connection-close?
client-ip host-ip))) (close-connection? headers major minor
client-ip host-ip))))
(define (make-ext:read-request
#:connection-close? [connection-close? #f])
(define read-request
(make-read-request #:connection-close? connection-close?))
(define (ext:read-request conn host-port port-addresses)
(with-handlers ([exn:fail?
(lambda (exn)
(kill-connection! conn)
(raise exn))])
(read-request conn host-port port-addresses)))
ext:read-request)
(define ext:read-request (make-ext:read-request #:connection-close? #f))
;; ************************************************** ;; **************************************************
;; close-connection? ;; close-connection?
@ -153,14 +169,14 @@
(cond (cond
[(bytes-ci=? #"GET" meth) [(bytes-ci=? #"GET" meth)
(values (delay (values (delay
(filter (lambda (x) x) (filter (lambda (x) x)
(map (match-lambda (map (match-lambda
[(list-rest k v) [(list-rest k v)
(if (and (symbol? k) (string? v)) (if (and (symbol? k) (string? v))
(make-binding:form (string->bytes/utf-8 (symbol->string k)) (make-binding:form (string->bytes/utf-8 (symbol->string k))
(string->bytes/utf-8 v)) (string->bytes/utf-8 v))
#f)]) #f)])
(url-query uri)))) (url-query uri))))
#f)] #f)]
[(bytes-ci=? #"POST" meth) [(bytes-ci=? #"POST" meth)
(local (local
@ -174,17 +190,17 @@
; I think this is reasonable because the Content-Type said it would have this format ; I think this is reasonable because the Content-Type said it would have this format
(define bs (define bs
(map (match-lambda (map (match-lambda
[(struct mime-part (headers contents)) [(struct mime-part (headers contents))
(define rhs (header-value (headers-assq* #"Content-Disposition" headers))) (define rhs (header-value (headers-assq* #"Content-Disposition" headers)))
(match (list (regexp-match #"filename=(\"([^\"]*)\"|([^ ;]*))" rhs) (match (list (regexp-match #"filename=(\"([^\"]*)\"|([^ ;]*))" rhs)
(regexp-match #"[^e]name=(\"([^\"]*)\"|([^ ;]*))" rhs)) (regexp-match #"[^e]name=(\"([^\"]*)\"|([^ ;]*))" rhs))
[(list #f #f) [(list #f #f)
(network-error 'reading-bindings "Couldn't extract form field name for file upload")] (network-error 'reading-bindings "Couldn't extract form field name for file upload")]
[(list #f (list _ _ f0 f1)) [(list #f (list _ _ f0 f1))
(make-binding:form (or f0 f1) (apply bytes-append contents))] (make-binding:form (or f0 f1) (apply bytes-append contents))]
[(list (list _ _ f00 f01) (list _ _ f10 f11)) [(list (list _ _ f00 f01) (list _ _ f10 f11))
(make-binding:file (or f10 f11) (or f00 f01) headers (apply bytes-append contents))])]) (make-binding:file (or f10 f11) (or f00 f01) headers (apply bytes-append contents))])])
(read-mime-multipart content-boundary in))) (read-mime-multipart content-boundary in)))
(values (values
(delay bs) (delay bs)
#f)])] #f)])]

View File

@ -22,6 +22,7 @@ This module provides functions for launching dispatching servers.
@defproc[(serve [#:dispatch dispatch dispatcher/c] @defproc[(serve [#:dispatch dispatch dispatcher/c]
[#:confirmation-channel confirmation-channel (or/c false/c async-channel?) #f] [#:confirmation-channel confirmation-channel (or/c false/c async-channel?) #f]
[#:connection-close? connection-close? boolean? #f]
[#:tcp@ tcp@ (unit/c (import) (export tcp^)) raw:tcp@] [#:tcp@ tcp@ (unit/c (import) (export tcp^)) raw:tcp@]
[#:port port tcp-listen-port? 80] [#:port port tcp-listen-port? 80]
[#:listen-ip listen-ip (or/c string? false/c) #f] [#:listen-ip listen-ip (or/c string? false/c) #f]
@ -31,6 +32,9 @@ This module provides functions for launching dispatching servers.
Constructs an appropriate @racket[dispatch-server-config^], invokes the Constructs an appropriate @racket[dispatch-server-config^], invokes the
@racket[dispatch-server@], and calls its @racket[serve] function. @racket[dispatch-server@], and calls its @racket[serve] function.
If @racket[connection-close?] is @racket[#t], then every connection is closed after one
request. Otherwise, the client decides based on what HTTP version it uses.
The @racket[#:tcp@] keyword is provided for building an SSL server. See @secref["faq:https"]. The @racket[#:tcp@] keyword is provided for building an SSL server. See @secref["faq:https"].
} }
@ -51,6 +55,7 @@ from a given path:
@defproc[(serve/ports [#:dispatch dispatch dispatcher/c] @defproc[(serve/ports [#:dispatch dispatch dispatcher/c]
[#:confirmation-channel confirmation-channel (or/c false/c async-channel?) #f] [#:confirmation-channel confirmation-channel (or/c false/c async-channel?) #f]
[#:connection-close? connection-close? boolean? #f]
[#:tcp@ tcp@ (unit/c (import) (export tcp^)) raw:tcp@] [#:tcp@ tcp@ (unit/c (import) (export tcp^)) raw:tcp@]
[#:ports ports (listof tcp-listen-port?) (list 80)] [#:ports ports (listof tcp-listen-port?) (list 80)]
[#:listen-ip listen-ip (or/c string? false/c) #f] [#:listen-ip listen-ip (or/c string? false/c) #f]
@ -63,6 +68,7 @@ from a given path:
@defproc[(serve/ips+ports [#:dispatch dispatch dispatcher/c] @defproc[(serve/ips+ports [#:dispatch dispatch dispatcher/c]
[#:confirmation-channel confirmation-channel (or/c false/c async-channel?) #f] [#:confirmation-channel confirmation-channel (or/c false/c async-channel?) #f]
[#:connection-close? connection-close? boolean? #f]
[#:tcp@ tcp@ (unit/c (import) (export tcp^)) raw:tcp@] [#:tcp@ tcp@ (unit/c (import) (export tcp^)) raw:tcp@]
[#:ips+ports ips+ports (listof (cons/c (or/c string? false/c) (listof tcp-listen-port?))) (list (cons #f (list 80)))] [#:ips+ports ips+ports (listof (cons/c (or/c string? false/c) (listof tcp-listen-port?))) (list (cons #f (list 80)))]
[#:max-waiting max-waiting integer? 40] [#:max-waiting max-waiting integer? 40]

View File

@ -46,6 +46,7 @@ These functions optimize the construction of dispatchers and launching of server
@defproc[(serve/launch/wait @defproc[(serve/launch/wait
[make-dispatcher (semaphore? . -> . dispatcher/c)] [make-dispatcher (semaphore? . -> . dispatcher/c)]
[#:connection-close? connection-close? boolean? #f]
[#:launch-path launch-path (or/c false/c string?) #f] [#:launch-path launch-path (or/c false/c string?) #f]
[#:banner? banner? boolean? #f] [#:banner? banner? boolean? #f]
[#:listen-ip listen-ip (or/c false/c string?) "127.0.0.1"] [#:listen-ip listen-ip (or/c false/c string?) "127.0.0.1"]
@ -67,9 +68,11 @@ These functions optimize the construction of dispatchers and launching of server
connections to all of the listening machine's addresses. Otherwise, the server accepts connections only at the interface(s) associated with the given string. connections to all of the listening machine's addresses. Otherwise, the server accepts connections only at the interface(s) associated with the given string.
For example, providing @racket["127.0.0.1"] (the default) as @racket[listen-ip] creates a server that accepts only connections to @racket["127.0.0.1"] (the loopback interface) from the local machine. For example, providing @racket["127.0.0.1"] (the default) as @racket[listen-ip] creates a server that accepts only connections to @racket["127.0.0.1"] (the loopback interface) from the local machine.
If @racket[ssl-key] and @racket[ssl-cert] are not false, then the server runs in HTTPS mode with @racket[ssl-cert] If @racket[ssl-key] and @racket[ssl-cert] are not false, then the server runs in HTTPS mode with @racket[ssl-cert]
and @racket[ssl-key] as paths to the certificate and private key. and @racket[ssl-key] as paths to the certificate and private key.
If @racket[connection-close?] is @racket[#t], then every connection is closed after one
request. Otherwise, the client decides based on what HTTP version it uses.
} }
} }

View File

@ -124,6 +124,7 @@ and if @racket[serve/servlet] is run in another module.
@defproc[(serve/servlet [start (request? . -> . response/c)] @defproc[(serve/servlet [start (request? . -> . response/c)]
[#:command-line? command-line? boolean? #f] [#:command-line? command-line? boolean? #f]
[#:connection-close? connection-close? boolean? #f]
[#:launch-browser? launch-browser? boolean? (not command-line?)] [#:launch-browser? launch-browser? boolean? (not command-line?)]
[#:quit? quit? boolean? (not command-line?)] [#:quit? quit? boolean? (not command-line?)]
[#:banner? banner? boolean? (not command-line?)] [#:banner? banner? boolean? (not command-line?)]
@ -204,6 +205,9 @@ and if @racket[serve/servlet] is run in another module.
If @racket[log-file] is given, then it used to log requests using @racket[log-format] as the format. Allowable formats If @racket[log-file] is given, then it used to log requests using @racket[log-format] as the format. Allowable formats
are those allowed by @racket[log-format->format]. are those allowed by @racket[log-format->format].
If @racket[connection-close?] is @racket[#t], then every connection is closed after one
request. Otherwise, the client decides based on what HTTP version it uses.
} }
} }

View File

@ -36,6 +36,7 @@
dispatcher/c)] dispatcher/c)]
[serve/launch/wait (((semaphore? . -> . dispatcher/c)) [serve/launch/wait (((semaphore? . -> . dispatcher/c))
(#:launch-path (or/c false/c string?) (#:launch-path (or/c false/c string?)
#:connection-close? boolean?
#:banner? boolean? #:banner? boolean?
#:listen-ip (or/c false/c string?) #:listen-ip (or/c false/c string?)
#:port tcp-listen-port? #:port tcp-listen-port?
@ -86,6 +87,8 @@
(define (serve/launch/wait (define (serve/launch/wait
dispatcher dispatcher
#:connection-close?
[connection-close? #f]
#:launch-path #:launch-path
[launch-path #f] [launch-path #f]
#:banner? #:banner?
@ -104,6 +107,7 @@
(define confirm-ch (make-async-channel 1)) (define confirm-ch (make-async-channel 1))
(define shutdown-server (define shutdown-server
(serve #:confirmation-channel confirm-ch (serve #:confirmation-channel confirm-ch
#:connection-close? connection-close?
#:dispatch (dispatcher sema) #:dispatch (dispatcher sema)
#:listen-ip listen-ip #:listen-ip listen-ip
#:port port-arg #:port port-arg

View File

@ -39,7 +39,8 @@
(provide/contract (provide/contract
[serve/servlet (((request? . -> . response/c)) [serve/servlet (((request? . -> . response/c))
(#:command-line? boolean? (#:connection-close? boolean?
#:command-line? boolean?
#:launch-browser? boolean? #:launch-browser? boolean?
#:quit? boolean? #:quit? boolean?
#:banner? boolean? #:banner? boolean?
@ -75,6 +76,8 @@
(define (serve/servlet (define (serve/servlet
start start
#:connection-close?
[connection-close? #f]
#:command-line? #:command-line?
[command-line? #f] [command-line? #f]
#:launch-browser? #:launch-browser?
@ -172,7 +175,8 @@
#:indices (list "index.html" "index.htm")) #:indices (list "index.html" "index.htm"))
(lift:make file-not-found-responder))) (lift:make file-not-found-responder)))
(serve/launch/wait (serve/launch/wait
dispatcher dispatcher
#:connection-close? connection-close?
#:launch-path (if launch-browser? servlet-path #f) #:launch-path (if launch-browser? servlet-path #f)
#:banner? banner? #:banner? banner?
#:listen-ip listen-ip #:listen-ip listen-ip

View File

@ -17,28 +17,31 @@
[serve [serve
(->* (#:dispatch dispatcher/c) (->* (#:dispatch dispatcher/c)
(#:confirmation-channel (or/c false/c async-channel?) (#:confirmation-channel (or/c false/c async-channel?)
#:tcp@ (unit/c (import) (export tcp^)) #:connection-close? boolean?
#:port tcp-listen-port? #:tcp@ (unit/c (import) (export tcp^))
#:listen-ip (or/c false/c string?) #:port tcp-listen-port?
#:max-waiting number? #:listen-ip (or/c false/c string?)
#:initial-connection-timeout number?) #:max-waiting number?
#:initial-connection-timeout number?)
(-> void))] (-> void))]
[serve/ports [serve/ports
(->* (#:dispatch dispatcher/c) (->* (#:dispatch dispatcher/c)
(#:confirmation-channel (or/c false/c async-channel?) (#:confirmation-channel (or/c false/c async-channel?)
#:tcp@ (unit/c (import) (export tcp^)) #:connection-close? boolean?
#:ports (listof tcp-listen-port?) #:tcp@ (unit/c (import) (export tcp^))
#:listen-ip (or/c false/c string?) #:ports (listof tcp-listen-port?)
#:max-waiting number? #:listen-ip (or/c false/c string?)
#:initial-connection-timeout number?) #:max-waiting number?
#:initial-connection-timeout number?)
(-> void))] (-> void))]
[serve/ips+ports [serve/ips+ports
(->* (#:dispatch dispatcher/c) (->* (#:dispatch dispatcher/c)
(#:confirmation-channel (or/c false/c async-channel?) (#:confirmation-channel (or/c false/c async-channel?)
#:tcp@ (unit/c (import) (export tcp^)) #:connection-close? boolean?
#:ips+ports (listof (cons/c (or/c false/c string?) (listof tcp-listen-port?))) #:tcp@ (unit/c (import) (export tcp^))
#:max-waiting number? #:ips+ports (listof (cons/c (or/c false/c string?) (listof tcp-listen-port?)))
#:initial-connection-timeout number?) #:max-waiting number?
#:initial-connection-timeout number?)
(-> void))] (-> void))]
[do-not-return (-> void)] [do-not-return (-> void)]
[serve/web-config@ (((unit/c (import) (export web-config^))) (#:tcp@ (unit/c (import) (export tcp^))) . ->* . (-> void?))]) [serve/web-config@ (((unit/c (import) (export web-config^))) (#:tcp@ (unit/c (import) (export tcp^))) . ->* . (-> void?))])
@ -49,12 +52,15 @@
(define (serve (define (serve
#:dispatch dispatch #:dispatch dispatch
#:confirmation-channel [confirmation-channel #f] #:confirmation-channel [confirmation-channel #f]
#:connection-close? [connection-close? #f]
#:tcp@ [tcp@ raw:tcp@] #:tcp@ [tcp@ raw:tcp@]
#:port [port 80] #:port [port 80]
#:listen-ip [listen-ip #f] #:listen-ip [listen-ip #f]
#:max-waiting [max-waiting 40] #:max-waiting [max-waiting 40]
#:initial-connection-timeout [initial-connection-timeout 60]) #:initial-connection-timeout [initial-connection-timeout 60])
(define read-request http:read-request) (define read-request
(http:make-read-request
#:connection-close? connection-close?))
(define-unit-binding a-tcp@ (define-unit-binding a-tcp@
tcp@ (import) (export tcp^)) tcp@ (import) (export tcp^))
(define-compound-unit/infer dispatch-server@/tcp@ (define-compound-unit/infer dispatch-server@/tcp@
@ -65,12 +71,13 @@
dispatch-server@/tcp@ dispatch-server@/tcp@
(import dispatch-server-config^) (import dispatch-server-config^)
(export dispatch-server^)) (export dispatch-server^))
(serve #:confirmation-channel confirmation-channel)) (serve #:confirmation-channel confirmation-channel))
(define (serve/ports (define (serve/ports
#:dispatch dispatch #:dispatch dispatch
#:confirmation-channel [confirmation-channel #f] #:confirmation-channel [confirmation-channel #f]
#:connection-close? [connection-close? #f]
#:tcp@ [tcp@ raw:tcp@] #:tcp@ [tcp@ raw:tcp@]
#:ports [ports (list 80)] #:ports [ports (list 80)]
#:listen-ip [listen-ip #f] #:listen-ip [listen-ip #f]
@ -80,6 +87,7 @@
(map (lambda (port) (map (lambda (port)
(serve #:dispatch dispatch (serve #:dispatch dispatch
#:confirmation-channel confirmation-channel #:confirmation-channel confirmation-channel
#:connection-close? connection-close?
#:tcp@ tcp@ #:tcp@ tcp@
#:port port #:port port
#:listen-ip listen-ip #:listen-ip listen-ip
@ -92,6 +100,7 @@
(define (serve/ips+ports (define (serve/ips+ports
#:dispatch dispatch #:dispatch dispatch
#:confirmation-channel [confirmation-channel #f] #:confirmation-channel [confirmation-channel #f]
#:connection-close? [connection-close? #f]
#:tcp@ [tcp@ raw:tcp@] #:tcp@ [tcp@ raw:tcp@]
#:ips+ports [ips+ports (list (cons #f (list 80)))] #:ips+ports [ips+ports (list (cons #f (list 80)))]
#:max-waiting [max-waiting 40] #:max-waiting [max-waiting 40]
@ -101,6 +110,7 @@
[(list-rest listen-ip ports) [(list-rest listen-ip ports)
(serve #:dispatch dispatch (serve #:dispatch dispatch
#:confirmation-channel confirmation-channel #:confirmation-channel confirmation-channel
#:connection-close? connection-close?
#:tcp@ tcp@ #:tcp@ tcp@
#:ports ports #:ports ports
#:listen-ip listen-ip #:listen-ip listen-ip