WsConnection.java

// SPDX-License-Identifier: Apache-2.0
package com.shengzhiai.yugu.stcompat.internal;

import java.io.IOException;
import java.util.Map;

/**
 * One WebSocket connection of the compat realtime channel. {@link WsClient} is the socket
 * implementation; tests install scripted connections through {@link CompatConfig#setWsConnector}.
 * Sends may come from any thread; listener callbacks arrive on the connection's reader thread.
 */
public interface WsConnection {

    /** Events of one connection, delivered on its reader thread. */
    interface Listener {
        /** A complete text message (continuation frames already joined). */
        void onText(String text);

        /** A complete binary message. */
        void onBinary(byte[] data);

        /** The server closed the connection with a close frame. Last callback of the connection. */
        void onClosed(int code, String reason);

        /** Transport failure, read timeout or protocol violation. Last callback of the connection. */
        void onFailure(IOException error);
    }

    /** Opens connections; {@link WsClient#CONNECTOR} by default. */
    interface Connector {
        /**
         * Connects and completes the opening handshake.
         *
         * @param url ws or wss URL with path and query
         * @param headers extra request headers of the handshake
         * @param connectTimeoutMs TCP connect timeout per resolved address, also bounds TLS and the handshake answer
         * @param readTimeoutMs read timeout of every later read
         * @throws IOException when the connection or the handshake fails
         */
        WsConnection connect(String url, Map<String, String> headers, int connectTimeoutMs, int readTimeoutMs,
                             Listener listener) throws IOException;
    }

    /** Arrival facts of one inbound data message, for the timing log (SDK 2.1.1). */
    final class Inbound {
        /** {@link System#nanoTime()} when the first frame header of the message arrived. */
        public final long firstByteNanos;
        /** {@link System#nanoTime()} when the last payload byte had been read, before inflation. */
        public final long completeNanos;
        /** Payload bytes as received: compressed when {@link #compressed}. */
        public final long wireBytes;
        /** Bytes of the message delivered to the listener, after inflation. */
        public final long dataBytes;
        /** The message was compressed with permessage-deflate (RFC 7692). */
        public final boolean compressed;

        public Inbound(long firstByteNanos, long completeNanos, long wireBytes, long dataBytes, boolean compressed) {
            this.firstByteNanos = firstByteNanos;
            this.completeNanos = completeNanos;
            this.wireBytes = wireBytes;
            this.dataBytes = dataBytes;
            this.compressed = compressed;
        }
    }

    void sendText(String text) throws IOException;

    void sendBinary(byte[] data, int offset, int length) throws IOException;

    /** Sends a protocol ping; the peer answers with a pong. */
    void ping() throws IOException;

    /**
     * Largest inbound frame and message accepted from now on, compressed and inflated; larger ones
     * close the connection with 1009.
     */
    void setMaxMessageBytes(int bytes);

    /**
     * The data message being delivered: valid inside {@link Listener#onText} and
     * {@link Listener#onBinary} on the reader thread, null when the implementation does not track it.
     */
    Inbound inbound();

    /** Extension agreed in the opening handshake, for example {@code permessage-deflate}; "" for none. */
    String extensions();

    /** Close handshake with {@code code}, bounded wait for the answer. No listener callbacks afterwards. Idempotent. */
    void close(int code, String reason);

    /** Drops the connection at once. No listener callbacks afterwards. Idempotent. */
    void abort();
}