mobile/android/thirdparty/com/codebutler/android_websockets/WebSocketClient.java

Wed, 31 Dec 2014 07:22:50 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 07:22:50 +0100
branch
TOR_BUG_3246
changeset 4
fc2d59ddac77
permissions
-rw-r--r--

Correct previous dual key logic pending first delivery installment.

michael@0 1 // This file was copied from:
michael@0 2 // https://github.com/koush/android-websockets/blob/master/src/com/codebutler/android_websockets/WebSocketClient.java
michael@0 3
michael@0 4 package com.codebutler.android_websockets;
michael@0 5
michael@0 6 import android.os.Handler;
michael@0 7 import android.os.HandlerThread;
michael@0 8 import android.text.TextUtils;
michael@0 9 import android.util.Base64;
michael@0 10 import android.util.Log;
michael@0 11 import org.apache.http.*;
michael@0 12 import org.apache.http.client.HttpResponseException;
michael@0 13 import org.apache.http.message.BasicLineParser;
michael@0 14 import org.apache.http.message.BasicNameValuePair;
michael@0 15
michael@0 16 import javax.net.SocketFactory;
michael@0 17 import javax.net.ssl.SSLContext;
michael@0 18 import javax.net.ssl.SSLException;
michael@0 19 import javax.net.ssl.SSLSocketFactory;
michael@0 20 import javax.net.ssl.TrustManager;
michael@0 21 import java.io.EOFException;
michael@0 22 import java.io.IOException;
michael@0 23 import java.io.OutputStream;
michael@0 24 import java.io.PrintWriter;
michael@0 25 import java.net.Socket;
michael@0 26 import java.net.URI;
michael@0 27 import java.security.KeyManagementException;
michael@0 28 import java.security.NoSuchAlgorithmException;
michael@0 29 import java.util.List;
michael@0 30
michael@0 31 public class WebSocketClient {
michael@0 32 private static final String TAG = "WebSocketClient";
michael@0 33
michael@0 34 private URI mURI;
michael@0 35 private Listener mListener;
michael@0 36 private Socket mSocket;
michael@0 37 private Thread mThread;
michael@0 38 private HandlerThread mHandlerThread;
michael@0 39 private Handler mHandler;
michael@0 40 private List<BasicNameValuePair> mExtraHeaders;
michael@0 41 private HybiParser mParser;
michael@0 42 private boolean mConnected;
michael@0 43
michael@0 44 private final Object mSendLock = new Object();
michael@0 45
michael@0 46 private static TrustManager[] sTrustManagers;
michael@0 47
michael@0 48 public static void setTrustManagers(TrustManager[] tm) {
michael@0 49 sTrustManagers = tm;
michael@0 50 }
michael@0 51
michael@0 52 public WebSocketClient(URI uri, Listener listener, List<BasicNameValuePair> extraHeaders) {
michael@0 53 mURI = uri;
michael@0 54 mListener = listener;
michael@0 55 mExtraHeaders = extraHeaders;
michael@0 56 mConnected = false;
michael@0 57 mParser = new HybiParser(this);
michael@0 58
michael@0 59 mHandlerThread = new HandlerThread("websocket-thread");
michael@0 60 mHandlerThread.start();
michael@0 61 mHandler = new Handler(mHandlerThread.getLooper());
michael@0 62 }
michael@0 63
michael@0 64 public Listener getListener() {
michael@0 65 return mListener;
michael@0 66 }
michael@0 67
michael@0 68 public void connect() {
michael@0 69 if (mThread != null && mThread.isAlive()) {
michael@0 70 return;
michael@0 71 }
michael@0 72
michael@0 73 mThread = new Thread(new Runnable() {
michael@0 74 @Override
michael@0 75 public void run() {
michael@0 76 try {
michael@0 77 int port = (mURI.getPort() != -1) ? mURI.getPort() : ((mURI.getScheme().equals("wss") || mURI.getScheme().equals("https")) ? 443 : 80);
michael@0 78
michael@0 79 String path = TextUtils.isEmpty(mURI.getPath()) ? "/" : mURI.getPath();
michael@0 80 if (!TextUtils.isEmpty(mURI.getQuery())) {
michael@0 81 path += "?" + mURI.getQuery();
michael@0 82 }
michael@0 83
michael@0 84 String originScheme = mURI.getScheme().equals("wss") ? "https" : "http";
michael@0 85 URI origin = new URI(originScheme, "//" + mURI.getHost(), null);
michael@0 86
michael@0 87 SocketFactory factory = (mURI.getScheme().equals("wss") || mURI.getScheme().equals("https")) ? getSSLSocketFactory() : SocketFactory.getDefault();
michael@0 88 mSocket = factory.createSocket(mURI.getHost(), port);
michael@0 89
michael@0 90 PrintWriter out = new PrintWriter(mSocket.getOutputStream());
michael@0 91 out.print("GET " + path + " HTTP/1.1\r\n");
michael@0 92 out.print("Upgrade: websocket\r\n");
michael@0 93 out.print("Connection: Upgrade\r\n");
michael@0 94 out.print("Host: " + mURI.getHost() + "\r\n");
michael@0 95 out.print("Origin: " + origin.toString() + "\r\n");
michael@0 96 out.print("Sec-WebSocket-Key: " + createSecret() + "\r\n");
michael@0 97 out.print("Sec-WebSocket-Version: 13\r\n");
michael@0 98 if (mExtraHeaders != null) {
michael@0 99 for (NameValuePair pair : mExtraHeaders) {
michael@0 100 out.print(String.format("%s: %s\r\n", pair.getName(), pair.getValue()));
michael@0 101 }
michael@0 102 }
michael@0 103 out.print("\r\n");
michael@0 104 out.flush();
michael@0 105
michael@0 106 HybiParser.HappyDataInputStream stream = new HybiParser.HappyDataInputStream(mSocket.getInputStream());
michael@0 107
michael@0 108 // Read HTTP response status line.
michael@0 109 StatusLine statusLine = parseStatusLine(readLine(stream));
michael@0 110 if (statusLine == null) {
michael@0 111 throw new HttpException("Received no reply from server.");
michael@0 112 } else if (statusLine.getStatusCode() != HttpStatus.SC_SWITCHING_PROTOCOLS) {
michael@0 113 throw new HttpResponseException(statusLine.getStatusCode(), statusLine.getReasonPhrase());
michael@0 114 }
michael@0 115
michael@0 116 // Read HTTP response headers.
michael@0 117 String line;
michael@0 118 while (!TextUtils.isEmpty(line = readLine(stream))) {
michael@0 119 Header header = parseHeader(line);
michael@0 120 if (header.getName().equals("Sec-WebSocket-Accept")) {
michael@0 121 // FIXME: Verify the response...
michael@0 122 }
michael@0 123 }
michael@0 124
michael@0 125 mListener.onConnect();
michael@0 126
michael@0 127 mConnected = true;
michael@0 128
michael@0 129 // Now decode websocket frames.
michael@0 130 mParser.start(stream);
michael@0 131
michael@0 132 } catch (EOFException ex) {
michael@0 133 Log.d(TAG, "WebSocket EOF!", ex);
michael@0 134 mListener.onDisconnect(0, "EOF");
michael@0 135 mConnected = false;
michael@0 136
michael@0 137 } catch (SSLException ex) {
michael@0 138 // Connection reset by peer
michael@0 139 Log.d(TAG, "Websocket SSL error!", ex);
michael@0 140 mListener.onDisconnect(0, "SSL");
michael@0 141 mConnected = false;
michael@0 142
michael@0 143 } catch (Exception ex) {
michael@0 144 mListener.onError(ex);
michael@0 145 }
michael@0 146 }
michael@0 147 });
michael@0 148 mThread.start();
michael@0 149 }
michael@0 150
michael@0 151 public void disconnect() {
michael@0 152 if (mSocket != null) {
michael@0 153 mHandler.post(new Runnable() {
michael@0 154 @Override
michael@0 155 public void run() {
michael@0 156 if (mSocket != null) {
michael@0 157 try {
michael@0 158 mSocket.close();
michael@0 159 } catch (IOException ex) {
michael@0 160 Log.d(TAG, "Error while disconnecting", ex);
michael@0 161 mListener.onError(ex);
michael@0 162 }
michael@0 163 mSocket = null;
michael@0 164 }
michael@0 165 mConnected = false;
michael@0 166 }
michael@0 167 });
michael@0 168 }
michael@0 169 }
michael@0 170
michael@0 171 public void send(String data) {
michael@0 172 sendFrame(mParser.frame(data));
michael@0 173 }
michael@0 174
michael@0 175 public void send(byte[] data) {
michael@0 176 sendFrame(mParser.frame(data));
michael@0 177 }
michael@0 178
michael@0 179 public boolean isConnected() {
michael@0 180 return mConnected;
michael@0 181 }
michael@0 182
michael@0 183 private StatusLine parseStatusLine(String line) {
michael@0 184 if (TextUtils.isEmpty(line)) {
michael@0 185 return null;
michael@0 186 }
michael@0 187 return BasicLineParser.parseStatusLine(line, new BasicLineParser());
michael@0 188 }
michael@0 189
michael@0 190 private Header parseHeader(String line) {
michael@0 191 return BasicLineParser.parseHeader(line, new BasicLineParser());
michael@0 192 }
michael@0 193
michael@0 194 // Can't use BufferedReader because it buffers past the HTTP data.
michael@0 195 private String readLine(HybiParser.HappyDataInputStream reader) throws IOException {
michael@0 196 int readChar = reader.read();
michael@0 197 if (readChar == -1) {
michael@0 198 return null;
michael@0 199 }
michael@0 200 StringBuilder string = new StringBuilder("");
michael@0 201 while (readChar != '\n') {
michael@0 202 if (readChar != '\r') {
michael@0 203 string.append((char) readChar);
michael@0 204 }
michael@0 205
michael@0 206 readChar = reader.read();
michael@0 207 if (readChar == -1) {
michael@0 208 return null;
michael@0 209 }
michael@0 210 }
michael@0 211 return string.toString();
michael@0 212 }
michael@0 213
michael@0 214 private String createSecret() {
michael@0 215 byte[] nonce = new byte[16];
michael@0 216 for (int i = 0; i < 16; i++) {
michael@0 217 nonce[i] = (byte) (Math.random() * 256);
michael@0 218 }
michael@0 219 return Base64.encodeToString(nonce, Base64.DEFAULT).trim();
michael@0 220 }
michael@0 221
michael@0 222 void sendFrame(final byte[] frame) {
michael@0 223 mHandler.post(new Runnable() {
michael@0 224 @Override
michael@0 225 public void run() {
michael@0 226 try {
michael@0 227 synchronized (mSendLock) {
michael@0 228 OutputStream outputStream = mSocket.getOutputStream();
michael@0 229 outputStream.write(frame);
michael@0 230 outputStream.flush();
michael@0 231 }
michael@0 232 } catch (IOException e) {
michael@0 233 mListener.onError(e);
michael@0 234 }
michael@0 235 }
michael@0 236 });
michael@0 237 }
michael@0 238
michael@0 239 public interface Listener {
michael@0 240 public void onConnect();
michael@0 241 public void onMessage(String message);
michael@0 242 public void onMessage(byte[] data);
michael@0 243 public void onDisconnect(int code, String reason);
michael@0 244 public void onError(Exception error);
michael@0 245 }
michael@0 246
michael@0 247 private SSLSocketFactory getSSLSocketFactory() throws NoSuchAlgorithmException, KeyManagementException {
michael@0 248 SSLContext context = SSLContext.getInstance("TLS");
michael@0 249 context.init(null, sTrustManagers, null);
michael@0 250 return context.getSocketFactory();
michael@0 251 }
michael@0 252 }

mercurial