Skip to content

Commit 985d535

Browse files
authored
Merge pull request #590 from arnaudgeiser/559
client: Accept empty file with SSL
2 parents 21e650f + 31654ff commit 985d535

File tree

5 files changed

+111
-72
lines changed

5 files changed

+111
-72
lines changed

src/aleph/http/core.clj

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -461,13 +461,16 @@
461461
(netty/write ch fr)
462462
(netty/write-and-flush ch empty-last-content)))
463463

464+
(defn- file->stream [^HttpFile file]
465+
(-> file
466+
(bs/to-byte-buffers {:chunk-size (.-chunk-size file)})
467+
s/->source))
468+
464469
(defn send-file-body [ch ssl? ^HttpMessage msg ^HttpFile file]
465470
(cond
466471
ssl?
467-
(send-streaming-body ch msg
468-
(-> file
469-
(bs/to-byte-buffers {:chunk-size (.-chunk-size file)})
470-
s/->source))
472+
(let [body (when (pos-int? (.length file)) (file->stream file))]
473+
(send-streaming-body ch msg body))
471474

472475
(chunked-writer-enabled? ch)
473476
(send-chunked-file ch msg file)

test/aleph/http_test.clj

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
(ns aleph.http-test
22
(:require
3+
[clojure.java.io :as io]
34
[clojure.string :as str]
45
[clojure.test :refer [deftest testing is]]
56
[aleph
67
[http :as http]
78
[netty :as netty]
89
[flow :as flow]
10+
[ssl :as ssl]
911
[tcp :as tcp]]
12+
[aleph.http.core :as core]
1013
[byte-streams :as bs]
1114
[manifold.deferred :as d]
1215
[manifold.stream :as s])
@@ -192,9 +195,12 @@
192195
(with-server (http/start-server ~handler {:port port :compression-level 3})
193196
~@body)))
194197

195-
(defmacro with-ssl-handler [handler & body]
196-
`(with-server (http/start-server ~handler {:port port, :ssl-context (netty/self-signed-ssl-context)})
197-
~@body))
198+
(def default-ssl-options {:port port, :ssl-context (netty/self-signed-ssl-context)})
199+
200+
(defmacro with-handler-options
201+
[handler options & body]
202+
`(with-server (http/start-server ~handler ~options)
203+
~@body))
198204

199205
(defmacro with-raw-handler [handler & body]
200206
`(with-server (http/start-server ~handler {:port port, :raw-stream? true})
@@ -228,8 +234,9 @@
228234
(is (some? unzipped) 'should-be-valid-gzip)
229235
(is (= result unzipped) 'decompressed-body-is-correct))))
230236

237+
231238
(deftest test-ssl-response-formats
232-
(with-ssl-handler basic-handler
239+
(with-handler-options basic-handler default-ssl-options
233240
(doseq [[index [path result]] (map-indexed vector expected-results)]
234241
(is
235242
(= result
@@ -238,6 +245,34 @@
238245
@(http-get (str "https://localhost:" port "/" path)))))
239246
(str path "path failed")))))
240247

248+
(deftest test-files
249+
(let [client-url (str "http://localhost:" port)]
250+
(with-handler identity
251+
(is (str/blank?
252+
(bs/to-string
253+
(:body @(http-put client-url
254+
{:body (io/file "test/empty.txt")})))))
255+
(is (= (slurp "test/file.txt" :encoding "UTF-8")
256+
(bs/to-string
257+
(:body @(http-put client-url
258+
{:body (io/file "test/file.txt")}))))))))
259+
260+
(deftest test-ssl-files
261+
(let [client-url (str "https://localhost:" port)
262+
client-options {:connection-options {:ssl-context ssl/client-ssl-context}}
263+
client-pool (http/connection-pool client-options)]
264+
(with-handler-options identity (merge default-ssl-options {:ssl-context ssl/server-ssl-context})
265+
(is (str/blank?
266+
(bs/to-string
267+
(:body @(http-put client-url
268+
{:body (io/file "test/empty.txt")
269+
:pool client-pool})))))
270+
(is (= (slurp "test/file.txt" :encoding "UTF-8")
271+
(bs/to-string
272+
(:body @(http-put client-url
273+
{:body (io/file "test/file.txt")
274+
:pool client-pool}))))))))
275+
241276
(def characters
242277
(let [charset (conj (mapv char (range 32 127)) \newline)]
243278
(repeatedly #(rand-nth charset))))

test/aleph/ssl.clj

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
(ns aleph.ssl
2+
(:require [aleph.netty :as netty])
3+
(:import [io.netty.handler.ssl SslContextBuilder ClientAuth]
4+
[java.io ByteArrayInputStream]
5+
[java.security KeyFactory PrivateKey]
6+
[java.security.cert CertificateFactory X509Certificate]
7+
[java.security.spec RSAPrivateCrtKeySpec]
8+
[org.apache.commons.codec.binary Base64]))
9+
10+
(set! *warn-on-reflection* false)
11+
12+
(defn gen-cert
13+
^X509Certificate [^String pemstr]
14+
(.generateCertificate (CertificateFactory/getInstance "X.509")
15+
(ByteArrayInputStream. (Base64/decodeBase64 pemstr))))
16+
17+
(defn gen-key
18+
^PrivateKey [public-exponent k]
19+
(let [k (zipmap
20+
(keys k)
21+
(->> k
22+
vals
23+
(map #(BigInteger. ^String % 16))))
24+
spec (RSAPrivateCrtKeySpec.
25+
(:modulus k)
26+
(biginteger public-exponent)
27+
(:privateExponent k)
28+
(:prime1 k)
29+
(:prime2 k)
30+
(:exponent1 k)
31+
(:exponent2 k)
32+
(:coefficient k))
33+
gen (KeyFactory/getInstance "RSA")]
34+
(.generatePrivate gen spec)))
35+
36+
(def server-cert (gen-cert (read-string (slurp "test/server_cert.edn"))))
37+
(def server-key (gen-key 65537 (read-string (slurp "test/server_key.edn"))))
38+
39+
(def ca-cert (gen-cert (read-string (slurp "test/ca_cert.edn"))))
40+
(def ca-key (gen-key 65537 (read-string (slurp "test/ca_key.edn"))))
41+
42+
(def ^X509Certificate client-cert (gen-cert (read-string (slurp "test/client_cert.edn"))))
43+
(def client-key (gen-key 65537 (read-string (slurp "test/client_key.edn"))))
44+
45+
(def server-ssl-context
46+
(-> (SslContextBuilder/forServer server-key (into-array X509Certificate [server-cert]))
47+
(.trustManager (into-array X509Certificate [ca-cert]))
48+
(.clientAuth ClientAuth/OPTIONAL)
49+
.build))
50+
51+
(def client-ssl-context
52+
(netty/ssl-client-context
53+
{:private-key client-key
54+
:certificate-chain (into-array X509Certificate [client-cert])
55+
:trust-store (into-array X509Certificate [ca-cert])}))

test/aleph/tcp_ssl_test.clj

Lines changed: 10 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -1,79 +1,25 @@
11
(ns aleph.tcp-ssl-test
2-
(:use
3-
[clojure test])
42
(:require [aleph.tcp-test :refer [with-server]]
5-
[aleph.tcp :as tcp]
6-
[aleph.netty :as netty]
7-
[manifold.stream :as s]
8-
[byte-streams :as bs])
9-
(:import
10-
[java.security KeyFactory PrivateKey]
11-
[java.security.cert CertificateFactory X509Certificate]
12-
[java.io ByteArrayInputStream]
13-
[java.security.spec RSAPrivateCrtKeySpec]
14-
[io.netty.handler.ssl SslContextBuilder ClientAuth]
15-
[org.apache.commons.codec.binary Base64]))
3+
[aleph.tcp :as tcp]
4+
[aleph.ssl :as ssl]
5+
[aleph.netty :as netty]
6+
[byte-streams :as bs]
7+
[clojure.test :refer [deftest is]]
8+
[manifold.stream :as s])
9+
(:import [java.security.cert X509Certificate]))
1610

1711
(netty/leak-detector-level! :paranoid)
1812

1913
(set! *warn-on-reflection* false)
2014

21-
(defn gen-key
22-
^PrivateKey [public-exponent k]
23-
(let [k (zipmap
24-
(keys k)
25-
(->> k
26-
vals
27-
(map #(BigInteger. % 16))))
28-
spec (RSAPrivateCrtKeySpec.
29-
(:modulus k)
30-
(biginteger public-exponent)
31-
(:privateExponent k)
32-
(:prime1 k)
33-
(:prime2 k)
34-
(:exponent1 k)
35-
(:exponent2 k)
36-
(:coefficient k))
37-
gen (KeyFactory/getInstance "RSA")]
38-
(.generatePrivate gen spec)))
39-
40-
(defn gen-cert
41-
^X509Certificate [^String pemstr]
42-
(.generateCertificate (CertificateFactory/getInstance "X.509")
43-
(ByteArrayInputStream. (Base64/decodeBase64 pemstr))))
44-
45-
(def ca-cert (gen-cert (read-string (slurp "test/ca_cert.edn"))))
46-
47-
(def ca-key (gen-key 65537 (read-string (slurp "test/ca_key.edn"))))
48-
49-
(def server-cert (gen-cert (read-string (slurp "test/server_cert.edn"))))
50-
51-
(def server-key (gen-key 65537 (read-string (slurp "test/server_key.edn"))))
52-
53-
(def ^X509Certificate client-cert (gen-cert (read-string (slurp "test/client_cert.edn"))))
54-
55-
(def client-key (gen-key 65537 (read-string (slurp "test/client_key.edn"))))
56-
57-
(def server-ssl-context
58-
(-> (SslContextBuilder/forServer server-key (into-array X509Certificate [server-cert]))
59-
(.trustManager (into-array X509Certificate [ca-cert]))
60-
(.clientAuth ClientAuth/OPTIONAL)
61-
.build))
62-
63-
(def client-ssl-context
64-
(netty/ssl-client-context
65-
{:private-key client-key
66-
:certificate-chain (into-array X509Certificate [client-cert])
67-
:trust-store (into-array X509Certificate [ca-cert])}))
68-
6915
(defn ssl-echo-handler
7016
[s c]
7117
(is (some? (:ssl-session c)) "SSL session should be defined")
7218
(s/connect
7319
; note we need to inspect the SSL session *after* we start reading
7420
; data. Otherwise, the session might not be set up yet.
7521
(s/map (fn [msg]
76-
(is (= (.getSubjectDN ^X509Certificate client-cert)
22+
(is (= (.getSubjectDN ^X509Certificate ssl/client-cert)
7723
(.getSubjectDN ^X509Certificate (first (.getPeerCertificates (:ssl-session c))))))
7824
msg)
7925
s)
@@ -82,9 +28,9 @@
8228
(deftest test-ssl-echo
8329
(with-server (tcp/start-server ssl-echo-handler
8430
{:port 10001
85-
:ssl-context server-ssl-context})
31+
:ssl-context ssl/server-ssl-context})
8632
(let [c @(tcp/client {:host "localhost"
8733
:port 10001
88-
:ssl-context client-ssl-context})]
34+
:ssl-context ssl/client-ssl-context})]
8935
(s/put! c "foo")
9036
(is (= "foo" (bs/to-string @(s/take! c)))))))

test/empty.txt

Whitespace-only changes.

0 commit comments

Comments
 (0)