I'm trying to build a reverse proxy in front of a Couchbase SyncGateway. Before sending requests to the sync gateway, I'd like to send them to an authentication server for authentication, then if all is good, send the request on (unmodified from original) to the sync gateway. The database is not staying up to date with the client modifications and I believe this is because I am not successfully proxying PUT/POST requests. Here is the code I have:
var http = require('http');
var httpProxy = require('http-proxy');
var apiProxy = httpProxy.createProxyServer();
var request = require('request').defaults({json: true});
var authServer = 'http://authserverdns:5000';
var syncGateway = 'http://syncgatewaydns:4984';
http.createServer(function (req, res) {
if (req.method == 'POST' || req.method == 'PUT') {
req.body = '';
req.addListener('data', function(chunk) {
req.body += chunk;
});
req.addListener('end', function() {
processRequest(req, res);
});
} else {
processRequest(req, res);
}
}).listen(8080);
function processRequest(req, res) {
request(authServer, function(error, response, body) {
if (body.authenticated) {
console.log('authenticated !!!');
apiProxy.web(this.req, this.res, {target: this.sg});
} else {
console.log('request denied !!!');
}
}.bind({req: req, res: res, sg: syncGateway}));
}
At first I was using an express server and having same issue. As I looked into the problem, it looks like maybe there is an issue with Express and proxying PUT/POST requests. So, I attempted to use some examples out there and this is what I've ended up with, but still not working. Any ideas as to where I'm going wrong here? Authenticated prints, so I know I'm getting to the point of proxying. And the sync gateway seems to be fine with the GET requests.
Thanks
ugh. I wasn't adding the rest of the URL to the forwarding address for the Sync Gateway. The post here helped.
Related
I have a Nodejs express app which receives POST requests (XML) and simply redirects them to a different host replying to the original caller (also with an XML message).
var app = require('express')();
app.post('/', function(req, res) {
res.redirect(307, 'http://localhost:8888/');
});
app.listen(3000, function() {
console.log('Application listening on http://localhost:3000/');
});
What I am trying to achieve is to modify the response from the second host (localhost:8888). How do I intercept and edit the response from the second host before it reaches the original caller?
I cannot figure it out from the documentation so any help would be very appreciated, thank you.
You cannot do that as the response from server 2 is fetched by the client handling the redirect (e.g. your browser). You have to fetch the response yourself in the server side, modify it and send it back.
var app = require('express')();
var request = // your preferred http library
app.post('/', function(req, res) {
request.get('http://localhost:8888/', function (err, response) {
if (err) {
return res.error(err);
}
// Here you have the response, you can modify it.
res.send(response.body);
});
});
app.listen(3000, function() {
console.log('Application listening on http://localhost:3000/');
});
I have a Nodejs Proxy server like this:
`var http = require('http'),
httpProxy = require('http-proxy');
var proxy = httpProxy.createProxyServer({});
proxy.on('proxyReq', function(proxyReq, req, res, options) {
proxyReq.setHeader('X-Special-Proxy-Header', 'foobar');
});
var server = http.createServer(function(req, res) {
console.log(req.body);
proxy.web(req, res, {
target: 'http://localhost:3000'
});
});
console.log("listening on port 9000")
server.listen(9000);`
What i want is get the req.body at the proxy server when i post a request to the Origin server, go through the proxy server.
I used "console.log(req.body);" at both proxy server and the origin server. I get the body object {"id": "user003"} at origin server, but undefined at the proxy server.
So how can i get the req.body at proxy server?
I suspect the problem here is that you don't have a body parser on your proxy server, but do have one on your origin.
req.body itself isn't set by node. Instead, you have to handle events emitted by the req stream:
let body = '';
req.on('data', (data) => {
// data is a buffer so need toString()
body += data.toString();
});
req.on('end', () => {
// at this point you'll have the full body
console.log(body);
});
You can make this easier with a body parser (e.g https://www.npmjs.com/package/body-parser) but since you're running a proxy server, you probably don't want to parse the entire req stream before forwarding onto your origin. So I'd maybe stick with handling the events.
I'm using Grunt and its proxying library grunt-connect-proxy. I have two servers setup for the desktop and mobile versions of my site (both of which have separate assets and such, hence the separation). Both sites are hosted on 0.0.0.0 but on different ports (9000 and 10000).
How can I proxy requests to the two different servers based on the User-Agent header (which will tell me if it is a mobile or desktop user)? Is there another solution in NodeJS I could use?
I ended up writing a NodeJS server which used the http-proxy and mobile-detect packages to proxy requests.
var servers = {
desktopClientFrontend: 'http://0.0.0.0:10000',
mobileClientFrontend: 'http://0.0.0.0:9000',
railsApiBackend: 'http://0.0.0.0:11000'
};
var http = require('http'),
url = require('url'),
httpProxy = require('http-proxy'),
MobileDetect = require('mobile-detect');
var proxy = httpProxy.createProxyServer({});
proxy.on('error', function (err, req, res) {
res.writeHead(500, {
'Content-Type': 'text/plain'
});
res.end('Something went wrong. And we are reporting a custom error message.');
});
var server = http.createServer(function(req, res) {
if(url.parse(req.url).pathname.match(/^\/api\//)) {
proxy.web(req, res, { target: servers.railsApiBackend });
} else {
var mobileDetect = new MobileDetect(req.headers['user-agent']);
if(mobileDetect.mobile()) {
proxy.web(req, res, { target: servers.mobileClientFrontend });
} else {
proxy.web(req, res, { target: servers.desktopClientFrontend });
}
}
});
server.listen(80);
I have a Node.js server that I am sending a web socket upgrade request to. The Authorization header of this request contains login information, which I need to compare against a database entry. I'm unsure how I can stop the web socket connection from opening until after my database query callback is executed.
The following is a simplification of what I am currently doing:
var Express = require('express')
var app = Express()
server = app.listen(app.get("port"), function () {})
server.on("upgrade", function (request, socket) {
//Query database
//On success set "authenticated" flag on request (later accessed through socket.upgradeReq)
//On failure abort connection
})
This works, but there is a brief period of time where the socket is open but I haven't verified the Authorization header, so it would be possible for a malicious user to send/receive data. I'm mitigating this risk in my implementation through the use of an "authenticated" flag, but it seems like there must be a better way.
I tried the following things, but while they seemed to intercept all requests except the upgrade ones:
Attempt #1:
app.use(function (request, response, next) {
//Query database, only call next if authenticated
next()
})
Attempt #2:
app.all("*", function (request, response, next) {
//Query database, only call next if authenticated
next()
})
Possibly worth noting:
I do have an HTTP server as well, it uses the same port and accepts POST requests for registration and login.
Thank you for any assistance, please let me know if additional information is needed.
I'm not sure if this is correct HTTP protocol communication but it seems to be working in my case:
server.on('upgrade', function (req, socket, head) {
var validationResult = validateCookie(req.headers.cookie);
if (validationResult) {
//...
} else {
socket.write('HTTP/1.1 401 Web Socket Protocol Handshake\r\n' +
'Upgrade: WebSocket\r\n' +
'Connection: Upgrade\r\n' +
'\r\n');
socket.close();
socket.destroy();
return;
}
//...
});
verifyClient is implemented for this purpose!
const WebSocketServer = require('ws').Server
const ws = new WebSocketServer({
verifyClient: (info, cb) => {
const token = info.req.headers.token
if (!token)
cb(false, 401, 'Unauthorized')
else {
jwt.verify(token, 'secret-key', (err, decoded) => {
if (err) {
cb(false, 401, 'Unauthorized')
} else {
info.req.user = decoded
cb(true)
}
})
}
}
})
src:
Websocket authentication in Node.js using JWT and WS
I'm expanding on a node.js reverse proxy I put together with mikeal's request module: https://github.com/mikeal/request . It is a connect app that uses a small piece of custom middleware to check if the the current request is for an API route and if so it proxies it to and from a different API server like this:
.use(function(req, res, next){
if( /^\/api\//.test(req.url) ){
req.pipe(request(apiUrl + req.url))
.on('error', function(err){
handlePipeError(err, 'Error piping request to proxy server. Details: ');
})
.pipe(res)
.on('error', function(err){
handlePipeError(err, 'Error piping proxy response to client. Details: ')
});
function handlePipeError(err, customMsg) {
console.log( customMsg, err );
res.writeHead(503, {
'Content-Length': Buffer.byteLength(err.message, 'utf8'),
'Content-Type': 'text/plain' });
res.end(err.message);
}
}
else {
next();
}
There is also some other middleware to handle static file serving and some other stuff. Once all the middleware is setup I start the server like this:
var server = require('http').createServer(app);
server.listen(port)
I would now like to expand this script to allow reverse proxying websocket connections. I tried using the nodejitsu/node-http-proxy to do modify the previous portion of the code like so:
var options = {
ws: true,
target: apiUrl,
secure: false,
xfwd: true
};
var wsProxy = require('http-proxy').createProxyServer(options);
var server = require('http').createServer(app);
server.on('upgrade', function (req, socket, head) {
console.log("--- CAUGHT UPGRADE ----");
wsProxy.ws(req, socket, head);
});
server.listen(port);
I notice the upgrade event fires off and the callback function does run but the websockets request isn't actually working (the client seems to never establish a connection). Is there a better way to achieve this