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

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/mobile/android/thirdparty/com/codebutler/android_websockets/WebSocketClient.java	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,252 @@
     1.4 +// This file was copied from:
     1.5 +// https://github.com/koush/android-websockets/blob/master/src/com/codebutler/android_websockets/WebSocketClient.java
     1.6 +
     1.7 +package com.codebutler.android_websockets;
     1.8 +
     1.9 +import android.os.Handler;
    1.10 +import android.os.HandlerThread;
    1.11 +import android.text.TextUtils;
    1.12 +import android.util.Base64;
    1.13 +import android.util.Log;
    1.14 +import org.apache.http.*;
    1.15 +import org.apache.http.client.HttpResponseException;
    1.16 +import org.apache.http.message.BasicLineParser;
    1.17 +import org.apache.http.message.BasicNameValuePair;
    1.18 +
    1.19 +import javax.net.SocketFactory;
    1.20 +import javax.net.ssl.SSLContext;
    1.21 +import javax.net.ssl.SSLException;
    1.22 +import javax.net.ssl.SSLSocketFactory;
    1.23 +import javax.net.ssl.TrustManager;
    1.24 +import java.io.EOFException;
    1.25 +import java.io.IOException;
    1.26 +import java.io.OutputStream;
    1.27 +import java.io.PrintWriter;
    1.28 +import java.net.Socket;
    1.29 +import java.net.URI;
    1.30 +import java.security.KeyManagementException;
    1.31 +import java.security.NoSuchAlgorithmException;
    1.32 +import java.util.List;
    1.33 +
    1.34 +public class WebSocketClient {
    1.35 +    private static final String TAG = "WebSocketClient";
    1.36 +
    1.37 +    private URI                      mURI;
    1.38 +    private Listener                 mListener;
    1.39 +    private Socket                   mSocket;
    1.40 +    private Thread                   mThread;
    1.41 +    private HandlerThread            mHandlerThread;
    1.42 +    private Handler                  mHandler;
    1.43 +    private List<BasicNameValuePair> mExtraHeaders;
    1.44 +    private HybiParser               mParser;
    1.45 +    private boolean                  mConnected;
    1.46 +
    1.47 +    private final Object mSendLock = new Object();
    1.48 +
    1.49 +    private static TrustManager[] sTrustManagers;
    1.50 +
    1.51 +    public static void setTrustManagers(TrustManager[] tm) {
    1.52 +        sTrustManagers = tm;
    1.53 +    }
    1.54 +
    1.55 +    public WebSocketClient(URI uri, Listener listener, List<BasicNameValuePair> extraHeaders) {
    1.56 +        mURI          = uri;
    1.57 +        mListener     = listener;
    1.58 +        mExtraHeaders = extraHeaders;
    1.59 +        mConnected    = false;
    1.60 +        mParser       = new HybiParser(this);
    1.61 +
    1.62 +        mHandlerThread = new HandlerThread("websocket-thread");
    1.63 +        mHandlerThread.start();
    1.64 +        mHandler = new Handler(mHandlerThread.getLooper());
    1.65 +    }
    1.66 +
    1.67 +    public Listener getListener() {
    1.68 +        return mListener;
    1.69 +    }
    1.70 +
    1.71 +    public void connect() {
    1.72 +        if (mThread != null && mThread.isAlive()) {
    1.73 +            return;
    1.74 +        }
    1.75 +
    1.76 +        mThread = new Thread(new Runnable() {
    1.77 +            @Override
    1.78 +            public void run() {
    1.79 +                try {
    1.80 +                    int port = (mURI.getPort() != -1) ? mURI.getPort() : ((mURI.getScheme().equals("wss") || mURI.getScheme().equals("https")) ? 443 : 80);
    1.81 +
    1.82 +                    String path = TextUtils.isEmpty(mURI.getPath()) ? "/" : mURI.getPath();
    1.83 +                    if (!TextUtils.isEmpty(mURI.getQuery())) {
    1.84 +                        path += "?" + mURI.getQuery();
    1.85 +                    }
    1.86 +
    1.87 +                    String originScheme = mURI.getScheme().equals("wss") ? "https" : "http";
    1.88 +                    URI origin = new URI(originScheme, "//" + mURI.getHost(), null);
    1.89 +
    1.90 +                    SocketFactory factory = (mURI.getScheme().equals("wss") || mURI.getScheme().equals("https")) ? getSSLSocketFactory() : SocketFactory.getDefault();
    1.91 +                    mSocket = factory.createSocket(mURI.getHost(), port);
    1.92 +
    1.93 +                    PrintWriter out = new PrintWriter(mSocket.getOutputStream());
    1.94 +                    out.print("GET " + path + " HTTP/1.1\r\n");
    1.95 +                    out.print("Upgrade: websocket\r\n");
    1.96 +                    out.print("Connection: Upgrade\r\n");
    1.97 +                    out.print("Host: " + mURI.getHost() + "\r\n");
    1.98 +                    out.print("Origin: " + origin.toString() + "\r\n");
    1.99 +                    out.print("Sec-WebSocket-Key: " + createSecret() + "\r\n");
   1.100 +                    out.print("Sec-WebSocket-Version: 13\r\n");
   1.101 +                    if (mExtraHeaders != null) {
   1.102 +                        for (NameValuePair pair : mExtraHeaders) {
   1.103 +                            out.print(String.format("%s: %s\r\n", pair.getName(), pair.getValue()));
   1.104 +                        }
   1.105 +                    }
   1.106 +                    out.print("\r\n");
   1.107 +                    out.flush();
   1.108 +
   1.109 +                    HybiParser.HappyDataInputStream stream = new HybiParser.HappyDataInputStream(mSocket.getInputStream());
   1.110 +
   1.111 +                    // Read HTTP response status line.
   1.112 +                    StatusLine statusLine = parseStatusLine(readLine(stream));
   1.113 +                    if (statusLine == null) {
   1.114 +                        throw new HttpException("Received no reply from server.");
   1.115 +                    } else if (statusLine.getStatusCode() != HttpStatus.SC_SWITCHING_PROTOCOLS) {
   1.116 +                        throw new HttpResponseException(statusLine.getStatusCode(), statusLine.getReasonPhrase());
   1.117 +                    }
   1.118 +
   1.119 +                    // Read HTTP response headers.
   1.120 +                    String line;
   1.121 +                    while (!TextUtils.isEmpty(line = readLine(stream))) {
   1.122 +                        Header header = parseHeader(line);
   1.123 +                        if (header.getName().equals("Sec-WebSocket-Accept")) {
   1.124 +                            // FIXME: Verify the response...
   1.125 +                        }
   1.126 +                    }
   1.127 +
   1.128 +                    mListener.onConnect();
   1.129 +
   1.130 +                    mConnected = true;
   1.131 +
   1.132 +                    // Now decode websocket frames.
   1.133 +                    mParser.start(stream);
   1.134 +
   1.135 +                } catch (EOFException ex) {
   1.136 +                    Log.d(TAG, "WebSocket EOF!", ex);
   1.137 +                    mListener.onDisconnect(0, "EOF");
   1.138 +                    mConnected = false;
   1.139 +
   1.140 +                } catch (SSLException ex) {
   1.141 +                    // Connection reset by peer
   1.142 +                    Log.d(TAG, "Websocket SSL error!", ex);
   1.143 +                    mListener.onDisconnect(0, "SSL");
   1.144 +                    mConnected = false;
   1.145 +
   1.146 +                } catch (Exception ex) {
   1.147 +                    mListener.onError(ex);
   1.148 +                }
   1.149 +            }
   1.150 +        });
   1.151 +        mThread.start();
   1.152 +    }
   1.153 +
   1.154 +    public void disconnect() {
   1.155 +        if (mSocket != null) {
   1.156 +            mHandler.post(new Runnable() {
   1.157 +                @Override
   1.158 +                public void run() {
   1.159 +                    if (mSocket != null) {
   1.160 +                        try {
   1.161 +                            mSocket.close();
   1.162 +                        } catch (IOException ex) {
   1.163 +                            Log.d(TAG, "Error while disconnecting", ex);
   1.164 +                            mListener.onError(ex);
   1.165 +                        }
   1.166 +                        mSocket = null;
   1.167 +                    }
   1.168 +                    mConnected = false;
   1.169 +                }
   1.170 +            });
   1.171 +        }
   1.172 +    }
   1.173 +
   1.174 +    public void send(String data) {
   1.175 +        sendFrame(mParser.frame(data));
   1.176 +    }
   1.177 +
   1.178 +    public void send(byte[] data) {
   1.179 +        sendFrame(mParser.frame(data));
   1.180 +    }
   1.181 +
   1.182 +    public boolean isConnected() {
   1.183 +        return mConnected;
   1.184 +    }
   1.185 +
   1.186 +    private StatusLine parseStatusLine(String line) {
   1.187 +        if (TextUtils.isEmpty(line)) {
   1.188 +            return null;
   1.189 +        }
   1.190 +        return BasicLineParser.parseStatusLine(line, new BasicLineParser());
   1.191 +    }
   1.192 +
   1.193 +    private Header parseHeader(String line) {
   1.194 +        return BasicLineParser.parseHeader(line, new BasicLineParser());
   1.195 +    }
   1.196 +
   1.197 +    // Can't use BufferedReader because it buffers past the HTTP data.
   1.198 +    private String readLine(HybiParser.HappyDataInputStream reader) throws IOException {
   1.199 +        int readChar = reader.read();
   1.200 +        if (readChar == -1) {
   1.201 +            return null;
   1.202 +        }
   1.203 +        StringBuilder string = new StringBuilder("");
   1.204 +        while (readChar != '\n') {
   1.205 +            if (readChar != '\r') {
   1.206 +                string.append((char) readChar);
   1.207 +            }
   1.208 +
   1.209 +            readChar = reader.read();
   1.210 +            if (readChar == -1) {
   1.211 +                return null;
   1.212 +            }
   1.213 +        }
   1.214 +        return string.toString();
   1.215 +    }
   1.216 +
   1.217 +    private String createSecret() {
   1.218 +        byte[] nonce = new byte[16];
   1.219 +        for (int i = 0; i < 16; i++) {
   1.220 +            nonce[i] = (byte) (Math.random() * 256);
   1.221 +        }
   1.222 +        return Base64.encodeToString(nonce, Base64.DEFAULT).trim();
   1.223 +    }
   1.224 +
   1.225 +    void sendFrame(final byte[] frame) {
   1.226 +        mHandler.post(new Runnable() {
   1.227 +            @Override
   1.228 +            public void run() {
   1.229 +                try {
   1.230 +                    synchronized (mSendLock) {
   1.231 +                        OutputStream outputStream = mSocket.getOutputStream();
   1.232 +                        outputStream.write(frame);
   1.233 +                        outputStream.flush();
   1.234 +                    }
   1.235 +                } catch (IOException e) {
   1.236 +                    mListener.onError(e);
   1.237 +                }
   1.238 +            }
   1.239 +        });
   1.240 +    }
   1.241 +
   1.242 +    public interface Listener {
   1.243 +        public void onConnect();
   1.244 +        public void onMessage(String message);
   1.245 +        public void onMessage(byte[] data);
   1.246 +        public void onDisconnect(int code, String reason);
   1.247 +        public void onError(Exception error);
   1.248 +    }
   1.249 +
   1.250 +    private SSLSocketFactory getSSLSocketFactory() throws NoSuchAlgorithmException, KeyManagementException {
   1.251 +        SSLContext context = SSLContext.getInstance("TLS");
   1.252 +        context.init(null, sTrustManagers, null);
   1.253 +        return context.getSocketFactory();
   1.254 +    }
   1.255 +}

mercurial