/* * RED5 Open Source Flash Server - https://github.com/red5 * * Copyright 2006-2015 by respective authors (see below). All rights reserved. * * 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 org.red5.net.websocket.codec; import org.apache.mina.core.buffer.IoBuffer; import org.apache.mina.core.session.IoSession; import org.apache.mina.filter.codec.ProtocolEncoderAdapter; import org.apache.mina.filter.codec.ProtocolEncoderOutput; import org.red5.net.websocket.Constants; import org.red5.net.websocket.WebSocketConnection; import org.red5.net.websocket.model.HandshakeRequest; import org.red5.net.websocket.model.HandshakeResponse; import org.red5.net.websocket.model.Packet; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Encodes incoming buffers in a manner that makes the receiving client type transparent to the encoders further up in the filter chain. If the receiving client is a native client then the buffer contents are simply passed through. If the receiving client is a websocket, it will encode the buffer contents in to WebSocket DataFrame before passing it along the filter chain. * * <i>Note: you must wrap the IoBuffer you want to send around a WebSocketCodecPacket instance.</i> * * @author Dhruv Chopra * @author Paul Gregoire */ public class WebSocketEncoder extends ProtocolEncoderAdapter { private static final Logger log = LoggerFactory.getLogger(WebSocketEncoder.class); @Override public void encode(IoSession session, Object message, ProtocolEncoderOutput out) throws Exception { WebSocketConnection conn = (WebSocketConnection) session.getAttribute(Constants.CONNECTION); IoBuffer resultBuffer; if (message instanceof Packet) { Packet packet = (Packet) message; // if the connection is not native / direct, add websocket encoding resultBuffer = conn.isWebConnection() ? encodeOutgoingData(packet) : packet.getData(); } else if (message instanceof HandshakeResponse) { HandshakeResponse resp = (HandshakeResponse) message; resultBuffer = resp.getResponse(); } else if (message instanceof HandshakeRequest) { HandshakeRequest req = (HandshakeRequest) message; resultBuffer = req.getRequest(); } else { throw new Exception("message not a websocket type"); } out.write(resultBuffer); } // Encode the in buffer according to the Section 5.2. RFC 6455 public static IoBuffer encodeOutgoingData(Packet packet) { log.debug("encode outgoing: {}", packet); // get the payload data IoBuffer data = packet.getData(); // get the frame length based on the byte count int frameLen = data.limit(); // start with frame length + 2b (header info) IoBuffer buffer = IoBuffer.allocate(frameLen + 2, false); buffer.setAutoExpand(true); // set the proper flags / opcode for the data byte frameInfo = (byte) (1 << 7); switch (packet.getType()) { case TEXT: if (log.isTraceEnabled()) { log.trace("Encoding text frame \r\n{}", new String(packet.getData().array())); } frameInfo = (byte) (frameInfo | 1); break; case BINARY: log.trace("Encoding binary frame"); frameInfo = (byte) (frameInfo | 2); break; case CLOSE: frameInfo = (byte) (frameInfo | 8); break; case CONTINUATION: frameInfo = (byte) (frameInfo | 0); break; case PING: log.trace("ping out"); frameInfo = (byte) (frameInfo | 9); break; case PONG: log.trace("pong out"); frameInfo = (byte) (frameInfo | 0xa); break; default: break; } buffer.put(frameInfo); // set the frame length log.trace("Frame length {} ", frameLen); if (frameLen <= 125) { buffer.put((byte) ((byte) frameLen & (byte) 0x7F)); } else if (frameLen > 125 && frameLen <= 65535) { buffer.put((byte) ((byte) 126 & (byte) 0x7F)); buffer.putShort((short) frameLen); } else { buffer.put((byte) ((byte) 127 & (byte) 0x7F)); buffer.putLong((int) frameLen); } buffer.put(data); buffer.flip(); if (log.isTraceEnabled()) { log.trace("Encoded: {}", buffer); } return buffer; } }