Skip to content

Commit

Permalink
Support CORS filter and more Tomcat connector properties
Browse files Browse the repository at this point in the history
  • Loading branch information
labkey-jeckels committed Jun 24, 2024
1 parent c1088e3 commit 5fa6b5a
Show file tree
Hide file tree
Showing 2 changed files with 199 additions and 20 deletions.
166 changes: 151 additions & 15 deletions server/embedded/src/org/labkey/embedded/LabKeyServer.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ public class LabKeyServer
public static final String SERVER_GUID_PARAMETER_NAME = "org.labkey.mothership." + SERVER_GUID;
public static final String SERVER_SSL_KEYSTORE = "org.labkey.serverSslKeystore";
public static final String CUSTOM_LOG4J_CONFIG = "org.labkey.customLog4JConfig";
public static final String CORS_PREFIX = "cors.";
static final String MAX_TOTAL_CONNECTIONS_DEFAULT = "50";
static final String MAX_IDLE_DEFAULT = "10";
static final String MAX_WAIT_MILLIS_DEFAULT = "120000";
Expand Down Expand Up @@ -183,9 +184,9 @@ public void setConditionUnless(String conditionUnless)
}

@Bean
public ManagementProperties managementSource()
public ManagementServerProperties managementServerSource()
{
return new ManagementProperties();
return new ManagementServerProperties();
}

@Bean
Expand All @@ -194,40 +195,175 @@ public LoggingProperties loggingSource()
return new LoggingProperties();
}

@Bean
public TomcatProperties tomcatProperties()
{
return new TomcatProperties();
}

/**
* This lets us snoop on the Spring Boot config for deploying the management endpoint on a different port, as
* we don't want to deploy LK on that port
*/
@Configuration
@ConfigurationProperties("management")
public static class ManagementProperties
@ConfigurationProperties("management.server")
public static class ManagementServerProperties
{
private ServerProperties _server;
private int _port;

public ServerProperties getServer()
public int getPort()
{
return _server;
return _port;
}

public void setServer(ServerProperties server)
public void setPort(int port)
{
_server = server;
_port = port;
}
}

public static class ServerProperties
/** Values that we'll propagate to org.apache.catalina.filters.CorsFilter */
public static class CorsProperties
{
private int _port;
private String _allowedOrigins;
private String _allowedMethods;
private String _allowedHeaders;
private String _exposedHeaders;
private String _supportCredentials;
private String _urlPattern;
private String _preflightMaxAge;
private String _requestDecorate;

public int getPort()
public String getAllowedOrigins()
{
return _port;
return _allowedOrigins;
}

public void setPort(int port)
public void setAllowedOrigins(String allowedOrigins)
{
_port = port;
_allowedOrigins = allowedOrigins;
}

public String getAllowedMethods()
{
return _allowedMethods;
}

public void setAllowedMethods(String allowedMethods)
{
_allowedMethods = allowedMethods;
}

public String getAllowedHeaders()
{
return _allowedHeaders;
}

public void setAllowedHeaders(String allowedHeaders)
{
_allowedHeaders = allowedHeaders;
}

public String getExposedHeaders()
{
return _exposedHeaders;
}

public void setExposedHeaders(String exposedHeaders)
{
_exposedHeaders = exposedHeaders;
}

public String getSupportCredentials()
{
return _supportCredentials;
}

public void setSupportCredentials(String supportCredentials)
{
_supportCredentials = supportCredentials;
}

public String getUrlPattern()
{
return _urlPattern;
}

public void setUrlPattern(String urlPattern)
{
_urlPattern = urlPattern;
}

public String getPreflightMaxAge()
{
return _preflightMaxAge;
}

public void setPreflightMaxAge(String preflightMaxAge)
{
_preflightMaxAge = preflightMaxAge;
}

public String getRequestDecorate()
{
return _requestDecorate;
}

public void setRequestDecorate(String requestDecorate)
{
_requestDecorate = requestDecorate;
}
}

/** Add some properties that Spring Boot doesn't support setting. See issue 50690 */
@Configuration
@ConfigurationProperties("server.tomcat")
public static class TomcatProperties
{
private Boolean _useSendfile;
private Boolean _disableUploadTimeout;
private Boolean _useBodyEncodingForURI;

private CorsProperties _cors;

public Boolean getUseSendfile()
{
return _useSendfile;
}

public void setUseSendfile(Boolean useSendfile)
{
_useSendfile = useSendfile;
}

public Boolean getDisableUploadTimeout()
{
return _disableUploadTimeout;
}

public void setDisableUploadTimeout(Boolean disableUploadTimeout)
{
_disableUploadTimeout = disableUploadTimeout;
}

public Boolean getUseBodyEncodingForURI()
{
return _useBodyEncodingForURI;
}

public void setUseBodyEncodingForURI(Boolean useBodyEncodingForURI)
{
_useBodyEncodingForURI = useBodyEncodingForURI;
}

public CorsProperties getCors()
{
return _cors;
}

public void setCors(CorsProperties cors)
{
_cors = cors;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import org.apache.catalina.valves.JsonAccessLogValve;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.coyote.http11.AbstractHttp11Protocol;
import org.apache.tomcat.util.descriptor.web.ContextResource;
import org.labkey.bootstrap.ConfigException;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
Expand All @@ -19,6 +20,7 @@
import java.util.Map;
import java.util.Objects;

import static org.labkey.embedded.LabKeyServer.CORS_PREFIX;
import static org.labkey.embedded.LabKeyServer.CUSTOM_LOG4J_CONFIG;
import static org.labkey.embedded.LabKeyServer.SERVER_GUID_PARAMETER_NAME;
import static org.labkey.embedded.LabKeyServer.SERVER_SSL_KEYSTORE;
Expand All @@ -31,6 +33,27 @@ class LabKeyTomcatServletWebServerFactory extends TomcatServletWebServerFactory
public LabKeyTomcatServletWebServerFactory(LabKeyServer server)
{
_server = server;

addConnectorCustomizers(connector -> {
LabKeyServer.TomcatProperties props = _server.tomcatProperties();

if (props.getUseBodyEncodingForURI() != null)
{
connector.setUseBodyEncodingForURI(props.getUseBodyEncodingForURI());
}

if (connector.getProtocolHandler() instanceof AbstractHttp11Protocol<?> handler)
{
if (props.getDisableUploadTimeout() != null)
{
handler.setDisableUploadTimeout(props.getDisableUploadTimeout());
}
if (props.getUseSendfile() != null)
{
handler.setUseSendfile(props.getUseSendfile());
}
}
});
}

@Override
Expand All @@ -48,10 +71,10 @@ protected void prepareContext(Host host, ServletContextInitializer[] initializer
@Override
protected TomcatWebServer getTomcatWebServer(Tomcat tomcat)
{
LabKeyServer.ManagementProperties props = _server.managementSource();
LabKeyServer.ManagementServerProperties props = _server.managementServerSource();

// Don't deploy LK webapp on the separate instance running on the management port
if (props == null || props.getServer() == null || props.getServer().getPort() != getPort())
if (props == null || props.getPort() != getPort())
{
tomcat.enableNaming();

Expand Down Expand Up @@ -164,10 +187,22 @@ protected TomcatWebServer getTomcatWebServer(Tomcat tomcat)
}
context.addParameter(CUSTOM_LOG4J_CONFIG, Boolean.toString(customLog4J));

// Add serverGUID for mothership - it tells mothership that 2 instances of a server should be considered the same for metrics gathering purposes.
if (null != contextProperties.getServerGUID())
// Add serverGUID for mothership - it tells mothership that 2 instances of a server should be considered the same for metrics gathering purposes
addContextProperty(context, contextProperties.getServerGUID(), SERVER_GUID_PARAMETER_NAME);

LabKeyServer.TomcatProperties tomcatProperties = _server.tomcatProperties();
if (tomcatProperties != null && tomcatProperties.getCors() != null)
{
context.addParameter(SERVER_GUID_PARAMETER_NAME, contextProperties.getServerGUID());
// Push these into the context so that ApiModule can register Tomcat's CorsFilter as desired
LabKeyServer.CorsProperties corsProperties = tomcatProperties.getCors();
addContextProperty(context, corsProperties.getAllowedOrigins(), CORS_PREFIX + "allowedOrigins");
addContextProperty(context, corsProperties.getAllowedMethods(), CORS_PREFIX + "allowedMethods");
addContextProperty(context, corsProperties.getAllowedHeaders(), CORS_PREFIX + "allowedHeaders");
addContextProperty(context, corsProperties.getExposedHeaders(), CORS_PREFIX + "exposedHeaders");
addContextProperty(context, corsProperties.getSupportCredentials(), CORS_PREFIX + "supportCredentials");
addContextProperty(context, corsProperties.getUrlPattern(), CORS_PREFIX + "urlPattern");
addContextProperty(context, corsProperties.getPreflightMaxAge(), CORS_PREFIX + "preflightMaxAge");
addContextProperty(context, corsProperties.getRequestDecorate(), CORS_PREFIX + "requestDecorate");
}

LabKeyServer.ServerSslProperties sslProps = _server.serverSslSource();
Expand Down Expand Up @@ -220,6 +255,14 @@ protected TomcatWebServer getTomcatWebServer(Tomcat tomcat)
return super.getTomcatWebServer(tomcat);
}

private void addContextProperty(StandardContext context, String value, String name)
{
if (null != value)
{
context.addParameter(name, value);
}
}

// Issue 48565: allow for JSON-formatted access logs in embedded tomcat
private void configureJsonAccessLogging(Tomcat tomcat, LabKeyServer.JsonAccessLog logConfig)
{
Expand Down

0 comments on commit 5fa6b5a

Please sign in to comment.