Skip to content

fix: ensure HTTP code is returned when image file is missing (404) or unreadable (500) - #5526

Draft
eduardomozart wants to merge 1 commit into
LizardByte:masterfrom
eduardomozart:fix/applist-cover-404-500
Draft

fix: ensure HTTP code is returned when image file is missing (404) or unreadable (500)#5526
eduardomozart wants to merge 1 commit into
LizardByte:masterfrom
eduardomozart:fix/applist-cover-404-500

Conversation

@eduardomozart

@eduardomozart eduardomozart commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

Description

This PR resolves a critical network stream bug in /appasset (Moonlight streaming protocol) endpoint.

When Sunshine is asked to serve an image file & fallback image file (box.png) that is missing or locked/unreadable due to insufficient permissions, std::ifstream silently fails to open the file. Prior to this fix, the code did not verify if the stream successfully opened (in.is_open()), and instead blindly proceeded to calculate the content length using tellg(). On a failed stream, tellg() returns -1, which was incorrectly cast to a 64-bit unsigned integer (yielding a massive impossible Content-Length like 18446744073709551615). This invalid header abruptly aborted the connection via libcurl.

This issue specifically impacted Moonlight clients (like Moonlight Tizen). When a custom app image was inaccessible and the fallback box.png was also unreadable, the connection abortion threw a cryptic Weird server reply (libcurl: 8) error but HTTP 200, breaking the fetch process completely.

This commit adds if (!in.is_open()) safeguards to appasset() in nvhttp.cpp. It also adds a std::filesystem::exists() check to differentiate the HTTP response correctly:

  • If the file is missing, it returns 404 Not Found.
  • If the file exists but is locked/unreadable, it returns 500 Internal Server Error.

After the fix:

Sunshine gracefully catches the file read failure, logs the warning, and safely returns a 500 or 404 response without breaking the network stream. The stream stays alive to handle subsequent background /serverinfo pings:
text [2026-08-18 18:41:56.480]: Debug: METHOD :: GET [2026-08-18 18:41:56.480]: Debug: DESTINATION :: /appasset ... [2026-08-18 18:41:56.481]: Warning: Invalid PNG file at path ["../Resources/assets/desktop-alt.png"] [2026-08-18 18:41:56.920]: Debug: /CN=NVIDIA GameStream Client -- verified [2026-08-18 18:41:56.920]: Debug: TUNNEL :: HTTPS [2026-08-18 18:41:56.920]: Debug: METHOD :: GET [2026-08-18 18:41:56.920]: Debug: DESTINATION :: /serverinfo
Moonlight Tizen safely catches the standard HTTP error, gracefully abandons the download, and uses its local fallback placeholder:
CURL: https://192.168.1.73:47984/appasset?uniqueid=... -> HTTP response code said error

Before the fix:

Sunshine attempts to serve the file and immediately crashes the libcurl connection due to the -1 Content-Length. The Sunshine logs abruptly end after the /appasset request because the stream dies:
text [2026-08-18 18:41:56.480]: Debug: METHOD :: GET [2026-08-18 18:41:56.480]: Debug: DESTINATION :: /appasset [2026-08-18 18:41:56.480]: Debug: appid -- 427450027
Moonlight Tizen fails to fetch the image and receives a crashed connection because of the massive unsigned Content-Length, logging:
CURL: https://192.168.1.73:47984/appasset?uniqueid=... -> Weird server reply

Reproducible Steps (using Web UI proxy)

Because hitting the mutual-TLS /appasset endpoint is complex from a standard CLI, you can easily reproduce the exact same C++ tellg() bug via the Web UI .ico endpoint:

  1. Make the icon unreadable: chmod 000 assets/web/images/sunshine.ico
  2. Run Sunshine.
  3. Attempt to fetch it: curl -v -k https://localhost:47990/images/sunshine.ico
  4. Before: Curl crashes with curl: (8) Weird server reply and an impossible Content-Length.
  5. After: Curl gracefully prints < HTTP/1.1 500 Internal Server Error (or 404 Not Found if you delete the file).

Screenshot

Issues Fixed or Closed

Roadmap Issues

Type of Change

  • feat: New feature (non-breaking change which adds functionality)
  • fix: Bug fix (non-breaking change which fixes an issue)
  • docs: Documentation only changes
  • style: Changes that do not affect the meaning of the code (white-space, formatting, missing semicolons, etc.)
  • refactor: Code change that neither fixes a bug nor adds a feature
  • perf: Code change that improves performance
  • test: Adding missing tests or correcting existing tests
  • build: Changes that affect the build system or external dependencies
  • ci: Changes to CI configuration files and scripts
  • chore: Other changes that don't modify src or test files
  • revert: Reverts a previous commit
  • BREAKING CHANGE: Introduces a breaking change (can be combined with any type above)

Checklist

  • Code follows the style guidelines of this project
  • Code has been self-reviewed
  • Code has been commented, particularly in hard-to-understand areas
  • Code docstring/documentation-blocks for new or existing methods/components have been added or updated
  • Unit tests have been added or updated for any new or modified functionality

AI Usage

See our AI usage policy.

  • None: No AI tools were used in creating this PR
  • Light: AI provided minor assistance (formatting, simple suggestions)
  • Moderate: AI helped with code generation or debugging specific parts
  • Heavy: AI generated most or all of the code changes

@sonarqubecloud

Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant