All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jivesoftware.openfire.http.HttpBindManager Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (C) 2005-2008 Jive Software. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.jivesoftware.openfire.http;

import org.apache.jasper.servlet.JasperInitializer;
import org.apache.tomcat.InstanceManager;
import org.apache.tomcat.SimpleInstanceManager;
import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.plus.annotation.ContainerInitializer;
import org.eclipse.jetty.server.*;
import org.eclipse.jetty.server.handler.HandlerCollection;
import org.eclipse.jetty.server.handler.HandlerList;
import org.eclipse.jetty.servlet.FilterHolder;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.eclipse.jetty.servlets.AsyncGzipFilter;
import org.eclipse.jetty.spdy.server.http.HTTPSPDYServerConnector;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.eclipse.jetty.util.thread.QueuedThreadPool;
import org.eclipse.jetty.webapp.WebAppContext;
import org.jivesoftware.openfire.Connection;
import org.jivesoftware.openfire.JMXManager;
import org.jivesoftware.openfire.XMPPServer;
import org.jivesoftware.openfire.keystore.CertificateStore;
import org.jivesoftware.openfire.keystore.IdentityStore;
import org.jivesoftware.openfire.spi.ConnectionConfiguration;
import org.jivesoftware.openfire.spi.ConnectionManagerImpl;
import org.jivesoftware.openfire.spi.ConnectionType;
import org.jivesoftware.openfire.spi.EncryptionArtifactFactory;
import org.jivesoftware.openfire.websocket.OpenfireWebSocketServlet;
import org.jivesoftware.util.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.DispatcherType;
import javax.servlet.Filter;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import java.io.File;
import java.security.KeyStore;
import java.security.cert.X509Certificate;
import java.util.*;

/**
 * Responsible for making available BOSH (functionality to the outside world, using an embedded web server.
 */
public final class HttpBindManager implements CertificateEventListener, PropertyEventListener {

    private static final Logger Log = LoggerFactory.getLogger(HttpBindManager.class);

    public static final String HTTP_BIND_ENABLED = "httpbind.enabled";

    public static final boolean HTTP_BIND_ENABLED_DEFAULT = true;

    public static final String HTTP_BIND_PORT = "httpbind.port.plain";

    public static final int HTTP_BIND_PORT_DEFAULT = 7070;

    public static final String HTTP_BIND_SECURE_PORT = "httpbind.port.secure";

    public static final int HTTP_BIND_SECURE_PORT_DEFAULT = 7443;

    public static final String HTTP_BIND_THREADS = "httpbind.client.processing.threads";

    public static final String HTTP_BIND_AUTH_PER_CLIENTCERT_POLICY = "httpbind.client.cert.policy";

    public static final int HTTP_BIND_THREADS_DEFAULT = 200;

    private static final String HTTP_BIND_FORWARDED = "httpbind.forwarded.enabled";

    private static final String HTTP_BIND_FORWARDED_FOR = "httpbind.forwarded.for.header";

    private static final String HTTP_BIND_FORWARDED_SERVER = "httpbind.forwarded.server.header";

    private static final String HTTP_BIND_FORWARDED_HOST = "httpbind.forwarded.host.header";

    private static final String HTTP_BIND_FORWARDED_HOST_NAME = "httpbind.forwarded.host.name";

    // http binding CORS default properties

    public static final String HTTP_BIND_CORS_ENABLED = "httpbind.CORS.enabled";

    public static final boolean HTTP_BIND_CORS_ENABLED_DEFAULT = true;

    public static final String HTTP_BIND_CORS_ALLOW_ORIGIN = "httpbind.CORS.domains";

    public static final String HTTP_BIND_CORS_ALLOW_ORIGIN_DEFAULT = "*";

    public static final String HTTP_BIND_CORS_ALLOW_METHODS_DEFAULT = "PROPFIND, PROPPATCH, COPY, MOVE, DELETE, MKCOL, LOCK, UNLOCK, PUT, GETLIB, VERSION-CONTROL, CHECKIN, CHECKOUT, UNCHECKOUT, REPORT, UPDATE, CANCELUPLOAD, HEAD, OPTIONS, GET, POST";

    public static final String HTTP_BIND_CORS_ALLOW_HEADERS_DEFAULT = "Overwrite, Destination, Content-Type, Depth, User-Agent, X-File-Size, X-Requested-With, If-Modified-Since, X-File-Name, Cache-Control";

    public static final String HTTP_BIND_CORS_MAX_AGE_DEFAULT = "86400";

    public static final String HTTP_BIND_REQUEST_HEADER_SIZE = "httpbind.request.header.size";

    public static final int HTTP_BIND_REQUEST_HEADER_SIZE_DEFAULT = 32768;

    public static Map HTTP_BIND_ALLOWED_ORIGINS = new HashMap<>();

    private static HttpBindManager instance = new HttpBindManager();

    private Server httpBindServer;

    private final HttpSessionManager httpSessionManager;

    /**
     * An ordered collection of all handlers (that is created to include the #extensionHandlers).
     *
     * A reference to this collection is maintained outside of the Jetty server implementation ({@link #httpBindServer})
     * as its lifecycle differs from that server: the server is recreated upon configuration changes, while the
     * collection of handlers need not be.
     *
     * This collection is ordered, which ensures that:
     * 
    *
  • The handlers providing BOSH functionality are tried first.
  • *
  • Any handlers that are registered by external sources ({@link #extensionHandlers}) are tried in between.
  • *
  • The 'catch-all' handler that maps to static content is tried last.
  • *
* * This collection should be regarded as immutable. When handlers are to be added/removed dynamically, this should * occur in {@link #extensionHandlers}, to which a reference is stored in this list by the constructor of this class. */ private final HandlerList handlerList = new HandlerList(); /** * Contains all Jetty handlers that are added as an extension. * * This collection is mutable. Handlers can be added and removed at runtime. */ private final HandlerCollection extensionHandlers = new HandlerCollection( true ); public static HttpBindManager getInstance() { return instance; } private HttpBindManager() { JiveGlobals.migrateProperty(HTTP_BIND_ENABLED); JiveGlobals.migrateProperty(HTTP_BIND_PORT); JiveGlobals.migrateProperty(HTTP_BIND_SECURE_PORT); JiveGlobals.migrateProperty(HTTP_BIND_THREADS); JiveGlobals.migrateProperty(HTTP_BIND_FORWARDED); JiveGlobals.migrateProperty(HTTP_BIND_FORWARDED_FOR); JiveGlobals.migrateProperty(HTTP_BIND_FORWARDED_SERVER); JiveGlobals.migrateProperty(HTTP_BIND_FORWARDED_HOST); JiveGlobals.migrateProperty(HTTP_BIND_FORWARDED_HOST_NAME); JiveGlobals.migrateProperty(HTTP_BIND_CORS_ENABLED); JiveGlobals.migrateProperty(HTTP_BIND_CORS_ALLOW_ORIGIN); JiveGlobals.migrateProperty(HTTP_BIND_REQUEST_HEADER_SIZE); PropertyEventDispatcher.addListener( this ); this.httpSessionManager = new HttpSessionManager(); // setup the cache for the allowed origins this.setupAllowedOriginsMap(); // Setup the default handlers. Order is important here. First, evaluate if the 'standard' handlers can be used to fulfill requests. this.handlerList.addHandler( createBoshHandler() ); this.handlerList.addHandler( createWebsocketHandler() ); this.handlerList.addHandler( createCrossDomainHandler() ); // When standard handling does not apply, see if any of the handlers in the extension pool of handlers applies to the request. this.handlerList.addHandler( this.extensionHandlers ); // When everything else fails, use the static content handler. This one should be last, as it is mapping to the root context. // This means that it will catch everything and prevent the invocation of later handlers. final Handler staticContentHandler = createStaticContentHandler(); if ( staticContentHandler != null ) { this.handlerList.addHandler( staticContentHandler ); } } public void start() { if (!isHttpBindServiceEnabled()) { return; } // this is the number of threads allocated to each connector/port final int processingThreads = JiveGlobals.getIntProperty(HTTP_BIND_THREADS, HTTP_BIND_THREADS_DEFAULT); final QueuedThreadPool tp = new QueuedThreadPool(processingThreads); tp.setName("Jetty-QTP-BOSH"); httpBindServer = new Server(tp); if (JMXManager.isEnabled()) { JMXManager jmx = JMXManager.getInstance(); httpBindServer.addBean(jmx.getContainer()); } final Connector httpConnector = createConnector( httpBindServer ); final Connector httpsConnector = createSSLConnector( httpBindServer); if (httpConnector == null && httpsConnector == null) { httpBindServer = null; return; } if (httpConnector != null) { httpBindServer.addConnector(httpConnector); } if (httpsConnector != null) { httpBindServer.addConnector(httpsConnector); } httpBindServer.setHandler( handlerList ); try { httpBindServer.start(); handlerList.start(); extensionHandlers.start(); CertificateManager.addListener(this); Log.info("HTTP bind service started"); } catch (Exception e) { Log.error("Error starting HTTP bind service", e); } } public void stop() { CertificateManager.removeListener(this); if (httpBindServer != null) { try { handlerList.stop(); extensionHandlers.stop(); httpBindServer.stop(); Log.info("HTTP bind service stopped"); } catch (Exception e) { Log.error("Error stopping HTTP bind service", e); } httpBindServer = null; } } public HttpSessionManager getSessionManager() { return httpSessionManager; } private boolean isHttpBindServiceEnabled() { return JiveGlobals.getBooleanProperty(HTTP_BIND_ENABLED, HTTP_BIND_ENABLED_DEFAULT); } private Connector createConnector( final Server httpBindServer ) { final int port = getHttpBindUnsecurePort(); if (port > 0) { HttpConfiguration httpConfig = new HttpConfiguration(); configureProxiedConnector(httpConfig); ServerConnector connector = new ServerConnector(httpBindServer, new HttpConnectionFactory(httpConfig)); // Listen on a specific network interface if it has been set. connector.setHost(getBindInterface()); connector.setPort(port); return connector; } else { return null; } } private Connector createSSLConnector( final Server httpBindServer ) { final int securePort = getHttpBindSecurePort(); try { final IdentityStore identityStore = XMPPServer.getInstance().getCertificateStoreManager().getIdentityStore( ConnectionType.BOSH_C2S ); if (securePort > 0 && identityStore.getStore().aliases().hasMoreElements() ) { if ( !identityStore.containsDomainCertificate( "RSA" ) ) { Log.warn("HTTP binding: Using RSA certificates but they are not valid for " + "the hosted domain"); } final ConnectionManagerImpl connectionManager = ((ConnectionManagerImpl) XMPPServer.getInstance().getConnectionManager()); final ConnectionConfiguration configuration = connectionManager.getListener( ConnectionType.BOSH_C2S, true ).generateConnectionConfiguration(); final SslContextFactory sslContextFactory = new EncryptionArtifactFactory(configuration).getSslContextFactory(); final HttpConfiguration httpsConfig = new HttpConfiguration(); httpsConfig.setSecureScheme("https"); httpsConfig.setSecurePort(securePort); configureProxiedConnector(httpsConfig); httpsConfig.addCustomizer(new SecureRequestCustomizer()); final ServerConnector sslConnector; if ("npn".equals(JiveGlobals.getXMLProperty("spdy.protocol", ""))) { sslConnector = new HTTPSPDYServerConnector(httpBindServer, sslContextFactory); } else { sslConnector = new ServerConnector(httpBindServer, new SslConnectionFactory(sslContextFactory, "http/1.1"), new HttpConnectionFactory(httpsConfig)); } sslConnector.setHost(getBindInterface()); sslConnector.setPort(securePort); return sslConnector; } } catch (Exception e) { Log.error("Error creating SSL connector for Http bind", e); } return null; } private void configureProxiedConnector(HttpConfiguration httpConfig) { // Check to see if we are deployed behind a proxy // Refer to http://eclipse.org/jetty/documentation/current/configuring-connectors.html if (isXFFEnabled()) { ForwardedRequestCustomizer customizer = new ForwardedRequestCustomizer(); // default: "X-Forwarded-For" String forwardedForHeader = getXFFHeader(); if (forwardedForHeader != null) { customizer.setForwardedForHeader(forwardedForHeader); } // default: "X-Forwarded-Server" String forwardedServerHeader = getXFFServerHeader(); if (forwardedServerHeader != null) { customizer.setForwardedServerHeader(forwardedServerHeader); } // default: "X-Forwarded-Host" String forwardedHostHeader = getXFFHostHeader(); if (forwardedHostHeader != null) { customizer.setForwardedHostHeader(forwardedHostHeader); } // default: none String hostName = getXFFHostName(); if (hostName != null) { customizer.setHostHeader(hostName); } httpConfig.addCustomizer(customizer); } httpConfig.setRequestHeaderSize(JiveGlobals.getIntProperty(HTTP_BIND_REQUEST_HEADER_SIZE, HTTP_BIND_REQUEST_HEADER_SIZE_DEFAULT)); } private String getBindInterface() { String interfaceName = JiveGlobals.getXMLProperty("network.interface"); String bindInterface = null; if (interfaceName != null) { if (interfaceName.trim().length() > 0) { bindInterface = interfaceName; } } return bindInterface; } /** * Returns true if the HTTP binding server is currently enabled. * * @return true if the HTTP binding server is currently enabled. */ public boolean isHttpBindEnabled() { return httpBindServer != null && httpBindServer.isRunning(); } /** * Returns true if a listener on the HTTP binding port is running. * * @return true if a listener on the HTTP binding port is running. */ public boolean isHttpBindActive() { if ( isHttpBindEnabled() ) { final int configuredPort = getHttpBindUnsecurePort(); for ( final Connector connector : httpBindServer.getConnectors() ) { if ( !( connector instanceof ServerConnector ) ) { continue; } final int activePort = ( (ServerConnector) connector ).getLocalPort(); if ( activePort == configuredPort ) { return true; } } } return false; } /** * Returns true if a listener on the HTTPS binding port is running. * * @return true if a listener on the HTTPS binding port is running. */ public boolean isHttpsBindActive() { if ( isHttpBindEnabled() ) { final int configuredPort = getHttpBindSecurePort(); for ( final Connector connector : httpBindServer.getConnectors() ) { if ( !( connector instanceof ServerConnector ) ) { continue; } final int activePort = ( (ServerConnector) connector ).getLocalPort(); if ( activePort == configuredPort ) { return true; } } } return false; } public String getHttpBindUnsecureAddress() { return "http://" + XMPPServer.getInstance().getServerInfo().getHostname() + ":" + getHttpBindUnsecurePort() + "/http-bind/"; } public String getHttpBindSecureAddress() { return "https://" + XMPPServer.getInstance().getServerInfo().getHostname() + ":" + getHttpBindSecurePort() + "/http-bind/"; } public String getJavaScriptUrl() { return "http://" + XMPPServer.getInstance().getServerInfo().getHostname() + ":" + getHttpBindUnsecurePort() + "/scripts/"; } // http binding CORS support start private void setupAllowedOriginsMap() { final String originString = getCORSAllowOrigin(); if (!originString.equals(HTTP_BIND_CORS_ALLOW_ORIGIN_DEFAULT)) { String[] origins = originString.split(","); // reset the cache HTTP_BIND_ALLOWED_ORIGINS.clear(); for (String str : origins) { HTTP_BIND_ALLOWED_ORIGINS.put(str, true); } } } public boolean isCORSEnabled() { return JiveGlobals.getBooleanProperty(HTTP_BIND_CORS_ENABLED, HTTP_BIND_CORS_ENABLED_DEFAULT); } public void setCORSEnabled(Boolean value) { if (value != null) JiveGlobals.setProperty(HTTP_BIND_CORS_ENABLED, String.valueOf(value)); } public String getCORSAllowOrigin() { return JiveGlobals.getProperty(HTTP_BIND_CORS_ALLOW_ORIGIN , HTTP_BIND_CORS_ALLOW_ORIGIN_DEFAULT); } public void setCORSAllowOrigin(String origins) { if (origins == null || origins.trim().length() == 0) origins = HTTP_BIND_CORS_ALLOW_ORIGIN_DEFAULT; else { origins = origins.replaceAll("\\s+", ""); } JiveGlobals.setProperty(HTTP_BIND_CORS_ALLOW_ORIGIN, origins); setupAllowedOriginsMap(); } public boolean isAllOriginsAllowed() { return HTTP_BIND_CORS_ALLOW_ORIGIN_DEFAULT.equals( getCORSAllowOrigin() ); } public boolean isThisOriginAllowed(String origin) { return isAllOriginsAllowed() || HTTP_BIND_ALLOWED_ORIGINS.get(origin) != null; } // http binding CORS support end public boolean isXFFEnabled() { return JiveGlobals.getBooleanProperty(HTTP_BIND_FORWARDED, false); } public void setXFFEnabled(boolean enabled) { JiveGlobals.setProperty(HTTP_BIND_FORWARDED, String.valueOf(enabled)); } public String getXFFHeader() { return JiveGlobals.getProperty(HTTP_BIND_FORWARDED_FOR); } public void setXFFHeader(String header) { if (header == null || header.trim().length() == 0) { JiveGlobals.deleteProperty(HTTP_BIND_FORWARDED_FOR); } else { JiveGlobals.setProperty(HTTP_BIND_FORWARDED_FOR, header); } } public String getXFFServerHeader() { return JiveGlobals.getProperty(HTTP_BIND_FORWARDED_SERVER); } public void setXFFServerHeader(String header) { if (header == null || header.trim().length() == 0) { JiveGlobals.deleteProperty(HTTP_BIND_FORWARDED_SERVER); } else { JiveGlobals.setProperty(HTTP_BIND_FORWARDED_SERVER, header); } } public String getXFFHostHeader() { return JiveGlobals.getProperty(HTTP_BIND_FORWARDED_HOST); } public void setXFFHostHeader(String header) { if (header == null || header.trim().length() == 0) { JiveGlobals.deleteProperty(HTTP_BIND_FORWARDED_HOST); } else { JiveGlobals.setProperty(HTTP_BIND_FORWARDED_HOST, header); } } public String getXFFHostName() { return JiveGlobals.getProperty(HTTP_BIND_FORWARDED_HOST_NAME); } public void setXFFHostName(String name) { if (name == null || name.trim().length() == 0) { JiveGlobals.deleteProperty(HTTP_BIND_FORWARDED_HOST_NAME); } else { JiveGlobals.setProperty(HTTP_BIND_FORWARDED_HOST_NAME, name); } } public void setHttpBindEnabled(boolean isEnabled) { JiveGlobals.setProperty(HTTP_BIND_ENABLED, String.valueOf(isEnabled)); } /** * Set the ports on which the HTTP binding service will be running. * * @param unsecurePort the unsecured connection port which clients can connect to. * @param securePort the secured connection port which clients can connect to. * @throws Exception when there is an error configuring the HTTP binding ports. */ public void setHttpBindPorts(int unsecurePort, int securePort) throws Exception { if (unsecurePort != HTTP_BIND_PORT_DEFAULT) { JiveGlobals.setProperty(HTTP_BIND_PORT, String.valueOf(unsecurePort)); } else { JiveGlobals.deleteProperty(HTTP_BIND_PORT); } if (securePort != HTTP_BIND_SECURE_PORT_DEFAULT) { JiveGlobals.setProperty(HTTP_BIND_SECURE_PORT, String.valueOf(securePort)); } else { JiveGlobals.deleteProperty(HTTP_BIND_SECURE_PORT); } } /** * Creates a Jetty context handler that can be used to expose BOSH (HTTP-Bind) functionality. * * Note that an invocation of this method will not register the handler (and thus make the related functionality * available to the end user). Instead, the created handler is returned by this method, and will need to be * registered with the embedded Jetty webserver by the caller. * * @return A Jetty context handler (never null). */ protected Handler createBoshHandler() { final ServletContextHandler context = new ServletContextHandler( null, "/http-bind", ServletContextHandler.SESSIONS ); // Ensure the JSP engine is initialized correctly (in order to be able to cope with Tomcat/Jasper precompiled JSPs). final List initializers = new ArrayList<>(); initializers.add( new ContainerInitializer( new JasperInitializer(), null ) ); context.setAttribute( "org.eclipse.jetty.containerInitializers", initializers ); context.setAttribute( InstanceManager.class.getName(), new SimpleInstanceManager() ); // Generic configuration of the context. context.setAllowNullPathInfo( true ); // Add the functionality-providers. context.addServlet( new ServletHolder( new HttpBindServlet() ), "/*" ); // Add compression filter when needed. if ( isHttpCompressionEnabled() ) { final Filter gzipFilter = new AsyncGzipFilter() { @Override public void init( FilterConfig config ) throws ServletException { super.init( config ); _methods.add( HttpMethod.POST.asString() ); Log.info( "Installed response compression filter" ); } }; final FilterHolder filterHolder = new FilterHolder(); filterHolder.setFilter( gzipFilter ); context.addFilter( filterHolder, "/*", EnumSet.of( DispatcherType.REQUEST ) ); } return context; } /** * Creates a Jetty context handler that can be used to expose Websocket functionality. * * Note that an invocation of this method will not register the handler (and thus make the related functionality * available to the end user). Instead, the created handler is returned by this method, and will need to be * registered with the embedded Jetty webserver by the caller. * * @return A Jetty context handler (never null). */ protected Handler createWebsocketHandler() { final ServletContextHandler context = new ServletContextHandler( null, "/ws", ServletContextHandler.SESSIONS ); // Add the functionality-providers. context.addServlet( new ServletHolder( new OpenfireWebSocketServlet() ), "/*" ); return context; } // NOTE: enabled by default private boolean isHttpCompressionEnabled() { final ConnectionManagerImpl connectionManager = ((ConnectionManagerImpl) XMPPServer.getInstance().getConnectionManager()); final ConnectionConfiguration configuration = connectionManager.getListener( ConnectionType.BOSH_C2S, true ).generateConnectionConfiguration(); return configuration.getCompressionPolicy() == null || configuration.getCompressionPolicy().equals( Connection.CompressionPolicy.optional ); } /** * Creates a Jetty context handler that can be used to expose the cross-domain functionality as implemented by * {@link FlashCrossDomainServlet}. * * Note that an invocation of this method will not register the handler (and thus make the related functionality * available to the end user). Instead, the created handler is returned by this method, and will need to be * registered with the embedded Jetty webserver by the caller. * * @return A Jetty context handler (never null). */ protected Handler createCrossDomainHandler() { final ServletContextHandler context = new ServletContextHandler( null, "/crossdomain.xml", ServletContextHandler.SESSIONS ); // Ensure the JSP engine is initialized correctly (in order to be able to cope with Tomcat/Jasper precompiled JSPs). final List initializers = new ArrayList<>(); initializers.add( new ContainerInitializer( new JasperInitializer(), null ) ); context.setAttribute( "org.eclipse.jetty.containerInitializers", initializers ); context.setAttribute( InstanceManager.class.getName(), new SimpleInstanceManager() ); // Generic configuration of the context. context.setAllowNullPathInfo( true ); // Add the functionality-providers. context.addServlet( new ServletHolder( new FlashCrossDomainServlet() ), "" ); return context; } /** * Creates a Jetty context handler that can be used to expose static files. * * Note that an invocation of this method will not register the handler (and thus make the related functionality * available to the end user). Instead, the created handler is returned by this method, and will need to be * registered with the embedded Jetty webserver by the caller. * * @return A Jetty context handler, or null when the static content could not be accessed. */ protected Handler createStaticContentHandler() { final File spankDirectory = new File( JiveGlobals.getHomeDirectory() + File.separator + "resources" + File.separator + "spank" ); if ( spankDirectory.exists() ) { if ( spankDirectory.canRead() ) { final WebAppContext context = new WebAppContext( null, spankDirectory.getPath(), "/" ); context.setWelcomeFiles( new String[] { "index.html" } ); return context; } else { Log.warn( "Openfire cannot read the directory: " + spankDirectory ); } } return null; } /** * Adds a Jetty handler to be added to the embedded web server that is used to expose BOSH (HTTP-bind) * functionality. * * @param handler The handler (cannot be null). */ public void addJettyHandler( Handler handler ) { if ( handler == null ) { throw new IllegalArgumentException( "Argument 'handler' cannot be null." ); } extensionHandlers.addHandler( handler ); if ( !handler.isStarted() && extensionHandlers.isStarted() ) { try { handler.start(); } catch ( Exception e ) { Log.warn( "Unable to start handler {}", handler, e ); } } } /** * Removes a Jetty handler to be added to the embedded web server that is used to expose BOSH (HTTP-bind) * functionality. * * Removing a handler, even when null, or non-existing, might have side-effects as introduced by the Jetty * implementation. At the time of writing, Jetty will re * * @param handler The handler (should not be null). */ public void removeJettyHandler( Handler handler ) { extensionHandlers.removeHandler( handler ); if ( handler.isStarted() ) { try { handler.stop(); } catch ( Exception e ) { Log.warn( "Unable to stop the handler that was removed: {}", handler, e ); } } } private void doEnableHttpBind(boolean shouldEnable) { if (shouldEnable && httpBindServer == null) { start(); } else if (!shouldEnable && httpBindServer != null) { stop(); } } /** * Returns the HTTP binding port which does not use SSL. * * @return the HTTP binding port which does not use SSL. */ public int getHttpBindUnsecurePort() { return JiveGlobals.getIntProperty(HTTP_BIND_PORT, HTTP_BIND_PORT_DEFAULT); } /** * Returns the HTTP binding port which uses SSL. * * @return the HTTP binding port which uses SSL. */ public int getHttpBindSecurePort() { return JiveGlobals.getIntProperty(HTTP_BIND_SECURE_PORT, HTTP_BIND_SECURE_PORT_DEFAULT); } /** * Returns true if script syntax is enabled. Script syntax allows BOSH to be used in * environments where clients may be restricted to using a particular server. Instead of using * standard HTTP Post requests to transmit data, HTTP Get requests are used. * * @return true if script syntax is enabled. * @see BOSH: Alternative Script * Syntax */ public boolean isScriptSyntaxEnabled() { return JiveGlobals.getBooleanProperty("xmpp.httpbind.scriptSyntax.enabled", false); } /** * Enables or disables script syntax. * * @param isEnabled true to enable script syntax and false to disable it. * @see #isScriptSyntaxEnabled() * @see BOSH: Alternative Script * Syntax */ public void setScriptSyntaxEnabled(boolean isEnabled) { final String property = "xmpp.httpbind.scriptSyntax.enabled"; if(!isEnabled) { JiveGlobals.deleteProperty(property); } else { JiveGlobals.setProperty(property, String.valueOf(isEnabled)); } } private synchronized void restartServer() { stop(); start(); } @Override public void propertySet(String property, Map params) { if (property.equalsIgnoreCase(HTTP_BIND_ENABLED)) { doEnableHttpBind(Boolean.valueOf(params.get("value").toString())); } else if (property.equalsIgnoreCase(HTTP_BIND_PORT)) { try { Integer.valueOf(params.get("value").toString()); } catch (NumberFormatException ne) { JiveGlobals.deleteProperty(HTTP_BIND_PORT); return; } restartServer(); } else if (property.equalsIgnoreCase(HTTP_BIND_SECURE_PORT)) { try { Integer.valueOf(params.get("value").toString()); } catch (NumberFormatException ne) { JiveGlobals.deleteProperty(HTTP_BIND_SECURE_PORT); return; } restartServer(); } else if (HTTP_BIND_AUTH_PER_CLIENTCERT_POLICY.equalsIgnoreCase( property )) { restartServer(); } } @Override public void propertyDeleted(String property, Map params) { if (property.equalsIgnoreCase(HTTP_BIND_ENABLED)) { doEnableHttpBind(HTTP_BIND_ENABLED_DEFAULT); } else if (property.equalsIgnoreCase(HTTP_BIND_PORT)) { restartServer(); } else if (property.equalsIgnoreCase(HTTP_BIND_SECURE_PORT)) { restartServer(); } else if (HTTP_BIND_AUTH_PER_CLIENTCERT_POLICY.equalsIgnoreCase( property )) { restartServer(); } } @Override public void xmlPropertySet(String property, Map params) { } @Override public void xmlPropertyDeleted(String property, Map params) { } @Override public void storeContentChanged( CertificateStore store ) { restartServer(); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy