users@jersey.java.net

[Jersey] FW: JettyConnector using Proxy over SSL/TLS

From: Roger Nordquist <roger.nordquist_at_valtech.se>
Date: Fri, 19 Sep 2014 08:01:43 +0000

Hello,

I’m in need of help to get JerseyClient to reuse connections when using SSL.
We use a client certificate with a truststore (see SSL config code in forwarded mail below)
The application we’ve built integrates with a system on the other side of the Atlantic and the latency is too high if the SSL handshake needs to take place each and every time a request is made.

I’ve looked through all guides and recommendations I’ve found on the internet in configuring the client correctly and checked with this page that we do it correctly.
http://docs.oracle.com/javase/8/docs/technotes/guides/net/http-keepalive.html

The question is if Jersey follows all that specification?

The server we access returns Connection: Keep-Alive and Keep-Alive: timeout=15, max=100 but the client recreates the connection anyway.
The response code is 200 and we read the response fully.

We have managed to get the reuse working with ApacheConnector (after some minor modification) but we haven’t found anyway of doing it with “vanilla” Jersey as we would like it to be.

Thanks,
Roger

From: Roger Nordquist <roger.nordquist_at_valtech.se<mailto:roger.nordquist_at_valtech.se>>
Date: Wednesday 10 September 2014 14:30
To: "users_at_jersey.java.net<mailto:users_at_jersey.java.net>" <users_at_jersey.java.net<mailto:users_at_jersey.java.net>>
Subject: JettyConnector using Proxy over SSL/TLS

Hello,

I am trying to use JettyConnectorProvider in Jersey 2.8 to make requests to a resource behind a proxy using SSL.
The reason why I chosen to replace the default provider is because the default one doesn’t seem to reuse connections according to the setting in the Keep-Alive header as we need it to do.
It’s for performance reasons really, but I can’t get it to work through the proxy.

Here is my setup:
    SslConfigurator sslConfig = SslConfigurator
        .newInstance()
        .trustStore(config.getTrustStore())
        .trustManagerFactoryAlgorithm(“SunX509")
        .keyPassword(config.getKeystorePass())
        .keyManagerFactoryAlgorithm(“SunX509")
        .keyStore(keyStore);

    ClientConfig clientConfig = new ClientConfig();
    clientConfig.connectorProvider(new JettyConnectorProvider());
    clientConfig.property(JettyClientProperties.SSL_CONFIG, sslConfig);
    clientConfig.property(ClientProperties.CONNECT_TIMEOUT, config.getConnectTimeout());
    clientConfig.property(ClientProperties.READ_TIMEOUT, config.getReadTimeout());

    if (config.usingHttpProxy()) {
      clientConfig.property(ClientProperties.PROXY_URI, String.format("http://%s:%s", config.getHttpProxyHost(), config.getHttpProxyPort()));
    }

    return ClientBuilder.newBuilder()
        .withConfig(clientConfig)
        .build()
        .register(getJacksonConfiguration())
        .register(new LoggingFilter())
        .register(new CharacterSetFilter())
        .register(new RequestTimeLogging());

The exception I get when using the proxy is the following:

java.nio.channels.ReadPendingException: null
        at org.eclipse.jetty.io.FillInterest.register(FillInterest.java:62) ~[jetty-io-9.1.1.v20140108.jar:3.19.0-SNAPSHOT]
        at org.eclipse.jetty.io.AbstractEndPoint.fillInterested(AbstractEndPoint.java:119) ~[jetty-io-9.1.1.v20140108.jar:3.19.0-SNAPSHOT]
        at org.eclipse.jetty.io.AbstractConnection$FillInterestedState.onEnter(AbstractConnection.java:269) ~[jetty-io-9.1.1.v20140108.jar:3.19.0-SNAPSHOT]
        at org.eclipse.jetty.io.AbstractConnection.next(AbstractConnection.java:238) ~[jetty-io-9.1.1.v20140108.jar:3.19.0-SNAPSHOT]
        at org.eclipse.jetty.io.AbstractConnection$1.run(AbstractConnection.java:512) ~[jetty-io-9.1.1.v20140108.jar:3.19.0-SNAPSHOT]
        at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:607) ~[jetty-util-9.1.1.v20140108.jar:3.19.0-SNAPSHOT]
        at org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:536) ~[jetty-util-9.1.1.v20140108.jar:3.19.0-SNAPSHOT]
        at java.lang.Thread.run(Unknown Source) [na:1.8.0_05]
2014-09-09 18:02:51,131 ExceptionMapper:24:ERROR - Uncaught exception occurred. - POST /resource …
javax.ws.rs.ProcessingException: java.util.concurrent.ExecutionException: java.io.EOFException
        at org.glassfish.jersey.jetty.connector.JettyConnector.apply(JettyConnector.java:277) ~[jersey-jetty-connector-2.8.jar:na]
        at org.glassfish.jersey.client.ClientRuntime.invoke(ClientRuntime.java:224) ~[jersey-client-2.8.jar:na]
        at org.glassfish.jersey.client.JerseyInvocation$1.call(JerseyInvocation.java:655) ~[jersey-client-2.8.jar:na]
        at org.glassfish.jersey.client.JerseyInvocation$1.call(JerseyInvocation.java:652) ~[jersey-client-2.8.jar:na]
        at org.glassfish.jersey.internal.Errors.process(Errors.java:315) [jersey-common-2.8.jar:na]
        at org.glassfish.jersey.internal.Errors.process(Errors.java:297) [jersey-common-2.8.jar:na]
        at org.glassfish.jersey.internal.Errors.process(Errors.java:228) [jersey-common-2.8.jar:na]
        at org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:424) [jersey-common-2.8.jar:na]
        at org.glassfish.jersey.client.JerseyInvocation.invoke(JerseyInvocation.java:652) ~[jersey-client-2.8.jar:na]
        at org.glassfish.jersey.client.JerseyInvocation$Builder.method(JerseyInvocation.java:412) ~[jersey-client-2.8.jar:na]
        at org.glassfish.jersey.client.JerseyInvocation$Builder.post(JerseyInvocation.java:321) ~[jersey-client-2.8.jar:na]
        ...
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_05]
        at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) ~[na:1.8.0_05]
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) ~[na:1.8.0_05]
        at java.lang.reflect.Method.invoke(Unknown Source) ~[na:1.8.0_05]
        at org.glassfish.jersey.server.model.internal.ResourceMethodInvocationHandlerFactory$1.invoke(ResourceMethodInvocationHandlerFactory.java:81) ~[jersey-server-2.8.jar:na]
        at org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher$1.run(AbstractJavaResourceMethodDispatcher.java:151) ~[jersey-server-2.8.jar:na]
        at org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher.invoke(AbstractJavaResourceMethodDispatcher.java:171) ~[jersey-server-2.8.jar:na]
        at org.glassfish.jersey.server.model.internal.JavaResourceMethodDispatcherProvider$ResponseOutInvoker.doDispatch(JavaResourceMethodDispatcherProvider.java:152) ~[jersey-server-2.8.jar:na]
        at org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher.dispatch(AbstractJavaResourceMethodDispatcher.java:104) ~[jersey-server-2.8.jar:na]
        at org.glassfish.jersey.server.model.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:387) ~[jersey-server-2.8.jar:na]
        at org.glassfish.jersey.server.model.ResourceMethodInvoker.apply(ResourceMethodInvoker.java:331) ~[jersey-server-2.8.jar:na]
        at org.glassfish.jersey.server.model.ResourceMethodInvoker.apply(ResourceMethodInvoker.java:103) ~[jersey-server-2.8.jar:na]
        at org.glassfish.jersey.server.ServerRuntime$1.run(ServerRuntime.java:269) ~[jersey-server-2.8.jar:na]
        at org.glassfish.jersey.internal.Errors$1.call(Errors.java:271) [jersey-common-2.8.jar:na]
        at org.glassfish.jersey.internal.Errors$1.call(Errors.java:267) [jersey-common-2.8.jar:na]
        at org.glassfish.jersey.internal.Errors.process(Errors.java:315) [jersey-common-2.8.jar:na]
        at org.glassfish.jersey.internal.Errors.process(Errors.java:297) [jersey-common-2.8.jar:na]
        at org.glassfish.jersey.internal.Errors.process(Errors.java:267) [jersey-common-2.8.jar:na]
        at org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:297) [jersey-common-2.8.jar:na]
        at org.glassfish.jersey.server.ServerRuntime.process(ServerRuntime.java:252) [jersey-server-2.8.jar:na]
        at org.glassfish.jersey.server.ApplicationHandler.handle(ApplicationHandler.java:1023) [jersey-server-2.8.jar:na]
        at org.glassfish.jersey.servlet.WebComponent.service(WebComponent.java:372) [jersey-container-servlet-core-2.8.jar:na]
        at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:382) [jersey-container-servlet-core-2.8.jar:na]
        at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:345) [jersey-container-servlet-core-2.8.jar:na]
        at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:220) [jersey-container-servlet-core-2.8.jar:na]
        at org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:751) []
        at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1666) []
        ...
        at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1645) []
        at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:564) []
        at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:143) []
        at org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:578) []
        at org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:221) []
        at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1111) []
        at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:498) []
        at org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:183) []
        at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1045) []
        at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:141) []
        at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:98) []
        at org.eclipse.jetty.server.Server.handle(Server.java:461) []
        at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:284) []
        at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:244) []
        at org.eclipse.jetty.io.AbstractConnection$2.run(AbstractConnection.java:534) []
        at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:607) []
        at org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:536) []
        at java.lang.Thread.run(Unknown Source) [na:1.8.0_05]
Caused by: java.util.concurrent.ExecutionException: java.io.EOFException
        at org.eclipse.jetty.client.util.FutureResponseListener.getResult(FutureResponseListener.java:118) ~[jetty-client-9.1.1.v20140108.jar:9.1.1.v20140108]
        at org.eclipse.jetty.client.util.FutureResponseListener.get(FutureResponseListener.java:110) ~[jetty-client-9.1.1.v20140108.jar:9.1.1.v20140108]
        at org.eclipse.jetty.client.HttpRequest.send(HttpRequest.java:579) ~[jetty-client-9.1.1.v20140108.jar:9.1.1.v20140108]
        at org.glassfish.jersey.jetty.connector.JettyConnector.apply(JettyConnector.java:259) ~[jersey-jetty-connector-2.8.jar:na]
        ... 68 common frames omitted
Caused by: java.io.EOFException: null
        at org.eclipse.jetty.client.http.HttpReceiverOverHTTP.earlyEOF(HttpReceiverOverHTTP.java:203) ~[]
        at org.eclipse.jetty.http.HttpParser.parseNext(HttpParser.java:1261) ~[]
        at org.eclipse.jetty.client.http.HttpReceiverOverHTTP.shutdown(HttpReceiverOverHTTP.java:129) ~[]
        at org.eclipse.jetty.client.http.HttpReceiverOverHTTP.receive(HttpReceiverOverHTTP.java:91) ~[]
        at org.eclipse.jetty.client.http.HttpChannelOverHTTP.receive(HttpChannelOverHTTP.java:75) ~[]
        at org.eclipse.jetty.client.http.HttpConnectionOverHTTP.onFillable(HttpConnectionOverHTTP.java:103) ~[]
        at org.eclipse.jetty.io.AbstractConnection$1.run(AbstractConnection.java:505) ~[]
        ... 3 common frames omitted

It looks like it cannot read the response properly but it only happens with the JettyConnector, not the default so I think the proxy is correctly configured.

When not using the proxy it looks like it works perfectly and connections are reused as I want them to, but I can’t say for sure for those tests were only made from my local computer, not the environment I get this exception on.

And btw, we are not using any asynchronous calls so this exception is kinda confusing :S

Is some additional configuration needed that I’m not aware of?
Any idea´s and pointers are welcome as I’m pretty stuck right now! :)

Thanks!

/Roger