11/* 
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. 
33 * 
44 * This program and the accompanying materials are made available under the 
55 * terms of the Eclipse Public License v. 2.0, which is available at 
1818
1919import  jakarta .ws .rs .SeBootstrap ;
2020import  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 ;
2124import  org .glassfish .jersey .server .internal .LocalizationMessages ;
2225import  org .glassfish .jersey .server .spi .Container ;
2326import  org .glassfish .jersey .server .spi .WebServer ;
2427
2528import  javax .net .ssl .SSLContext ;
29+ import  java .io .IOException ;
30+ import  java .net .ServerSocket ;
2631import  java .net .URI ;
2732import  java .security .NoSuchAlgorithmException ;
33+ import  java .util .Collections ;
2834import  java .util .HashMap ;
2935import  java .util .Map ;
3036import  java .util .Optional ;
37+ import  java .util .Random ;
3138import  java .util .function .BiFunction ;
3239import  java .util .logging .Logger ;
3340
41+ import  static  java .lang .Boolean .FALSE ;
3442import  static  java .lang .Boolean .TRUE ;
3543
3644/** 
4048 */ 
4149public  final  class  JerseySeBootstrapConfiguration  implements  SeBootstrap .Configuration  {
4250    private  static  final  Logger  LOGGER  = Logger .getLogger (JerseySeBootstrapConfiguration .class .getName ());
51+     protected  static  final  Random  RANDOM  = new  Random ();
4352    private  final  SeBootstrap .Configuration  configuration ;
4453
4554    private  JerseySeBootstrapConfiguration (SeBootstrap .Configuration  configuration ) {
@@ -61,16 +70,60 @@ public Object property(String name) {
6170    public  URI  uri (boolean  resolveDefaultPort ) {
6271        final  String  protocol  = configuration .protocol ();
6372        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 ();
6874        final  String  rootPath  = configuration .rootPath ();
6975        final  URI  uri  = UriBuilder .newInstance ().scheme (protocol .toLowerCase ()).host (host ).port (port ).path (rootPath )
7076                .build ();
7177        return  uri ;
7278    }
7379
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+ 
74127    /** 
75128     * Return {@link SSLContext} in the configuration if the protocol scheme is {@code HTTPS}. 
76129     * @return the SSLContext in the configuration. 
@@ -100,6 +153,16 @@ public boolean autoStart() {
100153        return  autoStart ;
101154    }
102155
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+ 
103166    /** 
104167     * Factory method creating {@code JerseySeBootstrapConfiguration} wrapper around {@link SeBootstrap.Configuration}. 
105168     * @param configuration wrapped configuration 
@@ -129,16 +192,17 @@ public static final class Builder implements SeBootstrap.Configuration.Builder {
129192            PROPERTY_TYPES .put (SeBootstrap .Configuration .ROOT_PATH , String .class );
130193            PROPERTY_TYPES .put (SeBootstrap .Configuration .SSL_CONTEXT , SSLContext .class );
131194            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 );
133196            PROPERTY_TYPES .put (ServerProperties .WEBSERVER_AUTO_START , Boolean .class );
197+             PROPERTY_TYPES .put (ServerProperties .WEBSERVER_CLASS , Class .class );
134198        }
135199
136200        private  final  Map <String , Object > properties  = new  HashMap <>();
137201
138202        private  Builder () {
139203            this .properties .put (SeBootstrap .Configuration .PROTOCOL , "HTTP" ); // upper case mandated by javadoc 
140204            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 
142206            this .properties .put (SeBootstrap .Configuration .ROOT_PATH , "/" );
143207            this .properties .put (ServerProperties .WEBSERVER_CLASS , WebServer .class ); // Auto-select first provider 
144208            try  {
@@ -149,6 +213,15 @@ private Builder() {
149213            this .properties .put (SeBootstrap .Configuration .SSL_CLIENT_AUTHENTICATION ,
150214                    SeBootstrap .Configuration .SSLClientAuthentication .NONE );
151215            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+             );
152225        }
153226
154227        @ Override 
@@ -208,4 +281,41 @@ public JerseySeBootstrapConfiguration.Builder from(Object externalConfig) {
208281            return  this ;
209282        }
210283    }
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+     }
211321}
0 commit comments