Skip to content

Commit 1216663

Browse files
authored
Exposing functions publicly (#13)
* Extracted the testing and updating of headers into public functions * Added tests for publicly exposed functions * Updated to include latest minor releases of Clojure
1 parent be49a86 commit 1216663

File tree

3 files changed

+124
-65
lines changed

3 files changed

+124
-65
lines changed

project.clj

+4-2
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,14 @@
22
:url "https://github.com/clj-commons/ring-gzip-middleware"
33
:description "Ring gzip encoding middleware"
44
:dependencies [[org.clojure/clojure "1.9.0"]]
5-
:profiles {:1.10 {:dependencies [[org.clojure/clojure "1.10.0-RC3"]]}
5+
:profiles {:1.12 {:dependencies [[org.clojure/clojure "1.12.0"]]}
6+
:1.11 {:dependencies [[org.clojure/clojure "1.11.4"]]}
7+
:1.10 {:dependencies [[org.clojure/clojure "1.10.3"]]}
68
:1.9 {}
79
:1.8 {:dependencies [[org.clojure/clojure "1.8.0"]]}
810
:1.7 {:dependencies [[org.clojure/clojure "1.7.0"]]}
911
:1.6 {:dependencies [[org.clojure/clojure "1.6.0"]]}}
1012
:deploy-repositories [["releases" :clojars]
1113
["snapshots" :clojars]]
12-
:aliases {"all" ["with-profile" "1.6:1.7:1.8:1.9:1.10"]}
14+
:aliases {"all" ["with-profile" "1.6:1.7:1.8:1.9:1.10:1.11:1.12"]}
1315
:min-lein-version "2.0.0")

src/ring/middleware/gzip.clj

+46-22
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,9 @@
3131
(loop []
3232
(let [size (.read input buffer)]
3333
(when (pos? size)
34-
(do (.write output buffer 0 size)
35-
(.flush output)
36-
(recur)))))))
34+
(.write output buffer 0 size)
35+
(.flush output)
36+
(recur))))))
3737

3838
(defn piped-gzipped-input-stream [in]
3939
(let [pipe-in (piped-gzipped-input-stream*)
@@ -52,28 +52,52 @@
5252
(.close ^Closeable in)))
5353
pipe-in))
5454

55+
(defn set-response-headers
56+
[headers]
57+
(-> headers
58+
(assoc "Content-Encoding" "gzip")
59+
(dissoc "Content-Length")))
60+
5561
(defn gzipped-response [resp]
5662
(-> resp
57-
(update-in [:headers]
58-
#(-> %
59-
(assoc "Content-Encoding" "gzip")
60-
(dissoc "Content-Length")))
61-
(update-in [:body] piped-gzipped-input-stream)))
63+
(update :headers set-response-headers)
64+
(update :body piped-gzipped-input-stream)))
65+
66+
(defn accepts-gzip?
67+
"Tests if the request indicates that the client can accept a gzipped response"
68+
[{:keys [headers]}]
69+
(let [accepts (or (get headers "accept-encoding")
70+
(get headers "Accept-Encoding")
71+
"")
72+
match (re-find #"(gzip|\*)(;q=((0|1)(.\d+)?))?" accepts)]
73+
(and match (not (contains? #{"0" "0.0" "0.00" "0.000"} (match 3))))))
74+
75+
(def ^:private default-status 200)
76+
77+
(def supported-status? #{200 201 202 203 204 205})
78+
79+
(def min-length 200)
80+
81+
(defn content-encoding?
82+
"Tests if the provided response has a content-encoding header"
83+
[{:keys [headers]}]
84+
(or (get headers "Content-Encoding")
85+
(get headers "content-encoding")))
86+
87+
(defn supported-response?
88+
[{:keys [body status] :as resp}]
89+
(and (supported-status? (or status default-status))
90+
(not (content-encoding? resp))
91+
(or
92+
(and (string? body) (> (count body) min-length))
93+
(instance? InputStream body)
94+
(instance? File body)
95+
(and (seq? body) @flushable-gzip?))))
6296

63-
(defn- gzip-response [req {:keys [body status] :as resp}]
64-
(if (and (= status 200)
65-
(not (get-in resp [:headers "Content-Encoding"]))
66-
(or
67-
(and (string? body) (> (count body) 200))
68-
(and (seq? body) @flushable-gzip?)
69-
(instance? InputStream body)
70-
(instance? File body)))
71-
(let [accepts (get-in req [:headers "accept-encoding"] "")
72-
match (re-find #"(gzip|\*)(;q=((0|1)(.\d+)?))?" accepts)]
73-
(if (and match (not (contains? #{"0" "0.0" "0.00" "0.000"}
74-
(match 3))))
75-
(gzipped-response resp)
76-
resp))
97+
(defn gzip-response [req resp]
98+
(if (and (supported-response? resp)
99+
(accepts-gzip? req))
100+
(gzipped-response resp)
77101
resp))
78102

79103
(defn wrap-gzip

test/ring/middleware/gzip_test.clj

+74-41
Original file line numberDiff line numberDiff line change
@@ -29,23 +29,29 @@
2929
(defn accepting [ctype]
3030
{:headers {"accept-encoding" ctype}})
3131

32+
(defn set-encoding
33+
([resp] (set-encoding resp false))
34+
([resp caps?]
35+
(let [content-encoding (if caps? "Content-Encoding" "content-encoding")]
36+
(assoc-in resp [:headers content-encoding] "text"))))
37+
3238
(deftest test-basic-gzip
3339
(let [resp (app (accepting "gzip"))]
3440
(is (= 200 (:status resp)))
3541
(is (= "gzip" (encoding resp)))
3642
(is (Arrays/equals (unzip (resp :body)) (.getBytes output)))))
3743

3844
(deftest test-basic-gzip-async
39-
"middleware should work with 3-arg async handlers as well"
40-
(let [app (wrap-gzip
41-
(fn [request respond raise]
42-
(respond {:status 200
43-
:body output
44-
:headers {}})))
45-
resp (app (accepting "gzip") identity identity)]
46-
(is (= 200 (:status resp)))
47-
(is (= "gzip" (encoding resp)))
48-
(is (Arrays/equals (unzip (resp :body)) (.getBytes output)))))
45+
(testing "middleware should work with 3-arg async handlers as well"
46+
(let [app (wrap-gzip
47+
(fn [request respond raise]
48+
(respond {:status 200
49+
:body output
50+
:headers {}})))
51+
resp (app (accepting "gzip") identity identity)]
52+
(is (= 200 (:status resp)))
53+
(is (= "gzip" (encoding resp)))
54+
(is (Arrays/equals (unzip (resp :body)) (.getBytes output))))))
4955

5056
(deftest test-inputstream-gzip
5157
(let [app (wrap-gzip (fn [req] {:status 200
@@ -75,40 +81,67 @@
7581
(is (= seq-body (resp :body)))))))
7682

7783
(deftest test-accepts
78-
(doseq [ctype ["gzip" "*" "gzip,deflate" "gzip,deflate,sdch"
79-
"gzip, deflate" "gzip;q=1" "deflate,gzip"
80-
"deflate,gzip,sdch" "deflate,gzip;q=1"
81-
"deflate,gzip;q=1,sdch"
82-
"gzip;q=0.5"]]
83-
(is (= "gzip" (encoding (app (accepting ctype))))))
84-
(doseq [ctype ["" "gzip;q=0" "deflate" "deflate,sdch"
85-
"deflate,gzip;q=0" "deflate,gzip;q=0,sdch"
86-
"gzip;q=0,deflate" "*;q=0"]]
87-
(is (nil? (encoding (app (accepting ctype)))))))
84+
(testing "appropriate requests will be zipped"
85+
(doseq [ctype ["gzip" "*" "gzip,deflate" "gzip,deflate,sdch"
86+
"gzip, deflate" "gzip;q=1" "deflate,gzip"
87+
"deflate,gzip,sdch" "deflate,gzip;q=1"
88+
"deflate,gzip;q=1,sdch"
89+
"gzip;q=0.5"]]
90+
(is (= "gzip" (encoding (app (accepting ctype)))))
91+
(is (accepts-gzip? (accepting ctype)))))
92+
(testing "requests that ask for a zip, but not the supported type of zip are not zipped"
93+
(doseq [ctype ["" "gzip;q=0" "deflate" "deflate,sdch"
94+
"deflate,gzip;q=0" "deflate,gzip;q=0,sdch"
95+
"gzip;q=0,deflate" "*;q=0"]]
96+
(is (nil? (encoding (app (accepting ctype))))))))
8897

8998
(deftest test-min-length
90-
"don't compress string bodies less than 200 characters long"
91-
(let [output (apply str (repeat 10 "a"))
92-
app (wrap-gzip (fn [req] {:status 200
93-
:body output
94-
:headers {}}))
95-
resp (app (accepting "gzip"))]
96-
(is (nil? (encoding (app (accepting "gzip")))))))
99+
(testing "Compress string bodies greater than the min-length (200) characters long"
100+
(let [output (apply str (repeat (inc min-length) "a"))
101+
resp {:status 200
102+
:body output
103+
:headers {}}
104+
app (wrap-gzip (fn [req] resp))]
105+
(is (= "gzip" (encoding (app (accepting "gzip")))))
106+
(is (supported-response? resp))
107+
(testing ", but not string bodies at or below min-length"
108+
(let [resp (update resp :body subs 1)
109+
app (wrap-gzip (fn [req] resp))]
110+
(is (nil? (encoding (app (accepting "gzip")))))
111+
(is (not (supported-response? resp))))))))
97112

98113
(deftest test-wrapped-encoding
99-
"don't compress responses which already have a content-encoding header"
100-
(let [app (wrap-gzip (fn [req] {:status 200
101-
:body output
102-
:headers {"Content-Encoding" "text"}}))
103-
resp (app (accepting "gzip"))]
104-
(is (= "text" (encoding resp)))
105-
(is (= output (:body resp)))))
114+
(testing "don't compress responses which already have a content-encoding header"
115+
(let [response {:status 200
116+
:body output
117+
:headers {"Content-Encoding" "text"}}
118+
app (wrap-gzip (fn [req] response))
119+
resp (app (accepting "gzip"))]
120+
(is (= "text" (encoding resp)))
121+
(is (= output (:body resp))))))
122+
123+
(deftest test-supported
124+
(testing "responses that already have an encoding cannot be zipped"
125+
(doseq [ctype ["gzip" "*" "gzip,deflate" "gzip,deflate,sdch"
126+
"gzip, deflate" "gzip;q=1" "deflate,gzip"
127+
"deflate,gzip,sdch" "deflate,gzip;q=1"
128+
"deflate,gzip;q=1,sdch"
129+
"gzip;q=0.5" "" "gzip;q=0" "deflate" "deflate,sdch"
130+
"deflate,gzip;q=0" "deflate,gzip;q=0,sdch"
131+
"gzip;q=0,deflate" "*;q=0"]]
132+
(is (not (supported-response? (set-encoding (accepting ctype)))))
133+
(is (not (supported-response? (set-encoding (accepting ctype) true)))))))
106134

107135
(deftest test-status
108-
"don't compress non-200 responses"
109-
(let [app (wrap-gzip (fn [req] {:status 404
110-
:body output
111-
:headers {}}))
112-
resp (app (accepting "gzip"))]
113-
(is (nil? (encoding resp)))
114-
(is (= output (:body resp)))))
136+
(testing "don't compress non-2xx responses"
137+
(let [app (wrap-gzip (fn [req] {:status 404
138+
:body output
139+
:headers {}}))
140+
resp (app (accepting "gzip"))]
141+
(is (nil? (encoding resp)))
142+
(is (= output (:body resp))))))
143+
144+
(deftest test-setting-headers
145+
(testing "updating the headers of a response to indicate that they have been gziped"
146+
(is (= {"Content-Encoding" "gzip"} (set-response-headers {"Content-Length" 201})))
147+
(is (= {"Content-Encoding" "gzip" "Age" 24} (set-response-headers {"Age" 24})))))

0 commit comments

Comments
 (0)