/*
* 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.client.net.rtmp;
import org.apache.commons.codec.binary.Hex;
import org.apache.mina.core.buffer.IoBuffer;
import org.apache.mina.core.service.IoHandlerAdapter;
import org.apache.mina.core.session.IoSession;
import org.red5.client.net.rtmpe.RTMPEIoFilter;
import org.red5.server.api.Red5;
import org.red5.server.net.rtmp.RTMPConnection;
import org.red5.server.net.rtmp.RTMPHandshake;
import org.red5.server.net.rtmp.RTMPMinaConnection;
import org.red5.server.net.rtmp.message.Packet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;
/**
* Handles all RTMP protocol events fired by the MINA framework.
*/
public class RTMPMinaIoHandler extends IoHandlerAdapter {
private static Logger log = LoggerFactory.getLogger(RTMPMinaIoHandler.class);
private boolean enableSwfVerification;
/**
* RTMP events handler
*/
protected BaseRTMPClientHandler handler;
/** {@inheritDoc} */
@Override
public void sessionCreated(IoSession session) throws Exception {
log.debug("Session created");
// add rtmpe filter, rtmp protocol filter is added upon successful handshake
session.getFilterChain().addFirst("rtmpeFilter", new RTMPEIoFilter());
// create a connection
RTMPMinaConnection conn = createRTMPMinaConnection();
// set the session on the connection
conn.setIoSession(session);
// add the connection
session.setAttribute(RTMPConnection.RTMP_SESSION_ID, conn.getSessionId());
// create an outbound handshake, defaults to non-encrypted mode
OutboundHandshake outgoingHandshake = new OutboundHandshake();
// add the handshake
session.setAttribute(RTMPConnection.RTMP_HANDSHAKE, outgoingHandshake);
// setup swf verification
if (enableSwfVerification) {
String swfUrl = (String) handler.getConnectionParams().get("swfUrl");
log.debug("SwfUrl: {}", swfUrl);
if (!StringUtils.isEmpty(swfUrl)) {
outgoingHandshake.initSwfVerification(swfUrl);
}
}
//if handler is rtmpe client set encryption on the protocol state
//if (handler instanceof RTMPEClient) {
//rtmp.setEncrypted(true);
//set the handshake type to encrypted as well
//outgoingHandshake.setHandshakeType(RTMPConnection.RTMP_ENCRYPTED);
//}
// set a reference to the handler on the sesssion
session.setAttribute(RTMPConnection.RTMP_HANDLER, handler);
// set a reference to the connection on the client
handler.setConnection((RTMPConnection) conn);
}
/** {@inheritDoc} */
@Override
public void sessionOpened(IoSession session) throws Exception {
log.debug("Session opened");
super.sessionOpened(session);
// get the handshake from the session
RTMPHandshake handshake = (RTMPHandshake) session.getAttribute(RTMPConnection.RTMP_HANDSHAKE);
// create and send C0+C1
IoBuffer clientRequest1 = ((OutboundHandshake) handshake).generateClientRequest1();
session.write(clientRequest1);
}
/** {@inheritDoc} */
@Override
public void sessionClosed(IoSession session) throws Exception {
log.debug("Session closed");
String sessionId = (String) session.getAttribute(RTMPConnection.RTMP_SESSION_ID);
if (sessionId != null) {
log.trace("Session id: {}", sessionId);
RTMPMinaConnection conn = (RTMPMinaConnection) RTMPConnManager.getInstance().getConnectionBySessionId(sessionId);
if (conn != null) {
conn.sendPendingServiceCallsCloseError();
// fire-off closed event
handler.connectionClosed(conn);
// clear any session attributes we may have previously set
session.removeAttribute(RTMPConnection.RTMP_HANDLER);
session.removeAttribute(RTMPConnection.RTMP_HANDSHAKE);
session.removeAttribute(RTMPConnection.RTMPE_CIPHER_IN);
session.removeAttribute(RTMPConnection.RTMPE_CIPHER_OUT);
} else {
log.warn("Connection was null in session");
}
} else {
log.debug("Connections session id was null in session, may already be closed");
}
}
/** {@inheritDoc} */
@Override
public void messageReceived(IoSession session, Object message) throws Exception {
log.debug("messageReceived");
if (message instanceof Packet && message != null) {
String sessionId = (String) session.getAttribute(RTMPConnection.RTMP_SESSION_ID);
log.trace("Session id: {}", sessionId);
RTMPMinaConnection conn = (RTMPMinaConnection) RTMPConnManager.getInstance().getConnectionBySessionId(sessionId);
Red5.setConnectionLocal(conn);
conn.handleMessageReceived((Packet) message);
Red5.setConnectionLocal(null);
} else {
log.debug("Not packet type: {}", message);
}
}
/** {@inheritDoc} */
@Override
public void messageSent(IoSession session, Object message) throws Exception {
log.debug("messageSent");
if (message instanceof Packet) {
String sessionId = (String) session.getAttribute(RTMPConnection.RTMP_SESSION_ID);
log.trace("Session id: {}", sessionId);
RTMPMinaConnection conn = (RTMPMinaConnection) RTMPConnManager.getInstance().getConnectionBySessionId(sessionId);
handler.messageSent(conn, (Packet) message);
} else {
log.trace("messageSent: {}", Hex.encodeHexString(((IoBuffer) message).array()));
}
}
/** {@inheritDoc} */
@Override
public void exceptionCaught(IoSession session, Throwable cause) throws Exception {
log.warn("Exception caught {}", cause.getMessage());
if (log.isDebugEnabled()) {
log.error("Exception detail", cause);
}
}
/**
* Setter for handler.
*
* @param handler RTMP events handler
*/
public void setHandler(BaseRTMPClientHandler handler) {
log.debug("Set handler: {}", handler);
this.handler = handler;
}
/**
* Setter to enable swf verification in the handshake.
*
* @param enableSwfVerification
*/
public void setEnableSwfVerification(boolean enableSwfVerification) {
this.enableSwfVerification = enableSwfVerification;
}
protected RTMPMinaConnection createRTMPMinaConnection() {
return (RTMPMinaConnection) RTMPConnManager.getInstance().createConnection(RTMPMinaConnection.class);
}
}