[Inteproxy-commits] r21 - trunk

scm-commit@wald.intevation.org scm-commit at wald.intevation.org
Wed Feb 7 19:04:34 CET 2007


Author: bh
Date: 2007-02-07 19:04:34 +0100 (Wed, 07 Feb 2007)
New Revision: 21

Added:
   trunk/transcoder.py
Modified:
   trunk/ChangeLog
   trunk/InteProxy.py
Log:
* transcoder.py: New module for classes that are used to modify
http requests

* InteProxy.py (InteProxyHTTPRequestHandler.handle_proxy_request):
Use a transcoder to convert urls and -- this is new -- also the
body of the request if there is one.
(InteProxyHTTPRequestHandler.convert_url): Removed.  The
functionality of this method is now in the transcoder module


Modified: trunk/ChangeLog
===================================================================
--- trunk/ChangeLog	2007-02-07 15:33:31 UTC (rev 20)
+++ trunk/ChangeLog	2007-02-07 18:04:34 UTC (rev 21)
@@ -1,5 +1,16 @@
 2007-02-07  Bernhard Herzog  <bh at intevation.de>
 
+	* transcoder.py: New module for classes that are used to modify
+	http requests
+
+	* InteProxy.py (InteProxyHTTPRequestHandler.handle_proxy_request):
+	Use a transcoder to convert urls and -- this is new -- also the
+	body of the request if there is one.
+	(InteProxyHTTPRequestHandler.convert_url): Removed.  The
+	functionality of this method is now in the transcoder module
+
+2007-02-07  Bernhard Herzog  <bh at intevation.de>
+
 	* getpassword.py: New module for the password handling code.
 
 	* InteProxy.py: Move the password handling into a separate module.

Modified: trunk/InteProxy.py
===================================================================
--- trunk/InteProxy.py	2007-02-07 15:33:31 UTC (rev 20)
+++ trunk/InteProxy.py	2007-02-07 18:04:34 UTC (rev 21)
@@ -25,7 +25,7 @@
 import Queue
 import socket
 import proxyconnection
-from getpassword import get_password_with_cache
+from transcoder import get_transcoder
 
 inteproxy_version = "0.1.1"
 
@@ -101,8 +101,8 @@
         #
         # Create a http request for the real location
         #
-        netloc, url = self.convert_url(self.path)
-        remote_url = "https://%s%s" % (netloc, url)
+        transcoder = get_transcoder(method, self.path)
+        remote_url = transcoder.get_url()
         self.log_debug("Converted url: %r", remote_url)
 
         # If any data is associated with the request read it
@@ -112,6 +112,14 @@
             data = self.rfile.read(length)
             self.log_debug("body of client request (%d bytes):\n%r",
                            length, data)
+            content_type = self.headers.getheader("Content-Type")
+            content_type, data = transcoder.convert_body(content_type, data)
+            self.log_debug("modified body of client request (%d bytes):\n%r",
+                           len(data), data)
+            if len(data) != length:
+                self.headers["Content-length"] = str(len(data))
+            if content_type is not None:
+                self.headers["Content-type"] = content_type
         else:
             self.log_debug("client request has no body")
             data = None
@@ -216,42 +224,6 @@
         if chunked:
             write("0\r\n\r\n")
 
-    def convert_url(self, url):
-        """Extract the real host from the url and return host and new url
-
-        The URL parameter is the part of the URL from the GET or POST
-        request which is actually only the path, query and fragment in
-        the terminology of the urlparse module.  For the InteProxy, the
-        first component in the path is the actual host to connect to.
-        This method extracts that part and creates a new path with that
-        part removed.  The return value of this method is the tuple
-        (real_host, new_url) where new_url is the combined new path and
-        the original query and fragment parts.
-        """
-        scheme, netloc, path, query, fragment = urlparse.urlsplit(url)
-
-        # XXX: This is hardcoded syntax for deegree OWS Proxy
-        # Eventually this should be moved to a method of its own
-        # and anyway this is just a dirty solution since passwords
-        # are not really managed.
-        user, password = get_password_with_cache(path)
-        # TODO: Building the query this way is not safe.
-        query += "&user=%s&password=%s" % (user, password)
-
-        # both scheme and netloc must be empty strings because the url
-        # we work with is the url on the first line of a HTTP request
-        # which doesn't include them
-        assert scheme == ""
-        assert netloc == ""
-
-        split_path = path.split("/")
-        if not split_path[0]:
-            del split_path[0]
-        real_netloc = split_path.pop(0)
-        real_path = "/" + "/".join(split_path)
-        return real_netloc, urlparse.urlunsplit(("", "", real_path, query,
-                                                 fragment))
-
     def log_exception(self, exc_info):
         """Log an exception
 

Added: trunk/transcoder.py
===================================================================
--- trunk/transcoder.py	2007-02-07 15:33:31 UTC (rev 20)
+++ trunk/transcoder.py	2007-02-07 18:04:34 UTC (rev 21)
@@ -0,0 +1,145 @@
+# Copyright (C) 2007 by Intevation GmbH
+# Authors:
+# Bernhard Herzog <bh at intevation.de>
+#
+# This program is free software under the GPL (>=v2)
+# Read the file COPYING coming with the software for details.
+
+"""Classes to modify HTTP requests."""
+
+import urlparse
+from lxml import etree
+from StringIO import StringIO
+
+import getpassword
+
+
+class IdentityTranscoder:
+
+    """The null transcoder that does not acutally change anything
+
+    Other transcoders can be derived from this class so that they only
+    need to implement the methods that actually change parts of a
+    request.
+    """
+
+    def __init__(self, method, spliturl):
+        """Initializes the transcoder
+
+        Parameters:
+        method -- The HTTP method, either 'GET' or 'POST'
+        spliturl -- A tuple with the split url of the real remote host.
+                    The tuple has the form of the returl value of
+                    urlparse.urlsplit
+        """
+        self.method = method
+        self.spliturl = spliturl
+
+    def get_password(self):
+        """Returns the username and password needed for the request
+
+        The default implementation here simply uses the host and path
+        parts of the url as the parameter for the
+        get_password_with_cache function.
+        """
+        return getpassword.get_password_with_cache("".join(self.spliturl[1:3]))
+
+    def get_url(self):
+        """Returns the real url to connect to when handling this request
+
+        The default implementation simply returns the recombined url
+        given to the constructor
+        """
+        return urlparse.urlunsplit(self.spliturl)
+
+    def convert_body(self, content_type, body):
+        """Convert the body of the request
+
+        Parameters:
+        content_type -- The value of the Content-Type header of the request.
+                        If no Content-Type was give, the value is None
+        body -- The body of the request as a string.
+
+        The return value is a tuple (new_content_type, new_body).  The
+        default implementation simply returns the parameters unchanged.
+        """
+        return content_type, body
+
+
+class OWSProxyGETTranscoder(IdentityTranscoder):
+
+    """Transcoder for GET requests to the OWSProxy"""
+
+    def get_url(self):
+        """Returns the URL with username and password added to the query"""
+        scheme, netloc, path, query, fragment = self.spliturl
+        if self.method == "GET":
+            user, password = self.get_password()
+            # FIXME: quote username and password properly
+            query = "&".join(query.split("&")+ ["user=%s" % user,
+                                                "password=%s" % password])
+        return urlparse.urlunsplit((scheme, netloc, path, query, fragment))
+
+
+class OWSProxyPOSTTranscoder(IdentityTranscoder):
+
+    """Transcoder for POST requests to the OWSProxy"""
+
+    def convert_body(self, content_type, body):
+        """Adds user and password attributes to the GetFeature element.
+        """
+        t = etree.parse(StringIO(body))
+        root = t.getroot()
+        if root.tag == "{http://www.opengis.net/wfs}GetFeature":
+            user, password = self.get_password()
+            root.set("user", user)
+            root.set("password", password)
+            body = etree.tostring(t, xml_declaration=True)
+            content_type = "text/xml"
+        return content_type, body
+
+
+# Maps the path of an InteProxy request to transcoders.  The key of the
+# transcoder_map is the path of the URL given to the InteProxy.  E.g. if
+# the URL entered in the client is http://localhost:123/remote-host/wfs
+# the path is /remote-host/wfs.  The values are dictionaries mapping the
+# http request method ("GET" or "POST") to the transcoder class.  For
+# example, a value for transcoder_map might be
+#
+# transcoder_map = {
+#     "/example.como/wfs": {"GET": OWSProxyGETTranscoder,
+#                           "POST": OWSProxyPOSTTranscoder},
+#     }
+
+transcoder_map = {
+    }
+
+def get_transcoder(method, url):
+    """Return a transcoder for the given HTTP method and URL"""
+    scheme, netloc, path, query, fragment = urlparse.urlsplit(url)
+
+    # both scheme and netloc must be empty strings because the url
+    # we work with is the url on the first line of a HTTP request
+    # which doesn't include them
+    assert scheme == ""
+    assert netloc == ""
+
+    # FIXME: ideally, we would use the IdentityTranscoder by default.
+    # However, until we have some way to populate the transcoder_map
+    # without hardcoding it, i. e. until we have a configuration file or
+    # a similar mechanism, we use the OWSProxyPOSTTranscoder and
+    # OWSProxyGETTranscoder as defaults so that the behavior is
+    # basically the same as before the introduction of the transcoders.
+    defaults = dict(GET=OWSProxyGETTranscoder, POST=OWSProxyPOSTTranscoder)
+    transcoder_class = transcoder_map.get(path, defaults).get(method,
+                                                           IdentityTranscoder)
+
+    # determine the remote host encoded in the path
+    split_path = path.split("/")
+    if not split_path[0]:
+        del split_path[0]
+    real_netloc = split_path.pop(0)
+    real_path = "/" + "/".join(split_path)
+
+    return transcoder_class(method,
+                            ("https", real_netloc, real_path, query, fragment))


Property changes on: trunk/transcoder.py
___________________________________________________________________
Name: svn:keywords
   + Id Revision
Name: svn:eol-style
   + native



More information about the Inteproxy-commits mailing list