router.get отправляет res дважды - PullRequest
0 голосов
/ 05 марта 2020

Итак, я сузил проблему со своим сценарием.

проблема в том, что эта строка

clients.push(res);

Кажется, что это происходит, стреляя 2/3 раза вместо 1.

в console.log показывает:

STATION_ID Completed
754542
get data connected!
undefined
STATION_ID Stream END
get POdcast ran
STATION_ID Completed
754542
get data connected!
undefined
STATION_ID Stream END
get POdcast ran

однако, когда я удаляю res из clients.push(res);, он срабатывает нормально, но не возвращается в браузере клиенту.

Есть предложения?

полный код:

    router.get('/track/:url(*)', (req, res) =>{
    var url = req.params.url.substr(0); 
    console.log('/track/'+url);

    var length = 0;
    var e = 0;
    /* AD SYS */
    var remote = "https://storage.googleapis.com/ad-system/testfolder/OUTOFAREA.mp3";  
    var adsys = needle.get(remote)
    /* PODCAST */
    var filesize = needle.get(url, function(error) {
       if(error){
        e = 505;
        res.send("<html><head></head><body>NOPE</body></html>");
        console.log(error)
        //filesize.end();
        res.end();

       }
    });


        adsys.on('response', function(resB) {
            console.log("STATION_ID Completed");
            length =  Number(resB.headers['content-length']);
           // console.log(length);           
        });

        filesize.on('response', function(resC) {
                console.error("get data connected!");
                console.log(resC.headers['content-length']);
                a = Number(resC.headers['content-length']);
                length = length+a;

      });
      res.set({
        "Content-Type": "audio/mpeg",
        'Transfer-Encoding': 'chunk',
    //    'Content-Disposition': 'attachment',
    //    'Content-Length':length
    });

      adsys.on("finish", function() {

        console.log(" X STATION_ID Stream END");

           getPodcast();



    });  


    adsys.on("data", function (chunk) {
            // console.log(clients.length);
           /* if (clients.length > 0){
                for (client in clients){
                    clients[client].write(chunk);
                    //console.log(chunk);
                };
            }*/
            res.write(chunk);

    });

    function getPodcast(){


        filesize.on("data", function (chunk) {
           res.write(chunk);
        });

        filesize.on('end', function(){
            console.log("end");
            res.end();
        });
    }
});

module.exports = router;

1 Ответ

0 голосов
/ 05 марта 2020

Хорошо, теперь у вас есть больше всего кода, так что я вижу, что вы пытаетесь выполнить sh.

Несколько проблем, которые я вижу:

  1. переменная a не определена, поэтому она является неявной глобальной. Плохо. Объявите это где-нибудь локально.
  2. При вычислении комбинированной длины контента между двумя вашими запросами возникает условие полной гонки. Ваш код предполагает, что adsys сначала дает вам response, но это не обязательно произойдет.
  3. Вы вычисляете эту длину, но фактически не используете ее. Вы не можете поместить это в свою инструкцию res.set(), потому что вы еще не закончили ее вычислять, когда она выполняется.
  4. Похоже, что вы пропускаете обработку ошибок по крайней мере по запросу adsys.
  5. В документации на иглы мне представляется, что событие завершения для запроса на иглу равно "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;

Это делает следующее:

  1. Правильно вычисляет длину без учета порядка гонки двух запросов.
  2. Добавить обработку ошибок для обоих запросов needle().
  3. Добавить правильную настройку заголовка content-length.
  4. Изменить мониторинг needle() завершено, чтобы использовать событие "done" для needle() документация.
  5. Код getAd() и getPodcast() аналогично.

Возможные проблемы все еще:

  1. Если для потоковой передачи требуется много времени объявление, я мог бы представить ваш запрос размера файла т 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.

...