[Inteproxy-commits] r143 - in trunk: . inteproxy test
scm-commit@wald.intevation.org
scm-commit at wald.intevation.org
Thu Jun 12 19:17:43 CEST 2008
Author: bh
Date: 2008-06-12 19:17:43 +0200 (Thu, 12 Jun 2008)
New Revision: 143
Added:
trunk/inteproxy/httpmessage.py
Modified:
trunk/ChangeLog
trunk/inteproxy/feesdialog.py
trunk/inteproxy/proxycore.py
trunk/inteproxy/transcoder.py
trunk/test/test_owsproxy_post_transcoder.py
Log:
* inteproxy/httpmessage.py: New module with abstractions of http
messages.
* inteproxy/transcoder.py (TranscoderRequest): Removed. superseded
by HTTPRequestMessage
* inteproxy/proxycore.py
(InteProxyHTTPRequestHandler.handle_proxy_request): Adapt to http
message objects
(InteProxyHTTPRequestHandler.read_client_request): Return a
HTTPRequestMessage instead of a TranscoderRequest
(InteProxyHTTPRequestHandler.open_http_connection): Return the
response as a HTTPResponseMessage instead of a httplib response
object
(InteProxyHTTPRequestHandler.handle_response): Adapt to the http
message objects. No longer accepts the response_read parameter
* inteproxy/feesdialog.py (handle_fees_and_access_constraints):
Adapt to the http message objects. No need to return the read
method anymore.
* test/test_owsproxy_post_transcoder.py
(TestOWSProxyPOSTTranscoder.create_transcoder_request)
(TestOWSProxyPOSTTranscoder.create_request): Rename
create_transcoder_request to create_request and use a
HTTPRequestMessage instead of a TranscoderRequest. Update callers
Modified: trunk/ChangeLog
===================================================================
--- trunk/ChangeLog 2008-06-12 16:09:45 UTC (rev 142)
+++ trunk/ChangeLog 2008-06-12 17:17:43 UTC (rev 143)
@@ -1,6 +1,35 @@
2008-06-12 Bernhard Herzog <bh at intevation.de>
+ * inteproxy/httpmessage.py: New module with abstractions of http
+ messages.
+
+ * inteproxy/transcoder.py (TranscoderRequest): Removed. superseded
+ by HTTPRequestMessage
+
* inteproxy/proxycore.py
+ (InteProxyHTTPRequestHandler.handle_proxy_request): Adapt to http
+ message objects
+ (InteProxyHTTPRequestHandler.read_client_request): Return a
+ HTTPRequestMessage instead of a TranscoderRequest
+ (InteProxyHTTPRequestHandler.open_http_connection): Return the
+ response as a HTTPResponseMessage instead of a httplib response
+ object
+ (InteProxyHTTPRequestHandler.handle_response): Adapt to the http
+ message objects. No longer accepts the response_read parameter
+
+ * inteproxy/feesdialog.py (handle_fees_and_access_constraints):
+ Adapt to the http message objects. No need to return the read
+ method anymore.
+
+ * test/test_owsproxy_post_transcoder.py
+ (TestOWSProxyPOSTTranscoder.create_transcoder_request)
+ (TestOWSProxyPOSTTranscoder.create_request): Rename
+ create_transcoder_request to create_request and use a
+ HTTPRequestMessage instead of a TranscoderRequest. Update callers
+
+2008-06-12 Bernhard Herzog <bh at intevation.de>
+
+ * inteproxy/proxycore.py
(InteProxyHTTPRequestHandler.open_http_connection): Handle URLs
with query and fragment parts correctly
Modified: trunk/inteproxy/feesdialog.py
===================================================================
--- trunk/inteproxy/feesdialog.py 2008-06-12 16:09:45 UTC (rev 142)
+++ trunk/inteproxy/feesdialog.py 2008-06-12 17:17:43 UTC (rev 143)
@@ -53,35 +53,23 @@
then parses the response as XML and extract Fees and
AccessConstraints information from it and shows them in a dialog.
- The response parameter should be the urllib2 response object for the
- remote url. This function has to read the entire response in order
- to parse it. A side effect of this is that the response's read
- method can no longer be used by the caller to read the response.
- Therefore this function always returns a callable object that
- behaves like the original response's read method that can be used
- instead.
+ The response parameter should be the HTTPRequestMessage object with
+ the response from the remote url.
The function keeps track of which URLs were already processed, so
that the dialog is never shown twice for the same URL.
"""
- response_read = response.read
-
# only try to handle anything for successful requests.
# Should we allow any other success response codes?
if response.status != 200:
- return response_read
+ return
if remote_url in dialog_shown_for:
- return response_read
+ return
if is_get_capabilities_url(remote_url):
- data = response_read()
- title, fees, access_constraints = parse_capabilities(data)
+ title, fees, access_constraints = parse_capabilities(response.body)
inteproxy.main.the_application.run_fees_dialog(title, fees,
access_constraints)
dialog_shown_for[remote_url] = True
-
- response_read = StringIO(data).read
-
- return response_read
Added: trunk/inteproxy/httpmessage.py
===================================================================
--- trunk/inteproxy/httpmessage.py 2008-06-12 16:09:45 UTC (rev 142)
+++ trunk/inteproxy/httpmessage.py 2008-06-12 17:17:43 UTC (rev 143)
@@ -0,0 +1,148 @@
+# Copyright (C) 2008 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.
+
+"""Abstractions for http request and response messages"""
+
+from StringIO import StringIO
+
+class HTTPMessage(object):
+
+ """Base class for HTTP Messages in InteProxy.
+
+ Parameters
+ headers -- The headers of the message as a mimetools.Message
+ instance.
+
+ infile -- File-object like object from which the body of the
+ message can be read.
+
+ The headers object can be accessed as the headers attribute. The
+ body should be accessed through the body property or the message's
+ read method. If the body is read via the body property, the body is
+ read into memory from the infile once. The body can also be set by
+ assignment ot the body attribute or the set_body method. The latter
+ can also set the content type.
+
+ If the body is read or set, the headers are adjusted a little. The
+ Content-length is set to the length of the body and any
+ Transfer-encoding headers are removed.
+ """
+
+ def __init__(self, headers, infile):
+ self.headers = headers
+ self.infile = infile
+ self._body = None
+ self._body_stream = None
+
+ def debug_log_message(self, log_function):
+ for header, value in self.headers.items():
+ log_function("header: %s:%r", header, value)
+ if self.body_has_been_read():
+ if self._body:
+ log_function("body: %r", self._body)
+ else:
+ log_function("no body")
+ else:
+ log_function("body may be present but has not been read yet")
+
+ def body_has_been_read(self):
+ return self._body != None
+
+ def set_body(self, body, content_type=None):
+ self.headers["Content-length"] = str(len(body))
+ del self.headers["Transfer-encoding"]
+ if content_type is not None:
+ self.headers["Content-type"] = content_type
+ self._body = body
+
+ def get_body(self):
+ self.read_entire_message()
+ return self._body
+
+ body = property(get_body, set_body)
+
+ def read_entire_message(self):
+ raise NotImplementedError
+
+ def read(self, amount):
+ if self._body_stream is None and self.body_has_been_read():
+ self._body_stream = StringIO(self.body)
+ if self._body_stream is not None:
+ return self._body_stream.read(amount)
+ else:
+ return self.infile.read(amount)
+
+
+class HTTPRequestMessage(HTTPMessage):
+
+ """Represents a HTTP Message for a request.
+
+ Additional parameters:
+ method -- The HTTP method of the request (usually 'GET' or 'POST'()
+
+ uri -- The uri of the request
+
+ version -- The http protocol version given in the request
+
+ All of these parameters are available as attributes of the same
+ name.
+ """
+
+ def __init__(self, method, uri, version, headers, infile):
+ super(HTTPRequestMessage, self).__init__(headers, infile)
+ self.method = method
+ self.uri = uri
+ self.version = version
+
+ def debug_log_message(self, log_function):
+ log_function("HTTPRequestMessage: %s %s %s",
+ self.method, self.uri, self.version)
+ super(HTTPRequestMessage, self).debug_log_message(log_function)
+
+ def read_entire_message(self):
+ if self.body_has_been_read():
+ return
+ body = None
+ length = int(self.headers.get("Content-Length", "0"))
+ if length:
+ # FIXME: check whether length bytes were read
+ body = self.infile.read(length)
+
+ if body is not None:
+ self.set_body(body)
+
+
+class HTTPResponseMessage(HTTPMessage):
+
+ """Represents a HTTP Message for a response.
+
+ Additional parameters:
+ version -- The http protocol version given in the response
+
+ status -- The HTTP status of the response
+
+ reason -- The reason given in the response
+
+ All of these parameters are available as attributes of the same
+ name.
+ """
+
+ def __init__(self, version, status, reason, headers, infile):
+ super(HTTPResponseMessage, self).__init__(headers, infile)
+ self.version = version
+ self.status = status
+ self.reason = reason
+
+ def debug_log_message(self, log_function):
+ log_function("HTTPResponseMessage: %s %s %s",
+ self.version, self.status, self.reason)
+ super(HTTPResponseMessage, self).debug_log_message(log_function)
+
+ def read_entire_message(self):
+ if self.body_has_been_read():
+ return
+ self.set_body(self.infile.read())
Property changes on: trunk/inteproxy/httpmessage.py
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ native
Modified: trunk/inteproxy/proxycore.py
===================================================================
--- trunk/inteproxy/proxycore.py 2008-06-12 16:09:45 UTC (rev 142)
+++ trunk/inteproxy/proxycore.py 2008-06-12 17:17:43 UTC (rev 143)
@@ -20,6 +20,7 @@
from inteproxy.threadpool import ThreadPool
from inteproxy.feesdialog import handle_fees_and_access_constraints
from inteproxy.httpserver import HTTPServer
+from inteproxy.httpmessage import HTTPRequestMessage, HTTPResponseMessage
from inteproxy.proxyconnection import HTTPSProxyConnection
@@ -95,7 +96,7 @@
#
transcoder.convert_request(client_request)
self.log_debug("modified client_request:")
- client_request.debug_log_request(self.log_debug)
+ client_request.debug_log_message(self.log_debug)
#
# Create and send new request to the real remote host.
@@ -112,31 +113,19 @@
self.send_error(502)
if response is not None:
- self.log_debug("received response: %s: %r", response.status,
- response.reason)
+ response.debug_log_message(self.log_debug)
# check for fees and access constraints and run a dialog
- response_read = handle_fees_and_access_constraints(remote_url,
- response)
- self.handle_response(response, response_read)
+ handle_fees_and_access_constraints(remote_url, response)
+ self.handle_response(response)
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, body)
- else:
- self.log_debug("client request has no body")
+ return HTTPRequestMessage(self.command, self.path, self.request_version,
+ self.headers, self.rfile)
- return inteproxy.transcoder.TranscoderRequest(self.command, self.path,
- self.headers, body)
-
def open_http_connection(self, remote_url, client_request):
"""Open a HTTP connection to remote_url and send client_request
@@ -179,14 +168,33 @@
connection.endheaders()
if client_request.body is not None:
+ self.log_debug("Sending body to server: %r", client_request.body)
connection.send(client_request.body)
self.log_debug("request sent")
- return connection.getresponse()
+ response = connection.getresponse()
+ for version_int, version in [(11, "HTTP/1.1"),
+ (10, "HTTP/1.0"),
+ ( 9, "HTTP/0.9")]:
+ if response.version == version_int:
+ break
+ else:
+ version = "UNKNOWN"
+ self.log_debug("Unknown http version from server: %s",
+ response.version)
+ if version != self.request_version:
+ self.log_debug("Response version %r does not match"
+ " request version %r",
+ version, self.request_version)
+ response_message = HTTPResponseMessage(version, response.status,
+ response.reason,
+ response.msg, response)
+ return response_message
- def handle_response(self, response, response_read):
+
+ def handle_response(self, response):
# Ideally, the HTTP version in our reply to the client should be
# what the remote server used in its reply. Unfortunately,
# there doesn't seem to be a way to get that information from
@@ -205,13 +213,13 @@
self.protocol_version = self.request_version
self.send_response(response.status, response.reason)
- for header, value in response.msg.items():
- self.log_debug("received header: %s:%r", header, value)
+ for header, value in response.headers.items():
+ self.log_debug("header to client: %s:%r", header, value)
self.send_header(header, value)
self.end_headers()
- transfer_encoding = response.msg.get("Transfer-encoding")
- self.transfer_data(response_read, self.wfile.write,
+ transfer_encoding = response.headers.get("Transfer-encoding")
+ self.transfer_data(response.read, self.wfile.write,
chunked = (transfer_encoding == "chunked"))
def transfer_data(self, read, write, length=None, chunked=False):
Modified: trunk/inteproxy/transcoder.py
===================================================================
--- trunk/inteproxy/transcoder.py 2008-06-12 16:09:45 UTC (rev 142)
+++ trunk/inteproxy/transcoder.py 2008-06-12 17:17:43 UTC (rev 143)
@@ -18,50 +18,6 @@
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", self.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
Modified: trunk/test/test_owsproxy_post_transcoder.py
===================================================================
--- trunk/test/test_owsproxy_post_transcoder.py 2008-06-12 16:09:45 UTC (rev 142)
+++ trunk/test/test_owsproxy_post_transcoder.py 2008-06-12 17:17:43 UTC (rev 143)
@@ -11,7 +11,8 @@
import mimetools
import unittest
-from inteproxy.transcoder import OWSProxyPOSTTranscoder, TranscoderRequest
+from inteproxy.transcoder import OWSProxyPOSTTranscoder
+from inteproxy.httpmessage import HTTPRequestMessage
class TestOWSProxyPOSTTranscoder(unittest.TestCase):
@@ -47,10 +48,12 @@
'</wfs:Query>'
'</wfs:GetFeature>')
- def create_transcoder_request(self, body):
- return TranscoderRequest("POST", "/foo",
- mimetools.Message(StringIO("\r\n\r\n")),
- body)
+ def create_request(self, body):
+ headers = ["Content-length: %d" % len(body)]
+ headers_object = mimetools.Message(StringIO("\r\n".join(headers)
+ + "\r\n\r\n"))
+ return HTTPRequestMessage("POST", "/foo", "HTTP/1.0", headers_object,
+ StringIO(body))
def test_username_password_substitution(self):
@@ -70,7 +73,7 @@
for user, password, userattr, passwordattr in test_users:
transcoder = Transcoder("POST",
("https", "example.com", "wfs", "", ""))
- request = self.create_transcoder_request(self.get_feature_orig)
+ request = self.create_request(self.get_feature_orig)
transcoder.convert_request(request)
self.assertEquals(request.body,
self.get_feature_modified_template % locals())
More information about the Inteproxy-commits
mailing list