|
8 | 8 | * Licensed under the MIT License
|
9 | 9 | */
|
10 | 10 |
|
11 |
| -package jayo.internal; |
| 11 | +package jayo.internal.tls; |
12 | 12 |
|
13 | 13 | import jayo.*;
|
14 |
| -import jayo.tls.Handshake; |
15 |
| -import jayo.tls.TlsEndpoint; |
| 14 | +import jayo.internal.RealTlsEndpoint; |
| 15 | +import jayo.tls.*; |
16 | 16 | import org.jspecify.annotations.NonNull;
|
17 | 17 |
|
18 | 18 | import javax.net.ssl.SSLContext;
|
19 | 19 | import javax.net.ssl.SSLEngine;
|
20 | 20 | import javax.net.ssl.SSLSession;
|
21 |
| -import java.security.NoSuchAlgorithmException; |
22 | 21 | import java.util.Objects;
|
23 | 22 | import java.util.function.Consumer;
|
| 23 | +import java.util.function.Function; |
| 24 | + |
| 25 | +import static java.lang.System.Logger.Level.DEBUG; |
| 26 | +import static java.lang.System.Logger.Level.TRACE; |
24 | 27 |
|
25 | 28 | /**
|
26 | 29 | * A client-side {@link TlsEndpoint}.
|
27 | 30 | */
|
28 |
| -public final class ClientTlsEndpoint implements TlsEndpoint { |
| 31 | +public final class RealClientTlsEndpoint implements ClientTlsEndpoint { |
| 32 | + private static final System.Logger LOGGER = System.getLogger("jayo.tls.ClientTlsEndpoint"); |
| 33 | + |
29 | 34 | private final @NonNull Endpoint encryptedEndpoint;
|
| 35 | + private final @NonNull ClientHandshakeCertificates handshakeCertificates; |
30 | 36 | private final @NonNull RealTlsEndpoint impl;
|
31 | 37 |
|
32 | 38 | private Reader reader = null;
|
33 | 39 | private Writer writer = null;
|
34 | 40 |
|
35 |
| - private ClientTlsEndpoint( |
| 41 | + private RealClientTlsEndpoint( |
36 | 42 | final @NonNull Endpoint encryptedEndpoint,
|
37 |
| - final @NonNull SSLEngine engine, |
| 43 | + final @NonNull ClientHandshakeCertificates handshakeCertificates, |
| 44 | + final @NonNull Function<@NonNull SSLContext, @NonNull SSLEngine> engineFactory, |
38 | 45 | final @NonNull Consumer<@NonNull SSLSession> sessionInitCallback,
|
39 | 46 | final boolean waitForCloseConfirmation) {
|
40 | 47 | assert encryptedEndpoint != null;
|
| 48 | + assert handshakeCertificates != null; |
| 49 | + assert engineFactory != null; |
41 | 50 | assert sessionInitCallback != null;
|
42 |
| - assert engine != null; |
43 | 51 |
|
44 | 52 | this.encryptedEndpoint = encryptedEndpoint;
|
| 53 | + this.handshakeCertificates = handshakeCertificates; |
| 54 | + |
| 55 | + final var context = ((RealHandshakeCertificates) handshakeCertificates).sslContext(); |
| 56 | + // call client code |
| 57 | + final SSLEngine engine; |
| 58 | + try { |
| 59 | + engine = engineFactory.apply(context); |
| 60 | + } catch (Exception e) { |
| 61 | + if (LOGGER.isLoggable(TRACE)) { |
| 62 | + LOGGER.log(TRACE, "Client threw exception in SSLEngine factory.", e); |
| 63 | + } else if (LOGGER.isLoggable(DEBUG)) { |
| 64 | + LOGGER.log(DEBUG, "Client threw exception in SSLEngine factory: {0}.", |
| 65 | + e.getMessage()); |
| 66 | + } |
| 67 | + throw new JayoTlsHandshakeCallbackException("SSLEngine creation callback failed", e); |
| 68 | + } |
45 | 69 |
|
46 | 70 | impl = new RealTlsEndpoint(
|
47 | 71 | encryptedEndpoint,
|
@@ -83,81 +107,78 @@ public boolean shutdown() {
|
83 | 107 |
|
84 | 108 | @Override
|
85 | 109 | public boolean shutdownReceived() {
|
86 |
| - return impl.shutdownReceived; |
| 110 | + return impl.shutdownReceived(); |
87 | 111 | }
|
88 | 112 |
|
89 | 113 | @Override
|
90 | 114 | public boolean shutdownSent() {
|
91 |
| - return impl.shutdownSent; |
| 115 | + return impl.shutdownSent(); |
92 | 116 | }
|
93 | 117 |
|
94 | 118 | @Override
|
95 | 119 | public void close() {
|
96 | 120 | impl.close();
|
97 | 121 | }
|
98 | 122 |
|
| 123 | + @Override |
| 124 | + public @NonNull ClientHandshakeCertificates getHandshakeCertificates() { |
| 125 | + return handshakeCertificates; |
| 126 | + } |
| 127 | + |
| 128 | + private static @NonNull SSLEngine defaultSSLEngineFactory(final @NonNull SSLContext sslContext) { |
| 129 | + assert sslContext != null; |
| 130 | + SSLEngine engine = sslContext.createSSLEngine(); |
| 131 | + engine.setUseClientMode(true); |
| 132 | + return engine; |
| 133 | + } |
| 134 | + |
99 | 135 | /**
|
100 |
| - * Builder of {@link ClientTlsEndpoint} |
| 136 | + * Builder of {@link RealClientTlsEndpoint} |
101 | 137 | */
|
102 |
| - public static final class Builder extends RealTlsEndpoint.Builder<ClientBuilder> implements ClientBuilder { |
103 |
| - private final @NonNull SSLEngine engine; |
104 |
| - |
105 |
| - public Builder() { |
106 |
| - // todo change this! |
107 |
| - try { |
108 |
| - engine = SSLContext.getDefault().createSSLEngine(); |
109 |
| - } catch (NoSuchAlgorithmException e) { |
110 |
| - throw new RuntimeException(e); |
111 |
| - } |
112 |
| - engine.setUseClientMode(true); |
113 |
| - } |
114 |
| - |
115 |
| - public Builder(final @NonNull SSLContext sslContext) { |
116 |
| - assert sslContext != null; |
| 138 | + public static final class Builder extends RealTlsEndpoint.Builder<ClientTlsEndpoint.Builder> |
| 139 | + implements ClientTlsEndpoint.Builder { |
| 140 | + private final @NonNull ClientHandshakeCertificates handshakeCertificates; |
117 | 141 |
|
118 |
| - engine = sslContext.createSSLEngine(); |
119 |
| - engine.setUseClientMode(true); |
120 |
| - } |
121 | 142 |
|
122 |
| - public Builder(final @NonNull SSLEngine engine) { |
123 |
| - assert engine != null; |
| 143 | + public Builder(final @NonNull ClientHandshakeCertificates handshakeCertificates) { |
| 144 | + assert handshakeCertificates != null; |
124 | 145 |
|
125 |
| - this.engine = engine; |
| 146 | + this.handshakeCertificates = handshakeCertificates; |
126 | 147 | }
|
127 | 148 |
|
128 | 149 | /**
|
129 | 150 | * The private constructor used by {@link #clone()}.
|
130 | 151 | */
|
131 |
| - private Builder(final @NonNull SSLEngine engine, |
| 152 | + private Builder(final @NonNull ClientHandshakeCertificates handshakeCertificates, |
132 | 153 | final @NonNull Consumer<@NonNull SSLSession> sessionInitCallback,
|
133 | 154 | final boolean waitForCloseConfirmation) {
|
134 |
| - assert engine != null; |
| 155 | + assert handshakeCertificates != null; |
135 | 156 | assert sessionInitCallback != null;
|
136 | 157 |
|
137 |
| - this.engine = engine; |
| 158 | + this.handshakeCertificates = handshakeCertificates; |
138 | 159 | this.sessionInitCallback = sessionInitCallback;
|
139 | 160 | this.waitForCloseConfirmation = waitForCloseConfirmation;
|
140 | 161 | }
|
141 | 162 |
|
142 | 163 | @Override
|
143 |
| - @NonNull |
144 |
| - Builder getThis() { |
| 164 | + protected @NonNull Builder getThis() { |
145 | 165 | return this;
|
146 | 166 | }
|
147 | 167 |
|
148 | 168 | @Override
|
149 |
| - public @NonNull TlsEndpoint build(final @NonNull Endpoint encryptedEndpoint) { |
| 169 | + public @NonNull ClientTlsEndpoint build(final @NonNull Endpoint encryptedEndpoint) { |
150 | 170 | Objects.requireNonNull(encryptedEndpoint);
|
151 |
| - return new ClientTlsEndpoint( |
| 171 | + return new RealClientTlsEndpoint( |
152 | 172 | encryptedEndpoint,
|
153 |
| - engine, |
| 173 | + handshakeCertificates, |
| 174 | + (sslEngineFactory != null) ? sslEngineFactory : RealClientTlsEndpoint::defaultSSLEngineFactory, |
154 | 175 | sessionInitCallback,
|
155 | 176 | waitForCloseConfirmation);
|
156 | 177 | }
|
157 | 178 |
|
158 | 179 | @Override
|
159 |
| - public @NonNull ClientBuilder clone() { |
160 |
| - return new Builder(engine, sessionInitCallback, waitForCloseConfirmation); |
| 180 | + public @NonNull Builder clone() { |
| 181 | + return new Builder(handshakeCertificates, sessionInitCallback, waitForCloseConfirmation); |
161 | 182 | }
|
162 | 183 | }
|
163 | 184 |
|
|
0 commit comments