src/java.net.http/share/classes/java/net/http/internal/websocket/Frame.java
branchhttp-client-branch
changeset 56092 fd85b2bf2b0d
parent 56091 aedd6133e7a0
child 56093 22d94c4a3641
equal deleted inserted replaced
56091:aedd6133e7a0 56092:fd85b2bf2b0d
     1 /*
       
     2  * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
       
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
       
     4  *
       
     5  * This code is free software; you can redistribute it and/or modify it
       
     6  * under the terms of the GNU General Public License version 2 only, as
       
     7  * published by the Free Software Foundation.  Oracle designates this
       
     8  * particular file as subject to the "Classpath" exception as provided
       
     9  * by Oracle in the LICENSE file that accompanied this code.
       
    10  *
       
    11  * This code is distributed in the hope that it will be useful, but WITHOUT
       
    12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
       
    13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
       
    14  * version 2 for more details (a copy is included in the LICENSE file that
       
    15  * accompanied this code).
       
    16  *
       
    17  * You should have received a copy of the GNU General Public License version
       
    18  * 2 along with this work; if not, write to the Free Software Foundation,
       
    19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
       
    20  *
       
    21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
       
    22  * or visit www.oracle.com if you need additional information or have any
       
    23  * questions.
       
    24  */
       
    25 
       
    26 package java.net.http.internal.websocket;
       
    27 
       
    28 import jdk.internal.vm.annotation.Stable;
       
    29 
       
    30 import java.nio.ByteBuffer;
       
    31 
       
    32 import static java.net.http.internal.common.Utils.dump;
       
    33 import static java.net.http.internal.websocket.Frame.Opcode.ofCode;
       
    34 
       
    35 /*
       
    36  * A collection of utilities for reading, writing, and masking frames.
       
    37  */
       
    38 final class Frame {
       
    39 
       
    40     private Frame() { }
       
    41 
       
    42     static final int MAX_HEADER_SIZE_BYTES = 2 + 8 + 4;
       
    43 
       
    44     enum Opcode {
       
    45 
       
    46         CONTINUATION   (0x0),
       
    47         TEXT           (0x1),
       
    48         BINARY         (0x2),
       
    49         NON_CONTROL_0x3(0x3),
       
    50         NON_CONTROL_0x4(0x4),
       
    51         NON_CONTROL_0x5(0x5),
       
    52         NON_CONTROL_0x6(0x6),
       
    53         NON_CONTROL_0x7(0x7),
       
    54         CLOSE          (0x8),
       
    55         PING           (0x9),
       
    56         PONG           (0xA),
       
    57         CONTROL_0xB    (0xB),
       
    58         CONTROL_0xC    (0xC),
       
    59         CONTROL_0xD    (0xD),
       
    60         CONTROL_0xE    (0xE),
       
    61         CONTROL_0xF    (0xF);
       
    62 
       
    63         @Stable
       
    64         private static final Opcode[] opcodes;
       
    65 
       
    66         static {
       
    67             Opcode[] values = values();
       
    68             opcodes = new Opcode[values.length];
       
    69             for (Opcode c : values) {
       
    70                 opcodes[c.code] = c;
       
    71             }
       
    72         }
       
    73 
       
    74         private final byte code;
       
    75 
       
    76         Opcode(int code) {
       
    77             this.code = (byte) code;
       
    78         }
       
    79 
       
    80         boolean isControl() {
       
    81             return (code & 0x8) != 0;
       
    82         }
       
    83 
       
    84         static Opcode ofCode(int code) {
       
    85             return opcodes[code & 0xF];
       
    86         }
       
    87     }
       
    88 
       
    89     /*
       
    90      * A utility for masking frame payload data.
       
    91      */
       
    92     static final class Masker {
       
    93 
       
    94         // Exploiting ByteBuffer's ability to read/write multi-byte integers
       
    95         private final ByteBuffer acc = ByteBuffer.allocate(8);
       
    96         private final int[] maskBytes = new int[4];
       
    97         private int offset;
       
    98         private long maskLong;
       
    99 
       
   100         /*
       
   101          * Reads all remaining bytes from the given input buffer, masks them
       
   102          * with the supplied mask and writes the resulting bytes to the given
       
   103          * output buffer.
       
   104          *
       
   105          * The source and the destination buffers may be the same instance.
       
   106          */
       
   107         static void transferMasking(ByteBuffer src, ByteBuffer dst, int mask) {
       
   108             if (src.remaining() > dst.remaining()) {
       
   109                 throw new IllegalArgumentException(dump(src, dst));
       
   110             }
       
   111             new Masker().mask(mask).transferMasking(src, dst);
       
   112         }
       
   113 
       
   114         /*
       
   115          * Clears this instance's state and sets the mask.
       
   116          *
       
   117          * The behaviour is as if the mask was set on a newly created instance.
       
   118          */
       
   119         Masker mask(int value) {
       
   120             acc.clear().putInt(value).putInt(value).flip();
       
   121             for (int i = 0; i < maskBytes.length; i++) {
       
   122                 maskBytes[i] = acc.get(i);
       
   123             }
       
   124             offset = 0;
       
   125             maskLong = acc.getLong(0);
       
   126             return this;
       
   127         }
       
   128 
       
   129         /*
       
   130          * Reads as many remaining bytes as possible from the given input
       
   131          * buffer, masks them with the previously set mask and writes the
       
   132          * resulting bytes to the given output buffer.
       
   133          *
       
   134          * The source and the destination buffers may be the same instance. If
       
   135          * the mask hasn't been previously set it is assumed to be 0.
       
   136          */
       
   137         Masker transferMasking(ByteBuffer src, ByteBuffer dst) {
       
   138             begin(src, dst);
       
   139             loop(src, dst);
       
   140             end(src, dst);
       
   141             return this;
       
   142         }
       
   143 
       
   144         /*
       
   145          * Applies up to 3 remaining from the previous pass bytes of the mask.
       
   146          */
       
   147         private void begin(ByteBuffer src, ByteBuffer dst) {
       
   148             if (offset == 0) { // No partially applied mask from the previous invocation
       
   149                 return;
       
   150             }
       
   151             int i = src.position(), j = dst.position();
       
   152             final int srcLim = src.limit(), dstLim = dst.limit();
       
   153             for (; offset < 4 && i < srcLim && j < dstLim; i++, j++, offset++)
       
   154             {
       
   155                 dst.put(j, (byte) (src.get(i) ^ maskBytes[offset]));
       
   156             }
       
   157             offset &= 3; // Will become 0 if the mask has been fully applied
       
   158             src.position(i);
       
   159             dst.position(j);
       
   160         }
       
   161 
       
   162         /*
       
   163          * Gallops one long (mask + mask) at a time.
       
   164          */
       
   165         private void loop(ByteBuffer src, ByteBuffer dst) {
       
   166             int i = src.position();
       
   167             int j = dst.position();
       
   168             final int srcLongLim = src.limit() - 7, dstLongLim = dst.limit() - 7;
       
   169             for (; i < srcLongLim && j < dstLongLim; i += 8, j += 8) {
       
   170                 dst.putLong(j, src.getLong(i) ^ maskLong);
       
   171             }
       
   172             if (i > src.limit()) {
       
   173                 src.position(i - 8);
       
   174             } else {
       
   175                 src.position(i);
       
   176             }
       
   177             if (j > dst.limit()) {
       
   178                 dst.position(j - 8);
       
   179             } else {
       
   180                 dst.position(j);
       
   181             }
       
   182         }
       
   183 
       
   184         /*
       
   185          * Applies up to 7 remaining from the "galloping" phase bytes of the
       
   186          * mask.
       
   187          */
       
   188         private void end(ByteBuffer src, ByteBuffer dst) {
       
   189             assert Math.min(src.remaining(), dst.remaining()) < 8;
       
   190             final int srcLim = src.limit(), dstLim = dst.limit();
       
   191             int i = src.position(), j = dst.position();
       
   192             for (; i < srcLim && j < dstLim;
       
   193                  i++, j++, offset = (offset + 1) & 3) // offset cycles through 0..3
       
   194             {
       
   195                 dst.put(j, (byte) (src.get(i) ^ maskBytes[offset]));
       
   196             }
       
   197             src.position(i);
       
   198             dst.position(j);
       
   199         }
       
   200     }
       
   201 
       
   202     /*
       
   203      * A builder-style writer of frame headers.
       
   204      *
       
   205      * The writer does not enforce any protocol-level rules, it simply writes a
       
   206      * header structure to the given buffer. The order of calls to intermediate
       
   207      * methods is NOT significant.
       
   208      */
       
   209     static final class HeaderWriter {
       
   210 
       
   211         private char firstChar;
       
   212         private long payloadLen;
       
   213         private int maskingKey;
       
   214         private boolean mask;
       
   215 
       
   216         HeaderWriter fin(boolean value) {
       
   217             if (value) {
       
   218                 firstChar |=  0b10000000_00000000;
       
   219             } else {
       
   220                 firstChar &= ~0b10000000_00000000;
       
   221             }
       
   222             return this;
       
   223         }
       
   224 
       
   225         HeaderWriter rsv1(boolean value) {
       
   226             if (value) {
       
   227                 firstChar |=  0b01000000_00000000;
       
   228             } else {
       
   229                 firstChar &= ~0b01000000_00000000;
       
   230             }
       
   231             return this;
       
   232         }
       
   233 
       
   234         HeaderWriter rsv2(boolean value) {
       
   235             if (value) {
       
   236                 firstChar |=  0b00100000_00000000;
       
   237             } else {
       
   238                 firstChar &= ~0b00100000_00000000;
       
   239             }
       
   240             return this;
       
   241         }
       
   242 
       
   243         HeaderWriter rsv3(boolean value) {
       
   244             if (value) {
       
   245                 firstChar |=  0b00010000_00000000;
       
   246             } else {
       
   247                 firstChar &= ~0b00010000_00000000;
       
   248             }
       
   249             return this;
       
   250         }
       
   251 
       
   252         HeaderWriter opcode(Opcode value) {
       
   253             firstChar = (char) ((firstChar & 0xF0FF) | (value.code << 8));
       
   254             return this;
       
   255         }
       
   256 
       
   257         HeaderWriter payloadLen(long value) {
       
   258             if (value < 0) {
       
   259                 throw new IllegalArgumentException("Negative: " + value);
       
   260             }
       
   261             payloadLen = value;
       
   262             firstChar &= 0b11111111_10000000; // Clear previous payload length leftovers
       
   263             if (payloadLen < 126) {
       
   264                 firstChar |= payloadLen;
       
   265             } else if (payloadLen < 65536) {
       
   266                 firstChar |= 126;
       
   267             } else {
       
   268                 firstChar |= 127;
       
   269             }
       
   270             return this;
       
   271         }
       
   272 
       
   273         HeaderWriter mask(int value) {
       
   274             firstChar |= 0b00000000_10000000;
       
   275             maskingKey = value;
       
   276             mask = true;
       
   277             return this;
       
   278         }
       
   279 
       
   280         HeaderWriter noMask() {
       
   281             firstChar &= ~0b00000000_10000000;
       
   282             mask = false;
       
   283             return this;
       
   284         }
       
   285 
       
   286         /*
       
   287          * Writes the header to the given buffer.
       
   288          *
       
   289          * The buffer must have at least MAX_HEADER_SIZE_BYTES remaining. The
       
   290          * buffer's position is incremented by the number of bytes written.
       
   291          */
       
   292         void write(ByteBuffer buffer) {
       
   293             buffer.putChar(firstChar);
       
   294             if (payloadLen >= 126) {
       
   295                 if (payloadLen < 65536) {
       
   296                     buffer.putChar((char) payloadLen);
       
   297                 } else {
       
   298                     buffer.putLong(payloadLen);
       
   299                 }
       
   300             }
       
   301             if (mask) {
       
   302                 buffer.putInt(maskingKey);
       
   303             }
       
   304         }
       
   305     }
       
   306 
       
   307     /*
       
   308      * A consumer of frame parts.
       
   309      *
       
   310      * Frame.Reader invokes the consumer's methods in the following order:
       
   311      *
       
   312      *     fin rsv1 rsv2 rsv3 opcode mask payloadLength maskingKey? payloadData+ endFrame
       
   313      */
       
   314     interface Consumer {
       
   315 
       
   316         void fin(boolean value);
       
   317 
       
   318         void rsv1(boolean value);
       
   319 
       
   320         void rsv2(boolean value);
       
   321 
       
   322         void rsv3(boolean value);
       
   323 
       
   324         void opcode(Opcode value);
       
   325 
       
   326         void mask(boolean value);
       
   327 
       
   328         void payloadLen(long value);
       
   329 
       
   330         void maskingKey(int value);
       
   331 
       
   332         /*
       
   333          * Called by the Frame.Reader when a part of the (or a complete) payload
       
   334          * is ready to be consumed.
       
   335          *
       
   336          * The sum of numbers of bytes consumed in each invocation of this
       
   337          * method corresponding to the given frame WILL be equal to
       
   338          * 'payloadLen', reported to `void payloadLen(long value)` before that.
       
   339          *
       
   340          * In particular, if `payloadLen` is 0, then there WILL be a single
       
   341          * invocation to this method.
       
   342          *
       
   343          * No unmasking is done.
       
   344          */
       
   345         void payloadData(ByteBuffer data);
       
   346 
       
   347         void endFrame();
       
   348     }
       
   349 
       
   350     /*
       
   351      * A Reader of frames.
       
   352      *
       
   353      * No protocol-level rules are checked.
       
   354      */
       
   355     static final class Reader {
       
   356 
       
   357         private static final int AWAITING_FIRST_BYTE  =  1;
       
   358         private static final int AWAITING_SECOND_BYTE =  2;
       
   359         private static final int READING_16_LENGTH    =  4;
       
   360         private static final int READING_64_LENGTH    =  8;
       
   361         private static final int READING_MASK         = 16;
       
   362         private static final int READING_PAYLOAD      = 32;
       
   363 
       
   364         // Exploiting ByteBuffer's ability to read multi-byte integers
       
   365         private final ByteBuffer accumulator = ByteBuffer.allocate(8);
       
   366         private int state = AWAITING_FIRST_BYTE;
       
   367         private boolean mask;
       
   368         private long remainingPayloadLength;
       
   369 
       
   370         /*
       
   371          * Reads at most one frame from the given buffer invoking the consumer's
       
   372          * methods corresponding to the frame parts found.
       
   373          *
       
   374          * As much of the frame's payload, if any, is read. The buffer's
       
   375          * position is updated to reflect the number of bytes read.
       
   376          *
       
   377          * Throws FailWebSocketException if detects the frame is malformed.
       
   378          */
       
   379         void readFrame(ByteBuffer input, Consumer consumer) {
       
   380             loop:
       
   381             while (true) {
       
   382                 byte b;
       
   383                 switch (state) {
       
   384                     case AWAITING_FIRST_BYTE:
       
   385                         if (!input.hasRemaining()) {
       
   386                             break loop;
       
   387                         }
       
   388                         b = input.get();
       
   389                         consumer.fin( (b & 0b10000000) != 0);
       
   390                         consumer.rsv1((b & 0b01000000) != 0);
       
   391                         consumer.rsv2((b & 0b00100000) != 0);
       
   392                         consumer.rsv3((b & 0b00010000) != 0);
       
   393                         consumer.opcode(ofCode(b));
       
   394                         state = AWAITING_SECOND_BYTE;
       
   395                         continue loop;
       
   396                     case AWAITING_SECOND_BYTE:
       
   397                         if (!input.hasRemaining()) {
       
   398                             break loop;
       
   399                         }
       
   400                         b = input.get();
       
   401                         consumer.mask(mask = (b & 0b10000000) != 0);
       
   402                         byte p1 = (byte) (b & 0b01111111);
       
   403                         if (p1 < 126) {
       
   404                             assert p1 >= 0 : p1;
       
   405                             consumer.payloadLen(remainingPayloadLength = p1);
       
   406                             state = mask ? READING_MASK : READING_PAYLOAD;
       
   407                         } else if (p1 < 127) {
       
   408                             state = READING_16_LENGTH;
       
   409                         } else {
       
   410                             state = READING_64_LENGTH;
       
   411                         }
       
   412                         continue loop;
       
   413                     case READING_16_LENGTH:
       
   414                         if (!input.hasRemaining()) {
       
   415                             break loop;
       
   416                         }
       
   417                         b = input.get();
       
   418                         if (accumulator.put(b).position() < 2) {
       
   419                             continue loop;
       
   420                         }
       
   421                         remainingPayloadLength = accumulator.flip().getChar();
       
   422                         if (remainingPayloadLength < 126) {
       
   423                             throw notMinimalEncoding(remainingPayloadLength);
       
   424                         }
       
   425                         consumer.payloadLen(remainingPayloadLength);
       
   426                         accumulator.clear();
       
   427                         state = mask ? READING_MASK : READING_PAYLOAD;
       
   428                         continue loop;
       
   429                     case READING_64_LENGTH:
       
   430                         if (!input.hasRemaining()) {
       
   431                             break loop;
       
   432                         }
       
   433                         b = input.get();
       
   434                         if (accumulator.put(b).position() < 8) {
       
   435                             continue loop;
       
   436                         }
       
   437                         remainingPayloadLength = accumulator.flip().getLong();
       
   438                         if (remainingPayloadLength < 0) {
       
   439                             throw negativePayload(remainingPayloadLength);
       
   440                         } else if (remainingPayloadLength < 65536) {
       
   441                             throw notMinimalEncoding(remainingPayloadLength);
       
   442                         }
       
   443                         consumer.payloadLen(remainingPayloadLength);
       
   444                         accumulator.clear();
       
   445                         state = mask ? READING_MASK : READING_PAYLOAD;
       
   446                         continue loop;
       
   447                     case READING_MASK:
       
   448                         if (!input.hasRemaining()) {
       
   449                             break loop;
       
   450                         }
       
   451                         b = input.get();
       
   452                         if (accumulator.put(b).position() != 4) {
       
   453                             continue loop;
       
   454                         }
       
   455                         consumer.maskingKey(accumulator.flip().getInt());
       
   456                         accumulator.clear();
       
   457                         state = READING_PAYLOAD;
       
   458                         continue loop;
       
   459                     case READING_PAYLOAD:
       
   460                         // This state does not require any bytes to be available
       
   461                         // in the input buffer in order to proceed
       
   462                         int deliverable = (int) Math.min(remainingPayloadLength,
       
   463                                                          input.remaining());
       
   464                         int oldLimit = input.limit();
       
   465                         input.limit(input.position() + deliverable);
       
   466                         if (deliverable != 0 || remainingPayloadLength == 0) {
       
   467                             consumer.payloadData(input);
       
   468                         }
       
   469                         int consumed = deliverable - input.remaining();
       
   470                         if (consumed < 0) {
       
   471                             // Consumer cannot consume more than there was available
       
   472                             throw new InternalError();
       
   473                         }
       
   474                         input.limit(oldLimit);
       
   475                         remainingPayloadLength -= consumed;
       
   476                         if (remainingPayloadLength == 0) {
       
   477                             consumer.endFrame();
       
   478                             state = AWAITING_FIRST_BYTE;
       
   479                         }
       
   480                         break loop;
       
   481                     default:
       
   482                         throw new InternalError(String.valueOf(state));
       
   483                 }
       
   484             }
       
   485         }
       
   486 
       
   487         private static FailWebSocketException negativePayload(long payloadLength)
       
   488         {
       
   489             return new FailWebSocketException(
       
   490                     "Negative payload length: " + payloadLength);
       
   491         }
       
   492 
       
   493         private static FailWebSocketException notMinimalEncoding(long payloadLength)
       
   494         {
       
   495             return new FailWebSocketException(
       
   496                     "Not minimally-encoded payload length:" + payloadLength);
       
   497         }
       
   498     }
       
   499 }