Add HttpRequestBuilder to replace HttpRequestArgs

master
Chunting Gu 6 years ago
parent d906470a4a
commit da1dbbb073

@ -65,8 +65,11 @@ void PrettyPrintJsonString(const std::string& str) {
// List public events. // List public events.
void ListEvents(webcc::HttpClientSession& session) { void ListEvents(webcc::HttpClientSession& session) {
try { try {
auto r = session.Get(kUrlRoot + "/events"); auto r = session.Request(webcc::HttpRequestBuilder{}.Get().
url(kUrlRoot + "/events")());
PRINT_JSON_STRING(r->content()); PRINT_JSON_STRING(r->content());
} catch (const webcc::Exception& e) { } catch (const webcc::Exception& e) {
std::cout << e.what() << std::endl; std::cout << e.what() << std::endl;
} }
@ -78,8 +81,11 @@ void ListEvents(webcc::HttpClientSession& session) {
void ListUserFollowers(webcc::HttpClientSession& session, void ListUserFollowers(webcc::HttpClientSession& session,
const std::string& user) { const std::string& user) {
try { try {
auto r = session.Get(kUrlRoot + "/users/" + user + "/followers"); auto r = session.Request(webcc::HttpRequestBuilder{}.Get().
url(kUrlRoot + "/users/" + user + "/followers")());
PRINT_JSON_STRING(r->content()); PRINT_JSON_STRING(r->content());
} catch (const webcc::Exception& e) { } catch (const webcc::Exception& e) {
std::cout << e.what() << std::endl; std::cout << e.what() << std::endl;
} }
@ -93,8 +99,9 @@ void ListUserFollowers(webcc::HttpClientSession& session,
void ListAuthUserFollowers(webcc::HttpClientSession& session, void ListAuthUserFollowers(webcc::HttpClientSession& session,
const std::string& auth) { const std::string& auth) {
try { try {
auto r = session.Get(kUrlRoot + "/user/followers", {}, auto r = session.Request(webcc::HttpRequestBuilder{}.Get().
{ "Authorization", auth }); url(kUrlRoot + "/user/followers").
header("Authorization", auth)());
PRINT_JSON_STRING(r->content()); PRINT_JSON_STRING(r->content());
@ -109,8 +116,11 @@ void CreateAuthorization(webcc::HttpClientSession& session,
std::string data = "{'note': 'Webcc test', 'scopes': ['public_repo', 'repo', 'repo:status', 'user']}"; std::string data = "{'note': 'Webcc test', 'scopes': ['public_repo', 'repo', 'repo:status', 'user']}";
auto r = session.Post(kUrlRoot + "/authorizations", std::move(data), true, auto r = session.Request(webcc::HttpRequestBuilder{}.Post().
{"Authorization", auth}); url(kUrlRoot + "/authorizations").
data(std::move(data)).
json(true).
header("Authorization", auth)());
std::cout << r->content() << std::endl; std::cout << r->content() << std::endl;
@ -125,7 +135,6 @@ int main() {
WEBCC_LOG_INIT("", webcc::LOG_CONSOLE); WEBCC_LOG_INIT("", webcc::LOG_CONSOLE);
webcc::HttpClientSession session; webcc::HttpClientSession session;
session.set_ssl_verify(kSslVerify); session.set_ssl_verify(kSslVerify);
ListEvents(session); ListEvents(session);

@ -19,55 +19,27 @@ bool kSslVerify = true;
// Examples // Examples
void ExampleBasic() { void ExampleBasic() {
HttpClientSession session; webcc::HttpClientSession session;
auto r = session.Request(HttpRequestArgs{"GET"} auto r = session.Request(webcc::HttpRequestBuilder{}.Get().
.url("http://httpbin.org/get") url("http://httpbin.org/get").
.parameters({"key1", "value1", "key2", "value2"}) parameter("key1", "value1").
.headers({"Accept", "application/json"}) parameter("key2", "value2").
.buffer_size(1000)); header("Accept", "application/json").
buffer(1000)());
std::cout << r->content() << std::endl; std::cout << r->content() << std::endl;
} }
// If you want to create the args object firstly, there might be an extra
// call to its move constructor (maybe only for MSVC).
// - constructor: HttpRequestArgs{ "GET" }
// - move constructor: auto args = ...
void ExampleArgs() {
HttpClientSession session;
auto args = HttpRequestArgs{"GET"}
.url("http://httpbin.org/get")
.parameters({"key1", "value1", "key2", "value2"})
.headers({"Accept", "application/json"})
.buffer_size(1000);
// Note the std::move().
session.Request(std::move(args));
}
// Use pre-defined wrappers.
void ExampleWrappers() {
HttpClientSession session;
session.Get("http://httpbin.org/get", {"key1", "value1", "key2", "value2"},
{"Accept", "application/json"},
HttpRequestArgs{}.buffer_size(1000));
session.Post("http://httpbin.org/post", "{'key': 'value'}", true,
{"Accept", "application/json"});
}
// HTTPS is auto-detected from the URL scheme. // HTTPS is auto-detected from the URL scheme.
void ExampleHttps() { void ExampleHttps() {
HttpClientSession session; webcc::HttpClientSession session;
session.set_ssl_verify(kSslVerify);
auto r = session.Request(HttpRequestArgs{"GET"} auto r = session.Request(webcc::HttpRequestBuilder{}.Get().
.url("https://httpbin.org/get") url("https://httpbin.org/get").
.parameters({"key1", "value1", "key2", "value2"}) parameter("key1", "value1").
.headers({"Accept", "application/json"}) header("Accept", "application/json")());
.ssl_verify(kSslVerify));
std::cout << r->content() << std::endl; std::cout << r->content() << std::endl;
} }
@ -86,27 +58,29 @@ void ExampleHttps() {
// //
void ExampleKeepAlive(const std::string& url) { void ExampleKeepAlive(const std::string& url) {
HttpClientSession session; HttpClientSession session;
session.set_ssl_verify(kSslVerify);
// Keep-Alive // Keep-Alive
session.Request(webcc::HttpRequestArgs("GET").url(url). session.Request(webcc::HttpRequestBuilder{}.Get().url(url)());
ssl_verify(kSslVerify));
// Close // Close
session.Request(webcc::HttpRequestArgs("GET").url(url). session.Request(webcc::HttpRequestBuilder{}.Get().url(url).keep_alive(false)());
ssl_verify(kSslVerify).keep_alive(false));
// Keep-Alive // Keep-Alive
session.Request(webcc::HttpRequestArgs("GET").url(url). session.Request(webcc::HttpRequestBuilder{}.Get().url(url)());
ssl_verify(kSslVerify));
} }
void ExampleCompression() { void ExampleCompression() {
HttpClientSession session; HttpClientSession session;
auto r = session.Get("http://httpbin.org/gzip"); auto r = session.Request(webcc::HttpRequestBuilder{}.
Get().url("http://httpbin.org/gzip")());
std::cout << r->content() << std::endl; std::cout << r->content() << std::endl;
r = session.Get("http://httpbin.org/deflate"); r = session.Request(webcc::HttpRequestBuilder{}.
Get().url("http://httpbin.org/deflate")());
std::cout << r->content() << std::endl; std::cout << r->content() << std::endl;
} }

@ -55,7 +55,8 @@ public:
bool ListBooks(std::list<Book>* books) { bool ListBooks(std::list<Book>* books) {
try { try {
auto r = session_.Get(url_ + "/books"); auto r = session_.Request(webcc::HttpRequestBuilder{}.Get().
url(url_ + "/books")());
if (!CheckStatus(r, webcc::http::Status::kOK)) { if (!CheckStatus(r, webcc::http::Status::kOK)) {
// Response HTTP status error. // Response HTTP status error.
@ -86,7 +87,10 @@ public:
req_json["price"] = price; req_json["price"] = price;
try { try {
auto r = session_.Post(url_ + "/books", JsonToString(req_json), true); auto r = session_.Request(webcc::HttpRequestBuilder{}.Post().
url(url_ + "/books").
data(JsonToString(req_json)).
json(true)());
if (!CheckStatus(r, webcc::http::Status::kCreated)) { if (!CheckStatus(r, webcc::http::Status::kCreated)) {
return false; return false;
@ -114,7 +118,8 @@ public:
bool GetBook(const std::string& id, Book* book) { bool GetBook(const std::string& id, Book* book) {
try { try {
auto r = session_.Get(url_ + "/books/" + id); auto r = session_.Request(webcc::HttpRequestBuilder{}.Get().
url(url_ + "/books/" + id)());
if (!CheckStatus(r, webcc::http::Status::kOK)) { if (!CheckStatus(r, webcc::http::Status::kOK)) {
return false; return false;
@ -135,7 +140,10 @@ public:
json["price"] = price; json["price"] = price;
try { try {
auto r = session_.Put(url_ + "/books/" + id, JsonToString(json), true); auto r = session_.Request(webcc::HttpRequestBuilder{}.Put().
url(url_ + "/books/" + id).
data(JsonToString(json)).
json(true)());
if (!CheckStatus(r, webcc::http::Status::kOK)) { if (!CheckStatus(r, webcc::http::Status::kOK)) {
return false; return false;
@ -151,7 +159,8 @@ public:
bool DeleteBook(const std::string& id) { bool DeleteBook(const std::string& id) {
try { try {
auto r = session_.Delete(url_ + "/books/" + id); auto r = session_.Request(webcc::HttpRequestBuilder{}.Delete().
url(url_ + "/books/" + id)());
if (!CheckStatus(r, webcc::http::Status::kOK)) { if (!CheckStatus(r, webcc::http::Status::kOK)) {
return false; return false;

@ -22,7 +22,7 @@ set(HEADERS
http_message.h http_message.h
http_parser.h http_parser.h
http_request.h http_request.h
http_request_args.h http_request_builder.h
http_request_handler.h http_request_handler.h
http_request_parser.h http_request_parser.h
http_response.h http_response.h
@ -49,6 +49,7 @@ set(SOURCES
http_message.cc http_message.cc
http_parser.cc http_parser.cc
http_request.cc http_request.cc
http_request_builder.cc
http_request_handler.cc http_request_handler.cc
http_request_parser.cc http_request_parser.cc
http_response.cc http_response.cc

@ -9,10 +9,9 @@ using boost::asio::ip::tcp;
namespace webcc { namespace webcc {
HttpClient::HttpClient(bool ssl_verify, std::size_t buffer_size) HttpClient::HttpClient(bool ssl_verify)
: timer_(io_context_), : timer_(io_context_),
ssl_verify_(ssl_verify), ssl_verify_(ssl_verify),
buffer_size_(buffer_size == 0 ? kBufferSize : buffer_size),
timeout_(kMaxReadSeconds), timeout_(kMaxReadSeconds),
closed_(false), closed_(false),
timer_canceled_(false), timer_canceled_(false),
@ -20,7 +19,7 @@ HttpClient::HttpClient(bool ssl_verify, std::size_t buffer_size)
error_(kNoError) { error_(kNoError) {
} }
bool HttpClient::Request(const HttpRequest& request, bool connect) { bool HttpClient::Request(HttpRequestPtr request, bool connect) {
io_context_.restart(); io_context_.restart();
response_.reset(new HttpResponse()); response_.reset(new HttpResponse());
@ -31,9 +30,14 @@ bool HttpClient::Request(const HttpRequest& request, bool connect) {
timed_out_ = false; timed_out_ = false;
error_ = kNoError; error_ = kNoError;
if (buffer_.size() != buffer_size_) { std::size_t buffer_size = request->buffer_size();
LOG_VERB("Resize buffer: %u -> %u.", buffer_.size(), buffer_size_); if (buffer_size == 0) {
buffer_.resize(buffer_size_); buffer_size = kBufferSize;
}
if (buffer_.size() != buffer_size) {
LOG_VERB("Resize buffer: %u -> %u.", buffer_.size(), buffer_size);
buffer_.resize(buffer_size);
} }
if (connect) { if (connect) {
@ -71,8 +75,8 @@ void HttpClient::Close() {
} }
} }
Error HttpClient::Connect(const HttpRequest& request) { Error HttpClient::Connect(HttpRequestPtr request) {
if (request.url().scheme() == "https") { if (request->url().scheme() == "https") {
socket_.reset(new HttpSslSocket{ io_context_, ssl_verify_ }); socket_.reset(new HttpSslSocket{ io_context_, ssl_verify_ });
return DoConnect(request, kPort443); return DoConnect(request, kPort443);
} else { } else {
@ -81,25 +85,25 @@ Error HttpClient::Connect(const HttpRequest& request) {
} }
} }
Error HttpClient::DoConnect(const HttpRequest& request, Error HttpClient::DoConnect(HttpRequestPtr request,
const std::string& default_port) { const std::string& default_port) {
tcp::resolver resolver(io_context_); tcp::resolver resolver(io_context_);
std::string port = request.port(default_port); std::string port = request->port(default_port);
boost::system::error_code ec; boost::system::error_code ec;
auto endpoints = resolver.resolve(tcp::v4(), request.host(), port, ec); auto endpoints = resolver.resolve(tcp::v4(), request->host(), port, ec);
if (ec) { if (ec) {
LOG_ERRO("Host resolve error (%s): %s, %s.", ec.message().c_str(), LOG_ERRO("Host resolve error (%s): %s, %s.", ec.message().c_str(),
request.host().c_str(), port.c_str()); request->host().c_str(), port.c_str());
return kHostResolveError; return kHostResolveError;
} }
LOG_VERB("Connect to server..."); LOG_VERB("Connect to server...");
// Use sync API directly since we don't need timeout control. // Use sync API directly since we don't need timeout control.
socket_->Connect(request.host(), endpoints, &ec); socket_->Connect(request->host(), endpoints, &ec);
// Determine whether a connection was successfully established. // Determine whether a connection was successfully established.
if (ec) { if (ec) {
@ -113,8 +117,8 @@ Error HttpClient::DoConnect(const HttpRequest& request,
return kNoError; return kNoError;
} }
Error HttpClient::WriteReqeust(const HttpRequest& request) { Error HttpClient::WriteReqeust(HttpRequestPtr request) {
LOG_VERB("HTTP request:\n%s", request.Dump(4, "> ").c_str()); LOG_VERB("HTTP request:\n%s", request->Dump(4, "> ").c_str());
// NOTE: // NOTE:
// It doesn't make much sense to set a timeout for socket write. // It doesn't make much sense to set a timeout for socket write.
@ -124,7 +128,7 @@ Error HttpClient::WriteReqeust(const HttpRequest& request) {
boost::system::error_code ec; boost::system::error_code ec;
// Use sync API directly since we don't need timeout control. // Use sync API directly since we don't need timeout control.
socket_->Write(request, &ec); socket_->Write(*request, &ec);
if (ec) { if (ec) {
LOG_ERRO("Socket write error (%s).", ec.message().c_str()); LOG_ERRO("Socket write error (%s).", ec.message().c_str());

@ -27,17 +27,13 @@ typedef std::shared_ptr<HttpClient> HttpClientPtr;
// Please don't use the same client object in multiple threads. // Please don't use the same client object in multiple threads.
class HttpClient { class HttpClient {
public: public:
explicit HttpClient(bool ssl_verify = true, std::size_t buffer_size = 0); explicit HttpClient(bool ssl_verify = true);
virtual ~HttpClient() = default; virtual ~HttpClient() = default;
HttpClient(const HttpClient&) = delete; HttpClient(const HttpClient&) = delete;
HttpClient& operator=(const HttpClient&) = delete; HttpClient& operator=(const HttpClient&) = delete;
void set_buffer_size(std::size_t buffer_size) {
buffer_size_ = (buffer_size == 0 ? kBufferSize : buffer_size);
}
void set_ssl_verify(bool ssl_verify) { void set_ssl_verify(bool ssl_verify) {
ssl_verify_ = ssl_verify; ssl_verify_ = ssl_verify;
} }
@ -50,23 +46,13 @@ public:
} }
// Connect to server, send request, wait until response is received. // Connect to server, send request, wait until response is received.
bool Request(const HttpRequest& request, bool connect = true); bool Request(HttpRequestPtr request, bool connect = true);
// Close the socket. // Close the socket.
void Close(); void Close();
HttpResponsePtr response() const { return response_; } HttpResponsePtr response() const { return response_; }
int response_status() const {
assert(response_);
return response_->status();
}
const std::string& response_content() const {
assert(response_);
return response_->content();
}
bool closed() const { return closed_; } bool closed() const { return closed_; }
bool timed_out() const { return timed_out_; } bool timed_out() const { return timed_out_; }
@ -74,11 +60,11 @@ public:
Error error() const { return error_; } Error error() const { return error_; }
private: private:
Error Connect(const HttpRequest& request); Error Connect(HttpRequestPtr request);
Error DoConnect(const HttpRequest& request, const std::string& default_port); Error DoConnect(HttpRequestPtr request, const std::string& default_port);
Error WriteReqeust(const HttpRequest& request); Error WriteReqeust(HttpRequestPtr request);
Error ReadResponse(); Error ReadResponse();
@ -108,10 +94,6 @@ private:
// Verify the certificate of the peer or not (for HTTPS). // Verify the certificate of the peer or not (for HTTPS).
bool ssl_verify_; bool ssl_verify_;
// The size of the buffer for reading response.
// Set 0 for using default value (e.g., 1024).
std::size_t buffer_size_;
// Maximum seconds to wait before the client cancels the operation. // Maximum seconds to wait before the client cancels the operation.
// Only for reading response from server. // Only for reading response from server.
int timeout_; int timeout_;

@ -1,7 +1,7 @@
#include "webcc/http_client_session.h" #include "webcc/http_client_session.h"
#include "webcc/logger.h"
#include "webcc/url.h" #include "webcc/url.h"
#include "webcc/zlib_wrapper.h"
namespace webcc { namespace webcc {
@ -10,25 +10,15 @@ namespace {
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// Helper functions. // Helper functions.
bool GetSslVerify(const boost::optional<bool>& session_ssl_verify, //bool GetSslVerify(const boost::optional<bool>& session_ssl_verify,
const boost::optional<bool>& request_ssl_verify) { // const boost::optional<bool>& request_ssl_verify) {
if (request_ssl_verify) { // if (request_ssl_verify) {
return request_ssl_verify.value(); // return request_ssl_verify.value();
} else if (session_ssl_verify) { // } else if (session_ssl_verify) {
return session_ssl_verify.value(); // return session_ssl_verify.value();
} // }
return true; // return true;
} //}
std::size_t GetBufferSize(std::size_t session_buffer_size,
std::size_t request_buffer_size) {
if (request_buffer_size != 0) {
return request_buffer_size;
} else if (session_buffer_size != 0) {
return session_buffer_size;
}
return 0;
}
} // namespace } // namespace
@ -38,72 +28,74 @@ HttpClientSession::HttpClientSession() {
InitHeaders(); InitHeaders();
} }
HttpResponsePtr HttpClientSession::Request(HttpRequestArgs&& args) { HttpResponsePtr HttpClientSession::Request(HttpRequestPtr request) {
assert(args.parameters_.size() % 2 == 0); assert(request);
assert(args.headers_.size() % 2 == 0);
HttpRequest request{args.method_, args.url_}; for (const auto& h : headers_.data()) {
if (!request->HaveHeader(h.first)) {
for (std::size_t i = 1; i < args.parameters_.size(); i += 2) { request->SetHeader(h.first, h.second);
request.AddParameter(args.parameters_[i - 1], args.parameters_[i]);
} }
// Apply the session-level headers.
for (const HttpHeader& h : headers_.data()) {
request.SetHeader(h.first, h.second);
} }
// Apply the request-level headers. if (!content_type_.empty() &&
// This will overwrite the session-level headers. !request->HaveHeader(http::headers::kContentType)) {
for (std::size_t i = 1; i < args.headers_.size(); i += 2) { request->SetContentType(content_type_, charset_);
request.SetHeader(std::move(args.headers_[i - 1]),
std::move(args.headers_[i]));
} }
// No keep-alive? request->Prepare();
if (!args.keep_alive_) {
request.SetHeader(http::headers::kConnection, "Close");
}
if (!args.data_.empty()) { return Send(request);
if (gzip_ && args.data_.size() > kGzipThreshold) {
std::string compressed;
if (Compress(args.data_, &compressed)) {
request.SetContent(std::move(compressed), true);
request.SetHeader(http::headers::kContentEncoding, "gzip");
} else {
LOG_WARN("Cannot compress the content data!");
request.SetContent(std::move(args.data_), true);
}
} else {
request.SetContent(std::move(args.data_), true);
} }
// TODO: Request-level charset. void HttpClientSession::InitHeaders() {
if (args.json_) { using namespace http::headers;
request.SetContentType(http::media_types::kApplicationJson, charset_);
} else if (!content_type_.empty()) { headers_.Set(kUserAgent, http::UserAgent());
request.SetContentType(content_type_, charset_);
} // Content-Encoding Tokens:
} // (https://en.wikipedia.org/wiki/HTTP_compression)
// * compress ¨C UNIX "compress" program method (historic; deprecated in most
// applications and replaced by gzip or deflate);
// * deflate ¨C compression based on the deflate algorithm, a combination of
// the LZ77 algorithm and Huffman coding, wrapped inside the
// zlib data format;
// * gzip ¨C GNU zip format. Uses the deflate algorithm for compression,
// but the data format and the checksum algorithm differ from
// the "deflate" content-encoding. This method is the most
// broadly supported as of March 2011.
// * identity ¨C No transformation is used. This is the default value for
// content coding.
//
// A note about "deflate":
// (https://www.zlib.net/zlib_faq.html#faq39)
// "gzip" is the gzip format, and "deflate" is the zlib format. They should
// probably have called the second one "zlib" instead to avoid confusion with
// the raw deflate compressed data format.
// Simply put, "deflate" is not recommended for HTTP 1.1 encoding.
//
headers_.Set(kAcceptEncoding, "gzip, deflate");
headers_.Set(kAccept, "*/*");
request.Prepare(); headers_.Set(kConnection, "Keep-Alive");
}
bool ssl_verify = GetSslVerify(ssl_verify_, args.ssl_verify_); HttpResponsePtr HttpClientSession::Send(HttpRequestPtr request) {
std::size_t buffer_size = GetBufferSize(buffer_size_, args.buffer_size_); if (request->buffer_size() == 0) {
request->set_buffer_size(buffer_size_);
}
const HttpClientPool::Key key{request.url()}; const HttpClientPool::Key key{request->url()};
// Reuse a connection or not. // Reuse a connection or not.
bool reuse = false; bool reuse = false;
HttpClientPtr client = pool_.Get(key); HttpClientPtr client = pool_.Get(key);
if (!client) { if (!client) {
client.reset(new HttpClient{ssl_verify, buffer_size}); client.reset(new HttpClient{ssl_verify_});
reuse = false; reuse = false;
} else { } else {
client->set_buffer_size(buffer_size); client->set_ssl_verify(ssl_verify_);
client->set_ssl_verify(ssl_verify);
reuse = true; reuse = true;
LOG_VERB("Reuse an existing connection."); LOG_VERB("Reuse an existing connection.");
} }
@ -143,77 +135,4 @@ HttpResponsePtr HttpClientSession::Request(HttpRequestArgs&& args) {
return client->response(); return client->response();
} }
HttpResponsePtr HttpClientSession::Get(const std::string& url,
std::vector<std::string>&& parameters,
std::vector<std::string>&& headers,
HttpRequestArgs&& args) {
return Request(args.method(http::kGet)
.url(url)
.parameters(std::move(parameters))
.headers(std::move(headers)));
}
HttpResponsePtr HttpClientSession::Post(const std::string& url,
std::string&& data, bool json,
std::vector<std::string>&& headers,
HttpRequestArgs&& args) {
return Request(args.method(http::kPost)
.url(url)
.data(std::move(data))
.json(json)
.headers(std::move(headers)));
}
HttpResponsePtr HttpClientSession::Put(const std::string& url,
std::string&& data, bool json,
std::vector<std::string>&& headers,
HttpRequestArgs&& args) {
return Request(args.method(http::kPut)
.url(url)
.data(std::move(data))
.json(json)
.headers(std::move(headers)));
}
HttpResponsePtr HttpClientSession::Delete(const std::string& url,
std::vector<std::string>&& headers,
HttpRequestArgs&& args) {
return Request(args.method(http::kDelete)
.url(url)
.headers(std::move(headers)));
}
void HttpClientSession::InitHeaders() {
using namespace http::headers;
headers_.Add(kUserAgent, http::UserAgent());
// Content-Encoding Tokens:
// (https://en.wikipedia.org/wiki/HTTP_compression)
// * compress ¨C UNIX "compress" program method (historic; deprecated in most
// applications and replaced by gzip or deflate);
// * deflate ¨C compression based on the deflate algorithm, a combination of
// the LZ77 algorithm and Huffman coding, wrapped inside the
// zlib data format;
// * gzip ¨C GNU zip format. Uses the deflate algorithm for compression,
// but the data format and the checksum algorithm differ from
// the "deflate" content-encoding. This method is the most
// broadly supported as of March 2011.
// * identity ¨C No transformation is used. This is the default value for
// content coding.
//
// A note about "deflate":
// (https://www.zlib.net/zlib_faq.html#faq39)
// "gzip" is the gzip format, and "deflate" is the zlib format. They should
// probably have called the second one "zlib" instead to avoid confusion with
// the raw deflate compressed data format.
// Simply put, "deflate" is not recommended for HTTP 1.1 encoding.
//
headers_.Add(kAcceptEncoding, "gzip, deflate");
headers_.Add(kAccept, "*/*");
headers_.Add(kConnection, "Keep-Alive");
}
} // namespace webcc } // namespace webcc

@ -8,7 +8,7 @@
#include "boost/optional.hpp" #include "boost/optional.hpp"
#include "webcc/http_client_pool.h" #include "webcc/http_client_pool.h"
#include "webcc/http_request_args.h" #include "webcc/http_request_builder.h"
#include "webcc/http_response.h" #include "webcc/http_response.h"
namespace webcc { namespace webcc {
@ -28,7 +28,7 @@ public:
} }
void set_ssl_verify(bool ssl_verify) { void set_ssl_verify(bool ssl_verify) {
ssl_verify_.emplace(ssl_verify); ssl_verify_ = ssl_verify;
} }
void set_buffer_size(std::size_t buffer_size) { void set_buffer_size(std::size_t buffer_size) {
@ -41,36 +41,17 @@ public:
} }
} }
void set_gzip(bool gzip) {
gzip_ = gzip;
}
void AddHeader(const std::string& key, const std::string& value) { void AddHeader(const std::string& key, const std::string& value) {
headers_.Add(key, value); headers_.Set(key, value);
} }
HttpResponsePtr Request(HttpRequestArgs&& args); HttpResponsePtr Request(HttpRequestPtr request);
HttpResponsePtr Get(const std::string& url,
std::vector<std::string>&& parameters = {},
std::vector<std::string>&& headers = {},
HttpRequestArgs&& args = HttpRequestArgs());
HttpResponsePtr Post(const std::string& url, std::string&& data, bool json,
std::vector<std::string>&& headers = {},
HttpRequestArgs&& args = HttpRequestArgs());
HttpResponsePtr Put(const std::string& url, std::string&& data, bool json,
std::vector<std::string>&& headers = {},
HttpRequestArgs&& args = HttpRequestArgs());
HttpResponsePtr Delete(const std::string& url,
std::vector<std::string>&& headers = {},
HttpRequestArgs&& args = HttpRequestArgs());
private: private:
void InitHeaders(); void InitHeaders();
HttpResponsePtr Send(HttpRequestPtr request);
private: private:
// E.g., "application/json". // E.g., "application/json".
std::string content_type_; std::string content_type_;
@ -82,7 +63,7 @@ private:
HttpHeaderDict headers_; HttpHeaderDict headers_;
// Verify the certificate of the peer or not. // Verify the certificate of the peer or not.
boost::optional<bool> ssl_verify_; bool ssl_verify_ = true;
// The size of the buffer for reading response. // The size of the buffer for reading response.
// 0 means default value will be used. // 0 means default value will be used.
@ -91,12 +72,6 @@ private:
// Timeout in seconds for receiving response. // Timeout in seconds for receiving response.
int timeout_ = 0; int timeout_ = 0;
// Compress the request content.
// NOTE: Most servers don't support compressed requests.
// Even the requests module from Python doesn't have a built-in support.
// See: https://github.com/kennethreitz/requests/issues/1753
bool gzip_ = false;
// Connection pool for keep-alive. // Connection pool for keep-alive.
HttpClientPool pool_; HttpClientPool pool_;
}; };

@ -24,7 +24,7 @@ std::ostream& operator<<(std::ostream& os, const HttpMessage& message) {
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
void HttpHeaderDict::Add(const std::string& key, const std::string& value) { void HttpHeaderDict::Set(const std::string& key, const std::string& value) {
auto it = Find(key); auto it = Find(key);
if (it != headers_.end()) { if (it != headers_.end()) {
it->second = value; it->second = value;
@ -33,7 +33,7 @@ void HttpHeaderDict::Add(const std::string& key, const std::string& value) {
} }
} }
void HttpHeaderDict::Add(std::string&& key, std::string&& value) { void HttpHeaderDict::Set(std::string&& key, std::string&& value) {
auto it = Find(key); auto it = Find(key);
if (it != headers_.end()) { if (it != headers_.end()) {
it->second = std::move(value); it->second = std::move(value);
@ -42,6 +42,10 @@ void HttpHeaderDict::Add(std::string&& key, std::string&& value) {
} }
} }
bool HttpHeaderDict::Have(const std::string& key) const {
return const_cast<HttpHeaderDict*>(this)->Find(key) != headers_.end();
}
const std::string& HttpHeaderDict::Get(const std::string& key, const std::string& HttpHeaderDict::Get(const std::string& key,
bool* existed) const { bool* existed) const {
auto it = const_cast<HttpHeaderDict*>(this)->Find(key); auto it = const_cast<HttpHeaderDict*>(this)->Find(key);

@ -31,13 +31,11 @@ public:
return headers_; return headers_;
} }
void Add(const std::string& key, const std::string& value); void Set(const std::string& key, const std::string& value);
void Add(std::string&& key, std::string&& value); void Set(std::string&& key, std::string&& value);
bool Has(const std::string& key) const { bool Have(const std::string& key) const;
return const_cast<HttpHeaderDict*>(this)->Find(key) != headers_.end();
}
// Get header by index. // Get header by index.
const HttpHeader& Get(std::size_t index) const { const HttpHeader& Get(std::size_t index) const {
@ -85,11 +83,11 @@ public:
bool IsConnectionKeepAlive() const; bool IsConnectionKeepAlive() const;
void SetHeader(const std::string& key, const std::string& value) { void SetHeader(const std::string& key, const std::string& value) {
headers_.Add(key, value); headers_.Set(key, value);
} }
void SetHeader(std::string&& key, std::string&& value) { void SetHeader(std::string&& key, std::string&& value) {
headers_.Add(std::move(key), std::move(value)); headers_.Set(std::move(key), std::move(value));
} }
const std::string& GetHeader(const std::string& key, const std::string& GetHeader(const std::string& key,
@ -97,6 +95,10 @@ public:
return headers_.Get(key, existed); return headers_.Get(key, existed);
} }
bool HaveHeader(const std::string& key) const {
return headers_.Have(key);
}
http::ContentEncoding GetContentEncoding() const; http::ContentEncoding GetContentEncoding() const;
// Return true if header Accept-Encoding contains "gzip". // Return true if header Accept-Encoding contains "gzip".

@ -57,6 +57,14 @@ public:
return port().empty() ? default_port : port(); return port().empty() ? default_port : port();
} }
std::size_t buffer_size() const {
return buffer_size_;
}
void set_buffer_size(std::size_t buffer_size) {
buffer_size_ = buffer_size;
}
// Prepare payload. // Prepare payload.
// Compose start line, set Host header, etc. // Compose start line, set Host header, etc.
bool Prepare() final; bool Prepare() final;
@ -64,6 +72,8 @@ public:
private: private:
std::string method_; std::string method_;
Url url_; Url url_;
std::size_t buffer_size_ = 0;
}; };
} // namespace webcc } // namespace webcc

@ -1,123 +0,0 @@
#ifndef WEBCC_HTTP_REQUEST_ARGS_H_
#define WEBCC_HTTP_REQUEST_ARGS_H_
#include <string>
#include <utility>
#include <vector>
#include "boost/optional.hpp"
#include "webcc/globals.h"
#include "webcc/logger.h"
namespace webcc {
class HttpClientSession;
// Args maker for HttpClientSession.
// Use method chaining to simulate Named Parameters.
class HttpRequestArgs {
public:
explicit HttpRequestArgs(const std::string& method = "")
: method_(method), json_(false), buffer_size_(0), keep_alive_(true) {
}
HttpRequestArgs(const HttpRequestArgs&) = default;
HttpRequestArgs& operator=(const HttpRequestArgs&) = default;
HttpRequestArgs(HttpRequestArgs&&) = default;
HttpRequestArgs& operator=(HttpRequestArgs&&) = default;
HttpRequestArgs&& method(const std::string& method) {
method_ = method;
return std::move(*this);
}
HttpRequestArgs&& url(const std::string& url) {
url_ = url;
return std::move(*this);
}
HttpRequestArgs&& parameters(const std::vector<std::string>& parameters) {
parameters_ = parameters;
return std::move(*this);
}
HttpRequestArgs&& parameters(std::vector<std::string>&& parameters) {
parameters_ = std::move(parameters);
return std::move(*this);
}
HttpRequestArgs&& data(const std::string& data) {
data_ = data;
return std::move(*this);
}
HttpRequestArgs&& data(std::string&& data) {
data_ = std::move(data);
return std::move(*this);
}
HttpRequestArgs&& json(bool json = true) {
json_ = json;
return std::move(*this);
}
HttpRequestArgs&& headers(const std::vector<std::string>& headers) {
headers_ = headers;
return std::move(*this);
}
HttpRequestArgs&& headers(std::vector<std::string>&& headers) {
headers_ = std::move(headers);
return std::move(*this);
}
HttpRequestArgs&& ssl_verify(bool ssl_verify = true) {
ssl_verify_.emplace(ssl_verify);
return std::move(*this);
}
HttpRequestArgs&& buffer_size(std::size_t buffer_size) {
buffer_size_ = buffer_size;
return std::move(*this);
}
HttpRequestArgs&& keep_alive(bool keep_alive) {
keep_alive_ = keep_alive;
return std::move(*this);
}
private:
friend class HttpClientSession;
std::string method_;
std::string url_;
// URL query parameters.
std::vector<std::string> parameters_;
// Data to send in the body of the request.
std::string data_;
// Is the data to send a JSON string?
bool json_;
// Additional request headers.
std::vector<std::string> headers_;
// Verify the certificate of the peer or not.
boost::optional<bool> ssl_verify_;
// Size of the buffer to read response.
// Leave it to 0 for using default value.
std::size_t buffer_size_;
// Persistent connection.
bool keep_alive_;
};
} // namespace webcc
#endif // WEBCC_HTTP_REQUEST_ARGS_H_

@ -0,0 +1,123 @@
#ifndef WEBCC_HTTP_REQUEST_BUILDER_H_
#define WEBCC_HTTP_REQUEST_BUILDER_H_
#include <string>
#include <vector>
#include "boost/optional.hpp"
#include "webcc/http_request.h"
namespace webcc {
// TODO: Add timeout()
// HTTP request builder.
class HttpRequestBuilder {
public:
HttpRequestBuilder() = default;
// Build the request.
HttpRequestPtr operator()();
HttpRequestBuilder& Get() { return method(http::kGet); }
HttpRequestBuilder& Post() { return method(http::kPost); }
HttpRequestBuilder& Put() { return method(http::kPut); }
HttpRequestBuilder& Delete() { return method(http::kDelete); }
HttpRequestBuilder& method(const std::string& method) {
method_ = method;
return *this;
}
HttpRequestBuilder& url(const std::string& url) {
url_ = url;
return *this;
}
HttpRequestBuilder& parameter(const std::string& key,
const std::string& value) {
parameters_.push_back(key);
parameters_.push_back(value);
return *this;
}
HttpRequestBuilder& data(const std::string& data) {
data_ = data;
return *this;
}
HttpRequestBuilder& data(std::string&& data) {
data_ = std::move(data);
return *this;
}
HttpRequestBuilder& json(bool json) {
json_ = json;
return *this;
}
HttpRequestBuilder& gzip(bool gzip) {
gzip_ = gzip;
return *this;
}
HttpRequestBuilder& header(const std::string& key,
const std::string& value) {
headers_.push_back(key);
headers_.push_back(value);
return *this;
}
HttpRequestBuilder& ssl_verify(bool ssl_verify = true) {
ssl_verify_.emplace(ssl_verify);
return *this;
}
HttpRequestBuilder& buffer(std::size_t buffer) {
buffer_ = buffer;
return *this;
}
HttpRequestBuilder& keep_alive(bool keep_alive) {
keep_alive_ = keep_alive;
return *this;
}
private:
std::string method_;
std::string url_;
// URL query parameters.
std::vector<std::string> parameters_;
// Data to send in the body of the request.
std::string data_;
// Is the data to send a JSON string?
bool json_ = false;
// Compress the request content.
// NOTE: Most servers don't support compressed requests.
// Even the requests module from Python doesn't have a built-in support.
// See: https://github.com/kennethreitz/requests/issues/1753
bool gzip_ = false;
// Additional request headers.
std::vector<std::string> headers_;
// Verify the certificate of the peer or not.
boost::optional<bool> ssl_verify_;
// Size of the buffer to read response.
// Leave it to 0 for using default value.
std::size_t buffer_ = 0;
// Persistent connection.
bool keep_alive_ = true;
};
} // namespace webcc
#endif // WEBCC_HTTP_REQUEST_BUILDER_H_

@ -10,11 +10,12 @@
namespace webcc { namespace webcc {
SoapClient::SoapClient(const std::string& url, SoapVersion soap_version, SoapClient::SoapClient(const std::string& url, SoapVersion soap_version)
std::size_t buffer_size) : url_(url),
: url_(url), soap_version_(soap_version), soap_version_(soap_version),
http_client_(buffer_size), http_client_(true),
format_raw_(true), error_(kNoError) { format_raw_(true),
error_(kNoError) {
} }
bool SoapClient::Request(const std::string& operation, bool SoapClient::Request(const std::string& operation,
@ -47,23 +48,25 @@ bool SoapClient::Request(const std::string& operation,
std::string http_content; std::string http_content;
soap_request.ToXml(format_raw_, indent_str_, &http_content); soap_request.ToXml(format_raw_, indent_str_, &http_content);
HttpRequest http_request(http::kPost, url_); auto http_request = std::make_shared<HttpRequest>(http::kPost, url_);
http_request.SetContent(std::move(http_content), true); http_request->SetContent(std::move(http_content), true);
if (soap_version_ == kSoapV11) { if (soap_version_ == kSoapV11) {
http_request.SetContentType(http::media_types::kTextXml, http_request->SetContentType(http::media_types::kTextXml,
http::charsets::kUtf8); http::charsets::kUtf8);
} else { } else {
http_request.SetContentType(http::media_types::kApplicationSoapXml, http_request->SetContentType(http::media_types::kApplicationSoapXml,
http::charsets::kUtf8); http::charsets::kUtf8);
} }
http_request.SetHeader(kSoapAction, operation); http_request->SetHeader(kSoapAction, operation);
http_request.Prepare(); http_request->set_buffer_size(buffer_size);
if (!http_client_.Request(http_request, buffer_size)) { http_request->Prepare();
if (!http_client_.Request(http_request, true)) {
error_ = http_client_.error(); error_ = http_client_.error();
return false; return false;
} }

@ -14,8 +14,7 @@ namespace webcc {
class SoapClient { class SoapClient {
public: public:
explicit SoapClient(const std::string& url, explicit SoapClient(const std::string& url,
SoapVersion soap_version = kSoapV12, SoapVersion soap_version = kSoapV12);
std::size_t buffer_size = 0);
~SoapClient() = default; ~SoapClient() = default;

Loading…
Cancel
Save