yxorp 0.1.4 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -21
- package/README.md +369 -81
- package/bin/yxorp.js +13 -22
- package/dist/index.d.ts +1 -0
- package/dist/index.js +59 -0
- package/dist/middleware/bootstrap.middleware.d.ts +15 -0
- package/dist/middleware/bootstrap.middleware.js +63 -0
- package/dist/middleware/mock.middleware.d.ts +9 -0
- package/dist/middleware/mock.middleware.js +54 -0
- package/dist/middleware/proxy.middleware.d.ts +15 -0
- package/dist/middleware/proxy.middleware.js +26 -0
- package/dist/middleware/proxyRes.middleware.d.ts +9 -0
- package/dist/middleware/proxyRes.middleware.js +28 -0
- package/dist/middleware/rawBody.middleware.d.ts +6 -0
- package/dist/middleware/rawBody.middleware.js +24 -0
- package/dist/middleware/rewrite.middleware.d.ts +9 -0
- package/dist/middleware/rewrite.middleware.js +54 -0
- package/dist/middleware/static.middleware.d.ts +12 -0
- package/dist/middleware/static.middleware.js +85 -0
- package/dist/services/config-resolver.d.ts +6 -0
- package/dist/services/config-resolver.js +45 -0
- package/dist/services/config.service.d.ts +6 -0
- package/dist/services/config.service.js +12 -0
- package/dist/services/http-proxy.service.d.ts +14 -0
- package/dist/services/http-proxy.service.js +19 -0
- package/dist/services/http-server.service.d.ts +15 -0
- package/dist/services/http-server.service.js +23 -0
- package/dist/services/logger.service.d.ts +7 -0
- package/dist/services/logger.service.js +17 -0
- package/dist/services/pipeline.service.d.ts +8 -0
- package/dist/services/pipeline.service.js +29 -0
- package/dist/services/rules-matchers/mock-rules-matcher.service.d.ts +8 -0
- package/dist/services/rules-matchers/mock-rules-matcher.service.js +35 -0
- package/dist/services/rules-matchers/remote-rules-matcher.service.d.ts +8 -0
- package/dist/services/rules-matchers/remote-rules-matcher.service.js +51 -0
- package/dist/services/rules-matchers/rewrite-rules-matcher.service.d.ts +8 -0
- package/dist/services/rules-matchers/rewrite-rules-matcher.service.js +35 -0
- package/dist/services/yxorp-server.service.d.ts +10 -0
- package/dist/services/yxorp-server.service.js +49 -0
- package/dist/types/yxorp-config.d.ts +54 -0
- package/dist/types/yxorp-config.js +2 -0
- package/package.json +45 -38
- package/.editorconfig +0 -16
- package/src/index.ts +0 -48
- package/src/middleware/bootstrap.middleware.ts +0 -87
- package/src/middleware/mock.middleware.ts +0 -61
- package/src/middleware/proxy.middleware.ts +0 -42
- package/src/middleware/proxyRes.middleware.ts +0 -34
- package/src/middleware/rawBody.middleware.ts +0 -22
- package/src/middleware/rewrite.middleware.ts +0 -64
- package/src/middleware/static.middleware.ts +0 -113
- package/src/services/config.service.ts +0 -26
- package/src/services/http-proxy.service.ts +0 -25
- package/src/services/http-server.service.ts +0 -24
- package/src/services/logger.service.ts +0 -21
- package/src/services/pipeline.service.ts +0 -44
- package/src/services/rules-matchers/mock-rules-matcher.service.ts +0 -48
- package/src/services/rules-matchers/remote-rules-matcher.service.ts +0 -49
- package/src/services/rules-matchers/rewrite-rules-matcher.service.ts +0 -48
- package/src/services/yxorp-server.service.ts +0 -73
- package/src/types/http.d.ts +0 -10
- package/src/types/yxorp-config.ts +0 -67
- package/tsconfig.json +0 -21
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { Config } from '../config.service';
|
|
2
|
+
import { RewriteRule } from '../../types/yxorp-config';
|
|
3
|
+
export declare class RewriteRulesMatcher {
|
|
4
|
+
private config;
|
|
5
|
+
constructor(config: Config);
|
|
6
|
+
match(url: string, method: string): RewriteRule | undefined;
|
|
7
|
+
params(url: string, rewriteRule: RewriteRule): Object | undefined;
|
|
8
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.RewriteRulesMatcher = void 0;
|
|
4
|
+
const path_to_regexp_1 = require("path-to-regexp");
|
|
5
|
+
class RewriteRulesMatcher {
|
|
6
|
+
constructor(config) {
|
|
7
|
+
this.config = config;
|
|
8
|
+
}
|
|
9
|
+
match(url, method) {
|
|
10
|
+
const rewriteRules = this.config.get().rewriteRules || [];
|
|
11
|
+
for (let rewriteRule of rewriteRules) {
|
|
12
|
+
if (rewriteRule.disable) {
|
|
13
|
+
continue;
|
|
14
|
+
}
|
|
15
|
+
if (rewriteRule.method.toLowerCase() !== method.toLowerCase()) {
|
|
16
|
+
continue;
|
|
17
|
+
}
|
|
18
|
+
const matchResult = (0, path_to_regexp_1.match)(rewriteRule.path, {
|
|
19
|
+
decode: decodeURIComponent,
|
|
20
|
+
})(url);
|
|
21
|
+
if (matchResult) {
|
|
22
|
+
return rewriteRule;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
params(url, rewriteRule) {
|
|
27
|
+
const matchResult = (0, path_to_regexp_1.match)(rewriteRule.path, {
|
|
28
|
+
decode: decodeURIComponent,
|
|
29
|
+
})(url);
|
|
30
|
+
if (matchResult) {
|
|
31
|
+
return matchResult.params;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
exports.RewriteRulesMatcher = RewriteRulesMatcher;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { HttpServer } from './http-server.service';
|
|
2
|
+
import { RemoteRulesMatcher } from './rules-matchers/remote-rules-matcher.service';
|
|
3
|
+
import { RewriteRulesMatcher } from './rules-matchers/rewrite-rules-matcher.service';
|
|
4
|
+
import { MockRulesMatcher } from './rules-matchers/mock-rules-matcher.service';
|
|
5
|
+
import { Config } from './config.service';
|
|
6
|
+
import { LoggerService } from './logger.service';
|
|
7
|
+
export declare function createServer(config: Config, logger: LoggerService, rewriteRulesMatcher: RewriteRulesMatcher, mockRulesMatcher: MockRulesMatcher, remoteRulesMatcher: RemoteRulesMatcher): {
|
|
8
|
+
server: HttpServer;
|
|
9
|
+
listen: (port: string | number, callback?: () => void) => void;
|
|
10
|
+
};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createServer = void 0;
|
|
4
|
+
const http_server_service_1 = require("./http-server.service");
|
|
5
|
+
const http_proxy_service_1 = require("./http-proxy.service");
|
|
6
|
+
const pipeline_service_1 = require("./pipeline.service");
|
|
7
|
+
const bootstrap_middleware_1 = require("../middleware/bootstrap.middleware");
|
|
8
|
+
const rawBody_middleware_1 = require("../middleware/rawBody.middleware");
|
|
9
|
+
const proxyRes_middleware_1 = require("../middleware/proxyRes.middleware");
|
|
10
|
+
const rewrite_middleware_1 = require("../middleware/rewrite.middleware");
|
|
11
|
+
const proxy_middleware_1 = require("../middleware/proxy.middleware");
|
|
12
|
+
const mock_middleware_1 = require("../middleware/mock.middleware");
|
|
13
|
+
const static_middleware_1 = require("../middleware/static.middleware");
|
|
14
|
+
function createServer(config, logger, rewriteRulesMatcher, mockRulesMatcher, remoteRulesMatcher) {
|
|
15
|
+
// 1. Proxy pipeline (outgoing responses) — no deps on proxy/server
|
|
16
|
+
const proxyPipeline = new pipeline_service_1.Pipeline();
|
|
17
|
+
proxyPipeline.use(new rawBody_middleware_1.RawBodyMiddleware(), new rewrite_middleware_1.RewriteMiddleware(logger), new proxyRes_middleware_1.ProxyResMiddleware(logger));
|
|
18
|
+
// 2. HttpProxy wraps the proxy pipeline
|
|
19
|
+
const proxy = new http_proxy_service_1.HttpProxy(proxyPipeline);
|
|
20
|
+
// 3. Server pipeline (incoming requests)
|
|
21
|
+
const serverPipeline = new pipeline_service_1.Pipeline();
|
|
22
|
+
serverPipeline.use(new static_middleware_1.StaticMiddleware(config, logger), new bootstrap_middleware_1.BootstrapMiddleware(rewriteRulesMatcher, mockRulesMatcher, logger), new mock_middleware_1.MockMiddleware(logger), new proxy_middleware_1.ProxyMiddleware(proxy, remoteRulesMatcher, config, logger));
|
|
23
|
+
// 4. HttpServer wraps the server pipeline
|
|
24
|
+
const server = new http_server_service_1.HttpServer(serverPipeline);
|
|
25
|
+
// 5. Attach proxy pipeline to proxyRes event
|
|
26
|
+
proxy.on('proxyRes', ((proxyRes, req, res) => {
|
|
27
|
+
proxy.execute(proxyRes, req, res);
|
|
28
|
+
}));
|
|
29
|
+
// 6. WebSocket upgrade handling
|
|
30
|
+
server.addListener('upgrade', (req, socket, head) => {
|
|
31
|
+
const url = req.url || '';
|
|
32
|
+
const proxyOptions = config.get().proxyOptions;
|
|
33
|
+
const remoteRule = remoteRulesMatcher.match(url, true);
|
|
34
|
+
const target = remoteRule
|
|
35
|
+
? remoteRulesMatcher.toPath(url, remoteRule)
|
|
36
|
+
: undefined;
|
|
37
|
+
const options = {
|
|
38
|
+
...proxyOptions,
|
|
39
|
+
prependPath: !target,
|
|
40
|
+
target: target || proxyOptions.target,
|
|
41
|
+
};
|
|
42
|
+
proxy.ws(req, socket, options, head).catch((error) => logger.info(error));
|
|
43
|
+
});
|
|
44
|
+
const listen = (port, callback) => {
|
|
45
|
+
server.listen(port, callback);
|
|
46
|
+
};
|
|
47
|
+
return { server, listen };
|
|
48
|
+
}
|
|
49
|
+
exports.createServer = createServer;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
export interface YxorpConfig extends ConfigFile {
|
|
2
|
+
proxyOptions: Record<string, any>;
|
|
3
|
+
}
|
|
4
|
+
export interface ConfigFile {
|
|
5
|
+
target: string;
|
|
6
|
+
proxyPort: string | number;
|
|
7
|
+
scripts?: string[];
|
|
8
|
+
proxyHeaders?: Record<string, string>;
|
|
9
|
+
remoteRules?: RemoteRule[];
|
|
10
|
+
staticRules?: StaticRule[];
|
|
11
|
+
mockRules?: MockRule[];
|
|
12
|
+
rewriteRules?: RewriteRule[];
|
|
13
|
+
}
|
|
14
|
+
export interface RemoteRule {
|
|
15
|
+
path: string;
|
|
16
|
+
target: string;
|
|
17
|
+
ws?: boolean;
|
|
18
|
+
disable?: boolean;
|
|
19
|
+
}
|
|
20
|
+
export type StaticRule = {
|
|
21
|
+
path: string;
|
|
22
|
+
directory: string;
|
|
23
|
+
caseInsensitive?: boolean;
|
|
24
|
+
directoryIndex?: string;
|
|
25
|
+
disable?: boolean;
|
|
26
|
+
};
|
|
27
|
+
export type MockFileRule = {
|
|
28
|
+
method: string;
|
|
29
|
+
path: string;
|
|
30
|
+
file: string;
|
|
31
|
+
statusCode?: number;
|
|
32
|
+
disable?: boolean;
|
|
33
|
+
};
|
|
34
|
+
export type MockScriptRule = {
|
|
35
|
+
method: string;
|
|
36
|
+
path: string;
|
|
37
|
+
script: string;
|
|
38
|
+
disable?: boolean;
|
|
39
|
+
};
|
|
40
|
+
export type MockRule = MockFileRule | MockScriptRule;
|
|
41
|
+
export type RewriteFileRule = {
|
|
42
|
+
method: string;
|
|
43
|
+
path: string;
|
|
44
|
+
file: string;
|
|
45
|
+
statusCode?: number;
|
|
46
|
+
disable?: boolean;
|
|
47
|
+
};
|
|
48
|
+
export type RewriteScriptRule = {
|
|
49
|
+
method: string;
|
|
50
|
+
path: string;
|
|
51
|
+
script: string;
|
|
52
|
+
disable?: boolean;
|
|
53
|
+
};
|
|
54
|
+
export type RewriteRule = RewriteFileRule | RewriteScriptRule;
|
package/package.json
CHANGED
|
@@ -1,38 +1,45 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "yxorp",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "
|
|
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
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "yxorp",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "A local reverse proxy for rewriting, mocking, and debugging API responses.",
|
|
5
|
+
"files": [
|
|
6
|
+
"dist/",
|
|
7
|
+
"bin/",
|
|
8
|
+
"LICENSE",
|
|
9
|
+
"README.md"
|
|
10
|
+
],
|
|
11
|
+
"main": "dist/index.js",
|
|
12
|
+
"bin": {
|
|
13
|
+
"yxorp": "bin/yxorp.js"
|
|
14
|
+
},
|
|
15
|
+
"scripts": {
|
|
16
|
+
"start": "nodemon src/index.ts",
|
|
17
|
+
"build": "tsc -p tsconfig.build.json",
|
|
18
|
+
"prepublishOnly": "npm run build",
|
|
19
|
+
"test": "vitest run",
|
|
20
|
+
"test:watch": "vitest"
|
|
21
|
+
},
|
|
22
|
+
"author": "Kirill Suntsov",
|
|
23
|
+
"license": "MIT",
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"http-encoding": "^2.2.0",
|
|
26
|
+
"httpxy": "^0.5.3",
|
|
27
|
+
"mime": "^3.0.0",
|
|
28
|
+
"path-to-regexp": "^8.4.2",
|
|
29
|
+
"qs": "^6.15.2",
|
|
30
|
+
"tslib": "^2.8.1",
|
|
31
|
+
"winston": "^3.19.0"
|
|
32
|
+
},
|
|
33
|
+
"devDependencies": {
|
|
34
|
+
"@types/mime": "^3.0.4",
|
|
35
|
+
"@types/node": "^20.19.41",
|
|
36
|
+
"@types/qs": "^6.15.1",
|
|
37
|
+
"nodemon": "^3.0.1",
|
|
38
|
+
"ts-node": "^10.9.1",
|
|
39
|
+
"typescript": "^5.2.2",
|
|
40
|
+
"vitest": "^4.1.7"
|
|
41
|
+
},
|
|
42
|
+
"engines": {
|
|
43
|
+
"node": ">= 18"
|
|
44
|
+
}
|
|
45
|
+
}
|
package/.editorconfig
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
# Editor configuration, see http://editorconfig.org
|
|
2
|
-
root = true
|
|
3
|
-
|
|
4
|
-
[*]
|
|
5
|
-
charset = utf-8
|
|
6
|
-
indent_style = space
|
|
7
|
-
indent_size = 2
|
|
8
|
-
insert_final_newline = true
|
|
9
|
-
trim_trailing_whitespace = true
|
|
10
|
-
|
|
11
|
-
[*.ts]
|
|
12
|
-
quote_type = single
|
|
13
|
-
|
|
14
|
-
[*.md]
|
|
15
|
-
max_line_length = off
|
|
16
|
-
trim_trailing_whitespace = false
|
package/src/index.ts
DELETED
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
import 'reflect-metadata';
|
|
2
|
-
|
|
3
|
-
import fs from 'fs';
|
|
4
|
-
import path from 'path';
|
|
5
|
-
import { Container } from 'typedi';
|
|
6
|
-
import { ServerOptions } from 'http-proxy';
|
|
7
|
-
import { YxorpServer } from './services/yxorp-server.service';
|
|
8
|
-
import { ConfigFile } from './types/yxorp-config';
|
|
9
|
-
import { ProxyConfigToken } from './services/config.service';
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
let config: ConfigFile;
|
|
13
|
-
|
|
14
|
-
try {
|
|
15
|
-
config = JSON.parse(fs.readFileSync('./yxorp.json').toString()) as ConfigFile;
|
|
16
|
-
} catch(e) {
|
|
17
|
-
console.error(e);
|
|
18
|
-
throw 'Can\'t read yxorp.json'
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
globalThis.require = require;
|
|
22
|
-
|
|
23
|
-
const proxyOptions: ServerOptions = {
|
|
24
|
-
target: config.target,
|
|
25
|
-
changeOrigin: true,
|
|
26
|
-
followRedirects: true,
|
|
27
|
-
secure: false,
|
|
28
|
-
localAddress: '0.0.0.0',
|
|
29
|
-
ws: true,
|
|
30
|
-
selfHandleResponse: true,
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
const proxyConfig = {
|
|
34
|
-
...config,
|
|
35
|
-
proxyOptions,
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
config?.scripts?.forEach(script => {
|
|
39
|
-
require(path.join(process.cwd(), script));
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
Container.set(ProxyConfigToken, proxyConfig);
|
|
43
|
-
|
|
44
|
-
const server = Container.get(YxorpServer);
|
|
45
|
-
|
|
46
|
-
server.listen(config.proxyPort, () => {
|
|
47
|
-
console.log(`Yxorp server started successfully on http://localhost:${config.proxyPort}`);
|
|
48
|
-
});
|
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
import { IncomingMessage } from 'http';
|
|
2
|
-
import { Service } from 'typedi';
|
|
3
|
-
import qs from 'qs';
|
|
4
|
-
import { Middleware } from '../services/pipeline.service';
|
|
5
|
-
import { RewriteRulesMatcher } from '../services/rules-matchers/rewrite-rules-matcher.service';
|
|
6
|
-
import { MockRulesMatcher } from '../services/rules-matchers/mock-rules-matcher.service';
|
|
7
|
-
import { LoggerService } from '../services/logger.service';
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
@Service({
|
|
11
|
-
global: true
|
|
12
|
-
})
|
|
13
|
-
export class BootstrapMiddleware<T extends any[]> implements Middleware<T> {
|
|
14
|
-
constructor(
|
|
15
|
-
private rewriteRulesMatcher: RewriteRulesMatcher,
|
|
16
|
-
private mockRulesMatcher: MockRulesMatcher,
|
|
17
|
-
private logger: LoggerService,
|
|
18
|
-
) {
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
public use(...args: [...T, () => void]): void {
|
|
22
|
-
const req: IncomingMessage = args[0] as IncomingMessage;
|
|
23
|
-
const next: () => void = args[args.length - 1];
|
|
24
|
-
|
|
25
|
-
try {
|
|
26
|
-
this.setRewriteRule(req);
|
|
27
|
-
this.setMockRule(req);
|
|
28
|
-
this.setQueryParams(req);
|
|
29
|
-
|
|
30
|
-
next();
|
|
31
|
-
} catch (e) {
|
|
32
|
-
this.logger.error(e);
|
|
33
|
-
next();
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
private setRewriteRule(req: IncomingMessage): void {
|
|
38
|
-
if (!req.url || !req.method) {
|
|
39
|
-
return;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
const rewriteRule = this.rewriteRulesMatcher.match(req.url, req.method);
|
|
43
|
-
|
|
44
|
-
if (rewriteRule) {
|
|
45
|
-
req.rewriteRule = rewriteRule;
|
|
46
|
-
|
|
47
|
-
const rewriteRuleParams = this.rewriteRulesMatcher.params(req.url, rewriteRule);
|
|
48
|
-
|
|
49
|
-
if (rewriteRuleParams) {
|
|
50
|
-
req.rewriteRuleParams = rewriteRuleParams || {};
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
private setMockRule(req: IncomingMessage): void {
|
|
56
|
-
if (!req.url || !req.method) {
|
|
57
|
-
return;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
const mockRule = this.mockRulesMatcher.match(req.url, req.method);
|
|
61
|
-
|
|
62
|
-
if (mockRule) {
|
|
63
|
-
req.mockRule = mockRule;
|
|
64
|
-
|
|
65
|
-
const mockRuleParams = this.mockRulesMatcher.params(req.url, mockRule);
|
|
66
|
-
|
|
67
|
-
if (mockRuleParams) {
|
|
68
|
-
req.mockRuleParams = mockRuleParams || {};
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
private setQueryParams(req: IncomingMessage): void {
|
|
74
|
-
if (!req.url) {
|
|
75
|
-
return;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
const url = new URL(req.url, 'http://fake.com');
|
|
79
|
-
const query = qs.parse(url.search, {
|
|
80
|
-
ignoreQueryPrefix: true,
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
req.query = query;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
}
|
|
87
|
-
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
import { Service } from 'typedi';
|
|
2
|
-
import { ServerResponse, IncomingMessage } from 'http';
|
|
3
|
-
import mime from 'mime';
|
|
4
|
-
import fs from 'fs/promises';
|
|
5
|
-
import path from 'path';
|
|
6
|
-
import { Middleware } from '../services/pipeline.service';
|
|
7
|
-
import { LoggerService } from '../services/logger.service';
|
|
8
|
-
|
|
9
|
-
@Service({
|
|
10
|
-
global: true
|
|
11
|
-
})
|
|
12
|
-
export class MockMiddleware implements Middleware<[req: IncomingMessage, res: ServerResponse]> {
|
|
13
|
-
constructor(
|
|
14
|
-
private logger: LoggerService,
|
|
15
|
-
) {
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
public async use(req: IncomingMessage, res: ServerResponse, next: () => void): Promise<void> {
|
|
19
|
-
try {
|
|
20
|
-
const mockRule = req?.mockRule;
|
|
21
|
-
|
|
22
|
-
if (!mockRule) {
|
|
23
|
-
next();
|
|
24
|
-
return;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
if ('script' in mockRule) {
|
|
28
|
-
const file = await fs.readFile(path.resolve(mockRule.script));
|
|
29
|
-
|
|
30
|
-
const func = new Function('req, res', file.toString());
|
|
31
|
-
await func(req, res);
|
|
32
|
-
|
|
33
|
-
this.logger.info(`[MOCK BY SCRIPT] ${res.statusCode || 200} ${req.url}`);
|
|
34
|
-
return;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
if ('file' in mockRule) {
|
|
38
|
-
const file = await fs.readFile(path.resolve(mockRule.file));
|
|
39
|
-
const mimeType = mime.getType(path.resolve(mockRule.file));
|
|
40
|
-
|
|
41
|
-
res.statusCode = mockRule.statusCode || res.statusCode;
|
|
42
|
-
|
|
43
|
-
if (mimeType) {
|
|
44
|
-
res.setHeader('content-type', mimeType);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
res.setHeader('content-length', file.length);
|
|
48
|
-
res.end(file);
|
|
49
|
-
|
|
50
|
-
this.logger.info(`[MOCK BY FILE] ${res.statusCode} ${req.url}`);
|
|
51
|
-
return;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
next();
|
|
55
|
-
} catch (e) {
|
|
56
|
-
this.logger.error(e);
|
|
57
|
-
next();
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
}
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
import { Service } from 'typedi';
|
|
2
|
-
import { IncomingMessage, ServerResponse } from 'http';
|
|
3
|
-
import { HttpProxy } from '../services/http-proxy.service';
|
|
4
|
-
import { RemoteRulesMatcher } from '../services/rules-matchers/remote-rules-matcher.service';
|
|
5
|
-
import { Config } from '../services/config.service';
|
|
6
|
-
import { Middleware } from '../services/pipeline.service';
|
|
7
|
-
import { LoggerService } from '../services/logger.service';
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
@Service({
|
|
11
|
-
global: true
|
|
12
|
-
})
|
|
13
|
-
export class ProxyMiddleware implements Middleware<[req: IncomingMessage, res: ServerResponse]> {
|
|
14
|
-
constructor(
|
|
15
|
-
private httpProxy: HttpProxy,
|
|
16
|
-
private remoteRulesMatcher: RemoteRulesMatcher,
|
|
17
|
-
private config: Config,
|
|
18
|
-
private logger: LoggerService,
|
|
19
|
-
) {
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
public use(req: IncomingMessage, res: ServerResponse) {
|
|
23
|
-
const url = req.url || '';
|
|
24
|
-
|
|
25
|
-
const proxyOptions = this.config.get().proxyOptions;
|
|
26
|
-
const remoteRule = this.remoteRulesMatcher.match(url);
|
|
27
|
-
const target = remoteRule
|
|
28
|
-
? this.remoteRulesMatcher.toPath(url, remoteRule)
|
|
29
|
-
: undefined;
|
|
30
|
-
|
|
31
|
-
const options = {
|
|
32
|
-
...proxyOptions,
|
|
33
|
-
prependPath: !target,
|
|
34
|
-
target: target || proxyOptions.target,
|
|
35
|
-
};
|
|
36
|
-
|
|
37
|
-
this.httpProxy.web(
|
|
38
|
-
req, res, options, (error) => this.logger.error(error)
|
|
39
|
-
);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
}
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import { Service } from 'typedi';
|
|
2
|
-
import { IncomingMessage, ServerResponse } from 'http';
|
|
3
|
-
import { Middleware } from '../services/pipeline.service';
|
|
4
|
-
import { LoggerService } from '../services/logger.service';
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
@Service({
|
|
8
|
-
global: true
|
|
9
|
-
})
|
|
10
|
-
export class ProxyResMiddleware implements Middleware<[proxyRes: IncomingMessage, req: IncomingMessage, res: ServerResponse]> {
|
|
11
|
-
constructor(
|
|
12
|
-
private logger: LoggerService,
|
|
13
|
-
) {
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
public use(proxyRes: IncomingMessage, req: IncomingMessage, res: ServerResponse): void {
|
|
17
|
-
try {
|
|
18
|
-
for (let key in proxyRes.headers) {
|
|
19
|
-
res.setHeader(key, proxyRes.headers[key] as any);
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
res.statusCode = proxyRes.statusCode as number;
|
|
23
|
-
res.statusMessage = proxyRes.statusMessage as string;
|
|
24
|
-
|
|
25
|
-
const response: Buffer = proxyRes.rawBody || Buffer.from('');
|
|
26
|
-
|
|
27
|
-
res.setHeader('content-length', response.length);
|
|
28
|
-
res.end(response);
|
|
29
|
-
} catch(e) {
|
|
30
|
-
this.logger.error(e);
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
}
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import { Service } from 'typedi';
|
|
2
|
-
import { IncomingMessage, ServerResponse } from 'http';
|
|
3
|
-
import { Middleware } from '../services/pipeline.service';
|
|
4
|
-
|
|
5
|
-
@Service({
|
|
6
|
-
global: true
|
|
7
|
-
})
|
|
8
|
-
export class RawBodyMiddleware implements Middleware<[proxyRes: IncomingMessage, req: IncomingMessage, res: ServerResponse]> {
|
|
9
|
-
public use(proxyRes: IncomingMessage, req: IncomingMessage, res: ServerResponse, next: () => void): void {
|
|
10
|
-
const body: Uint8Array[] = [];
|
|
11
|
-
|
|
12
|
-
proxyRes.on('data', (chunk: Uint8Array) => {
|
|
13
|
-
body.push(chunk);
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
proxyRes.on('end', () => {
|
|
17
|
-
proxyRes.rawBody = Buffer.concat(body);
|
|
18
|
-
next();
|
|
19
|
-
});
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
}
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
import { Service } from 'typedi';
|
|
2
|
-
import { IncomingMessage, ServerResponse } from 'http';
|
|
3
|
-
import { SUPPORTED_ENCODING, encodeBuffer, decodeBuffer } from 'http-encoding'
|
|
4
|
-
import fs from 'fs/promises';
|
|
5
|
-
import path from 'path';
|
|
6
|
-
import { Middleware } from '../services/pipeline.service';
|
|
7
|
-
import { LoggerService } from '../services/logger.service';
|
|
8
|
-
|
|
9
|
-
type RewriteFunction = (body: Buffer, proxyRes: IncomingMessage, req: IncomingMessage, res: ServerResponse) => Promise<Buffer> | Buffer;
|
|
10
|
-
|
|
11
|
-
@Service({
|
|
12
|
-
global: true
|
|
13
|
-
})
|
|
14
|
-
export class RewriteMiddleware implements Middleware<[proxyRes: IncomingMessage, req: IncomingMessage, res: ServerResponse]> {
|
|
15
|
-
constructor(
|
|
16
|
-
private logger: LoggerService,
|
|
17
|
-
) {
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
public async use(proxyRes: IncomingMessage, req: IncomingMessage, res: ServerResponse, next: () => void): Promise<void> {
|
|
21
|
-
const rewriteRule = req?.rewriteRule;
|
|
22
|
-
|
|
23
|
-
try {
|
|
24
|
-
if (!rewriteRule) {
|
|
25
|
-
next();
|
|
26
|
-
return;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
const encoding = proxyRes.headers['content-encoding'] as SUPPORTED_ENCODING;
|
|
30
|
-
const response: Buffer = proxyRes.rawBody || Buffer.from('');
|
|
31
|
-
|
|
32
|
-
if ('script' in rewriteRule) {
|
|
33
|
-
const file = await fs.readFile(path.resolve(rewriteRule.script));
|
|
34
|
-
const encodedResponse = await decodeBuffer(response, encoding);
|
|
35
|
-
const func = new Function('body, proxyRes, req, res', 'var result;' + file + ';return result;') as RewriteFunction;
|
|
36
|
-
|
|
37
|
-
const rewritedResponse: Buffer = await func(encodedResponse, proxyRes, req, res);
|
|
38
|
-
proxyRes.rawBody = await encodeBuffer(rewritedResponse, encoding);
|
|
39
|
-
|
|
40
|
-
this.logger.info(`[REWRITE BY SCRIPT] ${proxyRes.statusCode} ${req.url}`);
|
|
41
|
-
|
|
42
|
-
next();
|
|
43
|
-
return;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
if ('file' in rewriteRule) {
|
|
47
|
-
const file = await fs.readFile(path.resolve(rewriteRule.file));
|
|
48
|
-
proxyRes.rawBody = await encodeBuffer(file, encoding);
|
|
49
|
-
res.statusCode = rewriteRule.statusCode || res.statusCode;
|
|
50
|
-
|
|
51
|
-
this.logger.info(`[REWRITE BY FILE] ${proxyRes.statusCode} ${req.url}`);
|
|
52
|
-
|
|
53
|
-
next();
|
|
54
|
-
return;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
next();
|
|
58
|
-
} catch (e) {
|
|
59
|
-
this.logger.error(e);
|
|
60
|
-
next();
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
}
|