[Inteproxy-commits] r110 - in trunk: . inteproxy test

scm-commit@wald.intevation.org scm-commit at wald.intevation.org
Thu Feb 14 15:06:10 CET 2008


Author: bh
Date: 2008-02-14 15:06:08 +0100 (Thu, 14 Feb 2008)
New Revision: 110

Modified:
   trunk/ChangeLog
   trunk/inteproxy/proxycore.py
   trunk/inteproxy/transcoder.py
   trunk/test/test_owsproxy_post_transcoder.py
Log:
Make the transcoders more flexible in what they can change in a
request

* inteproxy/transcoder.py (TranscoderRequest): New class to
represent a request
(IdentityTranscoder.convert_body): Removed.  Use convert_request
instead
(IdentityTranscoder.convert_request): New. Replaces convert_body
and works on a whole request instead of just the body
(OWSProxyPOSTTranscoder.convert_body)
(OWSProxyPOSTTranscoder.convert_request): Renamed from
convert_body to convert_request and adapted accordingly

* inteproxy/proxycore.py
(InteProxyHTTPRequestHandler.handle_proxy_request): Adapt to the
transcoder changes
(InteProxyHTTPRequestHandler.read_client_request): New.  Read the
client request and return it as a TranscoderRequest instance

* test/test_owsproxy_post_transcoder.py
(TestOWSProxyPOSTTranscoder.create_transcoder_request): New.
Helper method to instantiate a TranscoderRequest
(TestOWSProxyPOSTTranscoder.test_username_password_substitution):
Adapt to transcoder changes


Modified: trunk/ChangeLog
===================================================================
--- trunk/ChangeLog	2008-02-14 11:29:03 UTC (rev 109)
+++ trunk/ChangeLog	2008-02-14 14:06:08 UTC (rev 110)
@@ -1,6 +1,33 @@
 2008-02-14  Bernhard Herzog  <bh at intevation.de>
 
+	Make the transcoders more flexible in what they can change in a
+	request
+
+	* inteproxy/transcoder.py (TranscoderRequest): New class to
+	represent a request
+	(IdentityTranscoder.convert_body): Removed.  Use convert_request
+	instead
+	(IdentityTranscoder.convert_request): New. Replaces convert_body
+	and works on a whole request instead of just the body
+	(OWSProxyPOSTTranscoder.convert_body)
+	(OWSProxyPOSTTranscoder.convert_request): Renamed from
+	convert_body to convert_request and adapted accordingly
+
 	* inteproxy/proxycore.py
+	(InteProxyHTTPRequestHandler.handle_proxy_request): Adapt to the
+	transcoder changes
+	(InteProxyHTTPRequestHandler.read_client_request): New.  Read the
+	client request and return it as a TranscoderRequest instance
+
+	* test/test_owsproxy_post_transcoder.py
+	(TestOWSProxyPOSTTranscoder.create_transcoder_request): New.
+	Helper method to instantiate a TranscoderRequest
+	(TestOWSProxyPOSTTranscoder.test_username_password_substitution):
+	Adapt to transcoder changes
+
+2008-02-14  Bernhard Herzog  <bh at intevation.de>
+
+	* inteproxy/proxycore.py
 	(InteProxyHTTPRequestHandler.handle_proxy_request): Remove the
 	method parameter.  Its value is always the same as self.command
 	and handle_proxy_request takes all other information about the

Modified: trunk/inteproxy/proxycore.py
===================================================================
--- trunk/inteproxy/proxycore.py	2008-02-14 11:29:03 UTC (rev 109)
+++ trunk/inteproxy/proxycore.py	2008-02-14 14:06:08 UTC (rev 110)
@@ -79,34 +79,30 @@
             self.log_debug("header from client: %s:%r", header, value)
 
         #
-        # Create a http request for the real location
+        #  Read the client request
         #
+        client_request = self.read_client_request()
+
+        #
+        # Determine the transcoder to use
+        #
         transcoder_map = inteproxy.transcoder.transcoder_map
         transcoder = transcoder_map.get_transcoder(self.command, self.path)
+
+        #
+        # convert request according to the transcoder rules
+        #
+        transcoder.convert_request(client_request)
+        self.log_debug("modified client_request:")
+        client_request.debug_log_request(self.log_debug)
+
+        #
+        # Create and send new request to the real remote host.
+        #
         remote_url = transcoder.get_url()
         self.log_debug("Converted url: %r", remote_url)
-
-        # If any data is associated with the request read it
-        length = int(self.headers.getheader("Content-Length", "0"))
-        if length:
-            # FIXME: check whether length bytes were read
-            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
-
         request = urllib2.Request(remote_url)
-        for header, value in self.headers.items():
+        for header, value in client_request.headers.items():
             if header.lower() == "host":
                 # the host header will be set by httplib and it should
                 # not be set twice, so we omit it.  The value we
@@ -132,8 +128,8 @@
             else:
                 request.add_header(header, value)
 
-        if data is not None:
-            request.add_data(data)
+        if client_request.body is not None:
+            request.add_data(client_request.body)
 
         self.log_debug("request sent")
 
@@ -181,6 +177,22 @@
                            chunked = (transfer_encoding == "chunked"))
         self.log_debug("request finished")
 
+    def read_client_request(self):
+        """Read the client request including the body, if any.
+        The request is returned as a TranscoderRequest instance."""
+        body = None
+        length = int(self.headers.getheader("Content-Length", "0"))
+        if length:
+            # FIXME: check whether length bytes were read
+            body = self.rfile.read(length)
+            self.log_debug("body of client request (%d bytes):\n%r",
+                           length, data)
+        else:
+            self.log_debug("client request has no body")
+
+        return inteproxy.transcoder.TranscoderRequest(self.command, self.path,
+                                                      self.headers, body)
+
     def transfer_data(self, read, write, length=None, chunked=False):
         """Transfer data from one 'file' to another in chunks
 

Modified: trunk/inteproxy/transcoder.py
===================================================================
--- trunk/inteproxy/transcoder.py	2008-02-14 11:29:03 UTC (rev 109)
+++ trunk/inteproxy/transcoder.py	2008-02-14 14:06:08 UTC (rev 110)
@@ -1,4 +1,4 @@
-# Copyright (C) 2007 by Intevation GmbH
+# Copyright (C) 2007, 2008 by Intevation GmbH
 # Authors:
 # Bernhard Herzog <bh at intevation.de>
 #
@@ -17,6 +17,50 @@
 import getpassword
 
 
+class TranscoderRequest(object):
+
+    """Represents a HTTP request for the transcoders
+
+    Parameters
+      method -- The HTTP method of the request ('GET' or 'POST')
+
+      path   -- The path of the request
+
+      headers -- The headers of the request as a mimetools.Message
+                 instance (the headers attribute of
+                 BaseHTTPRequestHandler instances is of the correct form
+                 already)
+
+      body    -- The body of the request.  Should be either None if the
+                 request has no body or a string.
+
+    All of the parameters are accessible as instance variables with the
+    same name.  The transcoders will change them and the headers in
+    place.
+    """
+
+    def __init__(self, method, path, headers, body):
+        self.method = method
+        self.path = path
+        self.headers = headers
+        self.body = body
+
+    def debug_log_request(self, log_function):
+        log_function("TranscoderRequest for %s %s", self.method, self.path)
+        for header, value in self.headers.items():
+            log_function("header: %s:%r", header, value)
+        if self.body:
+            log_function("body: %r", body)
+        else:
+            log_function("no body")
+
+    def set_body(self, body, content_type=None):
+        self.headers["Content-length"] = str(len(body))
+        if content_type is not None:
+            self.headers["Content-type"] = content_type
+        self.body = body
+
+
 class IdentityTranscoder(object):
 
     """A transcoder that does not change anything
@@ -55,18 +99,11 @@
         """
         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.
+    def convert_request(self, request):
+        """Converts the TranscoderRequest instance.
+        Derived classes should override this method to e.g. add or
+        modify headers and/or the body according to its needs.
         """
-        return content_type, body
 
 
 class HTTPSTranscoder(IdentityTranscoder):
@@ -102,19 +139,18 @@
 
     """Transcoder for POST requests to the OWSProxy"""
 
-    def convert_body(self, content_type, body):
+    def convert_request(self, request):
         """Adds user and password attributes to the GetFeature element.
         """
-        t = etree.parse(StringIO(body))
+        t = etree.parse(StringIO(request.body))
         root = t.getroot()
         if root.tag in ("{http://www.opengis.net/wfs}GetFeature",
                         "{http://www.opengis.net/wfs}Transaction"):
             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
+            request.set_body(etree.tostring(t, xml_declaration=True),
+                             "text/xml")
 
 
 class TranscoderMap(object):

Modified: trunk/test/test_owsproxy_post_transcoder.py
===================================================================
--- trunk/test/test_owsproxy_post_transcoder.py	2008-02-14 11:29:03 UTC (rev 109)
+++ trunk/test/test_owsproxy_post_transcoder.py	2008-02-14 14:06:08 UTC (rev 110)
@@ -1,4 +1,4 @@
-# Copyright (C) 2007 by Intevation GmbH
+# Copyright (C) 2007, 2008 by Intevation GmbH
 # Authors:
 # Bernhard Herzog <bh at intevation.de>
 #
@@ -7,8 +7,11 @@
 
 """Tests for OWSProxyPOSTTranscoder"""
 
+from StringIO import StringIO
+import mimetools
+
 import unittest
-from inteproxy.transcoder import OWSProxyPOSTTranscoder
+from inteproxy.transcoder import OWSProxyPOSTTranscoder, TranscoderRequest
 
 
 class TestOWSProxyPOSTTranscoder(unittest.TestCase):
@@ -44,6 +47,11 @@
         '</wfs:Query>'
         '</wfs:GetFeature>')
 
+    def create_transcoder_request(self, body):
+        return TranscoderRequest("POST", "/foo",
+                                 mimetools.Message(StringIO("\r\n\r\n")),
+                                 body)
+
     def test_username_password_substitution(self):
 
         class Transcoder(OWSProxyPOSTTranscoder):
@@ -62,9 +70,9 @@
         for user, password, userattr, passwordattr in test_users:
             transcoder = Transcoder("POST",
                                     ("https", "example.com", "wfs", "", ""))
-            converted_body = transcoder.convert_body(None,
-                                                     self.get_feature_orig)
-            self.assertEquals(converted_body[1],
+            request = self.create_transcoder_request(self.get_feature_orig)
+            transcoder.convert_request(request)
+            self.assertEquals(request.body,
                               self.get_feature_modified_template % locals())
-            self.assertEquals(converted_body[0],
+            self.assertEquals(request.headers["Content-type"],
                               "text/xml")



More information about the Inteproxy-commits mailing list