├── .gitignore ├── .travis.yml ├── CONTRIBUTING.md ├── LICENSE.md ├── README.md ├── index.js ├── package.json ├── support ├── server.crt └── server.key └── tests.js /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | 3 | # Logs 4 | logs 5 | *.log 6 | 7 | # Runtime data 8 | pids 9 | *.pid 10 | *.seed 11 | 12 | # Directory for instrumented libs generated by jscoverage/JSCover 13 | lib-cov 14 | 15 | # Coverage directory used by tools like istanbul 16 | coverage 17 | 18 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 19 | .grunt 20 | 21 | # Compiled binary addons (http://nodejs.org/api/addons.html) 22 | build/Release 23 | 24 | # Dependency directory 25 | # Commenting this out is preferred by some people, see 26 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git- 27 | node_modules 28 | 29 | # Users Environment Variables 30 | .lock-wscript 31 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "0.12" 4 | - "0.10" 5 | before_install: 6 | - npm install npm@latest -g 7 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # create-stream-server is an OPEN Open Source Project 2 | 3 | Individuals making significant and valuable contributions are given commit-access to the project to contribute as they see fit. This project is more like an open wiki than a standard guarded open source project. 4 | 5 | ## Rules 6 | 7 | There are a few basic ground-rules for contributors: 8 | 9 | 1. **No `--force` pushes** or modifying the Git history in any way. 10 | 1. **Non-master branches** ought to be used for ongoing work. 11 | 1. **External API changes and significant modifications** ought to be subject to an **internal pull-request** to solicit feedback from other contributors. 12 | 1. Internal pull-requests to solicit feedback are *encouraged* for any other non-trivial contribution but left to the discretion of the contributor. 13 | 1. Contributors should attempt to adhere to the prevailing code-style. 14 | 15 | ## Releases 16 | 17 | Declaring formal releases remains the prerogative of the project maintainer. 18 | 19 | ## Changes to this arrangement 20 | 21 | This is an experiment and feedback is welcome! This document may also be subject to pull-requests or changes by contributors where you believe you have something valuable to add or change. 22 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | 3 | ## Copyright (c) 2014-2015 create-stream-server contributors 4 | 5 | *create-stream-server contributors listed at * 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 8 | 9 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 10 | 11 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # create-stream-server  [![Build Status](https://travis-ci.org/mqttjs/create-stream-server.png)](https://travis-ci.org/mqttjs/create-stream-server) [![npm version](https://badge.fury.io/js/create-stream-server.svg)](http://badge.fury.io/js/create-stream-server) 2 | 3 | **create multiple stream servers easily** 4 | 5 | ## Example 6 | 7 | ```js 8 | var css = require('create-stream-server'); 9 | 10 | var servers = css({ 11 | s1: 'tcp://localhost:8080', 12 | s2: 'ssl://0.0.0.0:80', 13 | s3: { 14 | protocol: 'wss', 15 | host: 'localhost', 16 | port: 8888, 17 | ssl: { 18 | key: fs.readFileSync('./wss_server.key'), 19 | cert: fs.readFileSync('./wss_server.crt') 20 | } 21 | }, 22 | s4: { 23 | attach: existingHttpServer 24 | } 25 | }, { 26 | ssl: { 27 | key: fs.readFileSync('./server.key'), 28 | cert: fs.readFileSync('./server.crt') 29 | } 30 | }, function(clientStream, server){ 31 | // handle the connected client as a stream 32 | }); 33 | 34 | // to start 35 | servers.listen(function(){ 36 | console.log('launched!'); 37 | }); 38 | 39 | // after some time 40 | servers.close(function(){ 41 | console.log('done!'); 42 | }); 43 | 44 | // to release all resources 45 | servers.destroy(function(){ 46 | console.log('all gone!'); 47 | }); 48 | ``` 49 | 50 | ## Contributing 51 | 52 | create-stream-server is an **OPEN Open Source Project**. This means that: 53 | 54 | > Individuals making significant and valuable contributions are given commit-access to the project to contribute as they see fit. This project is more like an open wiki than a standard guarded open source project. 55 | 56 | See the [CONTRIBUTING.md](https://github.com/mqttjs/create-stream-server/blob/master/CONTRIBUTING.md) file for more details. 57 | 58 | ### Contributors 59 | 60 | create-stream-server is only possible due to the excellent work of the following contributors: 61 | 62 | 63 | 64 | 65 |
Joël GähwilerGitHub/256dpiTwitter/@256dpi
Matteo CollinaGitHub/mcollinaTwitter/@matteocollina
66 | 67 | ### License 68 | 69 | MIT 70 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | var net = require('net'); 2 | var tls = require('tls'); 3 | var http = require('http'); 4 | var https = require('https'); 5 | var ws = require('ws'); 6 | var url = require('url'); 7 | var wsStream = require('websocket-stream'); 8 | var async = require('async'); 9 | var enableDestroy = require('server-destroy'); 10 | 11 | function createServer(clientHandler) { 12 | return net.createServer(function(client){ 13 | clientHandler(client); 14 | }); 15 | } 16 | 17 | function createSecureServer(sslOptions, clientHandler){ 18 | return tls.createServer(sslOptions, function(client){ 19 | clientHandler(client); 20 | }); 21 | } 22 | 23 | function attachServer(server, clientHandler) { 24 | (new ws.Server({ 25 | server: server 26 | })).on('connection', function(ws) { 27 | clientHandler(wsStream(ws)); 28 | }); 29 | return server; 30 | } 31 | 32 | function createWebSocketServer(clientHandler){ 33 | var server = http.createServer(); 34 | attachServer(server, clientHandler); 35 | return server; 36 | } 37 | 38 | function createSecureWebSocketServer(sslOptions, clientHandler){ 39 | var server = https.createServer(sslOptions); 40 | attachServer(server, clientHandler); 41 | return server; 42 | } 43 | 44 | module.exports = function(serverConfig, sharedConfig, clientStreamHandler){ 45 | if(typeof sharedConfig == 'function') { 46 | clientStreamHandler = sharedConfig; 47 | sharedConfig = {}; 48 | } 49 | 50 | if(typeof sharedConfig != 'object') { 51 | sharedConfig = {}; 52 | } 53 | 54 | var servers = {}; 55 | 56 | Object.keys(serverConfig).forEach(function(id) { 57 | var config = serverConfig[id]; 58 | 59 | if(typeof config == 'string') { 60 | var c = url.parse(config); 61 | config = { 62 | protocol: c.protocol.replace(/:$/, ''), 63 | port: c.port, 64 | host: c.hostname 65 | }; 66 | } 67 | 68 | config.host = config.host || sharedConfig.host || 'localhost'; 69 | config.ssl = config.ssl || sharedConfig.ssl; 70 | 71 | var server; 72 | 73 | if(config.attach) { 74 | server = attachServer(config.attach, clientStreamHandler); 75 | server._css_exclude = true; 76 | } else if(config.protocol == 'tcp') { 77 | server = createServer(clientStreamHandler); 78 | } else if(config.protocol == 'ssl') { 79 | server = createSecureServer(config.ssl, clientStreamHandler); 80 | } else if(config.protocol == 'ws') { 81 | server = createWebSocketServer(clientStreamHandler); 82 | } else if(config.protocol == 'wss') { 83 | server = createSecureWebSocketServer(config.ssl, clientStreamHandler); 84 | } 85 | 86 | server._css_host = config.host; 87 | server._css_port = config.port; 88 | 89 | servers[id] = server; 90 | }); 91 | 92 | return { 93 | servers: servers, 94 | listen: function(callback){ 95 | async.mapSeries(Object.keys(servers), function(id, cb){ 96 | var server = servers[id]; 97 | if(server._css_exclude) return cb(); 98 | server.listen(server._css_port, server._css_host, function(){ 99 | enableDestroy(server); 100 | return cb(); 101 | }); 102 | }, callback || function(){}); 103 | }, 104 | close: function(callback){ 105 | async.mapSeries(Object.keys(servers), function(id, cb){ 106 | var server = servers[id]; 107 | if(server._css_exclude) return cb(); 108 | server.close(cb); 109 | }, callback || function(){}); 110 | }, 111 | destroy: function(callback){ 112 | async.mapSeries(Object.keys(servers), function(id, cb){ 113 | var server = servers[id]; 114 | if(server._css_exclude) return cb(); 115 | server.destroy(cb); 116 | }, callback || function(){}); 117 | } 118 | }; 119 | }; 120 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "create-stream-server", 3 | "version": "0.1.1", 4 | "description": "create multiple stream servers easily", 5 | "main": "index.js", 6 | "contributors": [ 7 | "Joël Gähwiler (https://github.com/256dpi)", 8 | "Matteo Collina (https://github.com/mcollina)" 9 | ], 10 | "scripts": { 11 | "test": "./node_modules/.bin/mocha --reporter list *.js" 12 | }, 13 | "repository": { 14 | "type": "git", 15 | "url": "https://github.com/mqttjs/create-stream-server.git" 16 | }, 17 | "license": "MIT", 18 | "bugs": { 19 | "url": "https://github.com/mqttjs/create-stream-server/issues" 20 | }, 21 | "homepage": "https://github.com/mqttjs/create-stream-server", 22 | "dependencies": { 23 | "async": "0.9.0", 24 | "server-destroy": "1.0.0", 25 | "websocket-stream": "1.3.2", 26 | "ws": "0.7.0" 27 | }, 28 | "devDependencies": { 29 | "mocha": "2.1.0" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /support/server.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDLDCCAhQCCQD0OFi+lx3bpjANBgkqhkiG9w0BAQUFADBYMQswCQYDVQQGEwJD 3 | SDEPMA0GA1UECBMGWnVyaWNoMQ8wDQYDVQQHEwZadXJpY2gxEjAQBgNVBAoTCXNo 4 | aWZ0ci5pbzETMBEGA1UEAxMKbWVrb25nLmRldjAeFw0xNDEyMjgxMTMzNTdaFw0x 5 | NTEyMjgxMTMzNTdaMFgxCzAJBgNVBAYTAkNIMQ8wDQYDVQQIEwZadXJpY2gxDzAN 6 | BgNVBAcTBlp1cmljaDESMBAGA1UEChMJc2hpZnRyLmlvMRMwEQYDVQQDEwptZWtv 7 | bmcuZGV2MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyqZScnDkk3lc 8 | h9MG00RlpD+U0jvGDGGykHi19J0bUZY+GmwCXHEdHqE2GuJDWEQcaB5RVBU5UOBd 9 | KqBcQ5AhU8HWoL3txO9+Z+E7JzchDHPhabBtwTNDD5TKcL9XcrYd3Al7FpGARsDd 10 | H8U3AC4i6srtUZb37lILQeD628qw5gL2OWL4noOr9Eszek63zURZhQQZ4aZqTi3W 11 | 2EU4brYSEy1ZW0WhkSYGm8gpeCC8f8gmufPFyalMwVPXLjNVJnD9EdWVyhmtiEYG 12 | Q5iJweFaiQwIuFvYC5jxeVAji86UFV/pE5qZ/ffZHgRMBwdoNh8TZiFMV/psRv6C 13 | 6JuKBeBauwIDAQABMA0GCSqGSIb3DQEBBQUAA4IBAQCgQTneRLx35T1XcEbKvqZq 14 | DGZnB+cVH7D5VGUGwOlWhVrxxRfurR3Yrcb4mUCqu6EjUrWXhauczsAdoXqrrzRY 15 | YyR9CbarAm6f80xj+aSINBm/lLZJ/GJd7XhrOUTbM3VIIHUGF3MbAP7a16t2aYsf 16 | IDa2U1GXllJfMBWyFbn53AH6Lf85fLbcmg47K1ffx+4Baw+IzIsVGuvzHG73TCA9 17 | R1/ygrIDABAXw/919zOe2vsF+BKIWZiDIeTPVxzM4M8gTuoky2ecHBBjVyAS2yXS 18 | 73mi7srYyxs+7iCmixn2csZUpSnrYmJE61CDPgMtmQSt3cXnyldq6gDzefiCnZdR 19 | -----END CERTIFICATE----- 20 | -------------------------------------------------------------------------------- /support/server.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEoAIBAAKCAQEAyqZScnDkk3lch9MG00RlpD+U0jvGDGGykHi19J0bUZY+GmwC 3 | XHEdHqE2GuJDWEQcaB5RVBU5UOBdKqBcQ5AhU8HWoL3txO9+Z+E7JzchDHPhabBt 4 | wTNDD5TKcL9XcrYd3Al7FpGARsDdH8U3AC4i6srtUZb37lILQeD628qw5gL2OWL4 5 | noOr9Eszek63zURZhQQZ4aZqTi3W2EU4brYSEy1ZW0WhkSYGm8gpeCC8f8gmufPF 6 | yalMwVPXLjNVJnD9EdWVyhmtiEYGQ5iJweFaiQwIuFvYC5jxeVAji86UFV/pE5qZ 7 | /ffZHgRMBwdoNh8TZiFMV/psRv6C6JuKBeBauwIDAQABAoIBAFGneNsiAAgoQ497 8 | CWoBSk9HS0j2ytNcXl32NaLt1v8l4bo1wTHMZiZcYPeuKeKb9zJA3RZbQvACp6ew 9 | W9zha3xbQ4cbYH4U3kMvLu2bOhbRbodujprlc+UIWBXcE3lmRlvN+ina0OwxdCgE 10 | CChrbqhawgs5IIeHyX9vDsWXQ3Y6DbzjC4OREUHZHTxx7KArheuqF86Wb40YMUQv 11 | un/dHyf7Bl/LoS7EmKk2Y3ldUSI9sMCroiVXx9shQ8IIOeAGrPtZdITCrlZOn9f2 12 | SQwHi+kVfQ73/rNcjgEXH8VKMOgWCyLv67WbP4ICVGIB+/OBnAZHVzbbNQuNI1AS 13 | T/mkmjkCgYEA5AH1yQBKqzjQgkPXA3bZWv1KW6yAnal9Jpicnl++UFSvlFZ054ax 14 | 42MA1p/HfVq/gYvmEUROyIT1CHQ502CH/BmFpCj2JCMWNSxs4x6FBxdln8wmMj/6 15 | nVT3Ckcl6jRneoaer9iQ9TqEgmm8v+UXiuv8lRlUMf0jvm7lWcIMhi0CgYEA44dj 16 | VSYaehZwLsTqYPl57rUEhWHTqEs7lhllFVYeYQ0Z9lr4LGbc4aQa1Y6dc51zGXhs 17 | 24MTe9dh8xjTJaeKvrzSvfJO3qXeQZ4OoDUrGS8m/Dv+c3BwrxQyKFu92AzvXYwD 18 | mJT+mz78nlhU8ZKE9EA3QaPa0h1k7vHPPQhsnYcCgYBKXllMtkukjWN1GauH9bvv 19 | ca5POHS6+A1aCW0MOy5YBUc/mvOGkOh0wlYDqxnmSTMtjfP8rcsEnFlP6Jjz2QiB 20 | sdFlOfcO0mLr9RGPAuVg6sC63luXCEc2CgCJ2asEOROHY2Fe+cROOEgAQXzPGmoT 21 | ZeV8vEY6B9cgxgsIu8JaAQKBgFOFKkBiaUuxmuKAJC0OxuSKDCvOGjznyOqzTbjE 22 | UQh9H6+f+wOJisFFVRhZbpC3Fj4eR49YkTlfebQbw75JvxN/CrjxDmSKbIiXtXS3 23 | r6dh+KSUfTXw61xJRJQuAQUi0mb7c4J6BvAD8gVKFXxLtYRXYjE1Laj9Y0SW/OTB 24 | h+VXAn88nRH/8k/9Xs1zHXHnEyvC5SJCBm9G+FR2RfueqygMWmsSzr2nE78bWDuj 25 | sHvrdW39qyciTYZSNJNrmdYIwqkbvWj8rIqPqQ6dloRpQHNupYPrF93IJxg6UWPU 26 | JvpC/dEKzLT+btYlgKExz6hgk15pqTHfcqgG7859aBIEMyip 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /tests.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'); 2 | var fs = require('fs'); 3 | var net = require('net'); 4 | var tls = require('tls'); 5 | var http = require('http'); 6 | var ws = require('ws'); 7 | 8 | var css = require('./index'); 9 | 10 | process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0"; // trust self signed certificate 11 | 12 | var ssl = { 13 | key: fs.readFileSync('./support/server.key'), 14 | cert: fs.readFileSync('./support/server.crt') 15 | }; 16 | 17 | describe('create-stream-server', function(){ 18 | it('should create a single tcp server', function(done){ 19 | var s = css({ 20 | s1: 'tcp://localhost:9001' 21 | }, function(stream){ 22 | stream.on('data', function(){ 23 | done(); 24 | }); 25 | }); 26 | 27 | s.listen(function(){ 28 | var c = net.connect(9001, function(){ 29 | c.write('A'); 30 | }); 31 | }); 32 | }); 33 | 34 | it('should create a single ssl server', function(done){ 35 | var s = css({ 36 | s1: 'ssl://localhost:9002' 37 | }, { 38 | ssl: ssl 39 | }, function(stream){ 40 | stream.on('data', function(){ 41 | done(); 42 | }); 43 | }); 44 | 45 | s.listen(function(){ 46 | var c = tls.connect(9002, function(){ 47 | c.write('A'); 48 | }); 49 | }); 50 | }); 51 | 52 | it('should create a single ws server', function(done){ 53 | var s = css({ 54 | s1: 'ws://localhost:9003' 55 | }, function(stream){ 56 | stream.on('data', function(){ 57 | done(); 58 | }); 59 | }); 60 | 61 | s.listen(function(){ 62 | var c = ws.connect('ws://localhost:9003', function(){ 63 | c.send('A'); 64 | }); 65 | }); 66 | }); 67 | 68 | it('should create a single wss server', function(done){ 69 | var s = css({ 70 | s1: 'wss://localhost:9004' 71 | }, { 72 | ssl: ssl 73 | }, function(stream){ 74 | stream.on('data', function(){ 75 | done(); 76 | }); 77 | }); 78 | 79 | s.listen(function(){ 80 | var c = ws.connect('wss://localhost:9004', function(){ 81 | c.send('A'); 82 | }); 83 | }); 84 | }); 85 | 86 | it('should allow object config', function(done){ 87 | var s = css({ 88 | s1: { 89 | protocol: 'ssl', 90 | port: 9005, 91 | ssl: ssl 92 | } 93 | }, function(stream){ 94 | stream.on('data', function(){ 95 | done(); 96 | }); 97 | }); 98 | 99 | s.listen(function(){ 100 | var c = tls.connect(9005, function(){ 101 | c.write('A'); 102 | }); 103 | }); 104 | }); 105 | 106 | it('should close servers', function(done){ 107 | var s = css({ 108 | s1: 'tcp://localhost:9006' 109 | }, function(){}); 110 | 111 | s.servers.s1.on('close', function(){ 112 | done(); 113 | }); 114 | 115 | s.listen(function(){ 116 | s.close(); 117 | }); 118 | }); 119 | 120 | it('should destroy servers', function(done){ 121 | var s = css({ 122 | s1: 'tcp://localhost:9007' 123 | }, function(){}); 124 | 125 | s.servers.s1.on('close', function(){ 126 | done(); 127 | }); 128 | 129 | s.listen(function(){ 130 | s.destroy(); 131 | }); 132 | }); 133 | 134 | it('should create multiple servers', function(done){ 135 | var servers = css({ 136 | s1: 'tcp://localhost:9008', 137 | s2: 'ssl://0.0.0.0:9009', 138 | s3: { 139 | protocol: 'wss', 140 | host: 'localhost', 141 | port: 9010 142 | } 143 | }, { 144 | ssl: ssl 145 | }, function(){}); 146 | 147 | servers.listen(function(){ 148 | servers.close(function(){ 149 | done(); 150 | }); 151 | }); 152 | }); 153 | 154 | it('should allow attaching ws servers to an existing http server', function(done) { 155 | var httpServer = http.createServer(); 156 | 157 | var servers = css({ 158 | s3: { 159 | attach: httpServer 160 | } 161 | }, function(){}); 162 | 163 | servers.listen(function () { 164 | servers.close(function () { 165 | done(); 166 | }); 167 | }); 168 | }); 169 | }); 170 | --------------------------------------------------------------------------------