/* * 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 #include #include #include 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; CHAR ch; 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 << ": #" << i << endl; Expect(':'); Expect(' '); Skip(13); ExpectCRLF(); return TRUE; } } return FALSE; } } return FALSE; } // MessageBody = *OCTET BOOL CHttpParser::MessageBody() { return FALSE; }