org.java_websocket.WebSocketImpl Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of Java-WebSocket Show documentation
Show all versions of Java-WebSocket Show documentation
A barebones WebSocket client and server implementation written in 100% Java.
/*
* Copyright (c) 2010-2020 Nathan Rajlich
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
package org.java_websocket;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.ByteChannel;
import java.nio.channels.SelectionKey;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import javax.net.ssl.SSLSession;
import org.java_websocket.drafts.Draft;
import org.java_websocket.drafts.Draft_6455;
import org.java_websocket.enums.CloseHandshakeType;
import org.java_websocket.enums.HandshakeState;
import org.java_websocket.enums.Opcode;
import org.java_websocket.enums.ReadyState;
import org.java_websocket.enums.Role;
import org.java_websocket.exceptions.IncompleteHandshakeException;
import org.java_websocket.exceptions.InvalidDataException;
import org.java_websocket.exceptions.InvalidHandshakeException;
import org.java_websocket.exceptions.LimitExceededException;
import org.java_websocket.exceptions.WebsocketNotConnectedException;
import org.java_websocket.framing.CloseFrame;
import org.java_websocket.framing.Framedata;
import org.java_websocket.framing.PingFrame;
import org.java_websocket.handshake.ClientHandshake;
import org.java_websocket.handshake.ClientHandshakeBuilder;
import org.java_websocket.handshake.Handshakedata;
import org.java_websocket.handshake.ServerHandshake;
import org.java_websocket.handshake.ServerHandshakeBuilder;
import org.java_websocket.interfaces.ISSLChannel;
import org.java_websocket.protocols.IProtocol;
import org.java_websocket.server.WebSocketServer.WebSocketWorker;
import org.java_websocket.util.Charsetfunctions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Represents one end (client or server) of a single WebSocketImpl connection. Takes care of the
* "handshake" phase, then allows for easy sending of text frames, and receiving frames through an
* event-based model.
*/
public class WebSocketImpl implements WebSocket {
/**
* The default port of WebSockets, as defined in the spec. If the nullary constructor is used,
* DEFAULT_PORT will be the port the WebSocketServer is binded to. Note that ports under 1024
* usually require root permissions.
*/
public static final int DEFAULT_PORT = 80;
/**
* The default wss port of WebSockets, as defined in the spec. If the nullary constructor is used,
* DEFAULT_WSS_PORT will be the port the WebSocketServer is binded to. Note that ports under 1024
* usually require root permissions.
*/
public static final int DEFAULT_WSS_PORT = 443;
/**
* Logger instance
*
* @since 1.4.0
*/
private final Logger log = LoggerFactory.getLogger(WebSocketImpl.class);
/**
* Queue of buffers that need to be sent to the client.
*/
public final BlockingQueue outQueue;
/**
* Queue of buffers that need to be processed
*/
public final BlockingQueue inQueue;
/**
* The listener to notify of WebSocket events.
*/
private final WebSocketListener wsl;
private SelectionKey key;
/**
* the possibly wrapped channel object whose selection is controlled by {@link #key}
*/
private ByteChannel channel;
/**
* Helper variable meant to store the thread which ( exclusively ) triggers this objects decode
* method.
**/
private WebSocketWorker workerThread;
/**
* When true no further frames may be submitted to be sent
*/
private boolean flushandclosestate = false;
/**
* The current state of the connection
*/
private volatile ReadyState readyState = ReadyState.NOT_YET_CONNECTED;
/**
* A list of drafts available for this websocket
*/
private List knownDrafts;
/**
* The draft which is used by this websocket
*/
private Draft draft = null;
/**
* The role which this websocket takes in the connection
*/
private Role role;
/**
* the bytes of an incomplete received handshake
*/
private ByteBuffer tmpHandshakeBytes = ByteBuffer.allocate(0);
/**
* stores the handshake sent by this websocket ( Role.CLIENT only )
*/
private ClientHandshake handshakerequest = null;
private String closemessage = null;
private Integer closecode = null;
private Boolean closedremotely = null;
private String resourceDescriptor = null;
/**
* Attribute, when the last pong was received
*/
private long lastPong = System.nanoTime();
/**
* Attribut to synchronize the write
*/
private final Object synchronizeWriteObject = new Object();
/**
* Attribute to store connection attachment
*
* @since 1.3.7
*/
private Object attachment;
/**
* Creates a websocket with server role
*
* @param listener The listener for this instance
* @param drafts The drafts which should be used
*/
public WebSocketImpl(WebSocketListener listener, List drafts) {
this(listener, (Draft) null);
this.role = Role.SERVER;
// draft.copyInstance will be called when the draft is first needed
if (drafts == null || drafts.isEmpty()) {
knownDrafts = new ArrayList<>();
knownDrafts.add(new Draft_6455());
} else {
knownDrafts = drafts;
}
}
/**
* creates a websocket with client role
*
* @param listener The listener for this instance
* @param draft The draft which should be used
*/
public WebSocketImpl(WebSocketListener listener, Draft draft) {
// socket can be null because we want do be able to create the object without already having a bound channel
if (listener == null || (draft == null && role == Role.SERVER)) {
throw new IllegalArgumentException("parameters must not be null");
}
this.outQueue = new LinkedBlockingQueue<>();
inQueue = new LinkedBlockingQueue<>();
this.wsl = listener;
this.role = Role.CLIENT;
if (draft != null) {
this.draft = draft.copyInstance();
}
}
/**
* Method to decode the provided ByteBuffer
*
* @param socketBuffer the ByteBuffer to decode
*/
public void decode(ByteBuffer socketBuffer) {
assert (socketBuffer.hasRemaining());
if (log.isTraceEnabled()) {
log.trace("process({}): ({})", socketBuffer.remaining(),
(socketBuffer.remaining() > 1000 ? "too big to display"
: new String(socketBuffer.array(), socketBuffer.position(), socketBuffer.remaining())));
}
WebSocketListener listener = getWebSocketListener();
if (listener instanceof IDecodeInjector) {
if (((IDecodeInjector) listener).inject(this, socketBuffer)) return;
}
if (readyState != ReadyState.NOT_YET_CONNECTED) {
if (readyState == ReadyState.OPEN) {
decodeFrames(socketBuffer);
}
} else {
if (decodeHandshake(socketBuffer) && (!isClosing() && !isClosed())) {
assert (tmpHandshakeBytes.hasRemaining() != socketBuffer.hasRemaining() || !socketBuffer
.hasRemaining()); // the buffers will never have remaining bytes at the same time
if (socketBuffer.hasRemaining()) {
decodeFrames(socketBuffer);
} else if (tmpHandshakeBytes.hasRemaining()) {
decodeFrames(tmpHandshakeBytes);
}
}
}
}
/**
* Returns whether the handshake phase has is completed. In case of a broken handshake this will
* be never the case.
**/
private boolean decodeHandshake(ByteBuffer socketBufferNew) {
ByteBuffer socketBuffer;
if (tmpHandshakeBytes.capacity() == 0) {
socketBuffer = socketBufferNew;
} else {
if (tmpHandshakeBytes.remaining() < socketBufferNew.remaining()) {
ByteBuffer buf = ByteBuffer
.allocate(tmpHandshakeBytes.capacity() + socketBufferNew.remaining());
tmpHandshakeBytes.flip();
buf.put(tmpHandshakeBytes);
tmpHandshakeBytes = buf;
}
tmpHandshakeBytes.put(socketBufferNew);
tmpHandshakeBytes.flip();
socketBuffer = tmpHandshakeBytes;
}
socketBuffer.mark();
try {
HandshakeState handshakestate;
try {
if (role == Role.SERVER) {
if (draft == null) {
for (Draft d : knownDrafts) {
d = d.copyInstance();
try {
d.setParseMode(role);
socketBuffer.reset();
Handshakedata tmphandshake = d.translateHandshake(socketBuffer);
if (!(tmphandshake instanceof ClientHandshake)) {
log.trace("Closing due to wrong handshake");
closeConnectionDueToWrongHandshake(
new InvalidDataException(CloseFrame.PROTOCOL_ERROR, "wrong http function"));
return false;
}
ClientHandshake handshake = (ClientHandshake) tmphandshake;
handshakestate = d.acceptHandshakeAsServer(handshake);
if (handshakestate == HandshakeState.MATCHED) {
resourceDescriptor = handshake.getResourceDescriptor();
ServerHandshakeBuilder response;
try {
response = wsl.onWebsocketHandshakeReceivedAsServer(this, d, handshake);
} catch (InvalidDataException e) {
log.trace("Closing due to wrong handshake. Possible handshake rejection", e);
closeConnectionDueToWrongHandshake(e);
return false;
} catch (RuntimeException e) {
log.error("Closing due to internal server error", e);
wsl.onWebsocketError(this, e);
closeConnectionDueToInternalServerError(e);
return false;
}
write(d.createHandshake(
d.postProcessHandshakeResponseAsServer(handshake, response)));
draft = d;
open(handshake);
return true;
}
} catch (InvalidHandshakeException e) {
// go on with an other draft
}
}
if (draft == null) {
log.trace("Closing due to protocol error: no draft matches");
closeConnectionDueToWrongHandshake(
new InvalidDataException(CloseFrame.PROTOCOL_ERROR, "no draft matches"));
}
return false;
} else {
// special case for multiple step handshakes
Handshakedata tmphandshake = draft.translateHandshake(socketBuffer);
if (!(tmphandshake instanceof ClientHandshake)) {
log.trace("Closing due to protocol error: wrong http function");
flushAndClose(CloseFrame.PROTOCOL_ERROR, "wrong http function", false);
return false;
}
ClientHandshake handshake = (ClientHandshake) tmphandshake;
handshakestate = draft.acceptHandshakeAsServer(handshake);
if (handshakestate == HandshakeState.MATCHED) {
open(handshake);
return true;
} else {
log.trace("Closing due to protocol error: the handshake did finally not match");
close(CloseFrame.PROTOCOL_ERROR, "the handshake did finally not match");
}
return false;
}
} else if (role == Role.CLIENT) {
draft.setParseMode(role);
Handshakedata tmphandshake = draft.translateHandshake(socketBuffer);
if (!(tmphandshake instanceof ServerHandshake)) {
log.trace("Closing due to protocol error: wrong http function");
flushAndClose(CloseFrame.PROTOCOL_ERROR, "wrong http function", false);
return false;
}
ServerHandshake handshake = (ServerHandshake) tmphandshake;
handshakestate = draft.acceptHandshakeAsClient(handshakerequest, handshake);
if (handshakestate == HandshakeState.MATCHED) {
try {
wsl.onWebsocketHandshakeReceivedAsClient(this, handshakerequest, handshake);
} catch (InvalidDataException e) {
log.trace("Closing due to invalid data exception. Possible handshake rejection", e);
flushAndClose(e.getCloseCode(), e.getMessage(), false);
return false;
} catch (RuntimeException e) {
log.error("Closing since client was never connected", e);
wsl.onWebsocketError(this, e);
flushAndClose(CloseFrame.NEVER_CONNECTED, e.getMessage(), false);
return false;
}
open(handshake);
return true;
} else {
log.trace("Closing due to protocol error: draft {} refuses handshake", draft);
close(CloseFrame.PROTOCOL_ERROR, "draft " + draft + " refuses handshake");
}
}
} catch (InvalidHandshakeException e) {
log.trace("Closing due to invalid handshake", e);
close(e);
}
} catch (IncompleteHandshakeException e) {
if (tmpHandshakeBytes.capacity() == 0) {
socketBuffer.reset();
int newsize = e.getPreferredSize();
if (newsize == 0) {
newsize = socketBuffer.capacity() + 16;
} else {
assert (e.getPreferredSize() >= socketBuffer.remaining());
}
tmpHandshakeBytes = ByteBuffer.allocate(newsize);
tmpHandshakeBytes.put(socketBufferNew);
// tmpHandshakeBytes.flip();
} else {
tmpHandshakeBytes.position(tmpHandshakeBytes.limit());
tmpHandshakeBytes.limit(tmpHandshakeBytes.capacity());
}
}
return false;
}
private void decodeFrames(ByteBuffer socketBuffer) {
List frames;
try {
frames = draft.translateFrame(socketBuffer);
for (Framedata f : frames) {
log.trace("matched frame: {}", f);
draft.processFrame(this, f);
}
} catch (LimitExceededException e) {
if (e.getLimit() == Integer.MAX_VALUE) {
log.error("Closing due to invalid size of frame", e);
wsl.onWebsocketError(this, e);
}
close(e);
} catch (InvalidDataException e) {
log.error("Closing due to invalid data in frame", e);
wsl.onWebsocketError(this, e);
close(e);
} catch (VirtualMachineError | ThreadDeath | LinkageError e) {
log.error("Got fatal error during frame processing");
throw e;
} catch (Error e) {
log.error("Closing web socket due to an error during frame processing");
Exception exception = new Exception(e);
wsl.onWebsocketError(this, exception);
String errorMessage = "Got error " + e.getClass().getName();
close(CloseFrame.UNEXPECTED_CONDITION, errorMessage);
}
}
/**
* Close the connection if the received handshake was not correct
*
* @param exception the InvalidDataException causing this problem
*/
private void closeConnectionDueToWrongHandshake(InvalidDataException exception) {
write(generateHttpResponseDueToError(404));
flushAndClose(exception.getCloseCode(), exception.getMessage(), false);
}
/**
* Close the connection if there was a server error by a RuntimeException
*
* @param exception the RuntimeException causing this problem
*/
private void closeConnectionDueToInternalServerError(RuntimeException exception) {
write(generateHttpResponseDueToError(500));
flushAndClose(CloseFrame.NEVER_CONNECTED, exception.getMessage(), false);
}
/**
* Generate a simple response for the corresponding endpoint to indicate some error
*
* @param errorCode the http error code
* @return the complete response as ByteBuffer
*/
private ByteBuffer generateHttpResponseDueToError(int errorCode) {
String errorCodeDescription;
switch (errorCode) {
case 404:
errorCodeDescription = "404 WebSocket Upgrade Failure";
break;
case 500:
default:
errorCodeDescription = "500 Internal Server Error";
}
return ByteBuffer.wrap(Charsetfunctions.asciiBytes("HTTP/1.1 " + errorCodeDescription
+ "\r\nContent-Type: text/html\r\nServer: TooTallNate Java-WebSocket\r\nContent-Length: "
+ (48 + errorCodeDescription.length()) + "\r\n\r\n"
+ errorCodeDescription + "
"));
}
public synchronized void close(int code, String message, boolean remote) {
if (readyState != ReadyState.CLOSING && readyState != ReadyState.CLOSED) {
if (readyState == ReadyState.OPEN) {
if (code == CloseFrame.ABNORMAL_CLOSE) {
assert (!remote);
readyState = ReadyState.CLOSING;
flushAndClose(code, message, false);
return;
}
if (draft.getCloseHandshakeType() != CloseHandshakeType.NONE) {
try {
if (!remote) {
try {
wsl.onWebsocketCloseInitiated(this, code, message);
} catch (RuntimeException e) {
wsl.onWebsocketError(this, e);
}
}
if (isOpen()) {
CloseFrame closeFrame = new CloseFrame();
closeFrame.setReason(message);
closeFrame.setCode(code);
closeFrame.isValid();
sendFrame(closeFrame);
}
} catch (InvalidDataException e) {
log.error("generated frame is invalid", e);
wsl.onWebsocketError(this, e);
flushAndClose(CloseFrame.ABNORMAL_CLOSE, "generated frame is invalid", false);
}
}
flushAndClose(code, message, remote);
} else if (code == CloseFrame.FLASHPOLICY) {
assert (remote);
flushAndClose(CloseFrame.FLASHPOLICY, message, true);
} else if (code == CloseFrame.PROTOCOL_ERROR) { // this endpoint found a PROTOCOL_ERROR
flushAndClose(code, message, remote);
} else {
flushAndClose(CloseFrame.NEVER_CONNECTED, message, false);
}
readyState = ReadyState.CLOSING;
tmpHandshakeBytes = null;
return;
}
}
@Override
public void close(int code, String message) {
close(code, message, false);
}
/**
* This will close the connection immediately without a proper close handshake. The code and the
* message therefore won't be transferred over the wire also they will be forwarded to
* onClose/onWebsocketClose.
*
* @param code the closing code
* @param message the closing message
* @param remote Indicates who "generated" code
.
* true
means that this endpoint received the code
from
* the other endpoint.
false means this endpoint decided to send the given
* code,
* remote
may also be true if this endpoint started the closing
* handshake since the other endpoint may not simply echo the code
but
* close the connection the same time this endpoint does do but with an other
* code
.
**/
public synchronized void closeConnection(int code, String message, boolean remote) {
if (readyState == ReadyState.CLOSED) {
return;
}
//Methods like eot() call this method without calling onClose(). Due to that reason we have to adjust the ReadyState manually
if (readyState == ReadyState.OPEN) {
if (code == CloseFrame.ABNORMAL_CLOSE) {
readyState = ReadyState.CLOSING;
}
}
if (key != null) {
// key.attach( null ); //see issue #114
key.cancel();
}
if (channel != null) {
try {
channel.close();
} catch (IOException e) {
if (e.getMessage() != null && e.getMessage().equals("Broken pipe")) {
log.trace("Caught IOException: Broken pipe during closeConnection()", e);
} else {
log.error("Exception during channel.close()", e);
wsl.onWebsocketError(this, e);
}
}
}
try {
this.wsl.onWebsocketClose(this, code, message, remote);
} catch (RuntimeException e) {
wsl.onWebsocketError(this, e);
}
if (draft != null) {
draft.reset();
}
handshakerequest = null;
readyState = ReadyState.CLOSED;
}
protected void closeConnection(int code, boolean remote) {
closeConnection(code, "", remote);
}
public void closeConnection() {
if (closedremotely == null) {
throw new IllegalStateException("this method must be used in conjunction with flushAndClose");
}
closeConnection(closecode, closemessage, closedremotely);
}
public void closeConnection(int code, String message) {
closeConnection(code, message, false);
}
public synchronized void flushAndClose(int code, String message, boolean remote) {
if (flushandclosestate) {
return;
}
closecode = code;
closemessage = message;
closedremotely = remote;
flushandclosestate = true;
wsl.onWriteDemand(
this); // ensures that all outgoing frames are flushed before closing the connection
try {
wsl.onWebsocketClosing(this, code, message, remote);
} catch (RuntimeException e) {
log.error("Exception in onWebsocketClosing", e);
wsl.onWebsocketError(this, e);
}
if (draft != null) {
draft.reset();
}
handshakerequest = null;
}
public void eot() {
if (readyState == ReadyState.NOT_YET_CONNECTED) {
closeConnection(CloseFrame.NEVER_CONNECTED, true);
} else if (flushandclosestate) {
closeConnection(closecode, closemessage, closedremotely);
} else if (draft.getCloseHandshakeType() == CloseHandshakeType.NONE) {
closeConnection(CloseFrame.NORMAL, true);
} else if (draft.getCloseHandshakeType() == CloseHandshakeType.ONEWAY) {
if (role == Role.SERVER) {
closeConnection(CloseFrame.ABNORMAL_CLOSE, true);
} else {
closeConnection(CloseFrame.NORMAL, true);
}
} else {
closeConnection(CloseFrame.ABNORMAL_CLOSE, true);
}
}
@Override
public void close(int code) {
close(code, "", false);
}
public void close(InvalidDataException e) {
close(e.getCloseCode(), e.getMessage(), false);
}
/**
* Send Text data to the other end.
*
* @throws WebsocketNotConnectedException websocket is not yet connected
*/
@Override
public void send(String text) {
if (text == null) {
throw new IllegalArgumentException("Cannot send 'null' data to a WebSocketImpl.");
}
send(draft.createFrames(text, role == Role.CLIENT));
}
/**
* Send Binary data (plain bytes) to the other end.
*
* @throws IllegalArgumentException the data is null
* @throws WebsocketNotConnectedException websocket is not yet connected
*/
@Override
public void send(ByteBuffer bytes) {
if (bytes == null) {
throw new IllegalArgumentException("Cannot send 'null' data to a WebSocketImpl.");
}
send(draft.createFrames(bytes, role == Role.CLIENT));
}
@Override
public void send(byte[] bytes) {
send(ByteBuffer.wrap(bytes));
}
private void send(Collection frames) {
if (!isOpen()) {
throw new WebsocketNotConnectedException();
}
if (frames == null) {
throw new IllegalArgumentException();
}
ArrayList outgoingFrames = new ArrayList<>();
for (Framedata f : frames) {
log.trace("send frame: {}", f);
outgoingFrames.add(draft.createBinaryFrame(f));
}
write(outgoingFrames);
}
@Override
public void sendFragmentedFrame(Opcode op, ByteBuffer buffer, boolean fin) {
send(draft.continuousFrame(op, buffer, fin));
}
@Override
public void sendFrame(Collection frames) {
send(frames);
}
@Override
public void sendFrame(Framedata framedata) {
send(Collections.singletonList(framedata));
}
public void sendPing() throws NullPointerException {
// Gets a PingFrame from WebSocketListener(wsl) and sends it.
PingFrame pingFrame = wsl.onPreparePing(this);
if (pingFrame == null) {
throw new NullPointerException(
"onPreparePing(WebSocket) returned null. PingFrame to sent can't be null.");
}
sendFrame(pingFrame);
}
@Override
public boolean hasBufferedData() {
return !this.outQueue.isEmpty();
}
public void startHandshake(ClientHandshakeBuilder handshakedata)
throws InvalidHandshakeException {
// Store the Handshake Request we are about to send
this.handshakerequest = draft.postProcessHandshakeRequestAsClient(handshakedata);
resourceDescriptor = handshakedata.getResourceDescriptor();
assert (resourceDescriptor != null);
// Notify Listener
try {
wsl.onWebsocketHandshakeSentAsClient(this, this.handshakerequest);
} catch (InvalidDataException e) {
// Stop if the client code throws an exception
throw new InvalidHandshakeException("Handshake data rejected by client.");
} catch (RuntimeException e) {
log.error("Exception in startHandshake", e);
wsl.onWebsocketError(this, e);
throw new InvalidHandshakeException("rejected because of " + e);
}
// Send
write(draft.createHandshake(this.handshakerequest));
}
private void write(ByteBuffer buf) {
log.trace("write({}): {}", buf.remaining(),
buf.remaining() > 1000 ? "too big to display" : new String(buf.array()));
outQueue.add(buf);
wsl.onWriteDemand(this);
}
/**
* Write a list of bytebuffer (frames in binary form) into the outgoing queue
*
* @param bufs the list of bytebuffer
*/
private void write(List bufs) {
synchronized (synchronizeWriteObject) {
for (ByteBuffer b : bufs) {
write(b);
}
}
}
private void open(Handshakedata d) {
log.trace("open using draft: {}", draft);
readyState = ReadyState.OPEN;
updateLastPong();
try {
wsl.onWebsocketOpen(this, d);
} catch (RuntimeException e) {
wsl.onWebsocketError(this, e);
}
}
@Override
public boolean isOpen() {
return readyState == ReadyState.OPEN;
}
@Override
public boolean isClosing() {
return readyState == ReadyState.CLOSING;
}
@Override
public boolean isFlushAndClose() {
return flushandclosestate;
}
@Override
public boolean isClosed() {
return readyState == ReadyState.CLOSED;
}
@Override
public ReadyState getReadyState() {
return readyState;
}
/**
* @param key the selection key of this implementation
*/
public void setSelectionKey(SelectionKey key) {
this.key = key;
}
/**
* @return the selection key of this implementation
*/
public SelectionKey getSelectionKey() {
return key;
}
@Override
public String toString() {
return super.toString(); // its nice to be able to set breakpoints here
}
@Override
public InetSocketAddress getRemoteSocketAddress() {
return wsl.getRemoteSocketAddress(this);
}
@Override
public InetSocketAddress getLocalSocketAddress() {
return wsl.getLocalSocketAddress(this);
}
@Override
public Draft getDraft() {
return draft;
}
@Override
public void close() {
close(CloseFrame.NORMAL);
}
@Override
public String getResourceDescriptor() {
return resourceDescriptor;
}
/**
* Getter for the last pong received
*
* @return the timestamp for the last received pong
*/
long getLastPong() {
return lastPong;
}
/**
* Update the timestamp when the last pong was received
*/
public void updateLastPong() {
this.lastPong = System.nanoTime();
}
/**
* Getter for the websocket listener
*
* @return the websocket listener associated with this instance
*/
public WebSocketListener getWebSocketListener() {
return wsl;
}
@Override
@SuppressWarnings("unchecked")
public T getAttachment() {
return (T) attachment;
}
@Override
public boolean hasSSLSupport() {
return channel instanceof ISSLChannel;
}
@Override
public SSLSession getSSLSession() {
if (!hasSSLSupport()) {
throw new IllegalArgumentException(
"This websocket uses ws instead of wss. No SSLSession available.");
}
return ((ISSLChannel) channel).getSSLEngine().getSession();
}
@Override
public IProtocol getProtocol() {
if (draft == null) {
return null;
}
if (!(draft instanceof Draft_6455)) {
throw new IllegalArgumentException("This draft does not support Sec-WebSocket-Protocol");
}
return ((Draft_6455) draft).getProtocol();
}
@Override
public void setAttachment(T attachment) {
this.attachment = attachment;
}
public ByteChannel getChannel() {
return channel;
}
public void setChannel(ByteChannel channel) {
this.channel = channel;
}
public WebSocketWorker getWorkerThread() {
return workerThread;
}
public void setWorkerThread(WebSocketWorker workerThread) {
this.workerThread = workerThread;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy