fix: Content-Disposition header format to comply with RFC 2183

Fixes #8914

The Content-Disposition header was set to `filename="name.ext"` which
doesn't match RFC 2183 specification. This caused third-party libraries
(e.g., Go's mime.ParseMediaType) to fail parsing the header.

Changed to `inline; filename="name.ext"` format which properly includes
the disposition-type as required by RFC 2183.
This commit is contained in:
lif 2025-12-18 00:31:10 +08:00
parent 3a5f239cb6
commit 8595a48795
2 changed files with 79 additions and 4 deletions

View File

@ -511,7 +511,7 @@ class PromptServer():
buffer.seek(0)
return web.Response(body=buffer.read(), content_type=f'image/{image_format}',
headers={"Content-Disposition": f"filename=\"{filename}\""})
headers={"Content-Disposition": f"inline; filename=\"{filename}\""})
if 'channel' not in request.rel_url.query:
channel = 'rgba'
@ -531,7 +531,7 @@ class PromptServer():
buffer.seek(0)
return web.Response(body=buffer.read(), content_type='image/png',
headers={"Content-Disposition": f"filename=\"{filename}\""})
headers={"Content-Disposition": f"inline; filename=\"{filename}\""})
elif channel == 'a':
with Image.open(file) as img:
@ -548,7 +548,7 @@ class PromptServer():
alpha_buffer.seek(0)
return web.Response(body=alpha_buffer.read(), content_type='image/png',
headers={"Content-Disposition": f"filename=\"{filename}\""})
headers={"Content-Disposition": f"inline; filename=\"{filename}\""})
else:
# Get content type from mimetype, defaulting to 'application/octet-stream'
content_type = mimetypes.guess_type(filename)[0] or 'application/octet-stream'
@ -560,7 +560,7 @@ class PromptServer():
return web.FileResponse(
file,
headers={
"Content-Disposition": f"filename=\"{filename}\"",
"Content-Disposition": f"inline; filename=\"{filename}\"",
"Content-Type": content_type
}
)

View File

@ -0,0 +1,75 @@
"""Tests for Content-Disposition header format (RFC 2183 compliance)
Relates to issue #8914: Content-Disposition Header not matching RFC2183 rules
"""
import pytest
import re
class TestContentDispositionHeader:
"""Test Content-Disposition header format compliance with RFC 2183."""
def test_rfc2183_format_with_inline(self):
"""Verify inline disposition type format matches RFC 2183."""
filename = "test_image.png"
header = f'inline; filename="{filename}"'
# RFC 2183 requires disposition-type followed by parameters
# Format: disposition-type ";" disposition-parm
pattern = r'^(inline|attachment);\s*filename="[^"]*"$'
assert re.match(pattern, header), f"Header '{header}' does not match RFC 2183 format"
def test_rfc2183_format_with_attachment(self):
"""Verify attachment disposition type format matches RFC 2183."""
filename = "download.mp4"
header = f'attachment; filename="{filename}"'
pattern = r'^(inline|attachment);\s*filename="[^"]*"$'
assert re.match(pattern, header), f"Header '{header}' does not match RFC 2183 format"
def test_invalid_format_missing_disposition_type(self):
"""Verify that format without disposition type is invalid."""
filename = "test.jpg"
invalid_header = f'filename="{filename}"'
pattern = r'^(inline|attachment);\s*filename="[^"]*"$'
assert not re.match(pattern, invalid_header), \
"Header without disposition type should not match RFC 2183 format"
@pytest.mark.parametrize("filename", [
"image.png",
"video.mp4",
"file with spaces.jpg",
"special_chars-123.webp",
])
def test_various_filenames(self, filename):
"""Test RFC 2183 format with various filename patterns."""
header = f'inline; filename="{filename}"'
# Should have disposition type before filename
assert header.startswith("inline; ") or header.startswith("attachment; ")
assert f'filename="{filename}"' in header
class TestContentDispositionParsing:
"""Test that Content-Disposition headers can be parsed by standard libraries."""
def test_parse_inline_disposition(self):
"""Test parsing inline disposition header."""
header = 'inline; filename="test.png"'
# Simple parsing test - split by semicolon
parts = [p.strip() for p in header.split(';')]
assert parts[0] in ('inline', 'attachment')
assert 'filename=' in parts[1]
def test_extract_filename(self):
"""Test extracting filename from header."""
filename = "my_image.jpg"
header = f'inline; filename="{filename}"'
# Extract filename using regex
match = re.search(r'filename="([^"]*)"', header)
assert match is not None
assert match.group(1) == filename