56

Is it possible currently to get node.js HTTP/2 (HTTP 2.0) server? And http 2.0 version of express.js?

WHITECOLOR
  • 24,996
  • 37
  • 121
  • 181

4 Answers4

30
var express = require('express');
var app = express();

app.get('/', function (req, res) {
  res.send('hello, http2!');
});

var options = {
  key: fs.readFileSync('./example/localhost.key'),
  cert: fs.readFileSync('./example/localhost.crt')
};

require('http2').createServer(options, app).listen(8080);

EDIT

This code snippet was taken from a conversation on Github.

andyrandy
  • 72,880
  • 8
  • 113
  • 130
HDK
  • 816
  • 1
  • 8
  • 13
  • 12
    FYI This isn't working with `express@4.13.3` and `http2@3.2.0`, and it looks like express won't support it until v5. https://github.com/molnarg/node-http2/issues/100 – Dean Rather Aug 04 '15 at 03:18
  • It is not working for me with `node@v6.7.0`, `express@5.0.0-alpha.2`, `http2@3.3.6`. _TypeError: dest.end is not a function_ – neoDev Sep 29 '16 at 20:02
28

If you are using express@^5 and http2@^3.3.4, then the correct way to start the server is:

const http2 = require('http2');
const express = require('express');

const app = express();

// app.use('/', ..);

http2
    .raw
    .createServer(app)
    .listen(8000, (err) => {
        if (err) {
            throw new Error(err);
        }

        /* eslint-disable no-console */
        console.log('Listening on port: ' + argv.port + '.');
        /* eslint-enable no-console */
    });

Notice the https2.raw. This is required if you want to accept TCP connections.

Note that at the time of this writing (2016 05 06), none of the major browsers support HTTP2 over TCP.

If you want to accept TCP and TLS connections, then you need to start the server using the default createServer method:

const http2 = require('http2');
const express = require('express');
const fs = require('fs');


const app = express();

// app.use('/', ..);

http2
    .createServer({
        key: fs.readFileSync('./localhost.key'),
        cert: fs.readFileSync('./localhost.crt')
    }, app)
    .listen(8000, (err) => {
        if (err) {
            throw new Error(err);
        }

        /* eslint-disable no-console */
        console.log('Listening on port: ' + argv.port + '.');
        /* eslint-enable no-console */
    });

Note that at the time of this writing, I did manage to make express and http2 to work (see https://github.com/molnarg/node-http2/issues/100#issuecomment-217417055). However, I have managed to get http2 (and SPDY) to work using spdy package.

const spdy = require('spdy');
const express = require('express');
const path = require('path');
const fs = require('fs'); 

const app = express();

app.get('/', (req, res) => {
    res.json({foo: 'test'});
});

spdy
    .createServer({
        key: fs.readFileSync(path.resolve(__dirname, './localhost.key')),
        cert: fs.readFileSync(path.resolve(__dirname, './localhost.crt'))
    }, app)
    .listen(8000, (err) => {
        if (err) {
            throw new Error(err);
        }

        /* eslint-disable no-console */
        console.log('Listening on port: ' + argv.port + '.');
        /* eslint-enable no-console */
    });
Tamás
  • 950
  • 2
  • 10
  • 29
Gajus
  • 69,002
  • 70
  • 275
  • 438
  • 1
    Interestingly enough when I used this and https://github.com/expressjs/express/issues/2761#issuecomment-216912022 I get this error. (node) warning: possible EventEmitter memory leak detected. 11 error listeners added. Use emitter.setMaxListeners() to increase limit. – zmanc May 25 '16 at 02:37
  • 2
    I'm trying to follow your first example, but `http2.raw.createServer(app).listen(...)` is throwing an error because `http2.raw` is `undefined`. I need to work with the raw TCP because the TLS encryption is being terminated by the server's reverse proxy. Any suggestions on how to address this? – Calaway Dec 23 '19 at 18:23
  • Note that http2 is built into Node 9+ – Richard Collette Jun 17 '21 at 17:12
2

There is an open pr for express 5.0 since 2018, https://github.com/expressjs/express/pull/3730. Until that is merged, it won't work out of the box.

I have created the solution in the form of a package, https://www.npmjs.com/package/http2-express-bridge

const express = require('express')
const http2Express = require('http2-express-bridge')
const http2 = require('http2')
const { readFileSync } = require('fs')

// Use the wrapper function that returns the application
const app = http2Express(express)

const options = {
    key: readFileSync('<Certificate Key>'),
    cert: readFileSync('<Certificate file>'),
    allowHTTP1: true
};


app.get('/', function (req, res) {
  res.send('Hello World')
})

const server = http2.createSecureServer(options, app)

server.listen(3000, () => {
        console.log(`listening on port 3000`)
})

This works, and it falls back to Http/1.1 when it receives an Http/1.1 request.

I have also included 'res.push' method for ease of server push. The package works with ESModules and Typescript.

Rah
  • 21
  • 2
1

This issue is still around today (2016 as of writing this), so I decided to have a go at making a workaround to make express and http2 packages work nicely together: https://www.npmjs.com/package/express-http2-workaround

Edit: Does not work on any NodeJS version above v8.4 due to the native 'http2' module.

Install via NPM: npm install express-http2-workaround --save

// Require Modules
var fs = require('fs');
var express = require('express');
var http = require('http');
var http2 = require('http2');

// Create Express Application
var app = express();

// Make HTTP2 work with Express (this must be before any other middleware)
require('express-http2-workaround')({ express:express, http2:http2, app:app });

// Setup HTTP/1.x Server
var httpServer = http.Server(app);
httpServer.listen(80,function(){
  console.log("Express HTTP/1 server started");
});

// Setup HTTP/2 Server
var httpsOptions = {
    'key' : fs.readFileSync(__dirname + '/keys/ssl.key'),
    'cert' : fs.readFileSync(__dirname + '/keys/ssl.crt'),
    'ca' : fs.readFileSync(__dirname + '/keys/ssl.crt')
};
var http2Server = http2.createServer(httpsOptions,app);
http2Server.listen(443,function(){
  console.log("Express HTTP/2 server started");
});

// Serve some content
app.get('/', function(req,res){
    res.send('Hello World! Via HTTP '+req.httpVersion);
});

The above code is a working express application that uses both the nodejs http module (for HTTP/1.x) and the http2 module (for HTTP/2).

As mentioned in the readme, this creates new express request and response objects and sets their prototypes to http2's IncomingMessage and ServerResponse objects. By default, it's the inbuilt nodejs http IncomingMessage and ServerResponse objects.

I hope this helps :)

Jashepp
  • 11
  • 1
  • 3
  • Don't working for me, I use node v13.7.0. Get this error: Missing IncomingMessage property on http2 module? I included http2 before... – Amn Feb 05 '20 at 10:48