Cleanup async client APIs.

master
Chunting Gu 6 years ago
parent ffa0794926
commit 31d0ea3c9d

@ -118,48 +118,17 @@ endif()
add_subdirectory(webcc) add_subdirectory(webcc)
if(WEBCC_ENABLE_EXAMPLES) if(WEBCC_ENABLE_EXAMPLES)
# Common libraries to link for examples.
set(EXAMPLE_COMMON_LIBS webcc ${Boost_LIBRARIES} ${OPENSSL_LIBRARIES}
"${CMAKE_THREAD_LIBS_INIT}")
if(WIN32)
set(EXAMPLE_COMMON_LIBS ${EXAMPLE_COMMON_LIBS} crypt32)
endif()
if(UNIX)
# Add `-ldl` for Linux to avoid "undefined reference to `dlopen'".
set(EXAMPLE_COMMON_LIBS ${EXAMPLE_COMMON_LIBS} ${CMAKE_DL_LIBS})
endif()
add_subdirectory(example/http_client)
# add_subdirectory(example/http_async_client)
if(WEBCC_ENABLE_REST) if(WEBCC_ENABLE_REST)
# For including jsoncpp as "json/json.h". # For including jsoncpp as "json/json.h".
include_directories(${THIRD_PARTY_DIR}/src/jsoncpp) include_directories(${THIRD_PARTY_DIR}/src/jsoncpp)
# REST examples need jsoncpp to parse and create JSON. # REST examples need jsoncpp to parse and create JSON.
add_subdirectory(${THIRD_PARTY_DIR}/src/jsoncpp) add_subdirectory(${THIRD_PARTY_DIR}/src/jsoncpp)
add_subdirectory(example/rest_book_server)
# add_subdirectory(example/rest_book_client)
# add_subdirectory(example/rest_book_async_client)
endif() endif()
if(WEBCC_ENABLE_SOAP) add_subdirectory(example)
add_subdirectory(example/soap_calc_server)
add_subdirectory(example/soap_calc_client)
add_subdirectory(example/soap_calc_client_parasoft)
add_subdirectory(example/soap_book_server)
add_subdirectory(example/soap_book_client)
endif()
add_subdirectory(example/http_ssl_client)
# add_subdirectory(example/http_ssl_async_client)
if(WEBCC_ENABLE_REST)
# add_subdirectory(example/github_rest_client)
endif()
endif() endif()
if(WEBCC_ENABLE_UNITTEST) if(WEBCC_ENABLE_UNITTEST)
add_subdirectory(third_party/src/gtest) add_subdirectory(${THIRD_PARTY_DIR}/src/gtest)
add_subdirectory(unittest) add_subdirectory(unittest)
endif() endif()

@ -0,0 +1,33 @@
# Examples
# Common libraries to link for examples.
set(EXAMPLE_COMMON_LIBS webcc ${Boost_LIBRARIES} ${OPENSSL_LIBRARIES}
"${CMAKE_THREAD_LIBS_INIT}")
if(WIN32)
set(EXAMPLE_COMMON_LIBS ${EXAMPLE_COMMON_LIBS} crypt32)
endif()
if(UNIX)
# Add `-ldl` for Linux to avoid "undefined reference to `dlopen'".
set(EXAMPLE_COMMON_LIBS ${EXAMPLE_COMMON_LIBS} ${CMAKE_DL_LIBS})
endif()
add_subdirectory(http_client)
if(WEBCC_ENABLE_REST)
add_subdirectory(rest_book_server)
# add_subdirectory(rest_book_client)
add_subdirectory(github_client)
endif()
if(WEBCC_ENABLE_SOAP)
add_subdirectory(soap_calc_server)
add_subdirectory(soap_book_server)
add_subdirectory(soap_book_client)
endif()
add_executable(soap_calc_client soap_calc_client.cc)
add_executable(soap_calc_client_parasoft soap_calc_client_parasoft.cc)
target_link_libraries(soap_calc_client ${EXAMPLE_COMMON_LIBS} pugixml)
target_link_libraries(soap_calc_client_parasoft ${EXAMPLE_COMMON_LIBS} pugixml)

@ -1,36 +0,0 @@
HttpBin (http://httpbin.org/) client example.
You request to different endpoints, and it returns information about what was in the request.
E.g., request:
```plain
GET /get HTTP/1.1
Host: httpbin.org:80
User-Agent: Webcc/0.1.0
```
Response:
```plain
HTTP/1.1 200 OK
Connection: keep-alive
Server: gunicorn/19.9.0
Content-Type: application/json
Content-Length: 191
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true
Via: 1.1 vegur
{
"args": {},
"headers": {
"Connection": "close",
"Host": "httpbin.org",
"User-Agent": "Webcc/0.1.0"
},
"origin": "198.55.94.81",
"url": "http://httpbin.org/get"
}
```
As you can see, the request information is returned in JSON format.

@ -20,7 +20,7 @@ std::ostream& operator<<(std::ostream& os, const Book& book);
extern const Book kNullBook; extern const Book kNullBook;
class BookStore { class BookStore {
public: public:
const std::list<Book>& books() const { return books_; } const std::list<Book>& books() const { return books_; }
const Book& GetBook(const std::string& id) const; const Book& GetBook(const std::string& id) const;
@ -33,7 +33,7 @@ class BookStore {
bool DeleteBook(const std::string& id); bool DeleteBook(const std::string& id);
private: private:
std::list<Book>::const_iterator FindBook(const std::string& id) const; std::list<Book>::const_iterator FindBook(const std::string& id) const;
std::list<Book>::iterator FindBook(const std::string& id); std::list<Book>::iterator FindBook(const std::string& id);

@ -0,0 +1,2 @@
add_executable(github_client main.cc)
target_link_libraries(github_client ${EXAMPLE_COMMON_LIBS} jsoncpp)

@ -3,8 +3,8 @@
#include "json/json.h" #include "json/json.h"
#include "webcc/http_client_session.h"
#include "webcc/logger.h" #include "webcc/logger.h"
#include "webcc/rest_ssl_client.h"
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
@ -19,7 +19,7 @@ bool kSslVerify = false;
bool kSslVerify = true; bool kSslVerify = true;
#endif #endif
const std::string kGithubHost = "api.github.com"; const std::size_t kBufferSize = 1500;
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
@ -57,43 +57,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) { //static void PrintError(const webcc::RestSslClient& client) {
std::cout << webcc::DescribeError(client.error()); // std::cout << webcc::DescribeError(client.error());
if (client.timed_out()) { // if (client.timed_out()) {
std::cout << " (timed out)"; // std::cout << " (timed out)";
} // }
std::cout << std::endl; // std::cout << std::endl;
} //}
//
// ----------------------------------------------------------------------------- //// -----------------------------------------------------------------------------
//
// List public events. //// List public events.
static void ListEvents(webcc::RestSslClient& client) { //static void ListEvents(webcc::RestSslClient& client) {
if (client.Get("/events")) { // if (client.Get("/events")) {
PRINT_JSON_STRING(client.response_content()); // PRINT_JSON_STRING(client.response_content());
} else { // } else {
PrintError(client); // PrintError(client);
} // }
} //}
//
// List the followers of the given user. //// List the followers of the given user.
static void ListUserFollowers(webcc::RestSslClient& client, //static void ListUserFollowers(webcc::RestSslClient& client,
const std::string& user) { // const std::string& user) {
if (client.Get("/users/" + user + "/followers")) { // if (client.Get("/users/" + user + "/followers")) {
PRINT_JSON_STRING(client.response_content()); // PRINT_JSON_STRING(client.response_content());
} else { // } else {
PrintError(client); // 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::RestSslClient& client, static void ListAuthorizedUserFollowers(webcc::HttpClientSession& session,
const std::string& auth) { const std::string& auth) {
if (client.Get("/user/followers", { { "Authorization", auth } })) { auto r = session.Request(webcc::HttpRequestArgs("GET").
PRINT_JSON_STRING(client.response_content()); url("https://api.github.com/user/followers").
headers({ { "Authorization", auth } }).
ssl_verify(kSslVerify).buffer_size(kBufferSize));
if (r) {
PRINT_JSON_STRING(r->content());
} else { } else {
PrintError(client); //PrintError(client);
} }
} }
@ -102,10 +107,10 @@ static void ListAuthorizedUserFollowers(webcc::RestSslClient& client,
int main() { int main() {
WEBCC_LOG_INIT("", webcc::LOG_CONSOLE); WEBCC_LOG_INIT("", webcc::LOG_CONSOLE);
webcc::RestSslClient client(kGithubHost, "", kSslVerify, {}, 1500); webcc::HttpClientSession session;
//ListAuthorizedUserFollowers(client, "Basic c3ByaW5mYWxsQGdtYWlsLmNvbTpYaWFvTHVhbjFA"); //ListAuthorizedUserFollowers(client, "Basic c3ByaW5mYWxsQGdtYWlsLmNvbTpYaWFvTHVhbjFA");
ListAuthorizedUserFollowers(client, "Token 1d42e2cce49929f2d24b1b6e96260003e5b3e1b0"); ListAuthorizedUserFollowers(session, "Token 1d42e2cce49929f2d24b1b6e96260003e5b3e1b0");
return 0; return 0;
} }

@ -1,2 +0,0 @@
add_executable(github_rest_client main.cc)
target_link_libraries(github_rest_client ${EXAMPLE_COMMON_LIBS} jsoncpp)

@ -1,4 +0,0 @@
add_executable(http_async_client main.cc)
target_link_libraries(http_async_client webcc ${Boost_LIBRARIES})
target_link_libraries(http_async_client "${CMAKE_THREAD_LIBS_INIT}")

@ -1,47 +0,0 @@
#include <iostream>
#include "boost/asio/io_context.hpp"
#include "webcc/http_async_client.h"
#include "webcc/logger.h"
// TODO: The program blocks during read response.
// Only HttpBin.org has this issue.
static void Test(boost::asio::io_context& io_context) {
auto request = webcc::HttpRequest::New(webcc::kHttpGet, "/get",
"httpbin.org");
auto client = webcc::HttpAsyncClient::New(io_context);
client->SetTimeout(3);
// Response callback.
auto callback = [](webcc::HttpResponsePtr response, webcc::Error error,
bool timed_out) {
if (error == webcc::kNoError) {
std::cout << response->content() << std::endl;
} else {
std::cout << DescribeError(error);
if (timed_out) {
std::cout << " (timed out)";
}
std::cout << std::endl;
}
};
client->Request(request, callback);
}
int main() {
WEBCC_LOG_INIT("", webcc::LOG_CONSOLE);
boost::asio::io_context io_context;
//Test(io_context);
Test(io_context);
io_context.run();
return 0;
}

@ -3,6 +3,14 @@
#include "webcc/http_client_session.h" #include "webcc/http_client_session.h"
#include "webcc/logger.h" #include "webcc/logger.h"
void GetBoostOrgLicense(webcc::HttpClientSession& session) {
auto r = session.Get("https://www.boost.org/LICENSE_1_0.txt");
if (r) {
std::cout << r->content() << std::endl;
}
}
int main() { int main() {
WEBCC_LOG_INIT("", webcc::LOG_CONSOLE); WEBCC_LOG_INIT("", webcc::LOG_CONSOLE);
@ -29,21 +37,21 @@ int main() {
// - constructor: HttpRequestArgs{ "GET" } // - constructor: HttpRequestArgs{ "GET" }
// - move constructor: auto args = ... // - move constructor: auto args = ...
auto args = HttpRequestArgs{"GET"}. //auto args = HttpRequestArgs{"GET"}.
url("http://httpbin.org/get"). // url("http://httpbin.org/get").
parameters({ "key1", "value1", "key2", "value2" }). // parameters({ "key1", "value1", "key2", "value2" }).
headers({ "Accept", "application/json" }). // headers({ "Accept", "application/json" }).
buffer_size(1000); // buffer_size(1000);
r = session.Request(std::move(args)); //r = session.Request(std::move(args));
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Use pre-defined wrappers. // Use pre-defined wrappers.
r = session.Get("http://httpbin.org/get", //r = session.Get("http://httpbin.org/get",
{ "key1", "value1", "key2", "value2" }, // { "key1", "value1", "key2", "value2" },
{ "Accept", "application/json" }, // { "Accept", "application/json" },
HttpRequestArgs{}.buffer_size(1000)); // HttpRequestArgs{}.buffer_size(1000));
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// HTTPS is auto-detected from the URL schema. // HTTPS is auto-detected from the URL schema.
@ -56,5 +64,7 @@ int main() {
std::cout << r->content() << std::endl; std::cout << r->content() << std::endl;
} }
GetBoostOrgLicense(session);
return 0; return 0;
} }

@ -1,11 +0,0 @@
add_executable(http_ssl_async_client main.cc)
# TODO
set(SSL_LIBS ${OPENSSL_LIBRARIES})
if(WIN32)
set(SSL_LIBS ${SSL_LIBS} crypt32)
endif()
target_link_libraries(http_ssl_async_client webcc ${Boost_LIBRARIES})
target_link_libraries(http_ssl_async_client "${CMAKE_THREAD_LIBS_INIT}")
target_link_libraries(http_ssl_async_client ${SSL_LIBS})

@ -1,56 +0,0 @@
#include <iostream>
#include "boost/asio/io_context.hpp"
#include "webcc/http_ssl_async_client.h"
#include "webcc/logger.h"
int main(int argc, char* argv[]) {
std::string host;
std::string url;
if (argc != 3) {
host = "www.boost.org";
url = "/LICENSE_1_0.txt";
} else {
host = argv[1];
url = argv[2];
}
std::cout << "Host: " << host << std::endl;
std::cout << "URL: " << url << std::endl;
std::cout << std::endl;
WEBCC_LOG_INIT("", webcc::LOG_CONSOLE);
boost::asio::io_context io_context;
// Leave port to default value.
auto request = webcc::HttpRequest::New(webcc::kHttpGet, url, host);
// Verify the certificate of the peer or not.
// See HttpSslClient::Request() for more details.
bool ssl_verify = false;
auto client = webcc::HttpSslAsyncClient::New(io_context, 2000, ssl_verify);
// Response callback.
auto callback = [](webcc::HttpResponsePtr response, webcc::Error error,
bool timed_out) {
if (error == webcc::kNoError) {
std::cout << response->content() << std::endl;
} else {
std::cout << DescribeError(error);
if (timed_out) {
std::cout << " (timed out)";
}
std::cout << std::endl;
}
};
client->Request(request, callback);
io_context.run();
return 0;
}

@ -1,2 +0,0 @@
add_executable(http_ssl_client main.cc)
target_link_libraries(http_ssl_client ${EXAMPLE_COMMON_LIBS})

@ -1,42 +0,0 @@
#include <iostream>
#include "webcc/http_ssl_client.h"
#include "webcc/logger.h"
int main(int argc, char* argv[]) {
std::string url;
if (argc != 3) {
url = "www.boost.org/LICENSE_1_0.txt";
} else {
url = argv[1];
}
std::cout << "URL: " << url << std::endl;
std::cout << std::endl;
WEBCC_LOG_INIT("", webcc::LOG_CONSOLE);
// Leave port to default value.
webcc::HttpRequest request(webcc::http::kGet, url);
request.Prepare();
// Verify the certificate of the peer or not.
// See HttpSslClient::Request() for more details.
bool ssl_verify = false;
webcc::HttpSslClient client(ssl_verify, 2000);
if (client.Request(request)) {
//std::cout << client.response()->content() << std::endl;
} else {
std::cout << webcc::DescribeError(client.error());
if (client.timed_out()) {
std::cout << " (timed out)";
}
std::cout << std::endl;
}
return 0;
}

@ -1,13 +0,0 @@
set(TARGET_NAME rest_book_async_client)
set(SRCS
../common/book.cc
../common/book.h
../common/book_json.cc
../common/book_json.h
main.cc)
add_executable(${TARGET_NAME} ${SRCS})
target_link_libraries(${TARGET_NAME} webcc jsoncpp ${Boost_LIBRARIES})
target_link_libraries(${TARGET_NAME} "${CMAKE_THREAD_LIBS_INIT}")

@ -1,194 +0,0 @@
#include <iostream>
#include "json/json.h"
#include "webcc/logger.h"
#include "webcc/rest_async_client.h"
#include "example/common/book.h"
#include "example/common/book_json.h"
// -----------------------------------------------------------------------------
class BookClientBase {
public:
BookClientBase(boost::asio::io_context& io_context,
const std::string& host, const std::string& port,
int timeout_seconds)
: rest_client_(io_context, host, port) {
rest_client_.SetTimeout(timeout_seconds);
}
virtual ~BookClientBase() = default;
protected:
void PrintSeparateLine() {
std::cout << "--------------------------------";
std::cout << "--------------------------------";
std::cout << std::endl;
}
// Generic response handler for RestAsyncClient APIs.
void GenericHandler(std::function<void(webcc::HttpResponsePtr)> rsp_callback,
webcc::HttpResponsePtr response,
webcc::Error error,
bool timed_out) {
if (error != webcc::kNoError) {
std::cout << webcc::DescribeError(error);
if (timed_out) {
std::cout << " (timed out)";
}
std::cout << std::endl;
} else {
// Call the response callback on success.
rsp_callback(response);
}
}
webcc::RestAsyncClient rest_client_;
};
// -----------------------------------------------------------------------------
class BookListClient : public BookClientBase {
public:
BookListClient(boost::asio::io_context& io_context,
const std::string& host, const std::string& port,
int timeout_seconds)
: BookClientBase(io_context, host, port, timeout_seconds) {
}
void ListBooks(webcc::HttpResponseCallback callback) {
std::cout << "ListBooks" << std::endl;
rest_client_.Get("/books", callback);
}
void CreateBook(const std::string& title, double price,
std::function<void(std::string)> id_callback) {
std::cout << "CreateBook: " << title << " " << price << std::endl;
Json::Value json(Json::objectValue);
json["title"] = title;
json["price"] = price;
auto rsp_callback = [id_callback](webcc::HttpResponsePtr response) {
Json::Value rsp_json = StringToJson(response->content());
id_callback(rsp_json["id"].asString());
};
rest_client_.Post("/books", JsonToString(json),
std::bind(&BookListClient::GenericHandler, this,
rsp_callback,
std::placeholders::_1,
std::placeholders::_2,
std::placeholders::_3));
}
};
// -----------------------------------------------------------------------------
class BookDetailClient : public BookClientBase {
public:
BookDetailClient(boost::asio::io_context& io_context,
const std::string& host, const std::string& port,
int timeout_seconds)
: BookClientBase(io_context, host, port, timeout_seconds) {
}
void GetBook(const std::string& id, webcc::HttpResponseCallback callback) {
std::cout << "GetBook: " << id << std::endl;
auto rsp_callback = [](webcc::HttpResponsePtr response) {
Json::Value rsp_json = StringToJson(response->content());
//id_callback(rsp_json["id"].asString());
};
rest_client_.Get("/books/" + id, callback);
}
void UpdateBook(const std::string& id,
const std::string& title,
double price,
webcc::HttpResponseCallback callback) {
std::cout << "UpdateBook: " << id << " " << title << " " << price
<< std::endl;
// NOTE: ID is already in the URL.
Json::Value json(Json::objectValue);
json["title"] = title;
json["price"] = price;
rest_client_.Put("/books/" + id, JsonToString(json), callback);
}
void DeleteBook(const std::string& id, webcc::HttpResponseCallback callback) {
std::cout << "DeleteBook: " << id << std::endl;
rest_client_.Delete("/books/" + id, callback);
}
};
// -----------------------------------------------------------------------------
void Help(const char* argv0) {
std::cout << "Usage: " << argv0 << " <host> <port> [timeout]" << std::endl;
std::cout << " E.g.," << std::endl;
std::cout << " " << argv0 << " localhost 8080" << std::endl;
std::cout << " " << argv0 << " localhost 8080 2" << std::endl;
}
int main(int argc, char* argv[]) {
if (argc < 3) {
Help(argv[0]);
return 1;
}
WEBCC_LOG_INIT("", webcc::LOG_CONSOLE);
std::string host = argv[1];
std::string port = argv[2];
int timeout_seconds = -1;
if (argc > 3) {
timeout_seconds = std::atoi(argv[3]);
}
boost::asio::io_context io_context;
BookListClient list_client(io_context, host, port, timeout_seconds);
BookDetailClient detail_client(io_context, host, port, timeout_seconds);
// Response handler.
auto handler = [](webcc::HttpResponsePtr response, webcc::Error error,
bool timed_out) {
if (error == webcc::kNoError) {
std::cout << response->content() << std::endl;
} else {
std::cout << webcc::DescribeError(error);
if (timed_out) {
std::cout << " (timed out)";
}
std::cout << std::endl;
}
};
list_client.ListBooks(handler);
list_client.CreateBook("1984", 12.3, [](std::string id) {
std::cout << "ID: " << id << std::endl;
});
//detail_client.GetBook("1", handler);
//detail_client.UpdateBook("1", "1Q84", 32.1, handler);
//detail_client.GetBook("1", handler);
//detail_client.DeleteBook("1", handler);
//list_client.ListBooks(handler);
io_context.run();
return 0;
}

@ -9,8 +9,7 @@ set(SRCS
add_executable(${TARGET_NAME} ${SRCS}) add_executable(${TARGET_NAME} ${SRCS})
target_link_libraries(${TARGET_NAME} webcc jsoncpp ${Boost_LIBRARIES}) target_link_libraries(${TARGET_NAME} ${EXAMPLE_COMMON_LIBS} jsoncpp)
target_link_libraries(${TARGET_NAME} "${CMAKE_THREAD_LIBS_INIT}")
# Install VLD DLLs to build dir so that the example can be launched from # Install VLD DLLs to build dir so that the example can be launched from
# inside VS. # inside VS.

@ -20,7 +20,7 @@
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
class BookClientBase { class BookClientBase {
public: public:
BookClientBase(const std::string& host, const std::string& port, BookClientBase(const std::string& host, const std::string& port,
int timeout_seconds) int timeout_seconds)
: host_(host), port_(port) { : host_(host), port_(port) {
@ -29,7 +29,7 @@ class BookClientBase {
virtual ~BookClientBase() = default; virtual ~BookClientBase() = default;
protected: public:
// Helper function to make a request. // Helper function to make a request.
webcc::HttpRequestPtr MakeRequest(const std::string& method, webcc::HttpRequestPtr MakeRequest(const std::string& method,
const std::string& url, const std::string& url,
@ -71,7 +71,7 @@ class BookClientBase {
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
class BookListClient : public BookClientBase { class BookListClient : public BookClientBase {
public: public:
BookListClient(const std::string& host, const std::string& port, BookListClient(const std::string& host, const std::string& port,
int timeout_seconds) int timeout_seconds)
: BookClientBase(host, port, timeout_seconds) { : BookClientBase(host, port, timeout_seconds) {
@ -131,7 +131,7 @@ class BookListClient : public BookClientBase {
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
class BookDetailClient : public BookClientBase { class BookDetailClient : public BookClientBase {
public: public:
BookDetailClient(const std::string& host, const std::string& port, BookDetailClient(const std::string& host, const std::string& port,
int timeout_seconds) int timeout_seconds)
: BookClientBase(host, port, timeout_seconds) { : BookClientBase(host, port, timeout_seconds) {

@ -9,12 +9,12 @@
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
class BookListService : public webcc::RestListService { class BookListService : public webcc::RestListService {
public: public:
explicit BookListService(int sleep_seconds) explicit BookListService(int sleep_seconds)
: sleep_seconds_(sleep_seconds) { : sleep_seconds_(sleep_seconds) {
} }
protected: public:
// 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;
@ -22,7 +22,7 @@ class BookListService : public webcc::RestListService {
void Post(const std::string& request_content, void Post(const std::string& request_content,
webcc::RestResponse* response) final; webcc::RestResponse* response) final;
private: private:
// Sleep some seconds before send back the response. // Sleep some seconds before send back the response.
// For testing timeout control in client side. // For testing timeout control in client side.
int sleep_seconds_; int sleep_seconds_;
@ -33,12 +33,12 @@ class BookListService : public webcc::RestListService {
// The URL is like '/books/{BookID}', and the 'url_sub_matches' parameter // The URL is like '/books/{BookID}', and the 'url_sub_matches' parameter
// contains the matched book ID. // contains the matched book ID.
class BookDetailService : public webcc::RestDetailService { class BookDetailService : public webcc::RestDetailService {
public: public:
explicit BookDetailService(int sleep_seconds) explicit BookDetailService(int sleep_seconds)
: sleep_seconds_(sleep_seconds) { : sleep_seconds_(sleep_seconds) {
} }
protected: public:
// Get the detailed information of a book. // Get the detailed information of a book.
void Get(const webcc::UrlSubMatches& url_sub_matches, void Get(const webcc::UrlSubMatches& url_sub_matches,
const webcc::UrlQuery& query, const webcc::UrlQuery& query,
@ -53,7 +53,7 @@ class BookDetailService : public webcc::RestDetailService {
void Delete(const webcc::UrlSubMatches& url_sub_matches, void Delete(const webcc::UrlSubMatches& url_sub_matches,
webcc::RestResponse* response) final; webcc::RestResponse* response) final;
private: private:
// Sleep some seconds before send back the response. // Sleep some seconds before send back the response.
// For testing timeout control in client side. // For testing timeout control in client side.
int sleep_seconds_; int sleep_seconds_;

@ -11,8 +11,7 @@ set(SRCS
add_executable(${TARGET_NAME} ${SRCS}) add_executable(${TARGET_NAME} ${SRCS})
target_link_libraries(${TARGET_NAME} webcc pugixml ${Boost_LIBRARIES}) target_link_libraries(${TARGET_NAME} ${EXAMPLE_COMMON_LIBS} pugixml)
target_link_libraries(${TARGET_NAME} "${CMAKE_THREAD_LIBS_INIT}")
# Install VLD DLLs to build dir so that the example can be launched from # Install VLD DLLs to build dir so that the example can be launched from
# inside VS. # inside VS.

@ -10,7 +10,7 @@
#include "example/common/book.h" #include "example/common/book.h"
class BookClient { class BookClient {
public: public:
BookClient(const std::string& host, const std::string& port); BookClient(const std::string& host, const std::string& port);
int code() const { return code_; } int code() const { return code_; }
@ -28,7 +28,7 @@ class BookClient {
// Delete a book by ID. // Delete a book by ID.
bool DeleteBook(const std::string& id); bool DeleteBook(const std::string& id);
private: private:
// Call with 0 parameter. // Call with 0 parameter.
bool Call0(const std::string& operation, std::string* result_str); bool Call0(const std::string& operation, std::string* result_str);

@ -4,11 +4,11 @@
#include "webcc/soap_service.h" #include "webcc/soap_service.h"
class BookService : public webcc::SoapService { class BookService : public webcc::SoapService {
public: public:
bool Handle(const webcc::SoapRequest& soap_request, bool Handle(const webcc::SoapRequest& soap_request,
webcc::SoapResponse* soap_response) override; webcc::SoapResponse* soap_response) override;
private: private:
bool CreateBook(const webcc::SoapRequest& soap_request, bool CreateBook(const webcc::SoapRequest& soap_request,
webcc::SoapResponse* soap_response); webcc::SoapResponse* soap_response);

@ -8,7 +8,7 @@
static const std::string kResultName = "Result"; static const std::string kResultName = "Result";
class CalcClient { class CalcClient {
public: public:
CalcClient(const std::string& host, const std::string& port) CalcClient(const std::string& host, const std::string& port)
: soap_client_(host, port) { : soap_client_(host, port) {
soap_client_.SetTimeout(5); soap_client_.SetTimeout(5);
@ -44,7 +44,7 @@ class CalcClient {
return Calc("unknown", "x", "y", x, y, result); return Calc("unknown", "x", "y", x, y, result);
} }
private: private:
bool Calc(const std::string& operation, bool Calc(const std::string& operation,
const std::string& x_name, const std::string& y_name, const std::string& x_name, const std::string& y_name,
double x, double y, double x, double y,

@ -1,6 +0,0 @@
set(TARGET_NAME soap_calc_client)
add_executable(${TARGET_NAME} main.cc)
target_link_libraries(${TARGET_NAME} webcc pugixml ${Boost_LIBRARIES})
target_link_libraries(${TARGET_NAME} "${CMAKE_THREAD_LIBS_INIT}")

@ -8,7 +8,7 @@
static const std::string kResultName = "Result"; static const std::string kResultName = "Result";
class CalcClient { class CalcClient {
public: public:
// NOTE: Parasoft's calculator service uses SOAP V1.1. // NOTE: Parasoft's calculator service uses SOAP V1.1.
CalcClient(const std::string& host, const std::string& port) CalcClient(const std::string& host, const std::string& port)
: soap_client_(host, port, webcc::kSoapV11) { : soap_client_(host, port, webcc::kSoapV11) {
@ -45,7 +45,7 @@ class CalcClient {
return Calc("unknown", "x", "y", x, y, result); return Calc("unknown", "x", "y", x, y, result);
} }
private: private:
bool Calc(const std::string& operation, bool Calc(const std::string& operation,
const std::string& x_name, const std::string& y_name, const std::string& x_name, const std::string& y_name,
double x, double y, double x, double y,

@ -1,6 +0,0 @@
set(TARGET_NAME soap_calc_client_parasoft)
add_executable(${TARGET_NAME} main.cc)
target_link_libraries(${TARGET_NAME} webcc pugixml ${Boost_LIBRARIES})
target_link_libraries(${TARGET_NAME} "${CMAKE_THREAD_LIBS_INIT}")

@ -1,250 +0,0 @@
<?xml version="1.0" encoding="US-ASCII"?>
<!--generated by GLUE Standard 4.1.2 on Fri Nov 21 13:50:48 PST 2003-->
<wsdl:definitions name="Calculator"
targetNamespace="http://www.parasoft.com/wsdl/calculator/"
xmlns:http="http://schemas.xmlsoap.org/wsdl/http/"
xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/"
xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:tme="http://www.themindelectric.com/"
xmlns:tns="http://www.parasoft.com/wsdl/calculator/"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<wsdl:types>
<xsd:schema
elementFormDefault="qualified"
targetNamespace="http://www.parasoft.com/wsdl/calculator/">
<xsd:element
name="add">
<xsd:complexType>
<xsd:sequence>
<xsd:element
name="x" type="xsd:float"/>
<xsd:element name="y"
type="xsd:float"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
<xsd:element
name="addResponse">
<xsd:complexType>
<xsd:sequence>
<xsd:element
name="Result"
type="xsd:float"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
<xsd:element
name="divide">
<xsd:complexType>
<xsd:sequence>
<xsd:element
name="numerator" type="xsd:float"/>
<xsd:element
name="denominator"
type="xsd:float"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
<xsd:element
name="divideResponse">
<xsd:complexType>
<xsd:sequence>
<xsd:element
name="Result"
type="xsd:float"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
<xsd:element
name="multiply">
<xsd:complexType>
<xsd:sequence>
<xsd:element
name="x" type="xsd:float"/>
<xsd:element name="y"
type="xsd:float"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
<xsd:element
name="multiplyResponse">
<xsd:complexType>
<xsd:sequence>
<xsd:element
name="Result"
type="xsd:float"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
<xsd:element
name="subtract">
<xsd:complexType>
<xsd:sequence>
<xsd:element
name="x" type="xsd:float"/>
<xsd:element name="y"
type="xsd:float"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
<xsd:element
name="subtractResponse">
<xsd:complexType>
<xsd:sequence>
<xsd:element
name="Result"
type="xsd:float"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:schema>
</wsdl:types>
<wsdl:message
name="add0In">
<wsdl:part element="tns:add"
name="parameters"/>
</wsdl:message>
<wsdl:message
name="add0Out">
<wsdl:part element="tns:addResponse"
name="parameters"/>
</wsdl:message>
<wsdl:message
name="divide1In">
<wsdl:part element="tns:divide"
name="parameters"/>
</wsdl:message>
<wsdl:message
name="divide1Out">
<wsdl:part element="tns:divideResponse"
name="parameters"/>
</wsdl:message>
<wsdl:message
name="multiply2In">
<wsdl:part element="tns:multiply"
name="parameters"/>
</wsdl:message>
<wsdl:message
name="multiply2Out">
<wsdl:part element="tns:multiplyResponse"
name="parameters"/>
</wsdl:message>
<wsdl:message
name="subtract3In">
<wsdl:part element="tns:subtract"
name="parameters"/>
</wsdl:message>
<wsdl:message
name="subtract3Out">
<wsdl:part element="tns:subtractResponse"
name="parameters"/>
</wsdl:message>
<wsdl:portType
name="ICalculator">
<wsdl:operation name="add"
parameterOrder="x y">
<wsdl:input message="tns:add0In"
name="add0In"/>
<wsdl:output message="tns:add0Out"
name="add0Out"/>
</wsdl:operation>
<wsdl:operation name="divide"
parameterOrder="numerator denominator">
<wsdl:input
message="tns:divide1In" name="divide1In"/>
<wsdl:output
message="tns:divide1Out"
name="divide1Out"/>
</wsdl:operation>
<wsdl:operation
name="multiply" parameterOrder="x y">
<wsdl:input
message="tns:multiply2In" name="multiply2In"/>
<wsdl:output
message="tns:multiply2Out"
name="multiply2Out"/>
</wsdl:operation>
<wsdl:operation
name="subtract" parameterOrder="x y">
<wsdl:input
message="tns:subtract3In" name="subtract3In"/>
<wsdl:output
message="tns:subtract3Out"
name="subtract3Out"/>
</wsdl:operation>
</wsdl:portType>
<wsdl:binding
name="ICalculator" type="tns:ICalculator">
<soap:binding
style="document"
transport="http://schemas.xmlsoap.org/soap/http"/>
<wsdl:operation
name="add">
<soap:operation soapAction="add"
style="document"/>
<wsdl:input name="add0In">
<soap:body
use="literal"/>
</wsdl:input>
<wsdl:output
name="add0Out">
<soap:body
use="literal"/>
</wsdl:output>
</wsdl:operation>
<wsdl:operation
name="divide">
<soap:operation soapAction="divide"
style="document"/>
<wsdl:input name="divide1In">
<soap:body
use="literal"/>
</wsdl:input>
<wsdl:output
name="divide1Out">
<soap:body
use="literal"/>
</wsdl:output>
</wsdl:operation>
<wsdl:operation
name="multiply">
<soap:operation soapAction="multiply"
style="document"/>
<wsdl:input name="multiply2In">
<soap:body
use="literal"/>
</wsdl:input>
<wsdl:output
name="multiply2Out">
<soap:body
use="literal"/>
</wsdl:output>
</wsdl:operation>
<wsdl:operation
name="subtract">
<soap:operation soapAction="subtract"
style="document"/>
<wsdl:input name="subtract3In">
<soap:body
use="literal"/>
</wsdl:input>
<wsdl:output
name="subtract3Out">
<soap:body
use="literal"/>
</wsdl:output>
</wsdl:operation>
</wsdl:binding>
<wsdl:service
name="Calculator">
<wsdl:documentation>instance of class webtool.soap.examples.calculator.Calculator</wsdl:documentation>
<wsdl:port
binding="tns:ICalculator" name="ICalculator">
<soap:address
location="http://ws1.parasoft.com/glue/calculator"/>
</wsdl:port>
</wsdl:service>
</wsdl:definitions>

@ -4,7 +4,7 @@
#include "webcc/soap_service.h" #include "webcc/soap_service.h"
class CalcService : public webcc::SoapService { class CalcService : public webcc::SoapService {
public: public:
CalcService() = default; CalcService() = default;
~CalcService() override = default; ~CalcService() override = default;

@ -693,7 +693,7 @@ private:
#endif #endif
#ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION #ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION
class CZString { class CZString {
public: public:
enum DuplicationPolicy { enum DuplicationPolicy {
noDuplication = 0, noDuplication = 0,
duplicate, duplicate,
@ -720,7 +720,7 @@ private:
unsigned length() const; unsigned length() const;
bool isStaticString() const; bool isStaticString() const;
private: private:
void swap(CZString& other); void swap(CZString& other);
struct StringStorage { struct StringStorage {
@ -1548,14 +1548,14 @@ private:
}; };
class Token { class Token {
public: public:
TokenType type_; TokenType type_;
Location start_; Location start_;
Location end_; Location end_;
}; };
class ErrorInfo { class ErrorInfo {
public: public:
Token token_; Token token_;
JSONCPP_STRING message_; JSONCPP_STRING message_;
Location extra_; Location extra_;
@ -1646,7 +1646,7 @@ public:
Value* root, JSONCPP_STRING* errs) = 0; Value* root, JSONCPP_STRING* errs) = 0;
class JSON_API Factory { class JSON_API Factory {
public: public:
virtual ~Factory() {} virtual ~Factory() {}
/** \brief Allocate a CharReader via operator new(). /** \brief Allocate a CharReader via operator new().
* \throw std::exception if something goes wrong (e.g. invalid settings) * \throw std::exception if something goes wrong (e.g. invalid settings)
@ -1853,7 +1853,7 @@ public:
/** \brief A simple abstract factory. /** \brief A simple abstract factory.
*/ */
class JSON_API Factory { class JSON_API Factory {
public: public:
virtual ~Factory(); virtual ~Factory();
/** \brief Allocate a CharReader via operator new(). /** \brief Allocate a CharReader via operator new().
* \throw std::exception if something goes wrong (e.g. invalid settings) * \throw std::exception if something goes wrong (e.g. invalid settings)

@ -1191,14 +1191,14 @@ private:
}; };
class Token { class Token {
public: public:
TokenType type_; TokenType type_;
Location start_; Location start_;
Location end_; Location end_;
}; };
class ErrorInfo { class ErrorInfo {
public: public:
Token token_; Token token_;
JSONCPP_STRING message_; JSONCPP_STRING message_;
Location extra_; Location extra_;

@ -5,7 +5,7 @@
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
class TestRestService : public webcc::RestService { class TestRestService : public webcc::RestService {
public: public:
void Handle(const webcc::RestRequest& request, void Handle(const webcc::RestRequest& request,
webcc::RestResponse* response) final { webcc::RestResponse* response) final {
response->status = webcc::http::Status::kOK; response->status = webcc::http::Status::kOK;
@ -37,6 +37,23 @@ TEST(RestServiceManager, URL_RegexBasic) {
EXPECT_FALSE(!!service); EXPECT_FALSE(!!service);
} }
TEST(RestServiceManager, URL_Temp) {
webcc::RestServiceManager service_manager;
service_manager.AddService(std::make_shared<TestRestService>(),
"/instance/([\\w.]+)", true);
std::vector<std::string> sub_matches;
std::string url = "/instance/123.45-+6aaa";
webcc::RestServicePtr service = service_manager.GetService(url, &sub_matches);
EXPECT_TRUE(!!service);
EXPECT_EQ(1, sub_matches.size());
EXPECT_EQ("123.45-6aaa", sub_matches[0]);
}
TEST(RestServiceManager, URL_RegexMultiple) { TEST(RestServiceManager, URL_RegexMultiple) {
webcc::RestServiceManager service_manager; webcc::RestServiceManager service_manager;

@ -15,8 +15,6 @@ include(GNUInstallDirs)
set(HEADERS set(HEADERS
globals.h globals.h
# http_async_client_base.h
# http_async_client.h
http_client_base.h http_client_base.h
http_client.h http_client.h
http_client_session.h http_client_session.h
@ -31,7 +29,6 @@ set(HEADERS
http_response_parser.h http_response_parser.h
http_server.h http_server.h
http_ssl_client.h http_ssl_client.h
# http_ssl_async_client.h
queue.h queue.h
url.h url.h
utility.h utility.h
@ -40,8 +37,6 @@ set(HEADERS
set(SOURCES set(SOURCES
globals.cc globals.cc
# http_async_client_base.cc
# http_async_client.cc
http_client_base.cc http_client_base.cc
http_client.cc http_client.cc
http_client_session.cc http_client_session.cc
@ -55,7 +50,6 @@ set(SOURCES
http_response_parser.cc http_response_parser.cc
http_server.cc http_server.cc
http_ssl_client.cc http_ssl_client.cc
# http_ssl_async_client.cc
logger.cc logger.cc
url.cc url.cc
utility.cc utility.cc
@ -63,16 +57,12 @@ set(SOURCES
if(WEBCC_ENABLE_REST) if(WEBCC_ENABLE_REST)
set(REST_HEADERS set(REST_HEADERS
# rest_async_client.h
# rest_client.h
rest_request_handler.h rest_request_handler.h
rest_server.h rest_server.h
rest_service.h rest_service.h
rest_service_manager.h rest_service_manager.h
) )
set(REST_SOURCES set(REST_SOURCES
# rest_async_client.cc
# rest_client.cc
rest_request_handler.cc rest_request_handler.cc
rest_service_manager.cc rest_service_manager.cc
rest_service.cc rest_service.cc
@ -84,7 +74,6 @@ endif()
if(WEBCC_ENABLE_SOAP) if(WEBCC_ENABLE_SOAP)
set(SOAP_HEADERS set(SOAP_HEADERS
# soap_async_client.h
soap_client.h soap_client.h
soap_globals.h soap_globals.h
soap_message.h soap_message.h
@ -98,7 +87,6 @@ if(WEBCC_ENABLE_SOAP)
) )
set(SOAP_SOURCES set(SOAP_SOURCES
# soap_async_client.cc
soap_client.cc soap_client.cc
soap_globals.cc soap_globals.cc
soap_message.cc soap_message.cc

@ -1,35 +0,0 @@
#include "webcc/http_async_client.h"
#include "boost/asio/connect.hpp"
#include "boost/asio/read.hpp"
#include "boost/asio/write.hpp"
#include "webcc/logger.h"
#include "webcc/utility.h"
namespace webcc {
HttpAsyncClient::HttpAsyncClient(boost::asio::io_context& io_context,
std::size_t buffer_size)
: HttpAsyncClientBase(io_context, buffer_size),
socket_(io_context) {
}
void HttpAsyncClient::SocketAsyncConnect(const Endpoints& endpoints,
ConnectHandler&& handler) {
boost::asio::async_connect(socket_, endpoints, std::move(handler));
}
void HttpAsyncClient::SocketAsyncWrite(WriteHandler&& handler) {
boost::asio::async_write(socket_, request_->ToBuffers(), std::move(handler));
}
void HttpAsyncClient::SocketAsyncReadSome(ReadHandler&& handler) {
socket_.async_read_some(boost::asio::buffer(buffer_), std::move(handler));
}
void HttpAsyncClient::SocketClose(boost::system::error_code* ec) {
socket_.close(*ec);
}
} // namespace webcc

@ -1,51 +0,0 @@
#ifndef WEBCC_HTTP_ASYNC_CLIENT_H_
#define WEBCC_HTTP_ASYNC_CLIENT_H_
#include "webcc/http_async_client_base.h"
namespace webcc {
class HttpAsyncClient;
typedef std::shared_ptr<HttpAsyncClient> HttpAsyncClientPtr;
// HTTP asynchronous client.
class HttpAsyncClient : public HttpAsyncClientBase {
public:
~HttpAsyncClient() = default;
// Forbid to create HttpAsyncClient in stack since it's derived from
// std::shared_from_this.
static HttpAsyncClientPtr New(boost::asio::io_context& io_context,
std::size_t buffer_size = 0) {
return HttpAsyncClientPtr{
new HttpAsyncClient(io_context, buffer_size)
};
}
private:
explicit HttpAsyncClient(boost::asio::io_context& io_context,
std::size_t buffer_size = 0);
void Resolve() final {
DoResolve(kHttpPort);
}
void OnConnected() final {
DoWrite();
}
void SocketAsyncConnect(const Endpoints& endpoints,
ConnectHandler&& handler) final;
void SocketAsyncWrite(WriteHandler&& handler) final;
void SocketAsyncReadSome(ReadHandler&& handler) final;
void SocketClose(boost::system::error_code* ec) final;
tcp::socket socket_;
};
} // namespace webcc
#endif // WEBCC_HTTP_ASYNC_CLIENT_H_

@ -1,236 +0,0 @@
#include "webcc/http_async_client_base.h"
#include "boost/asio/connect.hpp"
#include "boost/asio/read.hpp"
#include "boost/asio/write.hpp"
#include "webcc/logger.h"
#include "webcc/utility.h"
namespace webcc {
HttpAsyncClientBase::HttpAsyncClientBase(boost::asio::io_context& io_context,
std::size_t buffer_size)
: resolver_(io_context),
buffer_(buffer_size == 0 ? kBufferSize : buffer_size),
deadline_(io_context),
timeout_seconds_(kMaxReadSeconds),
stopped_(false),
timed_out_(false) {
}
void HttpAsyncClientBase::SetTimeout(int seconds) {
if (seconds > 0) {
timeout_seconds_ = seconds;
}
}
void HttpAsyncClientBase::Request(std::shared_ptr<HttpRequest> request,
HttpResponseCallback response_callback) {
assert(request);
assert(response_callback);
response_.reset(new HttpResponse());
response_parser_.reset(new HttpResponseParser(response_.get()));
stopped_ = false;
timed_out_ = false;
LOG_VERB("HTTP request:\n%s", request->Dump(4, "> ").c_str());
request_ = request;
response_callback_ = response_callback;
DoResolve(kHttpSslPort);
}
void HttpAsyncClientBase::Stop() {
LOG_INFO("The user asks to cancel the request.");
DoStop();
}
void HttpAsyncClientBase::DoResolve(const std::string& default_port) {
auto handler = std::bind(&HttpAsyncClientBase::OnResolve, shared_from_this(),
std::placeholders::_1, std::placeholders::_2);
resolver_.async_resolve(tcp::v4(), request_->host(),
request_->port(default_port), handler);
}
void HttpAsyncClientBase::OnResolve(boost::system::error_code ec,
tcp::resolver::results_type endpoints) {
if (ec) {
LOG_ERRO("Host resolve error (%s): %s, %s.", ec.message().c_str(),
request_->host().c_str(), request_->port().c_str());
response_callback_(response_, kHostResolveError, timed_out_);
} else {
LOG_VERB("Host resolved.");
DoConnect(endpoints);
}
}
void HttpAsyncClientBase::DoConnect(const Endpoints& endpoints) {
auto handler = std::bind(&HttpAsyncClientBase::OnConnect,
shared_from_this(),
std::placeholders::_1, std::placeholders::_2);
SocketAsyncConnect(endpoints, std::move(handler));
}
void HttpAsyncClientBase::OnConnect(boost::system::error_code ec,
tcp::endpoint endpoint) {
if (ec) {
LOG_ERRO("Socket connect error (%s).", ec.message().c_str());
DoStop();
response_callback_(response_, kEndpointConnectError, timed_out_);
return;
}
LOG_VERB("Socket connected.");
// Even though the connect operation notionally succeeded, the user could
// have stopped the operation by calling Stop(). And if we started the
// deadline timer, it could also be stopped due to timeout.
if (stopped_) {
// TODO: Use some other error.
response_callback_(response_, kEndpointConnectError, timed_out_);
return;
}
// Connection established.
OnConnected();
}
void HttpAsyncClientBase::DoWrite() {
// NOTE:
// It doesn't make much sense to set a timeout for socket write.
// I find that it's almost impossible to simulate a situation in the server
// side to test this timeout.
SocketAsyncWrite(std::bind(&HttpAsyncClientBase::OnWrite, shared_from_this(),
std::placeholders::_1, std::placeholders::_2));
}
void HttpAsyncClientBase::OnWrite(boost::system::error_code ec,
std::size_t /*length*/) {
if (stopped_) {
// TODO: Use some other error.
response_callback_(response_, kSocketWriteError, timed_out_);
return;
}
if (ec) {
LOG_ERRO("Socket write error (%s).", ec.message().c_str());
DoStop();
response_callback_(response_, kSocketWriteError, timed_out_);
} else {
LOG_INFO("Request sent.");
LOG_VERB("Read response (timeout: %ds)...", timeout_seconds_);
deadline_.expires_from_now(boost::posix_time::seconds(timeout_seconds_));
DoWaitDeadline();
DoRead();
}
}
void HttpAsyncClientBase::DoRead() {
auto handler = std::bind(&HttpAsyncClientBase::OnRead, shared_from_this(),
std::placeholders::_1, std::placeholders::_2);
SocketAsyncReadSome(std::move(handler));
}
void HttpAsyncClientBase::OnRead(boost::system::error_code ec,
std::size_t length) {
LOG_VERB("Socket async read handler.");
if (ec || length == 0) {
DoStop();
LOG_ERRO("Socket read error (%s).", ec.message().c_str());
response_callback_(response_, kSocketReadError, timed_out_);
return;
}
LOG_INFO("Read data, length: %u.", length);
// Parse the response piece just read.
// If the content has been fully received, |finished()| will be true.
if (!response_parser_->Parse(buffer_.data(), length)) {
DoStop();
LOG_ERRO("Failed to parse HTTP response.");
response_callback_(response_, kHttpError, timed_out_);
return;
}
if (response_parser_->finished()) {
DoStop();
LOG_INFO("Finished to read and parse HTTP response.");
LOG_VERB("HTTP response:\n%s", response_->Dump(4, "> ").c_str());
response_callback_(response_, kNoError, timed_out_);
return;
}
if (!stopped_) {
DoRead();
}
}
void HttpAsyncClientBase::DoWaitDeadline() {
deadline_.async_wait(std::bind(&HttpAsyncClientBase::OnDeadline,
shared_from_this(), std::placeholders::_1));
}
void HttpAsyncClientBase::OnDeadline(boost::system::error_code ec) {
if (stopped_) {
return;
}
LOG_VERB("OnDeadline.");
// NOTE: Can't check this:
// if (ec == boost::asio::error::operation_aborted) {
// LOG_VERB("Deadline timer canceled.");
// return;
// }
if (deadline_.expires_at() <=
boost::asio::deadline_timer::traits_type::now()) {
// The deadline has passed.
// The socket is closed so that any outstanding asynchronous operations
// are canceled.
LOG_WARN("HTTP client timed out.");
timed_out_ = true;
Stop();
return;
}
// Put the actor back to sleep.
DoWaitDeadline();
}
void HttpAsyncClientBase::DoStop() {
if (stopped_) {
return;
}
stopped_ = true;
LOG_INFO("Close socket...");
boost::system::error_code ec;
SocketClose(&ec);
if (ec) {
LOG_ERRO("Socket close error (%s).", ec.message().c_str());
}
LOG_INFO("Cancel deadline timer...");
deadline_.cancel();
}
} // namespace webcc

@ -1,130 +0,0 @@
#ifndef WEBCC_HTTP_ASYNC_CLIENT_BASE_H_
#define WEBCC_HTTP_ASYNC_CLIENT_BASE_H_
#include <functional>
#include <memory>
#include <vector>
#include "boost/asio/deadline_timer.hpp"
#include "boost/asio/io_context.hpp"
#include "boost/asio/ip/tcp.hpp"
#include "webcc/globals.h"
#include "webcc/http_request.h"
#include "webcc/http_response.h"
#include "webcc/http_response_parser.h"
namespace webcc {
// Response callback.
typedef std::function<void(HttpResponsePtr, Error, bool)> HttpResponseCallback;
// HTTP client session in asynchronous mode.
// A request will return without waiting for the response, the callback handler
// will be invoked when the response is received or timeout occurs.
// Don't use the same HttpAsyncClient object in multiple threads.
class HttpAsyncClientBase
: public std::enable_shared_from_this<HttpAsyncClientBase> {
public:
// The |buffer_size| is the bytes of the buffer for reading response.
// 0 means default value (e.g., 1024) will be used.
explicit HttpAsyncClientBase(boost::asio::io_context& io_context,
std::size_t buffer_size = 0);
virtual ~HttpAsyncClientBase() = default;
WEBCC_DELETE_COPY_ASSIGN(HttpAsyncClientBase);
// Set the timeout seconds for reading response.
// The |seconds| is only effective when greater than 0.
void SetTimeout(int seconds);
// Asynchronously connect to the server, send the request, read the response,
// and call the |response_callback| when all these finish.
void Request(HttpRequestPtr request, HttpResponseCallback response_callback);
// Called by the user to cancel the request.
void Stop();
protected:
using tcp = boost::asio::ip::tcp;
typedef tcp::resolver::results_type Endpoints;
typedef std::function<void(boost::system::error_code, std::size_t)>
ReadHandler;
typedef std::function<void(boost::system::error_code, tcp::endpoint)>
ConnectHandler;
typedef std::function<void(boost::system::error_code, std::size_t)>
WriteHandler;
// To enable_shared_from_this for both parent and derived.
// See https://stackoverflow.com/q/657155/6825348
template <typename Derived>
std::shared_ptr<Derived> shared_from_base() {
return std::static_pointer_cast<Derived>(shared_from_this());
}
protected:
virtual void Resolve() = 0;
void DoResolve(const std::string& default_port);
void OnResolve(boost::system::error_code ec,
tcp::resolver::results_type results);
void DoConnect(const Endpoints& endpoints);
void OnConnect(boost::system::error_code ec, tcp::endpoint endpoint);
virtual void OnConnected() = 0;
void DoWrite();
void OnWrite(boost::system::error_code ec, std::size_t length);
void DoRead();
void OnRead(boost::system::error_code ec, std::size_t length);
void DoWaitDeadline();
void OnDeadline(boost::system::error_code ec);
// Terminate all the actors to shut down the connection.
void DoStop();
virtual void SocketAsyncConnect(const Endpoints& endpoints,
ConnectHandler&& handler) = 0;
virtual void SocketAsyncWrite(WriteHandler&& handler) = 0;
virtual void SocketAsyncReadSome(ReadHandler&& handler) = 0;
virtual void SocketClose(boost::system::error_code* ec) = 0;
tcp::resolver resolver_;
HttpRequestPtr request_;
std::vector<char> buffer_;
HttpResponsePtr response_;
std::unique_ptr<HttpResponseParser> response_parser_;
HttpResponseCallback response_callback_;
// Timer for the timeout control.
boost::asio::deadline_timer deadline_;
// Maximum seconds to wait before the client cancels the operation.
// Only for receiving response from server.
int timeout_seconds_;
// Request stopped due to timeout or socket error.
bool stopped_;
// If the error was caused by timeout or not.
// Will be passed to the response handler/callback.
bool timed_out_;
};
} // namespace webcc
#endif // WEBCC_HTTP_ASYNC_CLIENT_BASE_H_

@ -7,12 +7,12 @@ namespace webcc {
// HTTP synchronous client. // HTTP synchronous client.
class HttpClient : public HttpClientBase { class HttpClient : public HttpClientBase {
public: public:
explicit HttpClient(std::size_t buffer_size = 0); explicit HttpClient(std::size_t buffer_size = 0);
~HttpClient() = default; ~HttpClient() = default;
private: private:
Error Connect(const HttpRequest& request) final { Error Connect(const HttpRequest& request) final {
return DoConnect(request, kPort80); return DoConnect(request, kPort80);
} }

@ -22,7 +22,7 @@ namespace webcc {
// or timeout occurs. // or timeout occurs.
// Please don't use the same client object in multiple threads. // Please don't use the same client object in multiple threads.
class HttpClientBase { class HttpClientBase {
public: public:
// The |buffer_size| is the bytes of the buffer for reading response. // The |buffer_size| is the bytes of the buffer for reading response.
// 0 means default value (e.g., 1024) will be used. // 0 means default value (e.g., 1024) will be used.
explicit HttpClientBase(std::size_t buffer_size = 0); explicit HttpClientBase(std::size_t buffer_size = 0);
@ -56,7 +56,7 @@ class HttpClientBase {
Error error() const { return error_; } Error error() const { return error_; }
protected: public:
typedef boost::asio::ip::tcp::resolver::results_type Endpoints; typedef boost::asio::ip::tcp::resolver::results_type Endpoints;
typedef std::function<void(boost::system::error_code, std::size_t)> typedef std::function<void(boost::system::error_code, std::size_t)>

@ -14,14 +14,20 @@ HttpResponsePtr HttpClientSession::Request(HttpRequestArgs&& args) {
assert(args.parameters_.size() % 2 == 0); assert(args.parameters_.size() % 2 == 0);
assert(args.headers_.size() % 2 == 0); assert(args.headers_.size() % 2 == 0);
HttpRequest request{ args.method_, args.url_, args.parameters_ }; 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]);
}
if (!args.data_.empty()) { if (!args.data_.empty()) {
request.SetContent(std::move(args.data_), true); request.SetContent(std::move(args.data_), true);
// TODO: charset/encoding // TODO: Request-level charset.
if (args.json_) { if (args.json_) {
request.SetContentType(http::media_types::kApplicationJson, ""); request.SetContentType(http::media_types::kApplicationJson, charset_);
} else if (!content_type_.empty()) {
request.SetContentType(content_type_, charset_);
} }
} }

@ -15,6 +15,14 @@ public:
~HttpClientSession() = default; ~HttpClientSession() = default;
void set_content_type(const std::string& content_type) {
content_type_ = content_type;
}
void set_charset(const std::string& charset) {
charset_ = charset;
}
void AddHeader(const std::string& key, const std::string& value) { void AddHeader(const std::string& key, const std::string& value) {
headers_.Add(key, value); headers_.Add(key, value);
} }
@ -34,7 +42,13 @@ public:
private: private:
void InitHeaders(); void InitHeaders();
// Headers to be sent on each request sent from this session. // E.g., "application/json".
std::string content_type_;
// E.g., "utf-8".
std::string charset_;
// Headers for each request sent from this session.
HttpHeaderDict headers_; HttpHeaderDict headers_;
}; };

@ -73,12 +73,6 @@ void HttpMessage::SetContent(std::string&& content, bool set_length) {
} }
} }
void HttpMessage::SetContentInAppJsonUtf8(std::string&& content,
bool set_length) {
SetContent(std::move(content), set_length);
SetContentType(http::media_types::kApplicationJson, http::charsets::kUtf8);
}
// ATTENTION: The buffers don't hold the memory! // ATTENTION: The buffers don't hold the memory!
std::vector<boost::asio::const_buffer> HttpMessage::ToBuffers() const { std::vector<boost::asio::const_buffer> HttpMessage::ToBuffers() const {
assert(!start_line_.empty()); assert(!start_line_.empty());

@ -88,8 +88,6 @@ public:
// TODO: Remove parameter |set_length|. // TODO: Remove parameter |set_length|.
void SetContent(std::string&& content, bool set_length); void SetContent(std::string&& content, bool set_length);
void SetContentInAppJsonUtf8(std::string&& content, bool set_length);
// Make the message (e.g., update start line). // Make the message (e.g., update start line).
// Must be called before ToBuffers()! // Must be called before ToBuffers()!
virtual bool Prepare() = 0; virtual bool Prepare() = 0;

@ -11,7 +11,7 @@ class HttpMessage;
// HttpParser parses HTTP request and response. // HttpParser parses HTTP request and response.
class HttpParser { class HttpParser {
public: public:
explicit HttpParser(HttpMessage* message); explicit HttpParser(HttpMessage* message);
virtual ~HttpParser() = default; virtual ~HttpParser() = default;
@ -24,7 +24,7 @@ class HttpParser {
bool Parse(const char* data, std::size_t length); bool Parse(const char* data, std::size_t length);
protected: public:
// Parse headers from pending data. // Parse headers from pending data.
// Return false only on syntax errors. // Return false only on syntax errors.
bool ParseHeaders(); bool ParseHeaders();

@ -4,16 +4,6 @@
namespace webcc { namespace webcc {
HttpRequest::HttpRequest(const std::string& method,
const std::string& url,
const std::vector<std::string>& parameters)
: method_(method), url_(url) {
assert(parameters.size() % 2 == 0);
for (std::size_t i = 1; i < parameters.size(); i += 2) {
url_.AddParameter(parameters[i - 1], parameters[i]);
}
}
bool HttpRequest::Prepare() { bool HttpRequest::Prepare() {
if (url_.host().empty()) { if (url_.host().empty()) {
LOG_ERRO("Invalid request: host is missing."); LOG_ERRO("Invalid request: host is missing.");
@ -41,17 +31,4 @@ bool HttpRequest::Prepare() {
return true; return true;
} }
HttpRequestPtr HttpRequest::New(const std::string& method,
const std::string& url,
const std::vector<std::string>& parameters,
bool prepare) {
HttpRequestPtr request{ new HttpRequest{ method, url, parameters } };
if (prepare) {
request->Prepare();
}
return request;
}
} // namespace webcc } // namespace webcc

@ -11,7 +11,6 @@
namespace webcc { namespace webcc {
class HttpRequest; class HttpRequest;
class HttpRequestParser;
typedef std::shared_ptr<HttpRequest> HttpRequestPtr; typedef std::shared_ptr<HttpRequest> HttpRequestPtr;
@ -19,13 +18,25 @@ class HttpRequest : public HttpMessage {
public: public:
HttpRequest() = default; HttpRequest() = default;
// TODO: Move parameters HttpRequest(const std::string& method, const std::string& url)
HttpRequest(const std::string& method, : method_(method), url_(url) {
const std::string& url, }
const std::vector<std::string>& parameters = {});
~HttpRequest() override = default; ~HttpRequest() override = default;
void set_method(const std::string& method) {
method_ = method;
}
void set_url(const std::string& url) {
url_.Init(url);
}
// Add URL query parameter.
void AddParameter(const std::string& key, const std::string& value) {
url_.AddParameter(key, value);
}
const std::string& method() const { const std::string& method() const {
return method_; return method_;
} }
@ -46,36 +57,11 @@ public:
return port().empty() ? default_port : port(); return port().empty() ? default_port : port();
} }
// Shortcut to set `Accept` header.
void Accept(const std::string& media_type) {
SetHeader(http::headers::kAccept, media_type);
}
// Shortcut to set `Accept` header.
void AcceptAppJson() {
SetHeader(http::headers::kAccept, http::media_types::kApplicationJson);
}
// Prepare payload. // Prepare payload.
// Compose start line, set Host header, etc. // Compose start line, set Host header, etc.
bool Prepare() override; bool Prepare() override;
// TODO: Re-place
static HttpRequestPtr New(const std::string& method,
const std::string& url,
const std::vector<std::string>& parameters = {},
bool prepare = true);
private: private:
friend class HttpRequestParser;
void set_method(const std::string& method) {
method_ = method;
}
void set_url(const std::string& url) {
url_.Init(url);
}
std::string method_; std::string method_;
Url url_; Url url_;
}; };

@ -19,8 +19,8 @@ bool HttpRequestParser::ParseStartLine(const std::string& line) {
return false; return false;
} }
request_->set_method(strs[0]); request_->set_method(std::move(strs[0]));
request_->set_url(strs[1]); request_->set_url(std::move(strs[1]));
// HTTP version is ignored. // HTTP version is ignored.

@ -10,12 +10,12 @@ namespace webcc {
class HttpRequest; class HttpRequest;
class HttpRequestParser : public HttpParser { class HttpRequestParser : public HttpParser {
public: public:
explicit HttpRequestParser(HttpRequest* request); explicit HttpRequestParser(HttpRequest* request);
~HttpRequestParser() override = default; ~HttpRequestParser() override = default;
private: private:
bool ParseStartLine(const std::string& line) override; bool ParseStartLine(const std::string& line) override;
HttpRequest* request_; HttpRequest* request_;

@ -9,7 +9,7 @@
namespace webcc { namespace webcc {
class HttpResponse : public HttpMessage { class HttpResponse : public HttpMessage {
public: public:
HttpResponse() : status_(http::Status::kOK) {} HttpResponse() : status_(http::Status::kOK) {}
~HttpResponse() override = default; ~HttpResponse() override = default;
@ -25,7 +25,7 @@ class HttpResponse : public HttpMessage {
// TODO: Avoid copy. // TODO: Avoid copy.
static HttpResponse Fault(http::Status status); static HttpResponse Fault(http::Status status);
private: private:
int status_; int status_;
}; };

@ -10,12 +10,12 @@ namespace webcc {
class HttpResponse; class HttpResponse;
class HttpResponseParser : public HttpParser { class HttpResponseParser : public HttpParser {
public: public:
explicit HttpResponseParser(HttpResponse* response); explicit HttpResponseParser(HttpResponse* response);
~HttpResponseParser() override = default; ~HttpResponseParser() override = default;
private: private:
// Parse HTTP start line; E.g., "HTTP/1.1 200 OK". // Parse HTTP start line; E.g., "HTTP/1.1 200 OK".
bool ParseStartLine(const std::string& line) override; bool ParseStartLine(const std::string& line) override;

@ -17,7 +17,7 @@ class HttpRequestHandler;
// HTTP server accepts TCP connections from TCP clients. // HTTP server accepts TCP connections from TCP clients.
// NOTE: Only support IPv4. // NOTE: Only support IPv4.
class HttpServer { class HttpServer {
public: public:
HttpServer(std::uint16_t port, std::size_t workers); HttpServer(std::uint16_t port, std::size_t workers);
virtual ~HttpServer() = default; virtual ~HttpServer() = default;
@ -27,7 +27,7 @@ class HttpServer {
// Run the server's io_service loop. // Run the server's io_service loop.
void Run(); void Run();
private: private:
// Register to handle the signals that indicate when the server should exit. // Register to handle the signals that indicate when the server should exit.
void RegisterSignals(); void RegisterSignals();

@ -1,71 +0,0 @@
#include "webcc/http_ssl_async_client.h"
#include "boost/asio/connect.hpp"
#include "webcc/logger.h"
using tcp = boost::asio::ip::tcp;
namespace ssl = boost::asio::ssl;
namespace webcc {
HttpSslAsyncClient::HttpSslAsyncClient(boost::asio::io_context& io_context,
std::size_t buffer_size,
bool ssl_verify)
: HttpAsyncClientBase(io_context, buffer_size),
ssl_context_(ssl::context::sslv23),
ssl_socket_(io_context, ssl_context_),
ssl_verify_(ssl_verify) {
// Use the default paths for finding CA certificates.
ssl_context_.set_default_verify_paths();
}
void HttpSslAsyncClient::Resolve() {
DoResolve(kHttpSslPort);
}
void HttpSslAsyncClient::DoHandshake() {
if (ssl_verify_) {
ssl_socket_.set_verify_mode(ssl::verify_peer);
} else {
ssl_socket_.set_verify_mode(ssl::verify_none);
}
ssl_socket_.set_verify_callback(ssl::rfc2818_verification(request_->host()));
ssl_socket_.async_handshake(ssl::stream_base::client,
std::bind(&HttpSslAsyncClient::OnHandshake,
shared_from_this(),
std::placeholders::_1));
}
void HttpSslAsyncClient::OnHandshake(boost::system::error_code ec) {
if (ec) {
LOG_ERRO("Handshake error (%s).", ec.message().c_str());
response_callback_(response_, kHandshakeError, false);
return;
}
DoWrite();
}
void HttpSslAsyncClient::SocketAsyncConnect(const Endpoints& endpoints,
ConnectHandler&& handler) {
boost::asio::async_connect(ssl_socket_.lowest_layer(), endpoints,
std::move(handler));
}
void HttpSslAsyncClient::SocketAsyncWrite(WriteHandler&& handler) {
boost::asio::async_write(ssl_socket_, request_->ToBuffers(),
std::move(handler));
}
void HttpSslAsyncClient::SocketAsyncReadSome(ReadHandler&& handler) {
ssl_socket_.async_read_some(boost::asio::buffer(buffer_), std::move(handler));
}
void HttpSslAsyncClient::SocketClose(boost::system::error_code* ec) {
ssl_socket_.lowest_layer().close(*ec);
}
} // namespace webcc

@ -1,69 +0,0 @@
#ifndef WEBCC_HTTP_SSL_ASYNC_CLIENT_H_
#define WEBCC_HTTP_SSL_ASYNC_CLIENT_H_
#include "webcc/http_async_client_base.h"
#include "boost/asio/ssl.hpp"
namespace webcc {
class HttpSslAsyncClient;
typedef std::shared_ptr<HttpSslAsyncClient> HttpSslAsyncClientPtr;
// HTTP SSL (a.k.a., HTTPS) asynchronous client.
class HttpSslAsyncClient : public HttpAsyncClientBase {
public:
~HttpSslAsyncClient() = default;
// Forbid to create HttpSslAsyncClient in stack since it's derived from
// std::shared_from_this.
static HttpSslAsyncClientPtr New(boost::asio::io_context& io_context,
std::size_t buffer_size = 0,
bool ssl_verify = true) {
return HttpSslAsyncClientPtr{
new HttpSslAsyncClient(io_context, buffer_size, ssl_verify)
};
}
// See https://stackoverflow.com/q/657155/6825348
std::shared_ptr<HttpSslAsyncClient> shared_from_this() {
return shared_from_base<HttpSslAsyncClient>();
}
private:
// SSL verification (|ssl_verify|) needs CA certificates to be found
// in the default verify paths of OpenSSL. On Windows, it means you need to
// set environment variable SSL_CERT_FILE properly.
explicit HttpSslAsyncClient(boost::asio::io_context& io_context,
std::size_t buffer_size = 0,
bool ssl_verify = true);
void Resolve() final;
// Override to do handshake after connected.
void OnConnected() final {
DoHandshake();
}
void DoHandshake();
void OnHandshake(boost::system::error_code ec);
void SocketAsyncConnect(const Endpoints& endpoints,
ConnectHandler&& handler) final;
void SocketAsyncWrite(WriteHandler&& handler) final;
void SocketAsyncReadSome(ReadHandler&& handler) final;
void SocketClose(boost::system::error_code* ec) final;
boost::asio::ssl::context ssl_context_;
boost::asio::ssl::stream<boost::asio::ip::tcp::socket> ssl_socket_;
// Verify the certificate of the peer (remote server) or not.
bool ssl_verify_;
};
} // namespace webcc
#endif // WEBCC_HTTP_SSL_ASYNC_CLIENT_H_

@ -9,7 +9,7 @@ namespace webcc {
// HTTP SSL (a.k.a., HTTPS) synchronous client. // HTTP SSL (a.k.a., HTTPS) synchronous client.
class HttpSslClient : public HttpClientBase { class HttpSslClient : public HttpClientBase {
public: public:
// SSL verification (|ssl_verify|) needs CA certificates to be found // SSL verification (|ssl_verify|) needs CA certificates to be found
// in the default verify paths of OpenSSL. On Windows, it means you need to // in the default verify paths of OpenSSL. On Windows, it means you need to
// set environment variable SSL_CERT_FILE properly. // set environment variable SSL_CERT_FILE properly.
@ -17,7 +17,7 @@ class HttpSslClient : public HttpClientBase {
~HttpSslClient() = default; ~HttpSslClient() = default;
private: private:
Error Handshake(const std::string& host); Error Handshake(const std::string& host);
// Override to do handshake after connected. // Override to do handshake after connected.

@ -12,7 +12,7 @@ namespace webcc {
template <typename T> template <typename T>
class Queue { class Queue {
public: public:
Queue() = default; Queue() = default;
Queue(const Queue&) = delete; Queue(const Queue&) = delete;
@ -49,7 +49,7 @@ class Queue {
not_empty_cv_.notify_one(); not_empty_cv_.notify_one();
} }
private: private:
std::list<T> message_list_; std::list<T> message_list_;
std::mutex mutex_; std::mutex mutex_;
std::condition_variable not_empty_cv_; std::condition_variable not_empty_cv_;

@ -1,38 +0,0 @@
#include "webcc/rest_async_client.h"
namespace webcc {
RestAsyncClient::RestAsyncClient(boost::asio::io_context& io_context,
const std::string& host,
const std::string& port,
std::size_t buffer_size)
: io_context_(io_context),
host_(host), port_(port),
timeout_seconds_(0),
buffer_size_(buffer_size) {
}
void RestAsyncClient::Request(const std::string& method,
const std::string& url,
std::string&& content,
HttpResponseCallback callback) {
HttpRequestPtr http_request(new HttpRequest(method, url, host_, port_));
if (!content.empty()) {
http_request->SetContent(std::move(content), true);
http_request->SetContentType(http::media_types::kApplicationJson,
http::charsets::kUtf8);
}
http_request->Prepare();
auto http_async_client = HttpAsyncClient::New(io_context_, buffer_size_);
if (timeout_seconds_ > 0) {
http_async_client->SetTimeout(timeout_seconds_);
}
http_async_client->Request(http_request, callback);
}
} // namespace webcc

@ -1,61 +0,0 @@
#ifndef WEBCC_REST_ASYNC_CLIENT_H_
#define WEBCC_REST_ASYNC_CLIENT_H_
#include <string>
#include <utility> // for move()
#include "webcc/http_async_client.h"
namespace webcc {
class RestAsyncClient {
public:
RestAsyncClient(boost::asio::io_context& io_context,
const std::string& host, const std::string& port,
std::size_t buffer_size = 0);
void SetTimeout(int seconds) {
timeout_seconds_ = seconds;
}
void Get(const std::string& url, HttpResponseCallback callback) {
Request(kHttpGet, url, "", callback);
}
void Post(const std::string& url, std::string&& content,
HttpResponseCallback callback) {
Request(kHttpPost, url, std::move(content), callback);
}
void Put(const std::string& url, std::string&& content,
HttpResponseCallback callback) {
Request(kHttpPut, url, std::move(content), callback);
}
void Patch(const std::string& url, std::string&& content,
HttpResponseCallback callback) {
Request(kHttpPatch, url, std::move(content), callback);
}
void Delete(const std::string& url, HttpResponseCallback callback) {
Request(kHttpDelete, url, "", callback);
}
private:
void Request(const std::string& method, const std::string& url,
std::string&& content, HttpResponseCallback callback);
boost::asio::io_context& io_context_;
std::string host_;
std::string port_;
// Timeout in seconds; only effective when > 0.
int timeout_seconds_;
std::size_t buffer_size_;
};
} // namespace webcc
#endif // WEBCC_REST_ASYNC_CLIENT_H_

@ -1,34 +0,0 @@
#include "webcc/rest_client.h"
#include "webcc/utility.h"
namespace webcc {
RestClient::RestClient(const std::string& host, const std::string& port,
std::size_t buffer_size)
: host_(host), port_(port), http_client_(buffer_size) {
AdjustHostPort(host_, port_);
}
bool RestClient::Request(const std::string& method, const std::string& url,
std::string&& content, std::size_t buffer_size) {
HttpRequest http_request(method, url, host_, port_);
http_request.SetHeader(http::headers::kAccept,
http::media_types::kApplicationJson);
if (!content.empty()) {
http_request.SetContent(std::move(content), true);
http_request.SetContentType(http::media_types::kApplicationJson,
http::charsets::kUtf8);
}
http_request.Prepare();
if (!http_client_.Request(http_request, buffer_size)) {
return false;
}
return true;
}
} // namespace webcc

@ -1,94 +0,0 @@
#ifndef WEBCC_REST_CLIENT_H_
#define WEBCC_REST_CLIENT_H_
#include <cassert>
#include <string>
#include <utility> // for move()
#include "webcc/globals.h"
#include "webcc/http_client.h"
#include "webcc/http_request.h"
#include "webcc/http_response.h"
namespace webcc {
class RestClient {
public:
// If |port| is empty, |host| will be checked to see if it contains port or
// not (separated by ':').
explicit RestClient(const std::string& host,
const std::string& port = "",
std::size_t buffer_size = 0);
~RestClient() = default;
WEBCC_DELETE_COPY_ASSIGN(RestClient);
void SetTimeout(int seconds) {
http_client_.SetTimeout(seconds);
}
// NOTE:
// The return value of the following methods (Get, Post, etc.) only indicates
// if the socket communication is successful or not. Check error() and
// timed_out() for more information if it's failed. Check response_status()
// instead for the HTTP status code.
inline bool Get(const std::string& url, std::size_t buffer_size = 0) {
return Request("GET", url, "", buffer_size);
}
inline bool Post(const std::string& url, std::string&& content,
std::size_t buffer_size = 0) {
return Request("POST", url, std::move(content), buffer_size);
}
inline bool Put(const std::string& url, std::string&& content,
std::size_t buffer_size = 0) {
return Request("PUT", url, std::move(content), buffer_size);
}
inline bool Patch(const std::string& url, std::string&& content,
std::size_t buffer_size = 0) {
return Request("PATCH", url, std::move(content), buffer_size);
}
inline bool Delete(const std::string& url, std::size_t buffer_size = 0) {
return Request(kHttpDelete, url, "", buffer_size);
}
HttpResponsePtr response() const {
return http_client_.response();
}
int response_status() const {
assert(response());
return response()->status();
}
const std::string& response_content() const {
assert(response());
return response()->content();
}
bool timed_out() const {
return http_client_.timed_out();
}
Error error() const {
return http_client_.error();
}
private:
bool Request(const std::string& method, const std::string& url,
std::string&& content, std::size_t buffer_size);
std::string host_;
std::string port_;
HttpClient http_client_;
};
} // namespace webcc
#endif // WEBCC_REST_CLIENT_H_

@ -12,7 +12,7 @@
namespace webcc { namespace webcc {
class RestServer : public HttpServer { class RestServer : public HttpServer {
public: public:
RestServer(std::uint16_t port, std::size_t workers) RestServer(std::uint16_t port, std::size_t workers)
: HttpServer(port, workers) { : HttpServer(port, workers) {
} }
@ -31,7 +31,7 @@ class RestServer : public HttpServer {
return request_handler_.Bind(service, url, is_regex); return request_handler_.Bind(service, url, is_regex);
} }
private: private:
HttpRequestHandler* GetRequestHandler() override { HttpRequestHandler* GetRequestHandler() override {
return &request_handler_; return &request_handler_;
} }

@ -46,7 +46,7 @@ struct RestResponse {
// Base class for your REST service. // Base class for your REST service.
class RestService { class RestService {
public: public:
virtual ~RestService() = default; virtual ~RestService() = default;
// Handle REST request, output response. // Handle REST request, output response.
@ -58,10 +58,10 @@ typedef std::shared_ptr<RestService> RestServicePtr;
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
class RestListService : public RestService { class RestListService : public RestService {
public: public:
void Handle(const RestRequest& request, RestResponse* response) final; void Handle(const RestRequest& request, RestResponse* response) final;
protected: public:
RestListService() = default; RestListService() = default;
virtual void Get(const UrlQuery& query, RestResponse* response) { virtual void Get(const UrlQuery& query, RestResponse* response) {
@ -75,10 +75,10 @@ class RestListService : public RestService {
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
class RestDetailService : public RestService { class RestDetailService : public RestService {
public: public:
void Handle(const RestRequest& request, RestResponse* response) final; void Handle(const RestRequest& request, RestResponse* response) final;
protected: public:
virtual void Get(const UrlSubMatches& url_sub_matches, virtual void Get(const UrlSubMatches& url_sub_matches,
const UrlQuery& query, const UrlQuery& query,
RestResponse* response) { RestResponse* response) {

@ -11,7 +11,7 @@
namespace webcc { namespace webcc {
class RestServiceManager { class RestServiceManager {
public: public:
RestServiceManager() = default; RestServiceManager() = default;
WEBCC_DELETE_COPY_ASSIGN(RestServiceManager); WEBCC_DELETE_COPY_ASSIGN(RestServiceManager);
@ -30,9 +30,9 @@ class RestServiceManager {
RestServicePtr GetService(const std::string& url, RestServicePtr GetService(const std::string& url,
std::vector<std::string>* sub_matches); std::vector<std::string>* sub_matches);
private: private:
class ServiceItem { class ServiceItem {
public: public:
ServiceItem(RestServicePtr _service, const std::string& _url, ServiceItem(RestServicePtr _service, const std::string& _url,
bool _is_regex) bool _is_regex)
: service(_service), url(_url), is_regex(_is_regex) { : service(_service), url(_url), is_regex(_is_regex) {

@ -1,54 +0,0 @@
#include "webcc/rest_ssl_client.h"
#include "boost/algorithm/string.hpp"
#include "webcc/utility.h"
namespace webcc {
RestClientBase::RestClientBase(HttpClientBase* http_client_base,
const SSMap& headers)
: http_client_base_(http_client_base),
content_media_type_(http::media_types::kApplicationJson),
content_charset_(http::charsets::kUtf8),
headers_(headers) {
}
RestSslClient::RestSslClient(const std::string& host, const std::string& port,
bool ssl_verify, const SSMap& headers,
std::size_t buffer_size)
: RestClientBase(&http_ssl_client_, headers),
host_(host), port_(port),
http_ssl_client_(ssl_verify, buffer_size) {
AdjustHostPort(host_, port_);
}
bool RestSslClient::Request(const std::string& method, const std::string& url,
std::string&& content, const SSMap& headers,
std::size_t buffer_size) {
HttpRequest http_request(method, url, host_, port_);
if (!content.empty()) {
http_request.SetContent(std::move(content), true);
http_request.SetContentType(content_media_type_, content_charset_);
}
for (auto& h : headers_) {
http_request.SetHeader(h.first, h.second);
}
for (auto& h : headers) {
http_request.SetHeader(h.first, h.second);
}
http_request.Prepare();
if (!http_ssl_client_.Request(http_request, buffer_size)) {
return false;
}
return true;
}
} // namespace webcc

@ -1,130 +0,0 @@
#ifndef WEBCC_REST_SSL_CLIENT_H_
#define WEBCC_REST_SSL_CLIENT_H_
#include <cassert>
#include <map>
#include <string>
#include <utility> // for move()
#include "webcc/globals.h"
#include "webcc/http_request.h"
#include "webcc/http_response.h"
#include "webcc/http_ssl_client.h"
namespace webcc {
typedef std::map<std::string, std::string> SSMap;
class RestClientBase {
public:
RestClientBase(HttpClientBase* http_client_base,
const SSMap& headers = {});
virtual ~RestClientBase() = default;
WEBCC_DELETE_COPY_ASSIGN(RestClientBase);
void SetTimeout(int seconds) {
http_client_base_->SetTimeout(seconds);
}
// Overwrite the default content type (json & utf-8).
void SetContentType(const std::string& media_type,
const std::string& charset) {
content_media_type_ = media_type;
content_charset_ = charset;
}
HttpResponsePtr response() const {
return http_client_base_->response();
}
int response_status() const {
assert(response());
return response()->status();
}
const std::string& response_content() const {
assert(response());
return response()->content();
}
bool timed_out() const {
return http_client_base_->timed_out();
}
Error error() const {
return http_client_base_->error();
}
protected:
// Default: "application/json".
std::string content_media_type_;
// Default: "utf-8".
std::string content_charset_;
// Default headers for each request sent from this session.
std::map<std::string, std::string> headers_;
private:
HttpClientBase* http_client_base_;
};
class RestSslClient : public RestClientBase {
public:
// If |port| is empty, |host| will be checked to see if it contains port or
// not (separated by ':').
explicit RestSslClient(const std::string& host,
const std::string& port = "",
bool ssl_verify = true,
const SSMap& headers = {},
std::size_t buffer_size = 0);
~RestSslClient() = default;
// NOTE:
// The return value of the following methods (Get, Post, etc.) only indicates
// if the socket communication is successful or not. Check error() and
// timed_out() for more information if it's failed. Check response_status()
// instead for the HTTP status code.
inline bool Get(const std::string& url, const SSMap& headers = {},
std::size_t buffer_size = 0) {
return Request(kHttpGet, url, "", headers, buffer_size);
}
inline bool Post(const std::string& url, std::string&& content,
const SSMap& headers = {}, std::size_t buffer_size = 0) {
return Request(Post, url, std::move(content), headers, buffer_size);
}
inline bool Put(const std::string& url, std::string&& content,
const SSMap& headers = {}, std::size_t buffer_size = 0) {
return Request(kHttpPut, url, std::move(content), headers, buffer_size);
}
inline bool Patch(const std::string& url, std::string&& content,
const SSMap& headers = {}, std::size_t buffer_size = 0) {
return Request(kHttpPatch, url, std::move(content), headers, buffer_size);
}
inline bool Delete(const std::string& url, const SSMap& headers = {},
std::size_t buffer_size = 0) {
return Request(kHttpDelete, url, "", headers, buffer_size);
}
bool Request(const std::string& method, const std::string& url,
std::string&& content, const SSMap& headers = {},
std::size_t buffer_size = 0);
private:
std::string host_;
std::string port_;
HttpSslClient http_ssl_client_;
};
} // namespace webcc
#endif // WEBCC_REST_SSL_CLIENT_H_

@ -1,102 +0,0 @@
#include "webcc/soap_async_client.h"
#include <cassert>
#include <utility> // for move()
#include "webcc/soap_globals.h"
#include "webcc/soap_request.h"
#include "webcc/soap_response.h"
#include "webcc/utility.h"
namespace webcc {
SoapAsyncClient::SoapAsyncClient(boost::asio::io_context& io_context,
const std::string& host,
const std::string& port,
SoapVersion soap_version,
std::size_t buffer_size)
: io_context_(io_context),
host_(host), port_(port),
soap_version_(soap_version),
buffer_size_(buffer_size),
format_raw_(true), timeout_seconds_(0) {
AdjustHostPort(host_, port_);
}
void SoapAsyncClient::Request(const std::string& operation,
std::vector<SoapParameter>&& parameters,
SoapResponseCallback soap_response_callback) {
assert(service_ns_.IsValid());
assert(!url_.empty() && !host_.empty());
assert(!result_name_.empty());
SoapRequest soap_request;
// Set SOAP envelope namespace according to SOAP version.
if (soap_version_ == kSoapV11) {
soap_request.set_soapenv_ns(kSoapEnvNamespaceV11);
} else {
soap_request.set_soapenv_ns(kSoapEnvNamespaceV12);
}
soap_request.set_service_ns(service_ns_);
soap_request.set_operation(operation);
for (SoapParameter& p : parameters) {
soap_request.AddParameter(std::move(p));
}
std::string http_content;
soap_request.ToXml(format_raw_, indent_str_, &http_content);
HttpRequestPtr http_request(new HttpRequest(kHttpPost, url_, host_, port_));
http_request->SetContent(std::move(http_content), true);
if (soap_version_ == kSoapV11) {
http_request->SetContentType(http::media_types::kTextXml,
http::charsets::kUtf8);
} else {
http_request->SetContentType(http::media_types::kApplicationSoapXml,
http::charsets::kUtf8);
}
http_request->SetHeader(kSoapAction, operation);
http_request->Prepare();
auto http_async_client = HttpAsyncClient::New(io_context_, buffer_size_);
if (timeout_seconds_ > 0) {
http_async_client->SetTimeout(timeout_seconds_);
}
auto http_response_callback = std::bind(&SoapAsyncClient::OnHttpResponse,
this, soap_response_callback,
std::placeholders::_1,
std::placeholders::_2,
std::placeholders::_3);
http_async_client->Request(http_request, http_response_callback);
}
void SoapAsyncClient::OnHttpResponse(SoapResponseCallback soap_response_callback,
HttpResponsePtr http_response,
Error error, bool timed_out) {
if (error != kNoError) {
soap_response_callback("", error, timed_out);
} else {
SoapResponse soap_response;
// TODO
//soap_response.set_result_name(result_name_);
if (!soap_response.FromXml(http_response->content())) {
soap_response_callback("", kXmlError, false);
} else {
// TODO
//soap_response_callback(soap_response.result_moved(), kNoError, false);
}
}
}
} // namespace webcc

@ -1,91 +0,0 @@
#ifndef WEBCC_SOAP_ASYNC_CLIENT_H_
#define WEBCC_SOAP_ASYNC_CLIENT_H_
#include <functional>
#include <string>
#include <vector>
#include "webcc/http_async_client.h"
#include "webcc/soap_message.h"
#include "webcc/soap_parameter.h"
namespace webcc {
// Response callback.
typedef std::function<void(std::string, Error, bool)> SoapResponseCallback;
class SoapAsyncClient {
public:
// If |port| is empty, |host| will be checked to see if it contains port or
// not (separated by ':').
SoapAsyncClient(boost::asio::io_context& io_context, // NOLINT
const std::string& host, const std::string& port = "",
SoapVersion soap_version = kSoapV12,
std::size_t buffer_size = 0);
~SoapAsyncClient() = default;
WEBCC_DELETE_COPY_ASSIGN(SoapAsyncClient);
void set_timeout_seconds(int timeout_seconds) {
timeout_seconds_ = timeout_seconds;
}
void set_url(const std::string& url) { url_ = url; }
void set_service_ns(const SoapNamespace& service_ns) {
service_ns_ = service_ns;
}
void set_result_name(const std::string& result_name) {
result_name_ = result_name;
}
void set_format_raw(bool format_raw) { format_raw_ = format_raw; }
void set_indent_str(const std::string& indent_str) {
indent_str_ = indent_str;
}
void Request(const std::string& operation,
std::vector<SoapParameter>&& parameters,
SoapResponseCallback soap_response_callback);
private:
void OnHttpResponse(SoapResponseCallback soap_response_callback,
HttpResponsePtr http_response,
Error error, bool timed_out);
boost::asio::io_context& io_context_;
std::string host_;
std::string port_; // Leave this empty to use default 80.
SoapVersion soap_version_;
std::size_t buffer_size_;
// Request URL.
std::string url_;
// Namespace for your web service.
SoapNamespace service_ns_;
// Response result XML node name.
// E.g., "Result".
std::string result_name_;
// Format request XML without any indentation or line breaks.
bool format_raw_;
// Indent string for request XML.
// Applicable when |format_raw_| is false.
std::string indent_str_;
// Timeout in seconds; only effective when > 0.
int timeout_seconds_;
};
} // namespace webcc
#endif // WEBCC_SOAP_ASYNC_CLIENT_H_

@ -12,7 +12,7 @@
namespace webcc { namespace webcc {
class SoapClient { class SoapClient {
public: public:
// If |port| is empty, |host| will be checked to see if it contains port or // If |port| is empty, |host| will be checked to see if it contains port or
// not (separated by ':'). // not (separated by ':').
explicit SoapClient(const std::string& host, const std::string& port = "", explicit SoapClient(const std::string& host, const std::string& port = "",
@ -69,7 +69,7 @@ class SoapClient {
std::shared_ptr<SoapFault> fault() const { return fault_; } std::shared_ptr<SoapFault> fault() const { return fault_; }
private: private:
std::string host_; std::string host_;
std::string port_; // Leave this empty to use default 80. std::string port_; // Leave this empty to use default 80.

@ -11,7 +11,7 @@ namespace webcc {
// Base class for SOAP request and response. // Base class for SOAP request and response.
class SoapMessage { class SoapMessage {
public: public:
virtual ~SoapMessage() = default; virtual ~SoapMessage() = default;
// E.g., set as kSoapEnvNamespace. // E.g., set as kSoapEnvNamespace.
@ -38,7 +38,7 @@ class SoapMessage {
// Parse from SOAP XML. // Parse from SOAP XML.
bool FromXml(const std::string& xml_string); bool FromXml(const std::string& xml_string);
protected: public:
// Convert to SOAP body XML. // Convert to SOAP body XML.
virtual void ToXmlBody(pugi::xml_node xbody) = 0; virtual void ToXmlBody(pugi::xml_node xbody) = 0;

@ -9,7 +9,7 @@ namespace webcc {
// Key-value SOAP parameter. // Key-value SOAP parameter.
class SoapParameter { class SoapParameter {
public: public:
SoapParameter() : as_cdata_(false) { SoapParameter() : as_cdata_(false) {
} }
@ -77,7 +77,7 @@ class SoapParameter {
bool as_cdata() const { return as_cdata_; } bool as_cdata() const { return as_cdata_; }
private: private:
std::string key_; std::string key_;
std::string value_; std::string value_;
bool as_cdata_; bool as_cdata_;

@ -13,7 +13,7 @@ namespace webcc {
// Used to compose the SOAP request envelope XML which will be sent as the HTTP // Used to compose the SOAP request envelope XML which will be sent as the HTTP
// request body. // request body.
class SoapRequest : public SoapMessage { class SoapRequest : public SoapMessage {
public: public:
void AddParameter(const SoapParameter& parameter); void AddParameter(const SoapParameter& parameter);
void AddParameter(SoapParameter&& parameter); void AddParameter(SoapParameter&& parameter);
@ -21,11 +21,11 @@ class SoapRequest : public SoapMessage {
// Get parameter value by key. // Get parameter value by key.
const std::string& GetParameter(const std::string& key) const; const std::string& GetParameter(const std::string& key) const;
protected: public:
void ToXmlBody(pugi::xml_node xbody) override; void ToXmlBody(pugi::xml_node xbody) override;
bool FromXmlBody(pugi::xml_node xbody) override; bool FromXmlBody(pugi::xml_node xbody) override;
private: private:
std::vector<SoapParameter> parameters_; std::vector<SoapParameter> parameters_;
}; };

@ -12,7 +12,7 @@
namespace webcc { namespace webcc {
class SoapResponse : public SoapMessage { class SoapResponse : public SoapMessage {
public: public:
// Response result parser. // Response result parser.
// Called on each child of the response node. // Called on each child of the response node.
// Example: // Example:
@ -50,11 +50,11 @@ class SoapResponse : public SoapMessage {
// - composer(xxxResponse); // - composer(xxxResponse);
// The composer then add proper children to xxxResponse as the result. // The composer then add proper children to xxxResponse as the result.
class Composer { class Composer {
public: public:
void operator()(pugi::xml_node xresponse) { void operator()(pugi::xml_node xresponse) {
Compose(xresponse); Compose(xresponse);
} }
private: private:
virtual void Compose(pugi::xml_node xresponse) = 0; virtual void Compose(pugi::xml_node xresponse) = 0;
}; };
@ -127,12 +127,12 @@ class SoapResponse : public SoapMessage {
// TODO: Set fault from server. // TODO: Set fault from server.
protected: public:
void ToXmlBody(pugi::xml_node xbody) override; void ToXmlBody(pugi::xml_node xbody) override;
bool FromXmlBody(pugi::xml_node xbody) override; bool FromXmlBody(pugi::xml_node xbody) override;
private: private:
// Fault element if any. // Fault element if any.
std::shared_ptr<SoapFault> fault_; std::shared_ptr<SoapFault> fault_;

@ -11,7 +11,7 @@
namespace webcc { namespace webcc {
class SoapServer : public HttpServer { class SoapServer : public HttpServer {
public: public:
SoapServer(std::uint16_t port, std::size_t workers, SoapServer(std::uint16_t port, std::size_t workers,
SoapVersion soap_version = kSoapV12) SoapVersion soap_version = kSoapV12)
: HttpServer(port, workers), : HttpServer(port, workers),
@ -36,7 +36,7 @@ class SoapServer : public HttpServer {
return request_handler_.Bind(service, url); return request_handler_.Bind(service, url);
} }
private: private:
HttpRequestHandler* GetRequestHandler() override { HttpRequestHandler* GetRequestHandler() override {
return &request_handler_; return &request_handler_;
} }

@ -12,14 +12,14 @@ class SoapResponse;
// Base class for your SOAP service. // Base class for your SOAP service.
class SoapService { class SoapService {
public: public:
virtual ~SoapService() = default; virtual ~SoapService() = default;
// Handle SOAP request, output the response. // Handle SOAP request, output the response.
virtual bool Handle(const SoapRequest& soap_request, virtual bool Handle(const SoapRequest& soap_request,
SoapResponse* soap_response) = 0; SoapResponse* soap_response) = 0;
protected: public:
http::Status http_status_ = http::Status::kOK; http::Status http_status_ = http::Status::kOK;
}; };

@ -73,7 +73,7 @@ std::string GetNSAttr(const pugi::xml_node& xnode,
// XmlStringWriter writer(&xml_string); // XmlStringWriter writer(&xml_string);
// xdoc.save/print(writer); // xdoc.save/print(writer);
class XmlStringWriter : public pugi::xml_writer { class XmlStringWriter : public pugi::xml_writer {
public: public:
explicit XmlStringWriter(std::string* result) : result_(result) { explicit XmlStringWriter(std::string* result) : result_(result) {
result_->clear(); result_->clear();
} }
@ -82,7 +82,7 @@ class XmlStringWriter : public pugi::xml_writer {
result_->append(static_cast<const char*>(data), size); result_->append(static_cast<const char*>(data), size);
} }
private: private:
std::string* result_; std::string* result_;
}; };

@ -7,6 +7,8 @@
#include <utility> #include <utility>
#include <vector> #include <vector>
#include "webcc/globals.h"
namespace webcc { namespace webcc {
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
@ -57,9 +59,36 @@ class Url {
public: public:
Url() = default; Url() = default;
// TODO: decode/encode/encoded?
explicit Url(const std::string& str, bool decode = true); explicit Url(const std::string& str, bool decode = true);
#if WEBCC_DEFAULT_MOVE_COPY_ASSIGN
Url(Url&&) = default;
Url& operator=(Url&&) = default;
#else
Url(Url&& rhs)
: scheme_(std::move(rhs.scheme_)),
host_(std::move(rhs.host_)),
port_(std::move(rhs.port_)),
path_(std::move(rhs.path_)),
query_(std::move(rhs.query_)) {
}
Url& operator=(Url&& rhs) {
if (&rhs != this) {
scheme_ = std::move(rhs.scheme_);
host_ = std::move(rhs.host_);
port_ = std::move(rhs.port_);
path_ = std::move(rhs.path_);
query_ = std::move(rhs.query_);
}
return *this;
}
#endif // WEBCC_DEFAULT_MOVE_COPY_ASSIGN
void Init(const std::string& str, bool decode = true, bool clear = true); void Init(const std::string& str, bool decode = true, bool clear = true);
const std::string& scheme() const { const std::string& scheme() const {

Loading…
Cancel
Save