Refine client session and its examples.

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

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

@ -1,5 +1,4 @@
#include <iostream> #include <iostream>
#include <thread>
#include "webcc/http_client_session.h" #include "webcc/http_client_session.h"
#include "webcc/logger.h" #include "webcc/logger.h"
@ -18,130 +17,99 @@ bool kSslVerify = true;
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
void GetBoostOrgLicense(HttpClientSession& session) { void ExampleBasic() {
try { HttpClientSession session;
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;
// ---------------------------------------------------------------------------
r = session.Request(HttpRequestArgs{ "GET" }. auto r = session.Request(HttpRequestArgs{"GET"}
url("http://httpbin.org/get"). // moved .url("http://httpbin.org/get")
parameters({ "key1", "value1", "key2", "value2" }). // moved .parameters({"key1", "value1", "key2", "value2"})
headers({ "Accept", "application/json" }). // moved .headers({"Accept", "application/json"})
buffer_size(1000)); .buffer_size(1000));
std::cout << r->content() << std::endl; 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).
// If you want to create the args object firstly, there'll be an extra call // - constructor: HttpRequestArgs{ "GET" }
// to its move constructor. // - move constructor: auto args = ...
// - constructor: HttpRequestArgs{ "GET" } void ExampleArgs() {
// - move constructor: auto args = ... HttpClientSession session;
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));
// ---------------------------------------------------------------------------
// Use pre-defined wrappers.
r = session.Get("http://httpbin.org/get", auto args = HttpRequestArgs{"GET"}
{ "key1", "value1", "key2", "value2" }, .url("http://httpbin.org/get")
{ "Accept", "application/json" }, .parameters({"key1", "value1", "key2", "value2"})
HttpRequestArgs{}.buffer_size(1000)); .headers({"Accept", "application/json"})
.buffer_size(1000);
// --------------------------------------------------------------------------- // Note the std::move().
// HTTPS is auto-detected from the URL schema. session.Request(std::move(args));
}
try { // Use pre-defined wrappers.
r = session.Post("httpt://httpbin.org/post", "{ 'key': 'value' }", true, void ExampleWrappers() {
{"Accept", "application/json"}, HttpClientSession session;
HttpRequestArgs{}.ssl_verify(false).buffer_size(1000));
std::cout << r->status() << std::endl << r->content() << std::endl; session.Get("http://httpbin.org/get", {"key1", "value1", "key2", "value2"},
{"Accept", "application/json"},
HttpRequestArgs{}.buffer_size(1000));
} catch (const webcc::Exception& e) { session.Post("http://httpbin.org/post", "{ 'key': 'value' }", true,
std::cout << "Exception: " << e.what() << std::endl; {"Accept", "application/json"});
}
} }
void TestKeepAlive2(HttpClientSession& session) { // HTTPS is auto-detected from the URL scheme.
try { void ExampleHttps() {
auto r = session.Request(webcc::HttpRequestArgs("GET"). HttpClientSession session;
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));
//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 << r->content() << std::endl;
std::cout << "Exception: " << e.what() << std::endl;
}
} }
void TestKeepAlive4(HttpClientSession& session) { void ExampleKeepAlive(const std::string& url) {
try { HttpClientSession session;
auto r = session.Request(webcc::HttpRequestArgs("GET").
url("https://www.google.com").
ssl_verify(false));
//std::cout << r->content() << std::endl; // Keep-Alive
session.Request(webcc::HttpRequestArgs("GET").url(url).
ssl_verify(kSslVerify));
} catch (const Exception& e) { // Close
std::cout << "Exception: " << e.what() << std::endl; 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() { int main() {
WEBCC_LOG_INIT("", LOG_CONSOLE); WEBCC_LOG_INIT("", LOG_CONSOLE);
HttpClientSession session; // Note that the exception handling is mandatory.
// TEST keep-alive.
try { try {
// Keep-Alive by default //ExampleBasic();
session.Get("http://httpbin.org/get"); //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) { } catch (const Exception& e) {
std::cout << "Exception: " << e.what() << std::endl; std::cout << "Exception: " << e.what() << std::endl;

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

@ -25,10 +25,16 @@ HttpClientPtr HttpClientPool::Get(const Key& key) const {
void HttpClientPool::Add(const Key& key, HttpClientPtr client) { void HttpClientPool::Add(const Key& key, HttpClientPtr client) {
clients_[key] = 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) { void HttpClientPool::Remove(const Key& key) {
clients_.erase(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 } // namespace webcc

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

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

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

Loading…
Cancel
Save