diff options
Diffstat (limited to 'module/network/HTTPDownload.py')
-rw-r--r-- | module/network/HTTPDownload.py | 91 |
1 files changed, 66 insertions, 25 deletions
diff --git a/module/network/HTTPDownload.py b/module/network/HTTPDownload.py index 1a2886332..13c674833 100644 --- a/module/network/HTTPDownload.py +++ b/module/network/HTTPDownload.py @@ -140,7 +140,7 @@ class HTTPDownload(): return self._download(chunks, False) else: - raise e + raise finally: self.close() @@ -161,7 +161,7 @@ class HTTPDownload(): lastFinishCheck = 0 lastTimeCheck = 0 - chunksDone = set() + chunksDone = set() # list of curl handles that are finished chunksCreated = False done = False if self.info.getCount() > 1: # This is a resume, if we were chunked originally assume still can @@ -202,32 +202,76 @@ class HTTPDownload(): t = time() # reduce these calls - while lastFinishCheck + 1 < t: + while lastFinishCheck + 0.5 < t: + # list of failed curl handles + failed = [] + ex = None # save only last exception, we can only raise one anyway + num_q, ok_list, err_list = self.m.info_read() for c in ok_list: - chunksDone.add(c) + chunk = self.findChunk(c) + try: # check if the header implies success, else add it to failed list + chunk.verifyHeader() + except BadHeader, e: + self.log.debug("Chunk %d failed: %s" % (chunk.id + 1, str(e))) + failed.append(chunk) + ex = e + else: + chunksDone.add(c) + for c in err_list: curl, errno, msg = c - #test if chunk was finished, otherwise raise the exception + chunk = self.findChunk(curl) + #test if chunk was finished if errno != 23 or "0 !=" not in msg: - raise pycurl.error(errno, msg) - - #@TODO KeyBoardInterrupts are seen as finished chunks, - #but normally not handled to this process, only in the testcase + failed.append(chunk) + ex = pycurl.error(errno, msg) + self.log.debug("Chunk %d failed: %s" % (chunk.id + 1, str(ex))) + continue + + try: # check if the header implies success, else add it to failed list + chunk.verifyHeader() + except BadHeader, e: + self.log.debug("Chunk %d failed: %s" % (chunk.id + 1, str(e))) + failed.append(chunk) + ex = e + else: + chunksDone.add(curl) + if not num_q: # no more infos to get + + # check if init is not finished so we reset download connections + # note that other chunks are closed and downloaded with init too + if failed and init not in failed and init.c not in chunksDone: + self.log.error(_("Download chunks failed, fallback to single connection | %s" % (str(ex)))) + + #list of chunks to clean and remove + to_clean = filter(lambda x: x is not init, self.chunks) + for chunk in to_clean: + self.closeChunk(chunk) + self.chunks.remove(chunk) + remove(self.info.getChunkName(chunk.id)) + + #let first chunk load the rest and update the info file + init.resetRange() + self.info.clear() + self.info.addChunk("%s.chunk0" % self.filename, (0, self.size)) + self.info.save() + elif failed: + raise ex - chunksDone.add(curl) - if not num_q: lastFinishCheck = t - if len(chunksDone) == len(self.chunks): - done = True #all chunks loaded + if len(chunksDone) >= len(self.chunks): + if len(chunksDone) > len(self.chunks): + self.log.warning("Finished download chunks size incorrect, please report bug.") + done = True #all chunks loaded break if done: break #all chunks loaded - # calc speed once per second + # calc speed once per second, averaging over 3 seconds if lastTimeCheck + 1 < t: diff = [c.arrived - (self.lastArrived[i] if len(self.lastArrived) > i else 0) for i, c in enumerate(self.chunks)] @@ -247,15 +291,7 @@ class HTTPDownload(): failed = False for chunk in self.chunks: - try: - chunk.verifyHeader() - except BadHeader, e: - failed = e.code - remove(self.info.getChunkName(chunk.id)) - - chunk.fp.flush() - fsync(chunk.fp.fileno()) #make sure everything was written to disk - chunk.fp.close() #needs to be closed, or merging chunks will fail + chunk.flushFile() #make sure downloads are written to disk if failed: raise BadHeader(failed) @@ -265,11 +301,16 @@ class HTTPDownload(): if self.progressNotify: self.progressNotify(self.percent) + def findChunk(self, handle): + """ linear search to find a chunk (should be ok since chunk size is usually low) """ + for chunk in self.chunks: + if chunk.c == handle: return chunk + def closeChunk(self, chunk): try: self.m.remove_handle(chunk.c) - except pycurl.error: - self.log.debug("Error removing chunk") + except pycurl.error, e: + self.log.debug("Error removing chunk: %s" % str(e)) finally: chunk.close() |