~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/http_transport.py

  • Committer: John Arbash Meinel
  • Date: 2005-09-14 20:49:08 UTC
  • mto: (1185.11.1)
  • mto: This revision was merged to the branch mainline in revision 1396.
  • Revision ID: john@arbash-meinel.com-20050914204908-766c55c17ec9ae4e
Trying to get pipelined http library working + tests.

Show diffs side-by-side

added added

removed removed

Lines of Context:
6
6
from bzrlib.transport import Transport, register_transport, \
7
7
    TransportNotPossible, NoSuchFile, NonRelativePath, \
8
8
    TransportError
9
 
import os, errno, time, asyncore
 
9
import os, errno
10
10
from cStringIO import StringIO
11
11
import urllib2
12
12
 
13
 
from bzrlib.errors import BzrError, BzrCheckError
14
 
from bzrlib.branch import Branch, BZR_BRANCH_FORMAT
15
 
from bzrlib.trace import mutter
16
 
 
17
 
from effbot.org.http_client import do_request
18
 
 
19
 
class HttpTransportError(TransportError):
20
 
    pass
21
 
 
22
 
class simple_wait_consumer(object):
23
 
    """This is a consumer object for effbot.org downloading.
24
 
 
25
 
    Basically, it takes a management object, which it expects to
26
 
    fill itself (eventually) if it waits long enough.
27
 
    So it loops to wait for the download to finish, until it
28
 
    finally gets closed.
29
 
    """
30
 
    def __init__(self, url, extra_headers=None):
31
 
        self.buffer = StringIO()
32
 
        self.done = False
33
 
        self.ok = True
34
 
        self.status = None
35
 
        self.url = url
36
 
        do_request(self.url, self)
37
 
 
38
 
    def get(self):
39
 
        self.finish()
40
 
 
41
 
        # Try and break cyclical loops
42
 
        if self.ok and (self.status is None or self.status[1] == '200'):
43
 
            self.buffer.seek(0)
44
 
            return self.buffer
 
13
from errors import BzrError, BzrCheckError
 
14
from branch import Branch, BZR_BRANCH_FORMAT
 
15
from trace import mutter
 
16
 
 
17
# velocitynet.com.au transparently proxies connections and thereby
 
18
# breaks keep-alive -- sucks!
 
19
 
 
20
 
 
21
ENABLE_URLGRABBER = True
 
22
 
 
23
 
 
24
if ENABLE_URLGRABBER:
 
25
    import urlgrabber
 
26
    import urlgrabber.keepalive
 
27
    import urlgrabber.grabber
 
28
    urlgrabber.keepalive.DEBUG = 0
 
29
    def get_url(path, compressed=False):
 
30
        try:
 
31
            url = path
 
32
            if compressed:
 
33
                url += '.gz'
 
34
            mutter("grab url %s" % url)
 
35
            url_f = urlgrabber.urlopen(url, keepalive=1, close_connection=0)
 
36
            if not compressed:
 
37
                return url_f
 
38
            else:
 
39
                return gzip.GzipFile(fileobj=StringIO(url_f.read()))
 
40
        except urllib2.URLError, e:
 
41
            raise BzrError("remote fetch failed: %r: %s" % (url, e))
 
42
        except urlgrabber.grabber.URLGrabError, e:
 
43
            raise BzrError("remote fetch failed: %r: %s" % (url, e))
 
44
else:
 
45
    def get_url(url, compressed=False):
 
46
        import urllib2
 
47
        if compressed:
 
48
            url += '.gz'
 
49
        mutter("get_url %s" % url)
 
50
        url_f = urllib2.urlopen(url)
 
51
        if compressed:
 
52
            return gzip.GzipFile(fileobj=StringIO(url_f.read()))
45
53
        else:
46
 
            raise NoSuchFile('Download of %r failed: %r' 
47
 
                    % (self.url, self.status))
48
 
 
49
 
    def finish(self):
50
 
        while not self.done:
51
 
            asyncore.poll(0.1)
52
 
 
53
 
    def feed(self, data):
54
 
        self.buffer.write(data)
55
 
 
56
 
    def close(self):
57
 
        self.done = True
58
 
 
59
 
    def http(self, ok, connection, *args, **kwargs):
60
 
        mutter('simple-wait-consumer: %s, %s, %s, %s' 
61
 
                % (self.url, ok, connection.status, connection.header))
62
 
        self.ok = ok
63
 
        if not ok:
64
 
            self.done = True
65
 
        self.status = connection.status
 
54
            return url_f
66
55
 
67
56
def _find_remote_root(url):
68
57
    """Return the prefix URL that corresponds to the branch root."""
103
92
        """Set the base path where files will be stored."""
104
93
        assert base.startswith('http://') or base.startswith('https://')
105
94
        super(HttpTransport, self).__init__(base)
 
95
        # In the future we might actually connect to the remote host
 
96
        # rather than using get_url
 
97
        # self._connection = None
106
98
 
107
99
    def should_cache(self):
108
100
        """Return True if the data pulled across should be cached locally.
141
133
        TODO: HttpTransport.has() should use a HEAD request,
142
134
        not a full GET request.
143
135
        """
144
 
        c = simple_wait_consumer(self.abspath(relpath))
145
 
        c.finish()
146
 
 
147
 
        # We might also look at c.status, but it doesn't seem to always be set
148
 
        # c.status is of the form ["HTTP/1.0", "200", "OK\r\n"] we could
149
 
        # check both the 200 and the OK.
150
 
        if c.ok and (c.status is None or c.status[1] == '200'):
 
136
        try:
 
137
            f = get_url(self.abspath(relpath))
151
138
            return True
152
 
        return False
 
139
        except BzrError:
 
140
            return False
 
141
        except urllib2.URLError:
 
142
            return False
 
143
        except IOError, e:
 
144
            if e.errno == errno.ENOENT:
 
145
                return False
 
146
            raise HttpTransportError(orig_error=e)
153
147
 
154
 
    def get(self, relpath):
 
148
    def get(self, relpath, decode=False):
155
149
        """Get the file at the given relative path.
156
150
 
157
151
        :param relpath: The relative path to the file
158
152
        """
159
 
        c = simple_wait_consumer(self.abspath(relpath))
160
 
        return c.get()
161
 
 
162
 
    ### def get_multi(self, relpaths, pb=None):
163
 
    ###     """Get a list of file-like objects, one for each entry in relpaths.
164
 
 
165
 
    ###     :param relpaths: A list of relative paths.
166
 
    ###     :param decode:  If True, assume the file is utf-8 encoded and
167
 
    ###                     decode it into Unicode
168
 
    ###     :param pb:  An optional ProgressBar for indicating percent done.
169
 
    ###     :return: A list or generator of file-like objects
170
 
    ###     """
171
 
    ###     consumers = []
172
 
    ###     for relpath in relpaths:
173
 
    ###         consumers.append(simple_wait_consumer(self.abspath(relpath)))
174
 
    ###     total = self._get_total(consumers)
175
 
    ###     count = 0
176
 
    ###     for c in consumers:
177
 
    ###         self._update_pb(pb, 'get', count, total)
178
 
    ###         yield c.get()
179
 
    ###         count += 1
 
153
        try:
 
154
            return get_url(self.abspath(relpath))
 
155
        except BzrError, e:
 
156
            raise NoSuchFile(orig_error=e)
 
157
        except urllib2.URLError, e:
 
158
            raise NoSuchFile(orig_error=e)
 
159
        except IOError, e:
 
160
            raise NoSuchFile(orig_error=e)
 
161
        except Exception,e:
 
162
            raise HttpTransportError(orig_error=e)
180
163
 
181
164
    def put(self, relpath, f):
182
165
        """Copy the file-like or string object into the location.