//throws eHTTPNoMoreData and eHTTPOutOfBuffer QTSS_Error RTSPRequest::ParseHeaders(StringParser& parser) { StrPtrLen theKeyWord; Bool16 isStreamOK; //Repeat until we get a \r\n\r\n, which signals the end of the headers while ((parser.PeekFast() != '\r') && (parser.PeekFast() != '\n')) { //First get the header identifier isStreamOK = parser.GetThru(&theKeyWord, ':'); if (!isStreamOK) return QTSSModuleUtils::SendErrorResponse(this, qtssClientBadRequest, qtssMsgNoColonAfterHeader, this->GetValue(qtssRTSPReqFullRequest)); theKeyWord.TrimWhitespace(); //Look up the proper header enumeration based on the header string. //Use the enumeration to look up the dictionary ID of this header, //and set that dictionary attribute to be whatever is in the body of the header UInt32 theHeader = RTSPProtocol::GetRequestHeader(theKeyWord); StrPtrLen theHeaderVal; parser.ConsumeUntil(&theHeaderVal, StringParser::sEOLMask); StrPtrLen theEOL; if ((parser.PeekFast() == '\r') || (parser.PeekFast() == '\n')) { isStreamOK = true; parser.ConsumeEOL(&theEOL); } else isStreamOK = false; while((parser.PeekFast() == ' ') || (parser.PeekFast() == '\t')) { theHeaderVal.Len += theEOL.Len; StrPtrLen temp; parser.ConsumeUntil(&temp, StringParser::sEOLMask); theHeaderVal.Len += temp.Len; if ((parser.PeekFast() == '\r') || (parser.PeekFast() == '\n')) { isStreamOK = true; parser.ConsumeEOL(&theEOL); } else isStreamOK = false; } // If this is an unknown header, ignore it. Otherwise, set the proper // dictionary attribute if (theHeader != qtssIllegalHeader) { Assert(theHeader < qtssNumHeaders); theHeaderVal.TrimWhitespace(); fHeaderDictionary.SetVal(theHeader, &theHeaderVal); } if (!isStreamOK) return QTSSModuleUtils::SendErrorResponse(this, qtssClientBadRequest, qtssMsgNoEOLAfterHeader); //some headers require some special processing. If this code begins //to get out of control, we made need to come up with a function pointer table switch (theHeader) { case qtssSessionHeader: ParseSessionHeader(); break; case qtssTransportHeader: ParseTransportHeader(); break; case qtssRangeHeader: ParseRangeHeader(); break; case qtssIfModifiedSinceHeader: ParseIfModSinceHeader();break; case qtssXRetransmitHeader: ParseRetransmitHeader();break; case qtssContentLengthHeader: ParseContentLengthHeader();break; case qtssSpeedHeader: ParseSpeedHeader(); break; case qtssXTransportOptionsHeader: ParseTransportOptionsHeader();break; case qtssXPreBufferHeader: ParsePrebufferHeader();break; case qtssXDynamicRateHeader: ParseDynamicRateHeader(); break; case qtssXRandomDataSizeHeader: ParseRandomDataSizeHeader(); break; case qtss3GPPAdaptationHeader: fRequest3GPP.ParseAdpationHeader(&fHeaderDictionary); break; case qtss3GPPLinkCharHeader: fRequest3GPP.ParseLinkCharHeader(&fHeaderDictionary); break; case qtssBandwidthHeader: ParseBandwidthHeader(); break; default: break; } } // Tell the session what the request body length is for this request // so that it can prevent people from reading past the end of the request. StrPtrLen* theContentLengthBody = fHeaderDictionary.GetValue(qtssContentLengthHeader); if (theContentLengthBody->Len > 0) { StringParser theHeaderParser(fHeaderDictionary.GetValue(qtssContentLengthHeader)); theHeaderParser.ConsumeWhitespace(); this->GetSession()->SetRequestBodyLength(theHeaderParser.ConsumeInteger(NULL)); } isStreamOK = parser.ExpectEOL(); Assert(isStreamOK); return QTSS_NoErr; }
//returns: SyntaxError if there was an error in the uri. Or InternalServerError QTSS_Error RTSPRequest::ParseURI(StringParser &parser) { //read in the complete URL, set it to be the qtssAbsoluteURLParam StrPtrLen theAbsURL; // RTSPRequestInterface::sPathURLStopConditions stop on ? as well as sURLStopConditions parser.ConsumeUntil(&theAbsURL, sURLStopConditions ); // set qtssRTSPReqAbsoluteURL to the URL throught the path component; will be : <protocol>://<host-addr>/<path> this->SetVal(qtssRTSPReqAbsoluteURL, &theAbsURL); StringParser urlParser(&theAbsURL); //we always should have a slash before the uri. //If not, that indicates this is a full URI. Also, this could be a '*' OPTIONS request if ((*theAbsURL.Ptr != '/') && (*theAbsURL.Ptr != '*')) { //if it is a full URL, store the host name off in a separate parameter StrPtrLen theRTSPString; urlParser.ConsumeLength(&theRTSPString, 7); //consume "rtsp://" //assign the host field here to the proper QTSS param StrPtrLen theHost; urlParser.ConsumeUntil(&theHost, '/'); fHeaderDictionary.SetVal(qtssHostHeader, &theHost); } // don't allow non-aggregate operations indicated by a url/media track=id // might need this for rate adapt if (qtssSetupMethod != fMethod && qtssOptionsMethod != fMethod && qtssSetParameterMethod != fMethod) // any method not a setup, options, or setparameter is not allowed to have a "/trackID=" in the url. if (qtssSetupMethod != fMethod) // any method not a setup is not allowed to have a "/trackID=" in the url. { StrPtrLenDel tempCStr(theAbsURL.GetAsCString()); StrPtrLen nonaggregate(tempCStr.FindString("/trackID=")); if (nonaggregate.Len > 0) // check for non-aggregate method and return error return QTSSModuleUtils::SendErrorResponse(this, qtssClientAggregateOptionAllowed, qtssMsgBadRTSPMethod, &theAbsURL); } // don't allow non-aggregate operations like a setup on a playing session if (qtssSetupMethod == fMethod) // if it is a setup but we are playing don't allow it { RTSPSession* theSession = (RTSPSession*)this->GetSession(); if (theSession != NULL && theSession->IsPlaying()) return QTSSModuleUtils::SendErrorResponse(this, qtssClientAggregateOptionAllowed, qtssMsgBadRTSPMethod, &theAbsURL); } // // In case there is no URI at all... we have to fake it. static char* sSlashURI = "/"; //whatever is in this position in the URL must be the URI. Store that //in the qtssURLParam. Confused? UInt32 uriLen = urlParser.GetDataReceivedLen() - urlParser.GetDataParsedLen(); if (uriLen > 0) this->SetVal(qtssRTSPReqURI, urlParser.GetCurrentPosition(), urlParser.GetDataReceivedLen() - urlParser.GetDataParsedLen()); else // // This might happen if there is nothing after the host at all, not even // a '/'. This is legal (RFC 2326, Sec 3.2). If so, just pretend that there // is a '/' this->SetVal(qtssRTSPReqURI, sSlashURI, 1); // parse the query string from the url if present. // init qtssRTSPReqQueryString dictionary to an empty string StrPtrLen queryString; this->SetVal(qtssRTSPReqQueryString, queryString.Ptr, queryString.Len); if ( parser.GetDataRemaining() > 0 ) { if ( parser.PeekFast() == '?' ) { // we've got some CGI param parser.ConsumeLength(&queryString, 1); // toss '?' // consume the rest of the line.. parser.ConsumeUntilWhitespace(&queryString); this->SetVal(qtssRTSPReqQueryString, queryString.Ptr, queryString.Len); } } // // If the is a '*', return right now because '*' is not a path // so the below functions don't make any sense. if ((*theAbsURL.Ptr == '*') && (theAbsURL.Len == 1)) { this->SetValue(qtssRTSPReqFilePath, 0, theAbsURL.Ptr, theAbsURL.Len, QTSSDictionary::kDontObeyReadOnly); return QTSS_NoErr; } //path strings are statically allocated. Therefore, if they are longer than //this length we won't be able to handle the request. StrPtrLen* theURLParam = this->GetValue(qtssRTSPReqURI); if (theURLParam->Len > RTSPRequestInterface::kMaxFilePathSizeInBytes) return QTSSModuleUtils::SendErrorResponse(this, qtssClientBadRequest, qtssMsgURLTooLong, theURLParam); //decode the URL, put the result in the separate buffer for the file path, //set the file path StrPtrLen to the proper value SInt32 theBytesWritten = StringTranslator::DecodeURL(theURLParam->Ptr, theURLParam->Len, fFilePath, RTSPRequestInterface::kMaxFilePathSizeInBytes); //if negative, an error occurred, reported as an QTSS_Error //we also need to leave room for a terminator. if ((theBytesWritten < 0) || (theBytesWritten == RTSPRequestInterface::kMaxFilePathSizeInBytes)) { return QTSSModuleUtils::SendErrorResponse(this, qtssClientBadRequest, qtssMsgURLInBadFormat, theURLParam); } // Convert from a / delimited path to a local file system path StringTranslator::DecodePath(fFilePath, theBytesWritten); //setup the proper QTSS param fFilePath[theBytesWritten] = '\0'; //this->SetVal(qtssRTSPReqFilePath, fFilePath, theBytesWritten); this->SetValue(qtssRTSPReqFilePath, 0, fFilePath, theBytesWritten, QTSSDictionary::kDontObeyReadOnly); return QTSS_NoErr; }