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

@superhero/http-server

v4.3.0

Published

HTTP(S) server component supporting both HTTP 1.1 and HTTP 2.0

Downloads

839

Readme

HTTP-Server

An HTTP server module for Node.js that supports both HTTP/1.1 and HTTP/2 protocols, with built-in routing, HTTPS support, and stream support that defaults to server-sent events (SSE). Designed to be robust, flexible and extendible, while easy to work with.

Table of Contents

Installation

Install the package using npm:

npm install @superhero/http-server

Getting Started

The @superhero/http-server module integrates with the @superhero/locator and @superhero/router modules to provide a flexible and modular HTTP server.

To get started, you'll need to set up a Locator instance, register your dispatchers, and then locate the HttpServer module using the locator.

Usage

Basic Example

import HttpServer from '@superhero/http-server';
import Locator    from '@superhero/locator';
import Router     from '@superhero/router';

// Instantiate the service locator
const locator = new Locator();

// Instantiate the router
const router = new Router(locator);

// Instantiate the server
const server = new HttpServer(router);

// Register the route dispatcher service
locator.set('hello-dispatcher', {
  dispatch: (request, session) => {
    session.view.body.message = 'Hello, World!';
  },
});

// Routes
const settings = {
  router: {
    routes: {
      hello: {
        criteria: '/hello',
        dispatcher: 'hello-dispatcher',
      },
    },
  },
};

// Bootstrap and start the server
await server.bootstrap(settings);
await server.listen(3000);

Explanation:

  • Import Statements: We instantiate the required components locator, router and server.
  • Dispatcher Registration: Register a dispatcher called 'hello-dispatcher' in the locator.
  • Server Settings: Define the routes, and possible other server configurations, in the settings object.
  • Bootstrap and Listen: Bootstrap the server with the settings and start listening on port 3000.
  • Ready to serve requests: Request to http://localhost:3000/hello will reply { "message": "Hello, World!" }.

HTTPS Setup with Self-Signed Certificate

import fs       from 'node:fs';
import Locator  from '@superhero/locator';

// Instantiate the service locator
const locator = new Locator();

// Locate the server
const server = await locator.lazyload('@superhero/http-server');

// Register necessary services
locator.set('secure-dispatcher', {
  dispatch: (request, session) => {
    session.view.body = { message: 'Secure Hello, World!' };
  },
});

// Server settings and routes
const serverSettings = {
  server: {
    key: fs.readFileSync('path/to/private.key'),
    cert: fs.readFileSync('path/to/server.cert'),
  },
  router: {
    routes: {
      secure: {
        criteria: '/secure',
        dispatcher: 'secure-dispatcher',
      },
    },
  },
};

await server.bootstrap(serverSettings);
await server.listen(443);

[!NOTE] Replace 'path/to/private.key' and 'path/to/server.cert' with the actual paths to your SSL key and certificate files.

Altering Response Body, Headers, and Status

locator.set('custom-dispatcher', {
  dispatch: (request, session) => {
    session.view.body = { data: 'Custom Data' };
    session.view.headers['Custom-Header'] = 'CustomValue';
    session.view.status = 201; // HTTP 201 Created
  },
});

// Update the routes in the settings
const settings = {
  router: {
    routes: {
      custom: {
        criteria: '/custom',
        dispatcher: 'custom-dispatcher',
      },
    },
  },
};

// Bootstrap and start the server
await server.bootstrap(settings);
await server.listen(3000);

Handling Aborted Requests

locator.set('abort-dispatcher', {
  dispatch: (request, session) => {
    // Abort the request with a custom error
    const error = new Error('Request Aborted');
    error.code = 'E_REQUEST_ABORTED';
    session.abortion.abort(error);
  },
});

// Update the routes in the settings
const settings = {
  router: {
    routes: {
      abort: {
        criteria: '/abort',
        dispatcher: 'abort-dispatcher',
      },
    },
  },
};

// Bootstrap and start the server
await server.bootstrap(settings);
await server.listen(3000);

[!NOTE] Will result in a status 500 response { "error": "Request Aborted", "code": "E_REQUEST_ABORTED" }

Streaming Server-Sent Events (SSE)

locator.set('sse-dispatcher', {
  dispatch: (request, session) => {
    // Write events to the stream
    session.view.stream.write({ data: 'First message' });
    session.view.stream.write({ data: 'Second message' });

    // End the stream
    session.view.stream.end();
  },
});

// Update the routes in the settings
const settings = {
  router: {
    routes: {
      sse: {
        criteria: '/sse',
        dispatcher: 'sse-dispatcher',
      },
    },
  },
};

// Bootstrap and start the server
await server.bootstrap(settings);
await server.listen(3000);

[!NOTE] By default responds with a text/event-stream content type:

data: { "data": "First message" }

data: { "data": "Second message" }

Custom Logging

You can override the default logging methods to integrate with your logging system.

Turn Off Info Logs

server.log.info = () => null;

Custom Error Logging

server.log.error = (error) => {
  // TODO: custom error logging logic...
};

Turn Off Log Colors

By default, the logger renders a colored output.

server.log.format = server.log.simple;

API

HttpServer

The main class responsible for handling HTTP requests.

  • Constructor: The server can be instantiated or located via the Locator.

    • Use locator.locate('@superhero/http-server') to get an instance.
  • Methods:

    • async bootstrap(settings): Bootstraps the server with the provided settings.
      • settings: An object containing server and router configurations.
    • async listen(port): Starts the server on the specified port.
      • port: The port number to listen on.
    • async close(): Closes the server and all active sessions.

request

An object used to read

  • Properties:
    • body: The request body (Promise).
    • method: The request HTTP method.
    • headers: The request HTTP headers.
    • url: The requested URL.

session.view

An object used within dispatchers to manipulate the response.

  • Properties:
    • body: The response body to be sent to the client.
    • headers: An object containing response headers.
    • status: HTTP status code of the response.
    • stream: A writable stream for sending SSE data, or to be configured to stream some other type of response to the client.

session.abortion

An AbortController used to trigger and manage dispatch abortion.

  • Methods:
    • abort(error): Aborts the request with the provided error.

Testing

The test suite uses Node.js's built-in testing module.

Running Tests

To run the tests, execute:

npm test

Test Coverage

▶ @superhero/http-server
  ▶ Lifecycle
    ✔ Can instantiate HttpServer (8.609311ms)
    ✔ Can bootstrap server with non-secure settings (2.813603ms)
    ✔ Can be configured by the configuration file (37.159723ms)
    ✔ Listens and closes the server as expected (5.81708ms)
    ✔ Rejects if server is not available to listen error (2.290967ms)
    ✔ Rejects if server is not available to close error (1.058432ms)
  ✔ Lifecycle (59.459503ms)
  
  ▶ Routing and Requests
    ▶ HTTP/1
      ✔ Can dispatch a request aligned to the route map (42.67787ms)
      ✔ Can alter the output body (5.709405ms)
      ✔ Can stream HTML5 standard Server-Sent Events (SSE) (9.72838ms)
      ✔ Can alter the output headers (9.330729ms)
      ✔ Can alter the output status (7.159825ms)
      ✔ Can abort the dispatcher (5.969359ms)
      ✔ Can describe an abortion in detail (7.225311ms)
      ✔ Can manage thrown errors in the dispatcher (10.332202ms)
      ✔ Can not mistakenly access the wrong view property (7.490531ms)
      ✔ Can not mistakenly assign a value to the wrong view property (5.522195ms)
      ✔ Support connection keep-alive header (10.362687ms)
    ✔ HTTP/1 (122.952777ms)

    ▶ HTTP/2
      ✔ Can dispatch a request aligned to the route map (24.833859ms)
      ✔ Can alter the output body (8.516916ms)
      ✔ Can stream HTML5 standard Server-Sent Events (SSE) (10.336142ms)
      ✔ Can alter the output headers (8.209538ms)
      ✔ Can alter the output status (7.35682ms)
      ✔ Can abort the dispatcher (8.554265ms)
      ✔ Can describe an abortion in detail (5.330231ms)
      ✔ Can manage thrown errors in the dispatcher (9.391412ms)
      ✔ Can not mistakenly access the wrong view property (7.222525ms)
      ✔ Can not mistakenly assign a value to the wrong view property (8.57349ms)
    ✔ HTTP/2 (99.758519ms)
  ✔ Routing and Requests (222.916463ms)

  ▶ HTTPS server with self-signed certificate
    ▶ TLSv1.2
      ▶ RSA:2048
        ✔ HTTP1 (10.68221ms)
        ✔ HTTP2 (14.607846ms)
      ✔ RSA:2048 (184.393685ms)

      ▶ RSA:4096
        ✔ HTTP1 (11.166249ms)
        ✔ HTTP2 (14.376707ms)
      ✔ RSA:4096 (239.998059ms)

      ▶ ECDSA:P-256
        ✔ HTTP1 (6.998372ms)
        ✔ HTTP2 (9.255564ms)
      ✔ ECDSA:P-256 (52.737888ms)

      ▶ ECDSA:P-384
        ✔ HTTP1 (8.610887ms)
        ✔ HTTP2 (9.7637ms)
      ✔ ECDSA:P-384 (52.826908ms)

      ▶ ECDSA:P-521
        ✔ HTTP1 (9.391868ms)
        ✔ HTTP2 (13.179682ms)
      ✔ ECDSA:P-521 (62.741409ms)

      ▶ EdDSA:Ed25519
        ✔ HTTP1 (10.110816ms)
        ✔ HTTP2 (9.99596ms)
      ✔ EdDSA:Ed25519 (58.01589ms)

      ▶ EdDSA:Ed448
        ✔ HTTP1 (4.651357ms)
        ✔ HTTP2 (10.031462ms)
      ✔ EdDSA:Ed448 (51.550013ms)
    ✔ TLSv1.2 (703.01496ms)

    ▶ TLSv1.3
      ▶ RSA:2048
        ✔ HTTP1 (6.43647ms)
        ✔ HTTP2 (9.551209ms)
      ✔ RSA:2048 (113.791235ms)

      ▶ RSA:4096
        ✔ HTTP1 (18.396852ms)
        ✔ HTTP2 (22.414178ms)
      ✔ RSA:4096 (819.288505ms)

      ▶ ECDSA:P-256
        ✔ HTTP1 (7.036644ms)
        ✔ HTTP2 (11.842337ms)
      ✔ ECDSA:P-256 (100.196088ms)

      ▶ ECDSA:P-384
        ✔ HTTP1 (8.183114ms)
        ✔ HTTP2 (12.428711ms)
      ✔ ECDSA:P-384 (61.367513ms)

      ▶ ECDSA:P-521
        ✔ HTTP1 (12.446168ms)
        ✔ HTTP2 (15.564381ms)
      ✔ ECDSA:P-521 (68.39823ms)

      ▶ EdDSA:Ed25519
        ✔ HTTP1 (4.44369ms)
        ✔ HTTP2 (11.703458ms)
      ✔ EdDSA:Ed25519 (54.613453ms)

      ▶ EdDSA:Ed448
        ✔ HTTP1 (13.499592ms)
        ✔ HTTP2 (11.474115ms)
      ✔ EdDSA:Ed448 (68.423656ms)
    ✔ TLSv1.3 (1287.220809ms)
  ✔ HTTPS server with self-signed certificate (1990.42993ms)
✔ @superhero/http-server (2273.646154ms)

tests 69
suites 8
pass 69

---------------------------------------------------------------------------------------------------------------------
file                   | line % | branch % | funcs % | uncovered lines
---------------------------------------------------------------------------------------------------------------------
index.js               |  91.75 |    91.18 |   74.07 | 92-94 128-129 135-137 266-269 369-373 389-394 397-402 405-410
index.test.js          | 100.00 |   100.00 |  100.00 | 
middleware             |        |          |         | 
 upstream              |        |          |         | 
  header               |        |          |         | 
   accept.js           |  19.23 |   100.00 |   33.33 | 10-51
   content-type.js     |  20.00 |   100.00 |   50.00 | 9-44
   content-type        |        |          |         | 
    application        |        |          |         | 
     json.js           |  31.03 |   100.00 |    0.00 | 9-28
  method.js            |  23.68 |   100.00 |   50.00 | 9-37
view.js                |  92.98 |    88.89 |   84.21 | 133-138 196-200 238-239 247-253
---------------------------------------------------------------------------------------------------------------------
all files              |  86.57 |    93.96 |   83.82 | 
---------------------------------------------------------------------------------------------------------------------

License

This project is licensed under the MIT License.

Contributing

Feel free to submit issues or pull requests for improvements or additional features.