##// END OF EJS Templates
largefiles: fix 'unexpected response' warning newlines...
Mads Kiilerich -
r19947:2a03faf8 stable
parent child Browse files
Show More
@@ -1,177 +1,177 b''
1 # Copyright 2011 Fog Creek Software
1 # Copyright 2011 Fog Creek Software
2 #
2 #
3 # This software may be used and distributed according to the terms of the
3 # This software may be used and distributed according to the terms of the
4 # GNU General Public License version 2 or any later version.
4 # GNU General Public License version 2 or any later version.
5
5
6 import os
6 import os
7 import urllib2
7 import urllib2
8 import re
8 import re
9
9
10 from mercurial import error, httppeer, util, wireproto
10 from mercurial import error, httppeer, util, wireproto
11 from mercurial.wireproto import batchable, future
11 from mercurial.wireproto import batchable, future
12 from mercurial.i18n import _
12 from mercurial.i18n import _
13
13
14 import lfutil
14 import lfutil
15
15
16 LARGEFILES_REQUIRED_MSG = ('\nThis repository uses the largefiles extension.'
16 LARGEFILES_REQUIRED_MSG = ('\nThis repository uses the largefiles extension.'
17 '\n\nPlease enable it in your Mercurial config '
17 '\n\nPlease enable it in your Mercurial config '
18 'file.\n')
18 'file.\n')
19
19
20 # these will all be replaced by largefiles.uisetup
20 # these will all be replaced by largefiles.uisetup
21 capabilitiesorig = None
21 capabilitiesorig = None
22 ssholdcallstream = None
22 ssholdcallstream = None
23 httpoldcallstream = None
23 httpoldcallstream = None
24
24
25 def putlfile(repo, proto, sha):
25 def putlfile(repo, proto, sha):
26 '''Put a largefile into a repository's local store and into the
26 '''Put a largefile into a repository's local store and into the
27 user cache.'''
27 user cache.'''
28 proto.redirect()
28 proto.redirect()
29
29
30 path = lfutil.storepath(repo, sha)
30 path = lfutil.storepath(repo, sha)
31 util.makedirs(os.path.dirname(path))
31 util.makedirs(os.path.dirname(path))
32 tmpfp = util.atomictempfile(path, createmode=repo.store.createmode)
32 tmpfp = util.atomictempfile(path, createmode=repo.store.createmode)
33
33
34 try:
34 try:
35 try:
35 try:
36 proto.getfile(tmpfp)
36 proto.getfile(tmpfp)
37 tmpfp._fp.seek(0)
37 tmpfp._fp.seek(0)
38 if sha != lfutil.hexsha1(tmpfp._fp):
38 if sha != lfutil.hexsha1(tmpfp._fp):
39 raise IOError(0, _('largefile contents do not match hash'))
39 raise IOError(0, _('largefile contents do not match hash'))
40 tmpfp.close()
40 tmpfp.close()
41 lfutil.linktousercache(repo, sha)
41 lfutil.linktousercache(repo, sha)
42 except IOError, e:
42 except IOError, e:
43 repo.ui.warn(_('largefiles: failed to put %s into store: %s') %
43 repo.ui.warn(_('largefiles: failed to put %s into store: %s') %
44 (sha, e.strerror))
44 (sha, e.strerror))
45 return wireproto.pushres(1)
45 return wireproto.pushres(1)
46 finally:
46 finally:
47 tmpfp.discard()
47 tmpfp.discard()
48
48
49 return wireproto.pushres(0)
49 return wireproto.pushres(0)
50
50
51 def getlfile(repo, proto, sha):
51 def getlfile(repo, proto, sha):
52 '''Retrieve a largefile from the repository-local cache or system
52 '''Retrieve a largefile from the repository-local cache or system
53 cache.'''
53 cache.'''
54 filename = lfutil.findfile(repo, sha)
54 filename = lfutil.findfile(repo, sha)
55 if not filename:
55 if not filename:
56 raise util.Abort(_('requested largefile %s not present in cache') % sha)
56 raise util.Abort(_('requested largefile %s not present in cache') % sha)
57 f = open(filename, 'rb')
57 f = open(filename, 'rb')
58 length = os.fstat(f.fileno())[6]
58 length = os.fstat(f.fileno())[6]
59
59
60 # Since we can't set an HTTP content-length header here, and
60 # Since we can't set an HTTP content-length header here, and
61 # Mercurial core provides no way to give the length of a streamres
61 # Mercurial core provides no way to give the length of a streamres
62 # (and reading the entire file into RAM would be ill-advised), we
62 # (and reading the entire file into RAM would be ill-advised), we
63 # just send the length on the first line of the response, like the
63 # just send the length on the first line of the response, like the
64 # ssh proto does for string responses.
64 # ssh proto does for string responses.
65 def generator():
65 def generator():
66 yield '%d\n' % length
66 yield '%d\n' % length
67 for chunk in util.filechunkiter(f):
67 for chunk in util.filechunkiter(f):
68 yield chunk
68 yield chunk
69 return wireproto.streamres(generator())
69 return wireproto.streamres(generator())
70
70
71 def statlfile(repo, proto, sha):
71 def statlfile(repo, proto, sha):
72 '''Return '2\n' if the largefile is missing, '0\n' if it seems to be in
72 '''Return '2\n' if the largefile is missing, '0\n' if it seems to be in
73 good condition.
73 good condition.
74
74
75 The value 1 is reserved for mismatched checksum, but that is too expensive
75 The value 1 is reserved for mismatched checksum, but that is too expensive
76 to be verified on every stat and must be caught be running 'hg verify'
76 to be verified on every stat and must be caught be running 'hg verify'
77 server side.'''
77 server side.'''
78 filename = lfutil.findfile(repo, sha)
78 filename = lfutil.findfile(repo, sha)
79 if not filename:
79 if not filename:
80 return '2\n'
80 return '2\n'
81 return '0\n'
81 return '0\n'
82
82
83 def wirereposetup(ui, repo):
83 def wirereposetup(ui, repo):
84 class lfileswirerepository(repo.__class__):
84 class lfileswirerepository(repo.__class__):
85 def putlfile(self, sha, fd):
85 def putlfile(self, sha, fd):
86 # unfortunately, httprepository._callpush tries to convert its
86 # unfortunately, httprepository._callpush tries to convert its
87 # input file-like into a bundle before sending it, so we can't use
87 # input file-like into a bundle before sending it, so we can't use
88 # it ...
88 # it ...
89 if issubclass(self.__class__, httppeer.httppeer):
89 if issubclass(self.__class__, httppeer.httppeer):
90 res = None
90 res = None
91 try:
91 try:
92 res = self._call('putlfile', data=fd, sha=sha,
92 res = self._call('putlfile', data=fd, sha=sha,
93 headers={'content-type':'application/mercurial-0.1'})
93 headers={'content-type':'application/mercurial-0.1'})
94 d, output = res.split('\n', 1)
94 d, output = res.split('\n', 1)
95 for l in output.splitlines(True):
95 for l in output.splitlines(True):
96 self.ui.warn(_('remote: '), l, '\n')
96 self.ui.warn(_('remote: '), l, '\n')
97 return int(d)
97 return int(d)
98 except (ValueError, urllib2.HTTPError):
98 except (ValueError, urllib2.HTTPError):
99 self.ui.warn(_('unexpected putlfile response: %s') % res)
99 self.ui.warn(_('unexpected putlfile response: %r\n') % res)
100 return 1
100 return 1
101 # ... but we can't use sshrepository._call because the data=
101 # ... but we can't use sshrepository._call because the data=
102 # argument won't get sent, and _callpush does exactly what we want
102 # argument won't get sent, and _callpush does exactly what we want
103 # in this case: send the data straight through
103 # in this case: send the data straight through
104 else:
104 else:
105 try:
105 try:
106 ret, output = self._callpush("putlfile", fd, sha=sha)
106 ret, output = self._callpush("putlfile", fd, sha=sha)
107 if ret == "":
107 if ret == "":
108 raise error.ResponseError(_('putlfile failed:'),
108 raise error.ResponseError(_('putlfile failed:'),
109 output)
109 output)
110 return int(ret)
110 return int(ret)
111 except IOError:
111 except IOError:
112 return 1
112 return 1
113 except ValueError:
113 except ValueError:
114 raise error.ResponseError(
114 raise error.ResponseError(
115 _('putlfile failed (unexpected response):'), ret)
115 _('putlfile failed (unexpected response):'), ret)
116
116
117 def getlfile(self, sha):
117 def getlfile(self, sha):
118 """returns an iterable with the chunks of the file with sha sha"""
118 """returns an iterable with the chunks of the file with sha sha"""
119 stream = self._callstream("getlfile", sha=sha)
119 stream = self._callstream("getlfile", sha=sha)
120 length = stream.readline()
120 length = stream.readline()
121 try:
121 try:
122 length = int(length)
122 length = int(length)
123 except ValueError:
123 except ValueError:
124 self._abort(error.ResponseError(_("unexpected response:"),
124 self._abort(error.ResponseError(_("unexpected response:"),
125 length))
125 length))
126
126
127 # SSH streams will block if reading more than length
127 # SSH streams will block if reading more than length
128 for chunk in util.filechunkiter(stream, 128 * 1024, length):
128 for chunk in util.filechunkiter(stream, 128 * 1024, length):
129 yield chunk
129 yield chunk
130 # HTTP streams must hit the end to process the last empty
130 # HTTP streams must hit the end to process the last empty
131 # chunk of Chunked-Encoding so the connection can be reused.
131 # chunk of Chunked-Encoding so the connection can be reused.
132 if issubclass(self.__class__, httppeer.httppeer):
132 if issubclass(self.__class__, httppeer.httppeer):
133 chunk = stream.read(1)
133 chunk = stream.read(1)
134 if chunk:
134 if chunk:
135 self._abort(error.ResponseError(_("unexpected response:"),
135 self._abort(error.ResponseError(_("unexpected response:"),
136 chunk))
136 chunk))
137
137
138 @batchable
138 @batchable
139 def statlfile(self, sha):
139 def statlfile(self, sha):
140 f = future()
140 f = future()
141 result = {'sha': sha}
141 result = {'sha': sha}
142 yield result, f
142 yield result, f
143 try:
143 try:
144 yield int(f.value)
144 yield int(f.value)
145 except (ValueError, urllib2.HTTPError):
145 except (ValueError, urllib2.HTTPError):
146 # If the server returns anything but an integer followed by a
146 # If the server returns anything but an integer followed by a
147 # newline, newline, it's not speaking our language; if we get
147 # newline, newline, it's not speaking our language; if we get
148 # an HTTP error, we can't be sure the largefile is present;
148 # an HTTP error, we can't be sure the largefile is present;
149 # either way, consider it missing.
149 # either way, consider it missing.
150 yield 2
150 yield 2
151
151
152 repo.__class__ = lfileswirerepository
152 repo.__class__ = lfileswirerepository
153
153
154 # advertise the largefiles=serve capability
154 # advertise the largefiles=serve capability
155 def capabilities(repo, proto):
155 def capabilities(repo, proto):
156 return capabilitiesorig(repo, proto) + ' largefiles=serve'
156 return capabilitiesorig(repo, proto) + ' largefiles=serve'
157
157
158 def heads(repo, proto):
158 def heads(repo, proto):
159 if lfutil.islfilesrepo(repo):
159 if lfutil.islfilesrepo(repo):
160 return wireproto.ooberror(LARGEFILES_REQUIRED_MSG)
160 return wireproto.ooberror(LARGEFILES_REQUIRED_MSG)
161 return wireproto.heads(repo, proto)
161 return wireproto.heads(repo, proto)
162
162
163 def sshrepocallstream(self, cmd, **args):
163 def sshrepocallstream(self, cmd, **args):
164 if cmd == 'heads' and self.capable('largefiles'):
164 if cmd == 'heads' and self.capable('largefiles'):
165 cmd = 'lheads'
165 cmd = 'lheads'
166 if cmd == 'batch' and self.capable('largefiles'):
166 if cmd == 'batch' and self.capable('largefiles'):
167 args['cmds'] = args['cmds'].replace('heads ', 'lheads ')
167 args['cmds'] = args['cmds'].replace('heads ', 'lheads ')
168 return ssholdcallstream(self, cmd, **args)
168 return ssholdcallstream(self, cmd, **args)
169
169
170 headsre = re.compile(r'(^|;)heads\b')
170 headsre = re.compile(r'(^|;)heads\b')
171
171
172 def httprepocallstream(self, cmd, **args):
172 def httprepocallstream(self, cmd, **args):
173 if cmd == 'heads' and self.capable('largefiles'):
173 if cmd == 'heads' and self.capable('largefiles'):
174 cmd = 'lheads'
174 cmd = 'lheads'
175 if cmd == 'batch' and self.capable('largefiles'):
175 if cmd == 'batch' and self.capable('largefiles'):
176 args['cmds'] = headsre.sub('lheads', args['cmds'])
176 args['cmds'] = headsre.sub('lheads', args['cmds'])
177 return httpoldcallstream(self, cmd, **args)
177 return httpoldcallstream(self, cmd, **args)
General Comments 0
You need to be logged in to leave comments. Login now