Support CDATA for SOAP request and response; Rename Parameter to SoapParameter; Add book server and client example for SOAP.
This commit is contained in:
@@ -0,0 +1,61 @@
|
||||
#include "example/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);
|
||||
}
|
||||
@@ -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_
|
||||
@@ -0,0 +1,147 @@
|
||||
#include "example/common/book_xml.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <functional>
|
||||
#include <sstream>
|
||||
|
||||
#include "example/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));
|
||||
}
|
||||
@@ -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_
|
||||
@@ -1,4 +1,8 @@
|
||||
add_executable(rest_book_client main.cc)
|
||||
set(TARGET_NAME rest_book_client)
|
||||
|
||||
target_link_libraries(rest_book_client webcc jsoncpp ${Boost_LIBRARIES})
|
||||
target_link_libraries(rest_book_client "${CMAKE_THREAD_LIBS_INIT}")
|
||||
set(SRCS main.cc)
|
||||
|
||||
add_executable(${TARGET_NAME} ${SRCS})
|
||||
|
||||
target_link_libraries(${TARGET_NAME} webcc jsoncpp ${Boost_LIBRARIES})
|
||||
target_link_libraries(${TARGET_NAME} "${CMAKE_THREAD_LIBS_INIT}")
|
||||
|
||||
@@ -1,6 +1,13 @@
|
||||
set(SRCS book_services.cc book_services.h main.cc)
|
||||
set(TARGET_NAME rest_book_server)
|
||||
|
||||
add_executable(rest_book_server ${SRCS})
|
||||
set(SRCS
|
||||
../common/book.cc
|
||||
../common/book.h
|
||||
services.cc
|
||||
services.h
|
||||
main.cc)
|
||||
|
||||
target_link_libraries(rest_book_server webcc jsoncpp ${Boost_LIBRARIES})
|
||||
target_link_libraries(rest_book_server "${CMAKE_THREAD_LIBS_INIT}")
|
||||
add_executable(${TARGET_NAME} ${SRCS})
|
||||
|
||||
target_link_libraries(${TARGET_NAME} webcc jsoncpp ${Boost_LIBRARIES})
|
||||
target_link_libraries(${TARGET_NAME} "${CMAKE_THREAD_LIBS_INIT}")
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#include "webcc/logger.h"
|
||||
#include "webcc/rest_server.h"
|
||||
|
||||
#include "example/rest_book_server/book_services.h"
|
||||
#include "example/rest_book_server/services.h"
|
||||
|
||||
// In order to run with VLD, please copy the following files to the example
|
||||
// output folder from "third_party\win32\bin":
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "example/rest_book_server/book_services.h"
|
||||
#include "example/rest_book_server/services.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <list>
|
||||
@@ -7,95 +7,21 @@
|
||||
#include "json/json.h"
|
||||
#include "webcc/logger.h"
|
||||
|
||||
#include "example/common/book.h"
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// 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();
|
||||
}
|
||||
|
||||
Json::Value ToJson() const {
|
||||
Json::Value root;
|
||||
root["id"] = id;
|
||||
root["title"] = title;
|
||||
root["price"] = price;
|
||||
return root;
|
||||
}
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const Book& book) {
|
||||
os << "{ " << book.id << ", " << book.title << ", " << book.price << " }";
|
||||
return os;
|
||||
}
|
||||
|
||||
static const Book kNullBook{};
|
||||
|
||||
class BookStore {
|
||||
public:
|
||||
BookStore() = default;
|
||||
|
||||
const std::list<Book>& books() const { return books_; }
|
||||
|
||||
const Book& GetBook(const std::string& id) const {
|
||||
auto it = FindBook(id);
|
||||
return (it == books_.end() ? kNullBook : *it);
|
||||
}
|
||||
|
||||
bool AddBook(const Book& new_book) {
|
||||
if (FindBook(new_book.id) == books_.end()) {
|
||||
books_.push_back(new_book);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool 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 DeleteBook(const std::string& id) {
|
||||
auto it = FindBook(id);
|
||||
|
||||
if (it != books_.end()) {
|
||||
books_.erase(it);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
std::list<Book>::const_iterator 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 FindBook(const std::string& id) {
|
||||
return std::find_if(books_.begin(), books_.end(),
|
||||
[&id](Book& book) { return book.id == id; });
|
||||
}
|
||||
|
||||
std::list<Book> books_;
|
||||
};
|
||||
|
||||
static BookStore g_book_store;
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
static Json::Value BookToJson(const Book& book) {
|
||||
Json::Value root;
|
||||
root["id"] = book.id;
|
||||
root["title"] = book.title;
|
||||
root["price"] = book.price;
|
||||
return root;
|
||||
}
|
||||
|
||||
static bool BookFromJson(const std::string& json, Book* book) {
|
||||
static bool JsonToBook(const std::string& json, Book* book) {
|
||||
Json::Value root;
|
||||
Json::CharReaderBuilder builder;
|
||||
std::stringstream stream(json);
|
||||
@@ -112,6 +38,8 @@ static bool BookFromJson(const std::string& json, Book* book) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// Return all books as a JSON array.
|
||||
// TODO: Support query parameters.
|
||||
bool BookListService::Get(const webcc::UrlQuery& /* query */,
|
||||
@@ -123,7 +51,7 @@ bool BookListService::Get(const webcc::UrlQuery& /* query */,
|
||||
|
||||
Json::Value root(Json::arrayValue);
|
||||
for (const Book& book : g_book_store.books()) {
|
||||
root.append(book.ToJson());
|
||||
root.append(BookToJson(book));
|
||||
}
|
||||
|
||||
Json::StreamWriterBuilder builder;
|
||||
@@ -142,8 +70,9 @@ bool BookListService::Post(const std::string& request_content,
|
||||
}
|
||||
|
||||
Book book;
|
||||
if (BookFromJson(request_content, &book)) {
|
||||
return g_book_store.AddBook(book);
|
||||
if (JsonToBook(request_content, &book)) {
|
||||
g_book_store.AddBook(book); // TODO: return ID
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -168,7 +97,7 @@ bool BookDetailService::Get(const std::vector<std::string>& url_sub_matches,
|
||||
const Book& book = g_book_store.GetBook(book_id);
|
||||
if (!book.IsNull()) {
|
||||
Json::StreamWriterBuilder builder;
|
||||
*response_content = Json::writeString(builder, book.ToJson());
|
||||
*response_content = Json::writeString(builder, BookToJson(book));
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -191,7 +120,7 @@ bool BookDetailService::Put(const std::vector<std::string>& url_sub_matches,
|
||||
const std::string& book_id = url_sub_matches[0];
|
||||
|
||||
Book book;
|
||||
if (BookFromJson(request_content, &book)) {
|
||||
if (JsonToBook(request_content, &book)) {
|
||||
book.id = book_id;
|
||||
return g_book_store.UpdateBook(book);
|
||||
}
|
||||
@@ -1,5 +1,8 @@
|
||||
#ifndef BOOK_SERVICES_H_
|
||||
#define BOOK_SERVICES_H_
|
||||
#ifndef EXAMPLE_REST_BOOK_SERVER_SERVICES_H_
|
||||
#define EXAMPLE_REST_BOOK_SERVER_SERVICES_H_
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "webcc/rest_service.h"
|
||||
|
||||
@@ -13,7 +16,7 @@
|
||||
// The query parameters could be regular expressions.
|
||||
class BookListService : public webcc::RestListService {
|
||||
public:
|
||||
BookListService(int sleep_seconds) : sleep_seconds_(sleep_seconds) {
|
||||
explicit BookListService(int sleep_seconds) : sleep_seconds_(sleep_seconds) {
|
||||
}
|
||||
|
||||
protected:
|
||||
@@ -39,7 +42,8 @@ class BookListService : public webcc::RestListService {
|
||||
// contains the matched book ID.
|
||||
class BookDetailService : public webcc::RestDetailService {
|
||||
public:
|
||||
BookDetailService(int sleep_seconds) : sleep_seconds_(sleep_seconds) {
|
||||
explicit BookDetailService(int sleep_seconds)
|
||||
: sleep_seconds_(sleep_seconds) {
|
||||
}
|
||||
|
||||
protected:
|
||||
@@ -58,4 +62,4 @@ class BookDetailService : public webcc::RestDetailService {
|
||||
int sleep_seconds_ = 0;
|
||||
};
|
||||
|
||||
#endif // BOOK_SERVICE_H_
|
||||
#endif // EXAMPLE_REST_BOOK_SERVER_SERVICES_H_
|
||||
@@ -0,0 +1,15 @@
|
||||
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} webcc pugixml ${Boost_LIBRARIES})
|
||||
target_link_libraries(${TARGET_NAME} "${CMAKE_THREAD_LIBS_INIT}")
|
||||
@@ -0,0 +1,136 @@
|
||||
#include "example/soap_book_client/book_client.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "webcc/logger.h"
|
||||
|
||||
#include "example/common/book_xml.h"
|
||||
|
||||
static void PrintSeparateLine() {
|
||||
std::cout << "--------------------------------";
|
||||
std::cout << "--------------------------------";
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
BookClient::BookClient(const std::string& host, const std::string& port)
|
||||
: webcc::SoapClient(host, port), code_(0) {
|
||||
url_ = "/book";
|
||||
service_ns_ = { "ser", "http://www.example.com/book/" };
|
||||
result_name_ = "Result";
|
||||
|
||||
// Customize response XML format.
|
||||
format_raw_ = false;
|
||||
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 CallX(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 CallX(operation, std::move(parameters), result_str);
|
||||
}
|
||||
|
||||
bool BookClient::CallX(const std::string& operation,
|
||||
std::vector<webcc::SoapParameter>&& parameters,
|
||||
std::string* result_str) {
|
||||
webcc::Error error = webcc::SoapClient::Call(operation,
|
||||
std::move(parameters),
|
||||
result_str);
|
||||
|
||||
if (error != webcc::kNoError) {
|
||||
LOG_ERRO("Operation '%s' failed: %s",
|
||||
operation, webcc::DescribeError(error));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
#ifndef EXAMPLE_SOAP_BOOK_CLIENT_H_
|
||||
#define EXAMPLE_SOAP_BOOK_CLIENT_H_
|
||||
|
||||
#include <functional>
|
||||
#include <string>
|
||||
|
||||
#include "webcc/soap_client.h"
|
||||
|
||||
#include "example/common/book.h"
|
||||
|
||||
class BookClient : public webcc::SoapClient {
|
||||
public:
|
||||
BookClient(const std::string& host, const std::string& port);
|
||||
|
||||
~BookClient() override = default;
|
||||
|
||||
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 webcc::SoapClient::Call() to log error if any.
|
||||
bool CallX(const std::string& operation,
|
||||
std::vector<webcc::SoapParameter>&& parameters,
|
||||
std::string* result_str);
|
||||
|
||||
bool ParseResultXml(const std::string& result_xml,
|
||||
std::function<bool(pugi::xml_node)> callback);
|
||||
|
||||
// Last status.
|
||||
int code_;
|
||||
std::string message_;
|
||||
};
|
||||
|
||||
#endif // EXAMPLE_SOAP_BOOK_CLIENT_H_
|
||||
@@ -0,0 +1,65 @@
|
||||
#include <iostream>
|
||||
|
||||
#include "webcc/logger.h"
|
||||
|
||||
#include "example/soap_book_client/book_client.h"
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
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}")
|
||||
@@ -0,0 +1,220 @@
|
||||
#include "example/soap_book_server/book_service.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <list>
|
||||
#include <sstream>
|
||||
|
||||
#include "webcc/logger.h"
|
||||
#include "webcc/soap_request.h"
|
||||
#include "webcc/soap_response.h"
|
||||
|
||||
#include "example/common/book.h"
|
||||
#include "example/common/book_xml.h"
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
static BookStore g_book_store;
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
bool BookService::Handle(const webcc::SoapRequest& soap_request,
|
||||
webcc::SoapResponse* soap_response) {
|
||||
const std::string& operation = soap_request.operation();
|
||||
|
||||
soap_response->set_soapenv_ns(webcc::kSoapEnvNamespace);
|
||||
soap_response->set_service_ns({
|
||||
"ser",
|
||||
"http://www.example.com/book/"
|
||||
});
|
||||
|
||||
soap_response->set_operation(operation);
|
||||
soap_response->set_result_name("Result");
|
||||
|
||||
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_result_moved(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_result_moved(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_result_moved(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_result_moved(NewResultXml(0, "ok"), true);
|
||||
} else {
|
||||
soap_response->set_result_moved(NewResultXml(1, "error"), true);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
#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_
|
||||
@@ -0,0 +1,40 @@
|
||||
#include <iostream>
|
||||
|
||||
#include "webcc/logger.h"
|
||||
#include "webcc/soap_server.h"
|
||||
|
||||
#include "example/soap_book_server/book_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<BookService>(), "/book");
|
||||
server.Run();
|
||||
} catch (const std::exception& e) {
|
||||
std::cerr << "Exception: " << e.what() << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
set(SRCS calc_client.cc calc_client.h main.cc)
|
||||
set(TARGET_NAME soap_calc_client)
|
||||
|
||||
add_executable(soap_calc_client ${SRCS})
|
||||
|
||||
target_link_libraries(soap_calc_client webcc pugixml ${Boost_LIBRARIES})
|
||||
target_link_libraries(soap_calc_client "${CMAKE_THREAD_LIBS_INIT}")
|
||||
add_executable(${TARGET_NAME} main.cc)
|
||||
|
||||
target_link_libraries(${TARGET_NAME} webcc pugixml ${Boost_LIBRARIES})
|
||||
target_link_libraries(${TARGET_NAME} "${CMAKE_THREAD_LIBS_INIT}")
|
||||
|
||||
@@ -1,89 +0,0 @@
|
||||
#include "example/soap_calc_client/calc_client.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "webcc/logger.h"
|
||||
|
||||
// Set to 0 to test our own calculator server created with webcc.
|
||||
#define ACCESS_PARASOFT 0
|
||||
|
||||
CalcClient::CalcClient() {
|
||||
Init();
|
||||
}
|
||||
|
||||
bool CalcClient::Add(double x, double y, double* result) {
|
||||
return Calc("add", "x", "y", x, y, result);
|
||||
}
|
||||
|
||||
bool CalcClient::Subtract(double x, double y, double* result) {
|
||||
return Calc("subtract", "x", "y", x, y, result);
|
||||
}
|
||||
|
||||
bool CalcClient::Multiply(double x, double y, double* result) {
|
||||
return Calc("multiply", "x", "y", x, y, result);
|
||||
}
|
||||
|
||||
bool CalcClient::Divide(double x, double y, double* result) {
|
||||
// ParaSoft's Calculator Service uses different parameter names for Divide.
|
||||
#if ACCESS_PARASOFT
|
||||
return Calc("divide", "numerator", "denominator", x, y, result);
|
||||
#else
|
||||
return Calc("divide", "x", "y", x, y, result);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool CalcClient::NotExist(double x, double y, double* result) {
|
||||
return Calc("not_exist", "x", "y", x, y, result);
|
||||
}
|
||||
|
||||
void CalcClient::Init() {
|
||||
// Override the default timeout.
|
||||
timeout_seconds_ = 5;
|
||||
|
||||
#if ACCESS_PARASOFT
|
||||
url_ = "/glue/calculator";
|
||||
host_ = "ws1.parasoft.com";
|
||||
port_ = ""; // Default to "80".
|
||||
service_ns_ = { "cal", "http://www.parasoft.com/wsdl/calculator/" };
|
||||
result_name_ = "Result";
|
||||
#else
|
||||
url_ = "/calculator";
|
||||
host_ = "localhost";
|
||||
port_ = "8080";
|
||||
service_ns_ = { "ser", "http://www.example.com/calculator/" };
|
||||
result_name_ = "Result";
|
||||
#endif
|
||||
}
|
||||
|
||||
bool CalcClient::Calc(const std::string& operation,
|
||||
const std::string& x_name,
|
||||
const std::string& y_name,
|
||||
double x,
|
||||
double y,
|
||||
double* result) {
|
||||
// Prepare parameters.
|
||||
std::vector<webcc::Parameter> parameters{
|
||||
{ x_name, x },
|
||||
{ y_name, y }
|
||||
};
|
||||
|
||||
// Make the call.
|
||||
std::string result_str;
|
||||
webcc::Error error = Call(operation, std::move(parameters), &result_str);
|
||||
|
||||
// Error handling if any.
|
||||
if (error != webcc::kNoError) {
|
||||
LOG_ERRO("Operation '%s' failed: %s", operation.c_str(),
|
||||
webcc::DescribeError(error));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Convert the result from string to double.
|
||||
try {
|
||||
*result = std::stod(result_str);
|
||||
} catch (const std::exception&) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
#ifndef CALC_CLIENT_H_
|
||||
#define CALC_CLIENT_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "webcc/soap_client.h"
|
||||
|
||||
class CalcClient : public webcc::SoapClient {
|
||||
public:
|
||||
CalcClient();
|
||||
|
||||
bool Add(double x, double y, double* result);
|
||||
|
||||
bool Subtract(double x, double y, double* result);
|
||||
|
||||
bool Multiply(double x, double y, double* result);
|
||||
|
||||
bool Divide(double x, double y, double* result);
|
||||
|
||||
// For testing purpose.
|
||||
bool NotExist(double x, double y, double* result);
|
||||
|
||||
protected:
|
||||
void Init();
|
||||
|
||||
// A more concrete wrapper to make a call.
|
||||
bool Calc(const std::string& operation,
|
||||
const std::string& x_name,
|
||||
const std::string& y_name,
|
||||
double x,
|
||||
double y,
|
||||
double* result);
|
||||
};
|
||||
|
||||
#endif // CALC_CLIENT_H_
|
||||
@@ -1,13 +1,95 @@
|
||||
#include <iostream>
|
||||
|
||||
#include "webcc/logger.h"
|
||||
#include "webcc/soap_client.h"
|
||||
|
||||
#include "example/soap_calc_client/calc_client.h"
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
class CalcClient : public webcc::SoapClient {
|
||||
public:
|
||||
CalcClient(const std::string& host, const std::string& port)
|
||||
: webcc::SoapClient(host, port) {
|
||||
timeout_seconds_ = 5; // Override the default timeout.
|
||||
|
||||
url_ = "/calculator";
|
||||
service_ns_ = { "ser", "http://www.example.com/calculator/" };
|
||||
result_name_ = "Result";
|
||||
|
||||
// Customize request XML format.
|
||||
format_raw_ = false;
|
||||
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);
|
||||
}
|
||||
|
||||
protected:
|
||||
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;
|
||||
webcc::Error error = Call(operation, std::move(parameters), &result_str);
|
||||
|
||||
if (error != webcc::kNoError) {
|
||||
LOG_ERRO("Operation '%s' failed: %s", operation.c_str(),
|
||||
webcc::DescribeError(error));
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
*result = std::stod(result_str);
|
||||
} catch (const std::exception&) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
int main() {
|
||||
WEBCC_LOG_INIT("", webcc::LOG_CONSOLE);
|
||||
|
||||
CalcClient calc;
|
||||
std::string host = argv[1];
|
||||
std::string port = argv[2];
|
||||
|
||||
CalcClient calc(host, port);
|
||||
|
||||
double x = 1.0;
|
||||
double y = 2.0;
|
||||
@@ -29,10 +111,7 @@ int main() {
|
||||
printf("divide: %.1f\n", result);
|
||||
}
|
||||
|
||||
// Try to call a non-existing operation.
|
||||
if (calc.NotExist(x, y, &result)) {
|
||||
printf("not_exist: %.1f\n", result);
|
||||
}
|
||||
calc.Unknown(x, y, &result);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
set(TARGET_NAME soap_calc_client_parasoft)
|
||||
|
||||
add_executable(${TARGET_NAME} main.cc)
|
||||
|
||||
target_link_libraries(${TARGET_NAME} webcc pugixml ${Boost_LIBRARIES})
|
||||
target_link_libraries(${TARGET_NAME} "${CMAKE_THREAD_LIBS_INIT}")
|
||||
@@ -0,0 +1,97 @@
|
||||
#include <iostream>
|
||||
|
||||
#include "webcc/logger.h"
|
||||
#include "webcc/soap_client.h"
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
class CalcClient : public webcc::SoapClient {
|
||||
public:
|
||||
CalcClient(const std::string& host, const std::string& port)
|
||||
: webcc::SoapClient(host, port) {
|
||||
timeout_seconds_ = 5; // Override the default timeout.
|
||||
|
||||
url_ = "/glue/calculator";
|
||||
service_ns_ = { "cal", "http://www.parasoft.com/wsdl/calculator/" };
|
||||
result_name_ = "Result";
|
||||
|
||||
// Customize request XML format.
|
||||
format_raw_ = false;
|
||||
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);
|
||||
}
|
||||
|
||||
protected:
|
||||
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;
|
||||
webcc::Error error = Call(operation, std::move(parameters), &result_str);
|
||||
|
||||
if (error != webcc::kNoError) {
|
||||
LOG_ERRO("Operation '%s' failed: %s", operation.c_str(),
|
||||
webcc::DescribeError(error));
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
*result = std::stod(result_str);
|
||||
} catch (const std::exception&) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
int main() {
|
||||
WEBCC_LOG_INIT("", webcc::LOG_CONSOLE);
|
||||
|
||||
// Default port 80.
|
||||
CalcClient calc("ws1.parasoft.com", "");
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -3,21 +3,10 @@
|
||||
#include <functional>
|
||||
#include <string>
|
||||
|
||||
// Sleep several seconds for the client to test timeout control.
|
||||
#define SLEEP_FOR_TIMEOUT_TEST 0
|
||||
|
||||
#if SLEEP_FOR_TIMEOUT_TEST
|
||||
#include "boost/thread/thread.hpp"
|
||||
#endif
|
||||
|
||||
#include "webcc/logger.h"
|
||||
#include "webcc/soap_request.h"
|
||||
#include "webcc/soap_response.h"
|
||||
|
||||
#if SLEEP_FOR_TIMEOUT_TEST
|
||||
static const int kSleepSeconds = 3;
|
||||
#endif
|
||||
|
||||
bool CalcService::Handle(const webcc::SoapRequest& soap_request,
|
||||
webcc::SoapResponse* soap_response) {
|
||||
double x = 0.0;
|
||||
@@ -26,22 +15,22 @@ bool CalcService::Handle(const webcc::SoapRequest& soap_request,
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::string& op = soap_request.operation();
|
||||
const std::string& operation = soap_request.operation();
|
||||
|
||||
LOG_INFO("Soap operation '%s': %.2f, %.2f", op.c_str(), x, y);
|
||||
LOG_INFO("Soap operation '%s': %.2f, %.2f", operation.c_str(), x, y);
|
||||
|
||||
std::function<double(double, double)> calc;
|
||||
|
||||
if (op == "add") {
|
||||
if (operation == "add") {
|
||||
calc = [](double x, double y) { return x + y; };
|
||||
|
||||
} else if (op == "subtract") {
|
||||
} else if (operation == "subtract") {
|
||||
calc = [](double x, double y) { return x - y; };
|
||||
|
||||
} else if (op == "multiply") {
|
||||
} else if (operation == "multiply") {
|
||||
calc = [](double x, double y) { return x * y; };
|
||||
|
||||
} else if (op == "divide") {
|
||||
} else if (operation == "divide") {
|
||||
calc = [](double x, double y) { return x / y; };
|
||||
|
||||
if (y == 0.0) {
|
||||
@@ -49,7 +38,7 @@ bool CalcService::Handle(const webcc::SoapRequest& soap_request,
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
LOG_ERRO("Operation '%s' is not supported.", op.c_str());
|
||||
LOG_ERRO("Operation '%s' is not supported.", operation.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -68,13 +57,7 @@ bool CalcService::Handle(const webcc::SoapRequest& soap_request,
|
||||
soap_response->set_operation(soap_request.operation());
|
||||
|
||||
soap_response->set_result_name("Result");
|
||||
soap_response->set_result(std::to_string(result));
|
||||
|
||||
#if SLEEP_FOR_TIMEOUT_TEST
|
||||
LOG_INFO("Sleep %d seconds for the client to test timeout control.",
|
||||
kSleepSeconds);
|
||||
boost::this_thread::sleep_for(boost::chrono::seconds(kSleepSeconds));
|
||||
#endif
|
||||
soap_response->set_result_moved(std::to_string(result), false);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -85,7 +68,7 @@ bool CalcService::GetParameters(const webcc::SoapRequest& soap_request,
|
||||
*x = std::stod(soap_request.GetParameter("x"));
|
||||
*y = std::stod(soap_request.GetParameter("y"));
|
||||
} catch (const std::exception& e) {
|
||||
LOG_ERRO("Parameter cast error: %s", e.what());
|
||||
LOG_ERRO("SoapParameter cast error: %s", e.what());
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -4,14 +4,14 @@
|
||||
#include "webcc/soap_service.h"
|
||||
|
||||
class CalcService : public webcc::SoapService {
|
||||
public:
|
||||
public:
|
||||
CalcService() = default;
|
||||
~CalcService() override = default;
|
||||
|
||||
bool Handle(const webcc::SoapRequest& soap_request,
|
||||
webcc::SoapResponse* soap_response) override;
|
||||
|
||||
private:
|
||||
private:
|
||||
bool GetParameters(const webcc::SoapRequest& soap_request,
|
||||
double* x, double* y);
|
||||
};
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
#include "example/soap_calc_server/calc_service.h"
|
||||
|
||||
static void Help(const char* argv0) {
|
||||
void Help(const char* argv0) {
|
||||
std::cout << "Usage: " << argv0 << " <port>" << std::endl;
|
||||
std::cout << " E.g.," << std::endl;
|
||||
std::cout << " " << argv0 << " 8080" << std::endl;
|
||||
@@ -24,6 +24,11 @@ int main(int argc, char* argv[]) {
|
||||
|
||||
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) {
|
||||
|
||||
Reference in New Issue
Block a user