summaryrefslogtreecommitdiffstats
path: root/module/network/HTTPDownload.py
diff options
context:
space:
mode:
Diffstat (limited to 'module/network/HTTPDownload.py')
-rw-r--r--module/network/HTTPDownload.py91
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()