@Override public void messageReceived(IoSession session, Object message) throws Exception { MRTMPPacket mrtmpPacket = (MRTMPPacket) message; int clientId = mrtmpPacket.getHeader().getClientId(); RTMPConnection conn = rtmpConnManager.getConnection(clientId); if (conn == null) { log.debug("Client " + clientId + " is already closed."); return; } RTMP rtmpState = conn.getState(); switch (mrtmpPacket.getHeader().getType()) { case MRTMPPacket.CLOSE: conn.setStateCode(RTMP.STATE_EDGE_DISCONNECTING); conn.close(); break; case MRTMPPacket.RTMP: RTMPHeader rtmpHeader = (RTMPHeader) mrtmpPacket.getHeader(); RTMPBody rtmpBody = (RTMPBody) mrtmpPacket.getBody(); boolean toDisconnect = false; // conn.getWriteLock().lock(); try { if (rtmpState.getState() == RTMP.STATE_ORIGIN_CONNECT_FORWARDED && rtmpHeader.getRtmpType() == TYPE_INVOKE) { // we got the connect invocation result from Origin // parse the result Invoke invoke = (Invoke) rtmpBody.getRtmpPacket().getMessage(); if ("connect".equals(invoke.getCall().getServiceMethodName())) { if (invoke.getCall().getStatus() == Call.STATUS_SUCCESS_RESULT) { rtmpState.setState(RTMP.STATE_CONNECTED); } else { // TODO set EdgeRTMP state to closing ? toDisconnect = true; } } } } finally { // conn.getWriteLock().unlock(); } log.debug("Forward packet to client: {}", rtmpBody.getRtmpPacket().getMessage()); // send the packet back to client conn.write(rtmpBody.getRtmpPacket()); if (toDisconnect) { conn.close(); } // conn.getWriteLock().lock(); try { if (rtmpState.getState() == RTMP.STATE_CONNECTED) { conn.startRoundTripMeasurement(); } } finally { // conn.getWriteLock().unlock(); } break; default: break; } }
/** {@inheritDoc} */ public void invoke(IServiceCall call, int channel) { // We need to use Invoke for all calls to the client Invoke invoke = new Invoke(); invoke.setCall(call); invoke.setInvokeId(getInvokeId()); if (call instanceof IPendingServiceCall) { registerPendingCall(invoke.getInvokeId(), (IPendingServiceCall) call); } getChannel(channel).write(invoke); }
/** {@inheritDoc} */ @Override public void connectionOpened(RTMPConnection conn) { log.trace("connectionOpened - conn: {}", conn); // Send "connect" call to the server Channel channel = conn.getChannel((byte) 3); PendingCall pendingCall = new PendingCall("connect"); pendingCall.setArguments(connectArguments); Invoke invoke = new Invoke(pendingCall); invoke.setConnectionParams(connectionParams); invoke.setTransactionId(1); if (connectCallback != null) { pendingCall.registerCallback(connectCallback); } conn.registerPendingCall(invoke.getTransactionId(), pendingCall); log.debug("Writing 'connect' invoke: {}, invokeId: {}", invoke, invoke.getTransactionId()); channel.write(invoke); }
/** * Handler for pending call result. Dispatches results to all pending call handlers. * * @param conn Connection * @param invoke Pending call result event context */ protected void handlePendingCallResult(RTMPConnection conn, Invoke invoke) { final IServiceCall call = invoke.getCall(); final IPendingServiceCall pendingCall = conn.retrievePendingCall(invoke.getTransactionId()); if (pendingCall != null) { // The client sent a response to a previously made call. Object[] args = call.getArguments(); if (args != null && args.length > 0) { // TODO: can a client return multiple results? pendingCall.setResult(args[0]); } Set<IPendingServiceCallback> callbacks = pendingCall.getCallbacks(); if (!callbacks.isEmpty()) { HashSet<IPendingServiceCallback> tmp = new HashSet<IPendingServiceCallback>(); tmp.addAll(callbacks); for (IPendingServiceCallback callback : tmp) { try { callback.resultReceived(pendingCall); } catch (Exception e) { log.error("Error while executing callback {}", callback, e); } } } } }
protected void handleConnect( RTMPConnection conn, Channel channel, Header header, Invoke invoke, RTMP rtmp) { final IPendingServiceCall call = invoke.getCall(); // Get parameters passed from client to NetConnection#connection final Map<String, Object> params = invoke.getConnectionParams(); // Get hostname String host = getHostname((String) params.get("tcUrl")); // App name as path, but without query string if there is one String path = (String) params.get("app"); if (path.indexOf("?") != -1) { int idx = path.indexOf("?"); params.put("queryString", path.substring(idx)); path = path.substring(0, idx); } params.put("path", path); final String sessionId = null; conn.setup(host, path, sessionId, params); // check the security constraints // send back "ConnectionRejected" if fails. if (!checkPermission(conn)) { call.setStatus(Call.STATUS_ACCESS_DENIED); call.setResult(getStatus(NC_CONNECT_REJECTED)); Invoke reply = new Invoke(); reply.setCall(call); reply.setInvokeId(invoke.getInvokeId()); channel.write(reply); conn.close(); } else { synchronized (rtmp) { // connect the origin sendConnectMessage(conn); rtmp.setState(RTMP.STATE_EDGE_CONNECT_ORIGIN_SENT); Packet packet = new Packet(header); packet.setMessage(invoke); forwardPacket(conn, packet); rtmp.setState(RTMP.STATE_ORIGIN_CONNECT_FORWARDED); // Evaluate request for AMF3 encoding if (Integer.valueOf(3).equals(params.get("objectEncoding"))) { rtmp.setEncoding(Encoding.AMF3); } } } }
/** {@inheritDoc} */ @Override protected void onCommand(RTMPConnection conn, Channel channel, Header source, ICommand command) { log.trace("onCommand: {}, id: {}", command, command.getTransactionId()); final IServiceCall call = command.getCall(); final String methodName = call.getServiceMethodName(); log.debug( "Service name: {} args[0]: {}", methodName, (call.getArguments().length != 0 ? call.getArguments()[0] : "")); if ("_result".equals(methodName) || "_error".equals(methodName)) { final IPendingServiceCall pendingCall = conn.getPendingCall(command.getTransactionId()); log.debug("Received result for pending call - {}", pendingCall); if (pendingCall != null) { if ("connect".equals(methodName)) { Integer encoding = (Integer) connectionParams.get("objectEncoding"); if (encoding != null && encoding.intValue() == 3) { log.debug("Setting encoding to AMF3"); conn.getState().setEncoding(IConnection.Encoding.AMF3); } } } handlePendingCallResult(conn, (Invoke) command); return; } // potentially used twice so get the value once boolean onStatus = "onStatus".equals(methodName); log.debug("onStatus {}", onStatus); if (onStatus) { Integer streamId = source.getStreamId(); if (log.isDebugEnabled()) { log.debug("Stream id from header: {}", streamId); // XXX create better to serialize ObjectMap to Status object ObjectMap<?, ?> objMap = (ObjectMap<?, ?>) call.getArguments()[0]; // should keep this as an Object to stay compatible with FMS3 etc log.debug("Client id from status: {}", objMap.get("clientid")); } if (streamId != null) { // try lookup by stream id NetStreamPrivateData streamData = streamDataMap.get(streamId); // if null return the first one in the map if (streamData == null) { log.debug("Stream data was not found by id. Map contents: {}", streamDataMap); if (!streamDataMap.isEmpty()) { streamData = streamDataMap.values().iterator().next(); } } if (streamData == null) { log.warn("Stream data was null for id: {}", streamId); } if (streamData != null && streamData.handler != null) { log.debug("Got stream data and handler"); streamData.handler.onStreamEvent((Notify) command); } } } // if this client supports service methods, forward the call if (serviceProvider == null) { // client doesn't support calling methods on him call.setStatus(Call.STATUS_METHOD_NOT_FOUND); call.setException(new MethodNotFoundException(methodName)); log.info( "No service provider / method not found; to handle calls like onBWCheck, add a service provider"); } else { serviceInvoker.invoke(call, serviceProvider); } if (call instanceof IPendingServiceCall) { IPendingServiceCall psc = (IPendingServiceCall) call; Object result = psc.getResult(); log.debug("Pending call result is: {}", result); if (result instanceof DeferredResult) { DeferredResult dr = (DeferredResult) result; dr.setTransactionId(command.getTransactionId()); dr.setServiceCall(psc); dr.setChannel(channel); conn.registerDeferredResult(dr); } else if (!onStatus) { if ("onBWCheck".equals(methodName)) { onBWCheck(call.getArguments().length > 0 ? call.getArguments()[0] : null); Invoke reply = new Invoke(); reply.setCall(call); reply.setTransactionId(command.getTransactionId()); channel.write(reply); } else if ("onBWDone".equals(methodName)) { onBWDone(call.getArguments().length > 0 ? call.getArguments()[0] : null); } else { Invoke reply = new Invoke(); reply.setCall(call); reply.setTransactionId(command.getTransactionId()); log.debug("Sending empty call reply: {}", reply); channel.write(reply); } } } }