@SuppressWarnings("unchecked") private void sendMessageBinary(ByteBuffer msg, boolean last) throws WsIOException { if (binaryMsgHandler instanceof WrappedMessageHandler) { long maxMessageSize = ((WrappedMessageHandler) binaryMsgHandler).getMaxMessageSize(); if (maxMessageSize > -1 && msg.remaining() > maxMessageSize) { throw new WsIOException(new CloseReason(CloseCodes.TOO_BIG, sm.getString("wsFrame.messageTooBig", Long.valueOf(msg.remaining()), Long.valueOf(maxMessageSize)))); } } try { if (binaryMsgHandler instanceof MessageHandler.Partial<?>) { ((MessageHandler.Partial<ByteBuffer>) binaryMsgHandler).onMessage(msg, last); } else { // Caller ensures last == true if this branch is used ((MessageHandler.Whole<ByteBuffer>) binaryMsgHandler).onMessage(msg); } } catch(Throwable t) { handleThrowableOnSend(t); } }
private void handleSendFailureWithEncode(Throwable t) throws IOException, EncodeException { // First, unwrap any execution exception if (t instanceof ExecutionException) { t = t.getCause(); } // Close the session wsSession.doClose(new CloseReason(CloseCodes.GOING_AWAY, t.getMessage()), new CloseReason(CloseCodes.CLOSED_ABNORMALLY, t.getMessage())); // Rethrow the exception if (t instanceof EncodeException) { throw (EncodeException) t; } if (t instanceof IOException) { throw (IOException) t; } throw new IOException(t); }
/** * Cleans up the resources still in use by WebSocket sessions created from * this container. This includes closing sessions and cancelling * {@link Future}s associated with blocking read/writes. */ public void destroy() { CloseReason cr = new CloseReason( CloseCodes.GOING_AWAY, sm.getString("wsWebSocketContainer.shutdown")); for (WsSession session : sessions.keySet()) { try { session.close(cr); } catch (IOException ioe) { log.debug(sm.getString( "wsWebSocketContainer.sessionCloseFail", session.getId()), ioe); } } // Only unregister with AsyncChannelGroupUtil if this instance // registered with it if (asynchronousChannelGroup != null) { synchronized (asynchronousChannelGroupLock) { if (asynchronousChannelGroup != null) { AsyncChannelGroupUtil.unregister(); asynchronousChannelGroup = null; } } } }
@Test public void testWsCloseThenTcpReset() throws Exception { startServer(TestEndpointConfig.class); TesterWsCloseClient client = new TesterWsCloseClient("localhost", getPort()); client.httpUpgrade(BaseEndpointConfig.PATH); client.sendCloseFrame(CloseCodes.GOING_AWAY); client.forceCloseSocket(); // WebSocket 1.1, section 2.1.5 requires this to be CLOSED_ABNORMALLY if // the container initiates the close and the close code from the client // if the client initiates it. When the client resets the TCP connection // after sending the close, different operating systems react different // ways. Some present the close message then drop the connection, some // just drop the connection. Therefore, this test has to handle both // close codes. awaitOnClose(CloseCodes.CLOSED_ABNORMALLY, CloseCodes.GOING_AWAY); }
@Test public void testWsCloseThenTcpCloseWhenOnMessageSends() throws Exception { events.onMessageSends = true; startServer(TestEndpointConfig.class); TesterWsCloseClient client = new TesterWsCloseClient("localhost", getPort()); client.httpUpgrade(BaseEndpointConfig.PATH); client.sendMessage("Test"); awaitLatch(events.onMessageCalled, "onMessage not called"); client.sendCloseFrame(CloseCodes.NORMAL_CLOSURE); client.closeSocket(); events.onMessageWait.countDown(); // BIO will see close from client before it sees the TCP close awaitOnClose(CloseCodes.CLOSED_ABNORMALLY, CloseCodes.NORMAL_CLOSURE); }
@Test public void testWsCloseThenTcpResetWhenOnMessageSends() throws Exception { events.onMessageSends = true; startServer(TestEndpointConfig.class); TesterWsCloseClient client = new TesterWsCloseClient("localhost", getPort()); client.httpUpgrade(BaseEndpointConfig.PATH); client.sendMessage("Test"); awaitLatch(events.onMessageCalled, "onMessage not called"); client.sendCloseFrame(CloseCodes.NORMAL_CLOSURE); client.forceCloseSocket(); events.onMessageWait.countDown(); // APR will see close from client before it sees the TCP reset awaitOnClose(CloseCodes.CLOSED_ABNORMALLY, CloseCodes.NORMAL_CLOSURE); }
/** * When user leaves the activity. */ @OnClose public void unregisterUser(Session websocket, CloseReason reason) { Long toolContentID = Long .valueOf(websocket.getRequestParameterMap().get(AttributeNames.PARAM_TOOL_CONTENT_ID).get(0)); websockets.get(toolContentID).remove(websocket); if (log.isDebugEnabled()) { // If there was something wrong with the connection, put it into logs. log.debug("User " + websocket.getUserPrincipal().getName() + " left Dokumaran with Tool Content ID: " + toolContentID + (!(reason.getCloseCode().equals(CloseCodes.GOING_AWAY) || reason.getCloseCode().equals(CloseCodes.NORMAL_CLOSURE)) ? ". Abnormal close. Code: " + reason.getCloseCode() + ". Reason: " + reason.getReasonPhrase() : "")); } }
/** * When user leaves the activity. */ @OnClose public void unregisterUser(Session websocket, CloseReason reason) { Long toolSessionId = Long .valueOf(websocket.getRequestParameterMap().get(AttributeNames.PARAM_TOOL_SESSION_ID).get(0)); websockets.get(toolSessionId).remove(websocket); if (log.isDebugEnabled()) { // If there was something wrong with the connection, put it into logs. log.debug("User " + websocket.getUserPrincipal().getName() + " left Leader Selection with Tool Session ID: " + toolSessionId + (!(reason.getCloseCode().equals(CloseCodes.GOING_AWAY) || reason.getCloseCode().equals(CloseCodes.NORMAL_CLOSURE)) ? ". Abnormal close. Code: " + reason.getCloseCode() + ". Reason: " + reason.getReasonPhrase() : "")); } }
/** * If there was something wrong with the connection, put it into logs. */ @OnClose public void unregisterUser(Session session, CloseReason reason) { Long lessonId = Long.valueOf(session.getRequestParameterMap().get(AttributeNames.PARAM_LESSON_ID).get(0)); Set<Websocket> lessonWebsockets = PresenceWebsocketServer.websockets.get(lessonId); Iterator<Websocket> websocketIterator = lessonWebsockets.iterator(); while (websocketIterator.hasNext()) { Websocket websocket = websocketIterator.next(); if (websocket.session.equals(session)) { websocketIterator.remove(); break; } } if (PresenceWebsocketServer.log.isDebugEnabled()) { PresenceWebsocketServer.log.debug( "User " + session.getUserPrincipal().getName() + " left Presence Chat with lessonId: " + lessonId + (!(reason.getCloseCode().equals(CloseCodes.GOING_AWAY) || reason.getCloseCode().equals(CloseCodes.NORMAL_CLOSURE)) ? ". Abnormal close. Code: " + reason.getCloseCode() + ". Reason: " + reason.getReasonPhrase() : "")); } }
/** * When user leaves the activity. */ @OnClose public void unregisterUser(Session session, CloseReason reason) { Long toolSessionId = Long .valueOf(session.getRequestParameterMap().get(AttributeNames.PARAM_TOOL_SESSION_ID).get(0)); Set<Websocket> sessionWebsockets = LearningWebsocketServer.websockets.get(toolSessionId); Iterator<Websocket> websocketIterator = sessionWebsockets.iterator(); while (websocketIterator.hasNext()) { Websocket websocket = websocketIterator.next(); if (websocket.session.equals(session)) { websocketIterator.remove(); break; } } if (LearningWebsocketServer.log.isDebugEnabled()) { LearningWebsocketServer.log.debug( "User " + session.getUserPrincipal().getName() + " left Chat with toolSessionId: " + toolSessionId + (!(reason.getCloseCode().equals(CloseCodes.GOING_AWAY) || reason.getCloseCode().equals(CloseCodes.NORMAL_CLOSURE)) ? ". Abnormal close. Code: " + reason.getCloseCode() + ". Reason: " + reason.getReasonPhrase() : "")); } }
/** * When user leaves the activity. */ @OnClose public void unregisterUser(Session websocket, CloseReason reason) { Long toolSessionId = Long .valueOf(websocket.getRequestParameterMap().get(AttributeNames.PARAM_TOOL_SESSION_ID).get(0)); LearningWebsocketServer.websockets.get(toolSessionId).remove(websocket); if (LearningWebsocketServer.log.isDebugEnabled()) { // If there was something wrong with the connection, put it into logs. LearningWebsocketServer.log.debug("User " + websocket.getUserPrincipal().getName() + " left Scratchie with Tool Session ID: " + toolSessionId + (!(reason.getCloseCode().equals(CloseCodes.GOING_AWAY) || reason.getCloseCode().equals(CloseCodes.NORMAL_CLOSURE)) ? ". Abnormal close. Code: " + reason.getCloseCode() + ". Reason: " + reason.getReasonPhrase() : "")); } }
/** * When user leaves the activity. */ @OnClose public void unregisterUser(Session websocket, CloseReason reason) { Long toolSessionId = Long .valueOf(websocket.getRequestParameterMap().get(AttributeNames.PARAM_TOOL_SESSION_ID).get(0)); LearningWebsocketServer.websockets.get(toolSessionId).remove(websocket); if (LearningWebsocketServer.log.isDebugEnabled()) { // If there was something wrong with the connection, put it into logs. LearningWebsocketServer.log.debug("User " + websocket.getUserPrincipal().getName() + " left Scribe with Tool Session ID: " + toolSessionId + (!(reason.getCloseCode().equals(CloseCodes.GOING_AWAY) || reason.getCloseCode().equals(CloseCodes.NORMAL_CLOSURE)) ? ". Abnormal close. Code: " + reason.getCloseCode() + ". Reason: " + reason.getReasonPhrase() : "")); } }
/** * Close Websocket connection Connection * @param session */ private void closeWebSocketConnection(final Session session ){ try { Thread.sleep(3000); } catch (InterruptedException e1) { } if (session != null && session.isOpen()) { try { CloseReason closeReason = new CloseReason( CloseCodes.NORMAL_CLOSURE, "Session Closed"); session.close(closeReason); } catch (IOException e) { e.printStackTrace(); } } }
/** * * Close websocket client connection. * @param session */ public void closeWebSocketConnection(Session session){ try { Thread.sleep(DELAY); } catch (InterruptedException e1) { } if (session != null && session.isOpen()) { try { CloseReason closeReason = new CloseReason(CloseCodes.NORMAL_CLOSURE,"Closed"); session.close(closeReason); logger.info("Session closed"); } catch (IOException e) { logger.error("Fail to close connection ",e); } } }
@SuppressWarnings("unchecked") private void sendMessageText(boolean last) throws WsIOException { if (textMsgHandler instanceof WrappedMessageHandler) { long maxMessageSize = ((WrappedMessageHandler) textMsgHandler).getMaxMessageSize(); if (maxMessageSize > -1 && messageBufferText.remaining() > maxMessageSize) { throw new WsIOException(new CloseReason(CloseCodes.TOO_BIG, sm.getString("wsFrame.messageTooBig", Long.valueOf(messageBufferText.remaining()), Long.valueOf(maxMessageSize)))); } } try { if (textMsgHandler instanceof MessageHandler.Partial<?>) { ((MessageHandler.Partial<String>) textMsgHandler).onMessage(messageBufferText.toString(), last); } else { // Caller ensures last == true if this branch is used ((MessageHandler.Whole<String>) textMsgHandler).onMessage(messageBufferText.toString()); } } catch (Throwable t) { handleThrowableOnSend(t); } finally { messageBufferText.clear(); } }
@SuppressWarnings("unchecked") private void sendMessageBinary(ByteBuffer msg, boolean last) throws WsIOException { if (binaryMsgHandler instanceof WrappedMessageHandler) { long maxMessageSize = ((WrappedMessageHandler) binaryMsgHandler).getMaxMessageSize(); if (maxMessageSize > -1 && msg.remaining() > maxMessageSize) { throw new WsIOException(new CloseReason(CloseCodes.TOO_BIG, sm.getString("wsFrame.messageTooBig", Long.valueOf(msg.remaining()), Long.valueOf(maxMessageSize)))); } } try { if (binaryMsgHandler instanceof MessageHandler.Partial<?>) { ((MessageHandler.Partial<ByteBuffer>) binaryMsgHandler).onMessage(msg, last); } else { // Caller ensures last == true if this branch is used ((MessageHandler.Whole<ByteBuffer>) binaryMsgHandler).onMessage(msg); } } catch (Throwable t) { handleThrowableOnSend(t); } }
/** * Cleans up the resources still in use by WebSocket sessions created from * this container. This includes closing sessions and cancelling * {@link Future}s associated with blocking read/writes. */ public void destroy() { CloseReason cr = new CloseReason(CloseCodes.GOING_AWAY, sm.getString("wsWebSocketContainer.shutdown")); for (WsSession session : sessions.keySet()) { try { session.close(cr); } catch (IOException ioe) { log.debug(sm.getString("wsWebSocketContainer.sessionCloseFail", session.getId()), ioe); } } // Only unregister with AsyncChannelGroupUtil if this instance // registered with it if (asynchronousChannelGroup != null) { synchronized (asynchronousChannelGroupLock) { if (asynchronousChannelGroup != null) { AsyncChannelGroupUtil.unregister(); asynchronousChannelGroup = null; } } } }
@Test public void onClose() { final Session session = mock(Session.class); when(session.getId()).thenReturn("sessionId"); final CloseReason reason = new CloseReason(CloseCodes.GOING_AWAY, "oooh"); when(this.sessionEvent.select(OnClose.Literal.onClose())).thenReturn(this.sessionEvent); this.endpoint.onClose(session, reason); verify(session).getId(); verify(session).getUserPrincipal(); verify(this.log).info("WebSocket connection closed. [id={},principle={},code={},reason={}]", "sessionId", null, reason.getCloseCode(), reason.getReasonPhrase()); verify(this.beanManager).getExtension(Extension.class); verify(this.registry).unregister(session); verify(this.sessionEvent).select(OnClose.Literal.onClose()); verify(this.sessionEvent).fire(session); verifyNoMoreInteractions(session); }
/** * Try sending the {@link Message} using * {@link Session#getBasicRemote()}, {@link Basic#sendObject(Object)}. * * @param session Session to send the message on * @param message Message to send * @return true if send was successful, or false if it failed */ private boolean sendMessageToSession(Session session, Message message) { if (session.isOpen()) { try { session.getBasicRemote().sendObject(message); return true; } catch (EncodeException e) { // Something was wrong encoding this message, but the connection // is likely just fine. Log.log(Level.FINE, this, "Unexpected condition writing message", e); } catch (IOException ioe) { // An IOException, on the other hand, suggests the connection is // in a bad state. Log.log(Level.FINE, this, "Unexpected condition writing message", ioe); tryToClose(session, new CloseReason(CloseCodes.UNEXPECTED_CONDITION, trimReason(ioe.toString()))); } } return false; }
@Test public void testClose_withException() throws Exception { underTest.openSocket(ConnectionType.SUBSCRIBER, session); verify(session).getId(); verify(session).setMaxIdleTimeout(0); verifyNoMoreInteractions(session); assertEquals(1, underTest.numSessions()); // make close throw an exception doThrow(IOException.class).when(session).close(); underTest.close(session, new CloseReason(CloseCodes.CANNOT_ACCEPT, "close")); verify(session).close(); verify(session, times(3)).getId(); verifyNoMoreInteractions(session); assertEquals(0, underTest.numSessions()); }
@Override public void onClose(Session session, final CloseReason closeReason) { SoapUI.log("WebSocketClose statusCode=" + closeReason.getCloseCode() + " reason=" + closeReason.getReasonPhrase()); messageQueue.clear(); resetProxySelector(); this.session.set(null); Future<?> future; if (closeReason.getCloseCode().getCode() > CloseCodes.NORMAL_CLOSURE.getCode()) throwable.set(websocketException("Websocket connection closed abnormaly.", closeReason)); else if ((future = this.future.get()) != null && !future.isDone()) throwable.set(websocketException("Websocket connection closed unexpected.", closeReason)); }
/** * Try sending the {@link RoutedMessage} using * {@link Session#getBasicRemote()}, {@link Basic#sendObject(Object)}. * * @param session * Session to send the message on * @param message * {@link RoutedMessage} to send * @return true if send was successful, or false if it failed */ public static boolean sendMessage(Session session, RoutedMessage message) { if (session.isOpen()) { try { session.getBasicRemote().sendObject(message); return true; } catch (EncodeException e) { Log.log(Level.FINEST, session, "Unexpected condition writing message", e); // Something was wrong encoding this message, but the connection // is likely just fine. } catch (IOException ioe) { // An IOException, on the other hand, suggests the connection is // in a bad state. Log.log(Level.FINEST, session, "Unexpected condition writing message", ioe); tryToClose(session, new CloseReason(CloseCodes.UNEXPECTED_CONDITION, trimReason(ioe.toString()))); } } return false; }
/** * Test setting a maximum message length and make sure the stream is closed. * * @throws Exception in case of an error */ @Test public void testMaxMessageLength() throws Exception { int maxMessageLength = 10; clientConfig.put(ClientConfig.WEBSOCKET_MAX_MESSAGE_LENGTH, maxMessageLength); CountDownLatch latch = new CountDownLatch(1); EventStreamClosedClient eventListener = new EventStreamClosedClient(latch); try (EventStream streamEvents = new EventStream(clientConfig, eventListener)) { latch.await(30, TimeUnit.SECONDS); Assert.assertTrue(streamEvents.isEventStreamClosed()); Assert.assertEquals(CloseCodes.TOO_BIG, eventListener.closeReason.getCloseCode()); String message = "Message length exceeded the configured maximum (" + maxMessageLength + " characters)"; Assert.assertEquals(message, eventListener.closeReason.getReasonPhrase()); } }
@Override public void shutdown() { synchronized (connections) { parent.channelCloses(this); for (final Session connection : connections) { final ExecutorService executorService = connectionThreads.get(connection); try { // If there is no executor service for a client, it may has already been shut down. if (executorService != null) { executorService.shutdownNow(); } connection .close(new CloseReason(CloseCodes.GOING_AWAY, "This SynchronizeFX channel is closed now.")); } catch (final IOException e) { callback.onClientConnectionError(connection, new SynchronizeFXException("Failed to close the connection to a connected client.", e)); } finally { connectionThreads.remove(connection); } } connections.clear(); } callback = null; }
@PreDestroy public void shutdown() { scheduledExecutorService.shutdown(); clientsSessions.forEach(session -> { try { if (!session.isOpen()) { return; } session.close(new CloseReason(CloseCodes.SERVICE_RESTART, "Shutting down")); } catch (Exception e) { LOGGER.error("Error while closing session", e); } }); }
private void sendCloseMessage(CloseReason closeReason) { // 125 is maximum size for the payload of a control message ByteBuffer msg = ByteBuffer.allocate(125); CloseCode closeCode = closeReason.getCloseCode(); // CLOSED_ABNORMALLY should not be put on the wire if (closeCode == CloseCodes.CLOSED_ABNORMALLY) { // PROTOCOL_ERROR is probably better than GOING_AWAY here msg.putShort((short) CloseCodes.PROTOCOL_ERROR.getCode()); } else { msg.putShort((short) closeCode.getCode()); } String reason = closeReason.getReasonPhrase(); if (reason != null && reason.length() > 0) { appendCloseReasonWithTruncation(msg, reason); } msg.flip(); try { wsRemoteEndpoint.startMessageBlock(Constants.OPCODE_CLOSE, msg, true); } catch (IOException ioe) { handleCloseException(ioe, closeCode); } catch (WritePendingException wpe) { handleCloseException(wpe, closeCode); } finally { webSocketContainer.unregisterSession(localEndpoint, this); } }
private void handleCloseException(Exception e, CloseCode closeCode) { // Failed to send close message. Close the socket and let the caller // deal with the Exception if (log.isDebugEnabled()) { log.debug(sm.getString("wsSession.sendCloseFail", id), e); } wsRemoteEndpoint.close(); // Failure to send a close message is not unexpected in the case of // an abnormal closure (usually triggered by a failure to read/write // from/to the client. In this case do not trigger the endpoint's // error handling if (closeCode != CloseCodes.CLOSED_ABNORMALLY) { localEndpoint.onError(this, e); } }
protected void checkExpiration() { long timeout = maxIdleTimeout; if (timeout < 1) { return; } if (System.currentTimeMillis() - lastActive > timeout) { String msg = sm.getString("wsSession.timeout"); doClose(new CloseReason(CloseCodes.GOING_AWAY, msg), new CloseReason(CloseCodes.CLOSED_ABNORMALLY, msg)); } }