Package restkit :: Package contrib :: Module wsgi_proxy
[hide private]

Source Code for Module restkit.contrib.wsgi_proxy

  1  # -*- coding: utf-8 - 
  2  # 
  3  # This file is part of restkit released under the MIT license. 
  4  # See the NOTICE for more information. 
  5   
  6  import urlparse 
  7   
  8  try: 
  9      from cStringIO import StringIO 
 10  except ImportError: 
 11      from StringIO import StringIO 
 12   
 13  from restkit.client import Client 
 14  from restkit.conn import MAX_BODY 
 15  from restkit.util import rewrite_location 
 16   
 17  ALLOWED_METHODS = ['GET', 'HEAD', 'POST', 'PUT', 'DELETE'] 
 18   
 19  BLOCK_SIZE = 4096 * 16 
 20   
 21  WEBOB_ERROR = ("Content-Length is set to -1. This usually mean that WebOb has " 
 22          "already parsed the content body. You should set the Content-Length " 
 23          "header to the correct value before forwarding your request to the " 
 24          "proxy: ``req.content_length = str(len(req.body));`` " 
 25          "req.get_response(proxy)") 
 26   
27 -class Proxy(object):
28 """A proxy wich redirect the request to SERVER_NAME:SERVER_PORT 29 and send HTTP_HOST header""" 30
31 - def __init__(self, manager=None, allowed_methods=ALLOWED_METHODS, 32 strip_script_name=True, **kwargs):
33 self.allowed_methods = allowed_methods 34 self.strip_script_name = strip_script_name 35 self.client = Client(**kwargs)
36
37 - def extract_uri(self, environ):
38 port = None 39 scheme = environ['wsgi.url_scheme'] 40 if 'SERVER_NAME' in environ: 41 host = environ['SERVER_NAME'] 42 else: 43 host = environ['HTTP_HOST'] 44 if ':' in host: 45 host, port = host.split(':') 46 47 if not port: 48 if 'SERVER_PORT' in environ: 49 port = environ['SERVER_PORT'] 50 else: 51 port = scheme == 'https' and '443' or '80' 52 53 uri = '%s://%s:%s' % (scheme, host, port) 54 return uri
55
56 - def __call__(self, environ, start_response):
57 method = environ['REQUEST_METHOD'] 58 if method not in self.allowed_methods: 59 start_response('403 Forbidden', ()) 60 return [''] 61 62 if self.strip_script_name: 63 path_info = '' 64 else: 65 path_info = environ['SCRIPT_NAME'] 66 path_info += environ['PATH_INFO'] 67 68 query_string = environ['QUERY_STRING'] 69 if query_string: 70 path_info += '?' + query_string 71 72 host_uri = self.extract_uri(environ) 73 uri = host_uri + path_info 74 75 new_headers = {} 76 for k, v in environ.items(): 77 if k.startswith('HTTP_'): 78 k = k[5:].replace('_', '-').title() 79 new_headers[k] = v 80 81 82 ctype = environ.get("CONTENT_TYPE") 83 if ctype and ctype is not None: 84 new_headers['Content-Type'] = ctype 85 86 clen = environ.get('CONTENT_LENGTH') 87 te = environ.get('transfer-encoding', '').lower() 88 if not clen and te != 'chunked': 89 new_headers['transfer-encoding'] = 'chunked' 90 elif clen: 91 new_headers['Content-Length'] = clen 92 93 if new_headers.get('Content-Length', '0') == '-1': 94 raise ValueError(WEBOB_ERROR) 95 96 response = self.client.request(uri, method, body=environ['wsgi.input'], 97 headers=new_headers) 98 99 if 'location' in response: 100 if self.strip_script_name: 101 prefix_path = environ['SCRIPT_NAME'] 102 103 new_location = rewrite_location(host_uri, response.location, 104 prefix_path=prefix_path) 105 106 headers = [] 107 for k, v in response.headerslist: 108 if k.lower() == 'location': 109 v = new_location 110 headers.append((k, v)) 111 else: 112 headers = response.headerslist 113 114 start_response(response.status, headers) 115 116 if method == "HEAD": 117 return StringIO() 118 119 return response.tee()
120
121 -class TransparentProxy(Proxy):
122 """A proxy based on HTTP_HOST environ variable""" 123
124 - def extract_uri(self, environ):
125 port = None 126 scheme = environ['wsgi.url_scheme'] 127 host = environ['HTTP_HOST'] 128 if ':' in host: 129 host, port = host.split(':') 130 131 if not port: 132 port = scheme == 'https' and '443' or '80' 133 134 uri = '%s://%s:%s' % (scheme, host, port) 135 return uri
136 137
138 -class HostProxy(Proxy):
139 """A proxy to redirect all request to a specific uri""" 140
141 - def __init__(self, uri, **kwargs):
142 super(HostProxy, self).__init__(**kwargs) 143 self.uri = uri.rstrip('/') 144 self.scheme, self.net_loc = urlparse.urlparse(self.uri)[0:2]
145
146 - def extract_uri(self, environ):
147 environ['HTTP_HOST'] = self.net_loc 148 return self.uri
149
150 -def get_config(local_config):
151 """parse paste config""" 152 config = {} 153 allowed_methods = local_config.get('allowed_methods', None) 154 if allowed_methods: 155 config['allowed_methods'] = [m.upper() for m in allowed_methods.split()] 156 strip_script_name = local_config.get('strip_script_name', 'true') 157 if strip_script_name.lower() in ('false', '0'): 158 config['strip_script_name'] = False 159 config['max_connections'] = int(local_config.get('max_connections', '5')) 160 return config
161
162 -def make_proxy(global_config, **local_config):
163 """TransparentProxy entry_point""" 164 config = get_config(local_config) 165 return TransparentProxy(**config)
166
167 -def make_host_proxy(global_config, uri=None, **local_config):
168 """HostProxy entry_point""" 169 uri = uri.rstrip('/') 170 config = get_config(local_config) 171 return HostProxy(uri, **config)
172