4#include "Stroika/Frameworks/StroikaPreComp.h"
9#include "Stroika/Foundation/Containers/Mapping.h"
10#include "Stroika/Foundation/DataExchange/BadFormatException.h"
14#include "Stroika/Foundation/DataExchange/XML/WriterUtils.h"
16#include "Stroika/Foundation/Execution/Throw.h"
17#include "Stroika/Foundation/IO/Network/HTTP/Headers.h"
18#include "Stroika/Foundation/IO/Network/HTTP/Methods.h"
19#include "Stroika/Foundation/IO/Network/HTTP/Status.h"
20#include "Stroika/Foundation/IO/Network/Transfer/Connection.h"
21#include "Stroika/Foundation/IO/Network/Transfer/Exception.h"
22#include "Stroika/Foundation/Streams/MemoryStream.h"
23#if qStroika_Foundation_DataExchange_XML_SupportDOM
24#include "Stroika/Foundation/DataExchange/XML/DOM.h"
34using namespace Stroika::Frameworks;
35using namespace Stroika::Frameworks::WebService;
39 constexpr string_view kEnvelopeNamespace_ =
"http://schemas.xmlsoap.org/soap/envelope/"sv;
41 optional<String> Lookup_ (
const Arguments& arguments,
const String& name)
51#if qStroika_Foundation_DataExchange_XML_SupportDOM
52 namespace WriteElement = XML::DOM::Element;
54 constexpr string_view kEnvelopePrefix_ =
"s"sv;
55 constexpr string_view kMethodPrefix_ =
"m"sv;
57 const IO::Network::URI kEncodingNamespaceURI_{
"http://schemas.xmlsoap.org/soap/encoding/"sv};
65 WriteElement::Ptr fBody;
67 Envelope_ (
const Options& options)
76 fDoc = XML::DOM::Document::New (options.
fXMLOptions);
77 WriteElement::Ptr envelope =
79 fBody = envelope.Append (
"Body"sv);
83 case EncodingStyleAttribute::eOnBody:
84 fBody.SetAttribute (kEncodingStyle_, kEncodingNamespaceURI_.As<
String> ());
86 case EncodingStyleAttribute::eOnEnvelope:
87 envelope.SetAttribute (kEncodingStyle_, kEncodingNamespaceURI_.As<
String> ());
89 case EncodingStyleAttribute::eOmitted:
103 void AppendMethodElement_ (WriteElement::Ptr body,
const String& elementName,
const String& methodNamespace,
const Arguments& arguments)
115#if qStroika_Foundation_DataExchange_XML_SupportDOM
116 namespace Element = XML::DOM::Element;
119 template <
typename RESULT>
120 RESULT WithBodyElement_ (
const Memory::BLOB& b,
const Options& options,
const function<RESULT (
const Element::Ptr&)>& f)
122 using namespace XML::DOM;
123 auto child = [] (
const Element::Ptr& e,
const optional<String>& name) -> Element::Ptr {
124 for (
const Element::Ptr& c : e.GetChildElements ()) {
125 if (not name or c.GetName ().fName == *name) {
134 Element::Ptr envelope = doc.GetRootElement ();
135 Element::Ptr body = envelope ==
nullptr or envelope.GetName ().fName !=
"Envelope"sv ? nullptr : child (envelope,
"Body"sv);
136 Element::Ptr top = body ==
nullptr ? nullptr : child (body, nullopt);
137 if (top ==
nullptr) {
138 Execution::Throw (
BadFormatException{
"not a SOAP message: no element in an Envelope's Body"sv});
142 String NamespaceOf_ (
const Element::Ptr& e)
144 optional<URI> ns = e.GetName ().fNamespace;
147 bool IsFault_ (
const Element::Ptr& e)
149 return e.GetName ().fName ==
"Fault"sv and NamespaceOf_ (e) == kEnvelopeNamespace_;
152 Arguments ChildValues_ (
const Element::Ptr& e)
155 for (
const Element::Ptr& c : e.GetChildElements ()) {
156 result.
Append ({c.GetName ().fName, c.GetValue ()});
161 [[noreturn]]
void NoXMLDOM_ ()
182 sb <<
"Namespace: "sv << fNamespace;
183 sb <<
", Method: "sv << fMethod;
184 sb <<
", Arguments: "sv << fArguments;
196 return Lookup_ (fArguments, name);
203 sb <<
"Namespace: "sv << fNamespace;
204 sb <<
", Method: "sv << fMethod;
205 sb <<
", Arguments: "sv << fArguments;
219 sb <<
"Fault-Code: "sv << fFaultCode;
220 sb <<
", Fault-String: "sv << fFaultString;
222 sb <<
", Detail: "sv << *fDetail;
233FaultException::FaultException (
const Fault& fault)
239Fault FaultException::GetFault ()
const
251#if qStroika_Foundation_DataExchange_XML_SupportDOM
252 Envelope_ envelope{options};
253 AppendMethodElement_ (envelope.fBody, request.fMethod, request.fNamespace, request.fArguments);
254 return envelope.Write ();
262#if qStroika_Foundation_DataExchange_XML_SupportDOM
263 Envelope_ envelope{options};
264 AppendMethodElement_ (envelope.fBody, response.fMethod +
"Response"sv, response.fNamespace, response.fArguments);
265 return envelope.Write ();
273#if qStroika_Foundation_DataExchange_XML_SupportDOM
274 Require (XML::IsNCName (fault.fFaultCode));
275 Envelope_ envelope{options};
276 WriteElement::Ptr faultElt = envelope.fBody.Append (
"Fault"sv);
290 return envelope.Write ();
304#if qStroika_Foundation_DataExchange_XML_SupportDOM
305 *request = WithBodyElement_<Request> (b, options, [] (
const Element::Ptr& e) {
309 return Request{.
fNamespace = NamespaceOf_ (e), .fMethod = e.GetName ().fName, .fArguments = ChildValues_ (e)};
319#if qStroika_Foundation_DataExchange_XML_SupportDOM
320 *response = WithBodyElement_<Response> (b, options, [] (
const Element::Ptr& e) {
321 const String name = e.GetName ().fName;
322 if (IsFault_ (e) or not name.
EndsWith (
"Response"sv)) {
323 Execution::Throw (BadFormatException{
"not a SOAP call's answer: {}"_f(name)});
335#if qStroika_Foundation_DataExchange_XML_SupportDOM
336 *fault = WithBodyElement_<Fault> (b, options, [] (
const Element::Ptr& e) {
337 if (not IsFault_ (e)) {
341 for (
const Element::Ptr& c : e.GetChildElements ()) {
342 const String name = c.GetName ().fName;
343 if (name ==
"faultcode"sv) {
345 optional<size_t> colon = code.
Find (
':');
347 if (not XML::IsNCName (local)) {
350 size_t n = local.
size ();
351 while (n > 0 and not XML::IsNCName (local.
SubString (0, n))) {
354 const String readAs = n == 0 ? Fault::kServer : local.
SubString (0, n);
355 DbgTrace (
"faultcode '{}' is not a QName: read as '{}'"_f, code, readAs);
360 else if (name ==
"faultstring"sv) {
363 else if (name ==
"detail"sv) {
365 for (
const Element::Ptr& d : c.GetChildElements ()) {
392 r.fMethod = HTTP::Methods::kPost;
402 if (answer.GetStatus () == HTTP::StatusCodes::kInternalError) {
403 optional<Fault> fault;
427 WebServer::Response& response = m.
rwResponse ();
430 response.status = HTTP::StatusCodes::kUnsupportedMediaType;
434 auto answerFault = [&] (
const Fault& fault) {
435 response.status = HTTP::StatusCodes::kInternalError;
436 response.write (
Serialize (fault, options));
443 answerFault (
Fault{.
fFaultCode = Fault::kClient, .fFaultString =
"not a SOAP call: {}"_f(e)});
447 response.write (
Serialize (handler (request), options));
450 answerFault (e.GetFault ());
#define RequireNotNull(p)
Similar to String, but intended to more efficiently construct a String. Mutable type (String is large...
Characters::String ToString() const
same as str (): so a StringBuilder formats as its text, with _f and std::format alike
nonvirtual String str() const
String is like std::u32string, except it is much easier to use, often much more space efficient,...
nonvirtual size_t size() const noexcept
nonvirtual bool EndsWith(const Character &c, CompareOptions co=eWithCase) const
nonvirtual String SubString(SZ from) const
nonvirtual String Trim(bool(*shouldBeTrimmed)(Character)=Character::IsWhitespace) const
nonvirtual optional< size_t > Find(Character c, CompareOptions co=eWithCase) const
A Mapping uniquely associates two elements: a key and a value (use Assocation to allow multiple value...
A generalization of a vector: a container whose elements are keyed by the natural numbers.
nonvirtual void Append(ArgByValueType< value_type > item)
TypedBLOB is a named tuple<Memory::BLOB, optional<InternetMediaType>> - with friendlier names,...
Document::Ptr is create with Document::New, and is a smart pointer to a DOM document object.
nonvirtual void Write(const Streams::OutputStream::Ptr< byte > &to, const SerializationOptions &options={}) const
The document as XML: to a byte stream as UTF-8, with no byte order mark - or as a String.
nonvirtual Element::Ptr GetRootElement() const
always returns Node of eElement or return nullptr if none
nonvirtual Element::Ptr ReplaceRootElement(const NameWithNamespace &newEltName, const optional< String > &namespacePrefix=nullopt) const
Make this the document's root element - and say HOW its namespace is declared on it,...
Exception<> is a replacement (subclass) for any std c++ exception class (e.g. the default 'std::excep...
nonvirtual void SetSchemeAndAuthority(const URI &url)
nonvirtual bool GetSucceeded() const
nonvirtual BLOB GetData() const
nonvirtual URI GetSchemeAndAuthority() const
nonvirtual RETURN_TYPE GetAuthorityRelativeResource() const
Return the (PCT etc encoded if a string) data AFTER the authority, but not including the fragment.
Common::ReadOnlyProperty< Response & > rwResponse
Common::ReadOnlyProperty< const Request & > request
Common::ReadOnlyProperty< Request & > rwRequest
A Fault, thrown: Invoke throws the one a server answers with, and a HandleRequest handler throws one ...
void Throw(T &&e2Throw)
like the builtin C++ 'throw', but stamps in the current Activity context and does helpful,...
const InternetMediaType kContentType
void DeSerialize(const Memory::BLOB &b, Request *request, const Options &options={})
The SOAP message read: what Serialize writes, and any other's. Throws DataExchange::BadFormatExceptio...
Memory::BLOB Serialize(const Request &request, const Options &options={})
The SOAP message - its XML, UTF-8 - which sends it: a call, its answer, or a fault.
Response Invoke(const URI &url, const String &soapAction, const Request &request, const Options &options={})
Call a method: POST request to url, with soapAction (in quotes, as SOAPAction headers are: section 6....
void HandleRequest(WebServer::Message &m, const function< Response(const Request &)> &handler, const Options &options={})
Answer a call, at its URL's route: 415 (Unsupported Media Type) if its body is not text/xml; else its...
An element's or attribute's name (local-name, not including a prefix), plus the namespace that name i...
optional< URI > fNamespace
The namespace fName is in, however the document said so - nullopt for a name in no namespace.
A call's answer, the method having not been done: why not (section 4.4) - sent in an HTTP response wh...
nonvirtual String ToString() const
optional< String > fDetail
What SOAP must be told that has a sensible default - taken by Serialize and DeSerialize,...
EncodingStyleAttribute fEncodingStyleAttribute
Where Serialize writes encodingStyle - nowhere, the default, so what is written holds to SOAP's schem...
DataExchange::XML::Schema::Ptr fValidateReadAgainst
Check a message being read against this schema - EnvelopeSchema - throwing where it does not hold; nu...
DataExchange::XML::Options fXMLOptions
How XML is read and written - by default, with Stroika's default provider; nearly always left as is (...
A call of a method (section 7.1): the Body's one element, named the method, in its namespace - its in...
nonvirtual optional< String > LookupArgument(const String &name) const
nonvirtual String ToString() const
A call's answer, the method having been done (section 7.1): the Body's one element,...
nonvirtual optional< String > LookupArgument(const String &name) const
nonvirtual String ToString() const