From: Scott Cantor Date: Wed, 22 Nov 2006 02:18:37 +0000 (+0000) Subject: Basic SOAP client, reworked transport streams. X-Git-Tag: 1.0-alpha1~144 X-Git-Url: http://www.project-moonshot.org/gitweb/?p=shibboleth%2Fcpp-xmltooling.git;a=commitdiff_plain;h=c7288a45e5a581ea8253cf4c1d45e84281172584 Basic SOAP client, reworked transport streams. --- diff --git a/.gitignore b/.gitignore index 8e8a97f..dec8e4c 100644 --- a/.gitignore +++ b/.gitignore @@ -38,3 +38,4 @@ /pkginfo /stamp-h1 /xmltooling.spec +/.settings diff --git a/xmltooling/Makefile.am b/xmltooling/Makefile.am index dba2c6e..d30c2a2 100644 --- a/xmltooling/Makefile.am +++ b/xmltooling/Makefile.am @@ -149,6 +149,7 @@ libxmltooling_la_SOURCES = \ io/AbstractXMLObjectUnmarshaller.cpp \ signature/impl/KeyInfoImpl.cpp \ signature/impl/KeyInfoSchemaValidators.cpp \ + soap/impl/SOAPClient.cpp \ soap/impl/SOAPImpl.cpp \ soap/impl/SOAPSchemaValidators.cpp \ soap/impl/CURLSOAPTransport.cpp \ diff --git a/xmltooling/soap/SOAPClient.h b/xmltooling/soap/SOAPClient.h index 3e11436..3263310 100644 --- a/xmltooling/soap/SOAPClient.h +++ b/xmltooling/soap/SOAPClient.h @@ -23,59 +23,79 @@ #ifndef __xmltooling_soap11client_h__ #define __xmltooling_soap11client_h__ -#include +#include #include namespace soap11 { - + + class XMLTOOL_API Envelope; + /** * Implements SOAP 1.1 messaging over a transport. * * In the abstract, this can be a one-way exchange, or use asynchronous - * transports, but this is mostly theoretical. + * transports, but this is mostly theoretical at this point. */ class XMLTOOL_API SOAPClient { MAKE_NONCOPYABLE(SOAPClient); public: - SOAPClient() : m_response(NULL) {} + SOAPClient() : m_validate(false), m_transport(NULL) {} virtual ~SOAPClient(); /** - * Sends the supplied envelope to the identified recipient/endpoint. + * Controls schema validation of incoming XML messages. + * This is separate from other forms of programmatic validation of objects, + * but can detect a much wider range of syntax errors. * - *

The caller is responsible for freeing the outgoing envelope. + * @param validate true iff the client should use a validating XML parser + */ + void setValidating(bool validate=true) { + m_validate = validate; + } + + /** + * Sends the supplied envelope to the identified recipient/endpoint. * *

The client object will instantiate a transport layer object * appropriate for the endpoint URL provided and supply it to the * prepareTransport() method below. * * @param env SOAP envelope to send - * @param to identifier/name of party to send message to + * @param peer peer to send message to, expressed in TrustEngine terms * @param endpoint URL of endpoint to recieve message */ - virtual void send(const Envelope* env, const char* to, const char* endpoint); + virtual void send(const Envelope* env, const xmltooling::KeyInfoSource& peer, const char* endpoint); /** * Returns the response message, if any. As long as a response is * "expected" but not available, NULL will be returned. If no response * will be forthcoming, an exception is raised. * - *

The caller is responsible for freeing the incoming envelope. + *

The caller is responsible for freeing the returned envelope. + * + *

Once a response is returned, the object will be reset for a subsequent call. */ virtual Envelope* receive(); + + /** + * Resets the object for another call. + */ + virtual void reset(); protected: /** * Allows client to supply transport-layer settings prior to sending message. * * @param transport reference to transport layer - * @return true iff transport preparation was successful */ - virtual bool prepareTransport(const xmltooling::SOAPTransport& transport) {} + virtual void prepareTransport(const xmltooling::SOAPTransport& transport) {} + /** Flag controlling schema validation. */ + bool m_validate; + /** Holds response until retrieved by caller. */ - Envelope* m_response; + xmltooling::SOAPTransport* m_transport; }; }; diff --git a/xmltooling/soap/SOAPTransport.h b/xmltooling/soap/SOAPTransport.h index c25f43d..395a327 100644 --- a/xmltooling/soap/SOAPTransport.h +++ b/xmltooling/soap/SOAPTransport.h @@ -110,12 +110,20 @@ namespace xmltooling { #endif /** - * Sends a stream of data over the transport, and writes the results into another. + * Sends a stream of data over the transport. The function may return without + * having received any data, depending on the nature of the transport. * * @param in input stream to send - * @param out output stream to write result into */ - virtual size_t send(std::istream& in, std::ostream& out)=0; + virtual void send(std::istream& in)=0; + + /** + * Returns reference to response stream. The resulting stream must be + * checked directly to determine whether data is available. + * + * @return reference to a stream containing the response, if any + */ + virtual std::istream& receive()=0; /** * Returns the MIME type of the response, if any. diff --git a/xmltooling/soap/impl/CURLSOAPTransport.cpp b/xmltooling/soap/impl/CURLSOAPTransport.cpp index 8b2aad3..da8a3f5 100644 --- a/xmltooling/soap/impl/CURLSOAPTransport.cpp +++ b/xmltooling/soap/impl/CURLSOAPTransport.cpp @@ -77,6 +77,7 @@ namespace xmltooling { curl_easy_setopt(m_handle,CURLOPT_HTTPAUTH,0); curl_easy_setopt(m_handle,CURLOPT_USERPWD,NULL); curl_easy_setopt(m_handle,CURLOPT_HEADERDATA,this); + m_headers=curl_slist_append(m_headers,"Content-Type: text/xml"); } virtual ~CURLSOAPTransport() { @@ -117,7 +118,11 @@ namespace xmltooling { return true; } - size_t send(istream& in, ostream& out); + void send(istream& in); + + istream& receive() { + return m_stream; + } string getContentType() const; @@ -141,6 +146,7 @@ namespace xmltooling { const KeyInfoSource& m_peer; string m_endpoint; CURL* m_handle; + stringstream m_stream; mutable struct curl_slist* m_headers; map > m_response_headers; mutable const OpenSSLCredentialResolver* m_credResolver; @@ -325,7 +331,7 @@ string CURLSOAPTransport::getContentType() const return content_type ? content_type : ""; } -size_t CURLSOAPTransport::send(istream& in, ostream& out) +void CURLSOAPTransport::send(istream& in) { #ifdef _DEBUG xmltooling::NDC ndc("send"); @@ -337,11 +343,9 @@ size_t CURLSOAPTransport::send(istream& in, ostream& out) // caller should have executed any set functions to manipulate it. // Setup standard per-call curl properties. - size_t content_length=0; - pair output = make_pair(&out,&content_length); curl_easy_setopt(m_handle,CURLOPT_POST,1); curl_easy_setopt(m_handle,CURLOPT_READDATA,&in); - curl_easy_setopt(m_handle,CURLOPT_FILE,&output); + curl_easy_setopt(m_handle,CURLOPT_FILE,&m_stream); curl_easy_setopt(m_handle,CURLOPT_DEBUGDATA,&log_curl); char curl_errorbuf[CURL_ERROR_SIZE]; @@ -374,8 +378,6 @@ size_t CURLSOAPTransport::send(istream& in, ostream& out) string("CURLSOAPTransport::send() failed while contacting SOAP responder: ") + (curl_errorbuf[0] ? curl_errorbuf : "no further information available")); } - - return content_length; } // callback to buffer headers from server @@ -417,10 +419,8 @@ size_t xmltooling::curl_read_hook(void* ptr, size_t size, size_t nmemb, void* st // callback to buffer data from server size_t xmltooling::curl_write_hook(void* ptr, size_t size, size_t nmemb, void* stream) { - pair* output = reinterpret_cast*>(stream); size_t len = size*nmemb; - output->first->write(reinterpret_cast(ptr),len); - *(output->second) += len; + reinterpret_cast(stream)->write(reinterpret_cast(ptr),len); return len; } diff --git a/xmltooling/soap/impl/SOAPClient.cpp b/xmltooling/soap/impl/SOAPClient.cpp new file mode 100644 index 0000000..584c7d0 --- /dev/null +++ b/xmltooling/soap/impl/SOAPClient.cpp @@ -0,0 +1,87 @@ +/* + * Copyright 2001-2006 Internet2 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * SOAPClient.cpp + * + * Implements SOAP 1.1 messaging over a transport. + */ + +#include "internal.h" +#include "exceptions.h" +#include "soap/SOAP.h" +#include "soap/SOAPClient.h" +#include "util/XMLHelper.h" + +#include + +using namespace soap11; +using namespace xmltooling; +using namespace std; + +SOAPClient::~SOAPClient() +{ + reset(); +} + +void SOAPClient::reset() +{ + delete m_transport; + m_transport=NULL; +} + +void SOAPClient::send(const Envelope* env, const KeyInfoSource& peer, const char* endpoint) +{ + // Prepare a transport object. + const char* pch = strchr(endpoint,':'); + if (!pch) + throw IOException("SOAP endpoint was not a URL."); + string scheme(endpoint, pch-endpoint); + m_transport = XMLToolingConfig::getConfig().SOAPTransportManager.newPlugin(scheme.c_str(), make_pair(&peer,endpoint)); + prepareTransport(*m_transport); + + // Serialize envelope. + stringstream s; + XMLHelper::serialize(env->marshall(), s); + + // Send to peer. + m_transport->send(s); +} + +Envelope* SOAPClient::receive() +{ + if (!m_transport) + throw IOException("No call is active."); + + // If we can get the stream, then the call is still active. + istream& out = m_transport->receive(); + if (!out) + return NULL; // nothing yet + + // Parse and bind the document into an XMLObject. + DOMDocument* doc = (m_validate ? XMLToolingConfig::getConfig().getValidatingParser() + : XMLToolingConfig::getConfig().getParser()).parse(out); + XercesJanitor janitor(doc); + auto_ptr xmlObject(XMLObjectBuilder::buildOneFromElement(doc->getDocumentElement(), true)); + janitor.release(); + + Envelope* env = dynamic_cast(xmlObject.get()); + if (!env) + throw IOException("Response was not a SOAP 1.1 Envelope."); + reset(); + xmlObject.release(); + return env; +} diff --git a/xmltooling/xmltooling.vcproj b/xmltooling/xmltooling.vcproj index d662bf2..0318607 100644 --- a/xmltooling/xmltooling.vcproj +++ b/xmltooling/xmltooling.vcproj @@ -408,6 +408,10 @@ > + +