jdk/src/java.httpclient/share/classes/java/net/http/WS.java
author prappo
Mon, 09 May 2016 23:33:09 +0100
changeset 37874 02589df0999a
child 38856 cc3a0d1e96e0
permissions -rw-r--r--
8087113: Websocket API and implementation Reviewed-by: chegar
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
37874
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
     1
/*
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
     2
 * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
     3
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
     4
 *
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
     5
 * This code is free software; you can redistribute it and/or modify it
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
     6
 * under the terms of the GNU General Public License version 2 only, as
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
     7
 * published by the Free Software Foundation.  Oracle designates this
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
     8
 * particular file as subject to the "Classpath" exception as provided
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
     9
 * by Oracle in the LICENSE file that accompanied this code.
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
    10
 *
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
    11
 * This code is distributed in the hope that it will be useful, but WITHOUT
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
    12
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
    13
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
    14
 * version 2 for more details (a copy is included in the LICENSE file that
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
    15
 * accompanied this code).
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
    16
 *
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
    17
 * You should have received a copy of the GNU General Public License version
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
    18
 * 2 along with this work; if not, write to the Free Software Foundation,
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
    19
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
    20
 *
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
    21
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
    22
 * or visit www.oracle.com if you need additional information or have any
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
    23
 * questions.
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
    24
 */
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
    25
package java.net.http;
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
    26
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
    27
import java.io.IOException;
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
    28
import java.net.ProtocolException;
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
    29
import java.net.http.WSOpeningHandshake.Result;
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
    30
import java.nio.ByteBuffer;
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
    31
import java.util.Optional;
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
    32
import java.util.concurrent.CompletableFuture;
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
    33
import java.util.concurrent.CompletionStage;
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
    34
import java.util.concurrent.Executor;
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
    35
import java.util.function.Consumer;
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
    36
import java.util.function.Supplier;
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
    37
import java.util.stream.Stream;
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
    38
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
    39
import static java.lang.System.Logger.Level.ERROR;
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
    40
import static java.lang.System.Logger.Level.WARNING;
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
    41
import static java.net.http.WSUtils.logger;
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
    42
import static java.util.Objects.requireNonNull;
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
    43
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
    44
/*
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
    45
 * A WebSocket client.
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
    46
 *
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
    47
 * Consists of two independent parts; a transmitter responsible for sending
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
    48
 * messages, and a receiver which notifies the listener of incoming messages.
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
    49
 */
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
    50
final class WS implements WebSocket {
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
    51
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
    52
    private final String subprotocol;
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
    53
    private final RawChannel channel;
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
    54
    private final WSTransmitter transmitter;
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
    55
    private final WSReceiver receiver;
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
    56
    private final Listener listener;
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
    57
    private final Object stateLock = new Object();
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
    58
    private volatile State state = State.CONNECTED;
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
    59
    private final CompletableFuture<Void> whenClosed = new CompletableFuture<>();
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
    60
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
    61
    static CompletableFuture<WebSocket> newInstanceAsync(WSBuilder b) {
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
    62
        CompletableFuture<Result> result = new WSOpeningHandshake(b).performAsync();
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
    63
        Listener listener = b.getListener();
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
    64
        Executor executor = b.getClient().executorService();
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
    65
        return result.thenApply(r -> {
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
    66
            WS ws = new WS(listener, r.subprotocol, r.channel, executor);
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
    67
            ws.start();
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
    68
            return ws;
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
    69
        });
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
    70
    }
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
    71
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
    72
    private WS(Listener listener, String subprotocol, RawChannel channel,
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
    73
               Executor executor) {
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
    74
        this.listener = wrapListener(listener);
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
    75
        this.channel = channel;
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
    76
        this.subprotocol = subprotocol;
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
    77
        Consumer<Throwable> errorHandler = error -> {
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
    78
            if (error == null) {
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
    79
                throw new InternalError();
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
    80
            }
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
    81
            // If the channel is closed, we need to update the state, to denote
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
    82
            // there's no point in trying to continue using WebSocket
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
    83
            if (!channel.isOpen()) {
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
    84
                synchronized (stateLock) {
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
    85
                    tryChangeState(State.ERROR);
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
    86
                }
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
    87
            }
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
    88
        };
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
    89
        transmitter = new WSTransmitter(executor, channel, errorHandler);
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
    90
        receiver = new WSReceiver(this.listener, this, executor, channel);
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
    91
    }
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
    92
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
    93
    private void start() {
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
    94
        receiver.start();
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
    95
    }
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
    96
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
    97
    @Override
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
    98
    public CompletableFuture<Void> sendText(ByteBuffer message, boolean isLast) {
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
    99
        throw new UnsupportedOperationException("Not implemented");
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   100
    }
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   101
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   102
    @Override
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   103
    public CompletableFuture<Void> sendText(CharSequence message, boolean isLast) {
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   104
        requireNonNull(message, "message");
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   105
        synchronized (stateLock) {
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   106
            checkState();
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   107
            return transmitter.sendText(message, isLast);
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   108
        }
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   109
    }
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   110
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   111
    @Override
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   112
    public CompletableFuture<Void> sendText(Stream<? extends CharSequence> message) {
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   113
        requireNonNull(message, "message");
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   114
        synchronized (stateLock) {
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   115
            checkState();
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   116
            return transmitter.sendText(message);
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   117
        }
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   118
    }
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   119
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   120
    @Override
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   121
    public CompletableFuture<Void> sendBinary(ByteBuffer message, boolean isLast) {
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   122
        requireNonNull(message, "message");
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   123
        synchronized (stateLock) {
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   124
            checkState();
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   125
            return transmitter.sendBinary(message, isLast);
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   126
        }
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   127
    }
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   128
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   129
    @Override
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   130
    public CompletableFuture<Void> sendPing(ByteBuffer message) {
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   131
        requireNonNull(message, "message");
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   132
        synchronized (stateLock) {
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   133
            checkState();
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   134
            return transmitter.sendPing(message);
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   135
        }
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   136
    }
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   137
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   138
    @Override
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   139
    public CompletableFuture<Void> sendPong(ByteBuffer message) {
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   140
        requireNonNull(message, "message");
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   141
        synchronized (stateLock) {
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   142
            checkState();
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   143
            return transmitter.sendPong(message);
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   144
        }
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   145
    }
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   146
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   147
    @Override
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   148
    public CompletableFuture<Void> sendClose(CloseCode code, CharSequence reason) {
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   149
        requireNonNull(code, "code");
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   150
        requireNonNull(reason, "reason");
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   151
        synchronized (stateLock) {
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   152
            return doSendClose(() -> transmitter.sendClose(code, reason));
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   153
        }
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   154
    }
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   155
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   156
    @Override
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   157
    public CompletableFuture<Void> sendClose() {
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   158
        synchronized (stateLock) {
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   159
            return doSendClose(() -> transmitter.sendClose());
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   160
        }
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   161
    }
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   162
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   163
    private CompletableFuture<Void> doSendClose(Supplier<CompletableFuture<Void>> s) {
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   164
        checkState();
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   165
        boolean closeChannel = false;
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   166
        synchronized (stateLock) {
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   167
            if (state == State.CLOSED_REMOTELY) {
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   168
                closeChannel = tryChangeState(State.CLOSED);
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   169
            } else {
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   170
                tryChangeState(State.CLOSED_LOCALLY);
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   171
            }
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   172
        }
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   173
        CompletableFuture<Void> sent = s.get();
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   174
        if (closeChannel) {
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   175
            sent.whenComplete((v, t) -> {
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   176
                try {
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   177
                    channel.close();
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   178
                } catch (IOException e) {
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   179
                    logger.log(ERROR, "Error transitioning to state " + State.CLOSED, e);
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   180
                }
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   181
            });
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   182
        }
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   183
        return sent;
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   184
    }
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   185
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   186
    @Override
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   187
    public long request(long n) {
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   188
        if (n < 0L) {
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   189
            throw new IllegalArgumentException("The number must not be negative: " + n);
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   190
        }
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   191
        return receiver.request(n);
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   192
    }
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   193
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   194
    @Override
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   195
    public String getSubprotocol() {
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   196
        return subprotocol;
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   197
    }
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   198
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   199
    @Override
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   200
    public boolean isClosed() {
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   201
        return state.isTerminal();
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   202
    }
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   203
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   204
    @Override
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   205
    public void abort() throws IOException {
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   206
        synchronized (stateLock) {
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   207
            tryChangeState(State.ABORTED);
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   208
        }
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   209
        channel.close();
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   210
    }
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   211
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   212
    @Override
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   213
    public String toString() {
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   214
        return super.toString() + "[" + state + "]";
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   215
    }
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   216
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   217
    private void checkState() {
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   218
        if (state.isTerminal() || state == State.CLOSED_LOCALLY) {
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   219
            throw new IllegalStateException("WebSocket is closed [" + state + "]");
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   220
        }
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   221
    }
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   222
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   223
    /*
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   224
     * Wraps the user's listener passed to the constructor into own listener to
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   225
     * intercept transitions to terminal states (onClose and onError) and to act
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   226
     * upon exceptions and values from the user's listener.
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   227
     */
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   228
    private Listener wrapListener(Listener listener) {
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   229
        return new Listener() {
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   230
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   231
            // Listener's method MUST be invoked in a happen-before order
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   232
            private final Object visibilityLock = new Object();
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   233
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   234
            @Override
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   235
            public void onOpen(WebSocket webSocket) {
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   236
                synchronized (visibilityLock) {
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   237
                    listener.onOpen(webSocket);
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   238
                }
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   239
            }
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   240
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   241
            @Override
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   242
            public CompletionStage<?> onText(WebSocket webSocket, Text message,
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   243
                                             MessagePart part) {
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   244
                synchronized (visibilityLock) {
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   245
                    return listener.onText(webSocket, message, part);
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   246
                }
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   247
            }
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   248
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   249
            @Override
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   250
            public CompletionStage<?> onBinary(WebSocket webSocket, ByteBuffer message,
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   251
                                               MessagePart part) {
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   252
                synchronized (visibilityLock) {
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   253
                    return listener.onBinary(webSocket, message, part);
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   254
                }
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   255
            }
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   256
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   257
            @Override
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   258
            public CompletionStage<?> onPing(WebSocket webSocket, ByteBuffer message) {
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   259
                synchronized (visibilityLock) {
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   260
                    return listener.onPing(webSocket, message);
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   261
                }
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   262
            }
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   263
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   264
            @Override
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   265
            public CompletionStage<?> onPong(WebSocket webSocket, ByteBuffer message) {
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   266
                synchronized (visibilityLock) {
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   267
                    return listener.onPong(webSocket, message);
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   268
                }
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   269
            }
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   270
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   271
            @Override
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   272
            public void onClose(WebSocket webSocket, Optional<CloseCode> code, String reason) {
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   273
                synchronized (stateLock) {
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   274
                    if (state == State.CLOSED_REMOTELY || state.isTerminal()) {
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   275
                        throw new InternalError("Unexpected onClose in state " + state);
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   276
                    } else if (state == State.CLOSED_LOCALLY) {
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   277
                        try {
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   278
                            channel.close();
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   279
                        } catch (IOException e) {
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   280
                            logger.log(ERROR, "Error transitioning to state " + State.CLOSED, e);
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   281
                        }
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   282
                        tryChangeState(State.CLOSED);
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   283
                    } else if (state == State.CONNECTED) {
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   284
                        tryChangeState(State.CLOSED_REMOTELY);
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   285
                    }
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   286
                }
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   287
                synchronized (visibilityLock) {
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   288
                    listener.onClose(webSocket, code, reason);
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   289
                }
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   290
            }
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   291
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   292
            @Override
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   293
            public void onError(WebSocket webSocket, Throwable error) {
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   294
                // An error doesn't necessarily mean the connection must be
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   295
                // closed automatically
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   296
                if (!channel.isOpen()) {
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   297
                    synchronized (stateLock) {
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   298
                        tryChangeState(State.ERROR);
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   299
                    }
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   300
                } else if (error instanceof ProtocolException
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   301
                        && error.getCause() instanceof WSProtocolException) {
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   302
                    WSProtocolException cause = (WSProtocolException) error.getCause();
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   303
                    logger.log(WARNING, "Failing connection {0}, reason: ''{1}''",
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   304
                            webSocket, cause.getMessage());
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   305
                    CloseCode cc = cause.getCloseCode();
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   306
                    transmitter.sendClose(cc, "").whenComplete((v, t) -> {
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   307
                        synchronized (stateLock) {
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   308
                            tryChangeState(State.ERROR);
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   309
                        }
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   310
                        try {
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   311
                            channel.close();
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   312
                        } catch (IOException e) {
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   313
                            logger.log(ERROR, e);
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   314
                        }
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   315
                    });
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   316
                }
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   317
                synchronized (visibilityLock) {
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   318
                    listener.onError(webSocket, error);
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   319
                }
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   320
            }
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   321
        };
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   322
    }
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   323
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   324
    private boolean tryChangeState(State newState) {
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   325
        assert Thread.holdsLock(stateLock);
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   326
        if (state.isTerminal()) {
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   327
            return false;
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   328
        }
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   329
        state = newState;
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   330
        if (newState.isTerminal()) {
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   331
            whenClosed.complete(null);
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   332
        }
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   333
        return true;
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   334
    }
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   335
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   336
    CompletionStage<Void> whenClosed() {
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   337
        return whenClosed;
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   338
    }
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   339
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   340
    /*
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   341
     * WebSocket connection internal state.
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   342
     */
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   343
    private enum State {
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   344
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   345
        /*
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   346
         * Initial WebSocket state. The WebSocket is connected (i.e. remains in
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   347
         * this state) unless proven otherwise. For example, by reading or
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   348
         * writing operations on the channel.
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   349
         */
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   350
        CONNECTED,
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   351
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   352
        /*
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   353
         * A Close message has been received by the client. No more messages
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   354
         * will be received.
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   355
         */
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   356
        CLOSED_REMOTELY,
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   357
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   358
        /*
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   359
         * A Close message has been sent by the client. No more messages can be
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   360
         * sent.
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   361
         */
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   362
        CLOSED_LOCALLY,
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   363
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   364
        /*
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   365
         * Close messages has been both sent and received (closing handshake)
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   366
         * and TCP connection closed. Closed _cleanly_ in terms of RFC 6455.
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   367
         */
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   368
        CLOSED,
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   369
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   370
        /*
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   371
         * The connection has been aborted by the client. Closed not _cleanly_
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   372
         * in terms of RFC 6455.
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   373
         */
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   374
        ABORTED,
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   375
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   376
        /*
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   377
         * The connection has been terminated due to a protocol or I/O error.
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   378
         * Might happen during sending or receiving.
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   379
         */
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   380
        ERROR;
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   381
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   382
        /*
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   383
         * Returns `true` if this state is terminal. If WebSocket has transited
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   384
         * to such a state, if remains in it forever.
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   385
         */
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   386
        boolean isTerminal() {
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   387
            return this == CLOSED || this == ABORTED || this == ERROR;
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   388
        }
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   389
    }
02589df0999a 8087113: Websocket API and implementation
prappo
parents:
diff changeset
   390
}