When doing web development, you'll occasionally run into a situation where you are developing a new user interface or otherwise redesigning a website, but either cannot or don't want to tear out the existing pieces you are replacing. Maybe the tyrannical IT department has to approve all reboots for the development server, or your new project is a quick prototype down a different line of thought and you don't want to devote time to replacing the current system before the prototype proves its value.
As it often does, Django steps in as a handy tool in this situation. With some minimal customization you can use the Django development server to serve certain urls patterns and transparently proxy the remainder to another server.
Why?
For development, the Django development server is a real boon. It isn't quick, but it's quick enough, and its automatic reboots on code changes really speeds up development.
You can transparently develop above an existing platform. This is especially true when building JavaScript heavy applications relying upon a RESTful api (because you are implementing new functionality on the client-side, and with your thoughtfully designed api you won't need to make changes to the server-side of the application).
In particular, this approach allows you to use
XMLHttpRequest-based Ajax functionality cross-domain without your browser slapping your hand and saying no. This works because you're requesting the URL locally, the proxy is fetching it from the remote host, and finally it is being returned to the requester it as if it all occurred locally.This also means that when you do integrate your new UI/feature into the existing application, you won't need to change any of the urls.
Don't need admin access (or ability/know-how to build) the main server. You don't have to wait for the server to reboot, caches to clear, or settings to reset.
How?
Using Django as a proxy is quite simple. First you'll need to install Django and Httplib2:
curl http://www.djangoproject.com/download/1.0/tarball/ > Django-1.0.tar.gz
tar xzvf Django-1.0.tar.gz
cd Django-1.0
sudo python setup.py install
sudo easy_install httplib2
Next put together a standard settings.py file. If you're just developing a new appearance or interface using static files, then your settings might look as simple as this:
import os
ROOT_PATH = os.path.dirname(__file__)
# Change this to the domain where you
# want requests to be proxied to.
PROXY_DOMAIN = "127.0.0.1:5678"
DEBUG = True
TEMPLATE_DEBUG = DEBUG
ADMINS = ()
MANAGERS = ADMINS
TIME_ZONE = 'America/Chicago'
LANGUAGE_CODE = 'en-us'
SITE_ID = 1
USE_I18N = True
MEDIA_ROOT = os.path.join(ROOT_PATH, 'ui')
MEDIA_URL = 'http://127.0.0.1:8000/media/'
ADMIN_MEDIA_PREFIX = '/admin_media/'
SECRET_KEY = 'etcetcetc'
TEMPLATE_LOADERS = ()
MIDDLEWARE_CLASSES = ('django.middleware.common.CommonMiddleware',)
ROOT_URLCONF = 'proxy_server.urls'
TEMPLATE_DIRS = ()
INSTALLED_APPS = ()
Then you also need to edit the project's urls.py, which is where the magic happens.
import httplib2
from urllib import urlencode
from django.conf.urls.defaults import *
from django.conf import settings
from django.http import HttpResponse
PROXY_FORMAT = u"http://%s/%s" % (settings.PROXY_DOMAIN, u"%s")
def proxy(request, url):
conn = httplib2.Http()
# optionally provide authentication for server
#conn.add_credentials('admin','admin-password')
if request.method == "GET":
url_ending = "%s?%s" % (url, urlencode(request.GET))
url = PROXY_FORMAT % url_ending
resp, content = conn.request(url, request.method)
return HttpResponse(content)
elif request.method == "POST":
url = PROXY_FORMAT % url
data = urlencode(request.POST)
resp, content = conn.request(url, request.method, data)
return HttpResponse(content)
urlpatterns = patterns('',
(r'^media/(?P<path>.*)$', 'django.views.static.serve', {'document_root': settings.MEDIA_ROOT }),
(r'^(?P<url>.*)$', proxy),
)
You may have to change urls around a bit for your specific circumstances, but that's basically it.
Let me know if there any questions or problems. As mentioned, this is definitely a development solution, as all solutions involving the Django dev server are, and especially as all solutions with the Django dev server serving media are.
Take it with a few grains of salt. Or a lemon. Or a shot of tequila. Preferably not in that order.
Usually it's the other way around - apache, or lighttpd, or one of the really small web-servers are normally used to serve static content, and act as a proxy to things like the django dev server.. as they are usually faster (less python'y overhead), more reliable/secure (very heavily tested web-servers), and more HTTP-compliant (again, testing)
Not quite sure you're reasons for using it this way are that valid.. If you proxy the dev server via apache, you get all the same benefits (automatic code reloading), with the above benefits... no?
Yep. This is true, but the article discusses using the django dev server as a proxy when doing prototyping work on top of an existing application. For example I am currently in a situation where the primary server has to be compiled (it is not a Django server) by someone else each time changes are made.
Even adding static media to that server is impossible without recompiling it, and there are licensing issues with me compiling it myself. However, I am building a JavaScript heavy UI on-top of the program, and am simply making api calls to the original server and serving static media myself.
Or as the first line of the article put it:
To further clarify: you could perform this form of proxying with Nginx or Apache instead of the Django devel server, but using the Django devel server has a number of benefits.
I am Mr Williams Harrison . I am a Legitimate And a Reputable money Lender. We are from Williams Loan firm with financial assistance.We loan funds out to individuals in need of financial assistance, that have a bad credit or indeed of money to pay bills,to invest on business.I want to use this medium to inform you that we render reliable and beneficiary assistance i will be glad to offer you a loan.Contact us via E-mail:williamsloanlenders43@hotmail.com
Thanks for the very useful article,
Would be good to also set the HttpResponse status:
return HttpResponse(content, status=resp.status)
so that 404s and other errors don't get masked.