annotate mercurial/hgweb/request.py @ 36882:97f44b0720e2

hgweb: port archive command to modern response API Well, I tried to go with PEP 3333's recommendations and only allow our WSGI application to emit data via a response generator. Unfortunately, the "archive" command calls into the zipfile and tarfile modules and these operator on file objects and must send their data to an object with write(). There's no easy way turn these write() calls into a generator. So, we teach our response type how to expose a file object like object that can be used to write() output. We try to keep the API consistent with how things work currently: callers must call a setbody*(), then sendresponse() to trigger sending of headers, and only then can they get a handle on the object to perform writing. This required overloading the return value of @webcommand functions even more. Fortunately, we're almost completely ported off the legacy API. So we should be able to simplify matters in the near future. A test relying on this functionality has also been updated to use the new API. Differential Revision: https://phab.mercurial-scm.org/D2792
author Gregory Szorc <gregory.szorc@gmail.com>
date Sat, 10 Mar 2018 20:16:20 -0800
parents 16499427f6de
children ccb70a77f746
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
rev   line source
2391
d351a3be3371 Fixing up comment headers for split up code.
Eric Hopper <hopper@omnifarious.org>
parents: 2355
diff changeset
1 # hgweb/request.py - An http request from either CGI or the standalone server.
131
c9d51742471c moving hgweb to mercurial subdir
jake@edge2.net
parents:
diff changeset
2 #
238
3b92f8fe47ae hgweb.py: kill #! line, clean up copyright notice
mpm@selenic.com
parents: 222
diff changeset
3 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
2859
345bac2bc4ec update copyrights.
Vadim Gelfer <vadim.gelfer@gmail.com>
parents: 2535
diff changeset
4 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
131
c9d51742471c moving hgweb to mercurial subdir
jake@edge2.net
parents:
diff changeset
5 #
8225
46293a0c7e9f updated license to be explicit about GPL version 2
Martin Geisler <mg@lazybytes.net>
parents: 7742
diff changeset
6 # This software may be used and distributed according to the terms of the
10263
25e572394f5c Update license to GPLv2+
Matt Mackall <mpm@selenic.com>
parents: 10261
diff changeset
7 # GNU General Public License version 2 or any later version.
131
c9d51742471c moving hgweb to mercurial subdir
jake@edge2.net
parents:
diff changeset
8
27046
37fcfe52c68c hgweb: use absolute_import
Yuya Nishihara <yuya@tcha.org>
parents: 26846
diff changeset
9 from __future__ import absolute_import
37fcfe52c68c hgweb: use absolute_import
Yuya Nishihara <yuya@tcha.org>
parents: 26846
diff changeset
10
37fcfe52c68c hgweb: use absolute_import
Yuya Nishihara <yuya@tcha.org>
parents: 26846
diff changeset
11 import errno
37fcfe52c68c hgweb: use absolute_import
Yuya Nishihara <yuya@tcha.org>
parents: 26846
diff changeset
12 import socket
36822
f9078c6caeb6 hgweb: parse and store HTTP request headers
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36819
diff changeset
13 import wsgiref.headers as wsgiheaders
36814
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36736
diff changeset
14 #import wsgiref.validate
27046
37fcfe52c68c hgweb: use absolute_import
Yuya Nishihara <yuya@tcha.org>
parents: 26846
diff changeset
15
37fcfe52c68c hgweb: use absolute_import
Yuya Nishihara <yuya@tcha.org>
parents: 26846
diff changeset
16 from .common import (
37fcfe52c68c hgweb: use absolute_import
Yuya Nishihara <yuya@tcha.org>
parents: 26846
diff changeset
17 ErrorResponse,
37fcfe52c68c hgweb: use absolute_import
Yuya Nishihara <yuya@tcha.org>
parents: 26846
diff changeset
18 HTTP_NOT_MODIFIED,
37fcfe52c68c hgweb: use absolute_import
Yuya Nishihara <yuya@tcha.org>
parents: 26846
diff changeset
19 statusmessage,
37fcfe52c68c hgweb: use absolute_import
Yuya Nishihara <yuya@tcha.org>
parents: 26846
diff changeset
20 )
37fcfe52c68c hgweb: use absolute_import
Yuya Nishihara <yuya@tcha.org>
parents: 26846
diff changeset
21
36814
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36736
diff changeset
22 from ..thirdparty import (
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36736
diff changeset
23 attr,
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36736
diff changeset
24 )
27046
37fcfe52c68c hgweb: use absolute_import
Yuya Nishihara <yuya@tcha.org>
parents: 26846
diff changeset
25 from .. import (
36867
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36865
diff changeset
26 error,
34514
528b21b853aa request: coerce content-type to native str
Augie Fackler <augie@google.com>
parents: 34513
diff changeset
27 pycompat,
27046
37fcfe52c68c hgweb: use absolute_import
Yuya Nishihara <yuya@tcha.org>
parents: 26846
diff changeset
28 util,
37fcfe52c68c hgweb: use absolute_import
Yuya Nishihara <yuya@tcha.org>
parents: 26846
diff changeset
29 )
138
c77a679e9cfa Revamped templated hgweb
mpm@selenic.com
parents: 137
diff changeset
30
36868
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36867
diff changeset
31 class multidict(object):
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36867
diff changeset
32 """A dict like object that can store multiple values for a key.
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36867
diff changeset
33
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36867
diff changeset
34 Used to store parsed request parameters.
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36867
diff changeset
35
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36867
diff changeset
36 This is inspired by WebOb's class of the same name.
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36867
diff changeset
37 """
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36867
diff changeset
38 def __init__(self):
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36867
diff changeset
39 # Stores (key, value) 2-tuples. This isn't the most efficient. But we
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36867
diff changeset
40 # don't rely on parameters that much, so it shouldn't be a perf issue.
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36867
diff changeset
41 # we can always add dict for fast lookups.
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36867
diff changeset
42 self._items = []
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36867
diff changeset
43
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36867
diff changeset
44 def __getitem__(self, key):
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36867
diff changeset
45 """Returns the last set value for a key."""
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36867
diff changeset
46 for k, v in reversed(self._items):
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36867
diff changeset
47 if k == key:
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36867
diff changeset
48 return v
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36867
diff changeset
49
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36867
diff changeset
50 raise KeyError(key)
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36867
diff changeset
51
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36867
diff changeset
52 def __setitem__(self, key, value):
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36867
diff changeset
53 """Replace a values for a key with a new value."""
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36867
diff changeset
54 try:
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36867
diff changeset
55 del self[key]
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36867
diff changeset
56 except KeyError:
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36867
diff changeset
57 pass
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36867
diff changeset
58
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36867
diff changeset
59 self._items.append((key, value))
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36867
diff changeset
60
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36867
diff changeset
61 def __delitem__(self, key):
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36867
diff changeset
62 """Delete all values for a key."""
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36867
diff changeset
63 oldlen = len(self._items)
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36867
diff changeset
64
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36867
diff changeset
65 self._items[:] = [(k, v) for k, v in self._items if k != key]
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36867
diff changeset
66
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36867
diff changeset
67 if oldlen == len(self._items):
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36867
diff changeset
68 raise KeyError(key)
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36867
diff changeset
69
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36867
diff changeset
70 def __contains__(self, key):
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36867
diff changeset
71 return any(k == key for k, v in self._items)
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36867
diff changeset
72
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36867
diff changeset
73 def __len__(self):
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36867
diff changeset
74 return len(self._items)
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36867
diff changeset
75
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36867
diff changeset
76 def get(self, key, default=None):
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36867
diff changeset
77 try:
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36867
diff changeset
78 return self.__getitem__(key)
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36867
diff changeset
79 except KeyError:
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36867
diff changeset
80 return default
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36867
diff changeset
81
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36867
diff changeset
82 def add(self, key, value):
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36867
diff changeset
83 """Add a new value for a key. Does not replace existing values."""
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36867
diff changeset
84 self._items.append((key, value))
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36867
diff changeset
85
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36867
diff changeset
86 def getall(self, key):
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36867
diff changeset
87 """Obtains all values for a key."""
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36867
diff changeset
88 return [v for k, v in self._items if k == key]
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36867
diff changeset
89
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36867
diff changeset
90 def getone(self, key):
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36867
diff changeset
91 """Obtain a single value for a key.
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36867
diff changeset
92
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36867
diff changeset
93 Raises KeyError if key not defined or it has multiple values set.
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36867
diff changeset
94 """
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36867
diff changeset
95 vals = self.getall(key)
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36867
diff changeset
96
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36867
diff changeset
97 if not vals:
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36867
diff changeset
98 raise KeyError(key)
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36867
diff changeset
99
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36867
diff changeset
100 if len(vals) > 1:
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36867
diff changeset
101 raise KeyError('multiple values for %r' % key)
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36867
diff changeset
102
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36867
diff changeset
103 return vals[0]
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36867
diff changeset
104
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36867
diff changeset
105 def asdictoflists(self):
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36867
diff changeset
106 d = {}
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36867
diff changeset
107 for k, v in self._items:
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36867
diff changeset
108 if k in d:
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36867
diff changeset
109 d[k].append(v)
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36867
diff changeset
110 else:
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36867
diff changeset
111 d[k] = [v]
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36867
diff changeset
112
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36867
diff changeset
113 return d
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36867
diff changeset
114
36814
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36736
diff changeset
115 @attr.s(frozen=True)
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36736
diff changeset
116 class parsedrequest(object):
36863
da4e2f87167d hgweb: expose input stream on parsed WSGI request object
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36862
diff changeset
117 """Represents a parsed WSGI request.
da4e2f87167d hgweb: expose input stream on parsed WSGI request object
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36862
diff changeset
118
da4e2f87167d hgweb: expose input stream on parsed WSGI request object
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36862
diff changeset
119 Contains both parsed parameters as well as a handle on the input stream.
da4e2f87167d hgweb: expose input stream on parsed WSGI request object
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36862
diff changeset
120 """
36814
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36736
diff changeset
121
36854
16292bbda39c hgweb: store and use request method on parsed request
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36853
diff changeset
122 # Request method.
16292bbda39c hgweb: store and use request method on parsed request
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36853
diff changeset
123 method = attr.ib()
36814
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36736
diff changeset
124 # Full URL for this request.
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36736
diff changeset
125 url = attr.ib()
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36736
diff changeset
126 # URL without any path components. Just <proto>://<host><port>.
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36736
diff changeset
127 baseurl = attr.ib()
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36736
diff changeset
128 # Advertised URL. Like ``url`` and ``baseurl`` but uses SERVER_NAME instead
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36736
diff changeset
129 # of HTTP: Host header for hostname. This is likely what clients used.
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36736
diff changeset
130 advertisedurl = attr.ib()
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36736
diff changeset
131 advertisedbaseurl = attr.ib()
36873
a755fd3b7146 hgweb: expose URL scheme and REMOTE_* attributes
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36872
diff changeset
132 # URL scheme (part before ``://``). e.g. ``http`` or ``https``.
a755fd3b7146 hgweb: expose URL scheme and REMOTE_* attributes
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36872
diff changeset
133 urlscheme = attr.ib()
a755fd3b7146 hgweb: expose URL scheme and REMOTE_* attributes
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36872
diff changeset
134 # Value of REMOTE_USER, if set, or None.
a755fd3b7146 hgweb: expose URL scheme and REMOTE_* attributes
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36872
diff changeset
135 remoteuser = attr.ib()
a755fd3b7146 hgweb: expose URL scheme and REMOTE_* attributes
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36872
diff changeset
136 # Value of REMOTE_HOST, if set, or None.
a755fd3b7146 hgweb: expose URL scheme and REMOTE_* attributes
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36872
diff changeset
137 remotehost = attr.ib()
36814
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36736
diff changeset
138 # WSGI application path.
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36736
diff changeset
139 apppath = attr.ib()
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36736
diff changeset
140 # List of path parts to be used for dispatch.
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36736
diff changeset
141 dispatchparts = attr.ib()
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36736
diff changeset
142 # URL path component (no query string) used for dispatch.
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36736
diff changeset
143 dispatchpath = attr.ib()
36819
cfb9ef24968c hgweb: use parsed request to construct query parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36817
diff changeset
144 # Whether there is a path component to this request. This can be true
cfb9ef24968c hgweb: use parsed request to construct query parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36817
diff changeset
145 # when ``dispatchpath`` is empty due to REPO_NAME muckery.
cfb9ef24968c hgweb: use parsed request to construct query parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36817
diff changeset
146 havepathinfo = attr.ib()
36874
8ddb5c354906 hgweb: expose repo name on parsedrequest
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36873
diff changeset
147 # The name of the repository being accessed.
8ddb5c354906 hgweb: expose repo name on parsedrequest
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36873
diff changeset
148 reponame = attr.ib()
36814
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36736
diff changeset
149 # Raw query string (part after "?" in URL).
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36736
diff changeset
150 querystring = attr.ib()
36868
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36867
diff changeset
151 # multidict of query string parameters.
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36867
diff changeset
152 qsparams = attr.ib()
36822
f9078c6caeb6 hgweb: parse and store HTTP request headers
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36819
diff changeset
153 # wsgiref.headers.Headers instance. Operates like a dict with case
f9078c6caeb6 hgweb: parse and store HTTP request headers
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36819
diff changeset
154 # insensitive keys.
f9078c6caeb6 hgweb: parse and store HTTP request headers
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36819
diff changeset
155 headers = attr.ib()
36863
da4e2f87167d hgweb: expose input stream on parsed WSGI request object
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36862
diff changeset
156 # Request body input stream.
da4e2f87167d hgweb: expose input stream on parsed WSGI request object
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36862
diff changeset
157 bodyfh = attr.ib()
36814
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36736
diff changeset
158
36863
da4e2f87167d hgweb: expose input stream on parsed WSGI request object
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36862
diff changeset
159 def parserequestfromenv(env, bodyfh):
36814
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36736
diff changeset
160 """Parse URL components from environment variables.
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36736
diff changeset
161
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36736
diff changeset
162 WSGI defines request attributes via environment variables. This function
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36736
diff changeset
163 parses the environment variables into a data structure.
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36736
diff changeset
164 """
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36736
diff changeset
165 # PEP-0333 defines the WSGI spec and is a useful reference for this code.
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36736
diff changeset
166
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36736
diff changeset
167 # We first validate that the incoming object conforms with the WSGI spec.
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36736
diff changeset
168 # We only want to be dealing with spec-conforming WSGI implementations.
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36736
diff changeset
169 # TODO enable this once we fix internal violations.
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36736
diff changeset
170 #wsgiref.validate.check_environ(env)
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36736
diff changeset
171
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36736
diff changeset
172 # PEP-0333 states that environment keys and values are native strings
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36736
diff changeset
173 # (bytes on Python 2 and str on Python 3). The code points for the Unicode
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36736
diff changeset
174 # strings on Python 3 must be between \00000-\000FF. We deal with bytes
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36736
diff changeset
175 # in Mercurial, so mass convert string keys and values to bytes.
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36736
diff changeset
176 if pycompat.ispy3:
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36736
diff changeset
177 env = {k.encode('latin-1'): v for k, v in env.iteritems()}
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36736
diff changeset
178 env = {k: v.encode('latin-1') if isinstance(v, str) else v
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36736
diff changeset
179 for k, v in env.iteritems()}
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36736
diff changeset
180
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36736
diff changeset
181 # https://www.python.org/dev/peps/pep-0333/#environ-variables defines
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36736
diff changeset
182 # the environment variables.
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36736
diff changeset
183 # https://www.python.org/dev/peps/pep-0333/#url-reconstruction defines
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36736
diff changeset
184 # how URLs are reconstructed.
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36736
diff changeset
185 fullurl = env['wsgi.url_scheme'] + '://'
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36736
diff changeset
186 advertisedfullurl = fullurl
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36736
diff changeset
187
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36736
diff changeset
188 def addport(s):
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36736
diff changeset
189 if env['wsgi.url_scheme'] == 'https':
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36736
diff changeset
190 if env['SERVER_PORT'] != '443':
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36736
diff changeset
191 s += ':' + env['SERVER_PORT']
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36736
diff changeset
192 else:
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36736
diff changeset
193 if env['SERVER_PORT'] != '80':
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36736
diff changeset
194 s += ':' + env['SERVER_PORT']
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36736
diff changeset
195
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36736
diff changeset
196 return s
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36736
diff changeset
197
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36736
diff changeset
198 if env.get('HTTP_HOST'):
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36736
diff changeset
199 fullurl += env['HTTP_HOST']
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36736
diff changeset
200 else:
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36736
diff changeset
201 fullurl += env['SERVER_NAME']
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36736
diff changeset
202 fullurl = addport(fullurl)
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36736
diff changeset
203
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36736
diff changeset
204 advertisedfullurl += env['SERVER_NAME']
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36736
diff changeset
205 advertisedfullurl = addport(advertisedfullurl)
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36736
diff changeset
206
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36736
diff changeset
207 baseurl = fullurl
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36736
diff changeset
208 advertisedbaseurl = advertisedfullurl
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36736
diff changeset
209
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36736
diff changeset
210 fullurl += util.urlreq.quote(env.get('SCRIPT_NAME', ''))
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36736
diff changeset
211 advertisedfullurl += util.urlreq.quote(env.get('SCRIPT_NAME', ''))
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36736
diff changeset
212 fullurl += util.urlreq.quote(env.get('PATH_INFO', ''))
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36736
diff changeset
213 advertisedfullurl += util.urlreq.quote(env.get('PATH_INFO', ''))
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36736
diff changeset
214
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36736
diff changeset
215 if env.get('QUERY_STRING'):
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36736
diff changeset
216 fullurl += '?' + env['QUERY_STRING']
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36736
diff changeset
217 advertisedfullurl += '?' + env['QUERY_STRING']
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36736
diff changeset
218
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36736
diff changeset
219 # When dispatching requests, we look at the URL components (PATH_INFO
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36736
diff changeset
220 # and QUERY_STRING) after the application root (SCRIPT_NAME). But hgwebdir
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36736
diff changeset
221 # has the concept of "virtual" repositories. This is defined via REPO_NAME.
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36736
diff changeset
222 # If REPO_NAME is defined, we append it to SCRIPT_NAME to form a new app
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36736
diff changeset
223 # root. We also exclude its path components from PATH_INFO when resolving
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36736
diff changeset
224 # the dispatch path.
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36736
diff changeset
225
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36736
diff changeset
226 apppath = env['SCRIPT_NAME']
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36736
diff changeset
227
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36736
diff changeset
228 if env.get('REPO_NAME'):
36816
0031e972ded2 hgweb: use the parsed application path directly
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36814
diff changeset
229 if not apppath.endswith('/'):
0031e972ded2 hgweb: use the parsed application path directly
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36814
diff changeset
230 apppath += '/'
0031e972ded2 hgweb: use the parsed application path directly
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36814
diff changeset
231
0031e972ded2 hgweb: use the parsed application path directly
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36814
diff changeset
232 apppath += env.get('REPO_NAME')
36814
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36736
diff changeset
233
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36736
diff changeset
234 if 'PATH_INFO' in env:
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36736
diff changeset
235 dispatchparts = env['PATH_INFO'].strip('/').split('/')
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36736
diff changeset
236
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36736
diff changeset
237 # Strip out repo parts.
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36736
diff changeset
238 repoparts = env.get('REPO_NAME', '').split('/')
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36736
diff changeset
239 if dispatchparts[:len(repoparts)] == repoparts:
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36736
diff changeset
240 dispatchparts = dispatchparts[len(repoparts):]
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36736
diff changeset
241 else:
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36736
diff changeset
242 dispatchparts = []
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36736
diff changeset
243
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36736
diff changeset
244 dispatchpath = '/'.join(dispatchparts)
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36736
diff changeset
245
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36736
diff changeset
246 querystring = env.get('QUERY_STRING', '')
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36736
diff changeset
247
36817
3c15b84ab66c hgweb: teach WSGI parser about query strings
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36816
diff changeset
248 # We store as a list so we have ordering information. We also store as
3c15b84ab66c hgweb: teach WSGI parser about query strings
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36816
diff changeset
249 # a dict to facilitate fast lookup.
36868
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36867
diff changeset
250 qsparams = multidict()
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36867
diff changeset
251 for k, v in util.urlreq.parseqsl(querystring, keep_blank_values=True):
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36867
diff changeset
252 qsparams.add(k, v)
36817
3c15b84ab66c hgweb: teach WSGI parser about query strings
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36816
diff changeset
253
36822
f9078c6caeb6 hgweb: parse and store HTTP request headers
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36819
diff changeset
254 # HTTP_* keys contain HTTP request headers. The Headers structure should
f9078c6caeb6 hgweb: parse and store HTTP request headers
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36819
diff changeset
255 # perform case normalization for us. We just rewrite underscore to dash
f9078c6caeb6 hgweb: parse and store HTTP request headers
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36819
diff changeset
256 # so keys match what likely went over the wire.
f9078c6caeb6 hgweb: parse and store HTTP request headers
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36819
diff changeset
257 headers = []
f9078c6caeb6 hgweb: parse and store HTTP request headers
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36819
diff changeset
258 for k, v in env.iteritems():
f9078c6caeb6 hgweb: parse and store HTTP request headers
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36819
diff changeset
259 if k.startswith('HTTP_'):
f9078c6caeb6 hgweb: parse and store HTTP request headers
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36819
diff changeset
260 headers.append((k[len('HTTP_'):].replace('_', '-'), v))
f9078c6caeb6 hgweb: parse and store HTTP request headers
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36819
diff changeset
261
f9078c6caeb6 hgweb: parse and store HTTP request headers
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36819
diff changeset
262 headers = wsgiheaders.Headers(headers)
f9078c6caeb6 hgweb: parse and store HTTP request headers
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36819
diff changeset
263
36853
ed0456fde625 hgweb: handle CONTENT_LENGTH
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36822
diff changeset
264 # This is kind of a lie because the HTTP header wasn't explicitly
ed0456fde625 hgweb: handle CONTENT_LENGTH
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36822
diff changeset
265 # sent. But for all intents and purposes it should be OK to lie about
ed0456fde625 hgweb: handle CONTENT_LENGTH
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36822
diff changeset
266 # this, since a consumer will either either value to determine how many
ed0456fde625 hgweb: handle CONTENT_LENGTH
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36822
diff changeset
267 # bytes are available to read.
ed0456fde625 hgweb: handle CONTENT_LENGTH
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36822
diff changeset
268 if 'CONTENT_LENGTH' in env and 'HTTP_CONTENT_LENGTH' not in env:
ed0456fde625 hgweb: handle CONTENT_LENGTH
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36822
diff changeset
269 headers['Content-Length'] = env['CONTENT_LENGTH']
ed0456fde625 hgweb: handle CONTENT_LENGTH
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36822
diff changeset
270
36863
da4e2f87167d hgweb: expose input stream on parsed WSGI request object
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36862
diff changeset
271 # TODO do this once we remove wsgirequest.inp, otherwise we could have
da4e2f87167d hgweb: expose input stream on parsed WSGI request object
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36862
diff changeset
272 # multiple readers from the underlying input stream.
da4e2f87167d hgweb: expose input stream on parsed WSGI request object
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36862
diff changeset
273 #bodyfh = env['wsgi.input']
da4e2f87167d hgweb: expose input stream on parsed WSGI request object
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36862
diff changeset
274 #if 'Content-Length' in headers:
da4e2f87167d hgweb: expose input stream on parsed WSGI request object
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36862
diff changeset
275 # bodyfh = util.cappedreader(bodyfh, int(headers['Content-Length']))
da4e2f87167d hgweb: expose input stream on parsed WSGI request object
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36862
diff changeset
276
36854
16292bbda39c hgweb: store and use request method on parsed request
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36853
diff changeset
277 return parsedrequest(method=env['REQUEST_METHOD'],
16292bbda39c hgweb: store and use request method on parsed request
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36853
diff changeset
278 url=fullurl, baseurl=baseurl,
36814
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36736
diff changeset
279 advertisedurl=advertisedfullurl,
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36736
diff changeset
280 advertisedbaseurl=advertisedbaseurl,
36873
a755fd3b7146 hgweb: expose URL scheme and REMOTE_* attributes
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36872
diff changeset
281 urlscheme=env['wsgi.url_scheme'],
a755fd3b7146 hgweb: expose URL scheme and REMOTE_* attributes
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36872
diff changeset
282 remoteuser=env.get('REMOTE_USER'),
a755fd3b7146 hgweb: expose URL scheme and REMOTE_* attributes
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36872
diff changeset
283 remotehost=env.get('REMOTE_HOST'),
36814
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36736
diff changeset
284 apppath=apppath,
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36736
diff changeset
285 dispatchparts=dispatchparts, dispatchpath=dispatchpath,
36819
cfb9ef24968c hgweb: use parsed request to construct query parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36817
diff changeset
286 havepathinfo='PATH_INFO' in env,
36874
8ddb5c354906 hgweb: expose repo name on parsedrequest
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36873
diff changeset
287 reponame=env.get('REPO_NAME'),
36817
3c15b84ab66c hgweb: teach WSGI parser about query strings
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36816
diff changeset
288 querystring=querystring,
36868
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36867
diff changeset
289 qsparams=qsparams,
36863
da4e2f87167d hgweb: expose input stream on parsed WSGI request object
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36862
diff changeset
290 headers=headers,
da4e2f87167d hgweb: expose input stream on parsed WSGI request object
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36862
diff changeset
291 bodyfh=bodyfh)
36814
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36736
diff changeset
292
36881
16499427f6de hgweb: refactor fake file object proxy for archiving
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36874
diff changeset
293 class offsettrackingwriter(object):
16499427f6de hgweb: refactor fake file object proxy for archiving
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36874
diff changeset
294 """A file object like object that is append only and tracks write count.
16499427f6de hgweb: refactor fake file object proxy for archiving
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36874
diff changeset
295
16499427f6de hgweb: refactor fake file object proxy for archiving
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36874
diff changeset
296 Instances are bound to a callable. This callable is called with data
16499427f6de hgweb: refactor fake file object proxy for archiving
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36874
diff changeset
297 whenever a ``write()`` is attempted.
16499427f6de hgweb: refactor fake file object proxy for archiving
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36874
diff changeset
298
16499427f6de hgweb: refactor fake file object proxy for archiving
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36874
diff changeset
299 Instances track the amount of written data so they can answer ``tell()``
16499427f6de hgweb: refactor fake file object proxy for archiving
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36874
diff changeset
300 requests.
16499427f6de hgweb: refactor fake file object proxy for archiving
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36874
diff changeset
301
16499427f6de hgweb: refactor fake file object proxy for archiving
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36874
diff changeset
302 The intent of this class is to wrap the ``write()`` function returned by
16499427f6de hgweb: refactor fake file object proxy for archiving
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36874
diff changeset
303 a WSGI ``start_response()`` function. Since ``write()`` is a callable and
16499427f6de hgweb: refactor fake file object proxy for archiving
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36874
diff changeset
304 not a file object, it doesn't implement other file object methods.
16499427f6de hgweb: refactor fake file object proxy for archiving
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36874
diff changeset
305 """
16499427f6de hgweb: refactor fake file object proxy for archiving
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36874
diff changeset
306 def __init__(self, writefn):
16499427f6de hgweb: refactor fake file object proxy for archiving
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36874
diff changeset
307 self._write = writefn
16499427f6de hgweb: refactor fake file object proxy for archiving
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36874
diff changeset
308 self._offset = 0
16499427f6de hgweb: refactor fake file object proxy for archiving
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36874
diff changeset
309
16499427f6de hgweb: refactor fake file object proxy for archiving
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36874
diff changeset
310 def write(self, s):
16499427f6de hgweb: refactor fake file object proxy for archiving
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36874
diff changeset
311 res = self._write(s)
16499427f6de hgweb: refactor fake file object proxy for archiving
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36874
diff changeset
312 # Some Python objects don't report the number of bytes written.
16499427f6de hgweb: refactor fake file object proxy for archiving
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36874
diff changeset
313 if res is None:
16499427f6de hgweb: refactor fake file object proxy for archiving
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36874
diff changeset
314 self._offset += len(s)
16499427f6de hgweb: refactor fake file object proxy for archiving
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36874
diff changeset
315 else:
16499427f6de hgweb: refactor fake file object proxy for archiving
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36874
diff changeset
316 self._offset += res
16499427f6de hgweb: refactor fake file object proxy for archiving
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36874
diff changeset
317
16499427f6de hgweb: refactor fake file object proxy for archiving
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36874
diff changeset
318 def flush(self):
16499427f6de hgweb: refactor fake file object proxy for archiving
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36874
diff changeset
319 pass
16499427f6de hgweb: refactor fake file object proxy for archiving
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36874
diff changeset
320
16499427f6de hgweb: refactor fake file object proxy for archiving
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36874
diff changeset
321 def tell(self):
16499427f6de hgweb: refactor fake file object proxy for archiving
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36874
diff changeset
322 return self._offset
16499427f6de hgweb: refactor fake file object proxy for archiving
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36874
diff changeset
323
36867
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36865
diff changeset
324 class wsgiresponse(object):
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36865
diff changeset
325 """Represents a response to a WSGI request.
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36865
diff changeset
326
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36865
diff changeset
327 A response consists of a status line, headers, and a body.
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36865
diff changeset
328
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36865
diff changeset
329 Consumers must populate the ``status`` and ``headers`` fields and
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36865
diff changeset
330 make a call to a ``setbody*()`` method before the response can be
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36865
diff changeset
331 issued.
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36865
diff changeset
332
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36865
diff changeset
333 When it is time to start sending the response over the wire,
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36865
diff changeset
334 ``sendresponse()`` is called. It handles emitting the header portion
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36865
diff changeset
335 of the response message. It then yields chunks of body data to be
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36865
diff changeset
336 written to the peer. Typically, the WSGI application itself calls
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36865
diff changeset
337 and returns the value from ``sendresponse()``.
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36865
diff changeset
338 """
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36865
diff changeset
339
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36865
diff changeset
340 def __init__(self, req, startresponse):
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36865
diff changeset
341 """Create an empty response tied to a specific request.
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36865
diff changeset
342
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36865
diff changeset
343 ``req`` is a ``parsedrequest``. ``startresponse`` is the
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36865
diff changeset
344 ``start_response`` function passed to the WSGI application.
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36865
diff changeset
345 """
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36865
diff changeset
346 self._req = req
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36865
diff changeset
347 self._startresponse = startresponse
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36865
diff changeset
348
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36865
diff changeset
349 self.status = None
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36865
diff changeset
350 self.headers = wsgiheaders.Headers([])
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36865
diff changeset
351
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36865
diff changeset
352 self._bodybytes = None
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36865
diff changeset
353 self._bodygen = None
36882
97f44b0720e2 hgweb: port archive command to modern response API
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36881
diff changeset
354 self._bodywillwrite = False
36867
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36865
diff changeset
355 self._started = False
36882
97f44b0720e2 hgweb: port archive command to modern response API
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36881
diff changeset
356 self._bodywritefn = None
97f44b0720e2 hgweb: port archive command to modern response API
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36881
diff changeset
357
97f44b0720e2 hgweb: port archive command to modern response API
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36881
diff changeset
358 def _verifybody(self):
97f44b0720e2 hgweb: port archive command to modern response API
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36881
diff changeset
359 if (self._bodybytes is not None or self._bodygen is not None
97f44b0720e2 hgweb: port archive command to modern response API
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36881
diff changeset
360 or self._bodywillwrite):
97f44b0720e2 hgweb: port archive command to modern response API
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36881
diff changeset
361 raise error.ProgrammingError('cannot define body multiple times')
36867
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36865
diff changeset
362
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36865
diff changeset
363 def setbodybytes(self, b):
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36865
diff changeset
364 """Define the response body as static bytes."""
36882
97f44b0720e2 hgweb: port archive command to modern response API
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36881
diff changeset
365 self._verifybody()
36867
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36865
diff changeset
366 self._bodybytes = b
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36865
diff changeset
367 self.headers['Content-Length'] = '%d' % len(b)
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36865
diff changeset
368
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36865
diff changeset
369 def setbodygen(self, gen):
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36865
diff changeset
370 """Define the response body as a generator of bytes."""
36882
97f44b0720e2 hgweb: port archive command to modern response API
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36881
diff changeset
371 self._verifybody()
97f44b0720e2 hgweb: port archive command to modern response API
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36881
diff changeset
372 self._bodygen = gen
97f44b0720e2 hgweb: port archive command to modern response API
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36881
diff changeset
373
97f44b0720e2 hgweb: port archive command to modern response API
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36881
diff changeset
374 def setbodywillwrite(self):
97f44b0720e2 hgweb: port archive command to modern response API
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36881
diff changeset
375 """Signal an intent to use write() to emit the response body.
97f44b0720e2 hgweb: port archive command to modern response API
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36881
diff changeset
376
97f44b0720e2 hgweb: port archive command to modern response API
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36881
diff changeset
377 **This is the least preferred way to send a body.**
36867
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36865
diff changeset
378
36882
97f44b0720e2 hgweb: port archive command to modern response API
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36881
diff changeset
379 It is preferred for WSGI applications to emit a generator of chunks
97f44b0720e2 hgweb: port archive command to modern response API
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36881
diff changeset
380 constituting the response body. However, some consumers can't emit
97f44b0720e2 hgweb: port archive command to modern response API
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36881
diff changeset
381 data this way. So, WSGI provides a way to obtain a ``write(data)``
97f44b0720e2 hgweb: port archive command to modern response API
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36881
diff changeset
382 function that can be used to synchronously perform an unbuffered
97f44b0720e2 hgweb: port archive command to modern response API
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36881
diff changeset
383 write.
97f44b0720e2 hgweb: port archive command to modern response API
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36881
diff changeset
384
97f44b0720e2 hgweb: port archive command to modern response API
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36881
diff changeset
385 Calling this function signals an intent to produce the body in this
97f44b0720e2 hgweb: port archive command to modern response API
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36881
diff changeset
386 manner.
97f44b0720e2 hgweb: port archive command to modern response API
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36881
diff changeset
387 """
97f44b0720e2 hgweb: port archive command to modern response API
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36881
diff changeset
388 self._verifybody()
97f44b0720e2 hgweb: port archive command to modern response API
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36881
diff changeset
389 self._bodywillwrite = True
36867
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36865
diff changeset
390
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36865
diff changeset
391 def sendresponse(self):
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36865
diff changeset
392 """Send the generated response to the client.
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36865
diff changeset
393
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36865
diff changeset
394 Before this is called, ``status`` must be set and one of
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36865
diff changeset
395 ``setbodybytes()`` or ``setbodygen()`` must be called.
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36865
diff changeset
396
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36865
diff changeset
397 Calling this method multiple times is not allowed.
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36865
diff changeset
398 """
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36865
diff changeset
399 if self._started:
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36865
diff changeset
400 raise error.ProgrammingError('sendresponse() called multiple times')
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36865
diff changeset
401
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36865
diff changeset
402 self._started = True
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36865
diff changeset
403
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36865
diff changeset
404 if not self.status:
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36865
diff changeset
405 raise error.ProgrammingError('status line not defined')
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36865
diff changeset
406
36882
97f44b0720e2 hgweb: port archive command to modern response API
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36881
diff changeset
407 if (self._bodybytes is None and self._bodygen is None
97f44b0720e2 hgweb: port archive command to modern response API
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36881
diff changeset
408 and not self._bodywillwrite):
36867
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36865
diff changeset
409 raise error.ProgrammingError('response body not defined')
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36865
diff changeset
410
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36865
diff changeset
411 # Various HTTP clients (notably httplib) won't read the HTTP response
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36865
diff changeset
412 # until the HTTP request has been sent in full. If servers (us) send a
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36865
diff changeset
413 # response before the HTTP request has been fully sent, the connection
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36865
diff changeset
414 # may deadlock because neither end is reading.
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36865
diff changeset
415 #
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36865
diff changeset
416 # We work around this by "draining" the request data before
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36865
diff changeset
417 # sending any response in some conditions.
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36865
diff changeset
418 drain = False
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36865
diff changeset
419 close = False
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36865
diff changeset
420
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36865
diff changeset
421 # If the client sent Expect: 100-continue, we assume it is smart enough
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36865
diff changeset
422 # to deal with the server sending a response before reading the request.
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36865
diff changeset
423 # (httplib doesn't do this.)
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36865
diff changeset
424 if self._req.headers.get('Expect', '').lower() == '100-continue':
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36865
diff changeset
425 pass
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36865
diff changeset
426 # Only tend to request methods that have bodies. Strictly speaking,
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36865
diff changeset
427 # we should sniff for a body. But this is fine for our existing
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36865
diff changeset
428 # WSGI applications.
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36865
diff changeset
429 elif self._req.method not in ('POST', 'PUT'):
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36865
diff changeset
430 pass
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36865
diff changeset
431 else:
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36865
diff changeset
432 # If we don't know how much data to read, there's no guarantee
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36865
diff changeset
433 # that we can drain the request responsibly. The WSGI
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36865
diff changeset
434 # specification only says that servers *should* ensure the
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36865
diff changeset
435 # input stream doesn't overrun the actual request. So there's
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36865
diff changeset
436 # no guarantee that reading until EOF won't corrupt the stream
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36865
diff changeset
437 # state.
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36865
diff changeset
438 if not isinstance(self._req.bodyfh, util.cappedreader):
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36865
diff changeset
439 close = True
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36865
diff changeset
440 else:
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36865
diff changeset
441 # We /could/ only drain certain HTTP response codes. But 200 and
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36865
diff changeset
442 # non-200 wire protocol responses both require draining. Since
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36865
diff changeset
443 # we have a capped reader in place for all situations where we
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36865
diff changeset
444 # drain, it is safe to read from that stream. We'll either do
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36865
diff changeset
445 # a drain or no-op if we're already at EOF.
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36865
diff changeset
446 drain = True
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36865
diff changeset
447
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36865
diff changeset
448 if close:
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36865
diff changeset
449 self.headers['Connection'] = 'Close'
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36865
diff changeset
450
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36865
diff changeset
451 if drain:
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36865
diff changeset
452 assert isinstance(self._req.bodyfh, util.cappedreader)
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36865
diff changeset
453 while True:
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36865
diff changeset
454 chunk = self._req.bodyfh.read(32768)
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36865
diff changeset
455 if not chunk:
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36865
diff changeset
456 break
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36865
diff changeset
457
36882
97f44b0720e2 hgweb: port archive command to modern response API
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36881
diff changeset
458 write = self._startresponse(pycompat.sysstr(self.status),
97f44b0720e2 hgweb: port archive command to modern response API
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36881
diff changeset
459 self.headers.items())
97f44b0720e2 hgweb: port archive command to modern response API
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36881
diff changeset
460
36867
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36865
diff changeset
461 if self._bodybytes:
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36865
diff changeset
462 yield self._bodybytes
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36865
diff changeset
463 elif self._bodygen:
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36865
diff changeset
464 for chunk in self._bodygen:
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36865
diff changeset
465 yield chunk
36882
97f44b0720e2 hgweb: port archive command to modern response API
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36881
diff changeset
466 elif self._bodywillwrite:
97f44b0720e2 hgweb: port archive command to modern response API
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36881
diff changeset
467 self._bodywritefn = write
36867
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36865
diff changeset
468 else:
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36865
diff changeset
469 error.ProgrammingError('do not know how to send body')
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36865
diff changeset
470
36882
97f44b0720e2 hgweb: port archive command to modern response API
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36881
diff changeset
471 def getbodyfile(self):
97f44b0720e2 hgweb: port archive command to modern response API
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36881
diff changeset
472 """Obtain a file object like object representing the response body.
97f44b0720e2 hgweb: port archive command to modern response API
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36881
diff changeset
473
97f44b0720e2 hgweb: port archive command to modern response API
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36881
diff changeset
474 For this to work, you must call ``setbodywillwrite()`` and then
97f44b0720e2 hgweb: port archive command to modern response API
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36881
diff changeset
475 ``sendresponse()`` first. ``sendresponse()`` is a generator and the
97f44b0720e2 hgweb: port archive command to modern response API
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36881
diff changeset
476 function won't run to completion unless the generator is advanced. The
97f44b0720e2 hgweb: port archive command to modern response API
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36881
diff changeset
477 generator yields not items. The easiest way to consume it is with
97f44b0720e2 hgweb: port archive command to modern response API
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36881
diff changeset
478 ``list(res.sendresponse())``, which should resolve to an empty list -
97f44b0720e2 hgweb: port archive command to modern response API
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36881
diff changeset
479 ``[]``.
97f44b0720e2 hgweb: port archive command to modern response API
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36881
diff changeset
480 """
97f44b0720e2 hgweb: port archive command to modern response API
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36881
diff changeset
481 if not self._bodywillwrite:
97f44b0720e2 hgweb: port archive command to modern response API
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36881
diff changeset
482 raise error.ProgrammingError('must call setbodywillwrite() first')
97f44b0720e2 hgweb: port archive command to modern response API
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36881
diff changeset
483
97f44b0720e2 hgweb: port archive command to modern response API
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36881
diff changeset
484 if not self._started:
97f44b0720e2 hgweb: port archive command to modern response API
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36881
diff changeset
485 raise error.ProgrammingError('must call sendresponse() first; did '
97f44b0720e2 hgweb: port archive command to modern response API
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36881
diff changeset
486 'you remember to consume it since it '
97f44b0720e2 hgweb: port archive command to modern response API
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36881
diff changeset
487 'is a generator?')
97f44b0720e2 hgweb: port archive command to modern response API
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36881
diff changeset
488
97f44b0720e2 hgweb: port archive command to modern response API
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36881
diff changeset
489 assert self._bodywritefn
97f44b0720e2 hgweb: port archive command to modern response API
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36881
diff changeset
490 return offsettrackingwriter(self._bodywritefn)
97f44b0720e2 hgweb: port archive command to modern response API
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36881
diff changeset
491
5566
d74fc8dec2b4 Less indirection in the WSGI web interface. This simplifies some code, and makes it more compliant with WSGI.
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 5563
diff changeset
492 class wsgirequest(object):
26132
9df8c729e2e7 hgweb: add some documentation
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25660
diff changeset
493 """Higher-level API for a WSGI request.
9df8c729e2e7 hgweb: add some documentation
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25660
diff changeset
494
9df8c729e2e7 hgweb: add some documentation
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25660
diff changeset
495 WSGI applications are invoked with 2 arguments. They are used to
9df8c729e2e7 hgweb: add some documentation
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25660
diff changeset
496 instantiate instances of this class, which provides higher-level APIs
9df8c729e2e7 hgweb: add some documentation
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25660
diff changeset
497 for obtaining request parameters, writing HTTP output, etc.
9df8c729e2e7 hgweb: add some documentation
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25660
diff changeset
498 """
5566
d74fc8dec2b4 Less indirection in the WSGI web interface. This simplifies some code, and makes it more compliant with WSGI.
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 5563
diff changeset
499 def __init__(self, wsgienv, start_response):
34512
482d6f6dba91 hgweb: when constructing or adding to a wsgi environ dict, use native strs
Augie Fackler <augie@google.com>
parents: 27046
diff changeset
500 version = wsgienv[r'wsgi.version']
3673
eb0b4a2d70a9 white space and line break cleanups
Thomas Arendsen Hein <thomas@intevation.de>
parents: 2859
diff changeset
501 if (version < (1, 0)) or (version >= (2, 0)):
4633
ff7253a0d1da Cleanup of whitespace, indentation and line continuation.
Thomas Arendsen Hein <thomas@intevation.de>
parents: 4250
diff changeset
502 raise RuntimeError("Unknown and unsupported WSGI version %d.%d"
2506
d0db3462d568 This patch make several WSGI related alterations.
Eric Hopper <hopper@omnifarious.org>
parents: 2466
diff changeset
503 % version)
36863
da4e2f87167d hgweb: expose input stream on parsed WSGI request object
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36862
diff changeset
504
da4e2f87167d hgweb: expose input stream on parsed WSGI request object
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36862
diff changeset
505 inp = wsgienv[r'wsgi.input']
36860
290fc4c3d1e0 hgweb: use a capped reader for WSGI input stream
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36858
diff changeset
506
290fc4c3d1e0 hgweb: use a capped reader for WSGI input stream
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36858
diff changeset
507 if r'HTTP_CONTENT_LENGTH' in wsgienv:
36863
da4e2f87167d hgweb: expose input stream on parsed WSGI request object
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36862
diff changeset
508 inp = util.cappedreader(inp, int(wsgienv[r'HTTP_CONTENT_LENGTH']))
36860
290fc4c3d1e0 hgweb: use a capped reader for WSGI input stream
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36858
diff changeset
509 elif r'CONTENT_LENGTH' in wsgienv:
36863
da4e2f87167d hgweb: expose input stream on parsed WSGI request object
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36862
diff changeset
510 inp = util.cappedreader(inp, int(wsgienv[r'CONTENT_LENGTH']))
36860
290fc4c3d1e0 hgweb: use a capped reader for WSGI input stream
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36858
diff changeset
511
34512
482d6f6dba91 hgweb: when constructing or adding to a wsgi environ dict, use native strs
Augie Fackler <augie@google.com>
parents: 27046
diff changeset
512 self.err = wsgienv[r'wsgi.errors']
482d6f6dba91 hgweb: when constructing or adding to a wsgi environ dict, use native strs
Augie Fackler <augie@google.com>
parents: 27046
diff changeset
513 self.threaded = wsgienv[r'wsgi.multithread']
482d6f6dba91 hgweb: when constructing or adding to a wsgi environ dict, use native strs
Augie Fackler <augie@google.com>
parents: 27046
diff changeset
514 self.multiprocess = wsgienv[r'wsgi.multiprocess']
482d6f6dba91 hgweb: when constructing or adding to a wsgi environ dict, use native strs
Augie Fackler <augie@google.com>
parents: 27046
diff changeset
515 self.run_once = wsgienv[r'wsgi.run_once']
2506
d0db3462d568 This patch make several WSGI related alterations.
Eric Hopper <hopper@omnifarious.org>
parents: 2466
diff changeset
516 self.env = wsgienv
36864
01f6bba64424 hgweb: remove support for POST form data (BC)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36863
diff changeset
517 self.req = parserequestfromenv(wsgienv, inp)
36867
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36865
diff changeset
518 self.res = wsgiresponse(self.req, start_response)
5888
956afc025c0f hgweb: separate out start_response() calling
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 5887
diff changeset
519 self._start_response = start_response
5993
948a41e77902 hgweb: explicit response status
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 5930
diff changeset
520 self.server_write = None
2506
d0db3462d568 This patch make several WSGI related alterations.
Eric Hopper <hopper@omnifarious.org>
parents: 2466
diff changeset
521 self.headers = []
d0db3462d568 This patch make several WSGI related alterations.
Eric Hopper <hopper@omnifarious.org>
parents: 2466
diff changeset
522
18352
e33b9b92a200 hgweb: pass the actual response body to request.response, not just the length
Mads Kiilerich <mads@kiilerich.com>
parents: 18351
diff changeset
523 def respond(self, status, type, filename=None, body=None):
34514
528b21b853aa request: coerce content-type to native str
Augie Fackler <augie@google.com>
parents: 34513
diff changeset
524 if not isinstance(type, str):
528b21b853aa request: coerce content-type to native str
Augie Fackler <augie@google.com>
parents: 34513
diff changeset
525 type = pycompat.sysstr(type)
5888
956afc025c0f hgweb: separate out start_response() calling
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 5887
diff changeset
526 if self._start_response is not None:
34722
95be8928d6b2 hgweb: fill in content-type and content-length as native strings
Augie Fackler <augie@google.com>
parents: 34514
diff changeset
527 self.headers.append((r'Content-Type', type))
18348
764a758780b6 hgweb: simplify wsgirequest header handling
Mads Kiilerich <mads@kiilerich.com>
parents: 18347
diff changeset
528 if filename:
26846
7c1b4840c2cd hgweb: replace some str.split() calls by str.partition() or str.rpartition()
Anton Shestakov <av6@dwimlabs.net>
parents: 26200
diff changeset
529 filename = (filename.rpartition('/')[-1]
18348
764a758780b6 hgweb: simplify wsgirequest header handling
Mads Kiilerich <mads@kiilerich.com>
parents: 18347
diff changeset
530 .replace('\\', '\\\\').replace('"', '\\"'))
764a758780b6 hgweb: simplify wsgirequest header handling
Mads Kiilerich <mads@kiilerich.com>
parents: 18347
diff changeset
531 self.headers.append(('Content-Disposition',
764a758780b6 hgweb: simplify wsgirequest header handling
Mads Kiilerich <mads@kiilerich.com>
parents: 18347
diff changeset
532 'inline; filename="%s"' % filename))
18352
e33b9b92a200 hgweb: pass the actual response body to request.response, not just the length
Mads Kiilerich <mads@kiilerich.com>
parents: 18351
diff changeset
533 if body is not None:
34722
95be8928d6b2 hgweb: fill in content-type and content-length as native strings
Augie Fackler <augie@google.com>
parents: 34514
diff changeset
534 self.headers.append((r'Content-Length', str(len(body))))
5888
956afc025c0f hgweb: separate out start_response() calling
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 5887
diff changeset
535
5926
15ef6b9c1f2f hgweb: be sure to send a valid content-type for raw files
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 5922
diff changeset
536 for k, v in self.headers:
15ef6b9c1f2f hgweb: be sure to send a valid content-type for raw files
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 5922
diff changeset
537 if not isinstance(v, str):
18348
764a758780b6 hgweb: simplify wsgirequest header handling
Mads Kiilerich <mads@kiilerich.com>
parents: 18347
diff changeset
538 raise TypeError('header value must be string: %r' % (v,))
5926
15ef6b9c1f2f hgweb: be sure to send a valid content-type for raw files
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 5922
diff changeset
539
5888
956afc025c0f hgweb: separate out start_response() calling
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 5887
diff changeset
540 if isinstance(status, ErrorResponse):
18348
764a758780b6 hgweb: simplify wsgirequest header handling
Mads Kiilerich <mads@kiilerich.com>
parents: 18347
diff changeset
541 self.headers.extend(status.headers)
12739
8dcd3203a261 hgweb: don't send a body or illegal headers during 304 response
Augie Fackler <durin42@gmail.com>
parents: 10263
diff changeset
542 if status.code == HTTP_NOT_MODIFIED:
8dcd3203a261 hgweb: don't send a body or illegal headers during 304 response
Augie Fackler <durin42@gmail.com>
parents: 10263
diff changeset
543 # RFC 2616 Section 10.3.5: 304 Not Modified has cases where
8dcd3203a261 hgweb: don't send a body or illegal headers during 304 response
Augie Fackler <durin42@gmail.com>
parents: 10263
diff changeset
544 # it MUST NOT include any headers other than these and no
8dcd3203a261 hgweb: don't send a body or illegal headers during 304 response
Augie Fackler <durin42@gmail.com>
parents: 10263
diff changeset
545 # body
8dcd3203a261 hgweb: don't send a body or illegal headers during 304 response
Augie Fackler <durin42@gmail.com>
parents: 10263
diff changeset
546 self.headers = [(k, v) for (k, v) in self.headers if
8dcd3203a261 hgweb: don't send a body or illegal headers during 304 response
Augie Fackler <durin42@gmail.com>
parents: 10263
diff changeset
547 k in ('Date', 'ETag', 'Expires',
8dcd3203a261 hgweb: don't send a body or illegal headers during 304 response
Augie Fackler <durin42@gmail.com>
parents: 10263
diff changeset
548 'Cache-Control', 'Vary')]
36288
a0a004b29a51 hgweb: correctly bytes-ify status, not string-ify
Augie Fackler <augie@google.com>
parents: 34722
diff changeset
549 status = statusmessage(status.code, pycompat.bytestr(status))
5993
948a41e77902 hgweb: explicit response status
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 5930
diff changeset
550 elif status == 200:
948a41e77902 hgweb: explicit response status
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 5930
diff changeset
551 status = '200 Script output follows'
5888
956afc025c0f hgweb: separate out start_response() calling
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 5887
diff changeset
552 elif isinstance(status, int):
956afc025c0f hgweb: separate out start_response() calling
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 5887
diff changeset
553 status = statusmessage(status)
956afc025c0f hgweb: separate out start_response() calling
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 5887
diff changeset
554
36861
2cdf47e14c30 hgweb: refactor the request draining code
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36860
diff changeset
555 # Various HTTP clients (notably httplib) won't read the HTTP
2cdf47e14c30 hgweb: refactor the request draining code
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36860
diff changeset
556 # response until the HTTP request has been sent in full. If servers
2cdf47e14c30 hgweb: refactor the request draining code
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36860
diff changeset
557 # (us) send a response before the HTTP request has been fully sent,
2cdf47e14c30 hgweb: refactor the request draining code
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36860
diff changeset
558 # the connection may deadlock because neither end is reading.
2cdf47e14c30 hgweb: refactor the request draining code
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36860
diff changeset
559 #
2cdf47e14c30 hgweb: refactor the request draining code
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36860
diff changeset
560 # We work around this by "draining" the request data before
2cdf47e14c30 hgweb: refactor the request draining code
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36860
diff changeset
561 # sending any response in some conditions.
2cdf47e14c30 hgweb: refactor the request draining code
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36860
diff changeset
562 drain = False
2cdf47e14c30 hgweb: refactor the request draining code
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36860
diff changeset
563 close = False
2cdf47e14c30 hgweb: refactor the request draining code
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36860
diff changeset
564
2cdf47e14c30 hgweb: refactor the request draining code
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36860
diff changeset
565 # If the client sent Expect: 100-continue, we assume it is smart
2cdf47e14c30 hgweb: refactor the request draining code
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36860
diff changeset
566 # enough to deal with the server sending a response before reading
2cdf47e14c30 hgweb: refactor the request draining code
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36860
diff changeset
567 # the request. (httplib doesn't do this.)
2cdf47e14c30 hgweb: refactor the request draining code
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36860
diff changeset
568 if self.env.get(r'HTTP_EXPECT', r'').lower() == r'100-continue':
2cdf47e14c30 hgweb: refactor the request draining code
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36860
diff changeset
569 pass
2cdf47e14c30 hgweb: refactor the request draining code
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36860
diff changeset
570 # Only tend to request methods that have bodies. Strictly speaking,
2cdf47e14c30 hgweb: refactor the request draining code
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36860
diff changeset
571 # we should sniff for a body. But this is fine for our existing
2cdf47e14c30 hgweb: refactor the request draining code
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36860
diff changeset
572 # WSGI applications.
2cdf47e14c30 hgweb: refactor the request draining code
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36860
diff changeset
573 elif self.env[r'REQUEST_METHOD'] not in (r'POST', r'PUT'):
2cdf47e14c30 hgweb: refactor the request draining code
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36860
diff changeset
574 pass
2cdf47e14c30 hgweb: refactor the request draining code
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36860
diff changeset
575 else:
2cdf47e14c30 hgweb: refactor the request draining code
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36860
diff changeset
576 # If we don't know how much data to read, there's no guarantee
2cdf47e14c30 hgweb: refactor the request draining code
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36860
diff changeset
577 # that we can drain the request responsibly. The WSGI
2cdf47e14c30 hgweb: refactor the request draining code
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36860
diff changeset
578 # specification only says that servers *should* ensure the
2cdf47e14c30 hgweb: refactor the request draining code
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36860
diff changeset
579 # input stream doesn't overrun the actual request. So there's
2cdf47e14c30 hgweb: refactor the request draining code
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36860
diff changeset
580 # no guarantee that reading until EOF won't corrupt the stream
2cdf47e14c30 hgweb: refactor the request draining code
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36860
diff changeset
581 # state.
36863
da4e2f87167d hgweb: expose input stream on parsed WSGI request object
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36862
diff changeset
582 if not isinstance(self.req.bodyfh, util.cappedreader):
36861
2cdf47e14c30 hgweb: refactor the request draining code
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36860
diff changeset
583 close = True
2cdf47e14c30 hgweb: refactor the request draining code
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36860
diff changeset
584 else:
2cdf47e14c30 hgweb: refactor the request draining code
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36860
diff changeset
585 # We /could/ only drain certain HTTP response codes. But 200
2cdf47e14c30 hgweb: refactor the request draining code
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36860
diff changeset
586 # and non-200 wire protocol responses both require draining.
2cdf47e14c30 hgweb: refactor the request draining code
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36860
diff changeset
587 # Since we have a capped reader in place for all situations
2cdf47e14c30 hgweb: refactor the request draining code
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36860
diff changeset
588 # where we drain, it is safe to read from that stream. We'll
2cdf47e14c30 hgweb: refactor the request draining code
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36860
diff changeset
589 # either do a drain or no-op if we're already at EOF.
2cdf47e14c30 hgweb: refactor the request draining code
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36860
diff changeset
590 drain = True
2cdf47e14c30 hgweb: refactor the request draining code
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36860
diff changeset
591
2cdf47e14c30 hgweb: refactor the request draining code
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36860
diff changeset
592 if close:
2cdf47e14c30 hgweb: refactor the request draining code
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36860
diff changeset
593 self.headers.append((r'Connection', r'Close'))
2cdf47e14c30 hgweb: refactor the request draining code
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36860
diff changeset
594
2cdf47e14c30 hgweb: refactor the request draining code
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36860
diff changeset
595 if drain:
36863
da4e2f87167d hgweb: expose input stream on parsed WSGI request object
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36862
diff changeset
596 assert isinstance(self.req.bodyfh, util.cappedreader)
36861
2cdf47e14c30 hgweb: refactor the request draining code
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36860
diff changeset
597 while True:
36863
da4e2f87167d hgweb: expose input stream on parsed WSGI request object
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36862
diff changeset
598 chunk = self.req.bodyfh.read(32768)
36861
2cdf47e14c30 hgweb: refactor the request draining code
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36860
diff changeset
599 if not chunk:
2cdf47e14c30 hgweb: refactor the request draining code
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36860
diff changeset
600 break
2cdf47e14c30 hgweb: refactor the request draining code
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36860
diff changeset
601
36291
af0a19d8812b py3: get bytes-repr of network errors portably
Augie Fackler <augie@google.com>
parents: 36288
diff changeset
602 self.server_write = self._start_response(
af0a19d8812b py3: get bytes-repr of network errors portably
Augie Fackler <augie@google.com>
parents: 36288
diff changeset
603 pycompat.sysstr(status), self.headers)
5888
956afc025c0f hgweb: separate out start_response() calling
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 5887
diff changeset
604 self._start_response = None
956afc025c0f hgweb: separate out start_response() calling
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 5887
diff changeset
605 self.headers = []
18352
e33b9b92a200 hgweb: pass the actual response body to request.response, not just the length
Mads Kiilerich <mads@kiilerich.com>
parents: 18351
diff changeset
606 if body is not None:
e33b9b92a200 hgweb: pass the actual response body to request.response, not just the length
Mads Kiilerich <mads@kiilerich.com>
parents: 18351
diff changeset
607 self.write(body)
e33b9b92a200 hgweb: pass the actual response body to request.response, not just the length
Mads Kiilerich <mads@kiilerich.com>
parents: 18351
diff changeset
608 self.server_write = None
5888
956afc025c0f hgweb: separate out start_response() calling
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 5887
diff changeset
609
5993
948a41e77902 hgweb: explicit response status
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 5930
diff changeset
610 def write(self, thing):
18351
3fbdbeab38cc hgweb: don't pass empty response chunks on
Mads Kiilerich <mads@kiilerich.com>
parents: 18350
diff changeset
611 if thing:
3fbdbeab38cc hgweb: don't pass empty response chunks on
Mads Kiilerich <mads@kiilerich.com>
parents: 18350
diff changeset
612 try:
3fbdbeab38cc hgweb: don't pass empty response chunks on
Mads Kiilerich <mads@kiilerich.com>
parents: 18350
diff changeset
613 self.server_write(thing)
25660
328739ea70c3 global: mass rewrite to use modern exception syntax
Gregory Szorc <gregory.szorc@gmail.com>
parents: 18352
diff changeset
614 except socket.error as inst:
18351
3fbdbeab38cc hgweb: don't pass empty response chunks on
Mads Kiilerich <mads@kiilerich.com>
parents: 18350
diff changeset
615 if inst[0] != errno.ECONNRESET:
3fbdbeab38cc hgweb: don't pass empty response chunks on
Mads Kiilerich <mads@kiilerich.com>
parents: 18350
diff changeset
616 raise
1159
b6f5a947e62e Change use of global sys.stdout, sys.stdin os.environ to a hgrequest object.
Vincent Wagelaar <vincent@ricardis.tudelft.nl>
parents: 1143
diff changeset
617
4246
cc81c512a531 avoid _wsgioutputfile <-> _wsgirequest circular reference
Alexis S. L. Carvalho <alexis@cecm.usp.br>
parents: 3673
diff changeset
618 def flush(self):
cc81c512a531 avoid _wsgioutputfile <-> _wsgirequest circular reference
Alexis S. L. Carvalho <alexis@cecm.usp.br>
parents: 3673
diff changeset
619 return None
cc81c512a531 avoid _wsgioutputfile <-> _wsgirequest circular reference
Alexis S. L. Carvalho <alexis@cecm.usp.br>
parents: 3673
diff changeset
620
5566
d74fc8dec2b4 Less indirection in the WSGI web interface. This simplifies some code, and makes it more compliant with WSGI.
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 5563
diff changeset
621 def wsgiapplication(app_maker):
5887
41a3fce17625 hgweb: return iterable, add deprecation note
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 5886
diff changeset
622 '''For compatibility with old CGI scripts. A plain hgweb() or hgwebdir()
41a3fce17625 hgweb: return iterable, add deprecation note
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 5886
diff changeset
623 can and should now be used as a WSGI application.'''
5760
0145f9afb0e7 Removed tabs and trailing whitespace in python files
Thomas Arendsen Hein <thomas@intevation.de>
parents: 5566
diff changeset
624 application = app_maker()
0145f9afb0e7 Removed tabs and trailing whitespace in python files
Thomas Arendsen Hein <thomas@intevation.de>
parents: 5566
diff changeset
625 def run_wsgi(env, respond):
5887
41a3fce17625 hgweb: return iterable, add deprecation note
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 5886
diff changeset
626 return application(env, respond)
5760
0145f9afb0e7 Removed tabs and trailing whitespace in python files
Thomas Arendsen Hein <thomas@intevation.de>
parents: 5566
diff changeset
627 return run_wsgi