/* * Copyright 2013 The Netty Project * * The Netty Project licenses this file to you 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.jboss.aerogear.io.netty.handler.codec.sockjs; import java.util.Arrays; import java.util.HashSet; import java.util.Iterator; import java.util.Set; import org.jboss.aerogear.io.netty.handler.codec.sockjs.util.ArgumentUtil; import io.netty.util.internal.StringUtil; /** * Configuration for a SockJS Session. */ public final class SockJsConfig { private final String prefix; private final boolean webSocketEnabled; private final long webSocketHeartbeatInterval; private final Set<String> webSocketProtocols; private final boolean cookiesNeeded; private final String sockjsUrl; private final long sessionTimeout; private final long heartbeatInterval; private final int maxStreamingBytesSize; private final boolean tls; private final String keyStore; private final String keystorePassword; private SockJsConfig(final Builder builder) { prefix = builder.prefix; webSocketEnabled = builder.webSocketEnabled; webSocketProtocols = builder.webSocketProtocols; webSocketHeartbeatInterval = builder.webSocketHeartbeatInterval; cookiesNeeded = builder.cookiesNeeded; sockjsUrl = builder.sockJsUrl; sessionTimeout = builder.sessionTimeout; heartbeatInterval = builder.heartbeatInterval; maxStreamingBytesSize = builder.maxStreamingBytesSize; tls = builder.tls; keyStore = builder.keyStore; keystorePassword = builder.keyStorePassword; } /** * The prefix/name, of the SockJS service. * For example, in the url "http://localhost/echo/111/12345/xhr", 'echo' is the prefix. * * @return {@code String} the prefix/name of the SockJS service. */ public String prefix() { return prefix; } /** * Determines whether WebSocket support will not be enabled. * * @return {@code true} if WebSocket support is enabled. */ public boolean isWebSocketEnabled() { return webSocketEnabled; } /** * The WebSocket heartbeat interval. * * This might be required in certain environments where idle connections * are closed by a proxy. It is a separate value from the hearbeat that * the streaming protocols use as it is often desirable to have a much * larger value for it. * * @return {@code long} how often, in ms, that a WebSocket heartbeat should be sent */ public long webSocketHeartbeatInterval() { return webSocketHeartbeatInterval; } /** * If WebSockets are in use the this give the oppertunity to specify * what 'WebSocket-Protocols' should be returned and supported by this * SockJS session. * * @return {@code Set<String>} of WebSocket protocols supported. */ public Set<String> webSocketProtocol() { return webSocketProtocols; } /** * If WebSockets are in use the this give the oppertunity to specify * what 'WebSocket-Protocols' should be returned and supported by this * SockJS session. * * @return {@code String} A comma separated value String with the WebSocket protocols supported */ public String webSocketProtocolCSV() { if (webSocketProtocols.isEmpty()) { return null; } final StringBuilder sb = new StringBuilder(); final Iterator<String> iterator = webSocketProtocols.iterator(); if (iterator.hasNext()) { sb.append(iterator.next()); while (iterator.hasNext()) { sb.append(',').append(iterator.next()); } } return sb.toString(); } /** * Determines if a {@code JSESSIONID} cookie will be set. This is used by some * load balancers to enable session stickyness. * * @return {@code true} if a {@code JSESSIONID} cookie should be set. */ public boolean areCookiesNeeded() { return cookiesNeeded; } /** * The url to the sock-js-<version>.json. This is used by the 'prefix/iframe' protocol and * the url is replaced in the script returned to the client. This allows for configuring * the version of sockjs used. By default it is 'http://cdn.jsdelivr.net/sockjs/0.3.4/sockjs.min.js'. * * @return {@code String} the url to the sockjs version to be used. */ public String sockJsUrl() { return sockjsUrl; } /** * A time out for inactive sessions. * * @return {@code long} the timeout in ms. The default is 5000ms. */ public long sessionTimeout() { return sessionTimeout; } /** * A heartbeat interval. * * @return {@code long} how often, in ms, that a heartbeat should be sent */ public long heartbeatInterval() { return heartbeatInterval; } /** * The max number of types that a streaming transport protocol should allow to be returned * before closing the connection, forcing the client to reconnect. This is done so that the * responseText in the XHR Object will not grow and be come an issue for the client. Instead, * by forcing a reconnect the client will create a new XHR object and this can be see as a * form of garbage collection. * * @return {@code int} the max number of bytes that can be written. Default is 131072. */ public int maxStreamingBytesSize() { return maxStreamingBytesSize; } /** * Determines whether transport layer security (TLS) should be used. * * @return {@code true} if transport layer security should be used. */ public boolean isTls() { return tls; } /** * Returns the keystore to be used if transport layer security is enabled. * * @return {@code String} the path to the keystore to be used */ public String keyStore() { return keyStore; } /** * Returns the keystore password to be used if transport layer security is enabled. * * @return {@code String} the password to the configured keystore */ public String keyStorePassword() { return keystorePassword; } public String toString() { return StringUtil.simpleClassName(this) + "[prefix=" + prefix + ", webSocketEnabled=" + webSocketEnabled + ", webSocketProtocols=" + webSocketProtocols + ", webSocketHeartbeatInterval=" + webSocketHeartbeatInterval + ", cookiesNeeded=" + cookiesNeeded + ", sockJsUrl=" + sockjsUrl + ", sessionTimeout=" + sessionTimeout + ", heartbeatInterval=" + heartbeatInterval + ", maxStreamingBytesSize=" + maxStreamingBytesSize + ", tls=" + tls + ", keyStore=" + keyStore + ']'; } /** * The prefix, or name, of the service. * For example, in the url "http://localhost/echo/111/12345/xhr", 'echo' is the prefix. * * @param prefix the prefix/name of the SockJS service. */ public static Builder withPrefix(final String prefix) { ArgumentUtil.checkNotNullAndNotEmpty(prefix, "prefix"); return new Builder(prefix); } public static class Builder { private final String prefix; private boolean webSocketEnabled = true; private long webSocketHeartbeatInterval = -1; private final Set<String> webSocketProtocols = new HashSet<String>(); private boolean cookiesNeeded; private String sockJsUrl = "http://cdn.jsdelivr.net/sockjs/0.3.4/sockjs.min.js"; private long sessionTimeout = 5000; private long heartbeatInterval = 25000; private int maxStreamingBytesSize = 128 * 1024; private boolean tls; private String keyStore; private String keyStorePassword; /** * The prefix, or name, of the service. * For example, in the url "http://localhost/echo/111/12345/xhr", 'echo' is the prefix. * * @param prefix the prefix/name of the SockJS service. */ public Builder(final String prefix) { this.prefix = prefix; } /** * Will disable WebSocket suppport. */ public Builder disableWebSocket() { webSocketEnabled = false; return this; } /** * Specifies a heartbeat interval for SockJS WebSocket transport. * This might be required in certain environments where idle connections * are closed by a proxy. It is a separate value from the hearbeat that * the streaming protocols use as it is often desirable to have a mush * larger value for it. * * @param ms how often that a WebSocket heartbeat should be sent */ public Builder webSocketHeartbeatInterval(final long ms) { webSocketHeartbeatInterval = ms; return this; } /** * Adds the given protocols which will be returned to during the * Http upgrade request as the header 'WebSocket-Protocol'. * * @param protocols the protocols that are supported. */ public Builder webSocketProtocols(final String... protocols) { webSocketProtocols.addAll(Arrays.asList(protocols)); return this; } /** * Determines if a {@code JSESSIONID} cookie will be set. This is used by some * load balancers to enable session stickyness. */ public Builder cookiesNeeded() { cookiesNeeded = true; return this; } /** * The url to the sock-js-<version>.json. This is used by the 'prefix/iframe' protocol and * the url is replaced in the script returned to the client. This allows for configuring * the version of sockjs used. By default it is 'http://cdn.jsdelivr.net/sockjs/0.3.4/sockjs.min.js'. * * @param sockJsUrl the url to the sockjs version to be used. */ public Builder sockJsUrl(final String sockJsUrl) { this.sockJsUrl = sockJsUrl; return this; } /** * The max number of types that a streaming transport protocol should allow to be returned * before closing the connection, forcing the client to reconnect. This is done so that the * responseText in the XHR Object will not grow and be come an issue for the client. Instead, * by forcing a reconnect the client will create a new XHR object and this can be see as a * form of garbage collection. * * @param ms the max number of bytes that can be written. Default is 131072. */ public Builder sessionTimeout(final long ms) { sessionTimeout = ms; return this; } /** * Specifies a heartbeat interval. * * @param ms how often that a heartbeat should be sent */ public Builder heartbeatInterval(final long ms) { heartbeatInterval = ms; return this; } /** * The max number of types that a streaming transport protocol should allow to be returned * before closing the connection, forcing the client to reconnect. This is done so that the * responseText in the XHR Object will not grow and be come an issue for the client. Instead, * by forcing a reconnect the client will create a new XHR object and this can be see as a * form of garbage collection. * * @param max the max number of bytes that can be written. Default is 131072. */ public Builder maxStreamingBytesSize(final int max) { maxStreamingBytesSize = max; return this; } /** * Determines whether transport layer security (TLS) should be used. * * @param tls if transport layer security should be used. */ public Builder tls(final boolean tls) { this.tls = tls; return this; } /** * Specifies the keystore to be used if transport layer security (TLS) is enabled. * * @param keyStore the keystore to be used when TLS is enabled. */ public Builder keyStore(final String keyStore) { this.keyStore = keyStore; return this; } /** * Specifies the keystore password to be used if transport layer security (TLS) is enabled. * * @param password the keystore password to be used when TLS is enabled. */ public Builder keyStorePassword(final String password) { keyStorePassword = password; return this; } /** * Builds Config with the previously set values. * * @return {@link SockJsConfig} the configuration for the SockJS service. */ public SockJsConfig build() { if (tls && (keyStore == null || keyStorePassword == null)) { throw new IllegalStateException("keyStore and keyStorePassword must be specified if 'tls' is enabled"); } return new SockJsConfig(this); } } }