Skip to content

Instantly share code, notes, and snippets.

@shepherdwind
Created April 11, 2012 04:36
Show Gist options
  • Save shepherdwind/2356909 to your computer and use it in GitHub Desktop.
Save shepherdwind/2356909 to your computer and use it in GitHub Desktop.
proxy for node
#!/usr/bin/env node
/**
* Created by [email protected]
*
* 使用方式,依赖于node
* 1. 下载node.js, http://nodejs.org/#download
* 2. 在命令行下执行:
* node proxy.js
*
* 静态文件代理工具,基于node实现,实现原理,把a.tbcdn.cn通过host设置,转到本地
* node所创建的服务器中,由node来处理请求。比如访问浏览器请求url地址:
* http://a.tbcdn.cn/??a/a.css,p/global/global-min.css,b/b.css,
* 当前目录结构如下
* .
* |____a
* | |____a.css
* |____b
* | |____c.css
* |____p
* | |____global
* | | |____global-min.css
* |____proxy.js
*
* 那么,通过proxy.js,a/a.css, p/global/global-min.css会请求本地的文件,然后返
* 回给浏览器,而b/b.css在本地目录中没有,则会从线上取文件。
* 通过host作用,访问a.tbcdn.cn可以进行配置,是否实行代理。
**/
"use strict";
var assert = require('assert'),
dns = require('dns'),
fs = require('fs'),
http = require('http'),
querystring = require('querystring');
var USE_LOCAL = true,
USE_PRE = false,
IP_PRE = '110.75.14.33',
IP_PUB,
PUB_SRV = 'assets.gslb.taobao.com',
MIME = {
js: 'application/x-javascript',
css: 'text/css',
txt: 'text/plain',
png: 'image/png',
gif: 'image/gif',
jpg: 'image/jpeg',
swf: 'application/x-shockwave-flash'
};
function getIp(cb) {//{{{
dns.resolve4(PUB_SRV, function (err, addresses) {
if (err) {
assert(!err, 'Can\'t connect to ' + PUB_SRV);
}
IP_PUB = addresses[0];
console.log('[status] pub server: ' + IP_PUB);
cb();
});
}//}}}
function setStatus(req, res) {//{{{
req.setEncoding('utf8');
var formData = '';
req.on('data', function (data) {
formData += data;
});
req.on('end', function (data) {
console.log('[status]' + formData);
formData = querystring.parse(formData);
USE_LOCAL = formData.on === 'true';
USE_PRE = formData.is_pre === 'true';
getStatus(req, res);
});
}//}}}
function getStatus(req, res) {//{{{
res.writeHead(200, {'Content-Type': 'text/html'});
res.end('<!doctype html><meta charset="utf-8"><title>CDN Proxy</title>' +
'<form method="post">' +
'<div class="control-group">' +
'<label class="control-label">本地代理开关</label>' +
'<div class="controls">' +
'<label class="radio"><input type="radio" name="on" value="true"' + (USE_LOCAL ? ' checked' : '') + '>开</label>' +
'<label class="radio"><input type="radio" name="on" value="false"' + (USE_LOCAL ? '' : ' checked') + '>关</label>' +
'</div>' +
'</div>' +
'<div class="control-group">' +
'<label class="control-label">远程文件地址:预发/线上</label>' +
'<div class="controls">' +
'<label class="radio"><input type="radio" name="is_pre" value="true"' + (USE_PRE ? ' checked' : '') + '>预发</label>' +
'<label class="radio"><input type="radio" name="is_pre" value="false"' + (USE_PRE ? '' : ' checked') + '>线上</label>' +
'</div>' +
'</div>' +
'<button type="submit">提交</button>' +
'</form>');
}//}}}
function proxy(req, res) {//{{{
var files = parse(req.url);
console.log('[list] ' + files);
getFiles(files,
function gotAll(fileContents) {
// 不能直接取1
// file: p/global/1.0/global-min.css 会出现bug
var suffix = files[0].split('.').pop();
res.writeHead(200, {'Content-Type': MIME[suffix] || MIME.txt});
fileContents.forEach(function (file) {
res.write(file);
});
res.end();
},
function doh(err) {
res.writeHead(200, {'Content-Type': MIME.txt});
res.end(err.message);
}
);
}//}}}
function parse(url) {//{{{
var ret = [],
combo = url.indexOf('??'),
base, files;
if (-1 !== combo) {
base = url.slice(0, combo);
files = url.slice(combo + 2);
files = files.split('?')[0];
files = files.split('#')[0];
files = files.split(',');
files.forEach(function (file) {
ret.push(base + file);
});
} else {
url = url.split('?')[0];
url = url.split('#')[0];
ret.push(url);
}
return ret;
}//}}}
function getFiles(files, success, fail) {//{{{
var len = files.length,
rets = [];
function cb(index) {
return function set(ret) {
if (ret instanceof Error) {
fail(ret);
} else {
rets[index] = ret;
var k = len,
finished = true;
while (finished && k--) {
finished = finished && undefined !== rets[k];
}
if (finished) {
success(rets);
}
}
};
}
files.forEach(function (file, index) {
getFile(file, cb(index));
});
}//}}}
function getFile(file, cb) {//{{{
var fileContent = null;
if (USE_LOCAL) {
fileContent = getLocal(file);
}
if (null !== fileContent) {
cb(fileContent);
} else {
getRemote(file, cb);
}
}//}}}
function getRemote(file, cb) {//{{{
http.get({
headers: {
host: 'a.tbcdn.cn'
},
host: USE_PRE ? IP_PRE : IP_PUB,
port: 80,
path: file
}, function (res) {
var fileBuffer,
buffers = [],
size = 0;
res.on('data', function (data) {
size += data.length;
buffers.push(data);
});
res.on('end', function () {
var p = 0;
fileBuffer = new Buffer(size);
buffers.forEach(function (buffer) {
var len = buffer.length;
buffer.copy(fileBuffer, p, 0, len);
p += len;
});
cb(fileBuffer);
console.log('[remote] got ' + file);
});
}).on('error', function (e) {
cb(new Error('File not found: ' + file));
console.log('[remote] not got ' + file);
});
}//}}}
function getLocal(file) {//{{{
var ret;
try {
ret = fs.readFileSync('.' + file);
console.log('[local] found ' + file);
} catch (e) {
ret = null;
console.log('[local] not found ' + file);
}
return ret;
}//}}}
getIp(function () {//{{{
http.createServer(function (req, res) {
if (req.method === 'POST') {
setStatus(req, res);
} else if (req.url === '/') {
getStatus(req, res);
} else {
proxy(req, res);
}
}).listen(80);
});//}}}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment