Squashed 'third_party/allwpilib_2019/' content from commit bd05dfa1c
Change-Id: I2b1c2250cdb9b055133780c33593292098c375b7
git-subtree-dir: third_party/allwpilib_2019
git-subtree-split: bd05dfa1c7cca74c4fac451e7b9d6a37e7b53447
diff --git a/wpiutil/src/main/native/cpp/HttpParser.cpp b/wpiutil/src/main/native/cpp/HttpParser.cpp
new file mode 100644
index 0000000..4560b38
--- /dev/null
+++ b/wpiutil/src/main/native/cpp/HttpParser.cpp
@@ -0,0 +1,176 @@
+/*----------------------------------------------------------------------------*/
+/* 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;
+}