src/java.net.http/share/classes/jdk/internal/net/http/websocket/MessageDecoder.java
branchhttp-client-branch
changeset 56320 f82729ca8660
parent 56304 065641767a75
child 56389 0ba90c4f1e3f
equal deleted inserted replaced
56319:bb9e25a8ea04 56320:f82729ca8660
    26 package jdk.internal.net.http.websocket;
    26 package jdk.internal.net.http.websocket;
    27 
    27 
    28 import jdk.internal.net.http.common.Utils;
    28 import jdk.internal.net.http.common.Utils;
    29 import jdk.internal.net.http.websocket.Frame.Opcode;
    29 import jdk.internal.net.http.websocket.Frame.Opcode;
    30 
    30 
    31 import java.net.http.WebSocket.MessagePart;
       
    32 import java.nio.ByteBuffer;
    31 import java.nio.ByteBuffer;
    33 import java.nio.CharBuffer;
    32 import java.nio.CharBuffer;
    34 import java.nio.charset.CharacterCodingException;
    33 import java.nio.charset.CharacterCodingException;
    35 
    34 
    36 import static java.lang.String.format;
    35 import static java.lang.String.format;
    57 
    56 
    58     private final MessageStreamConsumer output;
    57     private final MessageStreamConsumer output;
    59     private final UTF8AccumulatingDecoder decoder = new UTF8AccumulatingDecoder();
    58     private final UTF8AccumulatingDecoder decoder = new UTF8AccumulatingDecoder();
    60     private boolean fin;
    59     private boolean fin;
    61     private Opcode opcode, originatingOpcode;
    60     private Opcode opcode, originatingOpcode;
    62     private MessagePart part = MessagePart.WHOLE;
       
    63     private long payloadLen;
    61     private long payloadLen;
    64     private long unconsumedPayloadLen;
    62     private long unconsumedPayloadLen;
    65     private ByteBuffer binaryData;
    63     private ByteBuffer binaryData;
    66 
    64 
    67     MessageDecoder(MessageStreamConsumer output) {
    65     MessageDecoder(MessageStreamConsumer output) {
   169 
   167 
   170     @Override
   168     @Override
   171     public void payloadData(ByteBuffer data) {
   169     public void payloadData(ByteBuffer data) {
   172         debug.log(Level.DEBUG, "payload %s", data);
   170         debug.log(Level.DEBUG, "payload %s", data);
   173         unconsumedPayloadLen -= data.remaining();
   171         unconsumedPayloadLen -= data.remaining();
   174         boolean isLast = unconsumedPayloadLen == 0;
   172         boolean lastPayloadChunk = unconsumedPayloadLen == 0;
   175         if (opcode.isControl()) {
   173         if (opcode.isControl()) {
   176             if (binaryData != null) { // An intermediate or the last chunk
   174             if (binaryData != null) { // An intermediate or the last chunk
   177                 binaryData.put(data);
   175                 binaryData.put(data);
   178             } else if (!isLast) { // The first chunk
   176             } else if (!lastPayloadChunk) { // The first chunk
   179                 int remaining = data.remaining();
   177                 int remaining = data.remaining();
   180                 // It shouldn't be 125, otherwise the next chunk will be of size
   178                 // It shouldn't be 125, otherwise the next chunk will be of size
   181                 // 0, which is not what Reader promises to deliver (eager
   179                 // 0, which is not what Reader promises to deliver (eager
   182                 // reading)
   180                 // reading)
   183                 assert remaining < Frame.MAX_CONTROL_FRAME_PAYLOAD_LENGTH
   181                 assert remaining < Frame.MAX_CONTROL_FRAME_PAYLOAD_LENGTH
   186                         Frame.MAX_CONTROL_FRAME_PAYLOAD_LENGTH).put(data);
   184                         Frame.MAX_CONTROL_FRAME_PAYLOAD_LENGTH).put(data);
   187             } else { // The only chunk
   185             } else { // The only chunk
   188                 binaryData = ByteBuffer.allocate(data.remaining()).put(data);
   186                 binaryData = ByteBuffer.allocate(data.remaining()).put(data);
   189             }
   187             }
   190         } else {
   188         } else {
   191             part = determinePart(isLast);
   189             boolean last = fin && lastPayloadChunk;
   192             boolean text = opcode == Opcode.TEXT || originatingOpcode == Opcode.TEXT;
   190             boolean text = opcode == Opcode.TEXT || originatingOpcode == Opcode.TEXT;
   193             if (!text) {
   191             if (!text) {
   194                 output.onBinary(data.slice(), part);
   192                 output.onBinary(data.slice(), last);
   195                 data.position(data.limit()); // Consume
   193                 data.position(data.limit()); // Consume
   196             } else {
   194             } else {
   197                 boolean binaryNonEmpty = data.hasRemaining();
   195                 boolean binaryNonEmpty = data.hasRemaining();
   198                 CharBuffer textData;
   196                 CharBuffer textData;
   199                 try {
   197                 try {
   200                     boolean eof = part == MessagePart.WHOLE
   198                     textData = decoder.decode(data, last);
   201                             || part == MessagePart.LAST;
       
   202                     textData = decoder.decode(data, eof);
       
   203                 } catch (CharacterCodingException e) {
   199                 } catch (CharacterCodingException e) {
   204                     throw new FailWebSocketException(
   200                     throw new FailWebSocketException(
   205                             "Invalid UTF-8 in frame " + opcode,
   201                             "Invalid UTF-8 in frame " + opcode,
   206                             StatusCodes.NOT_CONSISTENT).initCause(e);
   202                             StatusCodes.NOT_CONSISTENT).initCause(e);
   207                 }
   203                 }
   208                 if (!(binaryNonEmpty && !textData.hasRemaining())) {
   204                 if (!(binaryNonEmpty && !textData.hasRemaining())) {
   209                     // If there's a binary data, that result in no text, then we
   205                     // If there's a binary data, that result in no text, then we
   210                     // don't deliver anything
   206                     // don't deliver anything, otherwise:
   211                     output.onText(textData, part);
   207                     output.onText(textData, last);
   212                 }
   208                 }
   213             }
   209             }
   214         }
   210         }
   215     }
   211     }
   216 
   212 
   262                 break;
   258                 break;
   263         }
   259         }
   264         payloadLen = 0;
   260         payloadLen = 0;
   265         opcode = null;
   261         opcode = null;
   266     }
   262     }
   267 
       
   268     private MessagePart determinePart(boolean isLast) {
       
   269         boolean lastChunk = fin && isLast;
       
   270         switch (part) {
       
   271             case LAST:
       
   272             case WHOLE:
       
   273                 return lastChunk ? MessagePart.WHOLE : MessagePart.FIRST;
       
   274             case FIRST:
       
   275             case PART:
       
   276                 return lastChunk ? MessagePart.LAST : MessagePart.PART;
       
   277             default:
       
   278                 throw new InternalError(String.valueOf(part));
       
   279         }
       
   280     }
       
   281 }
   263 }