npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@harvard-lil/portal

v0.0.2

Published

HTTP proxy implementation using Node.js' http.createServer.

Downloads

336

Readme

Portal

npm version JavaScript Style Guide Linting Test suite

🚧 Work-in-progress

HTTP proxy implementation using Node.js' http.createServer to accept connections and http(s).request to relay them to their destinations. Currently in use on @harvard-lil/scoop.

Philosophy

Portal uses standard Node.js networking components in order to provide a simple proxy with the following goals:

  • No dependencies
  • Interfaces that match existing Node.js conventions
  • The ability to intercept raw traffic

Portal achieves this by using "mirror" streams that buffer the data from each socket, allowing Node.js' standard parsing mechanism to parse the data while making that same raw data available for modification before being passed forward in the proxy.

Configuration

The entrypoint for Portal is the createServer function which, in addition to the options available to http.createServer, also accepts the following:

  • clientOptions(request) - a function which accepts the request http.IncomingMessage and returns an options object (or Promise) to be passed to new tls.TLSSocket when the client socket is upgraded after an HTTP CONNECT request. Most useful for dynamically generating a key / cert pair for the requested server name.
  • serverOptions(request) - a function which accepts the request http.IncomingMessage and returns an options object (or Promise) to be passed to http(s).request which will then be used to make requests to the destination. Most useful for setting SSL flags.
  • requestTransformer(request) - a function which accepts the request http.IncomingMessage and returns a stream.Transform instance (or Promise) through which the incoming request data will be passed before being forwarded to its destination.
  • responseTransformer(response, request) - a function which accepts the response and request http.IncomingMessages and returns a stream.Transform instance (or Promise) through which the incoming response data will be passed before being forwarded to its destination.

Events

The proxy server returned by createServer emits all of the events available on http.Server (ex: proxy.on('request')). Additionally, it emits all of the events from http.ClientRequest (ex: proxy.on('response')) with the caveat that the upgrade event is emitted as upgrade-client in order to avoid a collision with the http.Server event of the same name. Errors from both http.Server and http.ClientRequest are available via the 'error' event.

Example

import * as http from 'http'
import * as crypto from 'node:crypto'
import { TLSSocket } from 'tls'
import { Transform } from 'node:stream'
import { createServer } from './Portal.js'

const PORT = 1337
const HOST = '127.0.0.1'

const proxy = createServer({
  requestTransformer: (request) => new Transform({
    transform: (chunk, _encoding, callback) => {
      console.log('Raw data to be passed in the request', chunk.toString())
      callback(null, chunk)
    }
  }),
  responseTransformer: (response, request) => new Transform({
    transform: (chunk, _encoding, callback) => {
      console.log('Raw data to be passed in the response', chunk.toString())
      callback(null, chunk)
    }
  }),
  clientOptions: async (request) => {
    return {} // a custom key and cert could be returned here
  },
  serverOptions: async (request) => {
    return {
      // This flag allows legacy insecure renegotiation between OpenSSL and unpatched servers
      // @see {@link https://stackoverflow.com/questions/74324019/allow-legacy-renegotiation-for-nodejs}
      secureOptions: crypto.constants.SSL_OP_LEGACY_SERVER_CONNECT
    }
  }
})

proxy.on('request', (request) => {
  console.log('Parsed request to observe', request.headers)
})

proxy.on('response', (response, request) => {
  console.log('Parsed response to observe', response.headers)
})

proxy.on('error', (err) => {
  console.log('Handle error', err)
})

proxy.listen(PORT, HOST)

/*
 * Make an example request
 */
proxy.on('listening', () => {
  const options = {
    port: PORT,
    host: HOST,
    method: 'CONNECT',
    path: 'example.com:443'
  }

  const req = http.request(options)
  req.end()

  req.on('connect', (res, socket, head) => {
    const upgradedSocket = new TLSSocket(socket, {
      rejectUnauthorized: false,
      requestCert: false,
      isServer: false
    })

    upgradedSocket.write('GET / HTTP/1.1\r\n' +
      'Host: example.com:443\r\n' +
      'Connection: close\r\n' +
      '\r\n')
  })
})