1 | var _ = require("underscore")
|
2 | var Net = require("net")
|
3 | var Tls = require("tls")
|
4 | var Http = require("http")
|
5 | var Https = require("https")
|
6 | var ClientRequest = Http.ClientRequest
|
7 | var Socket = Net.Socket
|
8 | var EventEmitter = require("events").EventEmitter
|
9 | var InternalSocket = require("./lib/internal_socket")
|
10 | var Stubs = require("./lib/stubs")
|
11 | var normalizeConnectArgs = Net._normalizeConnectArgs
|
12 | var createRequestAndResponse = Http._connectionListener
|
13 | module.exports = Mitm
|
14 |
|
15 | function Mitm() {
|
16 | if (!(this instanceof Mitm))
|
17 | return Mitm.apply(Object.create(Mitm.prototype), arguments).enable()
|
18 |
|
19 | this.stubs = new Stubs
|
20 | this.on("request", addCrossReferences)
|
21 |
|
22 | return this
|
23 | }
|
24 |
|
25 | Mitm.prototype.on = EventEmitter.prototype.on
|
26 | Mitm.prototype.once = EventEmitter.prototype.once
|
27 | Mitm.prototype.off = EventEmitter.prototype.off
|
28 | Mitm.prototype.removeListener = EventEmitter.prototype.removeListener
|
29 | Mitm.prototype.emit = EventEmitter.prototype.emit
|
30 |
|
31 | var NODE_0_10 = !!process.version.match(/^v0\.10\./)
|
32 |
|
33 | Mitm.prototype.enable = function() {
|
34 |
|
35 | var netConnect = connect.bind(this, Net.connect)
|
36 | this.stubs.stub(Net, "connect", netConnect)
|
37 | this.stubs.stub(Net, "createConnection", netConnect)
|
38 | this.stubs.stub(Http.Agent.prototype, "createConnection", netConnect)
|
39 |
|
40 | if (NODE_0_10) {
|
41 |
|
42 | this.stubs.stub(Http.globalAgent, "createConnection", netConnect)
|
43 |
|
44 |
|
45 |
|
46 |
|
47 | this.stubs.stub(Http.globalAgent, "maxSockets", Infinity)
|
48 | this.stubs.stub(Https.globalAgent, "maxSockets", Infinity)
|
49 | }
|
50 |
|
51 |
|
52 | this.stubs.stub(Tls, "connect", _.compose(authorize, netConnect))
|
53 |
|
54 |
|
55 |
|
56 |
|
57 | var onSocket = _.compose(ClientRequest.prototype.onSocket, request.bind(this))
|
58 | this.stubs.stub(ClientRequest.prototype, "onSocket", onSocket)
|
59 |
|
60 | return this
|
61 | }
|
62 |
|
63 | Mitm.prototype.disable = function() {
|
64 | return this.stubs.restore(), this
|
65 | }
|
66 |
|
67 | function connect(orig, opts, done) {
|
68 | var args = normalizeConnectArgs(Array.prototype.slice.call(arguments, 1))
|
69 | opts = args[0]; done = args[1]
|
70 |
|
71 | var sockets = InternalSocket.pair()
|
72 | var client = new Socket(_.defaults({handle: sockets[0]}, opts))
|
73 | client.bypass = bypass
|
74 |
|
75 | this.emit("connect", client, opts)
|
76 | if (client.bypassed) return orig.call(this, opts, done)
|
77 |
|
78 |
|
79 |
|
80 | if (done) client.once("connect", done)
|
81 |
|
82 | var server = client.server = new Socket({handle: sockets[1]})
|
83 | this.emit("connection", server, opts)
|
84 |
|
85 |
|
86 |
|
87 | process.nextTick(client.emit.bind(client, "connect"))
|
88 | process.nextTick(server.emit.bind(server, "connect"))
|
89 |
|
90 | return client
|
91 | }
|
92 |
|
93 | function authorize(socket) {
|
94 | return socket.authorized = true, socket
|
95 | }
|
96 |
|
97 | function bypass() { this.bypassed = true }
|
98 |
|
99 | function request(socket) {
|
100 | if (!socket.server) return socket
|
101 |
|
102 |
|
103 |
|
104 |
|
105 |
|
106 | var self = this
|
107 | if (NODE_0_10) {
|
108 | self = Object.create(this)
|
109 | self.emit = _.compose(process.nextTick, Function.bind.bind(this.emit, this))
|
110 | }
|
111 |
|
112 | return createRequestAndResponse.call(self, socket.server), socket
|
113 | }
|
114 |
|
115 | function addCrossReferences(req, res) { req.res = res; res.req = req }
|