diff --git a/README.md b/README.md index 3fa6670..86af562 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,16 @@ Start your web server on the target port (`9000` in the example) and navigate to Using a dynamic DNS provider such as [noip](http://www.noip.com/personal/) or [DynDNS](http://dyn.com/dns/) or a static IP (if you have one) you can open a port in your firewall to allow external sites to call into your web server. This is great for developing applications using [OAuth](http://oauth.net/) without having to deploy externally. +## Specific bind address + +To pass a specific bind address to listen to, run: + +```sh +local-ssl-proxy --source 9001 --target 9000 --bindAddress '0.0.0.0' +``` + +This is often necessary when running local-ssl-proxy under WSL, if you are then using a browser on Windows as opposed to one installed directly to WSL. + ## Advanced You can also pass a configuration file, this helps share setups with team members. These can contain multiple proxies that `local-ssl-proxy` will open concurrently. diff --git a/package.json b/package.json index 1a3c73b..67b9f7a 100644 --- a/package.json +++ b/package.json @@ -30,10 +30,10 @@ }, "devDependencies": { "@tsconfig/node-lts-strictest": "^18.12.1", - "@types/http-proxy": "^1.17.10", + "@types/http-proxy": "1.17.15", "@types/node": "^18.15.0", "typescript": "^4.9.5", "vitest": "^0.29.2" }, "packageManager": "yarn@3.4.1" -} +} \ No newline at end of file diff --git a/src/lib.ts b/src/lib.ts index 79adb0f..85f3947 100644 --- a/src/lib.ts +++ b/src/lib.ts @@ -24,9 +24,10 @@ const program = createCommand(name) resolve(__dirname, '..', 'resources', 'localhost.pem') ) .option('-k, --key ', 'path to SSL key', exists, resolve(__dirname, '..', 'resources', 'localhost-key.pem')) - .option('-o, --config ', 'path to configuration file', (path) => require(absolutePath(path))); + .option('-o, --config ', 'path to configuration file', (path) => require(absolutePath(path))) + .option('-b, --bindAddress ', 'bind address for the server'); -type Proxy = { +export type Proxy = { hostname: string; source: number; target: number; @@ -34,10 +35,11 @@ type Proxy = { key: string; }; -type Config = { config: Record }; -type ParsedArguments = Proxy | Config; +export type Config = { config: Record }; +type BindAddress = { bindAddress: string }; +export type ParsedArguments = (Proxy | Config) & BindAddress; -function isConfig(args: unknown): args is Config { +export function isConfig(args: unknown): args is Config { return Boolean(args && typeof args === 'object' && 'config' in args); } @@ -58,9 +60,9 @@ export function isProxy(input: unknown): input is Proxy { ); } -export function parse(args?: string[]): Proxy | Record { +export function parse(args?: string[]): ParsedArguments { const proxy: ParsedArguments = args === undefined ? program.parse().opts() : program.parse(args, { from: 'user' }).opts(); - return isConfig(proxy) ? proxy.config : proxy; + return isConfig(proxy) ? {config: proxy.config, bindAddress: proxy.bindAddress} : {...proxy, bindAddress: proxy.bindAddress}; } diff --git a/src/main.ts b/src/main.ts index db69443..8ca1f4a 100755 --- a/src/main.ts +++ b/src/main.ts @@ -7,10 +7,11 @@ import { isProxy, parse } from './lib'; const parsed = parse(); -const config = isProxy(parsed) ? { proxy: parsed } : parsed; +const config = isProxy(parsed) ? { proxy: parsed } : parsed.config; for (const name of Object.keys(config)) { const { hostname, target, key, cert, source } = config[name]!; + const bindAddress = parsed.bindAddress || undefined; proxy .createServer({ @@ -28,7 +29,7 @@ for (const name of Object.keys(config)) { .on('error', (e: any) => { console.error(red('Request failed to ' + name + ': ' + bold(e.code))); }) - .listen(source); + .listen(source, bindAddress); console.log( green( diff --git a/test/lib.test.ts b/test/lib.test.ts index ebd7b2d..19e9b38 100644 --- a/test/lib.test.ts +++ b/test/lib.test.ts @@ -1,59 +1,95 @@ import { test, expect } from 'vitest'; -import { parse } from '../src/lib'; +import { isProxy, isConfig, parse, ParsedArguments, Proxy, Config } from '../src/lib'; + + +function expectProxy(result: ParsedArguments): Proxy { + if (isProxy(result)) { + return result; + } + + throw new Error('Returned object is not of type Proxy'); +} + +function expectConfig(result: ParsedArguments): Config { + if (isConfig(result)) { + return result; + } + + throw new Error('Returned object is not of type Proxy'); +} test('cert (default)', () => { - const { cert } = parse([]); + const result = parse([]); + const { cert } = expectProxy(result); expect(cert).toEqual(expect.any(String)); + expect(result.bindAddress).toBeUndefined(); }); test('cert', () => { - const { cert } = parse(['--cert', require.resolve('../resources/localhost.pem')]); + const result = parse(['--cert', require.resolve('../resources/localhost.pem')]); + const { cert } = expectProxy(result); expect(cert).toEqual(expect.any(String)); }); test('key (default)', () => { - const { key } = parse([]); + const result = parse([]); + const { key } = expectProxy(result); expect(key).toEqual(expect.any(String)); }); test('key', () => { - const { key } = parse(['--key', require.resolve('../resources/localhost-key.pem')]); + const result = parse(['--key', require.resolve('../resources/localhost-key.pem')]); + const { key } = expectProxy(result); expect(key).toEqual(expect.any(String)); }); test('hostname (default)', () => { - const { hostname } = parse([]); + const result = parse([]); + const { hostname } = expectProxy(result); expect(hostname).toBe('localhost'); }); test('hostname', () => { - const { hostname } = parse(['--hostname', '127.0.0.1']); + const result = parse(['--hostname', '127.0.0.1']); + const { hostname } = expectProxy(result); expect(hostname).toBe('127.0.0.1'); }); test('source (default)', () => { - const { source } = parse([]); + const result = parse([]); + const { source } = expectProxy(result); expect(source).toBe(9001); }); test('source', () => { - const { source } = parse(['--source', '5001']); + const result = parse(['--source', '5001']); + const { source } = expectProxy(result); expect(source).toBe(5001); }); test('target (default)', () => { - const { target } = parse([]); + const result = parse([]); + const { target } = expectProxy(result); expect(target).toBe(9000); }); test('target', () => { - const { target } = parse(['--target', '5000']); + const result = parse(['--target', '5000']); + const { target } = expectProxy(result); expect(target).toBe(5000); }); +test('bindAddress', () => { + const { bindAddress } = parse(['--bindAddress', '0.0.0.0']); + expect(bindAddress).toBe('0.0.0.0'); +}); + test('config', () => { - const config = parse(['--config', require.resolve('./test-config.json')]); - expect(config).toMatchInlineSnapshot(` + const result = parse(['--config', require.resolve('./test-config.json'),'--bindAddress', '0.0.0.0']); + expect(result.bindAddress).toBe('0.0.0.0'); + + const parsed = expectConfig(result); + expect(parsed.config).toMatchInlineSnapshot(` { "Proxy 1": { "cert": "/etc/apache2/server.pem", diff --git a/yarn.lock b/yarn.lock index a157b53..953ec06 100644 --- a/yarn.lock +++ b/yarn.lock @@ -216,12 +216,12 @@ __metadata: languageName: node linkType: hard -"@types/http-proxy@npm:^1.17.10": - version: 1.17.10 - resolution: "@types/http-proxy@npm:1.17.10" +"@types/http-proxy@npm:latest": + version: 1.17.15 + resolution: "@types/http-proxy@npm:1.17.15" dependencies: "@types/node": "*" - checksum: 8fabee5d01715e338f426715325121d6c4b7a9694dee716ab61c874e0aaccee9a0fff7ccc3c9d7e37a8feeaab7c783c17aaa9943efbc8849c5e79ecd7eaf02ab + checksum: d96eaf4e22232b587b46256b89c20525c453216684481015cf50fb385b0b319b883749ccb77dee9af57d107e8440cdacd56f4234f65176d317e9777077ff5bf3 languageName: node linkType: hard @@ -976,7 +976,7 @@ __metadata: resolution: "local-ssl-proxy@workspace:." dependencies: "@tsconfig/node-lts-strictest": ^18.12.1 - "@types/http-proxy": ^1.17.10 + "@types/http-proxy": latest "@types/node": ^18.15.0 ansi-colors: ^4.1.3 commander: ^10.0.0