001package com.pusher.client; 002 003import java.io.IOException; 004import java.io.InputStream; 005import java.net.Proxy; 006import java.util.Properties; 007 008/** 009 * Configuration for a {@link com.pusher.client.Pusher} instance. 010 */ 011public class PusherOptions { 012 013 private static final String SRC_LIB_DEV_VERSION = "@version@"; 014 private static final String LIB_DEV_VERSION = "0.0.0-dev"; 015 public static final String LIB_VERSION = readVersionFromProperties(); 016 017 private static final String URI_SUFFIX = "?client=java-client&protocol=5&version=" + LIB_VERSION; 018 private static final String WS_SCHEME = "ws"; 019 private static final String WSS_SCHEME = "wss"; 020 021 private static final int WS_PORT = 80; 022 private static final int WSS_PORT = 443; 023 private static final String PUSHER_DOMAIN = "pusher.com"; 024 025 private static final long DEFAULT_ACTIVITY_TIMEOUT = 120000; 026 private static final long DEFAULT_PONG_TIMEOUT = 30000; 027 028 private static final int MAX_RECONNECTION_ATTEMPTS = 6; //Taken from the Swift lib 029 private static final int MAX_RECONNECT_GAP_IN_SECONDS = 30; 030 031 // Note that the primary cluster lives on a different domain 032 // (others are subdomains of pusher.com). This is not an oversight. 033 // Legacy reasons. 034 private String host = "ws.pusherapp.com"; 035 private int wsPort = WS_PORT; 036 private int wssPort = WSS_PORT; 037 private boolean useTLS = true; 038 private long activityTimeout = DEFAULT_ACTIVITY_TIMEOUT; 039 private long pongTimeout = DEFAULT_PONG_TIMEOUT; 040 private UserAuthenticator userAuthenticator; 041 private ChannelAuthorizer channelAuthorizer; 042 private Authorizer authorizer; 043 private Proxy proxy = Proxy.NO_PROXY; 044 private int maxReconnectionAttempts = MAX_RECONNECTION_ATTEMPTS; 045 private int maxReconnectGapInSeconds = MAX_RECONNECT_GAP_IN_SECONDS; 046 047 /** 048 * @deprecated Please use isUseTLS 049 */ 050 @Deprecated 051 public boolean isEncrypted() { 052 return useTLS; 053 } 054 055 /** 056 * @deprecated Please use setUseTLS 057 */ 058 @Deprecated 059 public PusherOptions setEncrypted(final boolean encrypted) { 060 this.useTLS = encrypted; 061 return this; 062 } 063 064 /** 065 * @return whether the connection to Pusher should use TLS 066 */ 067 public boolean isUseTLS() { 068 return useTLS; 069 } 070 071 /** 072 * Sets whether the connection to Pusher should be use TLS. 073 * 074 * @param useTLS whether the connection should use TLS, by default this is true 075 * @return this, for chaining 076 */ 077 public PusherOptions setUseTLS(final boolean useTLS) { 078 this.useTLS = useTLS; 079 return this; 080 } 081 082 /** 083 * Gets the user authenticator to be used when signing in. 084 * 085 * @return the user authenticator 086 */ 087 public UserAuthenticator getUserAuthenticator() { 088 return userAuthenticator; 089 } 090 091 /** 092 * Sets the user authenticator to be used when signing in. 093 * 094 * @param userAuthenticator The user authenticator to be used. 095 * @return this, for chaining 096 */ 097 public PusherOptions setUserAuthenticator(final UserAuthenticator userAuthenticator) { 098 this.userAuthenticator = userAuthenticator; 099 return this; 100 } 101 102 /** 103 * Gets the channel authorizer to be used when authorizing private and presence 104 * channels. 105 * 106 * @return the channel authorizer 107 */ 108 public ChannelAuthorizer getChannelAuthorizer() { 109 return channelAuthorizer; 110 } 111 112 /** 113 * Sets the channel authorizer to be used when authorizing private and presence 114 * channels. 115 * 116 * @param channelAuthorizer The channel authorizer to be used. 117 * @return this, for chaining 118 */ 119 public PusherOptions setChannelAuthorizer(final ChannelAuthorizer channelAuthorizer) { 120 this.channelAuthorizer = channelAuthorizer; 121 return this; 122 } 123 124 /** 125 * @deprecated Please use getChannelauthorizer 126 */ 127 @Deprecated 128 public Authorizer getAuthorizer() { 129 return authorizer; 130 } 131 132 /** 133 * @deprecated Please use setChannelauthorizer 134 */ 135 @Deprecated 136 public PusherOptions setAuthorizer(final Authorizer authorizer) { 137 this.authorizer = authorizer; 138 return setChannelAuthorizer(authorizer); 139 } 140 141 /** 142 * The host to which connections will be made. 143 * <p> 144 * Note that if you wish to connect to a standard Pusher cluster, the 145 * convenience method setCluster will set the host and ports correctly from 146 * a single argument. 147 * 148 * @param host The host 149 * @return this, for chaining 150 */ 151 public PusherOptions setHost(final String host) { 152 this.host = host; 153 return this; 154 } 155 156 /** 157 * The port to which non TLS connections will be made. 158 * <p> 159 * Note that if you wish to connect to a standard Pusher cluster, the 160 * convenience method setCluster will set the host and ports correctly from 161 * a single argument. 162 * 163 * @param wsPort port number 164 * @return this, for chaining 165 */ 166 public PusherOptions setWsPort(final int wsPort) { 167 this.wsPort = wsPort; 168 return this; 169 } 170 171 /** 172 * The port to which encrypted connections will be made. 173 * <p> 174 * Note that if you wish to connect to a standard Pusher cluster, the 175 * convenience method setCluster will set the host and ports correctly from 176 * a single argument. 177 * 178 * @param wssPort port number 179 * @return this, for chaining 180 */ 181 public PusherOptions setWssPort(final int wssPort) { 182 this.wssPort = wssPort; 183 return this; 184 } 185 186 public PusherOptions setCluster(final String cluster) { 187 host = "ws-" + cluster + "." + PUSHER_DOMAIN; 188 wsPort = WS_PORT; 189 wssPort = WSS_PORT; 190 return this; 191 } 192 193 /** 194 * The number of milliseconds of inactivity at which a "ping" will be 195 * triggered to check the connection. 196 * <p> 197 * The default value is 120,000 (2 minutes). On some connections, where 198 * intermediate hops between the application and Pusher are aggressively 199 * culling connections they consider to be idle, a lower value may help 200 * preserve the connection. 201 * 202 * @param activityTimeout time to consider connection idle, in milliseconds 203 * @return this, for chaining 204 */ 205 public PusherOptions setActivityTimeout(final long activityTimeout) { 206 if (activityTimeout < 1000) { 207 throw new IllegalArgumentException( 208 "Activity timeout must be at least 1,000ms (and is recommended to be much higher)" 209 ); 210 } 211 212 this.activityTimeout = activityTimeout; 213 return this; 214 } 215 216 public long getActivityTimeout() { 217 return activityTimeout; 218 } 219 220 /** 221 * The number of milliseconds after a "ping" is sent that the client will 222 * wait to receive a "pong" response from the server before considering the 223 * connection broken and triggering a transition to the disconnected state. 224 * <p> 225 * The default value is 30,000. 226 * 227 * @param pongTimeout time to wait for pong response, in milliseconds 228 * @return this, for chaining 229 */ 230 public PusherOptions setPongTimeout(final long pongTimeout) { 231 if (pongTimeout < 1000) { 232 throw new IllegalArgumentException("Pong timeout must be at least 1,000ms (and is recommended to be much higher)"); 233 } 234 235 this.pongTimeout = pongTimeout; 236 return this; 237 } 238 239 /** 240 * Number of reconnect attempts when websocket connection failed 241 * 242 * @param maxReconnectionAttempts number of max reconnection attempts, default = {@link #MAX_RECONNECTION_ATTEMPTS} 6 243 * @return this, for chaining 244 */ 245 public PusherOptions setMaxReconnectionAttempts(int maxReconnectionAttempts) { 246 this.maxReconnectionAttempts = maxReconnectionAttempts; 247 return this; 248 } 249 250 /** 251 * The delay in two reconnection extends exponentially (1, 2, 4, .. seconds) This property sets the maximum in between two 252 * reconnection attempts. 253 * 254 * @param maxReconnectGapInSeconds time in seconds of the maximum gab between two reconnection attempts, default = {@link #MAX_RECONNECT_GAP_IN_SECONDS} 30s 255 * @return this, for chaining 256 */ 257 public PusherOptions setMaxReconnectGapInSeconds(int maxReconnectGapInSeconds) { 258 this.maxReconnectGapInSeconds = maxReconnectGapInSeconds; 259 return this; 260 } 261 262 public long getPongTimeout() { 263 return pongTimeout; 264 } 265 266 /** 267 * Construct the URL for the WebSocket connection based on the options 268 * previous set on this object and the provided API key 269 * 270 * @param apiKey The API key 271 * @return the WebSocket URL 272 */ 273 public String buildUrl(final String apiKey) { 274 return String.format( 275 "%s://%s:%s/app/%s%s", 276 useTLS ? WSS_SCHEME : WS_SCHEME, 277 host, 278 useTLS ? wssPort : wsPort, 279 apiKey, 280 URI_SUFFIX 281 ); 282 } 283 284 /** 285 * The default value is Proxy.NO_PROXY. 286 * 287 * @param proxy Specify a proxy, e.g. <code>options.setProxy( new Proxy( Proxy.Type.HTTP, new InetSocketAddress( "proxyaddress", 80 ) ) )</code>; 288 * @return this, for chaining 289 */ 290 public PusherOptions setProxy(Proxy proxy) { 291 if (proxy == null) { 292 throw new IllegalArgumentException("proxy must not be null (instead use Proxy.NO_PROXY)"); 293 } 294 this.proxy = proxy; 295 return this; 296 } 297 298 /** 299 * @return The proxy to be used when opening a websocket connection to Pusher. 300 */ 301 public Proxy getProxy() { 302 return this.proxy; 303 } 304 305 /** 306 * @return the maximum reconnection attempts 307 */ 308 public int getMaxReconnectionAttempts() { 309 return maxReconnectionAttempts; 310 } 311 312 /** 313 * @return the maximum reconnection gap in seconds 314 */ 315 public int getMaxReconnectGapInSeconds() { 316 return maxReconnectGapInSeconds; 317 } 318 319 private static String readVersionFromProperties() { 320 InputStream inStream = null; 321 try { 322 final Properties p = new Properties(); 323 inStream = PusherOptions.class.getResourceAsStream("/pusher.properties"); 324 p.load(inStream); 325 String version = (String) p.get("version"); 326 327 // If the properties file contents indicates the version is being run 328 // from source then replace with a dev indicator. Otherwise the Pusher 329 // Socket API will reject the connection. 330 if (version.equals(SRC_LIB_DEV_VERSION)) { 331 version = LIB_DEV_VERSION; 332 } 333 334 if (version != null && version.length() > 0) { 335 return version; 336 } 337 } catch (final Exception e) { 338 // Fall back to fixed value 339 } finally { 340 try { 341 if (inStream != null) { 342 inStream.close(); 343 } 344 } catch (final IOException e) { 345 // Ignore problem closing stream 346 } 347 } 348 return "0.0.0"; 349 } 350}