swift 2.23.2__py3-none-any.whl → 2.35.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (208) hide show
  1. swift/__init__.py +29 -50
  2. swift/account/auditor.py +21 -118
  3. swift/account/backend.py +33 -28
  4. swift/account/reaper.py +37 -28
  5. swift/account/replicator.py +22 -0
  6. swift/account/server.py +60 -26
  7. swift/account/utils.py +28 -11
  8. swift-2.23.2.data/scripts/swift-account-audit → swift/cli/account_audit.py +23 -13
  9. swift-2.23.2.data/scripts/swift-config → swift/cli/config.py +2 -2
  10. swift/cli/container_deleter.py +5 -11
  11. swift-2.23.2.data/scripts/swift-dispersion-populate → swift/cli/dispersion_populate.py +8 -7
  12. swift/cli/dispersion_report.py +10 -9
  13. swift-2.23.2.data/scripts/swift-drive-audit → swift/cli/drive_audit.py +63 -21
  14. swift/cli/form_signature.py +3 -7
  15. swift-2.23.2.data/scripts/swift-get-nodes → swift/cli/get_nodes.py +8 -2
  16. swift/cli/info.py +183 -29
  17. swift/cli/manage_shard_ranges.py +708 -37
  18. swift-2.23.2.data/scripts/swift-oldies → swift/cli/oldies.py +25 -14
  19. swift-2.23.2.data/scripts/swift-orphans → swift/cli/orphans.py +7 -3
  20. swift/cli/recon.py +196 -67
  21. swift-2.23.2.data/scripts/swift-recon-cron → swift/cli/recon_cron.py +17 -20
  22. swift-2.23.2.data/scripts/swift-reconciler-enqueue → swift/cli/reconciler_enqueue.py +2 -3
  23. swift/cli/relinker.py +807 -126
  24. swift/cli/reload.py +135 -0
  25. swift/cli/ringbuilder.py +217 -20
  26. swift/cli/ringcomposer.py +0 -1
  27. swift/cli/shard-info.py +4 -3
  28. swift/common/base_storage_server.py +9 -20
  29. swift/common/bufferedhttp.py +48 -74
  30. swift/common/constraints.py +20 -15
  31. swift/common/container_sync_realms.py +9 -11
  32. swift/common/daemon.py +25 -8
  33. swift/common/db.py +198 -127
  34. swift/common/db_auditor.py +168 -0
  35. swift/common/db_replicator.py +95 -55
  36. swift/common/digest.py +141 -0
  37. swift/common/direct_client.py +144 -33
  38. swift/common/error_limiter.py +93 -0
  39. swift/common/exceptions.py +25 -1
  40. swift/common/header_key_dict.py +2 -9
  41. swift/common/http_protocol.py +373 -0
  42. swift/common/internal_client.py +129 -59
  43. swift/common/linkat.py +3 -4
  44. swift/common/manager.py +284 -67
  45. swift/common/memcached.py +396 -147
  46. swift/common/middleware/__init__.py +4 -0
  47. swift/common/middleware/account_quotas.py +211 -46
  48. swift/common/middleware/acl.py +3 -8
  49. swift/common/middleware/backend_ratelimit.py +230 -0
  50. swift/common/middleware/bulk.py +22 -34
  51. swift/common/middleware/catch_errors.py +1 -3
  52. swift/common/middleware/cname_lookup.py +6 -11
  53. swift/common/middleware/container_quotas.py +1 -1
  54. swift/common/middleware/container_sync.py +39 -17
  55. swift/common/middleware/copy.py +12 -0
  56. swift/common/middleware/crossdomain.py +22 -9
  57. swift/common/middleware/crypto/__init__.py +2 -1
  58. swift/common/middleware/crypto/crypto_utils.py +11 -15
  59. swift/common/middleware/crypto/decrypter.py +28 -11
  60. swift/common/middleware/crypto/encrypter.py +12 -17
  61. swift/common/middleware/crypto/keymaster.py +8 -15
  62. swift/common/middleware/crypto/kms_keymaster.py +2 -1
  63. swift/common/middleware/dlo.py +15 -11
  64. swift/common/middleware/domain_remap.py +5 -4
  65. swift/common/middleware/etag_quoter.py +128 -0
  66. swift/common/middleware/formpost.py +73 -70
  67. swift/common/middleware/gatekeeper.py +8 -1
  68. swift/common/middleware/keystoneauth.py +33 -3
  69. swift/common/middleware/list_endpoints.py +4 -4
  70. swift/common/middleware/listing_formats.py +85 -49
  71. swift/common/middleware/memcache.py +4 -81
  72. swift/common/middleware/name_check.py +3 -2
  73. swift/common/middleware/proxy_logging.py +160 -92
  74. swift/common/middleware/ratelimit.py +17 -10
  75. swift/common/middleware/read_only.py +6 -4
  76. swift/common/middleware/recon.py +59 -22
  77. swift/common/middleware/s3api/acl_handlers.py +25 -3
  78. swift/common/middleware/s3api/acl_utils.py +6 -1
  79. swift/common/middleware/s3api/controllers/__init__.py +6 -0
  80. swift/common/middleware/s3api/controllers/acl.py +3 -2
  81. swift/common/middleware/s3api/controllers/bucket.py +242 -137
  82. swift/common/middleware/s3api/controllers/logging.py +2 -2
  83. swift/common/middleware/s3api/controllers/multi_delete.py +43 -20
  84. swift/common/middleware/s3api/controllers/multi_upload.py +219 -133
  85. swift/common/middleware/s3api/controllers/obj.py +112 -8
  86. swift/common/middleware/s3api/controllers/object_lock.py +44 -0
  87. swift/common/middleware/s3api/controllers/s3_acl.py +2 -2
  88. swift/common/middleware/s3api/controllers/tagging.py +57 -0
  89. swift/common/middleware/s3api/controllers/versioning.py +36 -7
  90. swift/common/middleware/s3api/etree.py +22 -9
  91. swift/common/middleware/s3api/exception.py +0 -4
  92. swift/common/middleware/s3api/s3api.py +113 -41
  93. swift/common/middleware/s3api/s3request.py +384 -218
  94. swift/common/middleware/s3api/s3response.py +126 -23
  95. swift/common/middleware/s3api/s3token.py +16 -17
  96. swift/common/middleware/s3api/schema/delete.rng +1 -1
  97. swift/common/middleware/s3api/subresource.py +7 -10
  98. swift/common/middleware/s3api/utils.py +27 -10
  99. swift/common/middleware/slo.py +665 -358
  100. swift/common/middleware/staticweb.py +64 -37
  101. swift/common/middleware/symlink.py +52 -19
  102. swift/common/middleware/tempauth.py +76 -58
  103. swift/common/middleware/tempurl.py +192 -174
  104. swift/common/middleware/versioned_writes/__init__.py +51 -0
  105. swift/common/middleware/{versioned_writes.py → versioned_writes/legacy.py} +27 -26
  106. swift/common/middleware/versioned_writes/object_versioning.py +1482 -0
  107. swift/common/middleware/x_profile/exceptions.py +1 -4
  108. swift/common/middleware/x_profile/html_viewer.py +18 -19
  109. swift/common/middleware/x_profile/profile_model.py +1 -2
  110. swift/common/middleware/xprofile.py +10 -10
  111. swift-2.23.2.data/scripts/swift-container-server → swift/common/recon.py +13 -8
  112. swift/common/registry.py +147 -0
  113. swift/common/request_helpers.py +324 -57
  114. swift/common/ring/builder.py +67 -25
  115. swift/common/ring/composite_builder.py +1 -1
  116. swift/common/ring/ring.py +177 -51
  117. swift/common/ring/utils.py +1 -1
  118. swift/common/splice.py +10 -6
  119. swift/common/statsd_client.py +205 -0
  120. swift/common/storage_policy.py +49 -44
  121. swift/common/swob.py +86 -102
  122. swift/common/{utils.py → utils/__init__.py} +2191 -2762
  123. swift/common/utils/base.py +131 -0
  124. swift/common/utils/config.py +433 -0
  125. swift/common/utils/ipaddrs.py +256 -0
  126. swift/common/utils/libc.py +345 -0
  127. swift/common/utils/logs.py +859 -0
  128. swift/common/utils/timestamp.py +412 -0
  129. swift/common/wsgi.py +555 -536
  130. swift/container/auditor.py +14 -100
  131. swift/container/backend.py +552 -227
  132. swift/container/reconciler.py +126 -37
  133. swift/container/replicator.py +96 -22
  134. swift/container/server.py +397 -176
  135. swift/container/sharder.py +1580 -639
  136. swift/container/sync.py +94 -88
  137. swift/container/updater.py +53 -32
  138. swift/obj/auditor.py +153 -35
  139. swift/obj/diskfile.py +466 -217
  140. swift/obj/expirer.py +406 -124
  141. swift/obj/mem_diskfile.py +7 -4
  142. swift/obj/mem_server.py +1 -0
  143. swift/obj/reconstructor.py +523 -262
  144. swift/obj/replicator.py +249 -188
  145. swift/obj/server.py +213 -122
  146. swift/obj/ssync_receiver.py +145 -85
  147. swift/obj/ssync_sender.py +113 -54
  148. swift/obj/updater.py +653 -139
  149. swift/obj/watchers/__init__.py +0 -0
  150. swift/obj/watchers/dark_data.py +213 -0
  151. swift/proxy/controllers/account.py +11 -11
  152. swift/proxy/controllers/base.py +848 -604
  153. swift/proxy/controllers/container.py +452 -86
  154. swift/proxy/controllers/info.py +3 -2
  155. swift/proxy/controllers/obj.py +1009 -490
  156. swift/proxy/server.py +185 -112
  157. swift-2.35.0.dist-info/AUTHORS +501 -0
  158. swift-2.35.0.dist-info/LICENSE +202 -0
  159. {swift-2.23.2.dist-info → swift-2.35.0.dist-info}/METADATA +52 -61
  160. swift-2.35.0.dist-info/RECORD +201 -0
  161. {swift-2.23.2.dist-info → swift-2.35.0.dist-info}/WHEEL +1 -1
  162. {swift-2.23.2.dist-info → swift-2.35.0.dist-info}/entry_points.txt +43 -0
  163. swift-2.35.0.dist-info/pbr.json +1 -0
  164. swift/locale/de/LC_MESSAGES/swift.po +0 -1216
  165. swift/locale/en_GB/LC_MESSAGES/swift.po +0 -1207
  166. swift/locale/es/LC_MESSAGES/swift.po +0 -1085
  167. swift/locale/fr/LC_MESSAGES/swift.po +0 -909
  168. swift/locale/it/LC_MESSAGES/swift.po +0 -894
  169. swift/locale/ja/LC_MESSAGES/swift.po +0 -965
  170. swift/locale/ko_KR/LC_MESSAGES/swift.po +0 -964
  171. swift/locale/pt_BR/LC_MESSAGES/swift.po +0 -881
  172. swift/locale/ru/LC_MESSAGES/swift.po +0 -891
  173. swift/locale/tr_TR/LC_MESSAGES/swift.po +0 -832
  174. swift/locale/zh_CN/LC_MESSAGES/swift.po +0 -833
  175. swift/locale/zh_TW/LC_MESSAGES/swift.po +0 -838
  176. swift-2.23.2.data/scripts/swift-account-auditor +0 -23
  177. swift-2.23.2.data/scripts/swift-account-info +0 -51
  178. swift-2.23.2.data/scripts/swift-account-reaper +0 -23
  179. swift-2.23.2.data/scripts/swift-account-replicator +0 -34
  180. swift-2.23.2.data/scripts/swift-account-server +0 -23
  181. swift-2.23.2.data/scripts/swift-container-auditor +0 -23
  182. swift-2.23.2.data/scripts/swift-container-info +0 -51
  183. swift-2.23.2.data/scripts/swift-container-reconciler +0 -21
  184. swift-2.23.2.data/scripts/swift-container-replicator +0 -34
  185. swift-2.23.2.data/scripts/swift-container-sharder +0 -33
  186. swift-2.23.2.data/scripts/swift-container-sync +0 -23
  187. swift-2.23.2.data/scripts/swift-container-updater +0 -23
  188. swift-2.23.2.data/scripts/swift-dispersion-report +0 -24
  189. swift-2.23.2.data/scripts/swift-form-signature +0 -20
  190. swift-2.23.2.data/scripts/swift-init +0 -119
  191. swift-2.23.2.data/scripts/swift-object-auditor +0 -29
  192. swift-2.23.2.data/scripts/swift-object-expirer +0 -33
  193. swift-2.23.2.data/scripts/swift-object-info +0 -60
  194. swift-2.23.2.data/scripts/swift-object-reconstructor +0 -33
  195. swift-2.23.2.data/scripts/swift-object-relinker +0 -41
  196. swift-2.23.2.data/scripts/swift-object-replicator +0 -37
  197. swift-2.23.2.data/scripts/swift-object-server +0 -27
  198. swift-2.23.2.data/scripts/swift-object-updater +0 -23
  199. swift-2.23.2.data/scripts/swift-proxy-server +0 -23
  200. swift-2.23.2.data/scripts/swift-recon +0 -24
  201. swift-2.23.2.data/scripts/swift-ring-builder +0 -24
  202. swift-2.23.2.data/scripts/swift-ring-builder-analyzer +0 -22
  203. swift-2.23.2.data/scripts/swift-ring-composer +0 -22
  204. swift-2.23.2.dist-info/DESCRIPTION.rst +0 -166
  205. swift-2.23.2.dist-info/RECORD +0 -220
  206. swift-2.23.2.dist-info/metadata.json +0 -1
  207. swift-2.23.2.dist-info/pbr.json +0 -1
  208. {swift-2.23.2.dist-info → swift-2.35.0.dist-info}/top_level.txt +0 -0
@@ -17,8 +17,7 @@ import os
17
17
  import string
18
18
  import sys
19
19
  import textwrap
20
- import six
21
- from six.moves.configparser import ConfigParser
20
+ from configparser import ConfigParser
22
21
  from swift.common.utils import (
23
22
  config_true_value, quorum_size, whataremyips, list_from_csv,
24
23
  config_positive_int_value, get_zero_indexed_base_string, load_pkg_resource)
@@ -37,11 +36,11 @@ DEFAULT_EC_OBJECT_SEGMENT_SIZE = 1048576
37
36
 
38
37
 
39
38
  class BindPortsCache(object):
40
- def __init__(self, swift_dir, bind_ip):
39
+ def __init__(self, swift_dir, ring_ip):
41
40
  self.swift_dir = swift_dir
42
41
  self.mtimes_by_ring_path = {}
43
42
  self.portsets_by_ring_path = {}
44
- self.my_ips = set(whataremyips(bind_ip))
43
+ self.my_ips = set(whataremyips(ring_ip))
45
44
 
46
45
  def all_bind_ports_for_node(self):
47
46
  """
@@ -80,8 +79,10 @@ class BindPortsCache(object):
80
79
  # the first one we notice.
81
80
 
82
81
  # Return the requested set of ports from our (now-freshened) cache
83
- return six.moves.reduce(set.union,
84
- self.portsets_by_ring_path.values(), set())
82
+ res = set()
83
+ for ports in self.portsets_by_ring_path.values():
84
+ res.update(ports)
85
+ return res
85
86
 
86
87
 
87
88
  class PolicyError(ValueError):
@@ -160,7 +161,7 @@ class BaseStoragePolicy(object):
160
161
  object_ring=None, aliases='',
161
162
  diskfile_module='egg:swift#replication.fs'):
162
163
  # do not allow BaseStoragePolicy class to be instantiated directly
163
- if type(self) == BaseStoragePolicy:
164
+ if type(self) is BaseStoragePolicy:
164
165
  raise TypeError("Can't instantiate BaseStoragePolicy directly")
165
166
  # policy parameter validation
166
167
  try:
@@ -366,15 +367,26 @@ class BaseStoragePolicy(object):
366
367
  self._validate_policy_name(name)
367
368
  self.alias_list.insert(0, name)
368
369
 
369
- def load_ring(self, swift_dir):
370
+ def validate_ring_data(self, ring_data):
371
+ """
372
+ Validation hook used when loading the ring; currently only used for EC
373
+ """
374
+
375
+ def load_ring(self, swift_dir, reload_time=None):
370
376
  """
371
377
  Load the ring for this policy immediately.
372
378
 
373
379
  :param swift_dir: path to rings
380
+ :param reload_time: time interval in seconds to check for a ring change
374
381
  """
375
382
  if self.object_ring:
383
+ if reload_time is not None:
384
+ self.object_ring.reload_time = reload_time
376
385
  return
377
- self.object_ring = Ring(swift_dir, ring_name=self.ring_name)
386
+
387
+ self.object_ring = Ring(
388
+ swift_dir, ring_name=self.ring_name,
389
+ validation_hook=self.validate_ring_data, reload_time=reload_time)
378
390
 
379
391
  @property
380
392
  def quorum(self):
@@ -403,7 +415,7 @@ class BaseStoragePolicy(object):
403
415
  (self.diskfile_module, self.name, err))
404
416
  try:
405
417
  dfm_cls.check_policy(self)
406
- except ValueError as err:
418
+ except ValueError:
407
419
  raise PolicyError(
408
420
  'Invalid diskfile_module %s for policy %s:%s (%s)' %
409
421
  (self.diskfile_module, int(self), self.name, self.policy_type))
@@ -643,38 +655,25 @@ class ECStoragePolicy(BaseStoragePolicy):
643
655
  """
644
656
  return self._ec_quorum_size * self.ec_duplication_factor
645
657
 
646
- def load_ring(self, swift_dir):
658
+ def validate_ring_data(self, ring_data):
647
659
  """
648
- Load the ring for this policy immediately.
660
+ EC specific validation
649
661
 
650
- :param swift_dir: path to rings
662
+ Replica count check - we need _at_least_ (#data + #parity) replicas
663
+ configured. Also if the replica count is larger than exactly that
664
+ number there's a non-zero risk of error for code that is
665
+ considering the number of nodes in the primary list from the ring.
651
666
  """
652
- if self.object_ring:
653
- return
654
667
 
655
- def validate_ring_data(ring_data):
656
- """
657
- EC specific validation
658
-
659
- Replica count check - we need _at_least_ (#data + #parity) replicas
660
- configured. Also if the replica count is larger than exactly that
661
- number there's a non-zero risk of error for code that is
662
- considering the number of nodes in the primary list from the ring.
663
- """
664
-
665
- configured_fragment_count = ring_data.replica_count
666
- required_fragment_count = \
667
- (self.ec_n_unique_fragments) * self.ec_duplication_factor
668
- if configured_fragment_count != required_fragment_count:
669
- raise RingLoadError(
670
- 'EC ring for policy %s needs to be configured with '
671
- 'exactly %d replicas. Got %s.' % (
672
- self.name, required_fragment_count,
673
- configured_fragment_count))
674
-
675
- self.object_ring = Ring(
676
- swift_dir, ring_name=self.ring_name,
677
- validation_hook=validate_ring_data)
668
+ configured_fragment_count = ring_data.replica_count
669
+ required_fragment_count = \
670
+ (self.ec_n_unique_fragments) * self.ec_duplication_factor
671
+ if configured_fragment_count != required_fragment_count:
672
+ raise RingLoadError(
673
+ 'EC ring for policy %s needs to be configured with '
674
+ 'exactly %d replicas. Got %s.' % (
675
+ self.name, required_fragment_count,
676
+ configured_fragment_count))
678
677
 
679
678
  def get_backend_index(self, node_index):
680
679
  """
@@ -819,6 +818,15 @@ class StoragePolicyCollection(object):
819
818
  return None
820
819
  return self.by_index.get(index)
821
820
 
821
+ def get_by_name_or_index(self, name_or_index):
822
+ by_name = self.get_by_name(name_or_index)
823
+ by_index = self.get_by_index(name_or_index)
824
+ if by_name and by_index and by_name != by_index:
825
+ raise PolicyError(
826
+ "Found different polices when searching by "
827
+ "name (%s) and by index (%s)" % (by_name, by_index))
828
+ return by_name or by_index
829
+
822
830
  @property
823
831
  def legacy(self):
824
832
  return self.get_by_index(None)
@@ -968,12 +976,9 @@ def reload_storage_policies():
968
976
  Reload POLICIES from ``swift.conf``.
969
977
  """
970
978
  global _POLICIES
971
- if six.PY2:
972
- policy_conf = ConfigParser()
973
- else:
974
- # Python 3.2 disallows section or option duplicates by default
975
- # strict=False allows us to preserve the older behavior
976
- policy_conf = ConfigParser(strict=False)
979
+ # Python disallows section or option duplicates by default
980
+ # strict=False allows them, which Swift has always done
981
+ policy_conf = ConfigParser(strict=False)
977
982
  policy_conf.read(utils.SWIFT_CONF_FILE)
978
983
  try:
979
984
  _POLICIES = parse_storage_policies(policy_conf)
swift/common/swob.py CHANGED
@@ -35,7 +35,8 @@ place to keep Swift working every time webob decides some interface
35
35
  needs to change.
36
36
  """
37
37
 
38
- from collections import defaultdict, MutableMapping
38
+ from collections import defaultdict
39
+ from collections.abc import MutableMapping
39
40
  import time
40
41
  from functools import partial
41
42
  from datetime import datetime
@@ -43,16 +44,14 @@ from email.utils import parsedate
43
44
  import re
44
45
  import random
45
46
  import functools
46
- import inspect
47
+ from io import BytesIO
47
48
 
48
- import six
49
- from six import BytesIO
50
- from six import StringIO
51
- from six.moves import urllib
49
+ from io import StringIO
50
+ import urllib
52
51
 
53
52
  from swift.common.header_key_dict import HeaderKeyDict
54
53
  from swift.common.utils import UTC, reiterate, split_path, Timestamp, pairs, \
55
- close_if_possible, closing_if_possible
54
+ close_if_possible, closing_if_possible, config_true_value, friendly_close
56
55
  from swift.common.exceptions import InvalidTimestamp
57
56
 
58
57
 
@@ -111,6 +110,8 @@ RESPONSE_REASONS = {
111
110
  'backend server.'),
112
111
  507: ('Insufficient Storage', 'There was not enough space to save the '
113
112
  'resource. Drive: %(drive)s'),
113
+ 529: ('Too Many Backend Requests', 'The server is incapable of performing '
114
+ 'the requested operation due to too many requests. Slow down.')
114
115
  }
115
116
 
116
117
  MAX_RANGE_OVERLAPS = 2
@@ -152,7 +153,7 @@ def _datetime_property(header):
152
153
  return None
153
154
 
154
155
  def setter(self, value):
155
- if isinstance(value, (float,) + six.integer_types):
156
+ if isinstance(value, (float, int)):
156
157
  self.headers[header] = time.strftime(
157
158
  "%a, %d %b %Y %H:%M:%S GMT", time.gmtime(value))
158
159
  elif isinstance(value, datetime):
@@ -246,9 +247,7 @@ class HeaderEnvironProxy(MutableMapping):
246
247
  def __setitem__(self, key, value):
247
248
  if value is None:
248
249
  self.environ.pop(header_to_environ_key(key), None)
249
- elif six.PY2 and isinstance(value, six.text_type):
250
- self.environ[header_to_environ_key(key)] = value.encode('utf-8')
251
- elif not six.PY2 and isinstance(value, six.binary_type):
250
+ elif isinstance(value, bytes):
252
251
  self.environ[header_to_environ_key(key)] = value.decode('latin1')
253
252
  else:
254
253
  self.environ[header_to_environ_key(key)] = str(value)
@@ -274,70 +273,42 @@ class HeaderEnvironProxy(MutableMapping):
274
273
  def wsgi_to_bytes(wsgi_str):
275
274
  if wsgi_str is None:
276
275
  return None
277
- if six.PY2:
278
- return wsgi_str
279
276
  return wsgi_str.encode('latin1')
280
277
 
281
278
 
282
279
  def wsgi_to_str(wsgi_str):
283
280
  if wsgi_str is None:
284
281
  return None
285
- if six.PY2:
286
- return wsgi_str
287
282
  return wsgi_to_bytes(wsgi_str).decode('utf8', errors='surrogateescape')
288
283
 
289
284
 
290
285
  def bytes_to_wsgi(byte_str):
291
- if six.PY2:
292
- return byte_str
293
286
  return byte_str.decode('latin1')
294
287
 
295
288
 
296
289
  def str_to_wsgi(native_str):
297
- if six.PY2:
298
- return native_str
299
290
  return bytes_to_wsgi(native_str.encode('utf8', errors='surrogateescape'))
300
291
 
301
292
 
302
293
  def wsgi_quote(wsgi_str, safe='/'):
303
- if six.PY2:
304
- if not isinstance(wsgi_str, bytes):
305
- raise TypeError('Expected a WSGI string; got %r' % wsgi_str)
306
- return urllib.parse.quote(wsgi_str, safe=safe)
307
-
308
294
  if not isinstance(wsgi_str, str) or any(ord(x) > 255 for x in wsgi_str):
309
295
  raise TypeError('Expected a WSGI string; got %r' % wsgi_str)
310
296
  return urllib.parse.quote(wsgi_str, safe=safe, encoding='latin-1')
311
297
 
312
298
 
313
299
  def wsgi_unquote(wsgi_str):
314
- if six.PY2:
315
- if not isinstance(wsgi_str, bytes):
316
- raise TypeError('Expected a WSGI string; got %r' % wsgi_str)
317
- return urllib.parse.unquote(wsgi_str)
318
-
319
300
  if not isinstance(wsgi_str, str) or any(ord(x) > 255 for x in wsgi_str):
320
301
  raise TypeError('Expected a WSGI string; got %r' % wsgi_str)
321
302
  return urllib.parse.unquote(wsgi_str, encoding='latin-1')
322
303
 
323
304
 
324
305
  def wsgi_quote_plus(wsgi_str):
325
- if six.PY2:
326
- if not isinstance(wsgi_str, bytes):
327
- raise TypeError('Expected a WSGI string; got %r' % wsgi_str)
328
- return urllib.parse.quote_plus(wsgi_str)
329
-
330
306
  if not isinstance(wsgi_str, str) or any(ord(x) > 255 for x in wsgi_str):
331
307
  raise TypeError('Expected a WSGI string; got %r' % wsgi_str)
332
308
  return urllib.parse.quote_plus(wsgi_str, encoding='latin-1')
333
309
 
334
310
 
335
311
  def wsgi_unquote_plus(wsgi_str):
336
- if six.PY2:
337
- if not isinstance(wsgi_str, bytes):
338
- raise TypeError('Expected a WSGI string; got %r' % wsgi_str)
339
- return urllib.parse.unquote_plus(wsgi_str)
340
-
341
312
  if not isinstance(wsgi_str, str) or any(ord(x) > 255 for x in wsgi_str):
342
313
  raise TypeError('Expected a WSGI string; got %r' % wsgi_str)
343
314
  return urllib.parse.unquote_plus(wsgi_str, encoding='latin-1')
@@ -355,7 +326,7 @@ def _resp_status_property():
355
326
  return '%s %s' % (self.status_int, self.title)
356
327
 
357
328
  def setter(self, value):
358
- if isinstance(value, six.integer_types):
329
+ if isinstance(value, int):
359
330
  self.status_int = value
360
331
  self.explanation = self.title = RESPONSE_REASONS[value][0]
361
332
  else:
@@ -383,9 +354,9 @@ def _resp_body_property():
383
354
  return self._body
384
355
 
385
356
  def setter(self, value):
386
- if isinstance(value, six.text_type):
357
+ if isinstance(value, str):
387
358
  raise TypeError('WSGI responses must be bytes')
388
- if isinstance(value, six.binary_type):
359
+ if isinstance(value, bytes):
389
360
  self.content_length = len(value)
390
361
  close_if_possible(self._app_iter)
391
362
  self._app_iter = None
@@ -689,6 +660,12 @@ class Range(object):
689
660
  return all_ranges
690
661
 
691
662
 
663
+ def normalize_etag(tag):
664
+ if tag and tag.startswith('"') and tag.endswith('"') and tag != '"':
665
+ return tag[1:-1]
666
+ return tag
667
+
668
+
692
669
  class Match(object):
693
670
  """
694
671
  Wraps a Request's If-[None-]Match header as a friendly object.
@@ -701,15 +678,10 @@ class Match(object):
701
678
  tag = tag.strip()
702
679
  if not tag:
703
680
  continue
704
- if tag.startswith('"') and tag.endswith('"'):
705
- self.tags.add(tag[1:-1])
706
- else:
707
- self.tags.add(tag)
681
+ self.tags.add(normalize_etag(tag))
708
682
 
709
683
  def __contains__(self, val):
710
- if val and val.startswith('"') and val.endswith('"'):
711
- val = val[1:-1]
712
- return '*' in self.tags or val in self.tags
684
+ return '*' in self.tags or normalize_etag(val) in self.tags
713
685
 
714
686
  def __repr__(self):
715
687
  return '%s(%r)' % (
@@ -724,7 +696,7 @@ class Accept(object):
724
696
  """
725
697
 
726
698
  # RFC 2616 section 2.2
727
- token = r'[^()<>@,;:\"/\[\]?={}\x00-\x20\x7f]+'
699
+ token = r'[^()<>@,;:\"/\[\]?={}\x00-\x20\x7f]+' # nosec B105
728
700
  qdtext = r'[^"]'
729
701
  quoted_pair = r'(?:\\.)'
730
702
  quoted_string = r'"(?:' + qdtext + r'|' + quoted_pair + r')*"'
@@ -799,19 +771,13 @@ def _req_environ_property(environ_field, is_wsgi_string_field=True):
799
771
  return self.environ.get(environ_field, None)
800
772
 
801
773
  def setter(self, value):
802
- if six.PY2:
803
- if isinstance(value, six.text_type):
804
- self.environ[environ_field] = value.encode('utf-8')
805
- else:
806
- self.environ[environ_field] = value
807
- else:
808
- if is_wsgi_string_field:
809
- # Check that input is valid before setting
810
- if isinstance(value, str):
811
- value.encode('latin1').decode('utf-8')
812
- if isinstance(value, bytes):
813
- value = value.decode('latin1')
814
- self.environ[environ_field] = value
774
+ if is_wsgi_string_field:
775
+ # Check that input is valid before setting
776
+ if isinstance(value, str):
777
+ value.encode('latin1').decode('utf-8')
778
+ if isinstance(value, bytes):
779
+ value = value.decode('latin1')
780
+ self.environ[environ_field] = value
815
781
 
816
782
  return property(getter, setter, doc=("Get and set the %s property "
817
783
  "in the WSGI environment") % environ_field)
@@ -829,7 +795,7 @@ def _req_body_property():
829
795
  return body
830
796
 
831
797
  def setter(self, value):
832
- if not isinstance(value, six.binary_type):
798
+ if not isinstance(value, bytes):
833
799
  value = value.encode('utf8')
834
800
  self.environ['wsgi.input'] = WsgiBytesIO(value)
835
801
  self.environ['CONTENT_LENGTH'] = str(len(value))
@@ -926,15 +892,11 @@ class Request(object):
926
892
  """
927
893
  headers = headers or {}
928
894
  environ = environ or {}
929
- if six.PY2:
930
- if isinstance(path, six.text_type):
931
- path = path.encode('utf-8')
895
+ if isinstance(path, bytes):
896
+ path = path.decode('latin1')
932
897
  else:
933
- if isinstance(path, six.binary_type):
934
- path = path.decode('latin1')
935
- else:
936
- # Check that the input is valid
937
- path.encode('latin1')
898
+ # Check that the input is valid
899
+ path.encode('latin1')
938
900
 
939
901
  parsed_path = urllib.parse.urlparse(path)
940
902
  server_name = 'localhost'
@@ -964,7 +926,7 @@ class Request(object):
964
926
  }
965
927
  env.update(environ)
966
928
  if body is not None:
967
- if not isinstance(body, six.binary_type):
929
+ if not isinstance(body, bytes):
968
930
  body = body.encode('utf8')
969
931
  env['wsgi.input'] = WsgiBytesIO(body)
970
932
  env['CONTENT_LENGTH'] = str(len(body))
@@ -990,13 +952,9 @@ class Request(object):
990
952
  "Provides QUERY_STRING parameters as a dictionary"
991
953
  if self._params_cache is None:
992
954
  if 'QUERY_STRING' in self.environ:
993
- if six.PY2:
994
- self._params_cache = dict(urllib.parse.parse_qsl(
995
- self.environ['QUERY_STRING'], True))
996
- else:
997
- self._params_cache = dict(urllib.parse.parse_qsl(
998
- self.environ['QUERY_STRING'],
999
- keep_blank_values=True, encoding='latin-1'))
955
+ self._params_cache = dict(urllib.parse.parse_qsl(
956
+ self.environ['QUERY_STRING'],
957
+ keep_blank_values=True, encoding='latin-1'))
1000
958
  else:
1001
959
  self._params_cache = {}
1002
960
  return self._params_cache
@@ -1005,11 +963,35 @@ class Request(object):
1005
963
  @params.setter
1006
964
  def params(self, param_pairs):
1007
965
  self._params_cache = None
1008
- if six.PY2:
1009
- self.query_string = urllib.parse.urlencode(param_pairs)
966
+ self.query_string = urllib.parse.urlencode(param_pairs,
967
+ encoding='latin-1')
968
+
969
+ def ensure_x_timestamp(self):
970
+ """
971
+ Similar to :attr:`timestamp`, but the ``X-Timestamp`` header will be
972
+ set if not present.
973
+
974
+ :raises HTTPBadRequest: if X-Timestamp is already set but not a valid
975
+ :class:`~swift.common.utils.Timestamp`
976
+ :returns: the request's X-Timestamp header,
977
+ as a :class:`~swift.common.utils.Timestamp`
978
+ """
979
+ # The container sync feature includes an x-timestamp header with
980
+ # requests. If present this is checked and preserved, otherwise a fresh
981
+ # timestamp is added.
982
+ if 'HTTP_X_TIMESTAMP' in self.environ:
983
+ try:
984
+ self._timestamp = Timestamp(self.environ['HTTP_X_TIMESTAMP'])
985
+ except ValueError:
986
+ raise HTTPBadRequest(
987
+ request=self, content_type='text/plain',
988
+ body='X-Timestamp should be a UNIX timestamp float value; '
989
+ 'was %r' % self.environ['HTTP_X_TIMESTAMP'])
1010
990
  else:
1011
- self.query_string = urllib.parse.urlencode(param_pairs,
1012
- encoding='latin-1')
991
+ self._timestamp = Timestamp.now()
992
+ # Always normalize it to the internal form
993
+ self.environ['HTTP_X_TIMESTAMP'] = self._timestamp.internal
994
+ return self._timestamp
1013
995
 
1014
996
  @property
1015
997
  def timestamp(self):
@@ -1063,6 +1045,11 @@ class Request(object):
1063
1045
  "Provides the full url of the request"
1064
1046
  return self.host_url + self.path_qs
1065
1047
 
1048
+ @property
1049
+ def allow_reserved_names(self):
1050
+ return config_true_value(self.environ.get(
1051
+ 'HTTP_X_BACKEND_ALLOW_RESERVED_NAMES'))
1052
+
1066
1053
  def as_referer(self):
1067
1054
  return self.method + ' ' + self.url
1068
1055
 
@@ -1227,7 +1214,7 @@ class Response(object):
1227
1214
  self.request = request
1228
1215
  self._app_iter = None
1229
1216
  # Allow error messages to come as natural strings on py3.
1230
- if isinstance(body, six.text_type):
1217
+ if isinstance(body, str):
1231
1218
  body = body.encode('utf8')
1232
1219
  self.body = body
1233
1220
  self.app_iter = app_iter
@@ -1357,11 +1344,15 @@ class Response(object):
1357
1344
  if empty_resp is not None:
1358
1345
  self.status = empty_resp
1359
1346
  self.content_length = 0
1347
+ # the existing successful response and it's app_iter have been
1348
+ # determined to not meet the conditions of the reqeust, the
1349
+ # response app_iter should be closed but not drained.
1360
1350
  close_if_possible(app_iter)
1361
1351
  return [b'']
1362
1352
 
1363
1353
  if self.request and self.request.method == 'HEAD':
1364
1354
  # We explicitly do NOT want to set self.content_length to 0 here
1355
+ friendly_close(app_iter) # be friendly to our app_iter
1365
1356
  return [b'']
1366
1357
 
1367
1358
  if self.conditional_response and self.request and \
@@ -1530,23 +1521,15 @@ def wsgify(func):
1530
1521
  return a Response object into WSGI callables. Also catches any raised
1531
1522
  HTTPExceptions and treats them as a returned Response.
1532
1523
  """
1533
- argspec = inspect.getargspec(func)
1534
- if argspec.args and argspec.args[0] == 'self':
1535
- @functools.wraps(func)
1536
- def _wsgify_self(self, env, start_response):
1537
- try:
1538
- return func(self, Request(env))(env, start_response)
1539
- except HTTPException as err_resp:
1540
- return err_resp(env, start_response)
1541
- return _wsgify_self
1542
- else:
1543
- @functools.wraps(func)
1544
- def _wsgify_bare(env, start_response):
1545
- try:
1546
- return func(Request(env))(env, start_response)
1547
- except HTTPException as err_resp:
1548
- return err_resp(env, start_response)
1549
- return _wsgify_bare
1524
+ @functools.wraps(func)
1525
+ def _wsgify(*args):
1526
+ env, start_response = args[-2:]
1527
+ new_args = args[:-2] + (Request(env), )
1528
+ try:
1529
+ return func(*new_args)(env, start_response)
1530
+ except HTTPException as err_resp:
1531
+ return err_resp(env, start_response)
1532
+ return _wsgify
1550
1533
 
1551
1534
 
1552
1535
  class StatusMap(object):
@@ -1591,3 +1574,4 @@ HTTPNotImplemented = status_map[501]
1591
1574
  HTTPBadGateway = status_map[502]
1592
1575
  HTTPServiceUnavailable = status_map[503]
1593
1576
  HTTPInsufficientStorage = status_map[507]
1577
+ HTTPTooManyBackendRequests = status_map[529]