diff --git a/pkgs/net-doc/net/scribblings/http-client.scrbl b/pkgs/net-doc/net/scribblings/http-client.scrbl index d0ad117443..3bc71f9645 100644 --- a/pkgs/net-doc/net/scribblings/http-client.scrbl +++ b/pkgs/net-doc/net/scribblings/http-client.scrbl @@ -98,7 +98,7 @@ configured to @tech{auto-reconnect}. [#:method method (or/c bytes? string? symbol?) #"GET"] [#:close? close? boolean? #f] [#:headers headers (listof (or/c bytes? string?)) empty] - [#:content-decode decodes (listof symbol?) '(gzip)] + [#:content-decode decodes (listof symbol?) '(gzip deflate)] [#:data data (or/c false/c bytes? string? data-procedure/c) #f]) void?]{ @@ -120,15 +120,16 @@ are accepted is automatically added. If @racket[close?] is @racket[#t] and @racket[headers] does not contain a @litchar{Connection} header, then a @litchar{Connection: -close} header will be added. +close} header will be added (currently, @racket['gzip] and @racket['deflate] are supported). This function does not support requests that expect @litchar{100 (Continue)} responses. +@history[#:changed "7.6.0.9" @elem{Added support for @racket['deflate] decoding.}] } @defproc[(http-conn-recv! [hc http-conn-liveable?] - [#:content-decode decodes (listof symbol?) '(gzip)] + [#:content-decode decodes (listof symbol?) '(gzip deflate)] [#:method method (or/c bytes? string? symbol?) #"GET"] [#:close? close? boolean? #f]) (values bytes? (listof bytes?) input-port?)]{ @@ -146,7 +147,9 @@ following the response parsing. If @racket[close?] is @racket[#f], then the connection is only closed if the server instructs the client to do so. -@history[#:changed "6.1.1.6" @elem{Added the @racket[#:method] argument.}]} +@history[#:changed "6.1.1.6" @elem{Added the @racket[#:method] argument.} + #:changed "7.6.0.9" @elem{Added support for @racket['deflate] decoding.}] +} @defproc[(http-conn-sendrecv! [hc http-conn-liveable?] [uri (or/c bytes? string?)] @@ -154,12 +157,13 @@ to do so. [#:method method (or/c bytes? string? symbol?) #"GET"] [#:headers headers (listof (or/c bytes? string?)) empty] [#:data data (or/c false/c bytes? string? data-procedure/c) #f] - [#:content-decode decodes (listof symbol?) '(gzip)] + [#:content-decode decodes (listof symbol?) '(gzip deflate)] [#:close? close? boolean? #f]) (values bytes? (listof bytes?) input-port?)]{ Calls @racket[http-conn-send!] and @racket[http-conn-recv!] in sequence. +@history[#:changed "7.6.0.9" @elem{Added support for @racket['deflate] decoding.}] } @defproc[(http-sendrecv [host (or/c bytes? string?)] [uri (or/c bytes? string?)] @@ -169,7 +173,7 @@ Calls @racket[http-conn-send!] and @racket[http-conn-recv!] in sequence. [#:method method (or/c bytes? string? symbol?) #"GET"] [#:headers headers (listof (or/c bytes? string?)) empty] [#:data data (or/c false/c bytes? string? data-procedure/c) #f] - [#:content-decode decodes (listof symbol?) '(gzip)]) + [#:content-decode decodes (listof symbol?) '(gzip deflate)]) (values bytes? (listof bytes?) input-port?)]{ Calls @racket[http-conn-send!] and @racket[http-conn-recv!] in @@ -180,6 +184,7 @@ The HTTP connection is not returned, so it is always closed after one response, which is why there is no @racket[#:closed?] argument like @racket[http-conn-recv!]. +@history[#:changed "7.6.0.9" @elem{Added support for @racket['deflate] decoding.}] } @defproc[(http-conn-CONNECT-tunnel [proxy-host (or/c bytes? string?)] diff --git a/pkgs/net-doc/net/scribblings/url.scrbl b/pkgs/net-doc/net/scribblings/url.scrbl index 937b62f7d4..bfb7d20b2b 100644 --- a/pkgs/net-doc/net/scribblings/url.scrbl +++ b/pkgs/net-doc/net/scribblings/url.scrbl @@ -596,14 +596,14 @@ and @racket[host], or @racket[#f] if no proxy is to be used.} [#:method method (or/c bytes? string? symbol?) #"GET"] [#:headers headers (listof (or/c bytes? string?)) empty] [#:data data (or/c false/c bytes? string? data-procedure/c) #f] - [#:content-decode decodes (listof symbol?) '(gzip)]) + [#:content-decode decodes (listof symbol?) '(gzip deflate)]) (values bytes? (listof bytes?) input-port?)]{ Calls @racket[http-sendrecv] using @racket[u] to populate the host, URI, port, and SSL parameters. This function does not support proxies. -} +@history[#:changed "7.6.0.9" @elem{Added support for @racket['deflate] decoding.}]} @defproc[(tcp-or-tunnel-connect [scheme string?] [host string?] diff --git a/pkgs/net-test/tests/net/http-client.rkt b/pkgs/net-test/tests/net/http-client.rkt index 12ea2b1b21..12f4435491 100644 --- a/pkgs/net-test/tests/net/http-client.rkt +++ b/pkgs/net-test/tests/net/http-client.rkt @@ -81,7 +81,7 @@ (define-syntax (test stx) (syntax-case stx () - [(_ method body raw ereq estatus eheaders econtent) + [(_ method body raw ereq estatus eheaders econtent extra ...) (quasisyntax/loc stx (begin #,(syntax/loc stx @@ -93,7 +93,8 @@ #:port the-port #:method method #:headers empty - #:data body)) + #:data body + extra ...)) raw ereq estatus eheaders econtent)) #,(syntax/loc stx (test-e the-port @@ -109,7 +110,8 @@ #:method method #:headers empty #:close? #t - #:data body) + #:data body + extra ...) (begin0 (hc:http-conn-recv! c #:method method @@ -133,7 +135,8 @@ #:method method #:headers empty #:close? #t - #:data body) + #:data body + extra ...) (begin0 (hc:http-conn-recv! c #:method method @@ -150,132 +153,154 @@ (list (u:path/param "" empty)) empty #f) #:method method #:headers empty - #:data body)) + #:data body + extra ...)) raw ereq estatus eheaders econtent))))])) (tests ["GET" #f "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" - #"GET / HTTP/1.1\r\nHost: localhost:REDACTED\r\nUser-Agent: Racket/REDACTED (net/http-client)\r\nAccept-Encoding: gzip\r\nContent-Length: 0\r\nConnection: close\r\n\r\n" + #"GET / HTTP/1.1\r\nHost: localhost:REDACTED\r\nUser-Agent: Racket/REDACTED (net/http-client)\r\nAccept-Encoding: gzip,deflate\r\nContent-Length: 0\r\nConnection: close\r\n\r\n" #"HTTP/1.1 200 OK" '(#"Content-Type: text/plain" #"Transfer-Encoding: chunked") #"This is the data in the first chunk and this is the second one"] - ["GET" #f "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" + ["GET" #f "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" #"GET / HTTP/1.1\r\nHost: localhost:REDACTED\r\nUser-Agent: Racket/REDACTED (net/http-client)\r\nAccept-Encoding: gzip\r\nContent-Length: 0\r\nConnection: close\r\n\r\n" + #"HTTP/1.1 200 OK" + '(#"Content-Type: text/plain" #"Transfer-Encoding: chunked") + #"This is the data in the first chunk and this is the second one" + #:content-decode '(gzip)] + + ["GET" #f "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" + #"GET / HTTP/1.1\r\nHost: localhost:REDACTED\r\nUser-Agent: Racket/REDACTED (net/http-client)\r\nAccept-Encoding: deflate\r\nContent-Length: 0\r\nConnection: close\r\n\r\n" + #"HTTP/1.1 200 OK" + '(#"Content-Type: text/plain" #"Transfer-Encoding: chunked") + #"This is the data in the first chunk and this is the second one" + #:content-decode '(deflate)] + + ["GET" #f "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" + #"GET / HTTP/1.1\r\nHost: localhost:REDACTED\r\nUser-Agent: Racket/REDACTED (net/http-client)\r\nContent-Length: 0\r\nConnection: close\r\n\r\n" + #"HTTP/1.1 200 OK" + '(#"Content-Type: text/plain" #"Transfer-Encoding: chunked") + #"This is the data in the first chunk and this is the second one" + #:content-decode '()] + + ["GET" #f "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" + #"GET / HTTP/1.1\r\nHost: localhost:REDACTED\r\nUser-Agent: Racket/REDACTED (net/http-client)\r\nAccept-Encoding: gzip,deflate\r\nContent-Length: 0\r\nConnection: close\r\n\r\n" #"HTTP/1.0 200 OK" '(#"Content-Type: text/plain") #"This is the data in the first chunk and this is the second one"] ["GET" #f "HTTP/1.0 200 OK\nContent-Type: text/plain\n\nThis is the data in the first chunk and this is the second one" - #"GET / HTTP/1.1\r\nHost: localhost:REDACTED\r\nUser-Agent: Racket/REDACTED (net/http-client)\r\nAccept-Encoding: gzip\r\nContent-Length: 0\r\nConnection: close\r\n\r\n" + #"GET / HTTP/1.1\r\nHost: localhost:REDACTED\r\nUser-Agent: Racket/REDACTED (net/http-client)\r\nAccept-Encoding: gzip,deflate\r\nContent-Length: 0\r\nConnection: close\r\n\r\n" #"HTTP/1.0 200 OK" '(#"Content-Type: text/plain") #"This is the data in the first chunk and this is the second one"] ["GET" #f "HTTP/1.0 200 OK\r\nContent-Type: text/plain\r\nContent-Length: 62\r\n\r\nThis is the data in the first chunk and this is the second one" - #"GET / HTTP/1.1\r\nHost: localhost:REDACTED\r\nUser-Agent: Racket/REDACTED (net/http-client)\r\nAccept-Encoding: gzip\r\nContent-Length: 0\r\nConnection: close\r\n\r\n" + #"GET / HTTP/1.1\r\nHost: localhost:REDACTED\r\nUser-Agent: Racket/REDACTED (net/http-client)\r\nAccept-Encoding: gzip,deflate\r\nContent-Length: 0\r\nConnection: close\r\n\r\n" #"HTTP/1.0 200 OK" '(#"Content-Type: text/plain" #"Content-Length: 62") #"This is the data in the first chunk and this is the second one"] ["GET" #f "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" - #"GET / HTTP/1.1\r\nHost: localhost:REDACTED\r\nUser-Agent: Racket/REDACTED (net/http-client)\r\nAccept-Encoding: gzip\r\nContent-Length: 0\r\nConnection: close\r\n\r\n" + #"GET / HTTP/1.1\r\nHost: localhost:REDACTED\r\nUser-Agent: Racket/REDACTED (net/http-client)\r\nAccept-Encoding: gzip,deflate\r\nContent-Length: 0\r\nConnection: close\r\n\r\n" #"HTTP/1.0 200 OK" '(#"Content-Type: text/plain") #"This is the data in the first chunk and this is the second one"] ["GET" #f "HTTP/1.0 200 OK\r\nContent-Type: text/plain\r\ncontent-length: 62\r\n\r\nThis is the data in the first chunk and this is the second one" - #"GET / HTTP/1.1\r\nHost: localhost:REDACTED\r\nUser-Agent: Racket/REDACTED (net/http-client)\r\nAccept-Encoding: gzip\r\nContent-Length: 0\r\nConnection: close\r\n\r\n" + #"GET / HTTP/1.1\r\nHost: localhost:REDACTED\r\nUser-Agent: Racket/REDACTED (net/http-client)\r\nAccept-Encoding: gzip,deflate\r\nContent-Length: 0\r\nConnection: close\r\n\r\n" #"HTTP/1.0 200 OK" '(#"Content-Type: text/plain" #"content-length: 62") #"This is the data in the first chunk and this is the second one"] ["GET" #f "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" - #"GET / HTTP/1.1\r\nHost: localhost:REDACTED\r\nUser-Agent: Racket/REDACTED (net/http-client)\r\nAccept-Encoding: gzip\r\nContent-Length: 0\r\nConnection: close\r\n\r\n" + #"GET / HTTP/1.1\r\nHost: localhost:REDACTED\r\nUser-Agent: Racket/REDACTED (net/http-client)\r\nAccept-Encoding: gzip,deflate\r\nContent-Length: 0\r\nConnection: close\r\n\r\n" #"HTTP/1.1 200 OK" '(#"Content-Type: text/plain" #"Transfer-Encoding: chunked") #"This is the data in the first chand this is the second oneXXXXXXX"] ["GET" #f "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" - #"GET / HTTP/1.1\r\nHost: localhost:REDACTED\r\nUser-Agent: Racket/REDACTED (net/http-client)\r\nAccept-Encoding: gzip\r\nContent-Length: 0\r\nConnection: close\r\n\r\n" + #"GET / HTTP/1.1\r\nHost: localhost:REDACTED\r\nUser-Agent: Racket/REDACTED (net/http-client)\r\nAccept-Encoding: gzip,deflate\r\nContent-Length: 0\r\nConnection: close\r\n\r\n" #"HTTP/1.1 200 OK" '(#"Content-Type: text/plain" #"Transfer-Encoding: chunked") #"This is the data in the first chunk and this is the second one"] ["GET" #f "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" - #"GET / HTTP/1.1\r\nHost: localhost:REDACTED\r\nUser-Agent: Racket/REDACTED (net/http-client)\r\nAccept-Encoding: gzip\r\nContent-Length: 0\r\nConnection: close\r\n\r\n" + #"GET / HTTP/1.1\r\nHost: localhost:REDACTED\r\nUser-Agent: Racket/REDACTED (net/http-client)\r\nAccept-Encoding: gzip,deflate\r\nContent-Length: 0\r\nConnection: close\r\n\r\n" #"HTTP/1.0 200 OK" '(#"Content-Type: text/plain") #"This is the data in the first chunk and this is the second one"] ["GET" #f "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" - #"GET / HTTP/1.1\r\nHost: localhost:REDACTED\r\nUser-Agent: Racket/REDACTED (net/http-client)\r\nAccept-Encoding: gzip\r\nContent-Length: 0\r\nConnection: close\r\n\r\n" + #"GET / HTTP/1.1\r\nHost: localhost:REDACTED\r\nUser-Agent: Racket/REDACTED (net/http-client)\r\nAccept-Encoding: gzip,deflate\r\nContent-Length: 0\r\nConnection: close\r\n\r\n" #"HTTP/1.1 200 OK" '(#"Content-Type: text/plain" #"Transfer-Encoding: chunked") #"This is the data in the first chand this is the second oneXXXXXXX"] ["GET" #f "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 / HTTP/1.1\r\nHost: localhost:REDACTED\r\nUser-Agent: Racket/REDACTED (net/http-client)\r\nAccept-Encoding: gzip\r\nContent-Length: 0\r\nConnection: close\r\n\r\n" + #"GET / HTTP/1.1\r\nHost: localhost:REDACTED\r\nUser-Agent: Racket/REDACTED (net/http-client)\r\nAccept-Encoding: gzip,deflate\r\nContent-Length: 0\r\nConnection: close\r\n\r\n" #"HTTP/1.0 200 OK" '(#"Content-Type: text/plain") #"This is the data in the first chunk and this is the second one\r\n"] ["GET" #f "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" - #"GET / HTTP/1.1\r\nHost: localhost:REDACTED\r\nUser-Agent: Racket/REDACTED (net/http-client)\r\nAccept-Encoding: gzip\r\nContent-Length: 0\r\nConnection: close\r\n\r\n" + #"GET / HTTP/1.1\r\nHost: localhost:REDACTED\r\nUser-Agent: Racket/REDACTED (net/http-client)\r\nAccept-Encoding: gzip,deflate\r\nContent-Length: 0\r\nConnection: close\r\n\r\n" #"HTTP/1.1 200 OK" '(#"Content-Type: text/plain" #"Transfer-Encoding: chunked") #"This is the data in the first chunk and this is the second one"] ["GET" #f "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" - #"GET / HTTP/1.1\r\nHost: localhost:REDACTED\r\nUser-Agent: Racket/REDACTED (net/http-client)\r\nAccept-Encoding: gzip\r\nContent-Length: 0\r\nConnection: close\r\n\r\n" + #"GET / HTTP/1.1\r\nHost: localhost:REDACTED\r\nUser-Agent: Racket/REDACTED (net/http-client)\r\nAccept-Encoding: gzip,deflate\r\nContent-Length: 0\r\nConnection: close\r\n\r\n" #"HTTP/1.0 200 OK" '(#"Content-Type: text/plain") #"This is the data in the first chunk and this is the second one"] ["GET" #f "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" - #"GET / HTTP/1.1\r\nHost: localhost:REDACTED\r\nUser-Agent: Racket/REDACTED (net/http-client)\r\nAccept-Encoding: gzip\r\nContent-Length: 0\r\nConnection: close\r\n\r\n" + #"GET / HTTP/1.1\r\nHost: localhost:REDACTED\r\nUser-Agent: Racket/REDACTED (net/http-client)\r\nAccept-Encoding: gzip,deflate\r\nContent-Length: 0\r\nConnection: close\r\n\r\n" #"HTTP/1.1 200 OK" '(#"Content-Type: text/plain" #"Transfer-Encoding: chunked") #"This is the data in the first chand this is the second oneXXXXXXX"] ["GET" #f "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" - #"GET / HTTP/1.1\r\nHost: localhost:REDACTED\r\nUser-Agent: Racket/REDACTED (net/http-client)\r\nAccept-Encoding: gzip\r\nContent-Length: 0\r\nConnection: close\r\n\r\n" + #"GET / HTTP/1.1\r\nHost: localhost:REDACTED\r\nUser-Agent: Racket/REDACTED (net/http-client)\r\nAccept-Encoding: gzip,deflate\r\nContent-Length: 0\r\nConnection: close\r\n\r\n" #"HTTP/1.1 200 OK" '(#"Content-Type: text/plain" #"Transfer-Encoding: chunked" #"Another-Header: ta-daa") #"This is the data in the first chand this is the second oneXXXXXXX"] ["GET" #f "HTTP/1.1 301 Moved Permanently\r\nLocation: http://localhost:9002/whatever\r\n\r\nstuff" - #"GET / HTTP/1.1\r\nHost: localhost:REDACTED\r\nUser-Agent: Racket/REDACTED (net/http-client)\r\nAccept-Encoding: gzip\r\nContent-Length: 0\r\nConnection: close\r\n\r\n" + #"GET / HTTP/1.1\r\nHost: localhost:REDACTED\r\nUser-Agent: Racket/REDACTED (net/http-client)\r\nAccept-Encoding: gzip,deflate\r\nContent-Length: 0\r\nConnection: close\r\n\r\n" #"HTTP/1.1 301 Moved Permanently" '(#"Location: http://localhost:9002/whatever") #"stuff"] ["GET" #f "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" - #"GET / HTTP/1.1\r\nHost: localhost:REDACTED\r\nUser-Agent: Racket/REDACTED (net/http-client)\r\nAccept-Encoding: gzip\r\nContent-Length: 0\r\nConnection: close\r\n\r\n" + #"GET / HTTP/1.1\r\nHost: localhost:REDACTED\r\nUser-Agent: Racket/REDACTED (net/http-client)\r\nAccept-Encoding: gzip,deflate\r\nContent-Length: 0\r\nConnection: close\r\n\r\n" #"HTTP/1.1 200 OK" '(#"Content-Type: text/plain" #"Transfer-Encoding: chunked" #"Another-Header: ta-daa") #"This is the data in the first chand this is the second oneXXXXXXX"] ["GET" #f "HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nTransfer-Encoding: chunked\r\nAnother-Header: ta-daa\r\n\r\nbb \r\n\n\t\t\t\t\t \n\t\t\t\t\t ABCNANOTECH Co., LTD.\n\t\t\t\t\t \n\t\t\t\t\t \n\t\t\t\t\t \n\t\t\t\t\t \r\n0\r\n\r\n" - #"GET / HTTP/1.1\r\nHost: localhost:REDACTED\r\nUser-Agent: Racket/REDACTED (net/http-client)\r\nAccept-Encoding: gzip\r\nContent-Length: 0\r\nConnection: close\r\n\r\n" + #"GET / HTTP/1.1\r\nHost: localhost:REDACTED\r\nUser-Agent: Racket/REDACTED (net/http-client)\r\nAccept-Encoding: gzip,deflate\r\nContent-Length: 0\r\nConnection: close\r\n\r\n" #"HTTP/1.1 200 OK" '(#"Content-Type: text/plain" #"Transfer-Encoding: chunked" #"Another-Header: ta-daa") #"\n\t\t\t\t\t \n\t\t\t\t\t ABCNANOTECH Co., LTD.\n\t\t\t\t\t \n\t\t\t\t\t \n\t\t\t\t\t \n\t\t\t\t\t "] ["PUT" #f "HTTP/1.1 200 OK\r\n\r\n" - #"PUT / HTTP/1.1\r\nHost: localhost:REDACTED\r\nUser-Agent: Racket/REDACTED (net/http-client)\r\nAccept-Encoding: gzip\r\nContent-Length: 0\r\nConnection: close\r\n\r\n" + #"PUT / HTTP/1.1\r\nHost: localhost:REDACTED\r\nUser-Agent: Racket/REDACTED (net/http-client)\r\nAccept-Encoding: gzip,deflate\r\nContent-Length: 0\r\nConnection: close\r\n\r\n" #"HTTP/1.1 200 OK" '() #""] ["PUT" #"frob" "HTTP/1.1 200 OK\r\n\r\n" - #"PUT / HTTP/1.1\r\nHost: localhost:REDACTED\r\nUser-Agent: Racket/REDACTED (net/http-client)\r\nAccept-Encoding: gzip\r\nContent-Length: 4\r\nConnection: close\r\n\r\nfrob" + #"PUT / HTTP/1.1\r\nHost: localhost:REDACTED\r\nUser-Agent: Racket/REDACTED (net/http-client)\r\nAccept-Encoding: gzip,deflate\r\nContent-Length: 4\r\nConnection: close\r\n\r\nfrob" #"HTTP/1.1 200 OK" '() #""] ["PUT" "frob" "HTTP/1.1 200 OK\r\n\r\n" - #"PUT / HTTP/1.1\r\nHost: localhost:REDACTED\r\nUser-Agent: Racket/REDACTED (net/http-client)\r\nAccept-Encoding: gzip\r\nContent-Length: 4\r\nConnection: close\r\n\r\nfrob" + #"PUT / HTTP/1.1\r\nHost: localhost:REDACTED\r\nUser-Agent: Racket/REDACTED (net/http-client)\r\nAccept-Encoding: gzip,deflate\r\nContent-Length: 4\r\nConnection: close\r\n\r\nfrob" #"HTTP/1.1 200 OK" '() #""] @@ -283,7 +308,7 @@ ["PUT" (λ (w) (w "fr") (w "ob")) "HTTP/1.1 200 OK\r\n\r\n" - #"PUT / HTTP/1.1\r\nHost: localhost:REDACTED\r\nUser-Agent: Racket/REDACTED (net/http-client)\r\nAccept-Encoding: gzip\r\nTransfer-Encoding: chunked\r\nConnection: close\r\n\r\n2\r\nfr\r\n2\r\nob\r\n0\r\n\r\n" + #"PUT / HTTP/1.1\r\nHost: localhost:REDACTED\r\nUser-Agent: Racket/REDACTED (net/http-client)\r\nAccept-Encoding: gzip,deflate\r\nTransfer-Encoding: chunked\r\nConnection: close\r\n\r\n2\r\nfr\r\n2\r\nob\r\n0\r\n\r\n" #"HTTP/1.1 200 OK" '() #""] @@ -291,13 +316,13 @@ ["PUT" (λ (w) (void)) "HTTP/1.1 200 OK\r\n\r\n" - #"PUT / HTTP/1.1\r\nHost: localhost:REDACTED\r\nUser-Agent: Racket/REDACTED (net/http-client)\r\nAccept-Encoding: gzip\r\nTransfer-Encoding: chunked\r\nConnection: close\r\n\r\n0\r\n\r\n" + #"PUT / HTTP/1.1\r\nHost: localhost:REDACTED\r\nUser-Agent: Racket/REDACTED (net/http-client)\r\nAccept-Encoding: gzip,deflate\r\nTransfer-Encoding: chunked\r\nConnection: close\r\n\r\n0\r\n\r\n" #"HTTP/1.1 200 OK" '() #""] ["HEAD" #f "HTTP/1.1 200 OK\r\n\r\n" - #"HEAD / HTTP/1.1\r\nHost: localhost:REDACTED\r\nUser-Agent: Racket/REDACTED (net/http-client)\r\nAccept-Encoding: gzip\r\nContent-Length: 0\r\nConnection: close\r\n\r\n" + #"HEAD / HTTP/1.1\r\nHost: localhost:REDACTED\r\nUser-Agent: Racket/REDACTED (net/http-client)\r\nAccept-Encoding: gzip,deflate\r\nContent-Length: 0\r\nConnection: close\r\n\r\n" #"HTTP/1.1 200 OK" '() #""]) diff --git a/racket/collects/net/http-client.rkt b/racket/collects/net/http-client.rkt index 0034b62f54..3fab95640f 100644 --- a/racket/collects/net/http-client.rkt +++ b/racket/collects/net/http-client.rkt @@ -154,7 +154,7 @@ #:method [method-bss #"GET"] #:close? [close? #f] #:headers [headers-bs empty] - #:content-decode [decodes '(gzip)] + #:content-decode [decodes '(gzip deflate)] #:data [data #f]) (http-conn-enliven! hc) (match-define (http-conn host port port-usual? to from _ @@ -168,9 +168,11 @@ (unless (regexp-member #rx"^(?i:User-Agent:) +.+$" headers-bs) (fprintf to "User-Agent: Racket/~a (net/http-client)\r\n" (version))) - (unless (or (not (memq 'gzip decodes)) + (unless (or (empty? decodes) (regexp-member #rx"^(?i:Accept-Encoding:) +.+$" headers-bs)) - (fprintf to "Accept-Encoding: gzip\r\n")) + (fprintf to "Accept-Encoding: ~a\r\n" + (string-join (map symbol->string decodes) ","))) + (define body (->bytes data)) (cond [(procedure? body) (fprintf to "Transfer-Encoding: chunked\r\n")] @@ -330,7 +332,7 @@ (define (http-conn-recv! hc #:method [method-bss #"GET"] - #:content-decode [decodes '(gzip)] + #:content-decode [decodes '(gzip deflate)] #:close? [iclose? #f]) (http-conn-enliven! hc) (define status (http-conn-status! hc)) @@ -360,27 +362,34 @@ [else (values (http-conn-response-port/rest! hc) #t)])) (define decoded-response-port - (cond - [(head? method-bss) raw-response-port] - [(and (memq 'gzip decodes) - (regexp-member #rx#"^(?i:Content-Encoding: +gzip)$" headers) - (not (eof-object? (peek-byte raw-response-port)))) - (define-values (in out) (make-pipe PIPE-SIZE)) - (define gunzip-t - (thread - (λ () - (gunzip-through-ports raw-response-port out)))) - (thread - (λ () - (thread-wait gunzip-t) - (when wait-for-close? - ;; Wait for an EOF from the raw port before we send an - ;; output on the decoding pipe: - (copy-port raw-response-port (open-output-nowhere))) - (close-output-port out))) - in] - [else - raw-response-port])) + (let ([decode-response + (λ (raw-response-port decode-function) + (define-values (in out) (make-pipe PIPE-SIZE)) + (define decode-t + (thread + (λ () + (decode-function raw-response-port out)))) + (thread + (λ () + (thread-wait decode-t) + (when wait-for-close? + ;; Wait for an EOF from the raw port before we send an + ;; output on the decoding pipe: + (copy-port raw-response-port (open-output-nowhere))) + (close-output-port out))) + in)]) + (cond + [(head? method-bss) raw-response-port] + [(and (memq 'gzip decodes) + (regexp-member #rx#"^(?i:Content-Encoding: +gzip)$" headers) + (not (eof-object? (peek-byte raw-response-port)))) + (decode-response raw-response-port gunzip-through-ports)] + [(and (memq 'deflate decodes) + (regexp-member #rx#"^(?i:Content-Encoding: +deflate)$" headers) + (not (eof-object? (peek-byte raw-response-port)))) + (decode-response raw-response-port inflate)] + [else + raw-response-port]))) (values status headers decoded-response-port)) (define (http-conn-sendrecv! hc url-bs @@ -388,7 +397,7 @@ #:method [method-bss #"GET"] #:headers [headers-bs empty] #:data [data #f] - #:content-decode [decodes '(gzip)] + #:content-decode [decodes '(gzip deflate)] #:close? [close? #f]) (http-conn-send! hc url-bs #:version version-bs @@ -409,7 +418,7 @@ #:method [method-bss #"GET"] #:headers [headers-bs empty] #:data [data #f] - #:content-decode [decodes '(gzip)]) + #:content-decode [decodes '(gzip deflate)]) (define hc (http-conn-open host-bs #:ssl? ssl? #:port port)) (begin0 (http-conn-sendrecv! hc url-bs #:version version-bs diff --git a/racket/collects/net/url.rkt b/racket/collects/net/url.rkt index d5928cf47d..8493539289 100644 --- a/racket/collects/net/url.rkt +++ b/racket/collects/net/url.rkt @@ -523,7 +523,7 @@ #:method [method-bss #"GET"] #:headers [headers-bs empty] #:data [data #f] - #:content-decode [decodes '(gzip)]) + #:content-decode [decodes '(gzip deflate)]) (unless (member (url-scheme u) '(#f "http" "https")) (error 'http-sendrecv/url "URL scheme ~e not supported" (url-scheme u))) (define ssl?