How to integrate http2 with ExpressJS using nodejs module http2? - node.js

I am creating an api with nodejs and express and I want to integrate http2 with ExpressJS
This is my code:
'use strict';
const http2 = require('http2');
const fs = require('fs');
const path = require('path');
const express = require('express');
const bodyParser = require('body-parser');
const app = express();
const port = process.env.PORT || 443;
// Middleware
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
// Routes variables
const indexRouter = require('./routes/index');
// Routes uses
app.use('/', indexRouter);
// Server configurations
const key = path.join(__dirname + '/security/key.pem');
const cert = path.join(__dirname + '/security/certificate.pem');
const options = {
key: fs.readFileSync(key),
cert: fs.readFileSync(cert)
}
const server = http2.createSecureServer(options, app);
server.on('error', err => console.log(err));
server.listen(port, () => {
console.log('Server running')
})
I am trying to pass express server as second parameter of createSecureServer() but I am not sure if I am right with this, cause I am getting this error:
[nodemon] 2.0.2 [nodemon] to restart at any time, enter rs [nodemon]
watching dir(s): . [nodemon] watching extensions: js,mjs,json
[nodemon] starting node index.js
_http_incoming.js:96 if (this.socket.readable)
^
TypeError: Cannot read property 'readable' of undefined
at IncomingMessage._read (_http_incoming.js:96:19)
at IncomingMessage.Readable.read (stream_readable.js:491:10)
at resume (_stream_readable.js:976:12)
at processTicksAndRejections (internal/process/task_queues.js:80:21) [nodemon] app crashed -
waiting for file changes before starting...
It should be noted that my certificate, although self-signed and unreliable, is loading correctly.
I try not to use a third-party module if I can do it with NodeJS. Any help?

expressjs still does not officially support Node http2.
For more details visit here
But you can use node-spdy. With this module, you can create HTTP2 / SPDY servers in node.js with natural http module interface and fallback to regular https (for browsers that support neither HTTP2 nor SPDY yet):
const spdy = require('spdy')
const express = require('express')
const path = require('path')
const fs = require('fs')
const port = 3000
const app = express()
app.get('*', (req, res) => {
res
.status(200)
.json({message: 'ok'})
})
const options = {
key: fs.readFileSync(__dirname + '/server.key'),
cert: fs.readFileSync(__dirname + '/server.crt')
}
console.log(options)
spdy
.createServer(options, app)
.listen(port, (error) => {
if (error) {
console.error(error)
return process.exit(1)
} else {
console.log('Listening on port: ' + port + '.')
}
})
For more datils on spdy, visit here.
If you have an option for other frameworks you can use 'KOA' or 'HAPI' which have support for node http2. This might be useful for you
Also, Read this Release 5.0#2237. It says that:
The goal of Express 5 is to be API tweaks & the removal of all code
from the Express repository, moving into components in the pillarjs
project (https://github.com/pillarjs), providing at least basic
support for promise-returning handlers and complete HTTP/2
functionality. Express 5 would become a "view into pillarjs" and would
be an arrangement of these components.

Related

Limit of making PUT request

I have a weird problem while I try to make the same PUT request multiple times, it works for about 8,9 times but after I have to refresh the page to work again.
I use React with Node.js (using Sequelize) for request:
React code:
return await axios
.put(`${API_URL}/api/user/createUser`, {
id_game_table: gameTableId,
name: nickName,
})
.then((response) => {
return [response.data, null];
})
.catch((error) => {
return [null, error.response];
});
My Node.js body destructuring:
What my Node.js looks like:
And the requests I was making:
Here, we can see that the first 9 PUT request was a success - this can be seen even in Node.js - but after 9 PUT requests was made all requests that I made was just OPTIONS and no other request was made. As another mention, there is not output in console.
The config for Node.js:
const express = require("express");
const app = express();
const cors = require("cors");
const http = require("http");
const server = http.createServer(app);
const socketIo = require("socket.io");
const index = require("./routes/index");
const port = process.env.PORT || 4001;
app.use(express.json());
app.use(cors());
app.use(index);
app.use("/api/gameTable", require("./routes/gameTable.routes"));
app.use("/api/user", require("./routes/user.routes"));
const io = socketIo(server, { cors: { origin: "*" } });
server.listen(port, () => console.log(`Listening on port ${port}`));
Note: The socket connection doesn't matter, I know that I had this request limit behaviour as well when using Angular with Spring in the past (without socket).

how to make socket io | node.js - with SSL working

i having an issue while using node.js with apache on site.
if using http without SSL node.js with apache are working with my domain at env. file
If I remove the SSL code it runs fine, however with it I get a request to http://mydomain.io/socket.io/1/?t=XXXXXXXXX
but when i enable SSL with let encrypt
my site are working but connect with Node js + socket io having error 404
*Note it's not trying https, which causes it to fail.
I'm testing on chrome, firefox and edge still can't fix.
I apologize if this is a simple question, I'm a node/socket.io newbie.
Thanks!
Here my details code are working when using http | but not working using https with let encrypt domain
const pino = require('pino')
const https = require('https');
const { Boom } = require('#hapi/boom')
const fs = require('fs')
const chalk = require('chalk')
require('dotenv/config')
const express = require('express')
const socket = require("socket.io");
const { toDataURL } = require('qrcode')
const mysql = require('mysql');
require('dotenv').config();
const request = require('request');
const app = express()
const host = process.env.HOST ?? '127.0.0.1'
const port = parseInt(process.env.PORT ?? 3000)
app.use(express.urlencoded({ extended: true }))
app.use(express.json())
const ser = app.listen(port, host, () => {
console.log(`Server is listening on http://${host}:${port}`)
})
const io = socket(ser);
const db = mysql.createConnection({
host: process.env.DB_HOSTNAME,
user: process.env.DB_USERNAME,
password: process.env.DB_PASSWORD,
database: process.env.DB_DATABASE
});
db.connect((err) => {
if (err) throw err;
console.log('Mysql Connected...');
});
const sessionMap = new Map()
I've had a similar issue before, you need 2 Different servers, one for http, and one for https.
var usinghttps = false;
if(usinghttps) {
var options = {
key: fs.readFileSync("/etc/letsencrypt/live/us2.swordbattle.io/privkey.pem"),
cert: fs.readFileSync("/etc/letsencrypt/live/us2.swordbattle.io/fullchain.pem"),
};
httpsserver = https.createServer(options, app).listen(443);
}
server = http.createServer(app);
And then to create the socket.io server,
const io = new Server(usinghttps ? httpsserver:server);
This personally worked for me, not sure if it works on all apps.

Express Static Directory over HTTPS

I would like to implement Node.js with Express for static content over HTTPS. Scouring the Web reveals tons of examples of Express with HTTPS and tons of examples of Express serving a static directory, but I cannot find an example using all three Express, HTTPS and static.
Moreover, looking at the examples I can find, I cannot piece together how to accomplish this.
Here is what I have found:
Express Static Directory over HTTP
var fs = require('fs')
var app = require("express");
var server = app();
server.use(app.static(staticDir))
server.listen(webPort)
Express over HTTPS (without Static Directory)
const app = require('express')();
const https = require('https');
const server = https.createServer(
{
key: fs.readFileSync('server.key'),
cert: fs.readFileSync('server.cert')
},
app
);
server.listen(APIPort);
When I try combining the two approaches, I get stuck because the static example bypasses createServer, and yet createServer is the crux of getting to HTTPS in the examples.
I'm sure the answer is simple, but I cannot arrive at or locate the solution.
Could you try the below code-snippet and see if it works for you?
const fs = require('fs');
const https = require('https');
const express = require('express');
const app = express();
app.use(express.static(process.env.SERVE_DIRECTORY));
app.get('/', function(req, res) {
return res.end('Serving static files!');
});
const key = fs.readFileSync(__dirname + '/selfsigned.key');
const cert = fs.readFileSync(__dirname + '/selfsigned.crt');
const options = {
key: key,
cert: cert
};
const server = https.createServer(options, app);
server.listen(PORT, () => {
console.log(`Serving on port ${PORT}`);
});
Please make sure to do the suitable changes before running the above code.

Node.js console.log not logging in terminal

I have a simple app where I'm trying to log to the console (terminal where I started Node, not the the browser console). Express seems to execute the route since I can see the response in my browser.
Also the logging in the app.listen(..) seems to work, but I don't see any logs afterwards.
'use strict';
// 3rd-party dependencies
const express = require('express');
const app = express();
const bodyParser = require('body-parser');
const dbSetup = require('./db-setup');
// Application config
const LOCAL_APP_PORT = 8080;
const PUBLIC_APP_PORT = process.env.PUBLIC_APP_PORT || LOCAL_APP_PORT;
global.dbType = process.env.DB_TYPE;
// Sanity check for debugging
console.log("local app port:", LOCAL_APP_PORT);
console.log("public app port:", PUBLIC_APP_PORT);
console.log("db type:", global.dbType);
// Database setup for either MongoDB or Postgres
dbSetup(global.dbType);
// Express middleware
app.use(bodyParser.json()); // for parsing application/json
// Import routes
//const index = require('./routes/index');
//const owner = require('./routes/owner');
//const shop = require('./routes/shop');
//const product = require('./routes/product');
// Set up express routes
app.use('/',(req,res,next) => {
console.log("This is not showing on the terminal.");
res.send("This is sent to the browser");
next();
})
//app.use('/owner', owner);
//app.use('/shop', shop);
//app.use('/product', product);
app.listen(LOCAL_APP_PORT, () => {
console.log('App started ...');
});
EDIT: Solved. Problem was my app was basically available on two ports at the same time. One the the public port however it was not showing any logs...
Terminal screenshot

node.js server and HTTP/2 (2.0) with express.js

Is it possible currently to get node.js HTTP/2 (HTTP 2.0) server? And http 2.0 version of express.js?
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.
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 */
});
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.
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 :)

Resources