Reorganize examples; fix soap issues.

master
Chunting Gu 6 years ago
parent 05782b773a
commit 616f5a3f5e

@ -8,7 +8,6 @@ endif()
project(webcc)
option(WEBCC_ENABLE_REST "Enable REST support?" ON)
option(WEBCC_ENABLE_SOAP "Enable SOAP support (need pugixml)?" ON)
option(WEBCC_ENABLE_UNITTEST "Build unit test?" ON)
option(WEBCC_ENABLE_EXAMPLES "Build examples?" ON)
@ -118,14 +117,12 @@ endif()
add_subdirectory(webcc)
if(WEBCC_ENABLE_EXAMPLES)
if(WEBCC_ENABLE_REST)
# For including jsoncpp as "json/json.h".
include_directories(${THIRD_PARTY_DIR}/src/jsoncpp)
# REST examples need jsoncpp to parse and create JSON.
add_subdirectory(${THIRD_PARTY_DIR}/src/jsoncpp)
endif()
# For including jsoncpp as "json/json.h".
include_directories(${THIRD_PARTY_DIR}/src/jsoncpp)
# REST examples need jsoncpp to parse and create JSON.
add_subdirectory(${THIRD_PARTY_DIR}/src/jsoncpp)
add_subdirectory(example)
add_subdirectory(examples)
endif()
if(WEBCC_ENABLE_UNITTEST)

@ -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 <iostream>

@ -1,11 +1,11 @@
#include "example/common/book_json.h"
#include "examples/common/book_json.h"
#include <sstream>
#include <iostream>
#include "json/json.h"
#include "example/common/book.h"
#include "examples/common/book.h"
std::string JsonToString(const Json::Value& json) {
Json::StreamWriterBuilder builder;

@ -1,10 +1,10 @@
#include "example/common/book_xml.h"
#include "examples/common/book_xml.h"
#include <cassert>
#include <functional>
#include <sstream>
#include "example/common/book.h"
#include "examples/common/book.h"
// -----------------------------------------------------------------------------

@ -63,10 +63,7 @@ void PrettyPrintJsonString(const std::string& str) {
// -----------------------------------------------------------------------------
// List public events.
void ListEvents() {
webcc::HttpClientSession session;
session.set_ssl_verify(kSslVerify);
void ListEvents(webcc::HttpClientSession& session) {
try {
auto r = session.Get(kUrlRoot + "/events");
PRINT_JSON_STRING(r->content());
@ -76,10 +73,10 @@ void ListEvents() {
}
// List the followers of the given user.
void ListUserFollowers(const std::string& user) {
webcc::HttpClientSession session;
session.set_ssl_verify(kSslVerify);
// Example:
// ListUserFollowers(session, "<login>")
void ListUserFollowers(webcc::HttpClientSession& session,
const std::string& user) {
try {
auto r = session.Get(kUrlRoot + "/users/" + user + "/followers");
PRINT_JSON_STRING(r->content());
@ -90,10 +87,11 @@ void ListUserFollowers(const std::string& user) {
// List the followers of the current authorized user.
// Header syntax: Authorization: <type> <credentials>
void ListAuthUserFollowers(const std::string& auth) {
webcc::HttpClientSession session;
session.set_ssl_verify(kSslVerify);
// Example:
// ListAuthUserFollowers(session, "Basic <base64 encoded login:password>")
// ListAuthUserFollowers(session, "Token <token>")
void ListAuthUserFollowers(webcc::HttpClientSession& session,
const std::string& auth) {
try {
auto r = session.Get(kUrlRoot + "/user/followers", {},
{ "Authorization", auth });
@ -110,11 +108,11 @@ void ListAuthUserFollowers(const std::string& auth) {
int main() {
WEBCC_LOG_INIT("", webcc::LOG_CONSOLE);
//ListEvents();
webcc::HttpClientSession session;
//ListUserFollowers("<login>");
session.set_ssl_verify(kSslVerify);
//ListAuthUserFollowers("Basic <base64 encoded login:password>");
ListEvents(session);
return 0;
}

@ -71,6 +71,18 @@ void ExampleHttps() {
std::cout << r->content() << std::endl;
}
// Example for testing Keep-Alive connection.
//
// Boost.org doesn't support persistent connection so always includes
// "Connection: Close" header in the response.
// Both Google and GitHub support persistent connection but they don't like
// to include "Connection: Keep-Alive" header in the responses.
//
// ExampleKeepAlive("http://httpbin.org/get");
// ExampleKeepAlive("https://www.boost.org/LICENSE_1_0.txt");
// ExampleKeepAlive("https://www.google.com");
// ExampleKeepAlive("https://api.github.com/events");
//
void ExampleKeepAlive(const std::string& url) {
HttpClientSession session;
@ -95,21 +107,8 @@ int main() {
// Note that the exception handling is mandatory.
try {
//ExampleBasic();
//ExampleArgs();
//ExampleWrappers();
//ExampleHttps();
// Boost.org doesn't support persistent connection so always includes
// "Connection: Close" header in the response.
// Both Google and GitHub support persistent connection but they don't like
// to include "Connection: Keep-Alive" header in the responses.
//ExampleKeepAlive("http://httpbin.org/get");
//ExampleKeepAlive("https://www.boost.org/LICENSE_1_0.txt");
//ExampleKeepAlive("https://www.google.com");
//ExampleKeepAlive("https://api.github.com/events");
ExampleBasic();
} catch (const Exception& e) {
std::cout << "Exception: " << e.what() << std::endl;

@ -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;
}

@ -1,10 +1,24 @@
#include "example/soap_book_client/book_client.h"
#include <functional>
#include <iostream>
#include <string>
#include "pugixml/pugixml.hpp"
#include "webcc/logger.h"
#include "webcc/soap_client.h"
#include "examples/common/book.h"
#include "examples/common/book_xml.h"
#include "example/common/book_xml.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 const std::string kResult = "Result";
@ -14,9 +28,56 @@ static void PrintSeparateLine() {
std::cout << std::endl;
}
BookClient::BookClient(const std::string& host, const std::string& port)
: soap_client_(host, port), code_(0) {
soap_client_.set_url("/book");
// -----------------------------------------------------------------------------
class BookClient {
public:
explicit BookClient(const std::string& url);
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_;
};
// -----------------------------------------------------------------------------
BookClient::BookClient(const std::string& url)
: soap_client_(url), code_(0) {
soap_client_.set_service_ns({ "ser", "http://www.example.com/book/" });
// Customize response XML format.
@ -139,3 +200,64 @@ bool BookClient::ParseResultXml(const std::string& result_xml,
return true;
}
// -----------------------------------------------------------------------------
void Help(const char* argv0) {
std::cout << "Usage: " << argv0 << " <url>" << std::endl;
std::cout << " E.g.," << std::endl;
std::cout << " " << argv0 << " http://localhost:8080" << std::endl;
}
int main(int argc, char* argv[]) {
if (argc < 2) {
Help(argv[0]);
return 1;
}
WEBCC_LOG_INIT("", webcc::LOG_CONSOLE);
std::string url = argv[1];
BookClient client(url + "/book");
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,5 +1,3 @@
#include "example/soap_book_server/book_service.h"
#include <iostream>
#include <list>
#include <sstream>
@ -7,9 +5,19 @@
#include "webcc/logger.h"
#include "webcc/soap_request.h"
#include "webcc/soap_response.h"
#include "webcc/soap_server.h"
#include "webcc/soap_service.h"
#include "examples/common/book.h"
#include "examples/common/book_xml.h"
#include "example/common/book.h"
#include "example/common/book_xml.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
// -----------------------------------------------------------------------------
@ -19,6 +27,27 @@ static const std::string kResult = "Result";
// -----------------------------------------------------------------------------
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);
};
// -----------------------------------------------------------------------------
bool BookService::Handle(const webcc::SoapRequest& soap_request,
webcc::SoapResponse* soap_response) {
const std::string& operation = soap_request.operation();
@ -218,3 +247,39 @@ bool BookService::DeleteBook(const webcc::SoapRequest& soap_request,
return true;
}
// -----------------------------------------------------------------------------
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;
}

@ -7,13 +7,14 @@
static const std::string kResultName = "Result";
// -----------------------------------------------------------------------------
class CalcClient {
public:
CalcClient(const std::string& host, const std::string& port)
: soap_client_(host, port) {
CalcClient(const std::string& url)
: soap_client_(url) {
soap_client_.SetTimeout(5);
soap_client_.set_url("/calculator");
soap_client_.set_service_ns({
"ser", "http://www.example.com/calculator/"
});
@ -84,23 +85,22 @@ private:
// -----------------------------------------------------------------------------
void Help(const char* argv0) {
std::cout << "Usage: " << argv0 << " <host> <port>" << std::endl;
std::cout << "Usage: " << argv0 << " <url>" << std::endl;
std::cout << " E.g.," << std::endl;
std::cout << " " << argv0 << " localhost 8080" << std::endl;
std::cout << " " << argv0 << "http://localhost:8080" << std::endl;
}
int main(int argc, char* argv[]) {
if (argc < 3) {
if (argc < 2) {
Help(argv[0]);
return 1;
}
WEBCC_LOG_INIT("", webcc::LOG_CONSOLE);
std::string host = argv[1];
std::string port = argv[2];
std::string url = argv[1];
CalcClient calc(host, port);
CalcClient calc(url + "/calculator");
double x = 1.0;
double y = 2.0;

@ -10,11 +10,10 @@ static const std::string kResultName = "Result";
class CalcClient {
public:
// NOTE: Parasoft's calculator service uses SOAP V1.1.
CalcClient(const std::string& host, const std::string& port)
: soap_client_(host, port, webcc::kSoapV11) {
CalcClient(const std::string& url)
: soap_client_(url, webcc::kSoapV11) {
soap_client_.SetTimeout(5);
soap_client_.set_url("/glue/calculator");
soap_client_.set_service_ns({
"cal", "http://www.parasoft.com/wsdl/calculator/"
});
@ -92,7 +91,7 @@ private:
int main() {
WEBCC_LOG_INIT("", webcc::LOG_CONSOLE);
CalcClient calc("ws1.parasoft.com", ""); // Use default port 80
CalcClient calc("http://ws1.parasoft.com/glue/calculator");
double x = 1.0;
double y = 2.0;

@ -1,11 +1,23 @@
#include "example/soap_calc_server/calc_service.h"
#include <functional>
#include <iostream>
#include <string>
#include "webcc/logger.h"
#include "webcc/soap_request.h"
#include "webcc/soap_response.h"
#include "webcc/soap_server.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;
};
bool CalcService::Handle(const webcc::SoapRequest& soap_request,
webcc::SoapResponse* soap_response) {
@ -62,3 +74,39 @@ bool CalcService::Handle(const webcc::SoapRequest& soap_request,
return true;
}
// -----------------------------------------------------------------------------
static 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;
}

@ -20,19 +20,19 @@ TEST(RestServiceManager, URL_RegexBasic) {
service_manager.AddService(std::make_shared<TestRestService>(),
"/instance/(\\d+)", true);
std::vector<std::string> sub_matches;
std::vector<std::string> matches;
std::string url = "/instance/12345";
webcc::RestServicePtr service = service_manager.GetService(url, &sub_matches);
webcc::RestServicePtr service = service_manager.GetService(url, &matches);
EXPECT_TRUE(!!service);
EXPECT_EQ(1, sub_matches.size());
EXPECT_EQ("12345", sub_matches[0]);
EXPECT_EQ(1, matches.size());
EXPECT_EQ("12345", matches[0]);
url = "/instance/abcde";
sub_matches.clear();
service = service_manager.GetService(url, &sub_matches);
matches.clear();
service = service_manager.GetService(url, &matches);
EXPECT_FALSE(!!service);
}
@ -43,15 +43,15 @@ TEST(RestServiceManager, URL_Temp) {
service_manager.AddService(std::make_shared<TestRestService>(),
"/instance/([\\w.]+)", true);
std::vector<std::string> sub_matches;
std::vector<std::string> matches;
std::string url = "/instance/123.45-+6aaa";
webcc::RestServicePtr service = service_manager.GetService(url, &sub_matches);
webcc::RestServicePtr service = service_manager.GetService(url, &matches);
EXPECT_TRUE(!!service);
EXPECT_EQ(1, sub_matches.size());
EXPECT_EQ("123.45-6aaa", sub_matches[0]);
EXPECT_EQ(1, matches.size());
EXPECT_EQ("123.45-6aaa", matches[0]);
}
TEST(RestServiceManager, URL_RegexMultiple) {
@ -61,21 +61,21 @@ TEST(RestServiceManager, URL_RegexMultiple) {
"/study/(\\d+)/series/(\\d+)/instance/(\\d+)",
true);
std::vector<std::string> sub_matches;
std::vector<std::string> matches;
std::string url = "/study/1/series/2/instance/3";
webcc::RestServicePtr service = service_manager.GetService(url, &sub_matches);
webcc::RestServicePtr service = service_manager.GetService(url, &matches);
EXPECT_TRUE(!!service);
EXPECT_EQ(3, sub_matches.size());
EXPECT_EQ("1", sub_matches[0]);
EXPECT_EQ("2", sub_matches[1]);
EXPECT_EQ("3", sub_matches[2]);
EXPECT_EQ(3, matches.size());
EXPECT_EQ("1", matches[0]);
EXPECT_EQ("2", matches[1]);
EXPECT_EQ("3", matches[2]);
url = "/study/a/series/b/instance/c";
sub_matches.clear();
service = service_manager.GetService(url, &sub_matches);
matches.clear();
service = service_manager.GetService(url, &matches);
EXPECT_FALSE(!!service);
}
@ -86,10 +86,10 @@ TEST(RestServiceManager, URL_NonRegex) {
service_manager.AddService(std::make_shared<TestRestService>(), "/instances",
false);
std::vector<std::string> sub_matches;
std::vector<std::string> matches;
std::string url = "/instances";
webcc::RestServicePtr service = service_manager.GetService(url, &sub_matches);
webcc::RestServicePtr service = service_manager.GetService(url, &matches);
EXPECT_TRUE(!!service);
EXPECT_EQ(0, sub_matches.size());
EXPECT_EQ(0, matches.size());
}

@ -30,6 +30,10 @@ set(HEADERS
http_server.h
http_socket.h
queue.h
rest_request_handler.h
rest_server.h
rest_service.h
rest_service_manager.h
url.h
utility.h
version.h
@ -51,27 +55,13 @@ set(SOURCES
http_server.cc
http_socket.cc
logger.cc
rest_request_handler.cc
rest_service_manager.cc
rest_service.cc
url.cc
utility.cc
)
if(WEBCC_ENABLE_REST)
set(REST_HEADERS
rest_request_handler.h
rest_server.h
rest_service.h
rest_service_manager.h
)
set(REST_SOURCES
rest_request_handler.cc
rest_service_manager.cc
rest_service.cc
)
set(HEADERS ${HEADERS} ${REST_HEADERS})
set(SOURCES ${SOURCES} ${REST_SOURCES})
endif()
if(WEBCC_ENABLE_SOAP)
set(SOAP_HEADERS
soap_client.h

@ -88,7 +88,6 @@ const char* const kTextXml = "text/xml";
} // namespace media_types
// TODO: Rename to encodings?
namespace charsets {
const char* const kUtf8 = "utf-8";

@ -103,6 +103,25 @@ HttpResponsePtr HttpClientSession::Post(const std::string& url,
.headers(std::move(headers)));
}
HttpResponsePtr HttpClientSession::Put(const std::string& url,
std::string&& data, bool json,
std::vector<std::string>&& headers,
HttpRequestArgs&& args) {
return Request(args.method(http::kPut)
.url(url)
.data(std::move(data))
.json(json)
.headers(std::move(headers)));
}
HttpResponsePtr HttpClientSession::Delete(const std::string& url,
std::vector<std::string>&& headers,
HttpRequestArgs&& args) {
return Request(args.method(http::kDelete)
.url(url)
.headers(std::move(headers)));
}
void HttpClientSession::InitHeaders() {
headers_.Add(http::headers::kUserAgent, http::UserAgent());

@ -46,6 +46,14 @@ public:
std::vector<std::string>&& headers = {},
HttpRequestArgs&& args = HttpRequestArgs());
HttpResponsePtr Put(const std::string& url, std::string&& data, bool json,
std::vector<std::string>&& headers = {},
HttpRequestArgs&& args = HttpRequestArgs());
HttpResponsePtr Delete(const std::string& url,
std::vector<std::string>&& headers = {},
HttpRequestArgs&& args = HttpRequestArgs());
private:
void InitHeaders();

@ -63,6 +63,9 @@ bool HttpResponse::Prepare() {
SetHeader("Server", http::UserAgent());
SetHeader("Date", GetHttpDateTimestamp());
// TODO: Support Keep-Alive.
SetHeader(http::headers::kConnection, "Close");
return true;
}

@ -16,21 +16,15 @@ bool RestRequestHandler::Bind(RestServicePtr service, const std::string& url,
void RestRequestHandler::HandleConnection(HttpConnectionPtr connection) {
const HttpRequest& http_request = connection->request();
// TODO
const Url& url = http_request.url();
if (url.path().empty()) {
connection->SendResponse(http::Status::kBadRequest);
return;
}
RestRequest rest_request{
http_request.method(), http_request.content(), url.query()
};
// Get service by URL path.
RestServicePtr service =
service_manager_.GetService(url.path(), &rest_request.url_sub_matches);
std::string path = "/" + url.path();
auto service = service_manager_.GetService(path, &rest_request.url_matches);
if (!service) {
LOG_WARN("No service matches the URL path: %s", url.path().c_str());

@ -23,13 +23,13 @@ void RestListService::Handle(const RestRequest& request,
void RestDetailService::Handle(const RestRequest& request,
RestResponse* response) {
if (request.method == http::kGet) {
Get(request.url_sub_matches, UrlQuery(request.url_query_str), response);
Get(request.url_matches, UrlQuery(request.url_query_str), response);
} else if (request.method == http::kPut) {
Put(request.url_sub_matches, request.content, response);
Put(request.url_matches, request.content, response);
} else if (request.method == http::kPatch) {
Patch(request.url_sub_matches, request.content, response);
Patch(request.url_matches, request.content, response);
} else if (request.method == http::kDelete) {
Delete(request.url_sub_matches, response);
Delete(request.url_matches, response);
} else {
LOG_ERRO("RestDetailService doesn't support '%s' method.",
request.method.c_str());

@ -21,7 +21,7 @@ namespace webcc {
// -----------------------------------------------------------------------------
// Regex sub-matches of the URL.
typedef std::vector<std::string> UrlSubMatches;
typedef std::vector<std::string> UrlMatches;
struct RestRequest {
// HTTP method (GET, POST, etc.).
@ -34,7 +34,7 @@ struct RestRequest {
const std::string& url_query_str;
// Regex sub-matches of the URL (usually resource ID's).
UrlSubMatches url_sub_matches;
UrlMatches url_matches;
};
struct RestResponse {
@ -79,22 +79,22 @@ public:
void Handle(const RestRequest& request, RestResponse* response) final;
public:
virtual void Get(const UrlSubMatches& url_sub_matches,
virtual void Get(const UrlMatches& url_matches,
const UrlQuery& query,
RestResponse* response) {
}
virtual void Put(const UrlSubMatches& url_sub_matches,
virtual void Put(const UrlMatches& url_matches,
const std::string& request_content,
RestResponse* response) {
}
virtual void Patch(const UrlSubMatches& url_sub_matches,
virtual void Patch(const UrlMatches& url_matches,
const std::string& request_content,
RestResponse* response) {
}
virtual void Delete(const UrlSubMatches& url_sub_matches,
virtual void Delete(const UrlMatches& url_matches,
RestResponse* response) {
}
};

@ -31,9 +31,9 @@ bool RestServiceManager::AddService(RestServicePtr service,
}
}
RestServicePtr RestServiceManager::GetService(
const std::string& url, std::vector<std::string>* sub_matches) {
assert(sub_matches != nullptr);
RestServicePtr RestServiceManager::GetService(const std::string& url,
UrlMatches* matches) {
assert(matches != nullptr);
for (ServiceItem& item : service_items_) {
if (item.is_regex) {
@ -43,7 +43,7 @@ RestServicePtr RestServiceManager::GetService(
// Any sub-matches?
// NOTE: Start from 1 because match[0] is the whole string itself.
for (size_t i = 1; i < match.size(); ++i) {
sub_matches->push_back(match[i].str());
matches->push_back(match[i].str());
}
return item.service;

@ -24,12 +24,11 @@ public:
bool AddService(RestServicePtr service, const std::string& url,
bool is_regex);
// The |sub_matches| is only available when the |url| bound to the
// service is a regular expression and has sub-expressions.
// The |matches| is only available when the |url| bound to the service is a
// regular expression and has sub-expressions.
// E.g., the URL bound to the service is "/instances/(\\d+)", now match
// "/instances/12345" against it, you will get one sub-match of "12345".
RestServicePtr GetService(const std::string& url,
std::vector<std::string>* sub_matches);
// "/instances/12345" against it, you will get one match of "12345".
RestServicePtr GetService(const std::string& url, UrlMatches* matches);
private:
class ServiceItem {

@ -10,19 +10,11 @@
namespace webcc {
SoapClient::SoapClient(const std::string& host, const std::string& port,
SoapVersion soap_version, std::size_t buffer_size)
: host_(host), port_(port), soap_version_(soap_version),
SoapClient::SoapClient(const std::string& url, SoapVersion soap_version,
std::size_t buffer_size)
: url_(url), soap_version_(soap_version),
http_client_(buffer_size),
format_raw_(true), error_(kNoError) {
// Try to extract port from host if it's empty.
if (port_.empty()) {
std::size_t i = host_.find_last_of(':');
if (i != std::string::npos) {
port_ = host_.substr(i + 1);
host_ = host_.substr(0, i);
}
}
}
bool SoapClient::Request(const std::string& operation,
@ -30,7 +22,7 @@ bool SoapClient::Request(const std::string& operation,
SoapResponse::Parser parser,
std::size_t buffer_size) {
assert(service_ns_.IsValid());
assert(!url_.empty() && !host_.empty());
assert(!url_.empty());
error_ = kNoError;
fault_.reset();
@ -55,14 +47,7 @@ bool SoapClient::Request(const std::string& operation,
std::string http_content;
soap_request.ToXml(format_raw_, indent_str_, &http_content);
// TODO
std::string url = host_;
url += url_;
if (!port_.empty()) {
url += ":" + port_;
}
HttpRequest http_request(http::kPost, url);
HttpRequest http_request(http::kPost, url_);
http_request.SetContent(std::move(http_content), true);

@ -13,9 +13,7 @@ namespace webcc {
class SoapClient {
public:
// If |port| is empty, |host| will be checked to see if it contains port or
// not (separated by ':').
explicit SoapClient(const std::string& host, const std::string& port = "",
explicit SoapClient(const std::string& url,
SoapVersion soap_version = kSoapV12,
std::size_t buffer_size = 0);
@ -28,8 +26,6 @@ public:
http_client_.SetTimeout(seconds);
}
void set_url(const std::string& url) { url_ = url; }
void set_service_ns(const SoapNamespace& service_ns) {
service_ns_ = service_ns;
}
@ -71,16 +67,12 @@ public:
std::shared_ptr<SoapFault> fault() const { return fault_; }
private:
std::string host_;
std::string port_; // Leave this empty to use default 80.
std::string url_;
SoapVersion soap_version_;
HttpClient http_client_;
// Request URL.
std::string url_;
// Namespace for your web service.
SoapNamespace service_ns_;

@ -16,7 +16,9 @@ bool SoapRequestHandler::Bind(SoapServicePtr service, const std::string& url) {
}
void SoapRequestHandler::HandleConnection(HttpConnectionPtr connection) {
SoapServicePtr service = GetServiceByUrl(connection->request().url().path());
std::string path = "/" + connection->request().url().path();
SoapServicePtr service = GetServiceByUrl(path);
if (!service) {
connection->SendResponse(http::Status::kBadRequest);
return;

Loading…
Cancel
Save