|
| 1 | +(ns ring.util.jakarta.test.servlet |
| 2 | + (:require [clojure.test :refer :all] |
| 3 | + [ring.util.jakarta.servlet :refer :all] |
| 4 | + [ring.core.protocols :as proto]) |
| 5 | + (:import [java.util Locale])) |
| 6 | + |
| 7 | +(defmacro ^:private with-locale [locale & body] |
| 8 | + `(let [old-locale# (Locale/getDefault)] |
| 9 | + (try (Locale/setDefault ~locale) |
| 10 | + (do ~@body) |
| 11 | + (finally (Locale/setDefault old-locale#))))) |
| 12 | + |
| 13 | +(defn- enumeration [coll] |
| 14 | + (let [e (atom coll)] |
| 15 | + (proxy [java.util.Enumeration] [] |
| 16 | + (hasMoreElements [] (not (empty? @e))) |
| 17 | + (nextElement [] (let [f (first @e)] (swap! e rest) f))))) |
| 18 | + |
| 19 | +(defn- async-context [completed] |
| 20 | + (proxy [jakarta.servlet.AsyncContext] [] |
| 21 | + (complete [] (reset! completed true)))) |
| 22 | + |
| 23 | +(defn- servlet-request [request] |
| 24 | + (let [attributes {"jakarta.servlet.request.X509Certificate" |
| 25 | + [(request :ssl-client-cert)]}] |
| 26 | + (proxy [jakarta.servlet.http.HttpServletRequest] [] |
| 27 | + (getServerPort [] (request :server-port)) |
| 28 | + (getServerName [] (request :server-name)) |
| 29 | + (getRemoteAddr [] (request :remote-addr)) |
| 30 | + (getRequestURI [] (request :uri)) |
| 31 | + (getQueryString [] (request :query-string)) |
| 32 | + (getContextPath [] (request :servlet-context-path)) |
| 33 | + (getScheme [] (name (request :scheme))) |
| 34 | + (getMethod [] (-> request :request-method name .toUpperCase)) |
| 35 | + (getProtocol [] (request :protocol)) |
| 36 | + (getHeaderNames [] (enumeration (keys (request :headers)))) |
| 37 | + (getHeaders [name] (enumeration (get-in request [:headers name]))) |
| 38 | + (getContentType [] (request :content-type)) |
| 39 | + (getContentLength [] (or (request :content-length) -1)) |
| 40 | + (getCharacterEncoding [] (request :character-encoding)) |
| 41 | + (getAttribute [k] (attributes k)) |
| 42 | + (getInputStream [] (request :body)) |
| 43 | + (startAsync [] (async-context (request :completed)))))) |
| 44 | + |
| 45 | +(defn- servlet-response [response] |
| 46 | + (let [output-stream (java.io.ByteArrayOutputStream.)] |
| 47 | + (swap! response assoc :body output-stream) |
| 48 | + (proxy [jakarta.servlet.http.HttpServletResponse] [] |
| 49 | + (getOutputStream [] |
| 50 | + (proxy [jakarta.servlet.ServletOutputStream] [] |
| 51 | + (write |
| 52 | + ([b] (.write output-stream b)) |
| 53 | + ([b off len] (.write output-stream b off len))))) |
| 54 | + (setStatus [status] |
| 55 | + (swap! response assoc :status status)) |
| 56 | + (setHeader [name value] |
| 57 | + (swap! response assoc-in [:headers name] value)) |
| 58 | + (setCharacterEncoding [value]) |
| 59 | + (setContentType [value] |
| 60 | + (swap! response assoc :content-type value))))) |
| 61 | + |
| 62 | +(defn- servlet-config [] |
| 63 | + (proxy [jakarta.servlet.ServletConfig] [] |
| 64 | + (getServletContext [] nil))) |
| 65 | + |
| 66 | +(defn- run-servlet |
| 67 | + ([handler request response] |
| 68 | + (run-servlet handler request response {})) |
| 69 | + ([handler request response options] |
| 70 | + (doto (servlet handler options) |
| 71 | + (.init (servlet-config)) |
| 72 | + (.service (servlet-request request) |
| 73 | + (servlet-response response))))) |
| 74 | + |
| 75 | +(deftest make-service-method-test |
| 76 | + (let [handler (constantly {:status 201 |
| 77 | + :headers {}}) |
| 78 | + method (make-service-method handler) |
| 79 | + servlet (doto (proxy [jakarta.servlet.http.HttpServlet] []) |
| 80 | + (.init (servlet-config))) |
| 81 | + request {:server-port 8080 |
| 82 | + :server-name "foobar" |
| 83 | + :remote-addr "127.0.0.1" |
| 84 | + :uri "/foo" |
| 85 | + :scheme :http |
| 86 | + :request-method :get |
| 87 | + :protocol "HTTP/1.1" |
| 88 | + :headers {}} |
| 89 | + response (atom {})] |
| 90 | + (method servlet (servlet-request request) (servlet-response response)) |
| 91 | + (is (= (@response :status) 201)))) |
| 92 | + |
| 93 | +(deftest servlet-test |
| 94 | + (let [body (proxy [jakarta.servlet.ServletInputStream] []) |
| 95 | + cert (proxy [java.security.cert.X509Certificate] []) |
| 96 | + request {:server-port 8080 |
| 97 | + :server-name "foobar" |
| 98 | + :remote-addr "127.0.0.1" |
| 99 | + :uri "/foo" |
| 100 | + :query-string "a=b" |
| 101 | + :scheme :http |
| 102 | + :request-method :get |
| 103 | + :protocol "HTTP/1.1" |
| 104 | + :headers {"X-Client" ["Foo", "Bar"] |
| 105 | + "X-Server" ["Baz"] |
| 106 | + "X-Capital-I" ["Qux"]} |
| 107 | + :content-type "text/plain" |
| 108 | + :content-length 10 |
| 109 | + :character-encoding "UTF-8" |
| 110 | + :servlet-context-path "/foo" |
| 111 | + :ssl-client-cert cert |
| 112 | + :body body} |
| 113 | + response (atom {})] |
| 114 | + (letfn [(handler [r] |
| 115 | + (are [k v] (= (r k) v) |
| 116 | + :server-port 8080 |
| 117 | + :server-name "foobar" |
| 118 | + :remote-addr "127.0.0.1" |
| 119 | + :uri "/foo" |
| 120 | + :query-string "a=b" |
| 121 | + :scheme :http |
| 122 | + :request-method :get |
| 123 | + :protocol "HTTP/1.1" |
| 124 | + :headers {"x-client" "Foo,Bar" |
| 125 | + "x-server" "Baz" |
| 126 | + "x-capital-i" "Qux"} |
| 127 | + :content-type "text/plain" |
| 128 | + :content-length 10 |
| 129 | + :character-encoding "UTF-8" |
| 130 | + :servlet-context-path "/foo" |
| 131 | + :ssl-client-cert cert |
| 132 | + :body body) |
| 133 | + {:status 200, :headers {}})] |
| 134 | + (testing "request" |
| 135 | + (run-servlet handler request response)) |
| 136 | + (testing "mapping request header names to lower case" |
| 137 | + (with-locale (Locale. "tr") |
| 138 | + (run-servlet handler request response)))) |
| 139 | + (testing "response" |
| 140 | + (letfn [(handler [r] |
| 141 | + {:status 200 |
| 142 | + :headers {"Content-Type" "text/plain" |
| 143 | + "X-Server" "Bar"} |
| 144 | + :body "Hello World"})] |
| 145 | + (run-servlet handler request response) |
| 146 | + (is (= (@response :status) 200)) |
| 147 | + (is (= (@response :content-type) "text/plain")) |
| 148 | + (is (= (get-in @response [:headers "X-Server"]) "Bar")) |
| 149 | + (is (= (.toString (@response :body)) "Hello World")))))) |
| 150 | + |
| 151 | +(deftest servlet-cps-test |
| 152 | + (let [handler (fn [_ respond _] |
| 153 | + (respond {:status 200 |
| 154 | + :headers {"Content-Type" "text/plain"} |
| 155 | + :body "Hello World"})) |
| 156 | + request {:completed (atom false) |
| 157 | + :server-port 8080 |
| 158 | + :server-name "foobar" |
| 159 | + :remote-addr "127.0.0.1" |
| 160 | + :uri "/foo" |
| 161 | + :scheme :http |
| 162 | + :request-method :get |
| 163 | + :protocol "HTTP/1.1" |
| 164 | + :headers {} |
| 165 | + :body nil} |
| 166 | + response (atom {})] |
| 167 | + (run-servlet handler request response {:async? true}) |
| 168 | + (is (= @(:completed request) true)) |
| 169 | + (is (= (@response :status) 200)) |
| 170 | + (is (= (@response :content-type) "text/plain")) |
| 171 | + (is (= (.toString (@response :body)) "Hello World")))) |
| 172 | + |
| 173 | +(defn- defservice-test* [service] |
| 174 | + (let [body (proxy [jakarta.servlet.ServletInputStream] []) |
| 175 | + servlet (doto (proxy [jakarta.servlet.http.HttpServlet] []) |
| 176 | + (.init (servlet-config))) |
| 177 | + request {:server-port 8080 |
| 178 | + :server-name "foobar" |
| 179 | + :remote-addr "127.0.0.1" |
| 180 | + :uri "/foo" |
| 181 | + :query-string "" |
| 182 | + :scheme :http |
| 183 | + :request-method :get |
| 184 | + :headers {} |
| 185 | + :content-type "text/plain" |
| 186 | + :content-length 10 |
| 187 | + :character-encoding "UTF-8" |
| 188 | + :body body} |
| 189 | + response (atom {})] |
| 190 | + (service servlet |
| 191 | + (servlet-request request) |
| 192 | + (servlet-response response)) |
| 193 | + (is (= (@response :status) 200)) |
| 194 | + (is (= (get-in @response [:headers "Content-Type" ]) "text/plain")) |
| 195 | + (is (= (.toString (@response :body)) "Hello World")))) |
| 196 | + |
| 197 | +(defn- service-handler [_] |
| 198 | + {:status 200 |
| 199 | + :headers {"Content-Type" "text/plain"} |
| 200 | + :body "Hello World"}) |
| 201 | + |
| 202 | +(defservice "foo-" service-handler) |
| 203 | +(defservice service-handler {}) |
| 204 | + |
| 205 | +(deftest defservice-test |
| 206 | + (defservice-test* foo-service) |
| 207 | + (defservice-test* -service)) |
| 208 | + |
| 209 | +(deftest make-output-stream-test |
| 210 | + (let [response (atom {})] |
| 211 | + (update-servlet-response |
| 212 | + (servlet-response response) |
| 213 | + (async-context (atom false)) |
| 214 | + {:status 200 |
| 215 | + :headers {} |
| 216 | + :body (reify proto/StreamableResponseBody |
| 217 | + (write-body-to-stream [_ _ os] |
| 218 | + (.write os (int \h)) |
| 219 | + (.write os (.getBytes "ello"))))}) |
| 220 | + (is (= "hello" (.toString (:body @response)))))) |
0 commit comments