src/java.net.http/share/classes/jdk/internal/net/http/websocket/MessageEncoder.java
branchhttp-client-branch
changeset 56269 234813fd33bc
parent 56263 4933a477d628
child 56291 c8c4c707ff3a
equal deleted inserted replaced
56268:481d8c9acc7f 56269:234813fd33bc
    40 
    40 
    41 /*
    41 /*
    42  * A stateful producer of binary representations of WebSocket messages being
    42  * A stateful producer of binary representations of WebSocket messages being
    43  * sent from the client to the server.
    43  * sent from the client to the server.
    44  *
    44  *
    45  * An encoding methods are given original messages and byte buffers to put the
    45  * An encoding method is given an original message and a byte buffer to put the
    46  * resulting bytes to.
    46  * resulting bytes to. The method is called until it returns true. Then the
    47  *
    47  * reset method is called. The whole sequence repeats with next message.
    48  * The method is called
       
    49  * repeatedly with a non-empty target buffer. Once the caller finds the buffer
       
    50  * unmodified after the call returns, the message has been completely encoded.
       
    51  */
    48  */
    52 
       
    53 /*
       
    54  * The state of encoding.An instance of this class is passed sequentially between messages, so
       
    55  * every message in a sequence can check the context it is in and update it
       
    56  * if necessary.
       
    57  */
       
    58 
       
    59 public class MessageEncoder {
    49 public class MessageEncoder {
    60 
       
    61     // FIXME: write frame method
       
    62 
    50 
    63     private final static boolean DEBUG = false;
    51     private final static boolean DEBUG = false;
    64 
    52 
    65     private final SecureRandom maskingKeySource = new SecureRandom();
    53     private final SecureRandom maskingKeySource = new SecureRandom();
    66     private final Frame.HeaderWriter headerWriter = new Frame.HeaderWriter();
    54     private final Frame.HeaderWriter headerWriter = new Frame.HeaderWriter();
    67     private final Frame.Masker payloadMasker = new Frame.Masker();
    55     private final Frame.Masker payloadMasker = new Frame.Masker();
    68     private final CharsetEncoder charsetEncoder
    56     private final CharsetEncoder charsetEncoder
    69             = StandardCharsets.UTF_8.newEncoder()
    57             = StandardCharsets.UTF_8.newEncoder()
    70             .onMalformedInput(CodingErrorAction.REPORT)
    58                                     .onMalformedInput(CodingErrorAction.REPORT)
    71             .onUnmappableCharacter(CodingErrorAction.REPORT);
    59                                     .onUnmappableCharacter(CodingErrorAction.REPORT);
    72     /*
    60     /*
    73      * This buffer is used both to encode characters to UTF-8 and to calculate
    61      * This buffer is used both to encode characters to UTF-8 and to calculate
    74      * the length of the resulting frame's payload. The length of the payload
    62      * the length of the resulting frame's payload. The length of the payload
    75      * must be known before the frame's header can be written.
    63      * must be known before the frame's header can be written.
    76      * For implementation reasons, this buffer must have a capacity of at least
    64      * For implementation reasons, this buffer must have a capacity of at least
    84 
    72 
    85     private boolean started;
    73     private boolean started;
    86     private boolean flushing;
    74     private boolean flushing;
    87     private boolean moreText = true;
    75     private boolean moreText = true;
    88     private long headerCount;
    76     private long headerCount;
    89     private boolean previousLast = true;
    77     /* Has the previous frame got its fin flag set? */
       
    78     private boolean previousFin = true;
       
    79     /* Was the previous frame TEXT or a CONTINUATION thereof? */
    90     private boolean previousText;
    80     private boolean previousText;
    91     private boolean closed;
    81     private boolean closed; // TODO: too late, need to check it before accepting otherwise the queue might blow up
    92 
    82 
    93     /*
    83     /*
    94      * How many bytes of the current message have been already encoded.
    84      * How many bytes of the current message have been already encoded.
    95      *
    85      *
    96      * Even though the user hands their buffers over to us, they still can
    86      * Even though the user hands their buffers over to us, they still can
   118                 "jdk.httpclient.websocket.intermediateBufferSize", 16384);
   108                 "jdk.httpclient.websocket.intermediateBufferSize", 16384);
   119         return ByteBuffer.allocate(Math.max(minSize, capacity));
   109         return ByteBuffer.allocate(Math.max(minSize, capacity));
   120     }
   110     }
   121 
   111 
   122     public void reset() {
   112     public void reset() {
   123         // Do not reset the message stream state fields, e.g. previousLast,
   113         // Do not reset the message stream state fields, e.g. previousFin,
   124         // previousText. Just an individual message state:
   114         // previousText. Just an individual message state:
   125         started = false;
   115         started = false;
   126         flushing = false;
   116         flushing = false;
   127         moreText = true;
   117         moreText = true;
   128         headerCount = 0;
   118         headerCount = 0;
   142         }
   132         }
   143         if (closed) {
   133         if (closed) {
   144             throw new IOException("Output closed");
   134             throw new IOException("Output closed");
   145         }
   135         }
   146         if (!started) {
   136         if (!started) {
   147             if (!previousText && !previousLast) {
   137             if (!previousText && !previousFin) {
   148                 // Previous data message was a partial binary message
   138                 // Previous data message was a partial binary message
   149                 throw new IllegalStateException("Unexpected text message");
   139                 throw new IllegalStateException("Unexpected text message");
   150             }
   140             }
   151             started = true;
   141             started = true;
   152             headerBuffer.position(0).limit(0);
   142             headerBuffer.position(0).limit(0);
   168             }
   158             }
   169             if (DEBUG) {
   159             if (DEBUG) {
   170                 System.out.printf("[Output] moreText%n");
   160                 System.out.printf("[Output] moreText%n");
   171             }
   161             }
   172             if (!moreText) {
   162             if (!moreText) {
       
   163                 previousFin = last;
       
   164                 previousText = true;
   173                 return true;
   165                 return true;
   174             }
   166             }
   175             intermediateBuffer.clear();
   167             intermediateBuffer.clear();
   176             CoderResult r = null;
   168             CoderResult r = null;
   177             if (!flushing) {
   169             if (!flushing) {
   194                 }
   186                 }
   195             }
   187             }
   196             if (DEBUG) {
   188             if (DEBUG) {
   197                 System.out.printf("[Output] header #%s%n", headerCount);
   189                 System.out.printf("[Output] header #%s%n", headerCount);
   198             }
   190             }
   199             if (headerCount == 0) { // set once
       
   200                 previousLast = last;
       
   201                 previousText = true;
       
   202             }
       
   203             intermediateBuffer.flip();
   191             intermediateBuffer.flip();
   204             headerBuffer.clear();
   192             headerBuffer.clear();
   205             int mask = maskingKeySource.nextInt();
   193             int mask = maskingKeySource.nextInt();
   206             Opcode opcode = previousLast && headerCount == 0
   194             Opcode opcode = previousFin && headerCount == 0
   207                     ? Opcode.TEXT : Opcode.CONTINUATION;
   195                     ? Opcode.TEXT : Opcode.CONTINUATION;
       
   196             boolean fin = last && !moreText;
   208             if (DEBUG) {
   197             if (DEBUG) {
   209                 System.out.printf("[Output] opcode %s%n", opcode);
   198                 System.out.printf("[Output] opcode %s%n", opcode);
   210             }
   199             }
   211             headerWriter.fin(last && !moreText)
   200             headerWriter.fin(fin)
   212                     .opcode(opcode)
   201                     .opcode(opcode)
   213                     .payloadLen(intermediateBuffer.remaining())
   202                     .payloadLen(intermediateBuffer.remaining())
   214                     .mask(mask)
   203                     .mask(mask)
   215                     .write(headerBuffer);
   204                     .write(headerBuffer);
   216             headerBuffer.flip();
   205             headerBuffer.flip();
   242         }
   231         }
   243         if (closed) {
   232         if (closed) {
   244             throw new IOException("Output closed");
   233             throw new IOException("Output closed");
   245         }
   234         }
   246         if (!started) {
   235         if (!started) {
   247             if (previousText && !previousLast) {
   236             if (previousText && !previousFin) {
   248                 // Previous data message was a partial text message
   237                 // Previous data message was a partial text message
   249                 throw new IllegalStateException("Unexpected binary message");
   238                 throw new IllegalStateException("Unexpected binary message");
   250             }
   239             }
   251             expectedLen = src.remaining();
   240             expectedLen = src.remaining();
   252             int mask = maskingKeySource.nextInt();
   241             int mask = maskingKeySource.nextInt();
   253             headerBuffer.clear();
   242             headerBuffer.clear();
   254             headerWriter.fin(last)
   243             headerWriter.fin(last)
   255                     .opcode(previousLast ? Opcode.BINARY : Opcode.CONTINUATION)
   244                     .opcode(previousFin ? Opcode.BINARY : Opcode.CONTINUATION)
   256                     .payloadLen(expectedLen)
   245                     .payloadLen(expectedLen)
   257                     .mask(mask)
   246                     .mask(mask)
   258                     .write(headerBuffer);
   247                     .write(headerBuffer);
   259             headerBuffer.flip();
   248             headerBuffer.flip();
   260             payloadMasker.mask(mask);
   249             payloadMasker.mask(mask);
   261             previousLast = last;
   250             previousFin = last;
   262             previousText = false;
   251             previousText = false;
   263             started = true;
   252             started = true;
   264         }
   253         }
   265         if (!putAvailable(headerBuffer, dst)) {
   254         if (!putAvailable(headerBuffer, dst)) {
   266             return false;
   255             return false;
   410             return false;
   399             return false;
   411         }
   400         }
   412         return maskAvailable(intermediateBuffer, dst) >= 0;
   401         return maskAvailable(intermediateBuffer, dst) >= 0;
   413     }
   402     }
   414 }
   403 }
   415 
       
   416