Prevent Django from blocking while proxying an HTTP request
|April 4, 2014||Posted by forumadmin under TechQns||
I’m working on a Django site that allows connecting to devices in restricted networks through a cloud service. The devices connect to a cloud server through a VPN or SSH tunnel and clients connect to a virtual host via HTTP. The Django part is required for managing complex organization-role-access-user relationships.
Currently I’m doing access control in a custom Django middleware module that parses HTTP_HOST, does authentication, gets the page and forwards it to the original requester. The problem is that while a request is going on, Django is not handling any other requests. Celery does not solve the problem because this isn’t really a background task. Clients are served through a single address and port, making firewall rules unsuitable for this task.
The relevant code is below:
class NodeProxyMiddleware: def process_request(self, request, *args, **kwargs): if not 'HTTP_HOST' in request.META: return None hardware_id = match_hwid.match(request.META["HTTP_HOST"]) if not hardware_id: return None kwargs["hardware_id"] = hardware_id.group("hwid") if not authenticate(request, *args, **kwargs): return HttpResponseForbidden("No access") return proxy_request(request, *args, **kwargs) @csrf_exempt def proxy_request(request, *args, **kwargs): # Get the port of target Node hardware_id = kwargs.get("hardware_id", "") try: port = Node.objects.filter(hardware_id=hardware_id).port except IndexError: # Node with given hwid was not found raise Http404 # We have to convert request.META back to original form manually headers = convert_headers(request) # HTTP_FOO_BAR to Foo-Bar headers["connection"] = "close" connection = httplib2.Http(timeout=5) url = "http://127.0.0.1:%d%s" % (port, request.META['PATH_INFO']) method = request.method # GET -- url ?d=a&t=a has to be urlencoded if method == "GET": data = None if request.GET: url += "?" + request.GET.urlencode() # POST -- body has to be urlencoded elif method == "POST": data = request.POST.urlencode() headers["content-type"] = "application/x-www-form-urlencoded" try: response, content = connection.request( url, method, data, headers=headers) except Exception as e: print e return HttpResponse(content=e, status=503) django_response = HttpResponse( content=content, status=int(response["status"]), mimetype=response["content-type"], ) # Strip hop-by-hop headers -- See RFC2616 semantically transparent # proxying. Also, WSGI forbids passing such headers back to it. hop_by_hop_headers = [ "connection", "keep-alive", "proxy-authenticate", "proxy-authorization", "te", "trailers", "transfer-encoding", "upgrade", ] for key, value in response.iteritems(): if key.lower() in hop_by_hop_headers: continue django_response[key] = value return django_response
Is it possible to do this kind of proxying at all in Django by tweaking the code above or other settings? The software stack I’m running on is Nginx + uWSGI + Django 1.6. The uWSGI configuration is:
[uwsgi] chdir = /home/foo/production/ file = /home/foo/production/wsgi.py home = /home/foo/virtualenv master = true processes = 8 socket = /var/nginx/foo.socket chmod-socket = 666 vacuum = true daemonize = /home/foo/production/uwsgi.log
|Asked By – andyn||Read Answers|