I've written a small proxy with nodejs, express and htt-proxy. It works well for serving local files but fails when it comes to proxy to external api:
var express = require('express'),
app = express.createServer(),
httpProxy = require('http-proxy');
app.use(express.bodyParser());
app.listen(process.env.PORT || 1235);
var proxy = new httpProxy.RoutingProxy();
app.get('/', function(req, res) {
res.sendfile(__dirname + '/index.html');
});
app.get('/js/*', function(req, res) {
res.sendfile(__dirname + req.url);
});
app.get('/css/*', function(req, res) {
res.sendfile(__dirname + req.url);
});
app.all('/*', function(req, res) {
req.url = 'v1/public/yql?q=show%20tables&format=json&callback=';
proxy.proxyRequest(req, res, {
host: 'query.yahooapis.com', //yahoo is just an example to verify its not the apis fault
port: 8080
});
});
The problem is that there is no response from the yahoo api, maybe there is an response but i dont came up in the browser.
Even simpler with pipe and request-Package
var request = require('request');
app.use('/api', function(req, res) {
var url = apiUrl + req.url;
req.pipe(request(url)).pipe(res);
});
It pipes the whole request to the API and pipes the response back to the requestor. This also handles POST/PUT/DELETE and all other requests \o/
If you also care about query string you should pipe it as well
req.pipe(request({ qs:req.query, uri: url })).pipe(res);
Maybe your code is different when you're testing, but I'm querying the same URL as in your code sample using the following:
http://query.yahooapis.com:8080/v1/public/yql?q=show%20tables&format=json&callback=
and I get nothing back. My guess is you want to change port to 80 (from 8080) -- it works when I change it like so:
http://query.yahooapis.com:80/v1/public/yql?q=show%20tables&format=json&callback=
So that means it should be:
proxy.proxyRequest(req, res, {
host: 'query.yahooapis.com', //yahoo is just an example to verify its not the apis fault
port: 80
});
Maybe I use http-proxy in a wrong way. Using restler does what I want:
var express = require('express'),
app = express.createServer(),
restler = require('restler');
app.use(express.bodyParser());
app.listen( 1234);
app.get('/', function(req, res) {
console.log(__dirname + '/index.html')
res.sendfile(__dirname + '/index.html');
});
app.get('/js/*', function(req, res) {
res.sendfile(__dirname + req.url);
});
app.get('/css/*', function(req, res) {
res.sendfile(__dirname + req.url);
});
app.all('/*', function(req, res) {
restler.get('http://myUrl.com:80/app_test.php/api' + req.url, {
}).on('complete', function (data) {
console.log(data)
res.json(data)
});
});
I ended up using http-proxy-middleware.
The code looks something like this:
var express = require("express");
var proxy = require("http-proxy-middleware");
const theProxy = proxy({
target: "query.yahooapis.com",
changeOrigin: true,
});
app.use("/", theProxy);
app.listen(process.env.PORT || 3002);
That's what I've been using for a while. Can handle both JSON and binary requests.
app.use('/api', (req, res, next) => {
const redirectUrl = config.api_server + req.url.slice(1);
const redirectedRequest = request({
url: redirectUrl,
method: req.method,
body: req.readable ? undefined : req.body,
json: req.readable ? false : true,
qs: req.query,
// Pass redirect back to the browser
followRedirect: false
});
if (req.readable) {
// Handles all the streamable data (e.g. image uploads)
req.pipe(redirectedRequest).pipe(res);
} else {
// Handles everything else
redirectedRequest.pipe(res);
}
});
Related
I'm new to frontend development and express server. When I tried to start an express.js server with react (with axios calls to external apis), it seems express.js is adding 'localhost:3000' in front of the external API calls so they fail.
In my server.js:
const path = require('path');
const express = require('express');
const app = express();
const publicPath = path.join(__dirname, '.', 'dist');
const port = process.env.PORT || 3000;
app.use(express.static(publicPath));
app.get('*', (req, res) => {
res.sendFile(path.join(publicPath, 'index.html'));
});
app.listen(port, () => {
console.log('Server is up!');
});
Which leads to the API call to www.example.com/api/ to become http://localhost:3000/www.example.com/api/
I also tried to filter the req by writing:
app.get('*', (req, res) => {
if (req.url.match(/\/api\//) === null) {
res.sendFile(path.join(publicPath, 'index.html'));
}
});
But it does not change things...
Can anyone help out this newbie that is me?
Update1 Adding the code for calling the api:
This is the api call:
const getSomething = () => {
try {
const url = endpoints.GET_SOMETHING;
return axios.get(url);
} catch (err) {
console.log(err);
}
};
endpoints.GET_SOMETHING is the api URL: www.example.com/api/getSomething
You need to put a / in the url
app.get('/*', (req, res) => {
res.sendFile(path.join(publicPath, 'index.html'));
});
and also your endpoint url should start with https://, http:// or //
I want to debug the client to nodejs server, print the http request original message like curl. any one can help me?
curl -vs -o /dev/null http://127.0.0.1:8080
var app = require('http').createServer(function (request, response) {
// print http orignal message like curl
});
app.listen(3000);
//express
var app=express();
app.use(function(req,res,next){ // print http orignal message like curl
console.log(req.url)
console.log(req.headers)
console.log(req.query)
console.log(req.body)
next();
});
So, not clear what you mean... but this should work (without express):
var app = require('http').createServer(function (request, response) {
// print http orignal message like curl
// request.method, URL and httpVersion
console.log(request.method + ' ' + request.url + ' HTTP/' + request.httpVersion);
// request.headers
for (var property in request.headers) {
if (request.headers.hasOwnProperty(property)) {
console.log(property + ': ' + request.headers[property])
}
}
});
app.listen(3000);
Or this one with express middleware:
const express = require('express')
const app = express();
// middleware to track request message
app.use(function (req, res, next) {
console.log(req.method + ' ' + req.url + ' HTTP/' + req.httpVersion);
for (var property in req.headers) {
if (req.headers.hasOwnProperty(property)) {
console.log(property + ': ' + req.headers[property])
}
}
next();
});
// your routes
app.get('/', function (req, res, next) {
res.send('Hello World');
});
app.listen(3000);
Is there any way to configure a node js application with express js 4 to serve some pages under http protocol and other, those which need more security, in https?
I describe my problem: I'm developing a shop online and I want to display certain pages, like the products list or the product detail views under http, and others which I think need more security, like login or the shopping cart views, under https protocol.
I have tried the express-force-ssl module, but it isn't working. The following code snippet is not from my app (which is too dirty) it is just an example which alos doesn't work for me:
var express = require('express');
var forceSSL = require('express-force-ssl');
var fs = require('fs');
var http = require('http');
var https = require('https');
var ssl_options = {
key: fs.readFileSync('./server-private-key.pem'),
cert: fs.readFileSync('./server-certificate.pem'),
ca: fs.readFileSync('./server-certificate-signing-request.pem')
};
var app = express();
var server = http.createServer(app);
var secureServer = https.createServer(ssl_options, app);
app.use(forceSSL);
app.get('/', function (req, res, next) {
res.send('Hello')
});
app.get('/user/:name', function (req, res, next) {
var user = req.params.name;
res.send('Hello ' + user + '')
});
app.get('/login', forceSSL, function (req, res, next) {
res.send('Hello<br/>Goodbye')
});
app.get('/logout', forceSSL, function (req, res, next) {
res.send('Hello')
});
secureServer.listen(443)
server.listen(8085)
console.log('server started');
The result is that when I launch the application, with url http://localhost:8085, the server automatically redirects it to https://localhost and serves all pages in https protocol.
What I want is to start on http://localhost:8085, navigate to http://localhost/user/userA, then from it go to https://localhost/login and, if click on "Hello" link, I would like to be redirected to http://localhost:8085.
Is there any missing code to get the behavior I want or even any other way to reach it without express-force-ssl module?
I have asked to the author of express-force-ssl module and he has told me that the redirect behavior works as expected. Here is the post.
But diving a little more in its code I've created a custom plugin to solve my problem. Here is the code:
var parseUrl = require('url').parse;
var isSecure = function (req) {
if (req.secure) {
return true;
}
else if (
req.get('X-Forwarded-Proto') &&
req.get('X-Forwarded-Proto').toLowerCase &&
req.get('X-Forwarded-Proto').toLowerCase() === 'https') {
return true;
}
return false;
};
exports = module.exports = function (req, res, next) {
if (isSecure(req)) {
if (req.method === "GET") {
var httpPort = req.app.get('httpPort') || 80;
var fullUrl = parseUrl(req.protocol + '://' + req.header('Host') + req.originalUrl);
res.redirect('http://' + fullUrl.hostname + ':' + httpPort + req.originalUrl);
}
else {
next();
}
}
else {
next();
}
};
It's very similar to force-ssl file but here we manage the opposite action, i.e., here I redirect to http when a route is forced to it. So it's needed to add the function to every route we want to see under http protocol:
var express = require('express');
var forceSSL = require('express-force-ssl');
var fs = require('fs');
var http = require('http');
var https = require('https');
var useHttp = require('./useHttp');
var ssl_options = {
key: fs.readFileSync('./server-private-key.pem'),
cert: fs.readFileSync('./server-certificate.pem')
};
var app = express();
var server = http.createServer(app);
var secureServer = https.createServer(ssl_options, app);
app.get('/', useHttp, function (req, res, next) {
res.send('Hello')
});
app.get('/user/:name', useHttp, function (req, res, next) {
var user = req.params.name;
res.send('Hello ' + user + '')
});
app.get('/login', forceSSL, function (req, res, next) {
res.send('Hello<br/>Goodbye')
});
app.get('/logout', forceSSL, function (req, res, next) {
res.send('Hello')
});
app.set('httpsPort', 9090);
app.set('httpPort', 8085);
secureServer.listen(9090)
server.listen(8085)
console.log('server started');
As you can see I need now to specify in all routes which protocol use: useHttp for http or forceSSL for https.
Although I'm not comfortable at all with this solution because I have to specify in all routes which kind of protocol I want. But at least it works. So I would be very pleased if someone finds any other solution, for isntance, adding in middleware layer a function to manage all http requests and just redirect to https when it is specified with forceSSL. By the moment, this works.
I'm trying to setup node-http-proxy module with caching
node-http-proxy module. I've managed to configure node-http-proxy to do what I need to do in terms of proxying calls, but I would like to find a way to cache some of these calls.
My current code is as follows (omitted some config bootstrapping):
var http = require('http');
var https = require('https');
var httpProxy = require('http-proxy');
var proxy = httpProxy.createProxyServer({});
var fs = require('fs');
var handler = function(req, res) {
proxy.web(req, res, {target: 'http://localhost:9000'});
};
var server = http.createServer(handler).listen(config.children.http.port, function() {
console.log('Listening on port %d', server.address().port);
});
var secure = https.createServer(config.children.https.options, handler).listen(config.children.https.port, function() {
console.log('Listening on port %d', secure.address().port);
});
Inside the handler function, I would like to be able to somehow capture what the proxy is reading from "target" and stream it into a fs.createWriteStream('/somepath') before piping it out to res. Then I would modify my function to do look to something along the line:
var handler = function(req, res) {
var path = '/somepath';
fs.exists(path, function(exists) {
if(exists) {
console.log('is file');
fs.createReadStream(path).pipe(res);
} else {
console.log('proxying');
// Here I need to find a way to write into path
proxy.web(req, res, {target: 'http://localhost:9000'});
}
});
};
Does anyone know how to do this ?
The answer to the question ended up being very simple:
var handler = function(req, res, next) {
var path = '/tmp/file';
fs.exists(path, function(exists) {
if(exists) {
fs.createReadStream(path).pipe(res);
} else {
proxy.on('proxyRes', function(proxyRes, req, res) {
proxyRes.pipe(fs.createWriteStream(path));
});
proxy.web(req, res, {target: 'http://localhost:9000'});
}
});
};
This is a resume about an authentication method.
I tried to use express.basicAuth, but it forces the browser to ask for user and pass, and I need to use my own login page, like google, facebook yahoo...
Is this right? Is there any better way to do this?
I want to avoid modules, like passport, if I can.
I want to use a function like this, using auth middleware (app.get('/loggedin', auth, function(req, res)...)
var express = require('express');
var app = express();
app.use(express.cookieParser());
var RedisStore = require('connect-redis')(express);
app.use(express.session({
store: new RedisStore({
host: 'localhost',
port: 6379,
db: 2,
pass: 'RedisPASS'
}), secret: '1234567890QWERTY'
}));
var auth = function(req, res, next) {
if (req.session.authStatus === 'loggedIn')
next();
else
res.redirect('/login');
};
app.get('/', function(req, res) {
console.log("/");
res.send('not authenticate');
});
app.get('/signin', function(req, res) {
console.log("/signin");
if (req.body.user && req.body.pass)
{
req.user = req.body.user;
req.remoteUser = req.body.user;
req.session.authStatus = 'loggedIn';
req.session.lastPage = '/signin';
res.redirect('/loggedin');
}
else
res.redirect('/login');
});
app.get('/loggedin', auth, function(req, res) {
if(req.session.lastPage) {
res.write('Last page was: ' + req.session.lastPage + '. ');
}
req.session.lastPage = '/loggedin';
res.write('Yeeeeeeeeeee');
res.end();
});
app.get('/loggedin2', auth, function(req, res) {
console.log("/loggedin2");
if(req.session.lastPage) {
res.write('Last page was: ' + req.session.lastPage + '. ');
}
req.session.lastPage = '/loggedin2';
res.write('WoWWWWW!!!!!!');
res.end();
});
app.get('/logout', auth, function(req, res) {
console.log("/logout");
req.session.destroy();
});
app.get('/login', function(req, res) {
console.log("/notlogged");
res.send('enter user and pass...');
});
app.listen(process.env.PORT || 8080);
You could use the passport middleware module in npm — the passport-local module provides authentication against a local resource such as a database.