中年engineerの独り言 - crumbjp

LinuxとApacheの憂鬱

axiosのソケットリークでハマった

このシンプルなコードでソケットリークが起こる事に気付いた。

実際には膨大なコードの中からココに疑いも持った段階で9合目って感じだが・・・

const axios = require('axios');
const consumers = require('stream/consumers');

const get = (url) => {
  return new Promise(async (resolve, reject) => {
    try {
      const res = await axios.get(url, {responseType: 'stream'});
      resolve(await consumers.buffer(res.data));
    } catch (e) {
      reject(e);
    }
  });
};

responseType: 'stream' を指定した場合は、Stream (Socket)がまだ開いていて自前で処理してあげなきゃならない。

consumers.buffer(res.data) は、普通にchunk処理してBlobに渡してるだけだが、Streamを最後まで読んでいるのでcloseまで到達しているはずである。

github.com

ここは問題ない

問題はここ

    } catch (e) {
      reject(e);
    }

盲点だったのだが、接続エラーで到達するならば問題ないが、なんとBad Request などでも到達するのだ。当然ペイロードがある。

そして responseType: 'stream' ならば未処理のStreamである。

コイツがリークする!!

例外で飛んできてるので、response が受け取れてないが、例外の中に埋められている。

const _ = require('lodash');
const axios = require('axios');
const consumers = require('stream/consumers');

const get = (url) => {
  return new Promise(async (resolve, reject) => {
    try {
      const res = await axios.get(url, {responseType: 'stream'});
      resolve(await consumers.buffer(res.data));
    } catch (e) {
      const socket = _.get(e, 'response.data.socket');
      if (socket) {
        socket.destroy();
      }
      reject(e);
    }
  });
};

これで解決。

全然情報が無かったのだけど、、

レスポンスが正常に返ってくれば問題ないから気付き難いだけで、みんなリーク喰らってるんじゃないかな?