Statistics
| Branch: | Tag: | Revision:

root / snf-django-lib / snf_django / lib / api / proxy / __init__.py @ cfc4b87c

History | View | Annotate | Download (4 kB)

1 33c84784 Sofia Papagiannaki
# Copyright 2012, 2013 GRNET S.A. All rights reserved.
2 33c84784 Sofia Papagiannaki
#
3 33c84784 Sofia Papagiannaki
# Redistribution and use in source and binary forms, with or
4 33c84784 Sofia Papagiannaki
# without modification, are permitted provided that the following
5 33c84784 Sofia Papagiannaki
# conditions are met:
6 33c84784 Sofia Papagiannaki
#
7 33c84784 Sofia Papagiannaki
#   1. Redistributions of source code must retain the above
8 33c84784 Sofia Papagiannaki
#      copyright notice, this list of conditions and the following
9 33c84784 Sofia Papagiannaki
#      disclaimer.
10 33c84784 Sofia Papagiannaki
#
11 33c84784 Sofia Papagiannaki
#   2. Redistributions in binary form must reproduce the above
12 33c84784 Sofia Papagiannaki
#      copyright notice, this list of conditions and the following
13 33c84784 Sofia Papagiannaki
#      disclaimer in the documentation and/or other materials
14 33c84784 Sofia Papagiannaki
#      provided with the distribution.
15 33c84784 Sofia Papagiannaki
#
16 33c84784 Sofia Papagiannaki
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17 33c84784 Sofia Papagiannaki
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 33c84784 Sofia Papagiannaki
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 33c84784 Sofia Papagiannaki
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20 33c84784 Sofia Papagiannaki
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 33c84784 Sofia Papagiannaki
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 33c84784 Sofia Papagiannaki
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23 33c84784 Sofia Papagiannaki
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24 33c84784 Sofia Papagiannaki
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 33c84784 Sofia Papagiannaki
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26 33c84784 Sofia Papagiannaki
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 33c84784 Sofia Papagiannaki
# POSSIBILITY OF SUCH DAMAGE.
28 33c84784 Sofia Papagiannaki
#
29 33c84784 Sofia Papagiannaki
# The views and conclusions contained in the software and
30 33c84784 Sofia Papagiannaki
# documentation are those of the authors and should not be
31 33c84784 Sofia Papagiannaki
# interpreted as representing official policies, either expressed
32 33c84784 Sofia Papagiannaki
# or implied, of GRNET S.A.
33 33c84784 Sofia Papagiannaki
34 33c84784 Sofia Papagiannaki
from django.http import HttpResponse
35 33c84784 Sofia Papagiannaki
36 fed0b230 Christos Stavrakakis
from objpool.http import PooledHTTPConnection
37 33c84784 Sofia Papagiannaki
38 5ad8eca1 Kostas Papadimitriou
from synnefo.lib import join_urls
39 5ad8eca1 Kostas Papadimitriou
40 33c84784 Sofia Papagiannaki
from .utils import fix_header, forward_header
41 33c84784 Sofia Papagiannaki
42 33c84784 Sofia Papagiannaki
import urllib
43 33c84784 Sofia Papagiannaki
import urlparse
44 33c84784 Sofia Papagiannaki
45 142133fb Kostas Papadimitriou
# We use proxy to delegate requests to another domain. Sending host specific
46 142133fb Kostas Papadimitriou
# headers (Host, Cookie) may cause confusion to the server we proxy to.
47 142133fb Kostas Papadimitriou
#
48 142133fb Kostas Papadimitriou
# http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.10
49 142133fb Kostas Papadimitriou
# Connection and MUST NOT be communicated by proxies over further connections
50 f870efe2 Sofia Papagiannaki
EXCLUDE_HEADERS = ['Host', 'Cookie', 'Connection', 'X-Forwarded-Host']
51 142133fb Kostas Papadimitriou
52 33c84784 Sofia Papagiannaki
53 e3ff6830 Georgios D. Tsoukalas
def proxy(request, proxy_base=None, target_base=None):
54 33c84784 Sofia Papagiannaki
    kwargs = {}
55 33c84784 Sofia Papagiannaki
56 e3ff6830 Georgios D. Tsoukalas
    if None in (proxy_base, target_base):
57 e3ff6830 Georgios D. Tsoukalas
        m = "proxy() needs both proxy_base and target_base argument not None"
58 e3ff6830 Georgios D. Tsoukalas
        raise AssertionError(m)
59 e3ff6830 Georgios D. Tsoukalas
60 e3ff6830 Georgios D. Tsoukalas
    parsed = urlparse.urlparse(target_base)
61 e3ff6830 Georgios D. Tsoukalas
    target_base = '/' + parsed.path.strip('/')
62 e3ff6830 Georgios D. Tsoukalas
    proxy_base = proxy_base.strip('/')
63 e3ff6830 Georgios D. Tsoukalas
64 33c84784 Sofia Papagiannaki
    # prepare headers
65 33c84784 Sofia Papagiannaki
    headers = dict(map(lambda (k, v): fix_header(k, v),
66 33c84784 Sofia Papagiannaki
                   filter(lambda (k, v): forward_header(k),
67 33c84784 Sofia Papagiannaki
                          request.META.iteritems())))
68 33c84784 Sofia Papagiannaki
69 142133fb Kostas Papadimitriou
    # set X-Forwarded-For, if already set, pass it through, otherwise set it
70 142133fb Kostas Papadimitriou
    # to the current request remote address
71 142133fb Kostas Papadimitriou
    SOURCE_IP = request.META.get('REMOTE_ADDR', None)
72 142133fb Kostas Papadimitriou
    if SOURCE_IP and not 'X-Forwarded-For' in headers:
73 142133fb Kostas Papadimitriou
        headers['X-Forwarded-For'] = SOURCE_IP
74 142133fb Kostas Papadimitriou
75 142133fb Kostas Papadimitriou
    # request.META remains cleanup
76 142133fb Kostas Papadimitriou
    for k in headers.keys():
77 142133fb Kostas Papadimitriou
        if '_' in k:
78 142133fb Kostas Papadimitriou
            headers.pop(k)
79 142133fb Kostas Papadimitriou
80 142133fb Kostas Papadimitriou
    for k in EXCLUDE_HEADERS:
81 142133fb Kostas Papadimitriou
        headers.pop(k, None)
82 142133fb Kostas Papadimitriou
83 33c84784 Sofia Papagiannaki
    kwargs['headers'] = headers
84 cfc4b87c Christos Stavrakakis
    kwargs['body'] = request.body
85 33c84784 Sofia Papagiannaki
86 e3ff6830 Georgios D. Tsoukalas
    path = request.path.lstrip('/')
87 e3ff6830 Georgios D. Tsoukalas
    if path.startswith(proxy_base):
88 e3ff6830 Georgios D. Tsoukalas
        m = "request path '{0}' does not start with proxy_base '{1}'"
89 e3ff6830 Georgios D. Tsoukalas
        m = m.format(path, proxy_base)
90 e3ff6830 Georgios D. Tsoukalas
    path = path.replace(proxy_base, '', 1)
91 e3ff6830 Georgios D. Tsoukalas
    path = join_urls(target_base, path)
92 e3ff6830 Georgios D. Tsoukalas
    with PooledHTTPConnection(parsed.netloc, parsed.scheme) as conn:
93 33c84784 Sofia Papagiannaki
        conn.request(
94 33c84784 Sofia Papagiannaki
            request.method,
95 5ad8eca1 Kostas Papadimitriou
            '?'.join([path, urllib.urlencode(request.GET)]), **kwargs)
96 33c84784 Sofia Papagiannaki
        response = conn.getresponse()
97 33c84784 Sofia Papagiannaki
98 33c84784 Sofia Papagiannaki
        # turn httplib.HttpResponse to django.http.Response
99 33c84784 Sofia Papagiannaki
        length = response.getheader('content-length', None)
100 33c84784 Sofia Papagiannaki
        data = response.read(length)
101 33c84784 Sofia Papagiannaki
        status = int(response.status)
102 152e7bba Georgios D. Tsoukalas
        django_http_response = HttpResponse(data, status=status)
103 152e7bba Georgios D. Tsoukalas
        # do we need to exclude any headers here?
104 152e7bba Georgios D. Tsoukalas
        for name, value in response.getheaders():
105 152e7bba Georgios D. Tsoukalas
            django_http_response[name] = value
106 152e7bba Georgios D. Tsoukalas
        return django_http_response