Rework the http header parsing.
This commit is contained in:
+11
-2
@@ -19,15 +19,24 @@ const char CRLF[] = { '\r', '\n' };
|
|||||||
|
|
||||||
void HttpMessage::SetHeader(const std::string& name, const std::string& value) {
|
void HttpMessage::SetHeader(const std::string& name, const std::string& value) {
|
||||||
for (HttpHeader& h : headers_) {
|
for (HttpHeader& h : headers_) {
|
||||||
if (h.name == name) {
|
if (boost::iequals(h.name, name)) {
|
||||||
h.value = value;
|
h.value = value;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
headers_.push_back({ name, value });
|
headers_.push_back({ name, value });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void HttpMessage::SetHeader(std::string&& name, std::string&& value) {
|
||||||
|
for (HttpHeader& h : headers_) {
|
||||||
|
if (boost::iequals(h.name, name)) {
|
||||||
|
h.value = std::move(value);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
headers_.push_back({ std::move(name), std::move(value) });
|
||||||
|
}
|
||||||
|
|
||||||
// ATTENTION: The buffers don't hold the memory!
|
// ATTENTION: The buffers don't hold the memory!
|
||||||
std::vector<boost::asio::const_buffer> HttpMessage::ToBuffers() const {
|
std::vector<boost::asio::const_buffer> HttpMessage::ToBuffers() const {
|
||||||
assert(!start_line_.empty());
|
assert(!start_line_.empty());
|
||||||
|
|||||||
@@ -36,6 +36,8 @@ class HttpMessage {
|
|||||||
|
|
||||||
void SetHeader(const std::string& name, const std::string& value);
|
void SetHeader(const std::string& name, const std::string& value);
|
||||||
|
|
||||||
|
void SetHeader(std::string&& name, std::string&& value);
|
||||||
|
|
||||||
// E.g., "text/xml; charset=utf-8"
|
// E.g., "text/xml; charset=utf-8"
|
||||||
void SetContentType(const std::string& content_type) {
|
void SetContentType(const std::string& content_type) {
|
||||||
SetHeader(kContentType, content_type);
|
SetHeader(kContentType, content_type);
|
||||||
|
|||||||
+57
-19
@@ -18,7 +18,7 @@ HttpParser::HttpParser(HttpMessage* message)
|
|||||||
|
|
||||||
bool HttpParser::Parse(const char* data, std::size_t length) {
|
bool HttpParser::Parse(const char* data, std::size_t length) {
|
||||||
if (header_parsed_) {
|
if (header_parsed_) {
|
||||||
// Add the data to the content.
|
// Append the data to the content.
|
||||||
AppendContent(data, length);
|
AppendContent(data, length);
|
||||||
|
|
||||||
if (IsContentFull()) {
|
if (IsContentFull()) {
|
||||||
@@ -29,6 +29,8 @@ bool HttpParser::Parse(const char* data, std::size_t length) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Continue to parse headers.
|
||||||
|
|
||||||
pending_data_.append(data, length);
|
pending_data_.append(data, length);
|
||||||
std::size_t off = 0;
|
std::size_t off = 0;
|
||||||
|
|
||||||
@@ -53,11 +55,7 @@ bool HttpParser::Parse(const char* data, std::size_t length) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Currently, only Content-Length is important to us.
|
ParseHeader(line);
|
||||||
// Other header fields are ignored.
|
|
||||||
if (!content_length_parsed_) {
|
|
||||||
ParseContentLength(line);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
off = pos + 2; // Skip CRLF.
|
off = pos + 2; // Skip CRLF.
|
||||||
@@ -92,28 +90,28 @@ bool HttpParser::Parse(const char* data, std::size_t length) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void HttpParser::ParseContentLength(const std::string& line) {
|
bool HttpParser::ParseHeader(const std::string& line) {
|
||||||
std::size_t pos = line.find(':');
|
std::vector<std::string> splitted;
|
||||||
if (pos == std::string::npos) {
|
boost::split(splitted, line, boost::is_any_of(":"));
|
||||||
return;
|
|
||||||
|
if (splitted.size() != 2) {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string name = line.substr(0, pos);
|
std::string& name = splitted[0];
|
||||||
|
std::string& value = splitted[1];
|
||||||
|
|
||||||
if (boost::iequals(name, kContentLength)) {
|
boost::trim(name);
|
||||||
|
boost::trim(value);
|
||||||
|
|
||||||
|
if (!content_length_parsed_ && boost::iequals(name, kContentLength)) {
|
||||||
content_length_parsed_ = true;
|
content_length_parsed_ = true;
|
||||||
|
|
||||||
++pos; // Skip ':'.
|
|
||||||
while (line[pos] == ' ') { // Skip spaces.
|
|
||||||
++pos;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string value = line.substr(pos);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
content_length_ = static_cast<std::size_t>(std::stoul(value));
|
content_length_ = static_cast<std::size_t>(std::stoul(value));
|
||||||
} catch (const std::exception&) {
|
} catch (const std::exception&) {
|
||||||
LOG_ERRO("Invalid content length: %s.", value.c_str());
|
LOG_ERRO("Invalid content length: %s.", value.c_str());
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG_INFO("Content length: %u.", content_length_);
|
LOG_INFO("Content length: %u.", content_length_);
|
||||||
@@ -123,9 +121,49 @@ void HttpParser::ParseContentLength(const std::string& line) {
|
|||||||
content_.reserve(content_length_);
|
content_.reserve(content_length_);
|
||||||
} catch (const std::exception& e) {
|
} catch (const std::exception& e) {
|
||||||
LOG_ERRO("Failed to reserve content memory: %s.", e.what());
|
LOG_ERRO("Failed to reserve content memory: %s.", e.what());
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
message_->SetHeader(std::move(name), std::move(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
//
|
||||||
|
//void HttpParser::ParseContentLength(const std::string& line) {
|
||||||
|
// std::size_t pos = line.find(':');
|
||||||
|
// if (pos == std::string::npos) {
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// std::string name = line.substr(0, pos);
|
||||||
|
//
|
||||||
|
// if (boost::iequals(name, kContentLength)) {
|
||||||
|
// content_length_parsed_ = true;
|
||||||
|
//
|
||||||
|
// ++pos; // Skip ':'.
|
||||||
|
// while (line[pos] == ' ') { // Skip spaces.
|
||||||
|
// ++pos;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// std::string value = line.substr(pos);
|
||||||
|
//
|
||||||
|
// try {
|
||||||
|
// content_length_ = static_cast<std::size_t>(std::stoul(value));
|
||||||
|
// } catch (const std::exception&) {
|
||||||
|
// LOG_ERRO("Invalid content length: %s.", value.c_str());
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// LOG_INFO("Content length: %u.", content_length_);
|
||||||
|
//
|
||||||
|
// try {
|
||||||
|
// // Reserve memory to avoid frequent reallocation when append.
|
||||||
|
// content_.reserve(content_length_);
|
||||||
|
// } catch (const std::exception& e) {
|
||||||
|
// LOG_ERRO("Failed to reserve content memory: %s.", e.what());
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
void HttpParser::Finish() {
|
void HttpParser::Finish() {
|
||||||
// Move content to message.
|
// Move content to message.
|
||||||
|
|||||||
+3
-1
@@ -28,7 +28,9 @@ class HttpParser {
|
|||||||
protected:
|
protected:
|
||||||
virtual bool ParseStartLine(const std::string& line) = 0;
|
virtual bool ParseStartLine(const std::string& line) = 0;
|
||||||
|
|
||||||
void ParseContentLength(const std::string& line);
|
bool ParseHeader(const std::string& line);
|
||||||
|
|
||||||
|
//void ParseContentLength(const std::string& line);
|
||||||
|
|
||||||
void Finish();
|
void Finish();
|
||||||
|
|
||||||
|
|||||||
@@ -13,9 +13,8 @@ RestClient::RestClient(const std::string& host, const std::string& port)
|
|||||||
error_(kNoError) {
|
error_(kNoError) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RestClient::Request(const std::string& method,
|
bool RestClient::Request(const std::string& method, const std::string& url,
|
||||||
const std::string& url,
|
std::string&& content) {
|
||||||
const std::string& content) {
|
|
||||||
response_.reset();
|
response_.reset();
|
||||||
|
|
||||||
error_ = kNoError;
|
error_ = kNoError;
|
||||||
@@ -28,7 +27,8 @@ bool RestClient::Request(const std::string& method,
|
|||||||
request.SetHost(host_, port_);
|
request.SetHost(host_, port_);
|
||||||
|
|
||||||
if (!content.empty()) {
|
if (!content.empty()) {
|
||||||
request.SetContent(content);
|
request.SetContent(std::move(content));
|
||||||
|
request.SetContentType(kTextJsonUtf8);
|
||||||
}
|
}
|
||||||
|
|
||||||
request.UpdateStartLine();
|
request.UpdateStartLine();
|
||||||
|
|||||||
+9
-9
@@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <utility> // for move()
|
||||||
|
|
||||||
#include "webcc/globals.h"
|
#include "webcc/globals.h"
|
||||||
#include "webcc/http_response.h"
|
#include "webcc/http_response.h"
|
||||||
@@ -41,16 +42,16 @@ class RestClient {
|
|||||||
return Request(kHttpGet, url, "");
|
return Request(kHttpGet, url, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool Post(const std::string& url, const std::string& content) {
|
inline bool Post(const std::string& url, std::string&& content) {
|
||||||
return Request(kHttpPost, url, content);
|
return Request(kHttpPost, url, std::move(content));
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool Put(const std::string& url, const std::string& content) {
|
inline bool Put(const std::string& url, std::string&& content) {
|
||||||
return Request(kHttpPut, url, content);
|
return Request(kHttpPut, url, std::move(content));
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool Patch(const std::string& url, const std::string& content) {
|
inline bool Patch(const std::string& url, std::string&& content) {
|
||||||
return Request(kHttpPatch, url, content);
|
return Request(kHttpPatch, url, std::move(content));
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool Delete(const std::string& url) {
|
inline bool Delete(const std::string& url) {
|
||||||
@@ -58,9 +59,8 @@ class RestClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool Request(const std::string& method,
|
bool Request(const std::string& method, const std::string& url,
|
||||||
const std::string& url,
|
std::string&& content);
|
||||||
const std::string& content);
|
|
||||||
|
|
||||||
std::string host_;
|
std::string host_;
|
||||||
std::string port_;
|
std::string port_;
|
||||||
|
|||||||
+1
-2
@@ -151,8 +151,7 @@ std::vector<std::string> Url::SplitPath(const std::string& path) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static bool SplitKeyValue(const std::string& kv,
|
static bool SplitKeyValue(const std::string& kv,
|
||||||
std::string* key,
|
std::string* key, std::string* value) {
|
||||||
std::string* value) {
|
|
||||||
std::size_t i = kv.find_first_of('=');
|
std::size_t i = kv.find_first_of('=');
|
||||||
if (i == std::string::npos || i == 0) {
|
if (i == std::string::npos || i == 0) {
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
Reference in New Issue
Block a user