Basic SOAP client, reworked transport streams.
authorScott Cantor <cantor.2@osu.edu>
Wed, 22 Nov 2006 02:18:37 +0000 (02:18 +0000)
committerScott Cantor <cantor.2@osu.edu>
Wed, 22 Nov 2006 02:18:37 +0000 (02:18 +0000)
.gitignore
xmltooling/Makefile.am
xmltooling/soap/SOAPClient.h
xmltooling/soap/SOAPTransport.h
xmltooling/soap/impl/CURLSOAPTransport.cpp
xmltooling/soap/impl/SOAPClient.cpp [new file with mode: 0644]
xmltooling/xmltooling.vcproj

index 8e8a97f..dec8e4c 100644 (file)
@@ -38,3 +38,4 @@
 /pkginfo
 /stamp-h1
 /xmltooling.spec
+/.settings
index dba2c6e..d30c2a2 100644 (file)
@@ -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 \
index 3e11436..3263310 100644 (file)
 #ifndef __xmltooling_soap11client_h__
 #define __xmltooling_soap11client_h__
 
-#include <xmltooling/soap/SOAP.h>
+#include <xmltooling/security/KeyInfoSource.h>
 #include <xmltooling/soap/SOAPTransport.h>
 
 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. 
          * 
-         * <p>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.
          * 
          * <p>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.
          * 
-         * <p>The caller is responsible for freeing the incoming envelope.
+         * <p>The caller is responsible for freeing the returned envelope.
+         * 
+         * <p>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;
     };
 
 };
index c25f43d..395a327 100644 (file)
@@ -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.
index 8b2aad3..da8a3f5 100644 (file)
@@ -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<string,vector<string> > 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<ostream*,size_t*> 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<ostream*,size_t*>* output = reinterpret_cast<pair<ostream*,size_t*>*>(stream); 
     size_t len = size*nmemb;
-    output->first->write(reinterpret_cast<const char*>(ptr),len);
-    *(output->second) += len;
+    reinterpret_cast<stringstream*>(stream)->write(reinterpret_cast<const char*>(ptr),len);
     return len;
 }
 
diff --git a/xmltooling/soap/impl/SOAPClient.cpp b/xmltooling/soap/impl/SOAPClient.cpp
new file mode 100644 (file)
index 0000000..584c7d0
--- /dev/null
@@ -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 <sstream>
+
+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<DOMDocument> janitor(doc);
+    auto_ptr<XMLObject> xmlObject(XMLObjectBuilder::buildOneFromElement(doc->getDocumentElement(), true));
+    janitor.release();
+
+    Envelope* env = dynamic_cast<Envelope*>(xmlObject.get());
+    if (!env)
+        throw IOException("Response was not a SOAP 1.1 Envelope.");
+    reset();
+    xmlObject.release();
+    return env;
+}
index d662bf2..0318607 100644 (file)
                                                >\r
                                        </File>\r
                                        <File\r
+                                               RelativePath=".\soap\impl\SOAPClient.cpp"\r
+                                               >\r
+                                       </File>\r
+                                       <File\r
                                                RelativePath=".\soap\impl\SOAPImpl.cpp"\r
                                                >\r
                                        </File>\r