2424import java .io .InputStream ;
2525import java .lang .management .ManagementFactory ;
2626import java .net .URL ;
27+ import java .nio .file .Files ;
28+ import java .nio .file .Path ;
29+ import java .nio .file .Paths ;
2730import java .util .Arrays ;
31+ import java .util .EnumSet ;
32+ import java .util .List ;
2833import java .util .Properties ;
2934
30- import com .cloud .api .ApiServer ;
35+ import javax .servlet .DispatcherType ;
36+
37+ import org .apache .cloudstack .utils .server .ServerPropertiesUtil ;
3138import org .apache .commons .daemon .Daemon ;
3239import org .apache .commons .daemon .DaemonContext ;
3340import org .apache .commons .lang3 .StringUtils ;
41+ import org .apache .logging .log4j .LogManager ;
42+ import org .apache .logging .log4j .Logger ;
3443import org .eclipse .jetty .jmx .MBeanContainer ;
3544import org .eclipse .jetty .server .ForwardedRequestCustomizer ;
45+ import org .eclipse .jetty .server .Handler ;
3646import org .eclipse .jetty .server .HttpConfiguration ;
3747import org .eclipse .jetty .server .HttpConnectionFactory ;
3848import org .eclipse .jetty .server .RequestLog ;
4656import org .eclipse .jetty .server .handler .RequestLogHandler ;
4757import org .eclipse .jetty .server .handler .gzip .GzipHandler ;
4858import org .eclipse .jetty .server .session .SessionHandler ;
59+ import org .eclipse .jetty .servlet .DefaultServlet ;
60+ import org .eclipse .jetty .servlet .FilterHolder ;
61+ import org .eclipse .jetty .servlet .ServletContextHandler ;
62+ import org .eclipse .jetty .servlet .ServletHolder ;
63+ import org .eclipse .jetty .util .resource .Resource ;
4964import org .eclipse .jetty .util .ssl .KeyStoreScanner ;
5065import org .eclipse .jetty .util .ssl .SslContextFactory ;
5166import org .eclipse .jetty .util .thread .QueuedThreadPool ;
5267import org .eclipse .jetty .util .thread .ScheduledExecutorScheduler ;
5368import org .eclipse .jetty .webapp .WebAppContext ;
54- import org .apache .logging .log4j .Logger ;
55- import org .apache .logging .log4j .LogManager ;
5669
70+ import com .cloud .api .ApiServer ;
5771import com .cloud .utils .Pair ;
5872import com .cloud .utils .PropertiesUtil ;
5973import com .cloud .utils .server .ServerProperties ;
@@ -111,6 +125,12 @@ public class ServerDaemon implements Daemon {
111125 private int minThreads ;
112126 private int maxThreads ;
113127
128+ private boolean shareEnabled = false ;
129+ private String shareBaseDir ;
130+ private String shareCacheCtl ;
131+ private boolean shareDirList = false ;
132+ private String shareSecret ;
133+
114134 //////////////////////////////////////////////////
115135 /////////////// Public methods ///////////////////
116136 //////////////////////////////////////////////////
@@ -121,6 +141,14 @@ public static void main(final String... anArgs) throws Exception {
121141 daemon .start ();
122142 }
123143
144+ protected void initShareConfigFromProperties () {
145+ setShareEnabled (ServerPropertiesUtil .getShareEnabled ());
146+ setShareBaseDir (ServerPropertiesUtil .getShareBaseDirectory ());
147+ setShareCacheCtl (ServerPropertiesUtil .getShareCacheControl ());
148+ setShareDirList (ServerPropertiesUtil .getShareDirAllowed ());
149+ setShareSecret (ServerPropertiesUtil .getShareSecret ());
150+ }
151+
124152 @ Override
125153 public void init (final DaemonContext context ) {
126154 final File confFile = PropertiesUtil .findConfigFile ("server.properties" );
@@ -153,6 +181,7 @@ public void init(final DaemonContext context) {
153181 setMaxFormKeys (Integer .valueOf (properties .getProperty (REQUEST_MAX_FORM_KEYS_KEY , String .valueOf (DEFAULT_REQUEST_MAX_FORM_KEYS ))));
154182 setMinThreads (Integer .valueOf (properties .getProperty (THREADS_MIN , "10" )));
155183 setMaxThreads (Integer .valueOf (properties .getProperty (THREADS_MAX , "500" )));
184+ initShareConfigFromProperties ();
156185 } catch (final IOException e ) {
157186 logger .warn ("Failed to read configuration from server.properties file" , e );
158187 } finally {
@@ -288,6 +317,52 @@ private void createHttpsConnector(final HttpConfiguration httpConfig) {
288317 }
289318 }
290319
320+ /**
321+ * Creates a Jetty context at /share to serve static files for modules (e.g. Extensions Framework).
322+ * Controlled via server properties
323+ *
324+ * @return a configured Handler or null if disabled.
325+ */
326+ private Handler createShareContextHandler () throws IOException {
327+ if (!shareEnabled ) {
328+ logger .info ("/{} context not mounted" , ServerPropertiesUtil .SHARE_DIR );
329+ return null ;
330+ }
331+
332+ final Path base = Paths .get (shareBaseDir );
333+ Files .createDirectories (base );
334+
335+ final ServletContextHandler shareCtx = new ServletContextHandler (ServletContextHandler .NO_SESSIONS );
336+ shareCtx .setContextPath ("/" + ServerPropertiesUtil .SHARE_DIR );
337+ shareCtx .setBaseResource (Resource .newResource (base .toAbsolutePath ().toUri ()));
338+
339+ // Efficient static file serving
340+ ServletHolder def = shareCtx .addServlet (DefaultServlet .class , "/*" );
341+ def .setInitParameter ("dirAllowed" , Boolean .toString (shareDirList ));
342+ def .setInitParameter ("etags" , "true" );
343+ def .setInitParameter ("cacheControl" , shareCacheCtl );
344+ def .setInitParameter ("useFileMappedBuffer" , "true" );
345+ def .setInitParameter ("acceptRanges" , "true" );
346+
347+ // Gzip using modern Jetty handler
348+ org .eclipse .jetty .server .handler .gzip .GzipHandler gzipHandler =
349+ new org .eclipse .jetty .server .handler .gzip .GzipHandler ();
350+ gzipHandler .setMinGzipSize (1024 );
351+ gzipHandler .setIncludedMimeTypes (
352+ "text/html" , "text/plain" , "text/css" , "text/javascript" ,
353+ "application/javascript" , "application/json" , "application/xml" );
354+ gzipHandler .setHandler (shareCtx );
355+
356+ // Optional signed-URL guard (path + "|" + exp => HMAC-SHA256, base64url)
357+ if (StringUtils .isNotBlank (shareSecret )) {
358+ shareCtx .addFilter (new FilterHolder (new ShareSignedUrlFilter (true , shareSecret )),
359+ "/*" , EnumSet .of (DispatcherType .REQUEST ));
360+ }
361+
362+ logger .info ("Mounted /{} static context at baseDir={}" , ServerPropertiesUtil .SHARE_DIR , base );
363+ return shareCtx ;
364+ }
365+
291366 private Pair <SessionHandler ,HandlerCollection > createHandlers () {
292367 final WebAppContext webApp = new WebAppContext ();
293368 webApp .setContextPath (contextPath );
@@ -318,8 +393,23 @@ private Pair<SessionHandler,HandlerCollection> createHandlers() {
318393 rootRedirect .setNewContextURL (contextPath );
319394 rootRedirect .setPermanent (true );
320395
396+ // Optional /share handler (served by createShareContextHandler)
397+ Handler shareHandler = null ;
398+ try {
399+ shareHandler = createShareContextHandler ();
400+ } catch (IOException e ) {
401+ logger .error ("Failed to initialize /share context" , e );
402+ }
403+
404+ List <Handler > handlers = new java .util .ArrayList <>();
405+ handlers .add (log );
406+ handlers .add (gzipHandler );
407+ if (shareHandler != null ) {
408+ handlers .add (shareHandler );
409+ }
321410 // Put rootRedirect at the end!
322- return new Pair <>(webApp .getSessionHandler (), new HandlerCollection (log , gzipHandler , rootRedirect ));
411+ handlers .add (rootRedirect );
412+ return new Pair <>(webApp .getSessionHandler (), new HandlerCollection (handlers .toArray (new Handler [0 ])));
323413 }
324414
325415 private RequestLog createRequestLog () {
@@ -408,4 +498,24 @@ public void setMinThreads(int minThreads) {
408498 public void setMaxThreads (int maxThreads ) {
409499 this .maxThreads = maxThreads ;
410500 }
501+
502+ public void setShareEnabled (boolean shareEnabled ) {
503+ this .shareEnabled = shareEnabled ;
504+ }
505+
506+ public void setShareBaseDir (String shareBaseDir ) {
507+ this .shareBaseDir = shareBaseDir ;
508+ }
509+
510+ public void setShareCacheCtl (String shareCacheCtl ) {
511+ this .shareCacheCtl = shareCacheCtl ;
512+ }
513+
514+ public void setShareDirList (boolean shareDirList ) {
515+ this .shareDirList = shareDirList ;
516+ }
517+
518+ public void setShareSecret (String shareSecret ) {
519+ this .shareSecret = shareSecret ;
520+ }
411521}
0 commit comments