forked from horaci/node-mitm-proxy
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathhttps_cache.js
119 lines (102 loc) · 4.2 KB
/
https_cache.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
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
/*
* https_cache.js
*
* Copyright (C) 2012, Bich C. Le, all rights reserved.
*
*/
var request = require('request');
var https = require('https');
var util = require('util');
var fs = require('fs');
var cg = require('certgen');
var events = require('events');
var srvCache = {};
var log = null;
//------------------------------------------------------------------------------------------------
function create_server(certOpts, appRequestCb) {
var https_srv = https.createServer(certOpts);
https_srv.on('error', function() {
log.error("error on https server?")
});
https_srv.on('request', appRequestCb);
return https_srv;
}
//------------------------------------------------------------------------------------------------
function queue_callback(emitter, remoteHostName, cb, appRequestCb) {
log.debug('Queuing cert lookup for ' + remoteHostName);
emitter.on('certDone', function onCertDone(err, server) {
if (err) {
log.error('Dequeuing lookup error for ' + remoteHostName);
return cb(err);
}
log.debug('Dequeuing cert lookup for ' + remoteHostName);
cb(null, server);
});
}
//------------------------------------------------------------------------------------------------
exports.init = function(logger) {
log = logger;
}
//------------------------------------------------------------------------------------------------
/*
* Return an https server with the correct certificate for the given hostname.
* options: global options, including external proxy
* remoteHostName: remote host name of the form 'host[:port]'
* cb: a callback of the form cb(err, https_srv), where the second parameter is an https instance
*/
exports.lookup = function (options, remoteHostName, cb, appRequestCb) {
var entry = srvCache[remoteHostName];
if (entry) {
if (entry instanceof https.Server) {
// The entry is an https server already in the cache
log.debug('Cache hit for ' + remoteHostName);
return process.nextTick(function () { cb(null, entry); });
}
// Ping in progress. The entry is a plain emitter.
return queue_callback(entry, remoteHostName, cb, appRequestCb);
}
// Use an event emitter to queue future requests while we're still waiting for the cert options
var emitter = new events.EventEmitter();
queue_callback(emitter, remoteHostName, cb, appRequestCb); // insert ourselves as first listener
srvCache[remoteHostName] = emitter;
var url = 'https://' + remoteHostName;
var pingOptions = { url: url,
proxy: options.external_proxy,
followRedirect: false,
jar: false,
method: 'HEAD' };
log.debug("Pinging remote with options: %j", pingOptions);
var ping = request(pingOptions, onPingResponse);
function onPingResponse(err, resp, body) {
if (err) {
delete srvCache[remoteHostName];
return emitter.emit('certDone', err);
}
log.debug ("Ping response code for %s is %d", url, resp.statusCode);
if (!ping.req.socket.getPeerCertificate) {
log.error('No certificate for ' + url);
log.error('Ping request: %j', ping);
delete srvCache[remoteHostName];
return emitter.emit('certDone', "Remote server " + url + " did not present a certificate");
}
var srvCert = ping.req.socket.getPeerCertificate();
log.trace("Server cert for %s:", url );
log.trace("Subject: %j", srvCert.subject);
log.trace("Issuer: %j", srvCert.issuer);
// Generate a new certificate with the same subject as the remote server's certificate
cg.generate_cert_buf(remoteHostName, options.keep_temp_files, srvCert, options.key_path,
options.cert_path, genCertCb);
function genCertCb(err, keyBuf, certBuf) {
var emitter = srvCache[remoteHostName];
if (err) {
delete srvCache[remoteHostName];
return emitter.emit('certDone', err);
}
// Replace the cache entry with the server, then emit event to finish
var opts = { key: keyBuf, cert: certBuf };
var server = create_server(opts, appRequestCb);
srvCache[remoteHostName] = server;
emitter.emit('certDone', null, server);
}
}
}