19
19
import java .io .IOException ;
20
20
import java .net .URI ;
21
21
import java .util .concurrent .TimeUnit ;
22
+ import javax .net .ssl .SSLException ;
22
23
23
24
import io .netty .bootstrap .Bootstrap ;
24
25
import io .netty .channel .ChannelConfig ;
32
33
import io .netty .handler .codec .http .HttpClientCodec ;
33
34
import io .netty .handler .codec .http .HttpObjectAggregator ;
34
35
import io .netty .handler .ssl .SslContext ;
36
+ import io .netty .handler .ssl .SslContextBuilder ;
35
37
import io .netty .handler .timeout .ReadTimeoutHandler ;
36
38
37
39
import org .springframework .beans .factory .DisposableBean ;
48
50
*
49
51
* @author Arjen Poutsma
50
52
* @author Rossen Stoyanchev
53
+ * @author Brian Clozel
51
54
* @since 4.1.2
52
55
*/
53
56
public class Netty4ClientHttpRequestFactory implements ClientHttpRequestFactory ,
@@ -74,6 +77,8 @@ public class Netty4ClientHttpRequestFactory implements ClientHttpRequestFactory,
74
77
75
78
private volatile Bootstrap bootstrap ;
76
79
80
+ private volatile Bootstrap sslBootstrap ;
81
+
77
82
78
83
/**
79
84
* Create a new {@code Netty4ClientHttpRequestFactory} with a default
@@ -99,6 +104,15 @@ public Netty4ClientHttpRequestFactory(EventLoopGroup eventLoopGroup) {
99
104
}
100
105
101
106
107
+ private SslContext getDefaultClientSslContext () {
108
+ try {
109
+ return SslContextBuilder .forClient ().build ();
110
+ }
111
+ catch (SSLException exc ) {
112
+ throw new IllegalStateException ("Could not create default client SslContext" , exc );
113
+ }
114
+ }
115
+
102
116
/**
103
117
* Set the default maximum response size.
104
118
* <p>By default this is set to {@link #DEFAULT_MAX_RESPONSE_SIZE}.
@@ -112,7 +126,7 @@ public void setMaxResponseSize(int maxResponseSize) {
112
126
/**
113
127
* Set the SSL context. When configured it is used to create and insert an
114
128
* {@link io.netty.handler.ssl.SslHandler} in the channel pipeline.
115
- * <p>By default this is not set .
129
+ * <p>A default client SslContext is configured if none has been provided .
116
130
*/
117
131
public void setSslContext (SslContext sslContext ) {
118
132
this .sslContext = sslContext ;
@@ -136,29 +150,44 @@ public void setReadTimeout(int readTimeout) {
136
150
this .readTimeout = readTimeout ;
137
151
}
138
152
139
- private Bootstrap getBootstrap () {
140
- if (this .bootstrap == null ) {
141
- Bootstrap bootstrap = new Bootstrap ();
142
- bootstrap .group (this .eventLoopGroup ).channel (NioSocketChannel .class )
143
- .handler (new ChannelInitializer <SocketChannel >() {
144
- @ Override
145
- protected void initChannel (SocketChannel channel ) throws Exception {
146
- configureChannel (channel .config ());
147
- ChannelPipeline pipeline = channel .pipeline ();
148
- if (sslContext != null ) {
149
- pipeline .addLast (sslContext .newHandler (channel .alloc ()));
150
- }
151
- pipeline .addLast (new HttpClientCodec ());
152
- pipeline .addLast (new HttpObjectAggregator (maxResponseSize ));
153
- if (readTimeout > 0 ) {
154
- pipeline .addLast (new ReadTimeoutHandler (readTimeout ,
155
- TimeUnit .MILLISECONDS ));
156
- }
157
- }
158
- });
159
- this .bootstrap = bootstrap ;
153
+ private Bootstrap getBootstrap (URI uri ) {
154
+ final boolean isSecure = (uri .getPort () == 443 )
155
+ || (uri .getPort () == -1 && "https" .equalsIgnoreCase (uri .getScheme ()));
156
+ if (isSecure ) {
157
+ if (this .sslBootstrap == null ) {
158
+ this .sslBootstrap = buildBootstrap (true );
159
+ }
160
+ return this .sslBootstrap ;
161
+ }
162
+ else {
163
+ if (this .bootstrap == null ) {
164
+ this .bootstrap = buildBootstrap (false );
165
+ }
166
+ return this .bootstrap ;
160
167
}
161
- return this .bootstrap ;
168
+ }
169
+
170
+ private Bootstrap buildBootstrap (final boolean isSecure ) {
171
+ Bootstrap bootstrap = new Bootstrap ();
172
+ bootstrap .group (this .eventLoopGroup ).channel (NioSocketChannel .class )
173
+ .handler (new ChannelInitializer <SocketChannel >() {
174
+ @ Override
175
+ protected void initChannel (SocketChannel channel ) throws Exception {
176
+ configureChannel (channel .config ());
177
+ ChannelPipeline pipeline = channel .pipeline ();
178
+ if (isSecure ) {
179
+ Assert .notNull (sslContext , "sslContext should not be null" );
180
+ pipeline .addLast (sslContext .newHandler (channel .alloc ()));
181
+ }
182
+ pipeline .addLast (new HttpClientCodec ());
183
+ pipeline .addLast (new HttpObjectAggregator (maxResponseSize ));
184
+ if (readTimeout > 0 ) {
185
+ pipeline .addLast (new ReadTimeoutHandler (readTimeout ,
186
+ TimeUnit .MILLISECONDS ));
187
+ }
188
+ }
189
+ });
190
+ return bootstrap ;
162
191
}
163
192
164
193
/**
@@ -173,11 +202,12 @@ protected void configureChannel(SocketChannelConfig config) {
173
202
}
174
203
175
204
@ Override
176
- public void afterPropertiesSet () {
177
- getBootstrap ();
205
+ public void afterPropertiesSet () throws Exception {
206
+ if (this .sslContext == null ) {
207
+ this .sslContext = getDefaultClientSslContext ();
208
+ }
178
209
}
179
210
180
-
181
211
@ Override
182
212
public ClientHttpRequest createRequest (URI uri , HttpMethod httpMethod ) throws IOException {
183
213
return createRequestInternal (uri , httpMethod );
@@ -189,7 +219,7 @@ public AsyncClientHttpRequest createAsyncRequest(URI uri, HttpMethod httpMethod)
189
219
}
190
220
191
221
private Netty4ClientHttpRequest createRequestInternal (URI uri , HttpMethod httpMethod ) {
192
- return new Netty4ClientHttpRequest (getBootstrap (), uri , httpMethod );
222
+ return new Netty4ClientHttpRequest (getBootstrap (uri ), uri , httpMethod );
193
223
}
194
224
195
225
0 commit comments