Хорошо, теперь у вас есть больше всего кода, так что я вижу, что вы пытаетесь выполнить sh.
Несколько проблем, которые я вижу:
- переменная
a
не определена, поэтому она является неявной глобальной. Плохо. Объявите это где-нибудь локально. - При вычислении комбинированной длины контента между двумя вашими запросами возникает условие полной гонки. Ваш код предполагает, что
adsys
сначала дает вам response
, но это не обязательно произойдет. - Вы вычисляете эту длину, но фактически не используете ее. Вы не можете поместить это в свою инструкцию
res.set()
, потому что вы еще не закончили ее вычислять, когда она выполняется. - Похоже, что вы пропускаете обработку ошибок по крайней мере по запросу adsys.
- В документации на иглы мне представляется, что событие завершения для запроса на иглу равно
"done"
, а не "finish"
.
Вот значительно очищенная версия вашего кода с куча новых журналов, чтобы помочь отладке:
let trackCntr = 0;
function getLogger() {
let id = ++trackCntr;
return function(...args) {
args.unshift(`(${id}): `);
console.log(...args);
}
}
router.get('/track/:url(*)', (req, res) => {
const log = getLogger();
const url = req.params.url.substr(0);
log(`(${trackCntr}) /track/${url}`);
/* AD SYS */
const remote = "https://storage.googleapis.com/ad-system/testfolder/OUTOFAREA.mp3";
const adsys = needle.get(remote);
/* PODCAST */
const filesize = needle.get(url);
let responseCntr = 0;
let length = 0;
let errReportedAlready = false;
adsys.on("response", function(resB) {
log(`adsys.on('response'), content-length=${resB.headers['content-length']}`);
length += +resB.headers['content-length'];
++responseCntr;
checkResponseCntr();
});
adsys.on("err", sendErr);
adsys.on("timeout", sendErr);
filesize.on("response", function(resC) {
log(`filesize.on('response'), content-length=${resC.headers['content-length']}`);
length += +resC.headers['content-length'];
++responseCntr;
checkResponseCntr();
});
filesize.on("err", sendErr);
filesize.on("timeout", sendErr);
// this is called if either needle requests gets an error
function sendErr(err) {
log("sendErr", err);
if (!errReportedAlready) {
errReportedAlready = true;
if (res.headersSent) {
// just need to abort the response because headers have already been sent
res.end();
} else {
// send error status
res.sendStatus(500);
}
}
}
// code continues here after both response headers above have completed
function checkResponseCntr() {
log(`checkResponseCntr(${responseCntr})`)
// if we have received both responses, then start streaming ad data
if (responseCntr === 2) {
log("got both responses");
res.set({
"Content-Type": "audio/mpeg",
"Transfer-Encoding": "chunk",
"Content-Length": length
});
// start streaming ad data
getAd();
}
}
function getAd() {
log("getAd()");
// this will cause adsys data to start flowing
adsys.on("data", function(chunk) {
if (!errReportedAlready) {
res.write(chunk);
}
});
adsys.on("done", function() {
log("adsys done");
// now trigger getting the podcast data
getPodcast();
});
}
function getPodcast() {
log("getPodcast()");
filesize.on("data", function(chunk) {
if (!errReportedAlready) {
res.write(chunk);
}
});
filesize.on("done", function() {
log("filesize done");
if (!errReportedAlready) {
log("res.end()")
res.end();
}
});
}
});
module.exports = router;
Это делает следующее:
- Правильно вычисляет длину без учета порядка гонки двух запросов.
- Добавить обработку ошибок для обоих запросов
needle()
. - Добавить правильную настройку заголовка
content-length
. - Изменить мониторинг
needle()
завершено, чтобы использовать событие "done"
для needle()
документация. - Код
getAd()
и getPodcast()
аналогично.
Возможные проблемы все еще:
- Если для потоковой передачи требуется много времени объявление, я мог бы представить ваш запрос размера файла т iming out.
Я могу запустить приведенный выше код в своем собственном небольшом nodejs приложении, и вот запись в журнал, которую я получаю:
(1): (1) /track/https://storage.googleapis.com/radiomediapodcast/wellwellnow/season1/S01E04.mp3
(1): adsys.on('response'), content-length=754542
(1): checkResponseCntr(1)
(1): filesize.on('response'), content-length=63062853
(1): checkResponseCntr(2)
(1): got both responses
(1): getAd()
(1): adsys done
(1): getPodcast()
(2): (2) /track/https://storage.googleapis.com/radiomediapodcast/wellwellnow/season1/S01E04.mp3
(2): adsys.on('response'), content-length=754542
(2): checkResponseCntr(1)
(2): filesize.on('response'), content-length=63062853
(2): checkResponseCntr(2)
(2): got both responses
(2): getAd()
(2): adsys done
(2): getPodcast()
(2): filesize done
(2): res.end()
(1): filesize done
(1): res.end()
You отчетливо видно, как поступают два отдельных запроса. Второй запрос приходит, как только первый запрос отправляет свои заголовки - не знаю, почему.
Я определил, что двойной запрос вызван помещением Аудио URL в адресную строку Chrome. Если я вставлю этот URL в аудио-тег на странице HTML, мы больше не получим двойные запросы. Я создал эту простую HTML страницу:
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
</head>
<body>
<figure>
<figcaption>Listen to the T-Rex:</figcaption>
<audio
controls
src="http://localhost/track/https%3A%2F%2Fstorage.googleapis.com%2Fradiomediapodcast%2Fwellwellnow%2Fseason1%2FS01E04.mp3">
Your browser does not support the
<code>audio</code> element.
</audio>
</figure>
</body>
</html>
И затем я получаю только этот журнал (который мне подходит):
(1): (1) /track/https://storage.googleapis.com/radiomediapodcast/wellwellnow/season1/S01E04.mp3
(1): Accept: */*
(1): adsys.on('response'), content-length=754542
(1): checkResponseCntr(1)
(1): filesize.on('response'), content-length=63062853
(1): checkResponseCntr(2)
(1): got both responses
(1): getAd()
(1): adsys done
(1): getPodcast()
(1): filesize done
(1): res.end()
И двойной запрос кажется быть Chrome / Edge URL bar bar, потому что это не происходит в Firefox, когда я помещаю URL в Firefox URL bar.