You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
dvmhost/network/rest/http/HTTPRequestLexer.cpp

362 lines
9.6 KiB

/**
* 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 rest::server;
#include <cctype>
// ---------------------------------------------------------------------------
// Public Class Members
// ---------------------------------------------------------------------------
/// <summary>
/// Initializes a new instance of the HTTPRequestLexer class.
/// </summary>
HTTPRequestLexer::HTTPRequestLexer() :
m_state(METHOD_START)
{
/* stub */
}
/// <summary>Reset to initial parser state.</summary>
void HTTPRequestLexer::reset()
{
m_state = METHOD_START;
}
// ---------------------------------------------------------------------------
// Private Class Members
// ---------------------------------------------------------------------------
/// <summary>
/// Handle the next character of input.
/// </summary>
/// <param name="req"></param>
/// <param name="input"></param>
/// <returns></returns>
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;
}
}
/// <summary>
/// Check if a byte is an HTTP character.
/// </summary>
/// <param name="c"></param>
/// <returns></returns>
bool HTTPRequestLexer::isChar(int c)
{
return c >= 0 && c <= 127;
}
/// <summary>
/// Check if a byte is an HTTP control character.
/// </summary>
/// <param name="c"></param>
/// <returns></returns>
bool HTTPRequestLexer::isControl(int c)
{
return (c >= 0 && c <= 31) || (c == 127);
}
/// <summary>
/// Check if a byte is an HTTP special character.
/// </summary>
/// <param name="c"></param>
/// <returns></returns>
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;
}
}
/// <summary>
/// Check if a byte is an digit.
/// </summary>
/// <param name="c"></param>
/// <returns></returns>
bool HTTPRequestLexer::isDigit(int c)
{
return c >= '0' && c <= '9';
}

Powered by TurnKey Linux.