Remove SOAP support

This commit is contained in:
Chunting Gu
2019-04-16 13:04:42 +08:00
parent b72820c9c5
commit e63dc748ab
38 changed files with 80 additions and 16773 deletions
+5 -86
View File
@@ -13,95 +13,14 @@ configure_file(
# Adhere to GNU filesystem layout conventions.
include(GNUInstallDirs)
set(HEADERS
base64.h
common.h
globals.h
http_client.h
http_client_pool.h
http_client_session.h
http_connection.h
http_connection_pool.h
http_message.h
http_parser.h
http_request.h
http_request_builder.h
http_request_handler.h
http_request_parser.h
http_response.h
http_response_parser.h
http_server.h
http_socket.h
queue.h
rest_request_handler.h
rest_server.h
rest_service.h
rest_service_manager.h
url.h
utility.h
version.h
zlib_wrapper.h
)
set(SOURCES
base64.cc
common.cc
globals.cc
http_client.cc
http_client_pool.cc
http_client_session.cc
http_connection.cc
http_connection_pool.cc
http_message.cc
http_parser.cc
http_request.cc
http_request_builder.cc
http_request_handler.cc
http_request_parser.cc
http_response.cc
http_response_parser.cc
http_server.cc
http_socket.cc
logger.cc
rest_request_handler.cc
rest_service_manager.cc
rest_service.cc
url.cc
utility.cc
zlib_wrapper.cc
)
if(WEBCC_ENABLE_SOAP)
set(SOAP_HEADERS
soap_client.h
soap_globals.h
soap_message.h
soap_parameter.h
soap_response.h
soap_request.h
soap_request_handler.h
soap_server.h
soap_service.h
soap_xml.h
)
set(SOAP_SOURCES
soap_client.cc
soap_globals.cc
soap_message.cc
soap_response.cc
soap_request.cc
soap_request_handler.cc
soap_xml.cc
)
set(HEADERS ${HEADERS} ${SOAP_HEADERS})
set(SOURCES ${SOURCES} ${SOAP_SOURCES})
endif()
file(GLOB SRCS
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/*.cc
${CMAKE_CURRENT_SOURCE_DIR}/*.h)
set(TARGET webcc)
add_library(${TARGET} STATIC ${HEADERS} ${SOURCES})
add_library(${TARGET} STATIC ${SRCS})
# Install lib and header files.
# On Linux, if CMAKE_INSTALL_PREFIX is ~, the lib (libwebcc.a) will be installed
-1
View File
@@ -7,7 +7,6 @@
#include "webcc/http_connection.h"
#include "webcc/queue.h"
#include "webcc/soap_service.h"
namespace webcc {
-1
View File
@@ -5,7 +5,6 @@
#include "webcc/http_request_handler.h"
#include "webcc/logger.h"
#include "webcc/soap_service.h"
#include "webcc/utility.h"
using tcp = boost::asio::ip::tcp;
-112
View File
@@ -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
-92
View File
@@ -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_
-44
View File
@@ -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
-49
View File
@@ -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_
-52
View File
@@ -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
-56
View File
@@ -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_
-89
View File
@@ -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_
-62
View File
@@ -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
-34
View File
@@ -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_
-89
View File
@@ -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
-51
View File
@@ -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_
-77
View File
@@ -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
-152
View File
@@ -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_
-49
View File
@@ -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_
-30
View File
@@ -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_
-120
View File
@@ -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
-96
View File
@@ -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_