bugl
bugl
HomeLearnPatternsSearch
HomeLearnPatternsSearch

Loading lesson path

Learn/Node.js/Node.js Advanced
Node.js•Node.js Advanced

Node.js HTTP/2 Module

What is HTTP/2?

The Node.js HTTP/2 module provides an implementation of the HTTP/2 protocol, offering improved performance, server push capabilities, header compression, and multiplexing over a single connection.

Formula

HTTP/2 improves upon HTTP/1.1 with several key features:

Binary protocol

: HTTP/2 uses a binary format for data transfer rather than the text format of HTTP/1.1, making it more efficient to parse.

Multiplexing

: Multiple requests and responses can be sent over a single connection simultaneously.

Header compression

Formula

: HTTP/2 compresses headers to reduce overhead.

Server push

: Servers can proactively send resources to clients before they request them.

Stream prioritization

: Resources can be delivered with different priorities.

Using the HTTP/2 Module

Formula

In Node.js, the HTTP/2 module can be accessed using:
const http2 = require('http2');
The HTTP/2 module is stable as of Node.js v10.0.0. It's important to note that HTTP/2 requires a secure connection (HTTPS) in most browsers, so most examples will use TLS/SSL.

Creating an HTTP/2 Server

Formula

Here's an example of creating a basic HTTP/2 server using TLS:
const http2 = require('http2');
const fs = require('fs');
const path = require('path');
// Read the TLS certificate and key const options = {
key: fs.readFileSync(path.join(__dirname, 'server.key')), cert: fs.readFileSync(path.join(__dirname, 'server.crt'))
};
// Create an HTTP/2 server const server = http2.createSecureServer(options);
// Handle stream events server.on('stream', (stream, headers) => {

Formula

// Get the path from headers const path = headers[':path'];
// Send a response if (path === '/') {
stream.respond({

Formula

'content - type': 'text/html',

':status': 200

});
stream.end('<h1>Hello from HTTP/2!</h1>');
} else {
stream.respond({
':status': 404
});
stream.end('Not found');
}
});
// Start the server const port = 8080;
server.listen(port, () => {
console.log(`HTTP/2 server running at https://localhost:${port}`);
});

This example assumes you have TLS certificate files. For development, you can generate self-signed certificates using OpenSSL. For production, use a trusted certificate authority.

Formula

You can also create an HTTP/2 server without TLS (for direct HTTP/2 connections without encryption):
const http2 = require('http2');

Formula

// Create an HTTP/2 server without TLS
const server = http2.createServer();
server.on('stream', (stream, headers) => {
stream.respond({

Formula

'content - type': 'text/html',

':status': 200

});
stream.end('<h1>Hello from HTTP/2 without TLS!</h1>');
});
server.listen(8080);

Most modern browsers only support HTTP/2 over TLS, so the insecure HTTP/2 server will typically only work with dedicated HTTP/2 clients that explicitly support cleartext HTTP/2.

HTTP/2 Client

Formula

Creating an HTTP/2 client to connect to an HTTP/2 server:
const http2 = require('http2');

Formula

// Create a client const client = http2.connect('https://localhost:8080', {
// For self - signed certificates in development rejectUnauthorized: false
});
// Error handling client.on('error', (err) => {
console.error('Client error:', err);
});

Formula

// Create a request const req = client.request({ ':path': '/' });
// Handle response data req.on('response', (headers) => {
console.log('Status:', headers[':status']);
console.log('Headers:', headers);
});
req.on('data', (chunk) => {
console.log('Received data:', chunk.toString());
});
req.on('end', () => {
console.log('Request completed');
client.close();
});
// Send the request req.end();

HTTP/2 Streams

HTTP/2 uses streams for communication between client and server. Each stream represents an independent, bidirectional sequence of frames exchanged between the client and server.

Stream Events

Important stream events include:

'headers' : Emitted when headers are received 'data' : Emitted when a chunk of data is received 'end' : Emitted when the stream is finished 'error'

: Emitted when an error occurs const http2 = require('http2');
const fs = require('fs');
const path = require('path');
// Create a server const server = http2.createSecureServer({
key: fs.readFileSync(path.join(__dirname, 'server.key')), cert: fs.readFileSync(path.join(__dirname, 'server.crt'))
});
server.on('stream', (stream, headers) => {
// Handle stream events stream.on('error', (error) => {
console.error('Stream error:', error);
});
stream.on('close', () => {
console.log('Stream closed');
});
// Handle request stream.respond({

Formula

'content - type': 'text/plain',

':status': 200

});
// Send data in multiple chunks stream.write('First chunk of data\n');
setTimeout(() => {
stream.write('Second chunk of data\n');
stream.end('Final chunk of data');
}, 1000);
});
server.listen(8080);

HTTP/2 Server Push

Server push allows the server to proactively send resources to the client before they are explicitly requested. This can improve performance by eliminating round-trip delays.

const http2 = require('http2');
const fs = require('fs');
const path = require('path');
const options = {
key: fs.readFileSync(path.join(__dirname, 'server.key')), cert: fs.readFileSync(path.join(__dirname, 'server.crt'))
};
const server = http2.createSecureServer(options);
server.on('stream', (stream, headers) => {
const requestPath = headers[':path'];
if (requestPath === '/') {
// Push CSS and JavaScript resources stream.pushStream({ ':path': '/style.css' }, (err, pushStream) => {
if (err) {
console.error('Error pushing stream:', err);
return;
}
pushStream.respond({

Formula

'content - type': 'text/css',

':status': 200

});
pushStream.end('body { color: blue; }');
});
stream.pushStream({ ':path': '/script.js' }, (err, pushStream) => {
if (err) {
console.error('Error pushing stream:', err);
return;
}
pushStream.respond({

Formula

'content - type': 'application/javascript',

':status': 200

});
pushStream.end('console.log("Hello from HTTP/2 server push!");');
});
// Send the main HTML document stream.respond({

Formula

'content - type': 'text/html',

':status': 200

});
stream.end(`

<!DOCTYPE html> <html> <head>

Formula

< title > HTTP/2 Server Push Example </title >

<link rel="stylesheet" href="/style.css"> <script src="/script.js"></script> </head> <body>

Formula

< h1 > HTTP/2 Server Push Demo </h1 >
< p > CSS and JavaScript were pushed by the server!</p >

</body> </html>

`);
} else {
// Serve pushed resources if requested directly if (requestPath === '/style.css') {
stream.respond({

Formula

'content - type': 'text/css',

':status': 200

});
stream.end('body { color: blue; }');
} else if (requestPath === '/script.js') {
stream.respond({

Formula

'content - type': 'application/javascript',

':status': 200

});
stream.end('console.log("Hello from HTTP/2 server push!");');
} else {
// Not found stream.respond({ ':status': 404 });
stream.end('Not found');
}
}
});
server.listen(8080);

HTTP/2 Headers

HTTP/2 uses a different format for headers. Notably, all headers are lowercase, and request pseudo-headers start with a colon (:).

const http2 = require('http2');
// HTTP/2 pseudo-headers const {

HTTP2_HEADER_METHOD, HTTP2_HEADER_PATH, HTTP2_HEADER_AUTHORITY, HTTP2_HEADER_SCHEME, HTTP2_HEADER_STATUS

} = http2.constants;

Formula

// Create a client const client = http2.connect('https://localhost:8080', {

rejectUnauthorized: false

});
// Send a request with custom headers const req = client.request({
[HTTP2_HEADER_METHOD]: 'GET',
[HTTP2_HEADER_PATH]: '/',
[HTTP2_HEADER_AUTHORITY]: 'localhost:8080',
[HTTP2_HEADER_SCHEME]: 'https',

Formula

'user - agent': 'node - http2/client',
'custom - header': 'custom - value'
});
req.on('response', (headers) => {
console.log('Response status:', headers[HTTP2_HEADER_STATUS]);
console.log('Response headers:', headers);
});
req.on('data', (chunk) => {
console.log('Received data:', chunk.toString());
});
req.on('end', () => {
client.close();
});
req.end();

HTTP/2 Settings

Formula

HTTP/2 allows configuring various protocol settings:
const http2 = require('http2');
const fs = require('fs');
const path = require('path');
const options = {
key: fs.readFileSync(path.join(__dirname, 'server.key')), cert: fs.readFileSync(path.join(__dirname, 'server.crt')),

Formula

// HTTP/2 settings settings: {

// Max concurrent streams per connection maxConcurrentStreams: 100,

Formula

// Initial window size for flow control initialWindowSize: 1024 * 1024, // 1MB

// Enable server push enablePush: true

}
};
const server = http2.createSecureServer(options);
server.on('stream', (stream, headers) => {
stream.respond({

Formula

'content - type': 'text/html',

':status': 200

});
stream.end('<h1>HTTP/2 Server with Custom Settings</h1>');
});
server.listen(8080);

Formula

Compatibility with HTTP/1.1
HTTP/2 servers can also handle HTTP/1.1 requests, providing a seamless upgrade path:
const http2 = require('http2');
const http = require('http');
const fs = require('fs');
const path = require('path');
// For HTTP/2 secure server const options = {
key: fs.readFileSync(path.join(__dirname, 'server.key')), cert: fs.readFileSync(path.join(__dirname, 'server.crt')), allowHTTP1: true // Allow HTTP/1.1 connections
};
const server = http2.createSecureServer(options);
// Handler function for both HTTP/1.1 and HTTP/2 const handler = (req, res) => {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end(`Hello from ${req.httpVersion} server!`);
};
// HTTP/1.1 compatibility request handler server.on('request', handler);
// HTTP/2 specific stream handler server.on('stream', (stream, headers) => {
stream.respond({

Formula

'content - type': 'text/plain',

':status': 200

});
stream.end(`Hello from HTTP/2 stream API!`);
});
server.listen(8080, () => {
console.log('Server running at https://localhost:8080/');
});

Performance Considerations

Formula

While HTTP/2 offers performance improvements, it's important to optimize your usage:

Connection Reuse

Formula

- With HTTP/2, you should aim to use a single connection for multiple requests, rather than creating new connections.

Proper Stream Management

  • Remember to close streams when they're no longer needed, and monitor the number of concurrent streams.

Server Push Strategy

  • Only push resources that are likely to be needed. Excessive pushing can waste bandwidth and resources.

Header Compression

Formula

- Take advantage of HTTP/2's header compression by minimizing the number and size of custom headers.
HTTP/2 vs HTTP/1.1
Key differences between HTTP/2 and HTTP/1.1:

Feature

Formula

HTTP/1.1

Http/2

Protocol Format

Text-based

Previous

Node.js WebAssembly

Next

Node.js Performance Hooks Module