Add shortcuts for GET, POST, etc. to HttpClientSession.

master
Chunting Gu 6 years ago
parent 52231d52e6
commit 3b7f4ecacf

@ -25,8 +25,7 @@ int main() {
try { try {
webcc::HttpClientSession session; webcc::HttpClientSession session;
auto r = session.Request(webcc::HttpRequestBuilder{}.Get(). auto r = session.Get("http://httpbin.org/get");
url("http://httpbin.org/get")());
std::cout << r->content() << std::endl; std::cout << r->content() << std::endl;
@ -40,8 +39,7 @@ int main() {
List GitHub public events: List GitHub public events:
```cpp ```cpp
auto r = session.Request(webcc::HttpRequestBuilder{}.Get(). auto r = session.Get("https://api.github.com/events");
url("https://api.github.com/events")());
// Parse r->content() to JSON object... // Parse r->content() to JSON object...
``` ```

@ -18,8 +18,6 @@ bool kSslVerify = false;
bool kSslVerify = true; bool kSslVerify = true;
#endif #endif
const std::size_t kBufferSize = 1500;
const std::string kUrlRoot = "https://api.github.com"; const std::string kUrlRoot = "https://api.github.com";
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
@ -65,8 +63,7 @@ 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.Request(webcc::HttpRequestBuilder{}.Get(). auto r = session.Get(kUrlRoot + "/events");
url(kUrlRoot + "/events")());
PRINT_JSON_STRING(r->content()); PRINT_JSON_STRING(r->content());
@ -81,8 +78,7 @@ 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.Request(webcc::HttpRequestBuilder{}.Get(). auto r = session.Get(kUrlRoot + "/users/" + user + "/followers");
url(kUrlRoot + "/users/" + user + "/followers")());
PRINT_JSON_STRING(r->content()); PRINT_JSON_STRING(r->content());
@ -99,9 +95,8 @@ 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.Request(webcc::HttpRequestBuilder{}.Get(). auto r = session.Get(kUrlRoot + "/user/followers", {},
url(kUrlRoot + "/user/followers"). {"Authorization", auth});
header("Authorization", auth)());
PRINT_JSON_STRING(r->content()); PRINT_JSON_STRING(r->content());
@ -116,11 +111,8 @@ 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.Request(webcc::HttpRequestBuilder{}.Post(). auto r = session.Post(kUrlRoot + "/authorizations", std::move(data), true,
url(kUrlRoot + "/authorizations"). {"Authorization", auth});
data(std::move(data)).
json(true).
header("Authorization", auth)());
std::cout << r->content() << std::endl; std::cout << r->content() << std::endl;

@ -21,12 +21,24 @@ bool kSslVerify = true;
void ExampleBasic() { void ExampleBasic() {
webcc::HttpClientSession session; webcc::HttpClientSession session;
auto r = session.Request(webcc::HttpRequestBuilder{}.Get(). auto r = session.Request(webcc::HttpRequestBuilder{}
url("http://httpbin.org/get"). .Get()
parameter("key1", "value1"). .url("http://httpbin.org/get")
parameter("key2", "value2"). .parameter("key1", "value1")
header("Accept", "application/json"). .parameter("key2", "value2")
buffer(1000)()); .header("Accept", "application/json")
.buffer(1000)());
std::cout << r->content() << std::endl;
}
// Use predefined shortcuts.
void ExampleShortcut() {
webcc::HttpClientSession session;
auto r = session.Get("http://httpbin.org/get",
{"key1", "value1", "key2", "value2"},
{"Accept", "application/json"});
std::cout << r->content() << std::endl; std::cout << r->content() << std::endl;
} }
@ -36,10 +48,11 @@ void ExampleHttps() {
webcc::HttpClientSession session; webcc::HttpClientSession session;
session.set_ssl_verify(kSslVerify); session.set_ssl_verify(kSslVerify);
auto r = session.Request(webcc::HttpRequestBuilder{}.Get(). auto r = session.Request(webcc::HttpRequestBuilder{}
url("https://httpbin.org/get"). .Get()
parameter("key1", "value1"). .url("https://httpbin.org/get")
header("Accept", "application/json")()); .parameter("key1", "value1")
.header("Accept", "application/json")());
std::cout << r->content() << std::endl; std::cout << r->content() << std::endl;
} }
@ -61,25 +74,23 @@ void ExampleKeepAlive(const std::string& url) {
session.set_ssl_verify(kSslVerify); session.set_ssl_verify(kSslVerify);
// Keep-Alive // Keep-Alive
session.Request(webcc::HttpRequestBuilder{}.Get().url(url)()); session.Get(url);
// Close // Close
session.Request(webcc::HttpRequestBuilder{}.Get().url(url).keep_alive(false)()); session.Get(url, {}, {"Connection", "Close"});
// Keep-Alive // Keep-Alive
session.Request(webcc::HttpRequestBuilder{}.Get().url(url)()); session.Get(url);
} }
void ExampleCompression() { void ExampleCompression() {
HttpClientSession session; HttpClientSession session;
auto r = session.Request(webcc::HttpRequestBuilder{}. auto r = session.Get("http://httpbin.org/gzip");
Get().url("http://httpbin.org/gzip")());
std::cout << r->content() << std::endl; std::cout << r->content() << std::endl;
r = session.Request(webcc::HttpRequestBuilder{}. r = session.Get("http://httpbin.org/deflate");
Get().url("http://httpbin.org/deflate")());
std::cout << r->content() << std::endl; std::cout << r->content() << std::endl;
} }

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

@ -40,7 +40,7 @@ public:
: sleep_seconds_(sleep_seconds) { : sleep_seconds_(sleep_seconds) {
} }
public: protected:
// Get a list of books based on query parameters. // Get a list of books based on query parameters.
void Get(const webcc::UrlQuery& query, webcc::RestResponse* response) final; void Get(const webcc::UrlQuery& query, webcc::RestResponse* response) final;
@ -64,7 +64,7 @@ public:
: sleep_seconds_(sleep_seconds) { : sleep_seconds_(sleep_seconds) {
} }
public: protected:
// Get the detailed information of a book. // Get the detailed information of a book.
void Get(const webcc::UrlMatches& url_matches, void Get(const webcc::UrlMatches& url_matches,
const webcc::UrlQuery& query, const webcc::UrlQuery& query,

@ -39,14 +39,22 @@ const std::size_t kGzipThreshold = 1400;
// HTTP headers. // HTTP headers.
namespace http { namespace http {
namespace methods {
// HTTP methods (verbs) in string. // HTTP methods (verbs) in string.
// Don't use enum to avoid converting back and forth. // Don't use enum to avoid converting back and forth.
const char* const kHead = "HEAD";
const char* const kGet = "GET"; const char* const kGet = "GET";
const char* const kPost = "POST"; const char* const kHead = "HEAD";
const char* const kPatch = "PATCH"; const char* const kPost = "POST";
const char* const kPut = "PUT"; const char* const kPut = "PUT";
const char* const kDelete = "DELETE"; const char* const kDelete = "DELETE";
const char* const kConnect = "CONNECT";
const char* const kOptions = "OPTIONS";
const char* const kTrace = "TRACE";
const char* const kPatch = "PATCH";
} // namespace methods
// HTTP response status. // HTTP response status.
// This is not a full list. // This is not a full list.
@ -69,6 +77,7 @@ namespace headers {
// NOTE: Field names are case-insensitive. // NOTE: Field names are case-insensitive.
// See https://stackoverflow.com/a/5259004 for more details. // See https://stackoverflow.com/a/5259004 for more details.
const char* const kHost = "Host"; const char* const kHost = "Host";
const char* const kDate = "Date"; const char* const kDate = "Date";
const char* const kContentType = "Content-Type"; const char* const kContentType = "Content-Type";

@ -9,9 +9,9 @@ using boost::asio::ip::tcp;
namespace webcc { namespace webcc {
HttpClient::HttpClient(bool ssl_verify) HttpClient::HttpClient()
: timer_(io_context_), : timer_(io_context_),
ssl_verify_(ssl_verify), ssl_verify_(true),
timeout_(kMaxReadSeconds), timeout_(kMaxReadSeconds),
closed_(false), closed_(false),
timer_canceled_(false), timer_canceled_(false),

@ -27,7 +27,7 @@ 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); HttpClient();
virtual ~HttpClient() = default; virtual ~HttpClient() = default;
@ -94,8 +94,7 @@ 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_;
// Maximum seconds to wait before the client cancels the operation. // Timeout (seconds) for receiving response.
// Only for reading response from server.
int timeout_; int timeout_;
// Connection closed. // Connection closed.

@ -5,25 +5,6 @@
namespace webcc { namespace webcc {
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;
//}
} // namespace
// -----------------------------------------------------------------------------
HttpClientSession::HttpClientSession() { HttpClientSession::HttpClientSession() {
InitHeaders(); InitHeaders();
} }
@ -47,6 +28,83 @@ HttpResponsePtr HttpClientSession::Request(HttpRequestPtr request) {
return Send(request); return Send(request);
} }
static void SetHeaders(const std::vector<std::string>& headers,
HttpRequestBuilder* builder) {
assert(headers.size() % 2 == 0);
for (std::size_t i = 1; i < headers.size(); i += 2) {
builder->header(headers[i - 1], headers[i]);
}
}
HttpResponsePtr HttpClientSession::Get(const std::string& url,
const std::vector<std::string>& parameters,
const std::vector<std::string>& headers) {
HttpRequestBuilder builder{http::methods::kGet};
builder.url(url);
assert(parameters.size() % 2 == 0);
for (std::size_t i = 1; i < parameters.size(); i += 2) {
builder.parameter(parameters[i - 1], parameters[i]);
}
SetHeaders(headers, &builder);
return Request(builder());
}
HttpResponsePtr HttpClientSession::Post(const std::string& url,
std::string&& data, bool json,
const std::vector<std::string>& headers) {
HttpRequestBuilder builder{http::methods::kPost};
builder.url(url);
SetHeaders(headers, &builder);
builder.data(std::move(data));
builder.json(json);
return Request(builder());
}
HttpResponsePtr HttpClientSession::Put(const std::string& url,
std::string&& data, bool json,
const std::vector<std::string>& headers) {
HttpRequestBuilder builder{http::methods::kPut};
builder.url(url);
SetHeaders(headers, &builder);
builder.data(std::move(data));
builder.json(json);
return Request(builder());
}
HttpResponsePtr HttpClientSession::Delete(const std::string& url,
const std::vector<std::string>& headers) {
HttpRequestBuilder builder{http::methods::kDelete};
builder.url(url);
SetHeaders(headers, &builder);
return Request(builder());
}
HttpResponsePtr HttpClientSession::Patch(const std::string& url,
std::string&& data, bool json,
const std::vector<std::string>& headers) {
HttpRequestBuilder builder{http::methods::kPatch};
builder.url(url);
SetHeaders(headers, &builder);
builder.data(std::move(data));
builder.json(json);
return Request(builder());
}
void HttpClientSession::InitHeaders() { void HttpClientSession::InitHeaders() {
using namespace http::headers; using namespace http::headers;
@ -92,14 +150,14 @@ HttpResponsePtr HttpClientSession::Send(HttpRequestPtr request) {
HttpClientPtr client = pool_.Get(key); HttpClientPtr client = pool_.Get(key);
if (!client) { if (!client) {
client.reset(new HttpClient{ssl_verify_}); client.reset(new HttpClient{});
reuse = false; reuse = false;
} else { } else {
client->set_ssl_verify(ssl_verify_);
reuse = true;
LOG_VERB("Reuse an existing connection."); LOG_VERB("Reuse an existing connection.");
reuse = true;
} }
client->set_ssl_verify(ssl_verify_);
client->set_timeout(timeout_); client->set_timeout(timeout_);
bool ok = client->Request(request, !reuse); bool ok = client->Request(request, !reuse);

@ -41,12 +41,35 @@ public:
} }
} }
void AddHeader(const std::string& key, const std::string& value) { void SetHeader(const std::string& key, const std::string& value) {
headers_.Set(key, value); headers_.Set(key, value);
} }
// Send a request.
// Please use HttpRequestBuilder to build the request.
HttpResponsePtr Request(HttpRequestPtr request); HttpResponsePtr Request(HttpRequestPtr request);
// Shortcut for GET request.
HttpResponsePtr Get(const std::string& url,
const std::vector<std::string>& parameters = {},
const std::vector<std::string>& headers = {});
// Shortcut for POST request.
HttpResponsePtr Post(const std::string& url, std::string&& data, bool json,
const std::vector<std::string>& headers = {});
// Shortcut for PUT request.
HttpResponsePtr Put(const std::string& url, std::string&& data, bool json,
const std::vector<std::string>& headers = {});
// Shortcut for DELETE request.
HttpResponsePtr Delete(const std::string& url,
const std::vector<std::string>& headers = {});
// Shortcut for PATCH request.
HttpResponsePtr Patch(const std::string& url, std::string&& data, bool json,
const std::vector<std::string>& headers = {});
private: private:
void InitHeaders(); void InitHeaders();

@ -11,7 +11,6 @@
namespace webcc { namespace webcc {
class HttpRequest; class HttpRequest;
typedef std::shared_ptr<HttpRequest> HttpRequestPtr; typedef std::shared_ptr<HttpRequest> HttpRequestPtr;
class HttpRequest : public HttpMessage { class HttpRequest : public HttpMessage {
@ -37,6 +36,14 @@ public:
url_.AddParameter(key, value); url_.AddParameter(key, value);
} }
void set_buffer_size(std::size_t buffer_size) {
buffer_size_ = buffer_size;
}
void set_ssl_verify(bool ssl_verify) {
ssl_verify_ = ssl_verify;
}
const std::string& method() const { const std::string& method() const {
return method_; return method_;
} }
@ -61,8 +68,8 @@ public:
return buffer_size_; return buffer_size_;
} }
void set_buffer_size(std::size_t buffer_size) { bool ssl_verify() const {
buffer_size_ = buffer_size; return ssl_verify_;
} }
// Prepare payload. // Prepare payload.
@ -73,6 +80,11 @@ private:
std::string method_; std::string method_;
Url url_; Url url_;
// Verify the certificate of the peer or not (for HTTPS).
bool ssl_verify_ = true;
// The size of the buffer for reading response.
// 0 means default value will be used.
std::size_t buffer_size_ = 0; std::size_t buffer_size_ = 0;
}; };

@ -4,26 +4,25 @@
#include <string> #include <string>
#include <vector> #include <vector>
#include "boost/optional.hpp"
#include "webcc/http_request.h" #include "webcc/http_request.h"
namespace webcc { namespace webcc {
// TODO: Add timeout()
// HTTP request builder.
class HttpRequestBuilder { class HttpRequestBuilder {
public: public:
HttpRequestBuilder() = default; explicit HttpRequestBuilder(const std::string& method = "")
: method_(method) {
}
// Build the request. // Build the request.
HttpRequestPtr operator()(); HttpRequestPtr operator()();
HttpRequestBuilder& Get() { return method(http::kGet); } HttpRequestBuilder& Get() { return method(http::methods::kGet); }
HttpRequestBuilder& Post() { return method(http::kPost); } HttpRequestBuilder& Head() { return method(http::methods::kHead); }
HttpRequestBuilder& Put() { return method(http::kPut); } HttpRequestBuilder& Post() { return method(http::methods::kPost); }
HttpRequestBuilder& Delete() { return method(http::kDelete); } HttpRequestBuilder& Put() { return method(http::methods::kPut); }
HttpRequestBuilder& Delete() { return method(http::methods::kDelete); }
HttpRequestBuilder& Patch() { return method(http::methods::kPatch); }
HttpRequestBuilder& method(const std::string& method) { HttpRequestBuilder& method(const std::string& method) {
method_ = method; method_ = method;
@ -69,11 +68,6 @@ public:
return *this; return *this;
} }
HttpRequestBuilder& ssl_verify(bool ssl_verify = true) {
ssl_verify_.emplace(ssl_verify);
return *this;
}
HttpRequestBuilder& buffer(std::size_t buffer) { HttpRequestBuilder& buffer(std::size_t buffer) {
buffer_ = buffer; buffer_ = buffer;
return *this; return *this;
@ -107,9 +101,6 @@ private:
// Additional request headers. // Additional request headers.
std::vector<std::string> 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. // Size of the buffer to read response.
// Leave it to 0 for using default value. // Leave it to 0 for using default value.
std::size_t buffer_ = 0; std::size_t buffer_ = 0;

@ -8,9 +8,9 @@ namespace webcc {
void RestListService::Handle(const RestRequest& request, void RestListService::Handle(const RestRequest& request,
RestResponse* response) { RestResponse* response) {
if (request.method == http::kGet) { if (request.method == http::methods::kGet) {
Get(UrlQuery(request.url_query_str), response); Get(UrlQuery(request.url_query_str), response);
} else if (request.method == http::kPost) { } else if (request.method == http::methods::kPost) {
Post(request.content, response); Post(request.content, response);
} else { } else {
LOG_ERRO("RestListService doesn't support '%s' method.", LOG_ERRO("RestListService doesn't support '%s' method.",
@ -22,13 +22,13 @@ void RestListService::Handle(const RestRequest& request,
void RestDetailService::Handle(const RestRequest& request, void RestDetailService::Handle(const RestRequest& request,
RestResponse* response) { RestResponse* response) {
if (request.method == http::kGet) { if (request.method == http::methods::kGet) {
Get(request.url_matches, UrlQuery(request.url_query_str), response); Get(request.url_matches, UrlQuery(request.url_query_str), response);
} else if (request.method == http::kPut) { } else if (request.method == http::methods::kPut) {
Put(request.url_matches, request.content, response); Put(request.url_matches, request.content, response);
} else if (request.method == http::kPatch) { } else if (request.method == http::methods::kPatch) {
Patch(request.url_matches, request.content, response); Patch(request.url_matches, request.content, response);
} else if (request.method == http::kDelete) { } else if (request.method == http::methods::kDelete) {
Delete(request.url_matches, response); Delete(request.url_matches, response);
} else { } else {
LOG_ERRO("RestDetailService doesn't support '%s' method.", LOG_ERRO("RestDetailService doesn't support '%s' method.",

@ -13,7 +13,6 @@ namespace webcc {
SoapClient::SoapClient(const std::string& url, SoapVersion soap_version) SoapClient::SoapClient(const std::string& url, SoapVersion soap_version)
: url_(url), : url_(url),
soap_version_(soap_version), soap_version_(soap_version),
http_client_(true),
format_raw_(true), format_raw_(true),
error_(kNoError) { error_(kNoError) {
} }
@ -48,7 +47,7 @@ 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);
auto http_request = std::make_shared<HttpRequest>(http::kPost, url_); auto http_request = std::make_shared<HttpRequest>(http::methods::kPost, url_);
http_request->SetContent(std::move(http_content), true); http_request->SetContent(std::move(http_content), true);

Loading…
Cancel
Save