Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions server.py
Original file line number Diff line number Diff line change
Expand Up @@ -511,7 +511,7 @@ async def view_image(request):
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'
Expand All @@ -531,7 +531,7 @@ async def view_image(request):
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:
Expand All @@ -548,7 +548,7 @@ async def view_image(request):
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'
Expand All @@ -560,7 +560,7 @@ async def view_image(request):
return web.FileResponse(
file,
headers={
"Content-Disposition": f"filename=\"{filename}\"",
"Content-Disposition": f"inline; filename=\"{filename}\"",
"Content-Type": content_type
}
)
Expand Down
75 changes: 75 additions & 0 deletions tests-unit/server_test/test_content_disposition.py
Original file line number Diff line number Diff line change
@@ -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