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
@@ -36,7 +36,7 @@ from swift.common.utils import get_logger, config_true_value
|
|
36
36
|
from swift.common.request_helpers import (
|
37
37
|
remove_items, get_sys_meta_prefix, OBJECT_TRANSIENT_SYSMETA_PREFIX
|
38
38
|
)
|
39
|
-
from
|
39
|
+
from urllib.parse import urlsplit
|
40
40
|
import re
|
41
41
|
|
42
42
|
#: A list of python regular expressions that will be used to
|
@@ -75,6 +75,8 @@ class GatekeeperMiddleware(object):
|
|
75
75
|
self.outbound_condition = make_exclusion_test(outbound_exclusions)
|
76
76
|
self.shunt_x_timestamp = config_true_value(
|
77
77
|
conf.get('shunt_inbound_x_timestamp', 'true'))
|
78
|
+
self.allow_reserved_names_header = config_true_value(
|
79
|
+
conf.get('allow_reserved_names_header', 'false'))
|
78
80
|
|
79
81
|
def __call__(self, env, start_response):
|
80
82
|
req = Request(env)
|
@@ -89,6 +91,11 @@ class GatekeeperMiddleware(object):
|
|
89
91
|
self.logger.debug('shunted request headers: %s' %
|
90
92
|
[('X-Timestamp', ts)])
|
91
93
|
|
94
|
+
if 'X-Allow-Reserved-Names' in req.headers \
|
95
|
+
and self.allow_reserved_names_header:
|
96
|
+
req.headers['X-Backend-Allow-Reserved-Names'] = \
|
97
|
+
req.headers.pop('X-Allow-Reserved-Names')
|
98
|
+
|
92
99
|
def gatekeeper_response(status, response_headers, exc_info=None):
|
93
100
|
def fixed_response_headers():
|
94
101
|
def relative_path(value):
|
@@ -178,9 +178,13 @@ class KeystoneAuth(object):
|
|
178
178
|
config_read_reseller_options(conf,
|
179
179
|
dict(operator_roles=['admin',
|
180
180
|
'swiftoperator'],
|
181
|
-
service_roles=[]
|
181
|
+
service_roles=[],
|
182
|
+
project_reader_roles=[]))
|
182
183
|
self.reseller_admin_role = conf.get('reseller_admin_role',
|
183
184
|
'ResellerAdmin').lower()
|
185
|
+
self.system_reader_roles = {role.lower() for role in list_from_csv(
|
186
|
+
conf.get('system_reader_roles', ''))}
|
187
|
+
|
184
188
|
config_is_admin = conf.get('is_admin', "false").lower()
|
185
189
|
if swift_utils.config_true_value(config_is_admin):
|
186
190
|
self.logger.warning("The 'is_admin' option for keystoneauth is no "
|
@@ -415,14 +419,26 @@ class KeystoneAuth(object):
|
|
415
419
|
user_service_roles = [r.lower() for r in env_identity.get(
|
416
420
|
'service_roles', [])]
|
417
421
|
|
418
|
-
# Give unconditional access to a user with the reseller_admin
|
419
|
-
# role.
|
422
|
+
# Give unconditional access to a user with the reseller_admin role.
|
420
423
|
if self.reseller_admin_role in user_roles:
|
421
424
|
msg = 'User %s has reseller admin authorizing'
|
422
425
|
self.logger.debug(msg, tenant_id)
|
423
426
|
req.environ['swift_owner'] = True
|
424
427
|
return
|
425
428
|
|
429
|
+
# Being in system_reader_roles is almost as good as reseller_admin.
|
430
|
+
if self.system_reader_roles.intersection(user_roles):
|
431
|
+
# Note that if a system reader is trying to write, we're letting
|
432
|
+
# the request fall on other access checks below. This way,
|
433
|
+
# a compliance auditor can write a log file as a normal member.
|
434
|
+
if req.method in ('GET', 'HEAD'):
|
435
|
+
msg = 'User %s has system reader authorizing'
|
436
|
+
self.logger.debug(msg, tenant_id)
|
437
|
+
# We aren't setting 'swift_owner' nor 'reseller_request'
|
438
|
+
# because they are only ever used for something that modifies
|
439
|
+
# the contents of the cluster (setting ACL, deleting accounts).
|
440
|
+
return
|
441
|
+
|
426
442
|
# If we are not reseller admin and user is trying to delete its own
|
427
443
|
# account then deny it.
|
428
444
|
if not container and not obj and req.method == 'DELETE':
|
@@ -485,6 +501,20 @@ class KeystoneAuth(object):
|
|
485
501
|
req.environ['swift_owner'] = True
|
486
502
|
return
|
487
503
|
|
504
|
+
# The project_reader_roles is almost as good as operator_roles. But
|
505
|
+
# it does not work with service tokens and does not get 'swift_owner'.
|
506
|
+
# And, it only serves GET requests, obviously.
|
507
|
+
project_reader_roles = self.account_rules[account_prefix][
|
508
|
+
'project_reader_roles']
|
509
|
+
have_reader_role = set(project_reader_roles).intersection(
|
510
|
+
set(user_roles))
|
511
|
+
if have_reader_role:
|
512
|
+
if req.method in ('GET', 'HEAD'):
|
513
|
+
msg = 'User %s with role(s) %s has project reader authorizing'
|
514
|
+
self.logger.debug(msg, tenant_id,
|
515
|
+
','.join(project_reader_roles))
|
516
|
+
return
|
517
|
+
|
488
518
|
if acl_authorized is not None:
|
489
519
|
return self.denied_response(req)
|
490
520
|
|
@@ -52,9 +52,9 @@ of endpoints having the same form as described above, and a key 'headers' that
|
|
52
52
|
maps to a dictionary of headers that should be sent with a request made to
|
53
53
|
the endpoints, e.g.::
|
54
54
|
|
55
|
-
{ "endpoints": {"http://10.1.1.1:
|
56
|
-
"http://10.1.1.1:
|
57
|
-
"http://10.1.1.1:
|
55
|
+
{ "endpoints": {"http://10.1.1.1:6210/sda1/2/a/c3/o1",
|
56
|
+
"http://10.1.1.1:6230/sda3/2/a/c3/o1",
|
57
|
+
"http://10.1.1.1:6240/sda4/2/a/c3/o1"},
|
58
58
|
"headers": {"X-Backend-Storage-Policy-Index": "1"}}
|
59
59
|
|
60
60
|
In this example, the 'headers' dictionary indicates that requests to the
|
@@ -80,7 +80,7 @@ environment (everyone can query the locality data using this middleware).
|
|
80
80
|
|
81
81
|
import json
|
82
82
|
|
83
|
-
from
|
83
|
+
from urllib.parse import quote, unquote
|
84
84
|
|
85
85
|
from swift.common.ring import Ring
|
86
86
|
from swift.common.utils import get_logger, split_path
|
@@ -14,14 +14,15 @@
|
|
14
14
|
# limitations under the License.
|
15
15
|
|
16
16
|
import json
|
17
|
-
import six
|
18
17
|
from xml.etree.cElementTree import Element, SubElement, tostring
|
19
18
|
|
20
19
|
from swift.common.constraints import valid_api_version
|
20
|
+
from swift.common.header_key_dict import HeaderKeyDict
|
21
21
|
from swift.common.http import HTTP_NO_CONTENT
|
22
22
|
from swift.common.request_helpers import get_param
|
23
23
|
from swift.common.swob import HTTPException, HTTPNotAcceptable, Request, \
|
24
|
-
RESPONSE_REASONS, HTTPBadRequest
|
24
|
+
RESPONSE_REASONS, HTTPBadRequest, wsgi_quote, wsgi_to_bytes
|
25
|
+
from swift.common.utils import RESERVED, get_logger, list_from_csv
|
25
26
|
|
26
27
|
|
27
28
|
#: Mapping of query string ``format=`` values to their corresponding
|
@@ -73,8 +74,6 @@ def to_xml(document_element):
|
|
73
74
|
|
74
75
|
|
75
76
|
def account_to_xml(listing, account_name):
|
76
|
-
if isinstance(account_name, bytes):
|
77
|
-
account_name = account_name.decode('utf-8')
|
78
77
|
doc = Element('account', name=account_name)
|
79
78
|
doc.text = '\n'
|
80
79
|
for record in listing:
|
@@ -84,15 +83,15 @@ def account_to_xml(listing, account_name):
|
|
84
83
|
else:
|
85
84
|
sub = SubElement(doc, 'container')
|
86
85
|
for field in ('name', 'count', 'bytes', 'last_modified'):
|
87
|
-
SubElement(sub, field).text =
|
88
|
-
|
86
|
+
SubElement(sub, field).text = str(record.pop(field))
|
87
|
+
for field in ('storage_policy',):
|
88
|
+
if field in record:
|
89
|
+
SubElement(sub, field).text = str(record.pop(field))
|
89
90
|
sub.tail = '\n'
|
90
91
|
return to_xml(doc)
|
91
92
|
|
92
93
|
|
93
94
|
def container_to_xml(listing, base_name):
|
94
|
-
if isinstance(base_name, bytes):
|
95
|
-
base_name = base_name.decode('utf-8')
|
96
95
|
doc = Element('container', name=base_name)
|
97
96
|
for record in listing:
|
98
97
|
if 'subdir' in record:
|
@@ -103,8 +102,7 @@ def container_to_xml(listing, base_name):
|
|
103
102
|
sub = SubElement(doc, 'object')
|
104
103
|
for field in ('name', 'hash', 'bytes', 'content_type',
|
105
104
|
'last_modified'):
|
106
|
-
SubElement(sub, field).text =
|
107
|
-
record.pop(field))
|
105
|
+
SubElement(sub, field).text = str(record.pop(field))
|
108
106
|
return to_xml(doc)
|
109
107
|
|
110
108
|
|
@@ -119,8 +117,31 @@ def listing_to_text(listing):
|
|
119
117
|
|
120
118
|
|
121
119
|
class ListingFilter(object):
|
122
|
-
def __init__(self, app):
|
120
|
+
def __init__(self, app, conf, logger=None):
|
123
121
|
self.app = app
|
122
|
+
self.logger = logger or get_logger(conf, log_route='listing-filter')
|
123
|
+
|
124
|
+
def filter_reserved(self, listing, account, container):
|
125
|
+
new_listing = []
|
126
|
+
for entry in list(listing):
|
127
|
+
for key in ('name', 'subdir'):
|
128
|
+
value = entry.get(key, '')
|
129
|
+
if RESERVED in value:
|
130
|
+
if container:
|
131
|
+
self.logger.warning(
|
132
|
+
'Container listing for %s/%s had '
|
133
|
+
'reserved byte in %s: %r',
|
134
|
+
wsgi_quote(account), wsgi_quote(container),
|
135
|
+
key, value)
|
136
|
+
else:
|
137
|
+
self.logger.warning(
|
138
|
+
'Account listing for %s had '
|
139
|
+
'reserved byte in %s: %r',
|
140
|
+
wsgi_quote(account), key, value)
|
141
|
+
break # out of the *key* loop; check next entry
|
142
|
+
else:
|
143
|
+
new_listing.append(entry)
|
144
|
+
return new_listing
|
124
145
|
|
125
146
|
def __call__(self, env, start_response):
|
126
147
|
req = Request(env)
|
@@ -128,10 +149,10 @@ class ListingFilter(object):
|
|
128
149
|
# account and container only
|
129
150
|
version, acct, cont = req.split_path(2, 3)
|
130
151
|
except ValueError:
|
131
|
-
|
152
|
+
is_account_or_container_req = False
|
132
153
|
else:
|
133
|
-
|
134
|
-
if not
|
154
|
+
is_account_or_container_req = True
|
155
|
+
if not is_account_or_container_req:
|
135
156
|
return self.app(env, start_response)
|
136
157
|
|
137
158
|
if not valid_api_version(version) or req.method not in ('GET', 'HEAD'):
|
@@ -145,46 +166,50 @@ class ListingFilter(object):
|
|
145
166
|
return err(env, start_response)
|
146
167
|
|
147
168
|
params = req.params
|
169
|
+
can_vary = 'format' not in params
|
148
170
|
params['format'] = 'json'
|
149
171
|
req.params = params
|
150
172
|
|
173
|
+
# Give other middlewares a chance to be in charge
|
174
|
+
env.setdefault('swift.format_listing', True)
|
151
175
|
status, headers, resp_iter = req.call_application(self.app)
|
152
|
-
|
153
|
-
header_to_index = {}
|
154
|
-
resp_content_type = resp_length = None
|
155
|
-
for i, (header, value) in enumerate(headers):
|
156
|
-
header = header.lower()
|
157
|
-
if header == 'content-type':
|
158
|
-
header_to_index[header] = i
|
159
|
-
resp_content_type = value.partition(';')[0]
|
160
|
-
elif header == 'content-length':
|
161
|
-
header_to_index[header] = i
|
162
|
-
resp_length = int(value)
|
163
|
-
|
164
|
-
if not status.startswith('200 '):
|
176
|
+
if not env.get('swift.format_listing'):
|
165
177
|
start_response(status, headers)
|
166
178
|
return resp_iter
|
167
179
|
|
168
|
-
if
|
180
|
+
if not status.startswith(('200 ', '204 ')):
|
169
181
|
start_response(status, headers)
|
170
182
|
return resp_iter
|
171
183
|
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
184
|
+
headers_dict = HeaderKeyDict(headers)
|
185
|
+
resp_content_type = headers_dict.get(
|
186
|
+
'content-type', '').partition(';')[0]
|
187
|
+
resp_length = headers_dict.get('content-length')
|
176
188
|
|
177
|
-
|
178
|
-
if
|
179
|
-
|
189
|
+
if can_vary:
|
190
|
+
if 'vary' in headers_dict:
|
191
|
+
value = headers_dict['vary']
|
192
|
+
if 'accept' not in list_from_csv(value.lower()):
|
193
|
+
headers_dict['vary'] = value + ', Accept'
|
180
194
|
else:
|
181
|
-
|
182
|
-
|
195
|
+
headers_dict['vary'] = 'Accept'
|
196
|
+
|
197
|
+
if resp_content_type != 'application/json':
|
198
|
+
start_response(status, list(headers_dict.items()))
|
199
|
+
return resp_iter
|
183
200
|
|
184
201
|
if req.method == 'HEAD':
|
185
|
-
|
186
|
-
|
187
|
-
|
202
|
+
headers_dict['content-type'] = out_content_type + '; charset=utf-8'
|
203
|
+
# proxy logging (and maybe other mw?) seem to be good about
|
204
|
+
# sticking this on HEAD/204 but we do it here to be responsible
|
205
|
+
# and explicit
|
206
|
+
headers_dict['content-length'] = 0
|
207
|
+
start_response(status, list(headers_dict.items()))
|
208
|
+
return resp_iter
|
209
|
+
|
210
|
+
if resp_length is None or \
|
211
|
+
int(resp_length) > MAX_CONTAINER_LISTING_CONTENT_LENGTH:
|
212
|
+
start_response(status, list(headers_dict.items()))
|
188
213
|
return resp_iter
|
189
214
|
|
190
215
|
body = b''.join(resp_iter)
|
@@ -198,32 +223,43 @@ class ListingFilter(object):
|
|
198
223
|
except ValueError:
|
199
224
|
# Static web listing that's returning invalid JSON?
|
200
225
|
# Just pass it straight through; that's about all we *can* do.
|
201
|
-
start_response(status,
|
226
|
+
start_response(status, list(headers_dict.items()))
|
202
227
|
return [body]
|
203
228
|
|
229
|
+
if not req.allow_reserved_names:
|
230
|
+
listing = self.filter_reserved(listing, acct, cont)
|
231
|
+
|
204
232
|
try:
|
205
233
|
if out_content_type.endswith('/xml'):
|
206
234
|
if cont:
|
207
|
-
body = container_to_xml(
|
235
|
+
body = container_to_xml(
|
236
|
+
listing, wsgi_to_bytes(cont).decode('utf-8'))
|
208
237
|
else:
|
209
|
-
body = account_to_xml(
|
238
|
+
body = account_to_xml(
|
239
|
+
listing, wsgi_to_bytes(acct).decode('utf-8'))
|
210
240
|
elif out_content_type == 'text/plain':
|
211
241
|
body = listing_to_text(listing)
|
212
|
-
|
242
|
+
else:
|
243
|
+
body = json.dumps(listing).encode('ascii')
|
213
244
|
except KeyError:
|
214
245
|
# listing was in a bad format -- funky static web listing??
|
215
|
-
start_response(status,
|
246
|
+
start_response(status, list(headers_dict.items()))
|
216
247
|
return [body]
|
217
248
|
|
218
249
|
if not body:
|
219
250
|
status = '%s %s' % (HTTP_NO_CONTENT,
|
220
251
|
RESPONSE_REASONS[HTTP_NO_CONTENT][0])
|
221
252
|
|
222
|
-
|
223
|
-
|
224
|
-
start_response(status,
|
253
|
+
headers_dict['content-type'] = out_content_type + '; charset=utf-8'
|
254
|
+
headers_dict['content-length'] = len(body)
|
255
|
+
start_response(status, list(headers_dict.items()))
|
225
256
|
return [body]
|
226
257
|
|
227
258
|
|
228
259
|
def filter_factory(global_conf, **local_conf):
|
229
|
-
|
260
|
+
conf = global_conf.copy()
|
261
|
+
conf.update(local_conf)
|
262
|
+
|
263
|
+
def listing_filter(app):
|
264
|
+
return ListingFilter(app, conf)
|
265
|
+
return listing_filter
|
@@ -13,14 +13,8 @@
|
|
13
13
|
# See the License for the specific language governing permissions and
|
14
14
|
# limitations under the License.
|
15
15
|
|
16
|
-
import
|
17
|
-
|
18
|
-
from eventlet.green import ssl
|
19
|
-
from six.moves.configparser import ConfigParser, NoSectionError, NoOptionError
|
20
|
-
|
21
|
-
from swift.common.memcached import (MemcacheRing, CONN_TIMEOUT, POOL_TIMEOUT,
|
22
|
-
IO_TIMEOUT, TRY_COUNT)
|
23
|
-
from swift.common.utils import config_true_value
|
16
|
+
from swift.common.memcached import load_memcache
|
17
|
+
from swift.common.utils import get_logger
|
24
18
|
|
25
19
|
|
26
20
|
class MemcacheMiddleware(object):
|
@@ -30,93 +24,8 @@ class MemcacheMiddleware(object):
|
|
30
24
|
|
31
25
|
def __init__(self, app, conf):
|
32
26
|
self.app = app
|
33
|
-
self.
|
34
|
-
|
35
|
-
try:
|
36
|
-
# Originally, while we documented using memcache_max_connections
|
37
|
-
# we only accepted max_connections
|
38
|
-
max_conns = int(conf.get('memcache_max_connections',
|
39
|
-
conf.get('max_connections', 0)))
|
40
|
-
except ValueError:
|
41
|
-
max_conns = 0
|
42
|
-
|
43
|
-
memcache_options = {}
|
44
|
-
if (not self.memcache_servers
|
45
|
-
or serialization_format is None
|
46
|
-
or max_conns <= 0):
|
47
|
-
path = os.path.join(conf.get('swift_dir', '/etc/swift'),
|
48
|
-
'memcache.conf')
|
49
|
-
memcache_conf = ConfigParser()
|
50
|
-
if memcache_conf.read(path):
|
51
|
-
# if memcache.conf exists we'll start with those base options
|
52
|
-
try:
|
53
|
-
memcache_options = dict(memcache_conf.items('memcache'))
|
54
|
-
except NoSectionError:
|
55
|
-
pass
|
56
|
-
|
57
|
-
if not self.memcache_servers:
|
58
|
-
try:
|
59
|
-
self.memcache_servers = \
|
60
|
-
memcache_conf.get('memcache', 'memcache_servers')
|
61
|
-
except (NoSectionError, NoOptionError):
|
62
|
-
pass
|
63
|
-
if serialization_format is None:
|
64
|
-
try:
|
65
|
-
serialization_format = \
|
66
|
-
memcache_conf.get('memcache',
|
67
|
-
'memcache_serialization_support')
|
68
|
-
except (NoSectionError, NoOptionError):
|
69
|
-
pass
|
70
|
-
if max_conns <= 0:
|
71
|
-
try:
|
72
|
-
new_max_conns = \
|
73
|
-
memcache_conf.get('memcache',
|
74
|
-
'memcache_max_connections')
|
75
|
-
max_conns = int(new_max_conns)
|
76
|
-
except (NoSectionError, NoOptionError, ValueError):
|
77
|
-
pass
|
78
|
-
|
79
|
-
# while memcache.conf options are the base for the memcache
|
80
|
-
# middleware, if you set the same option also in the filter
|
81
|
-
# section of the proxy config it is more specific.
|
82
|
-
memcache_options.update(conf)
|
83
|
-
connect_timeout = float(memcache_options.get(
|
84
|
-
'connect_timeout', CONN_TIMEOUT))
|
85
|
-
pool_timeout = float(memcache_options.get(
|
86
|
-
'pool_timeout', POOL_TIMEOUT))
|
87
|
-
tries = int(memcache_options.get('tries', TRY_COUNT))
|
88
|
-
io_timeout = float(memcache_options.get('io_timeout', IO_TIMEOUT))
|
89
|
-
if config_true_value(memcache_options.get('tls_enabled', 'false')):
|
90
|
-
tls_cafile = memcache_options.get('tls_cafile')
|
91
|
-
tls_certfile = memcache_options.get('tls_certfile')
|
92
|
-
tls_keyfile = memcache_options.get('tls_keyfile')
|
93
|
-
self.tls_context = ssl.create_default_context(
|
94
|
-
cafile=tls_cafile)
|
95
|
-
if tls_certfile:
|
96
|
-
self.tls_context.load_cert_chain(tls_certfile,
|
97
|
-
tls_keyfile)
|
98
|
-
else:
|
99
|
-
self.tls_context = None
|
100
|
-
|
101
|
-
if not self.memcache_servers:
|
102
|
-
self.memcache_servers = '127.0.0.1:11211'
|
103
|
-
if max_conns <= 0:
|
104
|
-
max_conns = 2
|
105
|
-
if serialization_format is None:
|
106
|
-
serialization_format = 2
|
107
|
-
else:
|
108
|
-
serialization_format = int(serialization_format)
|
109
|
-
|
110
|
-
self.memcache = MemcacheRing(
|
111
|
-
[s.strip() for s in self.memcache_servers.split(',') if s.strip()],
|
112
|
-
connect_timeout=connect_timeout,
|
113
|
-
pool_timeout=pool_timeout,
|
114
|
-
tries=tries,
|
115
|
-
io_timeout=io_timeout,
|
116
|
-
allow_pickle=(serialization_format == 0),
|
117
|
-
allow_unpickle=(serialization_format <= 1),
|
118
|
-
max_conns=max_conns,
|
119
|
-
tls_context=self.tls_context)
|
27
|
+
self.logger = get_logger(conf, log_route='memcache')
|
28
|
+
self.memcache = load_memcache(conf, self.logger)
|
120
29
|
|
121
30
|
def __call__(self, env, start_response):
|
122
31
|
env['swift.cache'] = self.memcache
|
@@ -41,14 +41,15 @@ The filter returns HTTPBadRequest if path is invalid.
|
|
41
41
|
'''
|
42
42
|
|
43
43
|
import re
|
44
|
-
from swift.common.utils import get_logger
|
44
|
+
from swift.common.utils import get_logger
|
45
|
+
from swift.common.registry import register_swift_info
|
45
46
|
|
46
47
|
from swift.common.swob import Request, HTTPBadRequest
|
47
48
|
|
48
49
|
|
49
50
|
FORBIDDEN_CHARS = "\'\"`<>"
|
50
51
|
MAX_LENGTH = 255
|
51
|
-
FORBIDDEN_REGEXP = "/\./|/\.\./|/\.$|/\.\.$"
|
52
|
+
FORBIDDEN_REGEXP = r"/\./|/\.\./|/\.$|/\.\.$"
|
52
53
|
|
53
54
|
|
54
55
|
class NameCheckMiddleware(object):
|