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;
}
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;
}
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());
}
}
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());
}
}
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());
}
}
Aggregations