swift 2.32.0__py2.py3-none-any.whl → 2.34.0__py2.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/account/auditor.py +11 -0
- swift/account/reaper.py +11 -1
- swift/account/replicator.py +22 -0
- swift/account/server.py +13 -12
- swift-2.32.0.data/scripts/swift-account-audit → swift/cli/account_audit.py +6 -2
- swift-2.32.0.data/scripts/swift-config → swift/cli/config.py +1 -1
- swift-2.32.0.data/scripts/swift-dispersion-populate → swift/cli/dispersion_populate.py +6 -2
- swift-2.32.0.data/scripts/swift-drive-audit → swift/cli/drive_audit.py +12 -3
- swift-2.32.0.data/scripts/swift-get-nodes → swift/cli/get_nodes.py +6 -2
- swift/cli/info.py +131 -3
- swift-2.32.0.data/scripts/swift-oldies → swift/cli/oldies.py +6 -3
- swift-2.32.0.data/scripts/swift-orphans → swift/cli/orphans.py +7 -2
- swift-2.32.0.data/scripts/swift-recon-cron → swift/cli/recon_cron.py +9 -18
- swift-2.32.0.data/scripts/swift-reconciler-enqueue → swift/cli/reconciler_enqueue.py +2 -3
- swift/cli/relinker.py +1 -1
- swift/cli/reload.py +141 -0
- swift/cli/ringbuilder.py +24 -0
- swift/common/daemon.py +12 -2
- swift/common/db.py +14 -9
- swift/common/db_auditor.py +2 -2
- swift/common/db_replicator.py +6 -0
- swift/common/exceptions.py +12 -0
- swift/common/http_protocol.py +76 -3
- swift/common/manager.py +120 -5
- swift/common/memcached.py +24 -25
- swift/common/middleware/account_quotas.py +144 -43
- swift/common/middleware/backend_ratelimit.py +166 -24
- swift/common/middleware/catch_errors.py +1 -3
- swift/common/middleware/cname_lookup.py +3 -5
- swift/common/middleware/container_sync.py +6 -10
- swift/common/middleware/crypto/crypto_utils.py +4 -5
- swift/common/middleware/crypto/decrypter.py +4 -5
- swift/common/middleware/crypto/kms_keymaster.py +2 -1
- swift/common/middleware/proxy_logging.py +57 -43
- swift/common/middleware/ratelimit.py +6 -7
- swift/common/middleware/recon.py +6 -7
- swift/common/middleware/s3api/acl_handlers.py +10 -1
- swift/common/middleware/s3api/controllers/__init__.py +3 -0
- swift/common/middleware/s3api/controllers/acl.py +3 -2
- swift/common/middleware/s3api/controllers/logging.py +2 -2
- swift/common/middleware/s3api/controllers/multi_upload.py +31 -15
- swift/common/middleware/s3api/controllers/obj.py +20 -1
- swift/common/middleware/s3api/controllers/object_lock.py +44 -0
- swift/common/middleware/s3api/s3api.py +6 -0
- swift/common/middleware/s3api/s3request.py +190 -74
- swift/common/middleware/s3api/s3response.py +48 -8
- swift/common/middleware/s3api/s3token.py +2 -2
- swift/common/middleware/s3api/utils.py +2 -1
- swift/common/middleware/slo.py +508 -310
- swift/common/middleware/staticweb.py +45 -14
- swift/common/middleware/tempauth.py +6 -4
- swift/common/middleware/tempurl.py +134 -93
- swift/common/middleware/x_profile/exceptions.py +1 -4
- swift/common/middleware/x_profile/html_viewer.py +9 -10
- swift/common/middleware/x_profile/profile_model.py +1 -2
- swift/common/middleware/xprofile.py +1 -2
- swift/common/request_helpers.py +101 -8
- swift/common/statsd_client.py +207 -0
- swift/common/storage_policy.py +1 -1
- swift/common/swob.py +5 -2
- swift/common/utils/__init__.py +331 -1774
- swift/common/utils/base.py +138 -0
- swift/common/utils/config.py +443 -0
- swift/common/utils/logs.py +999 -0
- swift/common/utils/timestamp.py +23 -2
- swift/common/wsgi.py +19 -3
- swift/container/auditor.py +11 -0
- swift/container/backend.py +136 -31
- swift/container/reconciler.py +11 -2
- swift/container/replicator.py +64 -7
- swift/container/server.py +276 -146
- swift/container/sharder.py +86 -42
- swift/container/sync.py +11 -1
- swift/container/updater.py +12 -2
- swift/obj/auditor.py +20 -3
- swift/obj/diskfile.py +63 -25
- swift/obj/expirer.py +154 -47
- swift/obj/mem_diskfile.py +2 -1
- swift/obj/mem_server.py +1 -0
- swift/obj/reconstructor.py +28 -4
- swift/obj/replicator.py +63 -24
- swift/obj/server.py +76 -59
- swift/obj/updater.py +12 -2
- swift/obj/watchers/dark_data.py +72 -34
- swift/proxy/controllers/account.py +3 -2
- swift/proxy/controllers/base.py +254 -148
- swift/proxy/controllers/container.py +274 -289
- swift/proxy/controllers/obj.py +120 -166
- swift/proxy/server.py +17 -13
- {swift-2.32.0.dist-info → swift-2.34.0.dist-info}/AUTHORS +14 -4
- {swift-2.32.0.dist-info → swift-2.34.0.dist-info}/METADATA +9 -7
- {swift-2.32.0.dist-info → swift-2.34.0.dist-info}/RECORD +97 -120
- {swift-2.32.0.dist-info → swift-2.34.0.dist-info}/entry_points.txt +39 -0
- swift-2.34.0.dist-info/pbr.json +1 -0
- swift-2.32.0.data/scripts/swift-account-auditor +0 -23
- swift-2.32.0.data/scripts/swift-account-info +0 -52
- swift-2.32.0.data/scripts/swift-account-reaper +0 -23
- swift-2.32.0.data/scripts/swift-account-replicator +0 -34
- swift-2.32.0.data/scripts/swift-account-server +0 -23
- swift-2.32.0.data/scripts/swift-container-auditor +0 -23
- swift-2.32.0.data/scripts/swift-container-info +0 -56
- swift-2.32.0.data/scripts/swift-container-reconciler +0 -21
- swift-2.32.0.data/scripts/swift-container-replicator +0 -34
- swift-2.32.0.data/scripts/swift-container-server +0 -23
- swift-2.32.0.data/scripts/swift-container-sharder +0 -37
- swift-2.32.0.data/scripts/swift-container-sync +0 -23
- swift-2.32.0.data/scripts/swift-container-updater +0 -23
- swift-2.32.0.data/scripts/swift-dispersion-report +0 -24
- swift-2.32.0.data/scripts/swift-form-signature +0 -20
- swift-2.32.0.data/scripts/swift-init +0 -119
- swift-2.32.0.data/scripts/swift-object-auditor +0 -29
- swift-2.32.0.data/scripts/swift-object-expirer +0 -33
- swift-2.32.0.data/scripts/swift-object-info +0 -60
- swift-2.32.0.data/scripts/swift-object-reconstructor +0 -33
- swift-2.32.0.data/scripts/swift-object-relinker +0 -23
- swift-2.32.0.data/scripts/swift-object-replicator +0 -37
- swift-2.32.0.data/scripts/swift-object-server +0 -27
- swift-2.32.0.data/scripts/swift-object-updater +0 -23
- swift-2.32.0.data/scripts/swift-proxy-server +0 -23
- swift-2.32.0.data/scripts/swift-recon +0 -24
- swift-2.32.0.data/scripts/swift-ring-builder +0 -37
- swift-2.32.0.data/scripts/swift-ring-builder-analyzer +0 -22
- swift-2.32.0.data/scripts/swift-ring-composer +0 -22
- swift-2.32.0.dist-info/pbr.json +0 -1
- {swift-2.32.0.dist-info → swift-2.34.0.dist-info}/LICENSE +0 -0
- {swift-2.32.0.dist-info → swift-2.34.0.dist-info}/WHEEL +0 -0
- {swift-2.32.0.dist-info → swift-2.34.0.dist-info}/top_level.txt +0 -0
@@ -59,7 +59,8 @@ requests for paths not found.
|
|
59
59
|
|
60
60
|
For pseudo paths that have no <index.name>, this middleware can serve HTML file
|
61
61
|
listings if you set the ``X-Container-Meta-Web-Listings: true`` metadata item
|
62
|
-
on the container.
|
62
|
+
on the container. Note that the listing must be authorized; you may want a
|
63
|
+
container ACL like ``X-Container-Read: .r:*,.rlistings``.
|
63
64
|
|
64
65
|
If listings are enabled, the listings can have a custom style sheet by setting
|
65
66
|
the X-Container-Meta-Web-Listings-CSS header. For instance, setting
|
@@ -68,6 +69,17 @@ the .../listing.css style sheet. If you "view source" in your browser on a
|
|
68
69
|
listing page, you will see the well defined document structure that can be
|
69
70
|
styled.
|
70
71
|
|
72
|
+
Additionally, prefix-based :ref:`tempurl` parameters may be used to authorize
|
73
|
+
requests instead of making the whole container publicly readable. This gives
|
74
|
+
clients dynamic discoverability of the objects available within that prefix.
|
75
|
+
|
76
|
+
.. note::
|
77
|
+
|
78
|
+
``temp_url_prefix`` values should typically end with a slash (``/``) when
|
79
|
+
used with StaticWeb. StaticWeb's redirects will not carry over any TempURL
|
80
|
+
parameters, as they likely indicate that the user created an overly-broad
|
81
|
+
TempURL.
|
82
|
+
|
71
83
|
By default, the listings will be rendered with a label of
|
72
84
|
"Listing of /v1/account/container/path". This can be altered by
|
73
85
|
setting a ``X-Container-Meta-Web-Listings-Label: <label>``. For example,
|
@@ -137,6 +149,7 @@ from swift.common.wsgi import make_env, WSGIContext
|
|
137
149
|
from swift.common.http import is_success, is_redirection, HTTP_NOT_FOUND
|
138
150
|
from swift.common.swob import Response, HTTPMovedPermanently, HTTPNotFound, \
|
139
151
|
Request, wsgi_quote, wsgi_to_str, str_to_wsgi
|
152
|
+
from swift.common.middleware.tempurl import get_temp_url_info
|
140
153
|
from swift.proxy.controllers.base import get_container_info
|
141
154
|
|
142
155
|
|
@@ -225,7 +238,7 @@ class _StaticWebContext(WSGIContext):
|
|
225
238
|
self._dir_type = meta.get('web-directory-type', '').strip()
|
226
239
|
return container_info
|
227
240
|
|
228
|
-
def _listing(self, env, start_response, prefix=
|
241
|
+
def _listing(self, env, start_response, prefix=''):
|
229
242
|
"""
|
230
243
|
Sends an HTML object listing to the remote client.
|
231
244
|
|
@@ -240,8 +253,7 @@ class _StaticWebContext(WSGIContext):
|
|
240
253
|
'/'.join(groups[4:]))
|
241
254
|
|
242
255
|
if not config_true_value(self._listings):
|
243
|
-
body = '<!DOCTYPE
|
244
|
-
'Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">\n' \
|
256
|
+
body = '<!DOCTYPE html>\n' \
|
245
257
|
'<html>\n' \
|
246
258
|
'<head>\n' \
|
247
259
|
'<title>Listing of %s</title>\n' % html_escape(label)
|
@@ -285,9 +297,28 @@ class _StaticWebContext(WSGIContext):
|
|
285
297
|
if prefix and not listing:
|
286
298
|
resp = HTTPNotFound()(env, self._start_response)
|
287
299
|
return self._error_response(resp, env, start_response)
|
288
|
-
|
289
|
-
|
290
|
-
|
300
|
+
|
301
|
+
tempurl_qs = tempurl_prefix = ''
|
302
|
+
if env.get('REMOTE_USER') == '.wsgi.tempurl':
|
303
|
+
sig, expires, tempurl_prefix, _filename, inline, ip_range = \
|
304
|
+
get_temp_url_info(env)
|
305
|
+
if tempurl_prefix is None:
|
306
|
+
tempurl_prefix = ''
|
307
|
+
else:
|
308
|
+
parts = [
|
309
|
+
'temp_url_prefix=%s' % quote(tempurl_prefix),
|
310
|
+
'temp_url_expires=%s' % quote(str(expires)),
|
311
|
+
'temp_url_sig=%s' % sig,
|
312
|
+
]
|
313
|
+
if ip_range:
|
314
|
+
parts.append('temp_url_ip_range=%s' % quote(ip_range))
|
315
|
+
if inline:
|
316
|
+
parts.append('inline')
|
317
|
+
tempurl_qs = '?' + '&'.join(parts)
|
318
|
+
|
319
|
+
headers = {'Content-Type': 'text/html; charset=UTF-8',
|
320
|
+
'X-Backend-Content-Generator': 'staticweb'}
|
321
|
+
body = '<!DOCTYPE html>\n' \
|
291
322
|
'<html>\n' \
|
292
323
|
' <head>\n' \
|
293
324
|
' <title>Listing of %s</title>\n' % \
|
@@ -311,12 +342,12 @@ class _StaticWebContext(WSGIContext):
|
|
311
342
|
' <th class="colsize">Size</th>\n' \
|
312
343
|
' <th class="coldate">Date</th>\n' \
|
313
344
|
' </tr>\n' % html_escape(label)
|
314
|
-
if prefix:
|
345
|
+
if len(prefix) > len(tempurl_prefix):
|
315
346
|
body += ' <tr id="parent" class="item">\n' \
|
316
|
-
' <td class="colname"><a href="
|
347
|
+
' <td class="colname"><a href="../%s">../</a></td>\n' \
|
317
348
|
' <td class="colsize"> </td>\n' \
|
318
349
|
' <td class="coldate"> </td>\n' \
|
319
|
-
' </tr>\n'
|
350
|
+
' </tr>\n' % tempurl_qs
|
320
351
|
for item in listing:
|
321
352
|
if 'subdir' in item:
|
322
353
|
subdir = item['subdir'] if six.PY3 else \
|
@@ -328,7 +359,7 @@ class _StaticWebContext(WSGIContext):
|
|
328
359
|
' <td class="colsize"> </td>\n' \
|
329
360
|
' <td class="coldate"> </td>\n' \
|
330
361
|
' </tr>\n' % \
|
331
|
-
(quote(subdir), html_escape(subdir))
|
362
|
+
(quote(subdir) + tempurl_qs, html_escape(subdir))
|
332
363
|
for item in listing:
|
333
364
|
if 'name' in item:
|
334
365
|
name = item['name'] if six.PY3 else \
|
@@ -349,7 +380,7 @@ class _StaticWebContext(WSGIContext):
|
|
349
380
|
' </tr>\n' % \
|
350
381
|
(' '.join('type-' + html_escape(t.lower())
|
351
382
|
for t in content_type.split('/')),
|
352
|
-
quote(name), html_escape(name),
|
383
|
+
quote(name) + tempurl_qs, html_escape(name),
|
353
384
|
bytes, last_modified)
|
354
385
|
body += ' </table>\n' \
|
355
386
|
' </body>\n' \
|
@@ -542,8 +573,8 @@ class StaticWeb(object):
|
|
542
573
|
return self.app(env, start_response)
|
543
574
|
if env['REQUEST_METHOD'] not in ('HEAD', 'GET'):
|
544
575
|
return self.app(env, start_response)
|
545
|
-
if env.get('REMOTE_USER') and \
|
546
|
-
not config_true_value(env.get('HTTP_X_WEB_MODE', 'f')):
|
576
|
+
if env.get('REMOTE_USER') and env['REMOTE_USER'] != '.wsgi.tempurl' \
|
577
|
+
and not config_true_value(env.get('HTTP_X_WEB_MODE', 'f')):
|
547
578
|
return self.app(env, start_response)
|
548
579
|
if not container:
|
549
580
|
return self.app(env, start_response)
|
@@ -184,9 +184,11 @@ import base64
|
|
184
184
|
from eventlet import Timeout
|
185
185
|
import six
|
186
186
|
from swift.common.memcached import MemcacheConnectionError
|
187
|
-
from swift.common.swob import
|
188
|
-
|
189
|
-
|
187
|
+
from swift.common.swob import (
|
188
|
+
Response, Request, wsgi_to_str, str_to_wsgi, wsgi_unquote,
|
189
|
+
HTTPBadRequest, HTTPForbidden, HTTPNotFound,
|
190
|
+
HTTPUnauthorized, HTTPMethodNotAllowed, HTTPServiceUnavailable,
|
191
|
+
)
|
190
192
|
|
191
193
|
from swift.common.request_helpers import get_sys_meta_prefix
|
192
194
|
from swift.common.middleware.acl import (
|
@@ -469,7 +471,7 @@ class TempAuth(object):
|
|
469
471
|
if not s3_auth_details['check_signature'](user['key']):
|
470
472
|
return None
|
471
473
|
env['PATH_INFO'] = env['PATH_INFO'].replace(
|
472
|
-
account_user, account_id, 1)
|
474
|
+
str_to_wsgi(account_user), wsgi_unquote(account_id), 1)
|
473
475
|
groups = self._get_user_groups(account, account_user, account_id)
|
474
476
|
|
475
477
|
return groups
|
@@ -255,7 +255,7 @@ This middleware understands the following configuration settings:
|
|
255
255
|
incoming requests. Names may optionally end with ``*`` to
|
256
256
|
indicate a prefix match. ``incoming_allow_headers`` is a
|
257
257
|
list of exceptions to these removals.
|
258
|
-
Default: ``x-timestamp``
|
258
|
+
Default: ``x-timestamp x-open-expired``
|
259
259
|
|
260
260
|
``incoming_allow_headers``
|
261
261
|
A whitespace-delimited list of the headers allowed as
|
@@ -309,13 +309,15 @@ from six.moves.urllib.parse import urlencode
|
|
309
309
|
|
310
310
|
from swift.proxy.controllers.base import get_account_info, get_container_info
|
311
311
|
from swift.common.header_key_dict import HeaderKeyDict
|
312
|
+
from swift.common.http import is_success
|
312
313
|
from swift.common.digest import get_allowed_digests, \
|
313
314
|
extract_digest_and_algorithm, DEFAULT_ALLOWED_DIGESTS, get_hmac
|
314
315
|
from swift.common.swob import header_to_environ_key, HTTPUnauthorized, \
|
315
316
|
HTTPBadRequest, wsgi_to_str
|
316
317
|
from swift.common.utils import split_path, get_valid_utf8_str, \
|
317
|
-
streq_const_time, quote, get_logger
|
318
|
+
streq_const_time, quote, get_logger, close_if_possible
|
318
319
|
from swift.common.registry import register_swift_info, register_sensitive_param
|
320
|
+
from swift.common.wsgi import WSGIContext
|
319
321
|
|
320
322
|
|
321
323
|
DISALLOWED_INCOMING_HEADERS = 'x-object-manifest x-symlink-target'
|
@@ -324,7 +326,7 @@ DISALLOWED_INCOMING_HEADERS = 'x-object-manifest x-symlink-target'
|
|
324
326
|
#: delimited list of header names and names can optionally end with '*' to
|
325
327
|
#: indicate a prefix match. DEFAULT_INCOMING_ALLOW_HEADERS is a list of
|
326
328
|
#: exceptions to these removals.
|
327
|
-
DEFAULT_INCOMING_REMOVE_HEADERS = 'x-timestamp'
|
329
|
+
DEFAULT_INCOMING_REMOVE_HEADERS = 'x-timestamp x-open-expired'
|
328
330
|
|
329
331
|
#: Default headers as exceptions to DEFAULT_INCOMING_REMOVE_HEADERS. Simply a
|
330
332
|
#: whitespace delimited list of header names and names can optionally end with
|
@@ -364,6 +366,55 @@ def get_tempurl_keys_from_metadata(meta):
|
|
364
366
|
if key.lower() in ('temp-url-key', 'temp-url-key-2')]
|
365
367
|
|
366
368
|
|
369
|
+
def normalize_temp_url_expires(value):
|
370
|
+
"""
|
371
|
+
Returns the normalized expiration value as an int
|
372
|
+
|
373
|
+
If not None, the value is converted to an int if possible or 0
|
374
|
+
if not, and checked for expiration (returns 0 if expired).
|
375
|
+
"""
|
376
|
+
if value is None:
|
377
|
+
return value
|
378
|
+
try:
|
379
|
+
temp_url_expires = int(value)
|
380
|
+
except ValueError:
|
381
|
+
try:
|
382
|
+
temp_url_expires = timegm(strptime(
|
383
|
+
value, EXPIRES_ISO8601_FORMAT))
|
384
|
+
except ValueError:
|
385
|
+
temp_url_expires = 0
|
386
|
+
if temp_url_expires < time():
|
387
|
+
temp_url_expires = 0
|
388
|
+
return temp_url_expires
|
389
|
+
|
390
|
+
|
391
|
+
def get_temp_url_info(env):
|
392
|
+
"""
|
393
|
+
Returns the provided temporary URL parameters (sig, expires, prefix,
|
394
|
+
temp_url_ip_range), if given and syntactically valid.
|
395
|
+
Either sig, expires or prefix could be None if not provided.
|
396
|
+
|
397
|
+
:param env: The WSGI environment for the request.
|
398
|
+
:returns: (sig, expires, prefix, filename, inline,
|
399
|
+
temp_url_ip_range) as described above.
|
400
|
+
"""
|
401
|
+
sig = expires = prefix = ip_range = filename = inline = None
|
402
|
+
qs = parse_qs(env.get('QUERY_STRING', ''), keep_blank_values=True)
|
403
|
+
if 'temp_url_ip_range' in qs:
|
404
|
+
ip_range = qs['temp_url_ip_range'][0]
|
405
|
+
if 'temp_url_sig' in qs:
|
406
|
+
sig = qs['temp_url_sig'][0]
|
407
|
+
if 'temp_url_expires' in qs:
|
408
|
+
expires = qs['temp_url_expires'][0]
|
409
|
+
if 'temp_url_prefix' in qs:
|
410
|
+
prefix = qs['temp_url_prefix'][0]
|
411
|
+
if 'filename' in qs:
|
412
|
+
filename = qs['filename'][0]
|
413
|
+
if 'inline' in qs:
|
414
|
+
inline = True
|
415
|
+
return (sig, expires, prefix, filename, inline, ip_range)
|
416
|
+
|
417
|
+
|
367
418
|
def disposition_format(disposition_type, filename):
|
368
419
|
# Content-Disposition in HTTP is defined in
|
369
420
|
# https://tools.ietf.org/html/rfc6266 and references
|
@@ -495,9 +546,10 @@ class TempURL(object):
|
|
495
546
|
"""
|
496
547
|
if env['REQUEST_METHOD'] == 'OPTIONS':
|
497
548
|
return self.app(env, start_response)
|
498
|
-
info =
|
499
|
-
temp_url_sig,
|
549
|
+
info = get_temp_url_info(env)
|
550
|
+
temp_url_sig, client_temp_url_expires, temp_url_prefix, filename, \
|
500
551
|
inline_disposition, temp_url_ip_range = info
|
552
|
+
temp_url_expires = normalize_temp_url_expires(client_temp_url_expires)
|
501
553
|
if temp_url_sig is None and temp_url_expires is None:
|
502
554
|
return self.app(env, start_response)
|
503
555
|
if not temp_url_sig or not temp_url_expires:
|
@@ -511,7 +563,10 @@ class TempURL(object):
|
|
511
563
|
if hash_algorithm not in self.allowed_digests:
|
512
564
|
return self._invalid(env, start_response)
|
513
565
|
|
514
|
-
account, container, obj = self._get_path_parts(
|
566
|
+
account, container, obj = self._get_path_parts(
|
567
|
+
env, allow_container_root=(
|
568
|
+
env['REQUEST_METHOD'] in ('GET', 'HEAD') and
|
569
|
+
temp_url_prefix == ""))
|
515
570
|
if not account:
|
516
571
|
return self._invalid(env, start_response)
|
517
572
|
|
@@ -577,116 +632,102 @@ class TempURL(object):
|
|
577
632
|
env['swift.authorize_override'] = True
|
578
633
|
env['REMOTE_USER'] = '.wsgi.tempurl'
|
579
634
|
qs = {'temp_url_sig': temp_url_sig,
|
580
|
-
'temp_url_expires':
|
635
|
+
'temp_url_expires': client_temp_url_expires}
|
581
636
|
if temp_url_prefix is not None:
|
582
637
|
qs['temp_url_prefix'] = temp_url_prefix
|
583
638
|
if filename:
|
584
639
|
qs['filename'] = filename
|
585
640
|
env['QUERY_STRING'] = urlencode(qs)
|
586
641
|
|
587
|
-
|
588
|
-
|
589
|
-
|
590
|
-
|
591
|
-
|
592
|
-
|
593
|
-
|
594
|
-
|
595
|
-
|
596
|
-
|
597
|
-
|
598
|
-
|
599
|
-
|
600
|
-
|
601
|
-
if
|
602
|
-
|
603
|
-
|
604
|
-
|
605
|
-
|
606
|
-
|
607
|
-
|
608
|
-
|
642
|
+
ctx = WSGIContext(self.app)
|
643
|
+
app_iter = ctx._app_call(env)
|
644
|
+
ctx._response_headers = self._clean_outgoing_headers(
|
645
|
+
ctx._response_headers)
|
646
|
+
if env['REQUEST_METHOD'] in ('GET', 'HEAD') and \
|
647
|
+
is_success(ctx._get_status_int()):
|
648
|
+
# figure out the right value for content-disposition
|
649
|
+
# 1) use the value from the query string
|
650
|
+
# 2) use the value from the object metadata
|
651
|
+
# 3) use the object name (default)
|
652
|
+
out_headers = []
|
653
|
+
existing_disposition = None
|
654
|
+
content_generator = None
|
655
|
+
for h, v in ctx._response_headers:
|
656
|
+
if h.lower() == 'x-backend-content-generator':
|
657
|
+
content_generator = v
|
658
|
+
|
659
|
+
if h.lower() != 'content-disposition':
|
660
|
+
out_headers.append((h, v))
|
661
|
+
else:
|
662
|
+
existing_disposition = v
|
663
|
+
if content_generator == 'staticweb':
|
664
|
+
inline_disposition = True
|
665
|
+
elif obj == "":
|
666
|
+
# Generally, tempurl requires an object. We carved out an
|
667
|
+
# exception to allow GETs at the container root for the sake
|
668
|
+
# of staticweb, but we can't tell whether we'll have a
|
669
|
+
# staticweb response or not until after we call the app
|
670
|
+
close_if_possible(app_iter)
|
671
|
+
return self._invalid(env, start_response)
|
672
|
+
|
673
|
+
if inline_disposition:
|
674
|
+
if filename:
|
675
|
+
disposition_value = disposition_format('inline',
|
609
676
|
filename)
|
610
|
-
elif existing_disposition:
|
611
|
-
disposition_value = existing_disposition
|
612
677
|
else:
|
613
|
-
|
614
|
-
|
615
|
-
|
616
|
-
|
617
|
-
|
618
|
-
|
619
|
-
|
620
|
-
|
621
|
-
|
622
|
-
|
623
|
-
|
624
|
-
|
625
|
-
|
626
|
-
|
627
|
-
|
628
|
-
|
629
|
-
|
630
|
-
|
678
|
+
disposition_value = 'inline'
|
679
|
+
elif filename:
|
680
|
+
disposition_value = disposition_format('attachment',
|
681
|
+
filename)
|
682
|
+
elif existing_disposition:
|
683
|
+
disposition_value = existing_disposition
|
684
|
+
else:
|
685
|
+
name = basename(wsgi_to_str(env['PATH_INFO']).rstrip('/'))
|
686
|
+
disposition_value = disposition_format('attachment',
|
687
|
+
name)
|
688
|
+
# this is probably just paranoia, I couldn't actually get a
|
689
|
+
# newline into existing_disposition
|
690
|
+
value = disposition_value.replace('\n', '%0A')
|
691
|
+
out_headers.append(('Content-Disposition', value))
|
692
|
+
|
693
|
+
# include Expires header for better cache-control
|
694
|
+
out_headers.append(('Expires', strftime(
|
695
|
+
"%a, %d %b %Y %H:%M:%S GMT",
|
696
|
+
gmtime(temp_url_expires))))
|
697
|
+
ctx._response_headers = out_headers
|
698
|
+
start_response(
|
699
|
+
ctx._response_status,
|
700
|
+
ctx._response_headers,
|
701
|
+
ctx._response_exc_info)
|
702
|
+
return app_iter
|
703
|
+
|
704
|
+
def _get_path_parts(self, env, allow_container_root=False):
|
631
705
|
"""
|
632
706
|
Return the account, container and object name for the request,
|
633
707
|
if it's an object request and one of the configured methods;
|
634
708
|
otherwise, None is returned.
|
635
709
|
|
710
|
+
If it's a container request and allow_root_container is true,
|
711
|
+
the object name returned will be the empty string.
|
712
|
+
|
636
713
|
:param env: The WSGI environment for the request.
|
714
|
+
:param allow_container_root: Whether requests to the root of a
|
715
|
+
container should be allowed.
|
637
716
|
:returns: (Account str, container str, object str) or
|
638
717
|
(None, None, None).
|
639
718
|
"""
|
640
719
|
if env['REQUEST_METHOD'] in self.conf['methods']:
|
641
720
|
try:
|
642
|
-
ver, acc, cont, obj = split_path(
|
721
|
+
ver, acc, cont, obj = split_path(
|
722
|
+
env['PATH_INFO'], 3 if allow_container_root else 4,
|
723
|
+
4, True)
|
643
724
|
except ValueError:
|
644
725
|
return (None, None, None)
|
645
|
-
if ver == 'v1' and obj.strip('/'):
|
646
|
-
return (wsgi_to_str(acc), wsgi_to_str(cont),
|
726
|
+
if ver == 'v1' and (allow_container_root or obj.strip('/')):
|
727
|
+
return (wsgi_to_str(acc), wsgi_to_str(cont),
|
728
|
+
wsgi_to_str(obj) if obj else '')
|
647
729
|
return (None, None, None)
|
648
730
|
|
649
|
-
def _get_temp_url_info(self, env):
|
650
|
-
"""
|
651
|
-
Returns the provided temporary URL parameters (sig, expires, prefix,
|
652
|
-
temp_url_ip_range), if given and syntactically valid.
|
653
|
-
Either sig, expires or prefix could be None if not provided.
|
654
|
-
If provided, expires is also converted to an int if possible or 0
|
655
|
-
if not, and checked for expiration (returns 0 if expired).
|
656
|
-
|
657
|
-
:param env: The WSGI environment for the request.
|
658
|
-
:returns: (sig, expires, prefix, filename, inline,
|
659
|
-
temp_url_ip_range) as described above.
|
660
|
-
"""
|
661
|
-
temp_url_sig = temp_url_expires = temp_url_prefix = filename =\
|
662
|
-
inline = None
|
663
|
-
temp_url_ip_range = None
|
664
|
-
qs = parse_qs(env.get('QUERY_STRING', ''), keep_blank_values=True)
|
665
|
-
if 'temp_url_ip_range' in qs:
|
666
|
-
temp_url_ip_range = qs['temp_url_ip_range'][0]
|
667
|
-
if 'temp_url_sig' in qs:
|
668
|
-
temp_url_sig = qs['temp_url_sig'][0]
|
669
|
-
if 'temp_url_expires' in qs:
|
670
|
-
try:
|
671
|
-
temp_url_expires = int(qs['temp_url_expires'][0])
|
672
|
-
except ValueError:
|
673
|
-
try:
|
674
|
-
temp_url_expires = timegm(strptime(
|
675
|
-
qs['temp_url_expires'][0],
|
676
|
-
EXPIRES_ISO8601_FORMAT))
|
677
|
-
except ValueError:
|
678
|
-
temp_url_expires = 0
|
679
|
-
if temp_url_expires < time():
|
680
|
-
temp_url_expires = 0
|
681
|
-
if 'temp_url_prefix' in qs:
|
682
|
-
temp_url_prefix = qs['temp_url_prefix'][0]
|
683
|
-
if 'filename' in qs:
|
684
|
-
filename = qs['filename'][0]
|
685
|
-
if 'inline' in qs:
|
686
|
-
inline = True
|
687
|
-
return (temp_url_sig, temp_url_expires, temp_url_prefix, filename,
|
688
|
-
inline, temp_url_ip_range)
|
689
|
-
|
690
731
|
def _get_keys(self, env):
|
691
732
|
"""
|
692
733
|
Returns the X-[Account|Container]-Meta-Temp-URL-Key[-2] header values
|
@@ -13,16 +13,13 @@
|
|
13
13
|
# See the License for the specific language governing permissions and
|
14
14
|
# limitations under the License.
|
15
15
|
|
16
|
-
from swift import gettext_ as _
|
17
|
-
|
18
|
-
|
19
16
|
class ProfileException(Exception):
|
20
17
|
|
21
18
|
def __init__(self, msg):
|
22
19
|
self.msg = msg
|
23
20
|
|
24
21
|
def __str__(self):
|
25
|
-
return
|
22
|
+
return 'Profiling Error: %s' % self.msg
|
26
23
|
|
27
24
|
|
28
25
|
class NotFoundException(ProfileException):
|
@@ -19,7 +19,6 @@ import re
|
|
19
19
|
import string
|
20
20
|
import tempfile
|
21
21
|
|
22
|
-
from swift import gettext_ as _
|
23
22
|
from swift.common.middleware.x_profile.exceptions import PLOTLIBNotInstalled
|
24
23
|
from swift.common.middleware.x_profile.exceptions import ODFLIBNotInstalled
|
25
24
|
from swift.common.middleware.x_profile.exceptions import NotFoundException
|
@@ -307,7 +306,7 @@ class HTMLViewer(object):
|
|
307
306
|
nfl_filter, download_format)
|
308
307
|
headers.append(('Access-Control-Allow-Origin', '*'))
|
309
308
|
else:
|
310
|
-
raise MethodNotAllowed(
|
309
|
+
raise MethodNotAllowed('method %s is not allowed.' % method)
|
311
310
|
return content, headers
|
312
311
|
|
313
312
|
def index_page(self, log_files=None, sort='time', limit=-1,
|
@@ -318,7 +317,7 @@ class HTMLViewer(object):
|
|
318
317
|
try:
|
319
318
|
stats = Stats2(*log_files)
|
320
319
|
except (IOError, ValueError):
|
321
|
-
raise DataLoadFailure(
|
320
|
+
raise DataLoadFailure('Can not load profile data from %s.'
|
322
321
|
% log_files)
|
323
322
|
if not fulldirs:
|
324
323
|
stats.strip_dirs()
|
@@ -369,7 +368,7 @@ class HTMLViewer(object):
|
|
369
368
|
def download(self, log_files, sort='time', limit=-1, nfl_filter='',
|
370
369
|
output_format='default'):
|
371
370
|
if len(log_files) == 0:
|
372
|
-
raise NotFoundException(
|
371
|
+
raise NotFoundException('no log file found')
|
373
372
|
try:
|
374
373
|
nfl_esc = nfl_filter.replace(r'(', r'\(').replace(r')', r'\)')
|
375
374
|
# remove the slash that is intentionally added in the URL
|
@@ -392,14 +391,14 @@ class HTMLViewer(object):
|
|
392
391
|
except ODFLIBNotInstalled:
|
393
392
|
raise
|
394
393
|
except Exception as ex:
|
395
|
-
raise ProfileException(
|
394
|
+
raise ProfileException('Data download error: %s' % ex)
|
396
395
|
|
397
396
|
def plot(self, log_files, sort='time', limit=10, nfl_filter='',
|
398
397
|
metric_selected='cc', plot_type='bar'):
|
399
398
|
if not PLOTLIB_INSTALLED:
|
400
|
-
raise PLOTLIBNotInstalled(
|
399
|
+
raise PLOTLIBNotInstalled('python-matplotlib not installed.')
|
401
400
|
if len(log_files) == 0:
|
402
|
-
raise NotFoundException(
|
401
|
+
raise NotFoundException('no log file found')
|
403
402
|
try:
|
404
403
|
stats = Stats2(*log_files)
|
405
404
|
stats.sort_stats(sort)
|
@@ -433,7 +432,7 @@ class HTMLViewer(object):
|
|
433
432
|
data = profile_img.read()
|
434
433
|
return data, [('content-type', 'image/jpg')]
|
435
434
|
except Exception as ex:
|
436
|
-
raise ProfileException(
|
435
|
+
raise ProfileException('plotting results failed due to %s' % ex)
|
437
436
|
|
438
437
|
def format_source_code(self, nfl):
|
439
438
|
nfls = re.split('[:()]', nfl)
|
@@ -444,7 +443,7 @@ class HTMLViewer(object):
|
|
444
443
|
lineno = 0
|
445
444
|
# for security reason, this need to be fixed.
|
446
445
|
if not file_path.endswith('.py'):
|
447
|
-
return
|
446
|
+
return 'The file type are forbidden to access!'
|
448
447
|
try:
|
449
448
|
data = []
|
450
449
|
i = 0
|
@@ -465,7 +464,7 @@ class HTMLViewer(object):
|
|
465
464
|
data.append(fmt % (i, i, i, el))
|
466
465
|
data = ''.join(data)
|
467
466
|
except Exception:
|
468
|
-
return
|
467
|
+
return 'Can not access the file %s.' % file_path
|
469
468
|
return '<pre>%s</pre>' % data
|
470
469
|
|
471
470
|
def generate_stats_html(self, stats, app_path, profile_id, *selection):
|
@@ -20,7 +20,6 @@ import pstats
|
|
20
20
|
import tempfile
|
21
21
|
import time
|
22
22
|
|
23
|
-
from swift import gettext_ as _
|
24
23
|
from swift.common.middleware.x_profile.exceptions import ODFLIBNotInstalled
|
25
24
|
|
26
25
|
|
@@ -125,7 +124,7 @@ class Stats2(pstats.Stats):
|
|
125
124
|
|
126
125
|
def to_ods(self, *selection):
|
127
126
|
if not ODFLIB_INSTALLED:
|
128
|
-
raise ODFLIBNotInstalled(
|
127
|
+
raise ODFLIBNotInstalled('odfpy not installed.')
|
129
128
|
if self.fcn_list:
|
130
129
|
stat_list = self.fcn_list[:]
|
131
130
|
order_text = " Ordered by: " + self.sort_type + '\n'
|
@@ -83,7 +83,6 @@ import eventlet.green.profile as eprofile
|
|
83
83
|
import six
|
84
84
|
from six.moves import urllib
|
85
85
|
|
86
|
-
from swift import gettext_ as _
|
87
86
|
from swift.common.utils import get_logger, config_true_value
|
88
87
|
from swift.common.swob import Request
|
89
88
|
from swift.common.middleware.x_profile.exceptions import MethodNotAllowed
|
@@ -227,7 +226,7 @@ class ProfileMiddleware(object):
|
|
227
226
|
return '%s' % pf
|
228
227
|
except Exception as ex:
|
229
228
|
start_response('500 Internal Server Error', [])
|
230
|
-
return
|
229
|
+
return 'Error on render profiling results: %s' % ex
|
231
230
|
else:
|
232
231
|
_locals = locals()
|
233
232
|
code = self.unwind and PROFILE_EXEC_EAGER or\
|