1
1
/*
2
- * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved.
2
+ * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved.
3
3
*
4
4
* This program and the accompanying materials are made available under the
5
5
* terms of the Eclipse Public License v. 2.0, which is available at
18
18
19
19
import jakarta .ws .rs .SeBootstrap ;
20
20
import jakarta .ws .rs .core .UriBuilder ;
21
+ import org .glassfish .jersey .internal .config .ExternalPropertiesConfigurationFactory ;
22
+ import org .glassfish .jersey .internal .config .SystemPropertiesConfigurationModel ;
23
+ import org .glassfish .jersey .internal .util .PropertiesClass ;
21
24
import org .glassfish .jersey .server .internal .LocalizationMessages ;
22
25
import org .glassfish .jersey .server .spi .Container ;
23
26
import org .glassfish .jersey .server .spi .WebServer ;
24
27
25
28
import javax .net .ssl .SSLContext ;
29
+ import java .io .IOException ;
30
+ import java .net .ServerSocket ;
26
31
import java .net .URI ;
27
32
import java .security .NoSuchAlgorithmException ;
33
+ import java .util .Collections ;
28
34
import java .util .HashMap ;
29
35
import java .util .Map ;
30
36
import java .util .Optional ;
37
+ import java .util .Random ;
31
38
import java .util .function .BiFunction ;
32
39
import java .util .logging .Logger ;
33
40
41
+ import static java .lang .Boolean .FALSE ;
34
42
import static java .lang .Boolean .TRUE ;
35
43
36
44
/**
40
48
*/
41
49
public final class JerseySeBootstrapConfiguration implements SeBootstrap .Configuration {
42
50
private static final Logger LOGGER = Logger .getLogger (JerseySeBootstrapConfiguration .class .getName ());
51
+ protected static final Random RANDOM = new Random ();
43
52
private final SeBootstrap .Configuration configuration ;
44
53
45
54
private JerseySeBootstrapConfiguration (SeBootstrap .Configuration configuration ) {
@@ -61,16 +70,60 @@ public Object property(String name) {
61
70
public URI uri (boolean resolveDefaultPort ) {
62
71
final String protocol = configuration .protocol ();
63
72
final String host = configuration .host ();
64
- final int configPort = configuration .port ();
65
- final int port = (configPort < 0 && resolveDefaultPort )
66
- ? isHttps () ? Container .DEFAULT_HTTPS_PORT : Container .DEFAULT_HTTP_PORT
67
- : configPort ;
73
+ final int port = resolveDefaultPort ? resolvePort () : configuration .port ();
68
74
final String rootPath = configuration .rootPath ();
69
75
final URI uri = UriBuilder .newInstance ().scheme (protocol .toLowerCase ()).host (host ).port (port ).path (rootPath )
70
76
.build ();
71
77
return uri ;
72
78
}
73
79
80
+ private int resolvePort () {
81
+ final int configPort = configuration .port ();
82
+ final int basePort = allowPrivilegedPorts () ? 0 : 8000 ;
83
+ final int port ;
84
+ switch (configPort ) {
85
+ case SeBootstrap .Configuration .DEFAULT_PORT :
86
+ port = basePort + (isHttps () ? Container .DEFAULT_HTTPS_PORT : Container .DEFAULT_HTTP_PORT );
87
+ break ;
88
+ case SeBootstrap .Configuration .FREE_PORT :
89
+ port = _resolvePort (basePort == 0 );
90
+ break ;
91
+ default :
92
+ port = configPort ;
93
+ break ;
94
+ }
95
+ return port ;
96
+ }
97
+
98
+ private int _resolvePort (boolean allowPrivilegedPort ) {
99
+ final int basePort = allowPrivilegedPort ? 0 : 1023 ;
100
+ // Get the initial range parameters
101
+ final int lower = basePort ;
102
+ final int range = 0xFFFF ;
103
+
104
+ // Select a start point in the range
105
+ final int initialOffset = RANDOM .nextInt (range - lower );
106
+
107
+ // Loop the offset through all ports in the range and attempt
108
+ // to bind to each
109
+ int offset = initialOffset ;
110
+ ServerSocket socket ;
111
+ do {
112
+ final int port = lower + offset ;
113
+ try {
114
+ socket = new ServerSocket (port );
115
+ socket .close ();
116
+ return port ;
117
+ } catch (IOException caught ) {
118
+ // Swallow exceptions until the end
119
+ }
120
+ offset = (offset + 1 ) % range ;
121
+ } while (offset != initialOffset );
122
+
123
+ // If a port can't be bound, throw the exception
124
+ throw new IllegalArgumentException (LocalizationMessages .COULD_NOT_BIND_TO_ANY_PORT ());
125
+ }
126
+
74
127
/**
75
128
* Return {@link SSLContext} in the configuration if the protocol scheme is {@code HTTPS}.
76
129
* @return the SSLContext in the configuration.
@@ -100,6 +153,16 @@ public boolean autoStart() {
100
153
return autoStart ;
101
154
}
102
155
156
+ /**
157
+ * Defines if the {@link WebServer} should start on a privileged port when port is not set.
158
+ * @return true if {@link ServerProperties#WEBSERVER_AUTO_START} is {@code true}, {@code false} otherwise.
159
+ */
160
+ public boolean allowPrivilegedPorts () {
161
+ return Optional .ofNullable (
162
+ (Boolean ) configuration .property (ServerProperties .WEBSERVER_ALLOW_PRIVILEGED_PORTS ))
163
+ .orElse (FALSE );
164
+ }
165
+
103
166
/**
104
167
* Factory method creating {@code JerseySeBootstrapConfiguration} wrapper around {@link SeBootstrap.Configuration}.
105
168
* @param configuration wrapped configuration
@@ -129,16 +192,17 @@ public static final class Builder implements SeBootstrap.Configuration.Builder {
129
192
PROPERTY_TYPES .put (SeBootstrap .Configuration .ROOT_PATH , String .class );
130
193
PROPERTY_TYPES .put (SeBootstrap .Configuration .SSL_CONTEXT , SSLContext .class );
131
194
PROPERTY_TYPES .put (SeBootstrap .Configuration .SSL_CLIENT_AUTHENTICATION , SSLClientAuthentication .class );
132
- PROPERTY_TYPES .put (ServerProperties .WEBSERVER_CLASS , Class .class );
195
+ PROPERTY_TYPES .put (ServerProperties .WEBSERVER_ALLOW_PRIVILEGED_PORTS , Boolean .class );
133
196
PROPERTY_TYPES .put (ServerProperties .WEBSERVER_AUTO_START , Boolean .class );
197
+ PROPERTY_TYPES .put (ServerProperties .WEBSERVER_CLASS , Class .class );
134
198
}
135
199
136
200
private final Map <String , Object > properties = new HashMap <>();
137
201
138
202
private Builder () {
139
203
this .properties .put (SeBootstrap .Configuration .PROTOCOL , "HTTP" ); // upper case mandated by javadoc
140
204
this .properties .put (SeBootstrap .Configuration .HOST , "localhost" );
141
- this .properties .put (SeBootstrap .Configuration .PORT , -1 ); // Auto-select port 80 for HTTP or 443 for HTTPS
205
+ this .properties .put (SeBootstrap .Configuration .PORT , -1 ); // Auto-select port 8080 for HTTP or 8443 for HTTPS
142
206
this .properties .put (SeBootstrap .Configuration .ROOT_PATH , "/" );
143
207
this .properties .put (ServerProperties .WEBSERVER_CLASS , WebServer .class ); // Auto-select first provider
144
208
try {
@@ -149,6 +213,15 @@ private Builder() {
149
213
this .properties .put (SeBootstrap .Configuration .SSL_CLIENT_AUTHENTICATION ,
150
214
SeBootstrap .Configuration .SSLClientAuthentication .NONE );
151
215
this .properties .put (ServerProperties .WEBSERVER_AUTO_START , TRUE );
216
+ this .properties .put (ServerProperties .WEBSERVER_ALLOW_PRIVILEGED_PORTS , FALSE );
217
+
218
+ SystemPropertiesConfigurationModel propertiesConfigurationModel = new SystemPropertiesConfigurationModel (
219
+ Collections .singletonList (Properties .class .getName ())
220
+ );
221
+ from ((name , aClass ) -> String .class .equals (aClass ) || Integer .class .equals (aClass ) || Boolean .class .equals (aClass )
222
+ ? propertiesConfigurationModel .getOptionalProperty (name , aClass )
223
+ : Optional .empty ()
224
+ );
152
225
}
153
226
154
227
@ Override
@@ -208,4 +281,41 @@ public JerseySeBootstrapConfiguration.Builder from(Object externalConfig) {
208
281
return this ;
209
282
}
210
283
}
284
+
285
+ /**
286
+ * Name the properties to be internally read from System properties by {@link ExternalPropertiesConfigurationFactory}.
287
+ * This is required just when SecurityManager is on, otherwise all system properties are read.
288
+ */
289
+ @ PropertiesClass
290
+ private static class Properties {
291
+ /**
292
+ * See {@link SeBootstrap.Configuration#PROTOCOL} property.
293
+ */
294
+ public static final String SE_BOOTSTRAP_CONFIGURATION_PROTOCOL = SeBootstrap .Configuration .PROTOCOL ;
295
+
296
+ /**
297
+ * See {@link SeBootstrap.Configuration#HOST} property.
298
+ */
299
+ public static final String SE_BOOTSTRAP_CONFIGURATION_HOST = SeBootstrap .Configuration .HOST ;
300
+
301
+ /**
302
+ * See {@link SeBootstrap.Configuration#PORT} property.
303
+ */
304
+ public static final String SE_BOOTSTRAP_CONFIGURATION_PORT = SeBootstrap .Configuration .PORT ;
305
+
306
+ /**
307
+ * See {@link SeBootstrap.Configuration#ROOT_PATH} property.
308
+ */
309
+ public static final String SE_BOOTSTRAP_CONFIGURATION_ROOT_PATH = SeBootstrap .Configuration .ROOT_PATH ;
310
+
311
+ /**
312
+ * See {@link ServerProperties#WEBSERVER_ALLOW_PRIVILEGED_PORTS} property.
313
+ */
314
+ public static final String WEBSERVER_ALLOW_PRIVILEGED_PORTS = ServerProperties .WEBSERVER_ALLOW_PRIVILEGED_PORTS ;
315
+
316
+ /**
317
+ * See {@link ServerProperties#WEBSERVER_AUTO_START} property.
318
+ */
319
+ public static final String WEBSERVER_AUTO_START = ServerProperties .WEBSERVER_AUTO_START ;
320
+ }
211
321
}
0 commit comments