XMLHTTPRequest not working on server on localhost - node.js

I'm developing a node.js RESTful server for home use on a RPI with Raspbian, and for testing, I've created a test HTML page that makes various XMLHttpRequests to it.
When developing I'm using a test nodemon server on my dev machine (a desktop machine, not the RPI), running on localhost:4000, and the test HTML page points to it.
Whenever I'm ready to commit the changes, I push them to the server (the RPI), even the test page. It should connect to the server on localhost.
Something bizzare happens whenever I'm testing the page on the server: localhost is not recognized in the XMLHttpRequest.open method, but if I put the address of the server machine in the network (not 127.0.0.1, but 192.168.1.X for example), it works.
The Command netstat -vltn shows that the node server is listening on port 4000, I've enabled CORS, I've already tried to write 127.0.0.1 instead of localhost, and I've even modified the app.listen function to listen to 0.0.0.0, like this:
app.listen(port, '0.0.0.0', function () {
console.log('RESTful API server started on: ' + port);
});
but still, every request from the test page hosted on the server, to localhost:4000 doesn't work.
My problem here is that, if I need to push the test page on the server, I need to manually change the IP address for the XMLHttpRequest each time, instead of just keeping localhost. Is there a way to enable the use of localhost?
EDIT: I'm adding some client code to flesh out the problem.
testpage.html (the one that should work both on the dev machine and the RPI)
<html>
<head>
<script type="text/javascript">
function sendData() {
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function () {
if (this.readyState == 4 && this.status == 200) {
// Typical action to be performed when the document is ready:
document.getElementById("demo").innerHTML = xhttp.responseText;
}
};
xhttp.onerror = function(e) {
console.log('error', e);
};
xhttp.open("POST", "http://127.0.0.1:4000/auth/loginTest", true);
xhttp.setRequestHeader("Content-Type", "application/json");
//I've omitted the part where I'm prepping a json with username/password data
xhttp.send(jsonString);
}
</script>
</head> <!--I'm skipping the rest of the code as there's only a div that
catches the json info sent by the server -->
server.js (the one that gets started on the RPI with node server.js)
var express = require('express');
var cors = require('cors');
var bodyParser = require('body-parser');
var port = process.env.PORT || 4000;
var auth = require(/*path to Auth middleware*/);
var app = express();
app.use(bodyParser.json({ type: 'application/json' }));
app.options('*', cors());
app.use(cors());
app.use('/auth', auth);
process
.on('unhandledRejection', (reason, p) => {
console.error(reason, 'Unhandled Rejection at Promise', p);
})
.on('uncaughtException', err => {
console.error(err, 'Uncaught Exception thrown');
process.exit(1);
});
app.listen(port, function () {
console.log('RESTful API server started on: ' + port);
});

Try changing this code:
app.listen(port, '0.0.0.0', function () {
console.log('RESTful API server started on: ' + port);
});
to this:
app.listen(port, function () {
console.log('RESTful API server started on: ' + port);
});
This will allow your app to listen on both IPv4 and IPv6. It is possible that localhost is resolving to the IPv6 address and your code is only listening on the IPv4 address. I know that a MAC uses IPv6 for localhost.
The other thing to try is stop using the word localhost on the client and use 127.0.0.1 and see if that makes any difference.
UPDATE:
Below is my server code that I generated from yours and it seems to work:
const express = require('express');
const cors = require('cors');
const bodyParser = require('body-parser');
const port = process.env.PORT || 4000;
function sendPage(req, res, next) {
console.log('sending page');
res.send(`<html>
<head>
<script>
function sendData() {
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function () {
if (this.readyState == 4 && this.status == 200) {
// Typical action to be performed when the document is ready:
document.getElementById("demo").innerHTML = xhttp.responseText;
}
};
xhttp.onerror = function(e) {
console.log('error', e);
};
xhttp.open("POST", "http://127.0.0.1:4000/auth/loginTest", true);
xhttp.setRequestHeader("Content-Type", "application/json");
//I've omitted the part where I'm prepping a json with username/password data
xhttp.send('{"user":"dog","pw":"woof"}');
}
</script>
</head>
<body onload="sendData()">
<h1>Test page</h1>
<div id="demo"></div>
<hr/>
</body>
</html>`);
}
function auth() {
console.log('auth called');
var router = express.Router();
router.post('/loginTest', (req, res, next) => {
console.log('auth was called');
console.log(req.body);
res.json({error: false, data:'hi'});
});
return router;
}
const app = express();
app.use(bodyParser.json({ type: 'application/json' }));
app.options('*', cors());
app.use(cors());
app.get('/', sendPage);
app.use('/auth', auth());
process
.on('unhandledRejection', (reason, p) => {
console.error(reason, 'Unhandled Rejection at Promise', p);
})
.on('uncaughtException', err => {
console.error(err, 'Uncaught Exception thrown');
process.exit(1);
});
app.listen(port, function () {
console.log('RESTful API server started on: ' + port);
});
If this doesn't come close to matching your code let me know where I got it wrong.

Sorry for the delay. I forgot to post the solution.
My approach will never work, because when the test page is loaded, it will try to execute a script on http://localhost:4000, which is fine if the page is loaded from the machine where the server is listening, but obviously won't work if it's on another machine (hence the ERR_CONNECTION_REFUSED error).
If I load this page from the server machine it will try to execute the script on my machine, which is an invalid request.
So I've solved it by simply substitute http://localhost:4000 in the request with the actual IP of the machine, e.g http://Write.Real.Address.Here:4000.

Related

Websocket post request to NODE server and not get response

I am trying to build a node server which as a middleman for my website. Several libraries are used.
Axios, I use axios to post requests to API and get the data from database
Socket.io, I use socket.io for recording who login and broadcast the message to every user if needed.
Express, I use it to host my React web app.
For the web app, I use componentDidMount and Axios to fetch data when the page is started and pressed the login button respectively. However, not every time the node server response, I will say its freezed. Sometime I press "Esc", and it will response the message back. How can I make sure it returns every time? Thanks a lot!
Partial Code from node js:
server.js
#for access DB
const DBhttp = require('http');
app.use(cors());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
app.use(morgan('common', { stream: serverLogStream}));
app.use('/api/login', loginRouter);
app.use('/api', router);
let DBserver;
DBserver = DBhttp.createServer(app)
#Express for host app
var AppServer;
var http;
var webApp = express();
webApp.use(express.static(path.join(__dirname, 'build')));
webApp.get('/', function(req, res) {
res.sendFile(path.join(__dirname, 'build', 'index.html'));
});
AppServer= http.createServer(options, webApp);
#socket.io commumicate between app
const socketIO = require("socket.io");
var io = socketIO.listen(server);
var clients = {};
io.sockets.on('connection', function (socket) {
#do the communication
}
React
react_index.js
initializeSession(this.state.loginName); #connect socket
this.setState({isLogin:true});
axios.post(SERVER_NAME + 'api/afterLogin')
.then((res) => {
this.setState({
full_name : res.data,
})
return Promise.resolve('Success')
})
You can add one more client right on your server to connect it to the same channel and see all the responses.
You can write the simple index.html with alike code:
<!doctype html>
<body>
<ul id="messages"></ul>
<script src="https://code.jquery.com/jquery-1.11.1.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.3.0/socket.io.js"></script>
<script>
function getParameterByName(name, url) {
if (!url) url = window.location.href;
name = name.replace(/[\[\]]/g, '\\$&');
var regex = new RegExp('[?&]' + name + '(=([^&#]*)|&|#|$)'),
results = regex.exec(url);
if (!results) return null;
if (!results[2]) return '';
return decodeURIComponent(results[2].replace(/\+/g, ' '));
}
var socket = io.connect({'YOUR PATH TO SOCKET SERVER'});
socket.on('connect', () => {
console.log('socket.on connect');
});
socket.on('message', function (msg) {
$('#messages').append($('<li>').text(JSON.stringify(msg)));
});
socket.on('update', function (msg) {
$('#messages').append($('<li>').text(JSON.stringify(msg)));
});
socket.on('disconnect', () => {
console.log('socket.on disconnect');
})
</script>
</body>
On editing it as you need, you can enable it like this:
app.get('/socketIo', (req, res) => {
res.sendFile(__dirname + '/index.html');
});
Now you can see all the responses, which your server sends to the address {YOUR PATH TO SERVER}/socketIo
Also it would be beneficial to add console.log, to get the information about the clients
io.clients((error, clients) => {
if (error) throw error;
console.log('clients ', clients);
});
This way you'll know whether your client is working

Why does my websocket handshake fail with unexpected response 200?

I have seen this question several times, but I feel that my use case is not quite addressed, so I'm posting it below:
I get this error when trying to connect to my websocket:
scripts.js:44 WebSocket connection to 'ws://localhost:3000/' failed: Error during WebSocket handshake: Unexpected response code: 200
There's one small change that makes it fail versus succeed. See below:
Server.js:
'use strict';
const express = require('express');
const WebSocket = require('ws');
const PORT = process.env.PORT || 3000;
// Failing:
const server = express().use(express.static('public'));
server.get('/', function (req, res) {
res.sendFile(path.join(__dirname + '/public/index.html'));
});
server.listen(PORT, () => {
console.log(`Example app listening on PORT ${PORT}!`)
});
///
// Working (no websocket connection issues):
///const server = express().use(express.static('public'))
// .listen(PORT, () => {
// console.log(`Example app listening on PORT ${PORT}!`)
// });
const wss = new WebSocket.Server({ server });
wss.on('connection', function connection(ws) {
console.log(ws);
ws.on('message', function incoming(message) {
console.log('received message: %s', message);
wss.clients.forEach(function each(client) {
if (client !== ws && client.readyState === WebSocket.OPEN) {
client.send(message);
}
});
});
});
relevant: client code:
var HOST = location.origin.replace(/^http/, 'ws');
var socket = new WebSocket(HOST); //this is the line where the connection is attempted.
Running on localhost, ngrok, or deployed to heroku seems to make no difference, unless I missed something...
I have tried various things from other posts but to no avail. They mention changing networking configurations (doesn't seem relevant to my use), or various ways to configuring express js, but they don't help. It's really just that small change in the above code. Is the websocket hitting the GET route?

The live data updates function works on local but doesn't work on live? Socket.io + Node.js + Vue.js + AWS EC2

The code works properly on my local computer, but when it is uploaded on live server on AWS EC2, the connection is being refused.
We tried removing the security for testing purposes on AWS EC2, and the function now works.
So we thought it might be somewhere on the Security configuration of the AWS EC2 or on the code of the Node server, we're not entirely sure.
The security of the Node server port (8001), is only allowing 1 specific IP address, which is the IP Address of the site itself (samplehostname.com) that is trying to connect the Node server.
The security of the node server for request is we allow everything (for testing):
app.use(function(req, res, next) {
res.header('Access-Control-Allow-Origin', "*:*");
res.header('Access-Control-Allow-Methods','GET');
res.header('Access-Control-Allow-Headers', 'Content-Type');
next();
})
The error log is:
GET http://samplehostname.com:8001/socket.io/?
EIO=3&transport=polling&t=MWnX8e4 net::ERR_CONNECTION_REFUSED
Node.js Code:
var app = require('express')()
var server = require('http').Server(app)
var io = require('socket.io')(server)
app.use(function(req, res, next) {
res.header('Access-Control-Allow-Origin', "*:*");
res.header('Access-Control-Allow-Methods','GET');
res.header('Access-Control-Allow-Headers', 'Content-Type');
next();
})
io.on('connect', function (socket) {
console.log(`Socket Connected ID: ${socket.id}`)
var Redis = require('ioredis')
var redis = new Redis({
host: 'localhost',
port: 1234,
password: 'samplepasswordhere',
})
redis.subscribe('(CHANNEL-HERE)')
redis.on("message", function (channel, message) {
var parsed_message = JSON.parse(message)
socket.emit(channel, parsed_message.data.data)
console.log(message)
})
})
var listener = server.listen(8001, ()=>{
console.log('Node Server Listening on port ' + listener.address().port)
});
Vue.js Code:
import io from 'socket.io-client'
...
beforeCreate () {
var socket = io(':8001', {secure: true})
socket.on('(CHANNEL-HERE)', function (data) {
vue.setPopulationData(JSON.parse(data))
})
}
...

Node.js + React: How to POST

Follow on from this question: Axios can GET but not POST to the same URL
I've been trying to figure this out for too long now.
I want to POST from my React app to a .JSON file. Can anyone tell me what I'm doing wrong?
My AJAX POST function using axios always returns a 404. I'm listening for it on the node server but app.post never fires.
Thanks.
POST request from my React app:
postJson = (postJsonData) => {
axios.post('./postJson/', {
postJsonData
})
.then(function (response) {
console.log("success!");
console.log(response);
})
.catch(function (error) {
console.log(error);
});
}
app.js (node server):
/*========== Default Setup for node server copied from node website ==========*/
const http = require('http');
const hostname = '127.0.0.1';
const port = 3001;
const server = http.createServer((req, res) => {
res.statusCode = 200;
res.setHeader('Content-Type', 'text/plain');
res.end('Hello World\n');
});
server.listen(port, hostname, () => {
console.log(`Server running at http://${hostname}:${port}/`);
});
/*========== Listen for POST (Trying to get the data from my REACT app
- will then assign it to "obj" below) ==========*/
var express = require("express");
var myParser = require("body-parser");
var app = express();
app.post("./postJson/", function(request, response) {
console.log("MURRRR");
console.log(request.body); //This prints the JSON document received (if it is a JSON document)
/*=== JSON Stuff ===*/
var jsonfile = require('jsonfile')
var file = './scene-setup.json'
var obj = {name: 'JP'}
jsonfile.writeFile(file, obj, function (err) {
console.error(err)
})
});
//Start the server and make it listen for connections on port 3000
app.listen(3000, function(){
console.log("server is listening to 3000");
});
Two things I noticed:
Your post endpoint doesn't need a leading "." I would make it just "/postJson"
Make sure you are posting to "http://localhost:3000/postJson"
Make sure you have the network tab open to see the actual URL you are requesting to.
Cheers
Turns out both react and my node server were running on localhost:3000 simultaneously which is apparently not okay.
Running my node server on localhost:3001 from a new command line window allowed me to do both at the same time.
Not sure how this would work when making a production build though.

HTTP connection gets prematurely terminated in nodejs app hosted on Amazon EC2

I have a REST api hosted on Amazon EC2, which is written with Nodejs (Express).
In a particular REST call, a reply of about 5MB is sent to the client. Before the client completely receives the reply, client prints following error message.
Premature end of Content-Length delimited message body
I added a connection listener in nodejs server like below to check what is going on the server.
var app = express();
var server = http.createServer(app);
var port = app.get('port');
server.listen(port);
server.on('error', onError);
server.on('listening', onListening);
server.on('connection', function (socket) {
log.debug('SOCKET OPENED' + JSON.stringify(socket.address()));
socket.setTimeout(300000); //5 minute timeout
socket.on('end', function () {
log.debug('SOCKET END: other end of the socket sends a FIN packet');
});
socket.on('timeout', function () {
log.warn('SOCKET TIMEOUT');
});
socket.on('error', function (error) {
log.warn('SOCKET ERROR: ' + JSON.stringify(error));
});
socket.on('close', function (had_error) {
log.debug('SOCKET CLOSED. IT WAS ERROR: ' + had_error);
});
});
I observed that SOCKET TIMEOUT gets logged in backend. In above code, I have increased the socket timeout to 5 minutes, but it doesn't seem to have any effect.
Earlier I had the REST API hosted in Google compute engine, and I didn't have this problem back then.
What could be the problem here?
Edit: Here is the code of REST API call.
I have following code in my app.js
require('./routes/index')(app);
Following is the index.js of routes directory.
var changeCase = require('change-case');
var express = require('express');
var routes = require('require-dir')();
module.exports = function (app) {
Object.keys(routes).forEach(function (routeName) {
var router = express.Router();
require('./' + routeName)(router);
app.use('/api/' + changeCase.paramCase(routeName), router);
});
};
As it can be seen, it loops through all the js files in the routes directory and registers the file name as the URL path in app.
Here is the code of this particular route for which I face this problem.
module.exports = function (router) {
router.get("/fetch", function (req, res, next) {
itemModel.fetch(req.user.clientId, function (error, items) {
if (error) {
res.status(500).json({error: error});
} else {
res.json(items); //items is a JSON array
}
});
});
}
Setting timeout for the HTTP server resolved the issue.
var server = http.createServer(app);
var port = app.get('port');
server.listen(port);
server.setTimeout(300000, function (socket) {
});

Resources