Search in sources :

Example 1 with StatusObject

use of org.red5.server.net.rtmp.status.StatusObject in project red5-server-common by Red5.

the class RTMPProtocolEncoder method generateErrorResult.

/**
 * Generate error object to return for given exception.
 *
 * @param code
 *            call
 * @param error
 *            error
 * @return status object
 */
protected StatusObject generateErrorResult(String code, Throwable error) {
    // Construct error object to return
    String message = "";
    while (error != null && error.getCause() != null) {
        error = error.getCause();
    }
    if (error != null && error.getMessage() != null) {
        message = error.getMessage();
    }
    StatusObject status = new StatusObject(code, "error", message);
    if (error instanceof ClientDetailsException) {
        // Return exception details to client
        status.setApplication(((ClientDetailsException) error).getParameters());
        if (((ClientDetailsException) error).includeStacktrace()) {
            List<String> stack = new ArrayList<String>();
            for (StackTraceElement element : error.getStackTrace()) {
                stack.add(element.toString());
            }
            status.setAdditional("stacktrace", stack);
        }
    } else if (error != null) {
        status.setApplication(error.getClass().getCanonicalName());
        List<String> stack = new ArrayList<String>();
        for (StackTraceElement element : error.getStackTrace()) {
            stack.add(element.toString());
        }
        status.setAdditional("stacktrace", stack);
    }
    return status;
}
Also used : ArrayList(java.util.ArrayList) StatusObject(org.red5.server.net.rtmp.status.StatusObject) ArrayList(java.util.ArrayList) List(java.util.List) ClientDetailsException(org.red5.server.exception.ClientDetailsException)

Example 2 with StatusObject

use of org.red5.server.net.rtmp.status.StatusObject in project red5-server by Red5.

the class RemotingProtocolEncoder method generateErrorResult.

/**
 * Generate error object to return for given exception.
 *
 * @param code
 *            call
 * @param error
 *            error
 * @return status object
 */
protected StatusObject generateErrorResult(String code, Throwable error) {
    // Construct error object to return
    String message = "";
    while (error != null && error.getCause() != null) {
        error = error.getCause();
    }
    if (error != null && error.getMessage() != null) {
        message = error.getMessage();
    }
    StatusObject status = new StatusObject(code, "error", message);
    if (error instanceof ClientDetailsException) {
        // Return exception details to client
        status.setApplication(((ClientDetailsException) error).getParameters());
        if (((ClientDetailsException) error).includeStacktrace()) {
            List<String> stack = new ArrayList<String>();
            for (StackTraceElement element : error.getStackTrace()) {
                stack.add(element.toString());
            }
            status.setAdditional("stacktrace", stack);
        }
    } else if (error != null) {
        status.setApplication(error.getClass().getCanonicalName());
    }
    return status;
}
Also used : ArrayList(java.util.ArrayList) StatusObject(org.red5.server.net.rtmp.status.StatusObject) ClientDetailsException(org.red5.server.exception.ClientDetailsException)

Example 3 with StatusObject

use of org.red5.server.net.rtmp.status.StatusObject in project red5-client by Red5.

the class RTMPClientProtocolEncoder method encodeCommand.

/**
 * Encode notification event and fill given byte buffer.
 *
 * @param out
 *            Byte buffer to fill
 * @param command
 *            Notification event
 */
@Override
protected void encodeCommand(IoBuffer out, ICommand command) {
    log.debug("encodeCommand - command: {}", command);
    RTMPConnection conn = (RTMPConnection) Red5.getConnectionLocal();
    Output output = new org.red5.io.amf.Output(out);
    final IServiceCall call = command.getCall();
    final boolean isPending = (call.getStatus() == Call.STATUS_PENDING);
    log.debug("Call: {} pending: {}", call, isPending);
    if (!isPending) {
        log.debug("Call has been executed, send result");
        Serializer.serialize(output, call.isSuccess() ? "_result" : "_error");
    } else {
        log.debug("This is a pending call, send request");
        // for request we need to use AMF3 for client mode if the connection is AMF3
        if (conn.getEncoding() == Encoding.AMF3) {
            output = new org.red5.io.amf3.Output(out);
        }
        final String action = (call.getServiceName() == null) ? call.getServiceMethodName() : call.getServiceName() + '.' + call.getServiceMethodName();
        Serializer.serialize(output, action);
    }
    if (command instanceof Invoke) {
        Serializer.serialize(output, Integer.valueOf(command.getTransactionId()));
        Serializer.serialize(output, command.getConnectionParams());
    }
    if (call.getServiceName() == null && "connect".equals(call.getServiceMethodName())) {
        // response to initial connect, always use AMF0
        output = new org.red5.io.amf.Output(out);
    } else {
        if (conn.getEncoding() == Encoding.AMF3) {
            output = new org.red5.io.amf3.Output(out);
        } else {
            output = new org.red5.io.amf.Output(out);
        }
    }
    if (!isPending && (command instanceof Invoke)) {
        IPendingServiceCall pendingCall = (IPendingServiceCall) call;
        if (!call.isSuccess()) {
            log.debug("Call was not successful");
            StatusObject status = generateErrorResult(StatusCodes.NC_CALL_FAILED, call.getException());
            pendingCall.setResult(status);
        }
        Object res = pendingCall.getResult();
        log.debug("Writing result: {}", res);
        Serializer.serialize(output, res);
    } else {
        log.debug("Writing params");
        final Object[] args = call.getArguments();
        if (args != null) {
            for (Object element : args) {
                Serializer.serialize(output, element);
            }
        }
    }
    if (command.getData() != null) {
        out.setAutoExpand(true);
        out.put(command.getData());
    }
}
Also used : RTMPConnection(org.red5.server.net.rtmp.RTMPConnection) IPendingServiceCall(org.red5.server.api.service.IPendingServiceCall) Invoke(org.red5.server.net.rtmp.event.Invoke) IServiceCall(org.red5.server.api.service.IServiceCall) Output(org.red5.io.object.Output) StatusObject(org.red5.server.net.rtmp.status.StatusObject) StatusObject(org.red5.server.net.rtmp.status.StatusObject)

Example 4 with StatusObject

use of org.red5.server.net.rtmp.status.StatusObject in project red5-server-common by Red5.

the class RTMPHandler method onCommand.

/**
 * {@inheritDoc}
 */
@SuppressWarnings({ "unchecked" })
@Override
protected void onCommand(RTMPConnection conn, Channel channel, Header source, ICommand command) {
    log.debug("onCommand {}", command);
    // incoming transaction id (response to 'connect' must be == 1)
    final int transId = command.getTransactionId();
    // get the call
    final IServiceCall call = command.getCall();
    if (isTrace) {
        log.trace("call: {}", call);
    }
    // get the method name
    final String action = call.getServiceMethodName();
    // If it's a callback for server remote call then pass it over to callbacks handler and return
    if ("_result".equals(action) || "_error".equals(action)) {
        handlePendingCallResult(conn, (Invoke) command);
        return;
    }
    boolean disconnectOnReturn = false;
    // "connected" here means that there is a scope associated with the connection (post-"connect")
    boolean connected = conn.isConnected();
    if (connected) {
        // If this is not a service call then handle connection...
        if (call.getServiceName() == null) {
            StreamAction streamAction = StreamAction.getEnum(action);
            if (isDebug) {
                log.debug("Stream action: {}", streamAction.toString());
            }
            // TODO change this to an application scope parameter and / or change to the listener pattern
            if (dispatchStreamActions) {
                // pass the stream action event to the handler
                try {
                    conn.getScope().getHandler().handleEvent(new StreamActionEvent(streamAction));
                } catch (Exception ex) {
                    log.warn("Exception passing stream action: {} to the scope handler", streamAction, ex);
                }
            }
            // if the "stream" action is not predefined a custom type will be returned
            switch(streamAction) {
                case DISCONNECT:
                    conn.close();
                    break;
                case CREATE_STREAM:
                case INIT_STREAM:
                case CLOSE_STREAM:
                case RELEASE_STREAM:
                case DELETE_STREAM:
                case PUBLISH:
                case PLAY:
                case PLAY2:
                case SEEK:
                case PAUSE:
                case PAUSE_RAW:
                case RECEIVE_VIDEO:
                case RECEIVE_AUDIO:
                    IStreamService streamService = (IStreamService) ScopeUtils.getScopeService(conn.getScope(), IStreamService.class, StreamService.class);
                    try {
                        log.debug("Invoking {} from {} with service: {}", new Object[] { call, conn.getSessionId(), streamService });
                        if (invokeCall(conn, call, streamService)) {
                            log.debug("Stream service invoke {} success", action);
                        } else {
                            Status status = getStatus(NS_INVALID_ARGUMENT).asStatus();
                            status.setDescription(String.format("Failed to %s (stream id: %d)", action, source.getStreamId()));
                            channel.sendStatus(status);
                        }
                    } catch (Throwable err) {
                        log.error("Error while invoking {} on stream service. {}", action, err);
                        Status status = getStatus(NS_FAILED).asStatus();
                        status.setDescription(String.format("Error while invoking %s (stream id: %d)", action, source.getStreamId()));
                        status.setDetails(err.getMessage());
                        channel.sendStatus(status);
                    }
                    break;
                default:
                    log.debug("Defaulting to invoke for: {}", action);
                    invokeCall(conn, call);
            }
        } else {
            // handle service calls
            invokeCall(conn, call);
        }
    } else if (StreamAction.CONNECT.equals(action)) {
        // Handle connection
        log.debug("connect - transaction id: {}", transId);
        // Get parameters passed from client to NetConnection#connection
        final Map<String, Object> params = command.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);
        // connection setup
        conn.setup(host, path, params);
        try {
            // Lookup server scope when connected using host and application name
            IGlobalScope global = server.lookupGlobal(host, path);
            log.trace("Global lookup result: {}", global);
            if (global != null) {
                final IContext context = global.getContext();
                IScope scope = null;
                try {
                    // TODO optimize this to use Scope instead of Context
                    scope = context.resolveScope(global, path);
                    if (scope != null) {
                        if (isDebug) {
                            log.debug("Connecting to: {}", scope.getName());
                            log.debug("Conn {}, scope {}, call {} args {}", new Object[] { conn, scope, call, call.getArguments() });
                        }
                        // if scope connection is allowed
                        if (scope.isConnectionAllowed(conn)) {
                            // connections connect result
                            boolean connectSuccess;
                            try {
                                if (call.getArguments() != null) {
                                    connectSuccess = conn.connect(scope, call.getArguments());
                                } else {
                                    connectSuccess = conn.connect(scope);
                                }
                                if (connectSuccess) {
                                    log.debug("Connected - {}", conn.getClient());
                                    call.setStatus(Call.STATUS_SUCCESS_RESULT);
                                    if (call instanceof IPendingServiceCall) {
                                        IPendingServiceCall pc = (IPendingServiceCall) call;
                                        // send fmsver and capabilities
                                        StatusObject result = getStatus(NC_CONNECT_SUCCESS);
                                        result.setAdditional("fmsVer", Red5.getFMSVersion());
                                        result.setAdditional("capabilities", Red5.getCapabilities());
                                        result.setAdditional("mode", Integer.valueOf(1));
                                        result.setAdditional("data", Red5.getDataVersion());
                                        pc.setResult(result);
                                    }
                                    // Measure initial round-trip time after connecting
                                    conn.ping(new Ping(Ping.STREAM_BEGIN, 0, -1));
                                } else {
                                    log.debug("Connect failed");
                                    call.setStatus(Call.STATUS_ACCESS_DENIED);
                                    if (call instanceof IPendingServiceCall) {
                                        IPendingServiceCall pc = (IPendingServiceCall) call;
                                        pc.setResult(getStatus(NC_CONNECT_REJECTED));
                                    }
                                    disconnectOnReturn = true;
                                }
                            } catch (ClientRejectedException rejected) {
                                log.debug("Connect rejected");
                                call.setStatus(Call.STATUS_ACCESS_DENIED);
                                if (call instanceof IPendingServiceCall) {
                                    IPendingServiceCall pc = (IPendingServiceCall) call;
                                    StatusObject status = getStatus(NC_CONNECT_REJECTED);
                                    Object reason = rejected.getReason();
                                    if (reason != null) {
                                        status.setApplication(reason);
                                        // should we set description?
                                        status.setDescription(reason.toString());
                                    }
                                    pc.setResult(status);
                                }
                                disconnectOnReturn = true;
                            }
                        } else {
                            // connection to specified scope is not allowed
                            log.debug("Connect to specified scope is not allowed");
                            call.setStatus(Call.STATUS_ACCESS_DENIED);
                            if (call instanceof IPendingServiceCall) {
                                IPendingServiceCall pc = (IPendingServiceCall) call;
                                StatusObject status = getStatus(NC_CONNECT_REJECTED);
                                status.setDescription(String.format("Connection to '%s' denied.", path));
                                pc.setResult(status);
                            }
                            disconnectOnReturn = true;
                        }
                    }
                } catch (ScopeNotFoundException err) {
                    log.warn("Scope not found", err);
                    call.setStatus(Call.STATUS_SERVICE_NOT_FOUND);
                    if (call instanceof IPendingServiceCall) {
                        StatusObject status = getStatus(NC_CONNECT_REJECTED);
                        status.setDescription(String.format("No scope '%s' on this server.", path));
                        ((IPendingServiceCall) call).setResult(status);
                    }
                    log.info("Scope {} not found on {}", path, host);
                    disconnectOnReturn = true;
                } catch (ScopeShuttingDownException err) {
                    log.warn("Scope shutting down", err);
                    call.setStatus(Call.STATUS_APP_SHUTTING_DOWN);
                    if (call instanceof IPendingServiceCall) {
                        StatusObject status = getStatus(NC_CONNECT_APPSHUTDOWN);
                        status.setDescription(String.format("Application at '%s' is currently shutting down.", path));
                        ((IPendingServiceCall) call).setResult(status);
                    }
                    log.info("Application at {} currently shutting down on {}", path, host);
                    disconnectOnReturn = true;
                }
            } else {
                log.warn("Scope {} not found", path);
                call.setStatus(Call.STATUS_SERVICE_NOT_FOUND);
                if (call instanceof IPendingServiceCall) {
                    StatusObject status = getStatus(NC_CONNECT_INVALID_APPLICATION);
                    status.setDescription(String.format("No scope '%s' on this server.", path));
                    ((IPendingServiceCall) call).setResult(status);
                }
                log.info("No application scope found for {} on host {}", path, host);
                disconnectOnReturn = true;
            }
        } catch (RuntimeException e) {
            call.setStatus(Call.STATUS_GENERAL_EXCEPTION);
            if (call instanceof IPendingServiceCall) {
                IPendingServiceCall pc = (IPendingServiceCall) call;
                pc.setResult(getStatus(NC_CONNECT_FAILED));
            }
            log.error("Error connecting {}", e);
            disconnectOnReturn = true;
        }
        // Evaluate request for AMF3 encoding
        if (Double.valueOf(3d).equals(params.get("objectEncoding"))) {
            if (call instanceof IPendingServiceCall) {
                Object pcResult = ((IPendingServiceCall) call).getResult();
                Map<String, Object> result;
                if (pcResult instanceof Map) {
                    result = (Map<String, Object>) pcResult;
                    result.put("objectEncoding", 3);
                } else if (pcResult instanceof StatusObject) {
                    result = new HashMap<>();
                    StatusObject status = (StatusObject) pcResult;
                    result.put("code", status.getCode());
                    result.put("description", status.getDescription());
                    result.put("application", status.getApplication());
                    result.put("level", status.getLevel());
                    result.put("objectEncoding", 3);
                    ((IPendingServiceCall) call).setResult(result);
                }
            }
            conn.getState().setEncoding(Encoding.AMF3);
        }
    } else {
        // not connected and attempting to send an invoke
        log.warn("Not connected, closing connection");
        conn.close();
    }
    if (command instanceof Invoke) {
        if (isDebug) {
            log.debug("Command type Invoke");
        }
        if ((source.getStreamId().intValue() != 0) && (call.getStatus() == Call.STATUS_SUCCESS_VOID || call.getStatus() == Call.STATUS_SUCCESS_NULL)) {
            // This fixes a bug in the FP on Intel Macs.
            log.debug("Method does not have return value, do not reply");
            return;
        }
        boolean sendResult = true;
        if (call instanceof IPendingServiceCall) {
            IPendingServiceCall psc = (IPendingServiceCall) call;
            Object result = psc.getResult();
            if (result instanceof DeferredResult) {
                // Remember the deferred result to be sent later
                DeferredResult dr = (DeferredResult) result;
                dr.setServiceCall(psc);
                dr.setChannel(channel);
                dr.setTransactionId(transId);
                conn.registerDeferredResult(dr);
                sendResult = false;
            }
        }
        if (sendResult) {
            // The client expects a result for the method call
            Invoke reply = new Invoke();
            reply.setCall(call);
            reply.setTransactionId(transId);
            channel.write(reply);
            if (disconnectOnReturn) {
                log.debug("Close connection due to connect handling exception: {}", conn.getSessionId());
                // must wait until flush to close as we just wrote asynchronously to the stream
                conn.getIoSession().closeOnFlush();
            }
        }
    } else if (isDebug) {
        log.debug("Command type: {}", command.getClass().getName());
    }
}
Also used : IContext(org.red5.server.api.IContext) IPendingServiceCall(org.red5.server.api.service.IPendingServiceCall) StreamActionEvent(org.red5.server.net.rtmp.event.StreamActionEvent) Invoke(org.red5.server.net.rtmp.event.Invoke) IGlobalScope(org.red5.server.api.scope.IGlobalScope) IStreamService(org.red5.server.api.stream.IStreamService) IScope(org.red5.server.api.scope.IScope) ScopeNotFoundException(org.red5.server.exception.ScopeNotFoundException) IStreamService(org.red5.server.api.stream.IStreamService) StreamService(org.red5.server.stream.StreamService) Status(org.red5.server.net.rtmp.status.Status) StreamAction(org.red5.io.object.StreamAction) ScopeShuttingDownException(org.red5.server.exception.ScopeShuttingDownException) ClientRejectedException(org.red5.server.exception.ClientRejectedException) ScopeShuttingDownException(org.red5.server.exception.ScopeShuttingDownException) ScopeNotFoundException(org.red5.server.exception.ScopeNotFoundException) IServiceCall(org.red5.server.api.service.IServiceCall) ClientRejectedException(org.red5.server.exception.ClientRejectedException) Ping(org.red5.server.net.rtmp.event.Ping) ISharedObject(org.red5.server.api.so.ISharedObject) StatusObject(org.red5.server.net.rtmp.status.StatusObject) StatusObject(org.red5.server.net.rtmp.status.StatusObject) HashMap(java.util.HashMap) Map(java.util.Map)

Example 5 with StatusObject

use of org.red5.server.net.rtmp.status.StatusObject in project red5-server-common by Red5.

the class RTMPProtocolEncoder method encodeCommand.

/**
 * Encode command event and fill given byte buffer.
 *
 * @param out
 *            Buffer to fill
 * @param command
 *            Command event
 */
protected void encodeCommand(IoBuffer out, ICommand command) {
    // TODO: tidy up here
    Output output = new org.red5.io.amf.Output(out);
    final IServiceCall call = command.getCall();
    final boolean isPending = (call.getStatus() == Call.STATUS_PENDING);
    log.debug("Call: {} pending: {}", call, isPending);
    if (!isPending) {
        log.debug("Call has been executed, send result");
        Serializer.serialize(output, call.isSuccess() ? "_result" : "_error");
    } else {
        log.debug("This is a pending call, send request");
        final String action = (call.getServiceName() == null) ? call.getServiceMethodName() : call.getServiceName() + '.' + call.getServiceMethodName();
        // seems right
        Serializer.serialize(output, action);
    }
    if (command instanceof Invoke) {
        Serializer.serialize(output, Integer.valueOf(command.getTransactionId()));
        Serializer.serialize(output, command.getConnectionParams());
    }
    if (call.getServiceName() == null && "connect".equals(call.getServiceMethodName())) {
        // response to initial connect, always use AMF0
        output = new org.red5.io.amf.Output(out);
    } else {
        if (Red5.getConnectionLocal().getEncoding() == Encoding.AMF3) {
            output = new org.red5.io.amf3.Output(out);
        } else {
            output = new org.red5.io.amf.Output(out);
        }
    }
    if (!isPending && (command instanceof Invoke)) {
        IPendingServiceCall pendingCall = (IPendingServiceCall) call;
        if (!call.isSuccess() && (call.getException() != null || pendingCall.getResult() == null)) {
            log.debug("Call was not successful");
            StatusObject status = generateErrorResult(StatusCodes.NC_CALL_FAILED, call.getException());
            pendingCall.setResult(status);
        }
        Object res = pendingCall.getResult();
        log.debug("Writing result: {}", res);
        Serializer.serialize(output, res);
    } else {
        log.debug("Writing params");
        final Object[] args = call.getArguments();
        if (args != null) {
            for (Object element : args) {
                if (element instanceof ByteBuffer) {
                    // a byte buffer indicates that serialization is already complete, send raw
                    final ByteBuffer buf = (ByteBuffer) element;
                    buf.mark();
                    try {
                        out.put(buf);
                    } finally {
                        buf.reset();
                    }
                } else {
                    // standard serialize
                    Serializer.serialize(output, element);
                }
            }
        }
    }
    if (command.getData() != null) {
        out.setAutoExpand(true);
        out.put(command.getData());
    }
}
Also used : IPendingServiceCall(org.red5.server.api.service.IPendingServiceCall) ByteBuffer(java.nio.ByteBuffer) Invoke(org.red5.server.net.rtmp.event.Invoke) IServiceCall(org.red5.server.api.service.IServiceCall) Output(org.red5.io.object.Output) StatusObject(org.red5.server.net.rtmp.status.StatusObject) StatusObject(org.red5.server.net.rtmp.status.StatusObject)

Aggregations

StatusObject (org.red5.server.net.rtmp.status.StatusObject)5 IPendingServiceCall (org.red5.server.api.service.IPendingServiceCall)3 IServiceCall (org.red5.server.api.service.IServiceCall)3 Invoke (org.red5.server.net.rtmp.event.Invoke)3 ArrayList (java.util.ArrayList)2 Output (org.red5.io.object.Output)2 ClientDetailsException (org.red5.server.exception.ClientDetailsException)2 ByteBuffer (java.nio.ByteBuffer)1 HashMap (java.util.HashMap)1 List (java.util.List)1 Map (java.util.Map)1 BeanMap (org.apache.commons.beanutils.BeanMap)1 Output (org.red5.io.amf.Output)1 StreamAction (org.red5.io.object.StreamAction)1 IContext (org.red5.server.api.IContext)1 IGlobalScope (org.red5.server.api.scope.IGlobalScope)1 IScope (org.red5.server.api.scope.IScope)1 ISharedObject (org.red5.server.api.so.ISharedObject)1 IStreamService (org.red5.server.api.stream.IStreamService)1 ClientRejectedException (org.red5.server.exception.ClientRejectedException)1