diff --git a/compile_commands.json b/compile_commands.json index a97838a..8a8645d 100644 --- a/compile_commands.json +++ b/compile_commands.json @@ -21,8 +21,8 @@ }, { "directory": "/home/adam/github/webcc/build/webcc", - "command": "/usr/bin/c++ -DBOOST_ASIO_NO_DEPRECATED -DWEBCC_ENABLE_LOG -DWEBCC_ENABLE_SOAP -DWEBCC_LOG_LEVEL=0 -I/usr/local/include -I/home/adam/github/webcc -I/home/adam/github/webcc/third_party/src -std=c++11 -o CMakeFiles/webcc.dir/http_connection.cc.o -c /home/adam/github/webcc/webcc/http_connection.cc", - "file": "/home/adam/github/webcc/webcc/http_connection.cc" + "command": "/usr/bin/c++ -DBOOST_ASIO_NO_DEPRECATED -DWEBCC_ENABLE_LOG -DWEBCC_ENABLE_SOAP -DWEBCC_LOG_LEVEL=0 -I/usr/local/include -I/home/adam/github/webcc -I/home/adam/github/webcc/third_party/src -std=c++11 -o CMakeFiles/webcc.dir/http_session.cc.o -c /home/adam/github/webcc/webcc/http_session.cc", + "file": "/home/adam/github/webcc/webcc/http_session.cc" }, { "directory": "/home/adam/github/webcc/build/webcc", diff --git a/example/soap_book_server/book_service.cc b/example/soap_book_server/book_service.cc index 962cd57..491616a 100644 --- a/example/soap_book_server/book_service.cc +++ b/example/soap_book_server/book_service.cc @@ -21,7 +21,6 @@ bool BookService::Handle(const webcc::SoapRequest& soap_request, webcc::SoapResponse* soap_response) { const std::string& operation = soap_request.operation(); - soap_response->set_soapenv_ns(webcc::kSoapEnvNamespace); soap_response->set_service_ns({ "ser", "http://www.example.com/book/" diff --git a/example/soap_calc_client_parasoft/main.cc b/example/soap_calc_client_parasoft/main.cc index 47638ca..2d9de0a 100644 --- a/example/soap_calc_client_parasoft/main.cc +++ b/example/soap_calc_client_parasoft/main.cc @@ -7,8 +7,9 @@ class CalcClient { public: + // NOTE: Parasoft's calculator service uses SOAP V1.1. CalcClient(const std::string& host, const std::string& port) - : soap_client_(host, port) { + : soap_client_(host, port, webcc::kSoapV11) { soap_client_.SetTimeout(5); soap_client_.set_url("/glue/calculator"); diff --git a/example/soap_calc_server/calc_service.cc b/example/soap_calc_server/calc_service.cc index 70a0103..2a35e4f 100644 --- a/example/soap_calc_server/calc_service.cc +++ b/example/soap_calc_server/calc_service.cc @@ -52,7 +52,6 @@ bool CalcService::Handle(const webcc::SoapRequest& soap_request, double result = calc(x, y); - soap_response->set_soapenv_ns(webcc::kSoapEnvNamespace); soap_response->set_service_ns({ "cal", "http://www.example.com/calculator/" diff --git a/webcc/soap_async_client.cc b/webcc/soap_async_client.cc index 70321a2..4710ff5 100644 --- a/webcc/soap_async_client.cc +++ b/webcc/soap_async_client.cc @@ -12,10 +12,11 @@ namespace webcc { SoapAsyncClient::SoapAsyncClient(boost::asio::io_context& io_context, const std::string& host, - const std::string& port) + const std::string& port, + SoapVersion soap_version) : io_context_(io_context), host_(host), port_(port), - soapenv_ns_(kSoapEnvNamespace), + soap_version_(soap_version), format_raw_(true), timeout_seconds_(0) { if (port_.empty()) { std::size_t i = host_.find_last_of(':'); @@ -35,7 +36,13 @@ void SoapAsyncClient::Request(const std::string& operation, SoapRequest soap_request; - soap_request.set_soapenv_ns(soapenv_ns_); + // 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); @@ -52,7 +59,13 @@ void SoapAsyncClient::Request(const std::string& operation, http_request->set_method(kHttpPost); http_request->set_url(url_); http_request->SetContent(std::move(http_content), true); - http_request->SetContentType(kTextXmlUtf8); + + if (soap_version_ == kSoapV11) { + http_request->SetContentType(kTextXmlUtf8); + } else { + http_request->SetContentType(kAppSoapXmlUtf8); + } + http_request->SetHost(host_, port_); http_request->SetHeader(kSoapAction, operation); http_request->UpdateStartLine(); diff --git a/webcc/soap_async_client.h b/webcc/soap_async_client.h index 516c7e1..2419da1 100644 --- a/webcc/soap_async_client.h +++ b/webcc/soap_async_client.h @@ -19,7 +19,8 @@ class SoapAsyncClient { // If |port| is empty, |host| will be checked to see if it contains port or // not (separated by ':'). SoapAsyncClient(boost::asio::io_context& io_context, // NOLINT - const std::string& host, const std::string& port = ""); + const std::string& host, const std::string& port = "", + SoapVersion soap_version = kSoapV12); ~SoapAsyncClient() = default; @@ -59,11 +60,13 @@ class SoapAsyncClient { std::string host_; std::string port_; // Leave this empty to use default 80. + SoapVersion soap_version_; + // Request URL. std::string url_; - SoapNamespace soapenv_ns_; // SOAP envelope namespace. - SoapNamespace service_ns_; // Namespace for your web service. + // Namespace for your web service. + SoapNamespace service_ns_; // Response result XML node name. // E.g., "Result". diff --git a/webcc/soap_client.cc b/webcc/soap_client.cc index 6080f50..f0d8d6e 100644 --- a/webcc/soap_client.cc +++ b/webcc/soap_client.cc @@ -8,9 +8,10 @@ namespace webcc { -SoapClient::SoapClient(const std::string& host, const std::string& port) +SoapClient::SoapClient(const std::string& host, const std::string& port, + SoapVersion soap_version) : host_(host), port_(port), - soapenv_ns_(kSoapEnvNamespace), + soap_version_(soap_version), format_raw_(true), error_(kNoError) { if (port_.empty()) { std::size_t i = host_.find_last_of(':'); @@ -32,7 +33,13 @@ bool SoapClient::Request(const std::string& operation, SoapRequest soap_request; - soap_request.set_soapenv_ns(soapenv_ns_); + // 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); @@ -49,7 +56,13 @@ bool SoapClient::Request(const std::string& operation, http_request.set_method(kHttpPost); http_request.set_url(url_); http_request.SetContent(std::move(http_content), true); - http_request.SetContentType(kTextXmlUtf8); + + if (soap_version_ == kSoapV11) { + http_request.SetContentType(kTextXmlUtf8); + } else { + http_request.SetContentType(kAppSoapXmlUtf8); + } + http_request.SetHost(host_, port_); http_request.SetHeader(kSoapAction, operation); http_request.UpdateStartLine(); diff --git a/webcc/soap_client.h b/webcc/soap_client.h index 1fd553f..8aec73d 100644 --- a/webcc/soap_client.h +++ b/webcc/soap_client.h @@ -14,7 +14,8 @@ class SoapClient { public: // If |port| is empty, |host| will be checked to see if it contains port or // not (separated by ':'). - explicit SoapClient(const std::string& host, const std::string& port = ""); + explicit SoapClient(const std::string& host, const std::string& port = "", + SoapVersion soap_version = kSoapV12); ~SoapClient() = default; @@ -56,11 +57,13 @@ class SoapClient { std::string host_; std::string port_; // Leave this empty to use default 80. + SoapVersion soap_version_; + // Request URL. std::string url_; - SoapNamespace soapenv_ns_; // SOAP envelope namespace. - SoapNamespace service_ns_; // Namespace for your web service. + // Namespace for your web service. + SoapNamespace service_ns_; // Response result XML node name. // E.g., "Result". diff --git a/webcc/soap_globals.cc b/webcc/soap_globals.cc index 377d5f1..b310b89 100644 --- a/webcc/soap_globals.cc +++ b/webcc/soap_globals.cc @@ -9,10 +9,25 @@ const std::string kSoapAction = "SOAPAction"; // 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"; -const SoapNamespace kSoapEnvNamespace{ +// 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" +}; + } // namespace webcc diff --git a/webcc/soap_globals.h b/webcc/soap_globals.h index 5d4195b..fc5e4a3 100644 --- a/webcc/soap_globals.h +++ b/webcc/soap_globals.h @@ -11,11 +11,17 @@ namespace webcc { 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://schemas.xmlsoap.org/soap/envelope/" } +// E.g., { "soap", "http://www.w3.org/2003/05/soap-envelope" } struct SoapNamespace { std::string name; std::string url; @@ -25,8 +31,9 @@ struct SoapNamespace { } }; -// CSoap's default namespace for SOAP Envelope. -extern const SoapNamespace kSoapEnvNamespace; +// Default namespaces for SOAP Envelope. +extern const SoapNamespace kSoapEnvNamespaceV11; +extern const SoapNamespace kSoapEnvNamespaceV12; } // namespace webcc diff --git a/webcc/soap_request_handler.cc b/webcc/soap_request_handler.cc index 2bfbc95..3f52e94 100644 --- a/webcc/soap_request_handler.cc +++ b/webcc/soap_request_handler.cc @@ -30,6 +30,15 @@ void SoapRequestHandler::HandleSession(HttpSessionPtr session) { } 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)) { session->SendResponse(HttpStatus::kBadRequest); return; @@ -37,7 +46,13 @@ void SoapRequestHandler::HandleSession(HttpSessionPtr session) { std::string content; soap_response.ToXml(format_raw_, indent_str_, &content); - session->SetResponseContent(std::move(content), kTextXmlUtf8); + + if (soap_version_ == kSoapV11) { + session->SetResponseContent(std::move(content), kTextXmlUtf8); + } else { + session->SetResponseContent(std::move(content), kAppSoapXmlUtf8); + } + session->SendResponse(HttpStatus::kOK); } diff --git a/webcc/soap_request_handler.h b/webcc/soap_request_handler.h index 888d75b..7ef8dc4 100644 --- a/webcc/soap_request_handler.h +++ b/webcc/soap_request_handler.h @@ -5,12 +5,16 @@ #include #include "webcc/http_request_handler.h" +#include "webcc/soap_globals.h" namespace webcc { class SoapRequestHandler : public HttpRequestHandler { public: - SoapRequestHandler() : format_raw_(true) {} + explicit SoapRequestHandler(SoapVersion soap_version) + : soap_version_(soap_version), + format_raw_(true) { + } ~SoapRequestHandler() override = default; @@ -30,6 +34,9 @@ class SoapRequestHandler : public HttpRequestHandler { typedef std::map UrlServiceMap; UrlServiceMap url_service_map_; + // Default to kSoapV12. + SoapVersion soap_version_; + // Format response XML without any indentation or line breaks. bool format_raw_; diff --git a/webcc/soap_server.h b/webcc/soap_server.h index e664ca9..a80ff6a 100644 --- a/webcc/soap_server.h +++ b/webcc/soap_server.h @@ -12,8 +12,10 @@ namespace webcc { class SoapServer : public HttpServer { public: - SoapServer(std::uint16_t port, std::size_t workers) - : HttpServer(port, workers) { + SoapServer(std::uint16_t port, std::size_t workers, + SoapVersion soap_version = kSoapV12) + : HttpServer(port, workers), + request_handler_(soap_version) { } ~SoapServer() override = default;