##// END OF EJS Templates
hgweb: fill in content-type and content-length as native strings...
Augie Fackler -
r34723:95be8928 default
parent child Browse files
Show More
@@ -1,155 +1,155 b''
1 1 # hgweb/request.py - An http request from either CGI or the standalone server.
2 2 #
3 3 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
4 4 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
5 5 #
6 6 # This software may be used and distributed according to the terms of the
7 7 # GNU General Public License version 2 or any later version.
8 8
9 9 from __future__ import absolute_import
10 10
11 11 import cgi
12 12 import errno
13 13 import socket
14 14
15 15 from .common import (
16 16 ErrorResponse,
17 17 HTTP_NOT_MODIFIED,
18 18 statusmessage,
19 19 )
20 20
21 21 from .. import (
22 22 pycompat,
23 23 util,
24 24 )
25 25
26 26 shortcuts = {
27 27 'cl': [('cmd', ['changelog']), ('rev', None)],
28 28 'sl': [('cmd', ['shortlog']), ('rev', None)],
29 29 'cs': [('cmd', ['changeset']), ('node', None)],
30 30 'f': [('cmd', ['file']), ('filenode', None)],
31 31 'fl': [('cmd', ['filelog']), ('filenode', None)],
32 32 'fd': [('cmd', ['filediff']), ('node', None)],
33 33 'fa': [('cmd', ['annotate']), ('filenode', None)],
34 34 'mf': [('cmd', ['manifest']), ('manifest', None)],
35 35 'ca': [('cmd', ['archive']), ('node', None)],
36 36 'tags': [('cmd', ['tags'])],
37 37 'tip': [('cmd', ['changeset']), ('node', ['tip'])],
38 38 'static': [('cmd', ['static']), ('file', None)]
39 39 }
40 40
41 41 def normalize(form):
42 42 # first expand the shortcuts
43 43 for k in shortcuts:
44 44 if k in form:
45 45 for name, value in shortcuts[k]:
46 46 if value is None:
47 47 value = form[k]
48 48 form[name] = value
49 49 del form[k]
50 50 # And strip the values
51 51 for k, v in form.iteritems():
52 52 form[k] = [i.strip() for i in v]
53 53 return form
54 54
55 55 class wsgirequest(object):
56 56 """Higher-level API for a WSGI request.
57 57
58 58 WSGI applications are invoked with 2 arguments. They are used to
59 59 instantiate instances of this class, which provides higher-level APIs
60 60 for obtaining request parameters, writing HTTP output, etc.
61 61 """
62 62 def __init__(self, wsgienv, start_response):
63 63 version = wsgienv[r'wsgi.version']
64 64 if (version < (1, 0)) or (version >= (2, 0)):
65 65 raise RuntimeError("Unknown and unsupported WSGI version %d.%d"
66 66 % version)
67 67 self.inp = wsgienv[r'wsgi.input']
68 68 self.err = wsgienv[r'wsgi.errors']
69 69 self.threaded = wsgienv[r'wsgi.multithread']
70 70 self.multiprocess = wsgienv[r'wsgi.multiprocess']
71 71 self.run_once = wsgienv[r'wsgi.run_once']
72 72 self.env = wsgienv
73 73 self.form = normalize(cgi.parse(self.inp,
74 74 self.env,
75 75 keep_blank_values=1))
76 76 self._start_response = start_response
77 77 self.server_write = None
78 78 self.headers = []
79 79
80 80 def __iter__(self):
81 81 return iter([])
82 82
83 83 def read(self, count=-1):
84 84 return self.inp.read(count)
85 85
86 86 def drain(self):
87 87 '''need to read all data from request, httplib is half-duplex'''
88 88 length = int(self.env.get('CONTENT_LENGTH') or 0)
89 89 for s in util.filechunkiter(self.inp, limit=length):
90 90 pass
91 91
92 92 def respond(self, status, type, filename=None, body=None):
93 93 if not isinstance(type, str):
94 94 type = pycompat.sysstr(type)
95 95 if self._start_response is not None:
96 self.headers.append(('Content-Type', type))
96 self.headers.append((r'Content-Type', type))
97 97 if filename:
98 98 filename = (filename.rpartition('/')[-1]
99 99 .replace('\\', '\\\\').replace('"', '\\"'))
100 100 self.headers.append(('Content-Disposition',
101 101 'inline; filename="%s"' % filename))
102 102 if body is not None:
103 self.headers.append(('Content-Length', str(len(body))))
103 self.headers.append((r'Content-Length', str(len(body))))
104 104
105 105 for k, v in self.headers:
106 106 if not isinstance(v, str):
107 107 raise TypeError('header value must be string: %r' % (v,))
108 108
109 109 if isinstance(status, ErrorResponse):
110 110 self.headers.extend(status.headers)
111 111 if status.code == HTTP_NOT_MODIFIED:
112 112 # RFC 2616 Section 10.3.5: 304 Not Modified has cases where
113 113 # it MUST NOT include any headers other than these and no
114 114 # body
115 115 self.headers = [(k, v) for (k, v) in self.headers if
116 116 k in ('Date', 'ETag', 'Expires',
117 117 'Cache-Control', 'Vary')]
118 118 status = statusmessage(status.code, str(status))
119 119 elif status == 200:
120 120 status = '200 Script output follows'
121 121 elif isinstance(status, int):
122 122 status = statusmessage(status)
123 123
124 124 self.server_write = self._start_response(status, self.headers)
125 125 self._start_response = None
126 126 self.headers = []
127 127 if body is not None:
128 128 self.write(body)
129 129 self.server_write = None
130 130
131 131 def write(self, thing):
132 132 if thing:
133 133 try:
134 134 self.server_write(thing)
135 135 except socket.error as inst:
136 136 if inst[0] != errno.ECONNRESET:
137 137 raise
138 138
139 139 def writelines(self, lines):
140 140 for line in lines:
141 141 self.write(line)
142 142
143 143 def flush(self):
144 144 return None
145 145
146 146 def close(self):
147 147 return None
148 148
149 149 def wsgiapplication(app_maker):
150 150 '''For compatibility with old CGI scripts. A plain hgweb() or hgwebdir()
151 151 can and should now be used as a WSGI application.'''
152 152 application = app_maker()
153 153 def run_wsgi(env, respond):
154 154 return application(env, respond)
155 155 return run_wsgi
General Comments 0
You need to be logged in to leave comments. Login now