Skip to content

Hypersequent/srvh2ch11

Repository files navigation

srvh2ch11

A simple Node.js module that creates a server supporting both HTTP/1.1 and HTTP/2 cleartext (h2c) with prior knowledge on the same port.

Why?

HTTP/1.1 has security limitations when used for connections from reverse proxies to upstream servers (http1mustdie.com). HTTP/2 offers significant improvements, but within secure networks and internal infrastructure, managing TLS certificates adds unnecessary complexity.

HTTP/2 Cleartext (h2c) provides some security benefits of HTTP/2 without requiring TLS (clear separation of request body and headers). However, migrating infrastructure from HTTP/1.1 to h2c requires a transition period where both protocols must be supported.

While Node.js's http2.createServer has an allowHTTP1 option, it only works with TLS connections. There are no plans to fix this limitation (nodejs/node#44887).

Hence this package.

How it works

srvh2ch11 solves this by:

  1. Creating a raw TCP server that listens for incoming connections
  2. Sniffing the initial bytes to detect the HTTP/2 connection preface (PRI * HTTP/2.0)
  3. Routing the connection to either an HTTP/1.1 or HTTP/2 server based on the detected protocol

Important: This module only supports HTTP/2 with prior knowledge. The client must know in advance that the server supports HTTP/2 cleartext and send the connection preface immediately. There is no HTTP/1.1 to HTTP/2 upgrade negotiation.

Installation

npm install srvh2ch11

Usage

Basic usage

import srvh2ch11 from "srvh2ch11";

const server = srvh2ch11.createServer((req, res) => {
    res.writeHead(200, { "Content-Type": "text/plain" });
    res.end("Hello from HTTP/" + req.httpVersion);
});

server.listen(8080, () => {
    console.log("Server listening on port 8080");
});

With separate options for HTTP/1.1 and HTTP/2

import srvh2ch11 from "srvh2ch11";

const options = {
    http1: {
        // HTTP/1.1 specific options
    },
    http2: {
        // HTTP/2 specific options
        maxConcurrentStreams: 100
    }
};

const server = srvh2ch11.createServer(options, (req, res) => {
    res.writeHead(200, { "Content-Type": "text/plain" });
    res.end("Hello from HTTP/" + req.httpVersion);
});

server.listen(8080);

Force H2C-only mode

import srvh2ch11 from "srvh2ch11";

const options = {
    forceH2c: process.env.SRV_FORCE_H2C === "true",  // Only accept H2C connections in production
    http2: {
        maxConcurrentStreams: 100
    }
};

const server = srvh2ch11.createServer(options, (req, res) => {
    res.writeHead(200, { "Content-Type": "text/plain" });
    res.end("Hello from HTTP/" + req.httpVersion);
});

server.listen(8080);

Run with:

SRV_FORCE_H2C=true node server.js

API

srvh2ch11.createServer([options], onRequestHandler)

Creates a server that can handle both HTTP/1.1 and HTTP/2 cleartext connections.

  • options (optional): Configuration object with the following structure:
    • http1: Options passed to http.createServer()
    • http2: Options passed to http2.createServer()
    • forceH2c: Boolean (default: false) - When true, forces HTTP/2-only mode, bypassing protocol detection and rejecting HTTP/1.1 connections
    • http11Compat: Boolean (default: false) - When true, enables HTTP/1.1 compatibility transformations for HTTP/2 requests:
      • Maps :authority header to host header
      • Filters out HTTP/1.1-specific headers (connection, keep-alive, etc.) to prevent warnings
  • onRequestHandler: The request handler function with signature (req, res) => {}

Returns an object with:

  • listen(port, [callback]): Start listening on the specified port
  • close([callback]): Close all servers (raw, HTTP/1.1, and HTTP/2)
  • on(event, handler): Add event listeners
  • address(): Get the server address
  • h1Server: The underlying HTTP/1.1 server instance
  • h2Server: The underlying HTTP/2 server instance
  • rawServer: The underlying raw TCP server instance

Testing

Test with HTTP/1.1:

curl http://localhost:8080/

Test with HTTP/2:

curl --http2-prior-knowledge http://localhost:8080/

Example

Run the included example:

npm run example

Testing

The module includes comprehensive test suites:

JavaScript tests (Node.js built-in test runner)

# Run tests (automatically builds TypeScript first)
npm test

Bash tests (curl-based)

# Run basic curl tests (automatically builds TypeScript first)
npm run test:bash

Multi-Node version testing (Docker-based)

# Test against Node.js 20, 22, and 24
npm run test:multi

The tests cover:

  • HTTP/1.1 GET, POST, and concurrent requests
  • HTTP/2 with prior knowledge (GET, POST, concurrent streams)
  • Mixed protocol handling (HTTP/1.1 and HTTP/2 simultaneously)
  • Server configuration with separate options
  • Force HTTP/2-only mode (forceH2c option)
  • HTTP/1.1 compatibility mode (http11Compat option)
  • Basic curl-based integration tests
  • Compatibility across multiple Node.js versions (20, 22, 24)

Limitations

  • HTTP/2 module maturity: Node.js's http2 module is less mature than http. For example, it doesn't offer closeAllConnections() and closeIdleConnections() methods that are available in the HTTP/1.1 server. There may be other API gaps as well.
  • Prior knowledge only: This module only supports HTTP/2 with prior knowledge. There is no HTTP/1.1 to HTTP/2 upgrade negotiation (note that Upgrade negotiation has been deprecated by RFC 9113 anyway).
  • Cleartext only: This module is designed for cleartext (non-TLS) connections. For TLS connections, use Node.js's native http2.createSecureServer() with allowHTTP1: true.

Acknowledgments

Thanks to GitHub users @fkoemep and @stT-e5gna2z5MBS who shared example implementations in the Node.js issue #44887.

This package was made possible by Hypersequent, creators of QA Sphere - a fast, pleasant to use Test Management System.

License

MIT

About

Node.js module that creates a server supporting both HTTP/1.1 and HTTP/2 cleartext (h2c)

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published