tyrus
  1. tyrus
  2. TYRUS-261

Text decoder disables Binary decoder if both are configured for the same server endpoint; endpoint cannot receive binary messages anymore

    Details

    • Type: Bug Bug
    • Status: Resolved
    • Priority: Major Major
    • Resolution: Fixed
    • Affects Version/s: 1.2.1
    • Fix Version/s: 1.3
    • Component/s: server
    • Labels:
      None

      Description

      When two decoders, one Text and one Binary, are configured into a programmatic Endpoint the Text decoder disables the Binary decoder. Only text messages can be received by the endpoint. Binary messages received throws this exception java.lang.IllegalStateException: Binary messageHandler not found.

      According to RFC 6455 both Text and Binary messages are supported by a websocket endpoint and they have different opcodes in the frames (%x1 for text and %x2 for binary) to be distinguishable by the server.
      This is also clarified in the implementation of the two EndpointWrapper.onMessage methods, one for Text one for Binary.

      The sample code to reproduce this error is here: https://github.com/raghucbz/websocket_bug1.git
      The project is setup using IntelliJ and contains both the client and the server.

      The Text Client (endpoint.StampingClientText.java) works fine. Here is the error you get when you run the Binary Client (endpoint.StampingClientBinaryStream.java)

      [2013-10-22T10:26:02.279-0400] [glassfish 4.0] [SEVERE] [] [] [tid: _ThreadID=23 _ThreadName=Thread-4] [timeMillis: 1382451962279] [levelValue: 1000] [[
        java.lang.IllegalStateException: Binary messageHandler not found. 
      Session: 'SessionImpl{uri=/websocket_bug1/StampingServerEndpoint, id='a4da753f-9a49-4593-ae6d-242ca9a4c464', endpoint=EndpointWrapper{endpointClass=class
      endpoint.StampingServerEndpoint, endpoint=null, uri='/websocket_bug1/StampingServerEndpoint', contextPath='/websocket_bug1'}}'.
      	at org.glassfish.tyrus.core.*EndpointWrapper.onMessage*(EndpointWrapper.java:469)
      	at org.glassfish.tyrus.server.TyrusEndpoint.onMessage(TyrusEndpoint.java:180)
      	at org.glassfish.tyrus.websockets.DefaultWebSocket.onMessage(DefaultWebSocket.java:148)
      	at org.glassfish.tyrus.websockets.frametypes.BinaryFrameType.respond(BinaryFrameType.java:52)
      	at org.glassfish.tyrus.websockets.DataFrame.respond(DataFrame.java:102)
      	at org.glassfish.tyrus.servlet.TyrusHttpUpgradeHandler.onDataAvailable(TyrusHttpUpgradeHandler.java:113)
      	at org.apache.catalina.connector.InputBuffer$ReadHandlerImpl.processDataAvailable(InputBuffer.java:488)
      	at org.apache.catalina.connector.InputBuffer$ReadHandlerImpl.onDataAvailable(InputBuffer.java:453)
      	at org.glassfish.grizzly.http.io.InputBuffer.append(InputBuffer.java:855)
      	at org.glassfish.grizzly.http.server.HttpServerFilter.handleRead(HttpServerFilter.java:222)
      	at org.glassfish.grizzly.filterchain.ExecutorResolver$9.execute(ExecutorResolver.java:119)
      	at org.glassfish.grizzly.filterchain.DefaultFilterChain.executeFilter(DefaultFilterChain.java:288)
      	at org.glassfish.grizzly.filterchain.DefaultFilterChain.executeChainPart(DefaultFilterChain.java:206)
      	at org.glassfish.grizzly.filterchain.DefaultFilterChain.execute(DefaultFilterChain.java:136)
      	at org.glassfish.grizzly.filterchain.DefaultFilterChain.process(DefaultFilterChain.java:114)
      	at org.glassfish.grizzly.ProcessorExecutor.execute(ProcessorExecutor.java:77)
      	at org.glassfish.grizzly.nio.transport.TCPNIOTransport.fireIOEvent(TCPNIOTransport.java:838)
      	at org.glassfish.grizzly.strategies.AbstractIOStrategy.fireIOEvent(AbstractIOStrategy.java:113)
      	at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy.run0(WorkerThreadIOStrategy.java:115)
      	at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy.access$100(WorkerThreadIOStrategy.java:55)
      	at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy$WorkerThreadRunnable.run(WorkerThreadIOStrategy.java:135)
      	at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.doWork(AbstractThreadPool.java:564)
      	at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.run(AbstractThreadPool.java:544)
      	at java.lang.Thread.run(Thread.java:724)]]
      

      Code that throws the exception - EndpointWrapper.onMessage(SPIRemoteEndpoint gs, ByteBuffer messageBytes)

      if (session.isWholeBinaryHandlerPresent()) {
          session.notifyMessageHandlers(messageBytes, findApplicableDecoders(session, messageBytes, false));
      } else if (session.isPartialBinaryHandlerPresent()) {
          session.notifyMessageHandlers(messageBytes, true);
      } else {
          throw new IllegalStateException(String.format("Binary messageHandler not found. Session: '%s'.", session));
      }
      

      session.isWholeBinaryHandlerPresent() is the same as MessageHandlerManager.binaryWholeHandlerPresent and both are false.

      When a programmatic endpoint is first called the onOpen() is executed and the messages handlers are registered using the call session.addMessageHandler() which in turn calls MessageHandlerManager.addMessageHandler(). In the code below you can see the comment that point to the if/else code block that skips processing the binary handlers if a text handler is found. If the two if/elses are converted to two if/ifs the problem would be solved.

      public void addMessageHandler(MessageHandler handler) throws IllegalStateException {
      
          if (!(handler instanceof MessageHandler.Whole) && !(handler instanceof MessageHandler.Partial)) {
              throwException("MessageHandler must implement MessageHandler.Whole or MessageHandler.Partial.");
          }
      
          final Class<?> handlerClass = getHandlerType(handler);
      
          if (handler instanceof MessageHandler.Whole) { //WHOLE MESSAGE HANDLER
              if (WHOLE_TEXT_HANDLER_TYPES.contains(handlerClass)) { // text
                  if (textHandlerPresent) {
                      throwException("Text MessageHandler already registered.");
                  } else {
                      if (Reader.class.isAssignableFrom(handlerClass)) {
                          readerHandlerPresent = true;
                      }
                      textHandlerPresent = true;
                      textWholeHandlerPresent = true;
                  }
              } else if (WHOLE_BINARY_HANDLER_TYPES.contains(handlerClass)) { // binary
                  if (binaryHandlerPresent) {
                      throwException("Binary MessageHandler already registered.");
                  } else {
                      if (InputStream.class.isAssignableFrom(handlerClass)) {
                          inputStreamHandlerPresent = true;
                      }
                      binaryHandlerPresent = true;
                      binaryWholeHandlerPresent = true;
                  }
              } else if (PONG_HANDLER_TYPE == handlerClass) { // pong
                  if (pongHandlerPresent) {
                      throwException("Pong MessageHander already registered.");
                  } else {
                      pongHandlerPresent = true;
                  }
              } else {
                  boolean decoderExists = false;
      
                  //IF/ELSE BLOCK THAT CHECKS FOR TEXT DECODERS AND IF FOUND SKIPS CHECK FOR BINARY DECODERS
      
                  if (checkTextDecoders(handlerClass)) {//decodable text
                      if (textHandlerPresent) {
                          throwException("Text MessageHandler already registered.");
                      } else {
                          textHandlerPresent = true;
                          textWholeHandlerPresent = true;
                          decoderExists = true;
                      }
                  }
                  else if (checkBinaryDecoders(handlerClass)) {//decodable binary
                      if (binaryHandlerPresent) {
                          throwException("Text MessageHandler already registered.");
                      } else {
                          binaryHandlerPresent = true;
                          binaryWholeHandlerPresent = true;
                          decoderExists = true;
                      }
                  }
      
                  if (!decoderExists) {
                      throwException(String.format("Decoder for type: %s has not been registered.", handlerClass));
                  }
              }
          } else { // PARTIAL MESSAGE HANDLER
      
              //IF/ELSE BLOCK THAT CHECKS FOR TEXT DECODERS AND IF FOUND SKIPS CHECK FOR BINARY DECODERS
      
              if (PARTIAL_TEXT_HANDLER_TYPE.equals(handlerClass)) { // text
                  if (textHandlerPresent) {
                      throwException("Text MessageHandler already registered.");
                  } else {
                      textHandlerPresent = true;
                  }
              }
              else if (PARTIAL_BINARY_HANDLER_TYPES.contains(handlerClass)) { // binary
                  if (binaryHandlerPresent) {
                      throwException("Binary MessageHandler already registered.");
                  } else {
                      binaryHandlerPresent = true;
                  }
              } else {
                  throwException(String.format("Partial MessageHandler can't be of type: %s.", handlerClass));
              }
          }
      
          // map of all registered handlers
          if (registeredHandlers.containsKey(handlerClass)) {
              throwException(String.format("MessageHandler for type: %s already registered.", handlerClass));
          } else {
              registeredHandlers.put(handlerClass, handler);
          }
      
          messageHandlerCache = null;
      }
      

      Please let me know if you have further questions.
      Thanks!

      Raghuram Krishnamachari aka raghu
      raghuramcbz at gmail dot com

        Activity

        No work has yet been logged on this issue.

          People

          • Assignee:
            Pavel Bucek
            Reporter:
            raghucbz
          • Votes:
            0 Vote for this issue
            Watchers:
            0 Start watching this issue

            Dates

            • Created:
              Updated:
              Resolved: