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

com.couchbase.lite.internal.core.C4Socket Maven / Gradle / Ivy

//
// Copyright (c) 2020, 2017 Couchbase, Inc All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
package com.couchbase.lite.internal.core;

import android.support.annotation.NonNull;
import android.support.annotation.Nullable;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

import com.couchbase.lite.LogDomain;
import com.couchbase.lite.internal.SocketFactory;
import com.couchbase.lite.internal.support.Log;


@SuppressWarnings({"LineLength", "PMD.TooManyMethods", "unused"})
public abstract class C4Socket extends C4NativePeer {
    //-------------------------------------------------------------------------
    // Constants
    //-------------------------------------------------------------------------

    // @formatter:off
    public static final int WS_STATUS_CLOSE_NORMAL = 1000;
    public static final int WS_STATUS_GOING_AWAY = 1001;               // Peer has to close, e.g. because host app is quitting
    public static final int WS_STATUS_CLOSE_PROTOCOL_ERROR = 1002;     // Protocol violation: invalid framing data
    public static final int WS_STATUS_CLOSE_DATA_ERROR = 1003;         // Message payload cannot be handled
    public static final int WS_STATUS_CLOSE_NO_CODE = 1005;            // Never sent, only received
    public static final int WS_STATUS_CLOSE_ABNORMAL = 1006;           // Never sent, only received
    public static final int WS_STATUS_CLOSE_BAD_MESSAGE_FORMAT = 1007; // Unparsable message
    public static final int WS_STATUS_CLOSE_POLICY_ERROR = 1008;       // Catch-all failure
    public static final int WS_STATUS_CLOSE_MESSAGE_TO_BIG = 1009;     // Message too big
    public static final int WS_STATUS_CLOSE_MISSING_EXTENSION = 1010;  // Peer doesn't provide a necessary extension
    public static final int WS_STATUS_CLOSE_CANT_FULFILL = 1011;       // Can't fulfill request due to "unexpected condition"
    public static final int WS_STATUS_CLOSE_TLS_FAILURE = 1015;        // Never sent, only received
    public static final int WS_STATUS_CLOSE_USER = 4000;               // First unregistered code for free-form use
    public static final int WS_STATUS_CLOSE_USER_TRANSIENT = WS_STATUS_CLOSE_USER + 1; // User-defined transient error
    public static final int WS_STATUS_CLOSE_USER_PERMANENT = WS_STATUS_CLOSE_USER + 2; // User-defined permanent error
    // @formatter:on

    private static final LogDomain LOG_DOMAIN = LogDomain.NETWORK;

    // C4SocketFraming (C4SocketFactory.framing)
    public static final int WEB_SOCKET_CLIENT_FRAMING = 0; ///< Frame as WebSocket client messages (masked)
    public static final int NO_FRAMING = 1;                ///< No framing; use messages as-is
    public static final int WEB_SOCKET_SERVER_FRAMING = 2; ///< Frame as WebSocket server messages (not masked)
    // @formatter:on

    //-------------------------------------------------------------------------
    // Static Variables
    //-------------------------------------------------------------------------

    // Lookup table: the handle to a native socket object maps to its Java companion
    private static final Map HANDLES_TO_SOCKETS = Collections.synchronizedMap(new HashMap<>());


    //-------------------------------------------------------------------------
    // JNI callback methods
    //-------------------------------------------------------------------------

    // This method is called by reflection.  Don't change its signature.
    @SuppressWarnings("unused")
    static void open(
        long handle,
        Object context,
        @Nullable String scheme,
        @Nullable String hostname,
        int port,
        @Nullable String path,
        byte[] options) {
        C4Socket socket = HANDLES_TO_SOCKETS.get(handle);
        Log.d(LOG_DOMAIN, "C4Socket.open @" + handle + ": " + socket + ", " + context);

        if (socket == null) {
            if (!(context instanceof SocketFactory)) {
                throw new IllegalArgumentException("Context is not a socket factory: " + context);
            }
            socket = ((SocketFactory) context).createSocket(handle, scheme, hostname, port, path, options);
        }

        socket.openSocket();
    }

    // This method is called by reflection.  Don't change its signature.
    @SuppressWarnings("unused")
    static void write(long handle, byte[] allocatedData) {
        if (allocatedData == null) {
            Log.v(LOG_DOMAIN, "C4Socket.callback.write: allocatedData is null");
            return;
        }

        final C4Socket socket = HANDLES_TO_SOCKETS.get(handle);
        Log.d(LOG_DOMAIN, "C4Socket.write @" + handle + ": " + socket);
        if (socket == null) { return; }

        socket.send(allocatedData);
    }

    // This method is called by reflection.  Don't change its signature.
    // NOTE: No further action is required?
    @SuppressWarnings("unused")
    static void completedReceive(long handle, long byteCount) {
        final C4Socket socket = HANDLES_TO_SOCKETS.get(handle);
        Log.d(LOG_DOMAIN, "C4Socket.completedReceive @" + handle + ": " + socket);
        if (socket == null) { return; }

        socket.completedReceive(byteCount);
    }

    // This method is called by reflection.  Don't change its signature.
    // NOTE: close(long) method should not be called.
    @SuppressWarnings("unused")
    static void close(long handle) {
        final C4Socket socket = HANDLES_TO_SOCKETS.get(handle);
        Log.d(LOG_DOMAIN, "C4Socket.close @" + handle + ": " + socket);
        if (socket == null) { return; }

        socket.close();
    }

    // This method is called by reflection.  Don't change its signature.
    @SuppressWarnings("unused")
    static void requestClose(long handle, int status, @Nullable String message) {
        final C4Socket socket = HANDLES_TO_SOCKETS.get(handle);
        Log.d(LOG_DOMAIN, "C4Socket.requestClose @" + handle + ": " + socket + " #" + status + "(" + message + ")");
        if (socket == null) { return; }

        socket.requestClose(status, message);
    }

    // This method is called by reflection.  Don't change its signature.
    // NOTE: close(long) method should not be called.
    @SuppressWarnings("unused")
    static void dispose(long handle) {
        final C4Socket socket = HANDLES_TO_SOCKETS.get(handle);
        Log.d(LOG_DOMAIN, "C4Socket.dispose @" + handle + ": " + socket);
        if (socket == null) { return; }

        release(socket);
    }

    //-------------------------------------------------------------------------
    // Private static methods
    //-------------------------------------------------------------------------

    private static void bind(@NonNull C4Socket socket) {
        final long handle = socket.getPeer();
        HANDLES_TO_SOCKETS.put(handle, socket);
        Log.d(LOG_DOMAIN, "C4Socket.bind @" + handle + ": " + HANDLES_TO_SOCKETS.size());
    }

    private static void release(@NonNull C4Socket socket) {
        final long handle = socket.getPeer();
        HANDLES_TO_SOCKETS.remove(handle);
        Log.d(LOG_DOMAIN, "C4Socket.release @" + handle + ": " + HANDLES_TO_SOCKETS.size());
    }


    //-------------------------------------------------------------------------
    // constructors
    //-------------------------------------------------------------------------

    protected C4Socket(long handle) {
        super(handle);
        bind(this);
    }

    protected C4Socket(String schema, String host, int port, String path, int framing) {
        setPeer(fromNative(this, schema, host, port, path, framing));
        bind(this);
    }

    //-------------------------------------------------------------------------
    // Abstract methods
    //-------------------------------------------------------------------------

    protected abstract void openSocket();

    protected abstract void send(byte[] allocatedData);

    // Apparently not used...
    protected abstract void completedReceive(long byteCount);

    protected abstract void close();

    protected abstract void requestClose(int status, @Nullable String message);

    //-------------------------------------------------------------------------
    // Protected methods
    //-------------------------------------------------------------------------

    protected boolean released() { return getPeerUnchecked() == 0L; }

    protected final void opened() {
        final long handle = getPeerUnchecked();
        Log.d(LOG_DOMAIN, "C4Socket.opened @" + handle);
        if (handle == 0) { return; }
        opened(handle);
    }

    protected final void completedWrite(long byteCount) {
        final long handle = getPeerUnchecked();
        Log.d(LOG_DOMAIN, "C4Socket.completedWrite @%d: %d", handle, byteCount);
        if (handle == 0) { return; }
        completedWrite(handle, byteCount);
    }

    protected final void received(byte[] data) {
        final long handle = getPeerUnchecked();
        Log.d(LOG_DOMAIN, "C4Socket.received @%d: %d", handle, data.length);
        if (handle == 0) { return; }
        received(handle, data);
    }

    protected final void closed(int errorDomain, int errorCode, String message) {
        final long handle = getPeerUnchecked();
        Log.d(LOG_DOMAIN, "C4Socket.closed @%d: %d", handle, errorCode);
        if (handle == 0) { return; }
        closed(handle, errorDomain, errorCode, message);
    }

    protected final void closeRequested(int status, String message) {
        final long handle = getPeerUnchecked();
        Log.d(LOG_DOMAIN, "C4Socket.closeRequested @%d: %d(%s)", handle, status, message);
        if (handle == 0) { return; }
        closeRequested(handle, status, message);
    }

    protected final void gotHTTPResponse(int httpStatus, byte[] responseHeadersFleece) {
        final long handle = getPeerUnchecked();
        Log.d(LOG_DOMAIN, "C4Socket.gotHTTPResponse  @%d: %d", handle, httpStatus);
        if (handle == 0) { return; }
        gotHTTPResponse(handle, httpStatus, responseHeadersFleece);
    }

    //-------------------------------------------------------------------------
    // package protected methods
    //-------------------------------------------------------------------------

    final long getHandle() { return getPeer(); }

    //-------------------------------------------------------------------------
    // native methods
    //-------------------------------------------------------------------------

    private static native void opened(long handle);

    private static native void completedWrite(long handle, long byteCount);

    private static native void received(long handle, byte[] data);

    private static native void closed(long handle, int errorDomain, int errorCode, String message);

    private static native void closeRequested(long handle, int status, String message);

    private static native void gotHTTPResponse(long handle, int httpStatus, byte[] responseHeadersFleece);

    private static native long fromNative(
        Object nativeHandle,
        String schema,
        String host,
        int port,
        String path,
        int framing);
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy