Files
plexamp-lite/test/PoolAbortTest.java
XoséandClaude Opus 5.5 a37711afc0 Plexamp Lite 1.14.1: read-ahead no longer cut off ("Unexpected end of stream")
Two causes, both fixed:
- Plex keeps one file stream per device and cuts the one in progress when another starts, so the read-ahead
  and the player cut each other off. Whole-file fetches (read-ahead, downloads, download-first) now ask for
  the file as a download (?download=1), which Plex leaves out of that rule; the plain address follows for
  servers that refuse downloads.
- Http.abort closed the socket of a response already read to the end, whose connection was back in the pool
  and could be carrying the read-ahead. abort now leaves finished responses alone (test/PoolAbortTest.java).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-01 19:09:53 +01:00

82 lines
3.3 KiB
Java

import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import com.minamp.net.Http;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;
/**
* Http.abort on a response that was already read to the end must not close its connection: by then it's back in
* the pool and may be carrying the next request. (The stream proxy aborts every response it served once done;
* that used to cut the read-ahead of the next track short with "Unexpected end of stream".)
* Run: test/run_pool_abort.sh
*/
public class PoolAbortTest {
static int fails;
static void check(boolean ok, String what) {
System.out.println((ok ? "PASS " : "FAIL ") + what);
if (!ok) fails++;
}
public static void main(String[] a) throws Exception {
HttpServer srv = HttpServer.create(new InetSocketAddress("127.0.0.1", 0), 0);
srv.createContext("/", new HttpHandler() {
public void handle(HttpExchange x) throws java.io.IOException {
int size = x.getRequestURI().getPath().startsWith("/big") ? 2 * 1024 * 1024 : 1000;
x.sendResponseHeaders(200, size);
OutputStream o = x.getResponseBody();
byte[] chunk = new byte[16384];
for (int sent = 0; sent < size; sent += chunk.length) {
o.write(chunk, 0, Math.min(chunk.length, size - sent));
o.flush();
if (size > 1000) try { Thread.sleep(2); } catch (InterruptedException ignored) {}
}
o.close();
}
});
srv.start();
String base = "http://127.0.0.1:" + srv.getAddress().getPort();
// 1. the player's response, read to the end: its connection goes back to the pool
Http.Response first = Http.get(base + "/small", null, 5000);
check(first.readAll().length == 1000, "first response read whole");
// 2. the read-ahead's request takes that pooled connection
Http.Response second = Http.get(base + "/big", null, 5000);
InputStream in = second.body;
byte[] buf = new byte[16384];
long got = 0;
int n = in.read(buf);
got += n;
// 3. the proxy cleans up the first response
Http.abort(first);
String err = "";
try {
while ((n = in.read(buf)) > 0) got += n;
} catch (java.io.IOException e) {
err = " (" + e.getMessage() + ")";
}
check(got == 2 * 1024 * 1024, "aborting a finished response leaves the next request alone: " + got + " bytes" + err);
// an unfinished response is still cut off
Http.Response third = Http.get(base + "/big", null, 5000);
third.body.read(buf);
Http.abort(third);
boolean cut = false;
try {
long left = 2 * 1024 * 1024;
while ((n = third.body.read(buf)) > 0) left -= n;
cut = left > 0;
} catch (java.io.IOException e) {
cut = true;
}
check(cut, "aborting a response still being read cuts it off");
srv.stop(0);
System.out.println(fails == 0 ? "all passed" : fails + " failed");
System.exit(fails == 0 ? 0 : 1);
}
}