Samesite - proxy that can cache partial transfers

Hex Artifact Content
anonymous

Hex Artifact Content

Artifact f1c4262f2edf836c9c55f863612cb9dd82965d4e42f02afb2b62e59288ce397c:


0000: 23 21 2f 75 73 72 2f 62 69 6e 2f 65 6e 76 20 70  #!/usr/bin/env p
0010: 79 74 68 6f 6e 33 2e 31 0a 0a 69 6d 70 6f 72 74  ython3.1..import
0020: 20 64 61 74 65 74 69 6d 65 2c 20 68 74 74 70 2e   datetime, http.
0030: 63 6f 6f 6b 69 65 6a 61 72 2c 20 6f 73 2c 20 73  cookiejar, os, s
0040: 79 73 2c 20 73 68 65 6c 76 65 2c 20 73 70 61 63  ys, shelve, spac
0050: 65 6d 61 70 2c 20 72 65 2c 20 75 72 6c 6c 69 62  emap, re, urllib
0060: 2e 72 65 71 75 65 73 74 0a 0a 63 6c 61 73 73 20  .request..class 
0070: 43 6f 6e 66 69 67 3a 0a 09 5f 5f 73 6c 6f 74 73  Config:..__slots
0080: 5f 5f 20 3d 20 66 72 6f 7a 65 6e 73 65 74 28 5b  __ = frozenset([
0090: 27 5f 63 6f 6e 66 69 67 27 2c 20 27 5f 64 65 66  '_config', '_def
00a0: 61 75 6c 74 27 2c 20 27 5f 73 65 63 74 69 6f 6e  ault', '_section
00b0: 27 2c 20 27 6f 70 74 69 6f 6e 73 27 2c 20 27 72  ', 'options', 'r
00c0: 6f 6f 74 27 5d 29 0a 09 5f 64 65 66 61 75 6c 74  oot']).._default
00d0: 20 3d 20 7b 0a 09 09 27 67 65 6e 65 72 61 6c 27   = {...'general'
00e0: 3a 20 7b 0a 09 09 09 27 70 6f 72 74 27 3a 20 27  : {....'port': '
00f0: 38 30 30 38 27 2c 0a 09 09 7d 2c 0a 09 09 27 5f  8008',...},...'_
0100: 6f 74 68 65 72 27 3a 20 7b 0a 09 09 09 27 76 65  other': {....'ve
0110: 72 62 6f 73 65 27 3a 20 27 6e 6f 27 2c 0a 09 09  rbose': 'no',...
0120: 09 27 6e 6f 65 74 61 67 27 3a 20 27 6e 6f 27 2c  .'noetag': 'no',
0130: 0a 09 09 09 27 6e 6f 70 61 72 74 73 27 3a 20 27  ....'noparts': '
0140: 6e 6f 27 2c 0a 09 7d 2c 7d 0a 0a 09 23 20 66 75  no',..},}...# fu
0150: 6e 63 74 69 6f 6e 20 74 6f 20 72 65 61 64 20 69  nction to read i
0160: 6e 20 63 6f 6e 66 69 67 20 66 69 6c 65 0a 09 64  n config file..d
0170: 65 66 20 5f 5f 69 6e 69 74 5f 5f 28 73 65 6c 66  ef __init__(self
0180: 29 3a 0a 09 09 69 6d 70 6f 72 74 20 63 6f 6e 66  ):...import conf
0190: 69 67 70 61 72 73 65 72 2c 20 6f 70 74 70 61 72  igparser, optpar
01a0: 73 65 0a 0a 09 09 70 61 72 73 65 72 20 3d 20 6f  se....parser = o
01b0: 70 74 70 61 72 73 65 2e 4f 70 74 69 6f 6e 50 61  ptparse.OptionPa
01c0: 72 73 65 72 28 29 0a 09 09 70 61 72 73 65 72 2e  rser()...parser.
01d0: 61 64 64 5f 6f 70 74 69 6f 6e 28 27 2d 63 27 2c  add_option('-c',
01e0: 20 27 2d 2d 63 6f 6e 66 69 67 27 2c 20 64 65 73   '--config', des
01f0: 74 20 3d 20 27 63 6f 6e 66 69 67 27 2c 20 68 65  t = 'config', he
0200: 6c 70 20 3d 20 27 63 6f 6e 66 69 67 20 66 69 6c  lp = 'config fil
0210: 65 20 6c 6f 63 61 74 69 6f 6e 27 2c 20 6d 65 74  e location', met
0220: 61 76 61 72 20 3d 20 27 46 49 4c 45 27 2c 20 64  avar = 'FILE', d
0230: 65 66 61 75 6c 74 20 3d 20 27 73 61 6d 65 73 69  efault = 'samesi
0240: 74 65 2e 63 6f 6e 66 27 29 0a 09 09 28 73 65 6c  te.conf')...(sel
0250: 66 2e 6f 70 74 69 6f 6e 73 2c 20 61 72 67 73 29  f.options, args)
0260: 20 3d 20 70 61 72 73 65 72 2e 70 61 72 73 65 5f   = parser.parse_
0270: 61 72 67 73 28 29 0a 0a 09 09 61 73 73 65 72 74  args()....assert
0280: 20 6f 73 2e 61 63 63 65 73 73 28 73 65 6c 66 2e   os.access(self.
0290: 6f 70 74 69 6f 6e 73 2e 63 6f 6e 66 69 67 2c 20  options.config, 
02a0: 6f 73 2e 52 5f 4f 4b 29 2c 20 22 46 61 74 61 6c  os.R_OK), "Fatal
02b0: 20 65 72 72 6f 72 3a 20 63 61 6e 27 74 20 72 65   error: can't re
02c0: 61 64 20 7b 7d 22 2e 66 6f 72 6d 61 74 28 73 65  ad {}".format(se
02d0: 6c 66 2e 6f 70 74 69 6f 6e 73 2e 63 6f 6e 66 69  lf.options.confi
02e0: 67 29 0a 0a 09 09 63 6f 6e 66 69 67 44 69 72 20  g)....configDir 
02f0: 3d 20 72 65 2e 63 6f 6d 70 69 6c 65 28 27 5e 28  = re.compile('^(
0300: 2e 2a 29 2f 5b 5e 2f 5d 2b 24 27 29 2e 6d 61 74  .*)/[^/]+$').mat
0310: 63 68 28 73 65 6c 66 2e 6f 70 74 69 6f 6e 73 2e  ch(self.options.
0320: 63 6f 6e 66 69 67 29 0a 09 09 69 66 20 63 6f 6e  config)...if con
0330: 66 69 67 44 69 72 3a 0a 09 09 09 73 65 6c 66 2e  figDir:....self.
0340: 72 6f 6f 74 20 3d 20 63 6f 6e 66 69 67 44 69 72  root = configDir
0350: 2e 67 72 6f 75 70 28 31 29 0a 09 09 65 6c 73 65  .group(1)...else
0360: 3a 0a 09 09 09 73 65 6c 66 2e 72 6f 6f 74 20 3d  :....self.root =
0370: 20 6f 73 2e 67 65 74 63 77 64 28 29 0a 0a 09 09   os.getcwd()....
0380: 73 65 6c 66 2e 5f 63 6f 6e 66 69 67 20 3d 20 63  self._config = c
0390: 6f 6e 66 69 67 70 61 72 73 65 72 2e 43 6f 6e 66  onfigparser.Conf
03a0: 69 67 50 61 72 73 65 72 28 29 0a 09 09 73 65 6c  igParser()...sel
03b0: 66 2e 5f 63 6f 6e 66 69 67 2e 72 65 61 64 66 70  f._config.readfp
03c0: 28 6f 70 65 6e 28 73 65 6c 66 2e 6f 70 74 69 6f  (open(self.optio
03d0: 6e 73 2e 63 6f 6e 66 69 67 29 29 0a 0a 09 09 66  ns.config))....f
03e0: 6f 72 20 73 65 63 74 69 6f 6e 20 69 6e 20 73 65  or section in se
03f0: 6c 66 2e 5f 63 6f 6e 66 69 67 2e 73 65 63 74 69  lf._config.secti
0400: 6f 6e 73 28 29 3a 0a 09 09 09 69 66 20 73 65 63  ons():....if sec
0410: 74 69 6f 6e 20 21 3d 20 27 67 65 6e 65 72 61 6c  tion != 'general
0420: 27 3a 0a 09 09 09 09 69 66 20 73 65 6c 66 2e 5f  ':.....if self._
0430: 63 6f 6e 66 69 67 2e 68 61 73 5f 6f 70 74 69 6f  config.has_optio
0440: 6e 28 73 65 63 74 69 6f 6e 2c 20 27 64 69 72 27  n(section, 'dir'
0450: 29 3a 0a 09 09 09 09 09 69 66 20 72 65 2e 63 6f  ):......if re.co
0460: 6d 70 69 6c 65 28 27 5e 2f 24 27 29 2e 6d 61 74  mpile('^/$').mat
0470: 63 68 28 73 65 6c 66 2e 5f 63 6f 6e 66 69 67 2e  ch(self._config.
0480: 67 65 74 28 73 65 63 74 69 6f 6e 2c 20 27 64 69  get(section, 'di
0490: 72 27 29 29 3a 0a 09 09 09 09 09 09 73 65 6c 66  r')):.......self
04a0: 2e 5f 63 6f 6e 66 69 67 2e 73 65 74 28 73 65 63  ._config.set(sec
04b0: 74 69 6f 6e 2c 20 27 64 69 72 27 2c 20 73 65 6c  tion, 'dir', sel
04c0: 66 2e 72 6f 6f 74 20 2b 20 6f 73 2e 73 65 70 20  f.root + os.sep 
04d0: 2b 20 73 65 63 74 69 6f 6e 29 0a 09 09 09 09 09  + section)......
04e0: 74 68 69 73 44 69 72 20 3d 20 72 65 2e 63 6f 6d  thisDir = re.com
04f0: 70 69 6c 65 28 27 5e 28 2e 2a 29 2f 24 27 29 2e  pile('^(.*)/$').
0500: 6d 61 74 63 68 28 73 65 6c 66 2e 5f 63 6f 6e 66  match(self._conf
0510: 69 67 2e 67 65 74 28 73 65 63 74 69 6f 6e 2c 20  ig.get(section, 
0520: 27 64 69 72 27 29 29 0a 09 09 09 09 09 69 66 20  'dir'))......if 
0530: 74 68 69 73 44 69 72 3a 0a 09 09 09 09 09 09 73  thisDir:.......s
0540: 65 6c 66 2e 5f 63 6f 6e 66 69 67 2e 73 65 74 28  elf._config.set(
0550: 73 65 63 74 69 6f 6e 2c 20 27 64 69 72 27 2c 20  section, 'dir', 
0560: 74 68 69 73 44 69 72 2e 67 72 6f 75 70 28 31 29  thisDir.group(1)
0570: 29 0a 09 09 09 09 09 69 66 20 6e 6f 74 20 72 65  )......if not re
0580: 2e 63 6f 6d 70 69 6c 65 28 27 5e 2f 28 2e 2a 29  .compile('^/(.*)
0590: 24 27 29 2e 6d 61 74 63 68 28 73 65 6c 66 2e 5f  $').match(self._
05a0: 63 6f 6e 66 69 67 2e 67 65 74 28 73 65 63 74 69  config.get(secti
05b0: 6f 6e 2c 20 27 64 69 72 27 29 29 3a 0a 09 09 09  on, 'dir')):....
05c0: 09 09 09 73 65 6c 66 2e 5f 63 6f 6e 66 69 67 2e  ...self._config.
05d0: 73 65 74 28 73 65 63 74 69 6f 6e 2c 20 27 64 69  set(section, 'di
05e0: 72 27 2c 20 73 65 6c 66 2e 72 6f 6f 74 20 2b 20  r', self.root + 
05f0: 6f 73 2e 73 65 70 20 2b 20 73 65 6c 66 2e 5f 63  os.sep + self._c
0600: 6f 6e 66 69 67 2e 67 65 74 28 73 65 63 74 69 6f  onfig.get(sectio
0610: 6e 2c 20 27 64 69 72 27 29 29 0a 09 09 09 09 65  n, 'dir')).....e
0620: 6c 73 65 3a 0a 09 09 09 09 09 73 65 6c 66 2e 5f  lse:......self._
0630: 63 6f 6e 66 69 67 2e 73 65 74 28 73 65 63 74 69  config.set(secti
0640: 6f 6e 2c 20 27 64 69 72 27 2c 20 73 65 6c 66 2e  on, 'dir', self.
0650: 72 6f 6f 74 20 2b 20 6f 73 2e 73 65 70 20 2b 20  root + os.sep + 
0660: 73 65 63 74 69 6f 6e 29 0a 0a 09 09 09 09 69 66  section)......if
0670: 20 6e 6f 74 20 73 65 6c 66 2e 5f 63 6f 6e 66 69   not self._confi
0680: 67 2e 68 61 73 5f 6f 70 74 69 6f 6e 28 73 65 63  g.has_option(sec
0690: 74 69 6f 6e 2c 20 27 72 6f 6f 74 27 29 3a 0a 09  tion, 'root'):..
06a0: 09 09 09 09 73 65 6c 66 2e 5f 63 6f 6e 66 69 67  ....self._config
06b0: 2e 73 65 74 28 73 65 63 74 69 6f 6e 2c 20 27 72  .set(section, 'r
06c0: 6f 6f 74 27 2c 20 73 65 63 74 69 6f 6e 29 0a 0a  oot', section)..
06d0: 09 23 20 66 75 6e 63 74 69 6f 6e 20 74 6f 20 73  .# function to s
06e0: 65 6c 65 63 74 20 63 6f 6e 66 69 67 20 66 69 6c  elect config fil
06f0: 65 20 73 65 63 74 69 6f 6e 20 6f 72 20 63 72 65  e section or cre
0700: 61 74 65 20 6f 6e 65 0a 09 64 65 66 20 73 65 63  ate one..def sec
0710: 74 69 6f 6e 28 73 65 6c 66 2c 20 73 65 63 74 69  tion(self, secti
0720: 6f 6e 29 3a 0a 09 09 69 66 20 6e 6f 74 20 73 65  on):...if not se
0730: 6c 66 2e 5f 63 6f 6e 66 69 67 2e 68 61 73 5f 73  lf._config.has_s
0740: 65 63 74 69 6f 6e 28 73 65 63 74 69 6f 6e 29 3a  ection(section):
0750: 0a 09 09 09 73 65 6c 66 2e 5f 63 6f 6e 66 69 67  ....self._config
0760: 2e 61 64 64 5f 73 65 63 74 69 6f 6e 28 73 65 63  .add_section(sec
0770: 74 69 6f 6e 29 0a 09 09 73 65 6c 66 2e 5f 73 65  tion)...self._se
0780: 63 74 69 6f 6e 20 3d 20 73 65 63 74 69 6f 6e 0a  ction = section.
0790: 0a 09 23 20 66 75 6e 63 74 69 6f 6e 20 74 6f 20  ..# function to 
07a0: 67 65 74 20 63 6f 6e 66 69 67 20 70 61 72 61 6d  get config param
07b0: 65 74 65 72 2c 20 69 66 20 70 61 72 61 6d 65 74  eter, if paramet
07c0: 65 72 20 64 6f 65 73 6e 27 74 20 65 78 69 73 74  er doesn't exist
07d0: 73 20 74 68 65 20 64 65 66 61 75 6c 74 0a 09 23  s the default..#
07e0: 20 76 61 6c 75 65 20 6f 72 20 4e 6f 6e 65 20 69   value or None i
07f0: 73 20 73 75 62 73 74 69 74 75 74 65 64 0a 09 64  s substituted..d
0800: 65 66 20 5f 5f 67 65 74 69 74 65 6d 5f 5f 28 73  ef __getitem__(s
0810: 65 6c 66 2c 20 6e 61 6d 65 29 3a 0a 09 09 69 66  elf, name):...if
0820: 20 6e 6f 74 20 73 65 6c 66 2e 5f 63 6f 6e 66 69   not self._confi
0830: 67 2e 68 61 73 5f 6f 70 74 69 6f 6e 28 73 65 6c  g.has_option(sel
0840: 66 2e 5f 73 65 63 74 69 6f 6e 2c 20 6e 61 6d 65  f._section, name
0850: 29 3a 0a 09 09 09 69 66 20 73 65 6c 66 2e 5f 73  ):....if self._s
0860: 65 63 74 69 6f 6e 20 69 6e 20 73 65 6c 66 2e 5f  ection in self._
0870: 64 65 66 61 75 6c 74 3a 0a 09 09 09 09 69 66 20  default:.....if 
0880: 6e 61 6d 65 20 69 6e 20 73 65 6c 66 2e 5f 64 65  name in self._de
0890: 66 61 75 6c 74 5b 73 65 6c 66 2e 5f 73 65 63 74  fault[self._sect
08a0: 69 6f 6e 5d 3a 0a 09 09 09 09 09 73 65 6c 66 2e  ion]:......self.
08b0: 5f 63 6f 6e 66 69 67 2e 73 65 74 28 73 65 6c 66  _config.set(self
08c0: 2e 5f 73 65 63 74 69 6f 6e 2c 20 6e 61 6d 65 2c  ._section, name,
08d0: 20 73 65 6c 66 2e 5f 64 65 66 61 75 6c 74 5b 73   self._default[s
08e0: 65 6c 66 2e 5f 73 65 63 74 69 6f 6e 5d 5b 6e 61  elf._section][na
08f0: 6d 65 5d 29 0a 09 09 09 09 65 6c 73 65 3a 0a 09  me]).....else:..
0900: 09 09 09 09 73 65 6c 66 2e 5f 63 6f 6e 66 69 67  ....self._config
0910: 2e 73 65 74 28 73 65 6c 66 2e 5f 73 65 63 74 69  .set(self._secti
0920: 6f 6e 2c 20 6e 61 6d 65 2c 20 4e 6f 6e 65 29 0a  on, name, None).
0930: 09 09 09 65 6c 69 66 20 6e 61 6d 65 20 69 6e 20  ...elif name in 
0940: 73 65 6c 66 2e 5f 64 65 66 61 75 6c 74 5b 27 5f  self._default['_
0950: 6f 74 68 65 72 27 5d 3a 0a 09 09 09 09 73 65 6c  other']:.....sel
0960: 66 2e 5f 63 6f 6e 66 69 67 2e 73 65 74 28 73 65  f._config.set(se
0970: 6c 66 2e 5f 73 65 63 74 69 6f 6e 2c 20 6e 61 6d  lf._section, nam
0980: 65 2c 20 73 65 6c 66 2e 5f 64 65 66 61 75 6c 74  e, self._default
0990: 5b 27 5f 6f 74 68 65 72 27 5d 5b 6e 61 6d 65 5d  ['_other'][name]
09a0: 29 0a 09 09 09 65 6c 73 65 3a 0a 09 09 09 09 73  )....else:.....s
09b0: 65 6c 66 2e 5f 63 6f 6e 66 69 67 2e 73 65 74 28  elf._config.set(
09c0: 73 65 6c 66 2e 5f 73 65 63 74 69 6f 6e 2c 20 6e  self._section, n
09d0: 61 6d 65 2c 20 4e 6f 6e 65 29 0a 09 09 72 65 74  ame, None)...ret
09e0: 75 72 6e 28 73 65 6c 66 2e 5f 63 6f 6e 66 69 67  urn(self._config
09f0: 2e 67 65 74 28 73 65 6c 66 2e 5f 73 65 63 74 69  .get(self._secti
0a00: 6f 6e 2c 20 6e 61 6d 65 29 29 0a 0a 63 6f 6e 66  on, name))..conf
0a10: 69 67 20 3d 20 43 6f 6e 66 69 67 28 29 0a 0a 23  ig = Config()..#
0a20: 61 73 73 65 72 74 20 6f 70 74 69 6f 6e 73 2e 70  assert options.p
0a30: 6f 72 74 20 6f 72 20 6f 73 2e 61 63 63 65 73 73  ort or os.access
0a40: 28 6f 70 74 69 6f 6e 73 2e 6c 6f 67 2c 20 6f 73  (options.log, os
0a50: 2e 52 5f 4f 4b 29 2c 20 27 4c 6f 67 20 66 69 6c  .R_OK), 'Log fil
0a60: 65 20 75 6e 72 65 61 64 61 62 6c 65 27 0a 0a 63  e unreadable'..c
0a70: 6f 6e 73 74 5f 64 65 73 63 5f 66 69 65 6c 64 73  onst_desc_fields
0a80: 20 3d 20 73 65 74 28 5b 27 43 6f 6e 74 65 6e 74   = set(['Content
0a90: 2d 4c 65 6e 67 74 68 27 2c 20 27 50 72 61 67 6d  -Length', 'Pragm
0aa0: 61 27 2c 20 27 4c 61 73 74 2d 4d 6f 64 69 66 69  a', 'Last-Modifi
0ab0: 65 64 27 5d 29 0a 63 6f 6e 73 74 5f 69 67 6e 6f  ed']).const_igno
0ac0: 72 65 5f 66 69 65 6c 64 73 20 3d 20 73 65 74 28  re_fields = set(
0ad0: 5b 27 41 63 63 65 70 74 2d 52 61 6e 67 65 73 27  ['Accept-Ranges'
0ae0: 2c 20 27 41 67 65 27 2c 20 27 43 61 63 68 65 2d  , 'Age', 'Cache-
0af0: 43 6f 6e 74 72 6f 6c 27 2c 20 27 43 6f 6e 6e 65  Control', 'Conne
0b00: 63 74 69 6f 6e 27 2c 20 27 43 6f 6e 74 65 6e 74  ction', 'Content
0b10: 2d 54 79 70 65 27 2c 20 27 44 61 74 65 27 2c 20  -Type', 'Date', 
0b20: 27 45 78 70 69 72 65 73 27 2c 20 27 53 65 72 76  'Expires', 'Serv
0b30: 65 72 27 2c 20 27 56 69 61 27 2c 20 27 58 2d 43  er', 'Via', 'X-C
0b40: 61 63 68 65 27 2c 20 27 58 2d 43 61 63 68 65 2d  ache', 'X-Cache-
0b50: 4c 6f 6f 6b 75 70 27 2c 20 27 58 2d 50 6f 77 65  Lookup', 'X-Powe
0b60: 72 65 64 2d 42 79 27 5d 29 0a 0a 62 6c 6f 63 6b  red-By'])..block
0b70: 5f 73 69 7a 65 20 3d 20 34 30 39 36 0a 0a 27 27  _size = 4096..''
0b80: 27 0a 23 20 6c 61 74 65 72 2c 20 6b 71 75 65 75  '.# later, kqueu
0b90: 65 20 77 6f 75 6c 64 20 62 65 20 67 6f 6f 64 20  e would be good 
0ba0: 62 75 74 20 6c 61 74 65 72 0a 63 6c 61 73 73 20  but later.class 
0bb0: 43 6f 6e 6e 65 63 74 69 6f 6e 3a 0a 09 5f 5f 73  Connection:..__s
0bc0: 6c 6f 74 73 5f 5f 20 3d 20 66 72 6f 7a 65 6e 73  lots__ = frozens
0bd0: 65 74 28 28 27 5f 5f 61 64 64 72 65 73 73 27 2c  et(('__address',
0be0: 20 27 5f 5f 69 6e 70 75 74 27 2c 20 27 5f 5f 73   '__input', '__s
0bf0: 6f 63 6b 65 74 27 2c 20 27 5f 5f 73 74 61 74 75  ocket', '__statu
0c00: 73 27 2c 20 27 65 72 72 6f 72 27 2c 20 27 6d 65  s', 'error', 'me
0c10: 74 68 6f 64 27 2c 20 27 75 72 6c 27 2c 20 27 68  thod', 'url', 'h
0c20: 74 74 70 5f 76 65 72 73 69 6f 6e 27 29 29 0a 0a  ttp_version'))..
0c30: 09 64 65 66 20 5f 5f 69 6e 69 74 5f 5f 28 73 65  .def __init__(se
0c40: 6c 66 2c 20 73 6f 63 6b 65 74 2c 20 61 64 64 72  lf, socket, addr
0c50: 65 73 73 29 3a 0a 09 09 73 65 6c 66 2e 5f 5f 61  ess):...self.__a
0c60: 64 64 72 65 73 73 20 3d 20 61 64 64 72 65 73 73  ddress = address
0c70: 0a 09 09 73 65 6c 66 2e 5f 5f 69 6e 70 75 74 20  ...self.__input 
0c80: 3d 20 62 27 27 0a 09 09 73 65 6c 66 2e 5f 5f 73  = b''...self.__s
0c90: 6f 63 6b 65 74 20 3d 20 73 6f 63 6b 65 74 0a 09  ocket = socket..
0ca0: 09 73 65 6c 66 2e 5f 5f 73 74 61 74 75 73 20 3d  .self.__status =
0cb0: 20 30 0a 0a 09 64 65 66 20 72 65 61 64 28 73 65   0...def read(se
0cc0: 6c 66 2c 20 6b 65 76 29 3a 0a 09 09 62 75 66 66  lf, kev):...buff
0cd0: 65 72 20 3d 20 73 65 6c 66 2e 5f 5f 73 6f 63 6b  er = self.__sock
0ce0: 65 74 2e 72 65 63 76 28 6b 65 76 2e 64 61 74 61  et.recv(kev.data
0cf0: 29 0a 09 09 65 78 68 61 75 73 74 65 64 20 3d 20  )...exhausted = 
0d00: 46 61 6c 73 65 0a 09 09 69 66 20 6c 65 6e 28 62  False...if len(b
0d10: 75 66 66 65 72 29 20 3d 3d 20 30 3a 0a 09 09 09  uffer) == 0:....
0d20: 65 6f 66 20 3d 20 54 72 75 65 0a 09 09 65 6c 73  eof = True...els
0d30: 65 3a 0a 09 09 09 73 65 6c 66 2e 5f 5f 69 6e 70  e:....self.__inp
0d40: 75 74 20 2b 3d 20 62 75 66 66 65 72 0a 09 09 09  ut += buffer....
0d50: 77 68 69 6c 65 20 6e 6f 74 20 65 78 68 61 75 73  while not exhaus
0d60: 74 65 64 3a 0a 09 09 09 09 69 66 20 73 65 6c 66  ted:.....if self
0d70: 2e 5f 5f 73 74 61 74 75 73 20 3d 3d 20 2d 31 3a  .__status == -1:
0d80: 0a 09 09 09 09 09 65 78 68 61 75 73 74 65 64 20  ......exhausted 
0d90: 3d 20 54 72 75 65 0a 09 09 09 09 65 6c 69 66 20  = True.....elif 
0da0: 73 65 6c 66 2e 5f 5f 73 74 61 74 75 73 20 3d 3d  self.__status ==
0db0: 20 30 3a 0a 09 09 09 09 09 65 6e 64 73 74 72 69   0:......endstri
0dc0: 6e 67 20 3d 20 73 65 6c 66 2e 5f 5f 69 6e 70 75  ng = self.__inpu
0dd0: 74 2e 66 69 6e 64 28 62 27 5c 6e 27 29 0a 09 09  t.find(b'\n')...
0de0: 09 09 09 69 66 20 65 6e 64 73 74 72 69 6e 67 20  ...if endstring 
0df0: 3e 20 30 3a 0a 09 09 09 09 09 09 70 72 69 6e 74  > 0:.......print
0e00: 28 27 50 72 6f 63 65 73 73 69 6e 67 20 72 65 71  ('Processing req
0e10: 75 65 73 74 20 6c 69 6e 65 2e 27 29 0a 09 09 09  uest line.')....
0e20: 09 09 09 6c 69 6e 65 20 3d 20 73 65 6c 66 2e 5f  ...line = self._
0e30: 5f 69 6e 70 75 74 5b 3a 65 6e 64 73 74 72 69 6e  _input[:endstrin
0e40: 67 5d 2e 64 65 63 6f 64 65 28 27 61 73 63 69 69  g].decode('ascii
0e50: 27 29 0a 09 09 09 09 09 09 73 65 6c 66 2e 5f 5f  ').......self.__
0e60: 69 6e 70 75 74 20 3d 20 73 65 6c 66 2e 5f 5f 69  input = self.__i
0e70: 6e 70 75 74 5b 65 6e 64 73 74 72 69 6e 67 20 2b  nput[endstring +
0e80: 20 31 3a 5d 0a 09 09 09 09 09 09 69 73 52 65 71   1:].......isReq
0e90: 75 65 73 74 20 3d 20 72 65 2e 63 6f 6d 70 69 6c  uest = re.compil
0ea0: 65 28 27 28 47 45 54 29 20 28 5b 5e 20 5d 2b 29  e('(GET) ([^ ]+)
0eb0: 20 48 54 54 50 2f 28 31 5c 2e 30 29 27 29 2e 6d   HTTP/(1\.0)').m
0ec0: 61 74 63 68 28 6c 69 6e 65 29 0a 09 09 09 09 09  atch(line)......
0ed0: 09 69 66 20 6e 6f 74 20 69 73 52 65 71 75 65 73  .if not isReques
0ee0: 74 3a 0a 09 09 09 09 09 09 09 73 65 6c 66 2e 65  t:........self.e
0ef0: 72 72 6f 72 20 3d 20 27 4e 6f 74 20 61 20 48 54  rror = 'Not a HT
0f00: 54 50 20 63 6f 6e 6e 65 63 74 69 6f 6e 2e 27 0a  TP connection.'.
0f10: 09 09 09 09 09 09 09 73 65 6c 66 2e 5f 5f 73 74  .......self.__st
0f20: 61 74 75 73 20 3d 20 2d 31 0a 09 09 09 09 09 09  atus = -1.......
0f30: 65 6c 73 65 3a 0a 09 09 09 09 09 09 09 73 65 6c  else:........sel
0f40: 66 2e 6d 65 74 68 6f 64 20 3d 20 69 73 52 65 71  f.method = isReq
0f50: 75 65 73 74 2e 67 72 6f 75 70 28 31 29 0a 09 09  uest.group(1)...
0f60: 09 09 09 09 09 73 65 6c 66 2e 75 72 6c 20 3d 20  .....self.url = 
0f70: 69 73 52 65 71 75 65 73 74 2e 67 72 6f 75 70 28  isRequest.group(
0f80: 32 29 0a 09 09 09 09 09 09 09 73 65 6c 66 2e 68  2)........self.h
0f90: 74 74 70 5f 76 65 72 73 69 6f 6e 20 3d 20 69 73  ttp_version = is
0fa0: 52 65 71 75 65 73 74 2e 67 72 6f 75 70 28 33 29  Request.group(3)
0fb0: 0a 09 09 09 09 09 09 09 73 65 6c 66 2e 5f 5f 73  ........self.__s
0fc0: 74 61 74 75 73 20 3d 20 31 0a 09 09 09 09 09 65  tatus = 1......e
0fd0: 6c 73 65 3a 0a 09 09 09 09 09 09 65 78 68 61 75  lse:.......exhau
0fe0: 73 74 65 64 20 3d 20 54 72 75 65 0a 09 09 09 09  sted = True.....
0ff0: 65 6c 69 66 20 73 65 6c 66 2e 5f 5f 73 74 61 74  elif self.__stat
1000: 75 73 20 3d 3d 20 31 3a 0a 09 09 09 09 09 65 6e  us == 1:......en
1010: 64 73 74 72 69 6e 67 20 3d 20 73 65 6c 66 2e 5f  dstring = self._
1020: 5f 69 6e 70 75 74 2e 66 69 6e 64 28 62 27 5c 6e  _input.find(b'\n
1030: 27 29 0a 09 09 09 09 09 69 66 20 65 6e 64 73 74  ')......if endst
1040: 72 69 6e 67 20 3e 20 30 3a 0a 09 09 09 09 09 09  ring > 0:.......
1050: 70 72 69 6e 74 28 27 50 72 6f 63 65 73 73 69 6e  print('Processin
1060: 67 20 68 65 61 64 65 72 20 6c 69 6e 65 2e 27 20  g header line.' 
1070: 2b 20 72 65 70 72 28 73 65 6c 66 2e 5f 5f 69 6e  + repr(self.__in
1080: 70 75 74 29 29 0a 09 09 09 09 09 09 6c 69 6e 65  put)).......line
1090: 20 3d 20 73 65 6c 66 2e 5f 5f 69 6e 70 75 74 5b   = self.__input[
10a0: 3a 65 6e 64 73 74 72 69 6e 67 5d 2e 64 65 63 6f  :endstring].deco
10b0: 64 65 28 27 61 73 63 69 69 27 29 0a 09 09 09 09  de('ascii').....
10c0: 09 09 73 65 6c 66 2e 5f 5f 69 6e 70 75 74 20 3d  ..self.__input =
10d0: 20 73 65 6c 66 2e 5f 5f 69 6e 70 75 74 5b 65 6e   self.__input[en
10e0: 64 73 74 72 69 6e 67 20 2b 20 31 3a 5d 0a 09 09  dstring + 1:]...
10f0: 09 09 09 09 69 73 48 65 61 64 65 72 20 3d 20 72  ....isHeader = r
1100: 65 2e 63 6f 6d 70 69 6c 65 28 27 28 5b 5e 3a 5d  e.compile('([^:]
1110: 2a 29 3a 20 2b 28 2e 2a 29 27 29 2e 6d 61 74 63  *): +(.*)').matc
1120: 68 28 6c 69 6e 65 29 0a 09 09 09 09 09 09 69 66  h(line).......if
1130: 20 6e 6f 74 20 69 73 48 65 61 64 65 72 3a 0a 09   not isHeader:..
1140: 09 09 09 09 09 09 73 65 6c 66 2e 65 72 72 6f 72  ......self.error
1150: 20 3d 20 27 42 61 64 20 68 65 61 64 65 72 2e 27   = 'Bad header.'
1160: 0a 09 09 09 09 09 09 09 72 65 74 75 72 6e 28 46  ........return(F
1170: 61 6c 73 65 29 0a 09 09 09 09 09 09 23 20 70 72  alse).......# pr
1180: 6f 63 65 73 73 20 68 65 61 64 65 72 20 68 65 72  ocess header her
1190: 65 0a 09 09 09 09 09 65 6c 69 66 20 65 6e 64 73  e......elif ends
11a0: 74 72 69 6e 67 20 3d 3d 20 30 3a 0a 09 09 09 09  tring == 0:.....
11b0: 09 09 73 65 6c 66 2e 5f 5f 73 74 61 74 75 73 20  ..self.__status 
11c0: 3d 20 32 0a 09 09 09 09 09 65 6c 73 65 3a 0a 09  = 2......else:..
11d0: 09 09 09 09 09 65 78 68 61 75 73 74 65 64 20 3d  .....exhausted =
11e0: 20 54 72 75 65 0a 0a 09 64 65 66 20 77 72 69 74   True...def writ
11f0: 65 28 73 65 6c 66 2c 20 6b 65 76 29 3a 0a 09 09  e(self, kev):...
1200: 70 61 73 73 0a 0a 69 66 20 6f 70 74 69 6f 6e 73  pass..if options
1210: 2e 70 6f 72 74 3a 0a 09 69 6d 70 6f 72 74 20 73  .port:..import s
1220: 65 6c 65 63 74 2c 20 73 6f 63 6b 65 74 0a 0a 09  elect, socket...
1230: 73 6f 63 6b 20 3d 20 73 6f 63 6b 65 74 2e 73 6f  sock = socket.so
1240: 63 6b 65 74 28 73 6f 63 6b 65 74 2e 41 46 5f 49  cket(socket.AF_I
1250: 4e 45 54 2c 20 73 6f 63 6b 65 74 2e 53 4f 43 4b  NET, socket.SOCK
1260: 5f 53 54 52 45 41 4d 29 0a 09 74 72 79 3a 0a 09  _STREAM)..try:..
1270: 09 73 6f 63 6b 2e 62 69 6e 64 28 28 27 31 32 37  .sock.bind(('127
1280: 2e 30 2e 30 2e 31 27 2c 20 69 6e 74 28 6f 70 74  .0.0.1', int(opt
1290: 69 6f 6e 73 2e 70 6f 72 74 29 29 29 0a 09 09 73  ions.port)))...s
12a0: 6f 63 6b 2e 6c 69 73 74 65 6e 28 2d 31 29 0a 0a  ock.listen(-1)..
12b0: 09 09 6b 71 20 3d 20 73 65 6c 65 63 74 2e 6b 71  ..kq = select.kq
12c0: 75 65 75 65 28 29 0a 09 09 61 73 73 65 72 74 20  ueue()...assert 
12d0: 6b 71 2e 66 69 6c 65 6e 6f 28 29 20 21 3d 20 2d  kq.fileno() != -
12e0: 31 2c 20 22 46 61 74 61 6c 20 65 72 72 6f 72 3a  1, "Fatal error:
12f0: 20 63 61 6e 27 74 20 69 6e 69 74 69 61 6c 69 73   can't initialis
1300: 65 20 6b 71 75 65 75 65 2e 22 0a 0a 09 09 6b 71  e kqueue."....kq
1310: 2e 63 6f 6e 74 72 6f 6c 28 5b 73 65 6c 65 63 74  .control([select
1320: 2e 6b 65 76 65 6e 74 28 73 6f 63 6b 2c 20 73 65  .kevent(sock, se
1330: 6c 65 63 74 2e 4b 51 5f 46 49 4c 54 45 52 5f 52  lect.KQ_FILTER_R
1340: 45 41 44 2c 20 73 65 6c 65 63 74 2e 4b 51 5f 45  EAD, select.KQ_E
1350: 56 5f 41 44 44 29 5d 2c 20 30 29 0a 09 09 74 69  V_ADD)], 0)...ti
1360: 6d 65 6f 75 74 20 3d 20 4e 6f 6e 65 0a 0a 09 09  meout = None....
1370: 63 6f 6e 6e 65 63 74 69 6f 6e 73 20 3d 20 7b 73  connections = {s
1380: 6f 63 6b 2e 66 69 6c 65 6e 6f 28 29 3a 20 4e 6f  ock.fileno(): No
1390: 6e 65 7d 0a 0a 09 09 77 68 69 6c 65 20 54 72 75  ne}....while Tru
13a0: 65 3a 0a 09 09 09 6b 65 76 73 20 3d 20 6b 71 2e  e:....kevs = kq.
13b0: 63 6f 6e 74 72 6f 6c 28 4e 6f 6e 65 2c 20 31 2c  control(None, 1,
13c0: 20 74 69 6d 65 6f 75 74 29 0a 0a 09 09 09 66 6f   timeout).....fo
13d0: 72 20 6b 65 76 20 69 6e 20 6b 65 76 73 3a 0a 09  r kev in kevs:..
13e0: 09 09 09 69 66 20 74 79 70 65 28 63 6f 6e 6e 65  ...if type(conne
13f0: 63 74 69 6f 6e 73 5b 6b 65 76 2e 69 64 65 6e 74  ctions[kev.ident
1400: 5d 29 20 3d 3d 20 43 6f 6e 6e 65 63 74 69 6f 6e  ]) == Connection
1410: 3a 0a 09 09 09 09 09 70 72 69 6e 74 28 6b 65 76  :......print(kev
1420: 2e 69 64 65 6e 74 2c 20 6b 65 76 2e 64 61 74 61  .ident, kev.data
1430: 2c 20 6b 65 76 2e 66 69 6c 74 65 72 2c 20 6b 65  , kev.filter, ke
1440: 76 2e 66 6c 61 67 73 29 0a 09 09 09 09 09 61 73  v.flags)......as
1450: 73 65 72 74 20 6b 65 76 2e 64 61 74 61 20 21 3d  sert kev.data !=
1460: 20 30 2c 20 27 4e 6f 20 64 61 74 61 20 61 76 61   0, 'No data ava
1470: 69 6c 61 62 6c 65 2e 27 0a 09 09 09 09 09 69 66  ilable.'......if
1480: 20 6b 65 76 2e 66 69 6c 74 65 72 20 3d 3d 20 73   kev.filter == s
1490: 65 6c 65 63 74 2e 4b 51 5f 46 49 4c 54 45 52 5f  elect.KQ_FILTER_
14a0: 52 45 41 44 3a 0a 09 09 09 09 09 09 63 6f 6e 6e  READ:.......conn
14b0: 65 63 74 69 6f 6e 73 5b 6b 65 76 2e 69 64 65 6e  ections[kev.iden
14c0: 74 5d 2e 72 65 61 64 28 6b 65 76 29 0a 09 09 09  t].read(kev)....
14d0: 09 09 65 6c 69 66 20 6b 65 76 2e 66 69 6c 74 65  ..elif kev.filte
14e0: 72 20 3d 3d 20 73 65 6c 65 63 74 2e 4b 51 5f 46  r == select.KQ_F
14f0: 49 4c 54 45 52 5f 57 52 49 54 45 3a 0a 09 09 09  ILTER_WRITE:....
1500: 09 09 09 63 6f 6e 6e 65 63 74 69 6f 6e 73 5b 6b  ...connections[k
1510: 65 76 2e 69 64 65 6e 74 5d 2e 77 72 69 74 65 28  ev.ident].write(
1520: 6b 65 76 29 0a 09 09 09 09 09 65 6c 73 65 3a 0a  kev)......else:.
1530: 09 09 09 09 09 09 61 73 73 65 72 74 20 6b 65 76  ......assert kev
1540: 2e 66 69 6c 74 65 72 20 69 6e 20 28 73 65 6c 65  .filter in (sele
1550: 63 74 2e 4b 51 5f 46 49 4c 54 45 52 5f 52 45 41  ct.KQ_FILTER_REA
1560: 44 2c 20 73 65 6c 65 63 74 2e 4b 51 5f 46 49 4c  D, select.KQ_FIL
1570: 54 45 52 5f 57 52 49 54 45 29 2c 20 27 44 6f 20  TER_WRITE), 'Do 
1580: 77 65 20 73 75 70 70 6f 72 74 20 6f 74 68 65 72  we support other
1590: 20 66 69 6c 74 65 72 73 3f 27 0a 09 09 09 09 65   filters?'.....e
15a0: 6c 73 65 3a 0a 09 09 09 09 09 28 63 6f 6e 6e 2c  lse:......(conn,
15b0: 20 61 64 64 72 29 20 3d 20 73 6f 63 6b 2e 61 63   addr) = sock.ac
15c0: 63 65 70 74 28 29 0a 09 09 09 09 09 70 72 69 6e  cept()......prin
15d0: 74 28 27 43 6f 6e 6e 65 63 74 69 6f 6e 20 66 72  t('Connection fr
15e0: 6f 6d 20 27 20 2b 20 72 65 70 72 28 61 64 64 72  om ' + repr(addr
15f0: 29 29 0a 09 09 09 09 09 6b 71 2e 63 6f 6e 74 72  ))......kq.contr
1600: 6f 6c 28 5b 73 65 6c 65 63 74 2e 6b 65 76 65 6e  ol([select.keven
1610: 74 28 63 6f 6e 6e 2c 20 73 65 6c 65 63 74 2e 4b  t(conn, select.K
1620: 51 5f 46 49 4c 54 45 52 5f 52 45 41 44 2c 20 73  Q_FILTER_READ, s
1630: 65 6c 65 63 74 2e 4b 51 5f 45 56 5f 41 44 44 29  elect.KQ_EV_ADD)
1640: 5d 2c 20 30 29 0a 09 09 09 09 09 63 6f 6e 6e 65  ], 0)......conne
1650: 63 74 69 6f 6e 73 5b 63 6f 6e 6e 2e 66 69 6c 65  ctions[conn.file
1660: 6e 6f 28 29 5d 20 3d 20 43 6f 6e 6e 65 63 74 69  no()] = Connecti
1670: 6f 6e 28 63 6f 6e 6e 2c 20 61 64 64 72 29 0a 0a  on(conn, addr)..
1680: 09 09 09 09 69 66 20 6b 65 76 2e 66 6c 61 67 73  ....if kev.flags
1690: 20 3e 3e 20 31 35 20 3d 3d 20 31 3a 0a 09 09 09   >> 15 == 1:....
16a0: 09 09 6b 71 2e 63 6f 6e 74 72 6f 6c 28 5b 73 65  ..kq.control([se
16b0: 6c 65 63 74 2e 6b 65 76 65 6e 74 28 6b 65 76 2e  lect.kevent(kev.
16c0: 69 64 65 6e 74 2c 20 73 65 6c 65 63 74 2e 4b 51  ident, select.KQ
16d0: 5f 46 49 4c 54 45 52 5f 52 45 41 44 2c 20 73 65  _FILTER_READ, se
16e0: 6c 65 63 74 2e 4b 51 5f 45 56 5f 44 45 4c 45 54  lect.KQ_EV_DELET
16f0: 45 29 5d 2c 20 30 29 0a 09 09 09 09 09 6b 71 2e  E)], 0)......kq.
1700: 63 6f 6e 74 72 6f 6c 28 5b 73 65 6c 65 63 74 2e  control([select.
1710: 6b 65 76 65 6e 74 28 6b 65 76 2e 69 64 65 6e 74  kevent(kev.ident
1720: 2c 20 73 65 6c 65 63 74 2e 4b 51 5f 46 49 4c 54  , select.KQ_FILT
1730: 45 52 5f 57 52 49 54 45 2c 20 73 65 6c 65 63 74  ER_WRITE, select
1740: 2e 4b 51 5f 45 56 5f 44 45 4c 45 54 45 29 5d 2c  .KQ_EV_DELETE)],
1750: 20 30 29 0a 09 09 09 09 09 64 65 6c 28 63 6f 6e   0)......del(con
1760: 6e 65 63 74 69 6f 6e 73 5b 6b 65 76 2e 69 64 65  nections[kev.ide
1770: 6e 74 5d 29 0a 09 66 69 6e 61 6c 6c 79 3a 0a 09  nt])..finally:..
1780: 09 73 6f 63 6b 2e 63 6c 6f 73 65 28 29 0a 27 27  .sock.close().''
1790: 27 0a 0a 23 20 58 58 58 20 68 6f 77 20 61 62 6f  '..# XXX how abo
17a0: 75 74 20 72 65 63 68 65 63 6b 69 6e 67 20 66 69  ut rechecking fi
17b0: 6c 65 73 3f 0a 69 66 20 54 72 75 65 3a 0a 09 69  les?.if True:..i
17c0: 6d 70 6f 72 74 20 68 74 74 70 2e 73 65 72 76 65  mport http.serve
17d0: 72 0a 0a 09 63 6c 61 73 73 20 4d 79 52 65 71 75  r...class MyRequ
17e0: 65 73 74 48 61 6e 64 6c 65 72 28 68 74 74 70 2e  estHandler(http.
17f0: 73 65 72 76 65 72 2e 42 61 73 65 48 54 54 50 52  server.BaseHTTPR
1800: 65 71 75 65 73 74 48 61 6e 64 6c 65 72 29 3a 0a  equestHandler):.
1810: 09 09 64 65 66 20 5f 5f 70 72 6f 63 65 73 73 28  ..def __process(
1820: 73 65 6c 66 29 3a 0a 09 09 09 23 20 72 65 6c 6f  self):....# relo
1830: 61 64 20 6d 65 61 6e 73 20 66 69 6c 65 20 6e 65  ad means file ne
1840: 65 64 73 20 74 6f 20 62 65 20 72 65 6c 6f 61 64  eds to be reload
1850: 65 64 20 74 6f 20 73 65 72 76 65 20 72 65 71 75  ed to serve requ
1860: 65 73 74 0a 09 09 09 72 65 6c 6f 61 64 20 3d 20  est....reload = 
1870: 46 61 6c 73 65 0a 09 09 09 23 20 72 65 63 68 65  False....# reche
1880: 63 6b 20 6d 65 61 6e 73 20 66 69 6c 65 20 6e 65  ck means file ne
1890: 65 64 73 20 74 6f 20 62 65 20 63 68 65 63 6b 65  eds to be checke
18a0: 64 2c 20 74 68 69 73 20 61 6c 73 6f 20 6d 65 61  d, this also mea
18b0: 6e 73 20 74 68 61 74 20 69 66 20 66 69 6c 65 20  ns that if file 
18c0: 68 61 76 20 62 65 65 6e 20 6d 6f 64 69 66 69 65  hav been modifie
18d0: 64 20 77 65 20 63 61 6e 20 73 65 72 76 65 20 6f  d we can serve o
18e0: 6c 64 65 72 20 63 6f 70 79 0a 09 09 09 72 65 63  lder copy....rec
18f0: 68 65 63 6b 20 3d 20 46 61 6c 73 65 0a 09 09 09  heck = False....
1900: 23 20 66 69 6c 65 5f 73 74 61 74 20 6d 65 61 6e  # file_stat mean
1910: 73 20 66 69 6c 65 20 64 65 66 69 6e 69 74 65 6c  s file definitel
1920: 79 20 65 78 69 73 74 73 0a 09 09 09 66 69 6c 65  y exists....file
1930: 5f 73 74 61 74 20 3d 20 4e 6f 6e 65 0a 09 09 09  _stat = None....
1940: 23 20 72 65 71 75 65 73 74 65 64 5f 72 61 6e 67  # requested_rang
1950: 65 73 20 68 6f 6c 64 73 20 64 61 74 61 20 61 62  es holds data ab
1960: 6f 75 74 20 61 6e 79 20 72 61 6e 67 65 20 72 65  out any range re
1970: 71 75 65 73 74 65 64 0a 09 09 09 72 65 71 75 65  quested....reque
1980: 73 74 65 64 5f 72 61 6e 67 65 73 20 3d 20 4e 6f  sted_ranges = No
1990: 6e 65 0a 09 09 09 23 20 72 65 63 6f 72 64 73 20  ne....# records 
19a0: 68 6f 6c 64 73 20 64 61 74 61 20 66 72 6f 6d 20  holds data from 
19b0: 69 6e 64 65 78 20 6c 6f 63 61 6c 6c 79 2c 20 73  index locally, s
19c0: 68 6f 75 6c 64 20 62 65 20 77 72 69 74 74 65 6e  hould be written
19d0: 20 62 61 63 6b 20 75 70 6f 6e 20 73 75 63 63 65   back upon succe
19e0: 73 73 66 75 6c 6c 20 63 6f 6d 70 6c 65 74 69 6f  ssfull completio
19f0: 6e 0a 09 09 09 72 65 63 6f 72 64 20 3d 20 4e 6f  n....record = No
1a00: 6e 65 0a 09 09 09 69 6e 66 6f 20 3d 20 27 43 68  ne....info = 'Ch
1a10: 65 63 6b 69 6e 67 20 66 69 6c 65 3a 20 27 20 2b  ecking file: ' +
1a20: 20 73 65 6c 66 2e 70 61 74 68 0a 0a 09 09 09 6d   self.path.....m
1a30: 79 50 61 74 68 20 3d 20 72 65 2e 63 6f 6d 70 69  yPath = re.compi
1a40: 6c 65 28 27 5e 28 2e 2a 3f 29 28 5c 3f 2e 2a 29  le('^(.*?)(\?.*)
1a50: 24 27 29 2e 6d 61 74 63 68 28 73 65 6c 66 2e 70  $').match(self.p
1a60: 61 74 68 29 0a 09 09 09 69 66 20 6d 79 50 61 74  ath)....if myPat
1a70: 68 3a 0a 09 09 09 09 6d 79 5f 70 61 74 68 20 3d  h:.....my_path =
1a80: 20 6d 79 50 61 74 68 2e 67 72 6f 75 70 28 31 29   myPath.group(1)
1a90: 0a 09 09 09 65 6c 73 65 3a 0a 09 09 09 09 6d 79  ....else:.....my
1aa0: 5f 70 61 74 68 20 3d 20 73 65 6c 66 2e 70 61 74  _path = self.pat
1ab0: 68 0a 0a 09 09 09 63 6f 6e 66 69 67 2e 73 65 63  h.....config.sec
1ac0: 74 69 6f 6e 28 73 65 6c 66 2e 68 65 61 64 65 72  tion(self.header
1ad0: 73 5b 27 48 6f 73 74 27 5d 29 0a 0a 09 09 09 69  s['Host']).....i
1ae0: 66 20 6e 6f 74 20 6f 73 2e 61 63 63 65 73 73 28  f not os.access(
1af0: 63 6f 6e 66 69 67 5b 27 64 69 72 27 5d 2c 20 6f  config['dir'], o
1b00: 73 2e 58 5f 4f 4b 29 3a 0a 09 09 09 09 6f 73 2e  s.X_OK):.....os.
1b10: 6d 6b 64 69 72 28 63 6f 6e 66 69 67 5b 27 64 69  mkdir(config['di
1b20: 72 27 5d 29 0a 09 09 09 23 20 74 68 69 73 20 69  r'])....# this i
1b30: 73 20 66 69 6c 65 20 69 6e 64 65 78 20 2d 20 65  s file index - e
1b40: 76 65 72 79 74 68 69 6e 67 20 69 73 20 73 74 6f  verything is sto
1b50: 72 65 64 20 69 6e 20 74 68 69 73 20 66 69 6c 65  red in this file
1b60: 0a 09 09 09 23 20 5f 70 61 72 74 73 20 2d 20 6c  ....# _parts - l
1b70: 69 73 74 20 6f 66 20 73 74 6f 72 65 64 20 70 61  ist of stored pa
1b80: 72 74 73 20 6f 66 20 66 69 6c 65 0a 09 09 09 23  rts of file....#
1b90: 20 5f 74 69 6d 65 20 2d 20 6c 61 73 74 20 74 69   _time - last ti
1ba0: 6d 65 20 74 68 65 20 66 69 6c 65 20 77 61 73 20  me the file was 
1bb0: 63 68 65 63 6b 65 64 0a 09 09 09 23 20 65 76 65  checked....# eve
1bc0: 72 79 74 68 69 6e 67 20 65 6c 73 65 20 69 73 20  rything else is 
1bd0: 6a 75 73 74 20 74 68 65 20 68 65 61 64 65 72 73  just the headers
1be0: 0a 09 09 09 69 6e 64 65 78 20 3d 20 73 68 65 6c  ....index = shel
1bf0: 76 65 2e 6f 70 65 6e 28 63 6f 6e 66 69 67 5b 27  ve.open(config['
1c00: 64 69 72 27 5d 20 2b 20 6f 73 2e 73 65 70 20 2b  dir'] + os.sep +
1c10: 20 27 2e 69 6e 64 65 78 27 29 0a 0a 09 09 09 64   '.index').....d
1c20: 65 73 63 5f 66 69 65 6c 64 73 20 3d 20 63 6f 6e  esc_fields = con
1c30: 73 74 5f 64 65 73 63 5f 66 69 65 6c 64 73 2e 63  st_desc_fields.c
1c40: 6f 70 79 28 29 0a 09 09 09 69 67 6e 6f 72 65 5f  opy()....ignore_
1c50: 66 69 65 6c 64 73 20 3d 20 63 6f 6e 73 74 5f 69  fields = const_i
1c60: 67 6e 6f 72 65 5f 66 69 65 6c 64 73 2e 63 6f 70  gnore_fields.cop
1c70: 79 28 29 0a 09 09 09 69 66 20 6e 6f 74 20 63 6f  y()....if not co
1c80: 6e 66 69 67 5b 27 6e 6f 65 74 61 67 27 5d 3a 0a  nfig['noetag']:.
1c90: 09 09 09 09 64 65 73 63 5f 66 69 65 6c 64 73 2e  ....desc_fields.
1ca0: 61 64 64 28 27 45 54 61 67 27 29 0a 09 09 09 65  add('ETag')....e
1cb0: 6c 73 65 3a 0a 09 09 09 09 69 67 6e 6f 72 65 5f  lse:.....ignore_
1cc0: 66 69 65 6c 64 73 2e 61 64 64 28 27 45 54 61 67  fields.add('ETag
1cd0: 27 29 0a 0a 09 09 09 70 72 6f 78 79 5f 69 67 6e  ').....proxy_ign
1ce0: 6f 72 65 64 20 3d 20 28 27 41 63 63 65 70 74 27  ored = ('Accept'
1cf0: 2c 20 27 41 63 63 65 70 74 2d 45 6e 63 6f 64 69  , 'Accept-Encodi
1d00: 6e 67 27 2c 0a 09 09 09 09 27 43 61 63 68 65 2d  ng',.....'Cache-
1d10: 43 6f 6e 74 72 6f 6c 27 2c 20 27 43 6f 6e 6e 65  Control', 'Conne
1d20: 63 74 69 6f 6e 27 2c 0a 09 09 09 09 27 48 6f 73  ction',.....'Hos
1d30: 74 27 2c 0a 09 09 09 09 27 49 66 2d 4d 6f 64 69  t',.....'If-Modi
1d40: 66 69 65 64 2d 53 69 6e 63 65 27 2c 20 27 49 66  fied-Since', 'If
1d50: 2d 55 6e 6d 6f 64 69 66 69 65 64 2d 53 69 6e 63  -Unmodified-Sinc
1d60: 65 27 2c 0a 09 09 09 09 27 55 73 65 72 2d 41 67  e',.....'User-Ag
1d70: 65 6e 74 27 2c 0a 09 09 09 09 27 56 69 61 27 2c  ent',.....'Via',
1d80: 0a 09 09 09 09 27 58 2d 46 6f 72 77 61 72 64 65  .....'X-Forwarde
1d90: 64 2d 46 6f 72 27 2c 0a 09 09 09 29 0a 0a 09 09  d-For',....)....
1da0: 09 70 72 69 6e 74 28 27 3d 3d 3d 3d 3d 3d 3d 3d  .print('========
1db0: 3d 3d 3d 3d 3d 3d 3d 5b 20 7b 7d 20 72 65 71 75  =======[ {} requ
1dc0: 65 73 74 20 5d 3d 3d 3d 27 2e 66 6f 72 6d 61 74  est ]==='.format
1dd0: 28 73 65 6c 66 2e 63 6f 6d 6d 61 6e 64 29 29 0a  (self.command)).
1de0: 0a 09 09 09 66 6f 72 20 68 65 61 64 65 72 20 69  ....for header i
1df0: 6e 20 73 65 6c 66 2e 68 65 61 64 65 72 73 3a 0a  n self.headers:.
1e00: 09 09 09 09 69 66 20 68 65 61 64 65 72 20 69 6e  ....if header in
1e10: 20 70 72 6f 78 79 5f 69 67 6e 6f 72 65 64 3a 0a   proxy_ignored:.
1e20: 09 09 09 09 09 70 61 73 73 0a 09 09 09 09 65 6c  .....pass.....el
1e30: 69 66 20 68 65 61 64 65 72 20 69 6e 20 28 27 52  if header in ('R
1e40: 61 6e 67 65 27 29 3a 0a 09 09 09 09 09 69 73 52  ange'):......isR
1e50: 61 6e 67 65 20 3d 20 72 65 2e 63 6f 6d 70 69 6c  ange = re.compil
1e60: 65 28 27 62 79 74 65 73 3d 28 5c 64 2b 29 2d 28  e('bytes=(\d+)-(
1e70: 5c 64 2b 29 27 29 2e 6d 61 74 63 68 28 73 65 6c  \d+)').match(sel
1e80: 66 2e 68 65 61 64 65 72 73 5b 68 65 61 64 65 72  f.headers[header
1e90: 5d 29 0a 09 09 09 09 09 69 66 20 69 73 52 61 6e  ])......if isRan
1ea0: 67 65 3a 0a 09 09 09 09 09 09 72 65 71 75 65 73  ge:.......reques
1eb0: 74 65 64 5f 72 61 6e 67 65 73 20 3d 20 73 70 61  ted_ranges = spa
1ec0: 63 65 6d 61 70 2e 53 70 61 63 65 4d 61 70 28 7b  cemap.SpaceMap({
1ed0: 69 6e 74 28 69 73 52 61 6e 67 65 2e 67 72 6f 75  int(isRange.grou
1ee0: 70 28 31 29 29 3a 20 69 6e 74 28 69 73 52 61 6e  p(1)): int(isRan
1ef0: 67 65 2e 67 72 6f 75 70 28 32 29 29 20 2b 20 31  ge.group(2)) + 1
1f00: 7d 29 0a 09 09 09 09 09 65 6c 73 65 3a 0a 09 09  })......else:...
1f10: 09 09 09 09 72 65 74 75 72 6e 28 29 0a 09 09 09  ....return()....
1f20: 09 65 6c 69 66 20 68 65 61 64 65 72 20 69 6e 20  .elif header in 
1f30: 28 27 50 72 61 67 6d 61 27 29 3a 0a 09 09 09 09  ('Pragma'):.....
1f40: 09 69 66 20 6d 79 5f 70 61 74 68 20 69 6e 20 69  .if my_path in i
1f50: 6e 64 65 78 3a 0a 09 09 09 09 09 09 69 6e 64 65  ndex:.......inde
1f60: 78 5b 6d 79 5f 70 61 74 68 5d 5b 68 65 61 64 65  x[my_path][heade
1f70: 72 5d 20 3d 20 73 65 6c 66 2e 68 65 61 64 65 72  r] = self.header
1f80: 73 5b 68 65 61 64 65 72 5d 0a 09 09 09 09 65 6c  s[header].....el
1f90: 73 65 3a 0a 09 09 09 09 09 70 72 69 6e 74 28 27  se:......print('
1fa0: 55 6e 6b 6e 6f 77 6e 20 68 65 61 64 65 72 20 2d  Unknown header -
1fb0: 20 27 2c 20 68 65 61 64 65 72 2c 20 27 3a 20 27   ', header, ': '
1fc0: 2c 20 73 65 6c 66 2e 68 65 61 64 65 72 73 5b 68  , self.headers[h
1fd0: 65 61 64 65 72 5d 2c 20 73 65 70 3d 27 27 29 0a  eader], sep='').
1fe0: 09 09 09 09 09 72 65 74 75 72 6e 28 29 0a 09 09  .....return()...
1ff0: 09 09 70 72 69 6e 74 28 68 65 61 64 65 72 2c 20  ..print(header, 
2000: 73 65 6c 66 2e 68 65 61 64 65 72 73 5b 68 65 61  self.headers[hea
2010: 64 65 72 5d 29 0a 0a 09 09 09 23 20 63 72 65 61  der]).....# crea
2020: 74 69 6e 67 20 65 6d 70 74 79 20 70 6c 61 63 65  ting empty place
2030: 68 6f 6c 64 65 72 20 69 6e 20 69 6e 64 65 78 0a  holder in index.
2040: 09 09 09 23 20 69 66 20 74 68 65 72 65 27 73 20  ...# if there's 
2050: 6e 6f 20 73 70 61 63 65 20 6d 61 70 20 61 6e 64  no space map and
2060: 20 74 68 65 72 65 27 73 20 6e 6f 20 66 69 6c 65   there's no file
2070: 20 69 6e 20 72 65 61 6c 20 64 69 72 65 63 74 6f   in real directo
2080: 72 79 20 2d 20 77 65 20 68 61 76 65 20 6e 6f 20  ry - we have no 
2090: 66 69 6c 65 0a 09 09 09 23 20 69 66 20 74 68 65  file....# if the
20a0: 72 65 27 73 20 61 6e 20 65 6d 70 74 79 20 73 70  re's an empty sp
20b0: 61 63 65 20 6d 61 70 20 2d 20 66 69 6c 65 20 69  ace map - file i
20c0: 73 20 66 75 6c 6c 0a 09 09 09 23 20 73 70 61 63  s full....# spac
20d0: 65 20 6d 61 70 20 67 65 6e 65 72 61 6c 6c 79 20  e map generally 
20e0: 63 6f 76 65 72 73 20 65 76 65 72 79 20 62 69 74  covers every bit
20f0: 20 6f 66 20 66 69 6c 65 20 77 65 20 64 6f 6e 27   of file we don'
2100: 74 20 70 6f 73 65 73 73 20 63 75 72 72 65 6e 74  t posess current
2110: 6c 79 0a 09 09 09 69 66 20 6e 6f 74 20 6d 79 5f  ly....if not my_
2120: 70 61 74 68 20 69 6e 20 69 6e 64 65 78 3a 0a 09  path in index:..
2130: 09 09 09 69 6e 66 6f 20 2b 3d 20 27 5c 6e 54 68  ...info += '\nTh
2140: 69 73 20 6f 6e 65 20 69 73 20 6e 65 77 2e 27 0a  is one is new.'.
2150: 09 09 09 09 72 65 6c 6f 61 64 20 3d 20 54 72 75  ....reload = Tru
2160: 65 0a 09 09 09 09 72 65 63 6f 72 64 20 3d 20 7b  e.....record = {
2170: 7d 0a 09 09 09 65 6c 73 65 3a 0a 09 09 09 09 72  }....else:.....r
2180: 65 63 6f 72 64 20 3d 20 69 6e 64 65 78 5b 6d 79  ecord = index[my
2190: 5f 70 61 74 68 5d 0a 0a 09 09 09 69 66 20 6e 6f  _path].....if no
21a0: 74 20 27 5f 70 61 72 74 73 27 20 69 6e 20 72 65  t '_parts' in re
21b0: 63 6f 72 64 3a 0a 09 09 09 09 72 65 63 6f 72 64  cord:.....record
21c0: 5b 27 5f 70 61 72 74 73 27 5d 20 3d 20 4e 6f 6e  ['_parts'] = Non
21d0: 65 0a 0a 09 09 09 23 20 63 72 65 61 74 69 6e 67  e.....# creating
21e0: 20 66 69 6c 65 20 6e 61 6d 65 20 66 72 6f 6d 20   file name from 
21f0: 6d 79 5f 70 61 74 68 0a 09 09 09 66 69 6c 65 5f  my_path....file_
2200: 6e 61 6d 65 20 3d 20 63 6f 6e 66 69 67 5b 27 64  name = config['d
2210: 69 72 27 5d 20 2b 20 6f 73 2e 73 65 70 20 2b 20  ir'] + os.sep + 
2220: 72 65 2e 63 6f 6d 70 69 6c 65 28 27 25 32 30 27  re.compile('%20'
2230: 29 2e 73 75 62 28 27 20 27 2c 20 6d 79 5f 70 61  ).sub(' ', my_pa
2240: 74 68 29 0a 09 09 09 23 20 70 61 72 74 69 61 6c  th)....# partial
2250: 20 66 69 6c 65 20 6f 72 20 75 6e 66 69 6e 69 73   file or unfinis
2260: 68 65 64 20 64 6f 77 6e 6c 6f 61 64 0a 09 09 09  hed download....
2270: 74 65 6d 70 5f 6e 61 6d 65 20 3d 20 63 6f 6e 66  temp_name = conf
2280: 69 67 5b 27 64 69 72 27 5d 20 2b 20 6f 73 2e 73  ig['dir'] + os.s
2290: 65 70 20 2b 20 27 2e 70 61 72 74 73 27 20 2b 20  ep + '.parts' + 
22a0: 72 65 2e 63 6f 6d 70 69 6c 65 28 27 25 32 30 27  re.compile('%20'
22b0: 29 2e 73 75 62 28 27 20 27 2c 20 6d 79 5f 70 61  ).sub(' ', my_pa
22c0: 74 68 29 0a 0a 09 09 09 23 20 66 6f 72 63 69 62  th).....# forcib
22d0: 6c 79 20 63 68 65 63 6b 69 6e 67 20 66 69 6c 65  ly checking file
22e0: 20 69 66 20 6e 6f 20 66 69 6c 65 20 70 72 65 73   if no file pres
22f0: 65 6e 74 0a 09 09 09 69 66 20 6f 73 2e 61 63 63  ent....if os.acc
2300: 65 73 73 28 66 69 6c 65 5f 6e 61 6d 65 2c 20 6f  ess(file_name, o
2310: 73 2e 52 5f 4f 4b 29 3a 0a 09 09 09 09 66 69 6c  s.R_OK):.....fil
2320: 65 5f 73 74 61 74 20 3d 20 6f 73 2e 73 74 61 74  e_stat = os.stat
2330: 28 66 69 6c 65 5f 6e 61 6d 65 29 0a 09 09 09 65  (file_name)....e
2340: 6c 69 66 20 27 5f 70 61 72 74 73 27 20 69 6e 20  lif '_parts' in 
2350: 72 65 63 6f 72 64 20 61 6e 64 20 6f 73 2e 61 63  record and os.ac
2360: 63 65 73 73 28 74 65 6d 70 5f 6e 61 6d 65 2c 20  cess(temp_name, 
2370: 6f 73 2e 52 5f 4f 4b 29 3a 0a 09 09 09 09 66 69  os.R_OK):.....fi
2380: 6c 65 5f 73 74 61 74 20 3d 20 6f 73 2e 73 74 61  le_stat = os.sta
2390: 74 28 74 65 6d 70 5f 6e 61 6d 65 29 0a 09 09 09  t(temp_name)....
23a0: 65 6c 69 66 20 6e 6f 74 20 72 65 6c 6f 61 64 3a  elif not reload:
23b0: 0a 09 09 09 09 70 72 69 6e 74 28 72 65 63 6f 72  .....print(recor
23c0: 64 29 0a 09 09 09 09 69 6e 66 6f 20 2b 3d 20 27  d).....info += '
23d0: 5c 6e 46 69 6c 65 20 6e 6f 74 20 66 6f 75 6e 64  \nFile not found
23e0: 20 6f 72 20 69 6e 61 63 63 65 73 73 69 62 6c 65   or inaccessible
23f0: 2e 27 0a 09 09 09 09 72 65 63 6f 72 64 5b 27 5f  .'.....record['_
2400: 70 61 72 74 73 27 5d 20 3d 20 4e 6f 6e 65 0a 09  parts'] = None..
2410: 09 09 09 72 65 6c 6f 61 64 20 3d 20 54 72 75 65  ...reload = True
2420: 0a 0a 09 09 09 23 20 66 6f 72 63 69 62 6c 79 20  .....# forcibly 
2430: 63 68 65 63 6b 69 6e 67 20 66 69 6c 65 20 69 66  checking file if
2440: 20 66 69 6c 65 20 73 69 7a 65 20 64 6f 65 73 6e   file size doesn
2450: 27 74 20 6d 61 74 63 68 20 77 69 74 68 20 69 6e  't match with in
2460: 64 65 78 20 64 61 74 61 0a 09 09 09 69 66 20 6e  dex data....if n
2470: 6f 74 20 72 65 6c 6f 61 64 3a 0a 09 09 09 09 69  ot reload:.....i
2480: 66 20 27 5f 70 61 72 74 73 27 20 69 6e 20 72 65  f '_parts' in re
2490: 63 6f 72 64 20 61 6e 64 20 72 65 63 6f 72 64 5b  cord and record[
24a0: 27 5f 70 61 72 74 73 27 5d 20 3d 3d 20 73 70 61  '_parts'] == spa
24b0: 63 65 6d 61 70 2e 53 70 61 63 65 4d 61 70 28 29  cemap.SpaceMap()
24c0: 3a 0a 09 09 09 09 09 69 66 20 27 43 6f 6e 74 65  :......if 'Conte
24d0: 6e 74 2d 4c 65 6e 67 74 68 27 20 69 6e 20 72 65  nt-Length' in re
24e0: 63 6f 72 64 20 61 6e 64 20 66 69 6c 65 5f 73 74  cord and file_st
24f0: 61 74 20 61 6e 64 20 66 69 6c 65 5f 73 74 61 74  at and file_stat
2500: 2e 73 74 5f 73 69 7a 65 20 21 3d 20 69 6e 74 28  .st_size != int(
2510: 72 65 63 6f 72 64 5b 27 43 6f 6e 74 65 6e 74 2d  record['Content-
2520: 4c 65 6e 67 74 68 27 5d 29 3a 0a 09 09 09 09 09  Length']):......
2530: 09 69 6e 66 6f 20 2b 3d 20 27 5c 6e 46 69 6c 65  .info += '\nFile
2540: 20 73 69 7a 65 20 69 73 20 7b 7d 20 61 6e 64 20   size is {} and 
2550: 73 74 6f 72 65 64 20 66 69 6c 65 20 73 69 7a 65  stored file size
2560: 20 69 73 20 7b 7d 2e 27 2e 66 6f 72 6d 61 74 28   is {}.'.format(
2570: 66 69 6c 65 5f 73 74 61 74 2e 73 74 5f 73 69 7a  file_stat.st_siz
2580: 65 2c 20 72 65 63 6f 72 64 5b 27 43 6f 6e 74 65  e, record['Conte
2590: 6e 74 2d 4c 65 6e 67 74 68 27 5d 29 0a 09 09 09  nt-Length'])....
25a0: 09 09 09 72 65 6c 6f 61 64 20 3d 20 54 72 75 65  ...reload = True
25b0: 0a 0a 09 09 09 23 20 66 6f 72 63 69 62 6c 79 20  .....# forcibly 
25c0: 63 68 65 63 6b 69 6e 67 20 66 69 6c 65 20 69 66  checking file if
25d0: 20 69 6e 64 65 78 20 68 6f 6c 64 73 20 50 72 61   index holds Pra
25e0: 67 6d 61 20 68 65 61 64 65 72 0a 09 09 09 69 66  gma header....if
25f0: 20 6e 6f 74 20 72 65 6c 6f 61 64 20 61 6e 64 20   not reload and 
2600: 27 50 72 61 67 6d 61 27 20 69 6e 20 72 65 63 6f  'Pragma' in reco
2610: 72 64 20 61 6e 64 20 72 65 63 6f 72 64 5b 27 50  rd and record['P
2620: 72 61 67 6d 61 27 5d 20 3d 3d 20 27 6e 6f 2d 63  ragma'] == 'no-c
2630: 61 63 68 65 27 3a 0a 09 09 09 09 69 6e 66 6f 20  ache':.....info 
2640: 2b 3d 27 5c 6e 50 72 61 67 6d 61 20 6f 6e 3a 20  +='\nPragma on: 
2650: 72 65 63 68 65 63 6b 20 69 6d 6d 69 6e 65 6e 74  recheck imminent
2660: 2e 27 0a 09 09 09 09 72 65 63 68 65 63 6b 20 3d  .'.....recheck =
2670: 20 54 72 75 65 0a 0a 09 09 09 23 20 73 6b 69 70   True.....# skip
2680: 70 69 6e 67 20 66 69 6c 65 20 70 72 6f 63 65 73  ping file proces
2690: 73 69 6e 67 20 69 66 20 74 68 65 72 65 27 73 20  sing if there's 
26a0: 6e 6f 20 6e 65 65 64 20 74 6f 20 72 65 63 68 65  no need to reche
26b0: 63 6b 20 69 74 20 61 6e 64 20 77 65 20 68 61 76  ck it and we hav
26c0: 65 20 63 68 65 63 6b 65 64 20 69 74 20 61 74 20  e checked it at 
26d0: 6c 65 61 73 74 20 34 20 68 6f 75 72 73 20 61 67  least 4 hours ag
26e0: 6f 0a 09 09 09 69 66 20 6e 6f 74 20 72 65 63 68  o....if not rech
26f0: 65 63 6b 20 61 6e 64 20 6e 6f 74 20 72 65 6c 6f  eck and not relo
2700: 61 64 20 61 6e 64 20 27 5f 74 69 6d 65 27 20 69  ad and '_time' i
2710: 6e 20 72 65 63 6f 72 64 20 61 6e 64 20 28 64 61  n record and (da
2720: 74 65 74 69 6d 65 2e 64 61 74 65 74 69 6d 65 2e  tetime.datetime.
2730: 6e 6f 77 28 29 20 2d 20 64 61 74 65 74 69 6d 65  now() - datetime
2740: 2e 74 69 6d 65 64 65 6c 74 61 28 68 6f 75 72 73  .timedelta(hours
2750: 20 3d 20 34 29 20 2d 20 72 65 63 6f 72 64 5b 27   = 4) - record['
2760: 5f 74 69 6d 65 27 5d 29 2e 64 61 79 73 20 3c 20  _time']).days < 
2770: 30 3a 0a 09 09 09 09 72 65 63 68 65 63 6b 20 3d  0:.....recheck =
2780: 20 54 72 75 65 0a 0a 09 09 09 70 72 69 6e 74 28   True.....print(
2790: 69 6e 66 6f 29 0a 09 09 09 69 66 20 72 65 6c 6f  info)....if relo
27a0: 61 64 20 6f 72 20 72 65 63 68 65 63 6b 3a 0a 0a  ad or recheck:..
27b0: 09 09 09 09 74 72 79 3a 0a 09 09 09 09 09 72 65  ....try:......re
27c0: 71 75 65 73 74 20 3d 20 27 68 74 74 70 3a 2f 2f  quest = 'http://
27d0: 27 20 2b 20 63 6f 6e 66 69 67 5b 27 72 6f 6f 74  ' + config['root
27e0: 27 5d 20 2b 20 6d 79 5f 70 61 74 68 0a 09 09 09  '] + my_path....
27f0: 09 09 6e 65 65 64 65 64 20 3d 20 4e 6f 6e 65 0a  ..needed = None.
2800: 09 09 09 09 09 23 20 58 58 58 20 61 6e 64 20 69  .....# XXX and i
2810: 66 20 77 65 20 73 70 65 63 69 66 79 20 66 75 6c  f we specify ful
2820: 6c 20 66 69 6c 65 20 77 65 20 64 6f 6e 27 74 20  l file we don't 
2830: 67 6f 20 70 61 72 74 69 61 6c 3f 0a 09 09 09 09  go partial?.....
2840: 09 69 66 20 72 65 71 75 65 73 74 65 64 5f 72 61  .if requested_ra
2850: 6e 67 65 73 20 21 3d 20 4e 6f 6e 65 3a 0a 09 09  nges != None:...
2860: 09 09 09 09 69 66 20 27 5f 70 61 72 74 73 27 20  ....if '_parts' 
2870: 69 6e 20 72 65 63 6f 72 64 20 61 6e 64 20 72 65  in record and re
2880: 63 6f 72 64 5b 27 5f 70 61 72 74 73 27 5d 20 21  cord['_parts'] !
2890: 3d 20 4e 6f 6e 65 3a 0a 09 09 09 09 09 09 09 6e  = None:........n
28a0: 65 65 64 65 64 20 3d 20 72 65 63 6f 72 64 5b 27  eeded = record['
28b0: 5f 70 61 72 74 73 27 5d 20 26 20 72 65 71 75 65  _parts'] & reque
28c0: 73 74 65 64 5f 72 61 6e 67 65 73 0a 09 09 09 09  sted_ranges.....
28d0: 09 09 65 6c 69 66 20 63 6f 6e 66 69 67 5b 27 6e  ..elif config['n
28e0: 6f 70 61 72 74 73 27 5d 3a 0a 09 09 09 09 09 09  oparts']:.......
28f0: 09 6e 65 65 64 65 64 20 3d 20 72 65 63 6f 72 64  .needed = record
2900: 5b 27 5f 70 61 72 74 73 27 5d 0a 09 09 09 09 09  ['_parts']......
2910: 09 65 6c 73 65 3a 0a 09 09 09 09 09 09 09 6e 65  .else:........ne
2920: 65 64 65 64 20 3d 20 72 65 71 75 65 73 74 65 64  eded = requested
2930: 5f 72 61 6e 67 65 73 0a 09 09 09 09 09 09 72 61  _ranges.......ra
2940: 6e 67 65 73 20 3d 20 28 29 0a 09 09 09 09 09 09  nges = ().......
2950: 70 72 69 6e 74 28 27 4d 69 73 73 69 6e 67 20 72  print('Missing r
2960: 61 6e 67 65 73 3a 20 7b 7d 2c 20 72 65 71 75 65  anges: {}, reque
2970: 73 74 65 64 20 72 61 6e 67 65 73 3a 20 7b 7d 2c  sted ranges: {},
2980: 20 6e 65 65 64 65 64 20 72 61 6e 67 65 73 3a 20   needed ranges: 
2990: 7b 7d 2e 27 2e 66 6f 72 6d 61 74 28 72 65 63 6f  {}.'.format(reco
29a0: 72 64 5b 27 5f 70 61 72 74 73 27 5d 2c 20 72 65  rd['_parts'], re
29b0: 71 75 65 73 74 65 64 5f 72 61 6e 67 65 73 2c 20  quested_ranges, 
29c0: 6e 65 65 64 65 64 29 29 0a 09 09 09 09 09 09 69  needed)).......i
29d0: 66 20 6c 65 6e 28 6e 65 65 64 65 64 29 20 3e 20  f len(needed) > 
29e0: 30 3a 0a 09 09 09 09 09 09 09 6e 65 65 64 65 64  0:........needed
29f0: 2e 72 65 77 69 6e 64 28 29 0a 09 09 09 09 09 09  .rewind().......
2a00: 09 77 68 69 6c 65 20 54 72 75 65 3a 0a 09 09 09  .while True:....
2a10: 09 09 09 09 09 72 61 6e 67 65 20 3d 20 6e 65 65  .....range = nee
2a20: 64 65 64 2e 70 6f 70 28 29 0a 09 09 09 09 09 09  ded.pop().......
2a30: 09 09 69 66 20 72 61 6e 67 65 5b 30 5d 20 3d 3d  ..if range[0] ==
2a40: 20 4e 6f 6e 65 3a 0a 09 09 09 09 09 09 09 09 09   None:..........
2a50: 62 72 65 61 6b 0a 09 09 09 09 09 09 09 09 72 61  break.........ra
2a60: 6e 67 65 73 20 2b 3d 20 27 7b 7d 2d 7b 7d 27 2e  nges += '{}-{}'.
2a70: 66 6f 72 6d 61 74 28 72 61 6e 67 65 5b 30 5d 2c  format(range[0],
2a80: 20 72 61 6e 67 65 5b 31 5d 20 2d 20 31 29 2c 0a   range[1] - 1),.
2a90: 09 09 09 09 09 09 09 72 65 71 75 65 73 74 20 3d  .......request =
2aa0: 20 75 72 6c 6c 69 62 2e 72 65 71 75 65 73 74 2e   urllib.request.
2ab0: 52 65 71 75 65 73 74 28 72 65 71 75 65 73 74 2c  Request(request,
2ac0: 20 68 65 61 64 65 72 73 20 3d 20 7b 27 52 61 6e   headers = {'Ran
2ad0: 67 65 27 3a 20 27 62 79 74 65 73 3d 27 20 2b 20  ge': 'bytes=' + 
2ae0: 27 2c 27 2e 6a 6f 69 6e 28 72 61 6e 67 65 73 29  ','.join(ranges)
2af0: 7d 29 0a 0a 09 09 09 09 09 77 69 74 68 20 75 72  }).......with ur
2b00: 6c 6c 69 62 2e 72 65 71 75 65 73 74 2e 75 72 6c  llib.request.url
2b10: 6f 70 65 6e 28 72 65 71 75 65 73 74 29 20 61 73  open(request) as
2b20: 20 73 6f 75 72 63 65 3a 0a 09 09 09 09 09 09 6e   source:.......n
2b30: 65 77 5f 72 65 63 6f 72 64 20 3d 20 7b 7d 0a 09  ew_record = {}..
2b40: 09 09 09 09 09 6e 65 77 5f 72 65 63 6f 72 64 5b  .....new_record[
2b50: 27 5f 70 61 72 74 73 27 5d 20 3d 20 72 65 63 6f  '_parts'] = reco
2b60: 72 64 5b 27 5f 70 61 72 74 73 27 5d 0a 09 09 09  rd['_parts']....
2b70: 09 09 09 68 65 61 64 65 72 73 20 3d 20 73 6f 75  ...headers = sou
2b80: 72 63 65 2e 69 6e 66 6f 28 29 0a 0a 09 09 09 09  rce.info()......
2b90: 09 09 23 20 73 74 72 69 70 70 69 6e 67 20 75 6e  ..# stripping un
2ba0: 6e 65 65 64 65 64 20 68 65 61 64 65 72 73 20 28  needed headers (
2bb0: 58 58 58 20 6d 61 6b 65 20 74 68 69 73 20 69 6e  XXX make this in
2bc0: 70 6c 61 63 65 3f 29 0a 09 09 09 09 09 09 66 6f  place?).......fo
2bd0: 72 20 68 65 61 64 65 72 20 69 6e 20 68 65 61 64  r header in head
2be0: 65 72 73 3a 0a 09 09 09 09 09 09 09 69 66 20 68  ers:........if h
2bf0: 65 61 64 65 72 20 69 6e 20 64 65 73 63 5f 66 69  eader in desc_fi
2c00: 65 6c 64 73 3a 0a 09 09 09 09 09 09 09 09 23 69  elds:.........#i
2c10: 66 20 68 65 61 64 65 72 20 3d 3d 20 27 50 72 61  f header == 'Pra
2c20: 67 6d 61 27 20 61 6e 64 20 68 65 61 64 65 72 73  gma' and headers
2c30: 5b 68 65 61 64 65 72 5d 20 21 3d 20 27 6e 6f 2d  [header] != 'no-
2c40: 63 61 63 68 65 27 3a 0a 09 09 09 09 09 09 09 09  cache':.........
2c50: 69 66 20 68 65 61 64 65 72 20 3d 3d 20 27 43 6f  if header == 'Co
2c60: 6e 74 65 6e 74 2d 4c 65 6e 67 74 68 27 3a 0a 09  ntent-Length':..
2c70: 09 09 09 09 09 09 09 09 69 66 20 27 43 6f 6e 74  ........if 'Cont
2c80: 65 6e 74 2d 52 61 6e 67 65 27 20 6e 6f 74 20 69  ent-Range' not i
2c90: 6e 20 68 65 61 64 65 72 73 3a 0a 09 09 09 09 09  n headers:......
2ca0: 09 09 09 09 09 6e 65 77 5f 72 65 63 6f 72 64 5b  .....new_record[
2cb0: 68 65 61 64 65 72 5d 20 3d 20 69 6e 74 28 68 65  header] = int(he
2cc0: 61 64 65 72 73 5b 68 65 61 64 65 72 5d 29 0a 09  aders[header])..
2cd0: 09 09 09 09 09 09 09 65 6c 73 65 3a 0a 09 09 09  .......else:....
2ce0: 09 09 09 09 09 09 6e 65 77 5f 72 65 63 6f 72 64  ......new_record
2cf0: 5b 68 65 61 64 65 72 5d 20 3d 20 68 65 61 64 65  [header] = heade
2d00: 72 73 5b 68 65 61 64 65 72 5d 0a 09 09 09 09 09  rs[header]......
2d10: 09 09 65 6c 69 66 20 68 65 61 64 65 72 20 3d 3d  ..elif header ==
2d20: 20 27 43 6f 6e 74 65 6e 74 2d 52 61 6e 67 65 27   'Content-Range'
2d30: 3a 0a 09 09 09 09 09 09 09 09 72 61 6e 67 65 20  :.........range 
2d40: 3d 20 72 65 2e 63 6f 6d 70 69 6c 65 28 27 5e 62  = re.compile('^b
2d50: 79 74 65 73 20 28 5c 64 2b 29 2d 28 5c 64 2b 29  ytes (\d+)-(\d+)
2d60: 2f 28 5c 64 2b 29 24 27 29 2e 6d 61 74 63 68 28  /(\d+)$').match(
2d70: 68 65 61 64 65 72 73 5b 68 65 61 64 65 72 5d 29  headers[header])
2d80: 0a 09 09 09 09 09 09 09 09 69 66 20 72 61 6e 67  .........if rang
2d90: 65 3a 0a 09 09 09 09 09 09 09 09 09 6e 65 77 5f  e:..........new_
2da0: 72 65 63 6f 72 64 5b 27 43 6f 6e 74 65 6e 74 2d  record['Content-
2db0: 4c 65 6e 67 74 68 27 5d 20 3d 20 69 6e 74 28 72  Length'] = int(r
2dc0: 61 6e 67 65 2e 67 72 6f 75 70 28 33 29 29 0a 09  ange.group(3))..
2dd0: 09 09 09 09 09 09 09 65 6c 73 65 3a 09 0a 09 09  .......else:....
2de0: 09 09 09 09 09 09 09 61 73 73 65 72 74 20 46 61  .......assert Fa
2df0: 6c 73 65 2c 20 27 43 6f 6e 74 65 6e 74 2d 52 61  lse, 'Content-Ra
2e00: 6e 67 65 20 75 6e 72 65 63 6f 67 6e 69 7a 65 64  nge unrecognized
2e10: 2e 27 0a 09 09 09 09 09 09 09 65 6c 69 66 20 6e  .'........elif n
2e20: 6f 74 20 68 65 61 64 65 72 20 69 6e 20 69 67 6e  ot header in ign
2e30: 6f 72 65 5f 66 69 65 6c 64 73 3a 0a 09 09 09 09  ore_fields:.....
2e40: 09 09 09 09 70 72 69 6e 74 28 27 55 6e 64 65 66  ....print('Undef
2e50: 69 6e 65 64 20 68 65 61 64 65 72 20 22 27 2c 20  ined header "', 
2e60: 68 65 61 64 65 72 2c 20 27 22 3a 20 27 2c 20 68  header, '": ', h
2e70: 65 61 64 65 72 73 5b 68 65 61 64 65 72 5d 2c 20  eaders[header], 
2e80: 73 65 70 3d 27 27 29 0a 0a 09 09 09 09 09 09 23  sep='')........#
2e90: 20 63 6f 6d 70 61 72 69 6e 67 20 68 65 61 64 65   comparing heade
2ea0: 72 73 20 77 69 74 68 20 64 61 74 61 20 66 6f 75  rs with data fou
2eb0: 6e 64 20 69 6e 20 69 6e 64 65 78 0a 09 09 09 09  nd in index.....
2ec0: 09 09 23 20 69 66 20 61 6e 79 20 68 65 61 64 65  ..# if any heade
2ed0: 72 20 68 61 73 20 63 68 61 6e 67 65 64 20 28 65  r has changed (e
2ee0: 78 63 65 70 74 20 50 72 61 67 6d 61 29 20 66 69  xcept Pragma) fi
2ef0: 6c 65 20 69 73 20 66 75 6c 6c 79 20 64 6f 77 6e  le is fully down
2f00: 6c 6f 61 64 65 64 0a 09 09 09 09 09 09 23 20 73  loaded.......# s
2f10: 61 6d 65 20 69 66 20 77 65 20 67 65 74 20 6d 6f  ame if we get mo
2f20: 72 65 20 6f 72 20 6c 65 73 73 20 68 65 61 64 65  re or less heade
2f30: 72 73 0a 09 09 09 09 09 09 6f 6c 64 5f 6b 65 79  rs.......old_key
2f40: 73 20 3d 20 73 65 74 28 72 65 63 6f 72 64 2e 6b  s = set(record.k
2f50: 65 79 73 28 29 29 0a 09 09 09 09 09 09 6f 6c 64  eys()).......old
2f60: 5f 6b 65 79 73 2e 64 69 73 63 61 72 64 28 27 5f  _keys.discard('_
2f70: 74 69 6d 65 27 29 0a 09 09 09 09 09 09 6f 6c 64  time').......old
2f80: 5f 6b 65 79 73 2e 64 69 73 63 61 72 64 28 27 50  _keys.discard('P
2f90: 72 61 67 6d 61 27 29 0a 09 09 09 09 09 09 6d 6f  ragma').......mo
2fa0: 72 65 5f 6b 65 79 73 20 3d 20 73 65 74 28 6e 65  re_keys = set(ne
2fb0: 77 5f 72 65 63 6f 72 64 2e 6b 65 79 73 28 29 29  w_record.keys())
2fc0: 20 2d 20 6f 6c 64 5f 6b 65 79 73 0a 09 09 09 09   - old_keys.....
2fd0: 09 09 6d 6f 72 65 5f 6b 65 79 73 2e 64 69 73 63  ..more_keys.disc
2fe0: 61 72 64 28 27 50 72 61 67 6d 61 27 29 0a 09 09  ard('Pragma')...
2ff0: 09 09 09 09 6c 65 73 73 5f 6b 65 79 73 20 3d 20  ....less_keys = 
3000: 6f 6c 64 5f 6b 65 79 73 20 2d 20 73 65 74 28 6e  old_keys - set(n
3010: 65 77 5f 72 65 63 6f 72 64 2e 6b 65 79 73 28 29  ew_record.keys()
3020: 29 0a 09 09 09 09 09 09 69 66 20 6c 65 6e 28 6d  ).......if len(m
3030: 6f 72 65 5f 6b 65 79 73 29 20 3e 20 30 3a 0a 09  ore_keys) > 0:..
3040: 09 09 09 09 09 09 69 66 20 6e 6f 74 20 6c 65 6e  ......if not len
3050: 28 6f 6c 64 5f 6b 65 79 73 29 20 3d 3d 20 30 3a  (old_keys) == 0:
3060: 0a 09 09 09 09 09 09 09 09 70 72 69 6e 74 28 27  .........print('
3070: 4d 6f 72 65 20 68 65 61 64 65 72 73 20 61 70 70  More headers app
3080: 65 61 72 3a 27 2c 20 6d 6f 72 65 5f 6b 65 79 73  ear:', more_keys
3090: 29 0a 09 09 09 09 09 09 09 72 65 6c 6f 61 64 20  )........reload 
30a0: 3d 20 54 72 75 65 0a 09 09 09 09 09 09 65 6c 69  = True.......eli
30b0: 66 20 6c 65 6e 28 6c 65 73 73 5f 6b 65 79 73 29  f len(less_keys)
30c0: 20 3e 20 30 3a 0a 09 09 09 09 09 09 09 70 72 69   > 0:........pri
30d0: 6e 74 28 27 4c 65 73 73 20 68 65 61 64 65 72 73  nt('Less headers
30e0: 20 61 70 70 65 61 72 3a 27 2c 20 6c 65 73 73 5f   appear:', less_
30f0: 6b 65 79 73 29 0a 09 09 09 09 09 09 65 6c 73 65  keys).......else
3100: 3a 0a 09 09 09 09 09 09 09 66 6f 72 20 6b 65 79  :........for key
3110: 20 69 6e 20 72 65 63 6f 72 64 2e 6b 65 79 73 28   in record.keys(
3120: 29 3a 0a 09 09 09 09 09 09 09 09 69 66 20 6b 65  ):.........if ke
3130: 79 5b 30 5d 20 21 3d 20 27 5f 27 20 61 6e 64 20  y[0] != '_' and 
3140: 6b 65 79 20 21 3d 20 27 50 72 61 67 6d 61 27 20  key != 'Pragma' 
3150: 61 6e 64 20 6e 6f 74 20 72 65 63 6f 72 64 5b 6b  and not record[k
3160: 65 79 5d 20 3d 3d 20 6e 65 77 5f 72 65 63 6f 72  ey] == new_recor
3170: 64 5b 6b 65 79 5d 3a 0a 09 09 09 09 09 09 09 09  d[key]:.........
3180: 09 70 72 69 6e 74 28 27 48 65 61 64 65 72 20 22  .print('Header "
3190: 27 2c 20 6b 65 79 2c 20 27 22 20 63 68 61 6e 67  ', key, '" chang
31a0: 65 64 20 66 72 6f 6d 20 5b 27 2c 20 72 65 63 6f  ed from [', reco
31b0: 72 64 5b 6b 65 79 5d 2c 20 27 5d 20 74 6f 20 5b  rd[key], '] to [
31c0: 27 2c 20 6e 65 77 5f 72 65 63 6f 72 64 5b 6b 65  ', new_record[ke
31d0: 79 5d 2c 20 27 5d 27 2c 20 73 65 70 3d 27 27 29  y], ']', sep='')
31e0: 0a 09 09 09 09 09 09 09 09 09 70 72 69 6e 74 28  ..........print(
31f0: 74 79 70 65 28 72 65 63 6f 72 64 5b 6b 65 79 5d  type(record[key]
3200: 29 2c 20 74 79 70 65 28 6e 65 77 5f 72 65 63 6f  ), type(new_reco
3210: 72 64 5b 6b 65 79 5d 29 29 0a 09 09 09 09 09 09  rd[key])).......
3220: 09 09 09 72 65 6c 6f 61 64 20 3d 20 54 72 75 65  ...reload = True
3230: 0a 0a 09 09 09 09 09 09 69 66 20 72 65 6c 6f 61  ........if reloa
3240: 64 3a 0a 09 09 09 09 09 09 09 70 72 69 6e 74 28  d:........print(
3250: 27 52 65 6c 6f 61 64 69 6e 67 2e 27 29 0a 09 09  'Reloading.')...
3260: 09 09 09 09 09 69 66 20 6f 73 2e 61 63 63 65 73  .....if os.acces
3270: 73 28 74 65 6d 70 5f 6e 61 6d 65 2c 20 6f 73 2e  s(temp_name, os.
3280: 52 5f 4f 4b 29 3a 0a 09 09 09 09 09 09 09 09 6f  R_OK):.........o
3290: 73 2e 75 6e 6c 69 6e 6b 28 74 65 6d 70 5f 6e 61  s.unlink(temp_na
32a0: 6d 65 29 0a 09 09 09 09 09 09 09 69 66 20 6f 73  me)........if os
32b0: 2e 61 63 63 65 73 73 28 66 69 6c 65 5f 6e 61 6d  .access(file_nam
32c0: 65 2c 20 6f 73 2e 52 5f 4f 4b 29 3a 0a 09 09 09  e, os.R_OK):....
32d0: 09 09 09 09 09 6f 73 2e 75 6e 6c 69 6e 6b 28 66  .....os.unlink(f
32e0: 69 6c 65 5f 6e 61 6d 65 29 0a 09 09 09 09 09 09  ile_name).......
32f0: 69 66 20 6e 65 77 5f 72 65 63 6f 72 64 5b 27 5f  if new_record['_
3300: 70 61 72 74 73 27 5d 20 3d 3d 20 4e 6f 6e 65 20  parts'] == None 
3310: 6f 72 20 72 65 6c 6f 61 64 3a 0a 09 09 09 09 09  or reload:......
3320: 09 09 6e 65 77 5f 72 65 63 6f 72 64 5b 27 5f 70  ..new_record['_p
3330: 61 72 74 73 27 5d 20 3d 20 73 70 61 63 65 6d 61  arts'] = spacema
3340: 70 2e 53 70 61 63 65 4d 61 70 28 7b 30 3a 20 69  p.SpaceMap({0: i
3350: 6e 74 28 6e 65 77 5f 72 65 63 6f 72 64 5b 27 43  nt(new_record['C
3360: 6f 6e 74 65 6e 74 2d 4c 65 6e 67 74 68 27 5d 29  ontent-Length'])
3370: 7d 29 0a 09 09 09 09 09 09 70 72 69 6e 74 28 6e  }).......print(n
3380: 65 77 5f 72 65 63 6f 72 64 29 0a 0a 09 09 09 09  ew_record)......
3390: 09 09 23 20 64 6f 77 6e 6c 6f 61 64 69 6e 67 20  ..# downloading 
33a0: 66 69 6c 65 20 6f 72 20 73 65 67 6d 65 6e 74 0a  file or segment.
33b0: 09 09 09 09 09 09 69 66 20 27 43 6f 6e 74 65 6e  ......if 'Conten
33c0: 74 2d 4c 65 6e 67 74 68 27 20 69 6e 20 6e 65 77  t-Length' in new
33d0: 5f 72 65 63 6f 72 64 3a 0a 09 09 09 09 09 09 09  _record:........
33e0: 69 66 20 6e 65 65 64 65 64 20 3d 3d 20 4e 6f 6e  if needed == Non
33f0: 65 3a 0a 09 09 09 09 09 09 09 09 6e 65 65 64 65  e:.........neede
3400: 64 20 3d 20 6e 65 77 5f 72 65 63 6f 72 64 5b 27  d = new_record['
3410: 5f 70 61 72 74 73 27 5d 0a 09 09 09 09 09 09 09  _parts']........
3420: 65 6c 73 65 3a 0a 09 09 09 09 09 09 09 09 69 66  else:.........if
3430: 20 6c 65 6e 28 6e 65 65 64 65 64 29 20 3e 20 31   len(needed) > 1
3440: 3a 0a 09 09 09 09 09 09 09 09 09 70 72 69 6e 74  :..........print
3450: 28 22 4d 75 6c 74 69 70 61 72 74 20 72 65 71 75  ("Multipart requ
3460: 65 73 74 73 20 63 75 72 72 65 6e 74 6c 79 20 6e  ests currently n
3470: 6f 74 20 73 75 70 70 6f 72 74 65 64 2e 22 29 0a  ot supported.").
3480: 09 09 09 09 09 09 09 09 09 61 73 73 65 72 74 20  .........assert 
3490: 46 61 6c 73 65 2c 20 27 53 6b 69 70 20 74 68 69  False, 'Skip thi
34a0: 73 20 6f 6e 65 20 66 6f 72 20 6e 6f 77 2e 27 0a  s one for now.'.
34b0: 09 09 09 09 09 09 65 6c 73 65 3a 0a 09 09 09 09  ......else:.....
34c0: 09 09 09 61 73 73 65 72 74 20 46 61 6c 73 65 2c  ...assert False,
34d0: 20 27 4e 6f 20 43 6f 6e 74 65 6e 74 2d 4c 65 6e   'No Content-Len
34e0: 67 74 68 20 6f 72 20 43 6f 6e 74 65 6e 74 2d 52  gth or Content-R
34f0: 61 6e 67 65 20 68 65 61 64 65 72 2e 27 0a 0a 09  ange header.'...
3500: 09 09 09 09 09 6e 65 77 5f 72 65 63 6f 72 64 5b  .....new_record[
3510: 27 5f 74 69 6d 65 27 5d 20 3d 20 64 61 74 65 74  '_time'] = datet
3520: 69 6d 65 2e 64 61 74 65 74 69 6d 65 2e 6e 6f 77  ime.datetime.now
3530: 28 29 0a 09 09 09 09 09 09 69 66 20 73 65 6c 66  ().......if self
3540: 2e 63 6f 6d 6d 61 6e 64 20 6e 6f 74 20 69 6e 20  .command not in 
3550: 28 27 48 45 41 44 27 29 3a 0a 09 09 09 09 09 09  ('HEAD'):.......
3560: 09 23 20 66 69 6c 65 20 69 73 20 63 72 65 61 74  .# file is creat
3570: 65 64 20 61 74 20 74 65 6d 70 6f 72 61 72 79 20  ed at temporary 
3580: 6c 6f 63 61 74 69 6f 6e 20 61 6e 64 20 6d 6f 76  location and mov
3590: 65 64 20 69 6e 20 70 6c 61 63 65 20 6f 6e 6c 79  ed in place only
35a0: 20 77 68 65 6e 20 64 6f 77 6e 6c 6f 61 64 20 63   when download c
35b0: 6f 6d 70 6c 65 74 65 73 0a 09 09 09 09 09 09 09  ompletes........
35c0: 69 66 20 6e 6f 74 20 6f 73 2e 61 63 63 65 73 73  if not os.access
35d0: 28 74 65 6d 70 5f 6e 61 6d 65 2c 20 6f 73 2e 52  (temp_name, os.R
35e0: 5f 4f 4b 29 3a 0a 09 09 09 09 09 09 09 09 65 6d  _OK):.........em
35f0: 70 74 79 5f 6e 61 6d 65 20 3d 20 63 6f 6e 66 69  pty_name = confi
3600: 67 5b 27 64 69 72 27 5d 20 2b 20 6f 73 2e 73 65  g['dir'] + os.se
3610: 70 20 2b 20 27 2e 74 6d 70 27 0a 09 09 09 09 09  p + '.tmp'......
3620: 09 09 09 77 69 74 68 20 6f 70 65 6e 28 65 6d 70  ...with open(emp
3630: 74 79 5f 6e 61 6d 65 2c 20 27 77 2b 62 27 29 20  ty_name, 'w+b') 
3640: 61 73 20 73 6f 6d 65 5f 66 69 6c 65 3a 0a 09 09  as some_file:...
3650: 09 09 09 09 09 09 09 70 61 73 73 0a 09 09 09 09  .......pass.....
3660: 09 09 09 09 6f 73 2e 72 65 6e 61 6d 65 73 28 65  ....os.renames(e
3670: 6d 70 74 79 5f 6e 61 6d 65 2c 20 74 65 6d 70 5f  mpty_name, temp_
3680: 6e 61 6d 65 29 0a 09 09 09 09 09 09 09 74 65 6d  name)........tem
3690: 70 5f 66 69 6c 65 20 3d 20 6f 70 65 6e 28 74 65  p_file = open(te
36a0: 6d 70 5f 6e 61 6d 65 2c 20 27 72 2b 62 27 29 0a  mp_name, 'r+b').
36b0: 09 09 09 09 09 09 09 6e 65 65 64 65 64 2e 72 65  .......needed.re
36c0: 77 69 6e 64 28 29 0a 09 09 09 09 09 09 09 77 68  wind()........wh
36d0: 69 6c 65 20 54 72 75 65 3a 0a 09 09 09 09 09 09  ile True:.......
36e0: 09 09 28 73 74 61 72 74 2c 20 65 6e 64 29 20 3d  ..(start, end) =
36f0: 20 6e 65 65 64 65 64 2e 70 6f 70 28 29 0a 09 09   needed.pop()...
3700: 09 09 09 09 09 09 69 66 20 73 74 61 72 74 20 3d  ......if start =
3710: 3d 20 4e 6f 6e 65 3a 0a 09 09 09 09 09 09 09 09  = None:.........
3720: 09 62 72 65 61 6b 0a 09 09 09 09 09 09 09 09 73  .break.........s
3730: 74 72 65 61 6d 5f 6c 61 73 74 20 3d 20 73 74 61  tream_last = sta
3740: 72 74 0a 09 09 09 09 09 09 09 09 6f 6c 64 5f 72  rt.........old_r
3750: 65 63 6f 72 64 20 3d 20 6e 65 77 5f 72 65 63 6f  ecord = new_reco
3760: 72 64 0a 09 09 09 09 09 09 09 09 69 66 20 65 6e  rd.........if en
3770: 64 20 2d 20 73 74 61 72 74 20 3c 20 62 6c 6f 63  d - start < bloc
3780: 6b 5f 73 69 7a 65 3a 0a 09 09 09 09 09 09 09 09  k_size:.........
3790: 09 72 65 71 5f 62 6c 6f 63 6b 5f 73 69 7a 65 20  .req_block_size 
37a0: 3d 20 65 6e 64 20 2d 20 73 74 61 72 74 0a 09 09  = end - start...
37b0: 09 09 09 09 09 09 65 6c 73 65 3a 0a 09 09 09 09  ......else:.....
37c0: 09 09 09 09 09 72 65 71 5f 62 6c 6f 63 6b 5f 73  .....req_block_s
37d0: 69 7a 65 20 3d 20 62 6c 6f 63 6b 5f 73 69 7a 65  ize = block_size
37e0: 0a 09 09 09 09 09 09 09 09 62 75 66 66 65 72 20  .........buffer 
37f0: 3d 20 73 6f 75 72 63 65 2e 72 65 61 64 28 72 65  = source.read(re
3800: 71 5f 62 6c 6f 63 6b 5f 73 69 7a 65 29 0a 09 09  q_block_size)...
3810: 09 09 09 09 09 09 6c 65 6e 67 74 68 20 3d 20 6c  ......length = l
3820: 65 6e 28 62 75 66 66 65 72 29 0a 09 09 09 09 09  en(buffer)......
3830: 09 09 09 77 68 69 6c 65 20 6c 65 6e 67 74 68 20  ...while length 
3840: 3e 20 30 20 61 6e 64 20 73 74 72 65 61 6d 5f 6c  > 0 and stream_l
3850: 61 73 74 20 3c 20 65 6e 64 3a 0a 09 09 09 09 09  ast < end:......
3860: 09 09 09 09 73 74 72 65 61 6d 5f 70 6f 73 20 3d  ....stream_pos =
3870: 20 73 74 72 65 61 6d 5f 6c 61 73 74 20 2b 20 6c   stream_last + l
3880: 65 6e 67 74 68 0a 09 09 09 09 09 09 09 09 09 61  ength..........a
3890: 73 73 65 72 74 20 6e 6f 74 20 73 74 72 65 61 6d  ssert not stream
38a0: 5f 70 6f 73 20 3e 20 65 6e 64 2c 20 27 52 65 63  _pos > end, 'Rec
38b0: 65 69 76 65 64 20 6d 6f 72 65 20 64 61 74 61 20  eived more data 
38c0: 74 68 65 6e 20 72 65 71 75 65 73 74 65 64 3a 20  then requested: 
38d0: 70 6f 73 3a 7b 7d 20 73 74 61 72 74 3a 7b 7d 20  pos:{} start:{} 
38e0: 65 6e 64 3a 7b 7d 2e 27 2e 66 6f 72 6d 61 74 28  end:{}.'.format(
38f0: 73 74 72 65 61 6d 5f 70 6f 73 2c 20 73 74 61 72  stream_pos, star
3900: 74 2c 20 65 6e 64 29 0a 09 09 09 09 09 09 09 09  t, end).........
3910: 09 74 65 6d 70 5f 66 69 6c 65 2e 73 65 65 6b 28  .temp_file.seek(
3920: 73 74 72 65 61 6d 5f 6c 61 73 74 29 0a 09 09 09  stream_last)....
3930: 09 09 09 09 09 09 74 65 6d 70 5f 66 69 6c 65 2e  ......temp_file.
3940: 77 72 69 74 65 28 62 75 66 66 65 72 29 0a 09 09  write(buffer)...
3950: 09 09 09 09 09 09 09 6e 65 77 5f 72 65 63 6f 72  .......new_recor
3960: 64 5b 27 5f 70 61 72 74 73 27 5d 20 3d 20 6e 65  d['_parts'] = ne
3970: 77 5f 72 65 63 6f 72 64 5b 27 5f 70 61 72 74 73  w_record['_parts
3980: 27 5d 20 2d 20 73 70 61 63 65 6d 61 70 2e 53 70  '] - spacemap.Sp
3990: 61 63 65 4d 61 70 28 7b 73 74 72 65 61 6d 5f 6c  aceMap({stream_l
39a0: 61 73 74 3a 20 73 74 72 65 61 6d 5f 70 6f 73 7d  ast: stream_pos}
39b0: 29 0a 09 09 09 09 09 09 09 09 09 69 6e 64 65 78  )..........index
39c0: 5b 6d 79 5f 70 61 74 68 5d 20 3d 20 6f 6c 64 5f  [my_path] = old_
39d0: 72 65 63 6f 72 64 0a 09 09 09 09 09 09 09 09 09  record..........
39e0: 69 6e 64 65 78 2e 73 79 6e 63 28 29 0a 09 09 09  index.sync()....
39f0: 09 09 09 09 09 09 6f 6c 64 5f 72 65 63 6f 72 64  ......old_record
3a00: 20 3d 20 6e 65 77 5f 72 65 63 6f 72 64 0a 09 09   = new_record...
3a10: 09 09 09 09 09 09 09 73 74 72 65 61 6d 5f 6c 61  .......stream_la
3a20: 73 74 20 3d 20 73 74 72 65 61 6d 5f 70 6f 73 0a  st = stream_pos.
3a30: 09 09 09 09 09 09 09 09 09 69 66 20 65 6e 64 20  .........if end 
3a40: 2d 20 73 74 72 65 61 6d 5f 6c 61 73 74 20 3c 20  - stream_last < 
3a50: 62 6c 6f 63 6b 5f 73 69 7a 65 3a 0a 09 09 09 09  block_size:.....
3a60: 09 09 09 09 09 09 72 65 71 5f 62 6c 6f 63 6b 5f  ......req_block_
3a70: 73 69 7a 65 20 3d 20 65 6e 64 20 2d 20 73 74 72  size = end - str
3a80: 65 61 6d 5f 6c 61 73 74 0a 09 09 09 09 09 09 09  eam_last........
3a90: 09 09 62 75 66 66 65 72 20 3d 20 73 6f 75 72 63  ..buffer = sourc
3aa0: 65 2e 72 65 61 64 28 72 65 71 5f 62 6c 6f 63 6b  e.read(req_block
3ab0: 5f 73 69 7a 65 29 0a 09 09 09 09 09 09 09 09 09  _size)..........
3ac0: 6c 65 6e 67 74 68 20 3d 20 6c 65 6e 28 62 75 66  length = len(buf
3ad0: 66 65 72 29 0a 09 09 09 09 09 09 09 23 20 6d 6f  fer)........# mo
3ae0: 76 69 6e 67 20 64 6f 77 6e 6c 6f 61 64 65 64 20  ving downloaded 
3af0: 64 61 74 61 20 74 6f 20 72 65 61 6c 20 66 69 6c  data to real fil
3b00: 65 0a 09 09 09 09 09 09 09 74 65 6d 70 5f 66 69  e........temp_fi
3b10: 6c 65 2e 63 6c 6f 73 65 28 29 0a 0a 09 09 09 09  le.close()......
3b20: 09 09 70 72 69 6e 74 28 6e 65 77 5f 72 65 63 6f  ..print(new_reco
3b30: 72 64 29 0a 09 09 09 09 09 09 69 6e 64 65 78 5b  rd).......index[
3b40: 6d 79 5f 70 61 74 68 5d 20 3d 20 6e 65 77 5f 72  my_path] = new_r
3b50: 65 63 6f 72 64 0a 09 09 09 09 09 09 69 6e 64 65  ecord.......inde
3b60: 78 2e 73 79 6e 63 28 29 0a 0a 09 09 09 09 65 78  x.sync()......ex
3b70: 63 65 70 74 20 75 72 6c 6c 69 62 2e 65 72 72 6f  cept urllib.erro
3b80: 72 2e 48 54 54 50 45 72 72 6f 72 20 61 73 20 65  r.HTTPError as e
3b90: 72 72 6f 72 3a 0a 09 09 09 09 09 23 20 69 6e 20  rror:......# in 
3ba0: 63 61 73 65 20 6f 66 20 65 72 72 6f 72 20 77 65  case of error we
3bb0: 20 64 6f 6e 27 74 20 6e 65 65 64 20 74 6f 20 64   don't need to d
3bc0: 6f 20 61 6e 79 74 68 69 6e 67 20 61 63 74 75 61  o anything actua
3bd0: 6c 6c 79 2c 0a 09 09 09 09 09 23 20 69 66 20 66  lly,......# if f
3be0: 69 6c 65 20 64 6f 77 6e 6c 6f 61 64 20 73 74 61  ile download sta
3bf0: 6c 6c 73 20 6f 72 20 66 61 69 6c 73 20 74 68 65  lls or fails the
3c00: 20 66 69 6c 65 20 77 6f 75 6c 64 20 6e 6f 74 20   file would not 
3c10: 62 65 20 6d 6f 76 65 64 20 74 6f 20 69 74 27 73  be moved to it's
3c20: 20 6c 6f 63 61 74 69 6f 6e 0a 09 09 09 09 09 70   location......p
3c30: 72 69 6e 74 28 65 72 72 6f 72 29 0a 0a 09 09 09  rint(error).....
3c40: 69 66 20 27 5f 70 61 72 74 73 27 20 69 6e 20 69  if '_parts' in i
3c50: 6e 64 65 78 5b 6d 79 5f 70 61 74 68 5d 20 61 6e  ndex[my_path] an
3c60: 64 20 69 6e 64 65 78 5b 6d 79 5f 70 61 74 68 5d  d index[my_path]
3c70: 5b 27 5f 70 61 72 74 73 27 5d 20 3d 3d 20 73 70  ['_parts'] == sp
3c80: 61 63 65 6d 61 70 2e 53 70 61 63 65 4d 61 70 28  acemap.SpaceMap(
3c90: 29 3a 0a 09 09 09 09 23 20 6a 75 73 74 20 6d 6f  ):.....# just mo
3ca0: 76 69 6e 67 0a 09 09 09 09 23 20 64 72 6f 70 20  ving.....# drop 
3cb0: 6f 6c 64 20 64 69 72 73 20 58 58 58 0a 09 09 09  old dirs XXX....
3cc0: 09 70 72 69 6e 74 28 27 4d 6f 76 69 6e 67 20 74  .print('Moving t
3cd0: 65 6d 70 6f 72 61 72 79 20 66 69 6c 65 20 74 6f  emporary file to
3ce0: 20 6e 65 77 20 64 65 73 74 69 6e 61 74 69 6f 6e   new destination
3cf0: 2e 27 29 0a 09 09 09 09 6f 73 2e 72 65 6e 61 6d  .').....os.renam
3d00: 65 73 28 74 65 6d 70 5f 6e 61 6d 65 2c 20 66 69  es(temp_name, fi
3d10: 6c 65 5f 6e 61 6d 65 29 0a 0a 09 09 09 69 66 20  le_name).....if 
3d20: 73 65 6c 66 2e 63 6f 6d 6d 61 6e 64 20 3d 3d 20  self.command == 
3d30: 27 48 45 41 44 27 3a 0a 09 09 09 09 73 65 6c 66  'HEAD':.....self
3d40: 2e 73 65 6e 64 5f 72 65 73 70 6f 6e 73 65 28 32  .send_response(2
3d50: 30 30 29 0a 09 09 09 09 69 66 20 27 43 6f 6e 74  00).....if 'Cont
3d60: 65 6e 74 2d 4c 65 6e 67 74 68 27 20 69 6e 20 69  ent-Length' in i
3d70: 6e 64 65 78 5b 6d 79 5f 70 61 74 68 5d 3a 0a 09  ndex[my_path]:..
3d80: 09 09 09 09 73 65 6c 66 2e 73 65 6e 64 5f 68 65  ....self.send_he
3d90: 61 64 65 72 28 27 43 6f 6e 74 65 6e 74 2d 4c 65  ader('Content-Le
3da0: 6e 67 74 68 27 2c 20 69 6e 64 65 78 5b 6d 79 5f  ngth', index[my_
3db0: 70 61 74 68 5d 5b 27 43 6f 6e 74 65 6e 74 2d 4c  path]['Content-L
3dc0: 65 6e 67 74 68 27 5d 29 0a 09 09 09 09 73 65 6c  ength']).....sel
3dd0: 66 2e 73 65 6e 64 5f 68 65 61 64 65 72 28 27 41  f.send_header('A
3de0: 63 63 65 70 74 2d 52 61 6e 67 65 73 27 2c 20 27  ccept-Ranges', '
3df0: 62 79 74 65 73 27 29 0a 09 09 09 09 73 65 6c 66  bytes').....self
3e00: 2e 73 65 6e 64 5f 68 65 61 64 65 72 28 27 43 6f  .send_header('Co
3e10: 6e 74 65 6e 74 2d 54 79 70 65 27 2c 20 27 61 70  ntent-Type', 'ap
3e20: 70 6c 69 63 61 74 69 6f 6e 2f 6f 63 74 65 74 2d  plication/octet-
3e30: 73 74 72 65 61 6d 27 29 0a 09 09 09 09 69 66 20  stream').....if 
3e40: 27 4c 61 73 74 2d 4d 6f 64 69 66 69 65 64 27 20  'Last-Modified' 
3e50: 69 6e 20 69 6e 64 65 78 5b 6d 79 5f 70 61 74 68  in index[my_path
3e60: 5d 3a 0a 09 09 09 09 09 73 65 6c 66 2e 73 65 6e  ]:......self.sen
3e70: 64 5f 68 65 61 64 65 72 28 27 4c 61 73 74 2d 4d  d_header('Last-M
3e80: 6f 64 69 66 69 65 64 27 2c 20 69 6e 64 65 78 5b  odified', index[
3e90: 6d 79 5f 70 61 74 68 5d 5b 27 4c 61 73 74 2d 4d  my_path]['Last-M
3ea0: 6f 64 69 66 69 65 64 27 5d 29 0a 09 09 09 09 73  odified']).....s
3eb0: 65 6c 66 2e 65 6e 64 5f 68 65 61 64 65 72 73 28  elf.end_headers(
3ec0: 29 0a 09 09 09 65 6c 73 65 3a 0a 09 09 09 09 69  )....else:.....i
3ed0: 66 20 28 27 5f 70 61 72 74 73 27 20 69 6e 20 69  f ('_parts' in i
3ee0: 6e 64 65 78 5b 6d 79 5f 70 61 74 68 5d 20 61 6e  ndex[my_path] an
3ef0: 64 20 69 6e 64 65 78 5b 6d 79 5f 70 61 74 68 5d  d index[my_path]
3f00: 5b 27 5f 70 61 72 74 73 27 5d 20 21 3d 20 73 70  ['_parts'] != sp
3f10: 61 63 65 6d 61 70 2e 53 70 61 63 65 4d 61 70 28  acemap.SpaceMap(
3f20: 29 29 20 6f 72 20 6e 6f 74 20 6f 73 2e 61 63 63  )) or not os.acc
3f30: 65 73 73 28 66 69 6c 65 5f 6e 61 6d 65 2c 20 6f  ess(file_name, o
3f40: 73 2e 52 5f 4f 4b 29 3a 0a 09 09 09 09 09 66 69  s.R_OK):......fi
3f50: 6c 65 5f 6e 61 6d 65 20 3d 20 74 65 6d 70 5f 6e  le_name = temp_n
3f60: 61 6d 65 0a 0a 09 09 09 09 77 69 74 68 20 6f 70  ame......with op
3f70: 65 6e 28 66 69 6c 65 5f 6e 61 6d 65 2c 20 27 72  en(file_name, 'r
3f80: 62 27 29 20 61 73 20 72 65 61 6c 5f 66 69 6c 65  b') as real_file
3f90: 3a 0a 09 09 09 09 09 66 69 6c 65 5f 73 74 61 74  :......file_stat
3fa0: 20 3d 20 6f 73 2e 73 74 61 74 28 66 69 6c 65 5f   = os.stat(file_
3fb0: 6e 61 6d 65 29 0a 09 09 09 09 09 69 66 20 27 52  name)......if 'R
3fc0: 61 6e 67 65 27 20 69 6e 20 73 65 6c 66 2e 68 65  ange' in self.he
3fd0: 61 64 65 72 73 3a 0a 09 09 09 09 09 09 73 65 6c  aders:.......sel
3fe0: 66 2e 73 65 6e 64 5f 72 65 73 70 6f 6e 73 65 28  f.send_response(
3ff0: 32 30 36 29 0a 09 09 09 09 09 09 72 61 6e 67 65  206).......range
4000: 73 20 3d 20 28 29 0a 09 09 09 09 09 09 72 65 71  s = ().......req
4010: 75 65 73 74 65 64 5f 72 61 6e 67 65 73 2e 72 65  uested_ranges.re
4020: 77 69 6e 64 28 29 0a 09 09 09 09 09 09 77 68 69  wind().......whi
4030: 6c 65 20 54 72 75 65 3a 0a 09 09 09 09 09 09 09  le True:........
4040: 70 61 69 72 20 3d 20 72 65 71 75 65 73 74 65 64  pair = requested
4050: 5f 72 61 6e 67 65 73 2e 70 6f 70 28 29 0a 09 09  _ranges.pop()...
4060: 09 09 09 09 09 69 66 20 70 61 69 72 5b 30 5d 20  .....if pair[0] 
4070: 3d 3d 20 4e 6f 6e 65 3a 0a 09 09 09 09 09 09 09  == None:........
4080: 09 62 72 65 61 6b 0a 09 09 09 09 09 09 09 72 61  .break........ra
4090: 6e 67 65 73 20 2b 3d 20 27 7b 7d 2d 7b 7d 27 2e  nges += '{}-{}'.
40a0: 66 6f 72 6d 61 74 28 70 61 69 72 5b 30 5d 2c 20  format(pair[0], 
40b0: 73 74 72 28 70 61 69 72 5b 31 5d 20 2d 20 31 29  str(pair[1] - 1)
40c0: 29 2c 0a 09 09 09 09 09 09 73 65 6c 66 2e 73 65  ),.......self.se
40d0: 6e 64 5f 68 65 61 64 65 72 28 27 43 6f 6e 74 65  nd_header('Conte
40e0: 6e 74 2d 52 61 6e 67 65 27 2c 20 27 62 79 74 65  nt-Range', 'byte
40f0: 73 20 7b 7d 2f 7b 7d 27 2e 66 6f 72 6d 61 74 28  s {}/{}'.format(
4100: 27 2c 27 2e 6a 6f 69 6e 28 72 61 6e 67 65 73 29  ','.join(ranges)
4110: 2c 20 69 6e 64 65 78 5b 6d 79 5f 70 61 74 68 5d  , index[my_path]
4120: 5b 27 43 6f 6e 74 65 6e 74 2d 4c 65 6e 67 74 68  ['Content-Length
4130: 27 5d 29 29 0a 09 09 09 09 09 65 6c 73 65 3a 0a  ']))......else:.
4140: 09 09 09 09 09 09 73 65 6c 66 2e 73 65 6e 64 5f  ......self.send_
4150: 72 65 73 70 6f 6e 73 65 28 32 30 30 29 0a 09 09  response(200)...
4160: 09 09 09 09 73 65 6c 66 2e 73 65 6e 64 5f 68 65  ....self.send_he
4170: 61 64 65 72 28 27 43 6f 6e 74 65 6e 74 2d 4c 65  ader('Content-Le
4180: 6e 67 74 68 27 2c 20 73 74 72 28 66 69 6c 65 5f  ngth', str(file_
4190: 73 74 61 74 2e 73 74 5f 73 69 7a 65 29 29 0a 09  stat.st_size))..
41a0: 09 09 09 09 09 72 65 71 75 65 73 74 65 64 5f 72  .....requested_r
41b0: 61 6e 67 65 73 20 3d 20 73 70 61 63 65 6d 61 70  anges = spacemap
41c0: 2e 53 70 61 63 65 4d 61 70 28 7b 30 3a 20 66 69  .SpaceMap({0: fi
41d0: 6c 65 5f 73 74 61 74 2e 73 74 5f 73 69 7a 65 7d  le_stat.st_size}
41e0: 29 0a 09 09 09 09 09 73 65 6c 66 2e 73 65 6e 64  )......self.send
41f0: 5f 68 65 61 64 65 72 28 27 4c 61 73 74 2d 4d 6f  _header('Last-Mo
4200: 64 69 66 69 65 64 27 2c 20 69 6e 64 65 78 5b 6d  dified', index[m
4210: 79 5f 70 61 74 68 5d 5b 27 4c 61 73 74 2d 4d 6f  y_path]['Last-Mo
4220: 64 69 66 69 65 64 27 5d 29 0a 09 09 09 09 09 73  dified'])......s
4230: 65 6c 66 2e 73 65 6e 64 5f 68 65 61 64 65 72 28  elf.send_header(
4240: 27 43 6f 6e 74 65 6e 74 2d 54 79 70 65 27 2c 20  'Content-Type', 
4250: 27 61 70 70 6c 69 63 61 74 69 6f 6e 2f 6f 63 74  'application/oct
4260: 65 74 2d 73 74 72 65 61 6d 27 29 0a 09 09 09 09  et-stream').....
4270: 09 73 65 6c 66 2e 65 6e 64 5f 68 65 61 64 65 72  .self.end_header
4280: 73 28 29 0a 09 09 09 09 09 69 66 20 73 65 6c 66  s()......if self
4290: 2e 63 6f 6d 6d 61 6e 64 20 69 6e 20 28 27 47 45  .command in ('GE
42a0: 54 27 29 3a 0a 09 09 09 09 09 09 69 66 20 6c 65  T'):.......if le
42b0: 6e 28 72 65 71 75 65 73 74 65 64 5f 72 61 6e 67  n(requested_rang
42c0: 65 73 29 20 3e 20 30 3a 0a 09 09 09 09 09 09 09  es) > 0:........
42d0: 72 65 71 75 65 73 74 65 64 5f 72 61 6e 67 65 73  requested_ranges
42e0: 2e 72 65 77 69 6e 64 28 29 0a 09 09 09 09 09 09  .rewind().......
42f0: 09 28 73 74 61 72 74 2c 20 65 6e 64 29 20 3d 20  .(start, end) = 
4300: 72 65 71 75 65 73 74 65 64 5f 72 61 6e 67 65 73  requested_ranges
4310: 2e 70 6f 70 28 29 0a 09 09 09 09 09 09 65 6c 73  .pop().......els
4320: 65 3a 0a 09 09 09 09 09 09 09 73 74 61 72 74 20  e:........start 
4330: 3d 20 30 0a 09 09 09 09 09 09 09 65 6e 64 20 3d  = 0........end =
4340: 20 69 6e 64 65 78 5b 6d 79 5f 70 61 74 68 5d 5b   index[my_path][
4350: 27 43 6f 6e 74 65 6e 74 2d 4c 65 6e 67 74 68 27  'Content-Length'
4360: 5d 0a 09 09 09 09 09 09 72 65 61 6c 5f 66 69 6c  ].......real_fil
4370: 65 2e 73 65 65 6b 28 73 74 61 72 74 29 0a 09 09  e.seek(start)...
4380: 09 09 09 09 69 66 20 62 6c 6f 63 6b 5f 73 69 7a  ....if block_siz
4390: 65 20 3e 20 65 6e 64 20 2d 20 73 74 61 72 74 3a  e > end - start:
43a0: 0a 09 09 09 09 09 09 09 72 65 71 5f 62 6c 6f 63  ........req_bloc
43b0: 6b 5f 73 69 7a 65 20 3d 20 65 6e 64 20 2d 20 73  k_size = end - s
43c0: 74 61 72 74 0a 09 09 09 09 09 09 65 6c 73 65 3a  tart.......else:
43d0: 0a 09 09 09 09 09 09 09 72 65 71 5f 62 6c 6f 63  ........req_bloc
43e0: 6b 5f 73 69 7a 65 20 3d 20 62 6c 6f 63 6b 5f 73  k_size = block_s
43f0: 69 7a 65 0a 09 09 09 09 09 09 62 75 66 66 65 72  ize.......buffer
4400: 20 3d 20 72 65 61 6c 5f 66 69 6c 65 2e 72 65 61   = real_file.rea
4410: 64 28 72 65 71 5f 62 6c 6f 63 6b 5f 73 69 7a 65  d(req_block_size
4420: 29 0a 09 09 09 09 09 09 6c 65 6e 67 74 68 20 3d  ).......length =
4430: 20 6c 65 6e 28 62 75 66 66 65 72 29 0a 09 09 09   len(buffer)....
4440: 09 09 09 77 68 69 6c 65 20 6c 65 6e 67 74 68 20  ...while length 
4450: 3e 20 30 3a 0a 09 09 09 09 09 09 09 73 65 6c 66  > 0:........self
4460: 2e 77 66 69 6c 65 2e 77 72 69 74 65 28 62 75 66  .wfile.write(buf
4470: 66 65 72 29 0a 09 09 09 09 09 09 09 73 74 61 72  fer)........star
4480: 74 20 2b 3d 20 6c 65 6e 28 62 75 66 66 65 72 29  t += len(buffer)
4490: 0a 09 09 09 09 09 09 09 69 66 20 72 65 71 5f 62  ........if req_b
44a0: 6c 6f 63 6b 5f 73 69 7a 65 20 3e 20 65 6e 64 20  lock_size > end 
44b0: 2d 20 73 74 61 72 74 3a 0a 09 09 09 09 09 09 09  - start:........
44c0: 09 72 65 71 5f 62 6c 6f 63 6b 5f 73 69 7a 65 20  .req_block_size 
44d0: 3d 20 65 6e 64 20 2d 20 73 74 61 72 74 0a 09 09  = end - start...
44e0: 09 09 09 09 09 69 66 20 72 65 71 5f 62 6c 6f 63  .....if req_bloc
44f0: 6b 5f 73 69 7a 65 20 3d 3d 20 30 3a 0a 09 09 09  k_size == 0:....
4500: 09 09 09 09 09 62 72 65 61 6b 0a 09 09 09 09 09  .....break......
4510: 09 09 62 75 66 66 65 72 20 3d 20 72 65 61 6c 5f  ..buffer = real_
4520: 66 69 6c 65 2e 72 65 61 64 28 72 65 71 5f 62 6c  file.read(req_bl
4530: 6f 63 6b 5f 73 69 7a 65 29 0a 09 09 09 09 09 09  ock_size).......
4540: 09 6c 65 6e 67 74 68 20 3d 20 6c 65 6e 28 62 75  .length = len(bu
4550: 66 66 65 72 29 0a 09 09 09 09 09 0a 09 09 64 65  ffer).........de
4560: 66 20 64 6f 5f 48 45 41 44 28 73 65 6c 66 29 3a  f do_HEAD(self):
4570: 0a 09 09 09 72 65 74 75 72 6e 20 73 65 6c 66 2e  ....return self.
4580: 5f 5f 70 72 6f 63 65 73 73 28 29 0a 09 09 64 65  __process()...de
4590: 66 20 64 6f 5f 47 45 54 28 73 65 6c 66 29 3a 0a  f do_GET(self):.
45a0: 09 09 09 72 65 74 75 72 6e 20 73 65 6c 66 2e 5f  ...return self._
45b0: 5f 70 72 6f 63 65 73 73 28 29 0a 0a 09 63 6f 6e  _process()...con
45c0: 66 69 67 2e 73 65 63 74 69 6f 6e 28 27 67 65 6e  fig.section('gen
45d0: 65 72 61 6c 27 29 0a 09 73 65 72 76 65 72 20 3d  eral')..server =
45e0: 20 68 74 74 70 2e 73 65 72 76 65 72 2e 48 54 54   http.server.HTT
45f0: 50 53 65 72 76 65 72 28 28 27 31 32 37 2e 30 2e  PServer(('127.0.
4600: 30 2e 31 27 2c 20 69 6e 74 28 63 6f 6e 66 69 67  0.1', int(config
4610: 5b 27 70 6f 72 74 27 5d 29 29 2c 20 4d 79 52 65  ['port'])), MyRe
4620: 71 75 65 73 74 48 61 6e 64 6c 65 72 29 0a 09 73  questHandler)..s
4630: 65 72 76 65 72 2e 73 65 72 76 65 5f 66 6f 72 65  erver.serve_fore
4640: 76 65 72 28 29 0a 0a 65 6c 73 65 3a 0a 09 77 68  ver()..else:..wh
4650: 69 6c 65 20 54 72 75 65 3a 0a 09 09 75 6e 63 68  ile True:...unch
4660: 65 63 6b 65 64 5f 66 69 6c 65 73 20 3d 20 73 65  ecked_files = se
4670: 74 28 29 0a 09 09 63 68 65 63 6b 65 64 5f 66 69  t()...checked_fi
4680: 6c 65 73 20 3d 20 30 0a 0a 09 09 23 20 72 65 61  les = 0....# rea
4690: 64 69 6e 67 20 6c 6f 67 20 61 6e 64 20 73 74 6f  ding log and sto
46a0: 72 69 6e 67 20 66 6f 75 6e 64 20 75 72 6c 73 20  ring found urls 
46b0: 66 6f 72 20 70 72 6f 63 65 73 73 69 6e 67 0a 09  for processing..
46c0: 09 23 20 63 68 65 63 6b 20 66 69 6c 65 20 6d 74  .# check file mt
46d0: 69 6d 65 20 58 58 58 0a 09 09 77 69 74 68 20 6f  ime XXX...with o
46e0: 70 65 6e 28 6f 70 74 69 6f 6e 73 2e 6c 6f 67 2c  pen(options.log,
46f0: 20 27 72 27 29 20 61 73 20 6c 6f 67 5f 66 69 6c   'r') as log_fil
4700: 65 3a 0a 09 09 09 6c 6f 67 5f 6c 69 6e 65 20 3d  e:....log_line =
4710: 20 72 65 2e 63 6f 6d 70 69 6c 65 28 27 5e 5b 5e   re.compile('^[^
4720: 20 5d 2b 20 2d 20 2d 20 5c 5b 2e 2a 5d 20 22 28   ]+ - - \[.*] "(
4730: 47 45 54 7c 48 45 41 44 29 20 28 2e 2a 3f 29 28  GET|HEAD) (.*?)(
4740: 5c 3f 2e 2a 29 3f 20 48 54 54 50 2f 31 2e 31 22  \?.*)? HTTP/1.1"
4750: 20 28 5c 64 2b 29 20 5c 64 2b 20 22 28 2e 2a 29   (\d+) \d+ "(.*)
4760: 22 20 22 28 2e 2a 29 22 24 27 29 0a 09 09 09 66  " "(.*)"$')....f
4770: 6f 72 20 6c 69 6e 65 20 69 6e 20 6c 6f 67 5f 66  or line in log_f
4780: 69 6c 65 3a 0a 09 09 09 09 74 68 69 73 5f 6c 69  ile:.....this_li
4790: 6e 65 20 3d 20 6c 6f 67 5f 6c 69 6e 65 2e 6d 61  ne = log_line.ma
47a0: 74 63 68 28 6c 69 6e 65 2e 73 74 72 69 70 28 29  tch(line.strip()
47b0: 29 0a 09 09 09 09 69 66 20 74 68 69 73 5f 6c 69  ).....if this_li
47c0: 6e 65 3a 0a 09 09 09 09 09 75 6e 63 68 65 63 6b  ne:......uncheck
47d0: 65 64 5f 66 69 6c 65 73 2e 61 64 64 28 74 68 69  ed_files.add(thi
47e0: 73 5f 6c 69 6e 65 2e 67 72 6f 75 70 28 32 29 29  s_line.group(2))
47f0: 0a 0a 09 09 66 6f 72 20 75 72 6c 20 69 6e 20 75  ....for url in u
4800: 6e 63 68 65 63 6b 65 64 5f 66 69 6c 65 73 3a 0a  nchecked_files:.
4810: 09 09 09 72 65 6c 6f 61 64 20 3d 20 46 61 6c 73  ...reload = Fals
4820: 65 0a 09 09 09 72 65 63 68 65 63 6b 20 3d 20 46  e....recheck = F
4830: 61 6c 73 65 0a 09 09 09 69 6e 66 6f 20 3d 20 27  alse....info = '
4840: 43 68 65 63 6b 69 6e 67 20 66 69 6c 65 3a 20 27  Checking file: '
4850: 20 2b 20 75 72 6c 0a 0a 09 09 09 23 20 63 72 65   + url.....# cre
4860: 61 74 69 6e 67 20 65 6d 70 74 79 20 70 6c 61 63  ating empty plac
4870: 65 68 6f 6c 64 65 72 20 69 6e 20 69 6e 64 65 78  eholder in index
4880: 0a 09 09 09 69 66 20 6e 6f 74 20 75 72 6c 20 69  ....if not url i
4890: 6e 20 69 6e 64 65 78 3a 0a 09 09 09 09 69 6e 66  n index:.....inf
48a0: 6f 20 2b 3d 20 27 5c 6e 54 68 69 73 20 6f 6e 65  o += '\nThis one
48b0: 20 69 73 20 6e 65 77 2e 27 0a 09 09 09 09 69 6e   is new.'.....in
48c0: 64 65 78 5b 75 72 6c 5d 20 3d 20 7b 7d 0a 09 09  dex[url] = {}...
48d0: 09 09 72 65 6c 6f 61 64 20 3d 20 54 72 75 65 0a  ..reload = True.
48e0: 0a 09 09 09 23 20 63 72 65 61 74 69 6e 67 20 66  ....# creating f
48f0: 69 6c 65 20 6e 61 6d 65 20 66 72 6f 6d 20 75 72  ile name from ur
4900: 6c 0a 09 09 09 66 69 6c 65 5f 6e 61 6d 65 20 3d  l....file_name =
4910: 20 6f 70 74 69 6f 6e 73 2e 64 69 72 20 2b 20 72   options.dir + r
4920: 65 2e 63 6f 6d 70 69 6c 65 28 27 25 32 30 27 29  e.compile('%20')
4930: 2e 73 75 62 28 27 20 27 2c 20 75 72 6c 29 0a 0a  .sub(' ', url)..
4940: 09 09 09 23 20 66 6f 72 63 69 62 6c 79 20 63 68  ...# forcibly ch
4950: 65 63 6b 69 6e 67 20 66 69 6c 65 20 69 66 20 6e  ecking file if n
4960: 6f 20 66 69 6c 65 20 70 72 65 73 65 6e 74 0a 09  o file present..
4970: 09 09 69 66 20 6e 6f 74 20 72 65 6c 6f 61 64 20  ..if not reload 
4980: 61 6e 64 20 6e 6f 74 20 6f 73 2e 61 63 63 65 73  and not os.acces
4990: 73 28 66 69 6c 65 5f 6e 61 6d 65 2c 20 6f 73 2e  s(file_name, os.
49a0: 52 5f 4f 4b 29 3a 0a 09 09 09 09 69 6e 66 6f 20  R_OK):.....info 
49b0: 2b 3d 20 27 5c 6e 46 69 6c 65 20 6e 6f 74 20 66  += '\nFile not f
49c0: 6f 75 6e 64 20 6f 72 20 69 6e 61 63 63 65 73 73  ound or inaccess
49d0: 69 62 6c 65 2e 27 0a 09 09 09 09 72 65 6c 6f 61  ible.'.....reloa
49e0: 64 20 3d 20 54 72 75 65 0a 0a 09 09 09 23 20 66  d = True.....# f
49f0: 6f 72 63 69 62 6c 79 20 63 68 65 63 6b 69 6e 67  orcibly checking
4a00: 20 66 69 6c 65 20 69 66 20 66 69 6c 65 20 73 69   file if file si
4a10: 7a 65 20 64 6f 65 73 6e 27 74 20 6d 61 74 63 68  ze doesn't match
4a20: 20 77 69 74 68 20 69 6e 64 65 78 20 64 61 74 61   with index data
4a30: 0a 09 09 09 65 6c 69 66 20 6e 6f 74 20 72 65 6c  ....elif not rel
4a40: 6f 61 64 20 61 6e 64 20 27 43 6f 6e 74 65 6e 74  oad and 'Content
4a50: 2d 4c 65 6e 67 74 68 27 20 69 6e 20 69 6e 64 65  -Length' in inde
4a60: 78 5b 75 72 6c 5d 20 61 6e 64 20 6f 73 2e 73 74  x[url] and os.st
4a70: 61 74 28 66 69 6c 65 5f 6e 61 6d 65 29 2e 73 74  at(file_name).st
4a80: 5f 73 69 7a 65 20 21 3d 20 69 6e 74 28 69 6e 64  _size != int(ind
4a90: 65 78 5b 75 72 6c 5d 5b 27 43 6f 6e 74 65 6e 74  ex[url]['Content
4aa0: 2d 4c 65 6e 67 74 68 27 5d 29 3a 0a 09 09 09 09  -Length']):.....
4ab0: 69 6e 66 6f 20 2b 3d 20 27 5c 6e 46 69 6c 65 20  info += '\nFile 
4ac0: 73 69 7a 65 20 69 73 20 27 20 2b 20 6f 73 2e 73  size is ' + os.s
4ad0: 74 61 74 28 66 69 6c 65 5f 6e 61 6d 65 29 2e 73  tat(file_name).s
4ae0: 74 5f 73 69 7a 65 20 2b 20 27 20 61 6e 64 20 73  t_size + ' and s
4af0: 74 6f 72 65 64 20 66 69 6c 65 20 73 69 7a 65 20  tored file size 
4b00: 69 73 20 27 20 2b 20 69 6e 64 65 78 5b 75 72 6c  is ' + index[url
4b10: 5d 5b 27 43 6f 6e 74 65 6e 74 2d 4c 65 6e 67 74  ]['Content-Lengt
4b20: 68 27 5d 20 2b 20 27 2e 27 0a 09 09 09 09 72 65  h'] + '.'.....re
4b30: 6c 6f 61 64 20 3d 20 54 72 75 65 0a 0a 09 09 09  load = True.....
4b40: 23 20 66 6f 72 63 69 62 6c 79 20 63 68 65 63 6b  # forcibly check
4b50: 69 6e 67 20 66 69 6c 65 20 69 66 20 69 6e 64 65  ing file if inde
4b60: 78 20 68 6f 64 73 20 50 72 61 67 6d 61 20 68 65  x hods Pragma he
4b70: 61 64 65 72 0a 09 09 09 69 66 20 6e 6f 74 20 72  ader....if not r
4b80: 65 6c 6f 61 64 20 61 6e 64 20 27 50 72 61 67 6d  eload and 'Pragm
4b90: 61 27 20 69 6e 20 69 6e 64 65 78 5b 75 72 6c 5d  a' in index[url]
4ba0: 20 61 6e 64 20 69 6e 64 65 78 5b 75 72 6c 5d 5b   and index[url][
4bb0: 27 50 72 61 67 6d 61 27 5d 20 3d 3d 20 27 6e 6f  'Pragma'] == 'no
4bc0: 2d 63 61 63 68 65 27 3a 0a 09 09 09 09 69 6e 66  -cache':.....inf
4bd0: 6f 20 2b 3d 27 5c 6e 50 72 61 67 6d 61 20 6f 6e  o +='\nPragma on
4be0: 3a 20 72 65 63 68 65 63 6b 20 69 6d 6d 69 6e 65  : recheck immine
4bf0: 6e 74 2e 27 0a 09 09 09 09 72 65 63 68 65 63 6b  nt.'.....recheck
4c00: 20 3d 20 54 72 75 65 0a 0a 09 09 09 23 20 73 6b   = True.....# sk
4c10: 69 70 70 69 6e 67 20 66 69 6c 65 20 70 72 6f 63  ipping file proc
4c20: 65 73 73 69 6e 67 20 69 66 20 74 68 65 72 65 27  essing if there'
4c30: 73 20 6e 6f 20 6e 65 65 64 20 74 6f 20 72 65 63  s no need to rec
4c40: 68 65 63 6b 20 69 74 20 61 6e 64 20 77 65 20 68  heck it and we h
4c50: 61 76 65 20 63 68 65 63 6b 65 64 20 69 74 20 61  ave checked it a
4c60: 74 20 6c 65 61 73 74 20 34 20 68 6f 75 72 73 20  t least 4 hours 
4c70: 61 67 6f 0a 09 09 09 69 66 20 6e 6f 74 20 72 65  ago....if not re
4c80: 63 68 65 63 6b 20 61 6e 64 20 6e 6f 74 20 72 65  check and not re
4c90: 6c 6f 61 64 20 61 6e 64 20 28 6f 70 74 69 6f 6e  load and (option
4ca0: 73 2e 6e 6f 75 70 64 61 74 65 20 6f 72 20 28 27  s.noupdate or ('
4cb0: 5f 74 69 6d 65 27 20 69 6e 20 69 6e 64 65 78 5b  _time' in index[
4cc0: 75 72 6c 5d 20 61 6e 64 20 28 64 61 74 65 74 69  url] and (dateti
4cd0: 6d 65 2e 64 61 74 65 74 69 6d 65 2e 6e 6f 77 28  me.datetime.now(
4ce0: 29 20 2d 20 64 61 74 65 74 69 6d 65 2e 74 69 6d  ) - datetime.tim
4cf0: 65 64 65 6c 74 61 28 68 6f 75 72 73 20 3d 20 34  edelta(hours = 4
4d00: 29 20 2d 20 69 6e 64 65 78 5b 75 72 6c 5d 5b 27  ) - index[url]['
4d10: 5f 74 69 6d 65 27 5d 29 2e 64 61 79 73 20 3c 20  _time']).days < 
4d20: 30 29 29 3a 0a 09 09 09 09 69 66 20 6f 70 74 69  0)):.....if opti
4d30: 6f 6e 73 2e 76 65 72 62 6f 73 65 3a 0a 09 09 09  ons.verbose:....
4d40: 09 09 70 72 69 6e 74 28 69 6e 66 6f 29 0a 09 09  ..print(info)...
4d50: 09 09 63 6f 6e 74 69 6e 75 65 0a 09 09 09 65 6c  ..continue....el
4d60: 73 65 3a 0a 09 09 09 09 70 72 69 6e 74 28 69 6e  se:.....print(in
4d70: 66 6f 29 0a 0a 09 09 09 74 72 79 3a 0a 09 09 09  fo).....try:....
4d80: 09 77 69 74 68 20 75 72 6c 6c 69 62 2e 72 65 71  .with urllib.req
4d90: 75 65 73 74 2e 75 72 6c 6f 70 65 6e 28 6f 70 74  uest.urlopen(opt
4da0: 69 6f 6e 73 2e 72 6f 6f 74 20 2b 20 75 72 6c 29  ions.root + url)
4db0: 20 61 73 20 73 6f 75 72 63 65 3a 0a 09 09 09 09   as source:.....
4dc0: 09 6e 65 77 5f 68 65 61 64 65 72 73 20 3d 20 7b  .new_headers = {
4dd0: 7d 0a 09 09 09 09 09 68 65 61 64 65 72 73 20 3d  }......headers =
4de0: 20 73 6f 75 72 63 65 2e 69 6e 66 6f 28 29 0a 0a   source.info()..
4df0: 09 09 09 09 09 23 20 73 74 72 69 70 70 69 6e 67  .....# stripping
4e00: 20 75 6e 6e 65 65 64 65 64 20 68 65 61 64 65 72   unneeded header
4e10: 73 20 28 58 58 58 20 6d 61 6b 65 20 74 68 69 73  s (XXX make this
4e20: 20 69 6e 70 6c 61 63 65 3f 29 0a 09 09 09 09 09   inplace?)......
4e30: 66 6f 72 20 68 65 61 64 65 72 20 69 6e 20 68 65  for header in he
4e40: 61 64 65 72 73 3a 0a 09 09 09 09 09 09 69 66 20  aders:.......if 
4e50: 68 65 61 64 65 72 20 69 6e 20 64 65 73 63 5f 66  header in desc_f
4e60: 69 65 6c 64 73 3a 0a 09 09 09 09 09 09 09 69 66  ields:........if
4e70: 20 68 65 61 64 65 72 20 3d 3d 20 27 50 72 61 67   header == 'Prag
4e80: 6d 61 27 20 61 6e 64 20 68 65 61 64 65 72 73 5b  ma' and headers[
4e90: 68 65 61 64 65 72 5d 20 21 3d 20 27 6e 6f 2d 63  header] != 'no-c
4ea0: 61 63 68 65 27 3a 0a 09 09 09 09 09 09 09 09 70  ache':.........p
4eb0: 72 69 6e 74 28 27 50 72 61 67 6d 61 3a 27 2c 20  rint('Pragma:', 
4ec0: 68 65 61 64 65 72 73 5b 68 65 61 64 65 72 5d 29  headers[header])
4ed0: 0a 09 09 09 09 09 09 09 6e 65 77 5f 68 65 61 64  ........new_head
4ee0: 65 72 73 5b 68 65 61 64 65 72 5d 20 3d 20 68 65  ers[header] = he
4ef0: 61 64 65 72 73 5b 68 65 61 64 65 72 5d 0a 09 09  aders[header]...
4f00: 09 09 09 09 65 6c 69 66 20 6e 6f 74 20 68 65 61  ....elif not hea
4f10: 64 65 72 20 69 6e 20 69 67 6e 6f 72 65 5f 66 69  der in ignore_fi
4f20: 65 6c 64 73 3a 0a 09 09 09 09 09 09 09 70 72 69  elds:........pri
4f30: 6e 74 28 27 55 6e 64 65 66 69 6e 65 64 20 68 65  nt('Undefined he
4f40: 61 64 65 72 20 22 27 2c 20 68 65 61 64 65 72 2c  ader "', header,
4f50: 20 27 22 3a 20 27 2c 20 68 65 61 64 65 72 73 5b   '": ', headers[
4f60: 68 65 61 64 65 72 5d 2c 20 73 65 70 3d 27 27 29  header], sep='')
4f70: 0a 0a 09 09 09 09 09 23 20 63 6f 6d 70 61 72 69  .......# compari
4f80: 6e 67 20 68 65 61 64 65 72 73 20 77 69 74 68 20  ng headers with 
4f90: 64 61 74 61 20 66 6f 75 6e 64 20 69 6e 20 69 6e  data found in in
4fa0: 64 65 78 0a 09 09 09 09 09 23 20 69 66 20 61 6e  dex......# if an
4fb0: 79 20 68 65 61 64 65 72 20 68 61 73 20 63 68 61  y header has cha
4fc0: 6e 67 65 64 20 28 65 78 63 65 70 74 20 50 72 61  nged (except Pra
4fd0: 67 6d 61 29 20 66 69 6c 65 20 69 73 20 66 75 6c  gma) file is ful
4fe0: 6c 79 20 64 6f 77 6e 6c 6f 61 64 65 64 0a 09 09  ly downloaded...
4ff0: 09 09 09 23 20 73 61 6d 65 20 69 66 20 77 65 20  ...# same if we 
5000: 67 65 74 20 6d 6f 72 65 20 6f 72 20 6c 65 73 73  get more or less
5010: 20 68 65 61 64 65 72 73 0a 09 09 09 09 09 6f 6c   headers......ol
5020: 64 5f 6b 65 79 73 20 3d 20 73 65 74 28 69 6e 64  d_keys = set(ind
5030: 65 78 5b 75 72 6c 5d 2e 6b 65 79 73 28 29 29 0a  ex[url].keys()).
5040: 09 09 09 09 09 6f 6c 64 5f 6b 65 79 73 2e 64 69  .....old_keys.di
5050: 73 63 61 72 64 28 27 5f 74 69 6d 65 27 29 0a 09  scard('_time')..
5060: 09 09 09 09 6f 6c 64 5f 6b 65 79 73 2e 64 69 73  ....old_keys.dis
5070: 63 61 72 64 28 27 50 72 61 67 6d 61 27 29 0a 09  card('Pragma')..
5080: 09 09 09 09 6d 6f 72 65 5f 6b 65 79 73 20 3d 20  ....more_keys = 
5090: 73 65 74 28 6e 65 77 5f 68 65 61 64 65 72 73 2e  set(new_headers.
50a0: 6b 65 79 73 28 29 29 20 2d 20 6f 6c 64 5f 6b 65  keys()) - old_ke
50b0: 79 73 0a 09 09 09 09 09 6d 6f 72 65 5f 6b 65 79  ys......more_key
50c0: 73 2e 64 69 73 63 61 72 64 28 27 50 72 61 67 6d  s.discard('Pragm
50d0: 61 27 29 0a 09 09 09 09 09 6c 65 73 73 5f 6b 65  a')......less_ke
50e0: 79 73 20 3d 20 6f 6c 64 5f 6b 65 79 73 20 2d 20  ys = old_keys - 
50f0: 73 65 74 28 6e 65 77 5f 68 65 61 64 65 72 73 2e  set(new_headers.
5100: 6b 65 79 73 28 29 29 0a 09 09 09 09 09 69 66 20  keys())......if 
5110: 6c 65 6e 28 6d 6f 72 65 5f 6b 65 79 73 29 20 3e  len(more_keys) >
5120: 20 30 3a 0a 09 09 09 09 09 09 69 66 20 6e 6f 74   0:.......if not
5130: 20 6c 65 6e 28 6f 6c 64 5f 6b 65 79 73 29 20 3d   len(old_keys) =
5140: 3d 20 30 3a 0a 09 09 09 09 09 09 09 70 72 69 6e  = 0:........prin
5150: 74 28 27 4d 6f 72 65 20 68 65 61 64 65 72 73 20  t('More headers 
5160: 61 70 70 65 61 72 3a 27 2c 20 6d 6f 72 65 5f 6b  appear:', more_k
5170: 65 79 73 29 0a 09 09 09 09 09 09 72 65 6c 6f 61  eys).......reloa
5180: 64 20 3d 20 54 72 75 65 0a 09 09 09 09 09 65 6c  d = True......el
5190: 69 66 20 6c 65 6e 28 6c 65 73 73 5f 6b 65 79 73  if len(less_keys
51a0: 29 20 3e 20 30 3a 0a 09 09 09 09 09 09 70 72 69  ) > 0:.......pri
51b0: 6e 74 28 27 4c 65 73 73 20 68 65 61 64 65 72 73  nt('Less headers
51c0: 20 61 70 70 65 61 72 3a 27 2c 20 6c 65 73 73 5f   appear:', less_
51d0: 6b 65 79 73 29 0a 09 09 09 09 09 65 6c 73 65 3a  keys)......else:
51e0: 0a 09 09 09 09 09 09 66 6f 72 20 6b 65 79 20 69  .......for key i
51f0: 6e 20 69 6e 64 65 78 5b 75 72 6c 5d 2e 6b 65 79  n index[url].key
5200: 73 28 29 3a 0a 09 09 09 09 09 09 09 69 66 20 6b  s():........if k
5210: 65 79 5b 30 5d 20 21 3d 20 27 5f 27 20 61 6e 64  ey[0] != '_' and
5220: 20 6b 65 79 20 21 3d 20 27 50 72 61 67 6d 61 27   key != 'Pragma'
5230: 20 61 6e 64 20 6e 6f 74 20 69 6e 64 65 78 5b 75   and not index[u
5240: 72 6c 5d 5b 6b 65 79 5d 20 3d 3d 20 6e 65 77 5f  rl][key] == new_
5250: 68 65 61 64 65 72 73 5b 6b 65 79 5d 3a 0a 09 09  headers[key]:...
5260: 09 09 09 09 09 09 70 72 69 6e 74 28 27 48 65 61  ......print('Hea
5270: 64 65 72 20 22 27 2c 20 6b 65 79 2c 20 27 22 20  der "', key, '" 
5280: 63 68 61 6e 67 65 64 20 66 72 6f 6d 20 5b 27 2c  changed from [',
5290: 20 69 6e 64 65 78 5b 75 72 6c 5d 5b 6b 65 79 5d   index[url][key]
52a0: 2c 20 27 5d 20 74 6f 20 5b 27 2c 20 6e 65 77 5f  , '] to [', new_
52b0: 68 65 61 64 65 72 73 5b 6b 65 79 5d 2c 20 27 5d  headers[key], ']
52c0: 27 2c 20 73 65 70 3d 27 27 29 0a 09 09 09 09 09  ', sep='')......
52d0: 09 09 09 72 65 6c 6f 61 64 20 3d 20 54 72 75 65  ...reload = True
52e0: 0a 0a 09 09 09 09 09 23 20 64 6f 77 6e 6c 6f 61  .......# downloa
52f0: 64 69 6e 67 20 66 69 6c 65 0a 09 09 09 09 09 69  ding file......i
5300: 66 20 72 65 6c 6f 61 64 3a 0a 09 09 09 09 09 09  f reload:.......
5310: 69 66 20 27 43 6f 6e 74 65 6e 74 2d 4c 65 6e 67  if 'Content-Leng
5320: 74 68 27 20 69 6e 20 68 65 61 64 65 72 73 3a 0a  th' in headers:.
5330: 09 09 09 09 09 09 09 70 72 69 6e 74 28 27 44 6f  .......print('Do
5340: 77 6e 6c 6f 61 64 69 6e 67 27 2c 20 68 65 61 64  wnloading', head
5350: 65 72 73 5b 27 43 6f 6e 74 65 6e 74 2d 4c 65 6e  ers['Content-Len
5360: 67 74 68 27 5d 2c 20 27 62 79 74 65 73 20 5b 27  gth'], 'bytes ['
5370: 2c 20 65 6e 64 3d 27 27 29 0a 09 09 09 09 09 09  , end='').......
5380: 65 6c 73 65 3a 0a 09 09 09 09 09 09 09 70 72 69  else:........pri
5390: 6e 74 28 27 44 6f 77 6e 6c 6f 61 64 69 6e 67 20  nt('Downloading 
53a0: 5b 27 2c 20 65 6e 64 3d 27 27 29 0a 09 09 09 09  [', end='').....
53b0: 09 09 73 79 73 2e 73 74 64 6f 75 74 2e 66 6c 75  ..sys.stdout.flu
53c0: 73 68 28 29 0a 0a 09 09 09 09 09 09 23 20 66 69  sh()........# fi
53d0: 6c 65 20 69 73 20 63 72 65 61 74 65 64 20 61 74  le is created at
53e0: 20 74 65 6d 70 6f 72 61 72 79 20 6c 6f 63 61 74   temporary locat
53f0: 69 6f 6e 20 61 6e 64 20 6d 6f 76 65 64 20 69 6e  ion and moved in
5400: 20 70 6c 61 63 65 20 6f 6e 6c 79 20 77 68 65 6e   place only when
5410: 20 64 6f 77 6e 6c 6f 61 64 20 63 6f 6d 70 6c 65   download comple
5420: 74 65 73 0a 09 09 09 09 09 09 74 65 6d 70 5f 66  tes.......temp_f
5430: 69 6c 65 20 3d 20 6f 70 65 6e 28 6f 70 74 69 6f  ile = open(optio
5440: 6e 73 2e 64 69 72 20 2b 20 6f 73 2e 73 65 70 20  ns.dir + os.sep 
5450: 2b 20 27 2e 74 6d 70 27 2c 20 27 77 62 27 29 0a  + '.tmp', 'wb').
5460: 09 09 09 09 09 09 62 75 66 66 65 72 20 3d 20 73  ......buffer = s
5470: 6f 75 72 63 65 2e 72 65 61 64 28 62 6c 6f 63 6b  ource.read(block
5480: 5f 73 69 7a 65 29 0a 09 09 09 09 09 09 6d 65 67  _size).......meg
5490: 61 62 6c 6f 63 6b 73 20 3d 20 30 0a 09 09 09 09  ablocks = 0.....
54a0: 09 09 62 6c 6f 63 6b 73 20 3d 20 30 0a 09 09 09  ..blocks = 0....
54b0: 09 09 09 6d 65 67 73 20 3d 20 30 0a 09 09 09 09  ...megs = 0.....
54c0: 09 09 77 68 69 6c 65 20 6c 65 6e 28 62 75 66 66  ..while len(buff
54d0: 65 72 29 20 3e 20 30 3a 0a 09 09 09 09 09 09 09  er) > 0:........
54e0: 74 65 6d 70 5f 66 69 6c 65 2e 77 72 69 74 65 28  temp_file.write(
54f0: 62 75 66 66 65 72 29 0a 09 09 09 09 09 09 09 62  buffer)........b
5500: 75 66 66 65 72 20 3d 20 73 6f 75 72 63 65 2e 72  uffer = source.r
5510: 65 61 64 28 62 6c 6f 63 6b 5f 73 69 7a 65 29 0a  ead(block_size).
5520: 09 09 09 09 09 09 09 62 6c 6f 63 6b 73 20 2b 3d  .......blocks +=
5530: 20 31 0a 09 09 09 09 09 09 09 69 66 20 62 6c 6f   1........if blo
5540: 63 6b 73 20 3e 20 31 30 32 34 30 30 2f 62 6c 6f  cks > 102400/blo
5550: 63 6b 5f 73 69 7a 65 3a 0a 09 09 09 09 09 09 09  ck_size:........
5560: 09 6d 65 67 61 62 6c 6f 63 6b 73 20 2b 3d 20 31  .megablocks += 1
5570: 0a 09 09 09 09 09 09 09 09 69 66 20 6d 65 67 61  .........if mega
5580: 62 6c 6f 63 6b 73 20 3e 20 31 30 3a 0a 09 09 09  blocks > 10:....
5590: 09 09 09 09 09 09 6d 65 67 61 62 6c 6f 63 6b 73  ......megablocks
55a0: 20 3d 20 6d 65 67 61 62 6c 6f 63 6b 73 20 2d 20   = megablocks - 
55b0: 31 30 0a 09 09 09 09 09 09 09 09 09 6d 65 67 73  10..........megs
55c0: 20 2b 3d 20 31 0a 09 09 09 09 09 09 09 09 09 70   += 1..........p
55d0: 72 69 6e 74 28 27 7b 7d 4d 62 27 2e 66 6f 72 6d  rint('{}Mb'.form
55e0: 61 74 28 6d 65 67 73 29 2c 20 65 6e 64 3d 27 27  at(megs), end=''
55f0: 29 0a 09 09 09 09 09 09 09 09 65 6c 73 65 3a 0a  ).........else:.
5600: 09 09 09 09 09 09 09 09 09 70 72 69 6e 74 28 27  .........print('
5610: 2e 27 2c 20 65 6e 64 3d 27 27 29 0a 09 09 09 09  .', end='').....
5620: 09 09 09 09 62 6c 6f 63 6b 73 20 3d 20 62 6c 6f  ....blocks = blo
5630: 63 6b 73 20 2d 20 31 30 32 34 30 30 2f 62 6c 6f  cks - 102400/blo
5640: 63 6b 5f 73 69 7a 65 0a 09 09 09 09 09 09 09 73  ck_size........s
5650: 79 73 2e 73 74 64 6f 75 74 2e 66 6c 75 73 68 28  ys.stdout.flush(
5660: 29 0a 09 09 09 09 09 09 74 65 6d 70 5f 66 69 6c  ).......temp_fil
5670: 65 2e 63 6c 6f 73 65 28 29 0a 09 09 09 09 09 09  e.close().......
5680: 70 72 69 6e 74 28 27 5d 27 29 0a 09 09 09 09 09  print(']')......
5690: 09 6f 73 2e 72 65 6e 61 6d 65 73 28 6f 70 74 69  .os.renames(opti
56a0: 6f 6e 73 2e 64 69 72 20 2b 20 6f 73 2e 73 65 70  ons.dir + os.sep
56b0: 20 2b 20 27 2e 74 6d 70 27 2c 20 66 69 6c 65 5f   + '.tmp', file_
56c0: 6e 61 6d 65 29 0a 0a 09 09 09 09 09 09 63 68 65  name)........che
56d0: 63 6b 65 64 5f 66 69 6c 65 73 20 2b 3d 20 31 0a  cked_files += 1.
56e0: 0a 09 09 09 09 09 23 20 73 74 6f 72 69 6e 67 20  ......# storing 
56f0: 6e 65 77 20 74 69 6d 65 20 6d 61 72 6b 20 61 6e  new time mark an
5700: 64 20 73 74 6f 72 69 6e 67 20 6e 65 77 20 68 65  d storing new he
5710: 61 64 65 72 73 0a 09 09 09 09 09 6e 65 77 5f 68  aders......new_h
5720: 65 61 64 65 72 73 5b 27 5f 74 69 6d 65 27 5d 20  eaders['_time'] 
5730: 3d 20 64 61 74 65 74 69 6d 65 2e 64 61 74 65 74  = datetime.datet
5740: 69 6d 65 2e 6e 6f 77 28 29 0a 09 09 09 09 09 69  ime.now()......i
5750: 6e 64 65 78 5b 75 72 6c 5d 20 3d 20 6e 65 77 5f  ndex[url] = new_
5760: 68 65 61 64 65 72 73 0a 09 09 09 09 09 69 6e 64  headers......ind
5770: 65 78 2e 73 79 6e 63 28 29 0a 0a 09 09 09 65 78  ex.sync().....ex
5780: 63 65 70 74 20 75 72 6c 6c 69 62 2e 65 72 72 6f  cept urllib.erro
5790: 72 2e 48 54 54 50 45 72 72 6f 72 20 61 73 20 65  r.HTTPError as e
57a0: 72 72 6f 72 3a 0a 09 09 09 09 23 20 69 6e 20 63  rror:.....# in c
57b0: 61 73 65 20 6f 66 20 65 72 72 6f 72 20 77 65 20  ase of error we 
57c0: 64 6f 6e 27 74 20 6e 65 65 64 20 74 6f 20 64 6f  don't need to do
57d0: 20 61 6e 79 74 68 69 6e 67 20 61 63 74 75 61 6c   anything actual
57e0: 6c 79 2c 0a 09 09 09 09 23 20 69 66 20 66 69 6c  ly,.....# if fil
57f0: 65 20 64 6f 77 6e 6c 6f 61 64 20 73 74 61 6c 6c  e download stall
5800: 73 20 6f 72 20 66 61 69 6c 73 20 74 68 65 20 66  s or fails the f
5810: 69 6c 65 20 77 6f 75 6c 64 20 6e 6f 74 20 62 65  ile would not be
5820: 20 6d 6f 76 65 64 20 74 6f 20 69 74 27 73 20 6c   moved to it's l
5830: 6f 63 61 74 69 6f 6e 0a 09 09 09 09 70 72 69 6e  ocation.....prin
5840: 74 28 65 72 72 6f 72 29 0a 0a 09 09 69 66 20 6f  t(error)....if o
5850: 70 74 69 6f 6e 73 2e 76 65 72 62 6f 73 65 3a 0a  ptions.verbose:.
5860: 09 09 09 70 72 69 6e 74 28 27 5b 27 2c 20 6c 65  ...print('[', le
5870: 6e 28 75 6e 63 68 65 63 6b 65 64 5f 66 69 6c 65  n(unchecked_file
5880: 73 29 2c 20 27 2f 27 2c 20 63 68 65 63 6b 65 64  s), '/', checked
5890: 5f 66 69 6c 65 73 2c 20 27 5d 27 29 0a 0a 09 09  _files, ']')....
58a0: 23 20 63 68 65 63 6b 69 6e 67 20 69 66 20 74 68  # checking if th
58b0: 65 72 65 20 77 65 72 65 20 61 6e 79 20 66 69 6c  ere were any fil
58c0: 65 73 20 64 6f 77 6e 6c 6f 61 64 65 64 2c 20 69  es downloaded, i
58d0: 66 20 79 65 73 20 2d 20 72 65 73 74 61 72 74 69  f yes - restarti
58e0: 6e 67 20 73 65 71 75 65 6e 63 65 0a 09 09 69 66  ng sequence...if
58f0: 20 63 68 65 63 6b 65 64 5f 66 69 6c 65 73 20 3d   checked_files =
5900: 3d 20 30 3a 0a 09 09 09 62 72 65 61 6b 0a        = 0:....break.