├── .nvmrc ├── .gitattributes ├── .travis.yml ├── .gitignore ├── renovate.json ├── test ├── fixtures │ ├── agent2-key.pem │ ├── agent2-csr.pem │ ├── agent2.cnf │ ├── agent2-cert.pem │ ├── example-com-cert.pem │ ├── example-net-cert.pem │ ├── example-ca-cert.pem │ ├── rootca-cert.pem │ ├── example-org-cert.pem │ ├── intermediate-cert.pem │ ├── agent3-cert.pem │ ├── agent3-key.pem │ ├── example-ca-key.pem │ ├── example-com-key.pem │ ├── example-net-key.pem │ └── example-org-key.pem └── create-servers-test.js ├── CHANGELOG.md ├── package.json ├── LICENSE ├── index.js └── README.md /.nvmrc: -------------------------------------------------------------------------------- 1 | 16 2 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | package-lock.json binary 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "0.12" 4 | - "0.10" 5 | - "0.8" 6 | - "iojs" 7 | before_install: 8 | - "npm install -g npm@1.4.x" 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | lib-cov 2 | *.seed 3 | *.log 4 | *.csv 5 | *.dat 6 | *.out 7 | *.pid 8 | *.gz 9 | 10 | pids 11 | logs 12 | results 13 | 14 | npm-debug.log 15 | node_modules 16 | 17 | .idea 18 | .vscode 19 | .prettierrc 20 | .prettierignore 21 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "platform": "github", 3 | "autodiscover": false, 4 | "requireConfig": true, 5 | "ignoreNpmrcFile": true, 6 | "rangeStrategy": "replace", 7 | "packageRules": [ 8 | { 9 | "packagePatterns": [ 10 | "*" 11 | ], 12 | "minor": { 13 | "groupName": "all non-major dependencies", 14 | "groupSlug": "all-minor-patch" 15 | } 16 | } 17 | ], 18 | "commitMessagePrefix": "[dist]" 19 | } 20 | -------------------------------------------------------------------------------- /test/fixtures/agent2-key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIBOgIBAAJBAMl2/Ba0XSm4ayi4C0rJ+tYtQu8O31VVXezkLJlf+6fVgdpVhYg5 3 | QlihlPUoiM/wOsDWQ1ALnNhPlcLaQk+etQECAwEAAQJBAMT6Bf34+UHKY1ObpsbH 4 | 9u2jsVblFq1rWvs8GPMY6oertzvwm3DpuSUp7PTgOB1nLTLYtCERbQ4ovtN8tn3p 5 | OHUCIQDzIEGsoCr5vlxXvy2zJwu+fxYuhTZWMVuo1397L0VyhwIhANQh+yzqUgaf 6 | WRtSB4T2W7ADtJI35ET61jKBty3CqJY3AiAIwju7dVW3A5WeD6Qc1SZGKZvp9yCb 7 | AFI2BfVwwaY11wIgXF3PeGcvACMyMWsuSv7aPXHfliswAbkWuzcwA4TW01ECIGWa 8 | cgsDvVFxmfM5NPSuT/UDTa6R5BFISB5ea0N0AR3I 9 | -----END RSA PRIVATE KEY----- 10 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # CHANGELOG 2 | 3 | ## 3.3.0 4 | 5 | - Support `keepAliveTimeout` 6 | 7 | ## 3.2.1 8 | 9 | - [#50] Fix bug where cert file reading errors do not surface 10 | 11 | ## 3.2.0 12 | 13 | - [#37] Support HTTP2 14 | 15 | ## 3.1.0 16 | 17 | - [#23], @DullReferenceException Support "*" for SNI host pattern 18 | 19 | ## 3.0.1 20 | 21 | - [#22] Return value compatibility. Do no return `null` valued keys. 22 | 23 | ## 3.0.0 24 | 25 | - [#21], @DullReferenceException Support creating multiple HTTP or HTTPS servers. 26 | - [BREAKING] Introduces `async` functions. 27 | -------------------------------------------------------------------------------- /test/fixtures/agent2-csr.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE REQUEST----- 2 | MIIBXTCCAQcCAQAwfTELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMQswCQYDVQQH 3 | EwJTRjEPMA0GA1UEChMGSm95ZW50MRAwDgYDVQQLEwdOb2RlLmpzMQ8wDQYDVQQD 4 | EwZhZ2VudDIxIDAeBgkqhkiG9w0BCQEWEXJ5QHRpbnljbG91ZHMub3JnMFwwDQYJ 5 | KoZIhvcNAQEBBQADSwAwSAJBAMl2/Ba0XSm4ayi4C0rJ+tYtQu8O31VVXezkLJlf 6 | +6fVgdpVhYg5QlihlPUoiM/wOsDWQ1ALnNhPlcLaQk+etQECAwEAAaAlMCMGCSqG 7 | SIb3DQEJBzEWExRBIGNoYWxsZW5nZSBwYXNzd29yZDANBgkqhkiG9w0BAQUFAANB 8 | AJnll2pt5l0pzskQSpjjLVTlFDFmJr/AZ3UK8v0WxBjYjCe5Jx4YehkChpxIyDUm 9 | U3J9q9MDUf0+Y2+EGkssFfk= 10 | -----END CERTIFICATE REQUEST----- 11 | -------------------------------------------------------------------------------- /test/fixtures/agent2.cnf: -------------------------------------------------------------------------------- 1 | [ req ] 2 | default_bits = 1024 3 | days = 999 4 | distinguished_name = req_distinguished_name 5 | attributes = req_attributes 6 | prompt = no 7 | 8 | [ req_distinguished_name ] 9 | C = US 10 | ST = CA 11 | L = SF 12 | O = Joyent 13 | OU = Node.js 14 | CN = agent2 15 | emailAddress = ry@tinyclouds.org 16 | 17 | [ req_attributes ] 18 | challengePassword = A challenge password 19 | 20 | -------------------------------------------------------------------------------- /test/fixtures/agent2-cert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIB7DCCAZYCCQC7gs0MDNn6MTANBgkqhkiG9w0BAQUFADB9MQswCQYDVQQGEwJV 3 | UzELMAkGA1UECBMCQ0ExCzAJBgNVBAcTAlNGMQ8wDQYDVQQKEwZKb3llbnQxEDAO 4 | BgNVBAsTB05vZGUuanMxDzANBgNVBAMTBmFnZW50MjEgMB4GCSqGSIb3DQEJARYR 5 | cnlAdGlueWNsb3Vkcy5vcmcwHhcNMTEwMzE0MTgyOTEyWhcNMzgwNzI5MTgyOTEy 6 | WjB9MQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0ExCzAJBgNVBAcTAlNGMQ8wDQYD 7 | VQQKEwZKb3llbnQxEDAOBgNVBAsTB05vZGUuanMxDzANBgNVBAMTBmFnZW50MjEg 8 | MB4GCSqGSIb3DQEJARYRcnlAdGlueWNsb3Vkcy5vcmcwXDANBgkqhkiG9w0BAQEF 9 | AANLADBIAkEAyXb8FrRdKbhrKLgLSsn61i1C7w7fVVVd7OQsmV/7p9WB2lWFiDlC 10 | WKGU9SiIz/A6wNZDUAuc2E+VwtpCT561AQIDAQABMA0GCSqGSIb3DQEBBQUAA0EA 11 | C8HzpuNhFLCI3A5KkBS5zHAQax6TFUOhbpBCR0aTDbJ6F1liDTK1lmU/BjvPoj+9 12 | 1LHwrmh29rK8kBPEjmymCQ== 13 | -----END CERTIFICATE----- 14 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "create-servers", 3 | "version": "3.3.0", 4 | "description": "Create an http AND/OR an https server and call the same request handler.", 5 | "main": "index.js", 6 | "files": [ 7 | "./index.js" 8 | ], 9 | "scripts": { 10 | "test": "node test/create-servers-test.js" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "https://github.com/http-party/create-servers" 15 | }, 16 | "keywords": [ 17 | "http", 18 | "https", 19 | "utility" 20 | ], 21 | "author": "Charlie Robbins ", 22 | "license": "MIT", 23 | "bugs": { 24 | "url": "https://github.com/http-party/create-servers/issues" 25 | }, 26 | "homepage": "https://github.com/http-party/create-servers", 27 | "dependencies": { 28 | "connected": "~0.0.2", 29 | "errs": "~0.3.0", 30 | "object-assign": "^4.1.0" 31 | }, 32 | "devDependencies": { 33 | "evil-dns": "^0.2.0", 34 | "sinon": "^5.0.7", 35 | "tape": "~4.9.0" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Charlie Robbins 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | 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, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /test/fixtures/example-com-cert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIC+TCCAeECCQC4TT5CXo6fKjANBgkqhkiG9w0BAQsFADBDMQswCQYDVQQGEwJV 3 | UzEVMBMGA1UECgwMRXhhbXBsZSwgTExDMR0wGwYDVQQDDBRFeGFtcGxlLCBMTEMg 4 | Um9vdCBDQTAeFw0xODA3MTYyMjA0MzNaFw0zMjAzMjQyMjA0MzNaMDoxCzAJBgNV 5 | BAYTAlVTMRUwEwYDVQQKDAxFeGFtcGxlLCBMTEMxFDASBgNVBAMMC2V4YW1wbGUu 6 | Y29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAueaNGs1WXtqgEiDt 7 | bTyJs/Qi8qijfo4VMQryMTbc8Nbo6SX7b/t1H9wF7Hd22C2iKDjcObKBvpkI/M/q 8 | Xd7g2HJDcDgO8JXJrw4xDitWkay08Ej4DhiQ2/9mxMA4gnsB1DMPGoKScf9bMalm 9 | T2vQA7TQHnIgaQ7CKECkkHCQ82/AaqFMY8NNMSYQsQt0YnJR2TYoWVE6zz/YgDrI 10 | Q7uR8GT70qIIUPJjvq/D0biIaLtJJcDZs3GUstHvYoUkTEP+fyr4psfNrCxCI34x 11 | vs9ItuwggXF+XnsmKC89iUgG233LS5gRXp+BHxh0UZtrIxRNo02WnduXtUPcLyS2 12 | bB+vowIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQB0AOzmburt7F+cofBMX+4MT6ZK 13 | TEFgYEBg6Elhu0nyYcoeYGjozmCodAgjIaWe2pqXYVcF1/KHlk+VAVbpGnK4SHor 14 | 8XRpuhsYfeu/q7BYwO0K7O5XOLy+EibAChDmb6WdkReqIiVHhsjMKEcwotAGObnv 15 | sU5EbfKu7+JryOObpwNBimh/kDLa2S1JEXfGVJWsOG/hHtOiJC+Fey9cuonwgOqI 16 | vsaIcT3YzSyElqC8PPbzQegDsdGr95wuVF3Wssam3+expoXuIHK+8BeFudIDdnuQ 17 | cy7EoUtowSZuIfEzGBeF2eUNEjcsKu3u+6qUawF8lGPIn45u+chYpNjAv72Y 18 | -----END CERTIFICATE----- 19 | -------------------------------------------------------------------------------- /test/fixtures/example-net-cert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIC+TCCAeECCQC4TT5CXo6fKzANBgkqhkiG9w0BAQsFADBDMQswCQYDVQQGEwJV 3 | UzEVMBMGA1UECgwMRXhhbXBsZSwgTExDMR0wGwYDVQQDDBRFeGFtcGxlLCBMTEMg 4 | Um9vdCBDQTAeFw0xODA3MTYyMjA1MDVaFw0zMjAzMjQyMjA1MDVaMDoxCzAJBgNV 5 | BAYTAlVTMRUwEwYDVQQKDAxFeGFtcGxlLCBMTEMxFDASBgNVBAMMC2V4YW1wbGUu 6 | bmV0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApKqQ4j7W7fDP8G/N 7 | FLSwhKs1uvU8y/hRuKNTXKgye6MYAmuJiQWE9u9OPxQdcTT7zgQ1swTPKC8ndJoD 8 | +bk0YQeqNEQwmR3LdAfwJbOyHora/SWG64uKgN7oZg9qETMmmBOOyD/YzrZiwcTv 9 | f6b7G4FvQ6R6kowXOt7iu8dBvWeRClvisGvifYWwkzYGVwqaFXhgUz5FhcQwICgR 10 | B5yxjjLSn/h17685TvUCPWR84Bx6GJ/KH4f8j+UFMmIRvkJlU05zKkVd9PGPvCGf 11 | mCdj+yZpaALNS7xbOKXfHMbWsMiLbFHwAF9IhYRKS9qIeZ028j5+HKstPDq+eM8O 12 | x45RhQIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQCf6DZ2ZCrFe4XRg5a3Yd7nmjHP 13 | W8VgyLEQaPDCY14fVHfwz6xtR2mWcNoFQiNsc3AfYVpoiaaZufK9q9iI0e7nFIiC 14 | lYHkyFOnf3wFq5ZZuuzKHkSFGAtLvbospLHWmEtRe8MhasEMREk9qI5e52f8UdGG 15 | 0hgifu2zB7bpVf69AAxC0QVDVhJ1tMAah4YcMwCsxlKo3pMaFtnCFSugeo7zM3+m 16 | 5U0XCRPFk4KY8+iA6JyWRBHwdS+vp+XhdCdMnjaV3Tveuuu7e61t5FpkXAMxfKoC 17 | 7tpXa10A0g03yyHQZ3vdgDfPZ6H+i5hJIrMB96/RxFjUnVV91oajJUx7pA0M 18 | -----END CERTIFICATE----- 19 | -------------------------------------------------------------------------------- /test/fixtures/example-ca-cert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDAjCCAeoCCQDYRRRzyGC6DTANBgkqhkiG9w0BAQsFADBDMQswCQYDVQQGEwJV 3 | UzEVMBMGA1UECgwMRXhhbXBsZSwgTExDMR0wGwYDVQQDDBRFeGFtcGxlLCBMTEMg 4 | Um9vdCBDQTAeFw0xODA3MTYyMjA0MjNaFw0zMjAzMjQyMjA0MjNaMEMxCzAJBgNV 5 | BAYTAlVTMRUwEwYDVQQKDAxFeGFtcGxlLCBMTEMxHTAbBgNVBAMMFEV4YW1wbGUs 6 | IExMQyBSb290IENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA26Od 7 | pa6GI2pdRHPgNJzMJdgs9ZQ7i9g4/U3n15xifAGNsGXQc7dgfe22fCdJ4YJBdCHK 8 | ikPQF5thS1iu4Sn03yMHj8QapPThAsgI5Xe43eoFqpCghsD27J3efsv+gncrxW8q 9 | i37o3vSmaom5gUTjY42l0WP0bN/Ehkeptz5TlhbKEfSq30oZFbh68BnTKU1p3ai6 10 | j7iHnervoHDlAWzI0QUTn/DufDyMOT2+AUUso6ElZsk+aqlmE7ZcYmVjwKKd82ZC 11 | k16tJQAyKvZSosQWPokH5uKSV64GJHJElE6l1GzNxrIAGocLeowF8fcZUvcNSXO9 12 | ZUYWccipgOalYyJyIwIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQAkLI/zlkuR0Suj 13 | /mWlAnxQee38bAA03phidFpVczoCM/EP9TtekK90ML0BGK49EvwyD0MJQh832G6s 14 | JKFL4rQFW7PfCyejIxyrn+p2tIfArEYbmxoLlfkWiBTT+y4Cpq3q72z8WDVRuMTR 15 | qaIClkyJ5RcNWOTV0rZFKrqm4Hu+O+3LWwxk6eoGb7M6cUqCSWnw71zX71SwnGuL 16 | 5pzEbUaovJN7Q2TCnqiBxcGF0ukhovWnN37ZT41OPi1Fz6sCG5BDDxEaB3xAWY9k 17 | 15uhxhqwRdYB7fDBuY51F02uUrA9weDFvN0kqfNwn0HRi6ptCqA0kuIBIqMqWhUz 18 | 7o1jaXrK 19 | -----END CERTIFICATE----- 20 | -------------------------------------------------------------------------------- /test/fixtures/rootca-cert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDFDCCAfwCCQCww2ht6TFNnDANBgkqhkiG9w0BAQsFADBMMQswCQYDVQQGEwJ1 3 | czETMBEGA1UECAwKV2FzaGluZ3RvbjEQMA4GA1UEBwwHU2VhdHRsZTEWMBQGA1UE 4 | AwwNRHVtbXkgUm9vdCBDQTAeFw0xODA2MjIyMDEzNDdaFw0yMTA0MTEyMDEzNDda 5 | MEwxCzAJBgNVBAYTAnVzMRMwEQYDVQQIDApXYXNoaW5ndG9uMRAwDgYDVQQHDAdT 6 | ZWF0dGxlMRYwFAYDVQQDDA1EdW1teSBSb290IENBMIIBIjANBgkqhkiG9w0BAQEF 7 | AAOCAQ8AMIIBCgKCAQEA4Uy7shQKMdYnO1RNBoDUon/AQCdZXI3hBXjEuD3FaquE 8 | aw2oOvEsgLKPyWHfSsNVHkiqyeFDjHq5qmPdPjOAK3RqRQ0AzNSenzFFc1HEcWft 9 | bG5YSGVwcK9JVAqgl6TGIBA+L6SMi2nD24kABqkMCWMbUQv0r/88knP/Mpm3arCv 10 | UgStHAo64Fv1wxXXO9vq2ouKWjpp8OJMC62v50xlwt4cScl7OlJAhXj0K88u6Bce 11 | YlyJHE+KIMB7Nd8IbCnJ0xXpL0ju05oqvwmp7u3z3iHyj+FB89Zy2CgKW0kqyyT0 12 | ilDJH2ond9J7O2onqwPS0KYAJrfQVSVcp/teS9rcowIDAQABMA0GCSqGSIb3DQEB 13 | CwUAA4IBAQArYHB4M3InUcXKuQbhYJ1A8MScjR3kWvaQ9iqIQiNHx4IRNEUFmnYa 14 | saSjXo8pmW0WYt1gmVGGIk8b52RcAVgKrWIZG2qCh54bxPf2Cc3rQyQffY+YxqhZ 15 | s7KuJ6TBJnSJC3QnW2lQzc4ZV4clzMd2R9gj6KItG7eRay3Q71L8LG+pYqXi6+D/ 16 | IGopnR9dVT2iVY1I+i60rKqNkR3kh0y0062Xju2TTP/+tBu45Pu/RlHKEiC0tP5E 17 | Wkso0rfipG45owySkcqINZClm3aGe55IwRcQD8+mE1g2y6mfE52WTZ8P/bnpb9ud 18 | W4+sed7/uYU1nDVznSZaxHlmjDpWsvqs 19 | -----END CERTIFICATE----- 20 | -------------------------------------------------------------------------------- /test/fixtures/example-org-cert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDHjCCAgagAwIBAgIJALhNPkJejp8sMA0GCSqGSIb3DQEBBQUAMEMxCzAJBgNV 3 | BAYTAlVTMRUwEwYDVQQKDAxFeGFtcGxlLCBMTEMxHTAbBgNVBAMMFEV4YW1wbGUs 4 | IExMQyBSb290IENBMB4XDTE4MDcxNjIyMDc0NFoXDTMyMDMyNDIyMDc0NFowPDEL 5 | MAkGA1UEBhMCVVMxFTATBgNVBAoMDEV4YW1wbGUsIExMQzEWMBQGA1UEAwwNKi5l 6 | eGFtcGxlLm9yZzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALCaCWmH 7 | OuFQpZgYSwXSSRxs2Bx0HeXpVlirylF5B8NJ8JAj1ydE6yYMrKEjvm6nBUCpOR6n 8 | ttGB99zd2K+S3tVmTxAo0CrZLv3Wgq7T+cfAuNdqY8TnRR6mpASA9nPwmQJvPs27 9 | 2sjNawgKt3knhlGNFbc8DEkOZ3ZcL30c/3pFw3Rx1JLYPSQEB//J3XpoanLS5Jlr 10 | 8RGIcI1Iei/kkT43Jkb01ksHCM2VfiQPYOJwfpPGNPW9tj+C/dIJW53zKzGneZtn 11 | qLmC84VYQsSpUBCd6G+WEuHAhqj01EoBU7Jc7FmboC/IocksaqnEbTA9l3uVOW1g 12 | usa/9N3q4vRilgcCAwEAAaMcMBowGAYDVR0RBBEwD4INKi5leGFtcGxlLm9yZzAN 13 | BgkqhkiG9w0BAQUFAAOCAQEAI4hSIfiINRyfuUjRZFDa7R4soYxcA3ZTm6fJxmWE 14 | W8K/9xazXQ5IvCb8kHhg6x2WnXDEgfFLiunEUGw0YWIUva+eWNlS7iHgmhMXtWcV 15 | WH6iPtdoV7LviM0c1PpH00NB3Np0fsZEulw9mRir7K8gOhlzwZ1T0Z6Lq0n26oxl 16 | 3rfd4CFpdalvHmSgIpvjiqWjjLmFjM7QTdpA5Mf+s8gtjiiMlry5ITZNgACkGswu 17 | x9YmKCWbphGwBlJv30g18xskaJWfiQkEBoMEfyT14MKJFIz7HkZLedblYIb1SFUF 18 | 9R12oU9qguWUAvygP99fkqzkL9gSTpYK6zFxSHmataDl9w== 19 | -----END CERTIFICATE----- 20 | -------------------------------------------------------------------------------- /test/fixtures/intermediate-cert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIEHDCCAwQCCQCCsqUfs9TMATANBgkqhkiG9w0BAQsFADBMMQswCQYDVQQGEwJ1 3 | czETMBEGA1UECAwKV2FzaGluZ3RvbjEQMA4GA1UEBwwHU2VhdHRsZTEWMBQGA1UE 4 | AwwNRHVtbXkgUm9vdCBDQTAeFw0xODA2MjIyMDI5MzlaFw0zMjAyMjkyMDI5Mzla 5 | MFQxCzAJBgNVBAYTAnVzMRMwEQYDVQQIDApXYXNoaW5ndG9uMRAwDgYDVQQHDAdT 6 | ZWF0dGxlMR4wHAYDVQQDDBVEdW1teSBJbnRlcm1lZGlhdGUgQ0EwggIiMA0GCSqG 7 | SIb3DQEBAQUAA4ICDwAwggIKAoICAQCoqDdxHsQrj+6wtrK2BhLaCOcpdt6qm5jH 8 | IGrrustz+gYWzA7Xyc1UqINWKjeEl9/m1K5kP+tc+fHftWmVOYFo639nAlpuXgbH 9 | jpRX3FplTqeLRm+7rF8Z5bT6KDcyb1b7/W4HfKYaMbV29+nmjWB3tfjT6B3HNAZT 10 | O4SFnsEERQ+VnoMf8FPPMQM5nwwUvk/9zFs5SkwVT7eM3USD6XQeDrRMb+akhd6f 11 | KaaDkwzEouwnuIS30PqCRdneb6HmzYEJBedlyp/+lTYzITGqpNVPzXN7emNwJPJV 12 | GRMlVezkJgxmlO+J2DWZxc3rUKwppYDlAEwrPB5T+zJKTOZd9WLdUS/dEgtsqA0K 13 | TV18aQBzyBpR0G5braZUUNZq2i2/ObMMDIoK9k56qeVGO07xm3dkcqFlzhvOLxC8 14 | 1ugDBFY6p5a34UIWEg/TWjt5yWdIv5hPtwFulo+B5Y7QVQv0PBD3QhhtL/OgqyZ3 15 | Mgp3wfG1NeAOpJ0xyTxBNx1EZmW5HKFbNrfjOMM+r9RIA+w6/8RtK06hVJKQnAWC 16 | yy1zOoz/fzi4UX45UeXonTCEYRal43DkYK2o4ED8PvEMVCTVR93a8fZf9NZv7nLn 17 | jY+kOYhe7Ik2sf9+UFtOylfTm3vj0Af1hQ4J3eCGW4ShtlXTpQku+wZ82jAOKc8w 18 | +iGoFoH9cwIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQC8XJsX5pvzoT08TdQL+8SR 19 | 3GUYezJjcTyXGpp6chw//L5SoSsEFZjb/gieLe5E/7mSIy2GeQW85ApuNorPWpvl 20 | L+gAl6OHWEqjoIzIakLYXAe8fyq+j/TdKEg7eRX4DBBILrhHMLyyqGbfWsLJySRk 21 | OFpPVGJ/xhxPvIt5jtKAEY5E+HSpLSZqDLjinH4CQyhdkZOJ9s5SwzmWTrCSpnWd 22 | ZRZW/aGlid6HT+1VdwjpL7Gce3kM44P5GVrH7Zix35DAXlUAghY7iZn4kgUW47uj 23 | SeJ0n//uxkMnb2kLv1RDz9Kgoz0EApgMVr3JKVL/dv2z2NgrfFAtDcS4oMcp8G5p 24 | -----END CERTIFICATE----- 25 | -------------------------------------------------------------------------------- /test/fixtures/agent3-cert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIEIDCCAggCCQChRDh/XiBF+zANBgkqhkiG9w0BAQsFADBUMQswCQYDVQQGEwJ1 3 | czETMBEGA1UECAwKV2FzaGluZ3RvbjEQMA4GA1UEBwwHU2VhdHRsZTEeMBwGA1UE 4 | AwwVRHVtbXkgSW50ZXJtZWRpYXRlIENBMB4XDTE4MDYyMjIwMzEwNFoXDTMyMDIy 5 | OTIwMzEwNFowUDELMAkGA1UEBhMCdXMxEzARBgNVBAgMCldhc2hpbmd0b24xEDAO 6 | BgNVBAcMB1NlYXR0bGUxGjAYBgNVBAMMEWR1bW15LmV4YW1wbGUuY29tMIIBIjAN 7 | BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvSQq3d8AeZMTvtqZ13jWCckikyXJ 8 | SACvkGCQUCJqOceESbg6IHdRzQdoccE4P3sbvNsf9BlbdJKM+neCxabqKaU1PPje 9 | 4P0tHT57t6yJrMuUh9NxEz3Bgh1srNHVS7saKvwHmcKm79jc+wxlioPmEQvQagjn 10 | y7oTkyLt0sn4LGxBjrcv2JoHOC9f1pxX7l47MaiN0/ctRau7Nr3PFn+pkB4Yf6Z0 11 | VyicVJbaUSz39Qo4HQWl1L2hiBP3CS1oKs2Yk0O1aOCMExWrhZQan+ZgHqL1rhgm 12 | kPpw2/zwwPt5Vf9CSakvHwg198EXuTTXtkzYduuIJAm8yp969iEIiG2xTwIDAQAB 13 | MA0GCSqGSIb3DQEBCwUAA4ICAQBnMSIo+kujkeXPh+iErFBmNtu/7EA+i/QnFPbN 14 | lSLngclYYBJAGQI+DhirJI8ghDi6vmlHB2THewDaOJXEKvC1czE8064wioIcA9HJ 15 | l3QJ3YYOFRctYdSHBU4TWdJbPgkLWDzYP5smjOfw8nDdr4WO/5jh9qRFcFpTFmQf 16 | DyU3xgWLsQnNK3qXLdJjWG75pEhHR+7TGo+Ob/RUho/1RX/P89Ux7/oVbzdKqqFu 17 | SErXAsjEIEFzWOM2uDOt6hrxDF6q+8/zudwQNEo422poEcTT9tDEFxMQ391CzZRi 18 | nozBm4igRn1f5S3YZzLI6VEUns0s76BNy2CzvFWn40DziTqNBExAMfFFj76wiMsX 19 | 6fTIdcvkaTBa0S9SZB0vN99qahBdcG17rt4RssMHVRH1Wn7NXMwe476L0yXZ6gO7 20 | Z4uNAPxgaI3BRP75EPfslLutCLZ+BC4Zzu6MY0Salbpfl0Go462EhsKCxvYhE2Dg 21 | T477pICLfETZfA499Fd1tOaIsoLCrILAia/+Yd76uf94MuXUIqykDng/4H7xCc47 22 | BZhNFJiPC6XHaXzN7NYSEUNX9VOwY8ncxKwtP6TXga96PdMUy/p98KIM8RZlDoxB 23 | Xy9dcZBFNn/zrqjW7R0CCWCUriDIFSmEP0wDZ91YYa6BVuJMb5uL/USkTLpjZS4/ 24 | HNGvug== 25 | -----END CERTIFICATE----- 26 | -------------------------------------------------------------------------------- /test/fixtures/agent3-key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEpQIBAAKCAQEAvSQq3d8AeZMTvtqZ13jWCckikyXJSACvkGCQUCJqOceESbg6 3 | IHdRzQdoccE4P3sbvNsf9BlbdJKM+neCxabqKaU1PPje4P0tHT57t6yJrMuUh9Nx 4 | Ez3Bgh1srNHVS7saKvwHmcKm79jc+wxlioPmEQvQagjny7oTkyLt0sn4LGxBjrcv 5 | 2JoHOC9f1pxX7l47MaiN0/ctRau7Nr3PFn+pkB4Yf6Z0VyicVJbaUSz39Qo4HQWl 6 | 1L2hiBP3CS1oKs2Yk0O1aOCMExWrhZQan+ZgHqL1rhgmkPpw2/zwwPt5Vf9CSakv 7 | Hwg198EXuTTXtkzYduuIJAm8yp969iEIiG2xTwIDAQABAoIBAGPIw/C/qJF7HYyv 8 | 6T+7GTiaa2o0IiehbP3/Y8NTFLWc49a8obXlHTvMr7Zr2I/tE+ojtIzkH9K1SjkN 9 | eelqsNj9tsOPDI6oIvftsflpxkxqLtclnt8m0oMhoObf4OaONDT/N8dP4SBiSdsM 10 | ZDmacnMFx5NZVWiup4sVf2CYexx7qks9FhyN2K5PArCQ4S9LHjFhSJVH4DSEpv7E 11 | Ykbp30rhpqV7wSwjgUsm8ZYvI2NOlmffzLSiPdt3vy2K5Q25S/MVEAicg83rfDgK 12 | 6EluHjeygRI1xU6DJ0hU7tnU7zE9KURoHPUycO3BKzZnzUH26AA36I58Pu4fXWw/ 13 | Cgmbv2ECgYEA+og9E4ziKCEi3p8gqjIfwTRgWZxDLjEzooB/K0UhEearn/xiX29A 14 | FiSzEHKfCB4uSrw5OENg2ckDs8uy08Qmxx7xFXL7AtufAl5fIYaWa0sNSqCaIk7p 15 | ebbUmPcaYhKiLzIEd1EYEL38sXVZ62wvSVMRSWvEMq44g1qnoRlDa/8CgYEAwUTt 16 | talYNwVmR9ZdkVEWm9ZxirdzoM6NaM6u4Tf34ygptpapdmIFSUhfq4iOiEnRGNg/ 17 | tuNqhNCIb3LNpJbhRPEzqN7E7qiF/mp7AcJgbuxLZBm12QuLuJdG3nrisKPFXcY1 18 | lA4A7CFmNgH3E4THFfgwzyDXsBOxVLXleTqn+rECgYEA9up1P6J3dtOJuV2d5P/3 19 | ugRz/X173LfTSxJXw36jZDAy8D/feG19/RT4gnplcKvGNhQiVOhbOOnbw0U8n2fQ 20 | TCmbs+cZqyxnH/+AxNsPvvk+RVHZ93xMsY/XIldP4l65B8jFDA+Zp06IESI2mEeM 21 | pzi+bd1Phh+dRSCA2865W2MCgYEAlxYsgmQ1WyX0dFpHYU+zzfXRYzDQyrhOYc2Z 22 | duVK+yCto1iad7pfCY/zgmRJkI+sT7DV9kJIRjXDQuTLkEyHJF8vFGe6KhxCS8aw 23 | DIsI2g4NTd6vg1J8UryoIUqNpqsQoqNNxUVBQVdG0ReuMGsPO8R/W50AIFz0txVP 24 | o/rP0LECgYEA7e/mOwCnR+ovmS/CAksmos3oIqvkRkXNKpKe513FVmp3TpTU38ex 25 | cBkFNU3hEO31FyrX1hGIKp3N5mHYSQ1lyODHM6teHW0OLWWTwIe8rIGvR2jfRLe0 26 | bbkdj40atYVkfeFmpz9uHHG24CUYxJdPc360jbXTVp4i3q8zqgL5aMY= 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /test/fixtures/example-ca-key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEpAIBAAKCAQEA26Odpa6GI2pdRHPgNJzMJdgs9ZQ7i9g4/U3n15xifAGNsGXQ 3 | c7dgfe22fCdJ4YJBdCHKikPQF5thS1iu4Sn03yMHj8QapPThAsgI5Xe43eoFqpCg 4 | hsD27J3efsv+gncrxW8qi37o3vSmaom5gUTjY42l0WP0bN/Ehkeptz5TlhbKEfSq 5 | 30oZFbh68BnTKU1p3ai6j7iHnervoHDlAWzI0QUTn/DufDyMOT2+AUUso6ElZsk+ 6 | aqlmE7ZcYmVjwKKd82ZCk16tJQAyKvZSosQWPokH5uKSV64GJHJElE6l1GzNxrIA 7 | GocLeowF8fcZUvcNSXO9ZUYWccipgOalYyJyIwIDAQABAoIBAQCpLyUWJJMq4Jmr 8 | St7faBBQ7m3jVSVxGUL8y2/Uw5+RCdlMkYC6QfzPw7YXeGQOkzk77XOIgaffJ08Q 9 | rOU4iAsZfnAqq9IiUoaoj9FagJgkSEnJEtK0Oht6NDjYzuXjNUzALltFTKKwax9t 10 | XV1JV/kYDFo4Kphy7QMCtFzky9uy9bms300BYqzNUI4CUFJZphU9oYvszD0tSeEP 11 | sYcYgYYLflq4EsvOKOs+mfP2AoWDl5gGFoQQghAjQUqD0ylNMuud9WjLm4uqCgcp 12 | 7eEC7fS3RBiTtlblZGbBK+Tb5gUea7/AbktO3bXyrzbGXfmGV21yz31jFw4dNLtR 13 | uyzmw9AxAoGBAO5muO7H2Tw8AXYCfEcV42cJWrNZagIB7YfevME/cajLHd3Rqiab 14 | 6CxgWE8fZ8+a1cyz79RcJmNEyKhEMBiQLo/cNu7aC6DUQCBW2SWiUpablVep2OSu 15 | ZsQ2WqGcTsQpw2yHt5edeyiJDV9EjeLKNEWMM4KJ1mL6NB3Pg2XQ0EllAoGBAOva 16 | VDLK8I1qo/cmj/aevvAEVZtOpbHKkVkk1LE2E27ndt0W6ucmazpVZWpB8yGMstMT 17 | jX0hB+QkRKTZKVnAbmltUU9RKXf6MBrQlt4cgxt3Nulzo5A1pn4I+eEPfp+wcDUO 18 | LwRowb4gAuDIyoxtWgrhLhg38PzyPG/9/Uuhh9jnAoGARs+IGpSNt9By9qRiWZLs 19 | yowCvsbOL+52qSbFlfjMP8ClDF/cVD92xMeRM182rHlnC7heggsk89NYbnJifslE 20 | q0JTprppPcoNig2BziXathVcVH+EhtguehVS8jHFS4rEGxZst8H09ONQb32jTzuU 21 | LxwnqJxca2W52Sjr9HADjekCgYEA1VTrmfHkehW+b9xqlYlrbdCN2mfeCh4PMJHW 22 | 7N/b3hHxevOy0Bf5H4+EpwM2Nt98FN53uXn6GEsR6AYhl/QuKqBdzp+8IbCkeGy6 23 | OfeSQ3Kmsa0Ogm90xEW8N+16/FhsdiD5OETFEgMEd2xHgZObwYGC3xcinLlDH42e 24 | 4h0esnsCgYA/NLMxcV+NamrY+6wbY0YFsbF3Ma7G8RK4wOolqkXj87LhjqAPTK1u 25 | DNmMDZZcBP0RFTCZggeYaxKIHCbAD/coMzLrfxxP6bRfzMwwlgv4MEGINd/yEUV8 26 | lfqBxE1jviyitbs9iBb5YDOQD3d2v+h0mbY+QwwF2ZGiU2lWgw0+MA== 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /test/fixtures/example-com-key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEowIBAAKCAQEAueaNGs1WXtqgEiDtbTyJs/Qi8qijfo4VMQryMTbc8Nbo6SX7 3 | b/t1H9wF7Hd22C2iKDjcObKBvpkI/M/qXd7g2HJDcDgO8JXJrw4xDitWkay08Ej4 4 | DhiQ2/9mxMA4gnsB1DMPGoKScf9bMalmT2vQA7TQHnIgaQ7CKECkkHCQ82/AaqFM 5 | Y8NNMSYQsQt0YnJR2TYoWVE6zz/YgDrIQ7uR8GT70qIIUPJjvq/D0biIaLtJJcDZ 6 | s3GUstHvYoUkTEP+fyr4psfNrCxCI34xvs9ItuwggXF+XnsmKC89iUgG233LS5gR 7 | Xp+BHxh0UZtrIxRNo02WnduXtUPcLyS2bB+vowIDAQABAoIBAQCOH60RKJs3uX1X 8 | XsUmFnpQkf2F91fHkWiyLeQrq/4OQr+eP+OTIXPR/6yntE2vyHOIvK/hjI+U3/r3 9 | DR3o1lSgJftY+AKz/FBM/VSTuJ5ekONkMOU0IaPeDczL7wlE+DXVvZN2eQztCoT1 10 | 2sTVQ1+s9beHD0moCBh4HbOymdy3vxETKePpgoroPj/1M03DY7P6V6BoT8Wt21QT 11 | 55iFhgs5P2OnE083gqS+3GFuB33rLFw5+S8jLucsce/KrumoCaaGK0NDpp1KDx3u 12 | lGJo6IOIf6HOZH1TQQsBBwdo3WaQxD/hqYl6uDIKVOXIItczQpNxPmB5HiPPnls8 13 | tEgxibDBAoGBAO/Z1b9gE5OckUicW3eGAhVftHnRcN6HiwmaQrOzBIB6KV3ooT7H 14 | HbYa+wCs6GjKUkiNieUTSewzNWNFtt6xxlF+iR8zvnsUd4H0uM08zeY0XbJsr82j 15 | eA08ucBxbH9K6z7/zwixRc0cPW6cYQPV+bl64FtfL1xalwfo7kgv0fAvAoGBAMZq 16 | zp8F3FZ8NroqapEfDqFIRlE2qCrqTUMRpUkvpPTvSo1ToRuPYVkkqTFz+60Sz3qd 17 | a0UWDGHuYIVtIbdnoCQIBX7LURgjT/5ycr0EGHTbkN7n92GOlt6KKoWOCxxmYAXz 18 | XY65r6ORBi+JxlzjRPkEke2qq+uo0ScHzIoVIsbNAoGAIURzk0G+NWEn/YxCW4eo 19 | zlIh9CuKnjLqpABTdB1GaJOJHU9g3hY8FsATRHq3GaHGW+3VzIIqZKqNoyFORgEe 20 | YrtMRZmlt+iU0t+gqkbewClPPao5kCo2Qo/Dq9+PjvBTRL+ydaAw8EwUcT0bb67E 21 | Hmfeqo2/Qe4GuMAkHLsVQV0CgYAql3rLAAMQPZGx3LELfdMyI8WUYVsv+yOipgMI 22 | 9sU+Hq+uLqmCtQB8ljYLe44m1Mbc1zXTmqwFyZnGimqWaBQkJ+SmFxbNS6c8Q20F 23 | zf9B9m/eqYSc2HLiSl5eVONlsUQjgt4uaQWe4t6lDboa7l475hM9JUnKSG69Jhtt 24 | +KOCcQKBgFYc7ni8XQpojPRdn3iv0UvKkePu59bV2cp8/c+0LYduI8env7Qo/mJ9 25 | +RlN13uptZah0a3PAKVnqxrtClG3OwyXAkZLuPg9vDt/c7i4wZgKOTZIcdwJz82r 26 | edZLAQMQzzqgPVg5MV9Xitm9YjWB931dims+aXOJwuKNYp3Hxunl 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /test/fixtures/example-net-key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEowIBAAKCAQEApKqQ4j7W7fDP8G/NFLSwhKs1uvU8y/hRuKNTXKgye6MYAmuJ 3 | iQWE9u9OPxQdcTT7zgQ1swTPKC8ndJoD+bk0YQeqNEQwmR3LdAfwJbOyHora/SWG 4 | 64uKgN7oZg9qETMmmBOOyD/YzrZiwcTvf6b7G4FvQ6R6kowXOt7iu8dBvWeRClvi 5 | sGvifYWwkzYGVwqaFXhgUz5FhcQwICgRB5yxjjLSn/h17685TvUCPWR84Bx6GJ/K 6 | H4f8j+UFMmIRvkJlU05zKkVd9PGPvCGfmCdj+yZpaALNS7xbOKXfHMbWsMiLbFHw 7 | AF9IhYRKS9qIeZ028j5+HKstPDq+eM8Ox45RhQIDAQABAoIBAQCFek2fV9jZX47v 8 | E3FmC2Ha6P3Q4uG5ZTy5MotsXFsvpXDc5VbDX1n84NbIgwGqtow232q4Bnn11ONi 9 | vUXjs6El1hS98ViDhYdaMuUTaqpD38LL2ROY86/97rLrqCsObiwmshPXdwnD5kv/ 10 | TifljfGWxkDuz/EbwbtC2VFpbYd4g6GvXcME4EhRi4NlaHyeoF70eH3A20MtaVbB 11 | 4cWJOKX//NvxX3gO015/UeQjgrECwSGPCe7madMJuyAY2k6IR3R/RdJ/BcEWwNLT 12 | w69Ii1tRwx+n/8BbW/nUpnbBt4CxcHBPGWpwHiUmCZxEkw+AtUoyY6+9P1lVwBr/ 13 | X8ayGBIxAoGBANo8HBu3zWeFUPIh3fEfoJWz4XPKeITfAYxs4ygo4Na9R4gErgdX 14 | 9a9coELJHnuk2xW5eMCsZQfNHcJ1z1HaW2aDIkPhhLkTFIiYbRMzL9mUt9C2TkEX 15 | KUqHZjvfPTJYw7By8nBvD6IlQzFN0MkRr1ClyGhn4VHXN2Hg5yibJoC/AoGBAMEp 16 | WTqX1uAK076I4VXGYHzA7fs8L3XIEO+1O5VB0cZhffXSTmpIfDxjPvgPXvvJ7Gxo 17 | qGfhKJPdQLBC0Q0sVKadKHS69abOsbU3ELDOFWsmHdz5By5ClhXznP4V70BqDjcE 18 | gY0NnfX23GjmD/ktHY3m7WQOp/BNqdKZ+WkkfTq7AoGABmS8bhN0cRP2yyEy312N 19 | 1tTuRfYokbv2z6ZhAUwxShlBfzqj3pM1H24XQ92ghv7NbFWCtCJY02Gu/YN2D2/W 20 | PaqZ1mNFACcO8vGEQIu/WyCAQh0VLdL/4q8Uz6w6nr76/LBhaenXC2kMDuW4uCtM 21 | d5Bjzqs8bqlfdf6gxw/EkQECgYANMsQUCtf1yweY10V+VOW24H/iUMR1acwudNWz 22 | ZyHsSjEPIOWWwYkrnyRoUrSMw8lRxvzqzCm3oMCQyp/6JHNMRyTChRvP4AOofDC5 23 | w/sWrS4RMTraYFZc/Z5Whi0o25ZJEBfCCZIngkyhJkclIX4JeiPyWEYFV2kpKLgw 24 | BPxBjQKBgAUlp/4OLFvGvMVhPE3/t9FMDmBjEKGoJTLyFS8miQReTTgkAsIoSQbr 25 | ViarB939jtuAZgYO0scWamM7dZV5wCclgdiRSE1YkuA51z4pm/rpJOrpC2E8Lc4I 26 | oIkUFDS8pdpCzwbhRK0axntDKBvJZMPRNC2xVsFAm7vcURzIRdp0 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /test/fixtures/example-org-key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEowIBAAKCAQEAsJoJaYc64VClmBhLBdJJHGzYHHQd5elWWKvKUXkHw0nwkCPX 3 | J0TrJgysoSO+bqcFQKk5Hqe20YH33N3Yr5Le1WZPECjQKtku/daCrtP5x8C412pj 4 | xOdFHqakBID2c/CZAm8+zbvayM1rCAq3eSeGUY0VtzwMSQ5ndlwvfRz/ekXDdHHU 5 | ktg9JAQH/8ndemhqctLkmWvxEYhwjUh6L+SRPjcmRvTWSwcIzZV+JA9g4nB+k8Y0 6 | 9b22P4L90glbnfMrMad5m2eouYLzhVhCxKlQEJ3ob5YS4cCGqPTUSgFTslzsWZug 7 | L8ihySxqqcRtMD2Xe5U5bWC6xr/03eri9GKWBwIDAQABAoIBAC6SgLbura1rui/R 8 | AD/pw9QWNCvUx2mxTk8WBsXTmCN9JU6t1PiLKUQghYn9RiQiMbVqqyCVdBXuI3KA 9 | orsyjUdTsrf9O2CBY+4XGuGGD1IJVfWhHex4XPcHVSvG4reSlJdaxQI2by0TVw2I 10 | 3fe6X6a0AS0QTeEaRlilWPSyqAETYmI6+BNwH61mTrmEa35bLJZhbdWZmAZg1K5N 11 | jJR7vr1du9ZXUbUouR5uQ9dXj6mGPCe60pTtpF29g4UNAyTAEW9QDQlvlHSie5pB 12 | thQLabrj//Bp24NKuwrAhOJ4S8vu9wLaYWSP/MyWm5QFpJyvX1aQdg3n6ztBLxi0 13 | qF48JFkCgYEA4zl4+CFoVKi6xNeCtWSsHLvlglNK4p1N93rrcWe2ZIbcpoFhwgwD 14 | ALLW7MJPTwfNy0k6jTmjw6JzB7iAjTeFubWsgjmGx3zf/Wcdy7VDLxepuWQpeCRl 15 | Gu9O7tl09EI93AbCm3zFYCfTqXk80VL0NdvQV7NkIWj0GwZZkNjzLesCgYEAxvdk 16 | uCWnnYJSfQ+dCa2+ZJdctaMGccVA5owK5vNHeH53rRxmzfhEHsAHToo9sTuGId13 17 | 730s9lcuEh27sr/JYVGJO5Xj0HOPr+lynQNCq++61VIcfHJI+YA2cIMPmvcI58zO 18 | Uf2K09VYsWPwkuXsI09XcJICRSf9+GqJ0xvMRVUCgYEAvbQO7cIec4fHHm36v2yE 19 | UR0CGRNnEtlX7YUfKjf3S+hYMV8VecdDfoLQ8yuqwQV35/+ENcHomn124fxEvyGa 20 | sJgfpT1X7BayfsJnWtpDc/6igZhBEv/3tUwUL0832HGxaY/vS5cYxhWaHgSNJQnH 21 | v2t8fqvTrh0Mp79eaZLnO+8CgYA1ahEWBrXa6TOv81MbWBN2+dOfx6ZhQK1Nt2bt 22 | 3X3gcUM7rBZFUVMoVjEf+qjZPbuit8wM9TPu5jSuFGFheW9cuwD2iCTiv0Nh3HAy 23 | ir+JXRBxWFvhZOUGZk83IVg9J/iyEh+Jj2GHwQzQgp4BMcyVJiW5SNlnK3dZC3D4 24 | xcuvCQKBgAeoP4mwCNhhowtq1CyfJZTH+ZoV6AfFLu5gRnZvjCGL8/CuJIu2vOK8 25 | gH08jHlsjmQDWMA2oIbRz/k/6UrYccsZ2INww+g+bAZ/LUUrfJpAfMHJmODSodZS 26 | kuJuCXmKOIni5cTt3/70zrb9fbv973Cb/Xw+80BeWadH4OKjqmv1 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /* 4 | * index.js: Create an http AND/OR an https server and call the same request handler. 5 | * 6 | * (C) 2013, Charlie Robbins. 7 | * 8 | */ 9 | 10 | const 11 | fs = require('fs').promises, 12 | tls = require('tls'), 13 | path = require('path'), 14 | constants = require('constants'), 15 | connected = require('connected'), 16 | errs = require('errs'), 17 | assign = require('object-assign'); 18 | 19 | const pemFormat = /-----BEGIN/; 20 | 21 | const CIPHERS = [ 22 | 'ECDHE-RSA-AES256-SHA384', 23 | 'DHE-RSA-AES256-SHA384', 24 | 'ECDHE-RSA-AES256-SHA256', 25 | 'DHE-RSA-AES256-SHA256', 26 | 'ECDHE-RSA-AES128-SHA256', 27 | 'DHE-RSA-AES128-SHA256', 28 | 'HIGH', 29 | '!aNULL', 30 | '!eNULL', 31 | '!EXPORT', 32 | '!DES', 33 | '!RC4', 34 | '!MD5', 35 | '!PSK', 36 | '!SRP', 37 | '!CAMELLIA' 38 | ].join(':'); 39 | 40 | const secureOptions = constants.SSL_OP_NO_SSLv3; 41 | 42 | /** 43 | * function createServers (dispatch, options, callback) 44 | * Creates and listens on both HTTP and HTTPS servers. 45 | */ 46 | module.exports = async function createServers(options, listening) { 47 | try { 48 | options = normalizeOptions(options); 49 | } catch (err) { 50 | return listening(err); 51 | } 52 | 53 | const [httpResult, httpsResult, http2Result] = await Promise.allSettled([ 54 | createHttp(options.http, options.log), 55 | createHttps(options.https, options.log), 56 | createHttps(options.http2, options.log, true) 57 | ]) 58 | 59 | const servers = {}; 60 | if (httpResult.value) servers.http = httpResult.value; 61 | if (httpsResult.value) servers.https = httpsResult.value; 62 | if (http2Result.value) servers.http2 = http2Result.value; 63 | 64 | const errorSource = httpResult.reason || httpsResult.reason || http2Result.reason; 65 | if (errorSource) { 66 | if (Array.isArray(errorSource)) { 67 | errorSource = errorSource[0]; 68 | } 69 | return listening( 70 | errs.create({ 71 | message: errorSource && errorSource.message, 72 | http2: http2Result.reason, 73 | https: httpsResult.reason, 74 | http: httpResult.reason 75 | }), 76 | servers 77 | ); 78 | } 79 | 80 | listening(undefined, servers); 81 | }; 82 | 83 | function normalizeOptions(options) { 84 | const http = normalizeHttpOptions(options.http, options); 85 | const https = normalizeHttpsOptions(options.https, options); 86 | const http2 = normalizeHttpsOptions(options.http2, options); 87 | 88 | if (!http && !https && !http2) { 89 | throw new Error('http, https, and/or http2 are required options'); 90 | } 91 | 92 | return { 93 | http, 94 | https, 95 | http2, 96 | log: options.log || function() {} 97 | }; 98 | } 99 | 100 | function normalizeHttpOptions(httpConfig, baseConfig) { 101 | if (typeof httpConfig === 'undefined') return; 102 | 103 | if (Array.isArray(httpConfig)) { 104 | return httpConfig.map(cfg => normalizeHttpOptions(cfg, baseConfig)); 105 | } 106 | 107 | let port = 108 | typeof httpConfig === 'object' && 'port' in httpConfig 109 | ? httpConfig.port 110 | : httpConfig; 111 | if (typeof port === 'undefined') { 112 | port = 80; 113 | } 114 | 115 | const http = { 116 | host: httpConfig.host || baseConfig.host, 117 | port: +port, 118 | handler: httpConfig.handler || baseConfig.handler, 119 | timeout: httpConfig.timeout || baseConfig.timeout, 120 | keepAliveTimeout: httpConfig.keepAliveTimeout || baseConfig.keepAliveTimeout 121 | }; 122 | 123 | if (!http.handler) { 124 | throw new Error('handler option is required'); 125 | } 126 | 127 | return http; 128 | } 129 | 130 | function normalizeHttpsOptions(httpsConfig, baseConfig) { 131 | if (typeof httpsConfig === 'undefined') return; 132 | 133 | if (Array.isArray(httpsConfig)) { 134 | return httpsConfig.map(cfg => normalizeHttpsOptions(cfg, baseConfig)); 135 | } 136 | 137 | const https = { 138 | ...httpsConfig, 139 | host: httpsConfig.host || baseConfig.host, 140 | port: +('port' in httpsConfig ? httpsConfig.port : 443), 141 | handler: httpsConfig.handler || baseConfig.handler, 142 | timeout: httpsConfig.timeout || baseConfig.timeout, 143 | keepAliveTimeout: httpsConfig.keepAliveTimeout || baseConfig.keepAliveTimeout 144 | }; 145 | 146 | if (!https.handler) { 147 | throw new Error('handler option is required'); 148 | } 149 | 150 | return https; 151 | } 152 | 153 | function normalizeCertContent(root, cert, key) { 154 | // Node accepts an array of certs, which must match up with an array of keys. 155 | // The user may instead intend for an array passed into cert to represent 156 | // a cert chain they want to concatenate. Therefore, if key is not an array, 157 | // we'll assume the latter. 158 | if (Array.isArray(cert)) { 159 | if (Array.isArray(key)) { 160 | // This is an array of certs/chains with corresponding keys 161 | return normalizeCertChainList(root, cert); 162 | } else { 163 | // This is a single cert chain 164 | return normalizeCertChain(root, cert); 165 | } 166 | } 167 | 168 | return normalizePEMContent(root, cert); 169 | } 170 | 171 | function normalizeCertChainList(root, data) { 172 | // If this is an array, treat like an array of bundles, otherwise a single 173 | // bundle 174 | return Array.isArray(data) 175 | ? Promise.all(data.map(function(item) { 176 | return normalizeCertChain(root, item); 177 | })) 178 | : normalizePEMContent(root, data); 179 | } 180 | 181 | async function normalizeCertChain(root, data) { 182 | // A chain can be an array, which we concatenate together into one PEM, 183 | // an already-concatenated chain, or a single PEM 184 | 185 | const content = await normalizePEMContent(root, data); 186 | return Array.isArray(content) ? content.join('\n') : content; 187 | } 188 | 189 | function normalizeCA(root, ca) { 190 | if (ca && !Array.isArray(ca)) { 191 | ca = [ca]; 192 | } 193 | return ca && Promise.all(ca.map(normalizePEMContent.bind(null, root))); 194 | } 195 | 196 | /** 197 | * function normalizePEMContent(root, file) 198 | * Returns the contents of `file` verbatim if it is determined to be 199 | * certificate material and not a file path. Otherwise, returns the 200 | * certificate material read from that file path. 201 | */ 202 | function normalizePEMContent(root, file) { 203 | if (Array.isArray(file)) 204 | return Promise.all(file.map(function map(item) { 205 | return normalizePEMContent(root, item); 206 | })); 207 | 208 | // 209 | // Assumption that this is a Buffer, a PEM file, or something broken 210 | // 211 | if (typeof file !== 'string' || pemFormat.test(file)) { 212 | return file; 213 | } 214 | 215 | return fs.readFile(path.resolve(root, file)); 216 | } 217 | 218 | function normalizeCiphers(ciphers) { 219 | ciphers = ciphers || CIPHERS; 220 | // 221 | // Remark: If an array is passed in lets join it like we do the defaults 222 | // 223 | if (Array.isArray(ciphers)) { 224 | ciphers = ciphers.join(':'); 225 | } 226 | return ciphers; 227 | } 228 | 229 | async function getSNIHandler(sslOpts) { 230 | const sniHosts = Object.keys(sslOpts.sni); 231 | 232 | // Pre-compile regexps for the hostname 233 | const hostRegexps = sniHosts.map(function(host) { 234 | return host === '*' ? /.*/ : new RegExp( 235 | '^' + 236 | host 237 | .replace('.', '\\.') // Match dots, not wildcards 238 | .replace('*\\.', '(?:.*\\.)?') + // Handle optional wildcard sub-domains 239 | '$', 240 | 'i' 241 | ); 242 | }); 243 | 244 | // Prepare secure contexts ahead-of-time 245 | const hostSecureContexts = await Promise.all(sniHosts.map(async function(host) { 246 | var hostOpts = sslOpts.sni[host]; 247 | 248 | var root = hostOpts.root || sslOpts.root; 249 | 250 | const [key, cert, ca] = await Promise.all([ 251 | normalizePEMContent(root, hostOpts.key), 252 | normalizeCertContent(root, hostOpts.cert), 253 | normalizeCA(root, hostOpts.ca || sslOpts.ca) 254 | ]) 255 | 256 | return tls.createSecureContext( 257 | assign({}, sslOpts, hostOpts, { 258 | key, 259 | cert, 260 | ca, 261 | ciphers: normalizeCiphers(hostOpts.ciphers || sslOpts.ciphers), 262 | honorCipherOrder: !!( 263 | hostOpts.honorCipherOrder || sslOpts.honorCipherOrder 264 | ), 265 | secureProtocol: 'SSLv23_method', 266 | secureOptions: secureOptions 267 | }) 268 | ); 269 | })); 270 | 271 | return function(hostname, cb) { 272 | var matchingHostIdx = sniHosts.findIndex(function(candidate, i) { 273 | return hostRegexps[i].test(hostname); 274 | }); 275 | 276 | if (matchingHostIdx === -1) { 277 | return void cb(new Error('Unrecognized hostname: ' + hostname)); 278 | } 279 | 280 | cb(null, hostSecureContexts[matchingHostIdx]); 281 | }; 282 | } 283 | 284 | // 285 | // ### function createHttp (httpConfig) 286 | // Attempts to create and listen on the the HTTP server. 287 | // 288 | async function createHttp(httpConfig, log) { 289 | if (typeof httpConfig === 'undefined') { 290 | log('http | no options.http; no server'); 291 | return null; 292 | } 293 | 294 | if (Array.isArray(httpConfig)) { 295 | return await createMultiple(createHttp, httpConfig, log); 296 | } 297 | 298 | const 299 | server = require('http').createServer(httpConfig.handler), 300 | port = httpConfig.port; 301 | 302 | commonPostCreateSetup(httpConfig, server); 303 | 304 | const args = [server, port]; 305 | if (httpConfig.host) { 306 | args.push(httpConfig.host); 307 | } 308 | 309 | log('http | try listen ' + port); 310 | 311 | return new Promise((resolve, reject) => { 312 | args.push(function listener(err) { 313 | err ? reject(err) : resolve(server); 314 | }); 315 | connected.apply(null, args); 316 | }); 317 | } 318 | 319 | // 320 | // ### function createHttps () 321 | // Attempts to create and listen on the HTTPS server. 322 | // 323 | async function createHttps(ssl, log, h2) { 324 | if (typeof ssl === 'undefined') { 325 | log('https | no options.https; no server'); 326 | return null; 327 | } 328 | 329 | if (Array.isArray(ssl)) { 330 | return await createMultiple(createHttps, ssl, log, h2); 331 | } 332 | 333 | const [key, cert, ca] = await Promise.all([ 334 | normalizePEMContent(ssl.root, ssl.key), 335 | normalizeCertContent(ssl.root, ssl.cert, ssl.key), 336 | normalizeCA(ssl.root, ssl.ca) 337 | ]); 338 | 339 | const finalHttpsOptions = assign({}, ssl, { 340 | key, 341 | cert, 342 | ca, 343 | // 344 | // Properly expose ciphers for an A+ SSL rating: 345 | // https://certsimple.com/blog/a-plus-node-js-ssl 346 | // 347 | ciphers: normalizeCiphers(ssl.ciphers), 348 | honorCipherOrder: !!ssl.honorCipherOrder, 349 | // 350 | // Protect against the POODLE attack by disabling SSLv3 351 | // @see http://googleonlinesecurity.blogspot.nl/2014/10/this-poodle-bites-exploiting-ssl-30.html 352 | // 353 | secureProtocol: 'SSLv23_method', 354 | secureOptions: secureOptions 355 | }); 356 | 357 | if (ssl.sni && !finalHttpsOptions.SNICallback) { 358 | finalHttpsOptions.SNICallback = await getSNIHandler(ssl); 359 | } 360 | 361 | const port = ssl.port; 362 | log('https | listening on %d', port); 363 | const server = h2 364 | ? require('http2').createSecureServer(finalHttpsOptions, ssl.handler) 365 | : require('https').createServer(finalHttpsOptions, ssl.handler); 366 | 367 | commonPostCreateSetup(ssl, server); 368 | const args = [server, port]; 369 | if (ssl.host) { 370 | args.push(ssl.host); 371 | } 372 | 373 | return new Promise((resolve, reject) => { 374 | args.push(function listener(err) { 375 | err ? reject(err) : resolve(server); 376 | }); 377 | connected.apply(null, args); 378 | }); 379 | } 380 | 381 | async function createMultiple(createFn, configArray, log) { 382 | const errorsOrServers = await Promise.allSettled( 383 | configArray.map(cfg => createFn(cfg, log)) 384 | ); 385 | 386 | const errors = [], servers = []; 387 | for (const result of errorsOrServers) { 388 | result.reason && errors.push(result.reason); 389 | result.value && servers.push(result.value); 390 | } 391 | 392 | if (errors.length) { 393 | throw errors; 394 | } else { 395 | return servers; 396 | } 397 | } 398 | 399 | function commonPostCreateSetup({ timeout, keepAliveTimeout }, server) { 400 | if (typeof timeout === 'number') { 401 | server.setTimeout(timeout); 402 | } 403 | if (typeof keepAliveTimeout === 'number') { 404 | server.keepAliveTimeout = keepAliveTimeout; 405 | } 406 | } 407 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # create-servers 2 | 3 | Create an http AND/OR an https server and call the same request handler. 4 | 5 | ## Usage 6 | 7 | The `create-servers` module exports a function that takes a config object and 8 | a node-style callback. The config object must have at minimum an `http` or 9 | `https` property (or both). The following config properties are supported: 10 | 11 | | Property | Description | 12 | | ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | 13 | | `handler` | Request handler to be used for any server, unless overridden specifically with `http.handler` or `https.handler`. | 14 | | `timeout` | Socket timeout in milliseconds for any server, unless overridden with `http.timeout` or `https.timeout`. Defaults to the node default of 2 minutes. | 15 | | `keepAliveTimeout` | Milliseconds of activity before sockets are destroyed. Defaults to the node default value (currently 5 seconds). | 16 | | `http` | Optional. If present, an HTTP server is started. This can be an object or a number. If it's a number, it's used as the TCP port for an HTTP server. You may also use an Array to start multiple servers. | 17 | | `http.port` | TCP port for the HTTP server. Defaults to `80`. | 18 | | `http.host` | The address the HTTP server is bound to. Defaults to `::` or `0.0.0.0`. | 19 | | `http.timeout` | Socket timeout in milliseconds for the server. If unspecified, the top-level `timeout` configuration is used. | 20 | | `http.keepAliveTimeout` | Overrides the top-level keepAliveTimeout setting if specified. | 21 | | `http.handler` | Handler for HTTP requests. If you want to share a handler with all servers, use a top-level `handler` config property instead. | 22 | | `https` | Optional object. If present, an HTTPS server is started. You may start multiple HTTPS servers by passing an array of objects | 23 | | `https.port` | TCP port for the HTTPS server. Defaults to `443`. | 24 | | `https.host` | The address the HTTPS server is bound to. Defaults to `::` or `0.0.0.0`. | 25 | | `https.timeout` | Socket timeout in milliseconds for the server. If unspecified, the top-level `timeout` configuration is used. | 26 | | `https.keepAliveTimeout` | Overrides the top-level keepAliveTimeout setting if specified. | 27 | | `https.ciphers` | Defaults to a [default cipher suite](#note-on-security). To customize, either supply a colon-separated string or array of strings for the ciphers you want the server to support. | 28 | | `https.honorCipherOrder` | If true, prefer the server's specified cipher order instead of the client's. Defaults to `false`. | 29 | | `https.root` | Root directory for certificate/key files. See [Certificate normalization](#certificate-normalization) for more details. | 30 | | `https.key` | PEM/file path for the server's private key. See [Certificate normalization](#certificate-normalization) for more details. | 31 | | `https.cert` | PEM/file path(s) for the server's certificate. See [Certificate normalization](#certificate-normalization) for more details. | 32 | | `https.ca` | Cert or array of certs specifying trusted authorities for peer certificates. Only required if your server accepts client certificate connections signed by authorities that are not trusted by default. See [Certificate normalization](#certificate-normalization) for more details. | 33 | | `https.sni` | See [SNI Support](#sni-support). | 34 | | `https.handler` | Handler for HTTPS requests. If you want to share a handler with all servers, use a top-level `handler` config property instead. | 35 | | `https.*` | Any other properties supported by [https.createServer](https://nodejs.org/dist/latest-v8.x/docs/api/https.html#https_https_createserver_options_requestlistener) can be added to the https object, except `secureProtocol` and `secureOptions` which are set to recommended values. | 36 | | `http2` | Optional object. If present, an HTTP/2 server is started. You may start multiple HTTP/2 servers by passing an array of objects | 37 | | `http2.allowHTTP1` | Enable [ALPN negotiation] allowing support for both HTTPS and HTTP/2 on the same socket. | 38 | | `http2.*` | The same `https` security options are allowed, as well as any other properties supported by [http2.createSecureServer](https://nodejs.org/dist/latest-v8.x/docs/api/http2.html#http2_http2_createsecureserver_options_onrequesthandler). | 39 | 40 | If successful, the `create-servers` callback is passed an object with the 41 | following properties: 42 | 43 | | Property | Description | 44 | | -------- | ----------------------------------------------------------------------------------------------- | 45 | | `http` | The HTTP server that was created, if any. If creating multiple servers, this will be an Array. | 46 | | `https` | The HTTPS server that was created, if any. If creating multiple servers, this will be an Array. | 47 | 48 | ### Certificate Normalization 49 | 50 | `create-servers` provides some conveniences for `https.ca`, `https.key`, and 51 | `https.cert` config properties. You may use PEM data directly (inside a `Buffer` 52 | or string) or a file name. When using a file name, you must also set an 53 | `https.root` config property if using relative paths to cert/key files. 54 | 55 | `https.ca`, `https.cert`, and `https.key` also support specifying an Array. 56 | Given an array for `cert`, you must have a matching array for `key` so each cert 57 | can be matched with its private key. 58 | 59 | ```js 60 | const createServers = require('create-servers'); 61 | 62 | createServers({ 63 | https: { 64 | root: '/cert/path', 65 | cert: ['cert1.crt', 'cert2.crt'], 66 | key: ['cert1.key', 'cert2.key'] 67 | } 68 | }, err => { 69 | // ... 70 | }) 71 | ``` 72 | 73 | If you have a cert that is signed by an intermediate CA, your server will need 74 | to append the untrusted parts of the CA chain with your cert. To make this more 75 | convenient, `create-servers` lets you use an array to automatically create a 76 | chain. 77 | 78 | ```js 79 | const createServers = require('create-servers'); 80 | 81 | createServers({ 82 | https: { 83 | root: '/cert/path', 84 | cert: ['cert.crt', 'intermediate.crt'], 85 | key: 'cert.key' 86 | } 87 | }, err => { 88 | // ... 89 | }) 90 | ``` 91 | 92 | If you are specifying multiple certs _and_ you want to create chains for each, 93 | use an array of arrays. 94 | 95 | ```js 96 | const createServers = require('create-servers'); 97 | 98 | createServers({ 99 | https: { 100 | root: '/cert/path', 101 | cert: [['cert1.crt', 'intermediate.crt'], 'cert2.crt'], 102 | key: ['cert1.key', 'cert2.key'] 103 | } 104 | }, err => { 105 | // ... 106 | }) 107 | ``` 108 | 109 | ### SNI Support 110 | 111 | [Server Name Indication](https://en.wikipedia.org/wiki/Server_Name_Indication), 112 | or SNI, lets HTTPS clients announce which hostname they wish to connect to 113 | before the server sends its certificate, enabling the use of the same server for 114 | multiple hosts. Although `SNICallback` can be used to support this, you lose the 115 | convenient certificate normalization provided by `create-servers`. The `sni` 116 | config option provides an easier way. 117 | 118 | The `sni` option is an object with each key being a supported hostname and each 119 | value being a subset of the HTTPS settings listed above. HTTPS settings defined 120 | at the top level are used as defaults for the hostname-specific settings. 121 | 122 | ```js 123 | const createServers = require('create-servers'); 124 | 125 | createServers( 126 | { 127 | https: { 128 | port: 443, 129 | sni: { 130 | 'example1.com': { 131 | key: '/certs/private/example1.com.key', 132 | cert: '/certs/public/example1.com.crt' 133 | }, 134 | 'example2.com': { 135 | key: '/certs/private/example2.com.key', 136 | cert: '/certs/public/example2.com.crt' 137 | } 138 | } 139 | }, 140 | handler: function (req, res) { 141 | res.end('Hello'); 142 | } 143 | }, 144 | function (errs) { 145 | if (errs) { 146 | return console.log(errs.https); 147 | } 148 | 149 | console.log('Listening on 443'); 150 | } 151 | ); 152 | ``` 153 | 154 | Use `*` in the hostname for wildcard certs. Example: `*.example.com`. The 155 | following settings are supported in the host-specific configuration: 156 | 157 | * key 158 | * cert 159 | * ca 160 | * ciphers 161 | * honorCipherOrder 162 | * Anything else supported by [`tls.createSecureContext`](https://nodejs.org/dist/latest-v8.x/docs/api/tls.html#tls_tls_createsecurecontext_options) 163 | 164 | ## NOTE on Security 165 | Inspired by [`iojs`][iojs] and a well written [article][article], we have defaulted 166 | our [ciphers][ciphers] to support "perfect-forward-security" as well as removing insecure 167 | cipher suites from being a possible choice. With this in mind, 168 | be aware that we will no longer support ie6 on windows XP by default. 169 | 170 | ## Examples 171 | 172 | ### http 173 | 174 | ```js 175 | var createServers = require('create-servers'); 176 | 177 | var servers = createServers( 178 | { 179 | http: 80, 180 | handler: function (req, res) { 181 | res.end('http only'); 182 | } 183 | }, 184 | function (errs) { 185 | if (errs) { 186 | return console.log(errs.http); 187 | } 188 | 189 | console.log('Listening on 80'); 190 | } 191 | ); 192 | ``` 193 | 194 | ### https 195 | 196 | ```js 197 | var servers = createServers( 198 | { 199 | https: { 200 | port: 443, 201 | root: '/path/to/ssl/files', 202 | key: 'your-key.pem', 203 | cert: 'your-cert.pem', 204 | ca: 'your-ca.pem' // Can be an Array of CAs 205 | }, 206 | handler: function (req, res) { 207 | res.end('https only'); 208 | } 209 | }, 210 | function (errs) { 211 | if (errs) { 212 | return console.log(errs.https); 213 | } 214 | 215 | console.log('Listening on 443'); 216 | } 217 | ); 218 | ``` 219 | 220 | ### http && https 221 | 222 | ```js 223 | var servers = createServers( 224 | { 225 | http: 80, 226 | https: { 227 | port: 443, 228 | root: '/path/to/ssl/files', 229 | key: 'your-key.pem', 230 | cert: 'your-cert.pem', 231 | ca: 'your-ca.pem' // Can be an Array of CAs 232 | }, 233 | handler: function (req, res) { 234 | res.end('http AND https'); 235 | } 236 | }, 237 | function (errs, servers) { 238 | if (errs) { 239 | return Object.keys(errs).forEach(function (key) { 240 | console.log('Error ' + key + ': ' + errs[key]); 241 | if (servers[key]) { 242 | servers[key].close(); 243 | } 244 | }); 245 | } 246 | 247 | console.log('Listening on 80 and 443'); 248 | } 249 | ); 250 | ``` 251 | 252 | ### http && https (different handlers) 253 | 254 | ```js 255 | var servers = createServers( 256 | { 257 | http: { 258 | port: 80, 259 | handler: function (req, res) { 260 | res.end('http'); 261 | } 262 | }, 263 | https: { 264 | port: 443, 265 | root: '/path/to/ssl/files', 266 | key: 'your-key.pem', 267 | cert: 'your-cert.pem', 268 | ca: 'your-ca.pem', // Can be an Array of CAs 269 | handler: function (req, res) { 270 | res.end('https'); 271 | } 272 | } 273 | }, 274 | function (errs, servers) { 275 | if (errs) { 276 | return Object.keys(errs).forEach(function (key) { 277 | console.log('Error ' + key + ': ' + errs[key]); 278 | if (servers[key]) { 279 | servers[key].close(); 280 | } 281 | }); 282 | } 283 | 284 | console.log('Listening on 80 and 443'); 285 | } 286 | ); 287 | ``` 288 | 289 | ## Author: [Charlie Robbins](https://github.com/indexzero) 290 | ## License: MIT 291 | 292 | [article]: https://certsimple.com/blog/a-plus-node-js-ssl 293 | [iojs]: https://github.com/iojs/io.js 294 | [ciphers]: https://iojs.org/api/tls.html#tls_tls_createserver_options_secureconnectionlistener 295 | [ALPN negotiation]: https://nodejs.org/api/http2.html#http2_alpn_negotiation 296 | -------------------------------------------------------------------------------- /test/create-servers-test.js: -------------------------------------------------------------------------------- 1 | /* 2 | * create-servers-test.js: Make sure creating both works 3 | * 4 | * (C) 2013, Charlie Robbins. 5 | * 6 | */ 7 | 8 | var path = require('path'), 9 | fs = require('fs'), 10 | url = require('url'), 11 | http = require('http'), 12 | https = require('https'), 13 | http2 = require('http2'), 14 | { promisify } = require('util'), 15 | test = require('tape'), 16 | sinon = require('sinon'), 17 | evilDNS = require('evil-dns'), 18 | createServers = require('../'); 19 | 20 | const createServersAsync = promisify(createServers); 21 | 22 | const { HTTP2_HEADER_PATH } = http2.constants; 23 | 24 | const ca = fs.readFileSync(path.join(__dirname, './fixtures/example-ca-cert.pem')); 25 | 26 | // 27 | // Immediately end a response. 28 | // 29 | function fend(req, res) { 30 | res.end(); 31 | } 32 | 33 | // 34 | // Request and download response from a URL 35 | // 36 | async function download(httpsURL) { 37 | return new Promise((resolve, reject) => { 38 | const req = https.get({ 39 | ...url.parse(httpsURL), 40 | ca 41 | }, res => { 42 | const chunks = []; 43 | res 44 | .on('data', chunk => chunks.push(chunk)) 45 | .once('end', () => { 46 | resolve(chunks.map(chunk => chunk.toString('utf8')).join('')); 47 | }) 48 | .once('aborted', reject) 49 | .once('close', reject) 50 | .once('error', reject); 51 | }); 52 | req.once('error', reject); 53 | }); 54 | } 55 | 56 | // 57 | // Request and download response from a URL using HTTP/2 58 | // 59 | async function download2(httpsURL) { 60 | return new Promise((resolve, reject) => { 61 | const clientSession = http2.connect(httpsURL); 62 | const fail = results => { 63 | clientSession.close(); 64 | reject(results); 65 | }; 66 | 67 | const req = clientSession.request({ [HTTP2_HEADER_PATH]: '/' }); 68 | req.on('response', () => { 69 | const chunks = []; 70 | req 71 | .on('data', chunk => chunks.push(chunk)) 72 | .once('end', () => { 73 | resolve(chunks.map(chunk => chunk.toString('utf8')).join('')); 74 | clientSession.close(); 75 | }); 76 | }) 77 | .once('aborted', fail) 78 | .once('close', fail) 79 | .once('error', fail); 80 | }); 81 | } 82 | 83 | /** 84 | * Helper to start a server with HTTP/2 support. 85 | * Returns stop function to handle server and dns cleanup after tests. 86 | * 87 | * Tests requests can be made to foo.example.org:3456 88 | * 89 | * @param {object} options - Additional http2 server options. 90 | * @returns {Promise<(function(): void)|*>} callback 91 | */ 92 | async function startHttp2Server(options = {}) { 93 | // disabling to avoid UNABLE_TO_VERIFY_LEAF_SIGNATURE for tests 94 | process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'; 95 | 96 | const servers = await createServersAsync({ 97 | http2: { 98 | port: 3456, 99 | root: path.join(__dirname, 'fixtures'), 100 | key: 'example-org-key.pem', 101 | cert: 'example-org-cert.pem', 102 | ...options 103 | }, 104 | handler: (req, res) => { 105 | const { httpVersion } = req; 106 | const { socket: { alpnProtocol } } = httpVersion === '2.0' ? req.stream.session : req; 107 | res.writeHead(200, { 'content-type': 'application/json' }); 108 | res.end(JSON.stringify({ 109 | alpnProtocol, 110 | httpVersion 111 | })); 112 | } 113 | }); 114 | 115 | evilDNS.add('foo.example.org', '0.0.0.0'); 116 | 117 | return function stop() { 118 | servers && servers.http2 && servers.http2.close(); 119 | evilDNS.clear(); 120 | delete process.env.NODE_TLS_REJECT_UNAUTHORIZED; 121 | }; 122 | } 123 | 124 | test('only http', function (t) { 125 | t.plan(5); 126 | createServers({ 127 | log: console.log, 128 | http: 9876, 129 | handler: fend 130 | }, function (err, servers) { 131 | console.dir(err); 132 | t.error(err); 133 | t.equals(typeof servers, 'object'); 134 | t.equals(typeof servers.http, 'object'); 135 | t.equals(servers.http instanceof Array, false); 136 | t.equals(servers.https, undefined); 137 | servers.http.close(); 138 | }); 139 | }); 140 | 141 | test('only http, port 0', function (t) { 142 | t.plan(4); 143 | createServers({ 144 | log: console.log, 145 | http: 0, 146 | handler: fend 147 | }, function (err, servers) { 148 | console.dir(err); 149 | t.error(err); 150 | t.equals(typeof servers, 'object'); 151 | t.equals(typeof servers.http, 'object'); 152 | t.equals(typeof servers.http.address().port, 'number'); 153 | servers.http.close(); 154 | }); 155 | }); 156 | 157 | test('only http, timeout', function (t) { 158 | t.plan(5); 159 | var time = 3000000; 160 | createServers({ 161 | log: console.log, 162 | timeout: time, 163 | http: 0, 164 | handler: fend 165 | }, function (err, servers) { 166 | console.dir(err); 167 | t.error(err); 168 | t.equals(typeof servers, 'object'); 169 | t.equals(typeof servers.http, 'object'); 170 | t.equals(typeof servers.http.address().port, 'number'); 171 | t.equals(servers.http.timeout, time); 172 | servers.http.close(); 173 | }); 174 | }); 175 | 176 | test('keepAliveTimeout', function (t) { 177 | t.plan(1); 178 | const time = 3000000; 179 | createServers({ 180 | log: console.log, 181 | keepAliveTimeout: time, 182 | http: 0, 183 | handler: fend 184 | }, function (err, servers) { 185 | t.equals(servers.http.keepAliveTimeout, time); 186 | servers.http.close(); 187 | }); 188 | }); 189 | 190 | test('only https', function (t) { 191 | t.plan(5); 192 | createServers({ 193 | log: console.log, 194 | https: { 195 | port: 3456, 196 | root: path.join(__dirname, 'fixtures'), 197 | key: 'example-org-key.pem', 198 | cert: 'example-org-cert.pem' 199 | }, 200 | handler: fend 201 | }, function (err, servers) { 202 | t.error(err); 203 | t.equals(typeof servers, 'object'); 204 | t.equals(typeof servers.https, 'object'); 205 | t.equals(servers.https instanceof Array, false); 206 | t.equals(servers.http, undefined); 207 | servers.https.close(); 208 | }); 209 | }); 210 | 211 | test('only https with timeout', function (t) { 212 | t.plan(4); 213 | var time = 4000000; 214 | createServers({ 215 | log: console.log, 216 | https: { 217 | timeout: time, 218 | port: 3456, 219 | root: path.join(__dirname, 'fixtures'), 220 | key: 'example-org-key.pem', 221 | cert: 'example-org-cert.pem' 222 | }, 223 | handler: fend 224 | }, function (err, servers) { 225 | t.error(err); 226 | t.equals(typeof servers, 'object'); 227 | t.equals(typeof servers.https, 'object'); 228 | t.equals(servers.https.timeout, time); 229 | servers.https.close(); 230 | }); 231 | }); 232 | 233 | test('catches cert file reading errors', function (t) { 234 | t.plan(2); 235 | createServers({ 236 | log: console.log, 237 | https: { 238 | port: 3456, 239 | root: path.join(__dirname, 'fixtures'), 240 | key: 'missing-example-org-key.pem', 241 | cert: 'example-org-cert.pem' 242 | }, 243 | handler: fend 244 | }, function (err) { 245 | t.doesNotEqual(err, null); 246 | t.doesNotEqual(err, undefined); 247 | }); 248 | }); 249 | 250 | test('only http2', function (t) { 251 | t.plan(4); 252 | var time = 4000000; 253 | createServers({ 254 | log: console.log, 255 | http2: { 256 | timeout: time, 257 | port: 3456, 258 | root: path.join(__dirname, 'fixtures'), 259 | key: 'example-org-key.pem', 260 | cert: 'example-org-cert.pem' 261 | }, 262 | handler: fend 263 | }, function (err, servers) { 264 | t.error(err); 265 | t.equals(typeof servers, 'object'); 266 | t.equals(typeof servers.http2, 'object'); 267 | t.equals(servers.http2.timeout, time); 268 | servers.http2.close(); 269 | }); 270 | }); 271 | 272 | test('absolute cert path resolution', function (t) { 273 | t.plan(3); 274 | createServers({ 275 | log: console.log, 276 | https: { 277 | port: 3456, 278 | root: '/', 279 | cert: path.resolve(__dirname, 'fixtures', 'example-org-cert.pem'), 280 | key: path.resolve(__dirname, 'fixtures', 'example-org-key.pem') 281 | }, 282 | handler: fend 283 | }, function (err, servers) { 284 | t.error(err); 285 | t.equals(typeof servers, 'object'); 286 | t.equals(typeof servers.https, 'object'); 287 | servers.https.close(); 288 | }); 289 | }); 290 | 291 | test('http && https', function (t) { 292 | t.plan(4); 293 | createServers({ 294 | log: console.log, 295 | http: 8765, 296 | https: { 297 | port: 3456, 298 | root: path.join(__dirname, 'fixtures'), 299 | key: 'example-org-key.pem', 300 | cert: 'example-org-cert.pem' 301 | }, 302 | handler: fend 303 | }, function (err, servers) { 304 | t.error(err); 305 | t.equals(typeof servers, 'object'); 306 | t.equals(typeof servers.http, 'object'); 307 | t.equals(typeof servers.https, 'object'); 308 | servers.http.close(); 309 | servers.https.close(); 310 | }); 311 | }); 312 | 313 | test('provides useful debug information', async function (t) { 314 | t.plan(4); 315 | 316 | const config = { 317 | log: console.log, 318 | https: { 319 | port: 3456, 320 | root: path.join(__dirname, 'fixtures'), 321 | key: 'example-org-key.pem', 322 | cert: 'example-org-cert.pem' 323 | }, 324 | handler: fend 325 | }; 326 | 327 | // Simulate a "port in use" error 328 | const { https: server1 } = await createServersAsync(config); 329 | 330 | try { 331 | await createServersAsync(config); 332 | } catch (err) { 333 | t.equals(typeof err, 'object'); 334 | t.equals(typeof err.https, 'object'); 335 | t.equals(typeof err.message, 'string'); 336 | t.notEqual(err.message, 'Unspecified error'); 337 | } finally { 338 | server1.close(); 339 | } 340 | }); 341 | 342 | test('http && https with different handlers', function (t) { 343 | t.plan(4); 344 | createServers({ 345 | log: console.log, 346 | http: { 347 | handler: function (req, res) { 348 | res.end('http'); 349 | }, 350 | port: 8765 351 | }, 352 | https: { 353 | handler: function (req, res) { 354 | res.end('https'); 355 | }, 356 | port: 3456, 357 | root: path.join(__dirname, 'fixtures'), 358 | key: 'example-org-key.pem', 359 | cert: 'example-org-cert.pem' 360 | } 361 | }, function (err, servers) { 362 | t.error(err); 363 | t.equals(typeof servers, 'object'); 364 | t.equals(typeof servers.http, 'object'); 365 | t.equals(typeof servers.https, 'object'); 366 | servers.http.close(); 367 | servers.https.close(); 368 | }); 369 | 370 | test('only http with string type input for http port', function (t) { 371 | t.plan(3); 372 | createServers({ 373 | log: console.log, 374 | http: '9876', 375 | handler: fend 376 | }, function (err, servers) { 377 | t.error(err); 378 | t.equals(typeof servers, 'object'); 379 | t.equals(typeof servers.http, 'object'); 380 | servers.http.close(); 381 | }); 382 | }); 383 | 384 | test('host can be provided to the server', function (t) { 385 | t.plan(4); 386 | createServers({ 387 | log: console.log, 388 | http: { 389 | port: 9877, 390 | host: '127.0.0.1' 391 | }, 392 | handler: fend 393 | }, function (err, servers) { 394 | t.error(err); 395 | t.equals(typeof servers, 'object'); 396 | t.equals(typeof servers.http, 'object'); 397 | t.equals(servers.http.address().address, '127.0.0.1'); 398 | 399 | servers.http.close(); 400 | }); 401 | }); 402 | }); 403 | 404 | test('supports cert contents instead of cert paths', function (t) { 405 | t.plan(3); 406 | var root = path.join(__dirname, 'fixtures'); 407 | createServers({ 408 | log: console.log, 409 | https: { 410 | port: 3456, 411 | root: root, 412 | cert: fs.readFileSync(path.resolve(root, 'example-org-cert.pem')), 413 | key: fs.readFileSync(path.resolve(root, 'example-org-key.pem')) 414 | }, 415 | handler: fend 416 | }, function (err, servers) { 417 | t.error(err); 418 | t.equals(typeof servers, 'object'); 419 | t.equals(typeof servers.https, 'object'); 420 | servers.https.close(); 421 | }); 422 | }); 423 | 424 | test('supports cert array instead of strings', function (t) { 425 | t.plan(3); 426 | var root = path.join(__dirname, 'fixtures'); 427 | createServers({ 428 | log: console.log, 429 | https: { 430 | port: 3456, 431 | root: root, 432 | key: 'example-org-key.pem', 433 | cert: 'example-org-cert.pem' 434 | }, 435 | handler: fend 436 | }, function (err, servers) { 437 | t.error(err); 438 | t.equals(typeof servers, 'object'); 439 | t.equals(typeof servers.https, 'object'); 440 | servers.https.close(); 441 | }); 442 | }); 443 | 444 | test('supports creating certificate chains', function (t) { 445 | t.plan(2); 446 | var root = path.join(__dirname, 'fixtures'); 447 | var agent3Cert = fs.readFileSync(path.resolve(root, 'agent3-cert.pem')); 448 | var intermediate = fs.readFileSync( 449 | path.resolve(root, 'intermediate-cert.pem') 450 | ); 451 | var spy = sinon.spy(https, 'createServer'); 452 | createServers({ 453 | log: console.log, 454 | https: { 455 | port: 3456, 456 | root: root, 457 | cert: ['agent3-cert.pem', 'intermediate-cert.pem'], 458 | key: 'agent3-key.pem' 459 | }, 460 | handler: fend 461 | }, function (err, servers) { 462 | t.error(err); 463 | 464 | const expectedBundle = [agent3Cert, intermediate].join('\n'); 465 | const cert = spy.lastCall.args[0].cert; 466 | t.equals(cert, expectedBundle, 'should create a cert chain'); 467 | 468 | servers.https.close(); 469 | spy.restore(); 470 | }); 471 | }); 472 | 473 | test('supports requestCert https option', function (t) { 474 | t.plan(2); 475 | var spy = sinon.spy(https, 'createServer'); 476 | createServers({ 477 | log: console.log, 478 | https: { 479 | port: 3456, 480 | root: path.join(__dirname, 'fixtures'), 481 | key: 'example-org-key.pem', 482 | cert: 'example-org-cert.pem', 483 | requestCert: true 484 | }, 485 | handler: fend 486 | }, function (err, servers) { 487 | t.error(err); 488 | t.equals( 489 | spy.lastCall.args[0].requestCert, 490 | true, 491 | 'should preserve the requestCert option' 492 | ); 493 | servers.https.close(); 494 | spy.restore(); 495 | }); 496 | }); 497 | 498 | test('supports SNI', async t => { 499 | await testSni(t, { 500 | 'example.com': { 501 | key: 'example-com-key.pem', 502 | cert: 'example-com-cert.pem' 503 | }, 504 | 'example.net': { 505 | key: 'example-net-key.pem', 506 | cert: 'example-net-cert.pem' 507 | }, 508 | '*.example.org': { 509 | key: 'example-org-key.pem', 510 | cert: 'example-org-cert.pem' 511 | } 512 | }, ['example.com', 'example.net', 'foo.example.org']); 513 | }); 514 | 515 | test('supports catch-all * for SNI', async t => { 516 | await testSni(t, { 517 | 'example.com': { 518 | key: 'example-com-key.pem', 519 | cert: 'example-com-cert.pem' 520 | }, 521 | '*': { 522 | key: 'example-org-key.pem', 523 | cert: 'example-org-cert.pem' 524 | } 525 | }, ['example.com', 'foo.example.org']); 526 | }); 527 | 528 | test('multiple https servers', async function (t) { 529 | t.plan(2); 530 | 531 | evilDNS.add('foo.example.org', '0.0.0.0'); 532 | const servers = await createServersAsync({ 533 | log: console.log, 534 | https: [ 535 | { 536 | port: 3456, 537 | root: path.join(__dirname, 'fixtures'), 538 | key: 'example-org-key.pem', 539 | cert: 'example-org-cert.pem' 540 | }, 541 | { 542 | port: 6543, 543 | root: path.join(__dirname, 'fixtures'), 544 | key: 'example-org-key.pem', 545 | cert: 'example-org-cert.pem' 546 | } 547 | ], 548 | handler: (req, res) => { 549 | res.end('Hello'); 550 | } 551 | }); 552 | 553 | try { 554 | t.equals(servers.https.length, 2, 'two servers were created'); 555 | const responses = await Promise.all([ 556 | download('https://foo.example.org:3456/'), 557 | download('https://foo.example.org:6543/') 558 | ]); 559 | t.equals( 560 | responses.every(str => str === 'Hello'), 561 | true, 562 | 'responses are as expected' 563 | ); 564 | } finally { 565 | let toClose = 566 | servers.https instanceof Array ? servers.https : [servers.https]; 567 | toClose.forEach(server => server.close()); 568 | evilDNS.clear(); 569 | } 570 | }); 571 | 572 | test('supports http2-only requests', async function (t) { 573 | t.plan(2); 574 | const url = 'https://foo.example.org:3456/'; 575 | 576 | let stopServer; 577 | try { 578 | stopServer = await startHttp2Server({}); 579 | 580 | const httpsResponse = await download(url); 581 | t.ok(httpsResponse.includes('Unknown ALPN Protocol')); 582 | 583 | const response = JSON.parse(await download2(url)); 584 | t.equals(response.httpVersion, '2.0'); 585 | 586 | } catch (err) { 587 | t.error(err); 588 | } finally { 589 | stopServer && stopServer(); 590 | } 591 | }); 592 | 593 | test('supports http2 and https requests', async function (t) { 594 | t.plan(2); 595 | const url = 'https://foo.example.org:3456/'; 596 | 597 | let stopServer; 598 | try { 599 | stopServer = await startHttp2Server({ 600 | allowHTTP1: true 601 | }); 602 | 603 | const httpsResponse = JSON.parse(await download(url)); 604 | t.equals(httpsResponse.httpVersion, '1.1'); 605 | 606 | const response = JSON.parse(await download2(url)); 607 | t.equals(response.httpVersion, '2.0'); 608 | 609 | } catch (err) { 610 | t.error(err); 611 | } finally { 612 | stopServer && stopServer(); 613 | } 614 | }); 615 | 616 | async function testSni(t, sniConfig, hostNames) { 617 | t.plan(1); 618 | 619 | let httpsServer; 620 | try { 621 | const servers = await createServersAsync({ 622 | https: { 623 | port: 3456, 624 | root: path.join(__dirname, 'fixtures'), 625 | sni: sniConfig 626 | }, 627 | handler: (req, res) => { 628 | res.write('Hello'); 629 | res.end(); 630 | } 631 | }); 632 | httpsServer = servers.https; 633 | 634 | hostNames.forEach(host => evilDNS.add(host, '0.0.0.0')); 635 | 636 | const responses = await Promise.all( 637 | hostNames.map(hostname => download(`https://${hostname}:3456/`)) 638 | ); 639 | 640 | t.equals( 641 | responses.every(str => str === 'Hello'), 642 | true, 643 | 'responses are as expected' 644 | ); 645 | } catch (err) { 646 | return void t.error(err); 647 | } finally { 648 | httpsServer && httpsServer.close(); 649 | evilDNS.clear(); 650 | } 651 | } 652 | --------------------------------------------------------------------------------