Files
ps5upload/client/src-tauri/tests/engine_http_proxy.rs
T
PhantomPtrandClaude Opus 5.5 534772d55a 5.41.0: installs the PS5 declined, working Docker images, Payload Manager fallback (#347)
* fix: installs the PS5 declined, Docker images that work, Payload Manager as a backup loader (5.41.0)

Installs
- A package the console serves itself (Upload & install, USB, the phone app)
  is refused on FW 13.60 with 0x80B2116F (Sony's DbgGetPatchInfo/DbgCancelPatch
  path, especially over an installed app). The unified install now re-serves
  the console's copy from the engine (ps5:// read back through the helper) and
  a refused link through the engine's link proxy. HW-verified on a 13.60 Pro:
  stream, Upload & install fresh and over an installed app, USB.
- Installer 1.2.0: fixed past Last-Modified, logs to /data/ps5upload/installer.log.
- NET_REACH (frame 148/149): the console checks it can reach the engine before
  a stream install and names a firewall/isolation cause in seconds.
- Staged-route refusals get their own reason and message (staged_refused).
- Folder-game register on 13.x: sceAppInstUtilAppInstallAll fallback,
  /system_ex rw remount on EROFS, repeated uninstall for its two records.
- Removed the incorrect "no fake packages above FW 11.60" warnings.

Loaders
- itsPLK Payload Manager (:8084) fallback: starts the installer, recovers a
  wedged elfldr (HW-verified) and puts the helper back when :9021 is dead.
  Only ever uses its own ps5upload-* folders.
- Setup wizard skips payloads already running; "Send only ps5upload".

Engine / self-hosting
- Engine clients ignore proxy env vars; errors carry the real cause.
- Docker: writable /tmp and /data (also under a user: override), HEALTHCHECK
  via --healthcheck, PS5UPLOAD_ALLOW_IP takes CIDR ranges, actionable 403,
  bridge-network warning, engine/compose.yaml, scripts/docker-smoke.sh run in
  the PR gate and before every release push.
- Desktop app with a remote engine browses the engine's disk; From this device
  uploads first. Android engine listens on the LAN so the phone can stream.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: accept Windows' timeout as the probe's underlying cause

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* style: rustfmt the probe test

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 16:38:55 -07:00

64 lines
2.4 KiB
Rust

//! The engine client must not go through a proxy from the environment.
//!
//! A user report: with a proxy variable set, every request to the engine
//! failed with "error sending request for url (http://127.0.0.1:19113/…)"
//! and the engine logged none of them. reqwest honours `http_proxy` /
//! `ALL_PROXY` and does not exempt 127.0.0.1. This is its own test binary
//! because it sets process-wide environment variables.
use std::io::{Read, Write};
use std::net::TcpListener;
use ps5upload_desktop_lib::engine_http::engine_client_builder;
/// A one-shot HTTP server on loopback that answers 200 to whatever arrives.
fn serve_once() -> u16 {
let listener = TcpListener::bind(("127.0.0.1", 0)).unwrap();
let port = listener.local_addr().unwrap().port();
std::thread::spawn(move || {
if let Ok((mut stream, _)) = listener.accept() {
let mut buf = [0u8; 1024];
let _ = stream.read(&mut buf);
let _ = stream.write_all(b"HTTP/1.1 200 OK\r\ncontent-length: 2\r\n\r\nok");
}
});
port
}
#[tokio::test]
async fn engine_requests_ignore_proxy_variables() {
// A proxy nobody is listening on, as when a VPN tool exported the
// variable and was then closed, plus the SOCKS form this build of
// reqwest cannot use at all.
let dead = TcpListener::bind(("127.0.0.1", 0))
.unwrap()
.local_addr()
.unwrap()
.port();
std::env::set_var("http_proxy", format!("http://127.0.0.1:{dead}"));
std::env::set_var("HTTP_PROXY", format!("http://127.0.0.1:{dead}"));
std::env::set_var("ALL_PROXY", format!("socks5://127.0.0.1:{dead}"));
std::env::remove_var("NO_PROXY");
std::env::remove_var("no_proxy");
// Control: a default client really is broken by these variables, so
// the assertion below is testing something.
let port = serve_once();
let url = format!("http://127.0.0.1:{port}/api/jobs");
assert!(
reqwest::Client::new().get(&url).send().await.is_err(),
"a default client should have been sent to the dead proxy"
);
let port = serve_once();
let url = format!("http://127.0.0.1:{port}/api/jobs");
let resp = engine_client_builder()
.build()
.unwrap()
.get(&url)
.send()
.await
.expect("engine client must reach loopback directly");
assert!(resp.status().is_success());
}