From 5ee9c5b516e5409bc05bb1dc1ec9fed845ffcf43 Mon Sep 17 00:00:00 2001 From: Maxime LUCE Date: Wed, 25 Sep 2019 22:11:08 +0200 Subject: [PATCH] Add anyproxy typings (#38599) --- types/anyproxy/anyproxy-tests.ts | 62 ++++++++ types/anyproxy/index.d.ts | 245 +++++++++++++++++++++++++++++++ types/anyproxy/tsconfig.json | 23 +++ types/anyproxy/tslint.json | 4 + 4 files changed, 334 insertions(+) create mode 100644 types/anyproxy/anyproxy-tests.ts create mode 100644 types/anyproxy/index.d.ts create mode 100644 types/anyproxy/tsconfig.json create mode 100644 types/anyproxy/tslint.json diff --git a/types/anyproxy/anyproxy-tests.ts b/types/anyproxy/anyproxy-tests.ts new file mode 100644 index 0000000000..35f4199ede --- /dev/null +++ b/types/anyproxy/anyproxy-tests.ts @@ -0,0 +1,62 @@ +import * as path from 'path'; + +import * as AnyProxy from 'anyproxy'; + +// Code took from http://anyproxy.io/en/#sample + +// Initialize certifiates +if (!AnyProxy.utils.certMgr.ifRootCAFileExists()) { + AnyProxy.utils.certMgr.generateRootCA((error, keyPath) => { + // let users to trust this CA before using proxy + if (!error) { + const certDir = path.dirname(keyPath); + console.log('The cert is generated at', certDir); + } else { + console.error('error when generating rootCA', error); + } + }); +} + +// Initialize server +const proxyServer = new AnyProxy.ProxyServer({ + port: 8001, + rule: { + summary: 'My Custom Rule', + + beforeSendResponse(requestDetail, responseDetail) { + if (requestDetail.url === 'http://httpbin.org/user-agent') { + const newResponse = responseDetail.response; + newResponse.body += '- AnyProxy Hacked!'; + + return new Promise(resolve => { + setTimeout(() => { + // delay + resolve({ response: newResponse }); + }, 5000); + }); + } + }, + }, + webInterface: { + enable: true, + webPort: 8002, + }, + throttle: 10000, + forceProxyHttps: false, + wsIntercept: false, + silent: false, +}); + +proxyServer.on('ready', () => { + console.log('Proxy Server is ready'); +}); + +proxyServer.on('error', e => { + console.log('Proxy Server error:', e); +}); + +// Start server +proxyServer.start(); + +// When finished +proxyServer.close(); diff --git a/types/anyproxy/index.d.ts b/types/anyproxy/index.d.ts new file mode 100644 index 0000000000..8da95a49f3 --- /dev/null +++ b/types/anyproxy/index.d.ts @@ -0,0 +1,245 @@ +// Type definitions for anyproxy 4.1 +// Project: https://github.com/alibaba/anyproxy +// Definitions by: Maxime LUCE +// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped + +/// + +import { + IncomingMessage, + ServerResponse, + RequestOptions +} from "http"; + +import { + EventEmitter +} from "events"; + +import { + Socket +} from "net"; + +export type MaybePromise = T | Promise; + +export interface ProxyOptions { + /** Port number of proxy server */ + port: string | number; + /** Your rule module */ + rule?: string | RuleModule; + /** Throttle in kb/s, unlimited for default */ + throttle?: number; + /** Type of the proxy server, could be 'http' or 'https'. */ + type?: "http" | "https"; + /** Host name of the proxy server, required when this is an https proxy */ + hostname?: string; + /** Force intercept all https request, default to false */ + forceProxyHttps?: boolean; + /** If keep silent in console, false for default false */ + silent?: boolean; + /** If ignore certificate error in request, default to false */ + dangerouslyIgnoreUnauthorized?: boolean; + /** Whether to intercept websocket, default to false */ + wsIntercept?: boolean; + /** Config for web interface */ + webInterface?: WebInterfaceOptions; + /** Recorder to use */ + recorder?: ProxyRecorder; +} + +export interface WebInterfaceOptions { + /** If enable web interface, default to false */ + enable?: boolean; + /** Port number for web interface */ + webPort?: number; +} + +export interface RuleModule { + /** Introduction of this rule file. AnyProxy will read this field and give some tip to user. */ + summary?: string; + /** Before sending request to server, AnyProxy will call beforeSendRequest with param requestDetail. */ + beforeSendRequest?(requestDetail: RequestDetail): MaybePromise; + /** Before sending response to client, AnyProxy will call beforeSendResponse with param requestDetail responseDetail. */ + beforeSendResponse?(requestDetail: RequestDetail, responseDetail: ResponseDetail): MaybePromise; + /** + * When receiving https request, AnyProxy will call beforeDealHttpsRequest with param requestDetail. + * If configed with forceProxyHttps in launching, AnyProxy will skip calling this method. + * Only by returning true, AnyProxy will try to replace the certificate and intercept the https request. + */ + beforeDealHttpsRequest?(requestDetail: BeforeDealHttpsRequestDetail): MaybePromise; + /** + * AnyProxy will call this method when an error happened in request handling. + * Errors usually are issued during requesting, e.g. DNS failure, request timeout. + */ + onError?(requestDetail: RequestDetail, err: Error): MaybePromise; + /** AnyProxy will call this method when failed to connect target server in https request. */ + onConnectError?(requestDetail: RequestDetail, err: Error): MaybePromise; +} + +// TypeScript Version: 2.2 +export interface BeforeSendRequestResult extends Partial { + response?: Partial; +} + +export interface BeforeSendResponseResult { + response: Partial; +} + +export interface BeforeDealHttpsRequestDetail { + host: string; + _req: IncomingMessage; +} + +export interface RequestDetail { + protocol: string; + url: string; + requestOptions: RequestOptions; + requestData: any; + _req: IncomingMessage; +} + +export interface ResponseDetail { + response: Response; + _res: ServerResponse; +} + +export interface Response { + statusCode: number; + header: Record; + body: any; +} + +export interface RecorderInfo { + _id: number; + id: number; + url: string; + host: string; + path: string; + method: string; + + // req + reqHeader: Record; + startTime: number; + reqBody: any; + protocol: string; + + // res + statusCode: number | string; + endTime: number | string; + resHeader: Record | string; + length: number | string; + mime: string; + duration: number | string; +} + +export class ProxyCore extends EventEmitter { + /** + * Creates an instance of ProxyCore. + * @param config - configs + */ + constructor(config?: ProxyOptions); + + /** + * Manage all created socket + * for each new socket, we put them to a map; + * if the socket is closed itself, we remove it from the map + * when the `close` method is called, we'll close the sockes before the server closed + * + * @param socket the http socket that is creating + */ + handleExistConnections(socket: Socket): void; + + /** Start the proxy server */ + start(): this; + + /** Close the proxy server */ + close(): Promise; +} + +export class ProxyServer extends ProxyCore { + constructor(config?: ProxyOptions); + + /** Emit when proxy server is ready */ + on(eventName: "ready", listener: () => void): this; + /** Emit when error happened inside proxy server */ + on(eventName: "error", listener: (err: Error) => void): this; + + /** Start proxy server */ + start(): this; + /** Close proxy server */ + close(): Promise; +} + +export class ProxyRecorder extends EventEmitter { + // TypeScript Version: 2.2 + constructor(config?: object); + + emitUpdate(id: number, info?: RecorderInfo): void; + + emitUpdateLatestWsMessage(id: number, message: any): void; + + updateRecord(id: number, info: RecorderInfo): void; + + updateRecordWsMessage(id: number, message: any): void; + + // TypeScript Version: 2.2 + updateExtInfo(id: number, extInfo: object): void; + + appendRecord(info: RecorderInfo): number; + + updateRecordBody(id: number, info: RecorderInfo): void; + + getBody(id: number, cb?: (err: Error, body: Buffer) => void): void; + + getDecodedBody(id: number, cb?: (err: Error, content: any) => void): void; + + getDecodedWsMessage(id: number, cb?: (err: Error, content: any) => void): void; + + getSingleRecord(id: number, cb: (err: Error, record: RecorderInfo) => void): void; + + getSummaryList(cb: (err: Error, records: RecorderInfo[]) => void): void; + + getRecords(idStart: number, limit: number, cb: (err: Error, records: RecorderInfo[]) => void): void; + + clear(): void; + + getCacheFile(fileName: string, cb: (err: Error, filePath: string) => void): void; +} + +export class ProxyWebServer extends EventEmitter { + /** + * Creates an instance of webInterface. + * + * @param config + * @param config.webPort + * @param recorder + */ + constructor(config: WebInterfaceOptions, recorder: ProxyRecorder); + + /** + * get the express server + */ + getServer(): any; + + start(): Promise; + + close(): void; +} + +export namespace utils { + /** Manage the system proxy config. sudo password may be required. */ + namespace systemProxyMgr { + /** Enable global system proxy with specified params. sudo password may be required. */ + function enableGlobalProxy(host: string, port: string | number): void; + /** Disable global system proxy. sudo password may be required. */ + function disableGlobalProxy(): void; + } + + /** Manage certificates of AnyProxy. */ + namespace certMgr { + /** Detect if AnyProx rootCA exists */ + function ifRootCAFileExists(): boolean; + + /** Generate a rootCA */ + function generateRootCA(callback: (err: Error, keyPath: string) => void): void; + } +} diff --git a/types/anyproxy/tsconfig.json b/types/anyproxy/tsconfig.json new file mode 100644 index 0000000000..94f7272cf2 --- /dev/null +++ b/types/anyproxy/tsconfig.json @@ -0,0 +1,23 @@ +{ + "compilerOptions": { + "module": "commonjs", + "lib": [ + "es6" + ], + "noImplicitAny": true, + "noImplicitThis": true, + "strictNullChecks": true, + "strictFunctionTypes": true, + "baseUrl": "../", + "typeRoots": [ + "../" + ], + "types": [], + "noEmit": true, + "forceConsistentCasingInFileNames": true + }, + "files": [ + "index.d.ts", + "anyproxy-tests.ts" + ] +} \ No newline at end of file diff --git a/types/anyproxy/tslint.json b/types/anyproxy/tslint.json new file mode 100644 index 0000000000..05b862328d --- /dev/null +++ b/types/anyproxy/tslint.json @@ -0,0 +1,4 @@ +{ + "extends": "dtslint/dt.json", + "strict-export-declare-modifiers": true +} \ No newline at end of file