forked from coolaj86/goldilocks.js
		
	rebrand to goldilocks
This commit is contained in:
		
						commit
						8df415c91d
					
				
							
								
								
									
										5
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										5
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@ -30,6 +30,7 @@ build/Release
 | 
			
		||||
# Dependency directories
 | 
			
		||||
node_modules
 | 
			
		||||
jspm_packages
 | 
			
		||||
bower_components
 | 
			
		||||
 | 
			
		||||
# Optional npm cache directory
 | 
			
		||||
.npm
 | 
			
		||||
@ -42,3 +43,7 @@ jspm_packages
 | 
			
		||||
 | 
			
		||||
# Output of 'npm pack'
 | 
			
		||||
*.tgz
 | 
			
		||||
 | 
			
		||||
# Dependency directory
 | 
			
		||||
# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git
 | 
			
		||||
node_modules
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										202
									
								
								LICENSE
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										202
									
								
								LICENSE
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,202 @@
 | 
			
		||||
                                 Apache License
 | 
			
		||||
                           Version 2.0, January 2004
 | 
			
		||||
                        http://www.apache.org/licenses/
 | 
			
		||||
 | 
			
		||||
   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
 | 
			
		||||
 | 
			
		||||
   1. Definitions.
 | 
			
		||||
 | 
			
		||||
      "License" shall mean the terms and conditions for use, reproduction,
 | 
			
		||||
      and distribution as defined by Sections 1 through 9 of this document.
 | 
			
		||||
 | 
			
		||||
      "Licensor" shall mean the copyright owner or entity authorized by
 | 
			
		||||
      the copyright owner that is granting the License.
 | 
			
		||||
 | 
			
		||||
      "Legal Entity" shall mean the union of the acting entity and all
 | 
			
		||||
      other entities that control, are controlled by, or are under common
 | 
			
		||||
      control with that entity. For the purposes of this definition,
 | 
			
		||||
      "control" means (i) the power, direct or indirect, to cause the
 | 
			
		||||
      direction or management of such entity, whether by contract or
 | 
			
		||||
      otherwise, or (ii) ownership of fifty percent (50%) or more of the
 | 
			
		||||
      outstanding shares, or (iii) beneficial ownership of such entity.
 | 
			
		||||
 | 
			
		||||
      "You" (or "Your") shall mean an individual or Legal Entity
 | 
			
		||||
      exercising permissions granted by this License.
 | 
			
		||||
 | 
			
		||||
      "Source" form shall mean the preferred form for making modifications,
 | 
			
		||||
      including but not limited to software source code, documentation
 | 
			
		||||
      source, and configuration files.
 | 
			
		||||
 | 
			
		||||
      "Object" form shall mean any form resulting from mechanical
 | 
			
		||||
      transformation or translation of a Source form, including but
 | 
			
		||||
      not limited to compiled object code, generated documentation,
 | 
			
		||||
      and conversions to other media types.
 | 
			
		||||
 | 
			
		||||
      "Work" shall mean the work of authorship, whether in Source or
 | 
			
		||||
      Object form, made available under the License, as indicated by a
 | 
			
		||||
      copyright notice that is included in or attached to the work
 | 
			
		||||
      (an example is provided in the Appendix below).
 | 
			
		||||
 | 
			
		||||
      "Derivative Works" shall mean any work, whether in Source or Object
 | 
			
		||||
      form, that is based on (or derived from) the Work and for which the
 | 
			
		||||
      editorial revisions, annotations, elaborations, or other modifications
 | 
			
		||||
      represent, as a whole, an original work of authorship. For the purposes
 | 
			
		||||
      of this License, Derivative Works shall not include works that remain
 | 
			
		||||
      separable from, or merely link (or bind by name) to the interfaces of,
 | 
			
		||||
      the Work and Derivative Works thereof.
 | 
			
		||||
 | 
			
		||||
      "Contribution" shall mean any work of authorship, including
 | 
			
		||||
      the original version of the Work and any modifications or additions
 | 
			
		||||
      to that Work or Derivative Works thereof, that is intentionally
 | 
			
		||||
      submitted to Licensor for inclusion in the Work by the copyright owner
 | 
			
		||||
      or by an individual or Legal Entity authorized to submit on behalf of
 | 
			
		||||
      the copyright owner. For the purposes of this definition, "submitted"
 | 
			
		||||
      means any form of electronic, verbal, or written communication sent
 | 
			
		||||
      to the Licensor or its representatives, including but not limited to
 | 
			
		||||
      communication on electronic mailing lists, source code control systems,
 | 
			
		||||
      and issue tracking systems that are managed by, or on behalf of, the
 | 
			
		||||
      Licensor for the purpose of discussing and improving the Work, but
 | 
			
		||||
      excluding communication that is conspicuously marked or otherwise
 | 
			
		||||
      designated in writing by the copyright owner as "Not a Contribution."
 | 
			
		||||
 | 
			
		||||
      "Contributor" shall mean Licensor and any individual or Legal Entity
 | 
			
		||||
      on behalf of whom a Contribution has been received by Licensor and
 | 
			
		||||
      subsequently incorporated within the Work.
 | 
			
		||||
 | 
			
		||||
   2. Grant of Copyright License. Subject to the terms and conditions of
 | 
			
		||||
      this License, each Contributor hereby grants to You a perpetual,
 | 
			
		||||
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
 | 
			
		||||
      copyright license to reproduce, prepare Derivative Works of,
 | 
			
		||||
      publicly display, publicly perform, sublicense, and distribute the
 | 
			
		||||
      Work and such Derivative Works in Source or Object form.
 | 
			
		||||
 | 
			
		||||
   3. Grant of Patent License. Subject to the terms and conditions of
 | 
			
		||||
      this License, each Contributor hereby grants to You a perpetual,
 | 
			
		||||
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
 | 
			
		||||
      (except as stated in this section) patent license to make, have made,
 | 
			
		||||
      use, offer to sell, sell, import, and otherwise transfer the Work,
 | 
			
		||||
      where such license applies only to those patent claims licensable
 | 
			
		||||
      by such Contributor that are necessarily infringed by their
 | 
			
		||||
      Contribution(s) alone or by combination of their Contribution(s)
 | 
			
		||||
      with the Work to which such Contribution(s) was submitted. If You
 | 
			
		||||
      institute patent litigation against any entity (including a
 | 
			
		||||
      cross-claim or counterclaim in a lawsuit) alleging that the Work
 | 
			
		||||
      or a Contribution incorporated within the Work constitutes direct
 | 
			
		||||
      or contributory patent infringement, then any patent licenses
 | 
			
		||||
      granted to You under this License for that Work shall terminate
 | 
			
		||||
      as of the date such litigation is filed.
 | 
			
		||||
 | 
			
		||||
   4. Redistribution. You may reproduce and distribute copies of the
 | 
			
		||||
      Work or Derivative Works thereof in any medium, with or without
 | 
			
		||||
      modifications, and in Source or Object form, provided that You
 | 
			
		||||
      meet the following conditions:
 | 
			
		||||
 | 
			
		||||
      (a) You must give any other recipients of the Work or
 | 
			
		||||
          Derivative Works a copy of this License; and
 | 
			
		||||
 | 
			
		||||
      (b) You must cause any modified files to carry prominent notices
 | 
			
		||||
          stating that You changed the files; and
 | 
			
		||||
 | 
			
		||||
      (c) You must retain, in the Source form of any Derivative Works
 | 
			
		||||
          that You distribute, all copyright, patent, trademark, and
 | 
			
		||||
          attribution notices from the Source form of the Work,
 | 
			
		||||
          excluding those notices that do not pertain to any part of
 | 
			
		||||
          the Derivative Works; and
 | 
			
		||||
 | 
			
		||||
      (d) If the Work includes a "NOTICE" text file as part of its
 | 
			
		||||
          distribution, then any Derivative Works that You distribute must
 | 
			
		||||
          include a readable copy of the attribution notices contained
 | 
			
		||||
          within such NOTICE file, excluding those notices that do not
 | 
			
		||||
          pertain to any part of the Derivative Works, in at least one
 | 
			
		||||
          of the following places: within a NOTICE text file distributed
 | 
			
		||||
          as part of the Derivative Works; within the Source form or
 | 
			
		||||
          documentation, if provided along with the Derivative Works; or,
 | 
			
		||||
          within a display generated by the Derivative Works, if and
 | 
			
		||||
          wherever such third-party notices normally appear. The contents
 | 
			
		||||
          of the NOTICE file are for informational purposes only and
 | 
			
		||||
          do not modify the License. You may add Your own attribution
 | 
			
		||||
          notices within Derivative Works that You distribute, alongside
 | 
			
		||||
          or as an addendum to the NOTICE text from the Work, provided
 | 
			
		||||
          that such additional attribution notices cannot be construed
 | 
			
		||||
          as modifying the License.
 | 
			
		||||
 | 
			
		||||
      You may add Your own copyright statement to Your modifications and
 | 
			
		||||
      may provide additional or different license terms and conditions
 | 
			
		||||
      for use, reproduction, or distribution of Your modifications, or
 | 
			
		||||
      for any such Derivative Works as a whole, provided Your use,
 | 
			
		||||
      reproduction, and distribution of the Work otherwise complies with
 | 
			
		||||
      the conditions stated in this License.
 | 
			
		||||
 | 
			
		||||
   5. Submission of Contributions. Unless You explicitly state otherwise,
 | 
			
		||||
      any Contribution intentionally submitted for inclusion in the Work
 | 
			
		||||
      by You to the Licensor shall be under the terms and conditions of
 | 
			
		||||
      this License, without any additional terms or conditions.
 | 
			
		||||
      Notwithstanding the above, nothing herein shall supersede or modify
 | 
			
		||||
      the terms of any separate license agreement you may have executed
 | 
			
		||||
      with Licensor regarding such Contributions.
 | 
			
		||||
 | 
			
		||||
   6. Trademarks. This License does not grant permission to use the trade
 | 
			
		||||
      names, trademarks, service marks, or product names of the Licensor,
 | 
			
		||||
      except as required for reasonable and customary use in describing the
 | 
			
		||||
      origin of the Work and reproducing the content of the NOTICE file.
 | 
			
		||||
 | 
			
		||||
   7. Disclaimer of Warranty. Unless required by applicable law or
 | 
			
		||||
      agreed to in writing, Licensor provides the Work (and each
 | 
			
		||||
      Contributor provides its Contributions) on an "AS IS" BASIS,
 | 
			
		||||
      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
 | 
			
		||||
      implied, including, without limitation, any warranties or conditions
 | 
			
		||||
      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
 | 
			
		||||
      PARTICULAR PURPOSE. You are solely responsible for determining the
 | 
			
		||||
      appropriateness of using or redistributing the Work and assume any
 | 
			
		||||
      risks associated with Your exercise of permissions under this License.
 | 
			
		||||
 | 
			
		||||
   8. Limitation of Liability. In no event and under no legal theory,
 | 
			
		||||
      whether in tort (including negligence), contract, or otherwise,
 | 
			
		||||
      unless required by applicable law (such as deliberate and grossly
 | 
			
		||||
      negligent acts) or agreed to in writing, shall any Contributor be
 | 
			
		||||
      liable to You for damages, including any direct, indirect, special,
 | 
			
		||||
      incidental, or consequential damages of any character arising as a
 | 
			
		||||
      result of this License or out of the use or inability to use the
 | 
			
		||||
      Work (including but not limited to damages for loss of goodwill,
 | 
			
		||||
      work stoppage, computer failure or malfunction, or any and all
 | 
			
		||||
      other commercial damages or losses), even if such Contributor
 | 
			
		||||
      has been advised of the possibility of such damages.
 | 
			
		||||
 | 
			
		||||
   9. Accepting Warranty or Additional Liability. While redistributing
 | 
			
		||||
      the Work or Derivative Works thereof, You may choose to offer,
 | 
			
		||||
      and charge a fee for, acceptance of support, warranty, indemnity,
 | 
			
		||||
      or other liability obligations and/or rights consistent with this
 | 
			
		||||
      License. However, in accepting such obligations, You may act only
 | 
			
		||||
      on Your own behalf and on Your sole responsibility, not on behalf
 | 
			
		||||
      of any other Contributor, and only if You agree to indemnify,
 | 
			
		||||
      defend, and hold each Contributor harmless for any liability
 | 
			
		||||
      incurred by, or claims asserted against, such Contributor by reason
 | 
			
		||||
      of your accepting any such warranty or additional liability.
 | 
			
		||||
 | 
			
		||||
   END OF TERMS AND CONDITIONS
 | 
			
		||||
 | 
			
		||||
   APPENDIX: How to apply the Apache License to your work.
 | 
			
		||||
 | 
			
		||||
      To apply the Apache License to your work, attach the following
 | 
			
		||||
      boilerplate notice, with the fields enclosed by brackets "{}"
 | 
			
		||||
      replaced with your own identifying information. (Don't include
 | 
			
		||||
      the brackets!)  The text should be enclosed in the appropriate
 | 
			
		||||
      comment syntax for the file format. We also recommend that a
 | 
			
		||||
      file or class name and description of purpose be included on the
 | 
			
		||||
      same "printed page" as the copyright notice for easier
 | 
			
		||||
      identification within third-party archives.
 | 
			
		||||
 | 
			
		||||
   Copyright {yyyy} {name of copyright owner}
 | 
			
		||||
 | 
			
		||||
   Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
   you may not use this file except in compliance with the License.
 | 
			
		||||
   You may obtain a copy of the License at
 | 
			
		||||
 | 
			
		||||
       http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 | 
			
		||||
   Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
   distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
   See the License for the specific language governing permissions and
 | 
			
		||||
   limitations under the License.
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										159
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										159
									
								
								README.md
									
									
									
									
									
								
							@ -1,4 +1,161 @@
 | 
			
		||||
<!-- BANNER_TPL_BEGIN -->
 | 
			
		||||
 | 
			
		||||
About Daplie: We're taking back the Internet!
 | 
			
		||||
--------------
 | 
			
		||||
 | 
			
		||||
Down with Google, Apple, and Facebook!
 | 
			
		||||
 | 
			
		||||
We're re-decentralizing the web and making it read-write again - one home cloud system at a time.
 | 
			
		||||
 | 
			
		||||
Tired of serving the Empire? Come join the Rebel Alliance:
 | 
			
		||||
 | 
			
		||||
<a href="mailto:jobs@daplie.com">jobs@daplie.com</a> | [Invest in Daplie on Wefunder](https://daplie.com/invest/) | [Pre-order Cloud](https://daplie.com/preorder/), The World's First Home Server for Everyone
 | 
			
		||||
 | 
			
		||||
<!-- BANNER_TPL_END -->
 | 
			
		||||
 | 
			
		||||
Goldilocks
 | 
			
		||||
==========
 | 
			
		||||
 | 
			
		||||
The webserver that's just right.
 | 
			
		||||
The node.js webserver that's just right.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
A simple HTTPS static file server with valid TLS (SSL) certs.
 | 
			
		||||
 | 
			
		||||
Comes bundled a valid certificate for localhost.daplie.me,
 | 
			
		||||
which is great for testing and development, and you can specify your own.
 | 
			
		||||
 | 
			
		||||
Also great for testing ACME certs from letsencrypt.org.
 | 
			
		||||
 | 
			
		||||
Install
 | 
			
		||||
-------
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
# v2 in npm
 | 
			
		||||
npm install -g goldilocks
 | 
			
		||||
 | 
			
		||||
# master in git (via ssh)
 | 
			
		||||
npm install -g git+ssh://git@git.daplie.com:Daplie/goldilocks.js
 | 
			
		||||
 | 
			
		||||
# master in git (unauthenticated)
 | 
			
		||||
npm install -g git+https://git@git.daplie.com:Daplie/goldilocks.js
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
goldilocks
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
Serving /Users/foo/ at https://localhost.daplie.me:8443
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Usage
 | 
			
		||||
-----
 | 
			
		||||
 | 
			
		||||
Examples:
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
# Install
 | 
			
		||||
npm install -g git+https://git@git.daplie.com:Daplie/goldilocks.js
 | 
			
		||||
 | 
			
		||||
# Use tunnel
 | 
			
		||||
goldilocks --sites jane.daplie.me --agree-tos --email jane@example.com --tunnel
 | 
			
		||||
 | 
			
		||||
# BEFORE you access in a browser for the first time, use curl
 | 
			
		||||
# (because there's a concurrency bug in the greenlock setup)
 | 
			
		||||
curl https://jane.daplie.me
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Options:
 | 
			
		||||
 | 
			
		||||
* `-p <port>` - i.e. `sudo goldilocks -p 443` (defaults to 80+443 or 8443)
 | 
			
		||||
* `-d <dirpath>` - i.e. `goldilocks -d /tmp/` (defaults to `pwd`)
 | 
			
		||||
  * you can use `:hostname` as a template for multiple directories
 | 
			
		||||
  * Example A: `goldilocks -d /srv/www/:hostname --sites localhost.foo.daplie.me,localhost.bar.daplie.me`
 | 
			
		||||
  * Example B: `goldilocks -d ./:hostname/public/ --sites localhost.foo.daplie.me,localhost.bar.daplie.me`
 | 
			
		||||
* `-c <content>` - i.e. `server-https -c 'Hello, World! '` (defaults to directory index)
 | 
			
		||||
* `--express-app <path>` - path to a file the exports an express-style app (`function (req, res, next) { ... }`)
 | 
			
		||||
* `--livereload` - inject livereload into all html pages (see also: [fswatch](http://stackoverflow.com/a/13807906/151312)), but be careful if `<dirpath>` has thousands of files it will spike your CPU usage to 100%
 | 
			
		||||
 | 
			
		||||
* `--email <email>` - email to use for Let's Encrypt, Daplie DNS, Daplie Tunnel
 | 
			
		||||
* `--agree-tos` - agree to terms for Let's Encrypt, Daplie DNS
 | 
			
		||||
* `--sites <domain.tld>` comma-separated list of domains to respond to (default is `localhost.daplie.me`)
 | 
			
		||||
  * optionally you may include the path to serve with `|` such as `example.com|/tmp,example.net/srv/www`
 | 
			
		||||
* `--tunnel` - make world-visible (must use `--sites`)
 | 
			
		||||
 | 
			
		||||
Specifying a custom HTTPS certificate:
 | 
			
		||||
 | 
			
		||||
* `--key /path/to/privkey.pem` specifies the server private key
 | 
			
		||||
* `--cert /path/to/fullchain.pem` specifies the bundle of server certificate and all intermediate certificates
 | 
			
		||||
* `--root /path/to/root.pem` specifies the certificate authority(ies)
 | 
			
		||||
 | 
			
		||||
Note: `--root` may specify single cert or a bundle, and may be used multiple times like so:
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
--root /path/to/primary-root.pem --root /path/to/cross-root.pem
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Other options:
 | 
			
		||||
 | 
			
		||||
* `--serve-root true` alias for `-c` with the contents of root.pem
 | 
			
		||||
* `--sites example.com` changes the servername logged to the console
 | 
			
		||||
* `--letsencrypt-certs example.com` sets and key, fullchain, and root to standard letsencrypt locations
 | 
			
		||||
 | 
			
		||||
Examples
 | 
			
		||||
--------
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
goldilocks -p 1443 -c 'Hello from 1443' &
 | 
			
		||||
goldilocks -p 2443 -c 'Hello from 2443' &
 | 
			
		||||
goldilocks -p 3443 -d /tmp &
 | 
			
		||||
 | 
			
		||||
curl https://localhost.daplie.me:1443
 | 
			
		||||
> Hello from 1443
 | 
			
		||||
 | 
			
		||||
curl --insecure https://localhost:2443
 | 
			
		||||
> Hello from 2443
 | 
			
		||||
 | 
			
		||||
curl https://localhost.daplie.me:3443
 | 
			
		||||
> [html index listing of /tmp]
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
And if you tested <http://localhost.daplie.me:3443> in a browser,
 | 
			
		||||
it would redirect to <https://localhost.daplie.me:3443> (on the same port).
 | 
			
		||||
 | 
			
		||||
(in curl it would just show an error message)
 | 
			
		||||
 | 
			
		||||
### Testing ACME Let's Encrypt certs
 | 
			
		||||
 | 
			
		||||
In case you didn't know, you can get free https certificates from
 | 
			
		||||
[letsencrypt.org](https://letsencrypt.org)
 | 
			
		||||
(ACME letsencrypt)
 | 
			
		||||
and even a free subdomain from <https://freedns.afraid.org>.
 | 
			
		||||
 | 
			
		||||
If you want to quickly test the certificates you installed,
 | 
			
		||||
you can do so like this:
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
goldilocks -p 8443 \
 | 
			
		||||
  --letsencrypt-certs test.mooo.com \
 | 
			
		||||
  --serve-root true
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
which is equilavent to
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
goldilocks -p 8443 \
 | 
			
		||||
  --sites test.mooo.com
 | 
			
		||||
  --key /etc/letsencrypt/live/test.mooo.com/privkey.pem \
 | 
			
		||||
  --cert /etc/letsencrypt/live/test.mooo.com/fullchain.pem \
 | 
			
		||||
  --root /etc/letsencrypt/live/test.mooo.com/root.pem \
 | 
			
		||||
  -c "$(cat 'sudo /etc/letsencrypt/live/test.mooo.com/root.pem')"
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
and can be tested like so
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
curl --insecure https://test.mooo.com:8443 > ./root.pem
 | 
			
		||||
curl https://test.mooo.com:8843 --cacert ./root.pem
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
* [QuickStart Guide for Let's Encrypt](https://coolaj86.com/articles/lets-encrypt-on-raspberry-pi/)
 | 
			
		||||
* [QuickStart Guide for FreeDNS](https://coolaj86.com/articles/free-dns-hosting-with-freedns-afraid-org.html)
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										543
									
								
								bin/goldilocks.js
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										543
									
								
								bin/goldilocks.js
									
									
									
									
									
										Executable file
									
								
							@ -0,0 +1,543 @@
 | 
			
		||||
#!/usr/bin/env node
 | 
			
		||||
'use strict';
 | 
			
		||||
 | 
			
		||||
//var PromiseA = global.Promise;
 | 
			
		||||
var PromiseA = require('bluebird');
 | 
			
		||||
var tls = require('tls');
 | 
			
		||||
var https = require('httpolyglot');
 | 
			
		||||
var http = require('http');
 | 
			
		||||
var fs = require('fs');
 | 
			
		||||
var path = require('path');
 | 
			
		||||
var DDNS = require('ddns-cli');
 | 
			
		||||
var httpPort = 80;
 | 
			
		||||
var httpsPort = 443;
 | 
			
		||||
var lrPort = 35729;
 | 
			
		||||
var portFallback = 8443;
 | 
			
		||||
var insecurePortFallback = 4080;
 | 
			
		||||
 | 
			
		||||
function showError(err, port) {
 | 
			
		||||
  if ('EACCES' === err.code) {
 | 
			
		||||
    console.error(err);
 | 
			
		||||
    console.warn("You do not have permission to use '" + port + "'.");
 | 
			
		||||
    console.warn("You can probably fix that by running as Administrator or root.");
 | 
			
		||||
  }
 | 
			
		||||
  else if ('EADDRINUSE' === err.code) {
 | 
			
		||||
    console.warn("Another server is already running on '" + port + "'.");
 | 
			
		||||
    console.warn("You can probably fix that by rebooting your computer (or stopping it if you know what it is).");
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function createInsecureServer(port, _delete_me_, opts) {
 | 
			
		||||
  return new PromiseA(function (realResolve) {
 | 
			
		||||
    var server = http.createServer();
 | 
			
		||||
 | 
			
		||||
    function resolve() {
 | 
			
		||||
      realResolve(server);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    server.on('error', function (err) {
 | 
			
		||||
      if (opts.errorInsecurePort || opts.manualInsecurePort) {
 | 
			
		||||
        showError(err, port);
 | 
			
		||||
        process.exit(1);
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      opts.errorInsecurePort = err.toString();
 | 
			
		||||
 | 
			
		||||
      return createInsecureServer(insecurePortFallback, null, opts).then(resolve);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    server.on('request', opts.redirectApp);
 | 
			
		||||
 | 
			
		||||
    server.listen(port, function () {
 | 
			
		||||
      opts.insecurePort = port;
 | 
			
		||||
      resolve();
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function createServer(port, _delete_me_, content, opts) {
 | 
			
		||||
  function approveDomains(params, certs, cb) {
 | 
			
		||||
    // This is where you check your database and associated
 | 
			
		||||
    // email addresses with domains and agreements and such
 | 
			
		||||
    var domains = params.domains;
 | 
			
		||||
    //var p;
 | 
			
		||||
    console.log('approveDomains');
 | 
			
		||||
    console.log(domains);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    // The domains being approved for the first time are listed in opts.domains
 | 
			
		||||
    // Certs being renewed are listed in certs.altnames
 | 
			
		||||
    if (certs) {
 | 
			
		||||
      params.domains = certs.altnames;
 | 
			
		||||
      //p = PromiseA.resolve();
 | 
			
		||||
    }
 | 
			
		||||
    else {
 | 
			
		||||
      //params.email = opts.email;
 | 
			
		||||
      if (!opts.agreeTos) {
 | 
			
		||||
        console.error("You have not previously registered '" + domains + "' so you must specify --agree-tos to agree to both the Let's Encrypt and Daplie DNS terms of service.");
 | 
			
		||||
        process.exit(1);
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
      params.agreeTos = opts.agreeTos;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // ddns.token(params.email, domains[0])
 | 
			
		||||
    params.email = opts.email;
 | 
			
		||||
    params.refreshToken = opts.refreshToken;
 | 
			
		||||
    params.challengeType = 'dns-01';
 | 
			
		||||
    params.cli = opts.argv;
 | 
			
		||||
 | 
			
		||||
    cb(null, { options: params, certs: certs });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return new PromiseA(function (realResolve) {
 | 
			
		||||
    var app = require('../lib/app.js');
 | 
			
		||||
 | 
			
		||||
    var directive = { content: content, livereload: opts.livereload
 | 
			
		||||
      , sites: opts.sites
 | 
			
		||||
      , expressApp: opts.expressApp };
 | 
			
		||||
    var insecureServer;
 | 
			
		||||
 | 
			
		||||
    function resolve() {
 | 
			
		||||
      realResolve({
 | 
			
		||||
        plainServer: insecureServer
 | 
			
		||||
      , server: server
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // returns an instance of node-letsencrypt with additional helper methods
 | 
			
		||||
    var webrootPath = require('os').tmpdir();
 | 
			
		||||
    var leChallengeFs = require('le-challenge-fs').create({ webrootPath: webrootPath });
 | 
			
		||||
    //var leChallengeSni = require('le-challenge-sni').create({ webrootPath: webrootPath });
 | 
			
		||||
    var leChallengeDdns = require('le-challenge-ddns').create({ ttl: 1 });
 | 
			
		||||
    var lex = require('greenlock-express').create({
 | 
			
		||||
      // set to https://acme-v01.api.letsencrypt.org/directory in production
 | 
			
		||||
      server: opts.debug ? 'staging' : 'https://acme-v01.api.letsencrypt.org/directory'
 | 
			
		||||
 | 
			
		||||
    // If you wish to replace the default plugins, you may do so here
 | 
			
		||||
    //
 | 
			
		||||
    , challenges: {
 | 
			
		||||
        'http-01': leChallengeFs
 | 
			
		||||
      , 'tls-sni-01': leChallengeFs // leChallengeSni
 | 
			
		||||
      , 'dns-01': leChallengeDdns
 | 
			
		||||
      }
 | 
			
		||||
    , challengeType: (opts.tunnel ? 'http-01' : 'dns-01')
 | 
			
		||||
    , store: require('le-store-certbot').create({
 | 
			
		||||
        webrootPath: webrootPath
 | 
			
		||||
      , configDir: path.join((opts.homedir || '~'), 'letsencrypt', 'etc')
 | 
			
		||||
      , homedir: opts.homedir
 | 
			
		||||
      })
 | 
			
		||||
    , webrootPath: webrootPath
 | 
			
		||||
 | 
			
		||||
    // You probably wouldn't need to replace the default sni handler
 | 
			
		||||
    // See https://git.daplie.com/Daplie/le-sni-auto if you think you do
 | 
			
		||||
    //, sni: require('le-sni-auto').create({})
 | 
			
		||||
 | 
			
		||||
    , approveDomains: approveDomains
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    var secureContexts = {
 | 
			
		||||
      'localhost.daplie.me': null
 | 
			
		||||
    };
 | 
			
		||||
    opts.httpsOptions.SNICallback = function (sni, cb ) {
 | 
			
		||||
      var tlsOptions;
 | 
			
		||||
      console.log('[https] sni', sni);
 | 
			
		||||
 | 
			
		||||
      // Static Certs
 | 
			
		||||
      if (/.*localhost.*\.daplie\.me/.test(sni.toLowerCase())) {
 | 
			
		||||
        // TODO implement
 | 
			
		||||
        if (!secureContexts[sni]) {
 | 
			
		||||
          tlsOptions = require('localhost.daplie.me-certificates').mergeTlsOptions(sni, {});
 | 
			
		||||
        }
 | 
			
		||||
        if (tlsOptions) {
 | 
			
		||||
          secureContexts[sni] = tls.createSecureContext(tlsOptions);
 | 
			
		||||
        }
 | 
			
		||||
        cb(null, secureContexts[sni]);
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      // Dynamic Certs
 | 
			
		||||
      lex.httpsOptions.SNICallback(sni, cb);
 | 
			
		||||
    };
 | 
			
		||||
    var server = https.createServer(opts.httpsOptions);
 | 
			
		||||
 | 
			
		||||
    server.on('error', function (err) {
 | 
			
		||||
      if (opts.errorPort || opts.manualPort) {
 | 
			
		||||
        showError(err, port);
 | 
			
		||||
        process.exit(1);
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      opts.errorPort = err.toString();
 | 
			
		||||
 | 
			
		||||
      return createServer(portFallback, null, content, opts).then(resolve);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    server.listen(port, function () {
 | 
			
		||||
      opts.port = port;
 | 
			
		||||
      opts.redirectOptions.port = port;
 | 
			
		||||
 | 
			
		||||
      if (opts.livereload) {
 | 
			
		||||
        opts.lrPort = opts.lrPort || lrPort;
 | 
			
		||||
        var livereload = require('livereload');
 | 
			
		||||
        var server2 = livereload.createServer({
 | 
			
		||||
          https: opts.httpsOptions
 | 
			
		||||
        , port: opts.lrPort
 | 
			
		||||
        , exclusions: [ 'node_modules' ]
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        console.info("[livereload] watching " + opts.pubdir);
 | 
			
		||||
        console.warn("WARNING: If CPU usage spikes to 100% it's because too many files are being watched");
 | 
			
		||||
        // TODO create map of directories to watch from opts.sites and iterate over it
 | 
			
		||||
        server2.watch(opts.pubdir);
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      // if we haven't disabled insecure port
 | 
			
		||||
      if ('false' !== opts.insecurePort) {
 | 
			
		||||
        // and both ports are the default
 | 
			
		||||
        if ((httpsPort === opts.port && httpPort === opts.insecurePort)
 | 
			
		||||
          // or other case
 | 
			
		||||
          || (httpPort !== opts.insecurePort && opts.port !== opts.insecurePort)
 | 
			
		||||
        ) {
 | 
			
		||||
          return createInsecureServer(opts.insecurePort, null, opts).then(function (_server) {
 | 
			
		||||
            insecureServer = _server;
 | 
			
		||||
            resolve();
 | 
			
		||||
          });
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      opts.insecurePort = opts.port;
 | 
			
		||||
      resolve();
 | 
			
		||||
      return;
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    if ('function' === typeof app) {
 | 
			
		||||
      app = app(directive);
 | 
			
		||||
    } else if ('function' === typeof app.create) {
 | 
			
		||||
      app = app.create(directive);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    server.on('request', function (req, res) {
 | 
			
		||||
      console.log('[' + req.method + '] ' + req.url);
 | 
			
		||||
      if (!req.socket.encrypted && !/\/\.well-known\/acme-challenge\//.test(req.url)) {
 | 
			
		||||
        opts.redirectApp(req, res);
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      if ('function' === typeof app) {
 | 
			
		||||
        app(req, res);
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      res.end('not ready');
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    return PromiseA.resolve(app).then(function (_app) {
 | 
			
		||||
      app = _app;
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
module.exports.createServer = createServer;
 | 
			
		||||
 | 
			
		||||
function run() {
 | 
			
		||||
  var defaultServername = 'localhost.daplie.me';
 | 
			
		||||
  var minimist = require('minimist');
 | 
			
		||||
  var argv = minimist(process.argv.slice(2));
 | 
			
		||||
  var port = parseInt(argv.p || argv.port || argv._[0], 10) || httpsPort;
 | 
			
		||||
  var livereload = argv.livereload;
 | 
			
		||||
  var defaultWebRoot = path.resolve(argv['default-web-root'] || argv.d || argv._[1] || process.cwd());
 | 
			
		||||
  var content = argv.c;
 | 
			
		||||
  var letsencryptHost = argv['letsencrypt-certs'];
 | 
			
		||||
 | 
			
		||||
  if (argv.V || argv.version || argv.v) {
 | 
			
		||||
    if (argv.v) {
 | 
			
		||||
      console.warn("flag -v is reserved for future use. Use -V or --version for version information.");
 | 
			
		||||
    }
 | 
			
		||||
    console.info('v' + require('../package.json').version);
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  if (argv.servername && argv.sites) {
 | 
			
		||||
    throw new Error('specify only --sites, not --servername');
 | 
			
		||||
  }
 | 
			
		||||
  argv.sites = argv.sites || argv.servername;
 | 
			
		||||
 | 
			
		||||
  // letsencrypt
 | 
			
		||||
  var httpsOptions = require('localhost.daplie.me-certificates').merge({});
 | 
			
		||||
  var secureContext;
 | 
			
		||||
 | 
			
		||||
  var opts = {
 | 
			
		||||
    agreeTos: argv.agreeTos || argv['agree-tos']
 | 
			
		||||
  , debug: argv.debug
 | 
			
		||||
  , device: argv.device
 | 
			
		||||
  , provider: (argv.provider && 'false' !== argv.provider) ? argv.provider : 'oauth3.org'
 | 
			
		||||
  , email: argv.email
 | 
			
		||||
  , httpsOptions: {
 | 
			
		||||
      key: httpsOptions.key
 | 
			
		||||
    , cert: httpsOptions.cert
 | 
			
		||||
    //, ca: httpsOptions.ca
 | 
			
		||||
    }
 | 
			
		||||
  , homedir: argv.homedir
 | 
			
		||||
  , argv: argv
 | 
			
		||||
  };
 | 
			
		||||
  var peerCa;
 | 
			
		||||
  var p;
 | 
			
		||||
 | 
			
		||||
  opts.PromiseA = PromiseA;
 | 
			
		||||
  opts.httpsOptions.SNICallback = function (sni, cb) {
 | 
			
		||||
    if (!secureContext) {
 | 
			
		||||
      secureContext = tls.createSecureContext(opts.httpsOptions);
 | 
			
		||||
    }
 | 
			
		||||
    cb(null, secureContext);
 | 
			
		||||
    return;
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  if (letsencryptHost) {
 | 
			
		||||
    // TODO remove in v3.x (aka goldilocks)
 | 
			
		||||
    argv.key = argv.key || '/etc/letsencrypt/live/' + letsencryptHost + '/privkey.pem';
 | 
			
		||||
    argv.cert = argv.cert || '/etc/letsencrypt/live/' + letsencryptHost + '/fullchain.pem';
 | 
			
		||||
    argv.root = argv.root || argv.chain || '';
 | 
			
		||||
    argv.sites = argv.sites || letsencryptHost;
 | 
			
		||||
    argv['serve-root'] = argv['serve-root'] || argv['serve-chain'];
 | 
			
		||||
    // argv[express-app]
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (argv['serve-root'] && !argv.root) {
 | 
			
		||||
    console.error("You must specify bath --root to use --serve-root");
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (argv.key || argv.cert || argv.root) {
 | 
			
		||||
    if (!argv.key || !argv.cert) {
 | 
			
		||||
      console.error("You must specify bath --key and --cert, and optionally --root (required with serve-root)");
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!Array.isArray(argv.root)) {
 | 
			
		||||
      argv.root = [argv.root];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    opts.httpsOptions.key = fs.readFileSync(argv.key);
 | 
			
		||||
    opts.httpsOptions.cert = fs.readFileSync(argv.cert);
 | 
			
		||||
 | 
			
		||||
    // turn multiple-cert pemfile into array of cert strings
 | 
			
		||||
    peerCa = argv.root.reduce(function (roots, fullpath) {
 | 
			
		||||
      if (!fs.existsSync(fullpath)) {
 | 
			
		||||
        return roots;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      return roots.concat(fs.readFileSync(fullpath, 'ascii')
 | 
			
		||||
      .split('-----END CERTIFICATE-----')
 | 
			
		||||
      .filter(function (ca) {
 | 
			
		||||
        return ca.trim();
 | 
			
		||||
      }).map(function (ca) {
 | 
			
		||||
        return (ca + '-----END CERTIFICATE-----').trim();
 | 
			
		||||
      }));
 | 
			
		||||
    }, []);
 | 
			
		||||
 | 
			
		||||
    // TODO * `--verify /path/to/root.pem` require peers to present certificates from said authority
 | 
			
		||||
    if (argv.verify) {
 | 
			
		||||
      opts.httpsOptions.ca = peerCa;
 | 
			
		||||
      opts.httpsOptions.requestCert = true;
 | 
			
		||||
      opts.httpsOptions.rejectUnauthorized = true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (argv['serve-root']) {
 | 
			
		||||
      content = peerCa.join('\r\n');
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  opts.sites = [ { name: defaultServername , path: '.' } ];
 | 
			
		||||
  if (argv.sites) {
 | 
			
		||||
    opts._externalHost = false;
 | 
			
		||||
    opts.sites = argv.sites.split(',').map(function (name) {
 | 
			
		||||
      var nameparts = name.split('|');
 | 
			
		||||
      var servername = nameparts.shift();
 | 
			
		||||
      opts._externalHost = opts._externalHost || !/(^|\.)localhost\./.test(servername);
 | 
			
		||||
      // TODO allow reverse proxy
 | 
			
		||||
      return {
 | 
			
		||||
        name: servername
 | 
			
		||||
        // there should always be a path
 | 
			
		||||
      , paths: nameparts.length && nameparts || [
 | 
			
		||||
          defaultWebRoot.replace(/(:hostname|:servername)/g, servername)
 | 
			
		||||
        ]
 | 
			
		||||
        // TODO check for existing custom path before issuing with greenlock
 | 
			
		||||
      , _hasCustomPath: !!nameparts.length
 | 
			
		||||
      };
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
  // TODO use arrays in all things
 | 
			
		||||
  opts._old_server_name = opts.sites[0].name;
 | 
			
		||||
  opts.pubdir = defaultWebRoot.replace(/(:hostname|:servername).*/, '');
 | 
			
		||||
 | 
			
		||||
  if (argv.p || argv.port || argv._[0]) {
 | 
			
		||||
    opts.manualPort = true;
 | 
			
		||||
  }
 | 
			
		||||
  if (argv.t || argv.tunnel) {
 | 
			
		||||
    opts.tunnel = true;
 | 
			
		||||
  }
 | 
			
		||||
  if (argv.i || argv['insecure-port']) {
 | 
			
		||||
    opts.manualInsecurePort = true;
 | 
			
		||||
  }
 | 
			
		||||
  opts.insecurePort = parseInt(argv.i || argv['insecure-port'], 10)
 | 
			
		||||
    || argv.i || argv['insecure-port']
 | 
			
		||||
    || httpPort
 | 
			
		||||
    ;
 | 
			
		||||
  opts.livereload = livereload;
 | 
			
		||||
 | 
			
		||||
  if (argv['express-app']) {
 | 
			
		||||
    opts.expressApp = require(path.resolve(process.cwd(), argv['express-app']));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (opts.email || opts._externalHost) {
 | 
			
		||||
    if (!opts.agreeTos) {
 | 
			
		||||
      console.warn("You may need to specify --agree-tos to agree to both the Let's Encrypt and Daplie DNS terms of service.");
 | 
			
		||||
    }
 | 
			
		||||
    if (!opts.email) {
 | 
			
		||||
      // TODO store email in .ddnsrc.json
 | 
			
		||||
      console.warn("You may need to specify --email to register with both the Let's Encrypt and Daplie DNS.");
 | 
			
		||||
    }
 | 
			
		||||
    p = DDNS.refreshToken({
 | 
			
		||||
      email: opts.email
 | 
			
		||||
    , providerUrl: opts.provider
 | 
			
		||||
    , silent: true
 | 
			
		||||
    , homedir: opts.homedir
 | 
			
		||||
    }, {
 | 
			
		||||
      debug: false
 | 
			
		||||
    , email: opts.argv.email
 | 
			
		||||
    }).then(function (refreshToken) {
 | 
			
		||||
      opts.refreshToken = refreshToken;
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
  else {
 | 
			
		||||
    p = PromiseA.resolve();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return p.then(function () {
 | 
			
		||||
 | 
			
		||||
  // can be changed to tunnel external port
 | 
			
		||||
  opts.redirectOptions = {
 | 
			
		||||
    port: opts.port
 | 
			
		||||
  };
 | 
			
		||||
  opts.redirectApp = require('redirect-https')(opts.redirectOptions);
 | 
			
		||||
 | 
			
		||||
  return createServer(port, null, content, opts).then(function (servers) {
 | 
			
		||||
    var p;
 | 
			
		||||
    var httpsUrl;
 | 
			
		||||
    var httpUrl;
 | 
			
		||||
    var promise;
 | 
			
		||||
 | 
			
		||||
    // TODO show all sites
 | 
			
		||||
    console.info('');
 | 
			
		||||
    console.info('Serving ' + opts.pubdir + ' at ');
 | 
			
		||||
    console.info('');
 | 
			
		||||
 | 
			
		||||
    // Port
 | 
			
		||||
    httpsUrl = 'https://' + opts._old_server_name;
 | 
			
		||||
    p = opts.port;
 | 
			
		||||
    if (httpsPort !== p) {
 | 
			
		||||
      httpsUrl += ':' + p;
 | 
			
		||||
    }
 | 
			
		||||
    console.info('\t' + httpsUrl);
 | 
			
		||||
 | 
			
		||||
    // Insecure Port
 | 
			
		||||
    httpUrl = 'http://' + opts._old_server_name;
 | 
			
		||||
    p = opts.insecurePort;
 | 
			
		||||
    if (httpPort !== p) {
 | 
			
		||||
      httpUrl += ':' + p;
 | 
			
		||||
    }
 | 
			
		||||
    console.info('\t' + httpUrl + ' (redirecting to https)');
 | 
			
		||||
    console.info('');
 | 
			
		||||
 | 
			
		||||
    if (!(argv.sites && (defaultServername !== argv.sites) && !(argv.key && argv.cert))) {
 | 
			
		||||
      // TODO what is this condition actually intending to test again?
 | 
			
		||||
      // (I think it can be replaced with if (!opts._externalHost) { ... }
 | 
			
		||||
 | 
			
		||||
      // ifaces
 | 
			
		||||
      opts.ifaces = require('../lib/local-ip.js').find();
 | 
			
		||||
      promise = PromiseA.resolve();
 | 
			
		||||
    } else {
 | 
			
		||||
      console.info("Attempting to resolve external connection for '" + opts._old_server_name + "'");
 | 
			
		||||
      try {
 | 
			
		||||
        promise = require('../lib/match-ips.js').match(opts._old_server_name, opts);
 | 
			
		||||
      } catch(e) {
 | 
			
		||||
        console.warn("Upgrade to version 2.x to use automatic certificate issuance for '" + opts._old_server_name + "'");
 | 
			
		||||
        promise = PromiseA.resolve();
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return promise.then(function (matchingIps) {
 | 
			
		||||
      if (matchingIps) {
 | 
			
		||||
        if (!matchingIps.length) {
 | 
			
		||||
          console.info("Neither the attached nor external interfaces match '" + opts._old_server_name + "'");
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      opts.matchingIps = matchingIps || [];
 | 
			
		||||
 | 
			
		||||
      if (opts.matchingIps.length) {
 | 
			
		||||
        console.info('');
 | 
			
		||||
        console.info('External IPs:');
 | 
			
		||||
        console.info('');
 | 
			
		||||
        opts.matchingIps.forEach(function (ip) {
 | 
			
		||||
          if ('IPv4' === ip.family) {
 | 
			
		||||
            httpsUrl = 'https://' + ip.address;
 | 
			
		||||
            if (httpsPort !== opts.port) {
 | 
			
		||||
              httpsUrl += ':' + opts.port;
 | 
			
		||||
            }
 | 
			
		||||
            console.info('\t' + httpsUrl);
 | 
			
		||||
          }
 | 
			
		||||
          else {
 | 
			
		||||
            httpsUrl = 'https://[' + ip.address + ']';
 | 
			
		||||
            if (httpsPort !== opts.port) {
 | 
			
		||||
              httpsUrl += ':' + opts.port;
 | 
			
		||||
            }
 | 
			
		||||
            console.info('\t' + httpsUrl);
 | 
			
		||||
          }
 | 
			
		||||
        });
 | 
			
		||||
      }
 | 
			
		||||
      else if (!opts.tunnel) {
 | 
			
		||||
        console.info("External IP address does not match local IP address.");
 | 
			
		||||
        console.info("Use --tunnel to allow the people of the Internet to access your server.");
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      if (opts.tunnel) {
 | 
			
		||||
        require('../lib/tunnel.js').create(opts, servers);
 | 
			
		||||
      }
 | 
			
		||||
      else if (opts.ddns) {
 | 
			
		||||
        require('../lib/ddns.js').create(opts, servers);
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      Object.keys(opts.ifaces).forEach(function (iname) {
 | 
			
		||||
        var iface = opts.ifaces[iname];
 | 
			
		||||
 | 
			
		||||
        if (iface.ipv4.length) {
 | 
			
		||||
          console.info('');
 | 
			
		||||
          console.info(iname + ':');
 | 
			
		||||
 | 
			
		||||
          httpsUrl = 'https://' + iface.ipv4[0].address;
 | 
			
		||||
          if (httpsPort !== opts.port) {
 | 
			
		||||
            httpsUrl += ':' + opts.port;
 | 
			
		||||
          }
 | 
			
		||||
          console.info('\t' + httpsUrl);
 | 
			
		||||
 | 
			
		||||
          if (iface.ipv6.length) {
 | 
			
		||||
            httpsUrl = 'https://[' + iface.ipv6[0].address + ']';
 | 
			
		||||
            if (httpsPort !== opts.port) {
 | 
			
		||||
              httpsUrl += ':' + opts.port;
 | 
			
		||||
            }
 | 
			
		||||
            console.info('\t' + httpsUrl);
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      console.info('');
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
if (require.main === module) {
 | 
			
		||||
  run();
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										108
									
								
								lib/app.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										108
									
								
								lib/app.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,108 @@
 | 
			
		||||
'use strict';
 | 
			
		||||
 | 
			
		||||
module.exports = function (opts) {
 | 
			
		||||
  var finalhandler = require('finalhandler');
 | 
			
		||||
  var serveStatic = require('serve-static');
 | 
			
		||||
  var serveIndex = require('serve-index');
 | 
			
		||||
 | 
			
		||||
  var hostsMap = {};
 | 
			
		||||
  var pathsMap = {};
 | 
			
		||||
  var content = opts.content;
 | 
			
		||||
  var server;
 | 
			
		||||
 | 
			
		||||
  function addServer(hostname) {
 | 
			
		||||
 | 
			
		||||
    if (hostsMap[hostname]) {
 | 
			
		||||
      return hostsMap[hostname];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    opts.sites.forEach(function (site) {
 | 
			
		||||
      if (hostname !== site.name) {
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      // path should exist before it gets to this point
 | 
			
		||||
      site.path = site.path || site.paths[0];
 | 
			
		||||
 | 
			
		||||
      if (!pathsMap[site.path]) {
 | 
			
		||||
        pathsMap[site.path] = {
 | 
			
		||||
          serve: serveStatic(site.path)
 | 
			
		||||
        // TODO option for dotfiles
 | 
			
		||||
        , index: serveIndex(site.path)
 | 
			
		||||
        };
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      hostsMap[hostname] = {
 | 
			
		||||
        serve: pathsMap[site.path].serve
 | 
			
		||||
      , index: pathsMap[site.path].index
 | 
			
		||||
      , app: site.app
 | 
			
		||||
      };
 | 
			
		||||
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  function _reloadWrite(data, enc, cb) {
 | 
			
		||||
    /*jshint validthis: true */
 | 
			
		||||
    if (this.headersSent) {
 | 
			
		||||
      this.__write(data, enc, cb);
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!/html/i.test(this.getHeader('Content-Type'))) {
 | 
			
		||||
      this.__write(data, enc, cb);
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (this.getHeader('Content-Length')) {
 | 
			
		||||
      this.setHeader('Content-Length', this.getHeader('Content-Length') + this.__my_addLen);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    this.__write(this.__my_livereload);
 | 
			
		||||
    this.__write(data, enc, cb);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  addServer(opts.sites[0].name);
 | 
			
		||||
 | 
			
		||||
  return function (req, res) {
 | 
			
		||||
    if (content && '/' === req.url) {
 | 
			
		||||
      // res.setHeader('Content-Type', 'application/octet-stream');
 | 
			
		||||
      res.end(content);
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    var done = finalhandler(req, res);
 | 
			
		||||
    var host = req.headers.host;
 | 
			
		||||
    var hostname = (host||'').split(':')[0] || opts.sites[0].name;
 | 
			
		||||
 | 
			
		||||
    function serveStatic(server) {
 | 
			
		||||
      if (server.expressApp) {
 | 
			
		||||
        server.expressApp(req, res, serveStatic);
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      server.serve(req, res, function (err) {
 | 
			
		||||
        if (err) { return done(err); }
 | 
			
		||||
        server.index(req, res, done);
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (opts.livereload) {
 | 
			
		||||
      res.__my_livereload = '<script src="//'
 | 
			
		||||
        + (host || opts.sites[0].name).split(':')[0]
 | 
			
		||||
        + ':35729/livereload.js?snipver=1"></script>';
 | 
			
		||||
      res.__my_addLen = res.__my_livereload.length;
 | 
			
		||||
 | 
			
		||||
      // TODO modify prototype instead of each instance?
 | 
			
		||||
      res.__write = res.write;
 | 
			
		||||
      res.write = _reloadWrite;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    console.log('hostname:', hostname);
 | 
			
		||||
 | 
			
		||||
    addServer(hostname);
 | 
			
		||||
    server = hostsMap[hostname] || hostsMap[opts.sites[0].name];
 | 
			
		||||
    serveStatic(server);
 | 
			
		||||
 | 
			
		||||
  };
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										88
									
								
								lib/ddns.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										88
									
								
								lib/ddns.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,88 @@
 | 
			
		||||
'use strict';
 | 
			
		||||
 | 
			
		||||
module.exports.create = function (opts/*, servers*/) {
 | 
			
		||||
  var PromiseA = opts.PromiseA;
 | 
			
		||||
  var dns = PromiseA.promisifyAll(require('dns'));
 | 
			
		||||
 | 
			
		||||
  return PromiseA.all([
 | 
			
		||||
    dns.resolve4Async(opts._old_server_name).then(function (results) {
 | 
			
		||||
      return results;
 | 
			
		||||
    }, function () {})
 | 
			
		||||
  , dns.resolve6Async(opts._old_server_name).then(function (results) {
 | 
			
		||||
      return results;
 | 
			
		||||
    }, function () {})
 | 
			
		||||
  ]).then(function (results) {
 | 
			
		||||
    var ipv4 = results[0] || [];
 | 
			
		||||
    var ipv6 = results[1] || [];
 | 
			
		||||
    var record;
 | 
			
		||||
 | 
			
		||||
    opts.dnsRecords = {
 | 
			
		||||
      A: ipv4
 | 
			
		||||
    , AAAA: ipv6
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    Object.keys(opts.ifaces).some(function (ifacename) {
 | 
			
		||||
      var iface = opts.ifaces[ifacename];
 | 
			
		||||
 | 
			
		||||
      return iface.ipv4.some(function (localIp) {
 | 
			
		||||
        return ipv4.some(function (remoteIp) {
 | 
			
		||||
          if (localIp.address === remoteIp) {
 | 
			
		||||
            record = localIp;
 | 
			
		||||
            return record;
 | 
			
		||||
          }
 | 
			
		||||
        });
 | 
			
		||||
      }) || iface.ipv6.some(function (localIp) {
 | 
			
		||||
        return ipv6.forEach(function (remoteIp) {
 | 
			
		||||
          if (localIp.address === remoteIp) {
 | 
			
		||||
            record = localIp;
 | 
			
		||||
            return record;
 | 
			
		||||
          }
 | 
			
		||||
        });
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    if (!record) {
 | 
			
		||||
      console.info("DNS Record '" + ipv4.concat(ipv6).join(',') + "' does not match any local IP address.");
 | 
			
		||||
      console.info("Use --ddns to allow the people of the Internet to access your server.");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    opts.externalIps.ipv4.some(function (localIp) {
 | 
			
		||||
      return ipv4.some(function (remoteIp) {
 | 
			
		||||
        if (localIp.address === remoteIp) {
 | 
			
		||||
          record = localIp;
 | 
			
		||||
          return record;
 | 
			
		||||
        }
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    opts.externalIps.ipv6.some(function (localIp) {
 | 
			
		||||
      return ipv6.some(function (remoteIp) {
 | 
			
		||||
        if (localIp.address === remoteIp) {
 | 
			
		||||
          record = localIp;
 | 
			
		||||
          return record;
 | 
			
		||||
        }
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    if (!record) {
 | 
			
		||||
      console.info("DNS Record '" + ipv4.concat(ipv6).join(',') + "' does not match any local IP address.");
 | 
			
		||||
      console.info("Use --ddns to allow the people of the Internet to access your server.");
 | 
			
		||||
    }
 | 
			
		||||
  });
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
if (require.main === module) {
 | 
			
		||||
  var opts = {
 | 
			
		||||
    _old_server_name: 'aj.daplie.me'
 | 
			
		||||
  , PromiseA: require('bluebird')
 | 
			
		||||
  };
 | 
			
		||||
  // ifaces
 | 
			
		||||
  opts.ifaces = require('./local-ip.js').find();
 | 
			
		||||
  console.log('opts.ifaces');
 | 
			
		||||
  console.log(opts.ifaces);
 | 
			
		||||
  require('./match-ips.js').match(opts._old_server_name, opts).then(function (ips) {
 | 
			
		||||
    opts.matchingIps = ips.matchingIps || [];
 | 
			
		||||
    opts.externalIps = ips.externalIps;
 | 
			
		||||
    module.exports.create(opts);
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										53
									
								
								lib/local-ip.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								lib/local-ip.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,53 @@
 | 
			
		||||
'use strict';
 | 
			
		||||
 | 
			
		||||
var os = require('os');
 | 
			
		||||
 | 
			
		||||
module.exports.find = function (opts) {
 | 
			
		||||
  opts = opts || {};
 | 
			
		||||
  opts.externals = opts.externals || [];
 | 
			
		||||
 | 
			
		||||
  var ifaceMap = os.networkInterfaces();
 | 
			
		||||
  var newMap = {};
 | 
			
		||||
 | 
			
		||||
  Object.keys(ifaceMap).forEach(function (iname) {
 | 
			
		||||
    var ifaces = ifaceMap[iname];
 | 
			
		||||
 | 
			
		||||
    ifaces = ifaces.filter(function (iface) {
 | 
			
		||||
      return opts.externals.some(function (ip) {
 | 
			
		||||
        if (ip.address === iface.address) {
 | 
			
		||||
          ip.external = true;
 | 
			
		||||
          return true;
 | 
			
		||||
        }
 | 
			
		||||
      }) || (!iface.internal && !/^fe80/.test(iface.address) && !/^[0:]+$/.test(iface.mac));
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    if (!ifaces.length) {
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    newMap[iname] = newMap[iname] || { ipv4: [], ipv6: [] };
 | 
			
		||||
 | 
			
		||||
    ifaces.forEach(function (addr) {
 | 
			
		||||
      addr.iface = iname;
 | 
			
		||||
      if ('IPv4' === addr.family) {
 | 
			
		||||
        newMap[iname].ipv4.push(addr);
 | 
			
		||||
      }
 | 
			
		||||
      else if ('IPv6' === addr.family) {
 | 
			
		||||
        newMap[iname].ipv6.push(addr);
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  return newMap;
 | 
			
		||||
 | 
			
		||||
  /*
 | 
			
		||||
https://[2601:681:300:92c0:2477:d58a:d69e:51a0]:8443
 | 
			
		||||
 | 
			
		||||
  console.log('');
 | 
			
		||||
 | 
			
		||||
    console.log('');
 | 
			
		||||
    console.log(iname);
 | 
			
		||||
    console.log(ifaces);
 | 
			
		||||
    console.log('');
 | 
			
		||||
  */
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										117
									
								
								lib/match-ips.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										117
									
								
								lib/match-ips.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,117 @@
 | 
			
		||||
'use strict';
 | 
			
		||||
 | 
			
		||||
var PromiseA = require('bluebird');
 | 
			
		||||
 | 
			
		||||
module.exports.match = function (servername, opts) {
 | 
			
		||||
  return PromiseA.promisify(require('ipify'))().then(function (externalIp) {
 | 
			
		||||
    var dns = PromiseA.promisifyAll(require('dns'));
 | 
			
		||||
 | 
			
		||||
    opts.externalIps = [ { address: externalIp, family: 'IPv4' } ];
 | 
			
		||||
    opts.ifaces = require('./local-ip.js').find({ externals: opts.externalIps });
 | 
			
		||||
    opts.externalIfaces = Object.keys(opts.ifaces).reduce(function (all, iname) {
 | 
			
		||||
      var iface = opts.ifaces[iname];
 | 
			
		||||
 | 
			
		||||
      iface.ipv4.forEach(function (addr) {
 | 
			
		||||
        if (addr.external) {
 | 
			
		||||
          addr.iface = iname;
 | 
			
		||||
          all.push(addr);
 | 
			
		||||
        }
 | 
			
		||||
      });
 | 
			
		||||
      iface.ipv6.forEach(function (addr) {
 | 
			
		||||
        if (addr.external) {
 | 
			
		||||
          addr.iface = iname;
 | 
			
		||||
          all.push(addr);
 | 
			
		||||
        }
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      return all;
 | 
			
		||||
    }, []).filter(Boolean);
 | 
			
		||||
 | 
			
		||||
    function resolveIps(hostname) {
 | 
			
		||||
      var allIps = [];
 | 
			
		||||
 | 
			
		||||
      return PromiseA.all([
 | 
			
		||||
        dns.resolve4Async(hostname).then(function (records) {
 | 
			
		||||
            records.forEach(function (ip) {
 | 
			
		||||
              allIps.push({
 | 
			
		||||
                address: ip
 | 
			
		||||
              , family: 'IPv4'
 | 
			
		||||
              });
 | 
			
		||||
            });
 | 
			
		||||
          }, function () {})
 | 
			
		||||
        , dns.resolve6Async(hostname).then(function (records) {
 | 
			
		||||
            records.forEach(function (ip) {
 | 
			
		||||
              allIps.push({
 | 
			
		||||
                address: ip
 | 
			
		||||
              , family: 'IPv6'
 | 
			
		||||
              });
 | 
			
		||||
            });
 | 
			
		||||
          }, function () {})
 | 
			
		||||
      ]).then(function () {
 | 
			
		||||
        return allIps;
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function resolveIpsAndCnames(hostname) {
 | 
			
		||||
      return PromiseA.all([
 | 
			
		||||
        resolveIps(hostname)
 | 
			
		||||
      , dns.resolveCnameAsync(hostname).then(function (records) {
 | 
			
		||||
          return PromiseA.all(records.map(function (hostname) {
 | 
			
		||||
            return resolveIps(hostname);
 | 
			
		||||
          })).then(function (allIps) {
 | 
			
		||||
            return allIps.reduce(function (all, ips) {
 | 
			
		||||
              return all.concat(ips);
 | 
			
		||||
            }, []);
 | 
			
		||||
          });
 | 
			
		||||
        }, function () {
 | 
			
		||||
          return [];
 | 
			
		||||
        })
 | 
			
		||||
      ]).then(function (ips) {
 | 
			
		||||
        return ips.reduce(function (all, set) {
 | 
			
		||||
          return all.concat(set);
 | 
			
		||||
        }, []);
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return resolveIpsAndCnames(servername).then(function (allIps) {
 | 
			
		||||
      var matchingIps = [];
 | 
			
		||||
 | 
			
		||||
      if (!allIps.length) {
 | 
			
		||||
        console.warn("Could not resolve '" + servername + "'");
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      // { address, family }
 | 
			
		||||
      allIps.some(function (ip) {
 | 
			
		||||
        function match(addr) {
 | 
			
		||||
          if (ip.address === addr.address) {
 | 
			
		||||
            matchingIps.push(addr);
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        opts.externalIps.forEach(match);
 | 
			
		||||
        // opts.externalIfaces.forEach(match);
 | 
			
		||||
 | 
			
		||||
        Object.keys(opts.ifaces).forEach(function (iname) {
 | 
			
		||||
          var iface = opts.ifaces[iname];
 | 
			
		||||
 | 
			
		||||
          iface.ipv4.forEach(match);
 | 
			
		||||
          iface.ipv6.forEach(match);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        return matchingIps.length;
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      matchingIps.externalIps = {
 | 
			
		||||
        ipv4: [
 | 
			
		||||
          { address: externalIp
 | 
			
		||||
          , family: 'IPv4'
 | 
			
		||||
          }
 | 
			
		||||
        ]
 | 
			
		||||
      , ipv6: [
 | 
			
		||||
        ]
 | 
			
		||||
      };
 | 
			
		||||
      matchingIps.matchingIps = matchingIps;
 | 
			
		||||
      return matchingIps;
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										144
									
								
								lib/tunnel.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										144
									
								
								lib/tunnel.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,144 @@
 | 
			
		||||
'use strict';
 | 
			
		||||
 | 
			
		||||
module.exports.create = function (opts, servers) {
 | 
			
		||||
  // servers = { plainserver, server }
 | 
			
		||||
  var Oauth3 = require('oauth3-cli');
 | 
			
		||||
  var Tunnel = require('daplie-tunnel').create({
 | 
			
		||||
    Oauth3: Oauth3
 | 
			
		||||
  , PromiseA: opts.PromiseA
 | 
			
		||||
  , CLI: {
 | 
			
		||||
      init: function (rs, ws/*, state, options*/) {
 | 
			
		||||
        // noop
 | 
			
		||||
        return ws;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }).Tunnel;
 | 
			
		||||
  var stunnel = require('stunnel');
 | 
			
		||||
  var killcount = 0;
 | 
			
		||||
 | 
			
		||||
  /*
 | 
			
		||||
  var Dup = {
 | 
			
		||||
    write: function (chunk, encoding, cb) {
 | 
			
		||||
      this.__my_socket.push(chunk, encoding);
 | 
			
		||||
      cb();
 | 
			
		||||
    }
 | 
			
		||||
  , read: function (size) {
 | 
			
		||||
      var x = this.__my_socket.read(size);
 | 
			
		||||
      if (x) { this.push(x); }
 | 
			
		||||
    }
 | 
			
		||||
  , setTimeout: function () {
 | 
			
		||||
      console.log('TODO implement setTimeout on Duplex');
 | 
			
		||||
    }
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  var httpServer = require('http').createServer(function (req, res) {
 | 
			
		||||
    console.log('req.socket.encrypted', req.socket.encrypted);
 | 
			
		||||
    res.end('Hello, tunneled World!');
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  var tlsServer = require('tls').createServer(opts.httpsOptions, function (tlsSocket) {
 | 
			
		||||
    console.log('tls connection');
 | 
			
		||||
    // things get a little messed up here
 | 
			
		||||
    httpServer.emit('connection', tlsSocket);
 | 
			
		||||
 | 
			
		||||
    // try again
 | 
			
		||||
    //servers.server.emit('connection', tlsSocket);
 | 
			
		||||
  });
 | 
			
		||||
  */
 | 
			
		||||
 | 
			
		||||
  process.on('SIGINT', function () {
 | 
			
		||||
    killcount += 1;
 | 
			
		||||
    console.log('[quit] closing http and https servers');
 | 
			
		||||
    if (killcount >= 3) {
 | 
			
		||||
      process.exit(1);
 | 
			
		||||
    }
 | 
			
		||||
    if (servers.server) {
 | 
			
		||||
      servers.server.close();
 | 
			
		||||
    }
 | 
			
		||||
    if (servers.insecureServer) {
 | 
			
		||||
      servers.insecureServer.close();
 | 
			
		||||
    }
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  return Tunnel.token({
 | 
			
		||||
    refreshToken: opts.refreshToken
 | 
			
		||||
  , email: opts.email
 | 
			
		||||
  , domains: opts.sites.map(function (site) {
 | 
			
		||||
      return site.name;
 | 
			
		||||
    })
 | 
			
		||||
  , device: { hostname: opts.devicename || opts.device }
 | 
			
		||||
  }).then(function (result) {
 | 
			
		||||
    // { jwt, tunnelUrl }
 | 
			
		||||
    var locals = [];
 | 
			
		||||
    opts.sites.map(function (site) {
 | 
			
		||||
      locals.push({
 | 
			
		||||
        protocol: 'https'
 | 
			
		||||
      , hostname: site.name
 | 
			
		||||
      , port: opts.port
 | 
			
		||||
      });
 | 
			
		||||
      locals.push({
 | 
			
		||||
        protocol: 'http'
 | 
			
		||||
      , hostname: site.name
 | 
			
		||||
      , port: opts.insecurePort || opts.port
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
    return stunnel.connect({
 | 
			
		||||
      token: result.jwt
 | 
			
		||||
    , stunneld: result.tunnelUrl
 | 
			
		||||
      // XXX TODO BUG // this is just for testing
 | 
			
		||||
    , insecure: /*opts.insecure*/ true
 | 
			
		||||
    , locals: locals
 | 
			
		||||
      // a simple passthru is proving to not be so simple
 | 
			
		||||
    , net: require('net') /*
 | 
			
		||||
      {
 | 
			
		||||
        createConnection: function (info, cb) {
 | 
			
		||||
          // data is the hello packet / first chunk
 | 
			
		||||
          // info = { data, servername, port, host, remoteAddress: { family, address, port } }
 | 
			
		||||
 | 
			
		||||
          var myDuplex = new (require('stream').Duplex)();
 | 
			
		||||
          var myDuplex2 = new (require('stream').Duplex)();
 | 
			
		||||
          // duplex = { write, push, end, events: [ 'readable', 'data', 'error', 'end' ] };
 | 
			
		||||
 | 
			
		||||
          myDuplex2.__my_socket = myDuplex;
 | 
			
		||||
          myDuplex.__my_socket = myDuplex2;
 | 
			
		||||
 | 
			
		||||
          myDuplex2._write = Dup.write;
 | 
			
		||||
          myDuplex2._read = Dup.read;
 | 
			
		||||
 | 
			
		||||
          myDuplex._write = Dup.write;
 | 
			
		||||
          myDuplex._read = Dup.read;
 | 
			
		||||
 | 
			
		||||
          myDuplex.remoteFamily = info.remoteFamily;
 | 
			
		||||
          myDuplex.remoteAddress = info.remoteAddress;
 | 
			
		||||
          myDuplex.remotePort = info.remotePort;
 | 
			
		||||
 | 
			
		||||
          // socket.local{Family,Address,Port}
 | 
			
		||||
          myDuplex.localFamily = 'IPv4';
 | 
			
		||||
          myDuplex.localAddress = '127.0.01';
 | 
			
		||||
          myDuplex.localPort = info.port;
 | 
			
		||||
 | 
			
		||||
          myDuplex.setTimeout = Dup.setTimeout;
 | 
			
		||||
 | 
			
		||||
          // this doesn't seem to work so well
 | 
			
		||||
          //servers.server.emit('connection', myDuplex);
 | 
			
		||||
 | 
			
		||||
          // try a little more manual wrapping / unwrapping
 | 
			
		||||
          var firstByte = info.data[0];
 | 
			
		||||
          if (firstByte < 32 || firstByte >= 127) {
 | 
			
		||||
            tlsServer.emit('connection', myDuplex);
 | 
			
		||||
          }
 | 
			
		||||
          else {
 | 
			
		||||
            httpServer.emit('connection', myDuplex);
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
          if (cb) {
 | 
			
		||||
            process.nextTick(cb);
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
          return myDuplex2;
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      //*/
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										60
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										60
									
								
								package.json
									
									
									
									
									
								
							@ -1,21 +1,55 @@
 | 
			
		||||
{
 | 
			
		||||
  "name": "goldilocks",
 | 
			
		||||
  "version": "1.0.0-placeholder",
 | 
			
		||||
  "description": "The webserver that's just right.",
 | 
			
		||||
  "keywords": [
 | 
			
		||||
    "greenlock"
 | 
			
		||||
  ],
 | 
			
		||||
  "main": "index.js",
 | 
			
		||||
  "scripts": {
 | 
			
		||||
    "test": "echo \"Error: no test specified\" && exit 1"
 | 
			
		||||
  },
 | 
			
		||||
  "version": "2.2.0",
 | 
			
		||||
  "description": "The node.js webserver that's just right, Greenlock (HTTPS/TLS/SSL via ACME/Let's Encrypt) and tunneling (RVPN) included.",
 | 
			
		||||
  "main": "bin/goldilocks.js",
 | 
			
		||||
  "repository": {
 | 
			
		||||
    "type": "git",
 | 
			
		||||
    "url": "git@git.daplie.com:Daplie/goldilocks.git"
 | 
			
		||||
    "url": "git@git.daplie.com:Daplie/goldilocks.js.git"
 | 
			
		||||
  },
 | 
			
		||||
  "author": "AJ ONeal <coolaj86@gmail.com> (https://coolaj86.com/)",
 | 
			
		||||
  "license": "(MIT OR Apache-2.0)",
 | 
			
		||||
  "author": "AJ ONeal <aj@daplie.com> (https://daplie.com/)",
 | 
			
		||||
  "license": "SEE LICENSE IN LICENSE.txt",
 | 
			
		||||
  "scripts": { "test": "node bin/goldilocks.js -p 8443 -d /tmp/" },
 | 
			
		||||
  "bin": { "goldilocks": "./bin/goldilocks.js" },
 | 
			
		||||
  "keywords": [
 | 
			
		||||
    "https",
 | 
			
		||||
    "local",
 | 
			
		||||
    "localhost",
 | 
			
		||||
    "development",
 | 
			
		||||
    "dev",
 | 
			
		||||
    "tls",
 | 
			
		||||
    "ssl",
 | 
			
		||||
    "cert",
 | 
			
		||||
    "certs",
 | 
			
		||||
    "certificate",
 | 
			
		||||
    "certificates",
 | 
			
		||||
    "http",
 | 
			
		||||
    "express",
 | 
			
		||||
    "connect",
 | 
			
		||||
    "serve",
 | 
			
		||||
    "server"
 | 
			
		||||
  ],
 | 
			
		||||
  "bugs": { "url": "https://git.daplie.com/Daplie/server-https/issues" },
 | 
			
		||||
  "homepage": "https://git.daplie.com/Daplie/goldilocks.js#readme",
 | 
			
		||||
  "dependencies": {
 | 
			
		||||
    "greenlock": "^2.1.11"
 | 
			
		||||
    "bluebird": "^3.4.6",
 | 
			
		||||
    "daplie-tunnel": "git+https://git.daplie.com/Daplie/daplie-cli-tunnel.git#master",
 | 
			
		||||
    "ddns-cli": "git+https://git.daplie.com/Daplie/node-ddns-client.git#master",
 | 
			
		||||
    "finalhandler": "^0.4.0",
 | 
			
		||||
    "httpolyglot": "^0.1.1",
 | 
			
		||||
    "ipify": "^1.1.0",
 | 
			
		||||
    "le-challenge-ddns": "git+https://git.daplie.com/Daplie/le-challenge-ddns.git#master",
 | 
			
		||||
    "le-challenge-fs": "git+https://git.daplie.com/Daplie/le-challenge-webroot.git#master",
 | 
			
		||||
    "le-challenge-sni": "^2.0.1",
 | 
			
		||||
    "greenlock-express": "git+https://git.daplie.com/Daplie/greenlock-express.git#master",
 | 
			
		||||
    "greenlock": "git+https://git.daplie.com/Daplie/node-greenlock.git#master",
 | 
			
		||||
    "livereload": "^0.6.0",
 | 
			
		||||
    "localhost.daplie.me-certificates": "^1.3.0",
 | 
			
		||||
    "minimist": "^1.1.1",
 | 
			
		||||
    "oauth3-cli": "git+https://git.daplie.com/OAuth3/oauth3-cli.git#master",
 | 
			
		||||
    "redirect-https": "^1.1.0",
 | 
			
		||||
    "serve-index": "^1.7.0",
 | 
			
		||||
    "serve-static": "^1.10.0",
 | 
			
		||||
    "stunnel": "git+https://git.daplie.com/Daplie/node-tunnel-client.git#master"
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										23
									
								
								stages/01-serve.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								stages/01-serve.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,23 @@
 | 
			
		||||
'use strict';
 | 
			
		||||
 | 
			
		||||
var https = require('httpolyglot');
 | 
			
		||||
var httpsOptions = require('localhost.daplie.me-certificates').merge({});
 | 
			
		||||
var httpsPort = 8443;
 | 
			
		||||
var redirectApp = require('redirect-https')({
 | 
			
		||||
  port: httpsPort
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
var server = https.createServer(httpsOptions);
 | 
			
		||||
 | 
			
		||||
server.on('request', function (req, res) {
 | 
			
		||||
  if (!req.socket.encrypted) {
 | 
			
		||||
    redirectApp(req, res);
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  res.end("Hello, Encrypted World!");
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
server.listen(httpsPort, function () {
 | 
			
		||||
  console.log('https://' + 'localhost.daplie.me' + (443 === httpsPort ? ':' : ':' + httpsPort));
 | 
			
		||||
});
 | 
			
		||||
							
								
								
									
										17
									
								
								test-chain.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										17
									
								
								test-chain.sh
									
									
									
									
									
										Executable file
									
								
							@ -0,0 +1,17 @@
 | 
			
		||||
#!/bin/bash
 | 
			
		||||
 | 
			
		||||
node serve.js \
 | 
			
		||||
  --port 8443 \
 | 
			
		||||
  --key node_modules/localhost.daplie.me-certificates/privkey.pem \
 | 
			
		||||
  --cert node_modules/localhost.daplie.me-certificates/fullchain.pem \
 | 
			
		||||
  --root node_modules/localhost.daplie.me-certificates/root.pem \
 | 
			
		||||
  -c "$(cat node_modules/localhost.daplie.me-certificates/root.pem)" &
 | 
			
		||||
 | 
			
		||||
PID=$!
 | 
			
		||||
 | 
			
		||||
sleep 1
 | 
			
		||||
curl -s --insecure http://localhost.daplie.me:8443 > ./root.pem
 | 
			
		||||
curl -s https://localhost.daplie.me:8443 --cacert ./root.pem
 | 
			
		||||
 | 
			
		||||
rm ./root.pem
 | 
			
		||||
kill $PID 2>/dev/null
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user