Rework the http header parsing.

master
Adam Gu 7 years ago
parent cbf3efbcba
commit 9b35d0f06c

@ -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);

@ -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;
}
std::string name = line.substr(0, pos); if (splitted.size() != 2) {
return false;
}
if (boost::iequals(name, kContentLength)) { std::string& name = splitted[0];
content_length_parsed_ = true; std::string& value = splitted[1];
++pos; // Skip ':'. boost::trim(name);
while (line[pos] == ' ') { // Skip spaces. boost::trim(value);
++pos;
}
std::string value = line.substr(pos); if (!content_length_parsed_ && boost::iequals(name, kContentLength)) {
content_length_parsed_ = true;
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.

@ -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();

@ -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_;

@ -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;

Loading…
Cancel
Save