/** * Digital Voice Modem - Host Software * GPLv2 Open Source. Use is subject to license terms. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * @package DVM / Host Software * */ // // Based on code from the CRUD project. (https://github.com/venediktov/CRUD) // Licensed under the BPL-1.0 License (https://opensource.org/license/bsl1-0-html) // /* * Copyright (c) 2003-2013 Christopher M. Kohlhoff * Copyright (C) 2023 by Bryan Biedenkapp N2PLL * * Permission is hereby granted, free of charge, to any person or organization * obtaining a copy of the software and accompanying documentation covered by * this license (the “Software”) to use, reproduce, display, distribute, execute, * and transmit the Software, and to prepare derivative works of the Software, and * to permit third-parties to whom the Software is furnished to do so, all subject * to the following: * * The copyright notices in the Software and this entire statement, including the * above license grant, this restriction and the following disclaimer, must be included * in all copies of the Software, in whole or in part, and all derivative works of the * Software, unless such copies or derivative works are solely in the form of * machine-executable object code generated by a source language processor. * * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR * PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR ANYONE * DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "Defines.h" #include "network/rest/http/HTTPRequestLexer.h" #include "network/rest/http/HTTPRequest.h" using namespace network::rest::http; #include // --------------------------------------------------------------------------- // Public Class Members // --------------------------------------------------------------------------- /// /// Initializes a new instance of the HTTPRequestLexer class. /// HTTPRequestLexer::HTTPRequestLexer() : m_state(METHOD_START) { /* stub */ } /// Reset to initial parser state. void HTTPRequestLexer::reset() { m_state = METHOD_START; } // --------------------------------------------------------------------------- // Private Class Members // --------------------------------------------------------------------------- /// /// Handle the next character of input. /// /// /// /// HTTPRequestLexer::ResultType HTTPRequestLexer::consume(HTTPRequest& req, char input) { switch (m_state) { /* ** HTTP Method */ case METHOD_START: if (!isChar(input) || isControl(input) || isSpecial(input)) return BAD; else { m_state = METHOD; req.method.push_back(input); return INDETERMINATE; } case METHOD: if (input == ' ') { m_state = URI; return INDETERMINATE; } else if (!isChar(input) || isControl(input) || isSpecial(input)) { return BAD; } else { req.method.push_back(input); return INDETERMINATE; } /* ** URI */ case URI: if (input == ' ') { m_state = HTTP_VERSION_H; return INDETERMINATE; } else if (isControl(input)) { return BAD; } else { req.uri.push_back(input); return INDETERMINATE; } /* ** HTTP/1.0 */ case HTTP_VERSION_H: if (input == 'H') { m_state = HTTP_VERSION_T_1; return INDETERMINATE; } else { return BAD; } case HTTP_VERSION_T_1: if (input == 'T') { m_state = HTTP_VERSION_T_2; return INDETERMINATE; } else { return BAD; } case HTTP_VERSION_T_2: if (input == 'T') { m_state = HTTP_VERSION_P; return INDETERMINATE; } else { return BAD; } case HTTP_VERSION_P: if (input == 'P') { m_state = HTTP_VERSION_SLASH; return INDETERMINATE; } else { return BAD; } case HTTP_VERSION_SLASH: if (input == '/') { req.httpVersionMajor = 0; req.httpVersionMinor = 0; m_state = HTTP_VERSION_MAJOR_START; return INDETERMINATE; } else { return BAD; } case HTTP_VERSION_MAJOR_START: if (isDigit(input)) { req.httpVersionMajor = req.httpVersionMajor * 10 + input - '0'; m_state = HTTP_VERSION_MAJOR; return INDETERMINATE; } else { return BAD; } case HTTP_VERSION_MAJOR: if (input == '.') { m_state = HTTP_VERSION_MINOR_START; return INDETERMINATE; } else if (isDigit(input)) { req.httpVersionMajor = req.httpVersionMajor * 10 + input - '0'; return INDETERMINATE; } else { return BAD; } case HTTP_VERSION_MINOR_START: if (isDigit(input)) { req.httpVersionMinor = req.httpVersionMinor * 10 + input - '0'; m_state = HTTP_VERSION_MINOR; return INDETERMINATE; } else { return BAD; } case HTTP_VERSION_MINOR: if (input == '\r') { m_state = EXPECTING_NEWLINE_1; return INDETERMINATE; } else if (isDigit(input)) { req.httpVersionMinor = req.httpVersionMinor * 10 + input - '0'; return INDETERMINATE; } else { return BAD; } case EXPECTING_NEWLINE_1: if (input == '\n') { m_state = HEADER_LINE_START; return INDETERMINATE; } else { return BAD; } /* ** Headers */ case HEADER_LINE_START: if (input == '\r') { m_state = EXPECTING_NEWLINE_3; return INDETERMINATE; } else if (!req.headers.empty() && (input == ' ' || input == '\t')) { m_state = HEADER_LWS; return INDETERMINATE; } else if (!isChar(input) || isControl(input) || isSpecial(input)) { return BAD; } else { req.headers.push_back(HTTPHeader()); req.headers.back().name.push_back(std::tolower(input)); m_state = HEADER_NAME; return INDETERMINATE; } case HEADER_LWS: if (input == '\r') { m_state = EXPECTING_NEWLINE_2; return INDETERMINATE; } else if (input == ' ' || input == '\t') { return INDETERMINATE; } else if (isControl(input)) { return BAD; } else { m_state = HEADER_VALUE; req.headers.back().value.push_back(input); return INDETERMINATE; } case HEADER_NAME: if (input == ':') { m_state = SPACE_BEFORE_HEADER_VALUE; return INDETERMINATE; } else if (!isChar(input) || isControl(input) || isSpecial(input)) { return BAD; } else { req.headers.back().name.push_back(std::tolower(input)); return INDETERMINATE; } case SPACE_BEFORE_HEADER_VALUE: if (input == ' ') { m_state = HEADER_VALUE; return INDETERMINATE; } else { return BAD; } case HEADER_VALUE: if (input == '\r') { m_state = EXPECTING_NEWLINE_2; return INDETERMINATE; } else if (isControl(input)) { return BAD; } else { req.headers.back().value.push_back(input); return INDETERMINATE; } case EXPECTING_NEWLINE_2: if (input == '\n') { m_state = HEADER_LINE_START; return INDETERMINATE; } else { return BAD; } case EXPECTING_NEWLINE_3: return (input == '\n') ? GOOD : BAD; default: return BAD; } } /// /// Check if a byte is an HTTP character. /// /// /// bool HTTPRequestLexer::isChar(int c) { return c >= 0 && c <= 127; } /// /// Check if a byte is an HTTP control character. /// /// /// bool HTTPRequestLexer::isControl(int c) { return (c >= 0 && c <= 31) || (c == 127); } /// /// Check if a byte is an HTTP special character. /// /// /// bool HTTPRequestLexer::isSpecial(int c) { switch (c) { case '(': case ')': case '<': case '>': case '@': case ',': case ';': case ':': case '\\': case '"': case '/': case '[': case ']': case '?': case '=': case '{': case '}': case ' ': case '\t': return true; default: return false; } } /// /// Check if a byte is an digit. /// /// /// bool HTTPRequestLexer::isDigit(int c) { return c >= '0' && c <= '9'; }