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 // Note that the primary cluster lives on a different domain 029 // (others are subdomains of pusher.com). This is not an oversight. 030 // Legacy reasons. 031 private String host = "ws.pusherapp.com"; 032 private int wsPort = WS_PORT; 033 private int wssPort = WSS_PORT; 034 private boolean encrypted = true; 035 private long activityTimeout = DEFAULT_ACTIVITY_TIMEOUT; 036 private long pongTimeout = DEFAULT_PONG_TIMEOUT; 037 private Authorizer authorizer; 038 private Proxy proxy = Proxy.NO_PROXY; 039 040 /** 041 * Gets whether an encrypted (SSL) connection should be used when connecting 042 * to Pusher. 043 * 044 * @return true if an encrypted connection should be used; otherwise false. 045 */ 046 public boolean isEncrypted() { 047 return encrypted; 048 } 049 050 /** 051 * Sets whether an encrypted (SSL) connection should be used when connecting to 052 * Pusher. 053 * 054 * @param encrypted Whether to use an SSL connection 055 * @return this, for chaining 056 */ 057 public PusherOptions setEncrypted(final boolean encrypted) { 058 this.encrypted = encrypted; 059 return this; 060 } 061 062 /** 063 * Gets the authorizer to be used when authenticating private and presence 064 * channels. 065 * 066 * @return the authorizer 067 */ 068 public Authorizer getAuthorizer() { 069 return authorizer; 070 } 071 072 /** 073 * Sets the authorizer to be used when authenticating private and presence 074 * channels. 075 * 076 * @param authorizer 077 * The authorizer to be used. 078 * @return this, for chaining 079 */ 080 public PusherOptions setAuthorizer(final Authorizer authorizer) { 081 this.authorizer = authorizer; 082 return this; 083 } 084 085 /** 086 * The host to which connections will be made. 087 * 088 * Note that if you wish to connect to a standard Pusher cluster, the 089 * convenience method setCluster will set the host and ports correctly from 090 * a single argument. 091 * 092 * @param host The host 093 * @return this, for chaining 094 */ 095 public PusherOptions setHost(final String host) { 096 this.host = host; 097 return this; 098 } 099 100 /** 101 * The port to which unencrypted connections will be made. 102 * 103 * Note that if you wish to connect to a standard Pusher cluster, the 104 * convenience method setCluster will set the host and ports correctly from 105 * a single argument. 106 * 107 * @param wsPort port number 108 * @return this, for chaining 109 */ 110 public PusherOptions setWsPort(final int wsPort) { 111 this.wsPort = wsPort; 112 return this; 113 } 114 115 /** 116 * The port to which encrypted connections will be made. 117 * 118 * Note that if you wish to connect to a standard Pusher cluster, the 119 * convenience method setCluster will set the host and ports correctly from 120 * a single argument. 121 * 122 * @param wssPort port number 123 * @return this, for chaining 124 */ 125 public PusherOptions setWssPort(final int wssPort) { 126 this.wssPort = wssPort; 127 return this; 128 } 129 130 public PusherOptions setCluster(final String cluster) { 131 host = "ws-" + cluster + "." + PUSHER_DOMAIN; 132 wsPort = WS_PORT; 133 wssPort = WSS_PORT; 134 return this; 135 } 136 137 /** 138 * The number of milliseconds of inactivity at which a "ping" will be 139 * triggered to check the connection. 140 * 141 * The default value is 120,000 (2 minutes). On some connections, where 142 * intermediate hops between the application and Pusher are aggressively 143 * culling connections they consider to be idle, a lower value may help 144 * preserve the connection. 145 * 146 * @param activityTimeout 147 * time to consider connection idle, in milliseconds 148 * @return this, for chaining 149 */ 150 public PusherOptions setActivityTimeout(final long activityTimeout) { 151 if (activityTimeout < 1000) { 152 throw new IllegalArgumentException( 153 "Activity timeout must be at least 1,000ms (and is recommended to be much higher)"); 154 } 155 156 this.activityTimeout = activityTimeout; 157 return this; 158 } 159 160 public long getActivityTimeout() { 161 return activityTimeout; 162 } 163 164 /** 165 * The number of milliseconds after a "ping" is sent that the client will 166 * wait to receive a "pong" response from the server before considering the 167 * connection broken and triggering a transition to the disconnected state. 168 * 169 * The default value is 30,000. 170 * 171 * @param pongTimeout 172 * time to wait for pong response, in milliseconds 173 * @return this, for chaining 174 */ 175 public PusherOptions setPongTimeout(final long pongTimeout) { 176 if (pongTimeout < 1000) { 177 throw new IllegalArgumentException( 178 "Pong timeout must be at least 1,000ms (and is recommended to be much higher)"); 179 } 180 181 this.pongTimeout = pongTimeout; 182 return this; 183 } 184 185 public long getPongTimeout() { 186 return pongTimeout; 187 } 188 189 /** 190 * Construct the URL for the WebSocket connection based on the options 191 * previous set on this object and the provided API key 192 * 193 * @param apiKey The API key 194 * @return the WebSocket URL 195 */ 196 public String buildUrl(final String apiKey) { 197 return String.format("%s://%s:%s/app/%s%s", encrypted ? WSS_SCHEME : WS_SCHEME, host, encrypted ? wssPort 198 : wsPort, apiKey, URI_SUFFIX); 199 } 200 201 /** 202 * 203 * The default value is Proxy.NO_PROXY. 204 * 205 * @param proxy 206 * Specify a proxy, e.g. <code>options.setProxy( new Proxy( Proxy.Type.HTTP, new InetSocketAddress( "proxyaddress", 80 ) ) )</code>; 207 * @return this, for chaining 208 */ 209 public PusherOptions setProxy(Proxy proxy){ 210 if (proxy == null) { 211 throw new IllegalArgumentException("proxy must not be null (instead use Proxy.NO_PROXY)"); 212 } 213 this.proxy = proxy; 214 return this; 215 } 216 217 /** 218 * @return The proxy to be used when opening a websocket connection to Pusher. 219 */ 220 public Proxy getProxy() { 221 return this.proxy; 222 } 223 224 private static String readVersionFromProperties() { 225 InputStream inStream = null; 226 try { 227 final Properties p = new Properties(); 228 inStream = PusherOptions.class.getResourceAsStream("/pusher.properties"); 229 p.load(inStream); 230 String version = (String)p.get("version"); 231 232 // If the properties file contents indicates the version is being run 233 // from source then replace with a dev indicator. Otherwise the Pusher 234 // Socket API will reject the connection. 235 if(version.equals(SRC_LIB_DEV_VERSION)) { 236 version = LIB_DEV_VERSION; 237 } 238 239 if (version != null && version.length() > 0) { 240 return version; 241 } 242 } 243 catch (final Exception e) { 244 // Fall back to fixed value 245 } 246 finally { 247 try { 248 if (inStream != null) { 249 inStream.close(); 250 } 251 } 252 catch (final IOException e) { 253 // Ignore problem closing stream 254 } 255 } 256 return "0.0.0"; 257 } 258}