動かざることバグの如し

近づきたいよ 君の理想に

ExpressでHTTPリクエストが中断されたのを検知したい

Expressでよく使われてるreq, resの中身はなにか - 動かざることバグの如しの続き

環境

  • Nodejs 10
  • Express 4.x

やりたいこと

例えば以下のようなExpressを用いたサーバーのコードがあったとする

const express = require('express');
const app = express();

app.get('/', (req, res) => {
  setTimeout( () => {
    console.log('5秒経過しました');
    res.send('Hello World!');
  }, 5000);
});

app.listen(3000, () => console.log('Example app listening on port 3000!'));

これを実行して http://localhost:3000 を開けば、なるほど確かに5秒後にターミナルに「5秒経過しました」の文字が表示されてブラウザでは「Hello World」が表示される。

がここで問題があって、5秒経過する前にリクエストを中止してしまっても、実行中の処理はそのまま実行されてしまう

例えば5秒の間にChromeではエスケープキーとか、curlだとCtrl + Cで中止するとレスポンスを返す事なく終わる。本来であればその時点で処理も中断され、5秒後の「5秒経過しました」は表示されなくなるようにしたい。

いや別に文字表示なら大したことないが、バッチ処理とか重いクエリを実行する必要があるとかなら話は別である。

対応した結果

以下が動作するコード

const express = require('express');
const app = express();

app.get('/', (req, res) => {
  const timer = setTimeout( () => {
    console.log('5秒経過しました');
    res.send('Hello World!');
  }, 5000);

  req.on('close', function() {
    console.log('close');
    clearTimeout(timer);
  });
});

app.listen(3000, () => console.log('Example app listening on port 3000!'));

試してみるとわかるが、「5秒経過しました」は表示されなくなる。

ポイントはやはり req.on('close')で、さっきの記事でIncomingMessageにcloseイベントリスナーがあることを知って使った。abortedでも可能のようだが、正直abortedとcloseでどっちを使うべきかはよくわかっていない。。。