blob: 3c18e0f6f8aa0874fbb3a287561ef3fc402ab0c7 [file] [log] [blame]
Austin Schuh812d0d12021-11-04 20:16:48 -07001// Copyright (c) FIRST and other WPILib contributors.
2// Open Source Software; you can modify and/or share it under the terms of
3// the WPILib BSD license file in the root directory of this project.
Brian Silverman8fce7482020-01-05 13:18:21 -08004
5#include "wpi/HttpParser.h"
6
7using namespace wpi;
8
9uint32_t HttpParser::GetParserVersion() {
10 return static_cast<uint32_t>(http_parser_version());
11}
12
13HttpParser::HttpParser(Type type) {
14 http_parser_init(&m_parser,
15 static_cast<http_parser_type>(static_cast<int>(type)));
16 m_parser.data = this;
17
18 http_parser_settings_init(&m_settings);
19
20 // Unlike the underlying http_parser library, we don't perform callbacks
21 // (other than body) with partial data; instead we buffer and call the user
22 // callback only when the data is complete.
23
24 // on_message_begin: initialize our state, call user callback
25 m_settings.on_message_begin = [](http_parser* p) -> int {
26 auto& self = *static_cast<HttpParser*>(p->data);
27 self.m_urlBuf.clear();
28 self.m_state = kStart;
29 self.messageBegin();
30 return self.m_aborted;
31 };
32
33 // on_url: collect into buffer
34 m_settings.on_url = [](http_parser* p, const char* at, size_t length) -> int {
35 auto& self = *static_cast<HttpParser*>(p->data);
36 // append to buffer
Austin Schuh812d0d12021-11-04 20:16:48 -070037 if ((self.m_urlBuf.size() + length) > self.m_maxLength) {
38 return 1;
39 }
40 self.m_urlBuf += std::string_view{at, length};
Brian Silverman8fce7482020-01-05 13:18:21 -080041 self.m_state = kUrl;
42 return 0;
43 };
44
45 // on_status: collect into buffer, call user URL callback
46 m_settings.on_status = [](http_parser* p, const char* at,
47 size_t length) -> int {
48 auto& self = *static_cast<HttpParser*>(p->data);
49 // use valueBuf for the status
Austin Schuh812d0d12021-11-04 20:16:48 -070050 if ((self.m_valueBuf.size() + length) > self.m_maxLength) {
51 return 1;
52 }
53 self.m_valueBuf += std::string_view{at, length};
Brian Silverman8fce7482020-01-05 13:18:21 -080054 self.m_state = kStatus;
55 return 0;
56 };
57
58 // on_header_field: collect into buffer, call user header/status callback
59 m_settings.on_header_field = [](http_parser* p, const char* at,
60 size_t length) -> int {
61 auto& self = *static_cast<HttpParser*>(p->data);
62
63 // once we're in header, we know the URL is complete
64 if (self.m_state == kUrl) {
65 self.url(self.m_urlBuf);
Austin Schuh812d0d12021-11-04 20:16:48 -070066 if (self.m_aborted) {
67 return 1;
68 }
Brian Silverman8fce7482020-01-05 13:18:21 -080069 }
70
71 // once we're in header, we know the status is complete
72 if (self.m_state == kStatus) {
73 self.status(self.m_valueBuf);
Austin Schuh812d0d12021-11-04 20:16:48 -070074 if (self.m_aborted) {
75 return 1;
76 }
Brian Silverman8fce7482020-01-05 13:18:21 -080077 }
78
79 // if we previously were in value state, that means we finished a header
80 if (self.m_state == kValue) {
81 self.header(self.m_fieldBuf, self.m_valueBuf);
Austin Schuh812d0d12021-11-04 20:16:48 -070082 if (self.m_aborted) {
83 return 1;
84 }
Brian Silverman8fce7482020-01-05 13:18:21 -080085 }
86
87 // clear field and value when we enter this state
88 if (self.m_state != kField) {
89 self.m_state = kField;
90 self.m_fieldBuf.clear();
91 self.m_valueBuf.clear();
92 }
93
94 // append data to field buffer
Austin Schuh812d0d12021-11-04 20:16:48 -070095 if ((self.m_fieldBuf.size() + length) > self.m_maxLength) {
96 return 1;
97 }
98 self.m_fieldBuf += std::string_view{at, length};
Brian Silverman8fce7482020-01-05 13:18:21 -080099 return 0;
100 };
101
102 // on_header_field: collect into buffer
103 m_settings.on_header_value = [](http_parser* p, const char* at,
104 size_t length) -> int {
105 auto& self = *static_cast<HttpParser*>(p->data);
106
107 // if we weren't previously in value state, clear the buffer
108 if (self.m_state != kValue) {
109 self.m_state = kValue;
110 self.m_valueBuf.clear();
111 }
112
113 // append data to value buffer
Austin Schuh812d0d12021-11-04 20:16:48 -0700114 if ((self.m_valueBuf.size() + length) > self.m_maxLength) {
115 return 1;
116 }
117 self.m_valueBuf += std::string_view{at, length};
Brian Silverman8fce7482020-01-05 13:18:21 -0800118 return 0;
119 };
120
121 // on_headers_complete: call user status/header/complete callback
122 m_settings.on_headers_complete = [](http_parser* p) -> int {
123 auto& self = *static_cast<HttpParser*>(p->data);
124
125 // if we previously were in url state, that means we finished the url
126 if (self.m_state == kUrl) {
127 self.url(self.m_urlBuf);
Austin Schuh812d0d12021-11-04 20:16:48 -0700128 if (self.m_aborted) {
129 return 1;
130 }
Brian Silverman8fce7482020-01-05 13:18:21 -0800131 }
132
133 // if we previously were in status state, that means we finished the status
134 if (self.m_state == kStatus) {
135 self.status(self.m_valueBuf);
Austin Schuh812d0d12021-11-04 20:16:48 -0700136 if (self.m_aborted) {
137 return 1;
138 }
Brian Silverman8fce7482020-01-05 13:18:21 -0800139 }
140
141 // if we previously were in value state, that means we finished a header
142 if (self.m_state == kValue) {
143 self.header(self.m_fieldBuf, self.m_valueBuf);
Austin Schuh812d0d12021-11-04 20:16:48 -0700144 if (self.m_aborted) {
145 return 1;
146 }
Brian Silverman8fce7482020-01-05 13:18:21 -0800147 }
148
149 self.headersComplete(self.ShouldKeepAlive());
150 return self.m_aborted;
151 };
152
153 // on_body: call user callback
154 m_settings.on_body = [](http_parser* p, const char* at,
155 size_t length) -> int {
156 auto& self = *static_cast<HttpParser*>(p->data);
Austin Schuh812d0d12021-11-04 20:16:48 -0700157 self.body(std::string_view{at, length}, self.IsBodyFinal());
Brian Silverman8fce7482020-01-05 13:18:21 -0800158 return self.m_aborted;
159 };
160
161 // on_message_complete: call user callback
162 m_settings.on_message_complete = [](http_parser* p) -> int {
163 auto& self = *static_cast<HttpParser*>(p->data);
164 self.messageComplete(self.ShouldKeepAlive());
165 return self.m_aborted;
166 };
167
168 // on_chunk_header: call user callback
169 m_settings.on_chunk_header = [](http_parser* p) -> int {
170 auto& self = *static_cast<HttpParser*>(p->data);
171 self.chunkHeader(p->content_length);
172 return self.m_aborted;
173 };
174
175 // on_chunk_complete: call user callback
176 m_settings.on_chunk_complete = [](http_parser* p) -> int {
177 auto& self = *static_cast<HttpParser*>(p->data);
178 self.chunkComplete();
179 return self.m_aborted;
180 };
181}
182
183void HttpParser::Reset(Type type) {
184 http_parser_init(&m_parser,
185 static_cast<http_parser_type>(static_cast<int>(type)));
186 m_parser.data = this;
187 m_maxLength = 1024;
188 m_state = kStart;
189 m_urlBuf.clear();
190 m_fieldBuf.clear();
191 m_valueBuf.clear();
192 m_aborted = false;
193}