How to reverse proxy client POST & PUT requests using node-http-proxy - node.js

I'm trying to use the node-http-proxy as a reverse proxy, but I can't seem to get POST and PUT requests to work. The file server1.js is the reverse proxy (at least for requests with the url "/forward-this") and server2.js is the server that receives the proxied requests. Please explain what I'm doing incorrectly.
Here's the code for server1.js:
// File: server1.js
//
var http = require('http');
var httpProxy = require('http-proxy');
httpProxy.createServer(function (req, res, proxy) {
if (req.method == 'POST' || req.method == 'PUT') {
req.body = '';
req.addListener('data', function(chunk) {
req.body += chunk;
});
req.addListener('end', function() {
processRequest(req, res, proxy);
});
} else {
processRequest(req, res, proxy);
}
}).listen(8080);
function processRequest(req, res, proxy) {
if (req.url == '/forward-this') {
console.log(req.method + ": " + req.url + "=> I'm going to forward this.");
proxy.proxyRequest(req, res, {
host: 'localhost',
port: 8855
});
} else {
console.log(req.method + ": " + req.url + "=> I'm handling this.");
res.writeHead(200, { "Content-Type": "text/plain" });
res.write("Server #1 responding to " + req.method + ": " + req.url + "\n");
res.end();
}
}
And here's the code for server2.js:
// File: server2.js
//
var http = require('http');
http.createServer(function (req, res, proxy) {
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(8855);
function processRequest(req, res) {
console.log(req.method + ": " + req.url + "=> I'm handling this.");
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.write("Server #2 responding to " + req.method + ': url=' + req.url + '\n');
res.end();
}

http-proxy depends on the data and end events for POST / PUT requests. The latency between the time that server1 receives the request and when it is proxied means that http-proxy misses those events entirely. You have two options here to get this to work correctly - you can buffer the request or you can use a routing proxy instead. The routing proxy seems the most appropriate here since you only need to proxy a subset of requests. Here's the revised server1.js:
// File: server1.js
//
var http = require('http');
var httpProxy = require('http-proxy');
var proxy = new httpProxy.RoutingProxy();
http.createServer(function (req, res) {
if (req.url == '/forward-this') {
return proxy.proxyRequest(req, res, {
host: 'localhost',
port: 8855
});
}
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) {
console.log(req.method + ": " + req.url + "=> I'm handling this.");
res.writeHead(200, { "Content-Type": "text/plain" });
res.write("Server #1 responding to " + req.method + ": " + req.url + "\n");
res.end();
}

In addition to #squamos
How to write a node express app that serves most local files, but reroutes some to another domain?
var proxy = new httpProxy.RoutingProxy();
"Above code is working for http-proxy ~0.10.x. Since then lot of things had changed in library. Below you can find example for new version (at time of writing ~1.0.2)"
var proxy = httpProxy.createProxyServer({});

Here is my solution for proxying POST requests. This isn't the most ideal solution, but it works and is easy to understand.
var request = require('request');
var http = require('http'),
httpProxy = require('http-proxy'),
proxy = httpProxy.createProxyServer({});
http.createServer(function(req, res) {
if (req.method == 'POST') {
request.post('http://localhost:10500/MyPostRoute',
{form: {}},
function(err, response, body) {
if (! err && res.statusCode == 200) {
// Notice I use "res" not "response" for returning response
res.writeHead(200, {'Content-Type': "application/json"});
res.end(body);
}
else {
res.writeHead(404, {'Content-Type': "application/json"});
res.end(JSON.stringify({'Error': err}));
}
});
}
else if (req.method == 'GET') {
proxy.web(req, res, { target: 'http://localhost/9000' }, function(err) {
console.log(err)
});
}
The ports 10500 and 9000 are arbitrary and in my code I dynamically assign them based on the services I host. This doesn't deal with PUT and it might be less efficient because I am creating another response instead of manipulating the current one.

Related

How to work with multiple external css file in same node js(without express) project?

i want to load style.css for home and about.css for about page
but it only load the style.css for all pages. the css from about page is not working
const http = require('http')
const { readFileSync } = require('fs')
const server = http.createServer(function (req, res) {
if (req.url === '/') {
let data = readFileSync('./home.html')
res.write(data)
res.end()
}else if (req.url === '/about') {
let data = readFileSync('./about.html')
res.write(data)
res.end()
}else if (req.url === '/fish.png'){
const image = readFileSync('fish.png')
res.writeHead(200, { 'Content-Type': 'image/png' })
res.write(image)
res.end()
} else if (req.url = '/style.css') {
const css = readFileSync('./style.css')
res.writeHead(200, { 'Content-Type': 'text/css' })
res.write(css)
res.end()
}else if (req.url = '/about.css') {
const css = readFileSync('./about.css')
res.writeHead(200, { 'Content-Type': 'text/css' })
res.write(css)
res.end()
}
else {
res.end('something is wrong with the server')
}
})
server.listen(8080, () => console.log('server is running on 8080'))
i want any solution without using express js
You must use strict equality like what you have declared above.
req.url === '/style.css'
req.url === '/about.css'

Node.js HTTP Server Routing

below node.js code is using express to route to different urls, how can I do the same with http instead of express?
var express = require('express');
var app = express();
app.use(express.static('public'));
app.get('/', function (req, res) {
res.send('Welcome Home');
});
app.get('/tcs', function (req, res) {
res.send('HI RCSer');
});
// Handle 404 - Keep this as a last route
app.use(function(req, res, next) {
res.status(404);
res.send('404: File Not Found');
});
app.listen(8080, function () {
console.log('Example app listening on port 8080!');
});
Here's a quick example. Obviously, there are many ways of doing this, and this is probably not the most scalable and efficient way, but it will hopefully give you an idea.
const http = require('http');
const fs = require('fs');
const server = http.createServer((req, res) => {
req.on('error', err => {
console.error(err);
// Handle error...
res.statusCode = 400;
res.end('400: Bad Request');
return;
});
res.on('error', err => {
console.error(err);
// Handle error...
});
fs.readFile('./public' + req.url, (err, data) => {
if (err) {
if (req.url === '/' && req.method === 'GET') {
res.end('Welcome Home');
} else if (req.url === '/tcs' && req.method === 'GET') {
res.end('HI RCSer');
} else {
res.statusCode = 404;
res.end('404: File Not Found');
}
} else {
// NOTE: The file name could be parsed to determine the
// appropriate data type to return. This is just a quick
// example.
res.setHeader('Content-Type', 'application/octet-stream');
res.end(data);
}
});
});
server.listen(8080, () => {
console.log('Example app listening on port 8080!');
});
Try the code below . It is a pretty basic example
var http = require('http');
//create a server object:
http.createServer(function (req, res) {
res.writeHead(200, {'Content-Type': 'text/html'}); // http header
var url = req.url;
if(url ==='/about'){
res.write('<h1>about us page<h1>'); //write a response
res.end(); //end the response
}else if(url ==='/contact'){
res.write('<h1>contact us page<h1>'); //write a response
res.end(); //end the response
}else{
res.write('<h1>Hello World!<h1>'); //write a response
res.end(); //end the response
}
}).listen(3000, function(){
console.log("server start at port 3000"); //the server object listens on port 3000
});
express can also be used by http directly.
From: https://expressjs.com/en/4x/api.html#app.listen
The app returned by express() is in fact a JavaScript Function, designed to be passed to Node’s HTTP servers as a callback to handle requests.
var http = require('http')
var express = require('express')
var app = express()
http.createServer(app).listen(80)
With this, you can still make use of express for routing, while keeping native http support.

Why doesn't my 404 error display unless I use res.end()?

I'm learning Node, and I have this code
var http = require('http');
// var data = require('fs').readFileSync(__dirname + '/index.html', 'utf8');
http.createServer(function (req, res) {
if (req.url === '/') {
res.writeHead(200, {"content-type": "text/html"});
var html = require('fs').createReadStream(__dirname + '/index.html');
html.on('data', function (chunk) {
res.write(chunk);
})
}
else if (req.url === '/api') {
var obj = {
firstname: 'John',
lastname: 'Smith'
};
res.writeHead(200, {"content-type": "application/json"});
res.write(JSON.stringify(obj));
}
else {
res.writeHead(404, {"content-type": "text/plain"});
res.write("Error 404: Page not found.");
res.end();
}
}).listen(1337, "127.0.0.1");
console.log('Server listening on port 1337');
For some reason, 404 response will not display unless I use res.end(), even though the other two responses display fine without res.end(). Anyone know why this is?

curl, node: posting JSON data to node server

I'm trying to test a small node server I've written with CURL and for some reason this fails. My script looks like this:
http.createServer(function (req, res)
{
"use strict";
res.writeHead(200, { 'Content-Type': 'text/plain' });
var queryObject = url.parse(req.url, true).query;
if (queryObject) {
if (queryObject.launch === "yes") {
launch();
else {
// what came through?
console.log(req.body);
}
}
}).listen(getPort(), '0.0.0.0');
When I point my browser to:
http://localhost:3000/foo.js?launch=yes
that works fine. My hope is to send some data via JSON so I added a section to see if I could read the body part of the request (the 'else' block). However, when I do this in Curl, I get 'undefined':
curl.exe -i -X POST -H "Content-Type: application/json" -d '{"username":"xyz","password":"xyz"}' http://localhost:3000/foo.js?moo=yes
I'm not sure why this fails.
The problem is that you are treating both requests as if they where GET requests.
In this example I use a different logic for each method. Consider that the req object acts as a ReadStream.
var http = require('http'),
url = require('url');
http.createServer(function (req, res) {
"use strict";
if (req.method == 'POST') {
console.log("POST");
var body = '';
req.on('data', function (data) {
body += data;
console.log("Partial body: " + body);
});
req.on('end', function () {
console.log("Body: " + body);
});
res.writeHead(200, {'Content-Type': 'text/html'});
res.end('post received');
} else {
var queryObject = url.parse(req.url, true).query;
console.log("GET");
res.writeHead(200, {'Content-Type': 'text/plain'});
if (queryObject.launch === "yes") {
res.end("LAUNCHED");
} else {
res.end("NOT LAUNCHED");
}
}
res.writeHead(200, { 'Content-Type': 'text/plain' });
}).listen(3000, '0.0.0.0');

why it is not calling a.html

var http = require('http');
http.createServer(function (req, res) {
if (req.url == '/')
{
req.url += "a.html";
//facebookAPI(req,res);
}
}).listen(1337);
when I typed the address in the browser it was not calling that url.
Any thought Thank you.
Here is how you could serve that file. This is untested!
var http = require('http');
var fs = require('fs');
http.createServer(function (req, res) {
if (req.url == '/')
{
fs.readFile('a.html', 'utf8', function(err, data) {
if(err) {
res.end('Error Loading File');
return;
}
res.end(data);
});
} else {
res.writeHead(404, {'Content-Type': 'text/plain'});
res.end('file not found');
}
}).listen(1337);
There are better ways to accomplish the same thing, but this should get you started.

Resources