forked from jpmckinney/image-proxy
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.js
96 lines (85 loc) · 3.41 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
// @see https://devcenter.heroku.com/articles/nodejs#write-your-app
var express = require('express')
, fs = require('fs') // node
, gm = require('gm')
, http = require('http') // node
, https = require('https') // node
, mime = require('mime') // express
, url = require('url') // node
// @see http://aaronheckmann.posterous.com/graphicsmagick-on-heroku-with-nodejs
, imageMagick = gm.subClass({imageMagick: true})
, app = express.createServer(express.logger())
, mimeTypes = [
'image/jpeg',
'image/png',
'image/gif',
'image/jpg',
];
app.get('/:url/', function (req, res, next) {
var retrieve = function (remote) {
// @see http://nodejs.org/api/url.html#url_url
var parts = url.parse(remote);
if (['https:', 'http:'].indexOf(parts.protocol) === -1) {
return res.send('Expected URI scheme to be HTTP or HTTPS', 404);
}
if (!parts.hostname) {
return res.send('Expected URI host to be non-empty', 404);
}
var agent = parts.protocol === 'http:' ? http : https
// @see http://nodejs.org/api/http.html#http_http_get_options_callback
, request = agent.get(remote, function (res2) {
// @see http://nodejs.org/api/http.html#http_response_statuscode
if ([301, 302].indexOf(res2.statusCode) !== -1 && res2.headers['location']) {
return retrieve(res2.headers['location']);
}
// The remote image must return status code 200.
if (res2.statusCode !== 200) {
return res.send('Expected response code 200, got ' + res2.statusCode, 404);
}
// The remote image must be a valid content type.
// @see http://nodejs.org/api/http.html#http_request_headers
var mimeType = res2.headers['content-type'].replace(/;.+/, '');
if (mimeTypes.indexOf(mimeType) === -1) {
return res.send('Expected content type ' + mimeTypes.join(', ') + ', got ' + mimeType, 404);
}
// @see https://github.com/aheckmann/gm#constructor
imageMagick(res2, 'image.' + mime.extension(mimeType))
// @see http://www.imagemagick.org/Usage/thumbnails/#cut
.stream(function (err, stdout, stderr) {
if (err) return next(err);
// Log errors in production.
stderr.pipe(process.stderr);
// @see http://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/Expiration.html
res.writeHead(200, {
'Content-Type': mimeType,
'Cache-Control': 'max-age=31536000, public', // 1 year
});
stdout.pipe(res);
});
}).on('error', next);
// Timeout after five seconds. Better luck next time.
request.setTimeout(5000, function () {
return res.send(504);
});
};
// Validate query string parameters.
if (whitelist.length) {
var parts = url.parse(req.params.url)
, any = false;
for (var _i = 0, _len = whitelist.length; _i < _len; _i++) {
if (whitelist[_i].test(parts.hostname)) {
any = true;
break;
}
}
if (!any) { // if none
return res.send('Expected URI host to be whitelisted', 404);
}
}
retrieve(req.params.url);
});
var port = process.env.PORT || 5000;
var whitelist = process.env.WHITELIST || []; // [/\.gov$/, /google\.com$/]
app.listen(port, function () {
console.log('Listening on ' + port);
});