add support for redirections to get-pure-port and add get-pure-port/headers
This commit is contained in:
parent
1fa6129afc
commit
11a3d9b0ac
|
@ -188,7 +188,8 @@ Determines the default conversion to and from strings for
|
||||||
|
|
||||||
@deftogether[(
|
@deftogether[(
|
||||||
@defproc[(get-pure-port [URL url?]
|
@defproc[(get-pure-port [URL url?]
|
||||||
[header (listof string?) null])
|
[header (listof string?) null]
|
||||||
|
[#:redirections redirections exact-nonnegative-integer? 0])
|
||||||
input-port?]
|
input-port?]
|
||||||
@defproc[(head-pure-port [URL url?]
|
@defproc[(head-pure-port [URL url?]
|
||||||
[header (listof string?) null])
|
[header (listof string?) null])
|
||||||
|
@ -204,7 +205,9 @@ optional list of strings can be used to send header lines to the
|
||||||
server.
|
server.
|
||||||
|
|
||||||
The GET method is used to retrieve whatever information is identified
|
The GET method is used to retrieve whatever information is identified
|
||||||
by @racket[URL].
|
by @racket[URL]. If @racket[redirections] is not @racket[0], then
|
||||||
|
@racket[get-pure-port] will follow redirections from the server,
|
||||||
|
up to the limit given by @racket[redirections].
|
||||||
|
|
||||||
The HEAD method is identical to GET, except the server must not return
|
The HEAD method is identical to GET, except the server must not return
|
||||||
a message body. The meta-information returned in a response to a HEAD
|
a message body. The meta-information returned in a response to a HEAD
|
||||||
|
@ -304,6 +307,18 @@ empty string, or it will be a string matching the following regexp:
|
||||||
#rx"^HTTP/.*?(\r\n\r\n|\n\n|\r\r)"
|
#rx"^HTTP/.*?(\r\n\r\n|\n\n|\r\r)"
|
||||||
]}
|
]}
|
||||||
|
|
||||||
|
@defproc[(get-pure-port/headers
|
||||||
|
[url url?]
|
||||||
|
[headers (listof string?) '()]
|
||||||
|
[#:redirections redirections exact-nonnegative-integer? 0])
|
||||||
|
(values input-port? string?)]{
|
||||||
|
This function is an alternative to calling @racket[get-impure-port] and
|
||||||
|
@racket[purify-port] when needing to follow redirections.
|
||||||
|
|
||||||
|
That is, it does a GET request on @racket[url], follows up to
|
||||||
|
@racket[redirections] redirections and returns a port containing
|
||||||
|
the data as well as the headers for the final connection.
|
||||||
|
}
|
||||||
|
|
||||||
@defproc*[([(call/input-url [URL url?]
|
@defproc*[([(call/input-url [URL url?]
|
||||||
[connect (url? . -> . input-port?)]
|
[connect (url? . -> . input-port?)]
|
||||||
|
|
|
@ -191,20 +191,58 @@
|
||||||
(schemeless-url url)]
|
(schemeless-url url)]
|
||||||
[(or (string=? scheme "http")
|
[(or (string=? scheme "http")
|
||||||
(string=? scheme "https"))
|
(string=? scheme "https"))
|
||||||
(let loop ([redirections redirections])
|
(cond
|
||||||
(cond
|
[(or (not get?)
|
||||||
[(zero? redirections)
|
;; do not follow redirections for POST
|
||||||
(let ([port (http://getpost-impure-port
|
(zero? redirections))
|
||||||
get? url post-data strings)])
|
(let ([port (http://getpost-impure-port
|
||||||
(purify-http-port port))]
|
get? url post-data strings)])
|
||||||
[else
|
(purify-http-port port))]
|
||||||
(let ([port (http://getpost-impure-port
|
[else
|
||||||
get? url post-data strings)])
|
(define-values (port header)
|
||||||
(purify-http-port port))]))]
|
(get-pure-port/headers url strings #:redirections redirections))
|
||||||
|
port])]
|
||||||
[(string=? scheme "file")
|
[(string=? scheme "file")
|
||||||
(file://get-pure-port url)]
|
(file://get-pure-port url)]
|
||||||
[else (url-error "Scheme ~a unsupported" scheme)])))
|
[else (url-error "Scheme ~a unsupported" scheme)])))
|
||||||
|
|
||||||
|
(define (get-pure-port/headers url [strings '()] #:redirections [redirections 0])
|
||||||
|
(let redirection-loop ([redirections redirections] [url url])
|
||||||
|
(define ip
|
||||||
|
(http://getpost-impure-port #t url #f '()))
|
||||||
|
(define status (read-line ip 'return-linefeed))
|
||||||
|
(define-values (new-url chunked? headers)
|
||||||
|
(let loop ([new-url #f] [chunked? #f] [headers '()])
|
||||||
|
(define line (read-line ip 'return-linefeed))
|
||||||
|
(when (eof-object? line)
|
||||||
|
(error 'getpost-pure-port
|
||||||
|
"connection ended before headers ended (when trying to follow a redirection)"))
|
||||||
|
(cond
|
||||||
|
[(equal? line "") (values new-url chunked? headers)]
|
||||||
|
[(equal? chunked-header-line line) (loop new-url #t (cons line headers))]
|
||||||
|
[(regexp-match #rx"^Location: (.*)$" line)
|
||||||
|
=>
|
||||||
|
(λ (m)
|
||||||
|
(define m1 (list-ref m 1))
|
||||||
|
(define url (with-handlers ((exn:fail? (λ (x) #f)))
|
||||||
|
(string->url m1)))
|
||||||
|
(loop (or url new-url) chunked? (cons line headers)))]
|
||||||
|
[else (loop new-url chunked? (cons line headers))])))
|
||||||
|
(define redirection-status-line?
|
||||||
|
(regexp-match #rx"^HTTP/[0-9]+[.][0-9]+ 3[0-9][0-9]" status))
|
||||||
|
(cond
|
||||||
|
[(and redirection-status-line? new-url (not (zero? redirections)))
|
||||||
|
(redirection-loop (- redirections 1) new-url)]
|
||||||
|
[else
|
||||||
|
(define-values (in-pipe out-pipe) (make-pipe))
|
||||||
|
(thread
|
||||||
|
(λ ()
|
||||||
|
(http-pipe-data chunked? ip out-pipe)
|
||||||
|
(close-input-port ip)))
|
||||||
|
(values in-pipe
|
||||||
|
(apply string-append (map (λ (x) (string-append x "\r\n"))
|
||||||
|
(reverse headers))))])))
|
||||||
|
|
||||||
;; get-pure-port : url [x list (str)] -> in-port
|
;; get-pure-port : url [x list (str)] -> in-port
|
||||||
(define (get-pure-port url [strings '()] #:redirections [redirections 0])
|
(define (get-pure-port url [strings '()] #:redirections [redirections 0])
|
||||||
(getpost-pure-port #t url #f strings redirections))
|
(getpost-pure-port #t url #f strings redirections))
|
||||||
|
@ -342,11 +380,13 @@
|
||||||
(error 'purify-http-port "Connection ended before headers ended"))
|
(error 'purify-http-port "Connection ended before headers ended"))
|
||||||
(if (string=? l "")
|
(if (string=? l "")
|
||||||
#f
|
#f
|
||||||
(if (string=? l "Transfer-Encoding: chunked")
|
(if (string=? l chunked-header-line)
|
||||||
(begin (http-read-headers ip)
|
(begin (http-read-headers ip)
|
||||||
#t)
|
#t)
|
||||||
(http-read-headers ip))))
|
(http-read-headers ip))))
|
||||||
|
|
||||||
|
(define chunked-header-line "Transfer-Encoding: chunked")
|
||||||
|
|
||||||
(define (http-pipe-data chunked? ip op)
|
(define (http-pipe-data chunked? ip op)
|
||||||
(if chunked?
|
(if chunked?
|
||||||
(http-pipe-chunk ip op)
|
(http-pipe-chunk ip op)
|
||||||
|
@ -626,7 +666,7 @@
|
||||||
(url->string (url? . -> . string?))
|
(url->string (url? . -> . string?))
|
||||||
(url->path (->* (url?) ((one-of/c 'unix 'windows)) path-for-some-system?))
|
(url->path (->* (url?) ((one-of/c 'unix 'windows)) path-for-some-system?))
|
||||||
|
|
||||||
(get-pure-port (->* (url?) ((listof string?)) input-port?))
|
(get-pure-port (->* (url?) ((listof string?) #:redirections exact-nonnegative-integer?) input-port?))
|
||||||
(get-impure-port (->* (url?) ((listof string?)) input-port?))
|
(get-impure-port (->* (url?) ((listof string?)) input-port?))
|
||||||
(post-pure-port (->* (url? (or/c false/c bytes?)) ((listof string?)) input-port?))
|
(post-pure-port (->* (url? (or/c false/c bytes?)) ((listof string?)) input-port?))
|
||||||
(post-impure-port (->* (url? bytes?) ((listof string?)) input-port?))
|
(post-impure-port (->* (url? bytes?) ((listof string?)) input-port?))
|
||||||
|
@ -638,6 +678,8 @@
|
||||||
(put-impure-port (->* (url? bytes?) ((listof string?)) input-port?))
|
(put-impure-port (->* (url? bytes?) ((listof string?)) input-port?))
|
||||||
(display-pure-port (input-port? . -> . void?))
|
(display-pure-port (input-port? . -> . void?))
|
||||||
(purify-port (input-port? . -> . string?))
|
(purify-port (input-port? . -> . string?))
|
||||||
|
(get-pure-port/headers (->* (url?) ((listof string?) #:redirections exact-nonnegative-integer?)
|
||||||
|
(values input-port? string?)))
|
||||||
(netscape/string->url (string? . -> . url?))
|
(netscape/string->url (string? . -> . url?))
|
||||||
(call/input-url (case-> (-> url?
|
(call/input-url (case-> (-> url?
|
||||||
(-> url? input-port?)
|
(-> url? input-port?)
|
||||||
|
|
|
@ -4,38 +4,48 @@
|
||||||
openssl
|
openssl
|
||||||
tests/eli-tester)
|
tests/eli-tester)
|
||||||
|
|
||||||
(define (run-tests scheme wrap-ports)
|
(define (run-tests scheme wrap-ports skip-actual-redirect?)
|
||||||
(define ((make-tester url->port) response)
|
(define ((make-tester url->port) response . responses)
|
||||||
(define port-no 9001)
|
(define first-port-no 9001)
|
||||||
(define server-cust
|
(define server-cust
|
||||||
(make-custodian))
|
(make-custodian))
|
||||||
(parameterize ([current-custodian server-cust])
|
(parameterize ([current-custodian server-cust])
|
||||||
(thread
|
(for ([response (in-list (cons response responses))]
|
||||||
(λ ()
|
[port-no (in-naturals first-port-no)])
|
||||||
(run-server port-no
|
(thread
|
||||||
(lambda (ip op)
|
(λ ()
|
||||||
(let-values ([(ip op) (wrap-ports ip op)])
|
(run-server port-no
|
||||||
(regexp-match #rx"(\r\n|^)\r\n" ip)
|
(lambda (ip op)
|
||||||
(display response op)
|
(let-values ([(ip op) (wrap-ports ip op)])
|
||||||
(close-output-port op)
|
(regexp-match #rx"(\r\n|^)\r\n" ip)
|
||||||
(close-input-port ip)))
|
(display response op)
|
||||||
+inf.0))))
|
(close-output-port op)
|
||||||
|
(close-input-port ip)))
|
||||||
|
+inf.0)))))
|
||||||
(sleep 1)
|
(sleep 1)
|
||||||
(dynamic-wind
|
(dynamic-wind
|
||||||
void
|
void
|
||||||
|
(λ ()
|
||||||
|
(call-with-values
|
||||||
(λ ()
|
(λ ()
|
||||||
(port->string
|
(url->port
|
||||||
(url->port
|
(url scheme #f "localhost" first-port-no
|
||||||
(url scheme #f "localhost" port-no
|
#t empty empty #f)))
|
||||||
#t empty empty #f))))
|
(λ vals (apply values (cons (port->string (car vals)) (cdr vals))))))
|
||||||
(λ ()
|
(λ ()
|
||||||
(custodian-shutdown-all server-cust))))
|
(custodian-shutdown-all server-cust))))
|
||||||
|
|
||||||
(define get-pure
|
(define get-pure
|
||||||
(make-tester get-pure-port))
|
(make-tester get-pure-port))
|
||||||
(define get-impure
|
(define get-impure
|
||||||
(make-tester get-impure-port))
|
(make-tester get-impure-port))
|
||||||
|
(define get-pure/redirect
|
||||||
|
(make-tester (λ (x) (get-pure-port x #:redirections 1))))
|
||||||
|
(define get-pure/headers
|
||||||
|
(make-tester get-pure-port/headers))
|
||||||
|
(define get-pure/headers/redirect
|
||||||
|
(make-tester (λ (x) (get-pure-port/headers x #:redirections 1))))
|
||||||
|
|
||||||
(test
|
(test
|
||||||
(get-pure
|
(get-pure
|
||||||
"HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nTransfer-Encoding: chunked\r\n\r\n24\r\nThis is the data in the first chunk \r\n1A\r\nand this is the second one\r\n0\r\n")
|
"HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nTransfer-Encoding: chunked\r\n\r\n24\r\nThis is the data in the first chunk \r\n1A\r\nand this is the second one\r\n0\r\n")
|
||||||
|
@ -52,21 +62,70 @@
|
||||||
=>
|
=>
|
||||||
"This is the data in the first chand this is the second oneXXXXXXX"
|
"This is the data in the first chand this is the second oneXXXXXXX"
|
||||||
|
|
||||||
(get-impure
|
(get-pure/redirect
|
||||||
"HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nTransfer-Encoding: chunked\r\n\r\n23\r\nThis is the data in the first chunk\r\n1A\r\nand this is the second one\r\n0\r\n")
|
"HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nTransfer-Encoding: chunked\r\n\r\n24\r\nThis is the data in the first chunk \r\n1A\r\nand this is the second one\r\n0\r\n")
|
||||||
=>
|
=>
|
||||||
"HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nTransfer-Encoding: chunked\r\n\r\n23\r\nThis is the data in the first chunk\r\n1A\r\nand this is the second one\r\n0\r\n"
|
"This is the data in the first chunk and this is the second one"
|
||||||
|
|
||||||
|
(get-pure/redirect
|
||||||
|
"HTTP/1.0 200 OK\r\nContent-Type: text/plain\r\n\r\nThis is the data in the first chunk and this is the second one")
|
||||||
|
=>
|
||||||
|
"This is the data in the first chunk and this is the second one"
|
||||||
|
|
||||||
|
(get-pure/redirect
|
||||||
|
"HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nTransfer-Encoding: chunked\r\n\r\n20\r\nThis is the data in the first ch\r\n21\r\nand this is the second oneXXXXXXX\r\n0\r\n")
|
||||||
|
=>
|
||||||
|
"This is the data in the first chand this is the second oneXXXXXXX"
|
||||||
|
|
||||||
(get-impure
|
(get-impure
|
||||||
"HTTP/1.0 200 OK\r\nContent-Type: text/plain\r\n\r\nThis is the data in the first chunk and this is the second one\r\n")
|
"HTTP/1.0 200 OK\r\nContent-Type: text/plain\r\n\r\nThis is the data in the first chunk and this is the second one\r\n")
|
||||||
=>
|
=>
|
||||||
"HTTP/1.0 200 OK\r\nContent-Type: text/plain\r\n\r\nThis is the data in the first chunk and this is the second one\r\n"
|
"HTTP/1.0 200 OK\r\nContent-Type: text/plain\r\n\r\nThis is the data in the first chunk and this is the second one\r\n"
|
||||||
))
|
|
||||||
|
(get-pure/headers
|
||||||
|
"HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nTransfer-Encoding: chunked\r\n\r\n24\r\nThis is the data in the first chunk \r\n1A\r\nand this is the second one\r\n0\r\n")
|
||||||
|
=>
|
||||||
|
(values "This is the data in the first chunk and this is the second one"
|
||||||
|
"Content-Type: text/plain\r\nTransfer-Encoding: chunked\r\n")
|
||||||
|
|
||||||
|
(get-pure/headers
|
||||||
|
"HTTP/1.0 200 OK\r\nContent-Type: text/plain\r\n\r\nThis is the data in the first chunk and this is the second one")
|
||||||
|
=>
|
||||||
|
(values "This is the data in the first chunk and this is the second one"
|
||||||
|
"Content-Type: text/plain\r\n")
|
||||||
|
|
||||||
|
(get-pure/headers
|
||||||
|
"HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nTransfer-Encoding: chunked\r\n\r\n20\r\nThis is the data in the first ch\r\n21\r\nand this is the second oneXXXXXXX\r\n0\r\n")
|
||||||
|
=>
|
||||||
|
(values "This is the data in the first chand this is the second oneXXXXXXX"
|
||||||
|
"Content-Type: text/plain\r\nTransfer-Encoding: chunked\r\n")
|
||||||
|
|
||||||
|
(get-pure/headers
|
||||||
|
"HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nTransfer-Encoding: chunked\r\nAnother-Header: ta-daa\r\n\r\n20\r\nThis is the data in the first ch\r\n21\r\nand this is the second oneXXXXXXX\r\n0\r\n")
|
||||||
|
=>
|
||||||
|
(values "This is the data in the first chand this is the second oneXXXXXXX"
|
||||||
|
"Content-Type: text/plain\r\nTransfer-Encoding: chunked\r\nAnother-Header: ta-daa\r\n")
|
||||||
|
)
|
||||||
|
|
||||||
|
(unless skip-actual-redirect?
|
||||||
|
(test
|
||||||
|
(get-pure/redirect
|
||||||
|
"HTTP/1.1 301 Moved Permanently\r\nLocation: http://localhost:9002/whatever\r\n\r\nstuff"
|
||||||
|
(string-append
|
||||||
|
"HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nTransfer-Encoding: chunked\r\n\r\n"
|
||||||
|
"24\r\nThis is the data in the first chunk \r\n1A\r\nand this is the second one\r\n0\r\n"))
|
||||||
|
|
||||||
|
(get-pure/headers/redirect
|
||||||
|
"HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nTransfer-Encoding: chunked\r\nAnother-Header: ta-daa\r\n\r\n20\r\nThis is the data in the first ch\r\n21\r\nand this is the second oneXXXXXXX\r\n0\r\n")
|
||||||
|
=>
|
||||||
|
(values "This is the data in the first chand this is the second oneXXXXXXX"
|
||||||
|
"Content-Type: text/plain\r\nTransfer-Encoding: chunked\r\nAnother-Header: ta-daa\r\n"))))
|
||||||
|
|
||||||
(run-tests "http" values)
|
(run-tests "http" values #f)
|
||||||
(run-tests "https" (let ([ctx (ssl-make-server-context)])
|
(run-tests "https" (let ([ctx (ssl-make-server-context)])
|
||||||
(ssl-load-certificate-chain! ctx (collection-file-path "test.pem" "openssl"))
|
(ssl-load-certificate-chain! ctx (collection-file-path "test.pem" "openssl"))
|
||||||
(ssl-load-private-key! ctx (collection-file-path "test.pem" "openssl"))
|
(ssl-load-private-key! ctx (collection-file-path "test.pem" "openssl"))
|
||||||
(lambda (in out)
|
(lambda (in out)
|
||||||
(ports->ssl-ports in out #:mode 'accept #:context ctx))))
|
(ports->ssl-ports in out #:mode 'accept #:context ctx)))
|
||||||
|
#t)
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@ Added exn:fail:syntax:unbound
|
||||||
Added date*, which extends date to include nanoseconds and a
|
Added date*, which extends date to include nanoseconds and a
|
||||||
time zone name
|
time zone name
|
||||||
Changed seconds->date to accept a real number return a date*
|
Changed seconds->date to accept a real number return a date*
|
||||||
|
Added support for redirections to get-pure-port
|
||||||
|
|
||||||
Version 5.1.3.10
|
Version 5.1.3.10
|
||||||
Added variable-reference->module-declare-inspector, which allows
|
Added variable-reference->module-declare-inspector, which allows
|
||||||
|
|
Loading…
Reference in New Issue
Block a user