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

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

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

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

@ -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]);
for (const auto& h : headers_.data()) {
if (!request->HaveHeader(h.first)) {
request->SetHeader(h.first, h.second);
}
}
// Apply the session-level headers.
for (const HttpHeader& h : headers_.data()) {
request.SetHeader(h.first, h.second);
if (!content_type_.empty() &&
!request->HaveHeader(http::headers::kContentType)) {
request->SetContentType(content_type_, charset_);
}
// 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]));
}
request->Prepare();
// No keep-alive?
if (!args.keep_alive_) {
request.SetHeader(http::headers::kConnection, "Close");
}
return Send(request);
}
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);
}
void HttpClientSession::InitHeaders() {
using namespace http::headers;
// TODO: Request-level charset.
if (args.json_) {
request.SetContentType(http::media_types::kApplicationJson, charset_);
} else if (!content_type_.empty()) {
request.SetContentType(content_type_, charset_);
}
}
headers_.Set(kUserAgent, http::UserAgent());
request.Prepare();
// 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, "*/*");
bool ssl_verify = GetSslVerify(ssl_verify_, args.ssl_verify_);
std::size_t buffer_size = GetBufferSize(buffer_size_, args.buffer_size_);
headers_.Set(kConnection, "Keep-Alive");
}
const HttpClientPool::Key key{request.url()};
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_

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

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

Loading…
Cancel
Save