Refine client session and its examples.
This commit is contained in:
@@ -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";
|
||||
|
||||
static Json::Value StringToJson(const std::string& str) {
|
||||
// -----------------------------------------------------------------------------
|
||||
// JSON helper functions (based on cppjson).
|
||||
|
||||
// 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));
|
||||
void ListAuthUserFollowers(const std::string& auth) {
|
||||
webcc::HttpClientSession session;
|
||||
session.set_ssl_verify(kSslVerify);
|
||||
|
||||
try {
|
||||
auto r = session.Get(kUrlRoot + "/user/followers", {},
|
||||
{ "Authorization", auth });
|
||||
|
||||
if (r) {
|
||||
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();
|
||||
|
||||
//ListAuthorizedUserFollowers(client, "Basic c3ByaW5mYWxsQGdtYWlsLmNvbTpYaWFvTHVhbjFA");
|
||||
ListAuthorizedUserFollowers(session, "Token 1d42e2cce49929f2d24b1b6e96260003e5b3e1b0");
|
||||
//ListUserFollowers("<login>");
|
||||
|
||||
//ListAuthUserFollowers("Basic <base64 encoded login:password>");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
+62
-94
@@ -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));
|
||||
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));
|
||||
|
||||
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;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
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));
|
||||
|
||||
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.
|
||||
// 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);
|
||||
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));
|
||||
// Note the std::move().
|
||||
session.Request(std::move(args));
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Use pre-defined wrappers.
|
||||
void ExampleWrappers() {
|
||||
HttpClientSession session;
|
||||
|
||||
r = session.Get("http://httpbin.org/get",
|
||||
{ "key1", "value1", "key2", "value2" },
|
||||
session.Get("http://httpbin.org/get", {"key1", "value1", "key2", "value2"},
|
||||
{"Accept", "application/json"},
|
||||
HttpRequestArgs{}.buffer_size(1000));
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// HTTPS is auto-detected from the URL schema.
|
||||
|
||||
try {
|
||||
r = session.Post("httpt://httpbin.org/post", "{ 'key': 'value' }", true,
|
||||
{"Accept", "application/json"},
|
||||
HttpRequestArgs{}.ssl_verify(false).buffer_size(1000));
|
||||
|
||||
std::cout << r->status() << std::endl << r->content() << std::endl;
|
||||
|
||||
} 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;
|
||||
}
|
||||
// HTTPS is auto-detected from the URL scheme.
|
||||
void ExampleHttps() {
|
||||
HttpClientSession session;
|
||||
|
||||
auto r = session.Request(HttpRequestArgs{"GET"}
|
||||
.url("https://httpbin.org/get")
|
||||
.parameters({"key1", "value1", "key2", "value2"})
|
||||
.headers({"Accept", "application/json"})
|
||||
.ssl_verify(kSslVerify));
|
||||
|
||||
std::cout << r->content() << 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));
|
||||
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" }));
|
||||
|
||||
void TestKeepAlive4(HttpClientSession& session) {
|
||||
try {
|
||||
auto r = session.Request(webcc::HttpRequestArgs("GET").
|
||||
url("https://www.google.com").
|
||||
ssl_verify(false));
|
||||
|
||||
//std::cout << r->content() << std::endl;
|
||||
|
||||
} catch (const Exception& e) {
|
||||
std::cout << "Exception: " << e.what() << std::endl;
|
||||
}
|
||||
// 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();
|
||||
|
||||
session.Get("http://httpbin.org/get", {}, { "Connection", "Close" });
|
||||
// Boost.org doesn't support persistent connection so always includes
|
||||
// "Connection: Close" header in the response.
|
||||
|
||||
session.Get("http://httpbin.org/get");
|
||||
// Both Google and GitHub support persistent connection but they don't like
|
||||
// to include "Connection: Keep-Alive" header in the responses.
|
||||
|
||||
//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.
|
||||
|
||||
Reference in New Issue
Block a user