liveMedia/RTSPOverHTTPServer.cpp

Go to the documentation of this file.
00001 /**********
00002 This library is free software; you can redistribute it and/or modify it under
00003 the terms of the GNU Lesser General Public License as published by the
00004 Free Software Foundation; either version 2.1 of the License, or (at your
00005 option) any later version. (See <http://www.gnu.org/copyleft/lesser.html>.)
00006 
00007 This library is distributed in the hope that it will be useful, but WITHOUT
00008 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
00009 FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
00010 more details.
00011 
00012 You should have received a copy of the GNU Lesser General Public License
00013 along with this library; if not, write to the Free Software Foundation, Inc.,
00014 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00015 **********/
00016 // "liveMedia"
00017 // Copyright (c) 1996-2008 Live Networks, Inc.  All rights reserved.
00018 // A simple HTTP server that acts solely to implement RTSP-over-HTTP tunneling
00019 // (to a separate RTSP server), as described in
00020 // http://developer.apple.com/documentation/QuickTime/QTSS/Concepts/chapter_2_section_14.html
00021 // Implementation
00022 
00023 #include "RTSPOverHTTPServer.hh"
00024 #include "RTSPCommon.hh"
00025 #include <GroupsockHelper.hh>
00026 
00027 #include <string.h>
00028 #if defined(__WIN32__) || defined(_WIN32) || defined(_QNX4)
00029 #define snprintf _snprintf
00030 #else
00031 #include <signal.h>
00032 #define USE_SIGNALS 1
00033 #endif
00034 
00035 
00036 #define DEBUG 1 //#####@@@@@
00038 
00039 #define HTTP_PARAM_STRING_MAX 100
00040 
00041 RTSPOverHTTPServer*
00042 RTSPOverHTTPServer::createNew(UsageEnvironment& env, Port ourHTTPPort,
00043                               Port rtspServerPort, char const* rtspServerHostName) {
00044   int ourSocket = -1;
00045 
00046   do {
00047     int ourSocket = setUpOurSocket(env, ourHTTPPort);
00048     if (ourSocket == -1) break;
00049 
00050     return new RTSPOverHTTPServer(env, ourSocket, rtspServerPort, rtspServerHostName);
00051   } while (0);
00052 
00053   if (ourSocket != -1) ::closeSocket(ourSocket);
00054   return NULL;
00055 }
00056 
00057 #define LISTEN_BACKLOG_SIZE 20
00058 
00059 int RTSPOverHTTPServer::setUpOurSocket(UsageEnvironment& env, Port& ourPort) {
00060   int ourSocket = -1;
00061 
00062   do {
00063     NoReuse dummy; // Don't use this socket if there's already a local server using it
00064 
00065     ourSocket = setupStreamSocket(env, ourPort);
00066     if (ourSocket < 0) break;
00067 
00068     // Make sure we have a big send buffer:
00069     if (!increaseSendBufferTo(env, ourSocket, 50*1024)) break;
00070 
00071     // Allow multiple simultaneous connections:
00072     if (listen(ourSocket, LISTEN_BACKLOG_SIZE) < 0) {
00073       env.setResultErrMsg("listen() failed: ");
00074       break;
00075     }
00076 
00077     if (ourPort.num() == 0) {
00078       // bind() will have chosen a port for us; return it also:
00079       if (!getSourcePort(env, ourSocket, ourPort)) break;
00080     }
00081 
00082     return ourSocket;
00083   } while (0);
00084 
00085   if (ourSocket != -1) ::closeSocket(ourSocket);
00086   return -1;
00087 }
00088 
00089 RTSPOverHTTPServer
00090 ::RTSPOverHTTPServer(UsageEnvironment& env, int ourSocket,
00091                      Port rtspServerPort, char const* rtspServerHostName)
00092   : Medium(env),
00093     fServerSocket(ourSocket),
00094     fRTSPServerPort(rtspServerPort), fRTSPServerHostName(strDup(rtspServerHostName)) {
00095 #ifdef USE_SIGNALS
00096   // Ignore the SIGPIPE signal, so that clients on the same host that are killed
00097   // don't also kill us:
00098   signal(SIGPIPE, SIG_IGN);
00099 #endif
00100 
00101   // Arrange to handle connections from others:
00102   env.taskScheduler().turnOnBackgroundReadHandling(fServerSocket,
00103            (TaskScheduler::BackgroundHandlerProc*)&incomingConnectionHandler,
00104                                                    this);
00105 }
00106 
00107 RTSPOverHTTPServer::~RTSPOverHTTPServer() {
00108   delete[] fRTSPServerHostName;
00109 }
00110 
00111 void RTSPOverHTTPServer::incomingConnectionHandler(void* instance, int /*mask*/) {
00112   RTSPOverHTTPServer* server = (RTSPOverHTTPServer*)instance;
00113   server->incomingConnectionHandler1();
00114 }
00115 
00116 void RTSPOverHTTPServer::incomingConnectionHandler1() {
00117   struct sockaddr_in clientAddr;
00118   SOCKLEN_T clientAddrLen = sizeof clientAddr;
00119   int clientSocket = accept(fServerSocket, (struct sockaddr*)&clientAddr,
00120                             &clientAddrLen);
00121   if (clientSocket < 0) {
00122     int err = envir().getErrno();
00123     if (err != EWOULDBLOCK) {
00124       envir().setResultErrMsg("accept() failed: ");
00125     }
00126     return;
00127   }
00128   makeSocketNonBlocking(clientSocket);
00129   increaseSendBufferTo(envir(), clientSocket, 50*1024);
00130 #if defined(DEBUG) || defined(DEBUG_CONNECTIONS)
00131   fprintf(stderr, "accept()ed connection from %s\n", our_inet_ntoa(clientAddr.sin_addr));
00132 #endif
00133 
00134   // Create a new object for handling this HTTP connection:
00135   new HTTPClientConnection(*this, clientSocket);
00136 }
00137 
00138 
00140 
00141 RTSPOverHTTPServer::HTTPClientConnection
00142 ::HTTPClientConnection(RTSPOverHTTPServer& ourServer, int clientSocket)
00143   : fOurServer(ourServer), fClientSocket(clientSocket), fSessionIsActive(True) {
00144   // Arrange to handle incoming requests:
00145   resetRequestBuffer();
00146   envir().taskScheduler().turnOnBackgroundReadHandling(fClientSocket,
00147                (TaskScheduler::BackgroundHandlerProc*)&incomingRequestHandler, this);
00148 }
00149 
00150 RTSPOverHTTPServer::HTTPClientConnection
00151 ::~HTTPClientConnection() {
00152   // Turn off background read handling:
00153   envir().taskScheduler().turnOffBackgroundReadHandling(fClientSocket);
00154 
00155   ::closeSocket(fClientSocket);
00156 }
00157 
00158 void RTSPOverHTTPServer::HTTPClientConnection
00159 ::incomingRequestHandler(void* instance, int /*mask*/) {
00160   HTTPClientConnection* connection = (HTTPClientConnection*)instance;
00161   connection->incomingRequestHandler1();
00162 }
00163 
00164 void RTSPOverHTTPServer::HTTPClientConnection::incomingRequestHandler1() {
00165   struct sockaddr_in dummy; // 'from' address, meaningless in this case
00166   Boolean endOfMsg = False;
00167   unsigned char* ptr = &fRequestBuffer[fRequestBytesAlreadySeen];
00168   
00169   int bytesRead = readSocket(envir(), fClientSocket,
00170                              ptr, fRequestBufferBytesLeft, dummy);
00171   if (bytesRead <= 0 || (unsigned)bytesRead >= fRequestBufferBytesLeft) {
00172     // Either the client socket has died, or the request was too big for us.
00173     // Terminate this connection:
00174 #ifdef DEBUG
00175     fprintf(stderr, "HTTPClientConnection[%p]::incomingRequestHandler1() read %d bytes (of %d); terminating connection!\n", this, bytesRead, fRequestBufferBytesLeft);
00176 #endif
00177     delete this;
00178     return;
00179   }
00180 #ifdef DEBUG
00181   ptr[bytesRead] = '\0';
00182   fprintf(stderr, "HTTPClientConnection[%p]::incomingRequestHandler1() read %d bytes:%s\n",
00183           this, bytesRead, ptr);
00184 #endif
00185 
00186   // Look for the end of the message: <CR><LF><CR><LF>
00187   unsigned char *tmpPtr = ptr;
00188   if (fRequestBytesAlreadySeen > 0) --tmpPtr;
00189   // in case the last read ended with a <CR>
00190   while (tmpPtr < &ptr[bytesRead-1]) {
00191     if (*tmpPtr == '\r' && *(tmpPtr+1) == '\n') {
00192       if (tmpPtr - fLastCRLF == 2) { // This is it:
00193         endOfMsg = 1;
00194         break;
00195       }
00196       fLastCRLF = tmpPtr;
00197     }
00198     ++tmpPtr;
00199   }
00200   
00201   fRequestBufferBytesLeft -= bytesRead;
00202   fRequestBytesAlreadySeen += bytesRead;
00203 
00204   if (!endOfMsg) return; // subsequent reads will be needed to complete the request
00205 
00206   // Parse the request string to get the (few) parameters that we care about,
00207   // then handle the command:
00208   fRequestBuffer[fRequestBytesAlreadySeen] = '\0';
00209   char cmdName[HTTP_PARAM_STRING_MAX];
00210   char sessionCookie[HTTP_PARAM_STRING_MAX];
00211   char acceptStr[HTTP_PARAM_STRING_MAX];
00212   char contentTypeStr[HTTP_PARAM_STRING_MAX];
00213   if (!parseHTTPRequestString(cmdName, sizeof cmdName,
00214                           sessionCookie, sizeof sessionCookie,
00215                           acceptStr, sizeof acceptStr,
00216                           contentTypeStr, sizeof contentTypeStr)) {
00217 #ifdef DEBUG
00218     fprintf(stderr, "parseHTTPRTSPRequestString() failed!\n");
00219 #endif
00220     handleCmd_bad();
00221   } else {
00222 #ifdef DEBUG
00223     fprintf(stderr, "parseHTTPRTSPRequestString() returned cmdName \"%s\", sessionCookie \"%s\", acceptStr \"%s\", contentTypeStr \"%s\"\n", cmdName, sessionCookie, acceptStr, contentTypeStr);
00224 #endif
00225 #if 0
00226     if (strcmp(cmdName, "OPTIONS") == 0) {
00227       handleCmd_OPTIONS(cseq);
00228     } else if (strcmp(cmdName, "DESCRIBE") == 0) {
00229       handleCmd_DESCRIBE(cseq, urlSuffix, (char const*)fRequestBuffer);
00230     } else if (strcmp(cmdName, "SETUP") == 0) {
00231       handleCmd_SETUP(cseq, urlPreSuffix, urlSuffix, (char const*)fRequestBuffer);
00232     } else if (strcmp(cmdName, "TEARDOWN") == 0
00233                || strcmp(cmdName, "PLAY") == 0
00234                || strcmp(cmdName, "PAUSE") == 0
00235                || strcmp(cmdName, "GET_PARAMETER") == 0) {
00236       handleCmd_withinSession(cmdName, urlPreSuffix, urlSuffix, cseq,
00237                               (char const*)fRequestBuffer);
00238     } else {
00239       handleCmd_notSupported(cseq);
00240     }
00241 #endif
00242   }
00243     
00244 #ifdef DEBUG
00245   fprintf(stderr, "sending response: %s", fResponseBuffer);
00246 #endif
00247   send(fClientSocket, (char const*)fResponseBuffer, strlen((char*)fResponseBuffer), 0);
00248 
00249   resetRequestBuffer(); // to prepare for any subsequent request
00250   if (!fSessionIsActive) delete this;
00251 }
00252 
00253 void RTSPOverHTTPServer::HTTPClientConnection::resetRequestBuffer() {
00254   fRequestBytesAlreadySeen = 0;
00255   fRequestBufferBytesLeft = sizeof fRequestBuffer;
00256   fLastCRLF = &fRequestBuffer[-3]; // hack
00257 }
00258 
00259 Boolean RTSPOverHTTPServer::HTTPClientConnection::
00260 parseHTTPRequestString(char* resultCmdName,
00261                    unsigned resultCmdNameMaxSize,
00262                    char* sessionCookie,
00263                    unsigned sessionCookieMaxSize,
00264                    char* acceptStr,
00265                    unsigned acceptStrMaxSize,
00266                    char* contentTypeStr,
00267                    unsigned contentTypeStrMaxSize) {
00268   return False; //#####@@@@@
00269 #if 0
00270   // This parser is currently rather dumb; it should be made smarter #####
00271 
00272   // Read everything up to the first space as the command name:
00273   Boolean parseSucceeded = False;
00274   unsigned i;
00275   for (i = 0; i < resultCmdNameMaxSize-1 && i < reqStrSize; ++i) {
00276     char c = reqStr[i];
00277     if (c == ' ' || c == '\t') {
00278       parseSucceeded = True;
00279       break;
00280     }
00281 
00282     resultCmdName[i] = c;
00283   }
00284   resultCmdName[i] = '\0';
00285   if (!parseSucceeded) return False;
00286 
00287   // Skip over the prefix of any "rtsp://" or "rtsp:/" URL that follows:
00288   unsigned j = i+1;
00289   while (j < reqStrSize && (reqStr[j] == ' ' || reqStr[j] == '\t')) ++j; // skip over any additional white space
00290    for (j = i+1; j < reqStrSize-8; ++j) {
00291      if ((reqStr[j] == 'r' || reqStr[j] == 'R')
00292          && (reqStr[j+1] == 't' || reqStr[j+1] == 'T')
00293          && (reqStr[j+2] == 's' || reqStr[j+2] == 'S')
00294          && (reqStr[j+3] == 'p' || reqStr[j+3] == 'P')
00295          && reqStr[j+4] == ':' && reqStr[j+5] == '/') {
00296        j += 6;
00297        if (reqStr[j] == '/') {
00298          // This is a "rtsp://" URL; skip over the host:port part that follows:
00299          ++j;
00300          while (j < reqStrSize && reqStr[j] != '/' && reqStr[j] != ' ') ++j;
00301        } else {
00302          // This is a "rtsp:/" URL; back up to the "/":
00303          --j;
00304        }
00305        i = j;
00306        break;
00307      }
00308    }
00309 
00310  // Look for the URL suffix (before the following "RTSP/"):
00311  parseSucceeded = False;
00312  for (unsigned k = i+1; k < reqStrSize-5; ++k) {
00313    if (reqStr[k] == 'R' && reqStr[k+1] == 'T' &&
00314        reqStr[k+2] == 'S' && reqStr[k+3] == 'P' && reqStr[k+4] == '/') {
00315      while (--k >= i && reqStr[k] == ' ') {} // go back over all spaces before "RTSP/"
00316       unsigned k1 = k;
00317       while (k1 > i && reqStr[k1] != '/' && reqStr[k1] != ' ') --k1;
00318       // the URL suffix comes from [k1+1,k]
00319 
00320       // Copy "resultURLSuffix":
00321       if (k - k1 + 1 > resultURLSuffixMaxSize) return False; // there's no room
00322       unsigned n = 0, k2 = k1+1;
00323       while (k2 <= k) resultURLSuffix[n++] = reqStr[k2++];
00324       resultURLSuffix[n] = '\0';
00325 
00326       // Also look for the URL 'pre-suffix' before this:
00327       unsigned k3 = --k1;
00328       while (k3 > i && reqStr[k3] != '/' && reqStr[k3] != ' ') --k3;
00329       // the URL pre-suffix comes from [k3+1,k1]
00330 
00331       // Copy "resultURLPreSuffix":
00332       if (k1 - k3 + 1 > resultURLPreSuffixMaxSize) return False; // there's no room
00333       n = 0; k2 = k3+1;
00334       while (k2 <= k1) resultURLPreSuffix[n++] = reqStr[k2++];
00335       resultURLPreSuffix[n] = '\0';
00336 
00337       i = k + 7; // to go past " RTSP/"
00338       parseSucceeded = True;
00339       break;
00340     }
00341   }
00342   if (!parseSucceeded) return False;
00343 
00344   // Look for "CSeq:", skip whitespace,
00345   // then read everything up to the next \r or \n as 'CSeq':
00346   parseSucceeded = False;
00347   for (j = i; j < reqStrSize-5; ++j) {
00348     if (reqStr[j] == 'C' && reqStr[j+1] == 'S' && reqStr[j+2] == 'e' &&
00349         reqStr[j+3] == 'q' && reqStr[j+4] == ':') {
00350       j += 5;
00351       unsigned n;
00352       while (j < reqStrSize && (reqStr[j] ==  ' ' || reqStr[j] == '\t')) ++j;
00353       for (n = 0; n < resultCSeqMaxSize-1 && j < reqStrSize; ++n,++j) {
00354         char c = reqStr[j];
00355         if (c == '\r' || c == '\n') {
00356           parseSucceeded = True;
00357           break;
00358         }
00359 
00360         resultCSeq[n] = c;
00361       }
00362       resultCSeq[n] = '\0';
00363       break;
00364     }
00365   }
00366   if (!parseSucceeded) return False;
00367 
00368   return True;
00369 #endif
00370 }
00371 
00372 static char const* allowedCommandNames = "GET, PUT";
00373 
00374 void RTSPOverHTTPServer::HTTPClientConnection::handleCmd_bad() {
00375   snprintf((char*)fResponseBuffer, sizeof fResponseBuffer,
00376            "HTTP/1.1 400 Bad Request\r\nAllow: %s\r\n\r\n",
00377            allowedCommandNames);
00378 }

Generated on Tue Jul 22 06:39:07 2008 for live by  doxygen 1.5.2