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.
- 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.2.data/scripts/swift-account-audit → swift/cli/account_audit.py +23 -13
- swift-2.23.2.data/scripts/swift-config → swift/cli/config.py +2 -2
- swift/cli/container_deleter.py +5 -11
- swift-2.23.2.data/scripts/swift-dispersion-populate → swift/cli/dispersion_populate.py +8 -7
- swift/cli/dispersion_report.py +10 -9
- swift-2.23.2.data/scripts/swift-drive-audit → swift/cli/drive_audit.py +63 -21
- swift/cli/form_signature.py +3 -7
- swift-2.23.2.data/scripts/swift-get-nodes → swift/cli/get_nodes.py +8 -2
- swift/cli/info.py +183 -29
- swift/cli/manage_shard_ranges.py +708 -37
- swift-2.23.2.data/scripts/swift-oldies → swift/cli/oldies.py +25 -14
- swift-2.23.2.data/scripts/swift-orphans → swift/cli/orphans.py +7 -3
- swift/cli/recon.py +196 -67
- swift-2.23.2.data/scripts/swift-recon-cron → swift/cli/recon_cron.py +17 -20
- swift-2.23.2.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 +198 -127
- 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 +396 -147
- 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 -81
- 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 +52 -19
- swift/common/middleware/tempauth.py +76 -58
- swift/common/middleware/tempurl.py +192 -174
- 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.2.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} +2191 -2762
- 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 +555 -536
- swift/container/auditor.py +14 -100
- swift/container/backend.py +552 -227
- swift/container/reconciler.py +126 -37
- swift/container/replicator.py +96 -22
- swift/container/server.py +397 -176
- swift/container/sharder.py +1580 -639
- 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 +213 -122
- swift/obj/ssync_receiver.py +145 -85
- swift/obj/ssync_sender.py +113 -54
- swift/obj/updater.py +653 -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 +452 -86
- swift/proxy/controllers/info.py +3 -2
- swift/proxy/controllers/obj.py +1009 -490
- swift/proxy/server.py +185 -112
- swift-2.35.0.dist-info/AUTHORS +501 -0
- swift-2.35.0.dist-info/LICENSE +202 -0
- {swift-2.23.2.dist-info → swift-2.35.0.dist-info}/METADATA +52 -61
- swift-2.35.0.dist-info/RECORD +201 -0
- {swift-2.23.2.dist-info → swift-2.35.0.dist-info}/WHEEL +1 -1
- {swift-2.23.2.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.2.data/scripts/swift-account-auditor +0 -23
- swift-2.23.2.data/scripts/swift-account-info +0 -51
- swift-2.23.2.data/scripts/swift-account-reaper +0 -23
- swift-2.23.2.data/scripts/swift-account-replicator +0 -34
- swift-2.23.2.data/scripts/swift-account-server +0 -23
- swift-2.23.2.data/scripts/swift-container-auditor +0 -23
- swift-2.23.2.data/scripts/swift-container-info +0 -51
- swift-2.23.2.data/scripts/swift-container-reconciler +0 -21
- swift-2.23.2.data/scripts/swift-container-replicator +0 -34
- swift-2.23.2.data/scripts/swift-container-sharder +0 -33
- swift-2.23.2.data/scripts/swift-container-sync +0 -23
- swift-2.23.2.data/scripts/swift-container-updater +0 -23
- swift-2.23.2.data/scripts/swift-dispersion-report +0 -24
- swift-2.23.2.data/scripts/swift-form-signature +0 -20
- swift-2.23.2.data/scripts/swift-init +0 -119
- swift-2.23.2.data/scripts/swift-object-auditor +0 -29
- swift-2.23.2.data/scripts/swift-object-expirer +0 -33
- swift-2.23.2.data/scripts/swift-object-info +0 -60
- swift-2.23.2.data/scripts/swift-object-reconstructor +0 -33
- swift-2.23.2.data/scripts/swift-object-relinker +0 -41
- swift-2.23.2.data/scripts/swift-object-replicator +0 -37
- swift-2.23.2.data/scripts/swift-object-server +0 -27
- swift-2.23.2.data/scripts/swift-object-updater +0 -23
- swift-2.23.2.data/scripts/swift-proxy-server +0 -23
- swift-2.23.2.data/scripts/swift-recon +0 -24
- swift-2.23.2.data/scripts/swift-ring-builder +0 -24
- swift-2.23.2.data/scripts/swift-ring-builder-analyzer +0 -22
- swift-2.23.2.data/scripts/swift-ring-composer +0 -22
- swift-2.23.2.dist-info/DESCRIPTION.rst +0 -166
- swift-2.23.2.dist-info/RECORD +0 -220
- swift-2.23.2.dist-info/metadata.json +0 -1
- swift-2.23.2.dist-info/pbr.json +0 -1
- {swift-2.23.2.dist-info → swift-2.35.0.dist-info}/top_level.txt +0 -0
swift/obj/server.py
CHANGED
@@ -15,51 +15,53 @@
|
|
15
15
|
|
16
16
|
""" Object Server for Swift """
|
17
17
|
|
18
|
-
import
|
19
|
-
|
18
|
+
import pickle # nosec: B403
|
19
|
+
from urllib.parse import unquote
|
20
20
|
import json
|
21
21
|
import os
|
22
22
|
import multiprocessing
|
23
|
+
import sys
|
23
24
|
import time
|
24
25
|
import traceback
|
25
26
|
import socket
|
26
|
-
import math
|
27
|
-
from swift import gettext_ as _
|
28
|
-
from hashlib import md5
|
29
27
|
|
30
28
|
from eventlet import sleep, wsgi, Timeout, tpool
|
31
29
|
from eventlet.greenthread import spawn
|
32
30
|
|
33
31
|
from swift.common.utils import public, get_logger, \
|
34
|
-
config_true_value, timing_stats, replication, \
|
32
|
+
config_true_value, config_percent_value, timing_stats, replication, \
|
35
33
|
normalize_delete_at_timestamp, get_log_line, Timestamp, \
|
36
|
-
|
34
|
+
parse_mime_headers, \
|
37
35
|
iter_multipart_mime_documents, extract_swift_bytes, safe_json_loads, \
|
38
|
-
config_auto_int_value, split_path, get_redirect_data,
|
36
|
+
config_auto_int_value, split_path, get_redirect_data, \
|
37
|
+
normalize_timestamp, md5, parse_options, CooperativeIterator
|
39
38
|
from swift.common.bufferedhttp import http_connect
|
40
39
|
from swift.common.constraints import check_object_creation, \
|
41
|
-
valid_timestamp, check_utf8
|
40
|
+
valid_timestamp, check_utf8, AUTO_CREATE_ACCOUNT_PREFIX
|
42
41
|
from swift.common.exceptions import ConnectionTimeout, DiskFileQuarantined, \
|
43
42
|
DiskFileNotExist, DiskFileCollision, DiskFileNoSpace, DiskFileDeleted, \
|
44
43
|
DiskFileDeviceUnavailable, DiskFileExpired, ChunkReadTimeout, \
|
45
44
|
ChunkReadError, DiskFileXattrNotSupported
|
46
|
-
from swift.common.request_helpers import \
|
45
|
+
from swift.common.request_helpers import resolve_ignore_range_header, \
|
47
46
|
OBJECT_SYSMETA_CONTAINER_UPDATE_OVERRIDE_PREFIX
|
48
|
-
from swift.obj import ssync_receiver
|
47
|
+
from swift.obj import ssync_receiver, expirer
|
49
48
|
from swift.common.http import is_success, HTTP_MOVED_PERMANENTLY
|
50
49
|
from swift.common.base_storage_server import BaseStorageServer
|
51
50
|
from swift.common.header_key_dict import HeaderKeyDict
|
52
51
|
from swift.common.request_helpers import get_name_and_placement, \
|
53
52
|
is_user_meta, is_sys_or_user_meta, is_object_transient_sysmeta, \
|
54
|
-
resolve_etag_is_at_header, is_sys_meta
|
53
|
+
resolve_etag_is_at_header, is_sys_meta, validate_internal_obj, \
|
54
|
+
is_backend_open_expired
|
55
55
|
from swift.common.swob import HTTPAccepted, HTTPBadRequest, HTTPCreated, \
|
56
56
|
HTTPInternalServerError, HTTPNoContent, HTTPNotFound, \
|
57
57
|
HTTPPreconditionFailed, HTTPRequestTimeout, HTTPUnprocessableEntity, \
|
58
58
|
HTTPClientDisconnect, HTTPMethodNotAllowed, Request, Response, \
|
59
59
|
HTTPInsufficientStorage, HTTPForbidden, HTTPException, HTTPConflict, \
|
60
|
-
HTTPServerError, wsgi_to_bytes, wsgi_to_str
|
60
|
+
HTTPServerError, bytes_to_wsgi, wsgi_to_bytes, wsgi_to_str, normalize_etag
|
61
|
+
from swift.common.wsgi import run_wsgi
|
61
62
|
from swift.obj.diskfile import RESERVED_DATAFILE_META, DiskFileRouter
|
62
|
-
from swift.obj.expirer import build_task_obj
|
63
|
+
from swift.obj.expirer import build_task_obj, embed_expirer_bytes_in_ctype, \
|
64
|
+
X_DELETE_TYPE
|
63
65
|
|
64
66
|
|
65
67
|
def iter_mime_headers_and_bodies(wsgi_input, mime_boundary, read_chunk_size):
|
@@ -89,6 +91,20 @@ def drain(file_like, read_size, timeout):
|
|
89
91
|
break
|
90
92
|
|
91
93
|
|
94
|
+
def get_obj_name_and_placement(request):
|
95
|
+
"""
|
96
|
+
Split and validate path for an object.
|
97
|
+
|
98
|
+
:param request: a swob request
|
99
|
+
|
100
|
+
:returns: a tuple of path parts and storage policy
|
101
|
+
"""
|
102
|
+
device, partition, account, container, obj, policy = \
|
103
|
+
get_name_and_placement(request, 5, 5, True)
|
104
|
+
validate_internal_obj(account, container, obj)
|
105
|
+
return device, partition, account, container, obj, policy
|
106
|
+
|
107
|
+
|
92
108
|
def _make_backend_fragments_header(fragments):
|
93
109
|
if fragments:
|
94
110
|
result = {}
|
@@ -129,7 +145,7 @@ class ObjectController(BaseStorageServer):
|
|
129
145
|
self.container_update_timeout = float(
|
130
146
|
conf.get('container_update_timeout', 1))
|
131
147
|
self.conn_timeout = float(conf.get('conn_timeout', 0.5))
|
132
|
-
self.client_timeout =
|
148
|
+
self.client_timeout = float(conf.get('client_timeout', 60))
|
133
149
|
self.disk_chunk_size = int(conf.get('disk_chunk_size', 65536))
|
134
150
|
self.network_chunk_size = int(conf.get('network_chunk_size', 65536))
|
135
151
|
self.log_requests = config_true_value(conf.get('log_requests', 'true'))
|
@@ -137,6 +153,11 @@ class ObjectController(BaseStorageServer):
|
|
137
153
|
self.slow = int(conf.get('slow', 0))
|
138
154
|
self.keep_cache_private = \
|
139
155
|
config_true_value(conf.get('keep_cache_private', 'false'))
|
156
|
+
self.keep_cache_slo_manifest = \
|
157
|
+
config_true_value(conf.get('keep_cache_slo_manifest', 'false'))
|
158
|
+
self.cooperative_period = int(conf.get("cooperative_period", 0))
|
159
|
+
self.etag_validate_frac = config_percent_value(
|
160
|
+
conf.get("etag_validate_pct", 100))
|
140
161
|
|
141
162
|
default_allowed_headers = '''
|
142
163
|
content-disposition,
|
@@ -158,12 +179,9 @@ class ObjectController(BaseStorageServer):
|
|
158
179
|
for header in extra_allowed_headers:
|
159
180
|
if header not in RESERVED_DATAFILE_META:
|
160
181
|
self.allowed_headers.add(header)
|
161
|
-
|
162
|
-
|
163
|
-
self.
|
164
|
-
(conf.get('expiring_objects_account_name') or 'expiring_objects')
|
165
|
-
self.expiring_objects_container_divisor = \
|
166
|
-
int(conf.get('expiring_objects_container_divisor') or 86400)
|
182
|
+
|
183
|
+
self.auto_create_account_prefix = AUTO_CREATE_ACCOUNT_PREFIX
|
184
|
+
self.expirer_config = expirer.ExpirerConfig(conf, logger=self.logger)
|
167
185
|
# Initialization was successful, so now apply the network chunk size
|
168
186
|
# parameter as the default read / write buffer size for the network
|
169
187
|
# sockets.
|
@@ -178,8 +196,8 @@ class ObjectController(BaseStorageServer):
|
|
178
196
|
# disk_chunk_size parameter. However, it affects all created sockets
|
179
197
|
# using this class so we have chosen to tie it to the
|
180
198
|
# network_chunk_size parameter value instead.
|
181
|
-
if six.PY2:
|
182
|
-
|
199
|
+
# if six.PY2:
|
200
|
+
# socket._fileobject.default_bufsize = self.network_chunk_size
|
183
201
|
# TODO: find a way to enable similar functionality in py3
|
184
202
|
|
185
203
|
# Provide further setup specific to an object server implementation.
|
@@ -252,7 +270,8 @@ class ObjectController(BaseStorageServer):
|
|
252
270
|
|
253
271
|
def async_update(self, op, account, container, obj, host, partition,
|
254
272
|
contdevice, headers_out, objdevice, policy,
|
255
|
-
logger_thread_locals=None, container_path=None
|
273
|
+
logger_thread_locals=None, container_path=None,
|
274
|
+
db_state=None):
|
256
275
|
"""
|
257
276
|
Sends or saves an async update.
|
258
277
|
|
@@ -274,6 +293,8 @@ class ObjectController(BaseStorageServer):
|
|
274
293
|
to which the update should be sent. If given this path will be used
|
275
294
|
instead of constructing a path from the ``account`` and
|
276
295
|
``container`` params.
|
296
|
+
:param db_state: The current database state of the container as
|
297
|
+
supplied to us by the proxy.
|
277
298
|
"""
|
278
299
|
if logger_thread_locals:
|
279
300
|
self.logger.thread_locals = logger_thread_locals
|
@@ -305,19 +326,19 @@ class ObjectController(BaseStorageServer):
|
|
305
326
|
'Container update failed for %r; problem with '
|
306
327
|
'redirect location: %s' % (obj, err))
|
307
328
|
else:
|
308
|
-
self.logger.error(
|
329
|
+
self.logger.error(
|
309
330
|
'ERROR Container update failed '
|
310
331
|
'(saving for async update later): %(status)d '
|
311
|
-
'response from %(ip)s:%(port)s/%(dev)s'
|
332
|
+
'response from %(ip)s:%(port)s/%(dev)s',
|
312
333
|
{'status': response.status, 'ip': ip, 'port': port,
|
313
334
|
'dev': contdevice})
|
314
335
|
except (Exception, Timeout):
|
315
|
-
self.logger.exception(
|
336
|
+
self.logger.exception(
|
316
337
|
'ERROR container update failed with '
|
317
|
-
'%(ip)s:%(port)s/%(dev)s (saving for async update later)'
|
338
|
+
'%(ip)s:%(port)s/%(dev)s (saving for async update later)',
|
318
339
|
{'ip': ip, 'port': port, 'dev': contdevice})
|
319
340
|
data = {'op': op, 'account': account, 'container': container,
|
320
|
-
'obj': obj, 'headers': headers_out}
|
341
|
+
'obj': obj, 'headers': headers_out, 'db_state': db_state}
|
321
342
|
if redirect_data:
|
322
343
|
self.logger.debug(
|
323
344
|
'Update to %(path)s redirected to %(redirect)s',
|
@@ -351,19 +372,25 @@ class ObjectController(BaseStorageServer):
|
|
351
372
|
contdevices = [d.strip() for d in
|
352
373
|
headers_in.get('X-Container-Device', '').split(',')]
|
353
374
|
contpartition = headers_in.get('X-Container-Partition', '')
|
354
|
-
|
375
|
+
contdbstate = headers_in.get('X-Container-Root-Db-State')
|
355
376
|
|
356
377
|
if len(conthosts) != len(contdevices):
|
357
378
|
# This shouldn't happen unless there's a bug in the proxy,
|
358
379
|
# but if there is, we want to know about it.
|
359
|
-
self.logger.error(
|
380
|
+
self.logger.error(
|
360
381
|
'ERROR Container update failed: different '
|
361
382
|
'numbers of hosts and devices in request: '
|
362
|
-
'"%(hosts)s" vs "%(devices)s"'
|
383
|
+
'"%(hosts)s" vs "%(devices)s"', {
|
363
384
|
'hosts': headers_in.get('X-Container-Host', ''),
|
364
385
|
'devices': headers_in.get('X-Container-Device', '')})
|
365
386
|
return
|
366
387
|
|
388
|
+
contpath = headers_in.get('X-Backend-Quoted-Container-Path')
|
389
|
+
if contpath:
|
390
|
+
contpath = unquote(contpath)
|
391
|
+
else:
|
392
|
+
contpath = headers_in.get('X-Backend-Container-Path')
|
393
|
+
|
367
394
|
if contpath:
|
368
395
|
try:
|
369
396
|
# TODO: this is very late in request handling to be validating
|
@@ -396,7 +423,7 @@ class ObjectController(BaseStorageServer):
|
|
396
423
|
conthost, contpartition, contdevice, headers_out,
|
397
424
|
objdevice, policy,
|
398
425
|
logger_thread_locals=self.logger.thread_locals,
|
399
|
-
container_path=contpath)
|
426
|
+
container_path=contpath, db_state=contdbstate)
|
400
427
|
update_greenthreads.append(gt)
|
401
428
|
# Wait a little bit to see if the container updates are successful.
|
402
429
|
# If we immediately return after firing off the greenthread above, then
|
@@ -415,7 +442,7 @@ class ObjectController(BaseStorageServer):
|
|
415
442
|
self.container_update_timeout, updates)
|
416
443
|
|
417
444
|
def delete_at_update(self, op, delete_at, account, container, obj,
|
418
|
-
request, objdevice, policy):
|
445
|
+
request, objdevice, policy, extra_headers=None):
|
419
446
|
"""
|
420
447
|
Update the expiring objects container when objects are updated.
|
421
448
|
|
@@ -427,15 +454,14 @@ class ObjectController(BaseStorageServer):
|
|
427
454
|
:param request: the original request driving the update
|
428
455
|
:param objdevice: device name that the object is in
|
429
456
|
:param policy: the BaseStoragePolicy instance (used for tmp dir)
|
457
|
+
:param extra_headers: dict of additional headers for the update
|
430
458
|
"""
|
431
459
|
if config_true_value(
|
432
460
|
request.headers.get('x-backend-replication', 'f')):
|
433
461
|
return
|
462
|
+
|
434
463
|
delete_at = normalize_delete_at_timestamp(delete_at)
|
435
|
-
updates = [(None, None)]
|
436
464
|
|
437
|
-
partition = None
|
438
|
-
hosts = contdevices = [None]
|
439
465
|
headers_in = request.headers
|
440
466
|
headers_out = HeaderKeyDict({
|
441
467
|
# system accounts are always Policy-0
|
@@ -443,26 +469,42 @@ class ObjectController(BaseStorageServer):
|
|
443
469
|
'x-timestamp': request.timestamp.internal,
|
444
470
|
'x-trans-id': headers_in.get('x-trans-id', '-'),
|
445
471
|
'referer': request.as_referer()})
|
472
|
+
|
473
|
+
expiring_objects_account_name, delete_at_container = \
|
474
|
+
self.expirer_config.get_expirer_account_and_container(
|
475
|
+
delete_at, account, container, obj)
|
446
476
|
if op != 'DELETE':
|
447
477
|
hosts = headers_in.get('X-Delete-At-Host', None)
|
448
478
|
if hosts is None:
|
449
479
|
# If header is missing, no update needed as sufficient other
|
450
480
|
# object servers should perform the required update.
|
451
481
|
return
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
|
464
|
-
|
465
|
-
|
482
|
+
|
483
|
+
proxy_delete_at_container = headers_in.get(
|
484
|
+
'X-Delete-At-Container', None)
|
485
|
+
if delete_at_container != proxy_delete_at_container:
|
486
|
+
if not proxy_delete_at_container:
|
487
|
+
# We carry this warning around for pre-2013 proxies
|
488
|
+
self.logger.warning(
|
489
|
+
'X-Delete-At-Container header must be specified for '
|
490
|
+
'expiring objects background %s to work properly. '
|
491
|
+
'Making best guess as to the container name '
|
492
|
+
'for now.', op)
|
493
|
+
proxy_delete_at_container = delete_at_container
|
494
|
+
else:
|
495
|
+
# Inconsistent configuration may lead to orphaned expirer
|
496
|
+
# task queue objects when X-Delete-At is updated, which can
|
497
|
+
# stick around for a whole reclaim age.
|
498
|
+
self.logger.debug(
|
499
|
+
'Proxy X-Delete-At-Container %r does not match '
|
500
|
+
'expected %r for current expirer_config.',
|
501
|
+
proxy_delete_at_container, delete_at_container)
|
502
|
+
# it's not possible to say which is "more correct", this will
|
503
|
+
# at least match the host/part/device
|
504
|
+
delete_at_container = normalize_delete_at_timestamp(
|
505
|
+
proxy_delete_at_container)
|
506
|
+
|
507
|
+
# new updates need to enqueue new x-delete-at
|
466
508
|
partition = headers_in.get('X-Delete-At-Partition', None)
|
467
509
|
contdevices = headers_in.get('X-Delete-At-Device', '')
|
468
510
|
updates = [upd for upd in
|
@@ -472,30 +514,22 @@ class ObjectController(BaseStorageServer):
|
|
472
514
|
if not updates:
|
473
515
|
updates = [(None, None)]
|
474
516
|
headers_out['x-size'] = '0'
|
475
|
-
headers_out['x-content-type'] =
|
517
|
+
headers_out['x-content-type'] = X_DELETE_TYPE
|
476
518
|
headers_out['x-etag'] = 'd41d8cd98f00b204e9800998ecf8427e'
|
519
|
+
if extra_headers:
|
520
|
+
headers_out.update(extra_headers)
|
477
521
|
else:
|
478
522
|
if not config_true_value(
|
479
523
|
request.headers.get(
|
480
524
|
'X-Backend-Clean-Expiring-Object-Queue', 't')):
|
481
525
|
return
|
482
|
-
|
483
|
-
|
484
|
-
|
485
|
-
# of the data, so a best guess is made here.
|
486
|
-
# Worst case is a DELETE is issued now for something that doesn't
|
487
|
-
# exist there and the original data is left where it is, where
|
488
|
-
# it will be ignored when the expirer eventually tries to issue the
|
489
|
-
# object DELETE later since the X-Delete-At value won't match up.
|
490
|
-
delete_at_container = get_expirer_container(
|
491
|
-
delete_at, self.expiring_objects_container_divisor,
|
492
|
-
account, container, obj)
|
493
|
-
delete_at_container = normalize_delete_at_timestamp(
|
494
|
-
delete_at_container)
|
526
|
+
# DELETE op always go directly to async_pending
|
527
|
+
partition = None
|
528
|
+
updates = [(None, None)]
|
495
529
|
|
496
530
|
for host, contdevice in updates:
|
497
531
|
self.async_update(
|
498
|
-
op,
|
532
|
+
op, expiring_objects_account_name, delete_at_container,
|
499
533
|
build_task_obj(delete_at, account, container, obj),
|
500
534
|
host, partition, contdevice, headers_out, objdevice,
|
501
535
|
policy)
|
@@ -552,7 +586,7 @@ class ObjectController(BaseStorageServer):
|
|
552
586
|
footer_md5 = footer_hdrs.get('Content-MD5')
|
553
587
|
if not footer_md5:
|
554
588
|
raise HTTPBadRequest(body="no Content-MD5 in footer")
|
555
|
-
if footer_md5 != md5(footer_body).hexdigest():
|
589
|
+
if footer_md5 != md5(footer_body, usedforsecurity=False).hexdigest():
|
556
590
|
raise HTTPUnprocessableEntity(body="footer MD5 mismatch")
|
557
591
|
|
558
592
|
try:
|
@@ -598,12 +632,31 @@ class ObjectController(BaseStorageServer):
|
|
598
632
|
override = key.lower().replace(override_prefix, 'x-')
|
599
633
|
update_headers[override] = val
|
600
634
|
|
635
|
+
def _conditional_delete_at_update(self, request, device, account,
|
636
|
+
container, obj, policy, metadata,
|
637
|
+
orig_delete_at, new_delete_at):
|
638
|
+
if new_delete_at:
|
639
|
+
extra_headers = {
|
640
|
+
'x-content-type': embed_expirer_bytes_in_ctype(
|
641
|
+
X_DELETE_TYPE, metadata),
|
642
|
+
'x-content-type-timestamp':
|
643
|
+
metadata.get('X-Timestamp'),
|
644
|
+
}
|
645
|
+
self.delete_at_update(
|
646
|
+
'PUT', new_delete_at, account, container, obj, request,
|
647
|
+
device, policy, extra_headers)
|
648
|
+
if orig_delete_at and orig_delete_at != new_delete_at:
|
649
|
+
self.delete_at_update(
|
650
|
+
'DELETE', orig_delete_at, account, container, obj,
|
651
|
+
request, device, policy)
|
652
|
+
|
601
653
|
@public
|
602
654
|
@timing_stats()
|
603
655
|
def POST(self, request):
|
604
656
|
"""Handle HTTP POST requests for the Swift Object Server."""
|
605
657
|
device, partition, account, container, obj, policy = \
|
606
|
-
|
658
|
+
get_obj_name_and_placement(request)
|
659
|
+
|
607
660
|
req_timestamp = valid_timestamp(request)
|
608
661
|
new_delete_at = int(request.headers.get('X-Delete-At') or 0)
|
609
662
|
if new_delete_at and new_delete_at < req_timestamp:
|
@@ -613,8 +666,7 @@ class ObjectController(BaseStorageServer):
|
|
613
666
|
try:
|
614
667
|
disk_file = self.get_diskfile(
|
615
668
|
device, partition, account, container, obj,
|
616
|
-
policy=policy, open_expired=
|
617
|
-
request.headers.get('x-backend-replication', 'false')),
|
669
|
+
policy=policy, open_expired=is_backend_open_expired(request),
|
618
670
|
next_part_power=next_part_power)
|
619
671
|
except DiskFileDeviceUnavailable:
|
620
672
|
return HTTPInsufficientStorage(drive=device, request=request)
|
@@ -649,18 +701,15 @@ class ObjectController(BaseStorageServer):
|
|
649
701
|
list(self.allowed_headers))
|
650
702
|
for header_key in headers_to_copy:
|
651
703
|
if header_key in request.headers:
|
652
|
-
header_caps =
|
704
|
+
header_caps = bytes_to_wsgi(
|
705
|
+
wsgi_to_bytes(header_key).title())
|
653
706
|
metadata[header_caps] = request.headers[header_key]
|
654
707
|
orig_delete_at = int(orig_metadata.get('X-Delete-At') or 0)
|
655
|
-
|
656
|
-
|
657
|
-
|
658
|
-
|
659
|
-
|
660
|
-
if orig_delete_at:
|
661
|
-
self.delete_at_update('DELETE', orig_delete_at, account,
|
662
|
-
container, obj, request, device,
|
663
|
-
policy)
|
708
|
+
disk_file_metadata = disk_file.get_datafile_metadata()
|
709
|
+
self._conditional_delete_at_update(
|
710
|
+
request, device, account, container, obj, policy,
|
711
|
+
disk_file_metadata, orig_delete_at, new_delete_at
|
712
|
+
)
|
664
713
|
else:
|
665
714
|
# preserve existing metadata, only content-type may be updated
|
666
715
|
metadata = dict(disk_file.get_metafile_metadata())
|
@@ -732,8 +781,9 @@ class ObjectController(BaseStorageServer):
|
|
732
781
|
'PUT', account, container, obj, request, update_headers,
|
733
782
|
device, policy)
|
734
783
|
|
735
|
-
# Add sysmeta to response
|
736
|
-
resp_headers = {
|
784
|
+
# Add current content-type and sysmeta to response
|
785
|
+
resp_headers = {
|
786
|
+
'X-Backend-Content-Type': content_type_headers['Content-Type']}
|
737
787
|
for key, value in orig_metadata.items():
|
738
788
|
if is_sys_meta('object', key):
|
739
789
|
resp_headers[key] = value
|
@@ -860,13 +910,20 @@ class ObjectController(BaseStorageServer):
|
|
860
910
|
elapsed_time = 0
|
861
911
|
upload_expiration = time.time() + self.max_upload_time
|
862
912
|
timeout_reader = self._make_timeout_reader(obj_input)
|
863
|
-
|
913
|
+
|
914
|
+
# Wrap the chunks in CooperativeIterator with specified period
|
915
|
+
cooperative_reader = CooperativeIterator(
|
916
|
+
iter(timeout_reader, b''), period=self.cooperative_period
|
917
|
+
)
|
918
|
+
|
919
|
+
for chunk in cooperative_reader:
|
864
920
|
start_time = time.time()
|
865
921
|
if start_time > upload_expiration:
|
866
922
|
self.logger.increment('PUT.timeouts')
|
867
923
|
raise HTTPRequestTimeout(request=request)
|
868
924
|
writer.write(chunk)
|
869
925
|
elapsed_time += time.time() - start_time
|
926
|
+
|
870
927
|
upload_size, etag = writer.chunks_finished()
|
871
928
|
if fsize is not None and fsize != upload_size:
|
872
929
|
raise HTTPClientDisconnect(request=request)
|
@@ -897,7 +954,8 @@ class ObjectController(BaseStorageServer):
|
|
897
954
|
list(self.allowed_headers))
|
898
955
|
for header_key in headers_to_copy:
|
899
956
|
if header_key in request.headers:
|
900
|
-
header_caps =
|
957
|
+
header_caps = bytes_to_wsgi(
|
958
|
+
wsgi_to_bytes(header_key).title())
|
901
959
|
metadata[header_caps] = request.headers[header_key]
|
902
960
|
return metadata
|
903
961
|
|
@@ -926,8 +984,8 @@ class ObjectController(BaseStorageServer):
|
|
926
984
|
if (is_sys_or_user_meta('object', val[0]) or
|
927
985
|
is_object_transient_sysmeta(val[0])))
|
928
986
|
# N.B. footers_metadata is a HeaderKeyDict
|
929
|
-
received_etag = footers_metadata.get(
|
930
|
-
'etag',
|
987
|
+
received_etag = normalize_etag(footers_metadata.get(
|
988
|
+
'etag', request.headers.get('etag', '')))
|
931
989
|
if received_etag and received_etag != metadata['ETag']:
|
932
990
|
raise HTTPUnprocessableEntity(request=request)
|
933
991
|
|
@@ -968,15 +1026,10 @@ class ObjectController(BaseStorageServer):
|
|
968
1026
|
orig_metadata, footers_metadata, metadata):
|
969
1027
|
orig_delete_at = int(orig_metadata.get('X-Delete-At') or 0)
|
970
1028
|
new_delete_at = int(request.headers.get('X-Delete-At') or 0)
|
971
|
-
|
972
|
-
|
973
|
-
|
974
|
-
|
975
|
-
device, policy)
|
976
|
-
if orig_delete_at:
|
977
|
-
self.delete_at_update(
|
978
|
-
'DELETE', orig_delete_at, account, container, obj,
|
979
|
-
request, device, policy)
|
1029
|
+
|
1030
|
+
self._conditional_delete_at_update(request, device, account, container,
|
1031
|
+
obj, policy, metadata,
|
1032
|
+
orig_delete_at, new_delete_at)
|
980
1033
|
|
981
1034
|
update_headers = HeaderKeyDict({
|
982
1035
|
'x-size': metadata['Content-Length'],
|
@@ -995,7 +1048,7 @@ class ObjectController(BaseStorageServer):
|
|
995
1048
|
def PUT(self, request):
|
996
1049
|
"""Handle HTTP PUT requests for the Swift Object Server."""
|
997
1050
|
device, partition, account, container, obj, policy = \
|
998
|
-
|
1051
|
+
get_obj_name_and_placement(request)
|
999
1052
|
disk_file, fsize, orig_metadata = self._pre_create_checks(
|
1000
1053
|
request, device, partition, account, container, obj, policy)
|
1001
1054
|
writer = disk_file.writer(size=fsize)
|
@@ -1016,7 +1069,9 @@ class ObjectController(BaseStorageServer):
|
|
1016
1069
|
if multi_stage_mime_state:
|
1017
1070
|
self._send_multi_stage_continue_headers(
|
1018
1071
|
request, **multi_stage_mime_state)
|
1019
|
-
|
1072
|
+
if not config_true_value(
|
1073
|
+
request.headers.get('X-Backend-No-Commit', False)):
|
1074
|
+
writer.commit(request.timestamp)
|
1020
1075
|
if multi_stage_mime_state:
|
1021
1076
|
self._drain_mime_request(**multi_stage_mime_state)
|
1022
1077
|
except (DiskFileXattrNotSupported, DiskFileNoSpace):
|
@@ -1037,7 +1092,7 @@ class ObjectController(BaseStorageServer):
|
|
1037
1092
|
def GET(self, request):
|
1038
1093
|
"""Handle HTTP GET requests for the Swift Object Server."""
|
1039
1094
|
device, partition, account, container, obj, policy = \
|
1040
|
-
|
1095
|
+
get_obj_name_and_placement(request)
|
1041
1096
|
request.headers.setdefault('X-Timestamp',
|
1042
1097
|
normalize_timestamp(time.time()))
|
1043
1098
|
req_timestamp = valid_timestamp(request)
|
@@ -1047,23 +1102,39 @@ class ObjectController(BaseStorageServer):
|
|
1047
1102
|
disk_file = self.get_diskfile(
|
1048
1103
|
device, partition, account, container, obj,
|
1049
1104
|
policy=policy, frag_prefs=frag_prefs,
|
1050
|
-
open_expired=
|
1051
|
-
request.headers.get('x-backend-replication', 'false')))
|
1105
|
+
open_expired=is_backend_open_expired(request))
|
1052
1106
|
except DiskFileDeviceUnavailable:
|
1053
1107
|
return HTTPInsufficientStorage(drive=device, request=request)
|
1054
1108
|
try:
|
1055
1109
|
with disk_file.open(current_time=req_timestamp):
|
1056
1110
|
metadata = disk_file.get_metadata()
|
1111
|
+
resolve_ignore_range_header(request, metadata)
|
1057
1112
|
obj_size = int(metadata['Content-Length'])
|
1058
1113
|
file_x_ts = Timestamp(metadata['X-Timestamp'])
|
1059
|
-
keep_cache = (
|
1060
|
-
|
1061
|
-
|
1114
|
+
keep_cache = (
|
1115
|
+
self.keep_cache_private
|
1116
|
+
or (
|
1117
|
+
"X-Auth-Token" not in request.headers
|
1118
|
+
and "X-Storage-Token" not in request.headers
|
1119
|
+
)
|
1120
|
+
or (
|
1121
|
+
self.keep_cache_slo_manifest
|
1122
|
+
and config_true_value(
|
1123
|
+
metadata.get("X-Static-Large-Object")
|
1124
|
+
)
|
1125
|
+
)
|
1126
|
+
)
|
1062
1127
|
conditional_etag = resolve_etag_is_at_header(request, metadata)
|
1128
|
+
app_iter = disk_file.reader(
|
1129
|
+
keep_cache=keep_cache,
|
1130
|
+
cooperative_period=self.cooperative_period,
|
1131
|
+
etag_validate_frac=self.etag_validate_frac,
|
1132
|
+
)
|
1063
1133
|
response = Response(
|
1064
|
-
app_iter=
|
1065
|
-
|
1066
|
-
conditional_etag=conditional_etag
|
1134
|
+
app_iter=app_iter, request=request,
|
1135
|
+
conditional_response=True,
|
1136
|
+
conditional_etag=conditional_etag,
|
1137
|
+
)
|
1067
1138
|
response.headers['Content-Type'] = metadata.get(
|
1068
1139
|
'Content-Type', 'application/octet-stream')
|
1069
1140
|
for key, value in metadata.items():
|
@@ -1072,7 +1143,7 @@ class ObjectController(BaseStorageServer):
|
|
1072
1143
|
key.lower() in self.allowed_headers):
|
1073
1144
|
response.headers[key] = value
|
1074
1145
|
response.etag = metadata['ETag']
|
1075
|
-
response.last_modified =
|
1146
|
+
response.last_modified = file_x_ts.ceil()
|
1076
1147
|
response.content_length = obj_size
|
1077
1148
|
try:
|
1078
1149
|
response.content_encoding = metadata[
|
@@ -1104,7 +1175,7 @@ class ObjectController(BaseStorageServer):
|
|
1104
1175
|
def HEAD(self, request):
|
1105
1176
|
"""Handle HTTP HEAD requests for the Swift Object Server."""
|
1106
1177
|
device, partition, account, container, obj, policy = \
|
1107
|
-
|
1178
|
+
get_obj_name_and_placement(request)
|
1108
1179
|
request.headers.setdefault('X-Timestamp',
|
1109
1180
|
normalize_timestamp(time.time()))
|
1110
1181
|
req_timestamp = valid_timestamp(request)
|
@@ -1114,8 +1185,7 @@ class ObjectController(BaseStorageServer):
|
|
1114
1185
|
disk_file = self.get_diskfile(
|
1115
1186
|
device, partition, account, container, obj,
|
1116
1187
|
policy=policy, frag_prefs=frag_prefs,
|
1117
|
-
open_expired=
|
1118
|
-
request.headers.get('x-backend-replication', 'false')))
|
1188
|
+
open_expired=is_backend_open_expired(request))
|
1119
1189
|
except DiskFileDeviceUnavailable:
|
1120
1190
|
return HTTPInsufficientStorage(drive=device, request=request)
|
1121
1191
|
try:
|
@@ -1140,7 +1210,7 @@ class ObjectController(BaseStorageServer):
|
|
1140
1210
|
response.headers[key] = value
|
1141
1211
|
response.etag = metadata['ETag']
|
1142
1212
|
ts = Timestamp(metadata['X-Timestamp'])
|
1143
|
-
response.last_modified =
|
1213
|
+
response.last_modified = ts.ceil()
|
1144
1214
|
# Needed for container sync feature
|
1145
1215
|
response.headers['X-Timestamp'] = ts.normal
|
1146
1216
|
response.headers['X-Backend-Timestamp'] = ts.internal
|
@@ -1163,7 +1233,7 @@ class ObjectController(BaseStorageServer):
|
|
1163
1233
|
def DELETE(self, request):
|
1164
1234
|
"""Handle HTTP DELETE requests for the Swift Object Server."""
|
1165
1235
|
device, partition, account, container, obj, policy = \
|
1166
|
-
|
1236
|
+
get_obj_name_and_placement(request)
|
1167
1237
|
req_timestamp = valid_timestamp(request)
|
1168
1238
|
next_part_power = request.headers.get('X-Backend-Next-Part-Power')
|
1169
1239
|
try:
|
@@ -1221,10 +1291,10 @@ class ObjectController(BaseStorageServer):
|
|
1221
1291
|
else:
|
1222
1292
|
# differentiate success from no object at all
|
1223
1293
|
response_class = HTTPNoContent
|
1224
|
-
|
1225
|
-
|
1226
|
-
|
1227
|
-
|
1294
|
+
self._conditional_delete_at_update(
|
1295
|
+
request, device, account, container, obj, policy, {},
|
1296
|
+
orig_delete_at, 0
|
1297
|
+
)
|
1228
1298
|
if orig_timestamp < req_timestamp:
|
1229
1299
|
try:
|
1230
1300
|
disk_file.delete(req_timestamp)
|
@@ -1236,7 +1306,9 @@ class ObjectController(BaseStorageServer):
|
|
1236
1306
|
device, policy)
|
1237
1307
|
return response_class(
|
1238
1308
|
request=request,
|
1239
|
-
headers={'X-Backend-Timestamp': response_timestamp.internal
|
1309
|
+
headers={'X-Backend-Timestamp': response_timestamp.internal,
|
1310
|
+
'X-Backend-Content-Type': orig_metadata.get(
|
1311
|
+
'Content-Type', '')})
|
1240
1312
|
|
1241
1313
|
@public
|
1242
1314
|
@replication
|
@@ -1255,7 +1327,8 @@ class ObjectController(BaseStorageServer):
|
|
1255
1327
|
suffixes = suffix_parts.split('-') if suffix_parts else []
|
1256
1328
|
try:
|
1257
1329
|
hashes = self._diskfile_router[policy].get_hashes(
|
1258
|
-
device, partition, suffixes, policy
|
1330
|
+
device, partition, suffixes, policy,
|
1331
|
+
skip_rehash=bool(suffixes))
|
1259
1332
|
except DiskFileDeviceUnavailable:
|
1260
1333
|
resp = HTTPInsufficientStorage(drive=device, request=request)
|
1261
1334
|
else:
|
@@ -1267,7 +1340,14 @@ class ObjectController(BaseStorageServer):
|
|
1267
1340
|
@replication
|
1268
1341
|
@timing_stats(sample_rate=0.1)
|
1269
1342
|
def SSYNC(self, request):
|
1270
|
-
|
1343
|
+
# the ssync sender may want to send PUT subrequests for non-durable
|
1344
|
+
# data that should not be committed; legacy behaviour has been to
|
1345
|
+
# commit all PUTs (subject to EC footer metadata), so we need to
|
1346
|
+
# indicate to the sender that this object server has been upgraded to
|
1347
|
+
# understand the X-Backend-No-Commit header.
|
1348
|
+
headers = {'X-Backend-Accept-No-Commit': True}
|
1349
|
+
return Response(app_iter=ssync_receiver.Receiver(self, request)(),
|
1350
|
+
headers=headers)
|
1271
1351
|
|
1272
1352
|
def __call__(self, env, start_response):
|
1273
1353
|
"""WSGI Application entry point for the Swift Object Server."""
|
@@ -1275,7 +1355,7 @@ class ObjectController(BaseStorageServer):
|
|
1275
1355
|
req = Request(env)
|
1276
1356
|
self.logger.txn_id = req.headers.get('x-trans-id', None)
|
1277
1357
|
|
1278
|
-
if not check_utf8(wsgi_to_str(req.path_info)):
|
1358
|
+
if not check_utf8(wsgi_to_str(req.path_info), internal=True):
|
1279
1359
|
res = HTTPPreconditionFailed(body='Invalid UTF8 or contains NULL')
|
1280
1360
|
else:
|
1281
1361
|
try:
|
@@ -1289,9 +1369,9 @@ class ObjectController(BaseStorageServer):
|
|
1289
1369
|
except HTTPException as error_response:
|
1290
1370
|
res = error_response
|
1291
1371
|
except (Exception, Timeout):
|
1292
|
-
self.logger.exception(
|
1372
|
+
self.logger.exception(
|
1293
1373
|
'ERROR __call__ error with %(method)s'
|
1294
|
-
' %(path)s '
|
1374
|
+
' %(path)s ', {'method': req.method, 'path': req.path})
|
1295
1375
|
res = HTTPInternalServerError(body=traceback.format_exc())
|
1296
1376
|
trans_time = time.time() - start_time
|
1297
1377
|
res.fix_conditional_response()
|
@@ -1390,3 +1470,14 @@ def app_factory(global_conf, **local_conf):
|
|
1390
1470
|
conf = global_conf.copy()
|
1391
1471
|
conf.update(local_conf)
|
1392
1472
|
return ObjectController(conf)
|
1473
|
+
|
1474
|
+
|
1475
|
+
def main():
|
1476
|
+
conf_file, options = parse_options(test_config=True)
|
1477
|
+
sys.exit(run_wsgi(conf_file, 'object-server',
|
1478
|
+
global_conf_callback=global_conf_callback,
|
1479
|
+
**options))
|
1480
|
+
|
1481
|
+
|
1482
|
+
if __name__ == '__main__':
|
1483
|
+
main()
|