[Node.js][Kibana]リバースプロキシ機能を追加してみた

概要

Elastic Searchクラスタが内部ネットワークからしかアクセスできないようになっていて、
そのためにリバースプロキシ経由でKibanaからElastic Searchクラスタにアクセスしていた。
だけど、そのリバースプロキシが使えなくなって、コストの関係で新規サーバを構築できないからKibanaにリバースプロキシ機能を追加した。 http://${kibana domain}/proxy/${elastic search url}
という形式でkibanaにアクセスすると、
裏で
${elastic search url}
にアクセスしてElastic Search からデータを取得してくる。

環境

  • OS
    • Linux www4322gi 3.2.0-64-generic #97-Ubuntu SMP Wed Jun 4 22:04:21 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux
  • Node
    • version: v0.10.26

ソースコード

ここ(Kibana3)とのdiff(差分)は以下。 sample/server.js
7c7,8
<     events = require('events');
---
>     events = require('events'),
>     path = require('path');
9c10,12
< var DEFAULT_PORT = 8000;
---
> var exec = require('child_process').exec;
>
> var DEFAULT_PORT = 80;
13,14c16,20
<     'GET': createServlet(StaticServlet),
<     'HEAD': createServlet(StaticServlet)
---
>     'GET'   : createServlet(StaticServlet),
>     'POST'  : createServlet(StaticServlet),
>     'PUT'   : createServlet(StaticServlet),
>     'HEAD'  : createServlet(StaticServlet),
>     'DELETE': createServlet(StaticServlet)
85c91
<   'svg': 'image/svg+xml'
---
>   'svg': 'image/svg+xml'
87a94,116
> // Common function for getting json data from ES
> function getDataFromES(url, method, postData, successCallback, failCallback, numRetry) {
>   var options = {};
>   var command = '';
>
>   if (method === 'DELETE') {
>     command = 'curl -X DELETE --max-time 20 -H "Accept-Encoding: gzip,deflate" "' + url + '"';
>   } else if (method === 'POST' || method === 'PUT') {
>     command = 'curl --max-time 20 -H "Accept-Encoding: gzip,deflate" -d \'' + postData + '\' "' + url + '"';
>   } else {
>     command = 'curl --max-time 20 -H "Accept-Encoding: gzip,deflate" "' + url + '"';
>   }
>
>   exec(command, function (err, stdout, stderr) {
>     if (!err) {
>       successCallback(stdout);
>     } else {
>       if (numRetry <= 0) failCallback();
>       else getDataFromES(url, method, postData, successCallback, failCallback, numRetry - 1);
>     }
>   });
> }
>
90,102c119,160
<   var path = ('../src/' + req.url.pathname).replace('//','/').replace(/%(..)/g, function(match, hex){
<     return String.fromCharCode(parseInt(hex, 16));
<   });
<   var parts = path.split('/');
<   if (parts[parts.length-1].charAt(0) === '.')
<     return self.sendForbidden_(req, res, path);
<   fs.stat(path, function(err, stat) {
<     if (err)
<       return self.sendMissing_(req, res, path);
<     if (stat.isDirectory())
<       return self.sendDirectory_(req, res, path);
<     return self.sendFile_(req, res, path);
<   });
---
>
>   // Reverse Proxy
>   if (/^\/proxy/.test(req.url.pathname)) {
>     var reqpath = req.url.pathname.substr(7);
>     var requrl = 'http://' + reqpath;
>
>     // Define callback funcs
>     var successCallback = function (result) {
>       res.writeHead(200, {'Content-Type' : 'application/json; charset=utf-8'});
>       res.end(result);
>     };
>     var failCallback = function () {
>       res.writeHead(503, {'Content-Type' : 'application/json; charset=utf-8'});
>       res.end("ERROR");
>     };
>
>     if (req.method === 'POST' || req.method === 'PUT') {
>       var postData='';
>       req.on('data', function (data) { postData += data; });
>       req.on('end',function(){
>         getDataFromES(requrl, 'POST', postData, successCallback, failCallback, 3);
>       });
>     } else {
>       getDataFromES(requrl, req.method, '', successCallback, failCallback, 3);
>     }
>   // Normal Kibana Function
>   } else {
>     if (req.url.pathname === '/') req.url.pathname = '/index.html';
>     var path = (__dirname + '/' + req.url.pathname).replace('//','/').replace(/%(..)/g, function(match, hex){
>       return String.fromCharCode(parseInt(hex, 16));
>     });
>     var parts = path.split('/');
>     if (parts[parts.length-1].charAt(0) === '.')
>       return self.sendForbidden_(req, res, path);
>     fs.stat(path, function(err, stat) {
>       if (err)
>         return self.sendMissing_(req, res, path);
>       if (stat.isDirectory())
>         return self.sendDirectory_(req, res, path);
>       return self.sendFile_(req, res, path);
>     });
>   }
完全なソースコードはここ
zuqqhi2

某Web系の会社でエンジニアをやっています。 学術的なことに非常に興味があります。 趣味は楽器演奏、ジョギング、読書、料理などなど手広くやっています。