1 | Mitm.js
|
2 | =======
|
3 | [![NPM version][npm-badge]](http://badge.fury.io/js/mitm)
|
4 | [![Build status][travis-badge]](https://travis-ci.org/moll/node-mitm)
|
5 | [npm-badge]: https://badge.fury.io/js/mitm.png
|
6 | [travis-badge]: https://travis-ci.org/moll/node-mitm.png?branch=master
|
7 |
|
8 | Mitm.js is a library for Node.js to **intercept and mock** outgoing network
|
9 | **TCP** and **HTTP** connections. Mitm.js intercepts and gives you
|
10 | a `Net.Socket` to communicate as if you were the remote server. For **HTTP
|
11 | requests** it even gives you `Http.IncomingMessage` and `Http.ServerResponse`
|
12 | — just like you're used to when writing Node.js servers. Except there's no
|
13 | actual server running, it's all just _In-Process Interception™_.
|
14 |
|
15 | Intercepting connections and requests is **extremely useful to test and ensure
|
16 | your code does what you expect**. Assert on request parameters and send back
|
17 | various responses to your code without ever having to hit the real network.
|
18 | **Fast as hell** and **a lot easier to develop with than external test
|
19 | servers**.
|
20 |
|
21 | Mitm.js should work both on the stable Node **v0.10.24** and up and **v0.11.11**
|
22 | and up and has **automated tests** to ensure it will stay that way.
|
23 |
|
24 | **Note**: This is a fairly early release of Mitm.js, so it might not cover all
|
25 | use cases you may come across. I've developed this on a need-to basis for
|
26 | testing [Monday Calendar][monday]'s syncing, so if you find a use-case I haven't
|
27 | come across, please fling me an [email][email], a [tweet][twitter] or [create an
|
28 | issue][issues] on GitHub.
|
29 |
|
30 | ### Tour
|
31 | - Intercept both **TCP socket connections** (`Net.connect`) and **HTTP
|
32 | requests** (`Http.request` and `Https.request`).
|
33 |
|
34 | - Hooks to Node.js's network functions at a **very low level** with the goal of
|
35 | not having to patch existing classes and have everything behave as if bytes
|
36 | were arriving from the network.
|
37 |
|
38 | - Does *not* have any kitchen sink features or yet another API to assert on
|
39 | intercepted connections.
|
40 | That's a different responsibility handled better by assertion libraries
|
41 | (you'll do no better than to pick [Must.js][must] for that ;-).
|
42 |
|
43 | - Use an **API you already know** to assert or respond to requests — Mitm.js
|
44 | gives you access to a vanilla `Net.Socket` to respond with:
|
45 |
|
46 | ```javascript
|
47 | mitm.on("connection", function(socket) { socket.write("Hello back!") })
|
48 |
|
49 | var socket = Net.connect(22, "example.org")
|
50 | socket.write("Hello!")
|
51 | socket.setEncoding("utf8")
|
52 | socket.read() // => "Hello back!"
|
53 | ```
|
54 |
|
55 | - When you do **HTTP or HTTPS** requests, Mitm.js gives you both
|
56 | a `Http.IncomingMessage` and `Http.ServerResponse` to play the server with.
|
57 | That means you'll be using an **API you're already familiar with**
|
58 | rather than yet another idiosyncratic domain specific language.
|
59 |
|
60 | Mitm.js comes very handy to ensure your code makes requests with the
|
61 | appropriate parameters:
|
62 | ```javascript
|
63 | mitm.on("request", function(req, res) {
|
64 | req.headers.authorization.must.equal("OAuth DEADBEEF")
|
65 | })
|
66 |
|
67 | Http.get("http://example.org")
|
68 | ```
|
69 |
|
70 | It's also useful to see if your code behaves as you'd expect if everything is
|
71 | not `200 OK`:
|
72 | ```javascript
|
73 | mitm.on("request", function(req, res) {
|
74 | res.statusCode = 402
|
75 | res.end("Pay up, sugar!")
|
76 | })
|
77 |
|
78 | Http.get("http://example.org", function(res) {
|
79 | res.setEncoding("utf8")
|
80 | res.statusCode // => 402
|
81 | res.on("data", console.log) // => "Pay up, sugar!"
|
82 | })
|
83 | ```
|
84 |
|
85 | `Http.IncomingMessage` and `Http.ServerResponse` are the same objects
|
86 | you get when you write Node.js HTTP servers with `Net.Server` or use a library
|
87 | like [Express.js][express].
|
88 |
|
89 | - **Bypass** interception selectively for some connections (such as your SQL
|
90 | server) and let them connect as usual.
|
91 | ```javascript
|
92 | mitm.on("connect", function(socket, opts) {
|
93 | if (opts.host == "sql.example.org" && opts.port = 5432) socket.bypass()
|
94 | })
|
95 | ```
|
96 |
|
97 | - **Developed with automated tests**. Yeah, I know, why should one list this
|
98 | a feature when writing tests is just a sign of professionalism and respect
|
99 | towards other developers? But in a world where so many libraries and
|
100 | "production" software are released without *any* tests, I like to point out
|
101 | that I even write tests for testing libraries. ;-)
|
102 |
|
103 | [must]: https://github.com/moll/js-must
|
104 | [express]: http://expressjs.com
|
105 |
|
106 |
|
107 | Installing
|
108 | ----------
|
109 | ```
|
110 | npm install mitm
|
111 | ```
|
112 |
|
113 | From v1.0.0 Mitm.js will follow [semantic versioning][semver], but until then,
|
114 | breaking changes may appear between minor versions (the middle number).
|
115 |
|
116 | [semver]: http://semver.org/
|
117 |
|
118 |
|
119 | Using
|
120 | -----
|
121 | Require Mitm.js and invoke it as a function to both create an instance of `Mitm`
|
122 | and enable intercepting:
|
123 | ```javascript
|
124 | var Mitm = require("mitm")
|
125 | var mitm = Mitm()
|
126 | ```
|
127 |
|
128 | Mitm.js will then intercept all requests until you disable it:
|
129 | ```javascript
|
130 | mitm.disable()
|
131 | ```
|
132 |
|
133 | ### Intercepting in tests
|
134 | In tests, it's best to use the _before_ and _after_ hooks to enable and disable
|
135 | intercepting for each test case:
|
136 | ```javascript
|
137 | beforeEach(function() { this.mitm = Mitm() })
|
138 | afterEach(function() { this.mitm.disable() })
|
139 | ```
|
140 |
|
141 | ### Intercepting TCP connections
|
142 | After you've called `Mitm()`, Mitm.js will intercept and emit `connection` on
|
143 | itself for each new connection.
|
144 | The `connection` event will be given a server side `Net.Socket` for you to reply
|
145 | with:
|
146 |
|
147 | ```javascript
|
148 | mitm.on("connection", function(socket) { socket.write("Hello back!") })
|
149 |
|
150 | var socket = Net.connect(22, "example.org")
|
151 | socket.write("Hello!")
|
152 | socket.setEncoding("utf8")
|
153 | socket.read() // => "Hello back!"
|
154 | ```
|
155 |
|
156 | ### Intercepting HTTP/HTTPS requests
|
157 | After you've called `Mitm()`, Mitm.js will intercept and emit `request` on itself for each new HTTP or HTTPS request.
|
158 | The `request` event will be given a server side `Http.IncomingMessage` and
|
159 | `Http.ServerResponse`.
|
160 |
|
161 | For example, asserting on HTTP requests would look something like this:
|
162 | ```javascript
|
163 | mitm.on("request", function(req, res) {
|
164 | req.headers.authorization.must.equal("OAuth DEADBEEF")
|
165 | })
|
166 |
|
167 | Http.get("http://example.org")
|
168 | ```
|
169 |
|
170 | Responding to requests is just as easy and exactly like you're used to from
|
171 | using Node.js HTTP servers (or from libraries like [Express.js][express]):
|
172 | ```javascript
|
173 | mitm.on("request", function(req, res) {
|
174 | res.statusCode = 402
|
175 | res.end("Pay up, sugar!")
|
176 | })
|
177 |
|
178 | Http.get("http://example.org", function(res) {
|
179 | res.statusCode // => 402
|
180 | res.setEncoding("utf8")
|
181 | res.on("data", console.log) // => "Pay up, sugar!"
|
182 | })
|
183 | ```
|
184 |
|
185 | Please note that HTTPS requests are currently "morphed" into HTTP requests.
|
186 | That's to save us from having to set up certificates and disable their
|
187 | verification. But if you do need to test this, please ping me and we'll see if
|
188 | we can get Mitm.js to support that.
|
189 |
|
190 | ### Bypassing interception
|
191 | You can bypass connections listening to the `connect` event on the Mitm instance
|
192 | and then calling `bypass` on the given socket. To help you do
|
193 | so selectively, `connect` is given the `options` object that was given to
|
194 | `Net.connect`:
|
195 |
|
196 | ```javascript
|
197 | mitm.on("connect", function(socket, opts) {
|
198 | if (opts.host == "sql.example.org" && opts.port = 5432) socket.bypass()
|
199 | })
|
200 | ```
|
201 |
|
202 | Bypassed connections do **not** emit `connection` or `request` events. They're
|
203 | ignored by Mitm.js.
|
204 |
|
205 | In most cases you don't need to bypass because by the time you call `Mitm` in
|
206 | your tests to start intercepting, all of the long-running connections, such as
|
207 | database or cache connections, are already made.
|
208 |
|
209 | You might need to bypass connections you make to *localhost* when you're running
|
210 | integration tests against the HTTP server you started in the test process, but
|
211 | still want to intercept some other connections that this request might invoke.
|
212 | The following should suffice:
|
213 |
|
214 | ```javascript
|
215 | mitm.on("connect", function(socket, opts) {
|
216 | if (opts.host == "localhost") socket.bypass()
|
217 | })
|
218 | ```
|
219 |
|
220 |
|
221 | Events
|
222 | ------
|
223 | All events that Mitm will emit on an instance of itself (see [Using
|
224 | Mitm.js](#using) for examples):
|
225 |
|
226 | Event | Description
|
227 | -----------|------------
|
228 | connect | Emitted when a TCP connection is made.<br> Given the client side `Net.Socket` and `options` from `Net.connect`.
|
229 | connection | Emitted when a TCP connection is made.<br> Given the server side `Net.Socket` and `options` from `Net.connect`.
|
230 | request | Emitted when a HTTP/HTTPS request is made.<br> Given the server side `Http.IncomingMessage` and `Http.ServerResponse`.
|
231 |
|
232 |
|
233 | License
|
234 | -------
|
235 | Mitm.js is released under a *Lesser GNU Affero General Public License*, which
|
236 | in summary means:
|
237 |
|
238 | - You **can** use this program for **no cost**.
|
239 | - You **can** use this program for **both personal and commercial reasons**.
|
240 | - You **do not have to share your own program's code** which uses this program.
|
241 | - You **have to share modifications** (e.g. bug-fixes) you've made to this
|
242 | program.
|
243 |
|
244 | For more convoluted language, see the `LICENSE` file.
|
245 |
|
246 |
|
247 | About
|
248 | -----
|
249 | **[Andri Möll][moll]** typed this and the code.
|
250 | [Monday Calendar][monday] supported the engineering work.
|
251 |
|
252 | If you find Mitm.js needs improving, please don't hesitate to type to me now
|
253 | at [andri@dot.ee][email] or [create an issue online][issues].
|
254 |
|
255 | [email]: mailto:andri@dot.ee
|
256 | [issues]: https://github.com/moll/node-mitm/issues
|
257 | [moll]: http://themoll.com
|
258 | [monday]: https://mondayapp.com
|
259 | [twitter]: https://twitter.com/theml
|