Private
Public Access
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>
82 lines
3.3 KiB
Java
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);
|
|
}
|
|
}
|