Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.xlightweb.WebSocketConnection Maven / Gradle / Ivy
Go to download
xLightweb is a lightweight, high performance,
scalable web network library
/*
* Copyright (c) xlightweb.org, 2008 - 2010. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Please refer to the LGPL license at: http://www.gnu.org/copyleft/lesser.txt
* The latest copy of this software may be found on http://www.xlightweb.org/
*/
package org.xlightweb;
import java.io.IOException;
import java.net.InetAddress;
import java.net.SocketTimeoutException;
import java.net.URI;
import java.net.URL;
import java.nio.BufferUnderflowException;
import java.nio.ByteBuffer;
import java.nio.channels.ClosedChannelException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.xlightweb.AbstractHttpConnection.IMultimodeExecutor;
import org.xlightweb.WebSocketHandlerAdapter.IPostConnectInterceptor;
import org.xlightweb.client.HttpClientConnection;
import org.xlightweb.client.IHttpClientEndpoint;
import org.xlightweb.server.HttpServerConnection;
import org.xsocket.DataConverter;
import org.xsocket.Execution;
import org.xsocket.MaxReadSizeExceededException;
import org.xsocket.connection.IConnectHandler;
import org.xsocket.connection.IDataHandler;
import org.xsocket.connection.IDisconnectHandler;
import org.xsocket.connection.INonBlockingConnection;
import org.xsocket.connection.NonBlockingConnectionPool;
/**
* NonBlockingWebSocketConnection
*
* This is an experimental implementation of the HTML5 draft and subject to change
*
*
* @author grro
*/
public final class WebSocketConnection implements IWebSocketConnection {
private static final Logger LOG = Logger.getLogger(WebSocketConnection.class.getName());
private INonBlockingConnection tcpConnection;
// sec key computation
private static final Random RANDOM = new Random();
// receive timeout
public static final int DEFAULT_RECEIVE_TIMEOUT = Integer.MAX_VALUE;
private int receiveTimeoutSec = DEFAULT_RECEIVE_TIMEOUT;
// protocol handling
private final WebSocketProtocolHandler protocolHandler = new WebSocketProtocolHandler();
private final IMultimodeExecutor executor;
private final List inQueue = new ArrayList();
private int inQueueVersion = 0;
// client protocol mode
private static final boolean CLIENT_USING_SEC_KEY = Boolean.parseBoolean(System.getProperty("org.xlightweb.websocket.client.usingSecKey", "true"));
// upgrade headers
private final IHttpRequestHeader upgradeRequestHeader;
private final IHttpResponseHeader upgradeResponseHeader;
// interceptor support
private final IPostWriteInterceptor interceptor;
// web socket handler
private final Object webSocketHandlerGuard = new Object();
private WebSocketHandlerAdapter webSocketHandlerAdapter = null;
// exception holder
private final AtomicReference exceptionRef = new AtomicReference(null);
// flags
private final AtomicBoolean isDisconnected = new AtomicBoolean(false);
private final AtomicBoolean isCloseMsgSent = new AtomicBoolean(false);
// attachment management
private AtomicReference attachmentRef = new AtomicReference(null);
public WebSocketConnection(String uriString) throws IOException {
this(uriString, (String) null);
}
public WebSocketConnection(String uriString, String protocol) throws IOException {
this(uriString, protocol, null);
}
public WebSocketConnection(String uriString, IWebSocketHandler webSocketHandler) throws IOException {
this(uriString, null, webSocketHandler);
}
public WebSocketConnection(String uriString, String protocol, IWebSocketHandler webSocketHandler) throws IOException {
this(URI.create(uriString), protocol, webSocketHandler);
}
private WebSocketConnection(URI uri, String protocol, IWebSocketHandler webSocketHandler) throws IOException {
this(connect(uri), uri, protocol, webSocketHandler);
}
private static HttpClientConnection connect(URI uri) throws IOException {
int port = uri.getPort();
if (port == -1) {
if (uri.getScheme().toLowerCase(Locale.US).equals("wss")) {
port = 443;
} else {
port = 80;
}
}
return new HttpClientConnection(uri.getHost(), port);
}
public WebSocketConnection(IHttpClientEndpoint httpClientEndpoint, URI uri, String protocol, IWebSocketHandler webSocketHandler) throws IOException {
this(performHandshake(httpClientEndpoint, uri, protocol), webSocketHandler);
}
private WebSocketConnection(HandeshakeResult handeshakeResult, IWebSocketHandler webSocketHandler) throws IOException {
this(handeshakeResult.con, webSocketHandler, handeshakeResult.upgradeRequestHeader, handeshakeResult.upgradeResponseHeader);
}
private WebSocketConnection(HttpClientConnection httpConnection, IWebSocketHandler webSocketHandler, IHttpRequestHeader upgradeRequestHeader, IHttpResponseHeader upgradeResponseHeader) throws IOException {
this(convertToTcpConnection(httpConnection), null, webSocketHandler, null, upgradeRequestHeader, upgradeResponseHeader);
}
private static HandeshakeResult performHandshake(IHttpClientEndpoint httpClientEndpoint, URI uri, String protocol) throws IOException {
GetRequest request;
byte[] challengeMd5 = null;
if (CLIENT_USING_SEC_KEY) {
ByteBuffer buf = ByteBuffer.allocate(16);
for (int i = 0; i < buf.limit(); i++) {
buf.put((byte) (33 + RANDOM.nextInt(80)));
}
buf.flip();
long key1 = buf.getInt();
long key2 = buf.getInt();
long challenge = buf.getLong();
challengeMd5 = computeMD5((int) key1, (int) key2, challenge);
// obfuscating the keys
int numSpaces1 = 10 + RANDOM.nextInt(Integer.MAX_VALUE) % 5;
int numSpaces2 = 10 + RANDOM.nextInt(Integer.MAX_VALUE) % 5;
String secKey1 = new String(generateSecKey(35, key1 * numSpaces1, numSpaces1));
String secKey2 = new String(generateSecKey(34, key2 * numSpaces2, numSpaces2));
request = new GetRequest(uri.toString(), DataConverter.toByteBuffer(challenge).array());
request.removeHeader("Content-Length");
request.setHeader("Sec-WebSocket-Key1", secKey1);
request.setHeader("Sec-WebSocket-Key2", secKey2);
} else {
request = new GetRequest(uri.toString());
}
if (protocol != null) {
if (CLIENT_USING_SEC_KEY) {
request.setHeader("Sec-WebSocket-Protocol", protocol);
} else {
request.setHeader("WebSocket-Protocol", protocol);
}
}
request.setHeader("Upgrade", "WebSocket");
request.setHeader("Connection", "Upgrade");
URL originURL = request.getRequestUrl();
String origin = originURL.getProtocol() + "//" + originURL.getHost();
if (originURL.getPort() != -1) {
origin += ":" + originURL.getPort();
}
request.setHeader("Origin", origin);
IHttpResponse response = httpClientEndpoint.call(request);
if (response.getStatus() != 101) {
if (response.getStatus() == 501) {
if (response.hasBody()) {
throw new UnsupportedProtocolException(response.getBody().toString());
} else {
throw new UnsupportedProtocolException();
}
} else {
throw new IOException(response.getStatus() + " " + response.getReason());
}
}
if (challengeMd5 != null) {
boolean match = true;
byte[] challengeMd5Response = response.getBody().readBytesByLength(16);
for (int i = 0; i < challengeMd5.length; i++) {
if (challengeMd5[i] != challengeMd5Response[i]) {
match = false;
}
}
if (!match) {
throw new IOException("server returns wrong md5");
}
}
HttpClientConnection httpCon = (HttpClientConnection) HttpUtils.getConnectionFromAttribute(response.getResponseHeader());
return new HandeshakeResult(httpCon, request.getRequestHeader(), response.getResponseHeader());
}
private static byte[] generateSecKey(int length, long key, int numSpaces) {
while (true) {
byte[] num = Long.toString(key).getBytes();
int numIdx = 0;
byte[] data = new byte[length];
for (int i = 0; i < data.length; i++) {
boolean isPrintNum = ((RANDOM.nextInt(length / num.length) % 2) == 1);
if (isPrintNum && (numIdx < num.length)) {
data[i] = num[numIdx];
numIdx++;
} else {
// generated random char (printable chars only)
data[i] = (byte) (33 + RANDOM.nextInt(15));
}
}
// add spaces
int spacesIdx = 0;
while (spacesIdx < numSpaces) {
int idx = 1 + RANDOM.nextInt(length - 3);
if ((data[idx] == 32) || ((data[idx] >= 48) && (data[idx] <= 57))) {
continue;
}
data[idx] = 32;
spacesIdx++;
}
if (numIdx == num.length) {
return data;
}
}
}
private static byte[] computeMD5(int key1, int key2, long num) throws IOException {
ByteBuffer buf = ByteBuffer.allocate(16);
buf.putInt(key1);
buf.putInt(key2);
buf.putLong(num);
buf.flip();
String s = DataConverter.toString(buf);
try {
MessageDigest md5 = MessageDigest.getInstance("MD5");
md5.reset();
md5.update(s.getBytes());
return md5.digest();
} catch (NoSuchAlgorithmException nse) {
throw new IOException(nse.toString());
}
}
private static final class HandeshakeResult {
private HttpClientConnection con;
private IHttpRequestHeader upgradeRequestHeader;
private IHttpResponseHeader upgradeResponseHeader;
public HandeshakeResult(HttpClientConnection con, IHttpRequestHeader upgradeRequestHeader, IHttpResponseHeader upgradeResponseHeader) {
this.con = con;
this.upgradeRequestHeader = upgradeRequestHeader;
this.upgradeResponseHeader = upgradeResponseHeader;
}
}
public WebSocketConnection(HttpServerConnection httpConnection, IWebSocketHandler webSocketHandler, IHttpExchange exchange) throws IOException {
this(httpConnection, webSocketHandler, new UpgradeResponseSender(exchange));
}
private WebSocketConnection(HttpServerConnection httpConnection, IWebSocketHandler webSocketHandler, UpgradeResponseSender upgradeHandler) throws IOException {
this(httpConnection, webSocketHandler, upgradeHandler.getRequestHeader(), upgradeHandler.getResponseHeader(), upgradeHandler);
}
private static INonBlockingConnection convertToTcpConnection(AbstractHttpConnection httpConnection) throws IOException {
INonBlockingConnection tcpCon = httpConnection.getUnderlyingTcpConnection();
tcpCon.setHandler(null);
return tcpCon;
}
private WebSocketConnection(HttpServerConnection httpConnection, IWebSocketHandler webSocketHandler, IHttpRequestHeader upgradeRequestHeader, IHttpResponseHeader upgradeResponseHeader, UpgradeResponseSender upgradeResponseSender) throws IOException {
this(convertToTcpConnection(httpConnection), upgradeResponseSender, webSocketHandler, upgradeResponseSender, upgradeRequestHeader, upgradeResponseHeader);
}
private static final class UpgradeResponseSender implements IPostConnectInterceptor, IPostWriteInterceptor {
private final IHttpExchange exchange;
private final AtomicBoolean isUpgradeSent = new AtomicBoolean(false);
private final byte[] md5;
private final IHttpResponseHeader responseHeader;
public UpgradeResponseSender(IHttpExchange exchange) throws IOException {
this.exchange = exchange;
IHttpRequest request = exchange.getRequest();
String webSocketLocation;
if (request.isSecure()) {
webSocketLocation = "wss://" + request.getHost() + request.getRequestURI();
} else {
webSocketLocation = "ws://" + request.getHost() + request.getRequestURI();
}
if (request.getHeader("Sec-WebSocket-Key1") != null) {
String protocol = request.getHeader("Sec-WebSocket-Protocol");
String webSocketOrigin = request.getHeader("Origin");
String secKey1 = request.getHeader("Sec-WebSocket-Key1");
long obfuscatedKey1 = extractNumber(secKey1.getBytes());
int numSpacesKey1 = computeNumSpaces(secKey1);
int key1 = (int) (obfuscatedKey1 / numSpacesKey1);
String secKey2 = request.getHeader("Sec-WebSocket-Key2");
long obfuscatedKey2 = extractNumber(secKey2.getBytes());
int numSpacesKey2 = computeNumSpaces(secKey2);
int key2 = (int) (obfuscatedKey2 / numSpacesKey2);
long challenge = request.getBody().readLong();
md5 = computeMD5(key1, key2, challenge);
responseHeader = new HttpResponseHeader(101);
responseHeader.setReason("Web Socket Protocol Handshake");
responseHeader.setHeader("Upgrade", "WebSocket");
responseHeader.setHeader("Connection", "Upgrade");
responseHeader.setHeader("Sec-WebSocket-Origin", webSocketOrigin);
responseHeader.setHeader("Sec-WebSocket-Location", webSocketLocation);
if (protocol != null) {
responseHeader.setHeader("Sec-WebSocket-Protocol", protocol);
}
} else {
md5 = null;
String protocol = request.getHeader("WebSocket-Protocol");
String webSocketOrigin = request.getHeader("Origin");
responseHeader = new HttpResponseHeader(101);
responseHeader.setReason("Web Socket Protocol Handshake");
responseHeader.setHeader("Upgrade", "WebSocket");
responseHeader.setHeader("Connection", "Upgrade");
responseHeader.setHeader("WebSocket-Origin", webSocketOrigin);
responseHeader.setHeader("WebSocket-Location", webSocketLocation);
if (protocol != null) {
responseHeader.setHeader("WebSocket-Protocol", protocol);
}
}
}
public void onConnectException(IOException ioe) {
if (ioe instanceof UnsupportedProtocolException) {
exchange.sendError(501, ioe.getMessage());
} else {
exchange.sendError(501);
}
}
public void onPostConnect() throws IOException {
sentUpgradeIfNecessary();
}
public void onPreWrite() throws IOException {
sentUpgradeIfNecessary() ;
}
private void sentUpgradeIfNecessary() throws IOException {
if (!isUpgradeSent.getAndSet(true)) {
HttpResponse response;
if (md5 == null) {
response = new HttpResponse(responseHeader);
} else {
response = new HttpResponse(responseHeader, DataConverter.toByteBuffer(md5).array());
response.removeHeader("Content-Length");
}
exchange.send(response);
}
}
IHttpRequestHeader getRequestHeader() {
return exchange.getRequest().getRequestHeader();
}
IHttpResponseHeader getResponseHeader() {
return responseHeader;
}
}
private static int computeNumSpaces(String key) {
int numSpaces = 0;
for (int b : key.getBytes()) {
if (b == (int) ' ') {
numSpaces++;
}
}
return numSpaces;
}
private static long extractNumber(byte[] key) {
StringBuilder sb = new StringBuilder();
for (int b : key) {
if ((b >= 48) && (b <= 57)) {
sb.append((char) b);
}
}
return Long.parseLong(sb.toString());
}
private WebSocketConnection(INonBlockingConnection tcpConnection, IPostWriteInterceptor connectInterceptor, IWebSocketHandler webSocketHandler, IPostConnectInterceptor postConnectInterceptor, IHttpRequestHeader upgradeRequestHeader, IHttpResponseHeader upgradeResponseHeader) throws IOException {
this.interceptor = connectInterceptor;
this.tcpConnection = tcpConnection;
this.upgradeRequestHeader = upgradeRequestHeader;
this.upgradeResponseHeader = upgradeResponseHeader;
executor = HttpUtils.newMultimodeExecutor(tcpConnection.getWorkerpool());
setMessageHandler(webSocketHandler, postConnectInterceptor);
protocolHandler.onConnect(tcpConnection);
tcpConnection.setHandler(protocolHandler);
}
/**
* {@inheritDoc}
*/
public String getProtocol() {
return getUpgradeResponseHeader().getHeader("WebSocket-Protocol");
}
/**
* {@inheritDoc}
*/
public String getWebSocketLocation() {
return getUpgradeResponseHeader().getHeader("WebSocket-Location");
}
/**
* {@inheritDoc}
*/
public String getWebSocketOrigin() {
return getUpgradeResponseHeader().getHeader("WebSocket-Origin");
}
public IHttpRequestHeader getUpgradeRequestHeader() {
return upgradeRequestHeader;
}
public IHttpResponseHeader getUpgradeResponseHeader() {
return upgradeResponseHeader;
}
public void destroy() {
// destroy (underlying tcp) connection by using connection pool. The underlying connection could be a pooled one)
// The connection pool detects automatically if the connection is pooled or not. The connection will be
// closed (logically) anyway
try {
NonBlockingConnectionPool.destroy(tcpConnection);
} catch (IOException ioe) {
if (LOG.isLoggable(Level.FINE)) {
LOG.fine("[" + getId() + "] error occured by destroying htttp connection " + getId() + " " + ioe.toString());
}
}
}
public INonBlockingConnection getUnderlyingTcpConnection() {
return tcpConnection;
}
public int availableMessages() {
synchronized (inQueue) {
return inQueue.size();
}
}
int getInQueueVersion() {
synchronized (inQueue) {
return inQueueVersion;
}
}
@Override
public String toString() {
return tcpConnection.toString();
}
/**
* {@inheritDoc}
*/
public TextMessage readTextMessage() throws IOException, SocketTimeoutException {
WebSocketMessage msg = readMessage();
if (msg.isTextMessage()) {
return (TextMessage) msg;
} else {
throw new IOException("got a " + msg.getClass().getSimpleName() + " message");
}
}
/**
* {@inheritDoc}
*/
public WebSocketMessage readMessage() throws BufferUnderflowException, SocketTimeoutException, ClosedChannelException, IOException {
long start = System.currentTimeMillis();
long remainingTime = receiveTimeoutSec;
do {
synchronized (inQueue) {
IOException ioe = exceptionRef.getAndSet(null);
if (ioe != null) {
throw ioe;
}
if (isDisconnected.get()) {
throw new ClosedChannelException();
}
if (inQueue.isEmpty()) {
try {
inQueue.wait(remainingTime);
} catch (InterruptedException ie) {
// Restore the interrupted status
Thread.currentThread().interrupt();
}
} else {
inQueueVersion++;
return inQueue.remove(0);
}
}
remainingTime = HttpUtils.computeRemainingTime(start, receiveTimeoutSec);
} while (remainingTime > 0);
if (LOG.isLoggable(Level.FINE)) {
LOG.fine("receive timeout " + receiveTimeoutSec + " sec reached. throwing timeout exception");
}
throw new SocketTimeoutException("timeout " + receiveTimeoutSec + " sec reached");
}
public int writeMessage(TextMessage msg) throws IOException {
return writeMessage((WebSocketMessage) msg);
}
public int writeMessage(WebSocketMessage msg) throws IOException {
if (isCloseMsgSent.get()) {
throw new ClosedChannelException();
}
return writeMessageIgnoreClose(msg);
}
public void writeMessage(TextMessage msg, IWriteCompleteHandler completeHandler) throws IOException {
writeMessage((WebSocketMessage) msg, completeHandler);
}
private int writeMessageIgnoreClose(WebSocketMessage msg) throws IOException {
if (interceptor != null) {
interceptor.onPreWrite();
}
return msg.writeTo(this, null);
}
public void writeMessage(WebSocketMessage msg, IWriteCompleteHandler writtenHandler) throws IOException {
if (isCloseMsgSent.get()) {
throw new ClosedChannelException();
}
if (interceptor != null) {
interceptor.onPreWrite();
}
msg.writeTo(this, writtenHandler);
}
public void closeQuitly() {
try {
close();
} catch (IOException ioe) {
if (LOG.isLoggable(Level.FINE)) {
LOG.fine("[" + getId() + "] error occured by closing connection " + getId() + " " + ioe.toString());
}
try {
NonBlockingConnectionPool.destroy(tcpConnection);
} catch (IOException e) {
if (LOG.isLoggable(Level.FINE)) {
LOG.fine("[" + getId() + "] error occured by closing connection " + getId() + " " + e.toString());
}
}
}
}
public void close() throws IOException {
if (!isCloseMsgSent.getAndSet(true)) {
if (LOG.isLoggable(Level.FINE)) {
LOG.fine("[" + getId() + "] sending close message");
}
writeMessageIgnoreClose(new CloseMessage());
}
}
void processNonthreaded(Runnable task) {
executor.processNonthreaded(task);
}
void processMultithreaded(Runnable task) {
executor.processMultithreaded(task);
}
private void setMessageHandler(IWebSocketHandler webSocketHandler, IPostConnectInterceptor postConnectInterceptor) throws IOException {
synchronized (webSocketHandlerGuard) {
if (webSocketHandlerAdapter != null) {
webSocketHandlerAdapter.onDisconnect(this);
}
webSocketHandlerAdapter = new WebSocketHandlerAdapter(webSocketHandler, postConnectInterceptor);
webSocketHandlerAdapter.onConnect(this);
}
}
public boolean isOpen() {
return !isCloseMsgSent.get() && tcpConnection.isOpen();
}
public boolean isServerSide() {
return tcpConnection.isServerSide();
}
public INonBlockingConnection getTcpConnection() {
return tcpConnection;
}
public void setAttachment(Object obj) {
attachmentRef.set(obj);
}
public Object getAttachment() {
return attachmentRef.get();
}
public long getConnectionTimeoutMillis() {
return tcpConnection.getConnectionTimeoutMillis();
}
public void setConnectionTimeoutMillis(long timeoutMillis) {
tcpConnection.setConnectionTimeoutMillis(timeoutMillis);
}
public long getRemainingMillisToConnectionTimeout() {
return tcpConnection.getRemainingMillisToConnectionTimeout();
}
public long getIdleTimeoutMillis() {
return tcpConnection.getIdleTimeoutMillis();
}
public void setIdleTimeoutMillis(long timeoutInMillis) {
tcpConnection.setIdleTimeoutMillis(timeoutInMillis);
}
public long getRemainingMillisToIdleTimeout() {
return tcpConnection.getRemainingMillisToIdleTimeout();
}
public String getId() {
return tcpConnection.getId();
}
public int getLocalPort() {
return tcpConnection.getLocalPort();
}
public InetAddress getLocalAddress() {
return tcpConnection.getLocalAddress();
}
public InetAddress getRemoteAddress() {
return tcpConnection.getRemoteAddress();
}
public int getRemotePort() {
return tcpConnection.getRemotePort();
}
public Object getOption(String name) throws IOException {
return tcpConnection.getOption(name);
}
@SuppressWarnings("unchecked")
public Map getOptions() {
return tcpConnection.getOptions();
}
public void setOption(String name, Object value) throws IOException {
tcpConnection.setOption(name, value);
}
@Execution(Execution.NONTHREADED)
private final class WebSocketProtocolHandler implements IConnectHandler, IDataHandler, IDisconnectHandler {
// network data
private ByteBuffer rawBuffer = null;
public boolean onConnect(INonBlockingConnection connection) throws IOException, BufferUnderflowException, MaxReadSizeExceededException {
synchronized (webSocketHandlerGuard) {
if (webSocketHandlerAdapter != null) {
webSocketHandlerAdapter.onConnect(WebSocketConnection.this);
}
}
return true;
}
public boolean onData(INonBlockingConnection connection) throws IOException, BufferUnderflowException, ClosedChannelException, MaxReadSizeExceededException {
if (connection.isOpen()) {
// copying available network data into raw data buffer
int available = connection.available();
ByteBuffer[] data = null;
if (available > 0) {
data = connection.readByteBufferByLength(available);
}
onData(data);
}
return true;
}
void onData(ByteBuffer[] data) throws IOException {
if (data == null) {
if (rawBuffer == null) {
rawBuffer = ByteBuffer.allocate(0);
}
} else {
if (rawBuffer == null) {
rawBuffer = HttpUtils.merge(data);
} else {
rawBuffer = HttpUtils.merge(rawBuffer, data);
}
}
parse(rawBuffer);
if (!rawBuffer.hasRemaining()) {
rawBuffer = null;
}
}
void parse(ByteBuffer buffer) throws IOException {
while (buffer.hasRemaining()) {
WebSocketMessage msg = WebSocketMessage.parse(buffer);
if (msg == null) {
return;
} else {
if (msg.isTextMessage()) {
synchronized (inQueue) {
inQueueVersion++;
inQueue.add(msg);
inQueue.notifyAll();
}
synchronized (webSocketHandlerGuard) {
if (webSocketHandlerAdapter != null) {
webSocketHandlerAdapter.onMessage(WebSocketConnection.this);
}
}
} else if (msg.isCloseMessage()) {
if (isCloseMsgSent.get()) {
if (LOG.isLoggable(Level.FINE)) {
LOG.fine("[" + getId() + "] echo close msg reveived. Destroying connection");
}
writeMessageIgnoreClose(msg);
destroy();
// peer initiated close
} else {
if (LOG.isLoggable(Level.FINE)) {
LOG.fine("[" + getId() + "] close msg reveived. echoing it and destroying connection");
}
isCloseMsgSent.set(true);
writeMessageIgnoreClose(msg);
destroy();
}
} else {
if (LOG.isLoggable(Level.FINE)) {
LOG.fine("[" + getId() + "] binary message received. The ws draft does not longer allow binary messages. Ignoring it");
}
}
}
}
}
public boolean onDisconnect(INonBlockingConnection connection) throws IOException {
synchronized (inQueue) {
isDisconnected.set(true);
if ((rawBuffer != null) && rawBuffer.hasRemaining()) {
exceptionRef.set(new IOException("connection terminated while receiving data"));
}
inQueue.notifyAll();
}
synchronized (webSocketHandlerGuard) {
if (webSocketHandlerAdapter != null) {
webSocketHandlerAdapter.onDisconnect(WebSocketConnection.this);
}
}
return true;
}
}
private static interface IPostWriteInterceptor {
void onPreWrite() throws IOException ;
}
}