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); |
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; |