Node.js request モジュールで Digest 認証の画像取得時にエラーハンドリングするメモ

見守りカメラ Qwatch TS-NS110W から Node.js で画像を取得する記事で request のサンプル書いていたら、エラーハンドリングがちゃんとできてなかったので、直してみます。
以前のコード
const request = require('request');
const fs = require('fs');
const url = 'http://<IPAddress>:<httpPort>/snapshot.jpg';
const options = {
'auth': {
'user': 'username',
'pass': 'password',
'sendImmediately': false
}
};
request.get(url, options).pipe(fs.createWriteStream('snapshot-record.jpg'))
前回の記事 ですが、正常系しか試してなくて、仮にエラーが起きたりアカウント認証できなくても、そのエラーをそのまま画像にしてしまって、中が見れないという状況になってました。
修正したコード
といいつつも「エラーを判定しつつ、その時は pipe の前で止めてデータを受け入れない」という処理が、思いのほか、調べるのが大変でした。
紆余曲折を経て、以下の記事を発見しました。
Simplified Stream/Pipe Error Handling · Issue #647 · request/request
ほうっておくと、やっぱり pipe が走ってしまうので pause で止めて今回の request 参照を保持しつつ、レスポンスを待つところが大事なところ。
HTTP ステータスエラーやそのほかのタイムアウトのようなエラーを判定して、もしも、正常に動いたときだけ今回の request 参照 で pipe を再開して保存する動きを作るようです。思ったより複雑だった・・・。
ということで、反映したソースはこちら。
request = require('request');
const fs = require('fs');
const url = 'http://<IPAddress>:<httpPort>/snapshot.jpg';
const options = {
'auth': {
'user': 'username',
'pass': 'password',
'sendImmediately': false
}
};
// https://github.com/request/request/issues/647#issuecomment-499518677
let r = request.get(url, options); // 変数 r で参照を保持
r.pause(); // 一旦リクエストを止めて待つ
r.on('response', function(response) {
if (response.statusCode === 200) {
r.pipe(fs.createWriteStream('snapshot-record.jpg'))
.on('error', function(err) {
// error handling here
console.log('createWriteStream error handling here');
console.log('err: ' + err);
});
r.resume();
} else {
// status "error" handling here
console.log('HTTP status "error" handling here');
// 例 : response.statusCode:401 認証エラーとか
console.log('response.statusCode: ' + response.statusCode);
}
})
.on("error", function(err) {
// error handling here
// 例 : err: Error: connect ETIMEDOUT 192.168.XXX.XXX:PORT
console.log('request error handling here');
console.log('err: ' + err);
});
うーむ、正直、あまり実装の想像つかなかったし、連携するときにはなかなか複雑な印象。こうなると、 await / async や Promise で非同期処理を行うほうが見通しが良いなあと思いつつ、とにもかくにも対処法が分かって良かったです!