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