mercurial/httppeer.py
author Gregory Szorc <gregory.szorc@gmail.com>
Tue, 10 Apr 2018 18:16:47 -0700
changeset 37558 8a73132214a3
parent 37556 b77aa48ba690
child 37609 01bfe5ad0c53
permissions -rw-r--r--
httppeer: support protocol upgrade With the new handshake defined and in place on the server, we can now implement it on the client. The HTTP handshake mechanism has been taught to add headers advertising its support for the new capabilities response. Response handling has been adjusted to allow CBOR responses through. And makepeer() has been taught to instantiate a mutually supported peer. The HTTPv2 peer class doesn't implement the full peer interface. So HTTPv2 is not yet usable as a peer. Like the server side, we support registering handlers for different API services. This allows extensions to easily implement API services and peers. A practical use case for this is to provide a previous implementation of the experimental version 2 wire protocol to a future version of Mercurial. We know there will be BC breaks after 4.6 ships. But someone could take the peer and server code from 4.6, drop it in an extension, and allow its use indefinitely. Differential Revision: https://phab.mercurial-scm.org/D3243
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
17192
1ac628cd7113 peer: introduce real peer classes
Peter Arrenbrecht <peter.arrenbrecht@gmail.com>
parents: 15246
diff changeset
     1
# httppeer.py - HTTP repository proxy classes for mercurial
0
9117c6561b0b Add back links from file revisions to changeset revisions
mpm@selenic.com
parents:
diff changeset
     2
#
2859
345bac2bc4ec update copyrights.
Vadim Gelfer <vadim.gelfer@gmail.com>
parents: 2740
diff changeset
     3
# Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
345bac2bc4ec update copyrights.
Vadim Gelfer <vadim.gelfer@gmail.com>
parents: 2740
diff changeset
     4
# Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
0
9117c6561b0b Add back links from file revisions to changeset revisions
mpm@selenic.com
parents:
diff changeset
     5
#
8225
46293a0c7e9f updated license to be explicit about GPL version 2
Martin Geisler <mg@lazybytes.net>
parents: 8206
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: 9878
diff changeset
     7
# GNU General Public License version 2 or any later version.
0
9117c6561b0b Add back links from file revisions to changeset revisions
mpm@selenic.com
parents:
diff changeset
     8
25954
7bbdb78d2842 httppeer: use absolute_import
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25660
diff changeset
     9
from __future__ import absolute_import
7bbdb78d2842 httppeer: use absolute_import
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25660
diff changeset
    10
7bbdb78d2842 httppeer: use absolute_import
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25660
diff changeset
    11
import errno
33821
3c91cc0c5fde httppeer: add support for httppostargs when we're sending a file
Augie Fackler <augie@google.com>
parents: 33806
diff changeset
    12
import io
25954
7bbdb78d2842 httppeer: use absolute_import
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25660
diff changeset
    13
import os
7bbdb78d2842 httppeer: use absolute_import
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25660
diff changeset
    14
import socket
30763
a520aefb96f1 httppeer: advertise and support application/mercurial-0.2
Gregory Szorc <gregory.szorc@gmail.com>
parents: 30759
diff changeset
    15
import struct
21074
f8a0d82b0463 httppeer: support for _calltwowaystream
Pierre-Yves David <pierre-yves.david@fb.com>
parents: 20905
diff changeset
    16
import tempfile
25954
7bbdb78d2842 httppeer: use absolute_import
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25660
diff changeset
    17
7bbdb78d2842 httppeer: use absolute_import
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25660
diff changeset
    18
from .i18n import _
37483
61e405fb6372 wireproto: crude support for version 2 HTTP peer
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37411
diff changeset
    19
from .thirdparty import (
61e405fb6372 wireproto: crude support for version 2 HTTP peer
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37411
diff changeset
    20
    cbor,
61e405fb6372 wireproto: crude support for version 2 HTTP peer
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37411
diff changeset
    21
)
25954
7bbdb78d2842 httppeer: use absolute_import
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25660
diff changeset
    22
from . import (
28666
ae53ecc47414 bundle: move writebundle() from changegroup.py to bundle2.py (API)
Martin von Zweigbergk <martinvonz@google.com>
parents: 28530
diff changeset
    23
    bundle2,
25954
7bbdb78d2842 httppeer: use absolute_import
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25660
diff changeset
    24
    error,
7bbdb78d2842 httppeer: use absolute_import
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25660
diff changeset
    25
    httpconnection,
30924
48dea083f66d py3: convert the mode argument of os.fdopen to unicodes (1 of 2)
Pulkit Goyal <7895pulkit@gmail.com>
parents: 30763
diff changeset
    26
    pycompat,
25954
7bbdb78d2842 httppeer: use absolute_import
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25660
diff changeset
    27
    statichttprepo,
36959
43815d87c6aa httppeer: alias url as urlmod
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36835
diff changeset
    28
    url as urlmod,
25954
7bbdb78d2842 httppeer: use absolute_import
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25660
diff changeset
    29
    util,
7bbdb78d2842 httppeer: use absolute_import
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25660
diff changeset
    30
    wireproto,
37483
61e405fb6372 wireproto: crude support for version 2 HTTP peer
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37411
diff changeset
    31
    wireprotoframing,
37558
8a73132214a3 httppeer: support protocol upgrade
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37556
diff changeset
    32
    wireprototypes,
37545
93397c4633f6 wireproto: extract HTTP version 2 code to own module
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37544
diff changeset
    33
    wireprotov2server,
25954
7bbdb78d2842 httppeer: use absolute_import
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25660
diff changeset
    34
)
4678
a814a5b11fff Work around urllib2 digest auth bug with Python < 2.5
Alexis S. L. Carvalho <alexis@cecm.usp.br>
parents: 4633
diff changeset
    35
29455
0c741fd6158a py3: conditionalize httplib import
Pulkit Goyal <7895pulkit@gmail.com>
parents: 29241
diff changeset
    36
httplib = util.httplib
28883
032c4c2f802a pycompat: switch to util.urlreq/util.urlerr for py3 compat
timeless <timeless@mozdev.org>
parents: 28666
diff changeset
    37
urlerr = util.urlerr
032c4c2f802a pycompat: switch to util.urlreq/util.urlerr for py3 compat
timeless <timeless@mozdev.org>
parents: 28666
diff changeset
    38
urlreq = util.urlreq
032c4c2f802a pycompat: switch to util.urlreq/util.urlerr for py3 compat
timeless <timeless@mozdev.org>
parents: 28666
diff changeset
    39
30759
3f5f0c98cd18 httppeer: extract code for HTTP header spanning
Gregory Szorc <gregory.szorc@gmail.com>
parents: 30564
diff changeset
    40
def encodevalueinheaders(value, header, limit):
3f5f0c98cd18 httppeer: extract code for HTTP header spanning
Gregory Szorc <gregory.szorc@gmail.com>
parents: 30564
diff changeset
    41
    """Encode a string value into multiple HTTP headers.
3f5f0c98cd18 httppeer: extract code for HTTP header spanning
Gregory Szorc <gregory.szorc@gmail.com>
parents: 30564
diff changeset
    42
3f5f0c98cd18 httppeer: extract code for HTTP header spanning
Gregory Szorc <gregory.szorc@gmail.com>
parents: 30564
diff changeset
    43
    ``value`` will be encoded into 1 or more HTTP headers with the names
3f5f0c98cd18 httppeer: extract code for HTTP header spanning
Gregory Szorc <gregory.szorc@gmail.com>
parents: 30564
diff changeset
    44
    ``header-<N>`` where ``<N>`` is an integer starting at 1. Each header
3f5f0c98cd18 httppeer: extract code for HTTP header spanning
Gregory Szorc <gregory.szorc@gmail.com>
parents: 30564
diff changeset
    45
    name + value will be at most ``limit`` bytes long.
3f5f0c98cd18 httppeer: extract code for HTTP header spanning
Gregory Szorc <gregory.szorc@gmail.com>
parents: 30564
diff changeset
    46
34732
67e9678efd98 httppeer: always produce native str header keys and values
Augie Fackler <augie@google.com>
parents: 34725
diff changeset
    47
    Returns an iterable of 2-tuples consisting of header names and
67e9678efd98 httppeer: always produce native str header keys and values
Augie Fackler <augie@google.com>
parents: 34725
diff changeset
    48
    values as native strings.
30759
3f5f0c98cd18 httppeer: extract code for HTTP header spanning
Gregory Szorc <gregory.szorc@gmail.com>
parents: 30564
diff changeset
    49
    """
34732
67e9678efd98 httppeer: always produce native str header keys and values
Augie Fackler <augie@google.com>
parents: 34725
diff changeset
    50
    # HTTP Headers are ASCII. Python 3 requires them to be unicodes,
67e9678efd98 httppeer: always produce native str header keys and values
Augie Fackler <augie@google.com>
parents: 34725
diff changeset
    51
    # not bytes. This function always takes bytes in as arguments.
67e9678efd98 httppeer: always produce native str header keys and values
Augie Fackler <augie@google.com>
parents: 34725
diff changeset
    52
    fmt = pycompat.strurl(header) + r'-%s'
67e9678efd98 httppeer: always produce native str header keys and values
Augie Fackler <augie@google.com>
parents: 34725
diff changeset
    53
    # Note: it is *NOT* a bug that the last bit here is a bytestring
67e9678efd98 httppeer: always produce native str header keys and values
Augie Fackler <augie@google.com>
parents: 34725
diff changeset
    54
    # and not a unicode: we're just getting the encoded length anyway,
67e9678efd98 httppeer: always produce native str header keys and values
Augie Fackler <augie@google.com>
parents: 34725
diff changeset
    55
    # and using an r-string to make it portable between Python 2 and 3
67e9678efd98 httppeer: always produce native str header keys and values
Augie Fackler <augie@google.com>
parents: 34725
diff changeset
    56
    # doesn't work because then the \r is a literal backslash-r
67e9678efd98 httppeer: always produce native str header keys and values
Augie Fackler <augie@google.com>
parents: 34725
diff changeset
    57
    # instead of a carriage return.
67e9678efd98 httppeer: always produce native str header keys and values
Augie Fackler <augie@google.com>
parents: 34725
diff changeset
    58
    valuelen = limit - len(fmt % r'000') - len(': \r\n')
30759
3f5f0c98cd18 httppeer: extract code for HTTP header spanning
Gregory Szorc <gregory.szorc@gmail.com>
parents: 30564
diff changeset
    59
    result = []
3f5f0c98cd18 httppeer: extract code for HTTP header spanning
Gregory Szorc <gregory.szorc@gmail.com>
parents: 30564
diff changeset
    60
3f5f0c98cd18 httppeer: extract code for HTTP header spanning
Gregory Szorc <gregory.szorc@gmail.com>
parents: 30564
diff changeset
    61
    n = 0
3f5f0c98cd18 httppeer: extract code for HTTP header spanning
Gregory Szorc <gregory.szorc@gmail.com>
parents: 30564
diff changeset
    62
    for i in xrange(0, len(value), valuelen):
3f5f0c98cd18 httppeer: extract code for HTTP header spanning
Gregory Szorc <gregory.szorc@gmail.com>
parents: 30564
diff changeset
    63
        n += 1
34732
67e9678efd98 httppeer: always produce native str header keys and values
Augie Fackler <augie@google.com>
parents: 34725
diff changeset
    64
        result.append((fmt % str(n), pycompat.strurl(value[i:i + valuelen])))
30759
3f5f0c98cd18 httppeer: extract code for HTTP header spanning
Gregory Szorc <gregory.szorc@gmail.com>
parents: 30564
diff changeset
    65
3f5f0c98cd18 httppeer: extract code for HTTP header spanning
Gregory Szorc <gregory.szorc@gmail.com>
parents: 30564
diff changeset
    66
    return result
3f5f0c98cd18 httppeer: extract code for HTTP header spanning
Gregory Szorc <gregory.szorc@gmail.com>
parents: 30564
diff changeset
    67
32002
bf855efe5664 httppeer: wrap HTTPResponse.read() globally
Gregory Szorc <gregory.szorc@gmail.com>
parents: 30924
diff changeset
    68
def _wraphttpresponse(resp):
bf855efe5664 httppeer: wrap HTTPResponse.read() globally
Gregory Szorc <gregory.szorc@gmail.com>
parents: 30924
diff changeset
    69
    """Wrap an HTTPResponse with common error handlers.
bf855efe5664 httppeer: wrap HTTPResponse.read() globally
Gregory Szorc <gregory.szorc@gmail.com>
parents: 30924
diff changeset
    70
bf855efe5664 httppeer: wrap HTTPResponse.read() globally
Gregory Szorc <gregory.szorc@gmail.com>
parents: 30924
diff changeset
    71
    This ensures that any I/O from any consumer raises the appropriate
bf855efe5664 httppeer: wrap HTTPResponse.read() globally
Gregory Szorc <gregory.szorc@gmail.com>
parents: 30924
diff changeset
    72
    error and messaging.
bf855efe5664 httppeer: wrap HTTPResponse.read() globally
Gregory Szorc <gregory.szorc@gmail.com>
parents: 30924
diff changeset
    73
    """
bf855efe5664 httppeer: wrap HTTPResponse.read() globally
Gregory Szorc <gregory.szorc@gmail.com>
parents: 30924
diff changeset
    74
    origread = resp.read
bf855efe5664 httppeer: wrap HTTPResponse.read() globally
Gregory Szorc <gregory.szorc@gmail.com>
parents: 30924
diff changeset
    75
bf855efe5664 httppeer: wrap HTTPResponse.read() globally
Gregory Szorc <gregory.szorc@gmail.com>
parents: 30924
diff changeset
    76
    class readerproxy(resp.__class__):
bf855efe5664 httppeer: wrap HTTPResponse.read() globally
Gregory Szorc <gregory.szorc@gmail.com>
parents: 30924
diff changeset
    77
        def read(self, size=None):
bf855efe5664 httppeer: wrap HTTPResponse.read() globally
Gregory Szorc <gregory.szorc@gmail.com>
parents: 30924
diff changeset
    78
            try:
bf855efe5664 httppeer: wrap HTTPResponse.read() globally
Gregory Szorc <gregory.szorc@gmail.com>
parents: 30924
diff changeset
    79
                return origread(size)
bf855efe5664 httppeer: wrap HTTPResponse.read() globally
Gregory Szorc <gregory.szorc@gmail.com>
parents: 30924
diff changeset
    80
            except httplib.IncompleteRead as e:
bf855efe5664 httppeer: wrap HTTPResponse.read() globally
Gregory Szorc <gregory.szorc@gmail.com>
parents: 30924
diff changeset
    81
                # e.expected is an integer if length known or None otherwise.
bf855efe5664 httppeer: wrap HTTPResponse.read() globally
Gregory Szorc <gregory.szorc@gmail.com>
parents: 30924
diff changeset
    82
                if e.expected:
bf855efe5664 httppeer: wrap HTTPResponse.read() globally
Gregory Szorc <gregory.szorc@gmail.com>
parents: 30924
diff changeset
    83
                    msg = _('HTTP request error (incomplete response; '
bf855efe5664 httppeer: wrap HTTPResponse.read() globally
Gregory Szorc <gregory.szorc@gmail.com>
parents: 30924
diff changeset
    84
                            'expected %d bytes got %d)') % (e.expected,
bf855efe5664 httppeer: wrap HTTPResponse.read() globally
Gregory Szorc <gregory.szorc@gmail.com>
parents: 30924
diff changeset
    85
                                                           len(e.partial))
bf855efe5664 httppeer: wrap HTTPResponse.read() globally
Gregory Szorc <gregory.szorc@gmail.com>
parents: 30924
diff changeset
    86
                else:
bf855efe5664 httppeer: wrap HTTPResponse.read() globally
Gregory Szorc <gregory.szorc@gmail.com>
parents: 30924
diff changeset
    87
                    msg = _('HTTP request error (incomplete response)')
bf855efe5664 httppeer: wrap HTTPResponse.read() globally
Gregory Szorc <gregory.szorc@gmail.com>
parents: 30924
diff changeset
    88
32023
a29580905771 error: rename RichIOError to PeerTransportError
Gregory Szorc <gregory.szorc@gmail.com>
parents: 32022
diff changeset
    89
                raise error.PeerTransportError(
32002
bf855efe5664 httppeer: wrap HTTPResponse.read() globally
Gregory Szorc <gregory.szorc@gmail.com>
parents: 30924
diff changeset
    90
                    msg,
bf855efe5664 httppeer: wrap HTTPResponse.read() globally
Gregory Szorc <gregory.szorc@gmail.com>
parents: 30924
diff changeset
    91
                    hint=_('this may be an intermittent network failure; '
bf855efe5664 httppeer: wrap HTTPResponse.read() globally
Gregory Szorc <gregory.szorc@gmail.com>
parents: 30924
diff changeset
    92
                           'if the error persists, consider contacting the '
bf855efe5664 httppeer: wrap HTTPResponse.read() globally
Gregory Szorc <gregory.szorc@gmail.com>
parents: 30924
diff changeset
    93
                           'network or server operator'))
bf855efe5664 httppeer: wrap HTTPResponse.read() globally
Gregory Szorc <gregory.szorc@gmail.com>
parents: 30924
diff changeset
    94
            except httplib.HTTPException as e:
32023
a29580905771 error: rename RichIOError to PeerTransportError
Gregory Szorc <gregory.szorc@gmail.com>
parents: 32022
diff changeset
    95
                raise error.PeerTransportError(
32002
bf855efe5664 httppeer: wrap HTTPResponse.read() globally
Gregory Szorc <gregory.szorc@gmail.com>
parents: 30924
diff changeset
    96
                    _('HTTP request error (%s)') % e,
32086
b59a292d0a53 httppeer: unify hint message for PeerTransportError
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 32023
diff changeset
    97
                    hint=_('this may be an intermittent network failure; '
32002
bf855efe5664 httppeer: wrap HTTPResponse.read() globally
Gregory Szorc <gregory.szorc@gmail.com>
parents: 30924
diff changeset
    98
                           'if the error persists, consider contacting the '
bf855efe5664 httppeer: wrap HTTPResponse.read() globally
Gregory Szorc <gregory.szorc@gmail.com>
parents: 30924
diff changeset
    99
                           'network or server operator'))
bf855efe5664 httppeer: wrap HTTPResponse.read() globally
Gregory Szorc <gregory.szorc@gmail.com>
parents: 30924
diff changeset
   100
bf855efe5664 httppeer: wrap HTTPResponse.read() globally
Gregory Szorc <gregory.szorc@gmail.com>
parents: 30924
diff changeset
   101
    resp.__class__ = readerproxy
bf855efe5664 httppeer: wrap HTTPResponse.read() globally
Gregory Szorc <gregory.szorc@gmail.com>
parents: 30924
diff changeset
   102
33821
3c91cc0c5fde httppeer: add support for httppostargs when we're sending a file
Augie Fackler <augie@google.com>
parents: 33806
diff changeset
   103
class _multifile(object):
3c91cc0c5fde httppeer: add support for httppostargs when we're sending a file
Augie Fackler <augie@google.com>
parents: 33806
diff changeset
   104
    def __init__(self, *fileobjs):
3c91cc0c5fde httppeer: add support for httppostargs when we're sending a file
Augie Fackler <augie@google.com>
parents: 33806
diff changeset
   105
        for f in fileobjs:
3c91cc0c5fde httppeer: add support for httppostargs when we're sending a file
Augie Fackler <augie@google.com>
parents: 33806
diff changeset
   106
            if not util.safehasattr(f, 'length'):
3c91cc0c5fde httppeer: add support for httppostargs when we're sending a file
Augie Fackler <augie@google.com>
parents: 33806
diff changeset
   107
                raise ValueError(
3c91cc0c5fde httppeer: add support for httppostargs when we're sending a file
Augie Fackler <augie@google.com>
parents: 33806
diff changeset
   108
                    '_multifile only supports file objects that '
3c91cc0c5fde httppeer: add support for httppostargs when we're sending a file
Augie Fackler <augie@google.com>
parents: 33806
diff changeset
   109
                    'have a length but this one does not:', type(f), f)
3c91cc0c5fde httppeer: add support for httppostargs when we're sending a file
Augie Fackler <augie@google.com>
parents: 33806
diff changeset
   110
        self._fileobjs = fileobjs
3c91cc0c5fde httppeer: add support for httppostargs when we're sending a file
Augie Fackler <augie@google.com>
parents: 33806
diff changeset
   111
        self._index = 0
3c91cc0c5fde httppeer: add support for httppostargs when we're sending a file
Augie Fackler <augie@google.com>
parents: 33806
diff changeset
   112
3c91cc0c5fde httppeer: add support for httppostargs when we're sending a file
Augie Fackler <augie@google.com>
parents: 33806
diff changeset
   113
    @property
3c91cc0c5fde httppeer: add support for httppostargs when we're sending a file
Augie Fackler <augie@google.com>
parents: 33806
diff changeset
   114
    def length(self):
3c91cc0c5fde httppeer: add support for httppostargs when we're sending a file
Augie Fackler <augie@google.com>
parents: 33806
diff changeset
   115
        return sum(f.length for f in self._fileobjs)
3c91cc0c5fde httppeer: add support for httppostargs when we're sending a file
Augie Fackler <augie@google.com>
parents: 33806
diff changeset
   116
3c91cc0c5fde httppeer: add support for httppostargs when we're sending a file
Augie Fackler <augie@google.com>
parents: 33806
diff changeset
   117
    def read(self, amt=None):
3c91cc0c5fde httppeer: add support for httppostargs when we're sending a file
Augie Fackler <augie@google.com>
parents: 33806
diff changeset
   118
        if amt <= 0:
3c91cc0c5fde httppeer: add support for httppostargs when we're sending a file
Augie Fackler <augie@google.com>
parents: 33806
diff changeset
   119
            return ''.join(f.read() for f in self._fileobjs)
3c91cc0c5fde httppeer: add support for httppostargs when we're sending a file
Augie Fackler <augie@google.com>
parents: 33806
diff changeset
   120
        parts = []
3c91cc0c5fde httppeer: add support for httppostargs when we're sending a file
Augie Fackler <augie@google.com>
parents: 33806
diff changeset
   121
        while amt and self._index < len(self._fileobjs):
3c91cc0c5fde httppeer: add support for httppostargs when we're sending a file
Augie Fackler <augie@google.com>
parents: 33806
diff changeset
   122
            parts.append(self._fileobjs[self._index].read(amt))
3c91cc0c5fde httppeer: add support for httppostargs when we're sending a file
Augie Fackler <augie@google.com>
parents: 33806
diff changeset
   123
            got = len(parts[-1])
3c91cc0c5fde httppeer: add support for httppostargs when we're sending a file
Augie Fackler <augie@google.com>
parents: 33806
diff changeset
   124
            if got < amt:
3c91cc0c5fde httppeer: add support for httppostargs when we're sending a file
Augie Fackler <augie@google.com>
parents: 33806
diff changeset
   125
                self._index += 1
3c91cc0c5fde httppeer: add support for httppostargs when we're sending a file
Augie Fackler <augie@google.com>
parents: 33806
diff changeset
   126
            amt -= got
3c91cc0c5fde httppeer: add support for httppostargs when we're sending a file
Augie Fackler <augie@google.com>
parents: 33806
diff changeset
   127
        return ''.join(parts)
3c91cc0c5fde httppeer: add support for httppostargs when we're sending a file
Augie Fackler <augie@google.com>
parents: 33806
diff changeset
   128
3c91cc0c5fde httppeer: add support for httppostargs when we're sending a file
Augie Fackler <augie@google.com>
parents: 33806
diff changeset
   129
    def seek(self, offset, whence=os.SEEK_SET):
3c91cc0c5fde httppeer: add support for httppostargs when we're sending a file
Augie Fackler <augie@google.com>
parents: 33806
diff changeset
   130
        if whence != os.SEEK_SET:
3c91cc0c5fde httppeer: add support for httppostargs when we're sending a file
Augie Fackler <augie@google.com>
parents: 33806
diff changeset
   131
            raise NotImplementedError(
3c91cc0c5fde httppeer: add support for httppostargs when we're sending a file
Augie Fackler <augie@google.com>
parents: 33806
diff changeset
   132
                '_multifile does not support anything other'
3c91cc0c5fde httppeer: add support for httppostargs when we're sending a file
Augie Fackler <augie@google.com>
parents: 33806
diff changeset
   133
                ' than os.SEEK_SET for whence on seek()')
3c91cc0c5fde httppeer: add support for httppostargs when we're sending a file
Augie Fackler <augie@google.com>
parents: 33806
diff changeset
   134
        if offset != 0:
3c91cc0c5fde httppeer: add support for httppostargs when we're sending a file
Augie Fackler <augie@google.com>
parents: 33806
diff changeset
   135
            raise NotImplementedError(
3c91cc0c5fde httppeer: add support for httppostargs when we're sending a file
Augie Fackler <augie@google.com>
parents: 33806
diff changeset
   136
                '_multifile only supports seeking to start, but that '
3c91cc0c5fde httppeer: add support for httppostargs when we're sending a file
Augie Fackler <augie@google.com>
parents: 33806
diff changeset
   137
                'could be fixed if you need it')
3c91cc0c5fde httppeer: add support for httppostargs when we're sending a file
Augie Fackler <augie@google.com>
parents: 33806
diff changeset
   138
        for f in self._fileobjs:
3c91cc0c5fde httppeer: add support for httppostargs when we're sending a file
Augie Fackler <augie@google.com>
parents: 33806
diff changeset
   139
            f.seek(0)
3c91cc0c5fde httppeer: add support for httppostargs when we're sending a file
Augie Fackler <augie@google.com>
parents: 33806
diff changeset
   140
        self._index = 0
3c91cc0c5fde httppeer: add support for httppostargs when we're sending a file
Augie Fackler <augie@google.com>
parents: 33806
diff changeset
   141
37549
66d1001e1500 httppeer: extract code for creating a request into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37548
diff changeset
   142
def makev1commandrequest(ui, requestbuilder, caps, capablefn,
66d1001e1500 httppeer: extract code for creating a request into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37548
diff changeset
   143
                         repobaseurl, cmd, args):
66d1001e1500 httppeer: extract code for creating a request into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37548
diff changeset
   144
    """Make an HTTP request to run a command for a version 1 client.
66d1001e1500 httppeer: extract code for creating a request into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37548
diff changeset
   145
66d1001e1500 httppeer: extract code for creating a request into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37548
diff changeset
   146
    ``caps`` is a set of known server capabilities. The value may be
66d1001e1500 httppeer: extract code for creating a request into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37548
diff changeset
   147
    None if capabilities are not yet known.
66d1001e1500 httppeer: extract code for creating a request into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37548
diff changeset
   148
66d1001e1500 httppeer: extract code for creating a request into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37548
diff changeset
   149
    ``capablefn`` is a function to evaluate a capability.
66d1001e1500 httppeer: extract code for creating a request into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37548
diff changeset
   150
66d1001e1500 httppeer: extract code for creating a request into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37548
diff changeset
   151
    ``cmd``, ``args``, and ``data`` define the command, its arguments, and
66d1001e1500 httppeer: extract code for creating a request into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37548
diff changeset
   152
    raw data to pass to it.
66d1001e1500 httppeer: extract code for creating a request into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37548
diff changeset
   153
    """
66d1001e1500 httppeer: extract code for creating a request into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37548
diff changeset
   154
    if cmd == 'pushkey':
66d1001e1500 httppeer: extract code for creating a request into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37548
diff changeset
   155
        args['data'] = ''
66d1001e1500 httppeer: extract code for creating a request into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37548
diff changeset
   156
    data = args.pop('data', None)
66d1001e1500 httppeer: extract code for creating a request into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37548
diff changeset
   157
    headers = args.pop('headers', {})
66d1001e1500 httppeer: extract code for creating a request into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37548
diff changeset
   158
66d1001e1500 httppeer: extract code for creating a request into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37548
diff changeset
   159
    ui.debug("sending %s command\n" % cmd)
66d1001e1500 httppeer: extract code for creating a request into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37548
diff changeset
   160
    q = [('cmd', cmd)]
66d1001e1500 httppeer: extract code for creating a request into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37548
diff changeset
   161
    headersize = 0
66d1001e1500 httppeer: extract code for creating a request into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37548
diff changeset
   162
    # Important: don't use self.capable() here or else you end up
66d1001e1500 httppeer: extract code for creating a request into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37548
diff changeset
   163
    # with infinite recursion when trying to look up capabilities
66d1001e1500 httppeer: extract code for creating a request into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37548
diff changeset
   164
    # for the first time.
66d1001e1500 httppeer: extract code for creating a request into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37548
diff changeset
   165
    postargsok = caps is not None and 'httppostargs' in caps
66d1001e1500 httppeer: extract code for creating a request into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37548
diff changeset
   166
66d1001e1500 httppeer: extract code for creating a request into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37548
diff changeset
   167
    # Send arguments via POST.
66d1001e1500 httppeer: extract code for creating a request into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37548
diff changeset
   168
    if postargsok and args:
66d1001e1500 httppeer: extract code for creating a request into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37548
diff changeset
   169
        strargs = urlreq.urlencode(sorted(args.items()))
66d1001e1500 httppeer: extract code for creating a request into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37548
diff changeset
   170
        if not data:
66d1001e1500 httppeer: extract code for creating a request into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37548
diff changeset
   171
            data = strargs
66d1001e1500 httppeer: extract code for creating a request into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37548
diff changeset
   172
        else:
66d1001e1500 httppeer: extract code for creating a request into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37548
diff changeset
   173
            if isinstance(data, bytes):
66d1001e1500 httppeer: extract code for creating a request into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37548
diff changeset
   174
                i = io.BytesIO(data)
66d1001e1500 httppeer: extract code for creating a request into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37548
diff changeset
   175
                i.length = len(data)
66d1001e1500 httppeer: extract code for creating a request into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37548
diff changeset
   176
                data = i
66d1001e1500 httppeer: extract code for creating a request into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37548
diff changeset
   177
            argsio = io.BytesIO(strargs)
66d1001e1500 httppeer: extract code for creating a request into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37548
diff changeset
   178
            argsio.length = len(strargs)
66d1001e1500 httppeer: extract code for creating a request into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37548
diff changeset
   179
            data = _multifile(argsio, data)
66d1001e1500 httppeer: extract code for creating a request into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37548
diff changeset
   180
        headers[r'X-HgArgs-Post'] = len(strargs)
66d1001e1500 httppeer: extract code for creating a request into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37548
diff changeset
   181
    elif args:
66d1001e1500 httppeer: extract code for creating a request into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37548
diff changeset
   182
        # Calling self.capable() can infinite loop if we are calling
66d1001e1500 httppeer: extract code for creating a request into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37548
diff changeset
   183
        # "capabilities". But that command should never accept wire
66d1001e1500 httppeer: extract code for creating a request into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37548
diff changeset
   184
        # protocol arguments. So this should never happen.
66d1001e1500 httppeer: extract code for creating a request into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37548
diff changeset
   185
        assert cmd != 'capabilities'
66d1001e1500 httppeer: extract code for creating a request into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37548
diff changeset
   186
        httpheader = capablefn('httpheader')
66d1001e1500 httppeer: extract code for creating a request into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37548
diff changeset
   187
        if httpheader:
66d1001e1500 httppeer: extract code for creating a request into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37548
diff changeset
   188
            headersize = int(httpheader.split(',', 1)[0])
66d1001e1500 httppeer: extract code for creating a request into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37548
diff changeset
   189
66d1001e1500 httppeer: extract code for creating a request into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37548
diff changeset
   190
        # Send arguments via HTTP headers.
66d1001e1500 httppeer: extract code for creating a request into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37548
diff changeset
   191
        if headersize > 0:
66d1001e1500 httppeer: extract code for creating a request into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37548
diff changeset
   192
            # The headers can typically carry more data than the URL.
66d1001e1500 httppeer: extract code for creating a request into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37548
diff changeset
   193
            encargs = urlreq.urlencode(sorted(args.items()))
66d1001e1500 httppeer: extract code for creating a request into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37548
diff changeset
   194
            for header, value in encodevalueinheaders(encargs, 'X-HgArg',
66d1001e1500 httppeer: extract code for creating a request into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37548
diff changeset
   195
                                                      headersize):
66d1001e1500 httppeer: extract code for creating a request into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37548
diff changeset
   196
                headers[header] = value
66d1001e1500 httppeer: extract code for creating a request into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37548
diff changeset
   197
        # Send arguments via query string (Mercurial <1.9).
66d1001e1500 httppeer: extract code for creating a request into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37548
diff changeset
   198
        else:
66d1001e1500 httppeer: extract code for creating a request into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37548
diff changeset
   199
            q += sorted(args.items())
66d1001e1500 httppeer: extract code for creating a request into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37548
diff changeset
   200
66d1001e1500 httppeer: extract code for creating a request into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37548
diff changeset
   201
    qs = '?%s' % urlreq.urlencode(q)
66d1001e1500 httppeer: extract code for creating a request into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37548
diff changeset
   202
    cu = "%s%s" % (repobaseurl, qs)
66d1001e1500 httppeer: extract code for creating a request into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37548
diff changeset
   203
    size = 0
66d1001e1500 httppeer: extract code for creating a request into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37548
diff changeset
   204
    if util.safehasattr(data, 'length'):
66d1001e1500 httppeer: extract code for creating a request into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37548
diff changeset
   205
        size = data.length
66d1001e1500 httppeer: extract code for creating a request into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37548
diff changeset
   206
    elif data is not None:
66d1001e1500 httppeer: extract code for creating a request into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37548
diff changeset
   207
        size = len(data)
66d1001e1500 httppeer: extract code for creating a request into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37548
diff changeset
   208
    if data is not None and r'Content-Type' not in headers:
66d1001e1500 httppeer: extract code for creating a request into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37548
diff changeset
   209
        headers[r'Content-Type'] = r'application/mercurial-0.1'
66d1001e1500 httppeer: extract code for creating a request into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37548
diff changeset
   210
66d1001e1500 httppeer: extract code for creating a request into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37548
diff changeset
   211
    # Tell the server we accept application/mercurial-0.2 and multiple
66d1001e1500 httppeer: extract code for creating a request into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37548
diff changeset
   212
    # compression formats if the server is capable of emitting those
66d1001e1500 httppeer: extract code for creating a request into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37548
diff changeset
   213
    # payloads.
37556
b77aa48ba690 httppeer: only advertise partial-pull if capabilities are known
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37555
diff changeset
   214
    # Note: Keep this set empty by default, as client advertisement of
b77aa48ba690 httppeer: only advertise partial-pull if capabilities are known
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37555
diff changeset
   215
    # protocol parameters should only occur after the handshake.
b77aa48ba690 httppeer: only advertise partial-pull if capabilities are known
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37555
diff changeset
   216
    protoparams = set()
37549
66d1001e1500 httppeer: extract code for creating a request into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37548
diff changeset
   217
66d1001e1500 httppeer: extract code for creating a request into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37548
diff changeset
   218
    mediatypes = set()
66d1001e1500 httppeer: extract code for creating a request into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37548
diff changeset
   219
    if caps is not None:
66d1001e1500 httppeer: extract code for creating a request into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37548
diff changeset
   220
        mt = capablefn('httpmediatype')
66d1001e1500 httppeer: extract code for creating a request into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37548
diff changeset
   221
        if mt:
66d1001e1500 httppeer: extract code for creating a request into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37548
diff changeset
   222
            protoparams.add('0.1')
66d1001e1500 httppeer: extract code for creating a request into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37548
diff changeset
   223
            mediatypes = set(mt.split(','))
66d1001e1500 httppeer: extract code for creating a request into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37548
diff changeset
   224
37556
b77aa48ba690 httppeer: only advertise partial-pull if capabilities are known
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37555
diff changeset
   225
        protoparams.add('partial-pull')
b77aa48ba690 httppeer: only advertise partial-pull if capabilities are known
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37555
diff changeset
   226
37549
66d1001e1500 httppeer: extract code for creating a request into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37548
diff changeset
   227
    if '0.2tx' in mediatypes:
66d1001e1500 httppeer: extract code for creating a request into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37548
diff changeset
   228
        protoparams.add('0.2')
66d1001e1500 httppeer: extract code for creating a request into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37548
diff changeset
   229
66d1001e1500 httppeer: extract code for creating a request into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37548
diff changeset
   230
    if '0.2tx' in mediatypes and capablefn('compression'):
66d1001e1500 httppeer: extract code for creating a request into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37548
diff changeset
   231
        # We /could/ compare supported compression formats and prune
66d1001e1500 httppeer: extract code for creating a request into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37548
diff changeset
   232
        # non-mutually supported or error if nothing is mutually supported.
66d1001e1500 httppeer: extract code for creating a request into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37548
diff changeset
   233
        # For now, send the full list to the server and have it error.
66d1001e1500 httppeer: extract code for creating a request into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37548
diff changeset
   234
        comps = [e.wireprotosupport().name for e in
66d1001e1500 httppeer: extract code for creating a request into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37548
diff changeset
   235
                 util.compengines.supportedwireengines(util.CLIENTROLE)]
66d1001e1500 httppeer: extract code for creating a request into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37548
diff changeset
   236
        protoparams.add('comp=%s' % ','.join(comps))
66d1001e1500 httppeer: extract code for creating a request into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37548
diff changeset
   237
66d1001e1500 httppeer: extract code for creating a request into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37548
diff changeset
   238
    if protoparams:
66d1001e1500 httppeer: extract code for creating a request into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37548
diff changeset
   239
        protoheaders = encodevalueinheaders(' '.join(sorted(protoparams)),
66d1001e1500 httppeer: extract code for creating a request into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37548
diff changeset
   240
                                            'X-HgProto',
66d1001e1500 httppeer: extract code for creating a request into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37548
diff changeset
   241
                                            headersize or 1024)
66d1001e1500 httppeer: extract code for creating a request into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37548
diff changeset
   242
        for header, value in protoheaders:
66d1001e1500 httppeer: extract code for creating a request into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37548
diff changeset
   243
            headers[header] = value
37555
930c433eb311 httppeer: always add x-hg* headers to Vary header
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37554
diff changeset
   244
930c433eb311 httppeer: always add x-hg* headers to Vary header
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37554
diff changeset
   245
    varyheaders = []
930c433eb311 httppeer: always add x-hg* headers to Vary header
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37554
diff changeset
   246
    for header in headers:
930c433eb311 httppeer: always add x-hg* headers to Vary header
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37554
diff changeset
   247
        if header.lower().startswith(r'x-hg'):
37549
66d1001e1500 httppeer: extract code for creating a request into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37548
diff changeset
   248
            varyheaders.append(header)
66d1001e1500 httppeer: extract code for creating a request into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37548
diff changeset
   249
66d1001e1500 httppeer: extract code for creating a request into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37548
diff changeset
   250
    if varyheaders:
37555
930c433eb311 httppeer: always add x-hg* headers to Vary header
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37554
diff changeset
   251
        headers[r'Vary'] = r','.join(sorted(varyheaders))
37549
66d1001e1500 httppeer: extract code for creating a request into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37548
diff changeset
   252
66d1001e1500 httppeer: extract code for creating a request into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37548
diff changeset
   253
    req = requestbuilder(pycompat.strurl(cu), data, headers)
66d1001e1500 httppeer: extract code for creating a request into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37548
diff changeset
   254
66d1001e1500 httppeer: extract code for creating a request into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37548
diff changeset
   255
    if data is not None:
66d1001e1500 httppeer: extract code for creating a request into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37548
diff changeset
   256
        ui.debug("sending %d bytes\n" % size)
66d1001e1500 httppeer: extract code for creating a request into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37548
diff changeset
   257
        req.add_unredirected_header(r'Content-Length', r'%d' % size)
66d1001e1500 httppeer: extract code for creating a request into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37548
diff changeset
   258
66d1001e1500 httppeer: extract code for creating a request into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37548
diff changeset
   259
    return req, cu, qs
66d1001e1500 httppeer: extract code for creating a request into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37548
diff changeset
   260
37548
8e7a4435ab6d httppeer: extract code for performing an HTTP request
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37547
diff changeset
   261
def sendrequest(ui, opener, req):
8e7a4435ab6d httppeer: extract code for performing an HTTP request
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37547
diff changeset
   262
    """Send a prepared HTTP request.
8e7a4435ab6d httppeer: extract code for performing an HTTP request
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37547
diff changeset
   263
8e7a4435ab6d httppeer: extract code for performing an HTTP request
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37547
diff changeset
   264
    Returns the response object.
8e7a4435ab6d httppeer: extract code for performing an HTTP request
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37547
diff changeset
   265
    """
8e7a4435ab6d httppeer: extract code for performing an HTTP request
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37547
diff changeset
   266
    if (ui.debugflag
8e7a4435ab6d httppeer: extract code for performing an HTTP request
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37547
diff changeset
   267
        and ui.configbool('devel', 'debug.peer-request')):
8e7a4435ab6d httppeer: extract code for performing an HTTP request
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37547
diff changeset
   268
        dbg = ui.debug
8e7a4435ab6d httppeer: extract code for performing an HTTP request
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37547
diff changeset
   269
        line = 'devel-peer-request: %s\n'
8e7a4435ab6d httppeer: extract code for performing an HTTP request
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37547
diff changeset
   270
        dbg(line % '%s %s' % (req.get_method(), req.get_full_url()))
8e7a4435ab6d httppeer: extract code for performing an HTTP request
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37547
diff changeset
   271
        hgargssize = None
8e7a4435ab6d httppeer: extract code for performing an HTTP request
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37547
diff changeset
   272
8e7a4435ab6d httppeer: extract code for performing an HTTP request
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37547
diff changeset
   273
        for header, value in sorted(req.header_items()):
8e7a4435ab6d httppeer: extract code for performing an HTTP request
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37547
diff changeset
   274
            if header.startswith('X-hgarg-'):
8e7a4435ab6d httppeer: extract code for performing an HTTP request
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37547
diff changeset
   275
                if hgargssize is None:
8e7a4435ab6d httppeer: extract code for performing an HTTP request
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37547
diff changeset
   276
                    hgargssize = 0
8e7a4435ab6d httppeer: extract code for performing an HTTP request
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37547
diff changeset
   277
                hgargssize += len(value)
8e7a4435ab6d httppeer: extract code for performing an HTTP request
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37547
diff changeset
   278
            else:
8e7a4435ab6d httppeer: extract code for performing an HTTP request
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37547
diff changeset
   279
                dbg(line % '  %s %s' % (header, value))
8e7a4435ab6d httppeer: extract code for performing an HTTP request
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37547
diff changeset
   280
8e7a4435ab6d httppeer: extract code for performing an HTTP request
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37547
diff changeset
   281
        if hgargssize is not None:
8e7a4435ab6d httppeer: extract code for performing an HTTP request
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37547
diff changeset
   282
            dbg(line % '  %d bytes of commands arguments in headers'
8e7a4435ab6d httppeer: extract code for performing an HTTP request
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37547
diff changeset
   283
                % hgargssize)
8e7a4435ab6d httppeer: extract code for performing an HTTP request
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37547
diff changeset
   284
8e7a4435ab6d httppeer: extract code for performing an HTTP request
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37547
diff changeset
   285
        if req.has_data():
8e7a4435ab6d httppeer: extract code for performing an HTTP request
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37547
diff changeset
   286
            data = req.get_data()
8e7a4435ab6d httppeer: extract code for performing an HTTP request
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37547
diff changeset
   287
            length = getattr(data, 'length', None)
8e7a4435ab6d httppeer: extract code for performing an HTTP request
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37547
diff changeset
   288
            if length is None:
8e7a4435ab6d httppeer: extract code for performing an HTTP request
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37547
diff changeset
   289
                length = len(data)
8e7a4435ab6d httppeer: extract code for performing an HTTP request
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37547
diff changeset
   290
            dbg(line % '  %d bytes of data' % length)
8e7a4435ab6d httppeer: extract code for performing an HTTP request
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37547
diff changeset
   291
8e7a4435ab6d httppeer: extract code for performing an HTTP request
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37547
diff changeset
   292
        start = util.timer()
8e7a4435ab6d httppeer: extract code for performing an HTTP request
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37547
diff changeset
   293
37550
b5862ee01abe httppeer: move error handling and response wrapping into sendrequest
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37549
diff changeset
   294
    try:
b5862ee01abe httppeer: move error handling and response wrapping into sendrequest
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37549
diff changeset
   295
        res = opener.open(req)
b5862ee01abe httppeer: move error handling and response wrapping into sendrequest
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37549
diff changeset
   296
    except urlerr.httperror as inst:
b5862ee01abe httppeer: move error handling and response wrapping into sendrequest
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37549
diff changeset
   297
        if inst.code == 401:
b5862ee01abe httppeer: move error handling and response wrapping into sendrequest
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37549
diff changeset
   298
            raise error.Abort(_('authorization failed'))
b5862ee01abe httppeer: move error handling and response wrapping into sendrequest
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37549
diff changeset
   299
        raise
b5862ee01abe httppeer: move error handling and response wrapping into sendrequest
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37549
diff changeset
   300
    except httplib.HTTPException as inst:
b5862ee01abe httppeer: move error handling and response wrapping into sendrequest
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37549
diff changeset
   301
        ui.debug('http error requesting %s\n' %
b5862ee01abe httppeer: move error handling and response wrapping into sendrequest
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37549
diff changeset
   302
                 util.hidepassword(req.get_full_url()))
b5862ee01abe httppeer: move error handling and response wrapping into sendrequest
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37549
diff changeset
   303
        ui.traceback()
b5862ee01abe httppeer: move error handling and response wrapping into sendrequest
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37549
diff changeset
   304
        raise IOError(None, inst)
b5862ee01abe httppeer: move error handling and response wrapping into sendrequest
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37549
diff changeset
   305
    finally:
b5862ee01abe httppeer: move error handling and response wrapping into sendrequest
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37549
diff changeset
   306
        if ui.configbool('devel', 'debug.peer-request'):
b5862ee01abe httppeer: move error handling and response wrapping into sendrequest
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37549
diff changeset
   307
            dbg(line % '  finished in %.4f seconds (%s)'
b5862ee01abe httppeer: move error handling and response wrapping into sendrequest
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37549
diff changeset
   308
                % (util.timer() - start, res.code))
b5862ee01abe httppeer: move error handling and response wrapping into sendrequest
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37549
diff changeset
   309
b5862ee01abe httppeer: move error handling and response wrapping into sendrequest
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37549
diff changeset
   310
    # Insert error handlers for common I/O failures.
b5862ee01abe httppeer: move error handling and response wrapping into sendrequest
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37549
diff changeset
   311
    _wraphttpresponse(res)
37548
8e7a4435ab6d httppeer: extract code for performing an HTTP request
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37547
diff changeset
   312
8e7a4435ab6d httppeer: extract code for performing an HTTP request
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37547
diff changeset
   313
    return res
8e7a4435ab6d httppeer: extract code for performing an HTTP request
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37547
diff changeset
   314
37558
8a73132214a3 httppeer: support protocol upgrade
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37556
diff changeset
   315
def parsev1commandresponse(ui, baseurl, requrl, qs, resp, compressible,
8a73132214a3 httppeer: support protocol upgrade
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37556
diff changeset
   316
                           allowcbor=False):
37551
946eb204ba67 httppeer: extract common response handling into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37550
diff changeset
   317
    # record the url we got redirected to
946eb204ba67 httppeer: extract common response handling into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37550
diff changeset
   318
    respurl = pycompat.bytesurl(resp.geturl())
946eb204ba67 httppeer: extract common response handling into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37550
diff changeset
   319
    if respurl.endswith(qs):
946eb204ba67 httppeer: extract common response handling into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37550
diff changeset
   320
        respurl = respurl[:-len(qs)]
946eb204ba67 httppeer: extract common response handling into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37550
diff changeset
   321
    if baseurl.rstrip('/') != respurl.rstrip('/'):
946eb204ba67 httppeer: extract common response handling into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37550
diff changeset
   322
        if not ui.quiet:
946eb204ba67 httppeer: extract common response handling into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37550
diff changeset
   323
            ui.warn(_('real URL is %s\n') % respurl)
946eb204ba67 httppeer: extract common response handling into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37550
diff changeset
   324
946eb204ba67 httppeer: extract common response handling into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37550
diff changeset
   325
    try:
946eb204ba67 httppeer: extract common response handling into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37550
diff changeset
   326
        proto = pycompat.bytesurl(resp.getheader(r'content-type', r''))
946eb204ba67 httppeer: extract common response handling into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37550
diff changeset
   327
    except AttributeError:
946eb204ba67 httppeer: extract common response handling into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37550
diff changeset
   328
        proto = pycompat.bytesurl(resp.headers.get(r'content-type', r''))
946eb204ba67 httppeer: extract common response handling into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37550
diff changeset
   329
946eb204ba67 httppeer: extract common response handling into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37550
diff changeset
   330
    safeurl = util.hidepassword(baseurl)
946eb204ba67 httppeer: extract common response handling into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37550
diff changeset
   331
    if proto.startswith('application/hg-error'):
946eb204ba67 httppeer: extract common response handling into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37550
diff changeset
   332
        raise error.OutOfBandError(resp.read())
37554
301a1d2e8016 httppeer: don't accept very old media types (BC)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37553
diff changeset
   333
301a1d2e8016 httppeer: don't accept very old media types (BC)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37553
diff changeset
   334
    # Pre 1.0 versions of Mercurial used text/plain and
301a1d2e8016 httppeer: don't accept very old media types (BC)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37553
diff changeset
   335
    # application/hg-changegroup. We don't support such old servers.
301a1d2e8016 httppeer: don't accept very old media types (BC)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37553
diff changeset
   336
    if not proto.startswith('application/mercurial-'):
37551
946eb204ba67 httppeer: extract common response handling into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37550
diff changeset
   337
        ui.debug("requested URL: '%s'\n" % util.hidepassword(requrl))
946eb204ba67 httppeer: extract common response handling into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37550
diff changeset
   338
        raise error.RepoError(
946eb204ba67 httppeer: extract common response handling into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37550
diff changeset
   339
            _("'%s' does not appear to be an hg repository:\n"
946eb204ba67 httppeer: extract common response handling into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37550
diff changeset
   340
              "---%%<--- (%s)\n%s\n---%%<---\n")
946eb204ba67 httppeer: extract common response handling into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37550
diff changeset
   341
            % (safeurl, proto or 'no content-type', resp.read(1024)))
946eb204ba67 httppeer: extract common response handling into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37550
diff changeset
   342
37554
301a1d2e8016 httppeer: don't accept very old media types (BC)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37553
diff changeset
   343
    try:
37558
8a73132214a3 httppeer: support protocol upgrade
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37556
diff changeset
   344
        subtype = proto.split('-', 1)[1]
8a73132214a3 httppeer: support protocol upgrade
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37556
diff changeset
   345
8a73132214a3 httppeer: support protocol upgrade
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37556
diff changeset
   346
        # Unless we end up supporting CBOR in the legacy wire protocol,
8a73132214a3 httppeer: support protocol upgrade
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37556
diff changeset
   347
        # this should ONLY be encountered for the initial capabilities
8a73132214a3 httppeer: support protocol upgrade
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37556
diff changeset
   348
        # request during handshake.
8a73132214a3 httppeer: support protocol upgrade
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37556
diff changeset
   349
        if subtype == 'cbor':
8a73132214a3 httppeer: support protocol upgrade
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37556
diff changeset
   350
            if allowcbor:
8a73132214a3 httppeer: support protocol upgrade
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37556
diff changeset
   351
                return respurl, proto, resp
8a73132214a3 httppeer: support protocol upgrade
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37556
diff changeset
   352
            else:
8a73132214a3 httppeer: support protocol upgrade
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37556
diff changeset
   353
                raise error.RepoError(_('unexpected CBOR response from '
8a73132214a3 httppeer: support protocol upgrade
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37556
diff changeset
   354
                                        'server'))
8a73132214a3 httppeer: support protocol upgrade
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37556
diff changeset
   355
8a73132214a3 httppeer: support protocol upgrade
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37556
diff changeset
   356
        version_info = tuple([int(n) for n in subtype.split('.')])
37554
301a1d2e8016 httppeer: don't accept very old media types (BC)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37553
diff changeset
   357
    except ValueError:
301a1d2e8016 httppeer: don't accept very old media types (BC)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37553
diff changeset
   358
        raise error.RepoError(_("'%s' sent a broken Content-Type "
301a1d2e8016 httppeer: don't accept very old media types (BC)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37553
diff changeset
   359
                                "header (%s)") % (safeurl, proto))
37551
946eb204ba67 httppeer: extract common response handling into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37550
diff changeset
   360
37554
301a1d2e8016 httppeer: don't accept very old media types (BC)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37553
diff changeset
   361
    # TODO consider switching to a decompression reader that uses
301a1d2e8016 httppeer: don't accept very old media types (BC)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37553
diff changeset
   362
    # generators.
301a1d2e8016 httppeer: don't accept very old media types (BC)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37553
diff changeset
   363
    if version_info == (0, 1):
301a1d2e8016 httppeer: don't accept very old media types (BC)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37553
diff changeset
   364
        if compressible:
301a1d2e8016 httppeer: don't accept very old media types (BC)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37553
diff changeset
   365
            resp = util.compengines['zlib'].decompressorreader(resp)
37551
946eb204ba67 httppeer: extract common response handling into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37550
diff changeset
   366
37554
301a1d2e8016 httppeer: don't accept very old media types (BC)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37553
diff changeset
   367
    elif version_info == (0, 2):
301a1d2e8016 httppeer: don't accept very old media types (BC)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37553
diff changeset
   368
        # application/mercurial-0.2 always identifies the compression
301a1d2e8016 httppeer: don't accept very old media types (BC)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37553
diff changeset
   369
        # engine in the payload header.
301a1d2e8016 httppeer: don't accept very old media types (BC)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37553
diff changeset
   370
        elen = struct.unpack('B', resp.read(1))[0]
301a1d2e8016 httppeer: don't accept very old media types (BC)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37553
diff changeset
   371
        ename = resp.read(elen)
301a1d2e8016 httppeer: don't accept very old media types (BC)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37553
diff changeset
   372
        engine = util.compengines.forwiretype(ename)
37551
946eb204ba67 httppeer: extract common response handling into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37550
diff changeset
   373
37554
301a1d2e8016 httppeer: don't accept very old media types (BC)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37553
diff changeset
   374
        resp = engine.decompressorreader(resp)
301a1d2e8016 httppeer: don't accept very old media types (BC)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37553
diff changeset
   375
    else:
301a1d2e8016 httppeer: don't accept very old media types (BC)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37553
diff changeset
   376
        raise error.RepoError(_("'%s' uses newer protocol %s") %
37558
8a73132214a3 httppeer: support protocol upgrade
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37556
diff changeset
   377
                              (safeurl, subtype))
37551
946eb204ba67 httppeer: extract common response handling into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37550
diff changeset
   378
37558
8a73132214a3 httppeer: support protocol upgrade
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37556
diff changeset
   379
    return respurl, proto, resp
37551
946eb204ba67 httppeer: extract common response handling into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37550
diff changeset
   380
33806
dedab036215d wireproto: use new peer interface
Gregory Szorc <gregory.szorc@gmail.com>
parents: 33805
diff changeset
   381
class httppeer(wireproto.wirepeer):
37552
8b8a845c85fc httppeer: perform capabilities request in makepeer()
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37551
diff changeset
   382
    def __init__(self, ui, path, url, opener, requestbuilder, caps):
37321
e826fe7a08c7 peer: make ui an attribute
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37320
diff changeset
   383
        self.ui = ui
33705
73fd395ee29e httppeer: make several instance attributes internal (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 33704
diff changeset
   384
        self._path = path
37006
8e89c2bec1f7 httppeer: refactor how httppeer is created (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36961
diff changeset
   385
        self._url = url
37552
8b8a845c85fc httppeer: perform capabilities request in makepeer()
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37551
diff changeset
   386
        self._caps = caps
37006
8e89c2bec1f7 httppeer: refactor how httppeer is created (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36961
diff changeset
   387
        self._urlopener = opener
37547
835ccc2a5ef1 httppeer: move requestbuilder defaults into makepeer() argument
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37546
diff changeset
   388
        self._requestbuilder = requestbuilder
4516
96d8a56d4ef9 Removed trailing whitespace and tabs from python files
Thomas Arendsen Hein <thomas@intevation.de>
parents: 4369
diff changeset
   389
7752
998fc8f62539 close sockets on httprepository deletion (issue1487)
Steve Borho <steve@borho.org>
parents: 7641
diff changeset
   390
    def __del__(self):
37006
8e89c2bec1f7 httppeer: refactor how httppeer is created (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36961
diff changeset
   391
        for h in self._urlopener.handlers:
8e89c2bec1f7 httppeer: refactor how httppeer is created (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36961
diff changeset
   392
            h.close()
8e89c2bec1f7 httppeer: refactor how httppeer is created (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36961
diff changeset
   393
            getattr(h, "close_all", lambda: None)()
7752
998fc8f62539 close sockets on httprepository deletion (issue1487)
Steve Borho <steve@borho.org>
parents: 7641
diff changeset
   394
37320
39f7d4ee8bcd repository: port peer interfaces to zope.interface
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37006
diff changeset
   395
    # Begin of ipeerconnection interface.
33805
f913e90f15a0 httppeer: use peer interface
Gregory Szorc <gregory.szorc@gmail.com>
parents: 33705
diff changeset
   396
2673
109a22f5434a hooks: add url to changegroup, incoming, prechangegroup, pretxnchangegroup hooks
Vadim Gelfer <vadim.gelfer@gmail.com>
parents: 2612
diff changeset
   397
    def url(self):
33705
73fd395ee29e httppeer: make several instance attributes internal (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 33704
diff changeset
   398
        return self._path
2673
109a22f5434a hooks: add url to changegroup, incoming, prechangegroup, pretxnchangegroup hooks
Vadim Gelfer <vadim.gelfer@gmail.com>
parents: 2612
diff changeset
   399
33805
f913e90f15a0 httppeer: use peer interface
Gregory Szorc <gregory.szorc@gmail.com>
parents: 33705
diff changeset
   400
    def local(self):
f913e90f15a0 httppeer: use peer interface
Gregory Szorc <gregory.szorc@gmail.com>
parents: 33705
diff changeset
   401
        return None
f913e90f15a0 httppeer: use peer interface
Gregory Szorc <gregory.szorc@gmail.com>
parents: 33705
diff changeset
   402
f913e90f15a0 httppeer: use peer interface
Gregory Szorc <gregory.szorc@gmail.com>
parents: 33705
diff changeset
   403
    def peer(self):
f913e90f15a0 httppeer: use peer interface
Gregory Szorc <gregory.szorc@gmail.com>
parents: 33705
diff changeset
   404
        return self
f913e90f15a0 httppeer: use peer interface
Gregory Szorc <gregory.szorc@gmail.com>
parents: 33705
diff changeset
   405
f913e90f15a0 httppeer: use peer interface
Gregory Szorc <gregory.szorc@gmail.com>
parents: 33705
diff changeset
   406
    def canpush(self):
f913e90f15a0 httppeer: use peer interface
Gregory Szorc <gregory.szorc@gmail.com>
parents: 33705
diff changeset
   407
        return True
2442
c660691fb45d http: query server for capabilities
Vadim Gelfer <vadim.gelfer@gmail.com>
parents: 2439
diff changeset
   408
33805
f913e90f15a0 httppeer: use peer interface
Gregory Szorc <gregory.szorc@gmail.com>
parents: 33705
diff changeset
   409
    def close(self):
f913e90f15a0 httppeer: use peer interface
Gregory Szorc <gregory.szorc@gmail.com>
parents: 33705
diff changeset
   410
        pass
13603
395a84f78736 httprepo: use caps instead of between for compat check
Peter Arrenbrecht <peter.arrenbrecht@gmail.com>
parents: 13555
diff changeset
   411
37320
39f7d4ee8bcd repository: port peer interfaces to zope.interface
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37006
diff changeset
   412
    # End of ipeerconnection interface.
33805
f913e90f15a0 httppeer: use peer interface
Gregory Szorc <gregory.szorc@gmail.com>
parents: 33705
diff changeset
   413
37320
39f7d4ee8bcd repository: port peer interfaces to zope.interface
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37006
diff changeset
   414
    # Begin of ipeercommands interface.
33805
f913e90f15a0 httppeer: use peer interface
Gregory Szorc <gregory.szorc@gmail.com>
parents: 33705
diff changeset
   415
f913e90f15a0 httppeer: use peer interface
Gregory Szorc <gregory.szorc@gmail.com>
parents: 33705
diff changeset
   416
    def capabilities(self):
33705
73fd395ee29e httppeer: make several instance attributes internal (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 33704
diff changeset
   417
        return self._caps
2442
c660691fb45d http: query server for capabilities
Vadim Gelfer <vadim.gelfer@gmail.com>
parents: 2439
diff changeset
   418
37320
39f7d4ee8bcd repository: port peer interfaces to zope.interface
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37006
diff changeset
   419
    # End of ipeercommands interface.
33805
f913e90f15a0 httppeer: use peer interface
Gregory Szorc <gregory.szorc@gmail.com>
parents: 33705
diff changeset
   420
f913e90f15a0 httppeer: use peer interface
Gregory Szorc <gregory.szorc@gmail.com>
parents: 33705
diff changeset
   421
    # look up capabilities only when needed
f913e90f15a0 httppeer: use peer interface
Gregory Szorc <gregory.szorc@gmail.com>
parents: 33705
diff changeset
   422
30464
e16e234b9ca3 httppeer: do decompression inside _callstream
Gregory Szorc <gregory.szorc@gmail.com>
parents: 30241
diff changeset
   423
    def _callstream(self, cmd, _compressible=False, **args):
35359
98bc4c43f570 py3: handle keyword arguments correctly in httppeer.py
Pulkit Goyal <7895pulkit@gmail.com>
parents: 35197
diff changeset
   424
        args = pycompat.byteskwargs(args)
36218
e4ccd7a69f77 httppeer: change logic around argument handling
Gregory Szorc <gregory.szorc@gmail.com>
parents: 35884
diff changeset
   425
37549
66d1001e1500 httppeer: extract code for creating a request into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37548
diff changeset
   426
        req, cu, qs = makev1commandrequest(self.ui, self._requestbuilder,
66d1001e1500 httppeer: extract code for creating a request into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37548
diff changeset
   427
                                           self._caps, self.capable,
66d1001e1500 httppeer: extract code for creating a request into own function
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37548
diff changeset
   428
                                           self._url, cmd, args)
30763
a520aefb96f1 httppeer: advertise and support application/mercurial-0.2
Gregory Szorc <gregory.szorc@gmail.com>
parents: 30759
diff changeset
   429
37550
b5862ee01abe httppeer: move error handling and response wrapping into sendrequest
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37549
diff changeset
   430
        resp = sendrequest(self.ui, self._urlopener, req)
32002
bf855efe5664 httppeer: wrap HTTPResponse.read() globally
Gregory Szorc <gregory.szorc@gmail.com>
parents: 30924
diff changeset
   431
37558
8a73132214a3 httppeer: support protocol upgrade
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37556
diff changeset
   432
        self._url, ct, resp = parsev1commandresponse(self.ui, self._url, cu, qs,
8a73132214a3 httppeer: support protocol upgrade
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37556
diff changeset
   433
                                                     resp, _compressible)
30464
e16e234b9ca3 httppeer: do decompression inside _callstream
Gregory Szorc <gregory.szorc@gmail.com>
parents: 30241
diff changeset
   434
752
c693eafd5967 Simplify content type checking
mpm@selenic.com
parents: 751
diff changeset
   435
        return resp
60
e32fdbd97839 Add hg:// protocol
mpm@selenic.com
parents: 56
diff changeset
   436
11589
e8d22fe2ddab protocol: clean up call-like functions in http and ssh clients
Matt Mackall <mpm@selenic.com>
parents: 11588
diff changeset
   437
    def _call(self, cmd, **args):
e8d22fe2ddab protocol: clean up call-like functions in http and ssh clients
Matt Mackall <mpm@selenic.com>
parents: 11588
diff changeset
   438
        fp = self._callstream(cmd, **args)
2435
ff2bac730b99 http client: support persistent connections.
Vadim Gelfer <vadim.gelfer@gmail.com>
parents: 2337
diff changeset
   439
        try:
ff2bac730b99 http client: support persistent connections.
Vadim Gelfer <vadim.gelfer@gmail.com>
parents: 2337
diff changeset
   440
            return fp.read()
ff2bac730b99 http client: support persistent connections.
Vadim Gelfer <vadim.gelfer@gmail.com>
parents: 2337
diff changeset
   441
        finally:
ff2bac730b99 http client: support persistent connections.
Vadim Gelfer <vadim.gelfer@gmail.com>
parents: 2337
diff changeset
   442
            # if using keepalive, allow connection to be reused
ff2bac730b99 http client: support persistent connections.
Vadim Gelfer <vadim.gelfer@gmail.com>
parents: 2337
diff changeset
   443
            fp.close()
ff2bac730b99 http client: support persistent connections.
Vadim Gelfer <vadim.gelfer@gmail.com>
parents: 2337
diff changeset
   444
11592
26e0782b8380 protocol: unify client unbundle support
Matt Mackall <mpm@selenic.com>
parents: 11591
diff changeset
   445
    def _callpush(self, cmd, cg, **args):
2465
c91118f425d0 push over http: client support.
Vadim Gelfer <vadim.gelfer@gmail.com>
parents: 2447
diff changeset
   446
        # have to stream bundle to a temp file because we do not have
c91118f425d0 push over http: client support.
Vadim Gelfer <vadim.gelfer@gmail.com>
parents: 2447
diff changeset
   447
        # http 1.1 chunked transfer.
c91118f425d0 push over http: client support.
Vadim Gelfer <vadim.gelfer@gmail.com>
parents: 2447
diff changeset
   448
3662
f4dc02d7fb71 unduplicate bundle writing code from httprepo
Matt Mackall <mpm@selenic.com>
parents: 3661
diff changeset
   449
        types = self.capable('unbundle')
3703
e674cae8efee fix push over HTTP to older servers
Alexis S. L. Carvalho <alexis@cecm.usp.br>
parents: 3662
diff changeset
   450
        try:
e674cae8efee fix push over HTTP to older servers
Alexis S. L. Carvalho <alexis@cecm.usp.br>
parents: 3662
diff changeset
   451
            types = types.split(',')
e674cae8efee fix push over HTTP to older servers
Alexis S. L. Carvalho <alexis@cecm.usp.br>
parents: 3662
diff changeset
   452
        except AttributeError:
14060
aaa9a5989405 bundle: more comments about the different header types, remove useless if
Benoit Boissinot <benoit.boissinot@ens-lyon.org>
parents: 13819
diff changeset
   453
            # servers older than d1b16a746db6 will send 'unbundle' as a
aaa9a5989405 bundle: more comments about the different header types, remove useless if
Benoit Boissinot <benoit.boissinot@ens-lyon.org>
parents: 13819
diff changeset
   454
            # boolean capability. They only support headerless/uncompressed
aaa9a5989405 bundle: more comments about the different header types, remove useless if
Benoit Boissinot <benoit.boissinot@ens-lyon.org>
parents: 13819
diff changeset
   455
            # bundles.
3703
e674cae8efee fix push over HTTP to older servers
Alexis S. L. Carvalho <alexis@cecm.usp.br>
parents: 3662
diff changeset
   456
            types = [""]
14060
aaa9a5989405 bundle: more comments about the different header types, remove useless if
Benoit Boissinot <benoit.boissinot@ens-lyon.org>
parents: 13819
diff changeset
   457
        for x in types:
28666
ae53ecc47414 bundle: move writebundle() from changegroup.py to bundle2.py (API)
Martin von Zweigbergk <martinvonz@google.com>
parents: 28530
diff changeset
   458
            if x in bundle2.bundletypes:
14060
aaa9a5989405 bundle: more comments about the different header types, remove useless if
Benoit Boissinot <benoit.boissinot@ens-lyon.org>
parents: 13819
diff changeset
   459
                type = x
aaa9a5989405 bundle: more comments about the different header types, remove useless if
Benoit Boissinot <benoit.boissinot@ens-lyon.org>
parents: 13819
diff changeset
   460
                break
3613
cbf352b9a3cd Client support for hgweb unbundle with versions.
Thomas Arendsen Hein <thomas@intevation.de>
parents: 3609
diff changeset
   461
28666
ae53ecc47414 bundle: move writebundle() from changegroup.py to bundle2.py (API)
Martin von Zweigbergk <martinvonz@google.com>
parents: 28530
diff changeset
   462
        tempname = bundle2.writebundle(self.ui, cg, None, type)
14244
e7525a555a64 url: use new http support if requested by the user
Augie Fackler <durin42@gmail.com>
parents: 14149
diff changeset
   463
        fp = httpconnection.httpsendfile(self.ui, tempname, "rb")
36297
a59ff82154b8 httppeer: headers are native strings
Augie Fackler <augie@google.com>
parents: 36270
diff changeset
   464
        headers = {r'Content-Type': r'application/mercurial-0.1'}
11592
26e0782b8380 protocol: unify client unbundle support
Matt Mackall <mpm@selenic.com>
parents: 11591
diff changeset
   465
2465
c91118f425d0 push over http: client support.
Vadim Gelfer <vadim.gelfer@gmail.com>
parents: 2447
diff changeset
   466
        try:
25085
e05734cd7902 httppeer: use try/except/finally
Matt Mackall <mpm@selenic.com>
parents: 23895
diff changeset
   467
            r = self._call(cmd, data=fp, headers=headers, **args)
e05734cd7902 httppeer: use try/except/finally
Matt Mackall <mpm@selenic.com>
parents: 23895
diff changeset
   468
            vals = r.split('\n', 1)
e05734cd7902 httppeer: use try/except/finally
Matt Mackall <mpm@selenic.com>
parents: 23895
diff changeset
   469
            if len(vals) < 2:
e05734cd7902 httppeer: use try/except/finally
Matt Mackall <mpm@selenic.com>
parents: 23895
diff changeset
   470
                raise error.ResponseError(_("unexpected response:"), r)
e05734cd7902 httppeer: use try/except/finally
Matt Mackall <mpm@selenic.com>
parents: 23895
diff changeset
   471
            return vals
36430
efebfa9b4cab httppeer: explicitly catch urlerr.httperror and re-raise
Augie Fackler <augie@google.com>
parents: 36426
diff changeset
   472
        except urlerr.httperror:
efebfa9b4cab httppeer: explicitly catch urlerr.httperror and re-raise
Augie Fackler <augie@google.com>
parents: 36426
diff changeset
   473
            # Catch and re-raise these so we don't try and treat them
efebfa9b4cab httppeer: explicitly catch urlerr.httperror and re-raise
Augie Fackler <augie@google.com>
parents: 36426
diff changeset
   474
            # like generic socket errors. They lack any values in
efebfa9b4cab httppeer: explicitly catch urlerr.httperror and re-raise
Augie Fackler <augie@google.com>
parents: 36426
diff changeset
   475
            # .args on Python 3 which breaks our socket.error block.
efebfa9b4cab httppeer: explicitly catch urlerr.httperror and re-raise
Augie Fackler <augie@google.com>
parents: 36426
diff changeset
   476
            raise
25660
328739ea70c3 global: mass rewrite to use modern exception syntax
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25500
diff changeset
   477
        except socket.error as err:
25085
e05734cd7902 httppeer: use try/except/finally
Matt Mackall <mpm@selenic.com>
parents: 23895
diff changeset
   478
            if err.args[0] in (errno.ECONNRESET, errno.EPIPE):
26587
56b2bcea2529 error: get Abort from 'error' instead of 'util'
Pierre-Yves David <pierre-yves.david@fb.com>
parents: 25954
diff changeset
   479
                raise error.Abort(_('push failed: %s') % err.args[1])
56b2bcea2529 error: get Abort from 'error' instead of 'util'
Pierre-Yves David <pierre-yves.david@fb.com>
parents: 25954
diff changeset
   480
            raise error.Abort(err.args[1])
2465
c91118f425d0 push over http: client support.
Vadim Gelfer <vadim.gelfer@gmail.com>
parents: 2447
diff changeset
   481
        finally:
c91118f425d0 push over http: client support.
Vadim Gelfer <vadim.gelfer@gmail.com>
parents: 2447
diff changeset
   482
            fp.close()
c91118f425d0 push over http: client support.
Vadim Gelfer <vadim.gelfer@gmail.com>
parents: 2447
diff changeset
   483
            os.unlink(tempname)
2439
e8c4f3d3df8c extend network protocol to stop clients from locking servers
Vadim Gelfer <vadim.gelfer@gmail.com>
parents: 2435
diff changeset
   484
21074
f8a0d82b0463 httppeer: support for _calltwowaystream
Pierre-Yves David <pierre-yves.david@fb.com>
parents: 20905
diff changeset
   485
    def _calltwowaystream(self, cmd, fp, **args):
f8a0d82b0463 httppeer: support for _calltwowaystream
Pierre-Yves David <pierre-yves.david@fb.com>
parents: 20905
diff changeset
   486
        fh = None
23086
cde6904f1992 httppeer: close the temporary bundle file after two-way streaming it
Matt Harbison <matt_harbison@yahoo.com>
parents: 21188
diff changeset
   487
        fp_ = None
21074
f8a0d82b0463 httppeer: support for _calltwowaystream
Pierre-Yves David <pierre-yves.david@fb.com>
parents: 20905
diff changeset
   488
        filename = None
f8a0d82b0463 httppeer: support for _calltwowaystream
Pierre-Yves David <pierre-yves.david@fb.com>
parents: 20905
diff changeset
   489
        try:
f8a0d82b0463 httppeer: support for _calltwowaystream
Pierre-Yves David <pierre-yves.david@fb.com>
parents: 20905
diff changeset
   490
            # dump bundle to disk
f8a0d82b0463 httppeer: support for _calltwowaystream
Pierre-Yves David <pierre-yves.david@fb.com>
parents: 20905
diff changeset
   491
            fd, filename = tempfile.mkstemp(prefix="hg-bundle-", suffix=".hg")
36835
5bc7ff103081 py3: use r'' instead of sysstr('') to get around code transformer
Yuya Nishihara <yuya@tcha.org>
parents: 36430
diff changeset
   492
            fh = os.fdopen(fd, r"wb")
21074
f8a0d82b0463 httppeer: support for _calltwowaystream
Pierre-Yves David <pierre-yves.david@fb.com>
parents: 20905
diff changeset
   493
            d = fp.read(4096)
f8a0d82b0463 httppeer: support for _calltwowaystream
Pierre-Yves David <pierre-yves.david@fb.com>
parents: 20905
diff changeset
   494
            while d:
f8a0d82b0463 httppeer: support for _calltwowaystream
Pierre-Yves David <pierre-yves.david@fb.com>
parents: 20905
diff changeset
   495
                fh.write(d)
f8a0d82b0463 httppeer: support for _calltwowaystream
Pierre-Yves David <pierre-yves.david@fb.com>
parents: 20905
diff changeset
   496
                d = fp.read(4096)
f8a0d82b0463 httppeer: support for _calltwowaystream
Pierre-Yves David <pierre-yves.david@fb.com>
parents: 20905
diff changeset
   497
            fh.close()
f8a0d82b0463 httppeer: support for _calltwowaystream
Pierre-Yves David <pierre-yves.david@fb.com>
parents: 20905
diff changeset
   498
            # start http push
23086
cde6904f1992 httppeer: close the temporary bundle file after two-way streaming it
Matt Harbison <matt_harbison@yahoo.com>
parents: 21188
diff changeset
   499
            fp_ = httpconnection.httpsendfile(self.ui, filename, "rb")
36297
a59ff82154b8 httppeer: headers are native strings
Augie Fackler <augie@google.com>
parents: 36270
diff changeset
   500
            headers = {r'Content-Type': r'application/mercurial-0.1'}
23086
cde6904f1992 httppeer: close the temporary bundle file after two-way streaming it
Matt Harbison <matt_harbison@yahoo.com>
parents: 21188
diff changeset
   501
            return self._callstream(cmd, data=fp_, headers=headers, **args)
21074
f8a0d82b0463 httppeer: support for _calltwowaystream
Pierre-Yves David <pierre-yves.david@fb.com>
parents: 20905
diff changeset
   502
        finally:
23086
cde6904f1992 httppeer: close the temporary bundle file after two-way streaming it
Matt Harbison <matt_harbison@yahoo.com>
parents: 21188
diff changeset
   503
            if fp_ is not None:
cde6904f1992 httppeer: close the temporary bundle file after two-way streaming it
Matt Harbison <matt_harbison@yahoo.com>
parents: 21188
diff changeset
   504
                fp_.close()
21074
f8a0d82b0463 httppeer: support for _calltwowaystream
Pierre-Yves David <pierre-yves.david@fb.com>
parents: 20905
diff changeset
   505
            if fh is not None:
f8a0d82b0463 httppeer: support for _calltwowaystream
Pierre-Yves David <pierre-yves.david@fb.com>
parents: 20905
diff changeset
   506
                fh.close()
f8a0d82b0463 httppeer: support for _calltwowaystream
Pierre-Yves David <pierre-yves.david@fb.com>
parents: 20905
diff changeset
   507
                os.unlink(filename)
f8a0d82b0463 httppeer: support for _calltwowaystream
Pierre-Yves David <pierre-yves.david@fb.com>
parents: 20905
diff changeset
   508
20905
167047ba3cfa wireproto: drop the _decompress method in favor a new call type
Pierre-Yves David <pierre-yves.david@fb.com>
parents: 18742
diff changeset
   509
    def _callcompressable(self, cmd, **args):
30464
e16e234b9ca3 httppeer: do decompression inside _callstream
Gregory Szorc <gregory.szorc@gmail.com>
parents: 30241
diff changeset
   510
        return self._callstream(cmd, _compressible=True, **args)
11370
db3f6f0e4e7d pushkey: add http support
Matt Mackall <mpm@selenic.com>
parents: 11153
diff changeset
   511
21188
d36440d84328 httppeer: reintroduce _abort that accidentally was removed in 167047ba3cfa
Mads Kiilerich <madski@unity3d.com>
parents: 21074
diff changeset
   512
    def _abort(self, exception):
d36440d84328 httppeer: reintroduce _abort that accidentally was removed in 167047ba3cfa
Mads Kiilerich <madski@unity3d.com>
parents: 21074
diff changeset
   513
        raise exception
d36440d84328 httppeer: reintroduce _abort that accidentally was removed in 167047ba3cfa
Mads Kiilerich <madski@unity3d.com>
parents: 21074
diff changeset
   514
37483
61e405fb6372 wireproto: crude support for version 2 HTTP peer
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37411
diff changeset
   515
# TODO implement interface for version 2 peers
61e405fb6372 wireproto: crude support for version 2 HTTP peer
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37411
diff changeset
   516
class httpv2peer(object):
37558
8a73132214a3 httppeer: support protocol upgrade
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37556
diff changeset
   517
    def __init__(self, ui, repourl, apipath, opener, requestbuilder,
8a73132214a3 httppeer: support protocol upgrade
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37556
diff changeset
   518
                 apidescriptor):
37483
61e405fb6372 wireproto: crude support for version 2 HTTP peer
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37411
diff changeset
   519
        self.ui = ui
61e405fb6372 wireproto: crude support for version 2 HTTP peer
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37411
diff changeset
   520
61e405fb6372 wireproto: crude support for version 2 HTTP peer
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37411
diff changeset
   521
        if repourl.endswith('/'):
61e405fb6372 wireproto: crude support for version 2 HTTP peer
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37411
diff changeset
   522
            repourl = repourl[:-1]
61e405fb6372 wireproto: crude support for version 2 HTTP peer
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37411
diff changeset
   523
61e405fb6372 wireproto: crude support for version 2 HTTP peer
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37411
diff changeset
   524
        self.url = repourl
37558
8a73132214a3 httppeer: support protocol upgrade
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37556
diff changeset
   525
        self._apipath = apipath
37483
61e405fb6372 wireproto: crude support for version 2 HTTP peer
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37411
diff changeset
   526
        self._opener = opener
37558
8a73132214a3 httppeer: support protocol upgrade
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37556
diff changeset
   527
        self._requestbuilder = requestbuilder
8a73132214a3 httppeer: support protocol upgrade
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37556
diff changeset
   528
        self._descriptor = apidescriptor
37483
61e405fb6372 wireproto: crude support for version 2 HTTP peer
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37411
diff changeset
   529
61e405fb6372 wireproto: crude support for version 2 HTTP peer
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37411
diff changeset
   530
    def close(self):
61e405fb6372 wireproto: crude support for version 2 HTTP peer
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37411
diff changeset
   531
        pass
61e405fb6372 wireproto: crude support for version 2 HTTP peer
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37411
diff changeset
   532
61e405fb6372 wireproto: crude support for version 2 HTTP peer
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37411
diff changeset
   533
    # TODO require to be part of a batched primitive, use futures.
61e405fb6372 wireproto: crude support for version 2 HTTP peer
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37411
diff changeset
   534
    def _call(self, name, **args):
61e405fb6372 wireproto: crude support for version 2 HTTP peer
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37411
diff changeset
   535
        """Call a wire protocol command with arguments."""
61e405fb6372 wireproto: crude support for version 2 HTTP peer
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37411
diff changeset
   536
37546
3a2367e6c6f2 wireproto: move version 2 command handlers to wireprotov2server
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37545
diff changeset
   537
        # Having this early has a side-effect of importing wireprotov2server,
3a2367e6c6f2 wireproto: move version 2 command handlers to wireprotov2server
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37545
diff changeset
   538
        # which has the side-effect of ensuring commands are registered.
3a2367e6c6f2 wireproto: move version 2 command handlers to wireprotov2server
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37545
diff changeset
   539
3a2367e6c6f2 wireproto: move version 2 command handlers to wireprotov2server
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37545
diff changeset
   540
        # TODO modify user-agent to reflect v2.
3a2367e6c6f2 wireproto: move version 2 command handlers to wireprotov2server
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37545
diff changeset
   541
        headers = {
3a2367e6c6f2 wireproto: move version 2 command handlers to wireprotov2server
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37545
diff changeset
   542
            r'Accept': wireprotov2server.FRAMINGTYPE,
3a2367e6c6f2 wireproto: move version 2 command handlers to wireprotov2server
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37545
diff changeset
   543
            r'Content-Type': wireprotov2server.FRAMINGTYPE,
3a2367e6c6f2 wireproto: move version 2 command handlers to wireprotov2server
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37545
diff changeset
   544
        }
3a2367e6c6f2 wireproto: move version 2 command handlers to wireprotov2server
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37545
diff changeset
   545
37483
61e405fb6372 wireproto: crude support for version 2 HTTP peer
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37411
diff changeset
   546
        # TODO permissions should come from capabilities results.
61e405fb6372 wireproto: crude support for version 2 HTTP peer
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37411
diff changeset
   547
        permission = wireproto.commandsv2[name].permission
61e405fb6372 wireproto: crude support for version 2 HTTP peer
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37411
diff changeset
   548
        if permission not in ('push', 'pull'):
61e405fb6372 wireproto: crude support for version 2 HTTP peer
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37411
diff changeset
   549
            raise error.ProgrammingError('unknown permission type: %s' %
61e405fb6372 wireproto: crude support for version 2 HTTP peer
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37411
diff changeset
   550
                                         permission)
61e405fb6372 wireproto: crude support for version 2 HTTP peer
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37411
diff changeset
   551
61e405fb6372 wireproto: crude support for version 2 HTTP peer
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37411
diff changeset
   552
        permission = {
61e405fb6372 wireproto: crude support for version 2 HTTP peer
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37411
diff changeset
   553
            'push': 'rw',
61e405fb6372 wireproto: crude support for version 2 HTTP peer
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37411
diff changeset
   554
            'pull': 'ro',
61e405fb6372 wireproto: crude support for version 2 HTTP peer
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37411
diff changeset
   555
        }[permission]
61e405fb6372 wireproto: crude support for version 2 HTTP peer
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37411
diff changeset
   556
37558
8a73132214a3 httppeer: support protocol upgrade
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37556
diff changeset
   557
        url = '%s/%s/%s/%s' % (self.url, self._apipath, permission, name)
37483
61e405fb6372 wireproto: crude support for version 2 HTTP peer
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37411
diff changeset
   558
61e405fb6372 wireproto: crude support for version 2 HTTP peer
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37411
diff changeset
   559
        # TODO this should be part of a generic peer for the frame-based
61e405fb6372 wireproto: crude support for version 2 HTTP peer
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37411
diff changeset
   560
        # protocol.
37543
01361be9e2dc wireproto: introduce a reactor for client-side state
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37498
diff changeset
   561
        reactor = wireprotoframing.clientreactor(hasmultiplesend=False,
01361be9e2dc wireproto: introduce a reactor for client-side state
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37498
diff changeset
   562
                                                 buffersends=True)
37483
61e405fb6372 wireproto: crude support for version 2 HTTP peer
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37411
diff changeset
   563
37543
01361be9e2dc wireproto: introduce a reactor for client-side state
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37498
diff changeset
   564
        request, action, meta = reactor.callcommand(name, args)
01361be9e2dc wireproto: introduce a reactor for client-side state
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37498
diff changeset
   565
        assert action == 'noop'
01361be9e2dc wireproto: introduce a reactor for client-side state
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37498
diff changeset
   566
01361be9e2dc wireproto: introduce a reactor for client-side state
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37498
diff changeset
   567
        action, meta = reactor.flushcommands()
01361be9e2dc wireproto: introduce a reactor for client-side state
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37498
diff changeset
   568
        assert action == 'sendframes'
01361be9e2dc wireproto: introduce a reactor for client-side state
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37498
diff changeset
   569
01361be9e2dc wireproto: introduce a reactor for client-side state
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37498
diff changeset
   570
        body = b''.join(map(bytes, meta['framegen']))
37483
61e405fb6372 wireproto: crude support for version 2 HTTP peer
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37411
diff changeset
   571
        req = self._requestbuilder(pycompat.strurl(url), body, headers)
61e405fb6372 wireproto: crude support for version 2 HTTP peer
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37411
diff changeset
   572
        req.add_unredirected_header(r'Content-Length', r'%d' % len(body))
61e405fb6372 wireproto: crude support for version 2 HTTP peer
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37411
diff changeset
   573
61e405fb6372 wireproto: crude support for version 2 HTTP peer
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37411
diff changeset
   574
        # TODO unify this code with httppeer.
61e405fb6372 wireproto: crude support for version 2 HTTP peer
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37411
diff changeset
   575
        try:
61e405fb6372 wireproto: crude support for version 2 HTTP peer
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37411
diff changeset
   576
            res = self._opener.open(req)
61e405fb6372 wireproto: crude support for version 2 HTTP peer
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37411
diff changeset
   577
        except urlerr.httperror as e:
61e405fb6372 wireproto: crude support for version 2 HTTP peer
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37411
diff changeset
   578
            if e.code == 401:
61e405fb6372 wireproto: crude support for version 2 HTTP peer
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37411
diff changeset
   579
                raise error.Abort(_('authorization failed'))
61e405fb6372 wireproto: crude support for version 2 HTTP peer
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37411
diff changeset
   580
61e405fb6372 wireproto: crude support for version 2 HTTP peer
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37411
diff changeset
   581
            raise
61e405fb6372 wireproto: crude support for version 2 HTTP peer
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37411
diff changeset
   582
        except httplib.HTTPException as e:
61e405fb6372 wireproto: crude support for version 2 HTTP peer
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37411
diff changeset
   583
            self.ui.traceback()
61e405fb6372 wireproto: crude support for version 2 HTTP peer
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37411
diff changeset
   584
            raise IOError(None, e)
61e405fb6372 wireproto: crude support for version 2 HTTP peer
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37411
diff changeset
   585
61e405fb6372 wireproto: crude support for version 2 HTTP peer
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37411
diff changeset
   586
        # TODO validate response type, wrap response to handle I/O errors.
61e405fb6372 wireproto: crude support for version 2 HTTP peer
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37411
diff changeset
   587
        # TODO more robust frame receiver.
61e405fb6372 wireproto: crude support for version 2 HTTP peer
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37411
diff changeset
   588
        results = []
61e405fb6372 wireproto: crude support for version 2 HTTP peer
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37411
diff changeset
   589
61e405fb6372 wireproto: crude support for version 2 HTTP peer
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37411
diff changeset
   590
        while True:
61e405fb6372 wireproto: crude support for version 2 HTTP peer
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37411
diff changeset
   591
            frame = wireprotoframing.readframe(res)
61e405fb6372 wireproto: crude support for version 2 HTTP peer
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37411
diff changeset
   592
            if frame is None:
61e405fb6372 wireproto: crude support for version 2 HTTP peer
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37411
diff changeset
   593
                break
61e405fb6372 wireproto: crude support for version 2 HTTP peer
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37411
diff changeset
   594
61e405fb6372 wireproto: crude support for version 2 HTTP peer
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37411
diff changeset
   595
            self.ui.note(_('received %r\n') % frame)
61e405fb6372 wireproto: crude support for version 2 HTTP peer
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37411
diff changeset
   596
37544
55b5ba8d4e68 wireproto: client reactor support for receiving frames
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37543
diff changeset
   597
            action, meta = reactor.onframerecv(frame)
55b5ba8d4e68 wireproto: client reactor support for receiving frames
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37543
diff changeset
   598
55b5ba8d4e68 wireproto: client reactor support for receiving frames
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37543
diff changeset
   599
            if action == 'responsedata':
55b5ba8d4e68 wireproto: client reactor support for receiving frames
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37543
diff changeset
   600
                if meta['cbor']:
55b5ba8d4e68 wireproto: client reactor support for receiving frames
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37543
diff changeset
   601
                    payload = util.bytesio(meta['data'])
37483
61e405fb6372 wireproto: crude support for version 2 HTTP peer
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37411
diff changeset
   602
61e405fb6372 wireproto: crude support for version 2 HTTP peer
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37411
diff changeset
   603
                    decoder = cbor.CBORDecoder(payload)
37544
55b5ba8d4e68 wireproto: client reactor support for receiving frames
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37543
diff changeset
   604
                    while payload.tell() + 1 < len(meta['data']):
37483
61e405fb6372 wireproto: crude support for version 2 HTTP peer
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37411
diff changeset
   605
                        results.append(decoder.decode())
61e405fb6372 wireproto: crude support for version 2 HTTP peer
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37411
diff changeset
   606
                else:
37544
55b5ba8d4e68 wireproto: client reactor support for receiving frames
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37543
diff changeset
   607
                    results.append(meta['data'])
37483
61e405fb6372 wireproto: crude support for version 2 HTTP peer
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37411
diff changeset
   608
            else:
37544
55b5ba8d4e68 wireproto: client reactor support for receiving frames
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37543
diff changeset
   609
                error.ProgrammingError('unhandled action: %s' % action)
37483
61e405fb6372 wireproto: crude support for version 2 HTTP peer
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37411
diff changeset
   610
61e405fb6372 wireproto: crude support for version 2 HTTP peer
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37411
diff changeset
   611
        return results
61e405fb6372 wireproto: crude support for version 2 HTTP peer
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37411
diff changeset
   612
37558
8a73132214a3 httppeer: support protocol upgrade
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37556
diff changeset
   613
# Registry of API service names to metadata about peers that handle it.
8a73132214a3 httppeer: support protocol upgrade
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37556
diff changeset
   614
#
8a73132214a3 httppeer: support protocol upgrade
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37556
diff changeset
   615
# The following keys are meaningful:
8a73132214a3 httppeer: support protocol upgrade
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37556
diff changeset
   616
#
8a73132214a3 httppeer: support protocol upgrade
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37556
diff changeset
   617
# init
8a73132214a3 httppeer: support protocol upgrade
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37556
diff changeset
   618
#    Callable receiving (ui, repourl, servicepath, opener, requestbuilder,
8a73132214a3 httppeer: support protocol upgrade
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37556
diff changeset
   619
#                        apidescriptor) to create a peer.
8a73132214a3 httppeer: support protocol upgrade
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37556
diff changeset
   620
#
8a73132214a3 httppeer: support protocol upgrade
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37556
diff changeset
   621
# priority
8a73132214a3 httppeer: support protocol upgrade
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37556
diff changeset
   622
#    Integer priority for the service. If we could choose from multiple
8a73132214a3 httppeer: support protocol upgrade
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37556
diff changeset
   623
#    services, we choose the one with the highest priority.
8a73132214a3 httppeer: support protocol upgrade
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37556
diff changeset
   624
API_PEERS = {
8a73132214a3 httppeer: support protocol upgrade
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37556
diff changeset
   625
    wireprototypes.HTTPV2: {
8a73132214a3 httppeer: support protocol upgrade
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37556
diff changeset
   626
        'init': httpv2peer,
8a73132214a3 httppeer: support protocol upgrade
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37556
diff changeset
   627
        'priority': 50,
8a73132214a3 httppeer: support protocol upgrade
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37556
diff changeset
   628
    },
8a73132214a3 httppeer: support protocol upgrade
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37556
diff changeset
   629
}
8a73132214a3 httppeer: support protocol upgrade
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37556
diff changeset
   630
37552
8b8a845c85fc httppeer: perform capabilities request in makepeer()
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37551
diff changeset
   631
def performhandshake(ui, url, opener, requestbuilder):
8b8a845c85fc httppeer: perform capabilities request in makepeer()
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37551
diff changeset
   632
    # The handshake is a request to the capabilities command.
8b8a845c85fc httppeer: perform capabilities request in makepeer()
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37551
diff changeset
   633
8b8a845c85fc httppeer: perform capabilities request in makepeer()
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37551
diff changeset
   634
    caps = None
8b8a845c85fc httppeer: perform capabilities request in makepeer()
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37551
diff changeset
   635
    def capable(x):
8b8a845c85fc httppeer: perform capabilities request in makepeer()
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37551
diff changeset
   636
        raise error.ProgrammingError('should not be called')
8b8a845c85fc httppeer: perform capabilities request in makepeer()
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37551
diff changeset
   637
37558
8a73132214a3 httppeer: support protocol upgrade
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37556
diff changeset
   638
    args = {}
8a73132214a3 httppeer: support protocol upgrade
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37556
diff changeset
   639
8a73132214a3 httppeer: support protocol upgrade
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37556
diff changeset
   640
    # The client advertises support for newer protocols by adding an
8a73132214a3 httppeer: support protocol upgrade
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37556
diff changeset
   641
    # X-HgUpgrade-* header with a list of supported APIs and an
8a73132214a3 httppeer: support protocol upgrade
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37556
diff changeset
   642
    # X-HgProto-* header advertising which serializing formats it supports.
8a73132214a3 httppeer: support protocol upgrade
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37556
diff changeset
   643
    # We only support the HTTP version 2 transport and CBOR responses for
8a73132214a3 httppeer: support protocol upgrade
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37556
diff changeset
   644
    # now.
8a73132214a3 httppeer: support protocol upgrade
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37556
diff changeset
   645
    advertisev2 = ui.configbool('experimental', 'httppeer.advertise-v2')
8a73132214a3 httppeer: support protocol upgrade
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37556
diff changeset
   646
8a73132214a3 httppeer: support protocol upgrade
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37556
diff changeset
   647
    if advertisev2:
8a73132214a3 httppeer: support protocol upgrade
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37556
diff changeset
   648
        args['headers'] = {
8a73132214a3 httppeer: support protocol upgrade
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37556
diff changeset
   649
            r'X-HgProto-1': r'cbor',
8a73132214a3 httppeer: support protocol upgrade
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37556
diff changeset
   650
        }
8a73132214a3 httppeer: support protocol upgrade
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37556
diff changeset
   651
8a73132214a3 httppeer: support protocol upgrade
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37556
diff changeset
   652
        args['headers'].update(
8a73132214a3 httppeer: support protocol upgrade
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37556
diff changeset
   653
            encodevalueinheaders(' '.join(sorted(API_PEERS)),
8a73132214a3 httppeer: support protocol upgrade
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37556
diff changeset
   654
                                 'X-HgUpgrade',
8a73132214a3 httppeer: support protocol upgrade
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37556
diff changeset
   655
                                 # We don't know the header limit this early.
8a73132214a3 httppeer: support protocol upgrade
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37556
diff changeset
   656
                                 # So make it small.
8a73132214a3 httppeer: support protocol upgrade
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37556
diff changeset
   657
                                 1024))
8a73132214a3 httppeer: support protocol upgrade
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37556
diff changeset
   658
37552
8b8a845c85fc httppeer: perform capabilities request in makepeer()
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37551
diff changeset
   659
    req, requrl, qs = makev1commandrequest(ui, requestbuilder, caps,
8b8a845c85fc httppeer: perform capabilities request in makepeer()
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37551
diff changeset
   660
                                           capable, url, 'capabilities',
37558
8a73132214a3 httppeer: support protocol upgrade
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37556
diff changeset
   661
                                           args)
37552
8b8a845c85fc httppeer: perform capabilities request in makepeer()
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37551
diff changeset
   662
8b8a845c85fc httppeer: perform capabilities request in makepeer()
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37551
diff changeset
   663
    resp = sendrequest(ui, opener, req)
8b8a845c85fc httppeer: perform capabilities request in makepeer()
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37551
diff changeset
   664
37558
8a73132214a3 httppeer: support protocol upgrade
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37556
diff changeset
   665
    respurl, ct, resp = parsev1commandresponse(ui, url, requrl, qs, resp,
8a73132214a3 httppeer: support protocol upgrade
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37556
diff changeset
   666
                                               compressible=False,
8a73132214a3 httppeer: support protocol upgrade
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37556
diff changeset
   667
                                               allowcbor=advertisev2)
37552
8b8a845c85fc httppeer: perform capabilities request in makepeer()
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37551
diff changeset
   668
8b8a845c85fc httppeer: perform capabilities request in makepeer()
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37551
diff changeset
   669
    try:
37558
8a73132214a3 httppeer: support protocol upgrade
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37556
diff changeset
   670
        rawdata = resp.read()
37552
8b8a845c85fc httppeer: perform capabilities request in makepeer()
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37551
diff changeset
   671
    finally:
8b8a845c85fc httppeer: perform capabilities request in makepeer()
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37551
diff changeset
   672
        resp.close()
8b8a845c85fc httppeer: perform capabilities request in makepeer()
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37551
diff changeset
   673
37558
8a73132214a3 httppeer: support protocol upgrade
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37556
diff changeset
   674
    if not ct.startswith('application/mercurial-'):
8a73132214a3 httppeer: support protocol upgrade
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37556
diff changeset
   675
        raise error.ProgrammingError('unexpected content-type: %s' % ct)
8a73132214a3 httppeer: support protocol upgrade
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37556
diff changeset
   676
8a73132214a3 httppeer: support protocol upgrade
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37556
diff changeset
   677
    if advertisev2:
8a73132214a3 httppeer: support protocol upgrade
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37556
diff changeset
   678
        if ct == 'application/mercurial-cbor':
8a73132214a3 httppeer: support protocol upgrade
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37556
diff changeset
   679
            try:
8a73132214a3 httppeer: support protocol upgrade
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37556
diff changeset
   680
                info = cbor.loads(rawdata)
8a73132214a3 httppeer: support protocol upgrade
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37556
diff changeset
   681
            except cbor.CBORDecodeError:
8a73132214a3 httppeer: support protocol upgrade
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37556
diff changeset
   682
                raise error.Abort(_('error decoding CBOR from remote server'),
8a73132214a3 httppeer: support protocol upgrade
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37556
diff changeset
   683
                                  hint=_('try again and consider contacting '
8a73132214a3 httppeer: support protocol upgrade
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37556
diff changeset
   684
                                         'the server operator'))
8a73132214a3 httppeer: support protocol upgrade
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37556
diff changeset
   685
8a73132214a3 httppeer: support protocol upgrade
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37556
diff changeset
   686
        # We got a legacy response. That's fine.
8a73132214a3 httppeer: support protocol upgrade
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37556
diff changeset
   687
        elif ct in ('application/mercurial-0.1', 'application/mercurial-0.2'):
8a73132214a3 httppeer: support protocol upgrade
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37556
diff changeset
   688
            info = {
8a73132214a3 httppeer: support protocol upgrade
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37556
diff changeset
   689
                'v1capabilities': set(rawdata.split())
8a73132214a3 httppeer: support protocol upgrade
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37556
diff changeset
   690
            }
8a73132214a3 httppeer: support protocol upgrade
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37556
diff changeset
   691
8a73132214a3 httppeer: support protocol upgrade
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37556
diff changeset
   692
        else:
8a73132214a3 httppeer: support protocol upgrade
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37556
diff changeset
   693
            raise error.RepoError(
8a73132214a3 httppeer: support protocol upgrade
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37556
diff changeset
   694
                _('unexpected response type from server: %s') % ct)
8a73132214a3 httppeer: support protocol upgrade
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37556
diff changeset
   695
    else:
8a73132214a3 httppeer: support protocol upgrade
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37556
diff changeset
   696
        info = {
8a73132214a3 httppeer: support protocol upgrade
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37556
diff changeset
   697
            'v1capabilities': set(rawdata.split())
8a73132214a3 httppeer: support protocol upgrade
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37556
diff changeset
   698
        }
8a73132214a3 httppeer: support protocol upgrade
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37556
diff changeset
   699
8a73132214a3 httppeer: support protocol upgrade
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37556
diff changeset
   700
    return respurl, info
37552
8b8a845c85fc httppeer: perform capabilities request in makepeer()
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37551
diff changeset
   701
37553
6b08cf6b900f httppeer: allow opener to be passed to makepeer()
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37552
diff changeset
   702
def makepeer(ui, path, opener=None, requestbuilder=urlreq.request):
37547
835ccc2a5ef1 httppeer: move requestbuilder defaults into makepeer() argument
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37546
diff changeset
   703
    """Construct an appropriate HTTP peer instance.
835ccc2a5ef1 httppeer: move requestbuilder defaults into makepeer() argument
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37546
diff changeset
   704
37553
6b08cf6b900f httppeer: allow opener to be passed to makepeer()
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37552
diff changeset
   705
    ``opener`` is an ``url.opener`` that should be used to establish
6b08cf6b900f httppeer: allow opener to be passed to makepeer()
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37552
diff changeset
   706
    connections, perform HTTP requests.
6b08cf6b900f httppeer: allow opener to be passed to makepeer()
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37552
diff changeset
   707
37547
835ccc2a5ef1 httppeer: move requestbuilder defaults into makepeer() argument
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37546
diff changeset
   708
    ``requestbuilder`` is the type used for constructing HTTP requests.
835ccc2a5ef1 httppeer: move requestbuilder defaults into makepeer() argument
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37546
diff changeset
   709
    It exists as an argument so extensions can override the default.
835ccc2a5ef1 httppeer: move requestbuilder defaults into makepeer() argument
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37546
diff changeset
   710
    """
37006
8e89c2bec1f7 httppeer: refactor how httppeer is created (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36961
diff changeset
   711
    u = util.url(path)
8e89c2bec1f7 httppeer: refactor how httppeer is created (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36961
diff changeset
   712
    if u.query or u.fragment:
8e89c2bec1f7 httppeer: refactor how httppeer is created (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36961
diff changeset
   713
        raise error.Abort(_('unsupported URL component: "%s"') %
8e89c2bec1f7 httppeer: refactor how httppeer is created (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36961
diff changeset
   714
                          (u.query or u.fragment))
8e89c2bec1f7 httppeer: refactor how httppeer is created (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36961
diff changeset
   715
8e89c2bec1f7 httppeer: refactor how httppeer is created (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36961
diff changeset
   716
    # urllib cannot handle URLs with embedded user or passwd.
8e89c2bec1f7 httppeer: refactor how httppeer is created (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36961
diff changeset
   717
    url, authinfo = u.authinfo()
8e89c2bec1f7 httppeer: refactor how httppeer is created (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36961
diff changeset
   718
    ui.debug('using %s\n' % url)
8e89c2bec1f7 httppeer: refactor how httppeer is created (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36961
diff changeset
   719
37553
6b08cf6b900f httppeer: allow opener to be passed to makepeer()
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37552
diff changeset
   720
    opener = opener or urlmod.opener(ui, authinfo)
37006
8e89c2bec1f7 httppeer: refactor how httppeer is created (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36961
diff changeset
   721
37558
8a73132214a3 httppeer: support protocol upgrade
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37556
diff changeset
   722
    respurl, info = performhandshake(ui, url, opener, requestbuilder)
8a73132214a3 httppeer: support protocol upgrade
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37556
diff changeset
   723
8a73132214a3 httppeer: support protocol upgrade
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37556
diff changeset
   724
    # Given the intersection of APIs that both we and the server support,
8a73132214a3 httppeer: support protocol upgrade
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37556
diff changeset
   725
    # sort by their advertised priority and pick the first one.
8a73132214a3 httppeer: support protocol upgrade
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37556
diff changeset
   726
    #
8a73132214a3 httppeer: support protocol upgrade
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37556
diff changeset
   727
    # TODO consider making this request-based and interface driven. For
8a73132214a3 httppeer: support protocol upgrade
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37556
diff changeset
   728
    # example, the caller could say "I want a peer that does X." It's quite
8a73132214a3 httppeer: support protocol upgrade
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37556
diff changeset
   729
    # possible that not all peers would do that. Since we know the service
8a73132214a3 httppeer: support protocol upgrade
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37556
diff changeset
   730
    # capabilities, we could filter out services not meeting the
8a73132214a3 httppeer: support protocol upgrade
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37556
diff changeset
   731
    # requirements. Possibly by consulting the interfaces defined by the
8a73132214a3 httppeer: support protocol upgrade
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37556
diff changeset
   732
    # peer type.
8a73132214a3 httppeer: support protocol upgrade
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37556
diff changeset
   733
    apipeerchoices = set(info.get('apis', {}).keys()) & set(API_PEERS.keys())
37552
8b8a845c85fc httppeer: perform capabilities request in makepeer()
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37551
diff changeset
   734
37558
8a73132214a3 httppeer: support protocol upgrade
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37556
diff changeset
   735
    preferredchoices = sorted(apipeerchoices,
8a73132214a3 httppeer: support protocol upgrade
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37556
diff changeset
   736
                              key=lambda x: API_PEERS[x]['priority'],
8a73132214a3 httppeer: support protocol upgrade
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37556
diff changeset
   737
                              reverse=True)
8a73132214a3 httppeer: support protocol upgrade
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37556
diff changeset
   738
8a73132214a3 httppeer: support protocol upgrade
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37556
diff changeset
   739
    for service in preferredchoices:
8a73132214a3 httppeer: support protocol upgrade
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37556
diff changeset
   740
        apipath = '%s/%s' % (info['apibase'].rstrip('/'), service)
8a73132214a3 httppeer: support protocol upgrade
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37556
diff changeset
   741
8a73132214a3 httppeer: support protocol upgrade
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37556
diff changeset
   742
        return API_PEERS[service]['init'](ui, respurl, apipath, opener,
8a73132214a3 httppeer: support protocol upgrade
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37556
diff changeset
   743
                                          requestbuilder,
8a73132214a3 httppeer: support protocol upgrade
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37556
diff changeset
   744
                                          info['apis'][service])
8a73132214a3 httppeer: support protocol upgrade
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37556
diff changeset
   745
8a73132214a3 httppeer: support protocol upgrade
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37556
diff changeset
   746
    # Failed to construct an API peer. Fall back to legacy.
8a73132214a3 httppeer: support protocol upgrade
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37556
diff changeset
   747
    return httppeer(ui, path, respurl, opener, requestbuilder,
8a73132214a3 httppeer: support protocol upgrade
Gregory Szorc <gregory.szorc@gmail.com>
parents: 37556
diff changeset
   748
                    info['v1capabilities'])
37006
8e89c2bec1f7 httppeer: refactor how httppeer is created (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36961
diff changeset
   749
2740
386f04d6ecb3 clean up hg.py: move repo constructor code into each repo module
Vadim Gelfer <vadim.gelfer@gmail.com>
parents: 2673
diff changeset
   750
def instance(ui, path, create):
386f04d6ecb3 clean up hg.py: move repo constructor code into each repo module
Vadim Gelfer <vadim.gelfer@gmail.com>
parents: 2673
diff changeset
   751
    if create:
26587
56b2bcea2529 error: get Abort from 'error' instead of 'util'
Pierre-Yves David <pierre-yves.david@fb.com>
parents: 25954
diff changeset
   752
        raise error.Abort(_('cannot create new http repository'))
7211
25c0dee16ee0 Autodetect static-http
Matt Mackall <mpm@selenic.com>
parents: 7207
diff changeset
   753
    try:
36959
43815d87c6aa httppeer: alias url as urlmod
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36835
diff changeset
   754
        if path.startswith('https:') and not urlmod.has_https:
36220
874209855f5c httppeer: remove httpspeer
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36219
diff changeset
   755
            raise error.Abort(_('Python support for SSL and HTTPS '
874209855f5c httppeer: remove httpspeer
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36219
diff changeset
   756
                                'is not installed'))
35884
197d10e157ce httppeer: remove support for connecting to <0.9.1 servers (BC)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 35698
diff changeset
   757
37006
8e89c2bec1f7 httppeer: refactor how httppeer is created (API)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 36961
diff changeset
   758
        inst = makepeer(ui, path)
35884
197d10e157ce httppeer: remove support for connecting to <0.9.1 servers (BC)
Gregory Szorc <gregory.szorc@gmail.com>
parents: 35698
diff changeset
   759
7211
25c0dee16ee0 Autodetect static-http
Matt Mackall <mpm@selenic.com>
parents: 7207
diff changeset
   760
        return inst
25660
328739ea70c3 global: mass rewrite to use modern exception syntax
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25500
diff changeset
   761
    except error.RepoError as httpexception:
14148
cc9366a3751b httprepo: use the original exception after falling back to static-http failed
Mads Kiilerich <mads@kiilerich.com>
parents: 14094
diff changeset
   762
        try:
cc9366a3751b httprepo: use the original exception after falling back to static-http failed
Mads Kiilerich <mads@kiilerich.com>
parents: 14094
diff changeset
   763
            r = statichttprepo.instance(ui, "static-" + path, create)
29241
269f7ea08983 httppeer: make a message translatable
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 28883
diff changeset
   764
            ui.note(_('(falling back to static-http)\n'))
14148
cc9366a3751b httprepo: use the original exception after falling back to static-http failed
Mads Kiilerich <mads@kiilerich.com>
parents: 14094
diff changeset
   765
            return r
cc9366a3751b httprepo: use the original exception after falling back to static-http failed
Mads Kiilerich <mads@kiilerich.com>
parents: 14094
diff changeset
   766
        except error.RepoError:
cc9366a3751b httprepo: use the original exception after falling back to static-http failed
Mads Kiilerich <mads@kiilerich.com>
parents: 14094
diff changeset
   767
            raise httpexception # use the original http RepoError instead