# any memory usage beyond this.
self.downloadable.write(plaintext)
+ def fail(self, why):
+ self.downloadable.fail(why)
+
def close(self):
self.crypttext_hash = self._crypttext_hasher.digest()
self.plaintext_hash = self._plaintext_hasher.digest()
self._num_needed_shares = d['needed_shares']
self._output = Output(downloadable, d['key'])
+ self._output.open()
self.active_buckets = {} # k: shnum, v: bucket
self._share_buckets = [] # list of (sharenum, bucket) tuples
d.addCallback(self._create_validated_buckets)
# once we know that, we can download blocks from everybody
d.addCallback(self._download_all_segments)
+ def _failed(why):
+ self._output.fail(why)
+ return why
+ d.addErrback(_failed)
d.addCallback(self._done)
return d
# of ValidatedBuckets, which themselves are wrappers around
# RIBucketReader references.
self.active_buckets = {} # k: shnum, v: ValidatedBucket instance
- self._output.open()
d = defer.succeed(None)
for segnum in range(self._total_segments-1):
self.f.write(data)
def close(self):
self.f.close()
- def fail(self):
+ def fail(self, why):
self.f.close()
os.unlink(self._filename)
def register_canceller(self, cb):
def close(self):
self.data = "".join(self._data)
del self._data
- def fail(self):
+ def fail(self, why):
del self._data
def register_canceller(self, cb):
pass # we won't use it
def close(self):
# the originator of the filehandle reserves the right to close it
pass
- def fail(self):
+ def fail(self, why):
pass
def register_canceller(self, cb):
pass
class IDownloadTarget(Interface):
def open():
- """Called before any calls to write() or close()."""
+ """Called before any calls to write(), close(), or fail()."""
def write(data):
"""Output some data to the target."""
def close():
"""Inform the target that there is no more data to be written."""
- def fail():
- """fail() is called to indicate that the download has failed. No
- further methods will be invoked on the IDownloadTarget after fail()."""
+ def fail(why):
+ """fail() is called to indicate that the download has failed. 'why'
+ is a Failure object indicating what went wrong. No further methods
+ will be invoked on the IDownloadTarget after fail()."""
def register_canceller(cb):
"""The FileDownloader uses this to register a no-argument function
that the target can call to cancel the download. Once this canceller
class IDownloader(Interface):
def download(uri, target):
"""Perform a CHK download, sending the data to the given target.
- 'target' must provide IDownloadTarget."""
+ 'target' must provide IDownloadTarget.
+
+ Returns a Deferred that fires (with the results of target.finish)
+ when the download is finished, or errbacks if something went wrong."""
class IUploadable(Interface):
def get_filehandle():
self.failUnlessEqual(page, self.data)
d.addCallback(_got_from_uri2)
+ # download from a bogus URI, make sure we get a reasonable error
+ def _get_from_bogus_uri(res):
+ return getPage(base + "download_uri/%s?filename=%s"
+ % (self.mangle_uri(self.uri), "mydata567"))
+ d.addCallback(_get_from_bogus_uri)
+ def _got_from_bogus_uri(page):
+ self.failUnlessEqual(page, "problem during download\n")
+ d.addCallback(_got_from_bogus_uri)
+
# download from a URI pasted into a form. Use POST, build a
# multipart/form-data, submit it. This actualy redirects us to a
# /download_uri?uri=%s URL, and twisted.web.client doesn't seem to
self._req.write(data)
def close(self):
self._req.finish()
- def fail(self):
+ def fail(self, why):
+ # I think the content-type is already set, and the response code has
+ # already been sent, so we can't provide a clean error indication. We
+ # can emit text (which a browser might interpret as something else),
+ # and if we sent a Size header, they might notice that we've
+ # truncated the data. Keep the error message small to improve the
+ # chances of having our error response be shorter than the intended
+ # results.
+ #
+ # We don't have a lot of options, unfortunately.
+
+ self._req.write("problem during download\n")
self._req.finish()
def register_canceller(self, cb):
pass
req.setHeader("content-type", type)
if encoding:
req.setHeader('content-encoding', encoding)
+ # TODO: it would be nice to set the size header here
- self._filenode.download(WebDownloadTarget(req))
+ d = self._filenode.download(WebDownloadTarget(req))
+ # exceptions during download are handled by the WebDownloadTarget
+ d.addErrback(lambda why: None)
return server.NOT_DONE_YET
class Manifest(rend.Page):