MoroJS now includes full native HTTP/2 support with advanced features including server push, stream prioritization, and multiplexing.
- ✅ Native HTTP/2 Server - Built on Node.js
http2module - ✅ Server Push - Proactively push resources to clients
- ✅ Stream Prioritization - Control resource loading order
- ✅ Multiplexing - Multiple requests over single connection
- ✅ Header Compression - HPACK compression built-in
- ✅ HTTP/1.1 Fallback - Automatic fallback support
- ✅ Auto-detect Push - Automatically detect and push CSS/JS from HTML
import { Moro } from '@morojs/moro';
import * as fs from 'fs';
const app = new Moro({
http2: true,
https: {
key: fs.readFileSync('key.pem'),
cert: fs.readFileSync('cert.pem'),
},
});
app.get('/', (req, res) => {
res.json({
message: 'Hello HTTP/2!',
version: req.httpVersion, // '2.0'
});
});
app.listen(3000);const app = new Moro({
http2: {
allowHTTP1: true, // Support HTTP/1.1 fallback
settings: {
enablePush: true,
maxConcurrentStreams: 100,
initialWindowSize: 65535,
maxFrameSize: 16384,
},
},
https: {
key: fs.readFileSync('key.pem'),
cert: fs.readFileSync('cert.pem'),
},
});Use the http2.push middleware with auto-detection:
import { middleware } from '@morojs/moro';
app.use(
middleware.http2.push({
autoDetect: true, // Auto-detect CSS/JS from HTML
resources: [
{
path: '/styles/main.css',
as: 'style',
type: 'text/css',
priority: 200, // High priority
},
{
path: '/scripts/app.js',
as: 'script',
type: 'application/javascript',
priority: 150,
},
],
condition: req => req.path === '/' || req.path.endsWith('.html'),
})
);Push resources manually within route handlers:
app.get('/', (req, res) => {
// Check if push is available
if (res.push) {
// Push CSS with high priority
const cssStream = res.push('/styles/main.css', {
headers: { 'content-type': 'text/css' },
priority: 200,
});
if (cssStream) {
cssStream.end('body { font-family: Arial; }');
}
// Push JavaScript
res.push('/scripts/app.js', {
headers: { 'content-type': 'application/javascript' },
priority: 150,
});
}
res.send('<html>...</html>');
});Control the priority of your responses:
// Critical API endpoint - highest priority
app.get('/api/critical', (req, res) => {
if (res.setPriority) {
res.setPriority({
weight: 256, // 1-256, higher = more important
exclusive: true, // Exclusively process this stream
});
}
res.json({ critical: 'data' });
});
// Background data - low priority
app.get('/api/background', (req, res) => {
if (res.setPriority) {
res.setPriority({ weight: 1 });
}
res.json({ background: 'data' });
});Priority weight ranges from 1-256:
- 256 - Critical resources (HTML, critical API data)
- 200 - High priority (CSS, fonts)
- 150 - Medium priority (JavaScript)
- 100 - Normal priority (images)
- 50 - Low priority (analytics, tracking)
- 1 - Lowest priority (background tasks)
app.get('/', (req, res) => {
const httpVersion = req.httpVersion; // '2.0' or '1.1'
if (httpVersion === '2.0') {
// Use HTTP/2 features
res.push('/assets/critical.css');
}
res.json({ version: httpVersion });
});HTTP/2 requires SSL/TLS. For development, generate self-signed certificates:
# Generate self-signed certificate
openssl req -x509 -newkey rsa:2048 -nodes -sha256 \
-subj '/CN=localhost' \
-keyout localhost-key.pem \
-out localhost-cert.pemFor production, use certificates from a trusted CA like Let's Encrypt.
Only push resources that are:
- Critical for initial render
- Small in size (< 100KB)
- Used by most users
app.use(
middleware.http2.push({
autoDetect: true, // Let MoroJS detect from HTML
resources: [
// Only push critical resources
{ path: '/critical.css', as: 'style', type: 'text/css', priority: 200 },
],
})
);// Critical path
res.setPriority({ weight: 256, exclusive: true });
// Important but not critical
res.setPriority({ weight: 200 });
// Background/analytics
res.setPriority({ weight: 1 });const app = new Moro({
http2: {
allowHTTP1: true, // Clients without HTTP/2 support
},
https: {/* ... */},
});app.get('/', (req, res) => {
const pushStream = res.push('/asset.css');
if (pushStream) {
pushStream.on('error', err => {
console.log('Push failed:', err.message);
});
}
});const app = new Moro({
http2: {
settings: {
maxConcurrentStreams: 100, // Default: 100
initialWindowSize: 65535, // 64KB
maxFrameSize: 16384, // 16KB
},
},
});const app = new Moro({
http2: {
maxSessionMemory: 10, // MB per session
settings: {
headerTableSize: 4096, // HPACK table size
maxHeaderListSize: 8192, // Max header size
},
},
});All MoroJS middleware works with HTTP/2:
import { middleware } from '@morojs/moro';
app.use(middleware.cors());
app.use(middleware.helmet());
app.use(middleware.compression()); // Works alongside HTTP/2 compression
app.use(middleware.requestLogger());# Test HTTP/2 endpoint
curl -k --http2 https://localhost:3000/
# Check protocol version
curl -k --http2 -I https://localhost:3000/ | grep HTTPconst http2 = require('http2');
const client = http2.connect('https://localhost:3000', {
rejectUnauthorized: false, // For self-signed certs
});
const req = client.request({ ':path': '/' });
req.on('response', headers => {
console.log('Status:', headers[':status']);
});
req.on('data', chunk => {
console.log('Data:', chunk.toString());
});
req.end();Open Chrome DevTools → Network tab → Protocol column shows h2 for HTTP/2.
const app = new Moro();
app.get('/', (req, res) => {
res.send('<html>...</html>');
});
app.listen(3000);const app = new Moro({
http2: true,
https: {
key: fs.readFileSync('key.pem'),
cert: fs.readFileSync('cert.pem'),
},
});
app.use(middleware.http2.push({ autoDetect: true }));
app.get('/', (req, res) => {
// Optionally use HTTP/2 features
if (res.push) {
res.push('/critical.css');
}
res.send('<html>...</html>');
});
app.listen(3000);-
Check if client supports HTTP/2:
if (req.httpVersion === '2.0' && res.push) { // Push is available }
-
Ensure SSL is configured:
// HTTP/2 requires HTTPS https: { key: fs.readFileSync('key.pem'), cert: fs.readFileSync('cert.pem'), }
-
Check if push is enabled:
http2: { settings: { enablePush: true, // Must be true }, }
- Verify certificates are valid
- Check
maxConcurrentStreamssetting - Monitor memory usage with
maxSessionMemory
- Don't over-push resources
- Set appropriate priorities
- Monitor stream counts
- Adjust
initialWindowSizeif needed
See /examples/http2-server.ts for a complete working example with:
- Server push configuration
- Stream prioritization
- Auto-detection of resources
- Manual push examples
- Priority testing endpoints
Push a resource to the client.
res.push(path: string, options?: {
headers?: Record<string, string>;
priority?: number; // 1-256
});Set stream priority for current response.
res.setPriority(options?: {
parent?: number;
weight?: number; // 1-256
exclusive?: boolean;
});Configure server push behavior.
middleware.http2.push({
autoDetect?: boolean;
resources?: Array<{
path: string;
as: string;
type?: string;
priority?: number;
}>;
condition?: (req) => boolean;
});