public bool HandleRTSPRequest(RtspProtocol from,Variant requestHeaders,string requestContent)
{
string method = requestHeaders[RTSP_FIRST_LINE, RTSP_METHOD];
//1. we need a CSeq
if (requestHeaders[RTSP_HEADERS, RTSP_HEADERS_CSEQ] == null)
{
FATAL("Request doesn't have {0}:\n{1}", RTSP_HEADERS_CSEQ, requestHeaders);
return false;
}
//2. Validate session id
string wantedSessionId = from.SessionId;
if (!string.IsNullOrEmpty(wantedSessionId))
{
var requestSessionId = "";
if (requestHeaders[RTSP_HEADERS, RTSP_HEADERS_SESSION] == null)
{
FATAL("No session id");
return false;
}
requestSessionId = requestHeaders[RTSP_HEADERS, RTSP_HEADERS_SESSION];
var parts = requestSessionId.Split(';');
if (parts.Length >= 1)
{
requestSessionId = parts[0];
}
if (requestSessionId != wantedSessionId)
{
FATAL("Invalid session ID. Wanted: `{0}`; Got: `{1}`",
(wantedSessionId),
(requestSessionId));
return false;
}
}
//4. Prepare a fresh new response. Add the sequence number
from.ClearResponseMessage();
from.PushResponseHeader(RTSP_HEADERS_CSEQ, requestHeaders[RTSP_HEADERS, RTSP_HEADERS_CSEQ]);
//5. Do we have authentication? We will authenticate everything except "OPTIONS"
if ( !string.IsNullOrEmpty(_usersFile) && NeedAuthentication(from,requestHeaders,requestContent))
{
//6. Re-parse authentication file if necessary
if (!ParseUsersFile())
{
FATAL("Unable to parse authentication file");
return false;
}
//7. Get the real name to use it further in authentication process
string realmName = GetAuthenticationRealm(from, requestHeaders,
requestContent);
//8. Do we have that realm?
if (_realms[realmName]==null)
{
FATAL("Realm `{0}` not found", (realmName));
return false;
}
Variant realm = _realms[realmName];
//9. Is the user even trying to authenticate?
if (requestHeaders[RTSP_HEADERS, RTSP_HEADERS_AUTHORIZATION] == null)
{
return SendAuthenticationChallenge(from, realm);
}
else
{
//14. The client sent us some response. Validate it now
//Did we ever sent him an authorization challange?
if (from.CustomParameters["wwwAuthenticate"] ==null)
{
FATAL("Client tried to authenticate and the server didn't required that");
return false;
}
//15. Get the server challenge
string wwwAuthenticate = from.CustomParameters["wwwAuthenticate"];
//16. Get the client response
string authorization = requestHeaders[RTSP_HEADERS, RTSP_HEADERS_AUTHORIZATION];
//17. Try to authenticate
if (!HTTPAuthHelper.ValidateAuthRequest(wwwAuthenticate,
authorization,
method,
(string)requestHeaders[RTSP_FIRST_LINE,RTSP_URL],
realm))
{
WARN("Authorization failed: challenge: {0}; response: {1}",
wwwAuthenticate, authorization);
return SendAuthenticationChallenge(from, realm);
}
//18. Success. User authenticated
//INFO("User authenticated: %s", (authorization));
}
}
switch (method)
{
case RTSP_METHOD_OPTIONS:
from.PushResponseFirstLine(RTSP_VERSION_1_0, 200, "OK");
from.PushResponseHeader(RTSP_HEADERS_PUBLIC, "DESCRIBE, OPTIONS, PAUSE, PLAY, SETUP, TEARDOWN, ANNOUNCE, RECORD");
return from.SendResponseMessage();
case RTSP_METHOD_DESCRIBE:
//1. get the stream name
Uri uri = new Uri(requestHeaders[RTSP_FIRST_LINE,RTSP_URL]);
string streamName = (uri.Segments.LastOrDefault(x=>!x.EndsWith("/"))??"")+uri.Query;
if (streamName == "")
{
FATAL("Invalid stream name");
return false;
}
//2. Get the inbound stream capabilities
IInNetStream pInStream = GetInboundStream(streamName);
//3. Prepare the body of the response
string outboundContent = ComputeSDP(from, streamName, "", "0.0.0.0");
if (outboundContent == "")
{
FATAL("Unable to compute SDP");
return false;
}
//4. Save the stream id for later usage
from.CustomParameters["streamId"] = pInStream.UniqueId;
//5. Mark this connection as outbound connection
from.CustomParameters["isInbound"] = false;
//6. prepare the response
from.PushResponseFirstLine(RTSP_VERSION_1_0, 200, "OK");
from.PushResponseHeader(RTSP_HEADERS_CONTENT_TYPE, RTSP_HEADERS_ACCEPT_APPLICATIONSDP);
from.PushResponseContent(outboundContent, false);
//7. Done
return from.SendResponseMessage();
case RTSP_METHOD_SETUP:
if (from.CustomParameters["isInbound"] != VariantType.Boolean)
{
FATAL("Invalid state");
return false;
}
return @from.CustomParameters["isInbound"] ? HandleRTSPRequestSetupInbound(@from, requestHeaders, requestContent) : HandleRTSPRequestSetupOutbound(@from, requestHeaders, requestContent);
case RTSP_METHOD_PLAY:
return HandleRTSPRequestPlay(from, requestHeaders, requestContent);
case RTSP_METHOD_TEARDOWN:
from.EnqueueForDelete();
return true;
case RTSP_METHOD_ANNOUNCE:
return HandleRTSPRequestAnnounce(from, requestHeaders, requestContent);
case RTSP_METHOD_RECORD:
//1. Make sure we have everything and we are in the proper state
if ((from.CustomParameters["isInbound"] != VariantType.Boolean)
|| ((bool)from.CustomParameters["isInbound"] != true))
{
FATAL("Invalid state");
return false;
}
if (from.CustomParameters["pendingTracks"] != VariantType.Map)
{
FATAL("Invalid state");
return false;
}
//3. Get the inbound connectivity
InboundConnectivity pConnectivity = from.InboundConnectivity;
if (pConnectivity == null)
{
FATAL("Unable to get inbound connectivity");
return false;
}
if (!pConnectivity.Initialize())
{
FATAL("Unable to initialize inbound connectivity");
return false;
}
//4. Send back the response
from.PushResponseFirstLine(RTSP_VERSION_1_0, 200, "OK");
return from.SendResponseMessage();
case RTSP_METHOD_PAUSE:
from.PushResponseFirstLine(RTSP_VERSION_1_0, 200, "OK");
return from.SendResponseMessage();
default:
return false;
}
}