blob: 4560b38508a8c42aebd4d42557744ef9e0d98430 [file] [log] [blame]
/*----------------------------------------------------------------------------*/
/* Copyright (c) 2018 FIRST. All Rights Reserved. */
/* Open Source Software - may be modified and shared by FRC teams. The code */
/* must be accompanied by the FIRST BSD license file in the root directory of */
/* the project. */
/*----------------------------------------------------------------------------*/
#include "wpi/HttpParser.h"
using namespace wpi;
uint32_t HttpParser::GetParserVersion() {
return static_cast<uint32_t>(http_parser_version());
}
HttpParser::HttpParser(Type type) {
http_parser_init(&m_parser,
static_cast<http_parser_type>(static_cast<int>(type)));
m_parser.data = this;
http_parser_settings_init(&m_settings);
// Unlike the underlying http_parser library, we don't perform callbacks
// (other than body) with partial data; instead we buffer and call the user
// callback only when the data is complete.
// on_message_begin: initialize our state, call user callback
m_settings.on_message_begin = [](http_parser* p) -> int {
auto& self = *static_cast<HttpParser*>(p->data);
self.m_urlBuf.clear();
self.m_state = kStart;
self.messageBegin();
return self.m_aborted;
};
// on_url: collect into buffer
m_settings.on_url = [](http_parser* p, const char* at, size_t length) -> int {
auto& self = *static_cast<HttpParser*>(p->data);
// append to buffer
if ((self.m_urlBuf.size() + length) > self.m_maxLength) return 1;
self.m_urlBuf += StringRef{at, length};
self.m_state = kUrl;
return 0;
};
// on_status: collect into buffer, call user URL callback
m_settings.on_status = [](http_parser* p, const char* at,
size_t length) -> int {
auto& self = *static_cast<HttpParser*>(p->data);
// use valueBuf for the status
if ((self.m_valueBuf.size() + length) > self.m_maxLength) return 1;
self.m_valueBuf += StringRef{at, length};
self.m_state = kStatus;
return 0;
};
// on_header_field: collect into buffer, call user header/status callback
m_settings.on_header_field = [](http_parser* p, const char* at,
size_t length) -> int {
auto& self = *static_cast<HttpParser*>(p->data);
// once we're in header, we know the URL is complete
if (self.m_state == kUrl) {
self.url(self.m_urlBuf);
if (self.m_aborted) return 1;
}
// once we're in header, we know the status is complete
if (self.m_state == kStatus) {
self.status(self.m_valueBuf);
if (self.m_aborted) return 1;
}
// if we previously were in value state, that means we finished a header
if (self.m_state == kValue) {
self.header(self.m_fieldBuf, self.m_valueBuf);
if (self.m_aborted) return 1;
}
// clear field and value when we enter this state
if (self.m_state != kField) {
self.m_state = kField;
self.m_fieldBuf.clear();
self.m_valueBuf.clear();
}
// append data to field buffer
if ((self.m_fieldBuf.size() + length) > self.m_maxLength) return 1;
self.m_fieldBuf += StringRef{at, length};
return 0;
};
// on_header_field: collect into buffer
m_settings.on_header_value = [](http_parser* p, const char* at,
size_t length) -> int {
auto& self = *static_cast<HttpParser*>(p->data);
// if we weren't previously in value state, clear the buffer
if (self.m_state != kValue) {
self.m_state = kValue;
self.m_valueBuf.clear();
}
// append data to value buffer
if ((self.m_valueBuf.size() + length) > self.m_maxLength) return 1;
self.m_valueBuf += StringRef{at, length};
return 0;
};
// on_headers_complete: call user status/header/complete callback
m_settings.on_headers_complete = [](http_parser* p) -> int {
auto& self = *static_cast<HttpParser*>(p->data);
// if we previously were in url state, that means we finished the url
if (self.m_state == kUrl) {
self.url(self.m_urlBuf);
if (self.m_aborted) return 1;
}
// if we previously were in status state, that means we finished the status
if (self.m_state == kStatus) {
self.status(self.m_valueBuf);
if (self.m_aborted) return 1;
}
// if we previously were in value state, that means we finished a header
if (self.m_state == kValue) {
self.header(self.m_fieldBuf, self.m_valueBuf);
if (self.m_aborted) return 1;
}
self.headersComplete(self.ShouldKeepAlive());
return self.m_aborted;
};
// on_body: call user callback
m_settings.on_body = [](http_parser* p, const char* at,
size_t length) -> int {
auto& self = *static_cast<HttpParser*>(p->data);
self.body(StringRef{at, length}, self.IsBodyFinal());
return self.m_aborted;
};
// on_message_complete: call user callback
m_settings.on_message_complete = [](http_parser* p) -> int {
auto& self = *static_cast<HttpParser*>(p->data);
self.messageComplete(self.ShouldKeepAlive());
return self.m_aborted;
};
// on_chunk_header: call user callback
m_settings.on_chunk_header = [](http_parser* p) -> int {
auto& self = *static_cast<HttpParser*>(p->data);
self.chunkHeader(p->content_length);
return self.m_aborted;
};
// on_chunk_complete: call user callback
m_settings.on_chunk_complete = [](http_parser* p) -> int {
auto& self = *static_cast<HttpParser*>(p->data);
self.chunkComplete();
return self.m_aborted;
};
}
void HttpParser::Reset(Type type) {
http_parser_init(&m_parser,
static_cast<http_parser_type>(static_cast<int>(type)));
m_parser.data = this;
m_maxLength = 1024;
m_state = kStart;
m_urlBuf.clear();
m_fieldBuf.clear();
m_valueBuf.clear();
m_aborted = false;
}