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

com.firenio.codec.http11.WebSocketCodec Maven / Gradle / Ivy

There is a newer version: 1.3.6
Show newest version
/*
 * Copyright 2015 The FireNio Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.firenio.codec.http11;

import java.io.IOException;
import java.util.List;

import com.firenio.buffer.ByteBuf;
import com.firenio.component.Frame;
import com.firenio.component.NioEventLoop;
import com.firenio.component.Channel;
import com.firenio.component.ProtocolCodec;

//FIXME 心跳貌似由服务端发起

/**
 * 
 *
 *       0               1               2               3
 *       0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
 *      +-+-+-+-+-------+-+-------------+-------------------------------+
 *      |F|R|R|R| opcode|M| Payload len |    Extended payload length    |
 *      |I|S|S|S|  (4)  |A|     (7)     |             (16/32)           |
 *      |N|V|V|V|       |S|             |   (if payload len==126/127)   |
 *      | |1|2|3|       |K|             |       (unsigned(2byte))       |
 *      +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
 *      |     Extended payload length continued, if payload len == 127  |
 *      + - - - - - - - - - - - - - - - +-------------------------------+
 *      |    payload len (4+2+2)        |Masking-key, if MASK set to 1  |
 *      +-------------------------------+-------------------------------+
 *      | Masking-key (continued)       |          Payload Data         |
 *      +-------------------------------- - - - - - - - - - - - - - - - +
 *      :                     Payload Data continued ...                :
 *      + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
 *      |                     Payload Data continued ...                |
 *      +---------------------------------------------------------------+
 *
 *   ref: https://tools.ietf.org/html/rfc6455
 * 
*/ public class WebSocketCodec extends ProtocolCodec { public static final String FRAME_STACK_KEY = "FRAME_WS_STACK_KEY"; public static final int HEADER_LENGTH = 2; public static final int MAX_HEADER_LENGTH = 10; public static final int MAX_UNSIGNED_SHORT = 0xffff; public static final IOException OVER_LIMIT = EXCEPTION("over limit"); public static final String PROTOCOL_ID = "WebSocket"; public static final byte TYPE_BINARY = 2; public static final byte TYPE_CLOSE = 8; public static final byte TYPE_CONTINUE = 0; public static final byte TYPE_PING = 9; public static final byte TYPE_PONG = 10; public static final byte TYPE_TEXT = 1; private final int frameStackSize; private final int limit; public WebSocketCodec() { this(1024 * 64); } public WebSocketCodec(int limit) { this(limit, 0); } public WebSocketCodec(int limit, int frameStackSize) { this.limit = limit; this.frameStackSize = frameStackSize; } @Override public Frame decode(Channel ch, ByteBuf src) throws IOException { if (src.remaining() < HEADER_LENGTH) { return null; } src.markP(); byte b0 = src.getByte(); byte b1 = src.getByte(); int dataLen = 0; boolean hasMask = (b1 & 0b10000000) > 0; if (hasMask) { dataLen += 4; } int payloadLen = (b1 & 0x7f); if (payloadLen < 126) { } else if (payloadLen == 126) { dataLen += 2; if (src.remaining() < dataLen) { src.resetP(); return null; } payloadLen = src.getUnsignedShort(); } else { dataLen += 8; if (src.remaining() < dataLen) { src.resetP(); return null; } payloadLen = (int) src.getLong(); if (payloadLen < 0) { throw OVER_LIMIT; } } if (payloadLen > limit) { throw OVER_LIMIT; } if (src.remaining() < payloadLen) { src.resetP(); return null; } boolean eof = (b0 & 0b10000000) > 0; byte type = (byte) (b0 & 0xF); if (type == TYPE_PING) { return newWebSocketFrame(ch).setPing(); } else if (type == TYPE_PONG) { return newWebSocketFrame(ch).setPong(); } byte[] array = new byte[payloadLen]; if (hasMask) { byte m0 = src.getByte(); byte m1 = src.getByte(); byte m2 = src.getByte(); byte m3 = src.getByte(); src.getBytes(array); int length = array.length; int len = (length / 4) * 4; for (int i = 0; i < len; i += 4) { array[i + 0] ^= m0; array[i + 1] ^= m1; array[i + 2] ^= m2; array[i + 3] ^= m3; } if (len < length) { int remain = length - len; if (remain == 1) { array[len + 0] ^= m0; } else if (remain == 2) { array[len + 0] ^= m0; array[len + 1] ^= m1; } else { array[len + 0] ^= m0; array[len + 1] ^= m1; array[len + 2] ^= m2; } } } else { src.getBytes(array); } WebSocketFrame f = newWebSocketFrame(ch); f.setEof(eof); f.setType(type); if (type == TYPE_TEXT) { f.setContent(new String(array, ch.getCharset())); } else if (type == TYPE_BINARY) { f.setContent(array); } return f; } @Override public ByteBuf encode(Channel ch, Frame frame) { WebSocketFrame f = (WebSocketFrame) frame; ByteBuf buf = f.getBufContent(); if (buf != null) { buf.flip(); } else { buf = ch.allocate().limit(MAX_HEADER_LENGTH); } int size = buf.limit() - 10; byte header0 = (byte) (0x8f & (f.getType() | 0xf0)); if (size < 126) { buf.position(8); buf.putByte(8, header0); buf.putByte(9, (byte) size); } else if (size <= MAX_UNSIGNED_SHORT) { buf.position(6); buf.putByte(6, header0); buf.putByte(7, (byte) 126); buf.putShort(8, size); } else { buf.putByte(header0); buf.putByte((byte) 127); buf.putLong(size); } return buf; } public int getFrameStackSize() { return frameStackSize; } @Override public String getProtocolId() { return PROTOCOL_ID; } @Override public int getHeaderLength() { return MAX_HEADER_LENGTH; } private WebSocketFrame newWebSocketFrame(Channel ch) { if (frameStackSize > 0) { // NioEventLoop eventLoop = ch.getEventLoop(); // FixedThreadStack stack = (FixedThreadStack) eventLoop // .getAttribute(FRAME_STACK_KEY); // if (stack == null) { // stack = new FixedThreadStack<>(frameStackSize); // eventLoop.setAttribute(FRAME_STACK_KEY, stack); // } // WebSocketFrame frame = stack.pop(); // if (frame == null) { // return new WebSocketFrame(ch, limit); // } // return frame.reset(ch, limit); } return new WebSocketFrame(); } @Override public Frame ping(Channel ch) { return new WebSocketFrame().setPing(); } @Override public Frame pong(Channel ch, Frame ping) { return ping.setPong(); } @SuppressWarnings("unchecked") public void release(NioEventLoop eventLoop, Frame frame) { //FIXME ..final statck is null or not null List stack = (List) eventLoop.getAttribute(FRAME_STACK_KEY); if (stack != null && stack.size() < frameStackSize) { stack.add((WebSocketFrame) frame); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy