[PR #131] [MERGED] [downloader] Make network waits finite so a silent server cannot hang #135

Closed
opened 2026-10-13 19:47:48 +00:00 by bsakaguchi · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/Glitchy-Sheep/boosty-downloader/pull/131
Author: @Glitchy-Sheep
Created: 8/9/2026
Status: ✅ Merged
Merged: 8/9/2026
Merged by: @Glitchy-Sheep

Base: main ← Head: fix/network-timeouts


📝 Commits (1)

  • f507675 fix: 🐛 Network waits are finite - a silent server cannot hang the app

📊 Changes

4 files changed (+18 additions, -3 deletions)

View changed files

📝 CHANGELOG.md (+1 -0)
📝 boosty_downloader/src/application/di/app_environment.py (+6 -1)
📝 boosty_downloader/src/application/di/initialized_app.py (+3 -1)
📝 boosty_downloader/src/infrastructure/update_checker/pypi_checker.py (+8 -1)

📄 Description

Summary

Two network calls had no time limit. A server that goes quiet without closing the connection froze the app forever - no error, no retry, just a progress bar stuck at 43% until the user notices in the morning.

Problem

  • The download session was built with ClientTimeout(total=None) and nothing else: a connection that stops sending data is waited on forever
  • The PyPI update check at startup used urlopen with no timeout: with PyPI unreachable (firewall, broken DNS) every command froze before doing any work
  • A hang is worse than an error: errors go through retries and the failure-streak stop, a hang is invisible

Fix

  • The session limits silence, not duration: 30s to connect, 90s max between received chunks, no limit on the whole download - big videos still take as long as they need
  • A timed-out connection raises a normal client error and joins the existing chain: retries, then skip into failed_downloads.log, then the failure-streak stop if everything is down
  • The update check waits 5 seconds at most and runs off the event loop (asyncio.to_thread)

Before / After

Before: wifi dies at 2am without closing the connection - the run is frozen until morning, nothing in the logs.
After: 90 seconds of silence turn into a retryable error; the night run either recovers by itself or stops with a diagnosis and resumes from cache.

Verification

  • Live mechanics test: a local server that accepts a connection and sends nothing - the request dies exactly at the sock_read limit (SocketTimeoutError), same for an unroutable address; no hang
  • The timeout error type is already in the transport client's retry list - no new handling branches were needed
  • Full task ci green: checks, 64 unit tests, 6 live integration tests, build

Notes

  • No unit tests for the constant values on purpose: asserting 30 == 30 has no failure story
  • Moving the update check out of the DI layer entirely is a separate roadmap step; this change only bounds and unblocks it

🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/Glitchy-Sheep/boosty-downloader/pull/131 **Author:** [@Glitchy-Sheep](https://github.com/Glitchy-Sheep) **Created:** 8/9/2026 **Status:** ✅ Merged **Merged:** 8/9/2026 **Merged by:** [@Glitchy-Sheep](https://github.com/Glitchy-Sheep) **Base:** `main` ← **Head:** `fix/network-timeouts` --- ### 📝 Commits (1) - [`f507675`](https://github.com/Glitchy-Sheep/boosty-downloader/commit/f50767590980c6803e092a32b84ee8e4cb83698a) fix: :bug: Network waits are finite - a silent server cannot hang the app ### 📊 Changes **4 files changed** (+18 additions, -3 deletions) <details> <summary>View changed files</summary> 📝 `CHANGELOG.md` (+1 -0) 📝 `boosty_downloader/src/application/di/app_environment.py` (+6 -1) 📝 `boosty_downloader/src/application/di/initialized_app.py` (+3 -1) 📝 `boosty_downloader/src/infrastructure/update_checker/pypi_checker.py` (+8 -1) </details> ### 📄 Description ## Summary Two network calls had no time limit. A server that goes quiet without closing the connection froze the app forever - no error, no retry, just a progress bar stuck at 43% until the user notices in the morning. ## Problem - The download session was built with `ClientTimeout(total=None)` and nothing else: a connection that stops sending data is waited on forever - The PyPI update check at startup used `urlopen` with no timeout: with PyPI unreachable (firewall, broken DNS) every command froze before doing any work - A hang is worse than an error: errors go through retries and the failure-streak stop, a hang is invisible ## Fix - The session limits silence, not duration: 30s to connect, 90s max between received chunks, no limit on the whole download - big videos still take as long as they need - A timed-out connection raises a normal client error and joins the existing chain: retries, then skip into `failed_downloads.log`, then the failure-streak stop if everything is down - The update check waits 5 seconds at most and runs off the event loop (`asyncio.to_thread`) ## Before / After **Before:** wifi dies at 2am without closing the connection - the run is frozen until morning, nothing in the logs. **After:** 90 seconds of silence turn into a retryable error; the night run either recovers by itself or stops with a diagnosis and resumes from cache. ## Verification - Live mechanics test: a local server that accepts a connection and sends nothing - the request dies exactly at the `sock_read` limit (`SocketTimeoutError`), same for an unroutable address; no hang - The timeout error type is already in the transport client's retry list - no new handling branches were needed - Full `task ci` green: checks, 64 unit tests, 6 live integration tests, build ## Notes - No unit tests for the constant values on purpose: asserting `30 == 30` has no failure story - Moving the update check out of the DI layer entirely is a separate roadmap step; this change only bounds and unblocks it --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
bsakaguchi 2026-10-13 19:47:48 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
Glitchy-Sheep/boosty-downloader#135
No description provided.