Skip to content

Commit a79d850

Browse files
Jenkinsopenstack-gerrit
Jenkins
authored andcommitted
Merge "Make Content-Disposition support inline; filename=... format."
2 parents 264e728 + 5064ebb commit a79d850

File tree

2 files changed

+31
-7
lines changed

2 files changed

+31
-7
lines changed

swift/common/middleware/tempurl.py

+28-6
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,16 @@
112112
temp_url_sig=da39a3ee5e6b4b0d3255bfef95601890afd80709&
113113
temp_url_expires=1323479485&inline
114114
115+
In some cases, the client might not able to present the content of the object,
116+
but you still want the content able to save to local with the specific
117+
filename. So you can cause ``Content-Disposition: inline; filename=...`` to be
118+
set on the response by adding the ``inline&filename=...`` parameter to the
119+
query string, like so::
120+
121+
https://swift-cluster.example.com/v1/AUTH_account/container/object?
122+
temp_url_sig=da39a3ee5e6b4b0d3255bfef95601890afd80709&
123+
temp_url_expires=1323479485&inline&filename=My+Test+File.pdf
124+
115125
---------------------
116126
Cluster Configuration
117127
---------------------
@@ -220,9 +230,15 @@ def get_tempurl_keys_from_metadata(meta):
220230
if key.lower() in ('temp-url-key', 'temp-url-key-2')]
221231

222232

223-
def disposition_format(filename):
224-
return '''attachment; filename="%s"; filename*=UTF-8''%s''' % (
225-
quote(filename, safe=' /'), quote(filename))
233+
def disposition_format(disposition_type, filename):
234+
# Content-Disposition in HTTP is defined in
235+
# https://tools.ietf.org/html/rfc6266 and references
236+
# https://tools.ietf.org/html/rfc5987#section-3.2
237+
# to explain the filename*= encoding format. The summary
238+
# is that it's the charset, then an optional (and empty) language
239+
# then the filename. Looks funny, but it's right.
240+
return '''%s; filename="%s"; filename*=UTF-8''%s''' % (
241+
disposition_type, quote(filename, safe=' /'), quote(filename))
226242

227243

228244
def authorize_same_account(account_to_match):
@@ -413,14 +429,20 @@ def _start_response(status, headers, exc_info=None):
413429
else:
414430
existing_disposition = v
415431
if inline_disposition:
416-
disposition_value = 'inline'
432+
if filename:
433+
disposition_value = disposition_format('inline',
434+
filename)
435+
else:
436+
disposition_value = 'inline'
417437
elif filename:
418-
disposition_value = disposition_format(filename)
438+
disposition_value = disposition_format('attachment',
439+
filename)
419440
elif existing_disposition:
420441
disposition_value = existing_disposition
421442
else:
422443
name = basename(env['PATH_INFO'].rstrip('/'))
423-
disposition_value = disposition_format(name)
444+
disposition_value = disposition_format('attachment',
445+
name)
424446
# this is probably just paranoia, I couldn't actually get a
425447
# newline into existing_disposition
426448
value = disposition_value.replace('\n', '%0A')

test/unit/common/middleware/test_tempurl.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -267,7 +267,9 @@ def test_get_valid_with_filename_and_inline(self, mock_time):
267267
self.tempurl.app = FakeApp(iter([('200 Ok', (), '123')]))
268268
resp = req.get_response(self.tempurl)
269269
self.assertEqual(resp.status_int, 200)
270-
self.assertEqual(resp.headers['content-disposition'], 'inline')
270+
self.assertEqual(resp.headers['content-disposition'],
271+
'inline; filename="bob %22killer%22.txt"; ' +
272+
"filename*=UTF-8''bob%20%22killer%22.txt")
271273
self.assertIn('expires', resp.headers)
272274
self.assertEqual('Thu, 01 Jan 1970 00:00:01 GMT',
273275
resp.headers['expires'])

0 commit comments

Comments
 (0)