├── .gitignore ├── LICENSE ├── README.md ├── example └── game-server │ ├── app.js │ ├── app │ ├── filters │ │ └── log.js │ └── servers │ │ └── gamehttp │ │ └── route │ │ └── testRoute.js │ ├── config │ ├── adminServer.json │ ├── adminUser.json │ ├── clientProtos.json │ ├── dictionary.json │ ├── http.json │ ├── log4js.json │ ├── master.json │ ├── serverProtos.json │ └── servers.json │ └── package.json ├── index.js ├── lib ├── components │ └── http.js └── events │ └── http.js └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *~ 3 | *.log 4 | ^. 5 | *.pem -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 pipi32167 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | pomelo-http-plugin 2 | ================== 3 | 4 | Wrap express module as pomelo http plugin. 5 | 6 | 7 | [wiki][] 8 | [wiki]: https://github.com/pipi32167/pomelo-http-plugin/wiki 9 | 10 | ###How to use pomelo-http-plugin: 11 | 12 | ###Single server 13 | 14 | For example, your http server name is gamehttp. 15 | 16 | #####1. Create config/http.json, configure your http server 17 | ```js 18 | { 19 | "development": { 20 | "gamehttp": { 21 | "host": "127.0.0.1", 22 | "port": 3001 23 | } 24 | } 25 | } 26 | ``` 27 | If you want to support https, you should add more keys to config/http.json 28 | ```js 29 | { 30 | "development": { 31 | "gamehttp": { 32 | "useSSL": true, 33 | "keyFile": "config/key.pem", 34 | "certFile": "config/cert.pem", 35 | "host": "127.0.0.1", 36 | "port": 3001 37 | } 38 | } 39 | } 40 | ``` 41 | #####2. Change servers.json, add gamehttp config 42 | ```js 43 | "gamehttp": [{ 44 | "id": "gamehttp", 45 | "port": 3002, 46 | "host": "127.0.0.1" 47 | }] 48 | ``` 49 | #####3. Change adminServer.json, add server type config 50 | ```js 51 | { 52 | "type": "gamehttp", 53 | "token": "agarxhqb98rpajloaxn34ga8xrunpagkjwlaw3ruxnpaagl29w4rxn" 54 | } 55 | ``` 56 | #####4. Change app.js 57 | ```js 58 | var httpPlugin = require('pomelo-http-plugin'); 59 | var path = require('path'); 60 | app.configure('development', 'gamehttp', function() { 61 | app.loadConfig('httpConfig', path.join(app.getBase(), 'config/http.json')); 62 | app.use(httpPlugin, { 63 | http: app.get('httpConfig')[app.getServerId()] 64 | }); 65 | }); 66 | ``` 67 | #####5. Create app/servers/gamehttp/route/testRoute.js 68 | ```js 69 | module.exports = function(app, http) { 70 | 71 | http.get('/test', function(req, res) { 72 | res.send('test success') 73 | }); 74 | }; 75 | ``` 76 | #####6. Run your app and open url http://127.0.0.1:3001/test 77 | 78 | ###Server cluster 79 | 80 | This example, we configure our http server as a server cluster, just have a little difference with the before example. 81 | 82 | #####1. Create config/http.json, configure your http server 83 | ```js 84 | { 85 | "development": { 86 | "gamehttp": { 87 | "isCluster": true, 88 | "host": "127.0.0.1", 89 | "port": "3001++" 90 | } 91 | } 92 | } 93 | ``` 94 | If you want to support https, you should add more keys to config/http.json 95 | ```js 96 | { 97 | "development": { 98 | "gamehttp": { 99 | "useSSL": true, 100 | "keyFile": "config/key.pem", 101 | "certFile": "config/cert.pem", 102 | "isCluster": true, 103 | "host": "127.0.0.1", 104 | "port": "3001++" 105 | } 106 | } 107 | } 108 | ``` 109 | #####2. Change servers.json, add gamehttp config 110 | ```js 111 | "gamehttp": [{ 112 | "id": "gamehttp", 113 | "clusterCount": 2, 114 | "port": "3101++", 115 | "host": "127.0.0.1" 116 | }] 117 | ``` 118 | #####3. Change adminServer.json, add server type config 119 | ```js 120 | { 121 | "type": "gamehttp", 122 | "token": "agarxhqb98rpajloaxn34ga8xrunpagkjwlaw3ruxnpaagl29w4rxn" 123 | } 124 | ``` 125 | #####4. Change app.js 126 | ```js 127 | var httpPlugin = require('pomelo-http-plugin'); 128 | var path = require('path'); 129 | 130 | app.configure('development', 'gamehttp', function() { 131 | app.loadConfig('httpConfig', path.join(app.getBase(), 'config/http.json')); 132 | app.use(httpPlugin, { 133 | http: app.get('httpConfig')[app.getServerType()] 134 | }); 135 | }); 136 | ``` 137 | #####5. Create app/servers/gamehttp/route/testRoute.js 138 | ```js 139 | module.exports = function(app, http) { 140 | 141 | http.get('/test', function(req, res) { 142 | res.send('test success') 143 | }); 144 | }; 145 | ``` 146 | #####6. Run your app and open urls: http://127.0.0.1:3001/test, http://127.0.0.1:3002/test 147 | #####7. Optional, you can use nginx or any other similar program to reverse proxy the http port, just google it! 148 | 149 | 150 | ## License 151 | 152 | The MIT License (MIT) 153 | 154 | Copyright (c) 2014 pipi32167 155 | 156 | Permission is hereby granted, free of charge, to any person obtaining a copy 157 | of this software and associated documentation files (the "Software"), to deal 158 | in the Software without restriction, including without limitation the rights 159 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 160 | copies of the Software, and to permit persons to whom the Software is 161 | furnished to do so, subject to the following conditions: 162 | 163 | The above copyright notice and this permission notice shall be included in all 164 | copies or substantial portions of the Software. 165 | 166 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 167 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 168 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 169 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 170 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 171 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 172 | SOFTWARE. -------------------------------------------------------------------------------- /example/game-server/app.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var pomelo = require('pomelo'); 3 | var httpPlugin = require('../../'); 4 | var path = require('path'); 5 | 6 | /** 7 | * Init app for client. 8 | */ 9 | var app = pomelo.createApp(); 10 | app.set('name', 'example'); 11 | 12 | 13 | // app configuration 14 | app.configure('development', 'gamehttp', function() { 15 | app.loadConfig('httpConfig', path.join(app.getBase(), 'config/http.json')); 16 | app.use(httpPlugin, { 17 | http: app.get('httpConfig').gamehttp, 18 | }); 19 | app.use(httpPlugin, { 20 | http: app.get('httpConfig').gamehttps, 21 | }); 22 | 23 | httpPlugin.filter(require('./app/filters/log')()); 24 | httpPlugin.afterFilter(function(req, res) { 25 | res.send(res.get('resp')); 26 | }); 27 | }); 28 | // start app 29 | app.start(); 30 | 31 | process.on('uncaughtException', function(err) { 32 | console.error(' Caught exception: ' + err.stack); 33 | }); -------------------------------------------------------------------------------- /example/game-server/app/filters/log.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function() { 4 | return new LogFilter(); 5 | } 6 | 7 | var LogFilter = function(plugin) { 8 | } 9 | 10 | LogFilter.prototype.before = function(req, res, next) { 11 | console.log('[http request]:', req.method, req.url); 12 | next(); 13 | } 14 | 15 | LogFilter.prototype.after = function(req, res, next) { 16 | console.log('[http response]:', req.method, req.url, res.get('resp')); 17 | next(); 18 | } -------------------------------------------------------------------------------- /example/game-server/app/servers/gamehttp/route/testRoute.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function(app, http, plugin) { 4 | 5 | if (plugin.useSSL) { 6 | 7 | http.get('/testHttps', function(req, res, next) { 8 | // console.log(req.body); 9 | res.set('resp', 'https success'); 10 | next(); 11 | }); 12 | } else { 13 | 14 | http.get('/testHttp', function(req, res, next) { 15 | // console.log(req.body); 16 | res.set('resp', 'http success'); 17 | next(); 18 | }); 19 | } 20 | }; -------------------------------------------------------------------------------- /example/game-server/config/adminServer.json: -------------------------------------------------------------------------------- 1 | [{ 2 | "type": "gamehttp", 3 | "token": "agarxhqb98rpajloaxn34ga8xrunpagkjwlaw3ruxnpaagl29w4rxn" 4 | } 5 | ] -------------------------------------------------------------------------------- /example/game-server/config/adminUser.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": "user-1", 4 | "username": "admin", 5 | "password": "admin", 6 | "level": 1 7 | }, 8 | 9 | { 10 | "id": "user-2", 11 | "username": "monitor", 12 | "password": "monitor", 13 | "level": 2 14 | }, 15 | 16 | { 17 | "id": "user-3", 18 | "username": "test", 19 | "password": "test", 20 | "level": 2 21 | } 22 | ] 23 | -------------------------------------------------------------------------------- /example/game-server/config/clientProtos.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /example/game-server/config/dictionary.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /example/game-server/config/http.json: -------------------------------------------------------------------------------- 1 | { 2 | "development": { 3 | "gamehttps": { 4 | "useSSL": true, 5 | "keyFile": "config/key.pem", 6 | "certFile": "config/cert.pem", 7 | "host": "127.0.0.1", 8 | "port": 3001 9 | }, 10 | "gamehttp": { 11 | "host": "127.0.0.1", 12 | "port": 3002 13 | } 14 | }, 15 | "production": { 16 | "gamehttps": { 17 | "useSSL": true, 18 | "keyFile": "config/key.pem", 19 | "certFile": "config/cert.pem", 20 | "host": "127.0.0.1", 21 | "port": 3001 22 | }, 23 | "gamehttp": { 24 | "host": "127.0.0.1", 25 | "port": 3002 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /example/game-server/config/log4js.json: -------------------------------------------------------------------------------- 1 | { 2 | "appenders": [ 3 | { 4 | "type": "console" 5 | }, 6 | { 7 | "type": "file", 8 | "filename": "${opts:base}/logs/con-log-${opts:serverId}.log", 9 | "pattern": "connector", 10 | "maxLogSize": 1048576, 11 | "layout": { 12 | "type": "basic" 13 | }, 14 | "backups": 5, 15 | "category": "con-log" 16 | }, 17 | { 18 | "type": "file", 19 | "filename": "${opts:base}/logs/rpc-log-${opts:serverId}.log", 20 | "maxLogSize": 1048576, 21 | "layout": { 22 | "type": "basic" 23 | }, 24 | "backups": 5, 25 | "category": "rpc-log" 26 | }, 27 | { 28 | "type": "file", 29 | "filename": "${opts:base}/logs/forward-log-${opts:serverId}.log", 30 | "maxLogSize": 1048576, 31 | "layout": { 32 | "type": "basic" 33 | }, 34 | "backups": 5, 35 | "category": "forward-log" 36 | }, 37 | { 38 | "type": "file", 39 | "filename": "${opts:base}/logs/rpc-debug-${opts:serverId}.log", 40 | "maxLogSize": 1048576, 41 | "layout": { 42 | "type": "basic" 43 | }, 44 | "backups": 5, 45 | "category": "rpc-debug" 46 | }, 47 | { 48 | "type": "file", 49 | "filename": "${opts:base}/logs/crash.log", 50 | "maxLogSize": 1048576, 51 | "layout": { 52 | "type": "basic" 53 | }, 54 | "backups": 5, 55 | "category":"crash-log" 56 | }, 57 | { 58 | "type": "file", 59 | "filename": "${opts:base}/logs/admin.log", 60 | "maxLogSize": 1048576, 61 | "layout": { 62 | "type": "basic" 63 | } 64 | ,"backups": 5, 65 | "category":"admin-log" 66 | }, 67 | { 68 | "type": "file", 69 | "filename": "${opts:base}/logs/pomelo-${opts:serverId}.log", 70 | "maxLogSize": 1048576, 71 | "layout": { 72 | "type": "basic" 73 | } 74 | ,"backups": 5, 75 | "category":"pomelo" 76 | }, 77 | { 78 | "type": "file", 79 | "filename": "${opts:base}/logs/pomelo-admin.log", 80 | "maxLogSize": 1048576, 81 | "layout": { 82 | "type": "basic" 83 | } 84 | ,"backups": 5, 85 | "category":"pomelo-admin" 86 | }, 87 | { 88 | "type": "file", 89 | "filename": "${opts:base}/logs/pomelo-rpc.log", 90 | "maxLogSize": 1048576, 91 | "layout": { 92 | "type": "basic" 93 | } 94 | ,"backups": 5, 95 | "category":"pomelo-rpc" 96 | } 97 | ], 98 | 99 | "levels": { 100 | "rpc-log" : "ERROR", 101 | "forward-log": "ERROR" 102 | }, 103 | 104 | "replaceConsole": true, 105 | 106 | "lineDebug": false 107 | } 108 | -------------------------------------------------------------------------------- /example/game-server/config/master.json: -------------------------------------------------------------------------------- 1 | { 2 | "development": { 3 | "id": "master-server-1", "host": "127.0.0.1", "port": 3005 4 | }, 5 | "production": { 6 | "id": "master-server-1", "host": "127.0.0.1", "port": 3005 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /example/game-server/config/serverProtos.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /example/game-server/config/servers.json: -------------------------------------------------------------------------------- 1 | { 2 | "development": { 3 | "gamehttp": [{ 4 | "id": "gamehttp", 5 | "port": 3011, 6 | "host": "127.0.0.1" 7 | }] 8 | }, 9 | "production": { 10 | "gamehttp": [{ 11 | "id": "gamehttp", 12 | "port": 3011, 13 | "host": "127.0.0.1" 14 | }] 15 | } 16 | } -------------------------------------------------------------------------------- /example/game-server/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name":"example", 3 | "version":"0.0.1", 4 | "private":false, 5 | "dependencies":{ 6 | "pomelo":"1.0.3" 7 | } 8 | } 9 | 10 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | module.exports = { 3 | 4 | components: __dirname + '/lib/components/', 5 | 6 | events: __dirname + '/lib/events/', 7 | 8 | beforeFilters: [], 9 | afterFilters: [], 10 | 11 | filter: function(filter) { 12 | if (filter.before) { 13 | this.beforeFilters.push(filter.before.bind(filter)); 14 | } 15 | if (filter.after) { 16 | this.afterFilters.push(filter.after.bind(filter)); 17 | } 18 | }, 19 | 20 | beforeFilter: function(filter) { 21 | this.beforeFilters.push(filter); 22 | }, 23 | 24 | afterFilter: function(filter) { 25 | this.afterFilters.push(filter); 26 | }, 27 | 28 | }; -------------------------------------------------------------------------------- /lib/components/http.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var express = require('express'); 4 | var http = require('http'); 5 | var https = require('https'); 6 | var path = require('path'); 7 | var fs = require('fs'); 8 | var assert = require('assert'); 9 | 10 | module.exports = function(app, opts) { 11 | return new Http(app, opts); 12 | }; 13 | 14 | var DEFAULT_HOST = '127.0.0.1'; 15 | var DEFAULT_PORT = 3001; 16 | 17 | var createExpressLogger = function(logger) { 18 | return express.logger({ 19 | format: 'short', 20 | stream: { 21 | write: function(str) { 22 | logger.debug(str); 23 | } 24 | }, 25 | }) 26 | }; 27 | 28 | var defaultLogger = function() { 29 | return { 30 | debug: console.log, 31 | info: console.log, 32 | warn: console.warn, 33 | error: console.error, 34 | } 35 | } 36 | 37 | var Http = function(app, opts) { 38 | opts = opts || {}; 39 | this.app = app; 40 | this.http = express(); 41 | // self.logger.info('Http opts:', opts); 42 | this.host = opts.host || DEFAULT_HOST; 43 | this.port = opts.port || DEFAULT_PORT; 44 | 45 | if (!!opts.isCluster) { 46 | var serverId = app.getServerId(); 47 | var params = serverId.split('-'); 48 | var idx = parseInt(params[params.length - 1], 10); 49 | if (/\d+\+\+/.test(this.port)) { 50 | 51 | this.port = parseInt(this.port.substr(0, this.port.length - 2)); 52 | } else { 53 | assert.ok(false, 'http cluster expect http port format like "3000++"'); 54 | } 55 | 56 | this.port = this.port + idx; 57 | } 58 | 59 | this.useSSL = !!opts.useSSL; 60 | this.sslOpts = {}; 61 | if (this.useSSL) { 62 | this.sslOpts.key = fs.readFileSync(path.join(app.getBase(), opts.keyFile)); 63 | this.sslOpts.cert = fs.readFileSync(path.join(app.getBase(), opts.certFile)); 64 | } 65 | 66 | this.logger = opts.logger || defaultLogger(); 67 | 68 | this.http.set('port', this.port); 69 | this.http.set('host', this.host); 70 | this.http.use(createExpressLogger(this.logger)); 71 | this.http.use(express.bodyParser()); 72 | this.http.use(express.urlencoded()); 73 | this.http.use(express.json()); 74 | this.http.use(express.methodOverride()); 75 | this.http.use(this.http.router); 76 | 77 | var self = this; 78 | this.app.configure(function() { 79 | self.http.use(express.errorHandler());; 80 | }); 81 | 82 | this.beforeFilters = require('../../index').beforeFilters; 83 | this.afterFilters = require('../../index').afterFilters; 84 | this.server = null; 85 | }; 86 | 87 | Http.prototype.loadRoutes = function() { 88 | this.http.get('/', function(req, res) { 89 | res.send('pomelo-http-plugin ok!'); 90 | }); 91 | 92 | var routesPath = path.join(this.app.getBase(), 'app/servers', this.app.getServerType(), 'route'); 93 | // self.logger.info(routesPath); 94 | assert.ok(fs.existsSync(routesPath), 'Cannot find route path: ' + routesPath); 95 | 96 | var self = this; 97 | fs.readdirSync(routesPath).forEach(function(file) { 98 | if (/.js$/.test(file)) { 99 | var routePath = path.join(routesPath, file); 100 | // self.logger.info(routePath); 101 | require(routePath)(self.app, self.http, self); 102 | } 103 | }); 104 | } 105 | 106 | Http.prototype.start = function(cb) { 107 | var self = this; 108 | 109 | this.beforeFilters.forEach(function(elem) { 110 | self.http.use(elem); 111 | }); 112 | 113 | this.loadRoutes(); 114 | 115 | this.afterFilters.forEach(function(elem) { 116 | self.http.use(elem); 117 | }); 118 | 119 | if (this.useSSL) { 120 | this.server = https.createServer(this.sslOpts, this.http).listen(this.port, this.host, function() { 121 | self.logger.info('Http start', self.app.getServerId(), 'url: https://' + self.host + ':' + self.port); 122 | self.logger.info('Http start success'); 123 | process.nextTick(cb); 124 | }); 125 | } else { 126 | this.server = http.createServer(this.http).listen(this.port, this.host, function() { 127 | self.logger.info('Http start', self.app.getServerId(), 'url: http://' + self.host + ':' + self.port); 128 | self.logger.info('Http start success'); 129 | process.nextTick(cb); 130 | }); 131 | } 132 | } 133 | 134 | Http.prototype.afterStart = function(cb) { 135 | this.logger.info('Http afterStart'); 136 | process.nextTick(cb); 137 | } 138 | 139 | Http.prototype.stop = function(force, cb) { 140 | var self = this; 141 | this.server.close(function() { 142 | self.logger.info('Http stop'); 143 | cb(); 144 | }); 145 | } -------------------------------------------------------------------------------- /lib/events/http.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | module.exports = function(app, opts) { 3 | return new Event(app, opts); 4 | }; 5 | 6 | var Event = function(app, opts) { 7 | //do construction 8 | }; 9 | 10 | Event.prototype.add_servers = function(servers) { 11 | //do something when application add servers 12 | }; 13 | 14 | Event.prototype.remove_servers = function(ids) { 15 | //do something when application remove servers 16 | }; 17 | 18 | Event.prototype.replace_servers = function(servers) { 19 | //do something when server reconnected 20 | }; 21 | 22 | Event.prototype.bind_session = function(session) { 23 | //do something when session binded 24 | }; 25 | 26 | Event.prototype.close_session = function(session) { 27 | //do something when session closed 28 | }; 29 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pomelo-http-plugin", 3 | "version": "0.0.25", 4 | "description": "Wrap express module as pomelo http plugin.", 5 | "main": "index.js", 6 | "private": false, 7 | "homepage": "https://github.com/pipi32167/pomelo-http-plugin", 8 | "repository": { 9 | "type": "git", 10 | "url": "https://github.com/pipi32167/pomelo-http-plugin.git" 11 | }, 12 | "bugs": { 13 | "url": "https://github.com/pipi32167/pomelo-http-plugin/issues" 14 | }, 15 | "licenses": [{ 16 | "type": "MIT", 17 | "url": "https://github.com/pipi32167/pomelo-http-plugin#license" 18 | }], 19 | "keywords": [ 20 | "pomelo", 21 | "express", 22 | "http", 23 | "plugin" 24 | ], 25 | "author": "Million Young", 26 | "license": "MIT", 27 | "dependencies": { 28 | "express": "~3.4.8" 29 | } 30 | } --------------------------------------------------------------------------------