<commit_before>#include "http/parse_request.hpp"

#include <new>
#include <string>
#include <utility>

#include <cctype>

namespace http
{

namespace
{

struct wrapper
{
	std::istream& src;
	request& req;
};

int hex_to_int(int ch)
{
	if(ch >= '0' && ch <= '9')
		return ch - '0';
	else if(ch >= 'A' && ch <= 'F')
		return ch - 'A' + 10;
	else if(ch >= 'a' && ch <= 'f')
		return ch - 'a' + 10;
	else
		return -1;
}

int parse_pchar(std::istream& src)
{
	int ch = src.get();

	if(std::isalpha(ch) || std::isdigit(ch))
	{
	        return ch;
	}

	switch(ch)
	{
	case '-': case '.': case '_': case '~': case ':': case '@':
	case '!': case '$': case '&': case '\'': case '(': case ')':
	case '*': case '+': case ',': case ';': case '=':
		return ch;
	default:
		break;
	}

	if(ch != '%')
	{
		return -1;
	}

	ch = 0;

	int temp = hex_to_int(src.get());
	if(temp < 0)
	{
		return -1;
	}
	ch += temp * 16;
	temp = hex_to_int(src.get());
	if(temp < 0)
	{
		return -1;
	}
	return ch + temp;
}

bool is_tchar(int c)
{
	switch(c)
	{
	case '!': case '#': case '$': case '%': case '&': case '\'': case '*':
	case '+': case '-': case '.': case '^': case '_': case '`': case '|': case '~':
		return true;
	default:
		break;
	}
	return std::isalnum(c);
}

parse_status parse_token(std::istream& src, std::string& token)
{
	int ch;

	token.clear();

	while(is_tchar(ch = src.get()))
	{
		token.push_back(ch);
	}
	if(ch != EOF)
	{
		src.putback(ch);
	}

	return token.empty() ? PARSE_FAILURE : PARSE_SUCCESS;
}

parse_status parse_request_target_origin_form(std::istream& src, request_target_origin_form& of)
{
	int ch;

	ch = src.get();
	if(ch != '/')
	{
		return PARSE_FAILURE;
	}
	while(true)
	{
		of.absolute_path.push_back(ch);
		ch = src.get();
		if(ch == '?')
		{
			break;
		}
		else if(ch == ' ')
		{
			return PARSE_SUCCESS;
		}
		else if(ch == EOF)
		{
			return PARSE_FAILURE;
		}
		else if(ch != '/')
		{
			src.putback(ch);
			ch = parse_pchar(src);
			if(ch < 0)
			{
				return PARSE_FAILURE;
			}
		}
	}

	of.query = std::string{};

	while(true)
	{
		ch = src.get();
		if(ch == '/' || ch == '?')
		{
			of.query->push_back(ch);
		}
		else
		{
			ch = parse_pchar(src);
			if(ch < 0)
			{
				return PARSE_FAILURE;
			}
			of.query->push_back(ch);
		}
	}

	return PARSE_SUCCESS;<commit_msg>Fixed query string parsing bug<commit_after>#include "http/parse_request.hpp"

#include <new>
#include <string>
#include <utility>

#include <cctype>

namespace http
{

namespace
{

struct wrapper
{
	std::istream& src;
	request& req;
};

int hex_to_int(int ch)
{
	if(ch >= '0' && ch <= '9')
		return ch - '0';
	else if(ch >= 'A' && ch <= 'F')
		return ch - 'A' + 10;
	else if(ch >= 'a' && ch <= 'f')
		return ch - 'a' + 10;
	else
		return -1;
}

int parse_pchar(std::istream& src)
{
	int ch = src.get();

	if(std::isalpha(ch) || std::isdigit(ch))
	{
	        return ch;
	}

	switch(ch)
	{
	case '-': case '.': case '_': case '~': case ':': case '@':
	case '!': case '$': case '&': case '\'': case '(': case ')':
	case '*': case '+': case ',': case ';': case '=':
		return ch;
	default:
		break;
	}

	if(ch != '%')
	{
		return -1;
	}

	ch = 0;

	int temp = hex_to_int(src.get());
	if(temp < 0)
	{
		return -1;
	}
	ch += temp * 16;
	temp = hex_to_int(src.get());
	if(temp < 0)
	{
		return -1;
	}
	return ch + temp;
}

bool is_tchar(int c)
{
	switch(c)
	{
	case '!': case '#': case '$': case '%': case '&': case '\'': case '*':
	case '+': case '-': case '.': case '^': case '_': case '`': case '|': case '~':
		return true;
	default:
		break;
	}
	return std::isalnum(c);
}

parse_status parse_token(std::istream& src, std::string& token)
{
	int ch;

	token.clear();

	while(is_tchar(ch = src.get()))
	{
		token.push_back(ch);
	}
	if(ch != EOF)
	{
		src.putback(ch);
	}

	return token.empty() ? PARSE_FAILURE : PARSE_SUCCESS;
}

parse_status parse_request_target_origin_form(std::istream& src, request_target_origin_form& of)
{
	int ch;

	ch = src.get();
	if(ch != '/')
	{
		return PARSE_FAILURE;
	}
	while(true)
	{
		of.absolute_path.push_back(ch);
		ch = src.get();
		if(ch == '?')
		{
			break;
		}
		else if(ch == ' ')
		{
			return PARSE_SUCCESS;
		}
		else if(ch == EOF)
		{
			return PARSE_FAILURE;
		}
		else if(ch != '/')
		{
			src.putback(ch);
			ch = parse_pchar(src);
			if(ch < 0)
			{
				return PARSE_FAILURE;
			}
		}
	}

	of.query = std::string{};

	while(true)
	{
		ch = src.get();
		if(ch == '/' || ch == '?')
		{
			of.query->push_back(ch);
		}
		else if(ch == ' ')
		{
			break;
		}
		else
		{
			src.putback(ch);
			ch = parse_pchar(src);
			if(ch < 0)
			{
				return PARSE_FAILURE;
			}
			of.query->push_back(ch);
		}
	}

	return PARSE_SUCCESS;
{'fraction_non_alphanumeric': 0.2184278350515464, 'fraction_numerical': 0.007731958762886598, 'mean_word_length': 2.884070058381985, 'pattern_counts': {'":': 0, '<': 25, '<?xml version=': 0, '>': 21, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 8, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6574082', 'n_tokens_mistral': 2261, 'n_tokens_neox': 2063, 'n_words': 413}