reactos/modules/rosapps/applications/net/roshttpd/http.cpp

378 lines
9.6 KiB
C++

/*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS HTTP Daemon
* FILE: http.cpp
* PURPOSE: HTTP 1.1 parser engine
* PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net)
* REVISIONS:
* CSH 01/09/2000 Created
* TODO: - Implement message-body
* - Implement more generel-header entries
* - Implement more request-header entries
* - Implement more entity-header entries
*/
#include <debug.h>
#include <iostream>
#include <string.h>
#include <http.h>
CHAR MethodTable[NUMMETHODS][8] = {"OPTIONS", "GET", "HEAD", "POST", "PUT",
"DELETE", "TRACE"};
CHAR GenerelTable[NUMGENERELS][18] = {"Cache-Control", "Connection", "Date", "Pragma",
"Transfer-Encoding", "Upgrade", "Via"};
CHAR RequestTable[NUMREQUESTS][20] = {"Accept", "Accept-Charset", "Accept-Encoding",
"Accept-Language", "Authorization", "From", "Host", "If-Modified-Since", "If-Match",
"If-None-Match", "If-Range", "If-Unmodified-Since", "Max-Forwards",
"Proxy-Authorization", "Range", "Referer", "User-Agent"};
CHAR EntityTable[NUMENTITIES][17] = {"Allow", "Content-Base", "Content-Encoding",
"Content-Language", "Content-Length", "Content-Location", "Content-MD5",
"Content-Range", "Content-Type", "ETag", "Expires", "Last-Modified"};
// *************************** CHttpParser ***************************
// Default constructor
CHttpParser::CHttpParser()
{
nHead = 0;
nTail = 0;
}
// Default destructor
CHttpParser::~CHttpParser()
{
}
// Returns TRUE if a complete HTTP message is in buffer
BOOL CHttpParser::Complete()
{
UINT nTmp;
/*DPRINT("--1:-%d---\n", sBuffer[nHead-2]);
DPRINT("--2:-%d---\n", sBuffer[nHead-1]);
sBuffer[nHead] = '!';
sBuffer[nHead+1] = 0;
DPRINT("Examining buffer: (Head: %d, Tail: %d)\n", nHead, nTail);
DPRINT("%s\n", (LPSTR)&sBuffer[nTail]);*/
nTmp = nTail;
if (!Parse()) {
if (!bUnknownMethod)
nTail = nTmp;
return FALSE;
} else
return TRUE;
}
// Read a character from buffer
BOOL CHttpParser::ReadChar(LPSTR lpsStr)
{
if (nTail <= nHead) {
if (nTail != nHead) {
lpsStr[0] = sBuffer[nTail];
nTail++;
return TRUE;
} else {
lpsStr[0] = 0;
return FALSE;
}
} else {
if (nTail == sizeof(sBuffer))
nTail = 0;
if (nTail != nHead) {
lpsStr[0] = sBuffer[nTail];
nTail++;
return TRUE;
} else {
lpsStr[0] = 0;
return FALSE;
}
}
}
// Peek at a character in the buffer
BOOL CHttpParser::PeekChar(LPSTR lpsStr)
{
UINT nFakeTail;
if (nTail == sizeof(sBuffer))
nFakeTail = 0;
else
nFakeTail = nTail;
if (nFakeTail != nHead) {
lpsStr[0] = sBuffer[nFakeTail];
return TRUE;
} else {
lpsStr[0] = 0;
return FALSE;
}
}
// Read a string from buffer. Only A-Z, a-z, 0-9 and '-' are valid characters
BOOL CHttpParser::ReadString(LPSTR lpsStr, UINT nLength)
{
UINT i = 0;
CHAR sTmp;
while (PeekChar(&sTmp)) {
if (((sTmp >= 'A') && (sTmp <= 'Z')) || ((sTmp >= 'a') && (sTmp <= 'z')) ||
((sTmp >= '0') && (sTmp <= '9')) || (sTmp == '-')) {
if (i >= (nLength - 1)) {
lpsStr[0] = 0;
return FALSE;
}
ReadChar(&sTmp);
lpsStr[i] = sTmp;
i++;
} else {
lpsStr[i] = 0;
return TRUE;
}
}
lpsStr[0] = 0;
return FALSE;
}
// Read a string from buffer. Stop if SP or CR is found or when there are no more
// characters
BOOL CHttpParser::ReadSpecial(LPSTR lpsStr, UINT nLength)
{
UINT i = 0;
CHAR sTmp;
while (PeekChar(&sTmp) && (sTmp != ' ') && (sTmp != 13)) {
if (i >= (nLength - 1)) {
lpsStr[nLength - 1] = 0;
return FALSE;
}
ReadChar(&sTmp);
lpsStr[i] = sTmp;
i++;
}
lpsStr[i] = 0;
return TRUE;
}
// Skip until "sCh" is found
VOID CHttpParser::Skip(CHAR sCh)
{
CHAR sTmp;
while (PeekChar(&sTmp) && (sTmp != sCh))
ReadChar(&sTmp);
}
// Return TRUE if sCh is the next character
BOOL CHttpParser::Expect(CHAR sCh)
{
CHAR sTmp;
if (PeekChar(&sTmp)) {
if (sTmp == sCh) {
ReadChar(&sTmp);
return TRUE;
}
}
return FALSE;
}
// Return TRUE if CRLF are the next characters
BOOL CHttpParser::ExpectCRLF()
{
return (Expect(13) && Expect(10));
}
// Request = RequestLine | *( GenerelHeader | RequestHeader | EntityHeader )
// CRLF [ MessageBody ]
BOOL CHttpParser::Parse()
{
BOOL bStatus;
if (RequestLine()) {
do {
if (!ReadString(sHeader, sizeof(sHeader)))
break;
bStatus = (GenerelHeader());
bStatus = (RequestHeader() || bStatus);
bStatus = (EntityHeader() || bStatus);
} while (bStatus);
// CRLF
if (!ExpectCRLF())
return FALSE;
MessageBody();
return TRUE;
}
return FALSE;
}
// RequestLine = Method SP RequestURI SP HTTP-Version CRLF
BOOL CHttpParser::RequestLine()
{
CHAR sCh;
UINT i;
bUnknownMethod = FALSE;
// RFC 2068 states that servers SHOULD ignore any empty nine(s) received where a
// Request-Line is expected
while (PeekChar(&sCh) && ((sCh == 13) || (sCh == 10)));
if (!ReadString(sMethod, sizeof(sMethod)))
return FALSE;
for (i = 0; i < NUMMETHODS; i++) {
if (strcmp(MethodTable[i], sMethod) == 0) {
nMethodNo = i;
if (!Expect(' '))
return FALSE;
// URI (ie. host/directory/resource)
if (!ReadSpecial(sUri, sizeof(sUri)))
return FALSE;
if (!Expect(' '))
return FALSE;
// HTTP version (eg. HTTP/1.1)
if (!ReadSpecial(sVersion, sizeof(sVersion)))
return FALSE;
// CRLF
if (!ExpectCRLF())
return FALSE;
return TRUE;
}
}
bUnknownMethod = TRUE;
return FALSE;
}
// GenerelHeader = Cache-Control | Connection | Date | Pragma | Transfer-Encoding |
// Upgrade | Via
BOOL CHttpParser::GenerelHeader()
{
INT i;
for (i = 0; i < NUMGENERELS; i++) {
if (strcmp(GenerelTable[i], sHeader) == 0) {
switch (i) {
case 1: {
//Connection
Expect(':');
Expect(' ');
Skip(13);
ExpectCRLF();
break;
}
default: {
Expect(':');
Expect(' ');
Skip(13);
ExpectCRLF();
}
}
return TRUE;
}
}
return FALSE;
}
// RequestHeader = Accept | Accept-Charset | Accept-Encoding | Accept-Language |
// Authorization | From | Host | If-Modified-Since | If-Match |
// If-None-Match | If-Range | If-Unmodified-Since | Max-Forwards |
// Proxy-Authorization | Range | Referer | User-Agent
BOOL CHttpParser::RequestHeader()
{
INT i;
for (i = 0; i < NUMREQUESTS; i++) {
if (strcmp(RequestTable[i], sHeader) == 0) {
switch (i) {
case 0: {
//Accept
Expect(':');
Expect(' ');
Skip(13);
ExpectCRLF();
break;
}
case 2: {
//Accept-Encoding
Expect(':');
Expect(' ');
Skip(13);
ExpectCRLF();
break;
}
case 3: {
//Accept-Language
Expect(':');
Expect(' ');
Skip(13);
ExpectCRLF();
break;
}
case 6: {
//Host
Expect(':');
Expect(' ');
Skip(13);
ExpectCRLF();
break;
}
case 16: {
//User-Agent
Expect(':');
Expect(' ');
Skip(13);
ExpectCRLF();
break;
}
default: {
Expect(':');
Expect(' ');
Skip(13);
ExpectCRLF();
return TRUE;
}
}
return TRUE;
}
}
return FALSE;
}
// EntityHeader = Allow | Content-Base | Content-Encoding | Content-Language |
// Content-Length | Content-Location | Content-MD5 |
// Content-Range | Content-Type | ETag | Expires |
// Last-Modified | extension-header
BOOL CHttpParser::EntityHeader()
{
INT i;
for (i = 0; i < NUMENTITIES; i++) {
if (strcmp(EntityTable[i], sHeader) == 0) {
switch (i) {
case 0:
default: {
//cout << "<Entity-Header>: #" << i << endl;
Expect(':');
Expect(' ');
Skip(13);
ExpectCRLF();
return TRUE;
}
}
return FALSE;
}
}
return FALSE;
}
// MessageBody = *OCTET
BOOL CHttpParser::MessageBody()
{
return FALSE;
}