Very rough first start, not even everything added
This commit is contained in:
commit
af3619d4fa
88 changed files with 24251 additions and 0 deletions
419
HTTPUtilitiesLib/HTTPRequest.cpp
Normal file
419
HTTPUtilitiesLib/HTTPRequest.cpp
Normal file
|
@ -0,0 +1,419 @@
|
|||
/*
|
||||
*
|
||||
* @APPLE_LICENSE_HEADER_START@
|
||||
*
|
||||
* Copyright (c) 1999-2008 Apple Inc. All Rights Reserved.
|
||||
*
|
||||
* This file contains Original Code and/or Modifications of Original Code
|
||||
* as defined in and that are subject to the Apple Public Source License
|
||||
* Version 2.0 (the 'License'). You may not use this file except in
|
||||
* compliance with the License. Please obtain a copy of the License at
|
||||
* http://www.opensource.apple.com/apsl/ and read it before using this
|
||||
* file.
|
||||
*
|
||||
* The Original Code and all software distributed under the License are
|
||||
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
|
||||
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
|
||||
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
|
||||
* Please see the License for the specific language governing rights and
|
||||
* limitations under the License.
|
||||
*
|
||||
* @APPLE_LICENSE_HEADER_END@
|
||||
*
|
||||
*/
|
||||
#include "HTTPRequest.h"
|
||||
#include "HTTPProtocol.h"
|
||||
#include "OSMemory.h"
|
||||
#include "StringParser.h"
|
||||
#include "StringTranslator.h"
|
||||
#include "ResizeableStringFormatter.h"
|
||||
#include "DateTranslator.h"
|
||||
|
||||
|
||||
StrPtrLen HTTPRequest::sColonSpace(": ", 2);
|
||||
static Bool16 sFalse = false;
|
||||
static Bool16 sTrue = true;
|
||||
static StrPtrLen sCloseString("close", 5);
|
||||
static StrPtrLen sKeepAliveString("keep-alive", 10);
|
||||
static StrPtrLen sDefaultRealm("Streaming Server", 19);
|
||||
UInt8 HTTPRequest::sURLStopConditions[] =
|
||||
{
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 1, //0-9 //'\t' is a stop condition
|
||||
1, 0, 0, 1, 0, 0, 0, 0, 0, 0, //10-19 //'\r' & '\n' are stop conditions
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //20-29
|
||||
0, 0, 1, 0, 0, 0, 0, 0, 0, 0, //30-39 //' '
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //40-49
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //50-59
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //60-69
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //70-79
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //80-89
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //90-99
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //100-109
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //110-119
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //120-129
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //130-139
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //140-149
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //150-159
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //160-169
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //170-179
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //180-189
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //190-199
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //200-209
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //210-219
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //220-229
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //230-239
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //240-249
|
||||
0, 0, 0, 0, 0, 0 //250-255
|
||||
};
|
||||
|
||||
|
||||
// Constructor
|
||||
HTTPRequest::HTTPRequest(StrPtrLen* serverHeader, StrPtrLen* requestPtr)
|
||||
{
|
||||
// Store the pointer to the server header field
|
||||
fSvrHeader = *serverHeader;
|
||||
|
||||
// Set initial state
|
||||
fRequestHeader = *requestPtr;
|
||||
fResponseHeader = NULL;
|
||||
fResponseFormatter = NULL;
|
||||
fMethod = httpIllegalMethod;
|
||||
fVersion = httpIllegalVersion;
|
||||
fAbsoluteURI = NULL;
|
||||
fRelativeURI = NULL;
|
||||
fAbsoluteURIScheme = NULL;
|
||||
fHostHeader = NULL;
|
||||
fRequestPath = NULL;
|
||||
fStatusCode = httpOK;
|
||||
fRequestKeepAlive = false; // Default value when there is no version string
|
||||
}
|
||||
|
||||
// Constructor for creating a response only
|
||||
HTTPRequest::HTTPRequest(StrPtrLen* serverHeader)
|
||||
{
|
||||
// Store the pointer to the server header field
|
||||
fSvrHeader = *serverHeader;
|
||||
|
||||
// We do not require any of these:
|
||||
fRequestHeader = NULL;
|
||||
|
||||
fMethod = httpIllegalMethod;
|
||||
fVersion = httpIllegalVersion;
|
||||
fRequestLine = NULL;
|
||||
fAbsoluteURI = NULL;
|
||||
fRelativeURI = NULL;
|
||||
fAbsoluteURIScheme = NULL;
|
||||
fHostHeader = NULL;
|
||||
fRequestPath = NULL;
|
||||
fStatusCode = 0;
|
||||
fRequestKeepAlive = false;
|
||||
|
||||
// We require the response but we allocate memory only when we call
|
||||
// CreateResponseHeader
|
||||
fResponseHeader = NULL;
|
||||
fResponseFormatter = NULL;
|
||||
}
|
||||
|
||||
// Destructor
|
||||
HTTPRequest::~HTTPRequest()
|
||||
{
|
||||
if (fResponseHeader != NULL)
|
||||
{
|
||||
if (fResponseHeader->Ptr != NULL)
|
||||
delete fResponseHeader->Ptr;
|
||||
delete fResponseHeader;
|
||||
}
|
||||
if (fResponseFormatter != NULL)
|
||||
delete fResponseFormatter;
|
||||
if (fRequestPath != NULL)
|
||||
delete [] fRequestPath;
|
||||
}
|
||||
//Parses the request
|
||||
QTSS_Error HTTPRequest::Parse()
|
||||
{
|
||||
Assert(fRequestHeader.Ptr != NULL);
|
||||
StringParser parser(&fRequestHeader);
|
||||
|
||||
// Store the request line (used for logging)
|
||||
// (ex: GET /index.html HTTP/1.0)
|
||||
StringParser requestLineParser(&fRequestHeader);
|
||||
requestLineParser.ConsumeUntil(&fRequestLine, StringParser::sEOLMask);
|
||||
|
||||
// Parse request line returns an error if there is an error in the
|
||||
// request URI or the formatting of the request line.
|
||||
// If the method or version are not found, they are set
|
||||
// to httpIllegalMethod or httpIllegalVersion respectively,
|
||||
// and QTSS_NoErr is returned.
|
||||
QTSS_Error err = ParseRequestLine(&parser);
|
||||
if (err != QTSS_NoErr)
|
||||
return err;
|
||||
|
||||
// Parse headers and set values of headers into fFieldValues array
|
||||
err = ParseHeaders(&parser);
|
||||
if (err != QTSS_NoErr)
|
||||
return err;
|
||||
|
||||
return QTSS_NoErr;
|
||||
}
|
||||
|
||||
QTSS_Error HTTPRequest::ParseRequestLine(StringParser* parser)
|
||||
{
|
||||
// Get the method - If the method is not one of the defined methods
|
||||
// then it doesn't return an error but sets fMethod to httpIllegalMethod
|
||||
StrPtrLen theParsedData;
|
||||
parser->ConsumeWord(&theParsedData);
|
||||
fMethod = HTTPProtocol::GetMethod(&theParsedData);
|
||||
|
||||
// Consume whitespace
|
||||
parser->ConsumeWhitespace();
|
||||
|
||||
// Parse the URI - If it fails returns an error after setting
|
||||
// the fStatusCode to the appropriate error code
|
||||
QTSS_Error err = ParseURI(parser);
|
||||
if (err != QTSS_NoErr)
|
||||
return err;
|
||||
|
||||
// Consume whitespace
|
||||
parser->ConsumeWhitespace();
|
||||
|
||||
// If there is a version, consume the version string
|
||||
StrPtrLen versionStr;
|
||||
parser->ConsumeUntil(&versionStr, StringParser::sEOLMask);
|
||||
// Check the version
|
||||
if (versionStr.Len > 0)
|
||||
fVersion = HTTPProtocol::GetVersion(&versionStr);
|
||||
|
||||
// Go past the end of line
|
||||
if (!parser->ExpectEOL())
|
||||
{
|
||||
fStatusCode = httpBadRequest;
|
||||
return QTSS_BadArgument; // Request line is not properly formatted!
|
||||
}
|
||||
|
||||
return QTSS_NoErr;
|
||||
}
|
||||
|
||||
QTSS_Error HTTPRequest::ParseURI(StringParser* parser)
|
||||
{
|
||||
|
||||
// read in the complete URL into fRequestAbsURI
|
||||
parser->ConsumeUntil(&fAbsoluteURI, sURLStopConditions);
|
||||
|
||||
StringParser urlParser(&fAbsoluteURI);
|
||||
|
||||
// we always should have a slash before the URI
|
||||
// If not, that indicates this is a full URI
|
||||
if (fAbsoluteURI.Ptr[0] != '/')
|
||||
{
|
||||
//if it is a full URL, store the scheme and host name
|
||||
urlParser.ConsumeLength(&fAbsoluteURIScheme, 7); //consume "http://"
|
||||
urlParser.ConsumeUntil(&fHostHeader, '/');
|
||||
}
|
||||
|
||||
// whatever is in this position is the relative URI
|
||||
StrPtrLen relativeURI(urlParser.GetCurrentPosition(), urlParser.GetDataReceivedLen() - urlParser.GetDataParsedLen());
|
||||
// read this URI into fRequestRelURI
|
||||
fRelativeURI = relativeURI;
|
||||
|
||||
// Allocate memory for fRequestPath
|
||||
UInt32 len = fRelativeURI.Len;
|
||||
len++;
|
||||
char* relativeURIDecoded = NEW char[len];
|
||||
|
||||
SInt32 theBytesWritten = StringTranslator::DecodeURL(fRelativeURI.Ptr, fRelativeURI.Len,
|
||||
relativeURIDecoded, len);
|
||||
|
||||
//if negative, an error occurred, reported as an QTSS_Error
|
||||
//we also need to leave room for a terminator.
|
||||
if ((theBytesWritten < 0) || ((UInt32)theBytesWritten == len))
|
||||
{
|
||||
fStatusCode = httpBadRequest;
|
||||
return QTSS_BadArgument;
|
||||
}
|
||||
fRequestPath = NEW char[theBytesWritten + 1];
|
||||
::memcpy(fRequestPath, relativeURIDecoded + 1, theBytesWritten);
|
||||
delete relativeURIDecoded;
|
||||
fRequestPath[theBytesWritten] = '\0';
|
||||
return QTSS_NoErr;
|
||||
}
|
||||
|
||||
// Parses the Connection header and makes sure that request is properly terminated
|
||||
QTSS_Error HTTPRequest::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)
|
||||
{ // No colon after header!
|
||||
fStatusCode = httpBadRequest;
|
||||
return QTSS_BadArgument;
|
||||
}
|
||||
|
||||
if (parser->PeekFast() == ' ')
|
||||
{ // handle space, if any
|
||||
isStreamOK = parser->Expect(' ');
|
||||
Assert(isStreamOK);
|
||||
}
|
||||
|
||||
//Look up the proper header enumeration based on the header string.
|
||||
HTTPHeader theHeader = HTTPProtocol::GetHeader(&theKeyWord);
|
||||
|
||||
StrPtrLen theHeaderVal;
|
||||
isStreamOK = parser->GetThruEOL(&theHeaderVal);
|
||||
|
||||
if (!isStreamOK)
|
||||
{ // No EOL after header!
|
||||
fStatusCode = httpBadRequest;
|
||||
return QTSS_BadArgument;
|
||||
}
|
||||
|
||||
// If this is the connection header
|
||||
if ( theHeader == httpConnectionHeader )
|
||||
{ // Set the keep alive boolean based on the connection header value
|
||||
SetKeepAlive(&theHeaderVal);
|
||||
}
|
||||
|
||||
// Have the header field and the value; Add value to the array
|
||||
// If the field is invalid (or unrecognized) just skip over gracefully
|
||||
if ( theHeader != httpIllegalHeader )
|
||||
fFieldValues[theHeader] = theHeaderVal;
|
||||
|
||||
}
|
||||
|
||||
isStreamOK = parser->ExpectEOL();
|
||||
Assert(isStreamOK);
|
||||
|
||||
return QTSS_NoErr;
|
||||
}
|
||||
|
||||
void HTTPRequest::SetKeepAlive(StrPtrLen *keepAliveValue)
|
||||
{
|
||||
if ( sCloseString.EqualIgnoreCase(keepAliveValue->Ptr, keepAliveValue->Len) )
|
||||
fRequestKeepAlive = sFalse;
|
||||
else
|
||||
{
|
||||
Assert( sKeepAliveString.EqualIgnoreCase(keepAliveValue->Ptr, keepAliveValue->Len) );
|
||||
fRequestKeepAlive = sTrue;
|
||||
}
|
||||
}
|
||||
|
||||
void HTTPRequest::PutStatusLine(StringFormatter* putStream, HTTPStatusCode status,
|
||||
HTTPVersion version)
|
||||
{
|
||||
putStream->Put(*(HTTPProtocol::GetVersionString(version)));
|
||||
putStream->PutSpace();
|
||||
putStream->Put(*(HTTPProtocol::GetStatusCodeAsString(status)));
|
||||
putStream->PutSpace();
|
||||
putStream->Put(*(HTTPProtocol::GetStatusCodeString(status)));
|
||||
putStream->PutEOL();
|
||||
}
|
||||
|
||||
StrPtrLen* HTTPRequest::GetHeaderValue(HTTPHeader inHeader)
|
||||
{
|
||||
if ( inHeader != httpIllegalHeader )
|
||||
return &fFieldValues[inHeader];
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void HTTPRequest:: CreateResponseHeader(HTTPVersion version, HTTPStatusCode statusCode)
|
||||
{
|
||||
// If we are creating a second response for the same request, make sure and
|
||||
// deallocate memory for old response and allocate fresh memory
|
||||
if (fResponseFormatter != NULL)
|
||||
{
|
||||
if(fResponseHeader->Ptr != NULL)
|
||||
delete fResponseHeader->Ptr;
|
||||
delete fResponseHeader;
|
||||
delete fResponseFormatter;
|
||||
}
|
||||
|
||||
// Allocate memory for the response when you first create it
|
||||
char* responseString = NEW char[kMinHeaderSizeInBytes];
|
||||
fResponseHeader = NEW StrPtrLen(responseString, kMinHeaderSizeInBytes);
|
||||
fResponseFormatter = NEW ResizeableStringFormatter(fResponseHeader->Ptr, fResponseHeader->Len);
|
||||
|
||||
//make a partial header for the given version and status code
|
||||
PutStatusLine(fResponseFormatter, statusCode, version);
|
||||
Assert(fSvrHeader.Ptr != NULL);
|
||||
fResponseFormatter->Put(fSvrHeader);
|
||||
fResponseFormatter->PutEOL();
|
||||
fResponseHeader->Len = fResponseFormatter->GetCurrentOffset();
|
||||
}
|
||||
|
||||
StrPtrLen* HTTPRequest::GetCompleteResponseHeader()
|
||||
{
|
||||
fResponseFormatter->PutEOL();
|
||||
fResponseHeader->Len = fResponseFormatter->GetCurrentOffset();
|
||||
return fResponseHeader;
|
||||
}
|
||||
|
||||
void HTTPRequest::AppendResponseHeader(HTTPHeader inHeader, StrPtrLen* inValue)
|
||||
{
|
||||
fResponseFormatter->Put(*(HTTPProtocol::GetHeaderString(inHeader)));
|
||||
fResponseFormatter->Put(sColonSpace);
|
||||
fResponseFormatter->Put(*inValue);
|
||||
fResponseFormatter->PutEOL();
|
||||
fResponseHeader->Len = fResponseFormatter->GetCurrentOffset();
|
||||
}
|
||||
|
||||
void HTTPRequest::AppendContentLengthHeader(UInt64 length_64bit)
|
||||
{
|
||||
char* contentLength = NEW char[256];
|
||||
qtss_sprintf(contentLength, "%"_64BITARG_"d", length_64bit);
|
||||
StrPtrLen contentLengthPtr(contentLength);
|
||||
AppendResponseHeader(httpContentLengthHeader, &contentLengthPtr);
|
||||
}
|
||||
|
||||
void HTTPRequest::AppendContentLengthHeader(UInt32 length_32bit)
|
||||
{
|
||||
char* contentLength = NEW char[256];
|
||||
qtss_sprintf(contentLength, "%"_U32BITARG_"", length_32bit);
|
||||
StrPtrLen contentLengthPtr(contentLength);
|
||||
AppendResponseHeader(httpContentLengthHeader, &contentLengthPtr);
|
||||
}
|
||||
|
||||
void HTTPRequest::AppendConnectionCloseHeader()
|
||||
{
|
||||
AppendResponseHeader(httpConnectionHeader, &sCloseString);
|
||||
}
|
||||
|
||||
void HTTPRequest::AppendConnectionKeepAliveHeader()
|
||||
{
|
||||
AppendResponseHeader(httpConnectionHeader, &sKeepAliveString);
|
||||
}
|
||||
|
||||
void HTTPRequest::AppendDateAndExpiresFields()
|
||||
{
|
||||
Assert(OSThread::GetCurrent() != NULL);
|
||||
DateBuffer* theDateBuffer = OSThread::GetCurrent()->GetDateBuffer();
|
||||
theDateBuffer->InexactUpdate(); // Update the date buffer to the current date & time
|
||||
StrPtrLen theDate(theDateBuffer->GetDateBuffer(), DateBuffer::kDateBufferLen);
|
||||
|
||||
// Append dates, and have this response expire immediately
|
||||
this->AppendResponseHeader(httpDateHeader, &theDate);
|
||||
this->AppendResponseHeader(httpExpiresHeader, &theDate);
|
||||
}
|
||||
|
||||
void HTTPRequest::AppendDateField()
|
||||
{
|
||||
Assert(OSThread::GetCurrent() != NULL);
|
||||
DateBuffer* theDateBuffer = OSThread::GetCurrent()->GetDateBuffer();
|
||||
theDateBuffer->InexactUpdate(); // Update the date buffer to the current date & time
|
||||
StrPtrLen theDate(theDateBuffer->GetDateBuffer(), DateBuffer::kDateBufferLen);
|
||||
|
||||
// Append date
|
||||
this->AppendResponseHeader(httpDateHeader, &theDate);
|
||||
}
|
||||
|
||||
time_t HTTPRequest::ParseIfModSinceHeader()
|
||||
{
|
||||
time_t theIfModSinceDate = (time_t) DateTranslator::ParseDate(&fFieldValues[httpIfModifiedSinceHeader]);
|
||||
return theIfModSinceDate;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue