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/obj/server.py
CHANGED
@@ -15,52 +15,53 @@
|
|
15
15
|
|
16
16
|
""" Object Server for Swift """
|
17
17
|
|
18
|
-
import
|
19
|
-
|
20
|
-
from six.moves.urllib.parse import unquote
|
18
|
+
import pickle # nosec: B403
|
19
|
+
from urllib.parse import unquote
|
21
20
|
import json
|
22
21
|
import os
|
23
22
|
import multiprocessing
|
23
|
+
import sys
|
24
24
|
import time
|
25
25
|
import traceback
|
26
26
|
import socket
|
27
|
-
import math
|
28
|
-
from swift import gettext_ as _
|
29
|
-
from hashlib import md5
|
30
27
|
|
31
28
|
from eventlet import sleep, wsgi, Timeout, tpool
|
32
29
|
from eventlet.greenthread import spawn
|
33
30
|
|
34
31
|
from swift.common.utils import public, get_logger, \
|
35
|
-
config_true_value, timing_stats, replication, \
|
32
|
+
config_true_value, config_percent_value, timing_stats, replication, \
|
36
33
|
normalize_delete_at_timestamp, get_log_line, Timestamp, \
|
37
|
-
|
34
|
+
parse_mime_headers, \
|
38
35
|
iter_multipart_mime_documents, extract_swift_bytes, safe_json_loads, \
|
39
|
-
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
|
40
38
|
from swift.common.bufferedhttp import http_connect
|
41
39
|
from swift.common.constraints import check_object_creation, \
|
42
|
-
valid_timestamp, check_utf8
|
40
|
+
valid_timestamp, check_utf8, AUTO_CREATE_ACCOUNT_PREFIX
|
43
41
|
from swift.common.exceptions import ConnectionTimeout, DiskFileQuarantined, \
|
44
42
|
DiskFileNotExist, DiskFileCollision, DiskFileNoSpace, DiskFileDeleted, \
|
45
43
|
DiskFileDeviceUnavailable, DiskFileExpired, ChunkReadTimeout, \
|
46
44
|
ChunkReadError, DiskFileXattrNotSupported
|
47
|
-
from swift.common.request_helpers import \
|
45
|
+
from swift.common.request_helpers import resolve_ignore_range_header, \
|
48
46
|
OBJECT_SYSMETA_CONTAINER_UPDATE_OVERRIDE_PREFIX
|
49
|
-
from swift.obj import ssync_receiver
|
47
|
+
from swift.obj import ssync_receiver, expirer
|
50
48
|
from swift.common.http import is_success, HTTP_MOVED_PERMANENTLY
|
51
49
|
from swift.common.base_storage_server import BaseStorageServer
|
52
50
|
from swift.common.header_key_dict import HeaderKeyDict
|
53
51
|
from swift.common.request_helpers import get_name_and_placement, \
|
54
52
|
is_user_meta, is_sys_or_user_meta, is_object_transient_sysmeta, \
|
55
|
-
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
|
56
55
|
from swift.common.swob import HTTPAccepted, HTTPBadRequest, HTTPCreated, \
|
57
56
|
HTTPInternalServerError, HTTPNoContent, HTTPNotFound, \
|
58
57
|
HTTPPreconditionFailed, HTTPRequestTimeout, HTTPUnprocessableEntity, \
|
59
58
|
HTTPClientDisconnect, HTTPMethodNotAllowed, Request, Response, \
|
60
59
|
HTTPInsufficientStorage, HTTPForbidden, HTTPException, HTTPConflict, \
|
61
|
-
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
|
62
62
|
from swift.obj.diskfile import RESERVED_DATAFILE_META, DiskFileRouter
|
63
|
-
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
|
64
65
|
|
65
66
|
|
66
67
|
def iter_mime_headers_and_bodies(wsgi_input, mime_boundary, read_chunk_size):
|
@@ -90,6 +91,20 @@ def drain(file_like, read_size, timeout):
|
|
90
91
|
break
|
91
92
|
|
92
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
|
+
|
93
108
|
def _make_backend_fragments_header(fragments):
|
94
109
|
if fragments:
|
95
110
|
result = {}
|
@@ -130,7 +145,7 @@ class ObjectController(BaseStorageServer):
|
|
130
145
|
self.container_update_timeout = float(
|
131
146
|
conf.get('container_update_timeout', 1))
|
132
147
|
self.conn_timeout = float(conf.get('conn_timeout', 0.5))
|
133
|
-
self.client_timeout =
|
148
|
+
self.client_timeout = float(conf.get('client_timeout', 60))
|
134
149
|
self.disk_chunk_size = int(conf.get('disk_chunk_size', 65536))
|
135
150
|
self.network_chunk_size = int(conf.get('network_chunk_size', 65536))
|
136
151
|
self.log_requests = config_true_value(conf.get('log_requests', 'true'))
|
@@ -138,6 +153,11 @@ class ObjectController(BaseStorageServer):
|
|
138
153
|
self.slow = int(conf.get('slow', 0))
|
139
154
|
self.keep_cache_private = \
|
140
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))
|
141
161
|
|
142
162
|
default_allowed_headers = '''
|
143
163
|
content-disposition,
|
@@ -159,12 +179,9 @@ class ObjectController(BaseStorageServer):
|
|
159
179
|
for header in extra_allowed_headers:
|
160
180
|
if header not in RESERVED_DATAFILE_META:
|
161
181
|
self.allowed_headers.add(header)
|
162
|
-
|
163
|
-
|
164
|
-
self.
|
165
|
-
(conf.get('expiring_objects_account_name') or 'expiring_objects')
|
166
|
-
self.expiring_objects_container_divisor = \
|
167
|
-
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)
|
168
185
|
# Initialization was successful, so now apply the network chunk size
|
169
186
|
# parameter as the default read / write buffer size for the network
|
170
187
|
# sockets.
|
@@ -179,8 +196,8 @@ class ObjectController(BaseStorageServer):
|
|
179
196
|
# disk_chunk_size parameter. However, it affects all created sockets
|
180
197
|
# using this class so we have chosen to tie it to the
|
181
198
|
# network_chunk_size parameter value instead.
|
182
|
-
if six.PY2:
|
183
|
-
|
199
|
+
# if six.PY2:
|
200
|
+
# socket._fileobject.default_bufsize = self.network_chunk_size
|
184
201
|
# TODO: find a way to enable similar functionality in py3
|
185
202
|
|
186
203
|
# Provide further setup specific to an object server implementation.
|
@@ -253,7 +270,8 @@ class ObjectController(BaseStorageServer):
|
|
253
270
|
|
254
271
|
def async_update(self, op, account, container, obj, host, partition,
|
255
272
|
contdevice, headers_out, objdevice, policy,
|
256
|
-
logger_thread_locals=None, container_path=None
|
273
|
+
logger_thread_locals=None, container_path=None,
|
274
|
+
db_state=None):
|
257
275
|
"""
|
258
276
|
Sends or saves an async update.
|
259
277
|
|
@@ -275,6 +293,8 @@ class ObjectController(BaseStorageServer):
|
|
275
293
|
to which the update should be sent. If given this path will be used
|
276
294
|
instead of constructing a path from the ``account`` and
|
277
295
|
``container`` params.
|
296
|
+
:param db_state: The current database state of the container as
|
297
|
+
supplied to us by the proxy.
|
278
298
|
"""
|
279
299
|
if logger_thread_locals:
|
280
300
|
self.logger.thread_locals = logger_thread_locals
|
@@ -306,19 +326,19 @@ class ObjectController(BaseStorageServer):
|
|
306
326
|
'Container update failed for %r; problem with '
|
307
327
|
'redirect location: %s' % (obj, err))
|
308
328
|
else:
|
309
|
-
self.logger.error(
|
329
|
+
self.logger.error(
|
310
330
|
'ERROR Container update failed '
|
311
331
|
'(saving for async update later): %(status)d '
|
312
|
-
'response from %(ip)s:%(port)s/%(dev)s'
|
332
|
+
'response from %(ip)s:%(port)s/%(dev)s',
|
313
333
|
{'status': response.status, 'ip': ip, 'port': port,
|
314
334
|
'dev': contdevice})
|
315
335
|
except (Exception, Timeout):
|
316
|
-
self.logger.exception(
|
336
|
+
self.logger.exception(
|
317
337
|
'ERROR container update failed with '
|
318
|
-
'%(ip)s:%(port)s/%(dev)s (saving for async update later)'
|
338
|
+
'%(ip)s:%(port)s/%(dev)s (saving for async update later)',
|
319
339
|
{'ip': ip, 'port': port, 'dev': contdevice})
|
320
340
|
data = {'op': op, 'account': account, 'container': container,
|
321
|
-
'obj': obj, 'headers': headers_out}
|
341
|
+
'obj': obj, 'headers': headers_out, 'db_state': db_state}
|
322
342
|
if redirect_data:
|
323
343
|
self.logger.debug(
|
324
344
|
'Update to %(path)s redirected to %(redirect)s',
|
@@ -352,14 +372,15 @@ class ObjectController(BaseStorageServer):
|
|
352
372
|
contdevices = [d.strip() for d in
|
353
373
|
headers_in.get('X-Container-Device', '').split(',')]
|
354
374
|
contpartition = headers_in.get('X-Container-Partition', '')
|
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
|
@@ -402,7 +423,7 @@ class ObjectController(BaseStorageServer):
|
|
402
423
|
conthost, contpartition, contdevice, headers_out,
|
403
424
|
objdevice, policy,
|
404
425
|
logger_thread_locals=self.logger.thread_locals,
|
405
|
-
container_path=contpath)
|
426
|
+
container_path=contpath, db_state=contdbstate)
|
406
427
|
update_greenthreads.append(gt)
|
407
428
|
# Wait a little bit to see if the container updates are successful.
|
408
429
|
# If we immediately return after firing off the greenthread above, then
|
@@ -421,7 +442,7 @@ class ObjectController(BaseStorageServer):
|
|
421
442
|
self.container_update_timeout, updates)
|
422
443
|
|
423
444
|
def delete_at_update(self, op, delete_at, account, container, obj,
|
424
|
-
request, objdevice, policy):
|
445
|
+
request, objdevice, policy, extra_headers=None):
|
425
446
|
"""
|
426
447
|
Update the expiring objects container when objects are updated.
|
427
448
|
|
@@ -433,15 +454,14 @@ class ObjectController(BaseStorageServer):
|
|
433
454
|
:param request: the original request driving the update
|
434
455
|
:param objdevice: device name that the object is in
|
435
456
|
:param policy: the BaseStoragePolicy instance (used for tmp dir)
|
457
|
+
:param extra_headers: dict of additional headers for the update
|
436
458
|
"""
|
437
459
|
if config_true_value(
|
438
460
|
request.headers.get('x-backend-replication', 'f')):
|
439
461
|
return
|
462
|
+
|
440
463
|
delete_at = normalize_delete_at_timestamp(delete_at)
|
441
|
-
updates = [(None, None)]
|
442
464
|
|
443
|
-
partition = None
|
444
|
-
hosts = contdevices = [None]
|
445
465
|
headers_in = request.headers
|
446
466
|
headers_out = HeaderKeyDict({
|
447
467
|
# system accounts are always Policy-0
|
@@ -449,26 +469,42 @@ class ObjectController(BaseStorageServer):
|
|
449
469
|
'x-timestamp': request.timestamp.internal,
|
450
470
|
'x-trans-id': headers_in.get('x-trans-id', '-'),
|
451
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)
|
452
476
|
if op != 'DELETE':
|
453
477
|
hosts = headers_in.get('X-Delete-At-Host', None)
|
454
478
|
if hosts is None:
|
455
479
|
# If header is missing, no update needed as sufficient other
|
456
480
|
# object servers should perform the required update.
|
457
481
|
return
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
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
|
472
508
|
partition = headers_in.get('X-Delete-At-Partition', None)
|
473
509
|
contdevices = headers_in.get('X-Delete-At-Device', '')
|
474
510
|
updates = [upd for upd in
|
@@ -478,30 +514,22 @@ class ObjectController(BaseStorageServer):
|
|
478
514
|
if not updates:
|
479
515
|
updates = [(None, None)]
|
480
516
|
headers_out['x-size'] = '0'
|
481
|
-
headers_out['x-content-type'] =
|
517
|
+
headers_out['x-content-type'] = X_DELETE_TYPE
|
482
518
|
headers_out['x-etag'] = 'd41d8cd98f00b204e9800998ecf8427e'
|
519
|
+
if extra_headers:
|
520
|
+
headers_out.update(extra_headers)
|
483
521
|
else:
|
484
522
|
if not config_true_value(
|
485
523
|
request.headers.get(
|
486
524
|
'X-Backend-Clean-Expiring-Object-Queue', 't')):
|
487
525
|
return
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
# of the data, so a best guess is made here.
|
492
|
-
# Worst case is a DELETE is issued now for something that doesn't
|
493
|
-
# exist there and the original data is left where it is, where
|
494
|
-
# it will be ignored when the expirer eventually tries to issue the
|
495
|
-
# object DELETE later since the X-Delete-At value won't match up.
|
496
|
-
delete_at_container = get_expirer_container(
|
497
|
-
delete_at, self.expiring_objects_container_divisor,
|
498
|
-
account, container, obj)
|
499
|
-
delete_at_container = normalize_delete_at_timestamp(
|
500
|
-
delete_at_container)
|
526
|
+
# DELETE op always go directly to async_pending
|
527
|
+
partition = None
|
528
|
+
updates = [(None, None)]
|
501
529
|
|
502
530
|
for host, contdevice in updates:
|
503
531
|
self.async_update(
|
504
|
-
op,
|
532
|
+
op, expiring_objects_account_name, delete_at_container,
|
505
533
|
build_task_obj(delete_at, account, container, obj),
|
506
534
|
host, partition, contdevice, headers_out, objdevice,
|
507
535
|
policy)
|
@@ -558,7 +586,7 @@ class ObjectController(BaseStorageServer):
|
|
558
586
|
footer_md5 = footer_hdrs.get('Content-MD5')
|
559
587
|
if not footer_md5:
|
560
588
|
raise HTTPBadRequest(body="no Content-MD5 in footer")
|
561
|
-
if footer_md5 != md5(footer_body).hexdigest():
|
589
|
+
if footer_md5 != md5(footer_body, usedforsecurity=False).hexdigest():
|
562
590
|
raise HTTPUnprocessableEntity(body="footer MD5 mismatch")
|
563
591
|
|
564
592
|
try:
|
@@ -604,12 +632,31 @@ class ObjectController(BaseStorageServer):
|
|
604
632
|
override = key.lower().replace(override_prefix, 'x-')
|
605
633
|
update_headers[override] = val
|
606
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
|
+
|
607
653
|
@public
|
608
654
|
@timing_stats()
|
609
655
|
def POST(self, request):
|
610
656
|
"""Handle HTTP POST requests for the Swift Object Server."""
|
611
657
|
device, partition, account, container, obj, policy = \
|
612
|
-
|
658
|
+
get_obj_name_and_placement(request)
|
659
|
+
|
613
660
|
req_timestamp = valid_timestamp(request)
|
614
661
|
new_delete_at = int(request.headers.get('X-Delete-At') or 0)
|
615
662
|
if new_delete_at and new_delete_at < req_timestamp:
|
@@ -619,8 +666,7 @@ class ObjectController(BaseStorageServer):
|
|
619
666
|
try:
|
620
667
|
disk_file = self.get_diskfile(
|
621
668
|
device, partition, account, container, obj,
|
622
|
-
policy=policy, open_expired=
|
623
|
-
request.headers.get('x-backend-replication', 'false')),
|
669
|
+
policy=policy, open_expired=is_backend_open_expired(request),
|
624
670
|
next_part_power=next_part_power)
|
625
671
|
except DiskFileDeviceUnavailable:
|
626
672
|
return HTTPInsufficientStorage(drive=device, request=request)
|
@@ -655,18 +701,15 @@ class ObjectController(BaseStorageServer):
|
|
655
701
|
list(self.allowed_headers))
|
656
702
|
for header_key in headers_to_copy:
|
657
703
|
if header_key in request.headers:
|
658
|
-
header_caps =
|
704
|
+
header_caps = bytes_to_wsgi(
|
705
|
+
wsgi_to_bytes(header_key).title())
|
659
706
|
metadata[header_caps] = request.headers[header_key]
|
660
707
|
orig_delete_at = int(orig_metadata.get('X-Delete-At') or 0)
|
661
|
-
|
662
|
-
|
663
|
-
|
664
|
-
|
665
|
-
|
666
|
-
if orig_delete_at:
|
667
|
-
self.delete_at_update('DELETE', orig_delete_at, account,
|
668
|
-
container, obj, request, device,
|
669
|
-
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
|
+
)
|
670
713
|
else:
|
671
714
|
# preserve existing metadata, only content-type may be updated
|
672
715
|
metadata = dict(disk_file.get_metafile_metadata())
|
@@ -738,8 +781,9 @@ class ObjectController(BaseStorageServer):
|
|
738
781
|
'PUT', account, container, obj, request, update_headers,
|
739
782
|
device, policy)
|
740
783
|
|
741
|
-
# Add sysmeta to response
|
742
|
-
resp_headers = {
|
784
|
+
# Add current content-type and sysmeta to response
|
785
|
+
resp_headers = {
|
786
|
+
'X-Backend-Content-Type': content_type_headers['Content-Type']}
|
743
787
|
for key, value in orig_metadata.items():
|
744
788
|
if is_sys_meta('object', key):
|
745
789
|
resp_headers[key] = value
|
@@ -866,13 +910,20 @@ class ObjectController(BaseStorageServer):
|
|
866
910
|
elapsed_time = 0
|
867
911
|
upload_expiration = time.time() + self.max_upload_time
|
868
912
|
timeout_reader = self._make_timeout_reader(obj_input)
|
869
|
-
|
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:
|
870
920
|
start_time = time.time()
|
871
921
|
if start_time > upload_expiration:
|
872
922
|
self.logger.increment('PUT.timeouts')
|
873
923
|
raise HTTPRequestTimeout(request=request)
|
874
924
|
writer.write(chunk)
|
875
925
|
elapsed_time += time.time() - start_time
|
926
|
+
|
876
927
|
upload_size, etag = writer.chunks_finished()
|
877
928
|
if fsize is not None and fsize != upload_size:
|
878
929
|
raise HTTPClientDisconnect(request=request)
|
@@ -903,7 +954,8 @@ class ObjectController(BaseStorageServer):
|
|
903
954
|
list(self.allowed_headers))
|
904
955
|
for header_key in headers_to_copy:
|
905
956
|
if header_key in request.headers:
|
906
|
-
header_caps =
|
957
|
+
header_caps = bytes_to_wsgi(
|
958
|
+
wsgi_to_bytes(header_key).title())
|
907
959
|
metadata[header_caps] = request.headers[header_key]
|
908
960
|
return metadata
|
909
961
|
|
@@ -932,8 +984,8 @@ class ObjectController(BaseStorageServer):
|
|
932
984
|
if (is_sys_or_user_meta('object', val[0]) or
|
933
985
|
is_object_transient_sysmeta(val[0])))
|
934
986
|
# N.B. footers_metadata is a HeaderKeyDict
|
935
|
-
received_etag = footers_metadata.get(
|
936
|
-
'etag',
|
987
|
+
received_etag = normalize_etag(footers_metadata.get(
|
988
|
+
'etag', request.headers.get('etag', '')))
|
937
989
|
if received_etag and received_etag != metadata['ETag']:
|
938
990
|
raise HTTPUnprocessableEntity(request=request)
|
939
991
|
|
@@ -974,15 +1026,10 @@ class ObjectController(BaseStorageServer):
|
|
974
1026
|
orig_metadata, footers_metadata, metadata):
|
975
1027
|
orig_delete_at = int(orig_metadata.get('X-Delete-At') or 0)
|
976
1028
|
new_delete_at = int(request.headers.get('X-Delete-At') or 0)
|
977
|
-
|
978
|
-
|
979
|
-
|
980
|
-
|
981
|
-
device, policy)
|
982
|
-
if orig_delete_at:
|
983
|
-
self.delete_at_update(
|
984
|
-
'DELETE', orig_delete_at, account, container, obj,
|
985
|
-
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)
|
986
1033
|
|
987
1034
|
update_headers = HeaderKeyDict({
|
988
1035
|
'x-size': metadata['Content-Length'],
|
@@ -1001,7 +1048,7 @@ class ObjectController(BaseStorageServer):
|
|
1001
1048
|
def PUT(self, request):
|
1002
1049
|
"""Handle HTTP PUT requests for the Swift Object Server."""
|
1003
1050
|
device, partition, account, container, obj, policy = \
|
1004
|
-
|
1051
|
+
get_obj_name_and_placement(request)
|
1005
1052
|
disk_file, fsize, orig_metadata = self._pre_create_checks(
|
1006
1053
|
request, device, partition, account, container, obj, policy)
|
1007
1054
|
writer = disk_file.writer(size=fsize)
|
@@ -1022,7 +1069,9 @@ class ObjectController(BaseStorageServer):
|
|
1022
1069
|
if multi_stage_mime_state:
|
1023
1070
|
self._send_multi_stage_continue_headers(
|
1024
1071
|
request, **multi_stage_mime_state)
|
1025
|
-
|
1072
|
+
if not config_true_value(
|
1073
|
+
request.headers.get('X-Backend-No-Commit', False)):
|
1074
|
+
writer.commit(request.timestamp)
|
1026
1075
|
if multi_stage_mime_state:
|
1027
1076
|
self._drain_mime_request(**multi_stage_mime_state)
|
1028
1077
|
except (DiskFileXattrNotSupported, DiskFileNoSpace):
|
@@ -1043,7 +1092,7 @@ class ObjectController(BaseStorageServer):
|
|
1043
1092
|
def GET(self, request):
|
1044
1093
|
"""Handle HTTP GET requests for the Swift Object Server."""
|
1045
1094
|
device, partition, account, container, obj, policy = \
|
1046
|
-
|
1095
|
+
get_obj_name_and_placement(request)
|
1047
1096
|
request.headers.setdefault('X-Timestamp',
|
1048
1097
|
normalize_timestamp(time.time()))
|
1049
1098
|
req_timestamp = valid_timestamp(request)
|
@@ -1053,23 +1102,39 @@ class ObjectController(BaseStorageServer):
|
|
1053
1102
|
disk_file = self.get_diskfile(
|
1054
1103
|
device, partition, account, container, obj,
|
1055
1104
|
policy=policy, frag_prefs=frag_prefs,
|
1056
|
-
open_expired=
|
1057
|
-
request.headers.get('x-backend-replication', 'false')))
|
1105
|
+
open_expired=is_backend_open_expired(request))
|
1058
1106
|
except DiskFileDeviceUnavailable:
|
1059
1107
|
return HTTPInsufficientStorage(drive=device, request=request)
|
1060
1108
|
try:
|
1061
1109
|
with disk_file.open(current_time=req_timestamp):
|
1062
1110
|
metadata = disk_file.get_metadata()
|
1111
|
+
resolve_ignore_range_header(request, metadata)
|
1063
1112
|
obj_size = int(metadata['Content-Length'])
|
1064
1113
|
file_x_ts = Timestamp(metadata['X-Timestamp'])
|
1065
|
-
keep_cache = (
|
1066
|
-
|
1067
|
-
|
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
|
+
)
|
1068
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
|
+
)
|
1069
1133
|
response = Response(
|
1070
|
-
app_iter=
|
1071
|
-
|
1072
|
-
conditional_etag=conditional_etag
|
1134
|
+
app_iter=app_iter, request=request,
|
1135
|
+
conditional_response=True,
|
1136
|
+
conditional_etag=conditional_etag,
|
1137
|
+
)
|
1073
1138
|
response.headers['Content-Type'] = metadata.get(
|
1074
1139
|
'Content-Type', 'application/octet-stream')
|
1075
1140
|
for key, value in metadata.items():
|
@@ -1078,7 +1143,7 @@ class ObjectController(BaseStorageServer):
|
|
1078
1143
|
key.lower() in self.allowed_headers):
|
1079
1144
|
response.headers[key] = value
|
1080
1145
|
response.etag = metadata['ETag']
|
1081
|
-
response.last_modified =
|
1146
|
+
response.last_modified = file_x_ts.ceil()
|
1082
1147
|
response.content_length = obj_size
|
1083
1148
|
try:
|
1084
1149
|
response.content_encoding = metadata[
|
@@ -1110,7 +1175,7 @@ class ObjectController(BaseStorageServer):
|
|
1110
1175
|
def HEAD(self, request):
|
1111
1176
|
"""Handle HTTP HEAD requests for the Swift Object Server."""
|
1112
1177
|
device, partition, account, container, obj, policy = \
|
1113
|
-
|
1178
|
+
get_obj_name_and_placement(request)
|
1114
1179
|
request.headers.setdefault('X-Timestamp',
|
1115
1180
|
normalize_timestamp(time.time()))
|
1116
1181
|
req_timestamp = valid_timestamp(request)
|
@@ -1120,8 +1185,7 @@ class ObjectController(BaseStorageServer):
|
|
1120
1185
|
disk_file = self.get_diskfile(
|
1121
1186
|
device, partition, account, container, obj,
|
1122
1187
|
policy=policy, frag_prefs=frag_prefs,
|
1123
|
-
open_expired=
|
1124
|
-
request.headers.get('x-backend-replication', 'false')))
|
1188
|
+
open_expired=is_backend_open_expired(request))
|
1125
1189
|
except DiskFileDeviceUnavailable:
|
1126
1190
|
return HTTPInsufficientStorage(drive=device, request=request)
|
1127
1191
|
try:
|
@@ -1146,7 +1210,7 @@ class ObjectController(BaseStorageServer):
|
|
1146
1210
|
response.headers[key] = value
|
1147
1211
|
response.etag = metadata['ETag']
|
1148
1212
|
ts = Timestamp(metadata['X-Timestamp'])
|
1149
|
-
response.last_modified =
|
1213
|
+
response.last_modified = ts.ceil()
|
1150
1214
|
# Needed for container sync feature
|
1151
1215
|
response.headers['X-Timestamp'] = ts.normal
|
1152
1216
|
response.headers['X-Backend-Timestamp'] = ts.internal
|
@@ -1169,7 +1233,7 @@ class ObjectController(BaseStorageServer):
|
|
1169
1233
|
def DELETE(self, request):
|
1170
1234
|
"""Handle HTTP DELETE requests for the Swift Object Server."""
|
1171
1235
|
device, partition, account, container, obj, policy = \
|
1172
|
-
|
1236
|
+
get_obj_name_and_placement(request)
|
1173
1237
|
req_timestamp = valid_timestamp(request)
|
1174
1238
|
next_part_power = request.headers.get('X-Backend-Next-Part-Power')
|
1175
1239
|
try:
|
@@ -1227,10 +1291,10 @@ class ObjectController(BaseStorageServer):
|
|
1227
1291
|
else:
|
1228
1292
|
# differentiate success from no object at all
|
1229
1293
|
response_class = HTTPNoContent
|
1230
|
-
|
1231
|
-
|
1232
|
-
|
1233
|
-
|
1294
|
+
self._conditional_delete_at_update(
|
1295
|
+
request, device, account, container, obj, policy, {},
|
1296
|
+
orig_delete_at, 0
|
1297
|
+
)
|
1234
1298
|
if orig_timestamp < req_timestamp:
|
1235
1299
|
try:
|
1236
1300
|
disk_file.delete(req_timestamp)
|
@@ -1242,7 +1306,9 @@ class ObjectController(BaseStorageServer):
|
|
1242
1306
|
device, policy)
|
1243
1307
|
return response_class(
|
1244
1308
|
request=request,
|
1245
|
-
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', '')})
|
1246
1312
|
|
1247
1313
|
@public
|
1248
1314
|
@replication
|
@@ -1261,7 +1327,8 @@ class ObjectController(BaseStorageServer):
|
|
1261
1327
|
suffixes = suffix_parts.split('-') if suffix_parts else []
|
1262
1328
|
try:
|
1263
1329
|
hashes = self._diskfile_router[policy].get_hashes(
|
1264
|
-
device, partition, suffixes, policy
|
1330
|
+
device, partition, suffixes, policy,
|
1331
|
+
skip_rehash=bool(suffixes))
|
1265
1332
|
except DiskFileDeviceUnavailable:
|
1266
1333
|
resp = HTTPInsufficientStorage(drive=device, request=request)
|
1267
1334
|
else:
|
@@ -1273,7 +1340,14 @@ class ObjectController(BaseStorageServer):
|
|
1273
1340
|
@replication
|
1274
1341
|
@timing_stats(sample_rate=0.1)
|
1275
1342
|
def SSYNC(self, request):
|
1276
|
-
|
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)
|
1277
1351
|
|
1278
1352
|
def __call__(self, env, start_response):
|
1279
1353
|
"""WSGI Application entry point for the Swift Object Server."""
|
@@ -1281,7 +1355,7 @@ class ObjectController(BaseStorageServer):
|
|
1281
1355
|
req = Request(env)
|
1282
1356
|
self.logger.txn_id = req.headers.get('x-trans-id', None)
|
1283
1357
|
|
1284
|
-
if not check_utf8(wsgi_to_str(req.path_info)):
|
1358
|
+
if not check_utf8(wsgi_to_str(req.path_info), internal=True):
|
1285
1359
|
res = HTTPPreconditionFailed(body='Invalid UTF8 or contains NULL')
|
1286
1360
|
else:
|
1287
1361
|
try:
|
@@ -1295,9 +1369,9 @@ class ObjectController(BaseStorageServer):
|
|
1295
1369
|
except HTTPException as error_response:
|
1296
1370
|
res = error_response
|
1297
1371
|
except (Exception, Timeout):
|
1298
|
-
self.logger.exception(
|
1372
|
+
self.logger.exception(
|
1299
1373
|
'ERROR __call__ error with %(method)s'
|
1300
|
-
' %(path)s '
|
1374
|
+
' %(path)s ', {'method': req.method, 'path': req.path})
|
1301
1375
|
res = HTTPInternalServerError(body=traceback.format_exc())
|
1302
1376
|
trans_time = time.time() - start_time
|
1303
1377
|
res.fix_conditional_response()
|
@@ -1396,3 +1470,14 @@ def app_factory(global_conf, **local_conf):
|
|
1396
1470
|
conf = global_conf.copy()
|
1397
1471
|
conf.update(local_conf)
|
1398
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()
|