How to skip specifying port in Node.js http.request - node.js

I am using node.js to connect to NetSuite's REST api. Issue is, I dont know what is port number in NetSuite.. so I want to skip specifying port number. When I dont provide a port number, it takes 80 (default) which is not correct. I have tried port 443 as its https but its not correct port for NetSuite.
I am getting error "getaddrinfo ENOTFOUND". Here is my code.
exports.helloGET = function helloGET (req, res) {
//remove extra node
var json_build = '{"__type" : "salesorder", "status":"Approved"}';
console.log(json_build);
//call NS rest api
var options = {
host: 'rest.na2.netsuite.com',
path: '/app/site/hosting/restlet.nl?script=55&deploy=1',
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Keep-alive': 'timeout=30',
'Authorization' : 'NLAuth nlauth_account="4678710", nlauth_email="someemail#domain.com", nlauth_signature="password", nlauth_role="3"'
}
};
var http = require('http');
var post_req = http.request(options, function(res) {
console.log('STATUS: ' + res.statusCode);
console.log('HEADERS: ' + JSON.stringify(res.headers));
res.on('data', function (chunk) {
console.log('BODY: ' + chunk);
});
res.on('end',function(){
console.log('response ended');
});
});
post_req.on('error', function(e) {
console.log('problem with request: ' + e.message);
});
//post data
post_req.write(json_build);
post_req.end();
};
It goes inside "post_req.on error".

The error ENOTFOUND typically means that the host you are specifying cannot be found in DNS so that may actually be where your error is. Several possible reasons for this here: What ENOTFOUND error in an http request, happening time to time, means?.
You have to know the port in order to connect. You cannot skip it. A TCP connection requires a destination IP address AND a port number. You can't make a TCP connection (which is what's under an HTTP connection) without the port number.
As you appear to know, the default port for HTTP is port 80 and the default port for HTTPS is 443. With http.request(), it will be port 80 unless you specify the port. With https.request() (note different https module), then it will be 443 if you don't specify the port.
So, the only answer here is for you to find out what port the service is running on that you want to connect to. If it isn't a well-known port known in advance, then there needs to be a way to query what port it's running on from some other known service or configuration data.
Also, if you're trying to connection to an HTTPS service, you need to use the https module, not the http module.

You’re actually not connecting to the correct host.
Use the getdatacenters API to look up the RESTlet host for your account.

Related

how can a path and a host be completely different in nodejs

I'm doing research in proxies in nodejs. I came across something that blew my mind. In one of the options for a http.request connection, the source code showed this as the options object
const options = {
port: 1337,
host: '127.0.0.1',
method: 'CONNECT',
path: 'www.google.com:80'
};
This was a part of a far bigger code which was the whole tunneling system. But can someone just explain how the options above work? The whole code is below
const http = require('http');
const net = require('net');
const { URL } = require('url');
// Create an HTTP tunneling proxy
const proxy = http.createServer((req, res) => {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('okay');
});
proxy.on('connect', (req, clientSocket, head) => {
// Connect to an origin server
const { port, hostname } = new URL(`http://${req.url}`);
const serverSocket = net.connect(port || 80, hostname, () => {
clientSocket.write('HTTP/1.1 200 Connection Established\r\n' +
'Proxy-agent: Node.js-Proxy\r\n' +
'\r\n');
serverSocket.write(head);
serverSocket.pipe(clientSocket);
clientSocket.pipe(serverSocket);
});
});
// Now that proxy is running
proxy.listen(1337, '127.0.0.1', () => {
// Make a request to a tunneling proxy
const options = {
port: 1337,
host: '127.0.0.1',
method: 'CONNECT',
path: 'www.google.com:80'
};
const req = http.request(options);
req.end();
req.on('connect', (res, socket, head) => {
console.log('got connected!');
// Make a request over an HTTP tunnel
socket.write('GET / HTTP/1.1\r\n' +
'Host: www.google.com:80\r\n' +
'Connection: close\r\n' +
'\r\n');
socket.on('data', (chunk) => {
console.log(chunk.toString());
});
socket.on('end', () => {
proxy.close();
});
});
});
Source: https://nodejs.org/api/http.html#http_event_connect
You probably have never used a network that requires you to configure a HTTP proxy. Most networks these days configure their firewall to allow HTTP traffic. This means most people these days have never needed to use a HTTP proxy to access the web.
A long-long time ago when I first started using the internet (around 1994) a lot of networks don't allow transparent internet access. Your PC does not have any connection to the outside world. But sysadmins would install a HTTP proxy that you can connect to. Your PC would only have access to the LAN (which the proxy is a part of) and only the HTTP proxy would have access to the internet.
Here's an example of how you'd configure Windows to use a HTTP proxy:
If you configure your PC as above, then when you connect to www.google.com your browser would connect to the host proxy.example.com on port 8080 and then request it to fetch data from www.google.com.
As for why it calls the requested resource path it's because it is sent in the "path" part of the packet.
For example, a normal GET request for getting this page looks something like this:
GET /questions/60498963 HTTP/1.1
Host: stackoverflow.com
And the string after GET and before protocol version is normally called the path:
.---------- this is normally called
| the "path"
v
GET /questions/60498963 HTTP/1.1
Host: stackoverflow.com
When making a proxy request the HTTP header looks like this:
CONNECT stackoverflow.com/questions/60498963 HTTP/1.1
So the url including the domain name is sent to the proxy in the part of the packet usually used to send file path.
Note that all this has nothing to do with Node.js. This is just basic networking (no programming languages involved).

Nodejs request Uncaught Error: write EPROTO when against port 443

I get the error Uncaught Error: write EPROTO when attempting to use my API through the nodejs request module on port 443. It works if I change the port to a non-443 port and requests to the app on port 443 work through the browser and curl.
Why would I get EPROTO and how might I fix it to get my automated tests working again?
Asked for code, it's just an https server with a simple request:
var server = https.createServer ({secureProtocol: 'TLSv1_2_method',
key : fs.readFileSync('key'),
cert : fs.readFileSync('cert'),
ca: [
fs.readFileSync('othercert')
]
}, app);
And a simple request:
var request = require('request');
var basepath = "https://localhost";
var req_opts = {
method: "POST",
uri: basepath + '/myroute',
body: {
},
rejectUnauthorized: false,
json: true
}
request(req_opts, function(err, response, body){
if(err){
console.log(err);
throw(err);
}
console.log(body);
});
This only happens on my local windows machine, not on the remote ubuntu server. Both have node version 4.2.3
Searching it looks like the request might not be able to negotiate a cipher, but then why does it succeed if not on port 443?
Skype was also listening locally on port 443 which is why this was occurring.

TCP socket client through proxy on nodejs

I need to make tcp socket connection to smtp server. Is it possible to connect through proxy server on nodejs? Is there any npm modules available to use? I couldn't find any at all.
var net = require('net');
var HOST = '127.0.0.1';
var PORT = 6969;
var client = new net.Socket();
client.connect(PORT, HOST, function() {
console.log('CONNECTED TO: ' + HOST + ':' + PORT);
client.write('I am here!');
});
// Add a 'data' event handler for the client socket
// data is what the server sent to this socket
client.on('data', function(data) {
console.log('DATA: ' + data);
});
// Add a 'close' event handler for the client socket
client.on('close', function() {
console.log('Connection closed');
});
net.socket, tls.connect and dgram have no proxy support.
The simplest way to use it with proxy is to replace some libc functions with proxychains or something similar.
var client = require('tls')
.connect(443, 'www.facebook.com', function() {
console.log('connected');
client.write('hello');
})
.on('data', function(data) {
console.log('received', data.toString());
})
.on('close', function() {
console.log('closed');
});
proxychains node fit.js
connected
received HTTP/1.1 400 Bad Request
...
closed
Other than 'net' you don't need another module to make a socket connect to a host through a proxy server as long at the proxy server supports HTTPS traffic.
Create a socket connection to the proxy server
Send an HTTP CONNECT message to inform the proxy server of the host and port you want to connect to
If the proxy server responds with an HTTP 200 response then the proxy server established a socket connection to your desired target and is now relaying the traffic between the sockets
If the proxy server responded with any HTTP response other than 200 then the connection was not established.
Even though this process starts with HTTP it doesn't mean it wouldn't work for SMTP traffic. The initial HTTP message is just used to negotiate the connection, after that its just raw traffic. A proxy server might look at the port or host you want to connect to and disallow it. For example, block connections to ports below 80 or below 443 such as port 25 so it really depends on the proxy server if the connection will be allowed.
Yes, it is possible with one of these NPM modules:
http-proxy-agent: An HTTP(s) proxy http.Agent implementation for HTTP endpoints
https-proxy-agent: An HTTP(s) proxy http.Agent implementation for HTTPS endpoints
pac-proxy-agent: A PAC file proxy http.Agent implementation for HTTP and HTTPS
socks-proxy-agent: A SOCKS (v4a) proxy http.Agent implementation for HTTP and HTTPS
HTTPS Proxy Example:
var url = require('url');
var WebSocket = require('ws');
var HttpsProxyAgent = require('https-proxy-agent');
// HTTP/HTTPS proxy to connect to
var proxy = process.env.http_proxy || 'http://168.63.76.32:3128';
console.log('using proxy server %j', proxy);
// WebSocket endpoint for the proxy to connect to
var endpoint = process.argv[2] || 'ws://echo.websocket.org';
var parsed = url.parse(endpoint);
console.log('attempting to connect to WebSocket %j', endpoint);
// create an instance of the `HttpsProxyAgent` class with the proxy server information
var opts = url.parse(proxy);
var agent = new HttpsProxyAgent(opts);
// finally, initiate the WebSocket connection
var socket = new WebSocket(endpoint, { agent: agent });
socket.on('open', function () {
console.log('"open" event!');
socket.send('hello world');
});
socket.on('message', function (data, flags) {
console.log('"message" event! %j %j', data, flags);
socket.close();
});
I hope this helps.

How can I make a successful http POST using node.js to an aws instance?

I am working on setting up an aws instance to listen for http POST's. When running the server and client both as local host, everything seems to work fine. However, when running trying to make a post with the client to an aws instance with the server running, I am getting a connect ECONNREFUSED error.
The aws instance (ubuntu server) that I am using has both ports 80 and 8080 open to all ip addresses. I am using the pm2 module to keep the server running. Although using pm2 is giving the same error.
Server Setup: (aws instance terminal)
$ sudo apt-get install git
$ curl -sL https://deb.nodesource.com/setup | sudo bash -
$ sudo apt-get install -y nodejs
$ sudo npm install pm2 -g --unsafe-perm
Using node to start server:
$ node nodeServerTest.js
Using pm2 to start server:
$ pm2 start nodeServerTest.js --name "nodeServerTest" -i max
Server Code:
// nodeServerTest.js
var http = require("http");
function startServer(port, ip) {
// requestListener handles incoming requests
function requestListener(request, response) {
if (request.method == 'POST') {
var body = '';
request.on('data', function (data) {
body += data;
// destroys connection if too long
if (body.length > 1e6) {
request.connection.destroy();
}
});
request.on('end', function() {
// checks for json, otherwise destroys connection
try {
var POST = JSON.parse(body);
console.log('valid post:')
console.log(POST)
response.end('post accepted');
}
catch(err) {
console.log('bad post, ending connection')
response.connection.destroy();
}
});
}
else {
response.end();
}
};
// creates the eventEmitter
var server = http.createServer(requestListener);
// beigns listening on specified port
server.listen(port, ip);
// logs when someone attempts to connect
server.on('connection', function (stream) {
console.log('someone connected!');
});
// notifies when server has started
console.log('Server running at http://'+ip+':'+port);
}
startServer(8080, '127.0.0.1');
Client Code: (I change the host field to the ip address when attempting to post to the aws instance)
// nodeClientTest.js
function httpPost() {
var http = require("http");
var postData = JSON.stringify({
'msg': 'Hello World!'
});
var options = {
host: "localhost", // using correct public ip
port: 8080,
method: 'POST',
path: '/',
headers: {
'Content-Type': 'application/json',
'Content-Length': postData.length
}
};
var req = http.request(options, function(res) {
console.log('STATUS: ' + res.statusCode);
console.log('HEADERS: ' + JSON.stringify(res.headers));
res.setEncoding('utf8');
res.on('data', function (chunk) {
console.log('BODY: ' + chunk);
});
});
req.on('error', function(e) {
console.log('problem with request: ' + e.message);
});
// write data to request body
req.write(postData);
req.end();
}
httpPost();
Thank you for any help, if more information is needed please leave a comment
You are binding your server to the loopback interface here:
startServer(8080, '127.0.0.1');
..which makes is available only locally. To allow access via your eth0 interface, so from other network hosts replace this line with:
startServer(8080);
..to listen on port 8080 TCP on all interfaces, according to Node.js manual.
PS Please remember that your private IP on AWS EC2 != public IP address. You can check both by running ec2metadata command on your EC2 host.
An explanation, as OP requested:
each TCP server socket has to be bound to a specific network interface. This is because you make you connect your client TCP socket to a combination of IP:port and the IP is bound to the interface.
loopback interface with a 127.0.0.1 IP address is as special virtual interface available only from the localhost, targeting ifself (hence the name),
So when you run your server binding it to loopback the only way you could have only made the request to it would be initiating the connection from that host itself, for example with telnet like this: telnet 127.0.0.1 8080.
You could have bind the server to the actual IP of eth0 interface but this is inpractical, especially on EC2 servers where private IPs change.
That's why I proposed the simpler, universal syntax. A side effect is that this way your server listens on both loopback and eth0 but it only helps, for example when you want to separate your own, local traffic from the rest of the traffic by based on the interface used.
It seems like your server is only binding to the local address of the server (127.0.0.1) and not listening to connections outside of the server.
Please try to change the listening address of the server to 0.0.0.0
startServer(8080, '0.0.0.0');
I would also advise you to check the express web framework for your server.

Simple example for nodejs behind a proxy

Here is a simple nodejs script for using node as an https client to interact with the outside world. How do you modify it so you don't get "ECONNREFUSED" errors when you run it behind a corporate proxy like http://10.10.10.10:8080 ?
var https = require('https');
var options = {
hostname: 'encrypted.google.com',
port: 443,
path: '/',
method: 'GET'
};
var req = https.request(options, function(res) {
console.log("statusCode: ", res.statusCode);
console.log("headers: ", res.headers);
res.on('data', function(d) {
process.stdout.write(d);
});
});
req.end();
req.on('error', function(e) {
console.error(e);
});
----Update---
I figured out how to with the npm 'request' package (https://github.com/mikeal/request). Still wondering how to do this with vanilla node libraries.
var request = require('request').defaults({proxy:'http://my.proxy.server:8080', agent:false});
request('https://www.google.com', function (error, response, body) {
if (!error && response.statusCode == 200) {
console.log(body) // Print the google web page.
}
})
Normally its best to handle proxying at the system level - configure your operating system to use the proxy not the script. However this may also fail, as most corporate proxies don't have all the ports open.
A universal solution would be to have a server (EC2 or Rackspace instance) to act as your own proxy and have it listen for ssh on port 443.Port 443 is open in corporate firewalls for https.
At which point you can either tunnel all you local network traffic to the server, or ssh to the server and run the script there.
If you were to make scripts proxy specific, they wouldn't be very portable, and not production hardened as they'd break when the proxy configuration changed.

Resources