blob: fdfe297ab2eb39ef863c5971bc82d341de35a17b [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2018 Obeo.
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Obeo - initial API and implementation
*******************************************************************************/
package org.eclipse.sirius.server.internal;
import java.io.File;
import java.util.EnumSet;
import java.util.List;
import java.util.stream.Collectors;
import javax.servlet.DispatcherType;
import javax.servlet.ServletException;
import javax.servlet.SessionCookieConfig;
import javax.websocket.DeploymentException;
import javax.websocket.server.ServerContainer;
import javax.websocket.server.ServerEndpointConfig;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.HttpConfiguration;
import org.eclipse.jetty.server.HttpConnectionFactory;
import org.eclipse.jetty.server.SecureRequestCustomizer;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.server.SslConnectionFactory;
import org.eclipse.jetty.server.handler.HandlerCollection;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.eclipse.jetty.websocket.jsr356.server.deploy.WebSocketServerContainerInitializer;
import org.eclipse.sirius.server.api.ISiriusServerConfigurator;
import org.eclipse.sirius.server.api.ISiriusServerEndpointConfigurationProvider;
/**
* The entry point of the back-end used to configure the Sirius server.
*
* @author sbegaudeau
*/
public class SiriusServerConfigurator implements ISiriusServerConfigurator {
/**
* The constant used to determine whether ssl is activated or not.
*/
private static final String HTTPS_ENABLED = "org.eclipse.sirius.server.https.enabled"; //$NON-NLS-1$
/**
* The constant used to customize the https host.
*/
private static final String HTTPS_HOST = "org.eclipse.sirius.server.https.host"; //$NON-NLS-1$
/**
* The constant used to customize the https port.
*/
private static final String HTTPS_PORT = "org.eclipse.sirius.server.https.port"; //$NON-NLS-1$
/**
* The constant used to give the location of the key store for ssl
* authentication.
*/
private static final String SSL_KEYSTORE_PATH = "org.eclipse.sirius.server.ssl.keystore.path"; //$NON-NLS-1$
/**
* The constant used to give the pass phrase for ssl authentication.
*/
private static final String SSL_KEYSTORE_PASSPHRASE = "org.eclipse.sirius.server.ssl.keystore.passphrase"; //$NON-NLS-1$
/**
* The constant used to customize the http host.
*/
private static final String HTTP_HOST = "org.eclipse.sirius.server.http.host"; //$NON-NLS-1$
/**
* The constant used to customize the http port.
*/
private static final String HTTP_PORT = "org.eclipse.sirius.server.http.port"; //$NON-NLS-1$
/**
* The default hostname.
*/
private static final String DEFAULT_HOSTNAME = "0.0.0.0"; //$NON-NLS-1$
/**
* The default http port.
*/
private static final int DEFAULT_HTTP_PORT = 0;
/**
* The default https port.
*/
private static final int DEFAULT_HTTPS_PORT = 0;
/**
* The context path of the Sirius HTTP API.
*/
private static final String HTTP_API_CONTEXT_PATH = "/api"; //$NON-NLS-1$
/**
* The context path of the Sirius WS API.
*/
private static final String WS_API_CONTEXT_PATH = "/ws"; //$NON-NLS-1$
/**
* The context path of all requests.
*/
private static final String ALL_PATH = "/*"; //$NON-NLS-1$
/**
* {@inheritDoc}
*
* @see org.eclipse.sirius.server.api.ISiriusServerConfigurator#configure(org.eclipse.sirius.server.api.Server)
*/
@Override
public void configure(Server server) {
boolean httpsEnabled = Boolean.parseBoolean(System.getProperty(HTTPS_ENABLED));
Connector connector = null;
if (httpsEnabled) {
connector = createHttpsConnector(server);
} else {
connector = createHttpConnector(server);
}
server.addConnector(connector);
ServletContextHandler httpAPIServletContextHandler = new ServletContextHandler(ServletContextHandler.SESSIONS | ServletContextHandler.GZIP);
httpAPIServletContextHandler.setContextPath(HTTP_API_CONTEXT_PATH);
httpAPIServletContextHandler.setErrorHandler(new SiriusServerErrorHandler());
SessionCookieConfig sessionCookieConfig = httpAPIServletContextHandler.getServletContext().getSessionCookieConfig();
sessionCookieConfig.setHttpOnly(true);
httpAPIServletContextHandler.addFilter(SiriusServerFilter.class, ALL_PATH, EnumSet.of(DispatcherType.REQUEST, DispatcherType.INCLUDE));
ServletContextHandler wsAPIServletContextHandler = new ServletContextHandler(ServletContextHandler.SESSIONS | ServletContextHandler.GZIP);
wsAPIServletContextHandler.setContextPath(WS_API_CONTEXT_PATH);
Handler handler = server.getHandler();
if (handler instanceof HandlerCollection) {
HandlerCollection handlerCollection = (HandlerCollection) handler;
handlerCollection.addHandler(httpAPIServletContextHandler);
handlerCollection.addHandler(wsAPIServletContextHandler);
}
try {
ServerContainer container = WebSocketServerContainerInitializer.configureContext(wsAPIServletContextHandler);
container.setDefaultMaxSessionIdleTimeout(0);
List<ISiriusServerEndpointConfigurationProvider> providers = SiriusServerPlugin.getPlugin().getEndpointConfigurationProviders();
List<ServerEndpointConfig> endpointConfigurations = providers.stream().map(ISiriusServerEndpointConfigurationProvider::getEndpointConfiguration).collect(Collectors.toList());
for (ServerEndpointConfig endpointConfiguration : endpointConfigurations) {
container.addEndpoint(endpointConfiguration);
}
} catch (DeploymentException | ServletException e) {
IStatus status = new Status(IStatus.ERROR, SiriusServerPlugin.PLUGIN_ID, e.getMessage(), e);
SiriusServerPlugin.INSTANCE.log(status);
}
}
/**
* Creates the http connector if https has been disabled using system
* properties.
*
* @param server
* The jetty server
* @return The created http connector
*/
private Connector createHttpConnector(Server server) {
HttpConfiguration httpConfiguration = new HttpConfiguration();
httpConfiguration.setSendServerVersion(false);
HttpConnectionFactory httpConnectionFactory = new HttpConnectionFactory(httpConfiguration);
ServerConnector serverConnector = new ServerConnector(server, httpConnectionFactory);
serverConnector.setHost(System.getProperty(HTTP_HOST, DEFAULT_HOSTNAME));
serverConnector.setPort(handleSystemPropertiesIntegerValue(HTTP_PORT, DEFAULT_HTTP_PORT));
return serverConnector;
}
/**
* Creates the https connector if https has been enabled using system
* properties.
*
* @param server
* The jetty server.
* @return The created https connector
*/
private Connector createHttpsConnector(Server server) {
SslContextFactory sslContextFactory = new SslContextFactory();
File file = new File(System.getProperty(SSL_KEYSTORE_PATH));
file.exists();
sslContextFactory.setKeyStorePath(file.getAbsolutePath());
sslContextFactory.setKeyStorePassword(System.getProperty(SSL_KEYSTORE_PASSPHRASE));
sslContextFactory.setKeyStoreType("JKS"); //$NON-NLS-1$
sslContextFactory.setProtocol("TLS"); //$NON-NLS-1$
sslContextFactory.setWantClientAuth(false);
sslContextFactory.setNeedClientAuth(false);
HttpConfiguration httpsConfiguration = new HttpConfiguration();
httpsConfiguration.setSendServerVersion(false);
httpsConfiguration.addCustomizer(new SecureRequestCustomizer());
ServerConnector serverConnector = new ServerConnector(server, new SslConnectionFactory(sslContextFactory, "http/1.1"), new HttpConnectionFactory(httpsConfiguration)); //$NON-NLS-1$
serverConnector.setHost(System.getProperty(HTTPS_HOST, DEFAULT_HOSTNAME));
serverConnector.setPort(handleSystemPropertiesIntegerValue(HTTPS_PORT, DEFAULT_HTTPS_PORT));
serverConnector.setIdleTimeout(30000);
return serverConnector;
}
/**
* Returns the integer value of the given system property value. If the
* system property is not a integer, the default value will be used instead.
*
* @param property
* The system property key
* @param defaultValue
* The default value
* @return The integer value of given system property value or the default
* value
*/
private int handleSystemPropertiesIntegerValue(String property, int defaultValue) {
int value = defaultValue;
String propertyValue = System.getProperty(property);
if (propertyValue != null && propertyValue.length() > 0) {
try {
value = Integer.parseInt(propertyValue);
} catch (NumberFormatException exception) {
String message = String.format(Messages.SiriusServerConfigurator_wrongPropertyTypeWarning, propertyValue);
IStatus status = new Status(IStatus.ERROR, SiriusServerPlugin.PLUGIN_ID, message, exception);
SiriusServerPlugin.getPlugin().getLog().log(status);
}
}
return value;
}
}