2013-05-01 6 views
8

Próbuję zdebugować przerażony błąd Can't set headers after they are sent w aplikacji Express.js. W szczególności używam biblioteki knox do rozmowy z s3.Najlepszy sposób debugowania "Nie można ustawić nagłówków po ich wysłaniu" błąd w Express/Node.js?

grubsza, mam ten program obsługi Express, za pomocą globalnej instancji s3client knox:

function foo(req, res) { 
    //Region A 
    var s3req = global.s3client.get('foo').on('response', function(s3res){ 
     //Region B 
     res.set('content-length', s3res.headers['content-length']); //This will fail 
     s3res.on('data', function(chunk){ 
      res.write(chunk); 
     }); 
    }); 
    //Region C 
    s3req.end(); 
} 

Jeżeli ustawić dowolny kod nagłówka lub stanu na res w obszarze A, wszystko działa bez zarzutu. Jeśli spróbuję tego w Regionie B, otrzymuję komunikat "Nie można ustawić nagłówków po ich wysłaniu". Zauważ, że chcę ustawić nagłówki na res nie s3res

Przypuszczalnie response.writeHead jest nazywany lub wyzwalane gdzieś przed odpowiedzią na oddzwanianie na s3client Knox miano. Czy jest jakaś flaga debugowania lub inny sposób, aby Node wypluł kiedy/gdzie został wywołany writeHead? Węzeł 0.10 dodaje response.headersSent, ale byłoby niezmiernie trudno wypełnić mój kod i wszystkie biblioteki innych firm, sprawdzając tę ​​flagę, aby dowiedzieć się, gdzie jest wywoływana. Czy jest jeszcze jakiś sposób na rozwiązanie tego problemu?

+0

Poznałem to jakiś czas temu. czy mogę zaproponować wypróbowanie innej wersji ekspresowej najpierw, co oznacza uaktualnienie lub obniżenie wersji pliku express.js. – wayne

+0

Próbowałem zarówno Express 3.1 i 3.2.1. Próbowałem również węzeł 0.10.3 i węzeł 0.10.5 –

Odpowiedz

11

Można spróbować to proste oprogramowanie pośredniczące, które zrzuca ślad stosu do stdout gdy writeHead nazywa się:

app.use(function(req, res, next) { 
    res.on('header', function() { 
    console.trace('HEADERS GOING TO BE WRITTEN'); 
    }); 
    next(); 
}); 

musisz wstawić że przed tras/middleware, które mogą powodować problem.

FWIW, moje przypuszczenie byłoby, że problem został wywołany przez coś dzieje się w regionie C (albo res.send, res.end, res.json lub res.render który jest nazywany tam):

function foo(req, res) { 
    //Region A 
    var s3req = global.s3client.get('foo').on('response', function(s3res){ 
     //Region B 
     res.set('content-length', s3res.headers['content-length']); 
    }); 
    //Region C 
} 

EDIT jeśli s3res jest właściwy strumień, można spróbować to:

function foo(req, res) { 
    global.s3client.get('foo').on('response', function(s3res) { 
    s3res.pipe(res); 
    }).end(); 
} 

Ale należy pamiętać, że wszystkich nagłówki zwrócone przez żądanie S3 zostaną przekazane do odpowiedzi Express.

+0

Dziękuję za odpowiedź. Edytowałem pytanie z większą ilością szczegółów. Rzeczywiście wywołuję s3req.end() w regionie C (patrz zaktualizowane pytanie). Ale usunięcie go daje 'Error: socket hang up'. Jeśli po prostu chcę przesłać strumieniowo pliki s3res z powrotem do wersji foo i ustawić nagłówek treści oraz nagłówki o długości treści, jakie jest najlepsze rozwiązanie? –

+0

's3req.end()' nie powinien być problemem, który nie jest częścią Express. Sądzę, że zależy to od kontekstu, w którym nazywacie 'foo()', chociaż (jak już sugerował @wayne), wydaje mi się, że pamiętam pewne problemy z Węzłem, które generowałyby ten problem bez wyraźnego powodu. – robertklep

+0

foo to tylko jedna z moich metod api, zarejestrowana jako obsługa pobierania ekspresowego. Celem tego sposobu jest zwrócenie zawartości prywatnej pamięci s3 (na marginesie, zdaję sobie sprawę, że mogę używać do tego wygasających adresów URL S3, ale na razie muszę zachować kompatybilność wsteczną, zanim użyłem s3). Czy istnieje prostszy sposób na proxy/dołączenie s3res do res (odpowiedź widziana przez wywołującego metodę API)? Robienie tego "ręcznie", tak jak ja to robię, jest podatne na błędy, jak widzisz. –

4

Nie jestem pewien, czy ma to zastosowanie, ale to, co od razu pomyślałem, kiedy zobaczyłem błąd, było prostym bardzo prostym błędem w "logice zwrotnej" gdzieś w "warstwie modelu". W dwóch oddzielnych przypadkach przeszukałem to, w obu przypadkach okazało się, że wywołałem wywołanie zwrotne (tj. Takie, które w końcu wywoływałoby wywołanie zwrotne od odpowiedzi) dwa razy. Stanie się tak tylko w przypadku błędu w moim warstwy modelu, ponieważ był kod jak:

if (err) cb(err) // note the missing "return" here 
cb(null,result) // alternatively, also no "else" 

Gdybym napotkasz takiego błędu teraz, pierwszą rzeczą, jaką należy zrobić byłoby, aby sprawdzić, czy odpowiedź pisanie callback jest wywoływany dwukrotnie dla każdej operacji. Po prostu dwa stwierdzenia console.log jedno w regionie A (zakres, w którym inicjowane jest żądanie Knox) ​​i jedno w regionie C (wywołanie zwrotne odpowiedzi) wystarczą przynajmniej do zlikwidowania tej możliwości.

Zasadniczo obiekt res powinien być dość chroniony. Tylko połączenie/ekspresowe oprogramowanie pośrednie ma dostęp do niego, a ten konkretny moduł obsługi żądań oczywiście. Więc jest tylko ograniczona liczba miejsc, które mają moc pisania nagłówka.

+1

Po prostu miałem ten sam problem, było to oczywiście bardziej skomplikowane, ale to się właśnie wydarzyło! –

+0

To prawda, że ​​późniejsze wersje ekspresu wydają się mniej tolerancyjne wobec tego błędu. –

Powiązane problemy