Support CDATA for SOAP request and response; Rename Parameter to SoapParameter; Add book server and client example for SOAP.

This commit is contained in:
Adam Gu
2018-07-19 11:19:51 +08:00
parent 23d948cebb
commit 223e59cc7a
48 changed files with 1395 additions and 412 deletions
+9 -8
View File
@@ -54,19 +54,20 @@ if(WEBCC_ENABLE_SOAP)
# SOAP specific sources.
set(SOAP_SRCS
soap_client.cc
soap_message.h
soap_request_handler.cc
soap_response.h
soap_xml.cc
soap_client.h
soap_message.cc
soap_message.h
soap_parameter.h
soap_response.cc
soap_response.h
soap_request.cc
soap_request.h
soap_request_handler.cc
soap_request_handler.h
soap_server.h
soap_xml.h
soap_message.cc
soap_request.h
soap_response.cc
soap_service.h
soap_xml.cc
soap_xml.h
)
set(SRCS ${SRCS} ${SOAP_SRCS})
-38
View File
@@ -54,42 +54,4 @@ const char* DescribeError(Error error) {
}
}
// -----------------------------------------------------------------------------
Parameter::Parameter(const std::string& key, const char* value)
: key_(key), value_(value) {
}
Parameter::Parameter(const std::string& key, const std::string& value)
: key_(key), value_(value) {
}
Parameter::Parameter(const std::string& key, std::string&& value)
: key_(key), value_(std::move(value)) {
}
Parameter::Parameter(const std::string& key, int value)
: key_(key), value_(std::to_string(value)) {
}
Parameter::Parameter(const std::string& key, double value)
: key_(key), value_(std::to_string(value)) {
}
Parameter::Parameter(const std::string& key, bool value)
: key_(key), value_(value ? "true" : "false") {
}
Parameter::Parameter(Parameter&& rhs)
: key_(std::move(rhs.key_)), value_(std::move(rhs.value_)) {
}
Parameter& Parameter::operator=(Parameter&& rhs) {
if (&rhs != this) {
key_ = std::move(rhs.key_);
value_ = std::move(rhs.value_);
}
return *this;
}
} // namespace webcc
-38
View File
@@ -2,7 +2,6 @@
#define WEBCC_GLOBALS_H_
#include <string>
#include <vector>
// -----------------------------------------------------------------------------
// Macros
@@ -86,43 +85,6 @@ enum Error {
// Return a descriptive message for the given error code.
const char* DescribeError(Error error);
// -----------------------------------------------------------------------------
// Key-value parameter.
class Parameter {
public:
Parameter() = default;
Parameter(const Parameter&) = default;
Parameter& operator=(const Parameter&) = default;
Parameter(const std::string& key, const char* value);
Parameter(const std::string& key, const std::string& value);
Parameter(const std::string& key, std::string&& value);
Parameter(const std::string& key, int value);
Parameter(const std::string& key, double value);
Parameter(const std::string& key, bool value);
// Use "= default" if drop the support of VS 2013.
Parameter(Parameter&& rhs);
// Use "= default" if drop the support of VS 2013.
Parameter& operator=(Parameter&& rhs);
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(); }
std::string ToString() const {
return key_ + "=" + value_;
}
private:
std::string key_;
std::string value_;
};
} // namespace webcc
#endif // WEBCC_GLOBALS_H_
+8 -5
View File
@@ -11,8 +11,13 @@
namespace webcc {
SoapClient::SoapClient(const std::string& host, const std::string& port)
: host_(host), port_(port),
format_raw_(true), timeout_seconds_(0), timed_out_(false) {
}
Error SoapClient::Call(const std::string& operation,
std::vector<Parameter>&& parameters,
std::vector<SoapParameter>&& parameters,
std::string* result) {
assert(service_ns_.IsValid());
assert(!url_.empty() && !host_.empty());
@@ -29,12 +34,12 @@ Error SoapClient::Call(const std::string& operation,
soap_request.set_operation(operation);
for (Parameter& p : parameters) {
for (SoapParameter& p : parameters) {
soap_request.AddParameter(std::move(p));
}
std::string http_content;
soap_request.ToXml(&http_content);
soap_request.ToXml(format_raw_, indent_str_, &http_content);
HttpRequest http_request;
@@ -46,8 +51,6 @@ Error SoapClient::Call(const std::string& operation,
http_request.SetHeader(kSoapAction, operation);
http_request.UpdateStartLine();
HttpResponse http_response;
HttpClient http_client;
if (timeout_seconds_ > 0) {
+22 -10
View File
@@ -4,8 +4,8 @@
#include <string>
#include <vector>
#include "webcc/globals.h"
#include "webcc/soap_message.h"
#include "webcc/soap_parameter.h"
namespace webcc {
@@ -17,22 +17,21 @@ class SoapClient {
bool timed_out() const { return timed_out_; }
protected:
SoapClient() : timeout_seconds_(0), timed_out_(false) {
void set_format_raw(bool format_raw) { format_raw_ = format_raw; }
void set_indent_str(const std::string& indent_str) {
indent_str_ = indent_str;
}
protected:
SoapClient(const std::string& host, const std::string& port);
// A generic wrapper to make a call.
// NOTE: The parameters should be movable.
Error Call(const std::string& operation,
std::vector<Parameter>&& parameters,
std::vector<SoapParameter>&& parameters,
std::string* result);
// Timeout in seconds; only effective when > 0.
int timeout_seconds_;
// If the error was caused by timeout or not.
bool timed_out_;
SoapNamespace soapenv_ns_; // SOAP envelope namespace.
SoapNamespace service_ns_; // Namespace for your web service.
@@ -45,6 +44,19 @@ class SoapClient {
// Response result XML node name.
// E.g., "Result".
std::string result_name_;
// 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_;
// Timeout in seconds; only effective when > 0.
int timeout_seconds_;
// If the error was caused by timeout or not.
bool timed_out_;
};
} // namespace webcc
+9 -12
View File
@@ -11,19 +11,12 @@ const SoapNamespace kSoapEnvNamespace{
"http://schemas.xmlsoap.org/soap/envelope/"
};
void SoapMessage::ToXml(std::string* xml_string) {
assert(soapenv_ns_.IsValid() &&
service_ns_.IsValid() &&
!operation_.empty());
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;
// TODO(Adam):
// When save with format_default, declaration will be generated
// automatically but without encoding.
// pugi::xml_node xdecl = xdoc.prepend_child(pugi::node_declaration);
// xdecl.append_attribute("version").set_value("1.0");
pugi::xml_node xroot = soap_xml::AddChild(xdoc, soapenv_ns_.name, "Envelope");
soap_xml::AddNSAttr(xroot, soapenv_ns_.name, soapenv_ns_.url);
@@ -32,8 +25,12 @@ void SoapMessage::ToXml(std::string* xml_string) {
ToXmlBody(xbody);
soap_xml::XmlStrRefWriter writer(xml_string);
xdoc.save(writer, "\t", pugi::format_default, pugi::encoding_utf8);
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) {
+3 -3
View File
@@ -11,8 +11,7 @@ namespace webcc {
// XML namespace name/url pair.
// E.g., { "soap", "http://schemas.xmlsoap.org/soap/envelope/" }
class SoapNamespace {
public:
struct SoapNamespace {
std::string name;
std::string url;
@@ -47,7 +46,8 @@ class SoapMessage {
}
// Convert to SOAP request XML.
void ToXml(std::string* xml_string);
void ToXml(bool format_raw, const std::string& indent,
std::string* xml_string);
// Parse from SOAP request XML.
bool FromXml(const std::string& xml_string);
+79
View File
@@ -0,0 +1,79 @@
#ifndef WEBCC_SOAP_PARAMETER_H_
#define WEBCC_SOAP_PARAMETER_H_
#include <string>
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) {
}
// Use "= default" if drop the support of VS 2013.
SoapParameter(SoapParameter&& rhs)
: key_(std::move(rhs.key_)), value_(std::move(rhs.value_)),
as_cdata_(rhs.as_cdata_) {
}
// Use "= default" if drop the support of VS 2013.
SoapParameter& operator=(SoapParameter&& rhs) {
if (&rhs != this) {
key_ = std::move(rhs.key_);
value_ = std::move(rhs.value_);
as_cdata_ = rhs.as_cdata_;
}
return *this;
}
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_
+10 -6
View File
@@ -4,16 +4,16 @@
namespace webcc {
void SoapRequest::AddParameter(const Parameter& parameter) {
void SoapRequest::AddParameter(const SoapParameter& parameter) {
parameters_.push_back(parameter);
}
void SoapRequest::AddParameter(Parameter&& parameter) {
void SoapRequest::AddParameter(SoapParameter&& parameter) {
parameters_.push_back(std::move(parameter));
}
const std::string& SoapRequest::GetParameter(const std::string& key) const {
for (const Parameter& p : parameters_) {
for (const SoapParameter& p : parameters_) {
if (p.key() == key) {
return p.value();
}
@@ -27,9 +27,12 @@ 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 (Parameter& p : parameters_) {
for (SoapParameter& p : parameters_) {
pugi::xml_node xparam = soap_xml::AddChild(xop, service_ns_.name, p.key());
xparam.text().set(p.value().c_str());
// xparam.text().set() also works for PCDATA.
xparam.append_child(p.as_cdata() ? pugi::node_cdata : pugi::node_pcdata)
.set_value(p.c_value());
}
}
@@ -46,7 +49,8 @@ bool SoapRequest::FromXmlBody(pugi::xml_node xbody) {
while (xparameter) {
parameters_.push_back({
soap_xml::GetNameNoPrefix(xparameter),
std::string(xparameter.text().as_string())
// xparameter.text().get/as_string() also works.
std::string(xparameter.child_value())
});
xparameter = xparameter.next_sibling();
+4 -3
View File
@@ -5,6 +5,7 @@
#include <vector>
#include "webcc/soap_message.h"
#include "webcc/soap_parameter.h"
namespace webcc {
@@ -13,9 +14,9 @@ namespace webcc {
// request body.
class SoapRequest : public SoapMessage {
public:
void AddParameter(const Parameter& parameter);
void AddParameter(const SoapParameter& parameter);
void AddParameter(Parameter&& parameter);
void AddParameter(SoapParameter&& parameter);
// Get parameter value by key.
const std::string& GetParameter(const std::string& key) const;
@@ -25,7 +26,7 @@ class SoapRequest : public SoapMessage {
bool FromXmlBody(pugi::xml_node xbody) override;
private:
std::vector<Parameter> parameters_;
std::vector<SoapParameter> parameters_;
};
} // namespace webcc
+1 -1
View File
@@ -36,7 +36,7 @@ void SoapRequestHandler::HandleConnection(HttpConnectionPtr connection) {
}
std::string content;
soap_response.ToXml(&content);
soap_response.ToXml(format_raw_, indent_str_, &content);
connection->SetResponseContent(std::move(content), kTextXmlUtf8);
connection->SendResponse(HttpStatus::kOK);
}
+15 -1
View File
@@ -10,9 +10,16 @@ namespace webcc {
class SoapRequestHandler : public HttpRequestHandler {
public:
SoapRequestHandler() = default;
SoapRequestHandler() : 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:
@@ -22,6 +29,13 @@ class SoapRequestHandler : public HttpRequestHandler {
typedef std::map<std::string, SoapServicePtr> UrlServiceMap;
UrlServiceMap url_service_map_;
// 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
+8 -2
View File
@@ -1,6 +1,7 @@
#include "webcc/soap_response.h"
#include <cassert>
#include "webcc/soap_xml.h"
namespace webcc {
@@ -12,7 +13,10 @@ void SoapResponse::ToXmlBody(pugi::xml_node xbody) {
pugi::xml_node xresult = soap_xml::AddChild(xop, service_ns_.name,
result_name_);
xresult.text().set(result_.c_str());
// xresult.text().set() also works for PCDATA.
xresult.append_child(is_cdata_ ? pugi::node_cdata : pugi::node_pcdata)
.set_value(result_.c_str());
}
bool SoapResponse::FromXmlBody(pugi::xml_node xbody) {
@@ -25,7 +29,9 @@ bool SoapResponse::FromXmlBody(pugi::xml_node xbody) {
pugi::xml_node xresult = soap_xml::GetChildNoNS(xresponse, result_name_);
if (xresult) {
result_ = xresult.text().get();
// The value of the first child node of type PCDATA/CDATA.
// xresult.text().get/as_string() also works.
result_ = xresult.child_value();
return true;
}
}
+9 -3
View File
@@ -8,9 +8,10 @@
namespace webcc {
// SOAP response.
class SoapResponse : public SoapMessage {
public:
SoapResponse() : is_cdata_(false) {}
// Could be "Price" for an operation/method like "GetXyzPrice".
// Really depend on the service.
// Most services use a general name "Result".
@@ -18,12 +19,14 @@ class SoapResponse : public SoapMessage {
result_name_ = result_name;
}
void set_result(const std::string& result) {
void set_result(const std::string& result, bool is_cdata) {
result_ = result;
is_cdata_ = is_cdata;
}
void set_result(std::string&& result) {
void set_result_moved(std::string&& result, bool is_cdata) {
result_ = std::move(result);
is_cdata_ = is_cdata;
}
std::string result_moved() {
@@ -46,6 +49,9 @@ class SoapResponse : public SoapMessage {
// Result value.
std::string result_;
// CDATA result.
bool is_cdata_;
};
} // namespace webcc
+9 -1
View File
@@ -5,8 +5,8 @@
#include <string>
#include "webcc/soap_request_handler.h"
#include "webcc/http_server.h"
#include "webcc/soap_request_handler.h"
namespace webcc {
@@ -18,6 +18,14 @@ class SoapServer : public HttpServer {
~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
+1 -1
View File
@@ -94,7 +94,7 @@ bool PrettyPrint(std::ostream& os, const std::string& xml_string,
return false;
}
xdoc.save(os, indent);
xdoc.print(os, indent);
return true;
}
+4 -4
View File
@@ -58,11 +58,11 @@ std::string GetNSAttr(const pugi::xml_node& xnode,
// pugi::xml_document xdoc;
// ...
// std::string xml_string;
// XmlStrRefWriter writer(&xml_string);
// xdoc.save(writer, "\t", pugi::format_default, pugi::encoding_utf8);
class XmlStrRefWriter : public pugi::xml_writer {
// XmlStringWriter writer(&xml_string);
// xdoc.save/print(writer);
class XmlStringWriter : public pugi::xml_writer {
public:
explicit XmlStrRefWriter(std::string* result) : result_(result) {
explicit XmlStringWriter(std::string* result) : result_(result) {
result_->clear();
}
+4 -5
View File
@@ -2,7 +2,6 @@
#include <algorithm>
#include <sstream>
#include <utility> // for move()
namespace webcc {
@@ -89,7 +88,7 @@ void UrlQuery::Remove(const std::string& key) {
const std::string& UrlQuery::Get(const std::string& key) const {
auto it = Find(key);
if (it != parameters_.end()) {
return it->value();
return it->second;
}
static const std::string kEmptyValue;
@@ -101,11 +100,11 @@ std::string UrlQuery::ToString() const {
return "";
}
std::string str = parameters_[0].ToString();
std::string str = parameters_[0].first + "=" + parameters_[0].second;
for (std::size_t i = 1; i < parameters_.size(); ++i) {
str += "&";
str += parameters_[i].ToString();
str += parameters_[i].first + "=" + parameters_[i].second;
}
return str;
@@ -114,7 +113,7 @@ std::string UrlQuery::ToString() const {
UrlQuery::ConstIterator UrlQuery::Find(const std::string& key) const {
return std::find_if(parameters_.begin(),
parameters_.end(),
[&key](const Parameter& p) { return p.key() == key; });
[&key](const SoapParameter& p) { return p.first == key; });
}
// -----------------------------------------------------------------------------
+4 -2
View File
@@ -9,9 +9,10 @@
#include <map>
#include <string>
#include <utility>
#include <vector>
#include "webcc/globals.h"
//#include "webcc/globals.h"
namespace webcc {
@@ -20,7 +21,8 @@ namespace webcc {
// URL query parameters.
class UrlQuery {
public:
typedef std::vector<Parameter> Parameters;
typedef std::pair<std::string, std::string> SoapParameter;
typedef std::vector<SoapParameter> Parameters;
UrlQuery() = default;