blob: 4560b38508a8c42aebd4d42557744ef9e0d98430 [file] [log] [blame]
Brian Silverman41cdd3e2019-01-19 19:48:58 -08001/*----------------------------------------------------------------------------*/
2/* Copyright (c) 2018 FIRST. All Rights Reserved. */
3/* Open Source Software - may be modified and shared by FRC teams. The code */
4/* must be accompanied by the FIRST BSD license file in the root directory of */
5/* the project. */
6/*----------------------------------------------------------------------------*/
7
8#include "wpi/HttpParser.h"
9
10using namespace wpi;
11
12uint32_t HttpParser::GetParserVersion() {
13 return static_cast<uint32_t>(http_parser_version());
14}
15
16HttpParser::HttpParser(Type type) {
17 http_parser_init(&m_parser,
18 static_cast<http_parser_type>(static_cast<int>(type)));
19 m_parser.data = this;
20
21 http_parser_settings_init(&m_settings);
22
23 // Unlike the underlying http_parser library, we don't perform callbacks
24 // (other than body) with partial data; instead we buffer and call the user
25 // callback only when the data is complete.
26
27 // on_message_begin: initialize our state, call user callback
28 m_settings.on_message_begin = [](http_parser* p) -> int {
29 auto& self = *static_cast<HttpParser*>(p->data);
30 self.m_urlBuf.clear();
31 self.m_state = kStart;
32 self.messageBegin();
33 return self.m_aborted;
34 };
35
36 // on_url: collect into buffer
37 m_settings.on_url = [](http_parser* p, const char* at, size_t length) -> int {
38 auto& self = *static_cast<HttpParser*>(p->data);
39 // append to buffer
40 if ((self.m_urlBuf.size() + length) > self.m_maxLength) return 1;
41 self.m_urlBuf += StringRef{at, length};
42 self.m_state = kUrl;
43 return 0;
44 };
45
46 // on_status: collect into buffer, call user URL callback
47 m_settings.on_status = [](http_parser* p, const char* at,
48 size_t length) -> int {
49 auto& self = *static_cast<HttpParser*>(p->data);
50 // use valueBuf for the status
51 if ((self.m_valueBuf.size() + length) > self.m_maxLength) return 1;
52 self.m_valueBuf += StringRef{at, length};
53 self.m_state = kStatus;
54 return 0;
55 };
56
57 // on_header_field: collect into buffer, call user header/status callback
58 m_settings.on_header_field = [](http_parser* p, const char* at,
59 size_t length) -> int {
60 auto& self = *static_cast<HttpParser*>(p->data);
61
62 // once we're in header, we know the URL is complete
63 if (self.m_state == kUrl) {
64 self.url(self.m_urlBuf);
65 if (self.m_aborted) return 1;
66 }
67
68 // once we're in header, we know the status is complete
69 if (self.m_state == kStatus) {
70 self.status(self.m_valueBuf);
71 if (self.m_aborted) return 1;
72 }
73
74 // if we previously were in value state, that means we finished a header
75 if (self.m_state == kValue) {
76 self.header(self.m_fieldBuf, self.m_valueBuf);
77 if (self.m_aborted) return 1;
78 }
79
80 // clear field and value when we enter this state
81 if (self.m_state != kField) {
82 self.m_state = kField;
83 self.m_fieldBuf.clear();
84 self.m_valueBuf.clear();
85 }
86
87 // append data to field buffer
88 if ((self.m_fieldBuf.size() + length) > self.m_maxLength) return 1;
89 self.m_fieldBuf += StringRef{at, length};
90 return 0;
91 };
92
93 // on_header_field: collect into buffer
94 m_settings.on_header_value = [](http_parser* p, const char* at,
95 size_t length) -> int {
96 auto& self = *static_cast<HttpParser*>(p->data);
97
98 // if we weren't previously in value state, clear the buffer
99 if (self.m_state != kValue) {
100 self.m_state = kValue;
101 self.m_valueBuf.clear();
102 }
103
104 // append data to value buffer
105 if ((self.m_valueBuf.size() + length) > self.m_maxLength) return 1;
106 self.m_valueBuf += StringRef{at, length};
107 return 0;
108 };
109
110 // on_headers_complete: call user status/header/complete callback
111 m_settings.on_headers_complete = [](http_parser* p) -> int {
112 auto& self = *static_cast<HttpParser*>(p->data);
113
114 // if we previously were in url state, that means we finished the url
115 if (self.m_state == kUrl) {
116 self.url(self.m_urlBuf);
117 if (self.m_aborted) return 1;
118 }
119
120 // if we previously were in status state, that means we finished the status
121 if (self.m_state == kStatus) {
122 self.status(self.m_valueBuf);
123 if (self.m_aborted) return 1;
124 }
125
126 // if we previously were in value state, that means we finished a header
127 if (self.m_state == kValue) {
128 self.header(self.m_fieldBuf, self.m_valueBuf);
129 if (self.m_aborted) return 1;
130 }
131
132 self.headersComplete(self.ShouldKeepAlive());
133 return self.m_aborted;
134 };
135
136 // on_body: call user callback
137 m_settings.on_body = [](http_parser* p, const char* at,
138 size_t length) -> int {
139 auto& self = *static_cast<HttpParser*>(p->data);
140 self.body(StringRef{at, length}, self.IsBodyFinal());
141 return self.m_aborted;
142 };
143
144 // on_message_complete: call user callback
145 m_settings.on_message_complete = [](http_parser* p) -> int {
146 auto& self = *static_cast<HttpParser*>(p->data);
147 self.messageComplete(self.ShouldKeepAlive());
148 return self.m_aborted;
149 };
150
151 // on_chunk_header: call user callback
152 m_settings.on_chunk_header = [](http_parser* p) -> int {
153 auto& self = *static_cast<HttpParser*>(p->data);
154 self.chunkHeader(p->content_length);
155 return self.m_aborted;
156 };
157
158 // on_chunk_complete: call user callback
159 m_settings.on_chunk_complete = [](http_parser* p) -> int {
160 auto& self = *static_cast<HttpParser*>(p->data);
161 self.chunkComplete();
162 return self.m_aborted;
163 };
164}
165
166void HttpParser::Reset(Type type) {
167 http_parser_init(&m_parser,
168 static_cast<http_parser_type>(static_cast<int>(type)));
169 m_parser.data = this;
170 m_maxLength = 1024;
171 m_state = kStart;
172 m_urlBuf.clear();
173 m_fieldBuf.clear();
174 m_valueBuf.clear();
175 m_aborted = false;
176}