Moved CGI parsing to OS, add handler base for remoting HTTP req/resp data.
authorcantor <cantor@cb58f699-b61c-0410-a6fe-9272a202ed29>
Wed, 28 Feb 2007 16:53:10 +0000 (16:53 +0000)
committercantor <cantor@cb58f699-b61c-0410-a6fe-9272a202ed29>
Wed, 28 Feb 2007 16:53:10 +0000 (16:53 +0000)
git-svn-id: https://svn.middleware.georgetown.edu/cpp-sp/trunk@2178 cb58f699-b61c-0410-a6fe-9272a202ed29

shibsp/AbstractSPRequest.cpp
shibsp/AbstractSPRequest.h
shibsp/Makefile.am
shibsp/remoting/RemotedHandler.h [new file with mode: 0644]
shibsp/remoting/impl/RemotedHandler.cpp [new file with mode: 0644]
shibsp/shibsp.vcproj
shibsp/util/CGIParser.cpp [deleted file]
shibsp/util/CGIParser.h [deleted file]

index 85dfe25..2d67b78 100644 (file)
 #include "Application.h"
 #include "ServiceProvider.h"
 #include "SessionCache.h"
-#include "util/CGIParser.h"
 
 #include <log4cpp/Category.hh>
+#include <saml/util/CGIParser.h>
 
 using namespace shibsp;
+using namespace opensaml;
 using namespace xmltooling;
 using namespace log4cpp;
 using namespace std;
 
 AbstractSPRequest::AbstractSPRequest()
     : m_sp(NULL), m_mapper(NULL), m_app(NULL), m_sessionTried(false), m_session(NULL),
-        m_log(&Category::getInstance(SHIBSP_LOGCAT)), m_parser(NULL)
+        m_log(&Category::getInstance(SHIBSP_LOGCAT".SPRequest")), m_parser(NULL)
 {
     m_sp=SPConfig::getConfig().getServiceProvider();
     m_sp->lock();
index 07000c3..ad3c269 100644 (file)
 
 #include <shibsp/exceptions.h>
 #include <shibsp/SPRequest.h>
+#include <saml/util/CGIParser.h>
 
 namespace shibsp {
-
-    class SHIBSP_API CGIParser;
     
     /**
      * Abstract base for SPRequest implementations
@@ -76,7 +75,7 @@ namespace shibsp {
         void* m_log; // declared void* to avoid log4cpp header conflicts in Apache
         mutable std::string m_handlerURL;
         mutable std::map<std::string,std::string> m_cookieMap;
-        mutable CGIParser* m_parser;
+        mutable opensaml::CGIParser* m_parser;
     };
 };
 
index 11b5fc4..1e0c521 100644 (file)
@@ -57,7 +57,8 @@ mdinclude_HEADERS = \
 
 reminclude_HEADERS = \
        ddf.h \
-       ListenerService.h
+       ListenerService.h \
+       RemotedHandler.h
        
 secinclude_HEADERS = \
        security/PKIXTrustEngine.h
@@ -95,6 +96,7 @@ libshibsp_la_SOURCES = \
        metadata/MetadataExtSchemaValidators.cpp \
        remoting/impl/ddf.cpp \
        remoting/impl/ListenerService.cpp \
+       remoting/impl/RemotedHandler.cpp \
        remoting/impl/SocketListener.cpp \
        remoting/impl/TCPListener.cpp \
        remoting/impl/UnixListener.cpp \
diff --git a/shibsp/remoting/RemotedHandler.h b/shibsp/remoting/RemotedHandler.h
new file mode 100644 (file)
index 0000000..f779ebc
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ *  Copyright 2001-2007 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.
+ */
+
+/**
+ * @file shibsp/remoting/RemotedHandler.h
+ * 
+ * Base class for handlers that need SP request/response layer to be remoted. 
+ */
+
+#ifndef __shibsp_remhandler_h__
+#define __shibsp_remhandler_h__
+
+#include <shibsp/SPRequest.h>
+#include <shibsp/Handler.h>
+#include <shibsp/remoting/ListenerService.h>
+
+namespace shibsp {
+
+    /**
+     * Base class for handlers that need HTTP request/response layer to be remoted.
+     */
+    class SHIBSP_API RemotedHandler : public Handler, public Remoted 
+    {
+    public:
+        virtual ~RemotedHandler() {}
+
+    protected:
+        RemotedHandler(const xercesc::DOMElement* e=NULL) {}
+
+        /**
+         * Wraps a request by annotating an outgoing data flow with the data needed
+         * to remote the request information.
+         *
+         * @param request   an SPRequest to remote
+         * @param in        the dataflow object to annotate
+         * @param headers   array of request headers to copy to remote request
+         * @param certs     true iff client certificates should be available for the remote request
+         * @return  the input dataflow object
+         */
+        const DDF& wrap(const SPRequest& request, DDF& in, const std::vector<std::string>& headers, bool certs=false) const;
+        
+        /**
+         * Unwraps a response by examining an incoming data flow to determine
+         * whether a response was produced by the remoted handler. 
+         * 
+         * @param request   SP request context
+         * @param out       the dataflow object to unpack
+         * @return  a pair containing a "request completed" indicator and a server-specific response code
+         */
+        virtual std::pair<bool,long> unwrap(SPRequest& request, DDF& out) const;
+        
+        /**
+         * Builds a new request instance around a remoted data object.
+         * 
+         * @param in    the dataflow object containing the remoted request
+         * @return  a call-specific request object based on the input, to be freed by the caller 
+         */
+        opensaml::HTTPRequest* getRequest(DDF& in) const;
+        
+        /**
+         * Builds a new response instance around an outgoing data object.
+         * 
+         * @param out   the dataflow object to be returned by the caller
+         * @return  a call-specific response object, to be freed by the caller 
+         */
+        opensaml::HTTPResponse* getResponse(DDF& out) const;
+    };
+};
+
+#endif /* __shibsp_remhandler_h__ */
diff --git a/shibsp/remoting/impl/RemotedHandler.cpp b/shibsp/remoting/impl/RemotedHandler.cpp
new file mode 100644 (file)
index 0000000..6a8695b
--- /dev/null
@@ -0,0 +1,270 @@
+/*
+ *  Copyright 2001-2007 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.
+ */
+
+/**
+ * RemotedHandler.cpp
+ * 
+ * Base class for handlers that need SP request/response layer to be remoted. 
+ */
+
+#include "internal.h"
+#include "remoting/RemotedHandler.h"
+
+#include <algorithm>
+#include <log4cpp/Category.hh>
+#include <saml/util/CGIParser.h>
+#include <xmltooling/unicode.h>
+#include <xsec/enc/OpenSSL/OpenSSLCryptoX509.hpp>
+#include <xsec/enc/XSECCryptoException.hpp>
+#include <xsec/framework/XSECException.hpp>
+#include <xsec/framework/XSECProvider.hpp>
+
+using namespace shibsp;
+using namespace opensaml;
+using namespace xmltooling;
+using namespace log4cpp;
+using namespace xercesc;
+using namespace std;
+
+namespace shibsp {
+    class SHIBSP_DLLLOCAL RemotedRequest : public virtual opensaml::HTTPRequest 
+    {
+        DDF& m_input;
+        mutable CGIParser* m_parser;
+        mutable vector<XSECCryptoX509*> m_certs;
+    public:
+        RemotedRequest(DDF& input) : m_input(input), m_parser(NULL) {}
+        virtual ~RemotedRequest() {
+            for_each(m_certs.begin(), m_certs.end(), xmltooling::cleanup<XSECCryptoX509>());
+            delete m_parser;
+        }
+
+        // GenericRequest
+        const char* getScheme() const {
+            return m_input["scheme"].string();
+        }
+        const char* getHostname() const {
+            return m_input["hostname"].string();
+        }
+        int getPort() const {
+            return m_input["port"].integer();
+        }
+        std::string getContentType() const {
+            DDF s = m_input["content_type"];
+            return s.string() ? s.string() : "";
+        }
+        long getContentLength() const {
+            return m_input["content_length"].integer();
+        }
+        const char* getRequestBody() const {
+            return m_input["body"].string();
+        }
+
+        const char* getParameter(const char* name) const;
+        std::vector<const char*>::size_type getParameters(const char* name, std::vector<const char*>& values) const;
+        
+        std::string getRemoteUser() const {
+            DDF s = m_input["remote_user"];
+            return s.string() ? s.string() : "";
+        }
+        std::string getRemoteAddr() const {
+            DDF s = m_input["client_addr"];
+            return s.string() ? s.string() : "";
+        }
+
+        const std::vector<XSECCryptoX509*>& getClientCertificates() const;
+        
+        // HTTPRequest
+        const char* getMethod() const {
+            return m_input["method"].string();
+        }
+        const char* getRequestURI() const {
+            return m_input["uri"].string();
+        }
+        const char* getRequestURL() const {
+            return m_input["url"].string();
+        }
+        const char* getQueryString() const {
+            return m_input["query"].string();
+        }
+        std::string getHeader(const char* name) const {
+            DDF s = m_input["headers"][name];
+            return s.string() ? s.string() : "";
+        }
+    };
+
+    class SHIBSP_DLLLOCAL RemotedResponse : public virtual opensaml::HTTPResponse 
+    {
+        DDF& m_output;
+    public:
+        RemotedResponse(DDF& output) : m_output(output) {}
+        virtual ~RemotedResponse() {}
+       
+        // GenericResponse
+        long sendResponse(std::istream& inputStream, long status);
+        
+        // HTTPResponse
+        void setResponseHeader(const char* name, const char* value);
+        long sendRedirect(const char* url);
+    };
+}
+
+const char* RemotedRequest::getParameter(const char* name) const
+{
+    if (!m_parser)
+        m_parser=new CGIParser(*this);
+    
+    pair<CGIParser::walker,CGIParser::walker> bounds=m_parser->getParameters(name);
+    return (bounds.first==bounds.second) ? NULL : bounds.first->second;
+}
+
+std::vector<const char*>::size_type RemotedRequest::getParameters(const char* name, std::vector<const char*>& values) const
+{
+    if (!m_parser)
+        m_parser=new CGIParser(*this);
+
+    pair<CGIParser::walker,CGIParser::walker> bounds=m_parser->getParameters(name);
+    while (bounds.first!=bounds.second) {
+        values.push_back(bounds.first->second);
+        ++bounds.first;
+    }
+    return values.size();
+}
+
+const std::vector<XSECCryptoX509*>& RemotedRequest::getClientCertificates() const
+{
+    if (m_certs.empty()) {
+        DDF cert = m_input["certificates"].first();
+        while (cert.isstring()) {
+            try {
+                auto_ptr<XSECCryptoX509> x509(XSECPlatformUtils::g_cryptoProvider->X509());
+                x509->loadX509Base64Bin(cert.string(), cert.strlen());
+                m_certs.push_back(x509.release());
+            }
+            catch(XSECException& e) {
+                auto_ptr_char temp(e.getMsg());
+                Category::getInstance(SHIBSP_LOGCAT".SPRequest").error("XML-Security exception loading client certificate: %s", temp.get());
+            }
+            catch(XSECCryptoException& e) {
+                Category::getInstance(SHIBSP_LOGCAT".SPRequest").error("XML-Security exception loading client certificate: %s", e.getMsg());
+            }
+            cert = cert.next();
+        }
+    }
+    return m_certs;
+}
+
+long RemotedResponse::sendResponse(std::istream& in, long status)
+{
+    string msg;
+    char buf[1024];
+    while (in) {
+        in.read(buf,1024);
+        msg.append(buf,in.gcount());
+    }
+    if (!m_output.isstruct())
+        m_output.structure();
+    m_output.addmember("response.data").string(msg.c_str());
+    m_output.addmember("response.status").integer(status);
+    return status;
+}
+
+void RemotedResponse::setResponseHeader(const char* name, const char* value)
+{
+    if (!m_output.isstruct())
+        m_output.structure();
+    DDF hdrs = m_output["headers"];
+    if (hdrs.isnull())
+        hdrs = m_output.addmember("headers").structure();
+    hdrs.addmember(name).string(value);
+}
+
+long RemotedResponse::sendRedirect(const char* url)
+{
+    if (!m_output.isstruct())
+        m_output.structure();
+    m_output.addmember("redirect").string(url);
+    return HTTPResponse::SAML_HTTP_STATUS_MOVED;
+}
+
+
+const DDF& RemotedHandler::wrap(const SPRequest& request, DDF& in, const vector<string>& headers, bool certs) const
+{
+    if (!in.isstruct())
+        in.structure();
+    in.addmember("scheme").string(request.getScheme());
+    in.addmember("hostname").string(request.getHostname());
+    in.addmember("port").integer(request.getPort());
+    in.addmember("content_type").string(request.getContentType().c_str());
+    in.addmember("content_length").integer(request.getContentLength());
+    in.addmember("body").string(request.getRequestBody());
+    in.addmember("remote_user").string(request.getRemoteUser().c_str());
+    in.addmember("client_addr").string(request.getRemoteAddr().c_str());
+    in.addmember("method").string(request.getMethod());
+    in.addmember("uri").string(request.getRequestURI());
+    in.addmember("url").string(request.getRequestURL());
+    in.addmember("query").string(request.getQueryString());
+
+    string hdr;
+    DDF hin = in.addmember("headers").structure();
+    for (vector<string>::const_iterator h = headers.begin(); h!=headers.end(); ++h) {
+        hdr = request.getHeader(h->c_str());
+        if (!hdr.empty())
+            hin.addmember(h->c_str()).string(hdr.c_str());
+    }
+
+    if (certs) {
+        const vector<XSECCryptoX509*>& xvec = request.getClientCertificates();
+        if (!xvec.empty()) {
+            hin = in.addmember("certificates").list();
+            for (vector<XSECCryptoX509*>::const_iterator x = xvec.begin(); x!=xvec.end(); ++x) {
+                DDF x509 = DDF(NULL).string((*x)->getDEREncodingSB().rawCharBuffer());
+                hin.add(x509);
+            }
+        }
+    }
+
+    return in;
+}
+
+pair<bool,long> RemotedHandler::unwrap(SPRequest& request, DDF& out) const
+{
+    DDF h = out["headers"];
+    h = h.first();
+    while (h.isstring()) {
+        request.setResponseHeader(h.name(), h.string());
+        h = h.next();
+    }
+    h = out["redirect"];
+    if (h.isstring())
+        return make_pair(true, request.sendRedirect(h.string()));
+    h = out["response"];
+    if (h.isstruct()) {
+        istringstream s(h["data"].string());
+        return make_pair(true, static_cast<GenericResponse&>(request).sendResponse(s, h["status"].integer()));
+    }
+    return make_pair(false,0);
+}
+
+HTTPRequest* RemotedHandler::getRequest(DDF& in) const
+{
+    return new RemotedRequest(in);
+}
+
+HTTPResponse* RemotedHandler::getResponse(DDF& out) const
+{
+    return new RemotedResponse(out);
+}
index cf1f41c..4701abe 100644 (file)
                                Name="util"\r
                                >\r
                                <File\r
-                                       RelativePath=".\util\CGIParser.cpp"\r
-                                       >\r
-                               </File>\r
-                               <File\r
                                        RelativePath=".\util\DOMPropertySet.cpp"\r
                                        >\r
                                </File>\r
                                                >\r
                                        </File>\r
                                        <File\r
+                                               RelativePath=".\remoting\impl\RemotedHandler.cpp"\r
+                                               >\r
+                                       </File>\r
+                                       <File\r
                                                RelativePath=".\remoting\impl\SocketListener.cpp"\r
                                                >\r
                                        </File>\r
                                Name="util"\r
                                >\r
                                <File\r
-                                       RelativePath=".\util\CGIParser.h"\r
-                                       >\r
-                               </File>\r
-                               <File\r
                                        RelativePath=".\util\DOMPropertySet.h"\r
                                        >\r
                                </File>\r
                                        RelativePath=".\remoting\ListenerService.h"\r
                                        >\r
                                </File>\r
+                               <File\r
+                                       RelativePath=".\remoting\RemotedHandler.h"\r
+                                       >\r
+                               </File>\r
                        </Filter>\r
                        <Filter\r
                                Name="attribute"\r
diff --git a/shibsp/util/CGIParser.cpp b/shibsp/util/CGIParser.cpp
deleted file mode 100644 (file)
index 9ecf107..0000000
+++ /dev/null
@@ -1,124 +0,0 @@
-/*
- *  Copyright 2001-2007 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.
- */
-
-/**
- * CGIParser.cpp
- * 
- * CGI GET/POST parameter parsing
- */
-
-#include "internal.h"
-#include "util/CGIParser.h"
-
-#include <saml/SAMLConfig.h>
-#include <saml/binding/URLEncoder.h>
-
-using namespace shibsp;
-using namespace opensaml;
-using namespace std;
-
-
-CGIParser::CGIParser(const SPRequest& request)
-{
-    const char* pch=NULL;
-    if (!strcmp(request.getMethod(),"POST"))
-        pch=request.getRequestBody();
-    else
-        pch=request.getQueryString();
-    size_t cl=pch ? strlen(pch) : 0;
-    
-        
-    while (cl && pch) {
-        char *name;
-        char *value;
-        value=fmakeword('&',&cl,&pch);
-        plustospace(value);
-        SAMLConfig::getConfig().getURLEncoder()->decode(value);
-        name=makeword(value,'=');
-        kvp_map.insert(pair<string,char*>(name,value));
-        free(name);
-    }
-}
-
-CGIParser::~CGIParser()
-{
-    for (multimap<string,char*>::iterator i=kvp_map.begin(); i!=kvp_map.end(); i++)
-        free(i->second);
-}
-
-pair<CGIParser::walker,CGIParser::walker> CGIParser::getParameters(const char* name) const
-{
-    return kvp_map.equal_range(name);
-}
-
-/* Parsing routines modified from NCSA source. */
-char* CGIParser::makeword(char *line, char stop)
-{
-    int x = 0,y;
-    char *word = (char *) malloc(sizeof(char) * (strlen(line) + 1));
-
-    for(x=0;((line[x]) && (line[x] != stop));x++)
-        word[x] = line[x];
-
-    word[x] = '\0';
-    if(line[x])
-        ++x;
-    y=0;
-
-    while(line[x])
-      line[y++] = line[x++];
-    line[y] = '\0';
-    return word;
-}
-
-char* CGIParser::fmakeword(char stop, size_t *cl, const char** ppch)
-{
-    int wsize;
-    char *word;
-    int ll;
-
-    wsize = 1024;
-    ll=0;
-    word = (char *) malloc(sizeof(char) * (wsize + 1));
-
-    while(1)
-    {
-        word[ll] = *((*ppch)++);
-        if(ll==wsize-1)
-        {
-            word[ll+1] = '\0';
-            wsize+=1024;
-            word = (char *)realloc(word,sizeof(char)*(wsize+1));
-        }
-        --(*cl);
-        if((word[ll] == stop) || word[ll] == EOF || (!(*cl)))
-        {
-            if(word[ll] != stop)
-                ll++;
-            word[ll] = '\0';
-            return word;
-        }
-        ++ll;
-    }
-}
-
-void CGIParser::plustospace(char *str)
-{
-    register int x;
-
-    for(x=0;str[x];x++)
-        if(str[x] == '+') str[x] = ' ';
-}
diff --git a/shibsp/util/CGIParser.h b/shibsp/util/CGIParser.h
deleted file mode 100644 (file)
index f711358..0000000
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- *  Copyright 2001-2007 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.
- */
-
-/**
- * @file shibsp/util/CGIParser.h
- * 
- * CGI GET/POST parameter parsing
- */
-
-#ifndef __shibsp_cgi_h__
-#define __shibsp_cgi_h__
-
-#include <shibsp/SPRequest.h>
-
-namespace shibsp {
-
-    /**
-     * CGI GET/POST parameter parsing
-     */
-    class SHIBSP_API CGIParser
-    {
-        MAKE_NONCOPYABLE(CGIParser);
-    public:
-        /**
-         * Constructor
-         * 
-         * @param request   SP request
-         */
-        CGIParser(const SPRequest& request);
-
-        ~CGIParser();
-
-        typedef std::multimap<std::string,char*>::const_iterator walker;
-        
-        /**
-         * Returns a pair of bounded iterators around the values of a parameter.
-         * 
-         * @param name  name of parameter
-         * @return  a pair of multimap iterators surrounding the matching value(s)
-         */
-        std::pair<walker,walker> getParameters(const char* name) const;
-        
-    private:
-        char* fmakeword(char stop, unsigned int *cl, const char** ppch);
-        char* makeword(char *line, char stop);
-        void plustospace(char *str);
-
-        std::multimap<std::string,char*> kvp_map;
-    };
-};
-
-#endif /* __shibsp_cgi_h__ */