Remove SOAP support
parent
b72820c9c5
commit
e63dc748ab
@ -0,0 +1,54 @@
|
|||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
#include "webcc/http_client_session.h"
|
||||||
|
#include "webcc/logger.h"
|
||||||
|
|
||||||
|
#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
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
WEBCC_LOG_INIT("", webcc::LOG_CONSOLE);
|
||||||
|
|
||||||
|
webcc::HttpClientSession session;
|
||||||
|
|
||||||
|
session.set_ssl_verify(kSslVerify);
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Using request builder:
|
||||||
|
auto r = session.Request(webcc::HttpRequestBuilder{}.Get().
|
||||||
|
Url("http://httpbin.org/get").
|
||||||
|
Query("key1", "value1").
|
||||||
|
Query("key2", "value2").
|
||||||
|
Header("Accept", "application/json")
|
||||||
|
());
|
||||||
|
|
||||||
|
std::cout << r->content() << std::endl;
|
||||||
|
|
||||||
|
// Using shortcut:
|
||||||
|
r = session.Get("http://httpbin.org/get",
|
||||||
|
{ "key1", "value1", "key2", "value2" },
|
||||||
|
{ "Accept", "application/json" });
|
||||||
|
|
||||||
|
std::cout << r->content() << std::endl;
|
||||||
|
|
||||||
|
#if WEBCC_ENABLE_SSL
|
||||||
|
|
||||||
|
// HTTPS support.
|
||||||
|
|
||||||
|
r = session.Get("https://httpbin.org/get");
|
||||||
|
|
||||||
|
std::cout << r->content() << std::endl;
|
||||||
|
|
||||||
|
#endif // WEBCC_ENABLE_SSL
|
||||||
|
|
||||||
|
} catch (const webcc::Exception& e) {
|
||||||
|
std::cout << "Exception: " << e.what() << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
@ -1,147 +0,0 @@
|
|||||||
#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));
|
|
||||||
}
|
|
@ -1,111 +0,0 @@
|
|||||||
#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,68 +0,0 @@
|
|||||||
#include <iostream>
|
|
||||||
#include <fstream>
|
|
||||||
|
|
||||||
#include "webcc/http_client_session.h"
|
|
||||||
#include "webcc/logger.h"
|
|
||||||
|
|
||||||
#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() {
|
|
||||||
webcc::HttpClientSession session;
|
|
||||||
|
|
||||||
auto r = session.Request(webcc::HttpRequestBuilder{}.Get().
|
|
||||||
Url("http://httpbin.org/get").
|
|
||||||
Query("key1", "value1").
|
|
||||||
Query("key2", "value2").
|
|
||||||
Header("Accept", "application/json")
|
|
||||||
());
|
|
||||||
|
|
||||||
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) {
|
|
||||||
webcc::HttpClientSession session;
|
|
||||||
session.set_ssl_verify(kSslVerify);
|
|
||||||
|
|
||||||
// Keep-Alive
|
|
||||||
session.Get(url);
|
|
||||||
|
|
||||||
// Close
|
|
||||||
session.Get(url, {}, {"Connection", "Close"});
|
|
||||||
|
|
||||||
// Keep-Alive
|
|
||||||
session.Get(url);
|
|
||||||
}
|
|
||||||
|
|
||||||
int main() {
|
|
||||||
WEBCC_LOG_INIT("", webcc::LOG_CONSOLE);
|
|
||||||
|
|
||||||
webcc::HttpClientSession session;
|
|
||||||
|
|
||||||
try {
|
|
||||||
|
|
||||||
ExampleBasic();
|
|
||||||
|
|
||||||
} catch (const webcc::Exception& e) {
|
|
||||||
std::cout << "Exception: " << e.what() << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
@ -1,263 +0,0 @@
|
|||||||
#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;
|
|
||||||
}
|
|
@ -1,285 +0,0 @@
|
|||||||
#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;
|
|
||||||
}
|
|
@ -1,128 +0,0 @@
|
|||||||
#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;
|
|
||||||
}
|
|
@ -1,119 +0,0 @@
|
|||||||
#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;
|
|
||||||
}
|
|
@ -1,112 +0,0 @@
|
|||||||
#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;
|
|
||||||
}
|
|
@ -1,15 +0,0 @@
|
|||||||
#include "webcc/logger.h"
|
|
||||||
|
|
||||||
int main() {
|
|
||||||
WEBCC_LOG_INIT("", webcc::LOG_CONSOLE);
|
|
||||||
|
|
||||||
LOG_INFO("info");
|
|
||||||
LOG_WARN("warn");
|
|
||||||
LOG_ERRO("erro");
|
|
||||||
LOG_VERB("verb");
|
|
||||||
LOG_FATA("fata");
|
|
||||||
LOG_INFO("info");
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
@ -1,6 +0,0 @@
|
|||||||
project(pugixml)
|
|
||||||
|
|
||||||
set(HEADERS pugixml.hpp pugiconfig.hpp)
|
|
||||||
set(SOURCES ${HEADERS} pugixml.cpp)
|
|
||||||
|
|
||||||
add_library(pugixml STATIC ${SOURCES})
|
|
@ -1,74 +0,0 @@
|
|||||||
/**
|
|
||||||
* pugixml parser - version 1.8
|
|
||||||
* --------------------------------------------------------
|
|
||||||
* Copyright (C) 2006-2016, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com)
|
|
||||||
* Report bugs and download new versions at http://pugixml.org/
|
|
||||||
*
|
|
||||||
* This library is distributed under the MIT License. See notice at the end
|
|
||||||
* of this file.
|
|
||||||
*
|
|
||||||
* This work is based on the pugxml parser, which is:
|
|
||||||
* Copyright (C) 2003, by Kristen Wegner (kristen@tima.net)
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef HEADER_PUGICONFIG_HPP
|
|
||||||
#define HEADER_PUGICONFIG_HPP
|
|
||||||
|
|
||||||
// Uncomment this to enable wchar_t mode
|
|
||||||
// #define PUGIXML_WCHAR_MODE
|
|
||||||
|
|
||||||
// Uncomment this to enable compact mode
|
|
||||||
// #define PUGIXML_COMPACT
|
|
||||||
|
|
||||||
// Uncomment this to disable XPath
|
|
||||||
// #define PUGIXML_NO_XPATH
|
|
||||||
|
|
||||||
// Uncomment this to disable STL
|
|
||||||
// #define PUGIXML_NO_STL
|
|
||||||
|
|
||||||
// Uncomment this to disable exceptions
|
|
||||||
// #define PUGIXML_NO_EXCEPTIONS
|
|
||||||
|
|
||||||
// Set this to control attributes for public classes/functions, i.e.:
|
|
||||||
// #define PUGIXML_API __declspec(dllexport) // to export all public symbols from DLL
|
|
||||||
// #define PUGIXML_CLASS __declspec(dllimport) // to import all classes from DLL
|
|
||||||
// #define PUGIXML_FUNCTION __fastcall // to set calling conventions to all public functions to fastcall
|
|
||||||
// In absence of PUGIXML_CLASS/PUGIXML_FUNCTION definitions PUGIXML_API is used instead
|
|
||||||
|
|
||||||
// Tune these constants to adjust memory-related behavior
|
|
||||||
// #define PUGIXML_MEMORY_PAGE_SIZE 32768
|
|
||||||
// #define PUGIXML_MEMORY_OUTPUT_STACK 10240
|
|
||||||
// #define PUGIXML_MEMORY_XPATH_PAGE_SIZE 4096
|
|
||||||
|
|
||||||
// Uncomment this to switch to header-only version
|
|
||||||
// #define PUGIXML_HEADER_ONLY
|
|
||||||
|
|
||||||
// Uncomment this to enable long long support
|
|
||||||
// #define PUGIXML_HAS_LONG_LONG
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Copyright (c) 2006-2016 Arseny Kapoulkine
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person
|
|
||||||
* obtaining a copy of this software and associated documentation
|
|
||||||
* files (the "Software"), to deal in the Software without
|
|
||||||
* restriction, including without limitation the rights to use,
|
|
||||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the
|
|
||||||
* Software is furnished to do so, subject to the following
|
|
||||||
* conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be
|
|
||||||
* included in all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
||||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
|
||||||
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
||||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|
||||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|
||||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
||||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
||||||
* OTHER DEALINGS IN THE SOFTWARE.
|
|
||||||
*/
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,112 +0,0 @@
|
|||||||
#include "webcc/soap_client.h"
|
|
||||||
|
|
||||||
#include <cassert>
|
|
||||||
#include <utility> // for move()
|
|
||||||
|
|
||||||
#include "boost/algorithm/string.hpp"
|
|
||||||
|
|
||||||
#include "webcc/soap_request.h"
|
|
||||||
#include "webcc/utility.h"
|
|
||||||
|
|
||||||
namespace webcc {
|
|
||||||
|
|
||||||
SoapClient::SoapClient(const std::string& url, SoapVersion soap_version)
|
|
||||||
: url_(url),
|
|
||||||
soap_version_(soap_version),
|
|
||||||
format_raw_(true),
|
|
||||||
error_(kNoError) {
|
|
||||||
}
|
|
||||||
|
|
||||||
bool SoapClient::Request(const std::string& operation,
|
|
||||||
std::vector<SoapParameter>&& parameters,
|
|
||||||
SoapResponse::Parser parser,
|
|
||||||
std::size_t buffer_size) {
|
|
||||||
assert(service_ns_.IsValid());
|
|
||||||
assert(!url_.empty());
|
|
||||||
|
|
||||||
error_ = kNoError;
|
|
||||||
fault_.reset();
|
|
||||||
|
|
||||||
SoapRequest soap_request;
|
|
||||||
|
|
||||||
// Set SOAP envelope namespace according to SOAP version.
|
|
||||||
if (soap_version_ == kSoapV11) {
|
|
||||||
soap_request.set_soapenv_ns(kSoapEnvNamespaceV11);
|
|
||||||
} else {
|
|
||||||
soap_request.set_soapenv_ns(kSoapEnvNamespaceV12);
|
|
||||||
}
|
|
||||||
|
|
||||||
soap_request.set_service_ns(service_ns_);
|
|
||||||
|
|
||||||
soap_request.set_operation(operation);
|
|
||||||
|
|
||||||
for (SoapParameter& p : parameters) {
|
|
||||||
soap_request.AddParameter(std::move(p));
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string http_content;
|
|
||||||
soap_request.ToXml(format_raw_, indent_str_, &http_content);
|
|
||||||
|
|
||||||
auto http_request = std::make_shared<HttpRequest>(http::methods::kPost, url_);
|
|
||||||
|
|
||||||
http_request->SetContent(std::move(http_content), true);
|
|
||||||
|
|
||||||
// NOTE:
|
|
||||||
// According to www.w3.org when placing SOAP messages in HTTP bodies, the HTTP
|
|
||||||
// Content-type header must be chosen as "application/soap+xml" [RFC 3902].
|
|
||||||
// But in practice, many web servers cannot understand it.
|
|
||||||
// See: https://www.w3.org/TR/2007/REC-soap12-part0-20070427/#L26854
|
|
||||||
|
|
||||||
if (soap_version_ == kSoapV11) {
|
|
||||||
http_request->SetContentType(http::media_types::kTextXml,
|
|
||||||
http::charsets::kUtf8);
|
|
||||||
} else {
|
|
||||||
http_request->SetContentType(http::media_types::kApplicationSoapXml,
|
|
||||||
http::charsets::kUtf8);
|
|
||||||
}
|
|
||||||
|
|
||||||
http_request->SetHeader(kSoapAction, operation);
|
|
||||||
|
|
||||||
http_request->Prepare();
|
|
||||||
|
|
||||||
http_client_.set_buffer_size(buffer_size);
|
|
||||||
|
|
||||||
if (!http_client_.Request(http_request, true)) {
|
|
||||||
error_ = http_client_.error();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
SoapResponse soap_response;
|
|
||||||
soap_response.set_operation(operation);
|
|
||||||
soap_response.set_parser(parser);
|
|
||||||
|
|
||||||
if (!soap_response.FromXml(http_client_.response()->content())) {
|
|
||||||
if (soap_response.fault()) {
|
|
||||||
fault_ = soap_response.fault();
|
|
||||||
error_ = kServerError;
|
|
||||||
} else {
|
|
||||||
error_ = kXmlError;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool SoapClient::Request(const std::string& operation,
|
|
||||||
std::vector<SoapParameter>&& parameters,
|
|
||||||
const std::string& result_name,
|
|
||||||
std::size_t buffer_size,
|
|
||||||
std::string* result) {
|
|
||||||
auto parser = [result, &result_name](pugi::xml_node xnode) {
|
|
||||||
if (boost::iequals(soap_xml::GetNameNoPrefix(xnode), result_name)) {
|
|
||||||
soap_xml::GetText(xnode, result);
|
|
||||||
}
|
|
||||||
return false; // Stop next call.
|
|
||||||
};
|
|
||||||
|
|
||||||
return Request(operation, std::move(parameters), parser, buffer_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace webcc
|
|
@ -1,92 +0,0 @@
|
|||||||
#ifndef WEBCC_SOAP_CLIENT_H_
|
|
||||||
#define WEBCC_SOAP_CLIENT_H_
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "webcc/http_client.h"
|
|
||||||
#include "webcc/soap_globals.h"
|
|
||||||
#include "webcc/soap_parameter.h"
|
|
||||||
#include "webcc/soap_response.h"
|
|
||||||
|
|
||||||
namespace webcc {
|
|
||||||
|
|
||||||
class SoapClient {
|
|
||||||
public:
|
|
||||||
explicit SoapClient(const std::string& url,
|
|
||||||
SoapVersion soap_version = kSoapV12);
|
|
||||||
|
|
||||||
~SoapClient() = default;
|
|
||||||
|
|
||||||
SoapClient(const SoapClient&) = delete;
|
|
||||||
SoapClient& operator=(const SoapClient&) = delete;
|
|
||||||
|
|
||||||
void SetTimeout(int seconds) {
|
|
||||||
http_client_.set_timeout(seconds);
|
|
||||||
}
|
|
||||||
|
|
||||||
void set_service_ns(const SoapNamespace& service_ns) {
|
|
||||||
service_ns_ = service_ns;
|
|
||||||
}
|
|
||||||
|
|
||||||
void set_format_raw(bool format_raw) {
|
|
||||||
format_raw_ = format_raw;
|
|
||||||
}
|
|
||||||
|
|
||||||
void set_indent_str(const std::string& indent_str) {
|
|
||||||
indent_str_ = indent_str;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Request(const std::string& operation,
|
|
||||||
std::vector<SoapParameter>&& parameters,
|
|
||||||
SoapResponse::Parser parser,
|
|
||||||
std::size_t buffer_size = 0);
|
|
||||||
|
|
||||||
// Shortcut for responses with single result node.
|
|
||||||
// The name of the single result node is specified by |result_name|.
|
|
||||||
// The text of the result node will be set to |result|.
|
|
||||||
bool Request(const std::string& operation,
|
|
||||||
std::vector<SoapParameter>&& parameters,
|
|
||||||
const std::string& result_name,
|
|
||||||
std::size_t buffer_size, // Pass 0 for using default size.
|
|
||||||
std::string* result);
|
|
||||||
|
|
||||||
// HTTP status code (200, 500, etc.) in the response.
|
|
||||||
int http_status() const {
|
|
||||||
assert(http_client_.response());
|
|
||||||
return http_client_.response()->status();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool timed_out() const {
|
|
||||||
return http_client_.timed_out();
|
|
||||||
}
|
|
||||||
|
|
||||||
Error error() const { return error_; }
|
|
||||||
|
|
||||||
std::shared_ptr<SoapFault> fault() const { return fault_; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::string url_;
|
|
||||||
|
|
||||||
SoapVersion soap_version_;
|
|
||||||
|
|
||||||
HttpClient http_client_;
|
|
||||||
|
|
||||||
// Namespace for your web service.
|
|
||||||
SoapNamespace service_ns_;
|
|
||||||
|
|
||||||
// Format request XML without any indentation or line breaks.
|
|
||||||
bool format_raw_;
|
|
||||||
|
|
||||||
// Indent string for request XML.
|
|
||||||
// Applicable when |format_raw_| is false.
|
|
||||||
std::string indent_str_;
|
|
||||||
|
|
||||||
Error error_;
|
|
||||||
|
|
||||||
std::shared_ptr<SoapFault> fault_;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace webcc
|
|
||||||
|
|
||||||
#endif // WEBCC_SOAP_CLIENT_H_
|
|
@ -1,44 +0,0 @@
|
|||||||
#include "webcc/soap_globals.h"
|
|
||||||
|
|
||||||
#include <ostream>
|
|
||||||
|
|
||||||
namespace webcc {
|
|
||||||
|
|
||||||
const std::string kSoapAction = "SOAPAction";
|
|
||||||
|
|
||||||
// According to www.w3.org when placing SOAP messages in HTTP bodies, the HTTP
|
|
||||||
// Content-type header must be chosen as "application/soap+xml" [RFC 3902].
|
|
||||||
// But in practice, many web servers cannot understand it.
|
|
||||||
// See: https://www.w3.org/TR/2007/REC-soap12-part0-20070427/#L26854
|
|
||||||
const std::string kTextXmlUtf8 = "text/xml; charset=utf-8";
|
|
||||||
const std::string kAppSoapXmlUtf8 = "application/soap+xml; charset=utf-8";
|
|
||||||
|
|
||||||
// NOTE:
|
|
||||||
// According to HTTP 1.1 RFC7231, the following examples are all equivalent,
|
|
||||||
// but the first is preferred for consistency:
|
|
||||||
// text/html;charset=utf-8
|
|
||||||
// text/html;charset=UTF-8
|
|
||||||
// Text/HTML;Charset="utf-8"
|
|
||||||
// text/html; charset="utf-8"
|
|
||||||
// See: https://tools.ietf.org/html/rfc7231#section-3.1.1.1
|
|
||||||
|
|
||||||
const SoapNamespace kSoapEnvNamespaceV11{
|
|
||||||
"soap",
|
|
||||||
"http://schemas.xmlsoap.org/soap/envelope/"
|
|
||||||
};
|
|
||||||
|
|
||||||
const SoapNamespace kSoapEnvNamespaceV12{
|
|
||||||
"soap",
|
|
||||||
"http://www.w3.org/2003/05/soap-envelope"
|
|
||||||
};
|
|
||||||
|
|
||||||
std::ostream& operator<<(std::ostream& os, const SoapFault& fault) {
|
|
||||||
os << "Fault: {" << std::endl
|
|
||||||
<< " faultcode: " << fault.faultcode << std::endl
|
|
||||||
<< " faultstring: " << fault.faultstring << std::endl
|
|
||||||
<< " detail: " << fault.detail << std::endl
|
|
||||||
<< "}";
|
|
||||||
return os;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace webcc
|
|
@ -1,49 +0,0 @@
|
|||||||
#ifndef WEBCC_SOAP_GLOBALS_H_
|
|
||||||
#define WEBCC_SOAP_GLOBALS_H_
|
|
||||||
|
|
||||||
#include <iosfwd>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
namespace webcc {
|
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
|
||||||
// Constants
|
|
||||||
|
|
||||||
extern const std::string kSoapAction;
|
|
||||||
|
|
||||||
extern const std::string kTextXmlUtf8;
|
|
||||||
extern const std::string kAppSoapXmlUtf8;
|
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
enum SoapVersion {
|
|
||||||
kSoapV11,
|
|
||||||
kSoapV12,
|
|
||||||
};
|
|
||||||
|
|
||||||
// XML namespace name/url pair.
|
|
||||||
// E.g., { "soap", "http://www.w3.org/2003/05/soap-envelope" }
|
|
||||||
struct SoapNamespace {
|
|
||||||
std::string name;
|
|
||||||
std::string url;
|
|
||||||
|
|
||||||
bool IsValid() const {
|
|
||||||
return !name.empty() && !url.empty();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Default namespaces for SOAP Envelope.
|
|
||||||
extern const SoapNamespace kSoapEnvNamespaceV11;
|
|
||||||
extern const SoapNamespace kSoapEnvNamespaceV12;
|
|
||||||
|
|
||||||
struct SoapFault {
|
|
||||||
std::string faultcode;
|
|
||||||
std::string faultstring;
|
|
||||||
std::string detail;
|
|
||||||
};
|
|
||||||
|
|
||||||
std::ostream& operator<<(std::ostream& os, const SoapFault& fault);
|
|
||||||
|
|
||||||
} // namespace webcc
|
|
||||||
|
|
||||||
#endif // WEBCC_SOAP_GLOBALS_H_
|
|
@ -1,52 +0,0 @@
|
|||||||
#include "webcc/soap_message.h"
|
|
||||||
|
|
||||||
#include <cassert>
|
|
||||||
|
|
||||||
#include "webcc/soap_xml.h"
|
|
||||||
|
|
||||||
namespace webcc {
|
|
||||||
|
|
||||||
void SoapMessage::ToXml(bool format_raw, const std::string& indent,
|
|
||||||
std::string* xml_string) {
|
|
||||||
assert(soapenv_ns_.IsValid() && service_ns_.IsValid() && !operation_.empty());
|
|
||||||
|
|
||||||
pugi::xml_document xdoc;
|
|
||||||
|
|
||||||
pugi::xml_node xroot = soap_xml::AddChild(xdoc, soapenv_ns_.name, "Envelope");
|
|
||||||
|
|
||||||
soap_xml::AddNSAttr(xroot, soapenv_ns_.name, soapenv_ns_.url);
|
|
||||||
|
|
||||||
pugi::xml_node xbody = soap_xml::AddChild(xroot, soapenv_ns_.name, "Body");
|
|
||||||
|
|
||||||
ToXmlBody(xbody);
|
|
||||||
|
|
||||||
soap_xml::XmlStringWriter writer(xml_string);
|
|
||||||
|
|
||||||
unsigned int flags = format_raw ? pugi::format_raw : pugi::format_indent;
|
|
||||||
|
|
||||||
// Use print() instead of save() for no declaration or BOM.
|
|
||||||
xdoc.print(writer, indent.c_str(), flags, pugi::encoding_utf8);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool SoapMessage::FromXml(const std::string& xml_string) {
|
|
||||||
pugi::xml_document xdoc;
|
|
||||||
pugi::xml_parse_result result = xdoc.load_string(xml_string.c_str());
|
|
||||||
|
|
||||||
if (!result) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
pugi::xml_node xroot = xdoc.document_element();
|
|
||||||
|
|
||||||
soapenv_ns_.name = soap_xml::GetPrefix(xroot);
|
|
||||||
soapenv_ns_.url = soap_xml::GetNSAttr(xroot, soapenv_ns_.name);
|
|
||||||
|
|
||||||
pugi::xml_node xbody = soap_xml::GetChild(xroot, soapenv_ns_.name, "Body");
|
|
||||||
if (xbody) {
|
|
||||||
return FromXmlBody(xbody);
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace webcc
|
|
@ -1,56 +0,0 @@
|
|||||||
#ifndef WEBCC_SOAP_MESSAGE_H_
|
|
||||||
#define WEBCC_SOAP_MESSAGE_H_
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
#include "pugixml/pugixml.hpp"
|
|
||||||
|
|
||||||
#include "webcc/soap_globals.h"
|
|
||||||
|
|
||||||
namespace webcc {
|
|
||||||
|
|
||||||
// Base class for SOAP request and response.
|
|
||||||
class SoapMessage {
|
|
||||||
public:
|
|
||||||
virtual ~SoapMessage() = default;
|
|
||||||
|
|
||||||
// E.g., set as kSoapEnvNamespace.
|
|
||||||
void set_soapenv_ns(const SoapNamespace& soapenv_ns) {
|
|
||||||
soapenv_ns_ = soapenv_ns;
|
|
||||||
}
|
|
||||||
|
|
||||||
void set_service_ns(const SoapNamespace& service_ns) {
|
|
||||||
service_ns_ = service_ns;
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::string& operation() const {
|
|
||||||
return operation_;
|
|
||||||
}
|
|
||||||
|
|
||||||
void set_operation(const std::string& operation) {
|
|
||||||
operation_ = operation;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert to SOAP XML.
|
|
||||||
void ToXml(bool format_raw, const std::string& indent,
|
|
||||||
std::string* xml_string);
|
|
||||||
|
|
||||||
// Parse from SOAP XML.
|
|
||||||
bool FromXml(const std::string& xml_string);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
// Convert to SOAP body XML.
|
|
||||||
virtual void ToXmlBody(pugi::xml_node xbody) = 0;
|
|
||||||
|
|
||||||
// Parse from SOAP body XML.
|
|
||||||
virtual bool FromXmlBody(pugi::xml_node xbody) = 0;
|
|
||||||
|
|
||||||
SoapNamespace soapenv_ns_; // SOAP envelope namespace.
|
|
||||||
SoapNamespace service_ns_; // Namespace for your web service.
|
|
||||||
|
|
||||||
std::string operation_;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace webcc
|
|
||||||
|
|
||||||
#endif // WEBCC_SOAP_MESSAGE_H_
|
|
@ -1,89 +0,0 @@
|
|||||||
#ifndef WEBCC_SOAP_PARAMETER_H_
|
|
||||||
#define WEBCC_SOAP_PARAMETER_H_
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
#include "webcc/globals.h" // for COPY_ASSIGN_MOVE_DEFAULT
|
|
||||||
|
|
||||||
namespace webcc {
|
|
||||||
|
|
||||||
// Key-value SOAP parameter.
|
|
||||||
class SoapParameter {
|
|
||||||
public:
|
|
||||||
SoapParameter() : as_cdata_(false) {
|
|
||||||
}
|
|
||||||
|
|
||||||
SoapParameter(const SoapParameter&) = default;
|
|
||||||
SoapParameter& operator=(const SoapParameter&) = default;
|
|
||||||
|
|
||||||
SoapParameter(const std::string& key, const char* value)
|
|
||||||
: key_(key), value_(value),
|
|
||||||
as_cdata_(false) {
|
|
||||||
}
|
|
||||||
|
|
||||||
SoapParameter(const std::string& key, const std::string& value,
|
|
||||||
bool as_cdata = false)
|
|
||||||
: key_(key), value_(value), as_cdata_(as_cdata) {
|
|
||||||
}
|
|
||||||
|
|
||||||
SoapParameter(const std::string& key, std::string&& value,
|
|
||||||
bool as_cdata = false)
|
|
||||||
: key_(key), value_(std::move(value)), as_cdata_(as_cdata) {
|
|
||||||
}
|
|
||||||
|
|
||||||
SoapParameter(const std::string& key, int value)
|
|
||||||
: key_(key), value_(std::to_string(value)),
|
|
||||||
as_cdata_(false) {
|
|
||||||
}
|
|
||||||
|
|
||||||
SoapParameter(const std::string& key, double value)
|
|
||||||
: key_(key), value_(std::to_string(value)),
|
|
||||||
as_cdata_(false) {
|
|
||||||
}
|
|
||||||
|
|
||||||
SoapParameter(const std::string& key, bool value)
|
|
||||||
: key_(key), value_(value ? "true" : "false"),
|
|
||||||
as_cdata_(false) {
|
|
||||||
}
|
|
||||||
|
|
||||||
#if WEBCC_DEFAULT_MOVE_COPY_ASSIGN
|
|
||||||
|
|
||||||
SoapParameter(SoapParameter&&) = default;
|
|
||||||
SoapParameter& operator=(SoapParameter&&) = default;
|
|
||||||
|
|
||||||
#else
|
|
||||||
|
|
||||||
SoapParameter(SoapParameter&& rhs)
|
|
||||||
: key_(std::move(rhs.key_)),
|
|
||||||
value_(std::move(rhs.value_)),
|
|
||||||
as_cdata_(rhs.as_cdata_) {
|
|
||||||
}
|
|
||||||
|
|
||||||
SoapParameter& operator=(SoapParameter&& rhs) {
|
|
||||||
if (&rhs != this) {
|
|
||||||
key_ = std::move(rhs.key_);
|
|
||||||
value_ = std::move(rhs.value_);
|
|
||||||
as_cdata_ = rhs.as_cdata_;
|
|
||||||
}
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // WEBCC_DEFAULT_MOVE_COPY_ASSIGN
|
|
||||||
|
|
||||||
const std::string& key() const { return key_; }
|
|
||||||
const std::string& value() const { return value_; }
|
|
||||||
|
|
||||||
const char* c_key() const { return key_.c_str(); }
|
|
||||||
const char* c_value() const { return value_.c_str(); }
|
|
||||||
|
|
||||||
bool as_cdata() const { return as_cdata_; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::string key_;
|
|
||||||
std::string value_;
|
|
||||||
bool as_cdata_;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace webcc
|
|
||||||
|
|
||||||
#endif // WEBCC_SOAP_PARAMETER_H_
|
|
@ -1,62 +0,0 @@
|
|||||||
#include "webcc/soap_request.h"
|
|
||||||
|
|
||||||
#include "webcc/soap_xml.h"
|
|
||||||
|
|
||||||
namespace webcc {
|
|
||||||
|
|
||||||
void SoapRequest::AddParameter(const SoapParameter& parameter) {
|
|
||||||
parameters_.push_back(parameter);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SoapRequest::AddParameter(SoapParameter&& parameter) {
|
|
||||||
parameters_.push_back(std::move(parameter));
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::string& SoapRequest::GetParameter(const std::string& key) const {
|
|
||||||
for (const SoapParameter& p : parameters_) {
|
|
||||||
if (p.key() == key) {
|
|
||||||
return p.value();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static const std::string kEmptyValue;
|
|
||||||
return kEmptyValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SoapRequest::ToXmlBody(pugi::xml_node xbody) {
|
|
||||||
pugi::xml_node xop = soap_xml::AddChild(xbody, service_ns_.name, operation_);
|
|
||||||
soap_xml::AddNSAttr(xop, service_ns_.name, service_ns_.url);
|
|
||||||
|
|
||||||
for (SoapParameter& p : parameters_) {
|
|
||||||
pugi::xml_node xparam = soap_xml::AddChild(xop, service_ns_.name, p.key());
|
|
||||||
|
|
||||||
// xparam.text().set() also works for PCDATA.
|
|
||||||
xparam.append_child(p.as_cdata() ? pugi::node_cdata : pugi::node_pcdata)
|
|
||||||
.set_value(p.c_value());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool SoapRequest::FromXmlBody(pugi::xml_node xbody) {
|
|
||||||
pugi::xml_node xoperation = xbody.first_child();
|
|
||||||
if (!xoperation) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
soap_xml::SplitName(xoperation, &service_ns_.name, &operation_);
|
|
||||||
service_ns_.url = soap_xml::GetNSAttr(xoperation, service_ns_.name);
|
|
||||||
|
|
||||||
pugi::xml_node xparameter = xoperation.first_child();
|
|
||||||
while (xparameter) {
|
|
||||||
parameters_.push_back({
|
|
||||||
soap_xml::GetNameNoPrefix(xparameter),
|
|
||||||
// xparameter.text().get/as_string() also works.
|
|
||||||
std::string(xparameter.child_value())
|
|
||||||
});
|
|
||||||
|
|
||||||
xparameter = xparameter.next_sibling();
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace webcc
|
|
@ -1,34 +0,0 @@
|
|||||||
#ifndef WEBCC_SOAP_REQUEST_H_
|
|
||||||
#define WEBCC_SOAP_REQUEST_H_
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "webcc/soap_message.h"
|
|
||||||
#include "webcc/soap_parameter.h"
|
|
||||||
|
|
||||||
namespace webcc {
|
|
||||||
|
|
||||||
// SOAP request.
|
|
||||||
// Used to compose the SOAP request envelope XML which will be sent as the HTTP
|
|
||||||
// request body.
|
|
||||||
class SoapRequest : public SoapMessage {
|
|
||||||
public:
|
|
||||||
void AddParameter(const SoapParameter& parameter);
|
|
||||||
|
|
||||||
void AddParameter(SoapParameter&& parameter);
|
|
||||||
|
|
||||||
// Get parameter value by key.
|
|
||||||
const std::string& GetParameter(const std::string& key) const;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void ToXmlBody(pugi::xml_node xbody) final;
|
|
||||||
bool FromXmlBody(pugi::xml_node xbody) final;
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::vector<SoapParameter> parameters_;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace webcc
|
|
||||||
|
|
||||||
#endif // WEBCC_SOAP_REQUEST_H_
|
|
@ -1,89 +0,0 @@
|
|||||||
#include "webcc/soap_request_handler.h"
|
|
||||||
|
|
||||||
#include <utility> // for move()
|
|
||||||
|
|
||||||
#include "webcc/logger.h"
|
|
||||||
#include "webcc/soap_globals.h"
|
|
||||||
#include "webcc/soap_request.h"
|
|
||||||
#include "webcc/soap_response.h"
|
|
||||||
|
|
||||||
namespace webcc {
|
|
||||||
|
|
||||||
bool SoapRequestHandler::Bind(SoapServicePtr service, const std::string& url) {
|
|
||||||
assert(service);
|
|
||||||
url_service_map_[url] = service;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SoapRequestHandler::HandleConnection(HttpConnectionPtr connection) {
|
|
||||||
HttpRequestPtr http_request = connection->request();
|
|
||||||
assert(http_request);
|
|
||||||
|
|
||||||
auto http_response = std::make_shared<HttpResponse>();
|
|
||||||
|
|
||||||
// TODO: Support keep-alive.
|
|
||||||
http_response->SetHeader(http::headers::kConnection, "Close");
|
|
||||||
|
|
||||||
std::string path = "/" + http_request->url().path();
|
|
||||||
SoapServicePtr service = GetServiceByUrl(path);
|
|
||||||
if (!service) {
|
|
||||||
http_response->set_status(http::Status::kBadRequest);
|
|
||||||
connection->SendResponse(http_response);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse the SOAP request XML.
|
|
||||||
SoapRequest soap_request;
|
|
||||||
if (!soap_request.FromXml(http_request->content())) {
|
|
||||||
http_response->set_status(http::Status::kBadRequest);
|
|
||||||
connection->SendResponse(http_response);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
SoapResponse soap_response;
|
|
||||||
|
|
||||||
// Set SOAP envelope namespace according to SOAP version.
|
|
||||||
// NOTE: This could be overwritten by service->Handle() anyway.
|
|
||||||
if (soap_version_ == kSoapV11) {
|
|
||||||
soap_response.set_soapenv_ns(kSoapEnvNamespaceV11);
|
|
||||||
} else {
|
|
||||||
soap_response.set_soapenv_ns(kSoapEnvNamespaceV12);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!service->Handle(soap_request, &soap_response)) {
|
|
||||||
http_response->set_status(http::Status::kBadRequest);
|
|
||||||
connection->SendResponse(http_response);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string content;
|
|
||||||
soap_response.ToXml(format_raw_, indent_str_, &content);
|
|
||||||
|
|
||||||
// TODO: Let the service provide charset.
|
|
||||||
if (soap_version_ == kSoapV11) {
|
|
||||||
http_response->SetContentType(http::media_types::kTextXml,
|
|
||||||
http::charsets::kUtf8);
|
|
||||||
} else {
|
|
||||||
http_response->SetContentType(http::media_types::kApplicationSoapXml,
|
|
||||||
http::charsets::kUtf8);
|
|
||||||
}
|
|
||||||
|
|
||||||
http_response->set_status(http::Status::kOK);
|
|
||||||
|
|
||||||
connection->SendResponse(http_response);
|
|
||||||
}
|
|
||||||
|
|
||||||
SoapServicePtr SoapRequestHandler::GetServiceByUrl(const std::string& url) {
|
|
||||||
UrlServiceMap::const_iterator it = url_service_map_.find(url);
|
|
||||||
|
|
||||||
if (it != url_service_map_.end()) {
|
|
||||||
LOG_VERB("Service matches the URL: %s", url.c_str());
|
|
||||||
return it->second;
|
|
||||||
}
|
|
||||||
|
|
||||||
LOG_WARN("No service matches the URL: %s", url.c_str());
|
|
||||||
|
|
||||||
return SoapServicePtr();
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace webcc
|
|
@ -1,51 +0,0 @@
|
|||||||
#ifndef WEBCC_SOAP_REQUEST_HANDLER_H_
|
|
||||||
#define WEBCC_SOAP_REQUEST_HANDLER_H_
|
|
||||||
|
|
||||||
#include <map>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
#include "webcc/http_request_handler.h"
|
|
||||||
#include "webcc/soap_globals.h"
|
|
||||||
|
|
||||||
namespace webcc {
|
|
||||||
|
|
||||||
class SoapRequestHandler : public HttpRequestHandler {
|
|
||||||
public:
|
|
||||||
explicit SoapRequestHandler(SoapVersion soap_version)
|
|
||||||
: soap_version_(soap_version), format_raw_(true) {
|
|
||||||
}
|
|
||||||
|
|
||||||
~SoapRequestHandler() override = default;
|
|
||||||
|
|
||||||
void set_format_raw(bool format_raw) {
|
|
||||||
format_raw_ = format_raw;
|
|
||||||
}
|
|
||||||
|
|
||||||
void set_indent_str(const std::string& indent_str) {
|
|
||||||
indent_str_ = indent_str;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Bind(SoapServicePtr service, const std::string& url);
|
|
||||||
|
|
||||||
private:
|
|
||||||
void HandleConnection(HttpConnectionPtr connection) final;
|
|
||||||
|
|
||||||
SoapServicePtr GetServiceByUrl(const std::string& url);
|
|
||||||
|
|
||||||
typedef std::map<std::string, SoapServicePtr> UrlServiceMap;
|
|
||||||
UrlServiceMap url_service_map_;
|
|
||||||
|
|
||||||
// Default to kSoapV12.
|
|
||||||
SoapVersion soap_version_;
|
|
||||||
|
|
||||||
// Format response XML without any indentation or line breaks.
|
|
||||||
bool format_raw_;
|
|
||||||
|
|
||||||
// Indent string for response XML.
|
|
||||||
// Applicable when |format_raw_| is false.
|
|
||||||
std::string indent_str_;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace webcc
|
|
||||||
|
|
||||||
#endif // WEBCC_SOAP_REQUEST_HANDLER_H_
|
|
@ -1,77 +0,0 @@
|
|||||||
#include "webcc/soap_response.h"
|
|
||||||
|
|
||||||
#include <cassert>
|
|
||||||
|
|
||||||
#include "webcc/soap_xml.h"
|
|
||||||
|
|
||||||
namespace webcc {
|
|
||||||
|
|
||||||
void SoapResponse::ToXmlBody(pugi::xml_node xbody) {
|
|
||||||
pugi::xml_node xresponse = soap_xml::AddChild(xbody, service_ns_.name,
|
|
||||||
operation_ + "Response");
|
|
||||||
soap_xml::AddNSAttr(xresponse, service_ns_.name, service_ns_.url);
|
|
||||||
|
|
||||||
if (simple_result_) {
|
|
||||||
pugi::xml_node xresult = soap_xml::AddChild(xresponse, service_ns_.name,
|
|
||||||
simple_result_->name);
|
|
||||||
soap_xml::SetText(xresult, simple_result_->value, simple_result_->is_cdata);
|
|
||||||
} else {
|
|
||||||
assert(composer_);
|
|
||||||
(*composer_)(xresponse);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool SoapResponse::FromXmlBody(pugi::xml_node xbody) {
|
|
||||||
assert(parser_);
|
|
||||||
|
|
||||||
// Check Fault element.
|
|
||||||
|
|
||||||
pugi::xml_node xfault = soap_xml::GetChildNoNS(xbody, "Fault");
|
|
||||||
|
|
||||||
// TODO: service_ns_.url
|
|
||||||
|
|
||||||
if (xfault) {
|
|
||||||
fault_.reset(new SoapFault);
|
|
||||||
|
|
||||||
pugi::xml_node xfaultcode = soap_xml::GetChildNoNS(xfault, "faultcode");
|
|
||||||
pugi::xml_node xfaultstring = soap_xml::GetChildNoNS(xfault, "faultstring");
|
|
||||||
pugi::xml_node xdetail = soap_xml::GetChildNoNS(xfault, "detail");
|
|
||||||
|
|
||||||
if (xfaultcode) {
|
|
||||||
fault_->faultcode = xfaultcode.text().as_string();
|
|
||||||
}
|
|
||||||
if (xfaultstring) {
|
|
||||||
fault_->faultstring = xfaultstring.text().as_string();
|
|
||||||
}
|
|
||||||
if (xdetail) {
|
|
||||||
fault_->detail = xdetail.text().as_string();
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check Response element.
|
|
||||||
|
|
||||||
pugi::xml_node xresponse = soap_xml::GetChildNoNS(xbody,
|
|
||||||
operation_ + "Response");
|
|
||||||
|
|
||||||
if (!xresponse) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
soap_xml::SplitName(xresponse, &service_ns_.name, nullptr);
|
|
||||||
service_ns_.url = soap_xml::GetNSAttr(xresponse, service_ns_.name);
|
|
||||||
|
|
||||||
// Call result parser on each child of the response node.
|
|
||||||
pugi::xml_node xchild = xresponse.first_child();
|
|
||||||
while (xchild) {
|
|
||||||
if (!parser_(xchild)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
xchild = xchild.next_sibling();
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace webcc
|
|
@ -1,152 +0,0 @@
|
|||||||
#ifndef WEBCC_SOAP_RESPONSE_H_
|
|
||||||
#define WEBCC_SOAP_RESPONSE_H_
|
|
||||||
|
|
||||||
#include <functional>
|
|
||||||
#include <memory>
|
|
||||||
#include <string>
|
|
||||||
#include <utility> // for move()
|
|
||||||
|
|
||||||
#include "webcc/soap_message.h"
|
|
||||||
#include "webcc/soap_xml.h"
|
|
||||||
|
|
||||||
namespace webcc {
|
|
||||||
|
|
||||||
class SoapResponse : public SoapMessage {
|
|
||||||
public:
|
|
||||||
// Response result parser.
|
|
||||||
// Called on each child of the response node.
|
|
||||||
// Example:
|
|
||||||
// - SOAP action: xxx
|
|
||||||
// - Given SOAP response:
|
|
||||||
// <soap:Body>
|
|
||||||
// <n:xxxResponse xmlns:n="...">
|
|
||||||
// <n:aaa>Blaah</aaa>
|
|
||||||
// <n:bbb>Blaah</bbb>
|
|
||||||
// <n:ccc>Blaah</ccc>
|
|
||||||
// </n:xxxResponse>
|
|
||||||
// <soap:Body>
|
|
||||||
// The parser will be called in the following sequence:
|
|
||||||
// - parser(aaa); // return true
|
|
||||||
// - parser(bbb); // return true
|
|
||||||
// - parser(ccc);
|
|
||||||
// If any of the parser returns false, the call sequence will be stopped:
|
|
||||||
// - parser(aaa); // return false
|
|
||||||
// <stopped>
|
|
||||||
// Then you can get the expected result by parsing the node one by one.
|
|
||||||
// When you implement the parser, you normally need to check the node name,
|
|
||||||
// the following helper can extract the name without any namespace prefix:
|
|
||||||
// webcc::soap_xml::GetNameNoPrefix(node);
|
|
||||||
typedef std::function<bool(pugi::xml_node)> Parser;
|
|
||||||
|
|
||||||
// Response result composer.
|
|
||||||
// Called on the response node.
|
|
||||||
// Example:
|
|
||||||
// - SOAP action: xxx
|
|
||||||
// - Given SOAP response:
|
|
||||||
// <soap:Body>
|
|
||||||
// <n:xxxResponse xmlns:n="..." />
|
|
||||||
// <soap:Body>
|
|
||||||
// The composer will be called as:
|
|
||||||
// - composer(xxxResponse);
|
|
||||||
// The composer then add proper children to xxxResponse as the result.
|
|
||||||
class Composer {
|
|
||||||
public:
|
|
||||||
void operator()(pugi::xml_node xresponse) {
|
|
||||||
Compose(xresponse);
|
|
||||||
}
|
|
||||||
private:
|
|
||||||
virtual void Compose(pugi::xml_node xresponse) = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef std::shared_ptr<Composer> ComposerPtr;
|
|
||||||
|
|
||||||
// Simple result means there's only one child of the response node.
|
|
||||||
// Normally, the value of the result is a string (could be CDATA to embed an
|
|
||||||
// XML string).
|
|
||||||
// Given SOAP action "xxx", the response could be:
|
|
||||||
// - Plain text as result value:
|
|
||||||
// <soap:Body>
|
|
||||||
// <n:xxxResponse xmlns:n="...">
|
|
||||||
// <n:Result>2.0</n:Result>
|
|
||||||
// </n:xxxResponse>
|
|
||||||
// <soap:Body>
|
|
||||||
// - CDATA as result value:
|
|
||||||
// <soap:Body>
|
|
||||||
// <n:xxxResponse xmlns:n="...">
|
|
||||||
// <n:Result>
|
|
||||||
// <![CDATA[
|
|
||||||
// <webcc type = "response">
|
|
||||||
// <status code = "0" message = "ok">
|
|
||||||
// <book>
|
|
||||||
// <id>1</id>
|
|
||||||
// </book>
|
|
||||||
// </webcc>
|
|
||||||
// ]]>
|
|
||||||
// </n:Result>
|
|
||||||
// </n:xxxResponse>
|
|
||||||
// <soap:Body>
|
|
||||||
struct SimpleResult {
|
|
||||||
std::string name; // Result XML node name.
|
|
||||||
std::string value; // Result value.
|
|
||||||
bool is_cdata; // CDATA result.
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef std::shared_ptr<SimpleResult> SimpleResultPtr;
|
|
||||||
|
|
||||||
SoapResponse() : parser_(nullptr) {}
|
|
||||||
|
|
||||||
// Set the parser to parse the result.
|
|
||||||
// Client only.
|
|
||||||
void set_parser(Parser parser) {
|
|
||||||
parser_ = parser;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the composer to compose the result.
|
|
||||||
// Composer will be ignored if the simple result is provided.
|
|
||||||
void set_composer(ComposerPtr composer) {
|
|
||||||
composer_ = composer;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set to compose from simple result.
|
|
||||||
void set_simple_result(SimpleResultPtr simple_result) {
|
|
||||||
simple_result_ = simple_result;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set to compose from simple result (a shortcut).
|
|
||||||
void set_simple_result(const std::string& name,
|
|
||||||
std::string&& value,
|
|
||||||
bool is_cdata) {
|
|
||||||
simple_result_.reset(new SimpleResult{
|
|
||||||
name, std::move(value), is_cdata
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<SoapFault> fault() const {
|
|
||||||
return fault_;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Set fault from server.
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void ToXmlBody(pugi::xml_node xbody) final;
|
|
||||||
|
|
||||||
bool FromXmlBody(pugi::xml_node xbody) final;
|
|
||||||
|
|
||||||
private:
|
|
||||||
// Fault element if any.
|
|
||||||
std::shared_ptr<SoapFault> fault_;
|
|
||||||
|
|
||||||
// Result parser (for client).
|
|
||||||
Parser parser_;
|
|
||||||
|
|
||||||
// Result composer (for server).
|
|
||||||
// Ignored if |simple_result_| is provided.
|
|
||||||
ComposerPtr composer_;
|
|
||||||
|
|
||||||
// Simple result (for server).
|
|
||||||
SimpleResultPtr simple_result_;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace webcc
|
|
||||||
|
|
||||||
#endif // WEBCC_SOAP_RESPONSE_H_
|
|
@ -1,49 +0,0 @@
|
|||||||
#ifndef WEBCC_SOAP_SERVER_H_
|
|
||||||
#define WEBCC_SOAP_SERVER_H_
|
|
||||||
|
|
||||||
// HTTP server handling SOAP requests.
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
#include "webcc/http_server.h"
|
|
||||||
#include "webcc/soap_request_handler.h"
|
|
||||||
|
|
||||||
namespace webcc {
|
|
||||||
|
|
||||||
class SoapServer : public HttpServer {
|
|
||||||
public:
|
|
||||||
SoapServer(std::uint16_t port, std::size_t workers,
|
|
||||||
SoapVersion soap_version = kSoapV12)
|
|
||||||
: HttpServer(port, workers),
|
|
||||||
request_handler_(soap_version) {
|
|
||||||
}
|
|
||||||
|
|
||||||
~SoapServer() override = default;
|
|
||||||
|
|
||||||
void set_format_raw(bool format_raw) {
|
|
||||||
request_handler_.set_format_raw(format_raw);
|
|
||||||
}
|
|
||||||
|
|
||||||
void set_indent_str(const std::string& indent_str) {
|
|
||||||
request_handler_.set_indent_str(indent_str);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bind a SOAP service to the given URL path.
|
|
||||||
// The |url| path must start with "/", e.g., "/calculator".
|
|
||||||
// Binding to the same URL multiple times is allowed, but only the last
|
|
||||||
// one takes effect.
|
|
||||||
bool Bind(SoapServicePtr service, const std::string& url) {
|
|
||||||
return request_handler_.Bind(service, url);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
HttpRequestHandler* GetRequestHandler() final {
|
|
||||||
return &request_handler_;
|
|
||||||
}
|
|
||||||
|
|
||||||
SoapRequestHandler request_handler_;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace webcc
|
|
||||||
|
|
||||||
#endif // WEBCC_SOAP_SERVER_H_
|
|
@ -1,30 +0,0 @@
|
|||||||
#ifndef WEBCC_SOAP_SERVICE_H_
|
|
||||||
#define WEBCC_SOAP_SERVICE_H_
|
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
#include "webcc/globals.h"
|
|
||||||
|
|
||||||
namespace webcc {
|
|
||||||
|
|
||||||
class SoapRequest;
|
|
||||||
class SoapResponse;
|
|
||||||
|
|
||||||
// Base class for your SOAP service.
|
|
||||||
class SoapService {
|
|
||||||
public:
|
|
||||||
virtual ~SoapService() = default;
|
|
||||||
|
|
||||||
// Handle SOAP request, output the response.
|
|
||||||
virtual bool Handle(const SoapRequest& soap_request,
|
|
||||||
SoapResponse* soap_response) = 0;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
http::Status http_status_ = http::Status::kOK;
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef std::shared_ptr<SoapService> SoapServicePtr;
|
|
||||||
|
|
||||||
} // namespace webcc
|
|
||||||
|
|
||||||
#endif // WEBCC_SOAP_SERVICE_H_
|
|
@ -1,120 +0,0 @@
|
|||||||
#include "webcc/soap_xml.h"
|
|
||||||
|
|
||||||
namespace webcc {
|
|
||||||
namespace soap_xml {
|
|
||||||
|
|
||||||
void SplitName(const pugi::xml_node& xnode, std::string* prefix,
|
|
||||||
std::string* name) {
|
|
||||||
std::string full_name = xnode.name();
|
|
||||||
|
|
||||||
std::size_t pos = full_name.find(':');
|
|
||||||
|
|
||||||
if (pos != std::string::npos) {
|
|
||||||
if (prefix != nullptr) {
|
|
||||||
*prefix = full_name.substr(0, pos);
|
|
||||||
}
|
|
||||||
if (name != nullptr) {
|
|
||||||
*name = full_name.substr(pos + 1);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (prefix != nullptr) {
|
|
||||||
*prefix = "";
|
|
||||||
}
|
|
||||||
if (name != nullptr) {
|
|
||||||
*name = full_name;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string GetPrefix(const pugi::xml_node& xnode) {
|
|
||||||
std::string ns_prefix;
|
|
||||||
SplitName(xnode, &ns_prefix, nullptr);
|
|
||||||
return ns_prefix;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string GetNameNoPrefix(const pugi::xml_node& xnode) {
|
|
||||||
std::string name;
|
|
||||||
SplitName(xnode, nullptr, &name);
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
// NOTE:
|
|
||||||
// The following 3 ways all work for PCDATA and CDATA:
|
|
||||||
// - xnode.text().get()
|
|
||||||
// - xnode.text().as_string()
|
|
||||||
// - xnode.child_value()
|
|
||||||
std::string GetText(const pugi::xml_node& xnode) {
|
|
||||||
return xnode.child_value();
|
|
||||||
}
|
|
||||||
|
|
||||||
void GetText(const pugi::xml_node& xnode, std::string* text) {
|
|
||||||
*text = xnode.child_value();
|
|
||||||
}
|
|
||||||
|
|
||||||
void SetText(pugi::xml_node xnode, const std::string& text, bool is_cdata) {
|
|
||||||
xnode.append_child(is_cdata ? pugi::node_cdata : pugi::node_pcdata)
|
|
||||||
.set_value(text.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
pugi::xml_node AddChild(pugi::xml_node xnode,
|
|
||||||
const std::string& ns, const std::string& name) {
|
|
||||||
return xnode.append_child((ns + ":" + name).c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
pugi::xml_node GetChild(const pugi::xml_node& xnode, const std::string& ns,
|
|
||||||
const std::string& name) {
|
|
||||||
return xnode.child((ns + ":" + name).c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
pugi::xml_node GetChildNoNS(const pugi::xml_node& xnode,
|
|
||||||
const std::string& name) {
|
|
||||||
pugi::xml_node xchild = xnode.first_child();
|
|
||||||
while (xchild) {
|
|
||||||
std::string child_name = xchild.name();
|
|
||||||
|
|
||||||
// Remove NS prefix.
|
|
||||||
std::size_t pos = child_name.find(':');
|
|
||||||
if (pos != std::string::npos) {
|
|
||||||
child_name = child_name.substr(pos + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (child_name == name) {
|
|
||||||
return xchild;
|
|
||||||
}
|
|
||||||
|
|
||||||
xchild = xchild.next_sibling();
|
|
||||||
}
|
|
||||||
|
|
||||||
return pugi::xml_node();
|
|
||||||
}
|
|
||||||
|
|
||||||
void AddAttr(pugi::xml_node xnode, const std::string& ns,
|
|
||||||
const std::string& name, const std::string& value) {
|
|
||||||
std::string ns_name = ns + ":" + name;
|
|
||||||
xnode.append_attribute(ns_name.c_str()) = value.c_str();
|
|
||||||
}
|
|
||||||
|
|
||||||
void AddNSAttr(pugi::xml_node xnode, const std::string& ns_name,
|
|
||||||
const std::string& ns_url) {
|
|
||||||
AddAttr(xnode, "xmlns", ns_name, ns_url);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string GetNSAttr(const pugi::xml_node& xnode, const std::string& ns_name) {
|
|
||||||
std::string attr_name = "xmlns:" + ns_name;
|
|
||||||
return xnode.attribute(attr_name.c_str()).as_string();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool PrettyPrint(std::ostream& os, const std::string& xml_string,
|
|
||||||
const char* indent) {
|
|
||||||
pugi::xml_document xdoc;
|
|
||||||
if (!xdoc.load_string(xml_string.c_str())) {
|
|
||||||
os << "Invalid XML" << std::endl;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
xdoc.print(os, indent);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace soap_xml
|
|
||||||
} // namespace webcc
|
|
@ -1,96 +0,0 @@
|
|||||||
#ifndef WEBCC_SOAP_XML_H_
|
|
||||||
#define WEBCC_SOAP_XML_H_
|
|
||||||
|
|
||||||
// XML helpers for SOAP messages.
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
#include "pugixml/pugixml.hpp"
|
|
||||||
|
|
||||||
namespace webcc {
|
|
||||||
namespace soap_xml {
|
|
||||||
|
|
||||||
// Split the node name into namespace prefix and real name.
|
|
||||||
// E.g., if the node name is "soapenv:Envelope", it will be splitted to
|
|
||||||
// "soapenv" and "Envelope".
|
|
||||||
void SplitName(const pugi::xml_node& xnode, std::string* prefix = nullptr,
|
|
||||||
std::string* name = nullptr);
|
|
||||||
|
|
||||||
// Get the namespace prefix from node name.
|
|
||||||
// E.g., if the node name is "soapenv:Envelope", NS prefix will be "soapenv".
|
|
||||||
std::string GetPrefix(const pugi::xml_node& xnode);
|
|
||||||
|
|
||||||
// Get the node name without namespace prefix.
|
|
||||||
std::string GetNameNoPrefix(const pugi::xml_node& xnode);
|
|
||||||
|
|
||||||
// Get node text (applicable for both PCDATA and CDATA).
|
|
||||||
// E.g., given the following node:
|
|
||||||
// <Name>Chunting Gu</Name>
|
|
||||||
// GetText returns "Chunting Gu".
|
|
||||||
std::string GetText(const pugi::xml_node& xnode);
|
|
||||||
|
|
||||||
// Output parameter version GetText.
|
|
||||||
void GetText(const pugi::xml_node& xnode, std::string* text);
|
|
||||||
|
|
||||||
// Set node text.
|
|
||||||
void SetText(pugi::xml_node xnode, const std::string& text, bool is_cdata);
|
|
||||||
|
|
||||||
// Add a child with the given name which is prefixed by a namespace.
|
|
||||||
// E.g., AppendChild(xnode, "soapenv", "Envelope") will append a child with
|
|
||||||
// name "soapenv:Envelope".
|
|
||||||
pugi::xml_node AddChild(pugi::xml_node xnode,
|
|
||||||
const std::string& ns, const std::string& name);
|
|
||||||
|
|
||||||
pugi::xml_node GetChild(const pugi::xml_node& xnode, const std::string& ns,
|
|
||||||
const std::string& name);
|
|
||||||
|
|
||||||
pugi::xml_node GetChildNoNS(const pugi::xml_node& xnode,
|
|
||||||
const std::string& name);
|
|
||||||
|
|
||||||
// Add an attribute with the given name which is prefixed by a namespace.
|
|
||||||
void AddAttr(pugi::xml_node xnode, const std::string& ns,
|
|
||||||
const std::string& name, const std::string& value);
|
|
||||||
|
|
||||||
// Append "xmlns" attribute.
|
|
||||||
// E.g., if the namespace is
|
|
||||||
// { "soapenv", "http://schemas.xmlsoap.org/soap/envelope/" }
|
|
||||||
// the attribute added will be
|
|
||||||
// xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
|
|
||||||
void AddNSAttr(pugi::xml_node xnode, const std::string& ns_name,
|
|
||||||
const std::string& ns_url);
|
|
||||||
|
|
||||||
// Get namespace attribute value.
|
|
||||||
// E.g., if the given namespace name is "soapenv", the value of
|
|
||||||
// attribute "xmlns:soapenv" will be returned.
|
|
||||||
std::string GetNSAttr(const pugi::xml_node& xnode,
|
|
||||||
const std::string& ns_name);
|
|
||||||
|
|
||||||
// An XML writer writing to a referenced string.
|
|
||||||
// Example:
|
|
||||||
// pugi::xml_document xdoc;
|
|
||||||
// ...
|
|
||||||
// std::string xml_string;
|
|
||||||
// XmlStringWriter writer(&xml_string);
|
|
||||||
// xdoc.save/print(writer);
|
|
||||||
class XmlStringWriter : public pugi::xml_writer {
|
|
||||||
public:
|
|
||||||
explicit XmlStringWriter(std::string* result) : result_(result) {
|
|
||||||
result_->clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
void write(const void* data, std::size_t size) override {
|
|
||||||
result_->append(static_cast<const char*>(data), size);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::string* result_;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Print the XML string to output stream in pretty format.
|
|
||||||
bool PrettyPrint(std::ostream& os, const std::string& xml_string,
|
|
||||||
const char* indent = "\t");
|
|
||||||
|
|
||||||
} // namespace soap_xml
|
|
||||||
} // namespace webcc
|
|
||||||
|
|
||||||
#endif // WEBCC_SOAP_XML_H_
|
|
Loading…
Reference in New Issue