Hello everyone,
The version control system mercurial (hg) has some functionality to allow its repositories to be viewed via the web. However, it does not have a builtin authentication system. I'm trying to get a setup working where I can put Django in front. Jesper Noehr of bitbucket.org has some code that looks like it would do what I want. Basically, it has Django act as a proxy (I hope I have the terminology right), which sents a request to the hgweb code, and gets back a response object. However, this code is currently giving errors, and they are at the Django end, so I'm CCing here in the hope that someone can set me straight about what is going on. I'm posting the view code from the hgwebproxy app in https://bitbucket.org/jespern/cx/. This basically does everything. I then follow it with the error message I see in Django. The error message comes from the line self.inp = self.env['wsgi.input'] Jesper thinks that a generic Django request should contain such keys, but the request I'm seeing doesn't. He thinks I need to upgrade Django, but before I do that, I thought I would write and ask if the behavior has really changed so much in recent versions. I've currently using a version from late May, and was waiting for 1.0 to upgrade. I looked at my current version, and it has a wsgi.py, though I have no idea how to use it, and could not find any documentation. >From what I understand from Jesper, _hgReqWrap wraps the Django request object into the sort of request that hgwebdir expects. However, I thought the whole point of wsgi was to provide a low-level compatability layer between different Python web applications, so shouldn't these kind of manipulations be unnecessary? To summarize, I'm asking two things a) What is the simplest/most direct way to fix the current code? b) Is there a simpler way to do the same thing? Ie. get django to proxy for hgweb? Please CC me on any reply. Thanks, Faheem. ********************************************************************* self.env = req.META where req is a django request (see__init__ of hgReqWrap below). ********************************************************************* self.env is {'AUTH_TYPE': None, 'HTTP_COOKIE': 'bixfile=d1e2ea28f5cc1d4f43a9a14e0db4970f', 'SERVER_SOFTWARE': 'mod_python', 'SCRIPT_NAME': None, 'REQUEST_METHOD': 'GET', 'PATH_INFO': '', 'SERVER_PROTOCOL': 'HTTP/1.1', 'QUERY_STRING': None, 'CONTENT_LENGTH': 0L, 'HTTP_ACCEPT_CHARSET': 'ISO-8859-1,utf-8;q=0.7,*;q=0.7', 'REMOTE_USER': None, 'HTTP_CONNECTION': 'keep-alive', 'SERVER_NAME': 'msi.home.earth', 'REMOTE_ADDR': '192.168.1.204', 'P ATH_TRANSLATED': None, 'SERVER_PORT': 443, 'HTTP_USER_AGENT': 'Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8. 0.14eol) Gecko/20070505 (Debian-1.8.0.15~pre080614d-0etch1) Epiphany/2.14', 'HTTP_HOST': 'msi', 'HTTP_CACHE_CONTROL': 'max-age=0', 'HTTP_ACCEPT': 'text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain ;q=0.8,image/png,*/*;q=0.5', 'GATEWAY_INTERFACE': 'CGI/1.1', 'HTTP_ACCEPT_LANGUAGE': 'en-us,en;q=0.5', 'REMOTE_IDENT': None, 'CONTENT_TYPE': None, 'REMOTE_HOST': None, 'HTTP_ACCEPT_ENCODING': 'gzip,deflate', 'HTTP_KEEP_A LIVE': '300'} ******************************************************************** ******************************************************************** views.py ******************************************************************** import os, re, cgi from django.http import HttpResponseRedirect, HttpResponse from django.conf import settings from django.shortcuts import render_to_response from django.template import RequestContext from django.contrib.auth.models import User from datetime import datetime from mercurial.hgweb.hgwebdir_mod import hgwebdir from mercurial import hg, ui from mercurial import __version__ try: from hashlib import md5 as md5 except ImportError: from md5 import md5 class _hgReqWrap(object): def __init__(self, req, resp): self.django_req = req self.env = req.META self.response = resp # Remove the prefix so HG will think it's running on its own. self.env['PATH_INFO'] = self.env['PATH_INFO'].lstrip("/hg") # Make sure there's a content-length. if not self.env.has_key('CONTENT_LENGTH'): self.env['CONTENT_LENGTH'] = 0 self.inp = self.env['wsgi.input'] self.form = cgi.parse(self.inp, self.env, keep_blank_values=1) self.headers = [ ] self.err = self.env['wsgi.errors'] self.out = [ ] def set_user(self, username): self.env['REMOTE_USER'] = username def read(self, count=-1): return self.inp.read(count) def flush(self): return None def respond(self, code, content_type=None, path=None, length=0): self.response.status_code = code self.response['content-type'] = content_type if path is not None and length is not None: self.response['content-type'] = content_type self.response['content-length'] = length self.response['content-disposition'] = 'inline; filename=%s' % path for directive, value in self.headers: self.response[directive.lower()] = value def header(self, headers=[('Content-Type','text/html')]): self.headers.extend(headers) def write(self, *a): for thing in a: if hasattr(thing, '__iter__'): for p in thing: self.write(p) else: thing = str(thing) self.response.write(thing) def digest_auth(request, realm, opaque, users): auth_string = request.META.get('HTTP_AUTHORIZATION', None) if auth_string is None and not str(auth_string).startswith("Digest"): return False parts = auth_string.lstrip("Digest ").split(",") auth = { } for part in parts: # `partition' only in 2.5 segs = part.lstrip().split("=") auth[segs[0]] = '='.join(segs[1:]).strip('"') # Quick opaque check if not opaque == auth['opaque']: return False for ha1 in users: ha2 = md5("%(method)s:%(path)s" % { 'method': request.method, 'path': request.get_full_path() }) response = md5("%(ha_one)s:%(nonce)s:%(nc)s:%(cnonce)s:%(qop)s:%(ha_two)s" \ % { 'ha_one': ha1, 'ha_two': ha2.hexdigest(), 'nonce': auth['nonce'], 'nc': auth['nc'], 'cnonce': auth['cnonce'], 'qop': auth['qop'] }) if response.hexdigest() == auth['response']: return auth['username'] return False def hgroot(request, *args): resp = HttpResponse() hgr = _hgReqWrap(request, resp) config = os.path.join(settings.BASE_DIR, 'apps', 'hgwebproxy', 'hgweb.conf') os.environ['HGRCPATH'] = config if request.method == "POST": realm = "[EMAIL PROTECTED]" % settings.SITE_NAME nonce = md5(str(datetime.now())+realm).hexdigest() opaque = md5(settings.SITE_NAME).hexdigest() users = settings.HG_DIGEST_USERS authed = digest_auth(request, realm, opaque, users) if not authed: resp.status_code = 401 resp['WWW-Authenticate'] = '''Digest realm="%s", qop="auth", nonce="%s", opaque="%s"''' % (realm, nonce, opaque) return resp else: hgr.set_user(authed) try: hgwebdir(config).run_wsgi(hgr) except KeyError: resp['content-type'] = 'text/html' resp.write('hgweb crashed.') pass # hgweb tends to throw these on invalid requests..? # nothing to do but ignore it. hg >1.0 might fix. if resp.has_header('content-type'): if not resp['content-type'].startswith("text/html"): return resp return render_to_response("hgwebproxy/flat.html", { 'content': resp.content, 'slugpath': request.path.lstrip("/hg"), 'hg_version': __version__.version, 'is_root': request.path == '/hg/' }, RequestContext(request)) ****************************************************************************** Traceback: File "/usr/local/lib/python2.4/site-packages/django/core/handlers/base.py" in get_response 82. response = callback(request, *callback_args, **callback_kwargs) File "/var/django/hg/hgwebproxy/views.py" in hgroot 111. hgr = _hgReqWrap(request, resp) File "/var/django/hg/hgwebproxy/views.py" in __init__ 35. self.inp = self.env['wsgi.input'] Exception Type: KeyError at /hg/ Exception Value: 'wsgi.input' --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Django users" group. To post to this group, send email to django-users@googlegroups.com To unsubscribe from this group, send email to [EMAIL PROTECTED] For more options, visit this group at http://groups.google.com/group/django-users?hl=en -~----------~----~----~----~------~----~------~--~---