44const async = require ( 'async' ) ,
55 https = require ( 'https' ) ,
66 tls = require ( 'tls' ) ,
7+ assert = require ( 'assert' ) ,
78 crypto = require ( 'crypto' ) ,
89 color = require ( 'colorful' ) ,
910 certMgr = require ( './certMgr' ) ,
1011 logUtil = require ( './log' ) ,
1112 util = require ( './util' ) ,
1213 wsServerMgr = require ( './wsServerMgr' ) ,
13- co = require ( 'co' ) ,
14- assert = require ( 'assert' ) ,
1514 constants = require ( 'constants' ) ,
1615 asyncTask = require ( 'async-task-mgr' ) ;
1716
18- const createSecureContext = tls . createSecureContext || crypto . createSecureContext ;
19- function SNIPrepareCert ( serverName , SNICallback ) {
20- let keyContent ,
21- crtContent ,
22- ctx ;
23-
24- async . series ( [
25- ( callback ) => {
26- certMgr . getCertificate ( serverName , ( err , key , crt ) => {
27- if ( err ) {
28- callback ( err ) ;
29- } else {
30- keyContent = key ;
31- crtContent = crt ;
32- callback ( ) ;
33- }
34- } ) ;
35- } ,
36- ( callback ) => {
37- try {
38- ctx = createSecureContext ( {
39- key : keyContent ,
40- cert : crtContent
41- } ) ;
42- callback ( ) ;
43- } catch ( e ) {
44- callback ( e ) ;
45- }
46- }
47- ] , ( err ) => {
48- if ( ! err ) {
49- const tipText = 'proxy server for __NAME established' . replace ( '__NAME' , serverName ) ;
50- logUtil . printLog ( color . yellow ( color . bold ( '[internal https]' ) ) + color . yellow ( tipText ) ) ;
51- SNICallback ( null , ctx ) ;
52- } else {
53- logUtil . printLog ( 'err occurred when prepare certs for SNI - ' + err , logUtil . T_ERR ) ;
54- logUtil . printLog ( 'err occurred when prepare certs for SNI - ' + err . stack , logUtil . T_ERR ) ;
55- }
56- } ) ;
57- }
58-
59- //config.port - port to start https server
60- //config.handler - request handler
61-
6217/**
6318 * Create an https server
6419 *
6520 * @param {object } config
6621 * @param {number } config.port
6722 * @param {function } config.handler
6823 */
69- function createHttpsServer ( config ) {
70- if ( ! config || ! config . port || ! config . handler ) {
71- throw ( new Error ( 'please assign a port' ) ) ;
24+ function createHttpsSNIServer ( port , handler ) {
25+ assert ( port && handler , 'invalid param for https SNI server' ) ;
26+
27+ const createSecureContext = tls . createSecureContext || crypto . createSecureContext ;
28+ function SNIPrepareCert ( serverName , SNICallback ) {
29+ let keyContent ,
30+ crtContent ,
31+ ctx ;
32+
33+ async . series ( [
34+ ( callback ) => {
35+ certMgr . getCertificate ( serverName , ( err , key , crt ) => {
36+ if ( err ) {
37+ callback ( err ) ;
38+ } else {
39+ keyContent = key ;
40+ crtContent = crt ;
41+ callback ( ) ;
42+ }
43+ } ) ;
44+ } ,
45+ ( callback ) => {
46+ try {
47+ ctx = createSecureContext ( {
48+ key : keyContent ,
49+ cert : crtContent
50+ } ) ;
51+ callback ( ) ;
52+ } catch ( e ) {
53+ callback ( e ) ;
54+ }
55+ }
56+ ] , ( err ) => {
57+ if ( ! err ) {
58+ const tipText = 'proxy server for __NAME established' . replace ( '__NAME' , serverName ) ;
59+ logUtil . printLog ( color . yellow ( color . bold ( '[internal https]' ) ) + color . yellow ( tipText ) ) ;
60+ SNICallback ( null , ctx ) ;
61+ } else {
62+ logUtil . printLog ( 'err occurred when prepare certs for SNI - ' + err , logUtil . T_ERR ) ;
63+ logUtil . printLog ( 'err occurred when prepare certs for SNI - ' + err . stack , logUtil . T_ERR ) ;
64+ SNICallback ( err ) ;
65+ }
66+ } ) ;
7267 }
7368
7469 return new Promise ( ( resolve ) => {
7570 const server = https . createServer ( {
7671 secureOptions : constants . SSL_OP_NO_SSLv3 || constants . SSL_OP_NO_TLSv1 ,
7772 SNICallback : SNIPrepareCert ,
78- } , config . handler ) . listen ( config . port ) ;
73+ } , handler ) . listen ( port ) ;
7974 resolve ( server ) ;
8075 } ) ;
8176}
8277
83- /**
84- *
85- *
86- * @class httpsServerMgr
87- * @param {object } config
88- * @param {function } config.handler handler to deal https request
89- *
90- */
78+ function createHttpsIPServer ( ip , port , handler ) {
79+ assert ( ip && port && handler , 'invalid param for https IP server' ) ;
80+
81+ return new Promise ( ( resolve , reject ) => {
82+ certMgr . getCertificate ( ip , ( err , keyContent , crtContent ) => {
83+ if ( err ) return reject ( err ) ;
84+ const server = https . createServer ( {
85+ secureOptions : constants . SSL_OP_NO_SSLv3 || constants . SSL_OP_NO_TLSv1 ,
86+ key : keyContent ,
87+ cert : crtContent ,
88+ } , handler ) . listen ( port ) ;
89+
90+ resolve ( server ) ;
91+ } ) ;
92+ } ) ;
93+ }
94+
9195class httpsServerMgr {
9296 constructor ( config ) {
93- assert ( config , 'config is required' ) ;
94- assert ( config . handler && config . wsHandler , 'handler and wsHandler are required' ) ;
95- assert ( config . hostname , 'hostname is required' ) ;
96- this . hostname = config . hostname ;
97- this . handler = config . handler ;
98- this . wsHandler = config . wsHandler ;
97+ if ( ! config || ! config . handler ) {
98+ throw new Error ( 'handler is required' ) ;
99+ }
99100 this . httpsAsyncTask = new asyncTask ( ) ;
100- this . asyncTaskName = `https_${ Math . random ( ) } ` ;
101- this . httpsServer = null ;
101+ this . handler = config . handler ;
102+ this . wsHandler = config . wsHandler
103+ this . asyncSNITaskName = `https_SNI_${ Math . random ( ) } ` ;
104+ this . activeServers = [ ] ;
102105 }
103106
104- getSharedHttpsServer ( ) {
107+ getSharedHttpsServer ( hostname ) {
105108 const self = this ;
106- const finalHost = self . hostname ;
107- function prepareServer ( callback ) {
108- let instancePort ;
109- co ( util . getFreePort )
110- . then ( co . wrap ( function * ( port ) {
111- instancePort = port ;
112- let httpsServer = null ;
109+ const ifIPHost = hostname && util . isIp ( hostname ) ;
110+ const serverHost = '127.0.0.1' ;
113111
114- httpsServer = yield createHttpsServer ( {
115- port,
116- handler : self . handler
117- } ) ;
112+ function prepareServer ( callback ) {
113+ let port ;
114+ Promise . resolve ( util . getFreePort ( ) )
115+ . then ( freePort => {
116+ port = freePort ;
117+ if ( ifIPHost ) {
118+ return createHttpsIPServer ( hostname , port , self . handler ) ;
119+ } else {
120+ return createHttpsSNIServer ( port , self . handler ) ;
121+ }
122+ } )
123+ . then ( httpsServer => {
124+ self . activeServers . push ( httpsServer ) ;
118125
119126 wsServerMgr . getWsServer ( {
120127 server : httpsServer ,
@@ -125,22 +132,20 @@ class httpsServerMgr {
125132 logUtil . debug ( 'will let WebSocket server to handle the upgrade event' ) ;
126133 } ) ;
127134
128- self . httpsServer = httpsServer ;
129-
130135 const result = {
131- host : finalHost ,
132- port : instancePort ,
136+ host : serverHost ,
137+ port,
133138 } ;
134139 callback ( null , result ) ;
135- return result ;
136- } ) )
140+ } )
137141 . catch ( e => {
138142 callback ( e ) ;
139143 } ) ;
140144 }
141145
146+ // same server for same host
142147 return new Promise ( ( resolve , reject ) => {
143- self . httpsAsyncTask . addTask ( self . asyncTaskName , prepareServer , ( error , serverInfo ) => {
148+ self . httpsAsyncTask . addTask ( ifIPHost ? hostname : serverHost , prepareServer , ( error , serverInfo ) => {
144149 if ( error ) {
145150 reject ( error ) ;
146151 } else {
@@ -151,7 +156,9 @@ class httpsServerMgr {
151156 }
152157
153158 close ( ) {
154- return this . httpsServer && this . httpsServer . close ( ) ;
159+ this . activeServers . forEach ( server => {
160+ server . close ( ) ;
161+ } ) ;
155162 }
156163}
157164
0 commit comments