diff --git a/build.gradle b/build.gradle
index 802d7fc..dc5f1f3 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,9 +1,36 @@
-def android_jar = new File(System.getenv('ANDROID_HOME'), '/platforms/android-17/android.jar')
+apply plugin: 'com.android.library'
-apply plugin: 'java'
+buildscript {
+ repositories {
+ jcenter()
+ }
+ dependencies {
+ classpath 'com.android.tools.build:gradle:1.2.3'
+ }
+}
-sourceCompatibility = 1.5
+android {
+ compileSdkVersion 22
+ buildToolsVersion "22.0.1"
+ defaultConfig {
+ minSdkVersion 9
+ targetSdkVersion 19
+ versionCode 1
+ versionName "1.0"
+ }
+ lintOptions {
+ abortOnError false
+ }
+
+}
+repositories {
+ jcenter()
+}
dependencies {
- compile files(android_jar)
+ repositories {
+ mavenCentral()
+ }
+ compile 'com.squareup.okhttp:okhttp:2.5.0'
+
}
diff --git a/src/main/AndroidManifest.xml b/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..1ab3b62
--- /dev/null
+++ b/src/main/AndroidManifest.xml
@@ -0,0 +1,4 @@
+
+
+
\ No newline at end of file
diff --git a/src/main/java/com/codebutler/android_websockets/WebSocketClient.java b/src/main/java/com/codebutler/android_websockets/WebSocketClient.java
index d5e1b73..765968a 100644
--- a/src/main/java/com/codebutler/android_websockets/WebSocketClient.java
+++ b/src/main/java/com/codebutler/android_websockets/WebSocketClient.java
@@ -5,16 +5,10 @@
import android.text.TextUtils;
import android.util.Base64;
import android.util.Log;
-import org.apache.http.*;
-import org.apache.http.client.HttpResponseException;
-import org.apache.http.message.BasicLineParser;
-import org.apache.http.message.BasicNameValuePair;
-import javax.net.SocketFactory;
-import javax.net.ssl.SSLContext;
-import javax.net.ssl.SSLException;
-import javax.net.ssl.SSLSocketFactory;
-import javax.net.ssl.TrustManager;
+import com.squareup.okhttp.Headers;
+import com.squareup.okhttp.internal.http.StatusLine;
+
import java.io.EOFException;
import java.io.IOException;
import java.io.OutputStream;
@@ -24,58 +18,82 @@
import java.security.KeyManagementException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
-import java.util.List;
+import java.util.Map;
+
+import javax.net.SocketFactory;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLException;
+import javax.net.ssl.SSLSocketFactory;
+import javax.net.ssl.TrustManager;
-public class WebSocketClient {
+public class WebSocketClient
+{
private static final String TAG = "WebSocketClient";
- private URI mURI;
- private Listener mListener;
- private Socket mSocket;
- private Thread mThread;
- private HandlerThread mHandlerThread;
- private Handler mHandler;
- private List mExtraHeaders;
- private HybiParser mParser;
+ private static final int SWITCHING_PROTOCOLS_CODE = 101;
+
+ private URI mURI;
+
+ private Listener mListener;
+
+ private Socket mSocket;
+
+ private Thread mThread;
+
+ private HandlerThread mHandlerThread;
+
+ private Handler mHandler;
+
+ private Map mExtraHeaders;
+
+ private HybiParser mParser;
private final Object mSendLock = new Object();
private static TrustManager[] sTrustManagers;
- public static void setTrustManagers(TrustManager[] tm) {
+ public static void setTrustManagers(TrustManager[] tm)
+ {
sTrustManagers = tm;
}
- public WebSocketClient(URI uri, Listener listener, List extraHeaders) {
- mURI = uri;
+ public WebSocketClient(URI uri, Listener listener, Map extraHeaders)
+ {
+ mURI = uri;
mListener = listener;
mExtraHeaders = extraHeaders;
- mParser = new HybiParser(this);
+ mParser = new HybiParser(this);
mHandlerThread = new HandlerThread("websocket-thread");
mHandlerThread.start();
mHandler = new Handler(mHandlerThread.getLooper());
}
- public Listener getListener() {
+ public Listener getListener()
+ {
return mListener;
}
- public void connect() {
- if (mThread != null && mThread.isAlive()) {
+ public void connect()
+ {
+ if(mThread != null && mThread.isAlive())
+ {
return;
}
- mThread = new Thread(new Runnable() {
+ mThread = new Thread(new Runnable()
+ {
@Override
- public void run() {
- try {
+ public void run()
+ {
+ try
+ {
String secret = createSecret();
int port = (mURI.getPort() != -1) ? mURI.getPort() : (mURI.getScheme().equals("wss") ? 443 : 80);
-
String path = TextUtils.isEmpty(mURI.getPath()) ? "/" : mURI.getPath();
- if (!TextUtils.isEmpty(mURI.getQuery())) {
+ if(!TextUtils.isEmpty(mURI.getQuery()))
+ {
path += "?" + mURI.getQuery();
}
@@ -93,9 +111,13 @@ public void run() {
out.print("Origin: " + origin.toString() + "\r\n");
out.print("Sec-WebSocket-Key: " + secret + "\r\n");
out.print("Sec-WebSocket-Version: 13\r\n");
- if (mExtraHeaders != null) {
- for (NameValuePair pair : mExtraHeaders) {
- out.print(String.format("%s: %s\r\n", pair.getName(), pair.getValue()));
+
+
+ if(mExtraHeaders != null)
+ {
+ for(String key : mExtraHeaders.keySet())
+ {
+ out.print(String.format("%s: %s\r\n", key, mExtraHeaders.get(key)));
}
}
out.print("\r\n");
@@ -103,67 +125,94 @@ public void run() {
HybiParser.HappyDataInputStream stream = new HybiParser.HappyDataInputStream(mSocket.getInputStream());
+ String statusLineString = readLine(stream);
// Read HTTP response status line.
- StatusLine statusLine = parseStatusLine(readLine(stream));
- if (statusLine == null) {
- throw new HttpException("Received no reply from server.");
- } else if (statusLine.getStatusCode() != HttpStatus.SC_SWITCHING_PROTOCOLS) {
- throw new HttpResponseException(statusLine.getStatusCode(), statusLine.getReasonPhrase());
+ StatusLine statusLine = StatusLine.parse(statusLineString);
+ if(statusLine == null)
+ {
+ throw new Exception("Received no reply from server.");
+ }
+ else if(statusLine.code != SWITCHING_PROTOCOLS_CODE)
+ {
+ throw new Exception("Wrong status code: " + statusLine.code);
}
// Read HTTP response headers.
String line;
boolean validated = false;
- while (!TextUtils.isEmpty(line = readLine(stream))) {
- Header header = parseHeader(line);
- if (header.getName().equals("Sec-WebSocket-Accept")) {
+ while(!TextUtils.isEmpty(line = readLine(stream)))
+ {
+ Headers header = parseHeader(line);
+ if(header.name(0).equals("Sec-WebSocket-Accept"))
+ {
String expected = createSecretValidation(secret);
- String actual = header.getValue().trim();
+ String actual = header.value(0).trim();
- if (!expected.equals(actual)) {
- throw new HttpException("Bad Sec-WebSocket-Accept header value.");
+ if(!expected.equals(actual))
+ {
+ throw new Exception("Bad Sec-WebSocket-Accept header value.");
}
validated = true;
}
}
- if (!validated) {
- throw new HttpException("No Sec-WebSocket-Accept header.");
+ if(!validated)
+ {
+ throw new Exception("No Sec-WebSocket-Accept header.");
}
mListener.onConnect();
// Now decode websocket frames.
mParser.start(stream);
-
- } catch (EOFException ex) {
+ }
+ catch(EOFException ex)
+ {
Log.d(TAG, "WebSocket EOF!", ex);
mListener.onDisconnect(0, "EOF");
-
- } catch (SSLException ex) {
+ }
+ catch(SSLException ex)
+ {
// Connection reset by peer
Log.d(TAG, "Websocket SSL error!", ex);
mListener.onDisconnect(0, "SSL");
-
- } catch (Exception ex) {
+ }
+ catch(Exception ex)
+ {
mListener.onError(ex);
}
}
});
mThread.start();
}
+ public Headers parseHeader(String line)
+ {
+ int index = line.indexOf(":");
+ if(index == -1)
+ {
+ throw new IllegalArgumentException("Unexpected header: " + line);
+ }
+ return new Headers.Builder().add(line.substring(0, index).trim(), line.substring(index + 1)).build();
+ }
- public void disconnect() {
- if (mSocket != null) {
- mHandler.post(new Runnable() {
+ public void disconnect()
+ {
+ if(mSocket != null)
+ {
+ mHandler.post(new Runnable()
+ {
@Override
- public void run() {
- try {
+ public void run()
+ {
+ try
+ {
mSocket.close();
mSocket = null;
- } catch (IOException ex) {
+ }
+ catch(IOException ex)
+ {
Log.d(TAG, "Error while disconnecting", ex);
mListener.onError(ex);
}
@@ -172,92 +221,109 @@ public void run() {
}
}
- public void send(String data) {
+ public void send(String data)
+ {
sendFrame(mParser.frame(data));
}
- public void send(byte[] data) {
+ public void send(byte[] data)
+ {
sendFrame(mParser.frame(data));
}
- private StatusLine parseStatusLine(String line) {
- if (TextUtils.isEmpty(line)) {
- return null;
- }
- return BasicLineParser.parseStatusLine(line, new BasicLineParser());
- }
-
- private Header parseHeader(String line) {
- return BasicLineParser.parseHeader(line, new BasicLineParser());
- }
// Can't use BufferedReader because it buffers past the HTTP data.
- private String readLine(HybiParser.HappyDataInputStream reader) throws IOException {
+ private String readLine(HybiParser.HappyDataInputStream reader) throws IOException
+ {
int readChar = reader.read();
- if (readChar == -1) {
+ if(readChar == -1)
+ {
return null;
}
StringBuilder string = new StringBuilder("");
- while (readChar != '\n') {
- if (readChar != '\r') {
+ while(readChar != '\n')
+ {
+ if(readChar != '\r')
+ {
string.append((char) readChar);
}
readChar = reader.read();
- if (readChar == -1) {
+ if(readChar == -1)
+ {
return null;
}
}
return string.toString();
}
- private String createSecret() {
+ private String createSecret()
+ {
byte[] nonce = new byte[16];
- for (int i = 0; i < 16; i++) {
+ for(int i = 0; i < 16; i++)
+ {
nonce[i] = (byte) (Math.random() * 256);
}
return Base64.encodeToString(nonce, Base64.DEFAULT).trim();
}
- private String createSecretValidation(String secret) {
- try {
+ private String createSecretValidation(String secret)
+ {
+ try
+ {
MessageDigest md = MessageDigest.getInstance("SHA-1");
md.update((secret + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11").getBytes());
return Base64.encodeToString(md.digest(), Base64.DEFAULT).trim();
- } catch (NoSuchAlgorithmException e) {
+ }
+ catch(NoSuchAlgorithmException e)
+ {
throw new RuntimeException(e);
}
}
- void sendFrame(final byte[] frame) {
- mHandler.post(new Runnable() {
+ void sendFrame(final byte[] frame)
+ {
+ mHandler.post(new Runnable()
+ {
@Override
- public void run() {
- try {
- synchronized (mSendLock) {
- if (mSocket == null) {
+ public void run()
+ {
+ try
+ {
+ synchronized(mSendLock)
+ {
+ if(mSocket == null)
+ {
throw new IllegalStateException("Socket not connected");
}
OutputStream outputStream = mSocket.getOutputStream();
outputStream.write(frame);
outputStream.flush();
}
- } catch (IOException e) {
+ }
+ catch(IOException e)
+ {
mListener.onError(e);
}
}
});
}
- public interface Listener {
- public void onConnect();
- public void onMessage(String message);
- public void onMessage(byte[] data);
- public void onDisconnect(int code, String reason);
- public void onError(Exception error);
+ public interface Listener
+ {
+ void onConnect();
+
+ void onMessage(String message);
+
+ void onMessage(byte[] data);
+
+ void onDisconnect(int code, String reason);
+
+ void onError(Exception error);
}
- private SSLSocketFactory getSSLSocketFactory() throws NoSuchAlgorithmException, KeyManagementException {
+ private SSLSocketFactory getSSLSocketFactory() throws NoSuchAlgorithmException, KeyManagementException
+ {
SSLContext context = SSLContext.getInstance("TLS");
context.init(null, sTrustManagers, null);
return context.getSocketFactory();