Line data Source code
1 : #include <sstream>
2 : #include "response_factory.hpp"
3 : #include "../../util/logger.hpp"
4 : #include "../common/http_response.hpp"
5 :
6 : namespace thinger::http{
7 :
8 142400 : boost::tribool response_factory::consume(char input, bool head_request) {
9 : // keep control of headers size
10 142400 : if(state_<=expecting_newline_3){
11 94568 : headers_size_++;
12 94568 : if(headers_size_>MAX_HEADERS_SIZE) return false;
13 : }
14 : // parse response
15 142400 : switch (state_) {
16 926 : case http_version_h:
17 926 : if (input == 'H') {
18 926 : state_ = http_version_t_1;
19 926 : return boost::indeterminate;
20 : }
21 0 : return false;
22 926 : case http_version_t_1:
23 926 : if (input == 'T') {
24 926 : state_ = http_version_t_2;
25 926 : return boost::indeterminate;
26 : }
27 0 : return false;
28 926 : case http_version_t_2:
29 926 : if (input == 'T') {
30 926 : state_ = http_version_p;
31 926 : return boost::indeterminate;
32 : }
33 0 : return false;
34 926 : case http_version_p:
35 926 : if (input == 'P') {
36 926 : state_ = http_version_slash;
37 926 : return boost::indeterminate;
38 : }
39 0 : return false;
40 926 : case http_version_slash:
41 926 : if (input == '/') {
42 926 : state_ = http_version_major_start;
43 926 : return boost::indeterminate;
44 : }
45 0 : return false;
46 926 : case http_version_major_start:
47 926 : if (is_digit(input)) {
48 926 : tempInt_ = input - '0';
49 926 : state_ = http_version_major;
50 926 : return boost::indeterminate;
51 : }
52 0 : return false;
53 926 : case http_version_major:
54 926 : if (input == '.') {
55 926 : if(!resp) resp = std::make_shared<http_response>();
56 926 : on_http_major_version(tempInt_);
57 926 : state_ = http_version_minor_start;
58 926 : return boost::indeterminate;
59 : }
60 0 : else if (is_digit(input)) {
61 0 : tempInt_ = tempInt_ * 10 + input - '0';
62 0 : return boost::indeterminate;
63 : }
64 0 : return false;
65 926 : case http_version_minor_start:
66 926 : if (is_digit(input)) {
67 926 : tempInt_ = input - '0';
68 926 : state_ = http_version_minor;
69 926 : return boost::indeterminate;
70 : }
71 0 : return false;
72 926 : case http_version_minor:
73 926 : if (is_digit(input)) {
74 0 : tempInt_ = tempInt_ * 10 + input - '0';
75 0 : return boost::indeterminate;
76 : }
77 926 : else if(input == ' '){
78 926 : on_http_minor_version(tempInt_);
79 926 : tempInt_ = 0;
80 926 : state_ = status_code;
81 926 : return boost::indeterminate;
82 : }
83 0 : return false;
84 3704 : case status_code:
85 3704 : if (is_digit(input)) {
86 2778 : tempInt_ = tempInt_ * 10 + input - '0';
87 2778 : return boost::indeterminate;
88 926 : }else if(input == ' '){
89 926 : on_http_status_code(tempInt_);
90 926 : state_ = reason_phrase;
91 926 : return boost::indeterminate;
92 : }
93 0 : return false;
94 5541 : case reason_phrase:
95 5541 : if(input == '\r'){
96 926 : on_http_reason_phrase(tempString1_);
97 926 : state_ = expecting_newline_1;
98 926 : return boost::indeterminate;
99 4615 : }else if (is_char(input) || input == ' '){
100 4615 : tempString1_.push_back(input);
101 4615 : return boost::indeterminate;
102 : }
103 0 : return false;
104 926 : case expecting_newline_1:
105 926 : if (input == '\n') {
106 926 : state_ = header_line_start;
107 926 : return boost::indeterminate;
108 : }
109 0 : return false;
110 3776 : case header_line_start:
111 3776 : if (input == '\r') {
112 926 : state_ = expecting_newline_3;
113 926 : return boost::indeterminate;
114 : }
115 2850 : else if (!empty_headers() && (input == ' ' || input == '\t')) {
116 0 : state_ = header_lws;
117 0 : return boost::indeterminate;
118 : }
119 2850 : else if (!is_char(input) || is_ctl(input) || is_tspecial(input)) {
120 0 : return false;
121 : }
122 : else {
123 2850 : tempString1_.clear();
124 2850 : tempString1_.push_back(input);
125 2850 : state_ = header_name;
126 2850 : return boost::indeterminate;
127 : }
128 0 : case header_lws:
129 0 : if (input == '\r') {
130 0 : state_ = expecting_newline_2;
131 0 : return boost::indeterminate;
132 : }
133 0 : else if (input == ' ' || input == '\t') {
134 0 : return boost::indeterminate;
135 : }
136 0 : else if (is_ctl(input)) {
137 0 : return false;
138 : }
139 : else {
140 0 : state_ = header_value;
141 0 : tempString1_.push_back(input);
142 0 : return boost::indeterminate;
143 : }
144 34290 : case header_name:
145 34290 : if (input == ':') {
146 2850 : state_ = space_before_header_value;
147 2850 : return boost::indeterminate;
148 : }
149 31440 : else if (!is_char(input) || is_ctl(input) || is_tspecial(input)) {
150 0 : return false;
151 : }
152 : else {
153 31440 : tempString1_.push_back(input);
154 31440 : return boost::indeterminate;
155 : }
156 2850 : case space_before_header_value:
157 2850 : if (input == ' ') {
158 2850 : tempString2_.clear();
159 2850 : state_ = header_value;
160 2850 : return boost::indeterminate;
161 : }
162 0 : return false;
163 31371 : case header_value:
164 31371 : if (input == '\r') {
165 2850 : on_http_header(tempString1_, tempString2_);
166 2850 : state_ = expecting_newline_2;
167 2850 : return boost::indeterminate;
168 : }
169 28521 : else if (is_ctl(input)) {
170 0 : return false;
171 : }
172 : else {
173 28521 : tempString2_.push_back(input);
174 28521 : return boost::indeterminate;
175 : }
176 2850 : case expecting_newline_2:
177 2850 : if (input == '\n') {
178 2850 : state_ = header_line_start;
179 2850 : return boost::indeterminate;
180 : }
181 0 : return false;
182 926 : case expecting_newline_3:
183 926 : if (input == '\n'){
184 926 : if(content_==lenght_delimited && get_content_length()>0){
185 : // if it is a head request, we are not expecting a body
186 777 : if(head_request) return true;
187 :
188 : // set state to length delimited content
189 777 : state_ = lenght_delimited_content;
190 : // save content size to read in tempInt_
191 777 : tempInt_ = get_content_length();
192 : // verify we are abe to read the content size
193 777 : if(on_length_delimited_content(tempInt_)){
194 777 : return boost::indeterminate;
195 : }else{
196 0 : return false;
197 : }
198 149 : }else if(content_==chunked){
199 : // if it is a head request, we are not expecting a body
200 8 : if(head_request) return true;
201 :
202 8 : tempInt_ = 0;
203 8 : lastChunk_ = false;
204 8 : state_ = chunked_content_size;
205 8 : if(on_chunked_){
206 0 : on_chunked_(0, std::string());
207 : }
208 8 : return boost::indeterminate;
209 : }
210 141 : return true;
211 : }
212 0 : return false;
213 47650 : case lenght_delimited_content:
214 : // save content
215 47650 : on_content_data(input);
216 : // check if we are done reading the data
217 47650 : if(--tempInt_>0){
218 46875 : return boost::indeterminate;
219 : }else{
220 775 : return true;
221 : }
222 40 : case chunked_content_size:
223 : // read chunked content size
224 40 : if(is_hexadecimal(input)){
225 22 : tempInt_ = tempInt_ * 16 + get_hex_value(input);
226 22 : return boost::indeterminate;
227 18 : }else if(input == '\r'){
228 18 : if(tempInt_==0) lastChunk_ = true;
229 18 : state_ = chunked_content_size_expecting_n;
230 18 : return boost::indeterminate;
231 : }
232 0 : return false;
233 18 : case chunked_content_size_expecting_n:
234 18 : if(input=='\n'){
235 : // check we are able to read the expected size
236 18 : if(on_chunk_read(tempInt_)){
237 18 : state_ = chunked_content;
238 18 : return boost::indeterminate;
239 : }else{
240 0 : return false;
241 : }
242 : }
243 0 : return false;
244 106 : case chunked_content:
245 106 : if(tempInt_>0){
246 88 : on_content_data(input);
247 88 : tempInt_--;
248 88 : return boost::indeterminate;
249 18 : }else if(input=='\r'){
250 18 : state_ = chunked_content_expecting_n;
251 18 : return boost::indeterminate;
252 : }
253 0 : return false;
254 18 : case chunked_content_expecting_n:
255 18 : if(input=='\n'){
256 18 : if(lastChunk_){
257 8 : if(on_chunked_) on_chunked_(2, std::string());
258 8 : return true;
259 : }else{
260 10 : tempInt_ = 0;
261 10 : state_ = chunked_content_size;
262 :
263 : // Call streaming callback if set
264 10 : if(on_streaming_ && !resp->get_content().empty()){
265 0 : streaming_downloaded_ += resp->get_content().size();
266 : // For chunked, total is 0 (unknown)
267 0 : if(!on_streaming_(resp->get_content(), streaming_downloaded_, 0)){
268 0 : streaming_aborted_ = true;
269 0 : return false;
270 : }
271 0 : resp->get_content().clear();
272 : }
273 : // Legacy chunked callback
274 10 : else if(on_chunked_){
275 0 : on_chunked_(1, resp->get_content());
276 0 : resp->get_content().clear();
277 : }
278 10 : return boost::indeterminate;
279 : }
280 : }
281 0 : return false;
282 0 : default:
283 0 : return false;
284 : }
285 : }
286 :
287 38905 : bool response_factory::is_char(char c) {
288 38905 : return c >= 0 && c <= 127;
289 : }
290 :
291 62811 : bool response_factory::is_ctl(char c) {
292 62811 : return (c >= 0 && c <= 31) || (c == 127);
293 : }
294 :
295 34290 : bool response_factory::is_tspecial(char c) {
296 34290 : switch (c) {
297 0 : case '(':
298 : case ')':
299 : case '<':
300 : case '>':
301 : case '@':
302 : case ',':
303 : case ';':
304 : case ':':
305 : case '\\':
306 : case '"':
307 : case '/':
308 : case '[':
309 : case ']':
310 : case '?':
311 : case '=':
312 : case '{':
313 : case '}':
314 : case ' ':
315 : case '\t':
316 0 : return true;
317 34290 : default:
318 34290 : return false;
319 : }
320 : }
321 :
322 6482 : bool response_factory::is_digit(char c) {
323 6482 : return c >= '0' && c <= '9';
324 : }
325 :
326 40 : bool response_factory::is_hexadecimal(char c){
327 40 : return (c >= '0' && c <= '9') || (c >= 'a' && c<='f') || (c>='A' && c <= 'F');
328 : }
329 :
330 22 : uint8_t response_factory::get_hex_value(char c)
331 : {
332 22 : if(c <= '9'){
333 22 : return c - '0';
334 0 : }else if(c<='F'){
335 0 : return c - 55;
336 0 : }else if(c<='f'){
337 0 : return c - 87;
338 : }
339 0 : return 0;
340 : }
341 :
342 926 : std::shared_ptr<http_response> response_factory::consume_response() {
343 926 : std::shared_ptr<http_response> request(resp);
344 926 : reset();
345 926 : request->log("HTTP CLIENT RESPONSE", 0);
346 926 : return request;
347 0 : }
348 :
349 2015 : void response_factory::reset() {
350 2015 : resp.reset();
351 2015 : content_ = none;
352 2015 : state_ = http_version_h;
353 2015 : tempString1_.clear();
354 2015 : tempString2_.clear();
355 2015 : headers_size_ = 0;
356 2015 : on_chunked_ = nullptr;
357 2015 : on_streaming_ = nullptr;
358 2015 : streaming_downloaded_ = 0;
359 2015 : streaming_aborted_ = false;
360 2015 : }
361 :
362 926 : void response_factory::on_http_status_code(unsigned short status_code){
363 926 : resp->set_status(status_code);
364 926 : }
365 :
366 926 : void response_factory::on_http_reason_phrase(const std::string& reason){
367 926 : resp->set_reason_phrase(reason);
368 926 : }
369 :
370 926 : void response_factory::on_http_major_version(uint8_t major)
371 : {
372 926 : resp->set_http_version_major(major);
373 926 : }
374 :
375 926 : void response_factory::on_http_minor_version(uint16_t minor){
376 926 : resp->set_http_version_minor(minor);
377 926 : }
378 :
379 18 : bool response_factory::on_chunk_read(size_t size){
380 18 : if(size==0) return true;
381 : // In streaming mode the content is handed off chunk-by-chunk and never
382 : // accumulated, so the max_content_size_ buffer limit does not apply.
383 10 : if(on_streaming_) return true;
384 10 : size_t expected_size = get_content_read() + size;
385 10 : if(expected_size>max_content_size_){
386 0 : LOG_ERROR("the response size exceeds the maximum allowed file size: %zu (%zu max)", size, max_content_size_);
387 0 : return false;
388 : }
389 : // upgrade buffer size
390 10 : resp->get_content().reserve(expected_size);
391 10 : return true;
392 : }
393 :
394 777 : bool response_factory::on_length_delimited_content(size_t size){
395 : // In streaming mode the content is handed off chunk-by-chunk and never
396 : // accumulated, so the max_content_size_ buffer limit does not apply.
397 777 : if(on_streaming_) return true;
398 775 : if(size>max_content_size_){
399 0 : LOG_ERROR("the response size exceeds the maximum allowed file size: %zu (%zu max)", size, max_content_size_);
400 0 : return false;
401 : }
402 775 : resp->get_content().reserve(size);
403 775 : return true;
404 : }
405 :
406 2850 : void response_factory::on_http_header(const std::string& name, const std::string& value){
407 2850 : if(boost::iequals(name, http::header::transfer_encoding) && boost::iequals(value, "chunked")){
408 8 : content_ = chunked;
409 2842 : }else if(boost::iequals(name, http::header::content_length)){
410 811 : content_ = lenght_delimited;
411 : }
412 2850 : resp->process_header(name, value);
413 2850 : }
414 :
415 47738 : void response_factory::on_content_data(char content){
416 47738 : resp->get_content().push_back(content);
417 47738 : }
418 :
419 1590 : size_t response_factory::get_content_length(){
420 1590 : return resp->get_content_length();
421 : }
422 :
423 10 : size_t response_factory::get_content_read(){
424 10 : return resp->get_content().size();
425 : }
426 :
427 2850 : bool response_factory::empty_headers(){
428 2850 : return resp->empty_headers();
429 : }
430 :
431 2 : int response_factory::get_status_code() const {
432 2 : return resp ? resp->get_status_code() : 0;
433 : }
434 :
435 971 : void response_factory::setOnChunked(const std::function<void(int, const std::string&)>& onChunked){
436 971 : on_chunked_ = onChunked;
437 971 : }
438 :
439 2 : void response_factory::setOnStreaming(std::function<bool(const std::string_view&, size_t, size_t)> callback){
440 2 : on_streaming_ = std::move(callback);
441 2 : streaming_downloaded_ = 0;
442 2 : streaming_aborted_ = false;
443 2 : }
444 :
445 : }
|