Refine client session and its examples.

master
Chunting Gu 6 years ago
parent 5abd7e275c
commit 05782b773a

@ -1,5 +1,4 @@
#include <iostream>
#include <map>
#include "json/json.h"
@ -21,9 +20,13 @@ bool kSslVerify = true;
const std::size_t kBufferSize = 1500;
const std::string kUrlRoot = "https://api.github.com";
// -----------------------------------------------------------------------------
// JSON helper functions (based on cppjson).
static Json::Value StringToJson(const std::string& str) {
// Parse a string to JSON object.
Json::Value StringToJson(const std::string& str) {
Json::Value json;
Json::CharReaderBuilder builder;
@ -37,7 +40,7 @@ static Json::Value StringToJson(const std::string& str) {
}
// Print the JSON string in pretty format.
static void PrettyPrintJsonString(const std::string& str) {
void PrettyPrintJsonString(const std::string& str) {
Json::Value json = StringToJson(str);
Json::StreamWriterBuilder builder;
@ -57,48 +60,48 @@ static void PrettyPrintJsonString(const std::string& str) {
#define PRINT_JSON_STRING(str)
#endif // PRINT_RESPONSE
//static void PrintError(const webcc::RestSslClient& client) {
// std::cout << webcc::DescribeError(client.error());
// if (client.timed_out()) {
// std::cout << " (timed out)";
// }
// std::cout << std::endl;
//}
//
//// -----------------------------------------------------------------------------
//
//// List public events.
//static void ListEvents(webcc::RestSslClient& client) {
// if (client.Get("/events")) {
// PRINT_JSON_STRING(client.response_content());
// } else {
// PrintError(client);
// }
//}
//
//// List the followers of the given user.
//static void ListUserFollowers(webcc::RestSslClient& client,
// const std::string& user) {
// if (client.Get("/users/" + user + "/followers")) {
// PRINT_JSON_STRING(client.response_content());
// } else {
// PrintError(client);
// }
//}
// -----------------------------------------------------------------------------
// List public events.
void ListEvents() {
webcc::HttpClientSession session;
session.set_ssl_verify(kSslVerify);
try {
auto r = session.Get(kUrlRoot + "/events");
PRINT_JSON_STRING(r->content());
} catch (const webcc::Exception& e) {
std::cout << e.what() << std::endl;
}
}
// List the followers of the given user.
void ListUserFollowers(const std::string& user) {
webcc::HttpClientSession session;
session.set_ssl_verify(kSslVerify);
try {
auto r = session.Get(kUrlRoot + "/users/" + user + "/followers");
PRINT_JSON_STRING(r->content());
} catch (const webcc::Exception& e) {
std::cout << e.what() << std::endl;
}
}
// List the followers of the current authorized user.
// Header syntax: Authorization: <type> <credentials>
static void ListAuthorizedUserFollowers(webcc::HttpClientSession& session,
const std::string& auth) {
auto r = session.Request(webcc::HttpRequestArgs("GET").
url("https://api.github.com/user/followers").
headers({ { "Authorization", auth } }).
ssl_verify(kSslVerify).buffer_size(kBufferSize));
if (r) {
void ListAuthUserFollowers(const std::string& auth) {
webcc::HttpClientSession session;
session.set_ssl_verify(kSslVerify);
try {
auto r = session.Get(kUrlRoot + "/user/followers", {},
{ "Authorization", auth });
PRINT_JSON_STRING(r->content());
} else {
//PrintError(client);
} catch (const webcc::Exception& e) {
std::cout << e.what() << std::endl;
}
}
@ -107,10 +110,11 @@ static void ListAuthorizedUserFollowers(webcc::HttpClientSession& session,
int main() {
WEBCC_LOG_INIT("", webcc::LOG_CONSOLE);
webcc::HttpClientSession session;
//ListEvents();
//ListUserFollowers("<login>");
//ListAuthorizedUserFollowers(client, "Basic c3ByaW5mYWxsQGdtYWlsLmNvbTpYaWFvTHVhbjFA");
ListAuthorizedUserFollowers(session, "Token 1d42e2cce49929f2d24b1b6e96260003e5b3e1b0");
//ListAuthUserFollowers("Basic <base64 encoded login:password>");
return 0;
}

@ -1,5 +1,4 @@
#include <iostream>
#include <thread>
#include "webcc/http_client_session.h"
#include "webcc/logger.h"
@ -18,130 +17,99 @@ bool kSslVerify = true;
// -----------------------------------------------------------------------------
void GetBoostOrgLicense(HttpClientSession& session) {
try {
auto r = session.Request(HttpRequestArgs{ "GET" }.
url("https://www.boost.org/LICENSE_1_0.txt").
ssl_verify(kSslVerify));
std::cout << r->content() << std::endl;
} catch (const webcc::Exception& e) {
std::cout << "Exception: " << e.what() << std::endl;
}
}
// TODO
void Test(HttpClientSession& session) {
HttpResponsePtr r;
// ---------------------------------------------------------------------------
void ExampleBasic() {
HttpClientSession session;
r = session.Request(HttpRequestArgs{ "GET" }.
url("http://httpbin.org/get"). // moved
parameters({ "key1", "value1", "key2", "value2" }). // moved
headers({ "Accept", "application/json" }). // moved
buffer_size(1000));
auto r = session.Request(HttpRequestArgs{"GET"}
.url("http://httpbin.org/get")
.parameters({"key1", "value1", "key2", "value2"})
.headers({"Accept", "application/json"})
.buffer_size(1000));
std::cout << r->content() << std::endl;
}
// ---------------------------------------------------------------------------
// If you want to create the args object firstly, there'll be an extra call
// to its move constructor.
// - constructor: HttpRequestArgs{ "GET" }
// - move constructor: auto args = ...
auto args = HttpRequestArgs{"GET"}.
url("http://httpbin.org/get").
parameters({ "key1", "value1", "key2", "value2" }).
headers({ "Accept", "application/json" }).
buffer_size(1000);
r = session.Request(std::move(args));
// 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;
// ---------------------------------------------------------------------------
// Use pre-defined wrappers.
auto args = HttpRequestArgs{"GET"}
.url("http://httpbin.org/get")
.parameters({"key1", "value1", "key2", "value2"})
.headers({"Accept", "application/json"})
.buffer_size(1000);
r = session.Get("http://httpbin.org/get",
{ "key1", "value1", "key2", "value2" },
{ "Accept", "application/json" },
HttpRequestArgs{}.buffer_size(1000));
// Note the std::move().
session.Request(std::move(args));
}
// ---------------------------------------------------------------------------
// HTTPS is auto-detected from the URL schema.
// Use pre-defined wrappers.
void ExampleWrappers() {
HttpClientSession session;
try {
r = session.Post("httpt://httpbin.org/post", "{ 'key': 'value' }", true,
session.Get("http://httpbin.org/get", {"key1", "value1", "key2", "value2"},
{"Accept", "application/json"},
HttpRequestArgs{}.ssl_verify(false).buffer_size(1000));
std::cout << r->status() << std::endl << r->content() << std::endl;
HttpRequestArgs{}.buffer_size(1000));
} catch (const webcc::Exception& e) {
std::cout << "Exception: " << e.what() << std::endl;
}
session.Post("http://httpbin.org/post", "{ 'key': 'value' }", true,
{"Accept", "application/json"});
}
void TestKeepAlive2(HttpClientSession& session) {
try {
auto r = session.Request(webcc::HttpRequestArgs("GET").
url("https://api.github.com/events").
ssl_verify(false).buffer_size(1500));
} catch (const Exception& e) {
std::cout << "Exception: " << e.what() << std::endl;
}
}
void TestKeepAlive3(HttpClientSession& session) {
try {
auto r = session.Request(webcc::HttpRequestArgs("GET").
url("https://www.boost.org/LICENSE_1_0.txt").
ssl_verify(false));
// HTTPS is auto-detected from the URL scheme.
void ExampleHttps() {
HttpClientSession session;
//std::cout << r->content() << std::endl;
auto r = session.Request(HttpRequestArgs{"GET"}
.url("https://httpbin.org/get")
.parameters({"key1", "value1", "key2", "value2"})
.headers({"Accept", "application/json"})
.ssl_verify(kSslVerify));
} catch (const Exception& e) {
std::cout << "Exception: " << e.what() << std::endl;
}
std::cout << r->content() << std::endl;
}
void TestKeepAlive4(HttpClientSession& session) {
try {
auto r = session.Request(webcc::HttpRequestArgs("GET").
url("https://www.google.com").
ssl_verify(false));
void ExampleKeepAlive(const std::string& url) {
HttpClientSession session;
//std::cout << r->content() << std::endl;
// Keep-Alive
session.Request(webcc::HttpRequestArgs("GET").url(url).
ssl_verify(kSslVerify));
} catch (const Exception& e) {
std::cout << "Exception: " << e.what() << std::endl;
}
// Close
session.Request(webcc::HttpRequestArgs("GET").url(url).
ssl_verify(kSslVerify).
headers({ "Connection", "Close" }));
// Keep-Alive
session.Request(webcc::HttpRequestArgs("GET").url(url).
ssl_verify(kSslVerify));
}
// -----------------------------------------------------------------------------
void Sleep(int seconds) {
if (seconds > 0) {
LOG_INFO("Sleep %d seconds...", seconds);
std::this_thread::sleep_for(std::chrono::seconds(seconds));
}
}
int main() {
WEBCC_LOG_INIT("", LOG_CONSOLE);
HttpClientSession session;
// TEST keep-alive.
// Note that the exception handling is mandatory.
try {
// Keep-Alive by default
session.Get("http://httpbin.org/get");
//ExampleBasic();
//ExampleArgs();
//ExampleWrappers();
//ExampleHttps();
// Boost.org doesn't support persistent connection so always includes
// "Connection: Close" header in the response.
session.Get("http://httpbin.org/get", {}, { "Connection", "Close" });
// Both Google and GitHub support persistent connection but they don't like
// to include "Connection: Keep-Alive" header in the responses.
session.Get("http://httpbin.org/get");
//ExampleKeepAlive("http://httpbin.org/get");
//ExampleKeepAlive("https://www.boost.org/LICENSE_1_0.txt");
//ExampleKeepAlive("https://www.google.com");
//ExampleKeepAlive("https://api.github.com/events");
} catch (const Exception& e) {
std::cout << "Exception: " << e.what() << std::endl;

@ -139,6 +139,10 @@ public:
return msg_.c_str();
}
Error error() const { return error_; }
bool timeout() const { return timeout_; }
private:
Error error_;

@ -25,10 +25,16 @@ HttpClientPtr HttpClientPool::Get(const Key& key) const {
void HttpClientPool::Add(const Key& key, HttpClientPtr client) {
clients_[key] = client;
LOG_INFO("Added connection to pool (%s, %s, %s).",
key.scheme.c_str(), key.host.c_str(), key.port.c_str());
}
void HttpClientPool::Remove(const Key& key) {
clients_.erase(key);
LOG_INFO("Removed connection from pool (%s, %s, %s).",
key.scheme.c_str(), key.host.c_str(), key.port.c_str());
}
} // namespace webcc

@ -43,35 +43,39 @@ HttpResponsePtr HttpClientSession::Request(HttpRequestArgs&& args) {
request.Prepare();
// Determine SSL verify flag.
bool ssl_verify = true;
if (args.ssl_verify_) {
ssl_verify = args.ssl_verify_.value();
} else if (ssl_verify_) {
ssl_verify = ssl_verify_.value();
}
bool reuse = false;
const HttpClientPool::Key key{ request.url() };
bool new_created = false;
HttpClientPtr client = pool_.Get(key);
if (!client) {
new_created = true;
client.reset(new HttpClient{ 0, args.ssl_verify_ });
client.reset(new HttpClient{ 0, ssl_verify });
reuse = false;
} else {
new_created = false;
// TODO: Apply args.ssl_verify even if reuse a client.
reuse = false;
LOG_VERB("Reuse an existing connection.");
}
if (!client->Request(request, args.buffer_size_, new_created)) {
if (!client->Request(request, args.buffer_size_, !reuse)) {
throw Exception(client->error(), client->timed_out());
}
if (new_created) {
if (!client->closed()) {
pool_.Add(key, client);
LOG_VERB("Added connection to the pool (%s, %s, %s).",
key.scheme.c_str(), key.host.c_str(), key.port.c_str());
}
} else {
// Update pool.
if (reuse) {
if (client->closed()) {
pool_.Remove(key);
LOG_VERB("Removed connection from the pool (%s, %s, %s).",
key.scheme.c_str(), key.host.c_str(), key.port.c_str());
}
} else {
if (!client->closed()) {
pool_.Add(key, client);
}
}

@ -5,6 +5,8 @@
#include <string>
#include <vector>
#include "boost/optional.hpp"
#include "webcc/http_client_pool.h"
#include "webcc/http_request_args.h"
#include "webcc/http_response.h"
@ -29,6 +31,10 @@ public:
headers_.Add(key, value);
}
void set_ssl_verify(bool ssl_verify) {
ssl_verify_.emplace(ssl_verify);
}
HttpResponsePtr Request(HttpRequestArgs&& args);
HttpResponsePtr Get(const std::string& url,
@ -43,6 +49,7 @@ public:
private:
void InitHeaders();
private:
// E.g., "application/json".
std::string content_type_;
@ -52,6 +59,9 @@ private:
// Headers for each request sent from this session.
HttpHeaderDict headers_;
// Verify the certificate of the peer or not.
boost::optional<bool> ssl_verify_;
// Connection pool for keep-alive.
HttpClientPool pool_;
};

@ -5,6 +5,8 @@
#include <utility>
#include <vector>
#include "boost/optional.hpp"
#include "webcc/globals.h"
#include "webcc/logger.h"
@ -17,7 +19,7 @@ class HttpClientSession;
class HttpRequestArgs {
public:
explicit HttpRequestArgs(const std::string& method = "")
: method_(method), json_(false), ssl_verify_(true), buffer_size_(0) {
: method_(method), json_(false), buffer_size_(0) {
LOG_VERB("HttpRequestArgs()");
}
@ -78,7 +80,7 @@ public:
}
HttpRequestArgs&& ssl_verify(bool ssl_verify = true) {
ssl_verify_ = ssl_verify;
ssl_verify_.emplace(ssl_verify);
return std::move(*this);
}
@ -94,6 +96,7 @@ private:
std::string url_;
// URL query parameters.
std::vector<std::string> parameters_;
// Data to send in the body of the request.
@ -102,11 +105,11 @@ private:
// Is the data to send a JSON string?
bool json_;
// Additional request headers.
std::vector<std::string> headers_;
// Verify the certificate of the peer (remote server) or not.
// HTTPS only.
bool ssl_verify_;
// 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.

Loading…
Cancel
Save