Skip to content

Commit

Permalink
Fixes #3553 - Support sslSession() in Jetty Client.
Browse files Browse the repository at this point in the history
Implemented Connection.getSSLSession(), where the Connection can be obtained from the Request:
request.getConnection().getSSLSession().

Updated documentation.

Signed-off-by: Simone Bordet <simone.bordet@gmail.com>
  • Loading branch information
sbordet committed Aug 20, 2024
1 parent 942e77c commit 4bc972c
Show file tree
Hide file tree
Showing 9 changed files with 132 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.SocketAddress;
import java.net.URI;
import java.nio.ByteBuffer;
import java.nio.file.Path;
Expand All @@ -26,6 +27,7 @@
import java.util.concurrent.TimeUnit;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLSession;

import org.eclipse.jetty.client.AsyncRequestContent;
import org.eclipse.jetty.client.Authentication;
Expand All @@ -34,6 +36,7 @@
import org.eclipse.jetty.client.BufferingResponseListener;
import org.eclipse.jetty.client.BytesRequestContent;
import org.eclipse.jetty.client.CompletableResponseListener;
import org.eclipse.jetty.client.Connection;
import org.eclipse.jetty.client.ConnectionPool;
import org.eclipse.jetty.client.ContentResponse;
import org.eclipse.jetty.client.Destination;
Expand Down Expand Up @@ -1180,4 +1183,29 @@ public void mixedTransports() throws Exception
.send();
// end::mixedTransports[]
}

public void connectionInformation() throws Exception
{
// tag::connectionInformation[]
HttpClient httpClient = new HttpClient();
httpClient.start();

ContentResponse response = httpClient.newRequest("http://domain.com/path")
// The connection information is only available starting from the request begin event.
.onRequestBegin(request ->
{
Connection connection = request.getConnection();

// Obtain the address of the server.
SocketAddress remoteAddress = connection.getRemoteSocketAddress();
System.getLogger("connection").log(INFO, "Server address: %s", remoteAddress);

// Obtain the SSLSession.
SSLSession sslSession = connection.getSSLSession();
if (sslSession != null)
System.getLogger("connection").log(INFO, "SSLSession: %s", sslSession);
})
.send();
// end::connectionInformation[]
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,12 @@ A second request with the same origin sent _after_ the first request/response cy
A second request with the same origin sent _concurrently_ with the first request will likely cause the opening of a second connection, depending on the connection pool implementation.
The configuration parameter `HttpClient.maxConnectionsPerDestination` (see also the <<configuration,configuration section>>) controls the max number of connections that can be opened for a destination.

NOTE: If opening connections to a given origin takes a long time, then requests for that origin will queue up in the corresponding destination until the connections are established.
[NOTE]
====
If opening connections to a given origin takes a long time, then requests for that origin will queue up in the corresponding destination until the connections are established.
To save the time spent opening connections, you can xref:connection-pool-precreate-connections[pre-create connections].
====

Each connection can handle a limited number of concurrent requests.
For HTTP/1.1, this number is always `1`: there can only be one outstanding request for each connection.
Expand Down Expand Up @@ -528,6 +533,28 @@ This is a fancy example of how to mix HTTP versions and low-level transports:
include::code:example$src/main/java/org/eclipse/jetty/docs/programming/client/http/HTTPClientDocs.java[tag=mixedTransports]
----

[[connection-information]]
=== Request Connection Information

In order to send a request, it is necessary to obtain a connection, as explained in the xref:request-processing[request processing section].

The HTTP/1.1 protocol may send only one request at a time on a single connection, while multiplexed protocols such as HTTP/2 may send many requests at a time on a single connection.

You can access the connection information, for example the local and remote `SocketAddress`, or the `SSLSession` if the connection is secured using TLS, in the following way:

[,java,indent=0]
----
include::code:example$src/main/java/org/eclipse/jetty/docs/programming/client/http/HTTPClientDocs.java[tag=connectionInformation]
----

[NOTE]
====
The connection information is only available when the request is associated with a connection.
This means that the connection is not available in the _request queued_ event, but only starting from the _request begin_ event.
For more information about request events, see xref:non-blocking[this section].
====

[[configuration]]
== HttpClient Configuration

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

import java.io.Closeable;
import java.net.SocketAddress;
import javax.net.ssl.SSLSession;

import org.eclipse.jetty.util.Promise;

Expand Down Expand Up @@ -63,4 +64,13 @@ default SocketAddress getRemoteSocketAddress()
{
return null;
}

/**
* @return the {@link SSLSession} associated with the connection, or {@code null}
* if the connection is not secure or the {@link SSLSession} is not available.
*/
default SSLSession getSSLSession()
{
return null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import javax.net.ssl.SSLSession;

import org.eclipse.jetty.client.internal.TunnelRequest;
import org.eclipse.jetty.client.transport.HttpConversation;
Expand Down Expand Up @@ -346,6 +347,12 @@ public SocketAddress getRemoteSocketAddress()
return connection.getRemoteSocketAddress();
}

@Override
public SSLSession getSSLSession()
{
return connection.getSSLSession();
}

@Override
public void send(Request request, Response.CompleteListener listener)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.LongAdder;
import javax.net.ssl.SSLSession;

import org.eclipse.jetty.client.Connection;
import org.eclipse.jetty.client.Destination;
Expand Down Expand Up @@ -116,6 +117,12 @@ public SocketAddress getRemoteSocketAddress()
return delegate.getRemoteSocketAddress();
}

@Override
public SSLSession getSSLSession()
{
return delegate.getSSLSession();
}

@Override
public long getBytesIn()
{
Expand Down Expand Up @@ -350,6 +357,13 @@ public SocketAddress getRemoteSocketAddress()
return getEndPoint().getRemoteSocketAddress();
}

@Override
public SSLSession getSSLSession()
{
EndPoint.SslSessionData sslSessionData = getEndPoint().getSslSessionData();
return sslSessionData == null ? null : sslSessionData.sslSession();
}

@Override
public SendFailure send(HttpExchange exchange)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import javax.net.ssl.SSLSession;

import org.eclipse.jetty.client.Connection;
import org.eclipse.jetty.client.Destination;
Expand Down Expand Up @@ -101,6 +102,12 @@ public SocketAddress getRemoteSocketAddress()
return delegate.getRemoteSocketAddress();
}

@Override
public SSLSession getSSLSession()
{
return delegate.getSSLSession();
}

protected Flusher getFlusher()
{
return flusher;
Expand Down Expand Up @@ -359,6 +366,13 @@ public SocketAddress getRemoteSocketAddress()
return getEndPoint().getRemoteSocketAddress();
}

@Override
public SSLSession getSSLSession()
{
EndPoint.SslSessionData sslSessionData = getEndPoint().getSslSessionData();
return sslSessionData == null ? null : sslSessionData.sslSession();
}

@Override
public SendFailure send(HttpExchange exchange)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import javax.net.ssl.SSLSession;

import org.eclipse.jetty.client.ConnectionPool;
import org.eclipse.jetty.client.Destination;
Expand All @@ -44,6 +45,7 @@
import org.eclipse.jetty.http2.api.Session;
import org.eclipse.jetty.http2.api.Stream;
import org.eclipse.jetty.http2.frames.HeadersFrame;
import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.thread.Sweeper;
import org.slf4j.Logger;
Expand Down Expand Up @@ -85,6 +87,13 @@ public SocketAddress getRemoteSocketAddress()
return session.getRemoteSocketAddress();
}

@Override
public SSLSession getSSLSession()
{
EndPoint.SslSessionData sslSessionData = connection.getEndPoint().getSslSessionData();
return sslSessionData == null ? null : sslSessionData.sslSession();
}

public boolean isRecycleHttpChannels()
{
return recycleHttpChannels;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.net.ssl.SSLSession;

import org.eclipse.jetty.client.ConnectionPool;
import org.eclipse.jetty.client.Destination;
Expand Down Expand Up @@ -64,6 +65,14 @@ public SocketAddress getRemoteSocketAddress()
return session.getRemoteSocketAddress();
}

@Override
public SSLSession getSSLSession()
{
// No SSLSession in QUIC as SSLSession is TLS specific,
// and QUIC does not use TLS to secure the communication.
return null;
}

@Override
public int getMaxMultiplex()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,11 @@
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.IntStream;
import javax.net.ssl.SSLSession;

import org.eclipse.jetty.client.BytesRequestContent;
import org.eclipse.jetty.client.CompletableResponseListener;
import org.eclipse.jetty.client.Connection;
import org.eclipse.jetty.client.ContentResponse;
import org.eclipse.jetty.client.Destination;
import org.eclipse.jetty.client.InputStreamResponseListener;
Expand Down Expand Up @@ -1024,9 +1026,19 @@ public void testRequestConnection(Transport transport) throws Exception
ContentResponse response = client.newRequest(newURI(transport))
.onRequestBegin(r ->
{
if (r.getConnection() == null)
Connection connection = r.getConnection();
if (connection == null)
r.abort(new IllegalStateException());
})
.onRequestHeaders(r ->
{
if (transport.isSecure() && transport != Transport.H3)
{
SSLSession sslSession = r.getConnection().getSSLSession();
if (sslSession == null)
r.abort(new IllegalStateException());
}
})
.send();

assertEquals(200, response.getStatus());
Expand Down

0 comments on commit 4bc972c

Please sign in to comment.