swift 2.23.3__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.
- swift/__init__.py +29 -50
- swift/account/auditor.py +21 -118
- swift/account/backend.py +33 -28
- swift/account/reaper.py +37 -28
- swift/account/replicator.py +22 -0
- swift/account/server.py +60 -26
- swift/account/utils.py +28 -11
- swift-2.23.3.data/scripts/swift-account-audit → swift/cli/account_audit.py +23 -13
- swift-2.23.3.data/scripts/swift-config → swift/cli/config.py +2 -2
- swift/cli/container_deleter.py +5 -11
- swift-2.23.3.data/scripts/swift-dispersion-populate → swift/cli/dispersion_populate.py +8 -7
- swift/cli/dispersion_report.py +10 -9
- swift-2.23.3.data/scripts/swift-drive-audit → swift/cli/drive_audit.py +63 -21
- swift/cli/form_signature.py +3 -7
- swift-2.23.3.data/scripts/swift-get-nodes → swift/cli/get_nodes.py +8 -2
- swift/cli/info.py +154 -14
- swift/cli/manage_shard_ranges.py +705 -37
- swift-2.23.3.data/scripts/swift-oldies → swift/cli/oldies.py +25 -14
- swift-2.23.3.data/scripts/swift-orphans → swift/cli/orphans.py +7 -3
- swift/cli/recon.py +196 -67
- swift-2.23.3.data/scripts/swift-recon-cron → swift/cli/recon_cron.py +17 -20
- swift-2.23.3.data/scripts/swift-reconciler-enqueue → swift/cli/reconciler_enqueue.py +2 -3
- swift/cli/relinker.py +807 -126
- swift/cli/reload.py +135 -0
- swift/cli/ringbuilder.py +217 -20
- swift/cli/ringcomposer.py +0 -1
- swift/cli/shard-info.py +4 -3
- swift/common/base_storage_server.py +9 -20
- swift/common/bufferedhttp.py +48 -74
- swift/common/constraints.py +20 -15
- swift/common/container_sync_realms.py +9 -11
- swift/common/daemon.py +25 -8
- swift/common/db.py +195 -128
- swift/common/db_auditor.py +168 -0
- swift/common/db_replicator.py +95 -55
- swift/common/digest.py +141 -0
- swift/common/direct_client.py +144 -33
- swift/common/error_limiter.py +93 -0
- swift/common/exceptions.py +25 -1
- swift/common/header_key_dict.py +2 -9
- swift/common/http_protocol.py +373 -0
- swift/common/internal_client.py +129 -59
- swift/common/linkat.py +3 -4
- swift/common/manager.py +284 -67
- swift/common/memcached.py +390 -145
- swift/common/middleware/__init__.py +4 -0
- swift/common/middleware/account_quotas.py +211 -46
- swift/common/middleware/acl.py +3 -8
- swift/common/middleware/backend_ratelimit.py +230 -0
- swift/common/middleware/bulk.py +22 -34
- swift/common/middleware/catch_errors.py +1 -3
- swift/common/middleware/cname_lookup.py +6 -11
- swift/common/middleware/container_quotas.py +1 -1
- swift/common/middleware/container_sync.py +39 -17
- swift/common/middleware/copy.py +12 -0
- swift/common/middleware/crossdomain.py +22 -9
- swift/common/middleware/crypto/__init__.py +2 -1
- swift/common/middleware/crypto/crypto_utils.py +11 -15
- swift/common/middleware/crypto/decrypter.py +28 -11
- swift/common/middleware/crypto/encrypter.py +12 -17
- swift/common/middleware/crypto/keymaster.py +8 -15
- swift/common/middleware/crypto/kms_keymaster.py +2 -1
- swift/common/middleware/dlo.py +15 -11
- swift/common/middleware/domain_remap.py +5 -4
- swift/common/middleware/etag_quoter.py +128 -0
- swift/common/middleware/formpost.py +73 -70
- swift/common/middleware/gatekeeper.py +8 -1
- swift/common/middleware/keystoneauth.py +33 -3
- swift/common/middleware/list_endpoints.py +4 -4
- swift/common/middleware/listing_formats.py +85 -49
- swift/common/middleware/memcache.py +4 -95
- swift/common/middleware/name_check.py +3 -2
- swift/common/middleware/proxy_logging.py +160 -92
- swift/common/middleware/ratelimit.py +17 -10
- swift/common/middleware/read_only.py +6 -4
- swift/common/middleware/recon.py +59 -22
- swift/common/middleware/s3api/acl_handlers.py +25 -3
- swift/common/middleware/s3api/acl_utils.py +6 -1
- swift/common/middleware/s3api/controllers/__init__.py +6 -0
- swift/common/middleware/s3api/controllers/acl.py +3 -2
- swift/common/middleware/s3api/controllers/bucket.py +242 -137
- swift/common/middleware/s3api/controllers/logging.py +2 -2
- swift/common/middleware/s3api/controllers/multi_delete.py +43 -20
- swift/common/middleware/s3api/controllers/multi_upload.py +219 -133
- swift/common/middleware/s3api/controllers/obj.py +112 -8
- swift/common/middleware/s3api/controllers/object_lock.py +44 -0
- swift/common/middleware/s3api/controllers/s3_acl.py +2 -2
- swift/common/middleware/s3api/controllers/tagging.py +57 -0
- swift/common/middleware/s3api/controllers/versioning.py +36 -7
- swift/common/middleware/s3api/etree.py +22 -9
- swift/common/middleware/s3api/exception.py +0 -4
- swift/common/middleware/s3api/s3api.py +113 -41
- swift/common/middleware/s3api/s3request.py +384 -218
- swift/common/middleware/s3api/s3response.py +126 -23
- swift/common/middleware/s3api/s3token.py +16 -17
- swift/common/middleware/s3api/schema/delete.rng +1 -1
- swift/common/middleware/s3api/subresource.py +7 -10
- swift/common/middleware/s3api/utils.py +27 -10
- swift/common/middleware/slo.py +665 -358
- swift/common/middleware/staticweb.py +64 -37
- swift/common/middleware/symlink.py +51 -18
- swift/common/middleware/tempauth.py +76 -58
- swift/common/middleware/tempurl.py +191 -173
- swift/common/middleware/versioned_writes/__init__.py +51 -0
- swift/common/middleware/{versioned_writes.py → versioned_writes/legacy.py} +27 -26
- swift/common/middleware/versioned_writes/object_versioning.py +1482 -0
- swift/common/middleware/x_profile/exceptions.py +1 -4
- swift/common/middleware/x_profile/html_viewer.py +18 -19
- swift/common/middleware/x_profile/profile_model.py +1 -2
- swift/common/middleware/xprofile.py +10 -10
- swift-2.23.3.data/scripts/swift-container-server → swift/common/recon.py +13 -8
- swift/common/registry.py +147 -0
- swift/common/request_helpers.py +324 -57
- swift/common/ring/builder.py +67 -25
- swift/common/ring/composite_builder.py +1 -1
- swift/common/ring/ring.py +177 -51
- swift/common/ring/utils.py +1 -1
- swift/common/splice.py +10 -6
- swift/common/statsd_client.py +205 -0
- swift/common/storage_policy.py +49 -44
- swift/common/swob.py +86 -102
- swift/common/{utils.py → utils/__init__.py} +2163 -2772
- swift/common/utils/base.py +131 -0
- swift/common/utils/config.py +433 -0
- swift/common/utils/ipaddrs.py +256 -0
- swift/common/utils/libc.py +345 -0
- swift/common/utils/logs.py +859 -0
- swift/common/utils/timestamp.py +412 -0
- swift/common/wsgi.py +553 -535
- swift/container/auditor.py +14 -100
- swift/container/backend.py +490 -231
- swift/container/reconciler.py +126 -37
- swift/container/replicator.py +96 -22
- swift/container/server.py +358 -165
- swift/container/sharder.py +1540 -684
- swift/container/sync.py +94 -88
- swift/container/updater.py +53 -32
- swift/obj/auditor.py +153 -35
- swift/obj/diskfile.py +466 -217
- swift/obj/expirer.py +406 -124
- swift/obj/mem_diskfile.py +7 -4
- swift/obj/mem_server.py +1 -0
- swift/obj/reconstructor.py +523 -262
- swift/obj/replicator.py +249 -188
- swift/obj/server.py +207 -122
- swift/obj/ssync_receiver.py +145 -85
- swift/obj/ssync_sender.py +113 -54
- swift/obj/updater.py +652 -139
- swift/obj/watchers/__init__.py +0 -0
- swift/obj/watchers/dark_data.py +213 -0
- swift/proxy/controllers/account.py +11 -11
- swift/proxy/controllers/base.py +848 -604
- swift/proxy/controllers/container.py +433 -92
- swift/proxy/controllers/info.py +3 -2
- swift/proxy/controllers/obj.py +1000 -489
- swift/proxy/server.py +185 -112
- {swift-2.23.3.dist-info → swift-2.35.0.dist-info}/AUTHORS +58 -11
- {swift-2.23.3.dist-info → swift-2.35.0.dist-info}/METADATA +51 -56
- swift-2.35.0.dist-info/RECORD +201 -0
- {swift-2.23.3.dist-info → swift-2.35.0.dist-info}/WHEEL +1 -1
- {swift-2.23.3.dist-info → swift-2.35.0.dist-info}/entry_points.txt +43 -0
- swift-2.35.0.dist-info/pbr.json +1 -0
- swift/locale/de/LC_MESSAGES/swift.po +0 -1216
- swift/locale/en_GB/LC_MESSAGES/swift.po +0 -1207
- swift/locale/es/LC_MESSAGES/swift.po +0 -1085
- swift/locale/fr/LC_MESSAGES/swift.po +0 -909
- swift/locale/it/LC_MESSAGES/swift.po +0 -894
- swift/locale/ja/LC_MESSAGES/swift.po +0 -965
- swift/locale/ko_KR/LC_MESSAGES/swift.po +0 -964
- swift/locale/pt_BR/LC_MESSAGES/swift.po +0 -881
- swift/locale/ru/LC_MESSAGES/swift.po +0 -891
- swift/locale/tr_TR/LC_MESSAGES/swift.po +0 -832
- swift/locale/zh_CN/LC_MESSAGES/swift.po +0 -833
- swift/locale/zh_TW/LC_MESSAGES/swift.po +0 -838
- swift-2.23.3.data/scripts/swift-account-auditor +0 -23
- swift-2.23.3.data/scripts/swift-account-info +0 -51
- swift-2.23.3.data/scripts/swift-account-reaper +0 -23
- swift-2.23.3.data/scripts/swift-account-replicator +0 -34
- swift-2.23.3.data/scripts/swift-account-server +0 -23
- swift-2.23.3.data/scripts/swift-container-auditor +0 -23
- swift-2.23.3.data/scripts/swift-container-info +0 -55
- swift-2.23.3.data/scripts/swift-container-reconciler +0 -21
- swift-2.23.3.data/scripts/swift-container-replicator +0 -34
- swift-2.23.3.data/scripts/swift-container-sharder +0 -37
- swift-2.23.3.data/scripts/swift-container-sync +0 -23
- swift-2.23.3.data/scripts/swift-container-updater +0 -23
- swift-2.23.3.data/scripts/swift-dispersion-report +0 -24
- swift-2.23.3.data/scripts/swift-form-signature +0 -20
- swift-2.23.3.data/scripts/swift-init +0 -119
- swift-2.23.3.data/scripts/swift-object-auditor +0 -29
- swift-2.23.3.data/scripts/swift-object-expirer +0 -33
- swift-2.23.3.data/scripts/swift-object-info +0 -60
- swift-2.23.3.data/scripts/swift-object-reconstructor +0 -33
- swift-2.23.3.data/scripts/swift-object-relinker +0 -41
- swift-2.23.3.data/scripts/swift-object-replicator +0 -37
- swift-2.23.3.data/scripts/swift-object-server +0 -27
- swift-2.23.3.data/scripts/swift-object-updater +0 -23
- swift-2.23.3.data/scripts/swift-proxy-server +0 -23
- swift-2.23.3.data/scripts/swift-recon +0 -24
- swift-2.23.3.data/scripts/swift-ring-builder +0 -24
- swift-2.23.3.data/scripts/swift-ring-builder-analyzer +0 -22
- swift-2.23.3.data/scripts/swift-ring-composer +0 -22
- swift-2.23.3.dist-info/RECORD +0 -220
- swift-2.23.3.dist-info/pbr.json +0 -1
- {swift-2.23.3.dist-info → swift-2.35.0.dist-info}/LICENSE +0 -0
- {swift-2.23.3.dist-info → swift-2.35.0.dist-info}/top_level.txt +0 -0
swift/common/storage_policy.py
CHANGED
@@ -17,8 +17,7 @@ import os
|
|
17
17
|
import string
|
18
18
|
import sys
|
19
19
|
import textwrap
|
20
|
-
import
|
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,
|
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(
|
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
|
-
|
84
|
-
|
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)
|
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
|
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
|
-
|
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
|
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
|
658
|
+
def validate_ring_data(self, ring_data):
|
647
659
|
"""
|
648
|
-
|
660
|
+
EC specific validation
|
649
661
|
|
650
|
-
|
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
|
-
|
656
|
-
|
657
|
-
|
658
|
-
|
659
|
-
|
660
|
-
|
661
|
-
|
662
|
-
|
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
|
-
|
972
|
-
|
973
|
-
|
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
|
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
|
47
|
+
from io import BytesIO
|
47
48
|
|
48
|
-
import
|
49
|
-
|
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,
|
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
|
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,
|
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,
|
357
|
+
if isinstance(value, str):
|
387
358
|
raise TypeError('WSGI responses must be bytes')
|
388
|
-
if isinstance(value,
|
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
|
-
|
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
|
-
|
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
|
803
|
-
|
804
|
-
|
805
|
-
|
806
|
-
|
807
|
-
|
808
|
-
|
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,
|
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
|
930
|
-
|
931
|
-
path = path.encode('utf-8')
|
895
|
+
if isinstance(path, bytes):
|
896
|
+
path = path.decode('latin1')
|
932
897
|
else:
|
933
|
-
|
934
|
-
|
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,
|
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
|
-
|
994
|
-
self.
|
995
|
-
|
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
|
-
|
1009
|
-
|
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.
|
1012
|
-
|
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,
|
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
|
-
|
1534
|
-
|
1535
|
-
|
1536
|
-
|
1537
|
-
|
1538
|
-
|
1539
|
-
|
1540
|
-
|
1541
|
-
|
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]
|