Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (4.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 dd21b1c4 Kostas Papadimitriou
from django.http import HttpResponse, HttpResponseRedirect
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 dd21b1c4 Kostas Papadimitriou
def proxy(request, proxy_base=None, target_base=None, redirect=False):
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 ef57e622 Ilias Tsitsimpis
    # Get strings from lazy objects
61 ef57e622 Ilias Tsitsimpis
    proxy_base = str(proxy_base)
62 ef57e622 Ilias Tsitsimpis
    target_base = str(target_base)
63 ef57e622 Ilias Tsitsimpis
64 e3ff6830 Georgios D. Tsoukalas
    parsed = urlparse.urlparse(target_base)
65 dd21b1c4 Kostas Papadimitriou
    target_path = '/' + parsed.path.strip('/')
66 e3ff6830 Georgios D. Tsoukalas
    proxy_base = proxy_base.strip('/')
67 e3ff6830 Georgios D. Tsoukalas
68 33c84784 Sofia Papagiannaki
    # prepare headers
69 33c84784 Sofia Papagiannaki
    headers = dict(map(lambda (k, v): fix_header(k, v),
70 33c84784 Sofia Papagiannaki
                   filter(lambda (k, v): forward_header(k),
71 33c84784 Sofia Papagiannaki
                          request.META.iteritems())))
72 33c84784 Sofia Papagiannaki
73 142133fb Kostas Papadimitriou
    # set X-Forwarded-For, if already set, pass it through, otherwise set it
74 142133fb Kostas Papadimitriou
    # to the current request remote address
75 39974c71 Ilias Tsitsimpis
    source_ip = request.META.get('REMOTE_ADDR', None)
76 39974c71 Ilias Tsitsimpis
    if source_ip and not 'X-Forwarded-For' in headers:
77 39974c71 Ilias Tsitsimpis
        headers['X-Forwarded-For'] = source_ip
78 142133fb Kostas Papadimitriou
79 142133fb Kostas Papadimitriou
    # request.META remains cleanup
80 142133fb Kostas Papadimitriou
    for k in headers.keys():
81 142133fb Kostas Papadimitriou
        if '_' in k:
82 142133fb Kostas Papadimitriou
            headers.pop(k)
83 142133fb Kostas Papadimitriou
84 142133fb Kostas Papadimitriou
    for k in EXCLUDE_HEADERS:
85 142133fb Kostas Papadimitriou
        headers.pop(k, None)
86 142133fb Kostas Papadimitriou
87 33c84784 Sofia Papagiannaki
    kwargs['headers'] = headers
88 b694875c Christos Stavrakakis
    kwargs['body'] = request.body
89 33c84784 Sofia Papagiannaki
90 e3ff6830 Georgios D. Tsoukalas
    path = request.path.lstrip('/')
91 39974c71 Ilias Tsitsimpis
    if not path.startswith(proxy_base):
92 e3ff6830 Georgios D. Tsoukalas
        m = "request path '{0}' does not start with proxy_base '{1}'"
93 e3ff6830 Georgios D. Tsoukalas
        m = m.format(path, proxy_base)
94 39974c71 Ilias Tsitsimpis
        raise AssertionError(m)
95 e3ff6830 Georgios D. Tsoukalas
    path = path.replace(proxy_base, '', 1)
96 dd21b1c4 Kostas Papadimitriou
97 dd21b1c4 Kostas Papadimitriou
    # redirect to target instead of proxing
98 dd21b1c4 Kostas Papadimitriou
    if redirect:
99 dd21b1c4 Kostas Papadimitriou
        redirect_url = join_urls(target_base, path)
100 dd21b1c4 Kostas Papadimitriou
        qs = urllib.urlencode(request.GET)
101 dd21b1c4 Kostas Papadimitriou
        return HttpResponseRedirect('?'.join([redirect_url, qs]))
102 dd21b1c4 Kostas Papadimitriou
103 dd21b1c4 Kostas Papadimitriou
    path = join_urls(target_path, path)
104 e3ff6830 Georgios D. Tsoukalas
    with PooledHTTPConnection(parsed.netloc, parsed.scheme) as conn:
105 33c84784 Sofia Papagiannaki
        conn.request(
106 33c84784 Sofia Papagiannaki
            request.method,
107 5ad8eca1 Kostas Papadimitriou
            '?'.join([path, urllib.urlencode(request.GET)]), **kwargs)
108 33c84784 Sofia Papagiannaki
        response = conn.getresponse()
109 33c84784 Sofia Papagiannaki
110 33c84784 Sofia Papagiannaki
        # turn httplib.HttpResponse to django.http.Response
111 33c84784 Sofia Papagiannaki
        length = response.getheader('content-length', None)
112 33c84784 Sofia Papagiannaki
        data = response.read(length)
113 33c84784 Sofia Papagiannaki
        status = int(response.status)
114 152e7bba Georgios D. Tsoukalas
        django_http_response = HttpResponse(data, status=status)
115 152e7bba Georgios D. Tsoukalas
        # do we need to exclude any headers here?
116 152e7bba Georgios D. Tsoukalas
        for name, value in response.getheaders():
117 152e7bba Georgios D. Tsoukalas
            django_http_response[name] = value
118 152e7bba Georgios D. Tsoukalas
        return django_http_response