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.
Files changed (127) hide show
  1. swift/account/auditor.py +11 -0
  2. swift/account/reaper.py +11 -1
  3. swift/account/replicator.py +22 -0
  4. swift/account/server.py +13 -12
  5. swift-2.32.0.data/scripts/swift-account-audit → swift/cli/account_audit.py +6 -2
  6. swift-2.32.0.data/scripts/swift-config → swift/cli/config.py +1 -1
  7. swift-2.32.0.data/scripts/swift-dispersion-populate → swift/cli/dispersion_populate.py +6 -2
  8. swift-2.32.0.data/scripts/swift-drive-audit → swift/cli/drive_audit.py +12 -3
  9. swift-2.32.0.data/scripts/swift-get-nodes → swift/cli/get_nodes.py +6 -2
  10. swift/cli/info.py +131 -3
  11. swift-2.32.0.data/scripts/swift-oldies → swift/cli/oldies.py +6 -3
  12. swift-2.32.0.data/scripts/swift-orphans → swift/cli/orphans.py +7 -2
  13. swift-2.32.0.data/scripts/swift-recon-cron → swift/cli/recon_cron.py +9 -18
  14. swift-2.32.0.data/scripts/swift-reconciler-enqueue → swift/cli/reconciler_enqueue.py +2 -3
  15. swift/cli/relinker.py +1 -1
  16. swift/cli/reload.py +141 -0
  17. swift/cli/ringbuilder.py +24 -0
  18. swift/common/daemon.py +12 -2
  19. swift/common/db.py +14 -9
  20. swift/common/db_auditor.py +2 -2
  21. swift/common/db_replicator.py +6 -0
  22. swift/common/exceptions.py +12 -0
  23. swift/common/http_protocol.py +76 -3
  24. swift/common/manager.py +120 -5
  25. swift/common/memcached.py +24 -25
  26. swift/common/middleware/account_quotas.py +144 -43
  27. swift/common/middleware/backend_ratelimit.py +166 -24
  28. swift/common/middleware/catch_errors.py +1 -3
  29. swift/common/middleware/cname_lookup.py +3 -5
  30. swift/common/middleware/container_sync.py +6 -10
  31. swift/common/middleware/crypto/crypto_utils.py +4 -5
  32. swift/common/middleware/crypto/decrypter.py +4 -5
  33. swift/common/middleware/crypto/kms_keymaster.py +2 -1
  34. swift/common/middleware/proxy_logging.py +57 -43
  35. swift/common/middleware/ratelimit.py +6 -7
  36. swift/common/middleware/recon.py +6 -7
  37. swift/common/middleware/s3api/acl_handlers.py +10 -1
  38. swift/common/middleware/s3api/controllers/__init__.py +3 -0
  39. swift/common/middleware/s3api/controllers/acl.py +3 -2
  40. swift/common/middleware/s3api/controllers/logging.py +2 -2
  41. swift/common/middleware/s3api/controllers/multi_upload.py +31 -15
  42. swift/common/middleware/s3api/controllers/obj.py +20 -1
  43. swift/common/middleware/s3api/controllers/object_lock.py +44 -0
  44. swift/common/middleware/s3api/s3api.py +6 -0
  45. swift/common/middleware/s3api/s3request.py +190 -74
  46. swift/common/middleware/s3api/s3response.py +48 -8
  47. swift/common/middleware/s3api/s3token.py +2 -2
  48. swift/common/middleware/s3api/utils.py +2 -1
  49. swift/common/middleware/slo.py +508 -310
  50. swift/common/middleware/staticweb.py +45 -14
  51. swift/common/middleware/tempauth.py +6 -4
  52. swift/common/middleware/tempurl.py +134 -93
  53. swift/common/middleware/x_profile/exceptions.py +1 -4
  54. swift/common/middleware/x_profile/html_viewer.py +9 -10
  55. swift/common/middleware/x_profile/profile_model.py +1 -2
  56. swift/common/middleware/xprofile.py +1 -2
  57. swift/common/request_helpers.py +101 -8
  58. swift/common/statsd_client.py +207 -0
  59. swift/common/storage_policy.py +1 -1
  60. swift/common/swob.py +5 -2
  61. swift/common/utils/__init__.py +331 -1774
  62. swift/common/utils/base.py +138 -0
  63. swift/common/utils/config.py +443 -0
  64. swift/common/utils/logs.py +999 -0
  65. swift/common/utils/timestamp.py +23 -2
  66. swift/common/wsgi.py +19 -3
  67. swift/container/auditor.py +11 -0
  68. swift/container/backend.py +136 -31
  69. swift/container/reconciler.py +11 -2
  70. swift/container/replicator.py +64 -7
  71. swift/container/server.py +276 -146
  72. swift/container/sharder.py +86 -42
  73. swift/container/sync.py +11 -1
  74. swift/container/updater.py +12 -2
  75. swift/obj/auditor.py +20 -3
  76. swift/obj/diskfile.py +63 -25
  77. swift/obj/expirer.py +154 -47
  78. swift/obj/mem_diskfile.py +2 -1
  79. swift/obj/mem_server.py +1 -0
  80. swift/obj/reconstructor.py +28 -4
  81. swift/obj/replicator.py +63 -24
  82. swift/obj/server.py +76 -59
  83. swift/obj/updater.py +12 -2
  84. swift/obj/watchers/dark_data.py +72 -34
  85. swift/proxy/controllers/account.py +3 -2
  86. swift/proxy/controllers/base.py +254 -148
  87. swift/proxy/controllers/container.py +274 -289
  88. swift/proxy/controllers/obj.py +120 -166
  89. swift/proxy/server.py +17 -13
  90. {swift-2.32.0.dist-info → swift-2.34.0.dist-info}/AUTHORS +14 -4
  91. {swift-2.32.0.dist-info → swift-2.34.0.dist-info}/METADATA +9 -7
  92. {swift-2.32.0.dist-info → swift-2.34.0.dist-info}/RECORD +97 -120
  93. {swift-2.32.0.dist-info → swift-2.34.0.dist-info}/entry_points.txt +39 -0
  94. swift-2.34.0.dist-info/pbr.json +1 -0
  95. swift-2.32.0.data/scripts/swift-account-auditor +0 -23
  96. swift-2.32.0.data/scripts/swift-account-info +0 -52
  97. swift-2.32.0.data/scripts/swift-account-reaper +0 -23
  98. swift-2.32.0.data/scripts/swift-account-replicator +0 -34
  99. swift-2.32.0.data/scripts/swift-account-server +0 -23
  100. swift-2.32.0.data/scripts/swift-container-auditor +0 -23
  101. swift-2.32.0.data/scripts/swift-container-info +0 -56
  102. swift-2.32.0.data/scripts/swift-container-reconciler +0 -21
  103. swift-2.32.0.data/scripts/swift-container-replicator +0 -34
  104. swift-2.32.0.data/scripts/swift-container-server +0 -23
  105. swift-2.32.0.data/scripts/swift-container-sharder +0 -37
  106. swift-2.32.0.data/scripts/swift-container-sync +0 -23
  107. swift-2.32.0.data/scripts/swift-container-updater +0 -23
  108. swift-2.32.0.data/scripts/swift-dispersion-report +0 -24
  109. swift-2.32.0.data/scripts/swift-form-signature +0 -20
  110. swift-2.32.0.data/scripts/swift-init +0 -119
  111. swift-2.32.0.data/scripts/swift-object-auditor +0 -29
  112. swift-2.32.0.data/scripts/swift-object-expirer +0 -33
  113. swift-2.32.0.data/scripts/swift-object-info +0 -60
  114. swift-2.32.0.data/scripts/swift-object-reconstructor +0 -33
  115. swift-2.32.0.data/scripts/swift-object-relinker +0 -23
  116. swift-2.32.0.data/scripts/swift-object-replicator +0 -37
  117. swift-2.32.0.data/scripts/swift-object-server +0 -27
  118. swift-2.32.0.data/scripts/swift-object-updater +0 -23
  119. swift-2.32.0.data/scripts/swift-proxy-server +0 -23
  120. swift-2.32.0.data/scripts/swift-recon +0 -24
  121. swift-2.32.0.data/scripts/swift-ring-builder +0 -37
  122. swift-2.32.0.data/scripts/swift-ring-builder-analyzer +0 -22
  123. swift-2.32.0.data/scripts/swift-ring-composer +0 -22
  124. swift-2.32.0.dist-info/pbr.json +0 -1
  125. {swift-2.32.0.dist-info → swift-2.34.0.dist-info}/LICENSE +0 -0
  126. {swift-2.32.0.dist-info → swift-2.34.0.dist-info}/WHEEL +0 -0
  127. {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=None):
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 HTML PUBLIC "-//W3C//DTD HTML 4.01 ' \
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
- headers = {'Content-Type': 'text/html; charset=UTF-8'}
289
- body = '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 ' \
290
- 'Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">\n' \
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 = '?' + '&amp;'.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="../">../</a></td>\n' \
347
+ ' <td class="colname"><a href="../%s">../</a></td>\n' \
317
348
  ' <td class="colsize">&nbsp;</td>\n' \
318
349
  ' <td class="coldate">&nbsp;</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">&nbsp;</td>\n' \
329
360
  ' <td class="coldate">&nbsp;</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 Response, Request, wsgi_to_str
188
- from swift.common.swob import HTTPBadRequest, HTTPForbidden, HTTPNotFound, \
189
- HTTPUnauthorized, HTTPMethodNotAllowed, HTTPServiceUnavailable
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 = self._get_temp_url_info(env)
499
- temp_url_sig, temp_url_expires, temp_url_prefix, filename, \
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(env)
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': 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
- def _start_response(status, headers, exc_info=None):
588
- headers = self._clean_outgoing_headers(headers)
589
- if env['REQUEST_METHOD'] in ('GET', 'HEAD') and status[0] == '2':
590
- # figure out the right value for content-disposition
591
- # 1) use the value from the query string
592
- # 2) use the value from the object metadata
593
- # 3) use the object name (default)
594
- out_headers = []
595
- existing_disposition = None
596
- for h, v in headers:
597
- if h.lower() != 'content-disposition':
598
- out_headers.append((h, v))
599
- else:
600
- existing_disposition = v
601
- if inline_disposition:
602
- if filename:
603
- disposition_value = disposition_format('inline',
604
- filename)
605
- else:
606
- disposition_value = 'inline'
607
- elif filename:
608
- disposition_value = disposition_format('attachment',
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
- name = basename(wsgi_to_str(env['PATH_INFO']).rstrip('/'))
614
- disposition_value = disposition_format('attachment',
615
- name)
616
- # this is probably just paranoia, I couldn't actually get a
617
- # newline into existing_disposition
618
- value = disposition_value.replace('\n', '%0A')
619
- out_headers.append(('Content-Disposition', value))
620
-
621
- # include Expires header for better cache-control
622
- out_headers.append(('Expires', strftime(
623
- "%a, %d %b %Y %H:%M:%S GMT",
624
- gmtime(temp_url_expires))))
625
- headers = out_headers
626
- return start_response(status, headers, exc_info)
627
-
628
- return self.app(env, _start_response)
629
-
630
- def _get_path_parts(self, env):
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(env['PATH_INFO'], 4, 4, True)
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), wsgi_to_str(obj))
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 _('Profiling Error: %s') % self.msg
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(_('method %s is not allowed.') % method)
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(_('Can not load profile data from %s.')
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(_('no log file found'))
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(_('Data download error: %s') % ex)
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(_('python-matplotlib not installed.'))
399
+ raise PLOTLIBNotInstalled('python-matplotlib not installed.')
401
400
  if len(log_files) == 0:
402
- raise NotFoundException(_('no log file found'))
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(_('plotting results failed due to %s') % ex)
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 _('The file type are forbidden to access!')
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 _('Can not access the file %s.') % file_path
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(_('odfpy not installed.'))
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 _('Error on render profiling results: %s') % ex
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\