Skip to content

Commit bfa8848

Browse files
author
pfeatherstone
committed
more detailed parsing
1 parent eb926e6 commit bfa8848

File tree

3 files changed

+163
-42
lines changed

3 files changed

+163
-42
lines changed

examples/unit_tests/message.cpp

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -87,12 +87,12 @@ TEST_SUITE("[MESSAGE]")
8787

8888
size_t blocksize{};
8989

90-
SUBCASE("") { blocksize = 1;}
91-
SUBCASE("") { blocksize = 10;}
92-
SUBCASE("") { blocksize = 99;}
93-
SUBCASE("") { blocksize = 128;}
94-
SUBCASE("") { blocksize = 1024;}
95-
90+
SUBCASE("blocksize == 1") { blocksize = 1;}
91+
SUBCASE("blocksize == 10") { blocksize = 10;}
92+
SUBCASE("blocksize == 99") { blocksize = 99;}
93+
SUBCASE("blocksize == 128") { blocksize = 128;}
94+
SUBCASE("blocksize == 1024") { blocksize = 1024;}
95+
9696
size_t nblocks = (buf.size() + blocksize - 1) / blocksize;
9797
std::string block;
9898

src/http.cpp

Lines changed: 154 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -923,35 +923,19 @@ namespace http
923923
//----------------------------------------------------------------------------------------------------------------
924924

925925
template<class Message>
926-
void parser<Message>::reset()
927-
{
928-
state = start_line;
929-
body_read = 0;
930-
}
931-
932-
void parse_start_line(request& req, const char* line, std::error_code& ec)
926+
parser<Message>::parser()
933927
{
934-
char method[8] = {0};
935-
req.uri.resize(strlen(line));
936-
int http_version_major = -1;
937-
req.http_version_minor = -1;
938-
sscanf(line, "%s %s HTTP/%i.%i", method, &req.uri[0], &http_version_major, &req.http_version_minor);
939-
req.verb = verb_enum(method);
940-
req.uri.resize(strlen(req.uri.c_str()));
941-
if (req.verb == UNKNOWN_VERB)
942-
ec = make_error_code(http_read_bad_method);
943-
else if (http_version_major != 1 || !(req.http_version_minor == 0 || req.http_version_minor == 1))
944-
ec = make_error_code(http_read_unsupported_http_version);
928+
reset();
945929
}
946930

947-
void parse_start_line(response& resp, const char* line, std::error_code& ec)
931+
template<class Message>
932+
void parser<Message>::reset()
948933
{
949-
resp.status = status_type::unknown;
950-
int http_version_major = -1;
951-
resp.http_version_minor = -1;
952-
sscanf(line, "HTTP/%i.%i %u", &http_version_major, &resp.http_version_minor, (unsigned int*)&resp.status);
953-
if (http_version_major != 1 || !(resp.http_version_minor == 0 || resp.http_version_minor == 1))
954-
ec = make_error_code(http_read_unsupported_http_version);
934+
if constexpr (std::is_same_v<Message, request>)
935+
state = method;
936+
else
937+
state = http_version;
938+
body_read = 0;
955939
}
956940

957941
template<class Message>
@@ -965,8 +949,149 @@ namespace http
965949
if (buf.size() > max_header_size)
966950
ec = make_error_code(http_read_header_line_too_big);
967951

968-
// Start line or header line
969-
else if (state == start_line || state == header_line)
952+
// Start line method (Request only)
953+
else if (state == method)
954+
{
955+
constexpr std::size_t max_method_size{16};
956+
957+
// Sufficient data
958+
if (buf.size() >= max_method_size)
959+
{
960+
std::string_view method_str(&buf[0], max_method_size);
961+
const auto end = method_str.find(" ");
962+
const verb_type method = verb_enum(method_str.substr(0, end));
963+
964+
// Found
965+
if (method != UNKNOWN_VERB)
966+
{
967+
if constexpr (std::is_same_v<Message, request>)
968+
msg.verb = method;
969+
970+
state = uri;
971+
buf.erase(begin(buf), begin(buf) + end + 1);
972+
}
973+
974+
// Not found
975+
else
976+
ec = make_error_code(http_read_bad_method);
977+
}
978+
979+
// Insufficient
980+
else
981+
break;
982+
}
983+
984+
// URI (Request only)
985+
else if (state == uri)
986+
{
987+
const auto end = buf.find(" ");
988+
989+
// Found
990+
if (end != std::string_view::npos)
991+
{
992+
if constexpr (std::is_same_v<Message, request>)
993+
msg.uri = buf.substr(0, end);
994+
995+
state = http_version;
996+
buf.erase(begin(buf), begin(buf) + end + 1);
997+
}
998+
999+
// Not found
1000+
else
1001+
break;
1002+
}
1003+
1004+
// HTTP version
1005+
else if (state == http_version)
1006+
{
1007+
constexpr std::size_t http_size{8};
1008+
1009+
// Sufficient data
1010+
if (buf.size() > 10)
1011+
{
1012+
buf[http_size] = '\0';
1013+
int major{-1};
1014+
int minor{-1};
1015+
const int ret = sscanf(&buf[0], "HTTP/%i.%i", &major, &minor);
1016+
1017+
// Found
1018+
if (ret == 2 && major == 1 && (minor == 0 || minor == 1))
1019+
{
1020+
if constexpr (std::is_same_v<Message, request>)
1021+
{
1022+
state = header_line;
1023+
buf.erase(begin(buf), begin(buf) + http_size + 2);
1024+
}
1025+
1026+
else
1027+
{
1028+
state = status_code;
1029+
buf.erase(begin(buf), begin(buf) + http_size + 1);
1030+
}
1031+
1032+
msg.http_version_minor = minor;
1033+
}
1034+
1035+
// Not found
1036+
else
1037+
ec = make_error_code(http_read_unsupported_http_version);
1038+
}
1039+
1040+
// Insufficient
1041+
else
1042+
break;
1043+
}
1044+
1045+
// Status code (response only)
1046+
else if (state == status_code)
1047+
{
1048+
const auto end = buf.find(" ");
1049+
1050+
// Sufficient
1051+
if (end != std::string::npos)
1052+
{
1053+
buf[end] = '\0';
1054+
int status{-1};
1055+
const int ret = sscanf(&buf[0], "%i", &status);
1056+
1057+
// Found
1058+
if (ret == 1 && status >= (int)status_type::continue_ && status <= 1000)
1059+
{
1060+
if constexpr (std::is_same_v<Message, response>)
1061+
msg.status = (status_type)status;
1062+
state = status_msg;
1063+
buf.erase(begin(buf), begin(buf) + end + 1);
1064+
}
1065+
1066+
// Not found
1067+
else
1068+
ec = make_error_code(http_read_bad_status);
1069+
}
1070+
1071+
// Insufficient
1072+
else
1073+
break;
1074+
}
1075+
1076+
// Status label
1077+
else if (state == status_msg)
1078+
{
1079+
const auto end = buf.find("\r\n");
1080+
1081+
// Found
1082+
if (end != std::string::npos)
1083+
{
1084+
state = header_line;
1085+
buf.erase(begin(buf), begin(buf) + end + 2);
1086+
}
1087+
1088+
// Not Found
1089+
else
1090+
ec = make_error_code(http_read_bad_status);
1091+
}
1092+
1093+
// Header line
1094+
else if (state == header_line)
9701095
{
9711096
// Find EOL
9721097
char* end = strstr(&buf[0], "\r\n");
@@ -980,15 +1105,8 @@ namespace http
9801105
{
9811106
*end = '\0';
9821107

983-
// Start line
984-
if (state == start_line)
985-
{
986-
state = header_line;
987-
parse_start_line(msg, &buf[0], ec);
988-
}
989-
9901108
// Header line
991-
else if (std::distance(&buf[0], end) > 0)
1109+
if (std::distance(&buf[0], end) > 0)
9921110
{
9931111
char* kend = strstr(&buf[0], ": ");
9941112

@@ -1197,6 +1315,7 @@ namespace http
11971315
case http_read_bad_method: return "HTTP request method bad";
11981316
case http_read_unsupported_http_version: return "HTTP version either bad or unsupported";
11991317
case http_read_header_kv_delimiter_not_found: return "Missing delimiter in HTTP header line";
1318+
case http_read_bad_status: return "HTTP status code bad";
12001319
case http_read_header_unsupported_field: return "HTTP header field unsupported";
12011320
case http_write_unsupported_http_version: return "HTTP message contains bad or unsupported http minor version";
12021321
case http_write_request_bad_verb: return "HTTP request contains bad verb";

src/http.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -561,10 +561,11 @@ namespace http
561561
{
562562
private:
563563
static constexpr std::size_t max_header_size = 8192;
564-
enum {start_line, header_line, body, done} state{start_line};
564+
enum {method, uri, http_version, status_code, status_msg, header_line, body, done} state;
565565
size_t body_read{0};
566566

567567
public:
568+
parser();
568569
void reset();
569570
bool parse(Message& req, std::string& buf, std::error_code& ec);
570571
};
@@ -591,6 +592,7 @@ namespace http
591592
http_read_header_line_too_big = 1,
592593
http_read_bad_method,
593594
http_read_unsupported_http_version,
595+
http_read_bad_status,
594596
http_read_header_kv_delimiter_not_found,
595597
http_read_header_unsupported_field,
596598
http_write_unsupported_http_version,

0 commit comments

Comments
 (0)