Reorganize examples; fix soap issues.
parent
05782b773a
commit
616f5a3f5e
@ -1,33 +0,0 @@
|
|||||||
# 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,2 +0,0 @@
|
|||||||
add_executable(github_client main.cc)
|
|
||||||
target_link_libraries(github_client ${EXAMPLE_COMMON_LIBS} jsoncpp)
|
|
@ -1,2 +0,0 @@
|
|||||||
add_executable(http_client main.cc)
|
|
||||||
target_link_libraries(http_client ${EXAMPLE_COMMON_LIBS})
|
|
@ -1,20 +0,0 @@
|
|||||||
set(TARGET_NAME rest_book_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} ${EXAMPLE_COMMON_LIBS} jsoncpp)
|
|
||||||
|
|
||||||
# Install VLD DLLs to build dir so that the example can be launched from
|
|
||||||
# inside VS.
|
|
||||||
if(WEBCC_ENABLE_VLD)
|
|
||||||
install(DIRECTORY ${THIRD_PARTY_DIR}/win32/bin/debug/vld/
|
|
||||||
CONFIGURATIONS Debug
|
|
||||||
DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/Debug)
|
|
||||||
endif()
|
|
@ -1,290 +0,0 @@
|
|||||||
#include <iostream>
|
|
||||||
#include <list>
|
|
||||||
|
|
||||||
#include "json/json.h"
|
|
||||||
|
|
||||||
#include "webcc/logger.h"
|
|
||||||
#include "webcc/rest_client.h"
|
|
||||||
|
|
||||||
#include "example/common/book.h"
|
|
||||||
#include "example/common/book_json.h"
|
|
||||||
|
|
||||||
#if (defined(WIN32) || defined(_WIN64))
|
|
||||||
#if defined(_DEBUG) && defined(WEBCC_ENABLE_VLD)
|
|
||||||
#pragma message ("< include vld.h >")
|
|
||||||
#include "vld/vld.h"
|
|
||||||
#pragma comment(lib, "vld")
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
class BookClientBase {
|
|
||||||
public:
|
|
||||||
BookClientBase(const std::string& host, const std::string& port,
|
|
||||||
int timeout_seconds)
|
|
||||||
: host_(host), port_(port) {
|
|
||||||
http_client_.SetTimeout(timeout_seconds);
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual ~BookClientBase() = default;
|
|
||||||
|
|
||||||
public:
|
|
||||||
// Helper function to make a request.
|
|
||||||
webcc::HttpRequestPtr MakeRequest(const std::string& method,
|
|
||||||
const std::string& url,
|
|
||||||
std::string&& content = "") {
|
|
||||||
auto request = webcc::HttpRequest::New(method, url, host_, port_);
|
|
||||||
request->AcceptAppJson();
|
|
||||||
if (!content.empty()) {
|
|
||||||
request->SetContentInAppJsonUtf8(JsonToString(content), true);
|
|
||||||
}
|
|
||||||
request->Prepare();
|
|
||||||
return request;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Log the socket communication error.
|
|
||||||
void LogError() {
|
|
||||||
if (http_client_.timed_out()) {
|
|
||||||
LOG_ERRO("%s (timed out)", webcc::DescribeError(http_client_.error()));
|
|
||||||
} else {
|
|
||||||
LOG_ERRO(webcc::DescribeError(http_client_.error()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check HTTP response status.
|
|
||||||
bool CheckStatus(webcc::http::Status expected_status) {
|
|
||||||
int status = http_client_.response_status();
|
|
||||||
if (status != expected_status) {
|
|
||||||
LOG_ERRO("HTTP status error (actual: %d, expected: %d).",
|
|
||||||
status, expected_status);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string host_;
|
|
||||||
std::string port_;
|
|
||||||
webcc::HttpClient http_client_;
|
|
||||||
};
|
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
class BookListClient : public BookClientBase {
|
|
||||||
public:
|
|
||||||
BookListClient(const std::string& host, const std::string& port,
|
|
||||||
int timeout_seconds)
|
|
||||||
: BookClientBase(host, port, timeout_seconds) {
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ListBooks(std::list<Book>* books) {
|
|
||||||
auto request = MakeRequest(webcc::kHttpGet, "/books");
|
|
||||||
|
|
||||||
if (!http_client_.Request(*request)) {
|
|
||||||
// Socket communication error.
|
|
||||||
LogError();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!CheckStatus(webcc::http::Status::kOK)) {
|
|
||||||
// Response HTTP status error.
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Json::Value rsp_json = StringToJson(http_client_.response_content());
|
|
||||||
|
|
||||||
if (!rsp_json.isArray()) {
|
|
||||||
return false; // Should be a JSON array of books.
|
|
||||||
}
|
|
||||||
|
|
||||||
for (Json::ArrayIndex i = 0; i < rsp_json.size(); ++i) {
|
|
||||||
books->push_back(JsonToBook(rsp_json[i]));
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CreateBook(const std::string& title, double price, std::string* id) {
|
|
||||||
Json::Value req_json;
|
|
||||||
req_json["title"] = title;
|
|
||||||
req_json["price"] = price;
|
|
||||||
|
|
||||||
auto request = MakeRequest(webcc::kHttpPost, "/books",
|
|
||||||
JsonToString(req_json));
|
|
||||||
|
|
||||||
if (!http_client_.Request(*request)) {
|
|
||||||
LogError();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!CheckStatus(webcc::http::Status::kCreated)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Json::Value rsp_json = StringToJson(http_client_.response_content());
|
|
||||||
*id = rsp_json["id"].asString();
|
|
||||||
|
|
||||||
return !id->empty();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
class BookDetailClient : public BookClientBase {
|
|
||||||
public:
|
|
||||||
BookDetailClient(const std::string& host, const std::string& port,
|
|
||||||
int timeout_seconds)
|
|
||||||
: BookClientBase(host, port, timeout_seconds) {
|
|
||||||
}
|
|
||||||
|
|
||||||
bool GetBook(const std::string& id, Book* book) {
|
|
||||||
auto request = MakeRequest(webcc::kHttpGet, "/books/" + id);
|
|
||||||
|
|
||||||
if (!http_client_.Request(*request)) {
|
|
||||||
LogError();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!CheckStatus(webcc::http::Status::kOK)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return JsonStringToBook(http_client_.response_content(), book);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool UpdateBook(const std::string& id, const std::string& title,
|
|
||||||
double price) {
|
|
||||||
Json::Value json;
|
|
||||||
json["title"] = title;
|
|
||||||
json["price"] = price;
|
|
||||||
|
|
||||||
auto request = MakeRequest(webcc::kHttpPut, "/books/" + id,
|
|
||||||
JsonToString(json));
|
|
||||||
|
|
||||||
if (!http_client_.Request(*request)) {
|
|
||||||
LogError();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!CheckStatus(webcc::http::Status::kOK)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool DeleteBook(const std::string& id) {
|
|
||||||
auto request = MakeRequest(webcc::kHttpDelete, "/books/" + id);
|
|
||||||
|
|
||||||
if (!http_client_.Request(*request)) {
|
|
||||||
LogError();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!CheckStatus(webcc::http::Status::kOK)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
void PrintSeparator() {
|
|
||||||
std::cout << std::string(80, '-') << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
void PrintBook(const Book& book) {
|
|
||||||
std::cout << "Book: " << book << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
void PrintBookList(const std::list<Book>& books) {
|
|
||||||
std::cout << "Book list: " << books.size() << std::endl;
|
|
||||||
for (const Book& book : books) {
|
|
||||||
std::cout << " Book: " << book << std::endl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
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_FILE_OVERWRITE);
|
|
||||||
|
|
||||||
std::string host = argv[1];
|
|
||||||
std::string port = argv[2];
|
|
||||||
|
|
||||||
int timeout_seconds = -1;
|
|
||||||
if (argc > 3) {
|
|
||||||
timeout_seconds = std::atoi(argv[3]);
|
|
||||||
}
|
|
||||||
|
|
||||||
BookListClient list_client(host, port, timeout_seconds);
|
|
||||||
BookDetailClient detail_client(host, port, timeout_seconds);
|
|
||||||
|
|
||||||
PrintSeparator();
|
|
||||||
|
|
||||||
std::list<Book> books;
|
|
||||||
if (list_client.ListBooks(&books)) {
|
|
||||||
PrintBookList(books);
|
|
||||||
}
|
|
||||||
|
|
||||||
PrintSeparator();
|
|
||||||
|
|
||||||
std::string id;
|
|
||||||
if (list_client.CreateBook("1984", 12.3, &id)) {
|
|
||||||
std::cout << "Book ID: " << id << std::endl;
|
|
||||||
} else {
|
|
||||||
id = "1";
|
|
||||||
std::cout << "Book ID: " << id << " (faked)"<< std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
PrintSeparator();
|
|
||||||
|
|
||||||
books.clear();
|
|
||||||
if (list_client.ListBooks(&books)) {
|
|
||||||
PrintBookList(books);
|
|
||||||
}
|
|
||||||
|
|
||||||
PrintSeparator();
|
|
||||||
|
|
||||||
Book book;
|
|
||||||
if (detail_client.GetBook(id, &book)) {
|
|
||||||
PrintBook(book);
|
|
||||||
}
|
|
||||||
|
|
||||||
//PrintSeparator();
|
|
||||||
|
|
||||||
//detail_client.UpdateBook(id, "1Q84", 32.1);
|
|
||||||
|
|
||||||
//PrintSeparator();
|
|
||||||
|
|
||||||
//if (detail_client.GetBook(id, &book)) {
|
|
||||||
// PrintBook(book);
|
|
||||||
//}
|
|
||||||
|
|
||||||
//PrintSeparator();
|
|
||||||
|
|
||||||
//detail_client.DeleteBook(id);
|
|
||||||
|
|
||||||
//PrintSeparator();
|
|
||||||
|
|
||||||
//books.clear();
|
|
||||||
//if (list_client.ListBooks(&books)) {
|
|
||||||
// PrintBookList(books);
|
|
||||||
//}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
@ -1,23 +0,0 @@
|
|||||||
set(TARGET_NAME rest_book_server)
|
|
||||||
|
|
||||||
set(SRCS
|
|
||||||
../common/book.cc
|
|
||||||
../common/book.h
|
|
||||||
../common/book_json.cc
|
|
||||||
../common/book_json.h
|
|
||||||
services.cc
|
|
||||||
services.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}")
|
|
||||||
|
|
||||||
# Install VLD DLLs to build dir so that the example can be launched from
|
|
||||||
# inside VS.
|
|
||||||
if(WEBCC_ENABLE_VLD)
|
|
||||||
install(DIRECTORY ${THIRD_PARTY_DIR}/win32/bin/debug/vld/
|
|
||||||
CONFIGURATIONS Debug
|
|
||||||
DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/Debug)
|
|
||||||
endif()
|
|
@ -1,60 +0,0 @@
|
|||||||
#include <iostream>
|
|
||||||
|
|
||||||
#include "webcc/logger.h"
|
|
||||||
#include "webcc/rest_server.h"
|
|
||||||
|
|
||||||
#include "example/rest_book_server/services.h"
|
|
||||||
|
|
||||||
#if (defined(WIN32) || defined(_WIN64))
|
|
||||||
#if defined(_DEBUG) && defined(WEBCC_ENABLE_VLD)
|
|
||||||
#pragma message ("< include vld.h >")
|
|
||||||
#include "vld/vld.h"
|
|
||||||
#pragma comment(lib, "vld")
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void Help(const char* argv0) {
|
|
||||||
std::cout << "Usage: " << argv0 << " <port> [seconds]" << std::endl;
|
|
||||||
std::cout << "If |seconds| is provided, the server will sleep these seconds "
|
|
||||||
"before sending back each response."
|
|
||||||
<< std::endl;
|
|
||||||
std::cout << " E.g.," << std::endl;
|
|
||||||
std::cout << " " << argv0 << " 8080" << std::endl;
|
|
||||||
std::cout << " " << argv0 << " 8080 3" << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char* argv[]) {
|
|
||||||
if (argc < 2) {
|
|
||||||
Help(argv[0]);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
WEBCC_LOG_INIT("", webcc::LOG_CONSOLE);
|
|
||||||
|
|
||||||
std::uint16_t port = static_cast<std::uint16_t>(std::atoi(argv[1]));
|
|
||||||
|
|
||||||
int sleep_seconds = 0;
|
|
||||||
if (argc >= 3) {
|
|
||||||
sleep_seconds = std::atoi(argv[2]);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::size_t workers = 2;
|
|
||||||
|
|
||||||
try {
|
|
||||||
webcc::RestServer server(port, workers);
|
|
||||||
|
|
||||||
server.Bind(std::make_shared<BookListService>(sleep_seconds),
|
|
||||||
"/books", false);
|
|
||||||
|
|
||||||
server.Bind(std::make_shared<BookDetailService>(sleep_seconds),
|
|
||||||
"/books/(\\d+)", true);
|
|
||||||
|
|
||||||
server.Run();
|
|
||||||
|
|
||||||
} catch (const std::exception& e) {
|
|
||||||
std::cerr << "Exception: " << e.what() << std::endl;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
@ -1,127 +0,0 @@
|
|||||||
#include "example/rest_book_server/services.h"
|
|
||||||
|
|
||||||
#include <iostream>
|
|
||||||
#include <list>
|
|
||||||
#include <thread>
|
|
||||||
|
|
||||||
#include "json/json.h"
|
|
||||||
#include "webcc/logger.h"
|
|
||||||
|
|
||||||
#include "example/common/book.h"
|
|
||||||
#include "example/common/book_json.h"
|
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
static BookStore g_book_store;
|
|
||||||
|
|
||||||
static void Sleep(int seconds) {
|
|
||||||
if (seconds > 0) {
|
|
||||||
LOG_INFO("Sleep %d seconds...", seconds);
|
|
||||||
std::this_thread::sleep_for(std::chrono::seconds(seconds));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
// Return all books as a JSON array.
|
|
||||||
void BookListService::Get(const webcc::UrlQuery& /*query*/,
|
|
||||||
webcc::RestResponse* response) {
|
|
||||||
Sleep(sleep_seconds_);
|
|
||||||
|
|
||||||
Json::Value json(Json::arrayValue);
|
|
||||||
|
|
||||||
for (const Book& book : g_book_store.books()) {
|
|
||||||
json.append(BookToJson(book));
|
|
||||||
}
|
|
||||||
|
|
||||||
response->content = JsonToString(json);
|
|
||||||
response->status = webcc::http::Status::kOK;
|
|
||||||
}
|
|
||||||
|
|
||||||
void BookListService::Post(const std::string& request_content,
|
|
||||||
webcc::RestResponse* response) {
|
|
||||||
Sleep(sleep_seconds_);
|
|
||||||
|
|
||||||
Book book;
|
|
||||||
if (JsonStringToBook(request_content, &book)) {
|
|
||||||
std::string id = g_book_store.AddBook(book);
|
|
||||||
|
|
||||||
Json::Value json;
|
|
||||||
json["id"] = id;
|
|
||||||
|
|
||||||
response->content = JsonToString(json);
|
|
||||||
response->status = webcc::http::Status::kCreated;
|
|
||||||
} else {
|
|
||||||
// Invalid JSON
|
|
||||||
response->status = webcc::http::Status::kBadRequest;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
void BookDetailService::Get(const webcc::UrlSubMatches& url_sub_matches,
|
|
||||||
const webcc::UrlQuery& query,
|
|
||||||
webcc::RestResponse* response) {
|
|
||||||
Sleep(sleep_seconds_);
|
|
||||||
|
|
||||||
if (url_sub_matches.size() != 1) {
|
|
||||||
// Using kNotFound means the resource specified by the URL cannot be found.
|
|
||||||
// kBadRequest could be another choice.
|
|
||||||
response->status = webcc::http::Status::kNotFound;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::string& book_id = url_sub_matches[0];
|
|
||||||
|
|
||||||
const Book& book = g_book_store.GetBook(book_id);
|
|
||||||
if (book.IsNull()) {
|
|
||||||
response->status = webcc::http::Status::kNotFound;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
response->content = BookToJsonString(book);
|
|
||||||
response->status = webcc::http::Status::kOK;
|
|
||||||
}
|
|
||||||
|
|
||||||
void BookDetailService::Put(const webcc::UrlSubMatches& url_sub_matches,
|
|
||||||
const std::string& request_content,
|
|
||||||
webcc::RestResponse* response) {
|
|
||||||
Sleep(sleep_seconds_);
|
|
||||||
|
|
||||||
if (url_sub_matches.size() != 1) {
|
|
||||||
response->status = webcc::http::Status::kNotFound;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::string& book_id = url_sub_matches[0];
|
|
||||||
|
|
||||||
Book book;
|
|
||||||
if (!JsonStringToBook(request_content, &book)) {
|
|
||||||
response->status = webcc::http::Status::kBadRequest;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
book.id = book_id;
|
|
||||||
g_book_store.UpdateBook(book);
|
|
||||||
|
|
||||||
response->status = webcc::http::Status::kOK;
|
|
||||||
}
|
|
||||||
|
|
||||||
void BookDetailService::Delete(const webcc::UrlSubMatches& url_sub_matches,
|
|
||||||
webcc::RestResponse* response) {
|
|
||||||
Sleep(sleep_seconds_);
|
|
||||||
|
|
||||||
if (url_sub_matches.size() != 1) {
|
|
||||||
response->status = webcc::http::Status::kNotFound;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::string& book_id = url_sub_matches[0];
|
|
||||||
|
|
||||||
if (!g_book_store.DeleteBook(book_id)) {
|
|
||||||
response->status = webcc::http::Status::kNotFound;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
response->status = webcc::http::Status::kOK;
|
|
||||||
}
|
|
@ -1,62 +0,0 @@
|
|||||||
#ifndef EXAMPLE_REST_BOOK_SERVER_SERVICES_H_
|
|
||||||
#define EXAMPLE_REST_BOOK_SERVER_SERVICES_H_
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "webcc/rest_service.h"
|
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
class BookListService : public webcc::RestListService {
|
|
||||||
public:
|
|
||||||
explicit BookListService(int sleep_seconds)
|
|
||||||
: sleep_seconds_(sleep_seconds) {
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
// Get a list of books based on query parameters.
|
|
||||||
void Get(const webcc::UrlQuery& query, webcc::RestResponse* response) final;
|
|
||||||
|
|
||||||
// Create a new book.
|
|
||||||
void Post(const std::string& request_content,
|
|
||||||
webcc::RestResponse* response) final;
|
|
||||||
|
|
||||||
private:
|
|
||||||
// Sleep some seconds before send back the response.
|
|
||||||
// For testing timeout control in client side.
|
|
||||||
int sleep_seconds_;
|
|
||||||
};
|
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
// The URL is like '/books/{BookID}', and the 'url_sub_matches' parameter
|
|
||||||
// contains the matched book ID.
|
|
||||||
class BookDetailService : public webcc::RestDetailService {
|
|
||||||
public:
|
|
||||||
explicit BookDetailService(int sleep_seconds)
|
|
||||||
: sleep_seconds_(sleep_seconds) {
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
// Get the detailed information of a book.
|
|
||||||
void Get(const webcc::UrlSubMatches& url_sub_matches,
|
|
||||||
const webcc::UrlQuery& query,
|
|
||||||
webcc::RestResponse* response) final;
|
|
||||||
|
|
||||||
// Update a book.
|
|
||||||
void Put(const webcc::UrlSubMatches& url_sub_matches,
|
|
||||||
const std::string& request_content,
|
|
||||||
webcc::RestResponse* response) final;
|
|
||||||
|
|
||||||
// Delete a book.
|
|
||||||
void Delete(const webcc::UrlSubMatches& url_sub_matches,
|
|
||||||
webcc::RestResponse* response) final;
|
|
||||||
|
|
||||||
private:
|
|
||||||
// Sleep some seconds before send back the response.
|
|
||||||
// For testing timeout control in client side.
|
|
||||||
int sleep_seconds_;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // EXAMPLE_REST_BOOK_SERVER_SERVICES_H_
|
|
@ -1,22 +0,0 @@
|
|||||||
set(TARGET_NAME soap_book_client)
|
|
||||||
|
|
||||||
set(SRCS
|
|
||||||
../common/book.cc
|
|
||||||
../common/book.h
|
|
||||||
../common/book_xml.cc
|
|
||||||
../common/book_xml.h
|
|
||||||
book_client.cc
|
|
||||||
book_client.h
|
|
||||||
main.cc)
|
|
||||||
|
|
||||||
add_executable(${TARGET_NAME} ${SRCS})
|
|
||||||
|
|
||||||
target_link_libraries(${TARGET_NAME} ${EXAMPLE_COMMON_LIBS} pugixml)
|
|
||||||
|
|
||||||
# Install VLD DLLs to build dir so that the example can be launched from
|
|
||||||
# inside VS.
|
|
||||||
if(WEBCC_ENABLE_VLD)
|
|
||||||
install(DIRECTORY ${THIRD_PARTY_DIR}/win32/bin/debug/vld/
|
|
||||||
CONFIGURATIONS Debug
|
|
||||||
DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/Debug)
|
|
||||||
endif()
|
|
@ -1,56 +0,0 @@
|
|||||||
#ifndef EXAMPLE_SOAP_BOOK_CLIENT_H_
|
|
||||||
#define EXAMPLE_SOAP_BOOK_CLIENT_H_
|
|
||||||
|
|
||||||
#include <functional>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
#include "pugixml/pugixml.hpp"
|
|
||||||
#include "webcc/soap_client.h"
|
|
||||||
|
|
||||||
#include "example/common/book.h"
|
|
||||||
|
|
||||||
class BookClient {
|
|
||||||
public:
|
|
||||||
BookClient(const std::string& host, const std::string& port);
|
|
||||||
|
|
||||||
int code() const { return code_; }
|
|
||||||
const std::string& message() const { return message_; }
|
|
||||||
|
|
||||||
// Create a book.
|
|
||||||
bool CreateBook(const std::string& title, double price, std::string* id);
|
|
||||||
|
|
||||||
// Get a book by ID.
|
|
||||||
bool GetBook(const std::string& id, Book* book);
|
|
||||||
|
|
||||||
// List all books.
|
|
||||||
bool ListBooks(std::list<Book>* books);
|
|
||||||
|
|
||||||
// Delete a book by ID.
|
|
||||||
bool DeleteBook(const std::string& id);
|
|
||||||
|
|
||||||
private:
|
|
||||||
// Call with 0 parameter.
|
|
||||||
bool Call0(const std::string& operation, std::string* result_str);
|
|
||||||
|
|
||||||
// Call with 1 parameter.
|
|
||||||
bool Call1(const std::string& operation, webcc::SoapParameter&& parameter,
|
|
||||||
std::string* result_str);
|
|
||||||
|
|
||||||
// Simple wrapper of SoapClient::Request() to log error if any.
|
|
||||||
bool Call(const std::string& operation,
|
|
||||||
std::vector<webcc::SoapParameter>&& parameters,
|
|
||||||
std::string* result_str);
|
|
||||||
|
|
||||||
void PrintError();
|
|
||||||
|
|
||||||
bool ParseResultXml(const std::string& result_xml,
|
|
||||||
std::function<bool(pugi::xml_node)> callback);
|
|
||||||
|
|
||||||
webcc::SoapClient soap_client_;
|
|
||||||
|
|
||||||
// Last status.
|
|
||||||
int code_;
|
|
||||||
std::string message_;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // EXAMPLE_SOAP_BOOK_CLIENT_H_
|
|
@ -1,73 +0,0 @@
|
|||||||
#include <iostream>
|
|
||||||
|
|
||||||
#include "webcc/logger.h"
|
|
||||||
|
|
||||||
#include "example/soap_book_client/book_client.h"
|
|
||||||
|
|
||||||
#if (defined(WIN32) || defined(_WIN64))
|
|
||||||
#if defined(_DEBUG) && defined(WEBCC_ENABLE_VLD)
|
|
||||||
#pragma message ("< include vld.h >")
|
|
||||||
#include "vld/vld.h"
|
|
||||||
#pragma comment(lib, "vld")
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void Help(const char* argv0) {
|
|
||||||
std::cout << "Usage: " << argv0 << " <host> <port>" << std::endl;
|
|
||||||
std::cout << " E.g.," << std::endl;
|
|
||||||
std::cout << " " << argv0 << " localhost 8080" << 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];
|
|
||||||
|
|
||||||
BookClient client(host, port);
|
|
||||||
|
|
||||||
std::string id1;
|
|
||||||
if (!client.CreateBook("1984", 12.3, &id1)) {
|
|
||||||
std::cerr << "Failed to create book." << std::endl;
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::cout << "Book ID: " << id1 << std::endl;
|
|
||||||
|
|
||||||
std::string id2;
|
|
||||||
if (!client.CreateBook("1Q84", 32.1, &id2)) {
|
|
||||||
std::cerr << "Failed to create book." << std::endl;
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::cout << "Book ID: " << id2 << std::endl;
|
|
||||||
|
|
||||||
Book book;
|
|
||||||
if (!client.GetBook(id1, &book)) {
|
|
||||||
std::cerr << "Failed to get book." << std::endl;
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::cout << "Book: " << book << std::endl;
|
|
||||||
|
|
||||||
std::list<Book> books;
|
|
||||||
if (!client.ListBooks(&books)) {
|
|
||||||
std::cerr << "Failed to list books." << std::endl;
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const Book& book : books) {
|
|
||||||
std::cout << "Book: " << book << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (client.DeleteBook(id1)) {
|
|
||||||
std::cout << "Book deleted: " << id1 << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
@ -1,23 +0,0 @@
|
|||||||
set(TARGET_NAME soap_book_server)
|
|
||||||
|
|
||||||
set(SRCS
|
|
||||||
../common/book.cc
|
|
||||||
../common/book.h
|
|
||||||
../common/book_xml.cc
|
|
||||||
../common/book_xml.h
|
|
||||||
book_service.cc
|
|
||||||
book_service.h
|
|
||||||
main.cc)
|
|
||||||
|
|
||||||
add_executable(${TARGET_NAME} ${SRCS})
|
|
||||||
|
|
||||||
target_link_libraries(${TARGET_NAME} webcc pugixml ${Boost_LIBRARIES})
|
|
||||||
target_link_libraries(${TARGET_NAME} "${CMAKE_THREAD_LIBS_INIT}")
|
|
||||||
|
|
||||||
# Install VLD DLLs to build dir so that the example can be launched from
|
|
||||||
# inside VS.
|
|
||||||
if(WEBCC_ENABLE_VLD)
|
|
||||||
install(DIRECTORY ${THIRD_PARTY_DIR}/win32/bin/debug/vld/
|
|
||||||
CONFIGURATIONS Debug
|
|
||||||
DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/Debug)
|
|
||||||
endif()
|
|
@ -1,25 +0,0 @@
|
|||||||
#ifndef EXAMPLE_SOAP_BOOK_SERVER_BOOK_SERVICE_H_
|
|
||||||
#define EXAMPLE_SOAP_BOOK_SERVER_BOOK_SERVICE_H_
|
|
||||||
|
|
||||||
#include "webcc/soap_service.h"
|
|
||||||
|
|
||||||
class BookService : public webcc::SoapService {
|
|
||||||
public:
|
|
||||||
bool Handle(const webcc::SoapRequest& soap_request,
|
|
||||||
webcc::SoapResponse* soap_response) override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
bool CreateBook(const webcc::SoapRequest& soap_request,
|
|
||||||
webcc::SoapResponse* soap_response);
|
|
||||||
|
|
||||||
bool GetBook(const webcc::SoapRequest& soap_request,
|
|
||||||
webcc::SoapResponse* soap_response);
|
|
||||||
|
|
||||||
bool ListBooks(const webcc::SoapRequest& soap_request,
|
|
||||||
webcc::SoapResponse* soap_response);
|
|
||||||
|
|
||||||
bool DeleteBook(const webcc::SoapRequest& soap_request,
|
|
||||||
webcc::SoapResponse* soap_response);
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // EXAMPLE_SOAP_BOOK_SERVER_BOOK_SERVICE_H_
|
|
@ -1,48 +0,0 @@
|
|||||||
#include <iostream>
|
|
||||||
|
|
||||||
#include "webcc/logger.h"
|
|
||||||
#include "webcc/soap_server.h"
|
|
||||||
|
|
||||||
#include "example/soap_book_server/book_service.h"
|
|
||||||
|
|
||||||
#if (defined(WIN32) || defined(_WIN64))
|
|
||||||
#if defined(_DEBUG) && defined(WEBCC_ENABLE_VLD)
|
|
||||||
#pragma message ("< include vld.h >")
|
|
||||||
#include "vld/vld.h"
|
|
||||||
#pragma comment(lib, "vld")
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void Help(const char* argv0) {
|
|
||||||
std::cout << "Usage: " << argv0 << " <port>" << std::endl;
|
|
||||||
std::cout << " E.g.," << std::endl;
|
|
||||||
std::cout << " " << argv0 << " 8080" << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char* argv[]) {
|
|
||||||
if (argc < 2) {
|
|
||||||
Help(argv[0]);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
WEBCC_LOG_INIT("", webcc::LOG_CONSOLE);
|
|
||||||
|
|
||||||
std::uint16_t port = static_cast<std::uint16_t>(std::atoi(argv[1]));
|
|
||||||
std::size_t workers = 2;
|
|
||||||
|
|
||||||
try {
|
|
||||||
webcc::SoapServer server(port, workers);
|
|
||||||
|
|
||||||
// Customize response XML format.
|
|
||||||
server.set_format_raw(false);
|
|
||||||
server.set_indent_str(" ");
|
|
||||||
|
|
||||||
server.Bind(std::make_shared<BookService>(), "/book");
|
|
||||||
server.Run();
|
|
||||||
} catch (const std::exception& e) {
|
|
||||||
std::cerr << "Exception: " << e.what() << std::endl;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
@ -1,6 +0,0 @@
|
|||||||
set(SRCS calc_service.cc calc_service.h main.cc)
|
|
||||||
|
|
||||||
add_executable(soap_calc_server ${SRCS})
|
|
||||||
|
|
||||||
target_link_libraries(soap_calc_server webcc pugixml ${Boost_LIBRARIES})
|
|
||||||
target_link_libraries(soap_calc_server "${CMAKE_THREAD_LIBS_INIT}")
|
|
@ -1,15 +0,0 @@
|
|||||||
#ifndef CALC_SERVICE_H_
|
|
||||||
#define CALC_SERVICE_H_
|
|
||||||
|
|
||||||
#include "webcc/soap_service.h"
|
|
||||||
|
|
||||||
class CalcService : public webcc::SoapService {
|
|
||||||
public:
|
|
||||||
CalcService() = default;
|
|
||||||
~CalcService() override = default;
|
|
||||||
|
|
||||||
bool Handle(const webcc::SoapRequest& soap_request,
|
|
||||||
webcc::SoapResponse* soap_response) final;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // CALC_SERVICE_H_
|
|
@ -1,40 +0,0 @@
|
|||||||
#include <iostream>
|
|
||||||
|
|
||||||
#include "webcc/logger.h"
|
|
||||||
#include "webcc/soap_server.h"
|
|
||||||
|
|
||||||
#include "example/soap_calc_server/calc_service.h"
|
|
||||||
|
|
||||||
void Help(const char* argv0) {
|
|
||||||
std::cout << "Usage: " << argv0 << " <port>" << std::endl;
|
|
||||||
std::cout << " E.g.," << std::endl;
|
|
||||||
std::cout << " " << argv0 << " 8080" << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char* argv[]) {
|
|
||||||
if (argc != 2) {
|
|
||||||
Help(argv[0]);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
WEBCC_LOG_INIT("", webcc::LOG_CONSOLE);
|
|
||||||
|
|
||||||
std::uint16_t port = static_cast<std::uint16_t>(std::atoi(argv[1]));
|
|
||||||
std::size_t workers = 2;
|
|
||||||
|
|
||||||
try {
|
|
||||||
webcc::SoapServer server(port, workers);
|
|
||||||
|
|
||||||
// Customize response XML format.
|
|
||||||
server.set_format_raw(false);
|
|
||||||
server.set_indent_str(" ");
|
|
||||||
|
|
||||||
server.Bind(std::make_shared<CalcService>(), "/calculator");
|
|
||||||
server.Run();
|
|
||||||
} catch (const std::exception& e) {
|
|
||||||
std::cerr << "Exception: " << e.what() << std::endl;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
@ -0,0 +1,55 @@
|
|||||||
|
# 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()
|
||||||
|
|
||||||
|
set(REST_BOOK_SRCS
|
||||||
|
common/book.cc
|
||||||
|
common/book.h
|
||||||
|
common/book_json.cc
|
||||||
|
common/book_json.h
|
||||||
|
)
|
||||||
|
|
||||||
|
add_executable(http_client http_client.cc)
|
||||||
|
add_executable(github_client github_client.cc)
|
||||||
|
|
||||||
|
target_link_libraries(http_client ${EXAMPLE_COMMON_LIBS})
|
||||||
|
target_link_libraries(github_client ${EXAMPLE_COMMON_LIBS} jsoncpp)
|
||||||
|
|
||||||
|
add_executable(rest_book_server rest_book_server.cc ${REST_BOOK_SRCS})
|
||||||
|
add_executable(rest_book_client rest_book_client.cc ${REST_BOOK_SRCS})
|
||||||
|
|
||||||
|
target_link_libraries(rest_book_server ${EXAMPLE_COMMON_LIBS} jsoncpp)
|
||||||
|
target_link_libraries(rest_book_client ${EXAMPLE_COMMON_LIBS} jsoncpp)
|
||||||
|
|
||||||
|
if(WEBCC_ENABLE_SOAP)
|
||||||
|
add_executable(soap_calc_server soap_calc_server.cc)
|
||||||
|
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_server ${EXAMPLE_COMMON_LIBS} pugixml)
|
||||||
|
target_link_libraries(soap_calc_client ${EXAMPLE_COMMON_LIBS} pugixml)
|
||||||
|
target_link_libraries(soap_calc_client_parasoft ${EXAMPLE_COMMON_LIBS} pugixml)
|
||||||
|
|
||||||
|
set(SOAP_BOOK_SRCS
|
||||||
|
common/book.cc
|
||||||
|
common/book.h
|
||||||
|
common/book_xml.cc
|
||||||
|
common/book_xml.h
|
||||||
|
)
|
||||||
|
|
||||||
|
add_executable(soap_book_server soap_book_server.cc ${SOAP_BOOK_SRCS})
|
||||||
|
add_executable(soap_book_client soap_book_client.cc ${SOAP_BOOK_SRCS})
|
||||||
|
|
||||||
|
target_link_libraries(soap_book_server ${EXAMPLE_COMMON_LIBS} pugixml)
|
||||||
|
target_link_libraries(soap_book_client ${EXAMPLE_COMMON_LIBS} pugixml)
|
||||||
|
endif()
|
@ -1,4 +1,4 @@
|
|||||||
#include "example/common/book.h"
|
#include "examples/common/book.h"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <iostream>
|
#include <iostream>
|
@ -1,11 +1,11 @@
|
|||||||
#include "example/common/book_json.h"
|
#include "examples/common/book_json.h"
|
||||||
|
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
#include "json/json.h"
|
#include "json/json.h"
|
||||||
|
|
||||||
#include "example/common/book.h"
|
#include "examples/common/book.h"
|
||||||
|
|
||||||
std::string JsonToString(const Json::Value& json) {
|
std::string JsonToString(const Json::Value& json) {
|
||||||
Json::StreamWriterBuilder builder;
|
Json::StreamWriterBuilder builder;
|
@ -1,10 +1,10 @@
|
|||||||
#include "example/common/book_xml.h"
|
#include "examples/common/book_xml.h"
|
||||||
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
|
||||||
#include "example/common/book.h"
|
#include "examples/common/book.h"
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
|
@ -0,0 +1,282 @@
|
|||||||
|
#include <iostream>
|
||||||
|
#include <list>
|
||||||
|
|
||||||
|
#include "json/json.h"
|
||||||
|
|
||||||
|
#include "webcc/http_client_session.h"
|
||||||
|
#include "webcc/logger.h"
|
||||||
|
|
||||||
|
#include "examples/common/book.h"
|
||||||
|
#include "examples/common/book_json.h"
|
||||||
|
|
||||||
|
#if (defined(WIN32) || defined(_WIN64))
|
||||||
|
#if defined(_DEBUG) && defined(WEBCC_ENABLE_VLD)
|
||||||
|
#pragma message ("< include vld.h >")
|
||||||
|
#include "vld/vld.h"
|
||||||
|
#pragma comment(lib, "vld")
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
class BookClientBase {
|
||||||
|
public:
|
||||||
|
BookClientBase(const std::string& url, int timeout_seconds) : url_(url) {
|
||||||
|
//session_.SetTimeout(timeout_seconds);
|
||||||
|
|
||||||
|
//session_.set_content_type("application/json");
|
||||||
|
session_.set_charset("utf-8");
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ~BookClientBase() = default;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// Helper function to make a request.
|
||||||
|
//webcc::HttpRequestPtr MakeRequest(const std::string& method,
|
||||||
|
// const std::string& url,
|
||||||
|
// std::string&& content = "") {
|
||||||
|
// auto request = webcc::HttpRequest::New(method, url, host_, port_);
|
||||||
|
// request->AcceptAppJson();
|
||||||
|
// if (!content.empty()) {
|
||||||
|
// request->SetContentInAppJsonUtf8(JsonToString(content), true);
|
||||||
|
// }
|
||||||
|
// request->Prepare();
|
||||||
|
// return request;
|
||||||
|
//}
|
||||||
|
|
||||||
|
// Check HTTP response status.
|
||||||
|
bool CheckStatus(webcc::HttpResponsePtr response, int expected_status) {
|
||||||
|
int status = response->status();
|
||||||
|
if (status != expected_status) {
|
||||||
|
LOG_ERRO("HTTP status error (actual: %d, expected: %d).",
|
||||||
|
status, expected_status);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
std::string url_;
|
||||||
|
|
||||||
|
webcc::HttpClientSession session_;
|
||||||
|
};
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
class BookListClient : public BookClientBase {
|
||||||
|
public:
|
||||||
|
BookListClient(const std::string& url, int timeout_seconds)
|
||||||
|
: BookClientBase(url, timeout_seconds) {
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ListBooks(std::list<Book>* books) {
|
||||||
|
try {
|
||||||
|
auto r = session_.Get(url_ + "/books");
|
||||||
|
|
||||||
|
if (!CheckStatus(r, webcc::http::Status::kOK)) {
|
||||||
|
// Response HTTP status error.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Json::Value rsp_json = StringToJson(r->content());
|
||||||
|
|
||||||
|
if (!rsp_json.isArray()) {
|
||||||
|
return false; // Should be a JSON array of books.
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Json::ArrayIndex i = 0; i < rsp_json.size(); ++i) {
|
||||||
|
books->push_back(JsonToBook(rsp_json[i]));
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
|
||||||
|
} catch (const webcc::Exception& e) {
|
||||||
|
std::cerr << e.what() << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CreateBook(const std::string& title, double price, std::string* id) {
|
||||||
|
Json::Value req_json;
|
||||||
|
req_json["title"] = title;
|
||||||
|
req_json["price"] = price;
|
||||||
|
|
||||||
|
try {
|
||||||
|
auto r = session_.Post(url_ + "/books", JsonToString(req_json), true);
|
||||||
|
|
||||||
|
if (!CheckStatus(r, webcc::http::Status::kCreated)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Json::Value rsp_json = StringToJson(r->content());
|
||||||
|
*id = rsp_json["id"].asString();
|
||||||
|
|
||||||
|
return !id->empty();
|
||||||
|
|
||||||
|
} catch (const webcc::Exception& e) {
|
||||||
|
std::cerr << e.what() << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
class BookDetailClient : public BookClientBase {
|
||||||
|
public:
|
||||||
|
BookDetailClient(const std::string& url, int timeout_seconds)
|
||||||
|
: BookClientBase(url, timeout_seconds) {
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GetBook(const std::string& id, Book* book) {
|
||||||
|
try {
|
||||||
|
auto r = session_.Get(url_ + "/books" + id);
|
||||||
|
|
||||||
|
if (!CheckStatus(r, webcc::http::Status::kOK)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return JsonStringToBook(r->content(), book);
|
||||||
|
|
||||||
|
} catch (const webcc::Exception& e) {
|
||||||
|
std::cerr << e.what() << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool UpdateBook(const std::string& id, const std::string& title,
|
||||||
|
double price) {
|
||||||
|
Json::Value json;
|
||||||
|
json["title"] = title;
|
||||||
|
json["price"] = price;
|
||||||
|
|
||||||
|
try {
|
||||||
|
auto r = session_.Put(url_ + "/books" + id, JsonToString(json), true);
|
||||||
|
|
||||||
|
if (!CheckStatus(r, webcc::http::Status::kOK)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
|
||||||
|
} catch (const webcc::Exception& e) {
|
||||||
|
std::cerr << e.what() << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DeleteBook(const std::string& id) {
|
||||||
|
try {
|
||||||
|
auto r = session_.Delete(url_ + "/books/" + id);
|
||||||
|
|
||||||
|
if (!CheckStatus(r, webcc::http::Status::kOK)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
|
||||||
|
} catch (const webcc::Exception& e) {
|
||||||
|
std::cerr << e.what() << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
void PrintSeparator() {
|
||||||
|
std::cout << std::string(80, '-') << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PrintBook(const Book& book) {
|
||||||
|
std::cout << "Book: " << book << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PrintBookList(const std::list<Book>& books) {
|
||||||
|
std::cout << "Book list: " << books.size() << std::endl;
|
||||||
|
for (const Book& book : books) {
|
||||||
|
std::cout << " Book: " << book << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
void Help(const char* argv0) {
|
||||||
|
std::cout << "Usage: " << argv0 << " <url> [timeout]" << std::endl;
|
||||||
|
std::cout << " E.g.," << std::endl;
|
||||||
|
std::cout << " " << argv0 << "http://localhost:8080" << std::endl;
|
||||||
|
std::cout << " " << argv0 << "http://localhost:8080 2" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char* argv[]) {
|
||||||
|
if (argc < 2) {
|
||||||
|
Help(argv[0]);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
WEBCC_LOG_INIT("", webcc::LOG_CONSOLE_FILE_OVERWRITE);
|
||||||
|
|
||||||
|
std::string url = argv[1];
|
||||||
|
|
||||||
|
int timeout_seconds = -1;
|
||||||
|
if (argc > 2) {
|
||||||
|
timeout_seconds = std::atoi(argv[2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
BookListClient list_client(url, timeout_seconds);
|
||||||
|
BookDetailClient detail_client(url, timeout_seconds);
|
||||||
|
|
||||||
|
PrintSeparator();
|
||||||
|
|
||||||
|
std::list<Book> books;
|
||||||
|
if (list_client.ListBooks(&books)) {
|
||||||
|
PrintBookList(books);
|
||||||
|
}
|
||||||
|
|
||||||
|
//PrintSeparator();
|
||||||
|
|
||||||
|
//std::string id;
|
||||||
|
//if (list_client.CreateBook("1984", 12.3, &id)) {
|
||||||
|
// std::cout << "Book ID: " << id << std::endl;
|
||||||
|
//} else {
|
||||||
|
// id = "1";
|
||||||
|
// std::cout << "Book ID: " << id << " (faked)"<< std::endl;
|
||||||
|
//}
|
||||||
|
|
||||||
|
//PrintSeparator();
|
||||||
|
|
||||||
|
//books.clear();
|
||||||
|
//if (list_client.ListBooks(&books)) {
|
||||||
|
// PrintBookList(books);
|
||||||
|
//}
|
||||||
|
|
||||||
|
//PrintSeparator();
|
||||||
|
|
||||||
|
//Book book;
|
||||||
|
//if (detail_client.GetBook(id, &book)) {
|
||||||
|
// PrintBook(book);
|
||||||
|
//}
|
||||||
|
|
||||||
|
//PrintSeparator();
|
||||||
|
|
||||||
|
//detail_client.UpdateBook(id, "1Q84", 32.1);
|
||||||
|
|
||||||
|
//PrintSeparator();
|
||||||
|
|
||||||
|
//if (detail_client.GetBook(id, &book)) {
|
||||||
|
// PrintBook(book);
|
||||||
|
//}
|
||||||
|
|
||||||
|
//PrintSeparator();
|
||||||
|
|
||||||
|
//detail_client.DeleteBook(id);
|
||||||
|
|
||||||
|
//PrintSeparator();
|
||||||
|
|
||||||
|
//books.clear();
|
||||||
|
//if (list_client.ListBooks(&books)) {
|
||||||
|
// PrintBookList(books);
|
||||||
|
//}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
@ -0,0 +1,239 @@
|
|||||||
|
#include <iostream>
|
||||||
|
#include <list>
|
||||||
|
#include <string>
|
||||||
|
#include <thread>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "json/json.h"
|
||||||
|
|
||||||
|
#include "webcc/logger.h"
|
||||||
|
#include "webcc/rest_server.h"
|
||||||
|
#include "webcc/rest_service.h"
|
||||||
|
|
||||||
|
#include "examples/common/book.h"
|
||||||
|
#include "examples/common/book_json.h"
|
||||||
|
|
||||||
|
#if (defined(WIN32) || defined(_WIN64))
|
||||||
|
#if defined(_DEBUG) && defined(WEBCC_ENABLE_VLD)
|
||||||
|
#pragma message ("< include vld.h >")
|
||||||
|
#include "vld/vld.h"
|
||||||
|
#pragma comment(lib, "vld")
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
static BookStore g_book_store;
|
||||||
|
|
||||||
|
static void Sleep(int seconds) {
|
||||||
|
if (seconds > 0) {
|
||||||
|
LOG_INFO("Sleep %d seconds...", seconds);
|
||||||
|
std::this_thread::sleep_for(std::chrono::seconds(seconds));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
class BookListService : public webcc::RestListService {
|
||||||
|
public:
|
||||||
|
explicit BookListService(int sleep_seconds)
|
||||||
|
: sleep_seconds_(sleep_seconds) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
// Get a list of books based on query parameters.
|
||||||
|
void Get(const webcc::UrlQuery& query, webcc::RestResponse* response) final;
|
||||||
|
|
||||||
|
// Create a new book.
|
||||||
|
void Post(const std::string& request_content,
|
||||||
|
webcc::RestResponse* response) final;
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Sleep some seconds before send back the response.
|
||||||
|
// For testing timeout control in client side.
|
||||||
|
int sleep_seconds_;
|
||||||
|
};
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// The URL is like '/books/{BookID}', and the 'url_matches' parameter
|
||||||
|
// contains the matched book ID.
|
||||||
|
class BookDetailService : public webcc::RestDetailService {
|
||||||
|
public:
|
||||||
|
explicit BookDetailService(int sleep_seconds)
|
||||||
|
: sleep_seconds_(sleep_seconds) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
// Get the detailed information of a book.
|
||||||
|
void Get(const webcc::UrlMatches& url_matches,
|
||||||
|
const webcc::UrlQuery& query,
|
||||||
|
webcc::RestResponse* response) final;
|
||||||
|
|
||||||
|
// Update a book.
|
||||||
|
void Put(const webcc::UrlMatches& url_matches,
|
||||||
|
const std::string& request_content,
|
||||||
|
webcc::RestResponse* response) final;
|
||||||
|
|
||||||
|
// Delete a book.
|
||||||
|
void Delete(const webcc::UrlMatches& url_matches,
|
||||||
|
webcc::RestResponse* response) final;
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Sleep some seconds before send back the response.
|
||||||
|
// For testing timeout control in client side.
|
||||||
|
int sleep_seconds_;
|
||||||
|
};
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// Return all books as a JSON array.
|
||||||
|
void BookListService::Get(const webcc::UrlQuery& /*query*/,
|
||||||
|
webcc::RestResponse* response) {
|
||||||
|
Sleep(sleep_seconds_);
|
||||||
|
|
||||||
|
Json::Value json(Json::arrayValue);
|
||||||
|
|
||||||
|
for (const Book& book : g_book_store.books()) {
|
||||||
|
json.append(BookToJson(book));
|
||||||
|
}
|
||||||
|
|
||||||
|
response->content = JsonToString(json);
|
||||||
|
response->status = webcc::http::Status::kOK;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BookListService::Post(const std::string& request_content,
|
||||||
|
webcc::RestResponse* response) {
|
||||||
|
Sleep(sleep_seconds_);
|
||||||
|
|
||||||
|
Book book;
|
||||||
|
if (JsonStringToBook(request_content, &book)) {
|
||||||
|
std::string id = g_book_store.AddBook(book);
|
||||||
|
|
||||||
|
Json::Value json;
|
||||||
|
json["id"] = id;
|
||||||
|
|
||||||
|
response->content = JsonToString(json);
|
||||||
|
response->status = webcc::http::Status::kCreated;
|
||||||
|
} else {
|
||||||
|
// Invalid JSON
|
||||||
|
response->status = webcc::http::Status::kBadRequest;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
void BookDetailService::Get(const webcc::UrlMatches& url_matches,
|
||||||
|
const webcc::UrlQuery& query,
|
||||||
|
webcc::RestResponse* response) {
|
||||||
|
Sleep(sleep_seconds_);
|
||||||
|
|
||||||
|
if (url_matches.size() != 1) {
|
||||||
|
// Using kNotFound means the resource specified by the URL cannot be found.
|
||||||
|
// kBadRequest could be another choice.
|
||||||
|
response->status = webcc::http::Status::kNotFound;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& book_id = url_matches[0];
|
||||||
|
|
||||||
|
const Book& book = g_book_store.GetBook(book_id);
|
||||||
|
if (book.IsNull()) {
|
||||||
|
response->status = webcc::http::Status::kNotFound;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
response->content = BookToJsonString(book);
|
||||||
|
response->status = webcc::http::Status::kOK;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BookDetailService::Put(const webcc::UrlMatches& url_matches,
|
||||||
|
const std::string& request_content,
|
||||||
|
webcc::RestResponse* response) {
|
||||||
|
Sleep(sleep_seconds_);
|
||||||
|
|
||||||
|
if (url_matches.size() != 1) {
|
||||||
|
response->status = webcc::http::Status::kNotFound;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& book_id = url_matches[0];
|
||||||
|
|
||||||
|
Book book;
|
||||||
|
if (!JsonStringToBook(request_content, &book)) {
|
||||||
|
response->status = webcc::http::Status::kBadRequest;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
book.id = book_id;
|
||||||
|
g_book_store.UpdateBook(book);
|
||||||
|
|
||||||
|
response->status = webcc::http::Status::kOK;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BookDetailService::Delete(const webcc::UrlMatches& url_matches,
|
||||||
|
webcc::RestResponse* response) {
|
||||||
|
Sleep(sleep_seconds_);
|
||||||
|
|
||||||
|
if (url_matches.size() != 1) {
|
||||||
|
response->status = webcc::http::Status::kNotFound;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& book_id = url_matches[0];
|
||||||
|
|
||||||
|
if (!g_book_store.DeleteBook(book_id)) {
|
||||||
|
response->status = webcc::http::Status::kNotFound;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
response->status = webcc::http::Status::kOK;
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
void Help(const char* argv0) {
|
||||||
|
std::cout << "Usage: " << argv0 << " <port> [seconds]" << std::endl;
|
||||||
|
std::cout << "If |seconds| is provided, the server will sleep these seconds "
|
||||||
|
"before sending back each response."
|
||||||
|
<< std::endl;
|
||||||
|
std::cout << " E.g.," << std::endl;
|
||||||
|
std::cout << " " << argv0 << " 8080" << std::endl;
|
||||||
|
std::cout << " " << argv0 << " 8080 3" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char* argv[]) {
|
||||||
|
if (argc < 2) {
|
||||||
|
Help(argv[0]);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
WEBCC_LOG_INIT("", webcc::LOG_CONSOLE);
|
||||||
|
|
||||||
|
std::uint16_t port = static_cast<std::uint16_t>(std::atoi(argv[1]));
|
||||||
|
|
||||||
|
int sleep_seconds = 0;
|
||||||
|
if (argc >= 3) {
|
||||||
|
sleep_seconds = std::atoi(argv[2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t workers = 2;
|
||||||
|
|
||||||
|
try {
|
||||||
|
webcc::RestServer server(port, workers);
|
||||||
|
|
||||||
|
server.Bind(std::make_shared<BookListService>(sleep_seconds),
|
||||||
|
"/books", false);
|
||||||
|
|
||||||
|
server.Bind(std::make_shared<BookDetailService>(sleep_seconds),
|
||||||
|
"/books/(\\d+)", true);
|
||||||
|
|
||||||
|
server.Run();
|
||||||
|
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
std::cerr << "Exception: " << e.what() << std::endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
Loading…
Reference in New Issue