< prev index next >

src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/OutgoingMessage.java

Print this page




  54  * with mutability, security, masking/unmasking, readonly status, etc. So
  55  * copying greatly simplifies the implementation.
  56  *
  57  * In the case of memory-sensitive environments an alternative implementation
  58  * could use an internal pool of buffers though at the cost of extra complexity
  59  * and possible performance degradation.
  60  */
  61 abstract class OutgoingMessage {
  62 
  63     // Share per WebSocket?
  64     private static final SecureRandom maskingKeys = new SecureRandom();
  65 
  66     protected ByteBuffer[] frame;
  67     protected int offset;
  68 
  69     /*
  70      * Performs contextualization. This method is not a part of the constructor
  71      * so it would be possible to defer the work it does until the most
  72      * convenient moment (up to the point where sentTo is invoked).
  73      */
  74     protected void contextualize(Context context) {
  75         // masking and charset decoding should be performed here rather than in
  76         // the constructor (as of today)
  77         if (context.isCloseSent()) {
  78             throw new IllegalStateException("Close sent");
  79         }

  80     }
  81 
  82     protected boolean sendTo(RawChannel channel) throws IOException {
  83         while ((offset = nextUnwrittenIndex()) != -1) {
  84             long n = channel.write(frame, offset, frame.length - offset);
  85             if (n == 0) {
  86                 return false;
  87             }
  88         }
  89         return true;
  90     }
  91 
  92     private int nextUnwrittenIndex() {
  93         for (int i = offset; i < frame.length; i++) {
  94             if (frame[i].hasRemaining()) {
  95                 return i;
  96             }
  97         }
  98         return -1;
  99     }
 100 
 101     static final class Text extends OutgoingMessage {
 102 
 103         private final ByteBuffer payload;
 104         private final boolean isLast;
 105 
 106         Text(CharSequence characters, boolean isLast) {
 107             CharsetEncoder encoder = UTF_8.newEncoder(); // Share per WebSocket?
 108             try {
 109                 payload = encoder.encode(CharBuffer.wrap(characters));
 110             } catch (CharacterCodingException e) {
 111                 throw new IllegalArgumentException(
 112                         "Malformed UTF-8 text message");
 113             }
 114             this.isLast = isLast;
 115         }
 116 
 117         @Override
 118         protected void contextualize(Context context) {
 119             super.contextualize(context);
 120             if (context.isPreviousBinary() && !context.isPreviousLast()) {
 121                 throw new IllegalStateException("Unexpected text message");
 122             }
 123             frame = getDataMessageBuffers(
 124                     TEXT, context.isPreviousLast(), isLast, payload, payload);
 125             context.setPreviousBinary(false);
 126             context.setPreviousText(true);
 127             context.setPreviousLast(isLast);

 128         }
 129     }
 130 
 131     static final class Binary extends OutgoingMessage {
 132 
 133         private final ByteBuffer payload;
 134         private final boolean isLast;
 135 
 136         Binary(ByteBuffer payload, boolean isLast) {
 137             this.payload = requireNonNull(payload);
 138             this.isLast = isLast;
 139         }
 140 
 141         @Override
 142         protected void contextualize(Context context) {
 143             super.contextualize(context);
 144             if (context.isPreviousText() && !context.isPreviousLast()) {
 145                 throw new IllegalStateException("Unexpected binary message");
 146             }
 147             ByteBuffer newBuffer = ByteBuffer.allocate(payload.remaining());
 148             frame = getDataMessageBuffers(
 149                     BINARY, context.isPreviousLast(), isLast, payload, newBuffer);
 150             context.setPreviousText(false);
 151             context.setPreviousBinary(true);
 152             context.setPreviousLast(isLast);

 153         }
 154     }
 155 
 156     static final class Ping extends OutgoingMessage {
 157 
 158         Ping(ByteBuffer payload) {
 159             frame = getControlMessageBuffers(PING, payload);
 160         }
 161     }
 162 
 163     static final class Pong extends OutgoingMessage {
 164 
 165         Pong(ByteBuffer payload) {
 166             frame = getControlMessageBuffers(PONG, payload);
 167         }
 168     }
 169 
 170     static final class Close extends OutgoingMessage {
 171 
 172         Close() {


 178                     .putChar((char) statusCode);
 179             CoderResult result = UTF_8.newEncoder()
 180                     .encode(CharBuffer.wrap(reason),
 181                             payload,
 182                             true);
 183             if (result.isOverflow()) {
 184                 throw new IllegalArgumentException("Long reason");
 185             } else if (result.isError()) {
 186                 try {
 187                     result.throwException();
 188                 } catch (CharacterCodingException e) {
 189                     throw new IllegalArgumentException(
 190                             "Malformed UTF-8 reason", e);
 191                 }
 192             }
 193             payload.flip();
 194             frame = getControlMessageBuffers(CLOSE, payload);
 195         }
 196 
 197         @Override
 198         protected void contextualize(Context context) {
 199             super.contextualize(context);


 200             context.setCloseSent();


 201         }
 202     }
 203 
 204     private static ByteBuffer[] getControlMessageBuffers(Opcode opcode,
 205                                                          ByteBuffer payload) {
 206         assert opcode.isControl() : opcode;
 207         int remaining = payload.remaining();
 208         if (remaining > 125) {
 209             throw new IllegalArgumentException
 210                     ("Long message: " + remaining);
 211         }
 212         ByteBuffer frame = ByteBuffer.allocate(MAX_HEADER_SIZE_BYTES + remaining);
 213         int mask = maskingKeys.nextInt();
 214         new Frame.HeaderWriter()
 215                 .fin(true)
 216                 .opcode(opcode)
 217                 .payloadLen(remaining)
 218                 .mask(mask)
 219                 .write(frame);
 220         Frame.Masker.transferMasking(payload, frame, mask);




  54  * with mutability, security, masking/unmasking, readonly status, etc. So
  55  * copying greatly simplifies the implementation.
  56  *
  57  * In the case of memory-sensitive environments an alternative implementation
  58  * could use an internal pool of buffers though at the cost of extra complexity
  59  * and possible performance degradation.
  60  */
  61 abstract class OutgoingMessage {
  62 
  63     // Share per WebSocket?
  64     private static final SecureRandom maskingKeys = new SecureRandom();
  65 
  66     protected ByteBuffer[] frame;
  67     protected int offset;
  68 
  69     /*
  70      * Performs contextualization. This method is not a part of the constructor
  71      * so it would be possible to defer the work it does until the most
  72      * convenient moment (up to the point where sentTo is invoked).
  73      */
  74     protected boolean contextualize(Context context) {
  75         // masking and charset decoding should be performed here rather than in
  76         // the constructor (as of today)
  77         if (context.isCloseSent()) {
  78             throw new IllegalStateException("Close sent");
  79         }
  80         return true;
  81     }
  82 
  83     protected boolean sendTo(RawChannel channel) throws IOException {
  84         while ((offset = nextUnwrittenIndex()) != -1) {
  85             long n = channel.write(frame, offset, frame.length - offset);
  86             if (n == 0) {
  87                 return false;
  88             }
  89         }
  90         return true;
  91     }
  92 
  93     private int nextUnwrittenIndex() {
  94         for (int i = offset; i < frame.length; i++) {
  95             if (frame[i].hasRemaining()) {
  96                 return i;
  97             }
  98         }
  99         return -1;
 100     }
 101 
 102     static final class Text extends OutgoingMessage {
 103 
 104         private final ByteBuffer payload;
 105         private final boolean isLast;
 106 
 107         Text(CharSequence characters, boolean isLast) {
 108             CharsetEncoder encoder = UTF_8.newEncoder(); // Share per WebSocket?
 109             try {
 110                 payload = encoder.encode(CharBuffer.wrap(characters));
 111             } catch (CharacterCodingException e) {
 112                 throw new IllegalArgumentException(
 113                         "Malformed UTF-8 text message");
 114             }
 115             this.isLast = isLast;
 116         }
 117 
 118         @Override
 119         protected boolean contextualize(Context context) {
 120             super.contextualize(context);
 121             if (context.isPreviousBinary() && !context.isPreviousLast()) {
 122                 throw new IllegalStateException("Unexpected text message");
 123             }
 124             frame = getDataMessageBuffers(
 125                     TEXT, context.isPreviousLast(), isLast, payload, payload);
 126             context.setPreviousBinary(false);
 127             context.setPreviousText(true);
 128             context.setPreviousLast(isLast);
 129             return true;
 130         }
 131     }
 132 
 133     static final class Binary extends OutgoingMessage {
 134 
 135         private final ByteBuffer payload;
 136         private final boolean isLast;
 137 
 138         Binary(ByteBuffer payload, boolean isLast) {
 139             this.payload = requireNonNull(payload);
 140             this.isLast = isLast;
 141         }
 142 
 143         @Override
 144         protected boolean contextualize(Context context) {
 145             super.contextualize(context);
 146             if (context.isPreviousText() && !context.isPreviousLast()) {
 147                 throw new IllegalStateException("Unexpected binary message");
 148             }
 149             ByteBuffer newBuffer = ByteBuffer.allocate(payload.remaining());
 150             frame = getDataMessageBuffers(
 151                     BINARY, context.isPreviousLast(), isLast, payload, newBuffer);
 152             context.setPreviousText(false);
 153             context.setPreviousBinary(true);
 154             context.setPreviousLast(isLast);
 155             return true;
 156         }
 157     }
 158 
 159     static final class Ping extends OutgoingMessage {
 160 
 161         Ping(ByteBuffer payload) {
 162             frame = getControlMessageBuffers(PING, payload);
 163         }
 164     }
 165 
 166     static final class Pong extends OutgoingMessage {
 167 
 168         Pong(ByteBuffer payload) {
 169             frame = getControlMessageBuffers(PONG, payload);
 170         }
 171     }
 172 
 173     static final class Close extends OutgoingMessage {
 174 
 175         Close() {


 181                     .putChar((char) statusCode);
 182             CoderResult result = UTF_8.newEncoder()
 183                     .encode(CharBuffer.wrap(reason),
 184                             payload,
 185                             true);
 186             if (result.isOverflow()) {
 187                 throw new IllegalArgumentException("Long reason");
 188             } else if (result.isError()) {
 189                 try {
 190                     result.throwException();
 191                 } catch (CharacterCodingException e) {
 192                     throw new IllegalArgumentException(
 193                             "Malformed UTF-8 reason", e);
 194                 }
 195             }
 196             payload.flip();
 197             frame = getControlMessageBuffers(CLOSE, payload);
 198         }
 199 
 200         @Override
 201         protected boolean contextualize(Context context) {
 202             if (context.isCloseSent()) {
 203                 return false;
 204             } else {
 205                 context.setCloseSent();
 206                 return true;
 207             }
 208         }
 209     }
 210 
 211     private static ByteBuffer[] getControlMessageBuffers(Opcode opcode,
 212                                                          ByteBuffer payload) {
 213         assert opcode.isControl() : opcode;
 214         int remaining = payload.remaining();
 215         if (remaining > 125) {
 216             throw new IllegalArgumentException
 217                     ("Long message: " + remaining);
 218         }
 219         ByteBuffer frame = ByteBuffer.allocate(MAX_HEADER_SIZE_BYTES + remaining);
 220         int mask = maskingKeys.nextInt();
 221         new Frame.HeaderWriter()
 222                 .fin(true)
 223                 .opcode(opcode)
 224                 .payloadLen(remaining)
 225                 .mask(mask)
 226                 .write(frame);
 227         Frame.Masker.transferMasking(payload, frame, mask);


< prev index next >