Nodejs request Uncaught Error: write EPROTO when against port 443 - node.js

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.

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).

How can I use an https proxy with node.js https/request Client?

I need to send my client HTTPS requests through an intranet proxy to a server.
I use both https and request+global-tunnel and neither solutions seem to work.
The similar code with 'http' works. Is there other settings I missed?
The code failed with an error:
REQUEST:
problem with request: tunneling socket could not be established, cause=socket hang up
HTTPS:
events.js:72
throw er; // Unhandled 'error' event
^
Error: socket hang up
at SecurePair.error (tls.js:1011:23)
at EncryptedStream.CryptoStream._done (tls.js:703:22)
at CleartextStream.read [as _read] (tls.js:499:24)
The code is the simple https test.
var http = require("https");
var options = {
host: "proxy.myplace.com",
port: 912,
path: "https://www.google.com",
headers: {
Host: "www.google.com"
}
};
http.get(options, function(res) {
console.log(res);
res.pipe(process.stdout);
});
You probably want to establish a TLS encrypted connection between your node app and target destination through a proxy.
In order to do this you need to send a CONNECT request with the target destination host name and port. The proxy will create a TCP connection to the target host and then simply forwards packs between you and the target destination.
I highly recommend using the request client. This package simplifies the process and handling of making HTTP/S requests.
Example code using request client:
var request = require('request');
request({
url: 'https://www.google.com',
proxy: 'http://97.77.104.22:3128'
}, function (error, response, body) {
if (error) {
console.log(error);
} else {
console.log(response);
}
});
Example code using no external dependencies:
var http = require('http'),
tls = require('tls');
var req = http.request({
host: '97.77.104.22',
port: 3128,
method: 'CONNECT',
path: 'twitter.com:443'
});
req.on('connect', function (res, socket, head) {
var tlsConnection = tls.connect({
host: 'twitter.com',
socket: socket
}, function () {
tlsConnection.write('GET / HTTP/1.1\r\nHost: twitter.com\r\n\r\n');
});
tlsConnection.on('data', function (data) {
console.log(data.toString());
});
});
req.end();

EPROTO error when trying to make a proxied HTTPS request with node.js 0.12

At a high level, I am trying to use Quota Guard Static to talk to an IP limited API from a Heroku app from a node.js application. The API has its own node.js client implementation, but underneath the covers it's just an HTTP[S] api. The library uses superagent and superagent-proxy underneath the covers to do the actual HTTP[S] requests.
In node 0.10, all worked fine. In node 0.12, I see errors like:
Error: write EPROTO 140735203734288:error:140770FC:SSL routines:SSL23_GET_SERVER_HELLO:unknown protocol:../deps/openssl/openssl/ssl/s23_clnt.c:782:
at exports._errnoException (util.js:746:11)
at WriteWrap.afterWrite (net.js:775:14)
In io.js 2.02 I see:
Error: write EPROTO at Object.exports._errnoException (util.js:844:11)
I tried globally using SSLv3 as shown in this answer, but it seemed to have no effect.
The proxy url is specified as an http URL with port 9293. This answer suggested using port 443, but since the proxy provider is external to me, I cannot change it.
How might I get the proxied request to work in node 0.12?
Tim here from QuotaGuard. This seems to be an issue manifesting itself in the https-proxy-agent used by superagent-proxy for HTTPS requests causing the request to be made to the secure endpoint on the wrong port.
This is a simple example that should connect to Google on port 443.
var url = require('url');
var https = require('https');
var HttpsProxyAgent = require('https-proxy-agent');
// HTTP/HTTPS proxy to connect to
var proxy = process.env.QUOTAGUARDSTATIC_URL;
console.log('using proxy server %j', proxy);
// HTTPS endpoint for the proxy to connect to
var endpoint = process.argv[2] || 'https://www.google.com/';
console.log('attempting to GET %j', endpoint);
var opts = url.parse(endpoint);
// create an instance of the `HttpsProxyAgent` class with the proxy server information
var agent = new HttpsProxyAgent(proxy);
opts.agent = agent;
https.get(opts, function (res) {
console.log('"response" event!', res.headers);
res.pipe(process.stdout);
});
The actual request is being made on port 80 so Google is rejecting it. Here are the HTTP headers:
["Proxy-Authorization: Basic Xgat28sa78saBZZ \r\n", "Host: www.google.com:80\r\n", "Connection: close\r\n"]
The same example on a patched version correctly connects to port 443 and works:
https://github.com/TooTallNate/node-https-proxy-agent/compare/master...timrwilliams:master
I suspect something has changed upstream which is causing the wrong port to be passed to https-proxy-agent but this type of problem is more appropriately discussed on Github issues.
A quick fix would be switching to use the request library instead:
var request = require('request');
var options = {
proxy: process.env.QUOTAGUARDSTATIC_URL,
url: 'https://www.google.com/',
headers: {
'User-Agent': 'node.js'
}
};
function callback(error, response, body) {
if (!error && response.statusCode == 200) {
console.log(body);
}
}
request(options, callback);

nodejs make a GET call to an https site

i am using nodejs v0.10.28 on ubuntu 12LTS,
am trying to make a request to https enable site behind a web proxy using this test code:
var http = require("http");
var options = {
host: "mycompany.proxy.host",
port: 8088,
//host: "https://login.launchpad.net",
//port: 443,
//path: "/",
headers: {
Host: "login.launchpad.net"
}
};
http.get(options, function(res) {
console.log(res);
res.pipe(process.stdout);
}).on('error', function(e) {
console.log(e);
});
but am not sure what are the correct option params to use to make a successful HTTP GET
You should be using node https instead of http module for making https requests. Otherwise the server will most likely reset your connection, and an ECONNRESET error will be caught by the error handler.

restify JSON client returns DEPTH_ZERO_SELF_SIGNED_CERT error

I have server runing on heroku with heroku SSL addon.
Server is created with this options:
name: 'ServerName',
version: '1.0.0',
And the I run server like this:
server.listen(process.env.PORT || 5000)
And it works fine, I can call my api for example: https://myapp.herokuapp.com/some-path. SSL cert on heroku is self-signed so there is a big warning in webbrowser but I can click continue and it works.
When I want to call my server with restify JSON client, created as follows:
var client = restify.createJsonClient({
url: 'https://myapp.herokuapp.com'
});
and then call some api like this client.get('/some-path',...) then client returns error:
DEPTH_ZERO_SELF_SIGNED_CERT
I tried to set option rejectUnauthorized on both server and client (as constructor option) but it didnt help...
I've just tested on my own HTTPS server with a self-signed certificate. rejectUnauthorized on the client side should definitely solve it for you
var restify = require('restify'),
client = restify.createJsonClient({
url: 'https://127.0.0.1/booking',
rejectUnauthorized: false
}),
assert = require('assert');
describe('/booking/ component\'s JSON-HAL HTTP API description', function () {
it('responds with status 200', function (done) {
client.get('/', function (error, request, response) {
assert(!error);
assert.strictEqual(response.statusCode, 200);
done();
});
});
});
As soon as I remove the rejectUnauthorized option, the test fails with DEPTH_ZERO_SELF_SIGNED_CERT.
For me rejectUnauthorized didn't work.
Finally I ended up with:
process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
And it works...

Resources