Source code

Revision control

Copy as Markdown

Other Tools

// Serves a real MP3 fixture without ever revealing its total length, and
// keeps the HTTP response open for a while after writing all of its bytes.
// This mimics a live stream (e.g. internet radio), whose connection stays
// open during normal playback instead of completing like a regular
// finite-length download.
//
// Simply omitting `Content-Length` isn't enough on its own: Gecko also
// reclassifies a resource as finite-length once its download completes
// (see `ChannelMediaDecoder::NotifyDownloadEnded()`), which happens near-
// instantly for any small static file served locally. Holding the
// connection (for `HOLD_OPEN_MS`) open avoids that, matching how a real broadcast behaves.
//
// The fixture itself is a short (~1s) MP3 stream carrying its own `Info`
// header, immediately followed by a second, header-less MP3 stream. This
// mimics a short pre-recorded prelude in front of a live broadcast: the
// prelude's duration is known upfront, while the broadcast portion has no
// declared duration, so Gecko instead reports its playback position as the
// growing duration - matching a real live stream.
const HOLD_OPEN_MS = 2000;
function handleRequest(request, response) {
var src = Services.dirsvc.get("CurWorkD", Ci.nsIFile);
var split =
"tests/dom/media/test/single-xing-header-no-content-length.mp3".split("/");
for (var i = 0; i < split.length; ++i) {
src.append(split[i]);
}
var file = Cc["@mozilla.org/network/file-input-stream;1"].createInstance(
Ci.nsIFileInputStream
);
file.init(src, -1, -1, false);
var input = Cc["@mozilla.org/binaryinputstream;1"].createInstance(
Ci.nsIBinaryInputStream
);
input.setInputStream(file);
var bytes = input.readBytes(input.available());
input.close();
response.setHeader("Content-Type", "audio/mpeg", false);
response.setHeader("Cache-Control", "no-store", false);
// Deliberately no Content-Length header.
response.write(bytes, bytes.length);
// Hold the connection for a while.
response.processAsync();
const Timer = Components.Constructor(
"@mozilla.org/timer;1",
"nsITimer",
"initWithCallback"
);
// eslint-disable-next-line no-unused-vars
var timer = new Timer(
function () {
response.finish();
},
HOLD_OPEN_MS,
Ci.nsITimer.TYPE_ONE_SHOT
);
}