Package restkit :: Package contrib :: Module wsgi_proxy
[hide private]
[frames] | no frames]

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 91 if new_headers.get('Content-Length', '0') == '-1': 92 raise ValueError(WEBOB_ERROR) 93 94 response = self.client.request(uri, method, body=environ['wsgi.input'], 95 headers=new_headers) 96 97 if 'location' in response: 98 if self.strip_script_name: 99 prefix_path = environ['SCRIPT_NAME'] 100 101 new_location = rewrite_location(host_uri, response.location, 102 prefix_path=prefix_path) 103 104 headers = [] 105 for k, v in response.headerslist: 106 if k.lower() == 'location': 107 v = new_location 108 headers.append((k, v)) 109 else: 110 headers = response.headerslist 111 112 start_response(response.status, headers) 113 114 if method == "HEAD": 115 return StringIO() 116 117 return response.tee()
118
119 -class TransparentProxy(Proxy):
120 """A proxy based on HTTP_HOST environ variable""" 121
122 - def extract_uri(self, environ):
123 port = None 124 scheme = environ['wsgi.url_scheme'] 125 host = environ['HTTP_HOST'] 126 if ':' in host: 127 host, port = host.split(':') 128 129 if not port: 130 port = scheme == 'https' and '443' or '80' 131 132 uri = '%s://%s:%s' % (scheme, host, port) 133 return uri
134 135
136 -class HostProxy(Proxy):
137 """A proxy to redirect all request to a specific uri""" 138
139 - def __init__(self, uri, **kwargs):
140 super(HostProxy, self).__init__(**kwargs) 141 self.uri = uri.rstrip('/') 142 self.scheme, self.net_loc = urlparse.urlparse(self.uri)[0:2]
143
144 - def extract_uri(self, environ):
145 environ['HTTP_HOST'] = self.net_loc 146 return self.uri
147
148 -def get_config(local_config):
149 """parse paste config""" 150 config = {} 151 allowed_methods = local_config.get('allowed_methods', None) 152 if allowed_methods: 153 config['allowed_methods'] = [m.upper() for m in allowed_methods.split()] 154 strip_script_name = local_config.get('strip_script_name', 'true') 155 if strip_script_name.lower() in ('false', '0'): 156 config['strip_script_name'] = False 157 config['max_connections'] = int(local_config.get('max_connections', '5')) 158 return config
159
160 -def make_proxy(global_config, **local_config):
161 """TransparentProxy entry_point""" 162 config = get_config(local_config) 163 return TransparentProxy(**config)
164
165 -def make_host_proxy(global_config, uri=None, **local_config):
166 """HostProxy entry_point""" 167 uri = uri.rstrip('/') 168 config = get_config(local_config) 169 return HostProxy(uri, **config)
170