Why
HTTP/2 is the most recent version(HTTP/3 is on its way) of the HTTP protocol. And comes with a bunch of improvements, like more compact headers via header compression, less sockets needed, as multiple streams are multiplexed over a single TCP connection, and much more.
How
Latest versions of Nodejs comes with an HTTP/2 module that you can use to make servers and clients.
mkdir nodeHttp2
touch nodeHttp2/server.js nodeHttp2/client.js
cd nodeHttp2
openssl genrsa -out key.pem
openssl req -new -key key.pem -out csr.pem
openssl x509 -req -days 9999 -in csr.pem -signkey key.pem -out cert.pem
rm csr.pem
We'll make a small mock http/2 server
const http2 = require("http2");
const fs = require("fs");
const options = {
key: fs.readFileSync("key.pem"),
cert: fs.readFileSync("cert.pem"),
};
const server = http2.createSecureServer(options);
const routes = {
GET: {
"/health": {
healthy: true,
},
"/users/1": {
id: 1,
name: "Archibald",
},
},
};
server.on("stream", (stream, headers) => {
const match = routes[headers[":method"]][headers[":path"]];
if (match) {
stream.respond({
"content-type": "application/json",
":status": 200,
});
stream.end(JSON.stringify(match));
} else {
stream.respond({
"content-type": "application/json",
":status": 404,
});
stream.end(JSON.stringify({ error: "RouteNotFound" }));
}
});
server.listen(3030);
And the client. Please notice the close method. If client close it's not called, the connection will stay open and the command line application won't exit. ES6 doesn't have a destructor concept, so we cannot automatically close the client once the client gets out of the scope. We could recreate the client on every request, but that removes one of the advantages of HTTP/2 - socket reuse, by having a single socket open we can skip needless TLS handshakes.
const http2 = require("http2");
class ApiClient {
constructor(target) {
this.client = http2.connect(target, {
rejectUnauthorized: false, // accept the self signed certificate
});
}
getUser(id) {
const req = this.client.request({ ":path": "/users/" + id });
return new Promise((resolve, reject) => {
let data = "";
req.on("data", (ch) => (data += ch));
req.on("end", () => resolve(JSON.parse(data)));
req.on("error", reject);
});
}
close() {
this.client.close();
}
}
async function main() {
const client = new ApiClient("https://localhost:3030");
const user = await client.getUser(1);
console.dir(user);
client.close();
}
main();
Conclusion
Node's built-in http2 module can help us write clients and servers that talk to external systems without having to rely on 3rd party modules. Sometimes this can be a good way to do some small services that talk via http.