From 161fc450487342ccb76aad053768c5c9aad64fe1 Mon Sep 17 00:00:00 2001 From: evgeny Date: Mon, 23 Sep 2024 16:27:33 +0100 Subject: [PATCH] chore: rearrange code before refactoring --- .../main/java/io/ably/lib/http/HttpCore.java | 268 ++++++++++-------- .../lib/transport/WebSocketTransport.java | 182 ++++++------ 2 files changed, 229 insertions(+), 221 deletions(-) diff --git a/lib/src/main/java/io/ably/lib/http/HttpCore.java b/lib/src/main/java/io/ably/lib/http/HttpCore.java index e224262c6..7b3bb64bb 100644 --- a/lib/src/main/java/io/ably/lib/http/HttpCore.java +++ b/lib/src/main/java/io/ably/lib/http/HttpCore.java @@ -1,21 +1,6 @@ package io.ably.lib.http; -import java.io.ByteArrayOutputStream; -import java.io.InputStream; -import java.io.IOException; -import java.io.OutputStream; -import java.lang.reflect.Field; -import java.net.HttpURLConnection; -import java.net.InetSocketAddress; -import java.net.Proxy; -import java.net.URL; -import java.util.HashMap; -import java.util.List; -import java.util.Locale; -import java.util.Map; - import com.google.gson.JsonParseException; - import io.ably.lib.debug.DebugOptions; import io.ably.lib.debug.DebugOptions.RawHttpListener; import io.ably.lib.rest.Auth; @@ -32,11 +17,56 @@ import io.ably.lib.util.Log; import io.ably.lib.util.PlatformAgentProvider; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.lang.reflect.Field; +import java.net.HttpURLConnection; +import java.net.InetSocketAddress; +import java.net.Proxy; +import java.net.URL; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; + /** * HttpCore performs authenticated HTTP synchronously. Internal; use Http or HttpScheduler instead. */ public class HttpCore { + private static final String TAG = HttpCore.class.getName(); + + /************************* + * Private state + *************************/ + + static { + /* if on Android, check version */ + Field androidVersionField = null; + int androidVersion = 0; + try { + androidVersionField = Class.forName("android.os.Build$VERSION").getField("SDK_INT"); + androidVersion = androidVersionField.getInt(androidVersionField); + } catch (Exception e) { + } + if (androidVersionField != null && androidVersion < 8) { + /* HTTP connection reuse which was buggy pre-froyo */ + System.setProperty("httpCore.keepAlive", "false"); + } + } + + public final String scheme; + public final int port; + final ClientOptions options; + final Hosts hosts; + private final Auth auth; + private final ProxyOptions proxyOptions; + private final PlatformAgentProvider platformAgentProvider; + private HttpAuth proxyAuth; + private Proxy proxy = Proxy.NO_PROXY; + /************************* * Public API *************************/ @@ -50,16 +80,22 @@ public HttpCore(ClientOptions options, Auth auth, PlatformAgentProvider platform this.hosts = new Hosts(options.restHost, Defaults.HOST_REST, options); this.proxyOptions = options.proxy; - if(proxyOptions != null) { + if (proxyOptions != null) { String proxyHost = proxyOptions.host; - if(proxyHost == null) { throw AblyException.fromErrorInfo(new ErrorInfo("Unable to configure proxy without proxy host", 40000, 400)); } + if (proxyHost == null) { + throw AblyException.fromErrorInfo(new ErrorInfo("Unable to configure proxy without proxy host", 40000, 400)); + } int proxyPort = proxyOptions.port; - if(proxyPort == 0) { throw AblyException.fromErrorInfo(new ErrorInfo("Unable to configure proxy without proxy port", 40000, 400)); } + if (proxyPort == 0) { + throw AblyException.fromErrorInfo(new ErrorInfo("Unable to configure proxy without proxy port", 40000, 400)); + } this.proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(proxyHost, proxyPort)); String proxyUser = proxyOptions.username; - if(proxyUser != null) { + if (proxyUser != null) { String proxyPassword = proxyOptions.password; - if(proxyPassword == null) { throw AblyException.fromErrorInfo(new ErrorInfo("Unable to configure proxy without proxy password", 40000, 400)); } + if (proxyPassword == null) { + throw AblyException.fromErrorInfo(new ErrorInfo("Unable to configure proxy without proxy password", 40000, 400)); + } proxyAuth = new HttpAuth(proxyUser, proxyPassword, proxyOptions.prefAuthType); } } @@ -67,6 +103,7 @@ public HttpCore(ClientOptions options, Auth auth, PlatformAgentProvider platform /** * Make a synchronous HTTP request specified by URL and proxy, retrying if necessary on WWW-Authenticate + * * @param url * @param method * @param headers @@ -77,21 +114,21 @@ public HttpCore(ClientOptions options, Auth auth, PlatformAgentProvider platform */ public T httpExecuteWithRetry(URL url, String method, Param[] headers, RequestBody requestBody, ResponseHandler responseHandler, boolean requireAblyAuth) throws AblyException { boolean renewPending = true, proxyAuthPending = true; - if(requireAblyAuth) { + if (requireAblyAuth) { authorize(false); } - while(true) { + while (true) { try { return httpExecute(url, getProxy(url), method, headers, requestBody, true, responseHandler); - } catch(AuthRequiredException are) { - if(are.authChallenge != null && requireAblyAuth) { - if(are.expired && renewPending) { + } catch (AuthRequiredException are) { + if (are.authChallenge != null && requireAblyAuth) { + if (are.expired && renewPending) { authorize(true); renewPending = false; continue; } } - if(are.proxyAuthChallenge != null && proxyAuthPending && proxyAuth != null) { + if (are.proxyAuthChallenge != null && proxyAuthPending && proxyAuth != null) { proxyAuth.processAuthenticateHeaders(are.proxyAuthChallenge); proxyAuthPending = false; continue; @@ -102,22 +139,21 @@ public T httpExecuteWithRetry(URL url, String method, Param[] headers, Reque } /** - * Sets host for this HTTP client + * Gets host for this HTTP client * - * @param host URL string + * @return */ - public void setPreferredHost(String host) { - hosts.setPreferredHost(host, false); + public String getPreferredHost() { + return hosts.getPreferredHost(); } /** - * Gets host for this HTTP client + * Sets host for this HTTP client * - * @return - + * @param host URL string */ - public String getPreferredHost() { - return hosts.getPreferredHost(); + public void setPreferredHost(String host) { + hosts.setPreferredHost(host, false); } /** @@ -139,6 +175,7 @@ void authorize(boolean renew) throws AblyException { /** * Make a synchronous HTTP request specified by URL and proxy + * * @param url * @param proxy * @param method @@ -152,13 +189,13 @@ void authorize(boolean renew) throws AblyException { public T httpExecute(URL url, Proxy proxy, String method, Param[] headers, RequestBody requestBody, boolean withCredentials, ResponseHandler responseHandler) throws AblyException { HttpURLConnection conn = null; try { - conn = (HttpURLConnection)url.openConnection(proxy); + conn = (HttpURLConnection) url.openConnection(proxy); boolean withProxyCredentials = (proxy != Proxy.NO_PROXY) && (proxyAuth != null); return httpExecute(conn, method, headers, requestBody, withCredentials, withProxyCredentials, responseHandler); - } catch(IOException ioe) { + } catch (IOException ioe) { throw AblyException.fromThrowable(ioe); } finally { - if(conn != null) { + if (conn != null) { conn.disconnect(); } } @@ -166,6 +203,7 @@ public T httpExecute(URL url, Proxy proxy, String method, Param[] headers, R /** * Make a synchronous HTTP request with a given HttpURLConnection + * * @param conn * @param method * @param headers @@ -191,32 +229,37 @@ T httpExecute(HttpURLConnection conn, String method, Param[] headers, Reques if (authHeader == null && auth != null) { authHeader = auth.getAuthorizationHeader(); } - if(withCredentials && authHeader != null) { + if (withCredentials && authHeader != null) { conn.setRequestProperty(HttpConstants.Headers.AUTHORIZATION, authHeader); credentialsIncluded = true; } - if(withProxyCredentials && proxyAuth.hasChallenge()) { + if (withProxyCredentials && proxyAuth.hasChallenge()) { byte[] encodedRequestBody = (requestBody != null) ? requestBody.getEncoded() : null; String proxyAuthorizationHeader = proxyAuth.getAuthorizationHeader(method, conn.getURL().getPath(), encodedRequestBody); conn.setRequestProperty(HttpConstants.Headers.PROXY_AUTHORIZATION, proxyAuthorizationHeader); } boolean acceptSet = false; - if(headers != null) { - for(Param header: headers) { + if (headers != null) { + for (Param header : headers) { conn.setRequestProperty(header.key, header.value); - if(header.key.equals(HttpConstants.Headers.ACCEPT)) { acceptSet = true; } + if (header.key.equals(HttpConstants.Headers.ACCEPT)) { + acceptSet = true; + } } } - if(!acceptSet) { conn.setRequestProperty(HttpConstants.Headers.ACCEPT, HttpConstants.ContentTypes.JSON); } + if (!acceptSet) { + conn.setRequestProperty(HttpConstants.Headers.ACCEPT, HttpConstants.ContentTypes.JSON); + } /* pass required headers */ conn.setRequestProperty(Defaults.ABLY_PROTOCOL_VERSION_HEADER, Defaults.ABLY_PROTOCOL_VERSION); // RSC7a conn.setRequestProperty(Defaults.ABLY_AGENT_HEADER, AgentHeaderCreator.create(options.agents, platformAgentProvider)); - if (options.clientId != null) conn.setRequestProperty(Defaults.ABLY_CLIENT_ID_HEADER, Base64Coder.encodeString(options.clientId)); + if (options.clientId != null) + conn.setRequestProperty(Defaults.ABLY_CLIENT_ID_HEADER, Base64Coder.encodeString(options.clientId)); /* prepare request body */ byte[] body = null; - if(requestBody != null) { + if (requestBody != null) { body = prepareRequestBody(requestBody, conn); // Check the logging level to avoid performance hit associated with building the message if (Log.level <= Log.VERBOSE) @@ -235,9 +278,9 @@ T httpExecute(HttpURLConnection conn, String method, Param[] headers, Reques Log.v(TAG, " " + entry.getKey() + ": " + val); } - if(options instanceof DebugOptions) { - rawHttpListener = ((DebugOptions)options).httpListener; - if(rawHttpListener != null) { + if (options instanceof DebugOptions) { + rawHttpListener = ((DebugOptions) options).httpListener; + if (rawHttpListener != null) { id = String.valueOf(Math.random()).substring(2); response = rawHttpListener.onRawHttpRequest(id, conn, method, (credentialsIncluded ? authHeader : null), requestProperties, requestBody); if (response != null) { @@ -247,15 +290,15 @@ T httpExecute(HttpURLConnection conn, String method, Param[] headers, Reques } /* send request body */ - if(requestBody != null) { + if (requestBody != null) { writeRequestBody(body, conn); } response = readResponse(conn); - if(rawHttpListener != null) { + if (rawHttpListener != null) { rawHttpListener.onRawHttpResponse(id, method, response); } - } catch(IOException ioe) { - if(rawHttpListener != null) { + } catch (IOException ioe) { + if (rawHttpListener != null) { rawHttpListener.onRawHttpException(id, method, ioe); } throw AblyException.fromThrowable(ioe); @@ -266,6 +309,7 @@ T httpExecute(HttpURLConnection conn, String method, Param[] headers, Reques /** * Handle HTTP response + * * @param conn * @param credentialsIncluded * @param response @@ -278,19 +322,19 @@ private T handleResponse(HttpURLConnection conn, boolean credentialsIncluded return null; } - if (response.statusCode >=500 && response.statusCode <= 504) { + if (response.statusCode >= 500 && response.statusCode <= 504) { ErrorInfo error = ErrorInfo.fromResponseStatus(response.statusLine, response.statusCode); throw AblyException.fromErrorInfo(error); } - if(response.statusCode >= 200 && response.statusCode < 300) { + if (response.statusCode >= 200 && response.statusCode < 300) { return (responseHandler != null) ? responseHandler.handleResponse(response, null) : null; } /* get any in-body error details */ ErrorInfo error = null; - if(response.body != null && response.body.length > 0) { - if(response.contentType != null && response.contentType.contains("msgpack")) { + if (response.body != null && response.body.length > 0) { + if (response.contentType != null && response.contentType.contains("msgpack")) { try { error = ErrorInfo.fromMsgpackBody(response.body); } catch (IOException e) { @@ -302,10 +346,10 @@ private T handleResponse(HttpURLConnection conn, boolean credentialsIncluded String bodyText = new String(response.body); try { ErrorResponse errorResponse = ErrorResponse.fromJSON(bodyText); - if(errorResponse != null) { + if (errorResponse != null) { error = errorResponse.error; } - } catch(JsonParseException jse) { + } catch (JsonParseException jse) { /* error pages aren't necessarily going to satisfy our Accept criteria ... */ System.err.println("Error message in unexpected format: " + bodyText); } @@ -313,50 +357,53 @@ private T handleResponse(HttpURLConnection conn, boolean credentialsIncluded } /* handle error details in header */ - if(error == null) { + if (error == null) { String errorCodeHeader = conn.getHeaderField("X-Ably-ErrorCode"); String errorMessageHeader = conn.getHeaderField("X-Ably-ErrorMessage"); - if(errorCodeHeader != null) { + if (errorCodeHeader != null) { try { error = new ErrorInfo(errorMessageHeader, response.statusCode, Integer.parseInt(errorCodeHeader)); - } catch(NumberFormatException e) {} + } catch (NumberFormatException e) { + } } } /* handle www-authenticate */ - if(response.statusCode == 401) { + if (response.statusCode == 401) { boolean stale = (error != null && error.code == 40140); List wwwAuthHeaders = response.getHeaderFields(HttpConstants.Headers.WWW_AUTHENTICATE); - if(wwwAuthHeaders != null && wwwAuthHeaders.size() > 0) { + if (wwwAuthHeaders != null && wwwAuthHeaders.size() > 0) { Map headersByType = HttpAuth.sortAuthenticateHeaders(wwwAuthHeaders); String tokenHeader = headersByType.get(HttpAuth.Type.X_ABLY_TOKEN); - if(tokenHeader != null) { stale |= (tokenHeader.indexOf("stale") > -1); } + if (tokenHeader != null) { + stale |= (tokenHeader.indexOf("stale") > -1); + } AuthRequiredException exception = new AuthRequiredException(null, error); exception.authChallenge = headersByType; - if(stale) { + if (stale) { exception.expired = true; throw exception; } - if(!credentialsIncluded) { + if (!credentialsIncluded) { throw exception; } } } /* handle proxy-authenticate */ - if(response.statusCode == 407) { + if (response.statusCode == 407) { List proxyAuthHeaders = response.getHeaderFields(HttpConstants.Headers.PROXY_AUTHENTICATE); - if(proxyAuthHeaders != null && proxyAuthHeaders.size() > 0) { + if (proxyAuthHeaders != null && proxyAuthHeaders.size() > 0) { AuthRequiredException exception = new AuthRequiredException(null, error); exception.proxyAuthChallenge = HttpAuth.sortAuthenticateHeaders(proxyAuthHeaders); throw exception; } } - if(error == null) { + if (error == null) { error = ErrorInfo.fromResponseStatus(response.statusLine, response.statusCode); } else { } - Log.e(TAG, "Error response from server: err = " + error.toString()); - if(responseHandler != null) { + Log.e(TAG, "Error response from server: err = " + error); + if (responseHandler != null) { return responseHandler.handleResponse(response, error); } throw AblyException.fromErrorInfo(error); @@ -364,6 +411,7 @@ private T handleResponse(HttpURLConnection conn, boolean credentialsIncluded /** * Emit the request body for an HTTP request + * * @param requestBody * @param conn * @return body @@ -386,6 +434,7 @@ private void writeRequestBody(byte[] body, HttpURLConnection conn) throws IOExce /** * Read the response for an HTTP request + * * @param connection * @return * @throws IOException @@ -410,7 +459,7 @@ private Response readResponse(HttpURLConnection connection) throws IOException { } } - if(response.statusCode == HttpURLConnection.HTTP_NO_CONTENT) { + if (response.statusCode == HttpURLConnection.HTTP_NO_CONTENT) { return response; } @@ -420,7 +469,8 @@ private Response readResponse(HttpURLConnection connection) throws IOException { InputStream is = null; try { is = connection.getInputStream(); - } catch (Throwable e) {} + } catch (Throwable e) { + } if (is == null) is = connection.getErrorStream(); @@ -433,7 +483,8 @@ private Response readResponse(HttpURLConnection connection) throws IOException { if (is != null) { try { is.close(); - } catch (IOException e) {} + } catch (IOException e) { + } } } @@ -451,16 +502,15 @@ private byte[] readInputStream(InputStream inputStream, int bytes) throws IOExce if (bytes == -1) { ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); byte[] buffer = new byte[4 * 1024]; - while((bytesRead = inputStream.read(buffer)) > -1) { + while ((bytesRead = inputStream.read(buffer)) > -1) { outputStream.write(buffer, 0, bytesRead); } return outputStream.toByteArray(); - } - else { + } else { int idx = 0; byte[] output = new byte[bytes]; - while((bytesRead = inputStream.read(output, idx, bytes - idx)) > -1) { + while ((bytesRead = inputStream.read(output, idx, bytes - idx)) > -1) { idx += bytesRead; } @@ -474,11 +524,11 @@ Proxy getProxy(URL url) { } private Proxy getProxy(String host) { - if(proxyOptions != null) { + if (proxyOptions != null) { String[] nonProxyHosts = proxyOptions.nonProxyHosts; - if(nonProxyHosts != null) { - for(String nonProxyHostPattern : nonProxyHosts) { - if(host.matches(nonProxyHostPattern)) { + if (nonProxyHosts != null) { + for (String nonProxyHostPattern : nonProxyHosts) { + if (host.matches(nonProxyHostPattern)) { return null; } } @@ -487,47 +537,18 @@ private Proxy getProxy(String host) { return proxy; } - /************************* - * Private state - *************************/ - - static { - /* if on Android, check version */ - Field androidVersionField = null; - int androidVersion = 0; - try { - androidVersionField = Class.forName("android.os.Build$VERSION").getField("SDK_INT"); - androidVersion = androidVersionField.getInt(androidVersionField); - } catch (Exception e) {} - if(androidVersionField != null && androidVersion < 8) { - /* HTTP connection reuse which was buggy pre-froyo */ - System.setProperty("httpCore.keepAlive", "false"); - } - } - - public final String scheme; - public final int port; - final ClientOptions options; - final Hosts hosts; - - private final Auth auth; - private final ProxyOptions proxyOptions; - private HttpAuth proxyAuth; - private Proxy proxy = Proxy.NO_PROXY; - private final PlatformAgentProvider platformAgentProvider; - - private static final String TAG = HttpCore.class.getName(); - /** * Interface for an entity that supplies an httpCore request body */ public interface RequestBody { byte[] getEncoded(); + String getContentType(); } /** * Interface for an entity that performs type-specific processing on an httpCore response body + * * @param */ public interface BodyHandler { @@ -536,6 +557,7 @@ public interface BodyHandler { /** * Interface for an entity that performs type-specific processing on an httpCore response + * * @param */ public interface ResponseHandler { @@ -548,7 +570,7 @@ public interface ResponseHandler { public static class Response { public int statusCode; public String statusLine; - public Map> headers; + public Map> headers; public String contentType; public int contentLength; public byte[] body; @@ -559,13 +581,12 @@ public static class Response { * If called on a connection that sets the same header multiple times * with possibly different values, only the last value is returned. * - * - * @param name the name of a header field. - * @return the value of the named header field, or {@code null} - * if there is no such field in the header. + * @param name the name of a header field. + * @return the value of the named header field, or {@code null} + * if there is no such field in the header. */ public List getHeaderFields(String name) { - if(headers == null) { + if (headers == null) { return null; } @@ -578,11 +599,12 @@ public List getHeaderFields(String name) { */ public static class AuthRequiredException extends AblyException { private static final long serialVersionUID = 1L; - public AuthRequiredException(Throwable throwable, ErrorInfo reason) { - super(throwable, reason); - } public boolean expired; public Map authChallenge; public Map proxyAuthChallenge; + + public AuthRequiredException(Throwable throwable, ErrorInfo reason) { + super(throwable, reason); + } } } diff --git a/lib/src/main/java/io/ably/lib/transport/WebSocketTransport.java b/lib/src/main/java/io/ably/lib/transport/WebSocketTransport.java index ba3399b7e..c389be18c 100644 --- a/lib/src/main/java/io/ably/lib/transport/WebSocketTransport.java +++ b/lib/src/main/java/io/ably/lib/transport/WebSocketTransport.java @@ -7,39 +7,50 @@ import io.ably.lib.types.ProtocolMessage; import io.ably.lib.types.ProtocolSerializer; import io.ably.lib.util.Log; - -import java.net.URI; -import java.nio.ByteBuffer; -import java.util.Timer; -import java.util.TimerTask; - -import javax.net.ssl.HttpsURLConnection; -import javax.net.ssl.SSLContext; -import javax.net.ssl.SSLParameters; -import javax.net.ssl.SSLSession; - +import org.java_websocket.WebSocket; import org.java_websocket.client.WebSocketClient; import org.java_websocket.exceptions.WebsocketNotConnectedException; import org.java_websocket.framing.CloseFrame; import org.java_websocket.framing.Framedata; import org.java_websocket.handshake.ServerHandshake; -import org.java_websocket.WebSocket; + +import javax.net.ssl.HttpsURLConnection; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLParameters; +import javax.net.ssl.SSLSession; +import java.net.URI; +import java.nio.ByteBuffer; +import java.util.Timer; +import java.util.TimerTask; public class WebSocketTransport implements ITransport { private static final String TAG = WebSocketTransport.class.getName(); - + private static final int NEVER_CONNECTED = -1; + private static final int BUGGYCLOSE = -2; + private static final int CLOSE_NORMAL = 1000; + private static final int GOING_AWAY = 1001; + private static final int CLOSE_PROTOCOL_ERROR = 1002; + private static final int REFUSE = 1003; + /* private static final int UNUSED = 1004; */ + /* private static final int NOCODE = 1005; */ + private static final int ABNORMAL_CLOSE = 1006; + private static final int NO_UTF8 = 1007; + private static final int POLICY_VALIDATION = 1008; + private static final int TOOBIG = 1009; + private static final int EXTENSION = 1010; + private static final int UNEXPECTED_CONDITION = 1011; + private static final int TLS_ERROR = 1015; /****************** - * public factory API + * private members ******************/ - public static class Factory implements ITransport.Factory { - @Override - public WebSocketTransport getTransport(TransportParams params, ConnectionManager connectionManager) { - return new WebSocketTransport(params, connectionManager); - } - } - + private final TransportParams params; + private final ConnectionManager connectionManager; + private final boolean channelBinaryMode; + private String wsUri; + private ConnectListener connectListener; + private WsClient wsConnection; /****************** * protected constructor ******************/ @@ -65,24 +76,24 @@ public void connect(ConnectListener connectListener) { wsUri = wsScheme + params.host + ':' + params.port + "/"; Param[] authParams = connectionManager.ably.auth.getAuthParams(); Param[] connectParams = params.getConnectParams(authParams); - if(connectParams.length > 0) + if (connectParams.length > 0) wsUri = HttpUtils.encodeParams(wsUri, connectParams); Log.d(TAG, "connect(); wsUri = " + wsUri); - synchronized(this) { + synchronized (this) { wsConnection = new WsClient(URI.create(wsUri), this::receive); - if(isTls) { + if (isTls) { SSLContext sslContext = SSLContext.getInstance("TLS"); - sslContext.init( null, null, null ); + sslContext.init(null, null, null); SafeSSLSocketFactory factory = new SafeSSLSocketFactory(sslContext.getSocketFactory()); wsConnection.setSocketFactory(factory); } } wsConnection.connect(); - } catch(AblyException e) { + } catch (AblyException e) { Log.e(TAG, "Unexpected exception attempting connection; wsUri = " + wsUri, e); connectListener.onTransportUnavailable(this, e.errorInfo); - } catch(Throwable t) { + } catch (Throwable t) { Log.e(TAG, "Unexpected exception attempting connection; wsUri = " + wsUri, t); connectListener.onTransportUnavailable(this, AblyException.fromThrowable(t).errorInfo); } @@ -91,8 +102,8 @@ public void connect(ConnectListener connectListener) { @Override public void close() { Log.d(TAG, "close()"); - synchronized(this) { - if(wsConnection != null) { + synchronized (this) { + if (wsConnection != null) { wsConnection.close(); wsConnection = null; } @@ -108,7 +119,7 @@ public void receive(ProtocolMessage msg) throws AblyException { public void send(ProtocolMessage msg) throws AblyException { Log.d(TAG, "send(); action = " + msg.action); try { - if(channelBinaryMode) { + if (channelBinaryMode) { byte[] encodedMsg = ProtocolSerializer.writeMsgpack(msg); // Check the logging level to avoid performance hit associated with building the message @@ -123,14 +134,12 @@ public void send(ProtocolMessage msg) throws AblyException { Log.v(TAG, "send(): " + new String(ProtocolSerializer.writeJSON(msg))); wsConnection.send(ProtocolSerializer.writeJSON(msg)); } - } - catch (WebsocketNotConnectedException e){ - if(connectListener != null) { + } catch (WebsocketNotConnectedException e) { + if (connectListener != null) { connectListener.onTransportUnavailable(this, AblyException.fromThrowable(e).errorInfo); } else throw AblyException.fromThrowable(e); - } - catch (Exception e) { + } catch (Exception e) { throw AblyException.fromThrowable(e); } } @@ -140,22 +149,47 @@ public String getHost() { return params.host; } - protected void preProcessReceivedMessage(ProtocolMessage message) - { + protected void preProcessReceivedMessage(ProtocolMessage message) { //Gives the chance to child classes to do message pre-processing } + public String toString() { + return WebSocketTransport.class.getName() + " {" + getURL() + "}"; + } + + public String getURL() { + return wsUri; + } //interface to transfer Protocol message from websocket interface WebSocketReceiver { void onMessage(ProtocolMessage protocolMessage) throws AblyException; } + /****************** + * public factory API + ******************/ + + public static class Factory implements ITransport.Factory { + @Override + public WebSocketTransport getTransport(TransportParams params, ConnectionManager connectionManager) { + return new WebSocketTransport(params, connectionManager); + } + } + /************************** * WebSocketHandler methods **************************/ - class WsClient extends WebSocketClient { - private final WebSocketReceiver receiver; + class WsClient extends WebSocketClient { + private final WebSocketReceiver receiver; + /*************************** + * WsClient private members + ***************************/ + + private Timer timer = new Timer(); + private TimerTask activityTimerTask = null; + private long lastActivityTime; + private boolean shouldExplicitlyVerifyHostname = true; WsClient(URI serverUri, WebSocketReceiver receiver) { super(serverUri); @@ -219,10 +253,10 @@ public void onMessage(String string) { /* This allows us to detect a websocket ping, so we don't need Ably pings. */ @Override - public void onWebsocketPing( WebSocket conn, Framedata f ) { + public void onWebsocketPing(WebSocket conn, Framedata f) { Log.d(TAG, "onWebsocketPing()"); /* Call superclass to ensure the pong is sent. */ - super.onWebsocketPing( conn, f ); + super.onWebsocketPing(conn, f); flagActivity(); } @@ -231,7 +265,7 @@ public void onClose(final int wsCode, final String wsReason, final boolean remot Log.d(TAG, "onClose(): wsCode = " + wsCode + "; wsReason = " + wsReason + "; remote = " + remote); ErrorInfo reason; - switch(wsCode) { + switch (wsCode) { case NEVER_CONNECTED: case CLOSE_NORMAL: case BUGGYCLOSE: @@ -291,7 +325,8 @@ private synchronized void dispose() { try { timer.cancel(); timer = null; - } catch(IllegalStateException e) {} + } catch (IllegalStateException e) { + } } private synchronized void flagActivity() { @@ -324,15 +359,13 @@ private synchronized void checkActivity() { startActivityTimer(timeout + 100); } - - private synchronized void startActivityTimer(long timeout) - { + private synchronized void startActivityTimer(long timeout) { if (activityTimerTask == null) { schedule((activityTimerTask = new TimerTask() { public void run() { try { onActivityTimerExpiry(); - } catch(Throwable t) { + } catch (Throwable t) { Log.e(TAG, "Unexpected exception in activity timer handler", t); } } @@ -341,17 +374,16 @@ public void run() { } private synchronized void schedule(TimerTask task, long delay) { - if(timer != null) { + if (timer != null) { try { timer.schedule(task, delay); - } catch(IllegalStateException ise) { + } catch (IllegalStateException ise) { Log.e(TAG, "Unexpected exception scheduling activity timer", ise); } } } - private synchronized void onActivityTimerExpiry() - { + private synchronized void onActivityTimerExpiry() { activityTimerTask = null; long timeSinceLastActivity = System.currentTimeMillis() - lastActivityTime; long timeRemaining = getActivityTimeout() - timeSinceLastActivity; @@ -368,55 +400,9 @@ private synchronized void onActivityTimerExpiry() startActivityTimer(timeRemaining + 100); } - private long getActivityTimeout() - { + private long getActivityTimeout() { return connectionManager.maxIdleInterval + connectionManager.ably.options.realtimeRequestTimeout; } - - /*************************** - * WsClient private members - ***************************/ - - private Timer timer = new Timer(); - private TimerTask activityTimerTask = null; - private long lastActivityTime; - private boolean shouldExplicitlyVerifyHostname = true; } - public String toString() { - return WebSocketTransport.class.getName() + " {" + getURL() + "}"; - } - - public String getURL() { - return wsUri; - } - - /****************** - * private members - ******************/ - - private final TransportParams params; - private final ConnectionManager connectionManager; - private final boolean channelBinaryMode; - private String wsUri; - private ConnectListener connectListener; - - private WsClient wsConnection; - - private static final int NEVER_CONNECTED = -1; - private static final int BUGGYCLOSE = -2; - private static final int CLOSE_NORMAL = 1000; - private static final int GOING_AWAY = 1001; - private static final int CLOSE_PROTOCOL_ERROR = 1002; - private static final int REFUSE = 1003; -/* private static final int UNUSED = 1004; */ -/* private static final int NOCODE = 1005; */ - private static final int ABNORMAL_CLOSE = 1006; - private static final int NO_UTF8 = 1007; - private static final int POLICY_VALIDATION = 1008; - private static final int TOOBIG = 1009; - private static final int EXTENSION = 1010; - private static final int UNEXPECTED_CONDITION = 1011; - private static final int TLS_ERROR = 1015; - }