OS_Error HTTPClientSocket::Read(void* inBuffer, const UInt32 inLength, UInt32* outRcvLen)
{
//
// Bring up the GET connection if we need to
if (!fGetSocket.IsConnected())
{
#if CLIENT_SOCKET_DEBUG
qtss_printf("HTTPClientSocket::Read: Sending GET\n");
#endif
qtss_sprintf(fSendBuffer.Ptr, "GET %s HTTP/1.0\r\nX-SessionCookie: %" _U32BITARG_ "\r\nAccept: application/x-rtsp-rtp-interleaved\r\nUser-Agent: QTSS/2.0\r\n\r\n", fURL.Ptr, fCookie);
fSendBuffer.Len = ::strlen(fSendBuffer.Ptr);
Assert(fSentLength == 0);
}
OS_Error theErr = this->Connect(&fGetSocket);
if (theErr != OS_NoErr)
return theErr;
if (fSendBuffer.Len > 0)
{
theErr = this->SendSendBuffer(&fGetSocket);
if (theErr != OS_NoErr)
return theErr;
fSentLength = 1; // So we know to execute the receive code below.
}
// We are done sending the GET. If we need to receive the GET response, do that here
if (fSentLength > 0)
{
*outRcvLen = 0;
do
{
// Loop, trying to receive the entire response.
theErr = fGetSocket.Read(&fSendBuffer.Ptr[fGetReceived], kSendBufferLen - fGetReceived, outRcvLen);
fGetReceived += *outRcvLen;
// Check to see if we've gotten a \r\n\r\n. If we have, then we've received
// the entire GET
fSendBuffer.Ptr[fGetReceived] = '\0';
char* theGetEnd = ::strstr(fSendBuffer.Ptr, "\r\n\r\n");
if (theGetEnd != NULL)
{
// We got the entire GET response, so we are ready to move onto
// real RTSP response data. First skip past the \r\n\r\n
theGetEnd += 4;
#if CLIENT_SOCKET_DEBUG
qtss_printf("HTTPClientSocket::Read: Received GET response\n");
#endif
// Whatever remains is part of an RTSP request, so move that to
// the beginning of the buffer and blow away the GET
*outRcvLen = fGetReceived - (theGetEnd - fSendBuffer.Ptr);
::memcpy(inBuffer, theGetEnd, *outRcvLen);
fGetReceived = fSentLength = 0;
return OS_NoErr;
}
Assert(fGetReceived < inLength);
} while (*outRcvLen > 0);
#if CLIENT_SOCKET_DEBUG
qtss_printf("HTTPClientSocket::Read: Waiting for GET response\n");
#endif
// Message wasn't entirely received. Caller should wait for a read event on the GET socket
Assert(theErr != OS_NoErr);
fSocketP = &fGetSocket;
fEventMask = EV_RE;
return theErr;
}
theErr = fGetSocket.Read(&((char*)inBuffer)[fGetReceived], inLength - fGetReceived, outRcvLen);
if (theErr != OS_NoErr)
{
#if CLIENT_SOCKET_DEBUG
//qtss_printf("HTTPClientSocket::Read: Waiting for data\n");
#endif
fSocketP = &fGetSocket;
fEventMask = EV_RE;
}
#if CLIENT_SOCKET_DEBUG
//else
//qtss_printf("HTTPClientSocket::Read: Got some data\n");
#endif
return theErr;
}
int main(int argc, char * argv[])
{
extern char* optarg;//命令参数,例如执行:CMS.exe -d -v
int ch;
char* theXMLFilePath = "./config.xml";
Bool16 notAService = false;
Bool16 theXMLPrefsExist = true;
Bool16 dontFork = false;
#if _DEBUG
char* compileType = "Compile_Flags/_DEBUG; ";
#else
char* compileType = "Compile_Flags/_RELEASE;";
#endif
qtss_printf("%s/%s ( Build/%s; Platform/%s; %s%s) Built on: %s\n",
QTSServerInterface::GetServerName().Ptr,
QTSServerInterface::GetServerVersion().Ptr,
QTSServerInterface::GetServerBuild().Ptr,
QTSServerInterface::GetServerPlatform().Ptr,
compileType,
QTSServerInterface::GetServerComment().Ptr,
QTSServerInterface::GetServerBuildDate().Ptr);
while ((ch = getopt(argc,argv, "vdp:c:irsS:I")) != EOF) //opt: means requires option
{
switch(ch)
{
case 'v':
qtss_printf("%s/%s ( Build/%s; Platform/%s; %s%s) Built on: %s\n",
QTSServerInterface::GetServerName().Ptr,
QTSServerInterface::GetServerVersion().Ptr,
QTSServerInterface::GetServerBuild().Ptr,
QTSServerInterface::GetServerPlatform().Ptr,
compileType,
QTSServerInterface::GetServerComment().Ptr,
QTSServerInterface::GetServerBuildDate().Ptr);
qtss_printf("usage: %s [ -d | -p port | -v | -c /myconfigpath.xml | -S numseconds | -I | -h ]\n", QTSServerInterface::GetServerName().Ptr);
qtss_printf("-d: Don't run as a Win32 Service\n");
qtss_printf("-p 60000: Specify the default listening port of the server\n");
qtss_printf("-c c:\\myconfigpath.xml: Specify a config file path\n");
qtss_printf("-i: Install the CMS service\n");
qtss_printf("-r: Remove the CMS service\n");
qtss_printf("-s: Start the CMS service\n");
qtss_printf("-S n: Display server stats in the console every \"n\" seconds\n");
qtss_printf("-I: Start the server in the idle state\n");
break;
case 'd':
notAService = true;
break;
case 'p':
Assert(optarg != NULL);// this means we didn't declare getopt options correctly or there is a bug in getopt.
sPort = ::atoi(optarg);
break;
case 'c':
Assert(optarg != NULL);// this means we didn't declare getopt options correctly or there is a bug in getopt.
theXMLFilePath = optarg;
break;
case 'S':
Assert(optarg != NULL);// this means we didn't declare getopt options correctly or there is a bug in getopt.
sStatsUpdateInterval = ::atoi(optarg);
break;
case 'i':
qtss_printf("Installing the CMS Server service...\n");
::InstallService("CMS");
qtss_printf("Starting the CMS Server service...\n");
::RunAsService("CMS");
::exit(0);
break;
case 'r':
qtss_printf("Removing the CMS Server service...\n");
::RemoveService("CMS");
::exit(0);
case 's':
qtss_printf("Starting the CMS Server service...\n");
::RunAsService("CMS");
::exit(0);
case 'I':
sInitialState = qtssIdleState;
break;
default:
break;
}
}
//检测软件是否过期
QTSSExpirationDate::PrintExpirationDate();
if (QTSSExpirationDate::IsSoftwareExpired())
{
qtss_printf("CMS Server Has Expired\n");
::exit(0);
}
//读取xml配置文件
sXMLParser = new XMLPrefsParser(theXMLFilePath);
//检测xml文件是否是以目录形式存在
//.........这里部分代码省略.........
/*
* This method is called because it is the only one that gets the full URL params, to capture
* the username and password. We don't have access to the client session here, so save
* the user and pass in the session for later. This is called BEFORE PostProcess
*/
QTSS_Error RTSPFilter(QTSS_StandardRTSP_Params* inParams) {
QTSS_Error theErr = QTSS_NoErr;
QTSS_RTSPRequestObject theRTSPRequest = inParams->inRTSPRequest;
QTSS_RTSPSessionObject theRTSPSession = inParams->inRTSPSession;
// FAIL - can't do this here. need to finally move the auth to preprocess, and use this
// just for param capture.
// On the flip site, it seems that everything is really early here - if query params
// don't work, can embed them in the URL path, extract here, and rewrite the URL in the request
// and it should work fine. this could be handy if proxy's become an issue, because each request
// would have a unique path.
// // see if the method is bypassable. if it is, skip processing
// qtss_printf("QTSSIcecastAuthModule::RTSPFilter: about to check if the method is bypassable\n");
// if (IsRequestMethodBypassable(&theRTSPRequest)) return QTSS_NoErr;
// see if the client is in the bypass list. if they are, skip all processing
if (IsClientInBypassList(&theRTSPSession)) return QTSS_NoErr;
// check to see if the session is already auth'd. If it is, skip processing
if (IsRTSPSessionAuthenticated(&theRTSPSession)) {
printf("QTSSIcecastAuthModule::RTSPFilter RTSP session is authenticated, do nothing.\n");
return QTSS_NoErr; // we are authenticated, don't do anything
}
char* qtssRTSPSesIDString = NULL;
(void) QTSS_GetValueAsString(theRTSPSession, qtssRTSPSesID, 0, &qtssRTSPSesIDString);
printf("QTSSIcecastAuthModule::RTSPFilter session qtssRTSPSesID: %s\n", qtssRTSPSesIDString);
char* qtssRTSPReqFullRequestString = NULL;
(void)QTSS_GetValueAsString(theRTSPRequest, qtssRTSPReqFullRequest, 0, &qtssRTSPReqFullRequestString);
qtss_printf("QTSSIcecastAuthModule::RTSPFilter: request qtssRTSPReqFullRequest: %s\n", qtssRTSPReqFullRequestString);
/* will want to modify this for proper tokenization, but it works for now */
char username[255];
bool usernameset = false;
char password[255];
bool passwordset = false;
Bool16 requiredAuthParametersProvided = false;
if(index(qtssRTSPReqFullRequestString, '?')){
char buf[512];
snprintf(buf, 512, qtssRTSPReqFullRequestString);
char* queryString;
char* progress1;
// split off everything after the first line, we don't need it.
queryString = ::strtok_r(buf, "\n", &progress1);
// split around the ?, ignore the first part
::strtok_r(buf, "?", &progress1);
// get the second part of the previous split
queryString = ::strtok_r(NULL, "?", &progress1);
// split working around the space
queryString = ::strtok_r(queryString, " ", &progress1);
//printf("queryString: %s\n", queryString);
// we should now have our url
char* tmp = strtok(queryString, "=&");
int iters;
for (iters=0; (tmp != NULL); iters++)
{
char name[255]; // I'm asking for a buffer overflow, aren't I? TODO - check this.
if ((iters % 2) != 1) {
// even - its a name. this will always be 'first'
strcpy(name, tmp);
//printf("name: %s\n", tmp);
}
else {
// non-even, its a value. this will always come second
//printf("value: %s\n", tmp);
if (strcmp(name, "u") == 0) {
// this value is the username
//printf("name is currently: %s. username being set to %s\n", name, tmp);
strcpy(username, tmp);
usernameset = true;
}
else if (strcmp(name, "p") == 0) {
// this value is the username
//printf("name is currently: %s. password being set to %s\n", name, tmp);
strcpy(password, tmp);
passwordset = true;
}
}
tmp = strtok(NULL, "=&");
}
//printf("username: %s, password: %s\n\n", username, password);
//.........这里部分代码省略.........
/*
* This method is used to capture the full session ID details, and to reject the session.
* The username and password from the query string can't be grabbed here, we need to
* do that in the Filter. This is called AFTER Filter
*/
QTSS_Error RTSPPreProcess(QTSS_StandardRTSP_Params* inParams) {
QTSS_Error theErr = QTSS_NoErr;
QTSS_RTSPRequestObject theRTSPRequest = inParams->inRTSPRequest;
QTSS_RTSPSessionObject theRTSPSession = inParams->inRTSPSession;
QTSS_RTSPHeaderObject theRTSPHeader = inParams->inRTSPHeaders;
QTSS_ClientSessionObject theClientSession = inParams->inClientSession;
Bool16 sessionValid = false;
// see if the method is bypassable. if it is, skip processing
qtss_printf("QTSSIcecastAuthModule::RTSPPreProcess: about to check if the method is bypassable\n");
if (IsRequestMethodBypassable(&theRTSPRequest)) return QTSS_NoErr;
// see if the client is in the bypass list. if they are, skip all processing
if (IsClientInBypassList(&theRTSPSession)) return QTSS_NoErr;
// check to see if the session is already auth'd. If it is, skip processing
if (IsRTSPSessionAuthenticated(&theRTSPSession)) {
printf("QTSSIcecastAuthModule::RTSPPreProcess RTSP session is authenticated, do nothing.\n");
return QTSS_NoErr; // we are authenticated, don't do anything
}
char* providedUsername = NULL;
(void)QTSS_GetValueAsString(theRTSPSession, attrRtspSessionProvidedUsername, 0, &providedUsername);
printf("QTSSIcecastAuthModule::RTSPPreProcess: Provided username extracted from session: %s\n", providedUsername);
char* providedPassword = NULL;
(void)QTSS_GetValueAsString(theRTSPSession, attrRtspSessionProvidedPassword, 0, &providedPassword);
printf("QTSSIcecastAuthModule::RTSPPreProcess: Provided password extracted from session: %s\n", providedPassword);
// check to see if the username and password have been provided. If they are, process. if not
// do nothing, we will default to an invalid session
if (providedUsername != NULL && providedPassword != NULL) {
printf("QTSSIcecastAuthModule::RTSPPreProcess: about to call authorize session\n");
// if we get to this point we have credentials that need to be validated, so validate them.
sessionValid = CallAuthorizeSession(&theClientSession, &theRTSPSession, &theRTSPRequest, providedUsername, providedPassword);
// request, qtssRTSPReqFilePath - the mount point (?)
// request, qtssRTSPReqURI - the request URI (query params parsed from here?)
// request, qtssRTSPReqAbsoluteURL - the request url with RTSP.
// request, qtssRTSPReqFullRequest - the full request
// session, qtssRTSPSesID - the session id
// note - QTSS_GetValueAsString is the least efficent method - should move to one of the more efficent methods.
//
// char* qtssRTSPReqFilePathString = NULL;
// (void)QTSS_GetValueAsString(theRTSPRequest, qtssRTSPReqFilePath, 0, &qtssRTSPReqFilePathString);
// printf("QTSSIcecastAuthModule::RTSPPreProcess: request qtssRTSPReqFilePath: %s\n", qtssRTSPReqFilePathString);
//
// char* qtssRTSPReqURIString = NULL;
// (void)QTSS_GetValueAsString(theRTSPRequest, qtssRTSPReqURI, 0, &qtssRTSPReqURIString);
// printf("QTSSIcecastAuthModule::RTSPPreProcess: request qtssRTSPReqURI: %s\n", qtssRTSPReqURIString);
//
// char* qtssRTSPReqAbsoluteURLString = NULL;
// (void)QTSS_GetValueAsString(theRTSPRequest, qtssRTSPReqAbsoluteURL, 0, &qtssRTSPReqAbsoluteURLString);
// printf("QTSSIcecastAuthModule::RTSPPreProcess: request qtssRTSPReqAbsoluteURL: %s\n", qtssRTSPReqAbsoluteURLString);
// //QTSS_Delete(qtssRTSPReqFilePathString);
// QTSS_Delete(qtssRTSPReqURIString);
// //QTSS_Delete(qtssRTSPReqAbsoluteURLString);
// QTSS_Delete(qtssRTSPSesIDString);
}
else {
printf("QTSSIcecastAuthModule::RTSPPreProcess: username and/or password are NULL\n");
}
// set the auth status on the RTSP session
(void)QTSS_SetValue(theRTSPSession, attrRtspSessionAuthenticated, 0, &sessionValid, sizeof(sessionValid));
if (sessionValid) {
// valid session, return
return QTSS_NoErr;
}
else {
// not a valid session, error
char* accessDeniedMessage = "Access DENIED";
StrPtrLen accessDeniedMessageStr(accessDeniedMessage,sizeof(accessDeniedMessage));
(void)QTSSModuleUtils::SendErrorResponseWithMessage(theRTSPRequest, qtssClientForbidden, &accessDeniedMessageStr);
return QTSS_NoErr;
//.........这里部分代码省略.........
QTSS_Error Register()
{
QTSS_Error theErr;
// Do role & attribute setup
// for when the server starts
theErr = QTSS_AddRole(QTSS_Initialize_Role);
//PrintQTSSError("QTSSIcecastAuthModule::Register", "register for initialize role", theErr);
// for when asked to re-read the prefs file
qtss_printf("QTSSIcecastAuthModule::Register about to register for reread prefs role\n");
(void)QTSS_AddRole(QTSS_RereadPrefs_Role);
qtss_printf("QTSSIcecastAuthModule::Register after register for reread prefs role, about to register for filter\n");
// can't find doc on these - apparently deprecated as of v3??
//(void)QTSS_AddRole(QTSS_RTSPAuthenticate_Role);
//(void)QTSS_AddRole(QTSS_RTSPAuthorize_Role);
// the earliest call on a RTSP request - needed to get the full query including
// query params.
(void)QTSS_AddRole(QTSS_RTSPFilter_Role);
// called to pre-process a RTSP request - has full client info, including timestamp
// for completely unique session ID's (rather then the increment)
(void)QTSS_AddRole(QTSS_RTSPPreProcessor_Role);
// the shutdown role, for cleanup stuff
(void)QTSS_AddRole(QTSS_Shutdown_Role);
// The client close role, to send the end message
(void)QTSS_AddRole(QTSS_ClientSessionClosing_Role);
qtss_printf("QTSSIcecastAuthModule::Register all roles registered, about to register attributes\n");
// add the attribute to hold the username when provided
(void)QTSS_AddStaticAttribute(qtssRTSPSessionObjectType,
"ProvidedUsername", NULL, qtssAttrDataTypeCharArray);
(void)QTSS_IDForAttr(qtssRTSPSessionObjectType, "ProvidedUsername", &attrRtspSessionProvidedUsername);
// add the attribute to hold the username when provided
(void)QTSS_AddStaticAttribute(qtssRTSPSessionObjectType,
"ProvidedPassword", NULL, qtssAttrDataTypeCharArray);
(void)QTSS_IDForAttr(qtssRTSPSessionObjectType, "ProvidedPassword", &attrRtspSessionProvidedPassword);
// add the attribute that holds the flag to show if the session has been authenticated
// we check this first, if the session is authenticated we can skip everything
theErr = QTSS_AddStaticAttribute(qtssRTSPSessionObjectType,
"SessionAuthenticated", NULL, qtssAttrDataTypeBool16);
//PrintQTSSError("QTSSIcecastAuthModule::Register", "add session authenticated attribute to rtsp session", theErr);
theErr = QTSS_IDForAttr(qtssRTSPSessionObjectType, "SessionAuthenticated", &attrRtspSessionAuthenticated);
//PrintQTSSError("QTSSIcecastAuthModule::Register", "get ID for session authenticated attribute on rtsp session", theErr);
// add to hold the 'full' session ID on the RTSP Session (start time millis CONCAT sever session id, to ensure uniqueness)
(void)QTSS_AddStaticAttribute(qtssClientSessionObjectType,
"FullSessionID", NULL, qtssAttrDataTypeCharArray);
(void)QTSS_IDForAttr(qtssClientSessionObjectType, "FullSessionID", &attrClientSessionFullSessionID);
// the mount point needs to be stashed in the client session, for reporting on teardown
(void)QTSS_AddStaticAttribute(qtssClientSessionObjectType,
"MountPoint", NULL, qtssAttrDataTypeCharArray);
(void)QTSS_IDForAttr(qtssClientSessionObjectType, "MountPoint", &attrClientSessionMountPoint);
qtss_printf("QTSSIcecastAuthModule::Register end of method\n");
return QTSS_NoErr; // need to return. should do something with any errors captured above.
}
SInt64 EasyCMSSession::Run()
{
OS_Error theErr = OS_NoErr;
EventFlags events = this->GetEvents();
if(events & Task::kKillEvent)
{
qtss_printf("kill event but not handle it!\n");
}
while(1)
{
switch (fState)
{
case kIdle://空闲
{
qtss_printf("kIdle state \n");
if(!IsConnected())
{
// TCPSocket未连接的情况,首先进行登录连接
Login();
}
else
{
// TCPSocket已连接的情况下区分具体事件类型
if(events & Task::kStartEvent)
{
// CMSSession掉线,重新进行上线动作
if(kSessionOffline == fSessionStatus)
Login();
}
if(events & Task::kReadEvent)
{
// 对已连接的TCP进行消息读取
fState = kReadingRequest;
}
if(events & Task::kTimeoutEvent)
{
// 保活时间到,需要发送保活报文
Login();
}
}
// 如果有消息需要发送则进入发送流程
if (fOutputStream.GetBytesWritten() > 0)
{
fState = kSendingResponse;
}
if(kIdle == fState) return 0;
break;
}
case kReadingRequest:
{
qtss_printf("kReadingRequest state \n");
// 读取锁,已经在处理一个报文包时,不进行新网络报文的读取和处理
OSMutexLocker readMutexLocker(&fReadMutex);
// 网络请求报文存储在fInputStream中
if ((theErr = fInputStream.ReadRequest()) == QTSS_NoErr)
{
//如果RequestStream返回QTSS_NoErr,就表示已经读取了目前所到达的网络数据
//但,还不能构成一个整体报文,还要继续等待读取...
fSocket->GetSocket()->SetTask(this);
fSocket->GetSocket()->RequestEvent(EV_RE);
return 0;
}
if ((theErr != QTSS_RequestArrived) && (theErr != E2BIG) && (theErr != QTSS_BadArgument))
{
//Any other error implies that the input connection has gone away.
// We should only kill the whole session if we aren't doing HTTP.
// (If we are doing HTTP, the POST connection can go away)
Assert(theErr > 0);
// If we've gotten here, this must be an HTTP session with
// a dead input connection. If that's the case, we should
// clean up immediately so as to not have an open socket
// needlessly lingering around, taking up space.
Assert(!fSocket->GetSocket()->IsConnected());
this->ResetClientSocket();
return 0;
}
fState = kProcessingRequest;
break;
}
case kProcessingRequest:
{
qtss_printf("kProcessingRequest state \n");
// 处理网络报文
Assert( fInputStream.GetRequestBuffer() );
Assert(fRequest == NULL);
// 根据具体请求报文构造HTTPRequest请求类
fRequest = NEW HTTPRequest(&sServiceStr, fInputStream.GetRequestBuffer());
//.........这里部分代码省略.........
请发表评论