PageRenderTime 58ms CodeModel.GetById 32ms RepoModel.GetById 0ms app.codeStats 1ms

/mercurial/hgweb/request.py

https://bitbucket.org/mirror/mercurial/
Python | 134 lines | 103 code | 16 blank | 15 comment | 26 complexity | 0a9e6d8d437700f3032f1cfa5f85c3db MD5 | raw file
Possible License(s): GPL-2.0
  1. # hgweb/request.py - An http request from either CGI or the standalone server.
  2. #
  3. # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
  4. # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
  5. #
  6. # This software may be used and distributed according to the terms of the
  7. # GNU General Public License version 2 or any later version.
  8. import socket, cgi, errno
  9. from mercurial import util
  10. from common import ErrorResponse, statusmessage, HTTP_NOT_MODIFIED
  11. shortcuts = {
  12. 'cl': [('cmd', ['changelog']), ('rev', None)],
  13. 'sl': [('cmd', ['shortlog']), ('rev', None)],
  14. 'cs': [('cmd', ['changeset']), ('node', None)],
  15. 'f': [('cmd', ['file']), ('filenode', None)],
  16. 'fl': [('cmd', ['filelog']), ('filenode', None)],
  17. 'fd': [('cmd', ['filediff']), ('node', None)],
  18. 'fa': [('cmd', ['annotate']), ('filenode', None)],
  19. 'mf': [('cmd', ['manifest']), ('manifest', None)],
  20. 'ca': [('cmd', ['archive']), ('node', None)],
  21. 'tags': [('cmd', ['tags'])],
  22. 'tip': [('cmd', ['changeset']), ('node', ['tip'])],
  23. 'static': [('cmd', ['static']), ('file', None)]
  24. }
  25. def normalize(form):
  26. # first expand the shortcuts
  27. for k in shortcuts.iterkeys():
  28. if k in form:
  29. for name, value in shortcuts[k]:
  30. if value is None:
  31. value = form[k]
  32. form[name] = value
  33. del form[k]
  34. # And strip the values
  35. for k, v in form.iteritems():
  36. form[k] = [i.strip() for i in v]
  37. return form
  38. class wsgirequest(object):
  39. def __init__(self, wsgienv, start_response):
  40. version = wsgienv['wsgi.version']
  41. if (version < (1, 0)) or (version >= (2, 0)):
  42. raise RuntimeError("Unknown and unsupported WSGI version %d.%d"
  43. % version)
  44. self.inp = wsgienv['wsgi.input']
  45. self.err = wsgienv['wsgi.errors']
  46. self.threaded = wsgienv['wsgi.multithread']
  47. self.multiprocess = wsgienv['wsgi.multiprocess']
  48. self.run_once = wsgienv['wsgi.run_once']
  49. self.env = wsgienv
  50. self.form = normalize(cgi.parse(self.inp,
  51. self.env,
  52. keep_blank_values=1))
  53. self._start_response = start_response
  54. self.server_write = None
  55. self.headers = []
  56. def __iter__(self):
  57. return iter([])
  58. def read(self, count=-1):
  59. return self.inp.read(count)
  60. def drain(self):
  61. '''need to read all data from request, httplib is half-duplex'''
  62. length = int(self.env.get('CONTENT_LENGTH') or 0)
  63. for s in util.filechunkiter(self.inp, limit=length):
  64. pass
  65. def respond(self, status, type, filename=None, body=None):
  66. if self._start_response is not None:
  67. self.headers.append(('Content-Type', type))
  68. if filename:
  69. filename = (filename.split('/')[-1]
  70. .replace('\\', '\\\\').replace('"', '\\"'))
  71. self.headers.append(('Content-Disposition',
  72. 'inline; filename="%s"' % filename))
  73. if body is not None:
  74. self.headers.append(('Content-Length', str(len(body))))
  75. for k, v in self.headers:
  76. if not isinstance(v, str):
  77. raise TypeError('header value must be string: %r' % (v,))
  78. if isinstance(status, ErrorResponse):
  79. self.headers.extend(status.headers)
  80. if status.code == HTTP_NOT_MODIFIED:
  81. # RFC 2616 Section 10.3.5: 304 Not Modified has cases where
  82. # it MUST NOT include any headers other than these and no
  83. # body
  84. self.headers = [(k, v) for (k, v) in self.headers if
  85. k in ('Date', 'ETag', 'Expires',
  86. 'Cache-Control', 'Vary')]
  87. status = statusmessage(status.code, status.message)
  88. elif status == 200:
  89. status = '200 Script output follows'
  90. elif isinstance(status, int):
  91. status = statusmessage(status)
  92. self.server_write = self._start_response(status, self.headers)
  93. self._start_response = None
  94. self.headers = []
  95. if body is not None:
  96. self.write(body)
  97. self.server_write = None
  98. def write(self, thing):
  99. if thing:
  100. try:
  101. self.server_write(thing)
  102. except socket.error, inst:
  103. if inst[0] != errno.ECONNRESET:
  104. raise
  105. def writelines(self, lines):
  106. for line in lines:
  107. self.write(line)
  108. def flush(self):
  109. return None
  110. def close(self):
  111. return None
  112. def wsgiapplication(app_maker):
  113. '''For compatibility with old CGI scripts. A plain hgweb() or hgwebdir()
  114. can and should now be used as a WSGI application.'''
  115. application = app_maker()
  116. def run_wsgi(env, respond):
  117. return application(env, respond)
  118. return run_wsgi