diff --git a/src/aleph/http.clj b/src/aleph/http.clj index a0a7d376..35abf8d4 100644 --- a/src/aleph/http.clj +++ b/src/aleph/http.clj @@ -231,7 +231,10 @@ (when (and force-h2c? (not-any? #{:http2} http-versions)) (throw (IllegalArgumentException. "force-h2c? may only be true when HTTP/2 is enabled.")))) - (let [log-activity (:log-activity connection-options) + (let [{:keys [log-activity + http-versions + insecure?] + :or {http-versions [:http1]}} connection-options dns-options' (if-not (and (some? dns-options) (not (or (contains? dns-options :transport) (contains? dns-options :epoll?)))) @@ -244,7 +247,10 @@ (assoc :name-resolver (netty/dns-resolver-group dns-options')) (some? log-activity) - (assoc :log-activity (netty/activity-logger "aleph-client" log-activity))) + (assoc :log-activity (netty/activity-logger "aleph-client" log-activity)) + + true + (update :ssl-context #(client/ssl-context % http-versions insecure?))) p (promise) create-pool-fn (or pool-builder-fn flow/instrumented-pool) diff --git a/src/aleph/http/client.clj b/src/aleph/http/client.clj index 67f2bebf..72bc1ed5 100644 --- a/src/aleph/http/client.clj +++ b/src/aleph/http/client.clj @@ -689,22 +689,6 @@ resp)))) -(defn- client-ssl-context - "Returns a client SslContext, or nil if none is requested. - Validates the ALPN setup." - ^SslContext - [ssl? ssl-context http-versions insecure?] - (if ssl? - (if ssl-context - (-> ssl-context - (common/ensure-consistent-alpn-config http-versions) - (netty/coerce-ssl-client-context)) - (let [ssl-ctx-opts {:application-protocol-config (netty/application-protocol-config http-versions)}] - (if insecure? - (netty/insecure-ssl-client-context ssl-ctx-opts) - (netty/ssl-client-context ssl-ctx-opts)))) - nil)) - (defn- setup-http1-client [{:keys [on-closed response-executor] :as opts}] @@ -761,8 +745,6 @@ bootstrap-transform name-resolver keep-alive? - insecure? - ssl-context ssl-endpoint-id-alg response-buffer-size epoll? @@ -770,7 +752,6 @@ proxy-options pipeline-transform log-activity - http-versions force-h2c? on-closed connect-timeout] @@ -784,7 +765,6 @@ epoll? false name-resolver :default log-activity :debug - http-versions [:http1] force-h2c? false} :as opts}] @@ -798,8 +778,6 @@ (get proxy-options :keep-alive? true)))) authority (str host (when explicit-port? (str ":" port))) - ssl-context (client-ssl-context ssl? ssl-context http-versions insecure?) - logger (cond (instance? LoggingHandler log-activity) log-activity (some? log-activity) (netty/activity-logger "aleph-client" log-activity) @@ -810,7 +788,6 @@ (assoc opts :proxy-connected proxy-connected :ssl? ssl? - :ssl-context ssl-context :ssl-endpoint-id-alg ssl-endpoint-id-alg :remote-address remote-address :raw-stream? raw-stream? @@ -868,7 +845,6 @@ :raw-stream? raw-stream? :remote-address remote-address :response-buffer-size response-buffer-size - :ssl-context ssl-context :ssl? ssl?)] (log/debug (str "Using HTTP protocol: " protocol) @@ -935,6 +911,19 @@ :response-buffer-size response-buffer-size :t0 t0}))))))))))))))) +(defn ssl-context + "Coerces a client SSL context, including enforcement of its ALPN setup." + (^SslContext [http-versions] (ssl-context nil http-versions false)) + (^SslContext [ssl-ctx http-versions insecure?] + (if ssl-ctx + (-> ssl-ctx + (common/ensure-consistent-alpn-config http-versions) + (netty/coerce-ssl-client-context)) + (let [ssl-ctx-opts {:application-protocol-config (netty/application-protocol-config http-versions)}] + (if insecure? + (netty/insecure-ssl-client-context ssl-ctx-opts) + (netty/ssl-client-context ssl-ctx-opts)))))) + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -970,7 +959,7 @@ (InetSocketAddress/createUnresolved "www.google.com" (int 443)) true {:on-closed #(println "http conn closed") - :http-versions [:http1]})) + :ssl-context (ssl-context [:http1])})) (conn {:request-method :get})) ) diff --git a/src/aleph/http/http2.clj b/src/aleph/http/http2.clj index d8328251..425e749c 100644 --- a/src/aleph/http/http2.clj +++ b/src/aleph/http/http2.clj @@ -1426,7 +1426,7 @@ (InetSocketAddress. "postman-echo.com" (int 443)) true {:on-closed #(log/debug "http conn closed") - :http-versions [:http2]})) + :ssl-context (aleph.http.client/ssl-context [:http2])})) (def result @(conn {:request-method :get ;;:raw-stream? true @@ -1438,7 +1438,7 @@ (InetSocketAddress. "postman-echo.com" (int 443)) true {:on-closed #(log/debug "http conn closed") - :http-versions [:http2]})) + :ssl-context (aleph.http.client/ssl-context [:http2])})) (let [body-string "Body test" fpath (Files/createTempFile "test" ".txt" (into-array FileAttribute [])) @@ -1482,7 +1482,7 @@ (InetSocketAddress. "postman-echo.com" (int 443)) true {:on-closed #(log/debug "http conn closed") - :http-versions [:http2]})) + :ssl-context (aleph.http.client/ssl-context [:http2])})) (def result @(conn {:request-method :post @@ -1504,7 +1504,7 @@ (InetSocketAddress. "postman-echo.com" (int 443)) true {:on-closed #(log/debug "http conn closed") - :http-versions [:http2]})) + :ssl-context (aleph.http.client/ssl-context [:http2])})) (let [req {:request-method :post :uri "/post" @@ -1521,7 +1521,7 @@ (InetSocketAddress. "postman-echo.com" (int 443)) true {:on-closed #(log/debug "http conn closed") - :http-versions [:http1]})) + :ssl-context (aleph.http.client/ssl-context [:http1])})) (def result @(conn {:request-method :get :uri "/gzip" #_ "/deflate" diff --git a/src/aleph/http/server.clj b/src/aleph/http/server.clj index 34cd6c80..938e90ee 100644 --- a/src/aleph/http/server.clj +++ b/src/aleph/http/server.clj @@ -885,7 +885,7 @@ (InetSocketAddress. "127.0.0.1" (int port)) true {:on-closed #(log/debug "http conn closed") - :http-versions [:http2]})) + :ssl-context (aleph.http.client/ssl-context [:http2])})) (def result @(d/timeout! (conn {:request-method :get}) 2000 @@ -906,8 +906,7 @@ (InetSocketAddress. "aleph.localhost" (int port)) true {:on-closed #(log/debug "http conn closed") - :http-versions [:http2] - :insecure? true})) + :ssl-context (aleph.http.client/ssl-context nil [:http2] true)})) (def result @(d/timeout! (conn {:request-method :get}) 2000 diff --git a/test/aleph/http_test.clj b/test/aleph/http_test.clj index a3b8154c..702ee684 100644 --- a/test/aleph/http_test.clj +++ b/test/aleph/http_test.clj @@ -456,6 +456,37 @@ :body bs/to-string)))))) +(deftest using-input-stream-as-ssl-context-trust-store + (let [num-requests 2 + file-name "test/ca_cert.pem" + client-options (fn [stream] + {:connection-options {:ssl-context {:private-key test-ssl/client-key + :certificate-chain [test-ssl/client-cert] + :trust-store stream}}}) + requests (fn [pool] + (repeatedly num-requests #(http-post "/" + {:body "hello!" + :pool pool})))] + (testing "multiple serial requests without connection reuse" + (with-open [stream (io/input-stream file-name)] + (let [client-pool (http/connection-pool (-> (client-options stream) + (assoc-in [:connection-options :keep-alive?] false)))] + (with-http-ssl-servers echo-handler {} + (is (every? + #{"hello!"} + (->> (requests client-pool) + (mapv (comp bs/to-string :body deref))))))))) + + (testing "multiple concurrent requests" + (with-open [stream (io/input-stream file-name)] + (let [client-pool (http/connection-pool (client-options stream))] + (with-http-ssl-servers echo-handler {} + (is (every? + #{"hello!"} + (->> (requests client-pool) + (doall) + (mapv (comp bs/to-string :body deref))))))))))) + (defn ssl-session-capture-handler [ssl-session-atom] (fn [req] (reset! ssl-session-atom (http.core/ring-request-ssl-session req)) diff --git a/test/ca_cert.pem b/test/ca_cert.pem new file mode 100644 index 00000000..798fa173 --- /dev/null +++ b/test/ca_cert.pem @@ -0,0 +1,23 @@ +-----BEGIN CERTIFICATE----- +MIIDyjCCArKgAwIBAgIJAPj8IfB83MXVMA0GCSqGSIb3DQEBCwUAMHIxCzAJBgNV +BAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRIwEAYDVQQHDAlDdXBlcnRpbm8x +GDAWBgNVBAoMD0JGUCBDb3Jwb3JhdGlvbjEOMAwGA1UECwwFQWxlcGgxEDAOBgNV +BAMMB1Jvb3QgQ0EwHhcNMTYxMTIxMjEzMTIzWhcNMzcwMjI0MjEzMTIzWjByMQsw +CQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTESMBAGA1UEBwwJQ3VwZXJ0 +aW5vMRgwFgYDVQQKDA9CRlAgQ29ycG9yYXRpb24xDjAMBgNVBAsMBUFsZXBoMRAw +DgYDVQQDDAdSb290IENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA +1kKISz7cCJIU7pk+JBOH8+6UfvtR7BS1hTkWMw+IsTa9O1EJJqEtiJZTF267nLog ++jfUr8AHSTR+qtKkbs77XrOMlaa6Zyq3Z2d/p8R3oUdurg6T3JECGwilYDsEMLNL +XnqnUdkeWQJ7ea7UzgJ7ACZ61I4+Dv9xJQ+5BGMRkH+SUTDQ/um8UmrPxbDDljR7 +TbTY7WtAPbxbALrEKA5EfNS1vdcYCfguN0BUcHaHEiBDAIU7IXZigdPBnSTDHhqB +YHjmgQZ9U/ojrvmjG9lsG6X5WGj5H1SZCmpWbp+WiNEgHckzhRkCKU5V53mpqcrF +Q5WJjAHGQrBF7CD1IUj6VwIDAQABo2MwYTAdBgNVHQ4EFgQUHZFU7TsvVmLorae0 +LntY0bhIRwIwHwYDVR0jBBgwFoAUHZFU7TsvVmLorae0LntY0bhIRwIwDwYDVR0T +AQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZIhvcNAQELBQADggEBACfu +Sp0gy8QI1BP6bAueT6/t7Nz2Yg2kwbIXac5sanLc9MjhjG/EjLrkwhCpEVEfFrKD +Bl/s0wdYoHcVTDlev4H3QOM4WeciaSUsEytihhey72f89ZyvQ+FGbif2BXNk4kPN +0eo3t5TXS8Fw/iBi371KZo4jTpdsB0Y3fwKtXw8ieUAlaF86yGHA9bMF7eGXorpS +hEJ8JRWWy2pV9WtkYw+tBWj7PtXQAIUx4t+J3+B9pSUyHxxArKmZUKa3GpJzBAKX +TLHddtadJLqptjZ6pq7OSiihAs3fxVF+TGDJyPyk8K48y9G2MinrYXVzKHeQWqPT +rO0jz1F4FL9LiD+HwLc= +-----END CERTIFICATE-----