An internal class used to hold metadata for open HTTP files. Used mostly to reassemble fragmented transfers.
| 109 | |
| 110 | |
| 111 | class HTTPFile(object): |
| 112 | """ |
| 113 | An internal class used to hold metadata for open HTTP files. |
| 114 | Used mostly to reassemble fragmented transfers. |
| 115 | """ |
| 116 | |
| 117 | def __init__(self, filename, plugin_instance): |
| 118 | self.complete = False |
| 119 | # Expected size in bytes of full file transfer |
| 120 | self.size = 0 |
| 121 | # List of tuples indicating byte chunks already received and written to |
| 122 | # disk |
| 123 | self.ranges = [] |
| 124 | self.plugin = plugin_instance |
| 125 | self.filename = filename |
| 126 | try: |
| 127 | self.fh = open(filename, 'wb') |
| 128 | except IOError as e: |
| 129 | self.plugin.error( |
| 130 | "Could not create file {!r}: {!s}".format(filename, e)) |
| 131 | self.fh = None |
| 132 | |
| 133 | def __del__(self): |
| 134 | if self.fh is None: |
| 135 | return |
| 136 | self.fh.close() |
| 137 | if not self.done(): |
| 138 | self.plugin.warning("Incomplete file: {!r}".format(self.filename)) |
| 139 | try: |
| 140 | os.rename(self.filename, self.filename + "_INCOMPLETE") |
| 141 | except: |
| 142 | pass |
| 143 | ls = 0 |
| 144 | le = 0 |
| 145 | for s, e in self.ranges: |
| 146 | if s > le + 1: |
| 147 | self.plugin.warning( |
| 148 | "Missing bytes between {0} and {1}".format(le, s)) |
| 149 | ls, le = s, e |
| 150 | |
| 151 | def handleresponse(self, response): |
| 152 | # Check for Content Range |
| 153 | range_start = 0 |
| 154 | range_end = len(response.body) - 1 |
| 155 | if 'content-range' in response.headers: |
| 156 | m = re.search( |
| 157 | 'bytes (\d+)-(\d+)/(\d+|\*)', response.headers['content-range']) |
| 158 | if m: |
| 159 | range_start = int(m.group(1)) |
| 160 | range_end = int(m.group(2)) |
| 161 | if len(response.body) < (range_end - range_start + 1): |
| 162 | range_end = range_start + len(response.body) - 1 |
| 163 | try: |
| 164 | if int(m.group(3)) > self.size: |
| 165 | self.size = int(m.group(3)) |
| 166 | except: |
| 167 | pass |
| 168 | elif 'content-length' in response.headers: |