├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── doc └── index.html ├── examples ├── basic.fallback.js └── basic.js ├── index.js ├── package.json └── tests ├── ssl ├── ssl.crt └── ssl.private.key └── unit.test.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011 Arnout Kazemier,3rd-Eden 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | doc: 2 | dox --title "FlashPolicyFileServer" lib/* > doc/index.html 3 | 4 | test: 5 | expresso -I lib $(TESTFLAGS) tests/*.test.js 6 | 7 | .PHONY: test doc -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## LOL, WUT? 2 | It basically allows you to allow or disallow Flash Player sockets from accessing your site. 3 | 4 | ## Installation 5 | 6 | ```bash 7 | npm install policyfile 8 | ``` 9 | ## Usage 10 | 11 | The server is based on the regular and know `net` and `http` server patterns. So it you can just listen 12 | for all the events that a `net` based server emits etc. But there is one extra event, the `connect_failed` 13 | event. This event is triggered when we are unable to listen on the supplied port number. 14 | 15 | ### createServer 16 | Creates a new server instance and accepts 2 optional arguments: 17 | 18 | - `options` **Object** Options to configure the server instance 19 | - `log` **Boolean** Enable logging to STDOUT and STDERR (defaults to true) 20 | - `origins` **Array** An Array of origins that are allowed by the server (defaults to *:*) 21 | 22 | ```js 23 | var pf = require('policyfile').createServer(); 24 | 25 | pf.listen(); 26 | ``` 27 | 28 | #### server.listen 29 | Start listening on the server and it takes 3 optional arguments 30 | 31 | - `port` **Number** On which port number should we listen? (defaults to 843, which is the first port number the FlashPlayer checks) 32 | - `server` **Server** A http server, if we are unable to accept requests or run the server we can also answer the policy requests inline over the supplied HTTP server. 33 | - `callback` **Function** A callback function that is called when listening to the server was successful. 34 | 35 | ```js 36 | var pf = require('policyfile').createServer(); 37 | 38 | pf.listen(1337, function(){ 39 | console.log(':3 yay') 40 | }); 41 | ``` 42 | 43 | Changing port numbers can be handy if you do not want to run your server as root and have port 843 forward to a non root port number (aka a number above 1024). 44 | 45 | ```js 46 | var pf = require('policyfile').createServer() 47 | , http = require('http'); 48 | 49 | server = http.createServer(function(q,r){r.writeHead(200);r.end('hello world')}); 50 | server.listen(80); 51 | 52 | pf.listen(1337, server, function(){ 53 | console.log(':3 yay') 54 | }); 55 | ``` 56 | 57 | Support for serving inline requests over a existing HTTP connection as the FlashPlayer will first check port 843, but if it's unable to get a response there it will send a policy file request over port 80, which is usually your http server. 58 | 59 | #### server.add 60 | Adds more origins to the policy file you can add as many arguments as you like. 61 | 62 | ```js 63 | var pf = require('policyfile').createServer(['google.com:80']); 64 | 65 | pf.listen(); 66 | pf.add('blog.3rd-Eden.com:80', 'blog.3rd-Eden.com:8080'); // now has 3 origins 67 | ``` 68 | 69 | #### server.remove 70 | Removes added origins from the policy file - you can add as many arguments as you like. 71 | 72 | ```js 73 | var pf = require('policyfile').createServer(['blog.3rd-Eden.com:80', 'blog.3rd-Eden.com:8080']); 74 | 75 | pf.listen(); 76 | pf.remove('blog.3rd-Eden.com:8080'); // only contains the :80 version now 77 | ``` 78 | 79 | #### server.close 80 | Shuts down the server 81 | 82 | ```js 83 | var pf = require('policyfile').createServer(); 84 | 85 | pf.listen(); 86 | pf.close(); // OH NVM. 87 | ``` 88 | 89 | ## API 90 | http://3rd-eden.com/FlashPolicyFileServer/ 91 | 92 | ## Examples 93 | See https://github.com/3rd-Eden/FlashPolicyFileServer/tree/master/examples for examples 94 | 95 | ## Licence 96 | 97 | MIT see LICENSE file in the repository -------------------------------------------------------------------------------- /doc/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | FlashPolicyFileServer 4 | 5 | 99 | 109 | 110 | 111 | 112 | 116 | 120 | 121 | 122 | 133 | 196 | 197 | 198 | 205 | 259 | 260 | 261 | 268 | 293 | 294 | 295 | 302 | 315 | 316 | 317 | 322 | 331 | 332 | 333 | 337 | 348 | 349 | 350 | 357 | 370 | 371 | 372 | 379 | 382 | 383 | 384 | 391 | 395 | 396 |

FlashPolicyFileServer

server

lib/server.js
113 |

Module dependencies and cached references. 114 |

115 |
117 |
var slice = Array.prototype.slice
118 |   , net = require('net');
119 |
123 |

The server that does the Policy File severing

124 | 125 |

Options

126 | 127 |
  • log false or a function that can output log information, defaults to console.log?
128 | 129 |

130 | 131 |
  • param: Object options Options to customize the servers functionality.

  • param: Array origins The origins that are allowed on this server, defaults to *:*.

  • api: public

132 |
134 |
function Server (options, origins) {
135 |   var me = this;
136 | 
137 |   this.origins = origins || ['*:*'];
138 |   this.port = 843;
139 |   this.log = console.log;
140 | 
141 |   // merge `this` with the options
142 |   Object.keys(options).forEach(function (key) {
143 |     me[key] && (me[key] = options[key])
144 |   });
145 | 
146 |   // create the net server
147 |   this.socket = net.createServer(function createServer (socket) {
148 |     socket.on('error', function socketError () { 
149 |       me.responder.call(me, socket);
150 |     });
151 | 
152 |     me.responder.call(me, socket);
153 |   });
154 | 
155 |   // Listen for errors as the port might be blocked because we do not have root priv.
156 |   this.socket.on('error', function serverError (err) {
157 |     // Special and common case error handling
158 |     if (err.errno == 13) {
159 |       me.log && me.log(
160 |         'Unable to listen to port `' + me.port + '` as your Node.js instance does not have root privileges. ' +
161 |         (
162 |           me.server
163 |           ? 'The Flash Policy File requests will only be served inline over the supplied HTTP server. Inline serving is slower than a dedicated server instance.'
164 |           : 'No fallback server supplied, we will be unable to answer Flash Policy File requests.'
165 |         )
166 |       );
167 | 
168 |       me.emit('connect_failed', err);
169 |       me.socket.removeAllListeners();
170 |       delete me.socket;
171 |     } else {
172 |       me.log && me.log('FlashPolicyFileServer received an error event:\n' + (err.message ? err.message : err));
173 |     }
174 |   });
175 | 
176 |   this.socket.on('timeout', function serverTimeout () {});
177 |   this.socket.on('close', function serverClosed (err) {
178 |     err && me.log && me.log('Server closing due to an error: \n' + (err.message ? err.message : err));
179 | 
180 |     if (me.server) {
181 |       // Remove the inline policy listener if we close down
182 |       // but only when the server was `online` (see listen prototype)
183 |       if (me.server['@'] && me.server.online) {
184 |         me.server.removeListener('connection', me.server['@']);
185 |       }
186 | 
187 |       // not online anymore
188 |       delete me.server.online;
189 |     }
190 |   });
191 | 
192 |   // Compile the initial `buffer`
193 |   this.compile();
194 | }
195 |
199 |

Start listening for requests

200 | 201 |

202 | 203 |
  • param: Number port The port number it should be listening to.

  • param: Server server A HTTP server instance, this will be used to listen for inline requests

  • param: Function cb The callback needs to be called once server is ready

  • api: public

204 |
206 |
Server.prototype.listen = function listen (port, server, cb){
207 |   var me = this
208 |     , args = slice.call(arguments, 0)
209 |     , callback;
210 |  
211 |   // assign the correct vars, for flexible arguments
212 |   args.forEach(function args (arg){
213 |     var type = typeof arg;
214 | 
215 |     if (type === 'number') me.port = arg;
216 |     if (type === 'function') callback = arg;
217 |     if (type === 'object') me.server = arg;
218 |   });
219 | 
220 |   if (this.server) {
221 | 
222 |     // no one in their right mind would ever create a `@` prototype, so Im just gonna store
223 |     // my function on the server, so I can remove it later again once the server(s) closes
224 |     this.server['@'] = function connection (socket) {
225 |       socket.once('data', function requestData (data) {
226 |         // if it's a Flash policy request, and we can write to the 
227 |         if (
228 |              data
229 |           && data[0] === 60
230 |           &amp;&amp; data.toString() === '<policy-file-request/>\0'
231 |           &amp;&amp; socket
232 |           &amp;&amp; (socket.readyState === 'open' || socket.readyState === 'writeOnly')
233 |         ){
234 |           // send the buffer
235 |           try { socket.end(me.buffer); }
236 |           catch (e) {}
237 |         }
238 |       });
239 |     };
240 | 
241 |     // attach it
242 |     this.server.on('connection', this.server['@']);
243 |   }
244 | 
245 |   // We add a callback method, so we can set a flag for when the server is `enabled` or `online`.
246 |   // this flag is needed because if a error occurs and the we cannot boot up the server the
247 |   // fallback functionality should not be removed during the `close` event
248 |   this.port &gt;= 0 &amp;&amp; this.socket.listen(this.port, function serverListening () {
249 |     me.socket.online = true;
250 |     if (callback) {
251 |       callback.call(me);
252 |       callback = undefined;
253 |     }
254 |   });
255 | 
256 |   return this;
257 | };
258 |
262 |

Adds a new origin to the Flash Policy File.

263 | 264 |

265 | 266 |
  • param: Arguments The origins that need to be added.

  • api: public

267 |
269 |
Server.prototype.add = function add(){
270 |   var args = slice.call(arguments, 0)
271 |     , i = args.length;
272 | 
273 |   // flag duplicates
274 |   while (i--) {
275 |     if (this.origins.indexOf(args[i]) &gt;= 0){
276 |       args[i] = null;
277 |     }
278 |   }
279 | 
280 |   // Add all the arguments to the array
281 |   // but first we want to remove all `falsy` values from the args
282 |   Array.prototype.push.apply(
283 |     this.origins
284 |   , args.filter(function filter (value) {
285 |       return !!value;
286 |     })
287 |   );
288 | 
289 |   this.compile();
290 |   return this;
291 | };
292 |
296 |

Removes a origin from the Flash Policy File.

297 | 298 |

299 | 300 |
  • param: String origin The origin that needs to be removed from the server

  • api: public

301 |
303 |
Server.prototype.remove = function remove (origin){
304 |   var position = this.origins.indexOf(origin);
305 | 
306 |   // only remove and recompile if we have a match
307 |   if (position &gt; 0) {
308 |     this.origins.splice(position, 1);
309 |     this.compile();
310 |   }
311 | 
312 |   return this;
313 | };
314 |
318 |

Closes and cleans up the server

319 | 320 |
  • api: public

321 |
323 |
Server.prototype.close = function close () {
324 |   this.socket.removeAllListeners();
325 |   try { this.socket.close(); }
326 |   catch (e) {}
327 | 
328 |   return this;
329 | };
330 |
334 |

Proxy the event listener requests to the created Net server 335 |

336 |
338 |
Object.keys(process.EventEmitter.prototype).forEach(function proxy (key){
339 |   Server.prototype[key] = Server.prototype[key] || function () {
340 |     if (this.socket) {
341 |       this.socket[key].apply(this.socket, arguments);
342 |     }
343 | 
344 |     return this;
345 |   };
346 | });
347 |
351 |

Creates a new server instance.

352 | 353 |

354 | 355 |
  • param: Object options A options object to override the default config

  • param: Array origins The origins that should be allowed by the server

  • api: public

356 |
358 |
exports.createServer = function createServer (options, origins) {
359 |   origins = Array.isArray(origins)
360 |       ? origins
361 |       : (Array.isArray(options) ? options : false);
362 | 
363 |   options = !Array.isArray(options) &amp;&amp; options 
364 |       ? options
365 |       : {};
366 | 
367 |   return new Server(options, origins);
368 | };
369 |
373 |

Provide a hook to the original server, so it can be extended if needed.

374 | 375 |

376 | 377 |
  • type: Net.Server

378 |
380 |
exports.Server = Server;
381 |
385 |

Module version

386 | 387 |

388 | 389 |
  • type: String

390 |
392 |
exports.version = '0.0.5';
393 | 
394 |
-------------------------------------------------------------------------------- /examples/basic.fallback.js: -------------------------------------------------------------------------------- 1 | var http = require('http') 2 | , fspfs = require('../'); 3 | 4 | var server = http.createServer(function(q,r){ r.writeHead(200); r.end(':3') }) 5 | , flash = fspfs.createServer(); 6 | 7 | server.listen(8080); 8 | flash.listen(8081,server); -------------------------------------------------------------------------------- /examples/basic.js: -------------------------------------------------------------------------------- 1 | var http = require('http') 2 | , fspfs = require('../'); 3 | 4 | var flash = fspfs.createServer(); 5 | flash.listen(); -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Module dependencies and cached references. 3 | */ 4 | 5 | var slice = Array.prototype.slice 6 | , EventEmitter = require('events') 7 | , net = require('net'); 8 | 9 | /** 10 | * The server that does the Policy File severing 11 | * 12 | * Options: 13 | * - `log` false or a function that can output log information, defaults to console.log? 14 | * 15 | * @param {Object} options Options to customize the servers functionality. 16 | * @param {Array} origins The origins that are allowed on this server, defaults to `*:*`. 17 | * @api public 18 | */ 19 | 20 | function Server (options, origins) { 21 | var me = this; 22 | 23 | this.origins = origins || ['*:*']; 24 | this.port = 843; 25 | this.log = console.log; 26 | 27 | // merge `this` with the options 28 | Object.keys(options).forEach(function (key) { 29 | me[key] && (me[key] = options[key]) 30 | }); 31 | 32 | // create the net server 33 | this.socket = net.createServer(function createServer (socket) { 34 | socket.on('error', function socketError () { 35 | me.responder.call(me, socket); 36 | }); 37 | 38 | me.responder.call(me, socket); 39 | }); 40 | 41 | // Listen for errors as the port might be blocked because we do not have root priv. 42 | this.socket.on('error', function serverError (err) { 43 | // Special and common case error handling 44 | if (err.errno == 13) { 45 | me.log && me.log( 46 | 'Unable to listen to port `' + me.port + '` as your Node.js instance does not have root privileges. ' + 47 | ( 48 | me.server 49 | ? 'The Flash Policy File requests will only be served inline over the supplied HTTP server. Inline serving is slower than a dedicated server instance.' 50 | : 'No fallback server supplied, we will be unable to answer Flash Policy File requests.' 51 | ) 52 | ); 53 | 54 | me.emit('connect_failed', err); 55 | me.socket.removeAllListeners(); 56 | delete me.socket; 57 | } else { 58 | me.log && me.log('FlashPolicyFileServer received an error event:\n' + (err.message ? err.message : err)); 59 | } 60 | }); 61 | 62 | this.socket.on('timeout', function serverTimeout () {}); 63 | this.socket.on('close', function serverClosed (err) { 64 | err && me.log && me.log('Server closing due to an error: \n' + (err.message ? err.message : err)); 65 | 66 | if (me.server) { 67 | // Remove the inline policy listener if we close down 68 | // but only when the server was `online` (see listen prototype) 69 | if (me.server['@'] && me.server.online) { 70 | me.server.removeListener('connection', me.server['@']); 71 | } 72 | 73 | // not online anymore 74 | delete me.server.online; 75 | } 76 | }); 77 | 78 | // Compile the initial `buffer` 79 | this.compile(); 80 | } 81 | 82 | /** 83 | * Start listening for requests 84 | * 85 | * @param {Number} port The port number it should be listening to. 86 | * @param {Server} server A HTTP server instance, this will be used to listen for inline requests 87 | * @param {Function} cb The callback needs to be called once server is ready 88 | * @api public 89 | */ 90 | 91 | Server.prototype.listen = function listen (port, server, cb){ 92 | var me = this 93 | , args = slice.call(arguments, 0) 94 | , callback; 95 | 96 | // assign the correct vars, for flexible arguments 97 | args.forEach(function args (arg){ 98 | var type = typeof arg; 99 | 100 | if (type === 'number') me.port = arg; 101 | if (type === 'function') callback = arg; 102 | if (type === 'object') me.server = arg; 103 | }); 104 | 105 | if (this.server) { 106 | 107 | // no one in their right mind would ever create a `@` prototype, so Im just gonna store 108 | // my function on the server, so I can remove it later again once the server(s) closes 109 | this.server['@'] = function connection (socket) { 110 | socket.once('data', function requestData (data) { 111 | // if it's a Flash policy request, and we can write to the 112 | if ( 113 | data 114 | && data[0] === 60 115 | && data.toString() === '\0' 116 | && socket 117 | && (socket.readyState === 'open' || socket.readyState === 'writeOnly') 118 | ){ 119 | // send the buffer 120 | try { socket.end(me.buffer); } 121 | catch (e) {} 122 | } 123 | }); 124 | }; 125 | 126 | // attach it 127 | this.server.on('connection', this.server['@']); 128 | } 129 | 130 | // We add a callback method, so we can set a flag for when the server is `enabled` or `online`. 131 | // this flag is needed because if a error occurs and the we cannot boot up the server the 132 | // fallback functionality should not be removed during the `close` event 133 | this.port >= 0 && this.socket.listen(this.port, function serverListening () { 134 | me.socket.online = true; 135 | if (callback) { 136 | callback.call(me); 137 | callback = undefined; 138 | } 139 | }); 140 | 141 | return this; 142 | }; 143 | 144 | /** 145 | * Responds to socket connects and writes the compile policy file. 146 | * 147 | * @param {net.Socket} socket The socket that needs to receive the message 148 | * @api private 149 | */ 150 | 151 | Server.prototype.responder = function responder (socket){ 152 | if (socket && socket.readyState == 'open' && socket.end) { 153 | try { socket.end(this.buffer); } 154 | catch (e) {} 155 | } 156 | }; 157 | 158 | /** 159 | * Compiles the supplied origins to a Flash Policy File format and stores it in a Node.js Buffer 160 | * this way it can be send over the wire without any performance loss. 161 | * 162 | * @api private 163 | */ 164 | 165 | Server.prototype.compile = function compile (){ 166 | var xml = [ 167 | '' 168 | , '' 169 | , '' 170 | ]; 171 | 172 | // add the allow access element 173 | this.origins.forEach(function origin (origin){ 174 | var parts = origin.split(':'); 175 | xml.push(''); 176 | }); 177 | 178 | xml.push(''); 179 | 180 | // store the result in a buffer so we don't have to re-generate it all the time 181 | this.buffer = new Buffer(xml.join(''), 'utf8'); 182 | 183 | return this; 184 | }; 185 | 186 | /** 187 | * Adds a new origin to the Flash Policy File. 188 | * 189 | * @param {Arguments} The origins that need to be added. 190 | * @api public 191 | */ 192 | 193 | Server.prototype.add = function add(){ 194 | var args = slice.call(arguments, 0) 195 | , i = args.length; 196 | 197 | // flag duplicates 198 | while (i--) { 199 | if (this.origins.indexOf(args[i]) >= 0){ 200 | args[i] = null; 201 | } 202 | } 203 | 204 | // Add all the arguments to the array 205 | // but first we want to remove all `falsy` values from the args 206 | Array.prototype.push.apply( 207 | this.origins 208 | , args.filter(function filter (value) { 209 | return !!value; 210 | }) 211 | ); 212 | 213 | this.compile(); 214 | return this; 215 | }; 216 | 217 | /** 218 | * Removes a origin from the Flash Policy File. 219 | * 220 | * @param {String} origin The origin that needs to be removed from the server 221 | * @api public 222 | */ 223 | 224 | Server.prototype.remove = function remove (origin){ 225 | var position = this.origins.indexOf(origin); 226 | 227 | // only remove and recompile if we have a match 228 | if (position > 0) { 229 | this.origins.splice(position, 1); 230 | this.compile(); 231 | } 232 | 233 | return this; 234 | }; 235 | 236 | /** 237 | * Closes and cleans up the server 238 | * 239 | * @api public 240 | */ 241 | 242 | Server.prototype.close = function close () { 243 | this.socket.removeAllListeners(); 244 | try { this.socket.close(); } 245 | catch (e) {} 246 | 247 | return this; 248 | }; 249 | 250 | /** 251 | * Proxy the event listener requests to the created Net server 252 | */ 253 | 254 | Object.keys(EventEmitter.prototype).forEach(function proxy (key){ 255 | Server.prototype[key] = Server.prototype[key] || function () { 256 | if (this.socket) { 257 | this.socket[key].apply(this.socket, arguments); 258 | } 259 | 260 | return this; 261 | }; 262 | }); 263 | 264 | /** 265 | * Creates a new server instance. 266 | * 267 | * @param {Object} options A options object to override the default config 268 | * @param {Array} origins The origins that should be allowed by the server 269 | * @api public 270 | */ 271 | exports.createServer = function createServer (options, origins) { 272 | origins = Array.isArray(origins) 273 | ? origins 274 | : (Array.isArray(options) ? options : false); 275 | 276 | options = !Array.isArray(options) && options 277 | ? options 278 | : {}; 279 | 280 | return new Server(options, origins); 281 | }; 282 | 283 | /** 284 | * Provide a hook to the original server, so it can be extended if needed. 285 | * 286 | * @type {Net.Server} 287 | */ 288 | 289 | exports.Server = Server; 290 | 291 | /** 292 | * Module version 293 | * 294 | * @type {String} 295 | */ 296 | 297 | exports.version = '0.0.5'; 298 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "policyfile", 3 | "version": "0.0.6", 4 | "author": "Arnout Kazemier", 5 | "description": "Flash Socket Policy File Server. A server to respond to Flash Socket Policy requests, both inline and through a dedicated server instance.", 6 | "keywords": [ 7 | "flash", 8 | "socket", 9 | "policy", 10 | "file", 11 | "server", 12 | "Flash Socket Policy File Server", 13 | "cross domain" 14 | ], 15 | "maintainers": [ 16 | { 17 | "name": "Arnout Kazemier", 18 | "email": "info@3rd-Eden.com", 19 | "web": "http://blog.3rd-Eden.com" 20 | } 21 | ], 22 | "license": { 23 | "type": "MIT", 24 | "url": "https://github.com/3rd-Eden/FlashPolicyFileServer/blob/master/LICENSE" 25 | }, 26 | "repository": { 27 | "type": "git", 28 | "url": "https://github.com/3rd-Eden/FlashPolicyFileServer.git" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /tests/ssl/ssl.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDXTCCAkWgAwIBAgIJAMUSOvlaeyQHMA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNV 3 | BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX 4 | aWRnaXRzIFB0eSBMdGQwHhcNMTAxMTE2MDkzMjQ5WhcNMTMxMTE1MDkzMjQ5WjBF 5 | MQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50 6 | ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB 7 | CgKCAQEAz+LXZOjcQCJq3+ZKUFabj71oo/ex/XsBcFqtBThjjTw9CVEVwfPQQp4X 8 | wtPiB204vnYXwQ1/R2NdTQqCZu47l79LssL/u2a5Y9+0NEU3nQA5qdt+1FAE0c5o 9 | exPimXOrR3GWfKz7PmZ2O0117IeCUUXPG5U8umhDe/4mDF4ZNJiKc404WthquTqg 10 | S7rLQZHhZ6D0EnGnOkzlmxJMYPNHSOY1/6ivdNUUcC87awNEA3lgfhy25IyBK3QJ 11 | c+aYKNTbt70Lery3bu2wWLFGtmNiGlQTS4JsxImRsECTI727ObS7/FWAQsqW+COL 12 | 0Sa5BuMFrFIpjPrEe0ih7vRRbdmXRwIDAQABo1AwTjAdBgNVHQ4EFgQUDnV4d6mD 13 | tOnluLoCjkUHTX/n4agwHwYDVR0jBBgwFoAUDnV4d6mDtOnluLoCjkUHTX/n4agw 14 | DAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEAFwV4MQfTo+qMv9JMiyno 15 | IEiqfOz4RgtmBqRnXUffcjS2dhc7/z+FPZnM79Kej8eLHoVfxCyWRHFlzm93vEdv 16 | wxOCrD13EDOi08OOZfxWyIlCa6Bg8cMAKqQzd2OvQOWqlRWBTThBJIhWflU33izX 17 | Qn5GdmYqhfpc+9ZHHGhvXNydtRQkdxVK2dZNzLBvBlLlRmtoClU7xm3A+/5dddeP 18 | AQHEPtyFlUw49VYtZ3ru6KqPms7MKvcRhYLsy9rwSfuuniMlx4d0bDR7TOkw0QQS 19 | A0N8MGQRQpzl4mw4jLzyM5d5QtuGBh2P6hPGa0YQxtI3RPT/p6ENzzBiAKXiSfzo 20 | xw== 21 | -----END CERTIFICATE----- 22 | -------------------------------------------------------------------------------- /tests/ssl/ssl.private.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEowIBAAKCAQEAz+LXZOjcQCJq3+ZKUFabj71oo/ex/XsBcFqtBThjjTw9CVEV 3 | wfPQQp4XwtPiB204vnYXwQ1/R2NdTQqCZu47l79LssL/u2a5Y9+0NEU3nQA5qdt+ 4 | 1FAE0c5oexPimXOrR3GWfKz7PmZ2O0117IeCUUXPG5U8umhDe/4mDF4ZNJiKc404 5 | WthquTqgS7rLQZHhZ6D0EnGnOkzlmxJMYPNHSOY1/6ivdNUUcC87awNEA3lgfhy2 6 | 5IyBK3QJc+aYKNTbt70Lery3bu2wWLFGtmNiGlQTS4JsxImRsECTI727ObS7/FWA 7 | QsqW+COL0Sa5BuMFrFIpjPrEe0ih7vRRbdmXRwIDAQABAoIBAGe4+9VqZfJN+dsq 8 | 8Osyuz01uQ8OmC0sAWTIqUlQgENIyf9rCJsUBlYmwR5BT6Z69XP6QhHdpSK+TiAR 9 | XUz0EqG9HYzcxHIBaACP7j6iRoQ8R4kbbiWKo0z3WqQGIOqFjvD/mKEuQdE5mEYw 10 | eOUCG6BnX1WY2Yr8WKd2AA/tp0/Y4d8z04u9eodMpSTbHTzYMJb5SbBN1vo6FY7q 11 | 8zSuO0BMzXlAxUsCwHsk1GQHFr8Oh3zIR7bQGtMBouI+6Lhh7sjFYsfxJboqMTBV 12 | IKaA216M6ggHG7MU1/jeKcMGDmEfqQLQoyWp29rMK6TklUgipME2L3UD7vTyAVzz 13 | xbVOpZkCgYEA8CXW4sZBBrSSrLR5SB+Ubu9qNTggLowOsC/kVKB2WJ4+xooc5HQo 14 | mFhq1v/WxPQoWIxdYsfg2odlL+JclK5Qcy6vXmRSdAQ5lK9gBDKxZSYc3NwAw2HA 15 | zyHCTK+I0n8PBYQ+yGcrxu0WqTGnlLW+Otk4CejO34WlgHwbH9bbY5UCgYEA3ZvT 16 | C4+OoMHXlmICSt29zUrYiL33IWsR3/MaONxTEDuvgkOSXXQOl/8Ebd6Nu+3WbsSN 17 | bjiPC/JyL1YCVmijdvFpl4gjtgvfJifs4G+QHvO6YfsYoVANk4u6g6rUuBIOwNK4 18 | RwYxwDc0oysp+g7tPxoSgDHReEVKJNzGBe9NGGsCgYEA4O4QP4gCEA3B9BF2J5+s 19 | n9uPVxmiyvZUK6Iv8zP4pThTBBMIzNIf09G9AHPQ7djikU2nioY8jXKTzC3xGTHM 20 | GJZ5m6fLsu7iH+nDvSreDSeNkTBfZqGAvoGYQ8uGE+L+ZuRfCcXYsxIOT5s6o4c3 21 | Dle2rVFpsuKzCY00urW796ECgYBn3go75+xEwrYGQSer6WR1nTgCV29GVYXKPooy 22 | zmmMOT1Yw80NSkEw0pFD4cTyqVYREsTrPU0mn1sPfrOXxnGfZSVFpcR/Je9QVfQ7 23 | eW7GYxwfom335aqHVj10SxRqteP+UoWWnHujCPz94VRKZMakBddYCIGSan+G6YdS 24 | 7sdmwwKBgBc2qj0wvGXDF2kCLwSGfWoMf8CS1+5fIiUIdT1e/+7MfDdbmLMIFVjF 25 | QKS3zVViXCbrG5SY6wS9hxoc57f6E2A8vcaX6zy2xkZlGHQCpWRtEM5R01OWJQaH 26 | HsHMmQZGUQVoDm1oRkDhrTFK4K3ukc3rAxzeTZ96utOQN8/KJsTv 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /tests/unit.test.js: -------------------------------------------------------------------------------- 1 | var fspfs = require('../') 2 | , fs = require('fs') 3 | , http = require('http') 4 | , https = require('https') 5 | , net = require('net') 6 | , should = require('should') 7 | , assert = require('assert'); 8 | 9 | module.exports = { 10 | // Library version should be Semver compatible 11 | 'Library version': function(){ 12 | fspfs.version.should.match(/^\d+\.\d+\.\d+$/); 13 | } 14 | 15 | // Creating a server instace should not cause any problems 16 | // either using the new Server or createServer method. 17 | , 'Create Server instance': function(){ 18 | var server = fspfs.createServer() 19 | , server2 = new fspfs.Server({log:false}, ['blog.3rd-Eden.com:1337']); 20 | 21 | // server 2 options test 22 | server2.log.should.be.false; 23 | server2.origins.length.should.equal(1); 24 | server2.origins[0].should.equal('blog.3rd-Eden.com:1337'); 25 | 26 | // server defaults 27 | (typeof server.log).should.be.equal('function'); 28 | server.origins.length.should.equal(1); 29 | server.origins[0].should.equal('*:*'); 30 | 31 | // instance checking, sanity check 32 | assert.ok(server instanceof fspfs.Server); 33 | assert.ok(!!server.buffer); 34 | 35 | // more options testing 36 | server = fspfs.createServer(['blog.3rd-Eden.com:80']); 37 | server.origins.length.should.equal(1); 38 | server.origins[0].should.equal('blog.3rd-Eden.com:80'); 39 | 40 | server = fspfs.createServer({log:false},['blog.3rd-Eden.com:80']); 41 | server.log.should.be.false; 42 | server.origins.length.should.equal(1); 43 | server.origins[0].should.equal('blog.3rd-Eden.com:80'); 44 | 45 | } 46 | 47 | , 'Add origin': function(){ 48 | var server = fspfs.createServer(); 49 | server.add('google.com:80', 'blog.3rd-Eden.com:1337'); 50 | 51 | server.origins.length.should.equal(3); 52 | server.origins.indexOf('google.com:80').should.be.above(0); 53 | 54 | // don't allow duplicates 55 | server.add('google.com:80', 'google.com:80'); 56 | 57 | var i = server.origins.length 58 | , count = 0; 59 | 60 | while(i--){ 61 | if (server.origins[i] === 'google.com:80'){ 62 | count++; 63 | } 64 | } 65 | 66 | count.should.equal(1); 67 | } 68 | 69 | , 'Remove origin': function(){ 70 | var server = fspfs.createServer(); 71 | server.add('google.com:80', 'blog.3rd-Eden.com:1337'); 72 | server.origins.length.should.equal(3); 73 | 74 | server.remove('google.com:80'); 75 | server.origins.length.should.equal(2); 76 | server.origins.indexOf('google.com:80').should.equal(-1); 77 | } 78 | 79 | , 'Buffer': function(){ 80 | var server = fspfs.createServer(); 81 | 82 | Buffer.isBuffer(server.buffer).should.be.true; 83 | server.buffer.toString().indexOf('to-ports="*"').should.be.above(0); 84 | server.buffer.toString().indexOf('domain="*"').should.be.above(0); 85 | server.buffer.toString().indexOf('domain="google.com"').should.equal(-1); 86 | 87 | // The buffers should be rebuild when new origins are added 88 | server.add('google.com:80'); 89 | server.buffer.toString().indexOf('to-ports="80"').should.be.above(0); 90 | server.buffer.toString().indexOf('domain="google.com"').should.be.above(0); 91 | 92 | server.remove('google.com:80'); 93 | server.buffer.toString().indexOf('to-ports="80"').should.equal(-1); 94 | server.buffer.toString().indexOf('domain="google.com"').should.equal(-1); 95 | } 96 | 97 | , 'Responder': function(){ 98 | var server = fspfs.createServer() 99 | , calls = 0 100 | // dummy socket to emulate a `real` socket 101 | , dummySocket = { 102 | readyState: 'open' 103 | , end: function(buffer){ 104 | calls++; 105 | Buffer.isBuffer(buffer).should.be.true; 106 | buffer.toString().should.equal(server.buffer.toString()); 107 | } 108 | }; 109 | 110 | server.responder(dummySocket); 111 | calls.should.equal(1); 112 | } 113 | 114 | , 'Event proxy': function(){ 115 | var server = fspfs.createServer() 116 | , calls = 0; 117 | 118 | Object.keys(process.EventEmitter.prototype).forEach(function proxy(key){ 119 | assert.ok(!!server[key] && typeof server[key] === 'function'); 120 | }); 121 | 122 | // test if it works by calling a none default event 123 | server.on('pew', function(){ 124 | calls++; 125 | }); 126 | 127 | server.emit('pew'); 128 | calls.should.equal(1); 129 | } 130 | 131 | , 'inline response http': function(){ 132 | var port = 1335 133 | , httpserver = http.createServer(function(q,r){r.writeHead(200);r.end(':3')}) 134 | , server = fspfs.createServer(); 135 | 136 | httpserver.listen(port, function(){ 137 | server.listen(port + 1, httpserver, function(){ 138 | var client = net.createConnection(port); 139 | client.write('\0'); 140 | client.on('error', function(err){ 141 | assert.ok(!err, err) 142 | }); 143 | client.on('data', function(data){ 144 | 145 | var response = data.toString(); 146 | console.log(response); 147 | 148 | response.indexOf('to-ports="*"').should.be.above(0); 149 | response.indexOf('domain="*"').should.be.above(0); 150 | response.indexOf('domain="google.com"').should.equal(-1); 151 | 152 | // clean up 153 | client.destroy(); 154 | server.close(); 155 | httpserver.close(); 156 | }); 157 | }); 158 | }); 159 | } 160 | 161 | , 'server response': function(){ 162 | var port = 1340 163 | , server = fspfs.createServer(); 164 | 165 | server.listen(port, function(){ 166 | var client = net.createConnection(port); 167 | client.write('\0'); 168 | client.on('error', function(err){ 169 | assert.ok(!err, err) 170 | }); 171 | client.on('data', function(data){ 172 | 173 | var response = data.toString(); 174 | 175 | response.indexOf('to-ports="*"').should.be.above(0); 176 | response.indexOf('domain="*"').should.be.above(0); 177 | response.indexOf('domain="google.com"').should.equal(-1); 178 | 179 | // clean up 180 | client.destroy(); 181 | server.close(); 182 | }); 183 | }); 184 | } 185 | 186 | , 'inline response https': function(){ 187 | var port = 1345 188 | , ssl = { 189 | key: fs.readFileSync(__dirname + '/ssl/ssl.private.key').toString() 190 | , cert: fs.readFileSync(__dirname + '/ssl/ssl.crt').toString() 191 | } 192 | , httpserver = https.createServer(ssl, function(q,r){r.writeHead(200);r.end(':3')}) 193 | , server = fspfs.createServer(); 194 | 195 | httpserver.listen(port, function(){ 196 | server.listen(port + 1, httpserver, function(){ 197 | var client = net.createConnection(port); 198 | client.write('\0'); 199 | client.on('error', function(err){ 200 | assert.ok(!err, err) 201 | }); 202 | client.on('data', function(data){ 203 | 204 | var response = data.toString(); 205 | 206 | response.indexOf('to-ports="*"').should.be.above(0); 207 | response.indexOf('domain="*"').should.be.above(0); 208 | response.indexOf('domain="google.com"').should.equal(-1); 209 | 210 | // clean up 211 | client.destroy(); 212 | server.close(); 213 | httpserver.close(); 214 | }); 215 | }); 216 | }); 217 | } 218 | 219 | , 'connect_failed': function(){ 220 | var server = fspfs.createServer(); 221 | 222 | server.on('connect_failed', function(){ 223 | assert.ok(true); 224 | }); 225 | 226 | server.listen(function(){ 227 | assert.ok(false, 'Run this test without root access'); 228 | server.close(); 229 | }); 230 | } 231 | }; --------------------------------------------------------------------------------