prxi
TypeScript icon, indicating that this package has built-in type declarations

1.3.4 • Public • Published

prxi

Tests Known Vulnerabilities codecov

prxi is a zero dependency reverse proxy module for Node.js

Installation

# For NPM users:
npm i prxi

# For Yarn users:
yarn add prxi

Usage

import { Prxi, HttpMethod, ProxyRequest, Request, Response } from 'prxi';
import { OutgoingHttpHeaders } from 'node:http';
import { ServerHttp2Stream } from 'node:http2';

// Instantiate new Prxi, requires a src/Configuration.ts configuration object
const proxy = new Prxi({
  // optional mode, can be HTTP or HTTP2, by default HTTP
  // When HTTP/2 is used, upstream services should be using HTTP/2 too
  mode: 'HTTP',

  // optional secure connection settings
  // by default disabled
  // NOTE: for secure WS connection upstream service should also use secure connection
  secure: {
    // For the list of available options please refer to https://nodejs.org/api/tls.html#tls_tls_createsecurecontext_options
  },

  // optional hooks
  on: {
    // optional hook to be called before HTTP/1.1 request
    beforeHTTPRequest: (req: Request, res: Response, context: Record<string, any>): void { /* ... */ },

    // optional hook to be called after HTTP/1.1 request
    afterHTTPRequest: (req: Request, res: Response, context: Record<string, any>): void { /* ... */ },

    // optional hook to be called on request upgrade
    upgrade: (req: Request, socket: Socket, head: Buffer): void { /* ... */ },

    // optional hook to be called after request upgrade processing
    afterUpgrade: (req: Request, socket: Socket, head: Buffer): void { /* ... */ },

    // optional hook to be called before HTTP/2 session processing
    beforeHTTP2Session(session: Http2Session, context: Record<string, any>): void { /* ... */ },

    // optional hook to be called after HTTP/2 session processing
    afterHTTP2Session(session: Http2Session, context: Record<string, any>): void { /* ... */ },

    // optional hook to be called before HTTP/2 stream
    beforeHTTP2Request(stream: Stream, headers: IncomingHttpHeaders, context: Record<string, any>): void { /* ... */ },

    // optional hook to be called after HTTP/2 stream
    afterHTTP2Request?: (stream: Stream, headers: IncomingHttpHeaders, context: Record<string, any>): void { /* ... */ },
  },

  // port to listen incoming requests on
  port: TestProxy.PORT,

  // optional hostname where proxy should listen for incoming connections
  hostname: 'localhost',

  // optional proxy request timeout in milliseconds (default value: 1 minute)
  proxyRequestTimeout: 30 * 1000,

  // optional log handlers, by default no handlers
  log: {
    // optional handler of debug logs
    debug(context, msg, params): void { /* ... */ },

    // optional handler of info logs
    info(context, msg, params): void { /* ... */ },

    // optional handler of error logs
    error(context, msg, error, params): void { /* ... */ },
  }
  // log message function
  logInfo: console.log,

  // log errors function
  logError: console.error,

  // optional custom error handler
  errorHandler,

  // optional custom error handler for HTTP/2 connection
  // only in use when mode is HTTP2
  http2ErrorHandler,

  // optional additional headers to add or remove from the upstream request
  // if value is null - header if presented will be removed
  // if value isn't null - header will be added or value replaced
  proxyRequestHeaders: {
    'X-ADD_TO_UPSTREAM_REQUEST': 'value',
    'X-REMOVE_FROM_REQUEST': null,
  },

  // optional additional headers to add or remove from the response
  // if value is null - header if presented will be removed
  // if value isn't null - header will be added or value replaced
  responseHeaders: {
    'X-ADD_TO_RESPONSE': 'value',
    'X-REMOVE_FROM_RESPONSE': null,
  },

  // Upstream list configuration
  upstream: [
    {
      // upstream endpoint
      target: `http://${this.host}:${TestServer.PORT}`,

      // optional custom error handler (overrides higher level setting)
      errorHandler,

      // optional proxy request timeout in milliseconds (overrides higher level setting)
      proxyRequestTimeout: 30 * 1000,

      // optional additional headers to add or remove from the upstream request
      // if value is null - header if presented will be removed
      // if value isn't null - header will be added or value replaced
      // Note: this setting will be merged with a higher level settings, so override will only happen if header names match between the two
      proxyRequestHeaders: {
        'X-ADD_TO_UPSTREAM_REQUEST': 'value',
        'X-REMOVE_FROM_REQUEST': null,
      },

      // optional additional headers to add or remove from the response
      // if value is null - header if presented will be removed
      // if value isn't null - header will be added or value replaced
      // Note: this setting will be merged with a higher level settings, so override will only happen if header names match between the two
      responseHeaders: {
        'X-ADD_TO_RESPONSE': 'value',
        'X-REMOVE_FROM_RESPONSE': null,
      },

      // optional custom error handler for given upstream only
      errorHandler,

      // optional custom error handler for HTTP/2 connection and given upstream only
      // only in use when mode =
      http2ErrorHandler,

      // optional list of request handlers
      requestHandlers,

      // optional list of websocket handlers
      webSocketHandlers,

      // optional HTTP/2 error handler
      // only used when mode is HTTP2
      http2ErrorHandler?: Http2ErrorHandler;

      // optional HTTP/2 handlers
      // only used when mode is HTTP2
      http2RequestHandlers;
    }
  ]
});

// HTTP/1.1 request handlers
const requestHandlers = [
  {
    // function to test the incoming request
    // if returns true `handle` function will process the request
    isMatching(method: HttpMethod, path: string, context: Record<string, any>, headers: IncomingHttpHeaders): boolean {
      return true
    },

    /**
     * Request handler
     */
    async handle(
      req: Request,
      res: Response,
      proxyRequest: ProxyRequest,
      method: HttpMethod,
      path: string,
      context: Record<string, any>
    ): Promise<void> {
      // proxy incoming request to the upstream
      // optionally pass ProxyRequestConfiguration object as a parameter
      await proxyRequest({
        // optionally provide alternative path for the upstream request
        url: '/another/path',

        // optionally provide another HTTP method for the upstream request
        method: 'PUT',

        // optionally use another target host for the upstream request
        target: 'http://127.0.0.1',

        // optionally use another target port for the upstream request
        port: 9999,

        // Proxy request headers to add/replace/remove on top of the Configuration ones (if any)
        proxyRequestHeaders: {
          'X-ADD_TO_UPSTREAM_REQUEST': 'value',
          'X-REMOVE_FROM_REQUEST': null,
        },

        // Proxy response headers to add/replace/remove on top of the Configuration ones (if any)
        proxyResponseHeaders: {
          'X-ADD_TO_RESPONSE': 'value',
          'X-REMOVE_FROM_RESPONSE': null,
        },

        /**
         * Optional handler before making the proxy request
         */
        onBeforeProxyRequest(
          options: RequestOptions | null,
          proxyHeaders: OutgoingHttpHeaders,
          context: Record<string, any>,
        ): void { /* ... */ },

        /**
         * Optional handler before sending a response
         */
        onBeforeResponse(
          res: Response,
          outgoingHeaders: OutgoingHttpHeaders,
          context: Record<string, any>,
        ): Promise<void> | void { /* ... */ },
      });
    }
  }
];

// HTTP/2 request handlers
const http2RequestHandlers = [
  {
    // function to test the incoming request
    // if returns true `handle` function will process the request
    isMatching(method: HttpMethod, path: string, context: Record<string, any>, headers: IncomingHttpHeaders): boolean {
      return true;
    },

    /**
     * Stream handler
     */
    async handle(
      stream: ServerHttp2Stream,
      headers: OutgoingHttpHeaders,
      proxyRequest: ProxyRequest,
      method: HttpMethod,
      path: string,
      context: Record<string, any>
    ): Promise<void> {
      // proxy incoming request to the upstream
      // optionally pass ProxyRequestConfiguration object as a parameter
      await proxyRequest({
        // optionally provide alternative path for the upstream request
        url: '/another/path',

        // optionally provide another HTTP method for the upstream request
        method: 'PUT',

        // optionally use another target host for the upstream request
        target: 'http://127.0.0.1',

        // optionally use another target port for the upstream request
        port: 9999,

        // Proxy request headers to add/replace/remove on top of the Configuration ones (if any)
        proxyRequestHeaders: {
          'X-ADD_TO_UPSTREAM_REQUEST': 'value',
          'X-REMOVE_FROM_REQUEST': null,
        },

        // Proxy response headers to add/replace/remove on top of the Configuration ones (if any)
        proxyResponseHeaders: {
          'X-ADD_TO_RESPONSE': 'value',
          'X-REMOVE_FROM_RESPONSE': null,
        },

        /**
         * Optional handler before making the proxy request
         * @param options - for HTTP/2 connection value is null
         */
        onBeforeProxyRequest(
          options: RequestOptions | null,
          proxyHeaders: OutgoingHttpHeaders,
          context: Record<string, any>,
        ): void { /* ... */ },

        /**
         * Optional handler before sending a response
         * @param res - for HTTP/2 connection value is null
         */
        onBeforeResponse(
          res: Response,
          outgoingHeaders: OutgoingHttpHeaders,
          context: Record<string, any>,
        ): Promise<void> | void { /* ... */ },
      }
    }
  }
];

// WS handlers
const webSocketHandlers = [
  {
    // function to test the incoming request
    // if returns true `handle` function will process the request
    isMatching(path: string, context: Record<string, any>, headers: IncomingHttpHeaders): boolean {
      return true;
    },

    /**
     * Request handler
     */
    async handle(
      req: Request,
      socket: Socket,
      head: Buffer,
      proxyRequest: ProxyRequest,
      cancelRequest: WebSocketProxyCancelRequest,
      path: string,
      context: Record<string, any>
    ): Promise<void> {
      // proxy incoming request to the upstream
      // optionally pass ProxyRequestConfiguration object as a parameter
      await proxyRequest(
        // optionally provide alternative path for the upstream request
        url: '/another/path',

        // NOTE: method won't have any affect
        // method: ...

        // optionally use another target host for the upstream request
        target: 'http://127.0.0.1',

        // optionally use another target port for the upstream request
        port: 9999,

        // Proxy request headers to add/replace/remove on top of the Configuration ones (if any)
        proxyRequestHeaders: {
          'X-ADD_TO_UPSTREAM_REQUEST': 'value',
          'X-REMOVE_FROM_REQUEST': null,
        },

        // Proxy response headers to add/replace/remove on top of the Configuration ones (if any)
        proxyResponseHeaders: {
          'X-ADD_TO_RESPONSE': 'value',
          'X-REMOVE_FROM_RESPONSE': null,
        },

        /**
         * Optional handler before making the proxy request
         * @param options request options, can be null for HTTP/2 request
         */
        onBeforeProxyRequest(
          options: RequestOptions | null,
          proxyHeaders: OutgoingHttpHeaders,
          context: Record<string, any>,
        ): void { /* ... */ },
      );

      // alternatively cancel request with custom http status code and message
      cancelRequest(418, 'I\'m a teapot');
    }
  }
]

/**
 * Custom error handler
 */
const errorHandler = async (req: Request, res: Response, err?: Error): Promise<void> {
    throw err;
};

/**
 * Custom HTTP/2 error handler
 */
const http2ErrorHandler = async (stream: ServerHttp2Stream, headers: IncomingHttpHeaders, err: Error): Promise<void> {
    throw err;
};

// start the proxy server
// later it can be stopped by calling `stop` method.
await proxy.start();

Package Sidebar

Install

npm i prxi

Weekly Downloads

6

Version

1.3.4

License

MIT

Unpacked Size

150 kB

Total Files

62

Last publish

Collaborators

  • vlad-tkachenko