Skip to content

No ssl-context via SslContext in Aleph 0.7.0 and later #727

Closed
@David-Ongaro

Description

@David-Ongaro

When trying to upgrade one of our services from Aleph 0.6.4 to 0.8.0 I saw this strange exception in the logs:

[...]
Caused by: clojure.lang.ExceptionInfo: Some desired HTTP versions are not part of ALPN config.
{:desired-http-versions [:http1], :alpn-protocols []}
 at aleph.http.common$assert_consistent_alpn_config_BANG_.invokeStatic (common.clj:139)
    aleph.http.common$assert_consistent_alpn_config_BANG_.invoke (common.clj:130)
    aleph.http.common$ensure_consistent_alpn_config.invokeStatic (common.clj:182)
    aleph.http.common$ensure_consistent_alpn_config.invoke (common.clj:143)
    aleph.http.client$client_ssl_context.invokeStatic (client.clj:698)
    aleph.http.client$client_ssl_context.invoke (client.clj:690)
    aleph.http.client$http_connection.invokeStatic (client.clj:799)
    aleph.http.client$http_connection.invoke (client.clj:752)
    aleph.http$create_connection.invokeStatic (http.clj:104)
    aleph.http$create_connection.invoke (http.clj:97)
    aleph.http$connection_pool$fn__21882.invoke (http.clj:239)
    aleph.flow$instrumented_pool$reify__12626.generate (flow.clj:47)
    io.aleph.dirigiste.Pool.addObject (Pool.java:273)
    io.aleph.dirigiste.Pool.acquire (Pool.java:466)
    aleph.flow$acquire$fn__12631.invoke (flow.clj:74)
    aleph.flow$acquire.invokeStatic (flow.clj:73)
    aleph.flow$acquire.invoke (flow.clj:68)
    aleph.http$eval21907$request__21911$fn__21915$fn__21916.invoke (http.clj:377)
    aleph.http$eval21907$request__21911$fn__21915.invoke (http.clj:371)
    aleph.http$eval21907$request__21911.invoke (http.clj:370)
[...]

When trying to pin this down, I could confirm that this already happens in Aleph 0.7.0 (even for the alpha versions) and it only happens when the pool is configured with an io.netty.handler.ssl.SslContext instance. I.e.

(def ssl-context (.build (io.netty.handler.ssl.SslContextBuilder/forClient)))

(def pool (http/connection-pool {:connection-options {:ssl-context ssl-context}}))

(http/get "https://example.com" {:pool pool}) =>
#<Deferred@346c5293: Error printing return value (ExceptionInfo) at aleph.http.common/assert-consistent-alpn-config! (common.clj:139).
Some desired HTTP versions are not part of ALPN config.

Note that the exception happens even before the deferred is dereferenced. Also note that I just use a plain io.netty.handler.ssl.JdkSslClientContext instance here without any special SSL config in this example, since the specific config doesn't seem to matter here.

If I use a plain map to initialize the ssl-context, e.g. say

(def ssl-context {:trust-store (io/as-file client-ca)})

(whereas io/as-file is clojure.java.io/as-file here, and client-ca a file path string), this doesn't happen. So that's a viable workaround. Still, I couldn't find any update in the documentation that says SslClientContext instances are not supported anymore, so this is at least a documentation "bug".


Addendum

As per the docs, instead of java.io.File instances, java.io.InputStream instances are also supported as keys for the :ssl-context map. But I can't figure out how this is supposed to be used, as I regularly get java.lang.IllegalArgumentException: Input stream does not contain valid certificates. exceptions. I.e., preparing a single request just works fine, but preparing them in quick succession may fail:

(def ssl-context {:trust-store (io/input-stream client-ca)})

(def pool (http/connection-pool {:connection-options {:ssl-context ssl-context}}))

(http/get "https://example.com" {:pool pool}) => #<Deferred@6fb6283a: :not-delivered>

[(http/get "https://example.com" {:pool pool}) (http/get "https://example.com" {:pool pool})] =>
[#<Deferred@58ac8429: Error printing return value (CertificateException) at io.netty.handler.ssl.PemReader/readCertificates (PemReader.java:114).
Error printing return value (CertificateException) at io.netty.handler.ssl.PemReader/readCertificates (PemReader.java:114).
found no certificates in input stream
            PemReader.java:  114  io.netty.handler.ssl.PemReader/readCertificates
           SslContext.java: 1263  io.netty.handler.ssl.SslContext/toX509Certificates
    SslContextBuilder.java:  276  io.netty.handler.ssl.SslContextBuilder/trustManager
                 netty.clj:  917  aleph.netty/eval23500/add-ssl-trust-manager!
                 netty.clj: 1026  aleph.netty/eval23500/ssl-client-context
                 netty.clj: 1193  aleph.netty/coerce-ssl-context
                 netty.clj: 1179  aleph.netty/coerce-ssl-context
                  core.clj: 2641  clojure.core/partial/fn
                client.clj:  699  aleph.http.client/client-ssl-context
                client.clj:  690  aleph.http.client/client-ssl-context
                client.clj:  799  aleph.http.client/http-connection
                client.clj:  752  aleph.http.client/http-connection
                  http.clj:  104  aleph.http/create-connection
                  http.clj:   97  aleph.http/create-connection
                  http.clj:  239  aleph.http/connection-pool/fn
                  flow.clj:   47  aleph.flow/instrumented-pool/reify
                 Pool.java:  273  io.aleph.dirigiste.Pool/addObject
                 Pool.java:  466  io.aleph.dirigiste.Pool/acquire
                  flow.clj:   74  aleph.flow/acquire/fn
                  flow.clj:   73  aleph.flow/acquire
                  flow.clj:   68  aleph.flow/acquire
                  http.clj:  377  aleph.http/eval30155/request/fn/fn
                  http.clj:  371  aleph.http/eval30155/request/fn
                  http.clj:  370  aleph.http/eval30155/request
                  http.clj:  481  aleph.http/req
                  http.clj:  477  aleph.http/req
                  core.clj: 2642  clojure.core/partial/fn

I didn't look into the implementation, but I suspect what's happening here is that when the first thread of the thread pool is initialized, it's exhausting the input-stream instance and this instance is reused during the initialization of a second thread. (At least that's what I hope is happening, since the alternative would be that each thread tries to reread the certificate on each request.)

So the question is, if this doesn't work, why is it even supported? But if this is indeed an issue, it probably should be handled in a separate ticket, since this behavior already applies to Aleph 0.6.4 and therefore can't be considered a regression.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions