mercurial/hgweb/request.py
author Martin von Zweigbergk <martinvonz@google.com>
Wed, 02 May 2018 22:56:10 -0700
changeset 37864 890bdf0e33c8
parent 37828 3e3acf5d6a07
child 40545 6107d4549fcc
permissions -rw-r--r--
shortest: extract function for checking if a prefix is a revnum Much of isvalid() was about testing if a prefix is a valid revnum. I want to reuse that soon, so let's move it out. There is no significant slowdown from the function call overhead. Differential Revision: https://phab.mercurial-scm.org/D3460
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
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
36806
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36713
diff changeset
    11
#import wsgiref.validate
27046
37fcfe52c68c hgweb: use absolute_import
Yuya Nishihara <yuya@tcha.org>
parents: 26846
diff changeset
    12
36806
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36713
diff changeset
    13
from ..thirdparty import (
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36713
diff changeset
    14
    attr,
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36713
diff changeset
    15
)
27046
37fcfe52c68c hgweb: use absolute_import
Yuya Nishihara <yuya@tcha.org>
parents: 26846
diff changeset
    16
from .. import (
36861
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36859
diff changeset
    17
    error,
34514
528b21b853aa request: coerce content-type to native str
Augie Fackler <augie@google.com>
parents: 34513
diff changeset
    18
    pycompat,
27046
37fcfe52c68c hgweb: use absolute_import
Yuya Nishihara <yuya@tcha.org>
parents: 26846
diff changeset
    19
    util,
37fcfe52c68c hgweb: use absolute_import
Yuya Nishihara <yuya@tcha.org>
parents: 26846
diff changeset
    20
)
138
c77a679e9cfa Revamped templated hgweb
mpm@selenic.com
parents: 137
diff changeset
    21
36862
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36861
diff changeset
    22
class multidict(object):
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36861
diff changeset
    23
    """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: 36861
diff changeset
    24
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36861
diff changeset
    25
    Used to store parsed request parameters.
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36861
diff changeset
    26
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36861
diff changeset
    27
    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: 36861
diff changeset
    28
    """
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36861
diff changeset
    29
    def __init__(self):
36997
44467a4d472f hgweb: refactor multirequest to be a dict of lists
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36911
diff changeset
    30
        self._items = {}
36862
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36861
diff changeset
    31
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36861
diff changeset
    32
    def __getitem__(self, key):
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36861
diff changeset
    33
        """Returns the last set value for a key."""
36997
44467a4d472f hgweb: refactor multirequest to be a dict of lists
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36911
diff changeset
    34
        return self._items[key][-1]
36862
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36861
diff changeset
    35
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36861
diff changeset
    36
    def __setitem__(self, key, value):
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36861
diff changeset
    37
        """Replace a values for a key with a new value."""
36997
44467a4d472f hgweb: refactor multirequest to be a dict of lists
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36911
diff changeset
    38
        self._items[key] = [value]
36862
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36861
diff changeset
    39
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36861
diff changeset
    40
    def __delitem__(self, key):
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36861
diff changeset
    41
        """Delete all values for a key."""
36997
44467a4d472f hgweb: refactor multirequest to be a dict of lists
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36911
diff changeset
    42
        del self._items[key]
36862
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36861
diff changeset
    43
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36861
diff changeset
    44
    def __contains__(self, key):
36997
44467a4d472f hgweb: refactor multirequest to be a dict of lists
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36911
diff changeset
    45
        return key in self._items
36862
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36861
diff changeset
    46
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36861
diff changeset
    47
    def __len__(self):
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36861
diff changeset
    48
        return len(self._items)
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36861
diff changeset
    49
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36861
diff changeset
    50
    def get(self, key, default=None):
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36861
diff changeset
    51
        try:
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36861
diff changeset
    52
            return self.__getitem__(key)
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36861
diff changeset
    53
        except KeyError:
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36861
diff changeset
    54
            return default
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36861
diff changeset
    55
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36861
diff changeset
    56
    def add(self, key, value):
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36861
diff changeset
    57
        """Add a new value for a key. Does not replace existing values."""
36997
44467a4d472f hgweb: refactor multirequest to be a dict of lists
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36911
diff changeset
    58
        self._items.setdefault(key, []).append(value)
36862
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36861
diff changeset
    59
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36861
diff changeset
    60
    def getall(self, key):
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36861
diff changeset
    61
        """Obtains all values for a key."""
36997
44467a4d472f hgweb: refactor multirequest to be a dict of lists
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36911
diff changeset
    62
        return self._items.get(key, [])
36862
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36861
diff changeset
    63
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36861
diff changeset
    64
    def getone(self, key):
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36861
diff changeset
    65
        """Obtain a single value for a key.
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36861
diff changeset
    66
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36861
diff changeset
    67
        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: 36861
diff changeset
    68
        """
36997
44467a4d472f hgweb: refactor multirequest to be a dict of lists
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36911
diff changeset
    69
        vals = self._items[key]
36862
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36861
diff changeset
    70
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36861
diff changeset
    71
        if len(vals) > 1:
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36861
diff changeset
    72
            raise KeyError('multiple values for %r' % key)
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36861
diff changeset
    73
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36861
diff changeset
    74
        return vals[0]
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36861
diff changeset
    75
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36861
diff changeset
    76
    def asdictoflists(self):
36997
44467a4d472f hgweb: refactor multirequest to be a dict of lists
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36911
diff changeset
    77
        return {k: list(v) for k, v in self._items.iteritems()}
36862
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36861
diff changeset
    78
36806
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36713
diff changeset
    79
@attr.s(frozen=True)
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36713
diff changeset
    80
class parsedrequest(object):
36857
da4e2f87167d hgweb: expose input stream on parsed WSGI request object
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36856
diff changeset
    81
    """Represents a parsed WSGI request.
da4e2f87167d hgweb: expose input stream on parsed WSGI request object
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36856
diff changeset
    82
da4e2f87167d hgweb: expose input stream on parsed WSGI request object
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36856
diff changeset
    83
    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: 36856
diff changeset
    84
    """
36806
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36713
diff changeset
    85
36848
16292bbda39c hgweb: store and use request method on parsed request
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36847
diff changeset
    86
    # Request method.
16292bbda39c hgweb: store and use request method on parsed request
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36847
diff changeset
    87
    method = attr.ib()
36806
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36713
diff changeset
    88
    # Full URL for this request.
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36713
diff changeset
    89
    url = attr.ib()
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36713
diff changeset
    90
    # 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: 36713
diff changeset
    91
    baseurl = attr.ib()
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36713
diff changeset
    92
    # 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: 36713
diff changeset
    93
    # 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: 36713
diff changeset
    94
    advertisedurl = attr.ib()
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36713
diff changeset
    95
    advertisedbaseurl = attr.ib()
36867
a755fd3b7146 hgweb: expose URL scheme and REMOTE_* attributes
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36866
diff changeset
    96
    # URL scheme (part before ``://``). e.g. ``http`` or ``https``.
a755fd3b7146 hgweb: expose URL scheme and REMOTE_* attributes
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36866
diff changeset
    97
    urlscheme = attr.ib()
a755fd3b7146 hgweb: expose URL scheme and REMOTE_* attributes
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36866
diff changeset
    98
    # Value of REMOTE_USER, if set, or None.
a755fd3b7146 hgweb: expose URL scheme and REMOTE_* attributes
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36866
diff changeset
    99
    remoteuser = attr.ib()
a755fd3b7146 hgweb: expose URL scheme and REMOTE_* attributes
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36866
diff changeset
   100
    # Value of REMOTE_HOST, if set, or None.
a755fd3b7146 hgweb: expose URL scheme and REMOTE_* attributes
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36866
diff changeset
   101
    remotehost = attr.ib()
36899
e67a2e05fa8a hgweb: clarify that apppath begins with a forward slash
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36898
diff changeset
   102
    # Relative WSGI application path. If defined, will begin with a
e67a2e05fa8a hgweb: clarify that apppath begins with a forward slash
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36898
diff changeset
   103
    # ``/``.
36806
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36713
diff changeset
   104
    apppath = attr.ib()
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36713
diff changeset
   105
    # 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: 36713
diff changeset
   106
    dispatchparts = attr.ib()
36898
d0b0fedbfb53 hgweb: change how dispatch path is reported
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36897
diff changeset
   107
    # URL path component (no query string) used for dispatch. Can be
d0b0fedbfb53 hgweb: change how dispatch path is reported
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36897
diff changeset
   108
    # ``None`` to signal no path component given to the request, an
d0b0fedbfb53 hgweb: change how dispatch path is reported
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36897
diff changeset
   109
    # empty string to signal a request to the application's root URL,
d0b0fedbfb53 hgweb: change how dispatch path is reported
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36897
diff changeset
   110
    # or a string not beginning with ``/`` containing the requested
d0b0fedbfb53 hgweb: change how dispatch path is reported
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36897
diff changeset
   111
    # path under the application.
36806
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36713
diff changeset
   112
    dispatchpath = attr.ib()
36868
8ddb5c354906 hgweb: expose repo name on parsedrequest
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36867
diff changeset
   113
    # The name of the repository being accessed.
8ddb5c354906 hgweb: expose repo name on parsedrequest
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36867
diff changeset
   114
    reponame = attr.ib()
36806
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36713
diff changeset
   115
    # Raw query string (part after "?" in URL).
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36713
diff changeset
   116
    querystring = attr.ib()
36862
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36861
diff changeset
   117
    # multidict of query string parameters.
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36861
diff changeset
   118
    qsparams = attr.ib()
36814
f9078c6caeb6 hgweb: parse and store HTTP request headers
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36811
diff changeset
   119
    # 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: 36811
diff changeset
   120
    # insensitive keys.
f9078c6caeb6 hgweb: parse and store HTTP request headers
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36811
diff changeset
   121
    headers = attr.ib()
36857
da4e2f87167d hgweb: expose input stream on parsed WSGI request object
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36856
diff changeset
   122
    # Request body input stream.
da4e2f87167d hgweb: expose input stream on parsed WSGI request object
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36856
diff changeset
   123
    bodyfh = attr.ib()
36909
84110a1d0f7d hgweb: store the raw WSGI environment dict
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36908
diff changeset
   124
    # WSGI environment dict, unmodified.
84110a1d0f7d hgweb: store the raw WSGI environment dict
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36908
diff changeset
   125
    rawenv = attr.ib()
36806
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36713
diff changeset
   126
37818
877185de62cf hgweb: reuse body file object when hgwebdir calls hgweb (issue5851)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37616
diff changeset
   127
def parserequestfromenv(env, reponame=None, altbaseurl=None, bodyfh=None):
36806
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36713
diff changeset
   128
    """Parse URL components from environment variables.
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36713
diff changeset
   129
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36713
diff changeset
   130
    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: 36713
diff changeset
   131
    parses the environment variables into a data structure.
36897
d7fd203e36cc hgweb: refactor repository name URL parsing
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36896
diff changeset
   132
d7fd203e36cc hgweb: refactor repository name URL parsing
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36896
diff changeset
   133
    If ``reponame`` is defined, the leading path components matching that
d7fd203e36cc hgweb: refactor repository name URL parsing
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36896
diff changeset
   134
    string are effectively shifted from ``PATH_INFO`` to ``SCRIPT_NAME``.
d7fd203e36cc hgweb: refactor repository name URL parsing
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36896
diff changeset
   135
    This simulates the world view of a WSGI application that processes
d7fd203e36cc hgweb: refactor repository name URL parsing
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36896
diff changeset
   136
    requests from the base URL of a repo.
36900
219b23359f4c hgweb: support constructing URLs from an alternate base URL
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36899
diff changeset
   137
219b23359f4c hgweb: support constructing URLs from an alternate base URL
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36899
diff changeset
   138
    If ``altbaseurl`` (typically comes from ``web.baseurl`` config option)
219b23359f4c hgweb: support constructing URLs from an alternate base URL
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36899
diff changeset
   139
    is defined, it is used - instead of the WSGI environment variables - for
219b23359f4c hgweb: support constructing URLs from an alternate base URL
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36899
diff changeset
   140
    constructing URL components up to and including the WSGI application path.
219b23359f4c hgweb: support constructing URLs from an alternate base URL
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36899
diff changeset
   141
    For example, if the current WSGI application is at ``/repo`` and a request
219b23359f4c hgweb: support constructing URLs from an alternate base URL
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36899
diff changeset
   142
    is made to ``/rev/@`` with this argument set to
219b23359f4c hgweb: support constructing URLs from an alternate base URL
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36899
diff changeset
   143
    ``http://myserver:9000/prefix``, the URL and path components will resolve as
219b23359f4c hgweb: support constructing URLs from an alternate base URL
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36899
diff changeset
   144
    if the request were to ``http://myserver:9000/prefix/rev/@``. In other
219b23359f4c hgweb: support constructing URLs from an alternate base URL
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36899
diff changeset
   145
    words, ``wsgi.url_scheme``, ``SERVER_NAME``, ``SERVER_PORT``, and
219b23359f4c hgweb: support constructing URLs from an alternate base URL
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36899
diff changeset
   146
    ``SCRIPT_NAME`` are all effectively replaced by components from this URL.
37818
877185de62cf hgweb: reuse body file object when hgwebdir calls hgweb (issue5851)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37616
diff changeset
   147
877185de62cf hgweb: reuse body file object when hgwebdir calls hgweb (issue5851)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37616
diff changeset
   148
    ``bodyfh`` can be used to specify a file object to read the request body
877185de62cf hgweb: reuse body file object when hgwebdir calls hgweb (issue5851)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37616
diff changeset
   149
    from. If not defined, ``wsgi.input`` from the environment dict is used.
36806
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36713
diff changeset
   150
    """
36900
219b23359f4c hgweb: support constructing URLs from an alternate base URL
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36899
diff changeset
   151
    # PEP 3333 defines the WSGI spec and is a useful reference for this code.
36806
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36713
diff changeset
   152
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36713
diff changeset
   153
    # 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: 36713
diff changeset
   154
    # 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: 36713
diff changeset
   155
    # TODO enable this once we fix internal violations.
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36713
diff changeset
   156
    #wsgiref.validate.check_environ(env)
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36713
diff changeset
   157
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36713
diff changeset
   158
    # 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: 36713
diff changeset
   159
    # (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: 36713
diff changeset
   160
    # 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: 36713
diff changeset
   161
    # 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: 36713
diff changeset
   162
    if pycompat.ispy3:
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36713
diff changeset
   163
        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: 36713
diff changeset
   164
        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: 36713
diff changeset
   165
               for k, v in env.iteritems()}
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36713
diff changeset
   166
37616
5e81cf9651c1 hgweb: fallback to checking wsgireq.env for REPO_NAME for 3rd party hosting
Matt Harbison <matt_harbison@yahoo.com>
parents: 37606
diff changeset
   167
    # Some hosting solutions are emulating hgwebdir, and dispatching directly
5e81cf9651c1 hgweb: fallback to checking wsgireq.env for REPO_NAME for 3rd party hosting
Matt Harbison <matt_harbison@yahoo.com>
parents: 37606
diff changeset
   168
    # to an hgweb instance using this environment variable.  This was always
5e81cf9651c1 hgweb: fallback to checking wsgireq.env for REPO_NAME for 3rd party hosting
Matt Harbison <matt_harbison@yahoo.com>
parents: 37606
diff changeset
   169
    # checked prior to d7fd203e36cc; keep doing so to avoid breaking them.
5e81cf9651c1 hgweb: fallback to checking wsgireq.env for REPO_NAME for 3rd party hosting
Matt Harbison <matt_harbison@yahoo.com>
parents: 37606
diff changeset
   170
    if not reponame:
5e81cf9651c1 hgweb: fallback to checking wsgireq.env for REPO_NAME for 3rd party hosting
Matt Harbison <matt_harbison@yahoo.com>
parents: 37606
diff changeset
   171
        reponame = env.get('REPO_NAME')
5e81cf9651c1 hgweb: fallback to checking wsgireq.env for REPO_NAME for 3rd party hosting
Matt Harbison <matt_harbison@yahoo.com>
parents: 37606
diff changeset
   172
36900
219b23359f4c hgweb: support constructing URLs from an alternate base URL
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36899
diff changeset
   173
    if altbaseurl:
219b23359f4c hgweb: support constructing URLs from an alternate base URL
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36899
diff changeset
   174
        altbaseurl = util.url(altbaseurl)
219b23359f4c hgweb: support constructing URLs from an alternate base URL
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36899
diff changeset
   175
36806
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36713
diff changeset
   176
    # 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: 36713
diff changeset
   177
    # the environment variables.
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36713
diff changeset
   178
    # 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: 36713
diff changeset
   179
    # how URLs are reconstructed.
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36713
diff changeset
   180
    fullurl = env['wsgi.url_scheme'] + '://'
36900
219b23359f4c hgweb: support constructing URLs from an alternate base URL
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36899
diff changeset
   181
219b23359f4c hgweb: support constructing URLs from an alternate base URL
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36899
diff changeset
   182
    if altbaseurl and altbaseurl.scheme:
219b23359f4c hgweb: support constructing URLs from an alternate base URL
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36899
diff changeset
   183
        advertisedfullurl = altbaseurl.scheme + '://'
219b23359f4c hgweb: support constructing URLs from an alternate base URL
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36899
diff changeset
   184
    else:
219b23359f4c hgweb: support constructing URLs from an alternate base URL
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36899
diff changeset
   185
        advertisedfullurl = fullurl
36806
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36713
diff changeset
   186
36900
219b23359f4c hgweb: support constructing URLs from an alternate base URL
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36899
diff changeset
   187
    def addport(s, port):
219b23359f4c hgweb: support constructing URLs from an alternate base URL
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36899
diff changeset
   188
        if s.startswith('https://'):
219b23359f4c hgweb: support constructing URLs from an alternate base URL
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36899
diff changeset
   189
            if port != '443':
219b23359f4c hgweb: support constructing URLs from an alternate base URL
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36899
diff changeset
   190
                s += ':' + port
36806
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36713
diff changeset
   191
        else:
36900
219b23359f4c hgweb: support constructing URLs from an alternate base URL
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36899
diff changeset
   192
            if port != '80':
219b23359f4c hgweb: support constructing URLs from an alternate base URL
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36899
diff changeset
   193
                s += ':' + port
36806
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36713
diff changeset
   194
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36713
diff changeset
   195
        return s
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36713
diff changeset
   196
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36713
diff changeset
   197
    if env.get('HTTP_HOST'):
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36713
diff changeset
   198
        fullurl += env['HTTP_HOST']
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36713
diff changeset
   199
    else:
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36713
diff changeset
   200
        fullurl += env['SERVER_NAME']
36900
219b23359f4c hgweb: support constructing URLs from an alternate base URL
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36899
diff changeset
   201
        fullurl = addport(fullurl, env['SERVER_PORT'])
219b23359f4c hgweb: support constructing URLs from an alternate base URL
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36899
diff changeset
   202
219b23359f4c hgweb: support constructing URLs from an alternate base URL
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36899
diff changeset
   203
    if altbaseurl and altbaseurl.host:
219b23359f4c hgweb: support constructing URLs from an alternate base URL
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36899
diff changeset
   204
        advertisedfullurl += altbaseurl.host
36806
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36713
diff changeset
   205
36900
219b23359f4c hgweb: support constructing URLs from an alternate base URL
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36899
diff changeset
   206
        if altbaseurl.port:
219b23359f4c hgweb: support constructing URLs from an alternate base URL
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36899
diff changeset
   207
            port = altbaseurl.port
219b23359f4c hgweb: support constructing URLs from an alternate base URL
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36899
diff changeset
   208
        elif altbaseurl.scheme == 'http' and not altbaseurl.port:
219b23359f4c hgweb: support constructing URLs from an alternate base URL
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36899
diff changeset
   209
            port = '80'
219b23359f4c hgweb: support constructing URLs from an alternate base URL
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36899
diff changeset
   210
        elif altbaseurl.scheme == 'https' and not altbaseurl.port:
219b23359f4c hgweb: support constructing URLs from an alternate base URL
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36899
diff changeset
   211
            port = '443'
219b23359f4c hgweb: support constructing URLs from an alternate base URL
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36899
diff changeset
   212
        else:
219b23359f4c hgweb: support constructing URLs from an alternate base URL
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36899
diff changeset
   213
            port = env['SERVER_PORT']
219b23359f4c hgweb: support constructing URLs from an alternate base URL
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36899
diff changeset
   214
219b23359f4c hgweb: support constructing URLs from an alternate base URL
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36899
diff changeset
   215
        advertisedfullurl = addport(advertisedfullurl, port)
219b23359f4c hgweb: support constructing URLs from an alternate base URL
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36899
diff changeset
   216
    else:
219b23359f4c hgweb: support constructing URLs from an alternate base URL
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36899
diff changeset
   217
        advertisedfullurl += env['SERVER_NAME']
219b23359f4c hgweb: support constructing URLs from an alternate base URL
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36899
diff changeset
   218
        advertisedfullurl = addport(advertisedfullurl, env['SERVER_PORT'])
36806
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36713
diff changeset
   219
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36713
diff changeset
   220
    baseurl = fullurl
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36713
diff changeset
   221
    advertisedbaseurl = advertisedfullurl
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36713
diff changeset
   222
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36713
diff changeset
   223
    fullurl += util.urlreq.quote(env.get('SCRIPT_NAME', ''))
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36713
diff changeset
   224
    fullurl += util.urlreq.quote(env.get('PATH_INFO', ''))
36900
219b23359f4c hgweb: support constructing URLs from an alternate base URL
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36899
diff changeset
   225
219b23359f4c hgweb: support constructing URLs from an alternate base URL
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36899
diff changeset
   226
    if altbaseurl:
219b23359f4c hgweb: support constructing URLs from an alternate base URL
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36899
diff changeset
   227
        path = altbaseurl.path or ''
219b23359f4c hgweb: support constructing URLs from an alternate base URL
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36899
diff changeset
   228
        if path and not path.startswith('/'):
219b23359f4c hgweb: support constructing URLs from an alternate base URL
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36899
diff changeset
   229
            path = '/' + path
219b23359f4c hgweb: support constructing URLs from an alternate base URL
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36899
diff changeset
   230
        advertisedfullurl += util.urlreq.quote(path)
219b23359f4c hgweb: support constructing URLs from an alternate base URL
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36899
diff changeset
   231
    else:
219b23359f4c hgweb: support constructing URLs from an alternate base URL
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36899
diff changeset
   232
        advertisedfullurl += util.urlreq.quote(env.get('SCRIPT_NAME', ''))
219b23359f4c hgweb: support constructing URLs from an alternate base URL
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36899
diff changeset
   233
36806
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36713
diff changeset
   234
    advertisedfullurl += util.urlreq.quote(env.get('PATH_INFO', ''))
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36713
diff changeset
   235
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36713
diff changeset
   236
    if env.get('QUERY_STRING'):
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36713
diff changeset
   237
        fullurl += '?' + env['QUERY_STRING']
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36713
diff changeset
   238
        advertisedfullurl += '?' + env['QUERY_STRING']
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36713
diff changeset
   239
36897
d7fd203e36cc hgweb: refactor repository name URL parsing
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36896
diff changeset
   240
    # If ``reponame`` is defined, that must be a prefix on PATH_INFO
d7fd203e36cc hgweb: refactor repository name URL parsing
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36896
diff changeset
   241
    # that represents the repository being dispatched to. When computing
d7fd203e36cc hgweb: refactor repository name URL parsing
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36896
diff changeset
   242
    # the dispatch info, we ignore these leading path components.
36806
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36713
diff changeset
   243
36900
219b23359f4c hgweb: support constructing URLs from an alternate base URL
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36899
diff changeset
   244
    if altbaseurl:
219b23359f4c hgweb: support constructing URLs from an alternate base URL
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36899
diff changeset
   245
        apppath = altbaseurl.path or ''
219b23359f4c hgweb: support constructing URLs from an alternate base URL
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36899
diff changeset
   246
        if apppath and not apppath.startswith('/'):
219b23359f4c hgweb: support constructing URLs from an alternate base URL
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36899
diff changeset
   247
            apppath = '/' + apppath
219b23359f4c hgweb: support constructing URLs from an alternate base URL
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36899
diff changeset
   248
    else:
219b23359f4c hgweb: support constructing URLs from an alternate base URL
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36899
diff changeset
   249
        apppath = env.get('SCRIPT_NAME', '')
36806
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36713
diff changeset
   250
36897
d7fd203e36cc hgweb: refactor repository name URL parsing
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36896
diff changeset
   251
    if reponame:
d7fd203e36cc hgweb: refactor repository name URL parsing
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36896
diff changeset
   252
        repoprefix = '/' + reponame.strip('/')
36808
0031e972ded2 hgweb: use the parsed application path directly
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36806
diff changeset
   253
36897
d7fd203e36cc hgweb: refactor repository name URL parsing
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36896
diff changeset
   254
        if not env.get('PATH_INFO'):
d7fd203e36cc hgweb: refactor repository name URL parsing
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36896
diff changeset
   255
            raise error.ProgrammingError('reponame requires PATH_INFO')
d7fd203e36cc hgweb: refactor repository name URL parsing
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36896
diff changeset
   256
d7fd203e36cc hgweb: refactor repository name URL parsing
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36896
diff changeset
   257
        if not env['PATH_INFO'].startswith(repoprefix):
d7fd203e36cc hgweb: refactor repository name URL parsing
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36896
diff changeset
   258
            raise error.ProgrammingError('PATH_INFO does not begin with repo '
d7fd203e36cc hgweb: refactor repository name URL parsing
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36896
diff changeset
   259
                                         'name: %s (%s)' % (env['PATH_INFO'],
d7fd203e36cc hgweb: refactor repository name URL parsing
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36896
diff changeset
   260
                                                            reponame))
36806
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36713
diff changeset
   261
36897
d7fd203e36cc hgweb: refactor repository name URL parsing
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36896
diff changeset
   262
        dispatchpath = env['PATH_INFO'][len(repoprefix):]
36806
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36713
diff changeset
   263
36897
d7fd203e36cc hgweb: refactor repository name URL parsing
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36896
diff changeset
   264
        if dispatchpath and not dispatchpath.startswith('/'):
d7fd203e36cc hgweb: refactor repository name URL parsing
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36896
diff changeset
   265
            raise error.ProgrammingError('reponame prefix of PATH_INFO does '
d7fd203e36cc hgweb: refactor repository name URL parsing
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36896
diff changeset
   266
                                         'not end at path delimiter: %s (%s)' %
d7fd203e36cc hgweb: refactor repository name URL parsing
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36896
diff changeset
   267
                                         (env['PATH_INFO'], reponame))
d7fd203e36cc hgweb: refactor repository name URL parsing
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36896
diff changeset
   268
d7fd203e36cc hgweb: refactor repository name URL parsing
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36896
diff changeset
   269
        apppath = apppath.rstrip('/') + repoprefix
d7fd203e36cc hgweb: refactor repository name URL parsing
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36896
diff changeset
   270
        dispatchparts = dispatchpath.strip('/').split('/')
36898
d0b0fedbfb53 hgweb: change how dispatch path is reported
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36897
diff changeset
   271
        dispatchpath = '/'.join(dispatchparts)
d0b0fedbfb53 hgweb: change how dispatch path is reported
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36897
diff changeset
   272
d0b0fedbfb53 hgweb: change how dispatch path is reported
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36897
diff changeset
   273
    elif 'PATH_INFO' in env:
d0b0fedbfb53 hgweb: change how dispatch path is reported
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36897
diff changeset
   274
        if env['PATH_INFO'].strip('/'):
d0b0fedbfb53 hgweb: change how dispatch path is reported
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36897
diff changeset
   275
            dispatchparts = env['PATH_INFO'].strip('/').split('/')
d0b0fedbfb53 hgweb: change how dispatch path is reported
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36897
diff changeset
   276
            dispatchpath = '/'.join(dispatchparts)
d0b0fedbfb53 hgweb: change how dispatch path is reported
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36897
diff changeset
   277
        else:
d0b0fedbfb53 hgweb: change how dispatch path is reported
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36897
diff changeset
   278
            dispatchparts = []
d0b0fedbfb53 hgweb: change how dispatch path is reported
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36897
diff changeset
   279
            dispatchpath = ''
36806
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36713
diff changeset
   280
    else:
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36713
diff changeset
   281
        dispatchparts = []
36898
d0b0fedbfb53 hgweb: change how dispatch path is reported
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36897
diff changeset
   282
        dispatchpath = None
36806
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36713
diff changeset
   283
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36713
diff changeset
   284
    querystring = env.get('QUERY_STRING', '')
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36713
diff changeset
   285
36809
3c15b84ab66c hgweb: teach WSGI parser about query strings
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36808
diff changeset
   286
    # 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: 36808
diff changeset
   287
    # a dict to facilitate fast lookup.
36862
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36861
diff changeset
   288
    qsparams = multidict()
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36861
diff changeset
   289
    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: 36861
diff changeset
   290
        qsparams.add(k, v)
36809
3c15b84ab66c hgweb: teach WSGI parser about query strings
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36808
diff changeset
   291
36814
f9078c6caeb6 hgweb: parse and store HTTP request headers
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36811
diff changeset
   292
    # 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: 36811
diff changeset
   293
    # 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: 36811
diff changeset
   294
    # so keys match what likely went over the wire.
f9078c6caeb6 hgweb: parse and store HTTP request headers
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36811
diff changeset
   295
    headers = []
f9078c6caeb6 hgweb: parse and store HTTP request headers
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36811
diff changeset
   296
    for k, v in env.iteritems():
f9078c6caeb6 hgweb: parse and store HTTP request headers
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36811
diff changeset
   297
        if k.startswith('HTTP_'):
f9078c6caeb6 hgweb: parse and store HTTP request headers
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36811
diff changeset
   298
            headers.append((k[len('HTTP_'):].replace('_', '-'), v))
f9078c6caeb6 hgweb: parse and store HTTP request headers
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36811
diff changeset
   299
37606
da84e26d85ed hgweb: use our forked wsgiheaders module instead of stdlib one
Augie Fackler <augie@google.com>
parents: 37589
diff changeset
   300
    from . import wsgiheaders # avoid cycle
36814
f9078c6caeb6 hgweb: parse and store HTTP request headers
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36811
diff changeset
   301
    headers = wsgiheaders.Headers(headers)
f9078c6caeb6 hgweb: parse and store HTTP request headers
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36811
diff changeset
   302
36847
ed0456fde625 hgweb: handle CONTENT_LENGTH
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36814
diff changeset
   303
    # 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: 36814
diff changeset
   304
    # 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: 36814
diff changeset
   305
    # this, since a consumer will either either value to determine how many
ed0456fde625 hgweb: handle CONTENT_LENGTH
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36814
diff changeset
   306
    # bytes are available to read.
ed0456fde625 hgweb: handle CONTENT_LENGTH
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36814
diff changeset
   307
    if 'CONTENT_LENGTH' in env and 'HTTP_CONTENT_LENGTH' not in env:
ed0456fde625 hgweb: handle CONTENT_LENGTH
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36814
diff changeset
   308
        headers['Content-Length'] = env['CONTENT_LENGTH']
ed0456fde625 hgweb: handle CONTENT_LENGTH
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36814
diff changeset
   309
37049
55e901396005 hgweb: also set Content-Type header
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36997
diff changeset
   310
    if 'CONTENT_TYPE' in env and 'HTTP_CONTENT_TYPE' not in env:
55e901396005 hgweb: also set Content-Type header
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36997
diff changeset
   311
        headers['Content-Type'] = env['CONTENT_TYPE']
55e901396005 hgweb: also set Content-Type header
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36997
diff changeset
   312
37818
877185de62cf hgweb: reuse body file object when hgwebdir calls hgweb (issue5851)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37616
diff changeset
   313
    if bodyfh is None:
877185de62cf hgweb: reuse body file object when hgwebdir calls hgweb (issue5851)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37616
diff changeset
   314
        bodyfh = env['wsgi.input']
877185de62cf hgweb: reuse body file object when hgwebdir calls hgweb (issue5851)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37616
diff changeset
   315
        if 'Content-Length' in headers:
37825
e82b137a8b4e hgweb: guard against empty Content-Length header
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37818
diff changeset
   316
            bodyfh = util.cappedreader(bodyfh,
e82b137a8b4e hgweb: guard against empty Content-Length header
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37818
diff changeset
   317
                                       int(headers['Content-Length'] or '0'))
36857
da4e2f87167d hgweb: expose input stream on parsed WSGI request object
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36856
diff changeset
   318
36848
16292bbda39c hgweb: store and use request method on parsed request
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36847
diff changeset
   319
    return parsedrequest(method=env['REQUEST_METHOD'],
16292bbda39c hgweb: store and use request method on parsed request
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36847
diff changeset
   320
                         url=fullurl, baseurl=baseurl,
36806
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36713
diff changeset
   321
                         advertisedurl=advertisedfullurl,
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36713
diff changeset
   322
                         advertisedbaseurl=advertisedbaseurl,
36867
a755fd3b7146 hgweb: expose URL scheme and REMOTE_* attributes
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36866
diff changeset
   323
                         urlscheme=env['wsgi.url_scheme'],
a755fd3b7146 hgweb: expose URL scheme and REMOTE_* attributes
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36866
diff changeset
   324
                         remoteuser=env.get('REMOTE_USER'),
a755fd3b7146 hgweb: expose URL scheme and REMOTE_* attributes
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36866
diff changeset
   325
                         remotehost=env.get('REMOTE_HOST'),
36806
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36713
diff changeset
   326
                         apppath=apppath,
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36713
diff changeset
   327
                         dispatchparts=dispatchparts, dispatchpath=dispatchpath,
36897
d7fd203e36cc hgweb: refactor repository name URL parsing
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36896
diff changeset
   328
                         reponame=reponame,
36809
3c15b84ab66c hgweb: teach WSGI parser about query strings
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36808
diff changeset
   329
                         querystring=querystring,
36862
ec0af9c59270 hgweb: use a multidict for holding query string parameters
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36861
diff changeset
   330
                         qsparams=qsparams,
36857
da4e2f87167d hgweb: expose input stream on parsed WSGI request object
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36856
diff changeset
   331
                         headers=headers,
36909
84110a1d0f7d hgweb: store the raw WSGI environment dict
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36908
diff changeset
   332
                         bodyfh=bodyfh,
84110a1d0f7d hgweb: store the raw WSGI environment dict
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36908
diff changeset
   333
                         rawenv=env)
36806
69b2d0900cd7 hgweb: parse WSGI request into a data structure
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36713
diff changeset
   334
36875
16499427f6de hgweb: refactor fake file object proxy for archiving
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36868
diff changeset
   335
class offsettrackingwriter(object):
16499427f6de hgweb: refactor fake file object proxy for archiving
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36868
diff changeset
   336
    """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: 36868
diff changeset
   337
16499427f6de hgweb: refactor fake file object proxy for archiving
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36868
diff changeset
   338
    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: 36868
diff changeset
   339
    whenever a ``write()`` is attempted.
16499427f6de hgweb: refactor fake file object proxy for archiving
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36868
diff changeset
   340
16499427f6de hgweb: refactor fake file object proxy for archiving
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36868
diff changeset
   341
    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: 36868
diff changeset
   342
    requests.
16499427f6de hgweb: refactor fake file object proxy for archiving
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36868
diff changeset
   343
16499427f6de hgweb: refactor fake file object proxy for archiving
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36868
diff changeset
   344
    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: 36868
diff changeset
   345
    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: 36868
diff changeset
   346
    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: 36868
diff changeset
   347
    """
16499427f6de hgweb: refactor fake file object proxy for archiving
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36868
diff changeset
   348
    def __init__(self, writefn):
16499427f6de hgweb: refactor fake file object proxy for archiving
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36868
diff changeset
   349
        self._write = writefn
16499427f6de hgweb: refactor fake file object proxy for archiving
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36868
diff changeset
   350
        self._offset = 0
16499427f6de hgweb: refactor fake file object proxy for archiving
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36868
diff changeset
   351
16499427f6de hgweb: refactor fake file object proxy for archiving
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36868
diff changeset
   352
    def write(self, s):
16499427f6de hgweb: refactor fake file object proxy for archiving
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36868
diff changeset
   353
        res = self._write(s)
16499427f6de hgweb: refactor fake file object proxy for archiving
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36868
diff changeset
   354
        # 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: 36868
diff changeset
   355
        if res is None:
16499427f6de hgweb: refactor fake file object proxy for archiving
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36868
diff changeset
   356
            self._offset += len(s)
16499427f6de hgweb: refactor fake file object proxy for archiving
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36868
diff changeset
   357
        else:
16499427f6de hgweb: refactor fake file object proxy for archiving
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36868
diff changeset
   358
            self._offset += res
16499427f6de hgweb: refactor fake file object proxy for archiving
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36868
diff changeset
   359
16499427f6de hgweb: refactor fake file object proxy for archiving
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36868
diff changeset
   360
    def flush(self):
16499427f6de hgweb: refactor fake file object proxy for archiving
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36868
diff changeset
   361
        pass
16499427f6de hgweb: refactor fake file object proxy for archiving
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36868
diff changeset
   362
16499427f6de hgweb: refactor fake file object proxy for archiving
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36868
diff changeset
   363
    def tell(self):
16499427f6de hgweb: refactor fake file object proxy for archiving
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36868
diff changeset
   364
        return self._offset
16499427f6de hgweb: refactor fake file object proxy for archiving
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36868
diff changeset
   365
36861
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36859
diff changeset
   366
class wsgiresponse(object):
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36859
diff changeset
   367
    """Represents a response to a WSGI request.
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36859
diff changeset
   368
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36859
diff changeset
   369
    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: 36859
diff changeset
   370
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36859
diff changeset
   371
    Consumers must populate the ``status`` and ``headers`` fields and
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36859
diff changeset
   372
    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: 36859
diff changeset
   373
    issued.
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36859
diff changeset
   374
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36859
diff changeset
   375
    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: 36859
diff changeset
   376
    ``sendresponse()`` is called. It handles emitting the header portion
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36859
diff changeset
   377
    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: 36859
diff changeset
   378
    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: 36859
diff changeset
   379
    and returns the value from ``sendresponse()``.
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36859
diff changeset
   380
    """
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36859
diff changeset
   381
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36859
diff changeset
   382
    def __init__(self, req, startresponse):
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36859
diff changeset
   383
        """Create an empty response tied to a specific request.
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36859
diff changeset
   384
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36859
diff changeset
   385
        ``req`` is a ``parsedrequest``. ``startresponse`` is the
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36859
diff changeset
   386
        ``start_response`` function passed to the WSGI application.
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36859
diff changeset
   387
        """
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36859
diff changeset
   388
        self._req = req
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36859
diff changeset
   389
        self._startresponse = startresponse
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36859
diff changeset
   390
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36859
diff changeset
   391
        self.status = None
37606
da84e26d85ed hgweb: use our forked wsgiheaders module instead of stdlib one
Augie Fackler <augie@google.com>
parents: 37589
diff changeset
   392
        from . import wsgiheaders # avoid cycle
36861
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36859
diff changeset
   393
        self.headers = wsgiheaders.Headers([])
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36859
diff changeset
   394
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36859
diff changeset
   395
        self._bodybytes = None
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36859
diff changeset
   396
        self._bodygen = None
36876
97f44b0720e2 hgweb: port archive command to modern response API
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36875
diff changeset
   397
        self._bodywillwrite = False
36861
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36859
diff changeset
   398
        self._started = False
36876
97f44b0720e2 hgweb: port archive command to modern response API
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36875
diff changeset
   399
        self._bodywritefn = None
97f44b0720e2 hgweb: port archive command to modern response API
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36875
diff changeset
   400
97f44b0720e2 hgweb: port archive command to modern response API
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36875
diff changeset
   401
    def _verifybody(self):
97f44b0720e2 hgweb: port archive command to modern response API
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36875
diff changeset
   402
        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: 36875
diff changeset
   403
            or self._bodywillwrite):
97f44b0720e2 hgweb: port archive command to modern response API
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36875
diff changeset
   404
            raise error.ProgrammingError('cannot define body multiple times')
36861
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36859
diff changeset
   405
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36859
diff changeset
   406
    def setbodybytes(self, b):
36878
ccb70a77f746 hgweb: refactor 304 handling code
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36876
diff changeset
   407
        """Define the response body as static bytes.
ccb70a77f746 hgweb: refactor 304 handling code
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36876
diff changeset
   408
ccb70a77f746 hgweb: refactor 304 handling code
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36876
diff changeset
   409
        The empty string signals that there is no response body.
ccb70a77f746 hgweb: refactor 304 handling code
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36876
diff changeset
   410
        """
36876
97f44b0720e2 hgweb: port archive command to modern response API
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36875
diff changeset
   411
        self._verifybody()
36861
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36859
diff changeset
   412
        self._bodybytes = b
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36859
diff changeset
   413
        self.headers['Content-Length'] = '%d' % len(b)
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36859
diff changeset
   414
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36859
diff changeset
   415
    def setbodygen(self, gen):
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36859
diff changeset
   416
        """Define the response body as a generator of bytes."""
36876
97f44b0720e2 hgweb: port archive command to modern response API
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36875
diff changeset
   417
        self._verifybody()
97f44b0720e2 hgweb: port archive command to modern response API
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36875
diff changeset
   418
        self._bodygen = gen
97f44b0720e2 hgweb: port archive command to modern response API
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36875
diff changeset
   419
97f44b0720e2 hgweb: port archive command to modern response API
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36875
diff changeset
   420
    def setbodywillwrite(self):
97f44b0720e2 hgweb: port archive command to modern response API
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36875
diff changeset
   421
        """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: 36875
diff changeset
   422
97f44b0720e2 hgweb: port archive command to modern response API
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36875
diff changeset
   423
        **This is the least preferred way to send a body.**
36861
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36859
diff changeset
   424
36876
97f44b0720e2 hgweb: port archive command to modern response API
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36875
diff changeset
   425
        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: 36875
diff changeset
   426
        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: 36875
diff changeset
   427
        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: 36875
diff changeset
   428
        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: 36875
diff changeset
   429
        write.
97f44b0720e2 hgweb: port archive command to modern response API
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36875
diff changeset
   430
97f44b0720e2 hgweb: port archive command to modern response API
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36875
diff changeset
   431
        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: 36875
diff changeset
   432
        manner.
97f44b0720e2 hgweb: port archive command to modern response API
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36875
diff changeset
   433
        """
97f44b0720e2 hgweb: port archive command to modern response API
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36875
diff changeset
   434
        self._verifybody()
97f44b0720e2 hgweb: port archive command to modern response API
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36875
diff changeset
   435
        self._bodywillwrite = True
36861
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36859
diff changeset
   436
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36859
diff changeset
   437
    def sendresponse(self):
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36859
diff changeset
   438
        """Send the generated response to the client.
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36859
diff changeset
   439
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36859
diff changeset
   440
        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: 36859
diff changeset
   441
        ``setbodybytes()`` or ``setbodygen()`` must be called.
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36859
diff changeset
   442
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36859
diff changeset
   443
        Calling this method multiple times is not allowed.
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36859
diff changeset
   444
        """
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36859
diff changeset
   445
        if self._started:
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36859
diff changeset
   446
            raise error.ProgrammingError('sendresponse() called multiple times')
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36859
diff changeset
   447
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36859
diff changeset
   448
        self._started = True
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36859
diff changeset
   449
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36859
diff changeset
   450
        if not self.status:
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36859
diff changeset
   451
            raise error.ProgrammingError('status line not defined')
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36859
diff changeset
   452
36876
97f44b0720e2 hgweb: port archive command to modern response API
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36875
diff changeset
   453
        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: 36875
diff changeset
   454
            and not self._bodywillwrite):
36861
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36859
diff changeset
   455
            raise error.ProgrammingError('response body not defined')
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36859
diff changeset
   456
36878
ccb70a77f746 hgweb: refactor 304 handling code
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36876
diff changeset
   457
        # RFC 7232 Section 4.1 states that a 304 MUST generate one of
ccb70a77f746 hgweb: refactor 304 handling code
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36876
diff changeset
   458
        # {Cache-Control, Content-Location, Date, ETag, Expires, Vary}
ccb70a77f746 hgweb: refactor 304 handling code
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36876
diff changeset
   459
        # and SHOULD NOT generate other headers unless they could be used
ccb70a77f746 hgweb: refactor 304 handling code
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36876
diff changeset
   460
        # to guide cache updates. Furthermore, RFC 7230 Section 3.3.2
ccb70a77f746 hgweb: refactor 304 handling code
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36876
diff changeset
   461
        # states that no response body can be issued. Content-Length can
ccb70a77f746 hgweb: refactor 304 handling code
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36876
diff changeset
   462
        # be sent. But if it is present, it should be the size of the response
ccb70a77f746 hgweb: refactor 304 handling code
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36876
diff changeset
   463
        # that wasn't transferred.
ccb70a77f746 hgweb: refactor 304 handling code
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36876
diff changeset
   464
        if self.status.startswith('304 '):
ccb70a77f746 hgweb: refactor 304 handling code
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36876
diff changeset
   465
            # setbodybytes('') will set C-L to 0. This doesn't conform with the
ccb70a77f746 hgweb: refactor 304 handling code
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36876
diff changeset
   466
            # spec. So remove it.
ccb70a77f746 hgweb: refactor 304 handling code
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36876
diff changeset
   467
            if self.headers.get('Content-Length') == '0':
ccb70a77f746 hgweb: refactor 304 handling code
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36876
diff changeset
   468
                del self.headers['Content-Length']
ccb70a77f746 hgweb: refactor 304 handling code
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36876
diff changeset
   469
ccb70a77f746 hgweb: refactor 304 handling code
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36876
diff changeset
   470
            # Strictly speaking, this is too strict. But until it causes
ccb70a77f746 hgweb: refactor 304 handling code
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36876
diff changeset
   471
            # problems, let's be strict.
ccb70a77f746 hgweb: refactor 304 handling code
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36876
diff changeset
   472
            badheaders = {k for k in self.headers.keys()
ccb70a77f746 hgweb: refactor 304 handling code
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36876
diff changeset
   473
                          if k.lower() not in ('date', 'etag', 'expires',
ccb70a77f746 hgweb: refactor 304 handling code
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36876
diff changeset
   474
                                               'cache-control',
ccb70a77f746 hgweb: refactor 304 handling code
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36876
diff changeset
   475
                                               'content-location',
37828
3e3acf5d6a07 hgweb: allow Content-Security-Policy header on 304 responses (issue5844)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37825
diff changeset
   476
                                               'content-security-policy',
36878
ccb70a77f746 hgweb: refactor 304 handling code
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36876
diff changeset
   477
                                               'vary')}
ccb70a77f746 hgweb: refactor 304 handling code
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36876
diff changeset
   478
            if badheaders:
ccb70a77f746 hgweb: refactor 304 handling code
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36876
diff changeset
   479
                raise error.ProgrammingError(
ccb70a77f746 hgweb: refactor 304 handling code
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36876
diff changeset
   480
                    'illegal header on 304 response: %s' %
ccb70a77f746 hgweb: refactor 304 handling code
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36876
diff changeset
   481
                    ', '.join(sorted(badheaders)))
ccb70a77f746 hgweb: refactor 304 handling code
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36876
diff changeset
   482
ccb70a77f746 hgweb: refactor 304 handling code
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36876
diff changeset
   483
            if self._bodygen is not None or self._bodywillwrite:
ccb70a77f746 hgweb: refactor 304 handling code
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36876
diff changeset
   484
                raise error.ProgrammingError("must use setbodybytes('') with "
ccb70a77f746 hgweb: refactor 304 handling code
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36876
diff changeset
   485
                                             "304 responses")
ccb70a77f746 hgweb: refactor 304 handling code
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36876
diff changeset
   486
36861
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36859
diff changeset
   487
        # 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: 36859
diff changeset
   488
        # 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: 36859
diff changeset
   489
        # 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: 36859
diff changeset
   490
        # may deadlock because neither end is reading.
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36859
diff changeset
   491
        #
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36859
diff changeset
   492
        # 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: 36859
diff changeset
   493
        # sending any response in some conditions.
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36859
diff changeset
   494
        drain = False
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36859
diff changeset
   495
        close = False
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36859
diff changeset
   496
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36859
diff changeset
   497
        # 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: 36859
diff changeset
   498
        # 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: 36859
diff changeset
   499
        # (httplib doesn't do this.)
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36859
diff changeset
   500
        if self._req.headers.get('Expect', '').lower() == '100-continue':
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36859
diff changeset
   501
            pass
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36859
diff changeset
   502
        # 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: 36859
diff changeset
   503
        # 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: 36859
diff changeset
   504
        # WSGI applications.
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36859
diff changeset
   505
        elif self._req.method not in ('POST', 'PUT'):
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36859
diff changeset
   506
            pass
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36859
diff changeset
   507
        else:
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36859
diff changeset
   508
            # 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: 36859
diff changeset
   509
            # that we can drain the request responsibly. The WSGI
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36859
diff changeset
   510
            # specification only says that servers *should* ensure the
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36859
diff changeset
   511
            # 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: 36859
diff changeset
   512
            # 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: 36859
diff changeset
   513
            # state.
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36859
diff changeset
   514
            if not isinstance(self._req.bodyfh, util.cappedreader):
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36859
diff changeset
   515
                close = True
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36859
diff changeset
   516
            else:
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36859
diff changeset
   517
                # 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: 36859
diff changeset
   518
                # non-200 wire protocol responses both require draining. Since
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36859
diff changeset
   519
                # 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: 36859
diff changeset
   520
                # 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: 36859
diff changeset
   521
                # 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: 36859
diff changeset
   522
                drain = True
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36859
diff changeset
   523
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36859
diff changeset
   524
        if close:
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36859
diff changeset
   525
            self.headers['Connection'] = 'Close'
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36859
diff changeset
   526
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36859
diff changeset
   527
        if drain:
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36859
diff changeset
   528
            assert isinstance(self._req.bodyfh, util.cappedreader)
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36859
diff changeset
   529
            while True:
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36859
diff changeset
   530
                chunk = self._req.bodyfh.read(32768)
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36859
diff changeset
   531
                if not chunk:
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36859
diff changeset
   532
                    break
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36859
diff changeset
   533
37589
e320d9405bba hgweb: put response headers back into str for Python 3
Augie Fackler <augie@google.com>
parents: 37049
diff changeset
   534
        strheaders = [(pycompat.strurl(k), pycompat.strurl(v)) for
e320d9405bba hgweb: put response headers back into str for Python 3
Augie Fackler <augie@google.com>
parents: 37049
diff changeset
   535
                      k, v in self.headers.items()]
36876
97f44b0720e2 hgweb: port archive command to modern response API
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36875
diff changeset
   536
        write = self._startresponse(pycompat.sysstr(self.status),
37589
e320d9405bba hgweb: put response headers back into str for Python 3
Augie Fackler <augie@google.com>
parents: 37049
diff changeset
   537
                                    strheaders)
36876
97f44b0720e2 hgweb: port archive command to modern response API
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36875
diff changeset
   538
36861
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36859
diff changeset
   539
        if self._bodybytes:
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36859
diff changeset
   540
            yield self._bodybytes
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36859
diff changeset
   541
        elif self._bodygen:
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36859
diff changeset
   542
            for chunk in self._bodygen:
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36859
diff changeset
   543
                yield chunk
36876
97f44b0720e2 hgweb: port archive command to modern response API
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36875
diff changeset
   544
        elif self._bodywillwrite:
97f44b0720e2 hgweb: port archive command to modern response API
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36875
diff changeset
   545
            self._bodywritefn = write
36861
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36859
diff changeset
   546
        else:
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36859
diff changeset
   547
            error.ProgrammingError('do not know how to send body')
a88d68dc3ee8 hgweb: create dedicated type for WSGI responses
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36859
diff changeset
   548
36876
97f44b0720e2 hgweb: port archive command to modern response API
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36875
diff changeset
   549
    def getbodyfile(self):
97f44b0720e2 hgweb: port archive command to modern response API
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36875
diff changeset
   550
        """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: 36875
diff changeset
   551
97f44b0720e2 hgweb: port archive command to modern response API
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36875
diff changeset
   552
        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: 36875
diff changeset
   553
        ``sendresponse()`` first. ``sendresponse()`` is a generator and the
97f44b0720e2 hgweb: port archive command to modern response API
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36875
diff changeset
   554
        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: 36875
diff changeset
   555
        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: 36875
diff changeset
   556
        ``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: 36875
diff changeset
   557
        ``[]``.
97f44b0720e2 hgweb: port archive command to modern response API
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36875
diff changeset
   558
        """
97f44b0720e2 hgweb: port archive command to modern response API
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36875
diff changeset
   559
        if not self._bodywillwrite:
97f44b0720e2 hgweb: port archive command to modern response API
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36875
diff changeset
   560
            raise error.ProgrammingError('must call setbodywillwrite() first')
97f44b0720e2 hgweb: port archive command to modern response API
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36875
diff changeset
   561
97f44b0720e2 hgweb: port archive command to modern response API
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36875
diff changeset
   562
        if not self._started:
97f44b0720e2 hgweb: port archive command to modern response API
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36875
diff changeset
   563
            raise error.ProgrammingError('must call sendresponse() first; did '
97f44b0720e2 hgweb: port archive command to modern response API
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36875
diff changeset
   564
                                         'you remember to consume it since it '
97f44b0720e2 hgweb: port archive command to modern response API
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36875
diff changeset
   565
                                         'is a generator?')
97f44b0720e2 hgweb: port archive command to modern response API
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36875
diff changeset
   566
97f44b0720e2 hgweb: port archive command to modern response API
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36875
diff changeset
   567
        assert self._bodywritefn
97f44b0720e2 hgweb: port archive command to modern response API
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36875
diff changeset
   568
        return offsettrackingwriter(self._bodywritefn)
97f44b0720e2 hgweb: port archive command to modern response API
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36875
diff changeset
   569
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
   570
def wsgiapplication(app_maker):
5887
41a3fce17625 hgweb: return iterable, add deprecation note
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 5886
diff changeset
   571
    '''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
   572
    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
   573
    application = app_maker()
0145f9afb0e7 Removed tabs and trailing whitespace in python files
Thomas Arendsen Hein <thomas@intevation.de>
parents: 5566
diff changeset
   574
    def run_wsgi(env, respond):
5887
41a3fce17625 hgweb: return iterable, add deprecation note
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 5886
diff changeset
   575
        return application(env, respond)
5760
0145f9afb0e7 Removed tabs and trailing whitespace in python files
Thomas Arendsen Hein <thomas@intevation.de>
parents: 5566
diff changeset
   576
    return run_wsgi