Reorganize examples; fix soap issues.

This commit is contained in:
Chunting Gu
2019-03-15 14:40:36 +08:00
parent 05782b773a
commit 616f5a3f5e
49 changed files with 959 additions and 1091 deletions
+55
View File
@@ -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()
+61
View File
@@ -0,0 +1,61 @@
#include "examples/common/book.h"
#include <algorithm>
#include <iostream>
const Book kNullBook{};
std::ostream& operator<<(std::ostream& os, const Book& book) {
os << "{ " << book.id << ", " << book.title << ", " << book.price << " }";
return os;
}
const Book& BookStore::GetBook(const std::string& id) const {
auto it = FindBook(id);
return (it == books_.end() ? kNullBook : *it);
}
std::string BookStore::AddBook(const Book& book) {
std::string id = NewID();
books_.push_back({ id, book.title, book.price });
return id;
}
bool BookStore::UpdateBook(const Book& book) {
auto it = FindBook(book.id);
if (it != books_.end()) {
it->title = book.title;
it->price = book.price;
return true;
}
return false;
}
bool BookStore::DeleteBook(const std::string& id) {
auto it = FindBook(id);
if (it != books_.end()) {
books_.erase(it);
return true;
}
return false;
}
std::list<Book>::const_iterator BookStore::FindBook(const std::string& id)
const {
return std::find_if(books_.begin(), books_.end(),
[&id](const Book& book) { return book.id == id; });
}
std::list<Book>::iterator BookStore::FindBook(const std::string& id) {
return std::find_if(books_.begin(), books_.end(),
[&id](Book& book) { return book.id == id; });
}
std::string BookStore::NewID() const {
static int s_id_counter = 0;
++s_id_counter;
return std::to_string(s_id_counter);
}
+47
View File
@@ -0,0 +1,47 @@
#ifndef EXAMPLE_COMMON_BOOK_H_
#define EXAMPLE_COMMON_BOOK_H_
#include <list>
#include <string>
// In-memory test data.
// There should be some database in a real product.
struct Book {
std::string id;
std::string title;
double price;
bool IsNull() const { return id.empty(); }
};
std::ostream& operator<<(std::ostream& os, const Book& book);
extern const Book kNullBook;
class BookStore {
public:
const std::list<Book>& books() const { return books_; }
const Book& GetBook(const std::string& id) const;
// Add a book, return the ID.
// NOTE: The ID of the input book will be ignored so should be empty.
std::string AddBook(const Book& book);
bool UpdateBook(const Book& book);
bool DeleteBook(const std::string& id);
private:
std::list<Book>::const_iterator FindBook(const std::string& id) const;
std::list<Book>::iterator FindBook(const std::string& id);
// Allocate a new book ID.
std::string NewID() const;
std::list<Book> books_;
};
#endif // EXAMPLE_COMMON_BOOK_H_
+57
View File
@@ -0,0 +1,57 @@
#include "examples/common/book_json.h"
#include <sstream>
#include <iostream>
#include "json/json.h"
#include "examples/common/book.h"
std::string JsonToString(const Json::Value& json) {
Json::StreamWriterBuilder builder;
return Json::writeString(builder, json);
}
Json::Value StringToJson(const std::string& str) {
Json::Value json;
Json::CharReaderBuilder builder;
std::stringstream stream(str);
std::string errs;
if (!Json::parseFromStream(builder, stream, &json, &errs)) {
std::cerr << errs << std::endl;
}
return json;
}
Json::Value BookToJson(const Book& book) {
Json::Value json;
json["id"] = book.id;
json["title"] = book.title;
json["price"] = book.price;
return json;
}
Book JsonToBook(const Json::Value& json) {
return {
json["id"].asString(),
json["title"].asString(),
json["price"].asDouble(),
};
}
std::string BookToJsonString(const Book& book) {
return JsonToString(BookToJson(book));
}
bool JsonStringToBook(const std::string& json_str, Book* book) {
Json::Value json = StringToJson(json_str);
if (!json) {
return false;
}
*book = JsonToBook(json);
return true;
}
+20
View File
@@ -0,0 +1,20 @@
#ifndef EXAMPLE_COMMON_BOOK_JSON_H_
#define EXAMPLE_COMMON_BOOK_JSON_H_
#include <string>
#include "json/json-forwards.h"
struct Book;
std::string JsonToString(const Json::Value& json);
Json::Value StringToJson(const std::string& str);
Json::Value BookToJson(const Book& book);
Book JsonToBook(const Json::Value& json);
std::string BookToJsonString(const Book& book);
bool JsonStringToBook(const std::string& json_str, Book* book);
#endif // EXAMPLE_COMMON_BOOK_JSON_H_
+147
View File
@@ -0,0 +1,147 @@
#include "examples/common/book_xml.h"
#include <cassert>
#include <functional>
#include <sstream>
#include "examples/common/book.h"
// -----------------------------------------------------------------------------
// Print a XML node to string.
static std::string PrintXml(pugi::xml_node xnode, bool format_raw = true,
const char* indent = "") {
std::stringstream ss;
unsigned int flags = format_raw ? pugi::format_raw : pugi::format_indent;
xnode.print(ss, indent, flags);
return ss.str();
}
// -----------------------------------------------------------------------------
bool XmlToBook(pugi::xml_node xbook, Book* book) {
assert(xbook.name() == std::string("book"));
book->id = xbook.child("id").text().as_string();
book->title = xbook.child("title").text().as_string();
book->price = xbook.child("price").text().as_double();
return true;
}
void BookToXml(const Book& book, pugi::xml_node* xparent) {
pugi::xml_node xbook = xparent->append_child("book");
xbook.append_child("id").text().set(book.id.c_str());
xbook.append_child("title").text().set(book.title.c_str());
xbook.append_child("price").text().set(book.price);
}
bool XmlToBookList(pugi::xml_node xbooks, std::list<Book>* books) {
assert(xbooks.name() == std::string("books"));
pugi::xml_node xbook = xbooks.child("book");
while (xbook) {
Book book{
xbook.child("id").text().as_string(),
xbook.child("title").text().as_string(),
xbook.child("price").text().as_double()
};
books->push_back(book);
xbook = xbook.next_sibling("book");
}
return true;
}
void BookListToXml(const std::list<Book>& books, pugi::xml_node* xparent) {
pugi::xml_node xbooks = xparent->append_child("books");
for (const Book& book : books) {
BookToXml(book, &xbooks);
}
}
bool XmlStringToBook(const std::string& xml_string, Book* book) {
pugi::xml_document xdoc;
if (!xdoc.load_string(xml_string.c_str())) {
return false;
}
pugi::xml_node xbook = xdoc.document_element();
if (!xbook) {
return false;
}
if (xbook.name() != std::string("book")) {
return false;
}
return XmlToBook(xbook, book);
}
std::string BookToXmlString(const Book& book, bool format_raw,
const char* indent) {
pugi::xml_document xdoc;
BookToXml(book, &xdoc);
return PrintXml(xdoc);
}
// -----------------------------------------------------------------------------
std::string NewRequestXml(const Book& book) {
pugi::xml_document xdoc;
pugi::xml_node xwebcc = xdoc.append_child("webcc");
xwebcc.append_attribute("type") = "request";
BookToXml(book, &xwebcc);
return PrintXml(xdoc, false, " ");
}
// -----------------------------------------------------------------------------
static std::string __NewResultXml(int code, const char* message,
std::function<void(pugi::xml_node*)> callback) {
pugi::xml_document xdoc;
pugi::xml_node xwebcc = xdoc.append_child("webcc");
xwebcc.append_attribute("type") = "response";
pugi::xml_node xstatus = xwebcc.append_child("status");
xstatus.append_attribute("code") = code;
xstatus.append_attribute("message") = message;
if (callback) {
callback(&xwebcc);
}
return PrintXml(xdoc, false, " ");
}
std::string NewResultXml(int code, const char* message) {
return __NewResultXml(code, message, {});
}
std::string NewResultXml(int code, const char* message, const char* node,
const char* key, const char* value) {
auto callback = [node, key, value](pugi::xml_node* xparent) {
pugi::xml_node xnode = xparent->append_child(node);
xnode.append_child(key).text() = value;
};
return __NewResultXml(code, message, callback);
}
std::string NewResultXml(int code, const char* message, const Book& book) {
return __NewResultXml(code, message,
std::bind(BookToXml, book, std::placeholders::_1));
}
std::string NewResultXml(int code, const char* message,
const std::list<Book>& books) {
return __NewResultXml(code, message,
std::bind(BookListToXml, books, std::placeholders::_1));
}
+111
View File
@@ -0,0 +1,111 @@
#ifndef EXAMPLE_COMMON_BOOK_XML_H_
#define EXAMPLE_COMMON_BOOK_XML_H_
#include <list>
#include <string>
#include "pugixml/pugixml.hpp"
struct Book;
// -----------------------------------------------------------------------------
// Convert the following XML node to a book object.
// <book>
// <id>1</id>
// <title>1984</title>
// <price>12.3</price>
// </book>
bool XmlToBook(pugi::xml_node xbook, Book* book);
// Convert a book object to XML and append to the given parent.
void BookToXml(const Book& book, pugi::xml_node* xparent);
// Convert the following XML node to a list of book objects.
// <books>
// <book>
// <id>1</id>
// <title>1984</title>
// <price>12.3</price>
// </book>
// ...
// </books>
bool XmlToBookList(pugi::xml_node xbooks, std::list<Book>* books);
// Convert a list of book objects to XML and append to the given parent.
void BookListToXml(const std::list<Book>& books, pugi::xml_node* xparent);
// Convert the following XML string to a book object.
// <book>
// <id>1</id>
// <title>1984</title>
// <price>12.3</price>
// </book>
bool XmlStringToBook(const std::string& xml_string, Book* book);
// Convert a book object to XML string.
std::string BookToXmlString(const Book& book, bool format_raw = true,
const char* indent = "");
// -----------------------------------------------------------------------------
// This example defines its own result XML which will be embedded into the SOAP
// envolope as CDATA. The general schema of this result XML is:
// <webcc type = "result">
// <status code = "{code}" message = "{message}">
// </webcc>
// The "status" node is mandatory, you should define proper status codes and
// messages according to your needs.
// Additional data is attached as the sibling of "status" node, e.g.,
// <webcc type = "result">
// <status code = "{code}" message = "{message}">
// <book>
// <id>{book.id}</id>
// <title>{book.title}</title>
// <price>{book.price}</price>
// </book>
// </webcc>
// Create a result XML as below:
// <webcc type = "result">
// <status code = "{code}" message = "{message}">
// </webcc>
std::string NewResultXml(int code, const char* message);
// Create a result XML as below:
// <webcc type = "result">
// <status code = "{code}" message = "{message}">
// <{node}>
// <{key}>{value}</{key}>
// </{node}>
// </webcc>
std::string NewResultXml(int code, const char* message, const char* node,
const char* key, const char* value);
// Create a result XML as below:
// <webcc type = "result">
// <status code = "{code}" message = "{message}">
// <book>
// <id>{book.id}</id>
// <title>{book.title}</title>
// <price>{book.price}</price>
// </book>
// </webcc>
std::string NewResultXml(int code, const char* message, const Book& book);
// Create a result XML as below:
// <webcc type = "result">
// <status code = "{code}" message = "{message}">
// <books>
// <book>
// <id>{book.id}</id>
// <title>{book.title}</title>
// <price>{book.price}</price>
// </book>
// ...
// </books>
// </webcc>
std::string NewResultXml(int code, const char* message,
const std::list<Book>& books);
#endif // EXAMPLE_COMMON_BOOK_XML_H_
+118
View File
@@ -0,0 +1,118 @@
#include <iostream>
#include "json/json.h"
#include "webcc/http_client_session.h"
#include "webcc/logger.h"
// -----------------------------------------------------------------------------
// Change to 1 to print response JSON.
#define PRINT_RESPONSE 0
#if (defined(WIN32) || defined(_WIN64))
// You need to set environment variable SSL_CERT_FILE properly to enable
// SSL verification.
bool kSslVerify = false;
#else
bool kSslVerify = true;
#endif
const std::size_t kBufferSize = 1500;
const std::string kUrlRoot = "https://api.github.com";
// -----------------------------------------------------------------------------
// JSON helper functions (based on cppjson).
// Parse a string to JSON object.
Json::Value StringToJson(const std::string& str) {
Json::Value json;
Json::CharReaderBuilder builder;
std::stringstream stream(str);
std::string errors;
if (!Json::parseFromStream(builder, stream, &json, &errors)) {
std::cerr << errors << std::endl;
}
return json;
}
// Print the JSON string in pretty format.
void PrettyPrintJsonString(const std::string& str) {
Json::Value json = StringToJson(str);
Json::StreamWriterBuilder builder;
builder["indentation"] = " ";
std::unique_ptr<Json::StreamWriter> writer(builder.newStreamWriter());
writer->write(json, &std::cout);
std::cout << std::endl;
}
// -----------------------------------------------------------------------------
#if PRINT_RESPONSE
#define PRINT_JSON_STRING(str) PrettyPrintJsonString(str)
#else
#define PRINT_JSON_STRING(str)
#endif // PRINT_RESPONSE
// -----------------------------------------------------------------------------
// List public events.
void ListEvents(webcc::HttpClientSession& session) {
try {
auto r = session.Get(kUrlRoot + "/events");
PRINT_JSON_STRING(r->content());
} catch (const webcc::Exception& e) {
std::cout << e.what() << std::endl;
}
}
// List the followers of the given user.
// 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());
} catch (const webcc::Exception& e) {
std::cout << e.what() << std::endl;
}
}
// List the followers of the current authorized user.
// Header syntax: Authorization: <type> <credentials>
// 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 });
PRINT_JSON_STRING(r->content());
} catch (const webcc::Exception& e) {
std::cout << e.what() << std::endl;
}
}
// -----------------------------------------------------------------------------
int main() {
WEBCC_LOG_INIT("", webcc::LOG_CONSOLE);
webcc::HttpClientSession session;
session.set_ssl_verify(kSslVerify);
ListEvents(session);
return 0;
}
+118
View File
@@ -0,0 +1,118 @@
#include <iostream>
#include "webcc/http_client_session.h"
#include "webcc/logger.h"
using namespace webcc;
// -----------------------------------------------------------------------------
#if (defined(WIN32) || defined(_WIN64))
// You need to set environment variable SSL_CERT_FILE properly to enable
// SSL verification.
bool kSslVerify = false;
#else
bool kSslVerify = true;
#endif
// -----------------------------------------------------------------------------
void ExampleBasic() {
HttpClientSession session;
auto r = session.Request(HttpRequestArgs{"GET"}
.url("http://httpbin.org/get")
.parameters({"key1", "value1", "key2", "value2"})
.headers({"Accept", "application/json"})
.buffer_size(1000));
std::cout << r->content() << std::endl;
}
// If you want to create the args object firstly, there might be an extra
// call to its move constructor (maybe only for MSVC).
// - constructor: HttpRequestArgs{ "GET" }
// - move constructor: auto args = ...
void ExampleArgs() {
HttpClientSession session;
auto args = HttpRequestArgs{"GET"}
.url("http://httpbin.org/get")
.parameters({"key1", "value1", "key2", "value2"})
.headers({"Accept", "application/json"})
.buffer_size(1000);
// Note the std::move().
session.Request(std::move(args));
}
// Use pre-defined wrappers.
void ExampleWrappers() {
HttpClientSession session;
session.Get("http://httpbin.org/get", {"key1", "value1", "key2", "value2"},
{"Accept", "application/json"},
HttpRequestArgs{}.buffer_size(1000));
session.Post("http://httpbin.org/post", "{ 'key': 'value' }", true,
{"Accept", "application/json"});
}
// HTTPS is auto-detected from the URL scheme.
void ExampleHttps() {
HttpClientSession session;
auto r = session.Request(HttpRequestArgs{"GET"}
.url("https://httpbin.org/get")
.parameters({"key1", "value1", "key2", "value2"})
.headers({"Accept", "application/json"})
.ssl_verify(kSslVerify));
std::cout << r->content() << std::endl;
}
// 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;
// Keep-Alive
session.Request(webcc::HttpRequestArgs("GET").url(url).
ssl_verify(kSslVerify));
// Close
session.Request(webcc::HttpRequestArgs("GET").url(url).
ssl_verify(kSslVerify).
headers({ "Connection", "Close" }));
// Keep-Alive
session.Request(webcc::HttpRequestArgs("GET").url(url).
ssl_verify(kSslVerify));
}
// -----------------------------------------------------------------------------
int main() {
WEBCC_LOG_INIT("", LOG_CONSOLE);
// Note that the exception handling is mandatory.
try {
ExampleBasic();
} catch (const Exception& e) {
std::cout << "Exception: " << e.what() << std::endl;
}
return 0;
}
+282
View File
@@ -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;
}
+239
View File
@@ -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;
}
+263
View File
@@ -0,0 +1,263 @@
#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"
#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";
static void PrintSeparateLine() {
std::cout << "--------------------------------";
std::cout << "--------------------------------";
std::cout << std::endl;
}
// -----------------------------------------------------------------------------
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.
soap_client_.set_format_raw(false);
soap_client_.set_indent_str(" ");
}
bool BookClient::CreateBook(const std::string& title, double price,
std::string* id) {
PrintSeparateLine();
std::cout << "CreateBook: " << title << ", " << price << std::endl;
webcc::SoapParameter parameter{
"book",
BookToXmlString({ "", title, price }),
true, // as_cdata
};
std::string result_xml;
if (!Call1("CreateBook", std::move(parameter), &result_xml)) {
return false;
}
auto callback = [id](pugi::xml_node xnode) {
*id = xnode.child("book").child("id").text().as_string();
return !id->empty();
};
return ParseResultXml(result_xml, callback);
}
bool BookClient::GetBook(const std::string& id, Book* book) {
PrintSeparateLine();
std::cout << "GetBook: " << id << std::endl;
std::string result_xml;
if (!Call1("GetBook", { "id", id }, &result_xml)) {
return false;
}
auto callback = [book](pugi::xml_node xnode) {
return XmlToBook(xnode.child("book"), book);
};
return ParseResultXml(result_xml, callback);
}
bool BookClient::ListBooks(std::list<Book>* books) {
PrintSeparateLine();
std::cout << "ListBooks" << std::endl;
std::string result_xml;
if (!Call0("ListBooks", &result_xml)) {
return false;
}
auto callback = [books](pugi::xml_node xnode) {
return XmlToBookList(xnode.child("books"), books);
};
return ParseResultXml(result_xml, callback);
}
bool BookClient::DeleteBook(const std::string& id) {
PrintSeparateLine();
std::cout << "DeleteBook: " << id << std::endl;
std::string result_xml;
if (!Call1("DeleteBook", { "id", id }, &result_xml)) {
return false;
}
return ParseResultXml(result_xml, {});
}
bool BookClient::Call0(const std::string& operation, std::string* result_str) {
return Call(operation, {}, result_str);
}
bool BookClient::Call1(const std::string& operation,
webcc::SoapParameter&& parameter,
std::string* result_str) {
std::vector<webcc::SoapParameter> parameters{
{ std::move(parameter) }
};
return Call(operation, std::move(parameters), result_str);
}
bool BookClient::Call(const std::string& operation,
std::vector<webcc::SoapParameter>&& parameters,
std::string* result_str) {
if (!soap_client_.Request(operation, std::move(parameters), kResult, 0,
result_str)) {
PrintError();
return false;
}
return true;
}
void BookClient::PrintError() {
std::cout << webcc::DescribeError(soap_client_.error());
if (soap_client_.timed_out()) {
std::cout << " (timed out)";
}
std::cout << std::endl;
}
bool BookClient::ParseResultXml(const std::string& result_xml,
std::function<bool(pugi::xml_node)> callback) {
pugi::xml_document xdoc;
if (!xdoc.load_string(result_xml.c_str())) {
return false;
}
pugi::xml_node xwebcc = xdoc.document_element();
pugi::xml_node xstatus = xwebcc.child("status");
code_ = xstatus.attribute("code").as_int();
message_ = xstatus.attribute("message").as_string();
if (callback) {
return callback(xwebcc);
}
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;
}
+285
View File
@@ -0,0 +1,285 @@
#include <iostream>
#include <list>
#include <sstream>
#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"
#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 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();
soap_response->set_service_ns({
"ser",
"http://www.example.com/book/"
});
soap_response->set_operation(operation);
if (operation == "CreateBook") {
return CreateBook(soap_request, soap_response);
} else if (operation == "GetBook") {
return GetBook(soap_request, soap_response);
} else if (operation == "ListBooks") {
return ListBooks(soap_request, soap_response);
} else if (operation == "DeleteBook") {
return DeleteBook(soap_request, soap_response);
} else {
LOG_ERRO("Operation '%s' is not supported.", operation.c_str());
return false;
}
return false;
}
bool BookService::CreateBook(const webcc::SoapRequest& soap_request,
webcc::SoapResponse* soap_response) {
// Request SOAP envelope:
// <soap:Envelope xmlns:soap="...">
// <soap:Body>
// <ser:CreateBook xmlns:ser="..." />
// <ser:book>
// <![CDATA[
// <book>
// <title>1984</title>
// <price>12.3</price>
// </book>
// ]]>
// </ser:book>
// </ser:CreateBook>
// </soap:Body>
// </soap:Envelope>
// Response SOAP envelope:
// <soap:Envelope xmlns:soap="...">
// <soap:Body>
// <ser:CreateBookResponse xmlns:ser="...">
// <ser:Result>
// <![CDATA[
// <webcc type = "response">
// <status code = "0" message = "ok">
// <book>
// <id>1</id>
// </book>
// </webcc>
// ]]>
// </ser:Result>
// </ser:CreateBookResponse>
// </soap:Body>
// </soap:Envelope>
const std::string& title = soap_request.GetParameter("title");
const std::string& book_xml = soap_request.GetParameter("book");
Book book;
XmlStringToBook(book_xml, &book); // TODO: Error handling
std::string id = g_book_store.AddBook(book);
std::string response_xml = NewResultXml(0, "ok", "book", "id",
id.c_str());
soap_response->set_simple_result(kResult, std::move(response_xml), true);
return true;
}
bool BookService::GetBook(const webcc::SoapRequest& soap_request,
webcc::SoapResponse* soap_response) {
// Request SOAP envelope:
// <soap:Envelope xmlns:soap="...">
// <soap:Body>
// <ser:GetBook xmlns:ser="..." />
// <ser:id>1</ser:id>
// </ser:GetBook>
// </soap:Body>
// </soap:Envelope>
// Response SOAP envelope:
// <soap:Envelope xmlns:soap="...">
// <soap:Body>
// <ser:GetBookResponse xmlns:ser="...">
// <ser:Result>
// <![CDATA[
// <webcc type = "response">
// <status code = "0" message = "ok">
// <book>
// <id>1</id>
// <title>1984</title>
// <price>12.3</price>
// </book>
// </webcc>
// ]]>
// </ser:Result>
// </ser:GetBookResponse>
// </soap:Body>
// </soap:Envelope>
const std::string& id = soap_request.GetParameter("id");
const Book& book = g_book_store.GetBook(id);
soap_response->set_simple_result(kResult, NewResultXml(0, "ok", book), true);
return true;
}
bool BookService::ListBooks(const webcc::SoapRequest& soap_request,
webcc::SoapResponse* soap_response) {
// Request SOAP envelope:
// <soap:Envelope xmlns:soap="...">
// <soap:Body>
// <ser:ListBooks xmlns:ser="..." />
// </soap:Body>
// </soap:Envelope>
// Response SOAP envelope:
// <soap:Envelope xmlns:soap="...">
// <soap:Body>
// <ser:ListBooksResponse xmlns:ser="...">
// <ser:Result>
// <![CDATA[
// <webcc type = "response">
// <status code = "0" message = "ok">
// <books>
// <book>
// <id>1</id>
// <title>1984</title>
// <price>12.3</price>
// </book>
// ...
// </books>
// </webcc>
// ]]>
// </ser:Result>
// </ser:ListBooksResponse>
// </soap:Body>
// </soap:Envelope>
const std::list<Book>& books = g_book_store.books();
soap_response->set_simple_result(kResult, NewResultXml(0, "ok", books), true);
return true;
}
bool BookService::DeleteBook(const webcc::SoapRequest& soap_request,
webcc::SoapResponse* soap_response) {
// Request SOAP envelope:
// <soap:Envelope xmlns:soap="...">
// <soap:Body>
// <ser:DeleteBook xmlns:ser="..." />
// <ser:id>1</ser:id>
// </ser:DeleteBook>
// </soap:Body>
// </soap:Envelope>
// Response SOAP envelope:
// <soap:Envelope xmlns:soap="...">
// <soap:Body>
// <ser:DeleteBookResponse xmlns:ser="...">
// <ser:Result>
// <![CDATA[
// <webcc type = "response">
// <status code = "0" message = "ok">
// </webcc>
// ]]>
// </ser:Result>
// </ser:DeleteBookResponse>
// </soap:Body>
// </soap:Envelope>
const std::string& id = soap_request.GetParameter("id");
if (g_book_store.DeleteBook(id)) {
soap_response->set_simple_result(kResult, NewResultXml(0, "ok"), true);
} else {
soap_response->set_simple_result(kResult, NewResultXml(1, "error"), true);
}
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;
}
+128
View File
@@ -0,0 +1,128 @@
#include <iostream>
#include "webcc/logger.h"
#include "webcc/soap_client.h"
// -----------------------------------------------------------------------------
static const std::string kResultName = "Result";
// -----------------------------------------------------------------------------
class CalcClient {
public:
CalcClient(const std::string& url)
: soap_client_(url) {
soap_client_.SetTimeout(5);
soap_client_.set_service_ns({
"ser", "http://www.example.com/calculator/"
});
// Customize request XML format.
soap_client_.set_format_raw(false);
soap_client_.set_indent_str(" ");
}
bool Add(double x, double y, double* result) {
return Calc("add", "x", "y", x, y, result);
}
bool Subtract(double x, double y, double* result) {
return Calc("subtract", "x", "y", x, y, result);
}
bool Multiply(double x, double y, double* result) {
return Calc("multiply", "x", "y", x, y, result);
}
bool Divide(double x, double y, double* result) {
return Calc("divide", "x", "y", x, y, result);
}
// Only for testing purpose.
bool Unknown(double x, double y, double* result) {
return Calc("unknown", "x", "y", x, y, result);
}
private:
bool Calc(const std::string& operation,
const std::string& x_name, const std::string& y_name,
double x, double y,
double* result) {
std::vector<webcc::SoapParameter> parameters{
{ x_name, x },
{ y_name, y }
};
std::string result_str;
if (!soap_client_.Request(operation, std::move(parameters), kResultName, 0,
&result_str)) {
PrintError();
return false;
}
try {
*result = std::stod(result_str);
} catch (const std::exception&) {
return false;
}
return true;
}
void PrintError() {
std::cout << webcc::DescribeError(soap_client_.error());
if (soap_client_.timed_out()) {
std::cout << " (timed out)";
}
std::cout << std::endl;
}
webcc::SoapClient soap_client_;
};
// -----------------------------------------------------------------------------
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];
CalcClient calc(url + "/calculator");
double x = 1.0;
double y = 2.0;
double result = 0.0;
if (calc.Add(x, y, &result)) {
printf("add: %.1f\n", result);
}
if (calc.Subtract(x, y, &result)) {
printf("subtract: %.1f\n", result);
}
if (calc.Multiply(x, y, &result)) {
printf("multiply: %.1f\n", result);
}
if (calc.Divide(x, y, &result)) {
printf("divide: %.1f\n", result);
}
calc.Unknown(x, y, &result);
return 0;
}
+119
View File
@@ -0,0 +1,119 @@
#include <iostream>
#include "webcc/logger.h"
#include "webcc/soap_client.h"
// -----------------------------------------------------------------------------
static const std::string kResultName = "Result";
class CalcClient {
public:
// NOTE: Parasoft's calculator service uses SOAP V1.1.
CalcClient(const std::string& url)
: soap_client_(url, webcc::kSoapV11) {
soap_client_.SetTimeout(5);
soap_client_.set_service_ns({
"cal", "http://www.parasoft.com/wsdl/calculator/"
});
// Customize request XML format.
soap_client_.set_format_raw(false);
soap_client_.set_indent_str(" ");
}
bool Add(double x, double y, double* result) {
return Calc("add", "x", "y", x, y, result);
}
bool Subtract(double x, double y, double* result) {
return Calc("subtract", "x", "y", x, y, result);
}
bool Multiply(double x, double y, double* result) {
return Calc("multiply", "x", "y", x, y, result);
}
bool Divide(double x, double y, double* result) {
return Calc("divide", "numerator", "denominator", x, y, result);
}
// Only for testing purpose.
bool Unknown(double x, double y, double* result) {
return Calc("unknown", "x", "y", x, y, result);
}
private:
bool Calc(const std::string& operation,
const std::string& x_name, const std::string& y_name,
double x, double y,
double* result) {
std::vector<webcc::SoapParameter> parameters{
{ x_name, x },
{ y_name, y }
};
std::string result_str;
if (!soap_client_.Request(operation, std::move(parameters), kResultName, 0,
&result_str)) {
PrintError();
return false;
}
try {
*result = std::stod(result_str);
} catch (const std::exception&) {
return false;
}
return true;
}
void PrintError() {
std::cout << webcc::DescribeError(soap_client_.error());
if (soap_client_.timed_out()) {
std::cout << " (timed out)";
}
std::cout << std::endl;
if (soap_client_.fault()) {
std::cout << *soap_client_.fault() << std::endl;
}
}
webcc::SoapClient soap_client_;
};
// -----------------------------------------------------------------------------
int main() {
WEBCC_LOG_INIT("", webcc::LOG_CONSOLE);
CalcClient calc("http://ws1.parasoft.com/glue/calculator");
double x = 1.0;
double y = 2.0;
double result = 0.0;
if (calc.Add(x, y, &result)) {
printf("add: %.1f\n", result);
}
if (calc.Subtract(x, y, &result)) {
printf("subtract: %.1f\n", result);
}
if (calc.Multiply(x, y, &result)) {
printf("multiply: %.1f\n", result);
}
if (calc.Divide(x, y, &result)) {
printf("divide: %.1f\n", result);
}
calc.Unknown(x, y, &result);
return 0;
}
+112
View File
@@ -0,0 +1,112 @@
#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) {
double x = 0.0;
double y = 0.0;
try {
x = std::stod(soap_request.GetParameter("x"));
y = std::stod(soap_request.GetParameter("y"));
} catch (const std::exception& e) {
LOG_ERRO("SoapParameter cast error: %s", e.what());
return false;
}
const std::string& operation = soap_request.operation();
LOG_INFO("Soap operation '%s': %.2f, %.2f", operation.c_str(), x, y);
std::function<double(double, double)> calc;
if (operation == "add") {
calc = [](double x, double y) { return x + y; };
} else if (operation == "subtract") {
calc = [](double x, double y) { return x - y; };
} else if (operation == "multiply") {
calc = [](double x, double y) { return x * y; };
} else if (operation == "divide") {
calc = [](double x, double y) { return x / y; };
if (y == 0.0) {
LOG_ERRO("Cannot divide by 0.");
return false;
}
} else {
LOG_ERRO("Operation '%s' is not supported.", operation.c_str());
return false;
}
if (!calc) {
return false;
}
double result = calc(x, y);
soap_response->set_service_ns({
"cal",
"http://www.example.com/calculator/"
});
soap_response->set_operation(soap_request.operation());
soap_response->set_simple_result("Result", std::to_string(result), false);
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;
}