How can I create a express server that acts as proxy?
Requirements:
http + https
works behind corporate proxy
option to return special content for some urls
I tried http-proxy, express-http-proxy, https-proxy-agent, request. I could not figure out how to use them correctly.
using request
The best result I got with request. But there are some issues.
var express = require('express'),
request = require('request'),
app = express();
var r = request.defaults({'proxy':'http://mycorporateproxy.com:8080'});
function apiProxy() {
return function (req, res, next) {
console.log(req.method);
r.get(req.url).pipe(res);
}
}
app.use(apiProxy());
app.listen(8080, function () {
'use strict';
console.log('Listening...');
});
Issues with this approach:
it uses get for all requests (HEAD, GET, ...)
headers of the source request are not passed
https isn't working
using http-proxy
var express = require('express'),
httpProxy = require('http-proxy'),
app = express();
var proxy = httpProxy.createProxyServer({
});
function apiProxy() {
return function (req, res, next) {
console.log(req.method);
proxy.web(req, res, {target: req.url});
}
}
app.use(apiProxy());
app.listen(8080, function () {
'use strict';
console.log('Listening...');
});
here I get (probably because of the missing corporate proxy)
Error: connect ECONNREFUSED xx.xx.xx.xx:80
at Object.exports._errnoException (util.js:1026:11)
at exports._exceptionWithHostPort (util.js:1049:20)
at TCPConnectWrap.afterConnect [as oncomplete] (net.js:1085:14)
I started google chrome with the option
--proxy-server="localhost:8080"
My .npmrc contains the proxies as well.
proxy=http://mycorporateproxy.com:8080
https-proxy=http://mycorporateproxy.com:8080
I've successfully used http-proxy-middleware with the following:
const express = require("express")
const app = require("express")()
const proxy = require("http-proxy-middleware")
const package = require("./package.json")
const target = process.env.TARGET_URL
const port = process.env.PORT
const wildcardProxy = proxy({ target })
app.use(wildcardProxy)
app.listen(config.port, () => console.log(`Application ${package.name} (v${package.version}) started on ${config.port}`))
I haven't had the need for https yet but it's mentioned in the docs: https://github.com/chimurai/http-proxy-middleware#http-proxy-options
Related
I have an existing project written in Express, where I've made a messaging system. Everything was working on POST/GET methods (to send and receive the messages).
I wanted to make them appear in real time, so I installed socket.io both on the client and server side. In my server.js I added these lines:
const http = require("http");
const io = require("socket.io");
const server = http.createServer();
const socket = io.listen(server);
and changed my app.listen(...) into server.listen(...).
Added also:
socket.on("connection", socket => {
console.log("New client connected");
socket.on('test', (test) => {
console.log('test-test')
});
socket.emit('hello', {hello:'hello!'});
socket.on("disconnect", () => console.log("Client disconnected"));
});
On the front part I put such code in the componentDidMount method:
const socket = socketIOClient();
socket.emit('test', {test:'test!'})
socket.on('hello', () => {
console.log('aaa')
})
Now I got 2 problems. Although the console.log() works correctly, I get an error on the React app:
WebSocket connection to 'ws://localhost:3000/sockjs-node/039/lmrt05dl/websocket' failed: WebSocket is closed before the connection is established.
Is that normal?
Also, when I change app.listen(...) into server.listen(...) in the server.js file, my routing stops working. All the POST and GET methods don't work, because the server is responding endlessly. Is that possible to use the socket.io just on a specific method in a specific routing file?
I keep my routes that way: app.use('/api/user', user); where user is a router file.
UPDATE:
full server.js require:
const express = require('express');
const mongoose = require('mongoose');
const dotenv = require('dotenv');
const bodyparser = require('body-parser');
const passport = require('passport');
const user = require('./routes/api/v1/User');
const company = require('./routes/api/v1/Company');
const http = require("http");
const io = require("socket.io");
const app = express();
dotenv.config();
app.use(passport.initialize());
require('./config/seed');
require('./config/passport')(passport);
const server = http.createServer();
const socket = io.listen(server);
You're not initializing server properly. Try making the following change
// const server = http.createServer();
const server = http.createServer(app);
and make sure you listen on server and not io
server.listen(PORT_GOES_HERE)
[UPDATE]
Working Example:
var app = require('express')();
var server = require('http').Server(app);
var io = require('socket.io')(server);
server.listen(80);
// WARNING: app.listen(80) will NOT work here!
// DO STUFF WITH EXPRESS SERVER
app.get('/', function (req, res) {
res.sendFile(__dirname + '/index.html');
});
io.on('connection', function (socket) {
socket.emit('news', { hello: 'world' });
socket.on('my other event', function (data) {
console.log(data);
});
});
For more details check this: https://socket.io/docs/
I'll try to make this as to the point as possible. I am trying to make a post request to my express backend. All of the post requests here work, except for "/addpayment". Here is my file called 'router.js'
module.exports = function(app) {
app.post('/signin', requireSignin, Authentication.signin)
app.post('/signup', Authentication.signup)
app.post('/addpayment', function(req, res, next) {
res.send({ message: 'why................' })
})
}
Here is my main 'server.js' file
const express = require('express')
const http = require('http')
const bodyParser = require('body-parser')
const morgan = require('morgan')
const app = express()
const router = require('./router')
const mongoose = require('mongoose')
const cors = require('cors')
// DB Connect
mongoose.connect('mongodb://localhost/demo-app')
// App
app.use(morgan('combined'))
app.use(cors())
app.use(bodyParser.json({ type: '*/*' }))
router(app)
// Server
const port = process.env.PORT || 3090
const server = http.createServer(app)
server.listen(port)
console.log('Server has been started, and is listening on port: ' + port)
I get a 404 in postman, and inside my app browser console. I am using passport in my other routes. I already tried running it through passport when I have a JWT token, and same thing(a 404).
I have already looked at all Stack Overflow/Github posts on the first few pages of google results, with no solution for my use case.
I have made a simplified version of your server and everything works as expected. Only difference that I have made is that I am not creating http server like you, but just calling app.listen
here is working example
router.js
module.exports = function(app) {
app.post('/addpayment', function(req, res, next) {
res.send({message: 'why................'})
})
};
server.js
var express = require('express');
var router = require('./router');
var app = express();
router(app);
//init server
app.listen(3000, function() {
console.log("Server running on port 3000");
});
I'm trying to use Socket.io combined with Express.JS (using Express application generator).
I've found some aswers how to do this (Using socket.io in Express 4 and express-generator's /bin/www).My problem is that i cannot make use of the sockets inside the routes folder.
I can use them in the app.js and bin/www.js files. When i call the route index.js it just keeps loading the webpage for a long time without giving any errors.
bin/www.js
...
/**
* Create HTTP server.
*/
var server = http.createServer(app);
var io = app.io
io.attach( server );
...
app.js
...
// Express
var app = express();
// Socket.io
var io = socket_io();
app.io = io;
var routes = require('./routes/index')(io);
...
routes/index.js
module.exports = function(io) {
var app = require('express');
var router = app.Router();
io.on('connection', function(socket) {
console.log('User connected');
});
return router;
}
Here is a simple example on how to use Socket.io with Express that I made available on GitHub here:
https://github.com/rsp/node-websocket-vs-socket.io
The backend code is this:
var path = require('path');
var app = require('express')();
var http = require('http').Server(app);
var io = require('socket.io')(http);
app.get('/', (req, res) => {
console.error('express connection');
res.sendFile(path.join(__dirname, 'si.html'));
});
io.on('connection', s => {
console.error('socket.io connection');
for (var t = 0; t < 3; t++)
setTimeout(() => s.emit('message', 'message from server'), 1000*t);
});
http.listen(3002, () => console.error('listening on http://localhost:3002/'));
console.error('socket.io example');
See https://github.com/rsp/node-websocket-vs-socket.io/blob/master/si.js
As you can see here, I am creating the express app with:
var app = require('express')();
Then I create an http server with that app with:
var http = require('http').Server(app);
And finally I use that http server to create the Socket.io instance:
var io = require('socket.io')(http);
After running:
http.listen(3002, () => console.error('listening on http://localhost:3002/'));
it all works together.
You can see the entire example on GitHub with both backend and frontend code that works. It currently uses Express 4.14.0 and socket.io 1.4.8.
For anyone who still want to use socket.io and express http request. Easiest way is to create two seprate instance of http server listing to different ports. 1 for websockets and 2nd for api requests.
const express = require("express");
const app = express();
const httpServer = require("http").createServer(app);
const io = require("socket.io")(httpServer, {
path: '/'
});
// routes and io on connection
httpServer.listen(5000, () => {
console.log("Websocket started at port ", 5000)
});
app.listen(3000, () =>{
console.log("Http server listening at", 3000)
})
I was reading the Socket.io Chat Demo here: http://socket.io/get-started/chat/
and I got confused when looking at their require statements.
var app = require('express')();
var http = require('http').Server(app);
var io = require('socket.io')(http);
app.get('/', function(req, res){
res.sendfile('index.html');
});
io.on('connection', function(socket){
console.log('a user connected');
});
http.listen(3000, function(){
console.log('listening on *:3000');
});
Am I correct in thinking that require("express") produces an executable Express function (with all of the necessary functions and fields that go with that), and that require("http").Server(app) creates a http.Server object with all of its fields and functions.
If so, I am confused because Express creates a server when we call the .listen function so it seems redundant and backwards to pass an Express app to an http module server.
So, my question is, what is really happening here?
The http server expects a function which has the following signature:
function(req, res)
require('express')(); will create a function with that signature which handles all the magic that express makes available like routing, middleware, etc. Express can create it's own http server instance, but since you're also using socket.io (which expects access to the http server as well), you'll need a separate http instance.
var app = require('express')(); //infact, app is a requestListener function.
var http = require('http').Server(app); // infact, function Server eqs http.createServer;
// infact,app.listen eqs http.listen
other code from nodejs and express model
we can see, require("express")() return app is a function.
//express/lib/express.js https://github.com/strongloop/express/blob/master/lib/express.js
var proto = require('./application');
exports = module.exports = createApplication;
function createApplication() {
var app = function(req, res, next) {
app.handle(req, res, next);
};
mixin(app, EventEmitter.prototype, false);
mixin(app, proto, false);
app.request = { __proto__: req, app: app };
app.response = { __proto__: res, app: app };
app.init();
return app;
}
we can see, node.createServer eqs Server
//nodejs/lib/http.js https://github.com/nodejs/node/blob/master/lib/http.js
exports.createServer = function(requestListener) {
return new Server(requestListener);
};
we can see, express app.listen eqs http.listen
//express/lib/application.js https://github.com/strongloop/express/blob/master/lib/application.js
app.listen = function listen() {
var server = http.createServer(this);
return server.listen.apply(server, arguments);
};
//nodejs/lib/_http_server.js https://github.com/nodejs/node/blob/master/lib/_http_server.js
function Server(requestListener) {
if (!(this instanceof Server)) return new Server(requestListener);
net.Server.call(this, { allowHalfOpen: true });
if (requestListener) {
this.addListener('request', requestListener);
}
this.httpAllowHalfOpen = false;
this.addListener('connection', connectionListener);
this.addListener('clientError', function(err, conn) {
conn.destroy(err);
});
const app = express();
let server = require('http').Server(app);
server.listen(3000);
This makes the server listening to the port we provide.
const app = express();
let server = require('http').createServer(app);
server.listen(3000);
So Server and createServer are the same according to the node.js HTTP library source code.
Ref : nodejs/lib/http.js
exports.Server = Server;
exports.createServer = function(requestListener) {
return new Server(requestListener);
};
Can I create an Express server listening on both HTTP and HTTPS, with the same routes and the same middlewares?
Currently I do this with Express on HTTP, with stunnel tunneling HTTPS to Express, but I prefer a pure Node solution.
I can do it with this code, but using the handle method that is marked as private:
var express = require( 'express' )
, https = require("https")
, fs = require( 'fs' );
var app = express.createServer();
// init routes and middlewares
app.listen( 80 );
var privateKey = fs.readFileSync( 'privatekey.pem' ).toString();
var certificate = fs.readFileSync( 'certificate.pem' ).toString();
var options = {key: privateKey, cert: certificate};
https.createServer( options, function(req,res)
{
app.handle( req, res );
} ).listen( 443 );
To enable your app to listen for both http and https on ports 80 and 443 respectively, do the following
Create an express app:
var express = require('express');
var app = express();
The app returned by express() is a JavaScript function. It can be be passed to Node’s HTTP servers as a callback to handle requests. This makes it easy to provide both HTTP and HTTPS versions of your app using the same code base.
You can do so as follows:
var express = require('express');
var https = require('https');
var http = require('http');
var fs = require('fs');
var app = express();
var options = {
key: fs.readFileSync('/path/to/key.pem'),
cert: fs.readFileSync('/path/to/cert.pem'),
ca: fs.readFileSync('/path/to/ca.pem')
};
http.createServer(app).listen(80);
https.createServer(options, app).listen(443);
For complete detail see the doc
As a possible update to this question, you might want to check out the changes here for express 3. The change document says:
The return value of express() is a JavaScript Function,
encapsulating everything that makes an Express app tick. This means
you can easily setup HTTP and HTTPS versions of your application by
passing it to node's http.createServer() and https.createServer():
In Express 3, express.createServer() is now express()
Here is a complete example for express 3:
var fs = require('fs')
, https = require('https')
, http = require('http')
, express = require('express')
, keys_dir = 'keys/'
, server_options = {
key : fs.readFileSync(keys_dir + 'privatekey.pem'),
ca : fs.readFileSync(keys_dir + 'certauthority.pem'),
cert : fs.readFileSync(keys_dir + 'certificate.pem')
}
, app = express();
app.configure(function(){
app.use(express.cookieParser());
app.use(express.bodyParser());
app.use(express.methodOverride());
app.use(express.session( { secret: '' } ));
app.use(app.router);
});
app.configure('development',function(){
app.use(express.static(__dirname + '/public'));
app.use(express.errorHandler({dumpExceptions: true, showStack:true}));
app.set('view options', { pretty: true });
});
app.get('/', function(req, res){
res.send('Hello World!');
});
https.createServer(server_options,app).listen(7000);
http.createServer(app).listen(8000);
You can share the implementation via something like:
var register = function (app) {
// config middleware
app.configure({
});
// config routes
app.get(...);
};
var http = express.createServer();
register(http);
http.listen(80);
var https = express.createServer({ key: /* https properties */ });
register(https);
https.listen(443);
You can use express and https in same port.
this works for me.
const express=require('express');
const app=express();
const cors=require('cors');
const path=require("path");
const routes=require('./routes/api');
const routerComplain=require('./routes/api');
const routerstores=require('./routes/api');
const routerstock=require('./routes/api');
const routerreport=require('./routes/api');
const routeritem=require('./routes/api');
const bodyParser=require('body-parser');
const routerRegister=require('./routes/api');
const mongoose=require('mongoose');
var server = require('http').Server(app);
var io = require('socket.io')(server);
require("dotenv").config();
mongoose.connect('mongodb://#################',{ useNewUrlParser: true },(err)=>{
if(!err){
console.log('db connected')
}else{
console.log('error in db')
}
});
mongoose.Promise = global.Promise;
app.use(express.static('public'));
app.use(bodyParser.json());
app.use(cors({credentials: true, origin:'http://localhost:3000'}));
app.use(express.static(path.join(__dirname, "client", "build")))
app.use('/reg',routes);
app.use('/complain',routerComplain);
app.use('/register',routerRegister);
app.use('/stores',routerstores);
app.use('/reports',routerreport);
app.use('/stock',routerstock);
app.use('/items',routeritem);
app.get("*", (req, res) => {
res.sendFile(path.join(__dirname, "client", "build", "index.html"));
});
io.on('connection', function (socket) {
socket.emit('news', { hello: 'world' });
socket.on('my other event', function (data) {
console.log(data);
});
})
const port = process.env.port||4000;
server.listen(port,function(){
console.log('now listening for request');
});
If you want to use the traditional two ports, one of the above solutions probably works, but using httpolyglot, you can really easily have http and https on the same port with the same middlewares.
https://github.com/mscdex/httpolyglot
Here's some skeleton code that worked for me:
var express = require('express');
var fs = require('fs');
var httpolyglot = require('httpolyglot');
var app = express();
const options = {
key: fs.readFileSync("/etc/ssl/certs/key"),
cert: fs.readFileSync("/etc/ssl/certs/cer.cer")
};
httpolyglot.createServer(options, app).listen(port);
and if you want http -> https forwarding, you can just add this middleware function before the createServer() call:
app.use(function(req, res, next) {
if (!req.secure ) {
res.redirect (301, 'https://' + req.hostname + ':port' + req.originalUrl);
}
next();
});
This can be set up on a custom port
Similar post
Can I configure expressjs to serve some pages over http and others over https?
Be aware that express now support creating Https servers with:
var app = require('express').createServer({ key: ... });