No subject


Tue Jun 24 15:43:11 UTC 2008


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 = "hg@%s" % 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'



More information about the Mercurial mailing list