Skip to content

Commit

Permalink
chore: wip
Browse files Browse the repository at this point in the history
  • Loading branch information
chrisbbreuer committed Mar 5, 2024
1 parent 454dd58 commit b55ae41
Show file tree
Hide file tree
Showing 5 changed files with 123 additions and 9 deletions.
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
bun install -d bun-reverse-proxy
```

Alternatively, you can install:
_Alternatively, you can install:_

```bash
brew install reverse-proxy # wip
Expand All @@ -34,7 +34,7 @@ pkgx install reverse-proxy # wip

## Usage

Now, you can use it in your project:
Given you installed the npm package, you can use it in your project:

```js
import { startProxy } from 'bun-reverse-proxy'
Expand Down Expand Up @@ -98,9 +98,9 @@ For casual chit-chat with others using this package:

## Postcardware

Two things are true: Stacks OSS will always stay open-source, and we do/would love to receive postcards from where Stacks is used! 🌍 _And we also publish them on our website._
Two things are true: Stacks OSS will always stay open-source, and we do/would love to receive postcards from wherever Stacks is used! 🌍 _And we also publish them on our website. -Thank you, Spatie_

Our address is: Stacks.js, 5710 Crescent Park #107, Playa Vista 90094, CA.
Our address: Stacks.js, 5710 Crescent Park #107, Playa Vista 90094, CA.

## License

Expand Down
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,9 @@
"typecheck": "bun --bun tsc --noEmit"
},
"dependencies": {
"@stacksjs/cli": "workspace:*",
"@stacksjs/path": "workspace:*",
"@stacksjs/storage": "workspace:*",
"@stacksjs/cli": "^0.58.73",
"@stacksjs/path": "^0.58.73",
"@stacksjs/storage": "^0.58.73",
"c12": "^1.9.0"
},
"devDependencies": {
Expand Down
8 changes: 8 additions & 0 deletions src/config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { loadConfig } from 'c12'

// Get loaded config
const { config } = await loadConfig({
name: 'reverse-proxy',
})

export { config }
3 changes: 1 addition & 2 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
export const one = 1
export const two = 'two'
export * from './start'
107 changes: 107 additions & 0 deletions src/start.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import * as net from 'node:net'
import * as http from 'node:http'
import * as https from 'node:https'
import * as fs from 'node:fs'
import type { Buffer } from 'node:buffer'
import { bold, green, log } from '@stacksjs/cli'
import { path } from '@stacksjs/path'
import { version } from '../package.json'

interface Option {
from?: string // domain to proxy from, defaults to localhost:3000
to?: string // domain to proxy to, defaults to stacks.localhost
keyPath?: string // absolute path to the key
certPath?: string // absolute path to the cert
}

type Options = Option | Option[]

export function startServer(option: Option = { from: 'localhost:3000', to: 'stacks.localhost' }): void {
log.debug('Starting Reverse Proxy Server')

const key = fs.readFileSync(option.keyPath ?? path.projectStoragePath(`keys/localhost-key.pem`))
const cert = fs.readFileSync(option.certPath ?? path.projectStoragePath(`keys/localhost-cert.pem`))

// Parse the option.from URL to dynamically set hostname and port
const fromUrl = new URL(option.from ? (option.from.startsWith('http') ? option.from : `http://${option.from}`) : 'http://localhost:3000')
const hostname = fromUrl.hostname
const port = Number.parseInt(fromUrl.port) || (fromUrl.protocol === 'https:' ? 443 : 80)

// Attempt to connect to the specified host and port
const socket = net.connect(port, hostname, () => {
log.debug(`Successfully connected to ${option.from}`)
socket.end()

// Proceed with setting up the reverse proxy after successful connection
setupReverseProxy({ key, cert, hostname, port, option })
})

socket.on('error', (err) => {
log.error(`Failed to connect to ${option.from}: ${err.message}`)
throw new Error(`Cannot start reverse proxy because ${option.from} is unreachable.`)
})
}

function setupReverseProxy({ key, cert, hostname, port, option }: { key: Buffer, cert: Buffer, hostname: string, port: number, option: Option }): void {
// This server will act as a reverse proxy
const httpsServer = https.createServer({ key, cert }, (req, res) => {
// Define the target server's options
const options = {
hostname,
port,
path: req.url,
method: req.method,
headers: req.headers,
}

// Create a request to the target server
const proxyReq = http.request(options, (proxyRes) => {
// Set the statusCode and headers from the proxied response
res.writeHead(proxyRes.statusCode || 500, proxyRes.headers)
// Pipe the proxied response's body directly to the original response
proxyRes.pipe(res, { end: true })
})

// Pipe the original request body directly to the proxied request
req.pipe(proxyReq, { end: true })

// Handle errors
proxyReq.on('error', (err) => {
console.error('Proxy to server error:', err)
res.writeHead(500)
res.end('Proxy error')
})
})

httpsServer.listen(443, '0.0.0.0', () => {
// eslint-disable-next-line no-console
console.log('')
// eslint-disable-next-line no-console
console.log(` ${green(bold('reverse-proxy'))} ${green(`v${version}`)}`)
// eslint-disable-next-line no-console
console.log('')
// eslint-disable-next-line no-console
console.log(` ${green('➜')} ${option.from}${option.to}`)
})

// http to https redirect
http.createServer((req, res) => {
res.writeHead(301, { Location: `https://${req.headers.host}${req.url}` })
res.end()
}).listen(80)
}

export function startProxy(option?: Option): void {
startProxies(option)
}

export function startProxies(options?: Options): void {
if (Array.isArray(options)) {
options.forEach((option: Option) => {
startServer(option)
})
}
else {
startServer(options)
}
}

0 comments on commit b55ae41

Please sign in to comment.