Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 | /* eslint-disable no-sequences */
import type { Prettify } from '@https-enable/types'
import type { AppType, SamePortOptions } from './type'
import http from 'node:http'
import net from 'node:net'
import process from 'node:process'
import tls from 'node:tls'
import { verifyCertificate } from '@https-enable/mkcert'
/**
*
* @param app
* @param options
* @link https://gist.github.com/Coolchickenguy/a424ab0f4d32f024b39cd8cdd2b912ae
*/
export function samePortSSL(app: AppType, options: SamePortOptions) {
const isRedirect = options.redirect ?? false
const host = options.host
const port = Number(options.port)
// The tcp server that receves all the requests
const tcpserver = net.createServer()
// The normal server ( MUST be http or else it will try sorting the encription out itself and will fail in this configuration)
// This is just as secure as the normal nodejs https server
// eslint-disable-next-line ts/ban-ts-comment
// @ts-expect-error
let server = http.createServer(options, app)
// A server that redirect all the requests to https, you could have this be the normal server too.
const redirectServer
= http.createServer(isRedirect
? (req, res) => (res.writeHead(302, { location: `https://${req.headers.host || req.url}` }), res.end())
: app)
// Make the proxy server listen
tcpserver.listen(port, host)
// Handle request
tcpserver.on('connection', (socket) => {
// Detect http or https/tls handskake
socket.once('data', (data) => {
// Buffer incomeing requests
socket.pause()
// Detect if the provided handshake data is TLS by checking if it starts with 22, which TLS always dose
if (data[0] === 22) {
// Https
// You may use this socket as a TLS socket, meaning you can attach this to the same http server
const sock = new tls.TLSSocket(socket, { isServer: true, ...options })
// Add the TLS socket as a connection to the main http server
server.emit('connection', sock)
// Append data to start of data buffer
socket.unshift(data)
}
else {
// Http
// Emit the socket to the redirect server
redirectServer.emit('connection', socket)
// Http views the events, meaning I can just refire the eventEmiter
socket.emit('data', data)
}
// Resume socket
process.nextTick(() => socket.resume())
})
})
class ServerInstance {
isAlive: boolean = true
/**
* Kill the server
* @returns A promise that resolves when the server ends
*/
kill() {
return new Promise<void>((resolve, fail) => {
tcpserver.close((err) => {
if (typeof err === 'undefined') {
server.closeAllConnections()
redirectServer.closeAllConnections()
this.isAlive = false
resolve()
}
else {
fail(err)
}
})
})
}
/**
* Change the server options
* @param newOptions The new server options
* @param newOptions.cert cert content
* @param newOptions.key key content
*/
async refresh(newOptions: { cert: string, key: string }) {
if (newOptions && newOptions.cert && newOptions.key && typeof newOptions.cert === 'string' && typeof newOptions.key === 'string') {
const verify = await verifyCertificate(newOptions.key, newOptions.cert)
if (!verify.match)
return
// 证书校验有效再更新 server
options = { ...options, ...newOptions }
// eslint-disable-next-line ts/ban-ts-comment
// @ts-expect-error
return server = http.createServer(options, app)
}
}
}
return new Promise<ServerInstance>(resolve => tcpserver.on('listening', () => resolve(new ServerInstance())))
}
export type ServerInstance = Prettify<Awaited<ReturnType<typeof samePortSSL>>>
|