Xerces 3 revisions.
[shibboleth/cpp-xmltooling.git] / xmltooling / soap / impl / SOAPClient.cpp
1 /*
2  *  Copyright 2001-2007 Internet2
3  * 
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 /**
18  * SOAPClient.cpp
19  * 
20  * Implements SOAP 1.1 messaging over a transport.
21  */
22
23 #include "internal.h"
24 #include "exceptions.h"
25 #include "logging.h"
26 #include "soap/SOAP.h"
27 #include "soap/SOAPClient.h"
28 #include "util/XMLHelper.h"
29 #include "validation/ValidatorSuite.h"
30
31 #include <sstream>
32
33 using namespace soap11;
34 using namespace xmltooling::logging;
35 using namespace xmltooling;
36 using namespace xercesc;
37 using namespace std;
38
39 void SOAPTransport::send(istream* in)
40 {
41     if (!in)
42         throw IOException("SOAP transport does not support an empty request body.");
43     return send(*in);
44 }
45
46 SOAPClient::~SOAPClient()
47 {
48     delete m_transport;
49 }
50
51 void SOAPClient::reset()
52 {
53     delete m_transport;
54     m_transport=NULL;
55 }
56
57 void SOAPClient::send(const Envelope& env, const SOAPTransport::Address& addr)
58 {
59     // Prepare a transport object.
60     const char* pch = strchr(addr.m_endpoint,':');
61     if (!pch)
62         throw IOException("SOAP endpoint was not a URL.");
63     string scheme(addr.m_endpoint, pch-addr.m_endpoint);
64     m_transport = XMLToolingConfig::getConfig().SOAPTransportManager.newPlugin(scheme.c_str(), addr);
65     prepareTransport(*m_transport);
66     
67     Category& log = Category::getInstance(XMLTOOLING_LOGCAT".SOAPClient");
68     if (log.isDebugEnabled())
69         log.debugStream() << "marshalled envelope:\n" << env << logging::eol;
70     
71     // Serialize envelope.
72     stringstream s;
73     s << env;
74     
75     // Send to peer.
76     m_transport->send(s);
77 }
78
79 Envelope* SOAPClient::receive()
80 {
81     if (!m_transport)
82         throw IOException("No call is active.");
83     
84     // If we can get the stream, then the call is still active.
85     istream& out = m_transport->receive();
86     if (!out)
87         return NULL;    // nothing yet
88     
89     // Check content type.
90     string s = m_transport->getContentType();
91     if (s.find("text/xml") == string::npos)
92         throw IOException("Incorrect content type ($1) for SOAP response.", params(1,s.c_str() ? s.c_str() : "none"));
93     
94     // Parse and bind the document into an XMLObject.
95     DOMDocument* doc = (m_validate ? XMLToolingConfig::getConfig().getValidatingParser()
96         : XMLToolingConfig::getConfig().getParser()).parse(out); 
97     XercesJanitor<DOMDocument> janitor(doc);
98
99     Category& log = Category::getInstance(XMLTOOLING_LOGCAT".SOAPClient");
100     if (log.isDebugEnabled()) {
101 #ifdef XMLTOOLING_LOG4SHIB
102         log.debugStream() << "received XML:\n" << *(doc->getDocumentElement()) << logging::eol;
103 #else
104         string buf;
105         XMLHelper::serialize(doc->getDocumentElement(), buf);
106         log.debugStream() << "received XML:\n" << buf << logging::eol;
107 #endif
108     }
109     
110     auto_ptr<XMLObject> xmlObject(XMLObjectBuilder::buildOneFromElement(doc->getDocumentElement(), true));
111     janitor.release();
112     if (!m_validate)
113         SchemaValidators.validate(xmlObject.get());
114
115     Envelope* env = dynamic_cast<Envelope*>(xmlObject.get());
116     if (!env)
117         throw IOException("Response was not a SOAP 1.1 Envelope.");
118
119     Body* body = env->getBody();
120     if (body && body->hasChildren()) {
121         //Check for a Fault.
122         const Fault* fault = dynamic_cast<Fault*>(body->getUnknownXMLObjects().front());
123         if (fault && handleFault(*fault))
124             throw IOException("SOAP client detected a Fault.");
125     }
126
127     xmlObject.release();
128     return env;
129 }
130
131 bool SOAPClient::handleFault(const Fault& fault)
132 {
133     const xmltooling::QName* code = (fault.getFaultcode() ? fault.getFaultcode()->getCode() : NULL);
134     auto_ptr_char str((fault.getFaultstring() ? fault.getFaultstring()->getString() : NULL));
135     Category::getInstance(XMLTOOLING_LOGCAT".SOAPClient").error(
136         "SOAP client detected a Fault: (%s) (%s)",
137         (code ? code->toString().c_str() : "no code"),
138         (str.get() ? str.get() : "no message")
139         );
140     return true;
141 }