aboutsummaryrefslogtreecommitdiff
path: root/websocket/src/main/java/fi/iki/elonen/WebSocketFrame.java
diff options
context:
space:
mode:
Diffstat (limited to 'websocket/src/main/java/fi/iki/elonen/WebSocketFrame.java')
-rw-r--r--websocket/src/main/java/fi/iki/elonen/WebSocketFrame.java430
1 files changed, 0 insertions, 430 deletions
diff --git a/websocket/src/main/java/fi/iki/elonen/WebSocketFrame.java b/websocket/src/main/java/fi/iki/elonen/WebSocketFrame.java
deleted file mode 100644
index 0e209df..0000000
--- a/websocket/src/main/java/fi/iki/elonen/WebSocketFrame.java
+++ /dev/null
@@ -1,430 +0,0 @@
-package fi.iki.elonen;
-
-import java.io.EOFException;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.nio.ByteBuffer;
-import java.nio.CharBuffer;
-import java.nio.charset.CharacterCodingException;
-import java.nio.charset.Charset;
-import java.nio.charset.CharsetDecoder;
-import java.nio.charset.CharsetEncoder;
-import java.util.Arrays;
-import java.util.List;
-
-public class WebSocketFrame {
- private OpCode opCode;
- private boolean fin;
- private byte[] maskingKey;
-
- private byte[] payload;
-
- private transient int _payloadLength;
- private transient String _payloadString;
-
- private WebSocketFrame(OpCode opCode, boolean fin) {
- setOpCode(opCode);
- setFin(fin);
- }
-
- public WebSocketFrame(OpCode opCode, boolean fin, byte[] payload, byte[] maskingKey) {
- this(opCode, fin);
- setMaskingKey(maskingKey);
- setBinaryPayload(payload);
- }
-
- public WebSocketFrame(OpCode opCode, boolean fin, byte[] payload) {
- this(opCode, fin, payload, null);
- }
-
- public WebSocketFrame(OpCode opCode, boolean fin, String payload, byte[] maskingKey) throws CharacterCodingException {
- this(opCode, fin);
- setMaskingKey(maskingKey);
- setTextPayload(payload);
- }
-
- public WebSocketFrame(OpCode opCode, boolean fin, String payload) throws CharacterCodingException {
- this(opCode, fin, payload, null);
- }
-
- public WebSocketFrame(WebSocketFrame clone) {
- setOpCode(clone.getOpCode());
- setFin(clone.isFin());
- setBinaryPayload(clone.getBinaryPayload());
- setMaskingKey(clone.getMaskingKey());
- }
-
- public WebSocketFrame(OpCode opCode, List<WebSocketFrame> fragments) throws WebSocketException {
- setOpCode(opCode);
- setFin(true);
-
- long _payloadLength = 0;
- for (WebSocketFrame inter : fragments) {
- _payloadLength += inter.getBinaryPayload().length;
- }
- if (_payloadLength < 0 || _payloadLength > Integer.MAX_VALUE) {
- throw new WebSocketException(CloseCode.MessageTooBig, "Max frame length has been exceeded.");
- }
- this._payloadLength = (int) _payloadLength;
- byte[] payload = new byte[this._payloadLength];
- int offset = 0;
- for (WebSocketFrame inter : fragments) {
- System.arraycopy(inter.getBinaryPayload(), 0, payload, offset, inter.getBinaryPayload().length);
- offset += inter.getBinaryPayload().length;
- }
- setBinaryPayload(payload);
- }
-
- // --------------------------------GETTERS---------------------------------
-
- public OpCode getOpCode() {
- return opCode;
- }
-
- public void setOpCode(OpCode opcode) {
- this.opCode = opcode;
- }
-
- public boolean isFin() {
- return fin;
- }
-
- public void setFin(boolean fin) {
- this.fin = fin;
- }
-
- public boolean isMasked() {
- return maskingKey != null && maskingKey.length == 4;
- }
-
- public byte[] getMaskingKey() {
- return maskingKey;
- }
-
- public void setMaskingKey(byte[] maskingKey) {
- if (maskingKey != null && maskingKey.length != 4) {
- throw new IllegalArgumentException("MaskingKey " + Arrays.toString(maskingKey) + " hasn't length 4");
- }
- this.maskingKey = maskingKey;
- }
-
- public void setUnmasked() {
- setMaskingKey(null);
- }
-
- public byte[] getBinaryPayload() {
- return payload;
- }
-
- public void setBinaryPayload(byte[] payload) {
- this.payload = payload;
- this._payloadLength = payload.length;
- this._payloadString = null;
- }
-
- public String getTextPayload() {
- if (_payloadString == null) {
- try {
- _payloadString = binary2Text(getBinaryPayload());
- } catch (CharacterCodingException e) {
- throw new RuntimeException("Undetected CharacterCodingException", e);
- }
- }
- return _payloadString;
- }
-
- public void setTextPayload(String payload) throws CharacterCodingException {
- this.payload = text2Binary(payload);
- this._payloadLength = payload.length();
- this._payloadString = payload;
- }
-
- // --------------------------------SERIALIZATION---------------------------
-
- public static WebSocketFrame read(InputStream in) throws IOException {
- byte head = (byte) checkedRead(in.read());
- boolean fin = ((head & 0x80) != 0);
- OpCode opCode = OpCode.find((byte) (head & 0x0F));
- if ((head & 0x70) != 0) {
- throw new WebSocketException(CloseCode.ProtocolError, "The reserved bits (" + Integer.toBinaryString(head & 0x70) + ") must be 0.");
- }
- if (opCode == null) {
- throw new WebSocketException(CloseCode.ProtocolError, "Received frame with reserved/unknown opcode " + (head & 0x0F) + ".");
- } else if (opCode.isControlFrame() && !fin) {
- throw new WebSocketException(CloseCode.ProtocolError, "Fragmented control frame.");
- }
-
- WebSocketFrame frame = new WebSocketFrame(opCode, fin);
- frame.readPayloadInfo(in);
- frame.readPayload(in);
- if (frame.getOpCode() == OpCode.Close) {
- return new CloseFrame(frame);
- } else {
- return frame;
- }
- }
-
- private static int checkedRead(int read) throws IOException {
- if (read < 0) {
- throw new EOFException();
- }
- //System.out.println(Integer.toBinaryString(read) + "/" + read + "/" + Integer.toHexString(read));
- return read;
- }
-
-
- private void readPayloadInfo(InputStream in) throws IOException {
- byte b = (byte) checkedRead(in.read());
- boolean masked = ((b & 0x80) != 0);
-
- _payloadLength = (byte) (0x7F & b);
- if (_payloadLength == 126) {
- // checkedRead must return int for this to work
- _payloadLength = (checkedRead(in.read()) << 8 | checkedRead(in.read())) & 0xFFFF;
- if (_payloadLength < 126) {
- throw new WebSocketException(CloseCode.ProtocolError, "Invalid data frame 2byte length. (not using minimal length encoding)");
- }
- } else if (_payloadLength == 127) {
- long _payloadLength = ((long) checkedRead(in.read())) << 56 |
- ((long) checkedRead(in.read())) << 48 |
- ((long) checkedRead(in.read())) << 40 |
- ((long) checkedRead(in.read())) << 32 |
- checkedRead(in.read()) << 24 | checkedRead(in.read()) << 16 | checkedRead(in.read()) << 8 | checkedRead(in.read());
- if (_payloadLength < 65536) {
- throw new WebSocketException(CloseCode.ProtocolError, "Invalid data frame 4byte length. (not using minimal length encoding)");
- }
- if (_payloadLength < 0 || _payloadLength > Integer.MAX_VALUE) {
- throw new WebSocketException(CloseCode.MessageTooBig, "Max frame length has been exceeded.");
- }
- this._payloadLength = (int) _payloadLength;
- }
-
- if (opCode.isControlFrame()) {
- if (_payloadLength > 125) {
- throw new WebSocketException(CloseCode.ProtocolError, "Control frame with payload length > 125 bytes.");
- }
- if (opCode == OpCode.Close && _payloadLength == 1) {
- throw new WebSocketException(CloseCode.ProtocolError, "Received close frame with payload len 1.");
- }
- }
-
- if (masked) {
- maskingKey = new byte[4];
- int read = 0;
- while (read < maskingKey.length) {
- read += checkedRead(in.read(maskingKey, read, maskingKey.length - read));
- }
- }
- }
-
- private void readPayload(InputStream in) throws IOException {
- payload = new byte[_payloadLength];
- int read = 0;
- while (read < _payloadLength) {
- read += checkedRead(in.read(payload, read, _payloadLength - read));
- }
-
- if (isMasked()) {
- for (int i = 0; i < payload.length; i++) {
- payload[i] ^= maskingKey[i % 4];
- }
- }
-
- //Test for Unicode errors
- if (getOpCode() == OpCode.Text) {
- _payloadString = binary2Text(getBinaryPayload());
- }
- }
-
- public void write(OutputStream out) throws IOException {
- byte header = 0;
- if (fin) {
- header |= 0x80;
- }
- header |= opCode.getValue() & 0x0F;
- out.write(header);
-
- _payloadLength = getBinaryPayload().length;
- if (_payloadLength <= 125) {
- out.write(isMasked() ? 0x80 | (byte) _payloadLength : (byte) _payloadLength);
- } else if (_payloadLength <= 0xFFFF) {
- out.write(isMasked() ? 0xFE : 126);
- out.write(_payloadLength >>> 8);
- out.write(_payloadLength);
- } else {
- out.write(isMasked() ? 0xFF : 127);
- out.write(_payloadLength >>> 56 & 0); //integer only contains 31 bit
- out.write(_payloadLength >>> 48 & 0);
- out.write(_payloadLength >>> 40 & 0);
- out.write(_payloadLength >>> 32 & 0);
- out.write(_payloadLength >>> 24);
- out.write(_payloadLength >>> 16);
- out.write(_payloadLength >>> 8);
- out.write(_payloadLength);
- }
-
-
- if (isMasked()) {
- out.write(maskingKey);
- for (int i = 0; i < _payloadLength; i++) {
- out.write(getBinaryPayload()[i] ^ maskingKey[i % 4]);
- }
- } else {
- out.write(getBinaryPayload());
- }
- out.flush();
- }
-
- // --------------------------------ENCODING--------------------------------
-
- public static final Charset TEXT_CHARSET = Charset.forName("UTF-8");
- public static final CharsetDecoder TEXT_DECODER = TEXT_CHARSET.newDecoder();
- public static final CharsetEncoder TEXT_ENCODER = TEXT_CHARSET.newEncoder();
-
-
- public static String binary2Text(byte[] payload) throws CharacterCodingException {
- return TEXT_DECODER.decode(ByteBuffer.wrap(payload)).toString();
- }
-
- public static String binary2Text(byte[] payload, int offset, int length) throws CharacterCodingException {
- return TEXT_DECODER.decode(ByteBuffer.wrap(payload, offset, length)).toString();
- }
-
- public static byte[] text2Binary(String payload) throws CharacterCodingException {
- return TEXT_ENCODER.encode(CharBuffer.wrap(payload)).array();
- }
-
- @Override
- public String toString() {
- final StringBuilder sb = new StringBuilder("WS[");
- sb.append(getOpCode());
- sb.append(", ").append(isFin() ? "fin" : "inter");
- sb.append(", ").append(isMasked() ? "masked" : "unmasked");
- sb.append(", ").append(payloadToString());
- sb.append(']');
- return sb.toString();
- }
-
- protected String payloadToString() {
- if (payload == null) return "null";
- else {
- final StringBuilder sb = new StringBuilder();
- sb.append('[').append(payload.length).append("b] ");
- if (getOpCode() == OpCode.Text) {
- String text = getTextPayload();
- if (text.length() > 100)
- sb.append(text.substring(0, 100)).append("...");
- else
- sb.append(text);
- } else {
- sb.append("0x");
- for (int i = 0; i < Math.min(payload.length, 50); ++i)
- sb.append(Integer.toHexString((int) payload[i] & 0xFF));
- if (payload.length > 50)
- sb.append("...");
- }
- return sb.toString();
- }
- }
-
- // --------------------------------CONSTANTS-------------------------------
-
- public static enum OpCode {
- Continuation(0), Text(1), Binary(2), Close(8), Ping(9), Pong(10);
-
- private final byte code;
-
- private OpCode(int code) {
- this.code = (byte) code;
- }
-
- public byte getValue() {
- return code;
- }
-
- public boolean isControlFrame() {
- return this == Close || this == Ping || this == Pong;
- }
-
- public static OpCode find(byte value) {
- for (OpCode opcode : values()) {
- if (opcode.getValue() == value) {
- return opcode;
- }
- }
- return null;
- }
- }
-
- public static enum CloseCode {
- NormalClosure(1000), GoingAway(1001), ProtocolError(1002), UnsupportedData(1003), NoStatusRcvd(1005),
- AbnormalClosure(1006), InvalidFramePayloadData(1007), PolicyViolation(1008), MessageTooBig(1009),
- MandatoryExt(1010), InternalServerError(1011), TLSHandshake(1015);
-
- private final int code;
-
- private CloseCode(int code) {
- this.code = code;
- }
-
- public int getValue() {
- return code;
- }
-
- public static CloseCode find(int value) {
- for (CloseCode code : values()) {
- if (code.getValue() == value) {
- return code;
- }
- }
- return null;
- }
- }
-
- // ------------------------------------------------------------------------
-
- public static class CloseFrame extends WebSocketFrame {
- private CloseCode _closeCode;
- private String _closeReason;
-
- private CloseFrame(WebSocketFrame wrap) throws CharacterCodingException {
- super(wrap);
- assert wrap.getOpCode() == OpCode.Close;
- if (wrap.getBinaryPayload().length >= 2) {
- _closeCode = CloseCode.find((wrap.getBinaryPayload()[0] & 0xFF) << 8 |
- (wrap.getBinaryPayload()[1] & 0xFF));
- _closeReason = binary2Text(getBinaryPayload(), 2, getBinaryPayload().length - 2);
- }
- }
-
- public CloseFrame(CloseCode code, String closeReason) throws CharacterCodingException {
- super(OpCode.Close, true, generatePayload(code, closeReason));
- }
-
- private static byte[] generatePayload(CloseCode code, String closeReason) throws CharacterCodingException {
- if (code != null) {
- byte[] reasonBytes = text2Binary(closeReason);
- byte[] payload = new byte[reasonBytes.length + 2];
- payload[0] = (byte) ((code.getValue() >> 8) & 0xFF);
- payload[1] = (byte) ((code.getValue()) & 0xFF);
- System.arraycopy(reasonBytes, 0, payload, 2, reasonBytes.length);
- return payload;
- } else {
- return new byte[0];
- }
- }
-
- protected String payloadToString() {
- return (_closeCode != null ? _closeCode : "UnknownCloseCode[" + _closeCode + "]") + (_closeReason != null && !_closeReason.isEmpty() ? ": " + _closeReason : "");
- }
-
- public CloseCode getCloseCode() {
- return _closeCode;
- }
-
- public String getCloseReason() {
- return _closeReason;
- }
- }
-}