@makeflow/gateway
Advanced tools
Comparing version 0.1.4 to 0.1.5
@@ -5,2 +5,3 @@ "use strict"; | ||
const tslib_1 = require("tslib"); | ||
const assert_1 = tslib_1.__importDefault(require("assert")); | ||
const koa_1 = tslib_1.__importDefault(require("koa")); | ||
@@ -23,2 +24,3 @@ const koa_session_1 = tslib_1.__importDefault(require("koa-session")); | ||
if (typeof candidateBase === 'string') { | ||
assert_1.default(context.url.startsWith(candidateBase)); | ||
target = candidateTarget; | ||
@@ -25,0 +27,0 @@ base = candidateBase; |
@@ -11,4 +11,7 @@ import { ServerOptions } from 'http-proxy'; | ||
private proxy; | ||
private websocketUpgradeInitialized; | ||
constructor(descriptor: ProxyTargetDescriptor); | ||
handle(context: Context, _next: Next, base: string): Promise<void>; | ||
private ensureWebsocketUpgrade; | ||
private buildTargetURL; | ||
} |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.ProxyTarget = void 0; | ||
const tslib_1 = require("tslib"); | ||
const assert_1 = tslib_1.__importDefault(require("assert")); | ||
const http_1 = require("http"); | ||
@@ -13,2 +11,3 @@ const http_proxy_1 = require("http-proxy"); | ||
super(descriptor); | ||
this.websocketUpgradeInitialized = false; | ||
let { options } = descriptor; | ||
@@ -18,6 +17,7 @@ this.proxy = http_proxy_1.createProxyServer({ ...options, ignorePath: true }); | ||
async handle(context, _next, base) { | ||
let { target } = this.descriptor; | ||
let url = context.url; | ||
assert_1.default(url.startsWith(base)); | ||
target = `${target}${url.slice(base.length)}`; | ||
let { options: { ws: websocketEnabled = false } = {} } = this.descriptor; | ||
if (websocketEnabled) { | ||
this.ensureWebsocketUpgrade(context); | ||
} | ||
let target = this.buildTargetURL(context.url, base); | ||
let setCookieHeaders = context.response.headers['set-cookie']; | ||
@@ -52,2 +52,28 @@ setCookieHeaders = | ||
} | ||
ensureWebsocketUpgrade(context) { | ||
if (this.websocketUpgradeInitialized) { | ||
return; | ||
} | ||
this.websocketUpgradeInitialized = true; | ||
let server = context.req.connection.server; | ||
server.on('upgrade', (req, socket, head) => { | ||
let url = req.url; | ||
let base = this.match({ | ||
url, | ||
path: url.match(/^[^?]*/)[0], | ||
headers: req.headers, | ||
}); | ||
if (base === undefined) { | ||
return; | ||
} | ||
let target = this.buildTargetURL(url, base); | ||
this.proxy.ws(req, socket, head, { | ||
target, | ||
}); | ||
}); | ||
} | ||
buildTargetURL(url, base) { | ||
let { target } = this.descriptor; | ||
return `${target.replace('{base}', base)}${url.slice(base.length)}`; | ||
} | ||
} | ||
@@ -54,0 +80,0 @@ exports.ProxyTarget = ProxyTarget; |
@@ -0,8 +1,15 @@ | ||
/// <reference types="node" /> | ||
import { IncomingHttpHeaders } from 'http'; | ||
import { Context, Next } from 'koa'; | ||
import { Dict } from 'tslang'; | ||
export declare type GatewayTargetMatchFunction = (context: Context) => string | undefined; | ||
export interface GatewayTargetMatchContext { | ||
url: string; | ||
path: string; | ||
headers: IncomingHttpHeaders; | ||
} | ||
export declare type GatewayTargetMatchFunction = (context: GatewayTargetMatchContext | Context) => string | undefined; | ||
export interface IGatewayTargetDescriptor { | ||
type: string; | ||
match?: string | RegExp | GatewayTargetMatchFunction | { | ||
path?: string | RegExp; | ||
match?: string | string[] | RegExp | GatewayTargetMatchFunction | { | ||
path?: string | string[] | RegExp; | ||
headers?: Dict<string | RegExp | boolean>; | ||
@@ -17,3 +24,3 @@ }; | ||
abstract handle(context: Context, next: Next, base: string): Promise<void>; | ||
match(context: Context): string | undefined; | ||
match(context: GatewayTargetMatchContext): string | undefined; | ||
} | ||
@@ -20,0 +27,0 @@ export declare const AbstractGatewayTarget: typeof GatewayTarget; |
@@ -18,3 +18,5 @@ "use strict"; | ||
let { match = '' } = this.descriptor; | ||
if (typeof match === 'string' || match instanceof RegExp) { | ||
if (typeof match === 'string' || | ||
Array.isArray(match) || | ||
match instanceof RegExp) { | ||
match = { | ||
@@ -46,10 +48,18 @@ path: match, | ||
if (typeof pattern === 'string') { | ||
// E.g. pattern '/app' matches both '/app' and '/app/workbench', not not | ||
// '/app-workbench'. | ||
let matched = path.startsWith(pattern) && | ||
(path.length === pattern.length || | ||
pattern[pattern.length - 1] === '/' || | ||
path[pattern.length] === '/'); | ||
return matched ? pattern : undefined; | ||
pattern = [pattern]; | ||
} | ||
if (Array.isArray(pattern)) { | ||
for (let stringPattern of pattern) { | ||
// E.g. pattern '/app' matches both '/app' and '/app/workbench', not not | ||
// '/app-workbench'. | ||
let matched = path.startsWith(stringPattern) && | ||
(path.length === stringPattern.length || | ||
stringPattern[stringPattern.length - 1] === '/' || | ||
path[stringPattern.length] === '/'); | ||
if (matched) { | ||
return stringPattern; | ||
} | ||
} | ||
return undefined; | ||
} | ||
else { | ||
@@ -61,7 +71,5 @@ let groups = pattern.exec(path); | ||
} | ||
function matchHeaders(headerDict, headerPatternDict) { | ||
function matchHeaders(headers, headerPatternDict) { | ||
for (let [name, valuePattern] of Object.entries(headerPatternDict)) { | ||
let value = hasOwnProperty.call(headerDict, name) | ||
? headerDict[name] | ||
: undefined; | ||
let value = hasOwnProperty.call(headers, name) ? headers[name] : undefined; | ||
if (typeof valuePattern === 'boolean') { | ||
@@ -68,0 +76,0 @@ if (valuePattern ? value === undefined : value !== undefined) { |
{ | ||
"name": "@makeflow/gateway", | ||
"version": "0.1.4", | ||
"version": "0.1.5", | ||
"repository": { | ||
@@ -5,0 +5,0 @@ "type": "git", |
@@ -0,1 +1,2 @@ | ||
import assert from 'assert'; | ||
import {Server} from 'http'; | ||
@@ -89,2 +90,4 @@ import {ListenOptions} from 'net'; | ||
if (typeof candidateBase === 'string') { | ||
assert(context.url.startsWith(candidateBase)); | ||
target = candidateTarget; | ||
@@ -91,0 +94,0 @@ base = candidateBase; |
@@ -1,3 +0,2 @@ | ||
import assert from 'assert'; | ||
import {OutgoingMessage} from 'http'; | ||
import {IncomingMessage, OutgoingMessage, Server as HTTPServer} from 'http'; | ||
@@ -20,2 +19,4 @@ import Server, {ServerOptions, createProxyServer} from 'http-proxy'; | ||
private websocketUpgradeInitialized = false; | ||
constructor(descriptor: ProxyTargetDescriptor) { | ||
@@ -30,10 +31,10 @@ super(descriptor); | ||
async handle(context: Context, _next: Next, base: string): Promise<void> { | ||
let {target} = this.descriptor; | ||
let {options: {ws: websocketEnabled = false} = {}} = this.descriptor; | ||
let url = context.url; | ||
if (websocketEnabled) { | ||
this.ensureWebsocketUpgrade(context); | ||
} | ||
assert(url.startsWith(base)); | ||
let target = this.buildTargetURL(context.url, base); | ||
target = `${target}${url.slice(base.length)}`; | ||
let setCookieHeaders = context.response.headers['set-cookie'] as | ||
@@ -83,2 +84,38 @@ | string[] | ||
} | ||
private ensureWebsocketUpgrade(context: Context): void { | ||
if (this.websocketUpgradeInitialized) { | ||
return; | ||
} | ||
this.websocketUpgradeInitialized = true; | ||
let server = (context.req.connection as any).server as HTTPServer; | ||
server.on('upgrade', (req: IncomingMessage, socket, head) => { | ||
let url = req.url!; | ||
let base = this.match({ | ||
url, | ||
path: url.match(/^[^?]*/)![0], | ||
headers: req.headers, | ||
}); | ||
if (base === undefined) { | ||
return; | ||
} | ||
let target = this.buildTargetURL(url, base); | ||
this.proxy.ws(req, socket, head, { | ||
target, | ||
}); | ||
}); | ||
} | ||
private buildTargetURL(url: string, base: string): string { | ||
let {target} = this.descriptor; | ||
return `${target.replace('{base}', base)}${url.slice(base.length)}`; | ||
} | ||
} | ||
@@ -85,0 +122,0 @@ |
@@ -0,1 +1,3 @@ | ||
import {IncomingHttpHeaders} from 'http'; | ||
import {Context, Next} from 'koa'; | ||
@@ -10,4 +12,10 @@ import {Dict} from 'tslang'; | ||
export interface GatewayTargetMatchContext { | ||
url: string; | ||
path: string; | ||
headers: IncomingHttpHeaders; | ||
} | ||
export type GatewayTargetMatchFunction = ( | ||
context: Context, | ||
context: GatewayTargetMatchContext | Context, | ||
) => string | undefined; | ||
@@ -19,6 +27,7 @@ | ||
| string | ||
| string[] | ||
| RegExp | ||
| GatewayTargetMatchFunction | ||
| { | ||
path?: string | RegExp; | ||
path?: string | string[] | RegExp; | ||
headers?: Dict<string | RegExp | boolean>; | ||
@@ -42,6 +51,10 @@ }; | ||
match(context: Context): string | undefined { | ||
match(context: GatewayTargetMatchContext): string | undefined { | ||
let {match = ''} = this.descriptor; | ||
if (typeof match === 'string' || match instanceof RegExp) { | ||
if ( | ||
typeof match === 'string' || | ||
Array.isArray(match) || | ||
match instanceof RegExp | ||
) { | ||
match = { | ||
@@ -89,13 +102,26 @@ path: match, | ||
function matchPath(path: string, pattern: string | RegExp): string | undefined { | ||
function matchPath( | ||
path: string, | ||
pattern: string | string[] | RegExp, | ||
): string | undefined { | ||
if (typeof pattern === 'string') { | ||
// E.g. pattern '/app' matches both '/app' and '/app/workbench', not not | ||
// '/app-workbench'. | ||
let matched = | ||
path.startsWith(pattern) && | ||
(path.length === pattern.length || | ||
pattern[pattern.length - 1] === '/' || | ||
path[pattern.length] === '/'); | ||
pattern = [pattern]; | ||
} | ||
return matched ? pattern : undefined; | ||
if (Array.isArray(pattern)) { | ||
for (let stringPattern of pattern) { | ||
// E.g. pattern '/app' matches both '/app' and '/app/workbench', not not | ||
// '/app-workbench'. | ||
let matched = | ||
path.startsWith(stringPattern) && | ||
(path.length === stringPattern.length || | ||
stringPattern[stringPattern.length - 1] === '/' || | ||
path[stringPattern.length] === '/'); | ||
if (matched) { | ||
return stringPattern; | ||
} | ||
} | ||
return undefined; | ||
} else { | ||
@@ -110,9 +136,7 @@ let groups = pattern.exec(path); | ||
function matchHeaders( | ||
headerDict: Dict<string | string[]>, | ||
headers: IncomingHttpHeaders, | ||
headerPatternDict: Dict<string | RegExp | boolean>, | ||
): boolean { | ||
for (let [name, valuePattern] of Object.entries(headerPatternDict)) { | ||
let value = hasOwnProperty.call(headerDict, name) | ||
? headerDict[name] | ||
: undefined; | ||
let value = hasOwnProperty.call(headers, name) ? headers[name] : undefined; | ||
@@ -119,0 +143,0 @@ if (typeof valuePattern === 'boolean') { |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
47443
929