All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.socket.engineio.client.transports.WebSocket Maven / Gradle / Ivy

There is a newer version: 1.1.28
Show newest version
package io.socket.engineio.client.transports;


import io.socket.engineio.client.Transport;
import io.socket.engineio.parser.Packet;
import io.socket.engineio.parser.Parser;
import io.socket.parseqs.ParseQS;
import io.socket.thread.EventThread;
import io.socket.utf8.UTF8Exception;
import io.socket.yeast.Yeast;
import okhttp3.Dns;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import okhttp3.ResponseBody;
import okhttp3.ws.WebSocketCall;
import okhttp3.ws.WebSocketListener;
import okio.Buffer;

import javax.net.ssl.SSLSocketFactory;
import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.concurrent.TimeUnit;
import java.util.logging.Logger;

import static okhttp3.ws.WebSocket.BINARY;
import static okhttp3.ws.WebSocket.TEXT;

public class WebSocket extends Transport {

    public static final String NAME = "websocket";

    private static final Logger logger = Logger.getLogger(PollingXHR.class.getName());

    private okhttp3.ws.WebSocket ws;
    private WebSocketCall wsCall;

    public WebSocket(Options opts) {
        super(opts);
        this.name = NAME;
    }

    protected void doOpen() {
        Map> headers = new TreeMap>(String.CASE_INSENSITIVE_ORDER);
        this.emit(EVENT_REQUEST_HEADERS, headers);

        final WebSocket self = this;
        OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder()
                // turn off timeouts (github.com/socketio/engine.io-client-java/issues/32)
                .connectTimeout(0, TimeUnit.MILLISECONDS)
                .readTimeout(0, TimeUnit.MILLISECONDS)
                .writeTimeout(0, TimeUnit.MILLISECONDS);

        if (this.sslContext != null) {
            SSLSocketFactory factory = sslContext.getSocketFactory();// (SSLSocketFactory) SSLSocketFactory.getDefault();
            clientBuilder.sslSocketFactory(factory);
        }
        if (this.hostnameVerifier != null) {
            clientBuilder.hostnameVerifier(this.hostnameVerifier);
        }
        Request.Builder builder = new Request.Builder().url(uri());
        for (Map.Entry> entry : headers.entrySet()) {
            for (String v : entry.getValue()) {
                builder.addHeader(entry.getKey(), v);
            }
        }
        final Request request = builder.build();
        final OkHttpClient client = clientBuilder.dns(new Dns() {
            @Override
            public List lookup(String hostname) throws UnknownHostException {
                if (hostname == null) throw new UnknownHostException("hostname == null");
                try {
                    return Arrays.asList(InetAddress.getAllByName(hostname));
                } catch (Exception e) {
                    throw new UnknownHostException(e.getMessage());
                }
            }
        }).build();
        wsCall = WebSocketCall.create(client, request);
        wsCall.enqueue(new WebSocketListener() {
            @Override
            public void onOpen(okhttp3.ws.WebSocket webSocket, Response response) {
                ws = webSocket;
                final Map> headers = response.headers().toMultimap();
                EventThread.exec(new Runnable() {
                    @Override
                    public void run() {
                        self.emit(EVENT_RESPONSE_HEADERS, headers);
                        self.onOpen();
                    }
                });
            }

            @Override
            public void onMessage(final ResponseBody responseBody) throws IOException {
                Object data = null;
                if (responseBody.contentType() == TEXT) {
                    data = responseBody.string();
                } else if (responseBody.contentType() == BINARY) {
                    data = responseBody.source().readByteArray();
                } else {
                    EventThread.exec(new Runnable() {
                        @Override
                        public void run() {
                            self.onError("Unknown payload type: " + responseBody.contentType(), new IllegalStateException());
                        }
                    });
                }
                responseBody.source().close();
                final Object finalData = data;
                EventThread.exec(new Runnable() {
                    @Override
                    public void run() {
                        if (finalData == null) {
                            return;
                        }
                        if (finalData instanceof String) {
                            self.onData((String) finalData);
                        } else {
                            self.onData((byte[]) finalData);
                        }
                    }
                });

            }

            @Override
            public void onPong(Buffer payload) {
            }

            @Override
            public void onClose(int code, String reason) {
                EventThread.exec(new Runnable() {
                    @Override
                    public void run() {
                        self.onClose();
                    }
                });
            }

            @Override
            public void onFailure(final IOException e, final Response response) {
                EventThread.exec(new Runnable() {
                    @Override
                    public void run() {
                        self.onError("websocket error", e);
                    }
                });
            }
        });
        client.dispatcher().executorService().shutdown();
    }

    protected void write(Packet[] packets) throws UTF8Exception {
        final WebSocket self = this;
        this.writable = false;

        final Runnable done = new Runnable() {
            @Override
            public void run() {
                // fake drain
                // defer to next tick to allow Socket to clear writeBuffer
                EventThread.nextTick(new Runnable() {
                    @Override
                    public void run() {
                        self.writable = true;
                        self.emit(EVENT_DRAIN);
                    }
                });
            }
        };

        final int[] total = new int[]{packets.length};
        for (Packet packet : packets) {
            Parser.encodePacket(packet, new Parser.EncodeCallback() {
                @Override
                public void call(Object packet) {
                    try {
                        if (packet instanceof String) {
                            self.ws.sendMessage(RequestBody.create(TEXT, (String) packet));
                        } else if (packet instanceof byte[]) {
                            self.ws.sendMessage(RequestBody.create(BINARY, (byte[]) packet));
                        }
                    } catch (IOException e) {
                        logger.fine("websocket closed before onclose event");
                    } catch (IllegalStateException e) {
                        logger.fine("websocket IllegalStateException " + e.getMessage());
                        doClose();
                    }

                    if (0 == --total[0]) done.run();
                }
            });
        }
    }

    @Override
    protected void onClose() {
        super.onClose();
    }

    protected void doClose() {
        if (wsCall != null) {
            wsCall.cancel();
        }
        if (ws != null) {
            try {
                ws.close(1000, "");
            } catch (IOException e) {
                // websocket already closed
            } catch (IllegalStateException e) {
                // websocket already closed
            }
        }
    }

    protected String uri() {
        Map query = this.query;
        if (query == null) {
            query = new HashMap();
        }
        String schema = this.secure ? "wss" : "ws";
        String port = "";

        if (this.port > 0 && (("wss".equals(schema) && this.port != 443)
                || ("ws".equals(schema) && this.port != 80))) {
            port = ":" + this.port;
        }

        if (this.timestampRequests) {
            query.put(this.timestampParam, Yeast.yeast());
        }

        String derivedQuery = ParseQS.encode(query);
        if (derivedQuery.length() > 0) {
            derivedQuery = "?" + derivedQuery;
        }

        boolean ipv6 = this.hostname.contains(":");
        return schema + "://" + (ipv6 ? "[" + this.hostname + "]" : this.hostname) + port + this.path + derivedQuery;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy