All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.fireflysource.net.websocket.common.frame.WebSocketFrame Maven / Gradle / Ivy

There is a newer version: 5.0.2
Show newest version
package com.fireflysource.net.websocket.common.frame;

import com.fireflysource.common.io.BufferUtils;
import com.fireflysource.net.websocket.common.model.OpCode;

import java.nio.ByteBuffer;
import java.util.Arrays;

/**
 * A Base Frame as seen in RFC 6455. Sec 5.2
 *
 * 
 *    0                   1                   2                   3
 *    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
 *   +-+-+-+-+-------+-+-------------+-------------------------------+
 *   |F|R|R|R| opcode|M| Payload len |    Extended payload length    |
 *   |I|S|S|S|  (4)  |A|     (7)     |             (16/64)           |
 *   |N|V|V|V|       |S|             |   (if payload len==126/127)   |
 *   | |1|2|3|       |K|             |                               |
 *   +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
 *   |     Extended payload length continued, if payload len == 127  |
 *   + - - - - - - - - - - - - - - - +-------------------------------+
 *   |                               |Masking-key, if MASK set to 1  |
 *   +-------------------------------+-------------------------------+
 *   | Masking-key (continued)       |          Payload Data         |
 *   +-------------------------------- - - - - - - - - - - - - - - - +
 *   :                     Payload Data continued ...                :
 *   + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
 *   |                     Payload Data continued ...                |
 *   +---------------------------------------------------------------+
 * 
*/ public abstract class WebSocketFrame implements Frame { public static WebSocketFrame copy(Frame original) { WebSocketFrame copy; switch (original.getOpCode()) { case OpCode.BINARY: copy = new BinaryFrame(); break; case OpCode.TEXT: copy = new TextFrame(); break; case OpCode.CLOSE: copy = new CloseFrame(); break; case OpCode.CONTINUATION: copy = new ContinuationFrame(); break; case OpCode.PING: copy = new PingFrame(); break; case OpCode.PONG: copy = new PongFrame(); break; default: throw new IllegalArgumentException("Cannot copy frame with opcode " + original.getOpCode() + " - " + original); } copy.copyHeaders(original); ByteBuffer payload = original.getPayload(); if (payload != null) { ByteBuffer payloadCopy = ByteBuffer.allocate(payload.remaining()); payloadCopy.put(payload.slice()).flip(); copy.setPayload(payloadCopy); } return copy; } /** * Combined FIN + RSV1 + RSV2 + RSV3 + OpCode byte. * *
     *   1000_0000 (0x80) = fin
     *   0100_0000 (0x40) = rsv1
     *   0010_0000 (0x20) = rsv2
     *   0001_0000 (0x10) = rsv3
     *   0000_1111 (0x0F) = opcode
     * 
*/ protected byte finRsvOp; protected boolean masked = false; protected byte[] mask; /** * The payload data. *

* It is assumed to always be in FLUSH mode (ready to read) in this object. */ protected ByteBuffer data; /** * Construct form opcode * * @param opcode the opcode the frame is based on */ protected WebSocketFrame(byte opcode) { reset(); setOpCode(opcode); } public abstract void assertValid(); protected void copyHeaders(Frame frame) { finRsvOp = 0x00; finRsvOp |= frame.isFin() ? 0x80 : 0x00; finRsvOp |= frame.isRsv1() ? 0x40 : 0x00; finRsvOp |= frame.isRsv2() ? 0x20 : 0x00; finRsvOp |= frame.isRsv3() ? 0x10 : 0x00; finRsvOp |= frame.getOpCode() & 0x0F; masked = frame.isMasked(); if (masked) { mask = frame.getMask(); } else { mask = null; } } protected void copyHeaders(WebSocketFrame copy) { finRsvOp = copy.finRsvOp; masked = copy.masked; mask = null; if (copy.mask != null) mask = Arrays.copyOf(copy.mask, copy.mask.length); } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } WebSocketFrame other = (WebSocketFrame) obj; if (data == null) { if (other.data != null) { return false; } } else if (!data.equals(other.data)) { return false; } if (finRsvOp != other.finRsvOp) { return false; } if (!Arrays.equals(mask, other.mask)) { return false; } return masked == other.masked; } @Override public byte[] getMask() { return mask; } @Override public final byte getOpCode() { return (byte) (finRsvOp & 0x0F); } /** * Get the payload ByteBuffer. possible null. */ @Override public ByteBuffer getPayload() { return data; } public String getPayloadAsUTF8() { return BufferUtils.toUTF8String(getPayload()); } @Override public int getPayloadLength() { if (data == null) { return 0; } return data.remaining(); } @Override public Type getType() { return Type.from(getOpCode()); } @Override public int hashCode() { final int prime = 31; int result = 1; result = (prime * result) + ((data == null) ? 0 : data.hashCode()); result = (prime * result) + finRsvOp; result = (prime * result) + Arrays.hashCode(mask); return result; } @Override public boolean hasPayload() { return ((data != null) && data.hasRemaining()); } public abstract boolean isControlFrame(); public abstract boolean isDataFrame(); @Override public boolean isFin() { return (byte) (finRsvOp & 0x80) != 0; } @Override public boolean isMasked() { return masked; } @Override public boolean isRsv1() { return (byte) (finRsvOp & 0x40) != 0; } @Override public boolean isRsv2() { return (byte) (finRsvOp & 0x20) != 0; } @Override public boolean isRsv3() { return (byte) (finRsvOp & 0x10) != 0; } public void reset() { finRsvOp = (byte) 0x80; // FIN (!RSV, opcode 0) masked = false; data = null; mask = null; } public WebSocketFrame setFin(boolean fin) { // set bit 1 this.finRsvOp = (byte) ((finRsvOp & 0x7F) | (fin ? 0x80 : 0x00)); return this; } public Frame setMask(byte[] maskingKey) { this.mask = maskingKey; this.masked = (mask != null); return this; } public Frame setMasked(boolean mask) { this.masked = mask; return this; } protected WebSocketFrame setOpCode(byte op) { this.finRsvOp = (byte) ((finRsvOp & 0xF0) | (op & 0x0F)); return this; } /** * Set the data payload. *

* The provided buffer will be used as is, no copying of bytes performed. *

* The provided buffer should be flipped and ready to READ from. * * @param buf the bytebuffer to set * @return the frame itself */ public WebSocketFrame setPayload(ByteBuffer buf) { data = buf; return this; } public WebSocketFrame setRsv1(boolean rsv1) { // set bit 2 this.finRsvOp = (byte) ((finRsvOp & 0xBF) | (rsv1 ? 0x40 : 0x00)); return this; } public WebSocketFrame setRsv2(boolean rsv2) { // set bit 3 this.finRsvOp = (byte) ((finRsvOp & 0xDF) | (rsv2 ? 0x20 : 0x00)); return this; } public WebSocketFrame setRsv3(boolean rsv3) { // set bit 4 this.finRsvOp = (byte) ((finRsvOp & 0xEF) | (rsv3 ? 0x10 : 0x00)); return this; } @Override public String toString() { StringBuilder b = new StringBuilder(); b.append(OpCode.name((byte) (finRsvOp & 0x0F))); b.append('['); b.append("len=").append(getPayloadLength()); b.append(",fin=").append((finRsvOp & 0x80) != 0); b.append(",rsv="); b.append(((finRsvOp & 0x40) != 0) ? '1' : '.'); b.append(((finRsvOp & 0x20) != 0) ? '1' : '.'); b.append(((finRsvOp & 0x10) != 0) ? '1' : '.'); b.append(",masked=").append(masked); b.append(']'); return b.toString(); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy