Add HttpRequestBuilder to replace HttpRequestArgs
This commit is contained in:
@@ -65,8 +65,11 @@ void PrettyPrintJsonString(const std::string& str) {
|
||||
// List public events.
|
||||
void ListEvents(webcc::HttpClientSession& session) {
|
||||
try {
|
||||
auto r = session.Get(kUrlRoot + "/events");
|
||||
auto r = session.Request(webcc::HttpRequestBuilder{}.Get().
|
||||
url(kUrlRoot + "/events")());
|
||||
|
||||
PRINT_JSON_STRING(r->content());
|
||||
|
||||
} catch (const webcc::Exception& e) {
|
||||
std::cout << e.what() << std::endl;
|
||||
}
|
||||
@@ -78,8 +81,11 @@ void ListEvents(webcc::HttpClientSession& session) {
|
||||
void ListUserFollowers(webcc::HttpClientSession& session,
|
||||
const std::string& user) {
|
||||
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());
|
||||
|
||||
} catch (const webcc::Exception& e) {
|
||||
std::cout << e.what() << std::endl;
|
||||
}
|
||||
@@ -93,8 +99,9 @@ void ListUserFollowers(webcc::HttpClientSession& session,
|
||||
void ListAuthUserFollowers(webcc::HttpClientSession& session,
|
||||
const std::string& auth) {
|
||||
try {
|
||||
auto r = session.Get(kUrlRoot + "/user/followers", {},
|
||||
{ "Authorization", auth });
|
||||
auto r = session.Request(webcc::HttpRequestBuilder{}.Get().
|
||||
url(kUrlRoot + "/user/followers").
|
||||
header("Authorization", auth)());
|
||||
|
||||
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']}";
|
||||
|
||||
auto r = session.Post(kUrlRoot + "/authorizations", std::move(data), true,
|
||||
{"Authorization", auth});
|
||||
auto r = session.Request(webcc::HttpRequestBuilder{}.Post().
|
||||
url(kUrlRoot + "/authorizations").
|
||||
data(std::move(data)).
|
||||
json(true).
|
||||
header("Authorization", auth)());
|
||||
|
||||
std::cout << r->content() << std::endl;
|
||||
|
||||
@@ -125,7 +135,6 @@ int main() {
|
||||
WEBCC_LOG_INIT("", webcc::LOG_CONSOLE);
|
||||
|
||||
webcc::HttpClientSession session;
|
||||
|
||||
session.set_ssl_verify(kSslVerify);
|
||||
|
||||
ListEvents(session);
|
||||
|
||||
+24
-50
@@ -19,55 +19,27 @@ bool kSslVerify = true;
|
||||
// Examples
|
||||
|
||||
void ExampleBasic() {
|
||||
HttpClientSession session;
|
||||
|
||||
auto r = session.Request(HttpRequestArgs{"GET"}
|
||||
.url("http://httpbin.org/get")
|
||||
.parameters({"key1", "value1", "key2", "value2"})
|
||||
.headers({"Accept", "application/json"})
|
||||
.buffer_size(1000));
|
||||
webcc::HttpClientSession session;
|
||||
|
||||
auto r = session.Request(webcc::HttpRequestBuilder{}.Get().
|
||||
url("http://httpbin.org/get").
|
||||
parameter("key1", "value1").
|
||||
parameter("key2", "value2").
|
||||
header("Accept", "application/json").
|
||||
buffer(1000)());
|
||||
|
||||
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.
|
||||
void ExampleHttps() {
|
||||
HttpClientSession session;
|
||||
webcc::HttpClientSession session;
|
||||
session.set_ssl_verify(kSslVerify);
|
||||
|
||||
auto r = session.Request(HttpRequestArgs{"GET"}
|
||||
.url("https://httpbin.org/get")
|
||||
.parameters({"key1", "value1", "key2", "value2"})
|
||||
.headers({"Accept", "application/json"})
|
||||
.ssl_verify(kSslVerify));
|
||||
auto r = session.Request(webcc::HttpRequestBuilder{}.Get().
|
||||
url("https://httpbin.org/get").
|
||||
parameter("key1", "value1").
|
||||
header("Accept", "application/json")());
|
||||
|
||||
std::cout << r->content() << std::endl;
|
||||
}
|
||||
@@ -86,27 +58,29 @@ void ExampleHttps() {
|
||||
//
|
||||
void ExampleKeepAlive(const std::string& url) {
|
||||
HttpClientSession session;
|
||||
session.set_ssl_verify(kSslVerify);
|
||||
|
||||
// Keep-Alive
|
||||
session.Request(webcc::HttpRequestArgs("GET").url(url).
|
||||
ssl_verify(kSslVerify));
|
||||
session.Request(webcc::HttpRequestBuilder{}.Get().url(url)());
|
||||
|
||||
// Close
|
||||
session.Request(webcc::HttpRequestArgs("GET").url(url).
|
||||
ssl_verify(kSslVerify).keep_alive(false));
|
||||
session.Request(webcc::HttpRequestBuilder{}.Get().url(url).keep_alive(false)());
|
||||
|
||||
// Keep-Alive
|
||||
session.Request(webcc::HttpRequestArgs("GET").url(url).
|
||||
ssl_verify(kSslVerify));
|
||||
session.Request(webcc::HttpRequestBuilder{}.Get().url(url)());
|
||||
}
|
||||
|
||||
void ExampleCompression() {
|
||||
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;
|
||||
|
||||
r = session.Get("http://httpbin.org/deflate");
|
||||
r = session.Request(webcc::HttpRequestBuilder{}.
|
||||
Get().url("http://httpbin.org/deflate")());
|
||||
|
||||
std::cout << r->content() << std::endl;
|
||||
}
|
||||
|
||||
|
||||
@@ -55,7 +55,8 @@ public:
|
||||
|
||||
bool ListBooks(std::list<Book>* books) {
|
||||
try {
|
||||
auto r = session_.Get(url_ + "/books");
|
||||
auto r = session_.Request(webcc::HttpRequestBuilder{}.Get().
|
||||
url(url_ + "/books")());
|
||||
|
||||
if (!CheckStatus(r, webcc::http::Status::kOK)) {
|
||||
// Response HTTP status error.
|
||||
@@ -86,7 +87,10 @@ public:
|
||||
req_json["price"] = price;
|
||||
|
||||
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)) {
|
||||
return false;
|
||||
@@ -114,7 +118,8 @@ public:
|
||||
|
||||
bool GetBook(const std::string& id, Book* book) {
|
||||
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)) {
|
||||
return false;
|
||||
@@ -135,7 +140,10 @@ public:
|
||||
json["price"] = price;
|
||||
|
||||
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)) {
|
||||
return false;
|
||||
@@ -151,7 +159,8 @@ public:
|
||||
|
||||
bool DeleteBook(const std::string& id) {
|
||||
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)) {
|
||||
return false;
|
||||
|
||||
@@ -22,7 +22,7 @@ set(HEADERS
|
||||
http_message.h
|
||||
http_parser.h
|
||||
http_request.h
|
||||
http_request_args.h
|
||||
http_request_builder.h
|
||||
http_request_handler.h
|
||||
http_request_parser.h
|
||||
http_response.h
|
||||
@@ -49,6 +49,7 @@ set(SOURCES
|
||||
http_message.cc
|
||||
http_parser.cc
|
||||
http_request.cc
|
||||
http_request_builder.cc
|
||||
http_request_handler.cc
|
||||
http_request_parser.cc
|
||||
http_response.cc
|
||||
|
||||
+20
-16
@@ -9,10 +9,9 @@ using boost::asio::ip::tcp;
|
||||
|
||||
namespace webcc {
|
||||
|
||||
HttpClient::HttpClient(bool ssl_verify, std::size_t buffer_size)
|
||||
HttpClient::HttpClient(bool ssl_verify)
|
||||
: timer_(io_context_),
|
||||
ssl_verify_(ssl_verify),
|
||||
buffer_size_(buffer_size == 0 ? kBufferSize : buffer_size),
|
||||
timeout_(kMaxReadSeconds),
|
||||
closed_(false),
|
||||
timer_canceled_(false),
|
||||
@@ -20,7 +19,7 @@ HttpClient::HttpClient(bool ssl_verify, std::size_t buffer_size)
|
||||
error_(kNoError) {
|
||||
}
|
||||
|
||||
bool HttpClient::Request(const HttpRequest& request, bool connect) {
|
||||
bool HttpClient::Request(HttpRequestPtr request, bool connect) {
|
||||
io_context_.restart();
|
||||
|
||||
response_.reset(new HttpResponse());
|
||||
@@ -31,9 +30,14 @@ bool HttpClient::Request(const HttpRequest& request, bool connect) {
|
||||
timed_out_ = false;
|
||||
error_ = kNoError;
|
||||
|
||||
if (buffer_.size() != buffer_size_) {
|
||||
LOG_VERB("Resize buffer: %u -> %u.", buffer_.size(), buffer_size_);
|
||||
buffer_.resize(buffer_size_);
|
||||
std::size_t buffer_size = request->buffer_size();
|
||||
if (buffer_size == 0) {
|
||||
buffer_size = kBufferSize;
|
||||
}
|
||||
|
||||
if (buffer_.size() != buffer_size) {
|
||||
LOG_VERB("Resize buffer: %u -> %u.", buffer_.size(), buffer_size);
|
||||
buffer_.resize(buffer_size);
|
||||
}
|
||||
|
||||
if (connect) {
|
||||
@@ -71,8 +75,8 @@ void HttpClient::Close() {
|
||||
}
|
||||
}
|
||||
|
||||
Error HttpClient::Connect(const HttpRequest& request) {
|
||||
if (request.url().scheme() == "https") {
|
||||
Error HttpClient::Connect(HttpRequestPtr request) {
|
||||
if (request->url().scheme() == "https") {
|
||||
socket_.reset(new HttpSslSocket{ io_context_, ssl_verify_ });
|
||||
return DoConnect(request, kPort443);
|
||||
} 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) {
|
||||
tcp::resolver resolver(io_context_);
|
||||
|
||||
std::string port = request.port(default_port);
|
||||
std::string port = request->port(default_port);
|
||||
|
||||
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) {
|
||||
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;
|
||||
}
|
||||
|
||||
LOG_VERB("Connect to server...");
|
||||
|
||||
// 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.
|
||||
if (ec) {
|
||||
@@ -113,8 +117,8 @@ Error HttpClient::DoConnect(const HttpRequest& request,
|
||||
return kNoError;
|
||||
}
|
||||
|
||||
Error HttpClient::WriteReqeust(const HttpRequest& request) {
|
||||
LOG_VERB("HTTP request:\n%s", request.Dump(4, "> ").c_str());
|
||||
Error HttpClient::WriteReqeust(HttpRequestPtr request) {
|
||||
LOG_VERB("HTTP request:\n%s", request->Dump(4, "> ").c_str());
|
||||
|
||||
// NOTE:
|
||||
// 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;
|
||||
|
||||
// Use sync API directly since we don't need timeout control.
|
||||
socket_->Write(request, &ec);
|
||||
socket_->Write(*request, &ec);
|
||||
|
||||
if (ec) {
|
||||
LOG_ERRO("Socket write error (%s).", ec.message().c_str());
|
||||
|
||||
+5
-23
@@ -27,17 +27,13 @@ typedef std::shared_ptr<HttpClient> HttpClientPtr;
|
||||
// Please don't use the same client object in multiple threads.
|
||||
class HttpClient {
|
||||
public:
|
||||
explicit HttpClient(bool ssl_verify = true, std::size_t buffer_size = 0);
|
||||
explicit HttpClient(bool ssl_verify = true);
|
||||
|
||||
virtual ~HttpClient() = default;
|
||||
|
||||
HttpClient(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) {
|
||||
ssl_verify_ = ssl_verify;
|
||||
}
|
||||
@@ -50,23 +46,13 @@ public:
|
||||
}
|
||||
|
||||
// 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.
|
||||
void Close();
|
||||
|
||||
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 timed_out() const { return timed_out_; }
|
||||
@@ -74,11 +60,11 @@ public:
|
||||
Error error() const { return error_; }
|
||||
|
||||
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();
|
||||
|
||||
@@ -108,10 +94,6 @@ private:
|
||||
// Verify the certificate of the peer or not (for HTTPS).
|
||||
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.
|
||||
// Only for reading response from server.
|
||||
int timeout_;
|
||||
|
||||
+64
-145
@@ -1,7 +1,7 @@
|
||||
#include "webcc/http_client_session.h"
|
||||
|
||||
#include "webcc/logger.h"
|
||||
#include "webcc/url.h"
|
||||
#include "webcc/zlib_wrapper.h"
|
||||
|
||||
namespace webcc {
|
||||
|
||||
@@ -10,25 +10,15 @@ namespace {
|
||||
// -----------------------------------------------------------------------------
|
||||
// Helper functions.
|
||||
|
||||
bool GetSslVerify(const boost::optional<bool>& session_ssl_verify,
|
||||
const boost::optional<bool>& request_ssl_verify) {
|
||||
if (request_ssl_verify) {
|
||||
return request_ssl_verify.value();
|
||||
} else if (session_ssl_verify) {
|
||||
return session_ssl_verify.value();
|
||||
}
|
||||
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;
|
||||
}
|
||||
//bool GetSslVerify(const boost::optional<bool>& session_ssl_verify,
|
||||
// const boost::optional<bool>& request_ssl_verify) {
|
||||
// if (request_ssl_verify) {
|
||||
// return request_ssl_verify.value();
|
||||
// } else if (session_ssl_verify) {
|
||||
// return session_ssl_verify.value();
|
||||
// }
|
||||
// return true;
|
||||
//}
|
||||
|
||||
} // namespace
|
||||
|
||||
@@ -38,72 +28,74 @@ HttpClientSession::HttpClientSession() {
|
||||
InitHeaders();
|
||||
}
|
||||
|
||||
HttpResponsePtr HttpClientSession::Request(HttpRequestArgs&& args) {
|
||||
assert(args.parameters_.size() % 2 == 0);
|
||||
assert(args.headers_.size() % 2 == 0);
|
||||
HttpResponsePtr HttpClientSession::Request(HttpRequestPtr request) {
|
||||
assert(request);
|
||||
|
||||
HttpRequest request{args.method_, args.url_};
|
||||
|
||||
for (std::size_t i = 1; i < args.parameters_.size(); i += 2) {
|
||||
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.
|
||||
// This will overwrite the session-level headers.
|
||||
for (std::size_t i = 1; i < args.headers_.size(); i += 2) {
|
||||
request.SetHeader(std::move(args.headers_[i - 1]),
|
||||
std::move(args.headers_[i]));
|
||||
}
|
||||
|
||||
// No keep-alive?
|
||||
if (!args.keep_alive_) {
|
||||
request.SetHeader(http::headers::kConnection, "Close");
|
||||
}
|
||||
|
||||
if (!args.data_.empty()) {
|
||||
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.
|
||||
if (args.json_) {
|
||||
request.SetContentType(http::media_types::kApplicationJson, charset_);
|
||||
} else if (!content_type_.empty()) {
|
||||
request.SetContentType(content_type_, charset_);
|
||||
for (const auto& h : headers_.data()) {
|
||||
if (!request->HaveHeader(h.first)) {
|
||||
request->SetHeader(h.first, h.second);
|
||||
}
|
||||
}
|
||||
|
||||
request.Prepare();
|
||||
if (!content_type_.empty() &&
|
||||
!request->HaveHeader(http::headers::kContentType)) {
|
||||
request->SetContentType(content_type_, charset_);
|
||||
}
|
||||
|
||||
bool ssl_verify = GetSslVerify(ssl_verify_, args.ssl_verify_);
|
||||
std::size_t buffer_size = GetBufferSize(buffer_size_, args.buffer_size_);
|
||||
request->Prepare();
|
||||
|
||||
const HttpClientPool::Key key{request.url()};
|
||||
return Send(request);
|
||||
}
|
||||
|
||||
void HttpClientSession::InitHeaders() {
|
||||
using namespace http::headers;
|
||||
|
||||
headers_.Set(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_.Set(kAcceptEncoding, "gzip, deflate");
|
||||
|
||||
headers_.Set(kAccept, "*/*");
|
||||
|
||||
headers_.Set(kConnection, "Keep-Alive");
|
||||
}
|
||||
|
||||
HttpResponsePtr HttpClientSession::Send(HttpRequestPtr request) {
|
||||
if (request->buffer_size() == 0) {
|
||||
request->set_buffer_size(buffer_size_);
|
||||
}
|
||||
|
||||
const HttpClientPool::Key key{request->url()};
|
||||
|
||||
// Reuse a connection or not.
|
||||
bool reuse = false;
|
||||
|
||||
HttpClientPtr client = pool_.Get(key);
|
||||
if (!client) {
|
||||
client.reset(new HttpClient{ssl_verify, buffer_size});
|
||||
client.reset(new HttpClient{ssl_verify_});
|
||||
reuse = false;
|
||||
} else {
|
||||
client->set_buffer_size(buffer_size);
|
||||
client->set_ssl_verify(ssl_verify);
|
||||
client->set_ssl_verify(ssl_verify_);
|
||||
reuse = true;
|
||||
LOG_VERB("Reuse an existing connection.");
|
||||
}
|
||||
@@ -143,77 +135,4 @@ HttpResponsePtr HttpClientSession::Request(HttpRequestArgs&& args) {
|
||||
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
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
#include "boost/optional.hpp"
|
||||
|
||||
#include "webcc/http_client_pool.h"
|
||||
#include "webcc/http_request_args.h"
|
||||
#include "webcc/http_request_builder.h"
|
||||
#include "webcc/http_response.h"
|
||||
|
||||
namespace webcc {
|
||||
@@ -28,7 +28,7 @@ public:
|
||||
}
|
||||
|
||||
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) {
|
||||
@@ -41,36 +41,17 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void set_gzip(bool gzip) {
|
||||
gzip_ = gzip;
|
||||
}
|
||||
|
||||
void AddHeader(const std::string& key, const std::string& value) {
|
||||
headers_.Add(key, value);
|
||||
headers_.Set(key, value);
|
||||
}
|
||||
|
||||
HttpResponsePtr Request(HttpRequestArgs&& args);
|
||||
|
||||
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());
|
||||
HttpResponsePtr Request(HttpRequestPtr request);
|
||||
|
||||
private:
|
||||
void InitHeaders();
|
||||
|
||||
HttpResponsePtr Send(HttpRequestPtr request);
|
||||
|
||||
private:
|
||||
// E.g., "application/json".
|
||||
std::string content_type_;
|
||||
@@ -82,7 +63,7 @@ private:
|
||||
HttpHeaderDict headers_;
|
||||
|
||||
// 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.
|
||||
// 0 means default value will be used.
|
||||
@@ -91,12 +72,6 @@ private:
|
||||
// Timeout in seconds for receiving response.
|
||||
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.
|
||||
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);
|
||||
if (it != headers_.end()) {
|
||||
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);
|
||||
if (it != headers_.end()) {
|
||||
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,
|
||||
bool* existed) const {
|
||||
auto it = const_cast<HttpHeaderDict*>(this)->Find(key);
|
||||
|
||||
@@ -31,13 +31,11 @@ public:
|
||||
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 {
|
||||
return const_cast<HttpHeaderDict*>(this)->Find(key) != headers_.end();
|
||||
}
|
||||
bool Have(const std::string& key) const;
|
||||
|
||||
// Get header by index.
|
||||
const HttpHeader& Get(std::size_t index) const {
|
||||
@@ -85,11 +83,11 @@ public:
|
||||
bool IsConnectionKeepAlive() const;
|
||||
|
||||
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) {
|
||||
headers_.Add(std::move(key), std::move(value));
|
||||
headers_.Set(std::move(key), std::move(value));
|
||||
}
|
||||
|
||||
const std::string& GetHeader(const std::string& key,
|
||||
@@ -97,6 +95,10 @@ public:
|
||||
return headers_.Get(key, existed);
|
||||
}
|
||||
|
||||
bool HaveHeader(const std::string& key) const {
|
||||
return headers_.Have(key);
|
||||
}
|
||||
|
||||
http::ContentEncoding GetContentEncoding() const;
|
||||
|
||||
// Return true if header Accept-Encoding contains "gzip".
|
||||
|
||||
@@ -57,6 +57,14 @@ public:
|
||||
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.
|
||||
// Compose start line, set Host header, etc.
|
||||
bool Prepare() final;
|
||||
@@ -64,6 +72,8 @@ public:
|
||||
private:
|
||||
std::string method_;
|
||||
Url url_;
|
||||
|
||||
std::size_t buffer_size_ = 0;
|
||||
};
|
||||
|
||||
} // 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_
|
||||
+17
-14
@@ -10,11 +10,12 @@
|
||||
|
||||
namespace webcc {
|
||||
|
||||
SoapClient::SoapClient(const std::string& url, SoapVersion soap_version,
|
||||
std::size_t buffer_size)
|
||||
: url_(url), soap_version_(soap_version),
|
||||
http_client_(buffer_size),
|
||||
format_raw_(true), error_(kNoError) {
|
||||
SoapClient::SoapClient(const std::string& url, SoapVersion soap_version)
|
||||
: url_(url),
|
||||
soap_version_(soap_version),
|
||||
http_client_(true),
|
||||
format_raw_(true),
|
||||
error_(kNoError) {
|
||||
}
|
||||
|
||||
bool SoapClient::Request(const std::string& operation,
|
||||
@@ -47,23 +48,25 @@ bool SoapClient::Request(const std::string& operation,
|
||||
std::string 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) {
|
||||
http_request.SetContentType(http::media_types::kTextXml,
|
||||
http::charsets::kUtf8);
|
||||
http_request->SetContentType(http::media_types::kTextXml,
|
||||
http::charsets::kUtf8);
|
||||
} else {
|
||||
http_request.SetContentType(http::media_types::kApplicationSoapXml,
|
||||
http::charsets::kUtf8);
|
||||
http_request->SetContentType(http::media_types::kApplicationSoapXml,
|
||||
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();
|
||||
return false;
|
||||
}
|
||||
|
||||
+1
-2
@@ -14,8 +14,7 @@ namespace webcc {
|
||||
class SoapClient {
|
||||
public:
|
||||
explicit SoapClient(const std::string& url,
|
||||
SoapVersion soap_version = kSoapV12,
|
||||
std::size_t buffer_size = 0);
|
||||
SoapVersion soap_version = kSoapV12);
|
||||
|
||||
~SoapClient() = default;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user