infinite redirect on server-side http request - http

I'm using node.js but I have a feeling this isn't necessarily related to just node - anyway
I'm writing a url shortener in node, and i want to hit the shortened url to get the page title - this works in most cases, usually follows redirects properly, etc.
But when I hit gmail.com, it goes into an infinite redirect loop - http://gmail.com redirects to https://www.google.com/accounds/ServiceLogin?service=mail&passive=true&rm=false&continue=....... which in turn redirects to itself forever.
my code is basically like
var http = require('http'),
https = require('https'),
URL = require('url'),
querystring = require('url');
var http_client = {};
function _makeRequest(url, callback) {
var urlInfo = URL.parse(url);
var reqUrl = urlInfo.pathname || '/';
reqUrl += urlInfo.search || '';
reqUrl += urlInfo.hash || '';
var opts = {
host: urlInfo.hostname,
port: urlInfo.port || (urlInfo.protocol == 'https' ? 443 : 80),
path = reqUrl,
method: 'GET'
};
var protocol = (urlInfo.protocol == 'https' ? https : http);
var req = protocol.request(opts, function(res) {
var content = '';
res.setEncoding('utf8');
res.addListener('data', function(chunk) {
content += chunk;
});
res.addListener('end', function() {
_requestReceived(content, res.headers, callback);
});
});
req.end();
};
function _requestReceived(content, headers, callback) {
var redirect = false;
if(headers.location) {
newLocation = headers.location
redirect = true;
}
if(redirect) {
console.log('redirecting to :'+newLocation);
_makeRequest(newLocation, callback)
} else {
callback(null, content);
}
};
yep!

ahh okay, got it!
my check for https was like
var protocol = (urlInfo.protocol == 'https' ? https : http);
but node adds a colon to the protocol, so it should have been
var protocol = (urlInfo.protocol == 'https:' ? https : http);
Because of this it kept using http and gmail would redirect be to https forever

Related

Firebase short URLs not redirecting

I created a Google Sheet that uses a Google Script to generate short URLs via Firebase API.
This is the code in the Google Script
function URLShortener(longURL) {
var body = {
"dynamicLinkInfo": {
"domainUriPrefix": "https://go.example.com",
"link" : longURL
},
"suffix": {
"option": "SHORT"
}
};
var key = 'xxxxxxx'
var url = "https://firebasedynamiclinks.googleapis.com/v1/shortLinks?key=" + key;
var options = {
'method': 'POST',
"contentType": "application/json",
'payload': JSON.stringify(body),
};
var response = UrlFetchApp.fetch(url, options);
var json = response.getContentText();
var data = JSON.parse(json);
var obj = data["shortLink"];
return obj;
Logger.log(obj)
}
The script works and it generates URLs similar to https://go.example.com/Xdka but these link redirect to https://example.com/Xdka instead of the actual URL that is sent, e.g. https://example.com/final_url.
If I try to generate these short links from the Firebase dashboard the same happens.
Did I misunderstand how these short URLs work or am I missing something?

Get request async call in protractor

I have a test case where my code make request to server and check if the filename with specified date exists. Since GET request is async call, how can I make sure that I have the filename from the server before I check if it is a specified date?
Here's excerpt of my code :
var re = new RInterface();
it('data show exists', function() {
target.each(function(ele){
browser.actions().mouseMove(ele).perform();
re.get(function(result){
expect(result).toEqual(true);
});
});
});
RInterface.js
var Service = function() {
var serv = this;
var uname = atob(settings.username);
var pwd = atob(settings.password);
var url = 'https://' + uname + ':' + pwd + '#' + settings.Url + '/' + settings.format + '/' + settings.period;
var completeURL = url;
var today = DateString();
serv.get = function(callback) {
var dataStrAry = [];
var count = 0;
request(completeURL, function (error, response, body) {
if (!error && response.statusCode == 200) {
var serverData = JSON.parse(body);
var split = serverData[serverData.length-1].Name.split(" ");
var target = split[split.length-1].split(".")[0];
// Check if the file with current date is available
// If it is, then assume data is saved on the server
if(target == today) {
console.log("equal");
callback(true);
}
else {
console.log("not equal");
callback(false);
}
}
else {
console.log("errror call");
callback(false);
return;
}
});
};
So, re.get is where I make GET request to the server and I passed callback function to be called at the end of get request. The problem, I think is protractor complete executing the test before my code gets data from the server. How do I force protractor to wait so that I can check the returned data? My current workaround is put the get request inside beforeEach and seems that protractor forces test to wait for it finish executing.
You need to handle the result of your request with a Promise if you want the control flow to wait for it:
var re = new RInterface();
it('data show exists', function() {
target.each(function(ele){
browser.actions().mouseMove(ele).perform();
expect(re.get()).toEqual(true);
});
});
var Service = function() {
...
this.get = function() {
var defer = protractor.promise.defer();
...
request(completeURL, function (error, response, body) {
if (!error && response.statusCode == 200) {
var result = ...
defer.fulfill(result);
} else {
defer.reject(error);
}
});
return defer.promise;
};
};
You can make it easier by using browser.wait, just wrap the request function (which returns a promise) like
browser.wait(request...).then(result => {
// here continue execution with the result of the request
}
This will make the browser wait for your promise.

XDomainRequest in IE is giving Access is Denied error

This is the code I am using is as follows down below:
I am using IE9 and am unable to see the request being sent in the Network tab. I do have Access-Control headers set in the JSP as:
<% response.setHeader("Access-Control-Allow-Origin", "*");%>
Code to get the AJAX HTML Content from the JSP:
if ($.browser.msie && window.XDomainRequest) {
var xdr = new window.XDomainRequest();
xdr.open("GET", "http://dev01.org:11110/crs/qw/qw.jsp?&_=" + Math.random());
xdr.contentType = "text/plain";
xdr.timeout = 5000;
xdr.onerror = function () {
console.log('we have an error!');
}
xdr.onprogress = function () {
console.log('this sucks!');
};
xdr.ontimeout = function () {
console.log('it timed out!');
};
xdr.onopen = function () {
console.log('we open the xdomainrequest');
};
xdr.onload = function() {
alert(xdr.responseText);
};
xdr.send(null);
} else { ...... }
I am getting a Access is Denied Error. Any help would be much appreciated!
Requests must be targeted to the same scheme as the hosting page
In your example you are doing request to:
http://dev01 ...
And you should do this from HTTP protocol.
For example:
If your site, where js script is located: http://dev.org
You can do this:
xhr = new XDomainRequest();
xhr.open("GET", "http://dev01.org?p=1");
but this throws "Access denied":
xhr = new XDomainRequest();
xhr.open("GET", "https://dev01.org?p=1");
My experience with XDomainRequest is that it doesn't respect Access-Control-Allow-Origin: *. Instead, you must specify the domain. This can be obtained from the HTTP_REFERER header if you need to dynamically generate it, or if you are only expecting requests from one domain you can set it manually. This article might help.
<% response.setHeader("Access-Control-Allow-Origin", "http://dev01.org");%>

How to send consecutive requests with HTTP keep-alive in node.js?

I'm using node.js 0.6.18, and the following code makes node.js close the TCP connection between every two requests (verified with strace on Linux). How do I make node.js reuse the same TCP connection for multiple HTTP requests (i.e. keep-alive)? Please note that the webserver is capable of keep-alive, it works with other clients. The webserver returns a chunked HTTP response.
var http = require('http');
var cookie = 'FOO=bar';
function work() {
var options = {
host: '127.0.0.1',
port: 3333,
path: '/',
method: 'GET',
headers: {Cookie: cookie},
};
process.stderr.write('.')
var req = http.request(options, function(res) {
if (res.statusCode != 200) {
console.log('STATUS: ' + res.statusCode);
console.log('HEADERS: ' + JSON.stringify(res.headers));
process.exit(1)
}
res.setEncoding('utf8');
res.on('data', function (chunk) {});
res.on('end', function () { work(); });
});
req.on('error', function(e) {
console.log('problem with request: ' + e.message);
process.exit(1);
});
req.end();
}
work()
I was able to get this to work (verified with strace) by creating an http.Agent and setting its maxSockets property to 1. I don't know if this is the ideal way to do it; however, it does meet the requirements. One thing that I did notice is that what the docs claimed about http.Agent behavior did not accurately describe how it worked in practice. Code below:
var http = require('http');
var cookie = 'FOO=bar';
var agent = new http.Agent;
agent.maxSockets = 1;
function work() {
var options = {
host: '127.0.0.1',
port: 3000,
path: '/',
method: 'GET',
headers: {Cookie: cookie},
agent: agent
};
process.stderr.write('.')
var req = http.request(options, function(res) {
if (res.statusCode != 200) {
console.log('STATUS: ' + res.statusCode);
console.log('HEADERS: ' + JSON.stringify(res.headers));
process.exit(1)
}
res.setEncoding('utf8');
res.on('data', function (chunk) {});
res.on('end', function () { work(); });
});
req.on('error', function(e) {
console.log('problem with request: ' + e.message);
process.exit(1);
});
req.end();
}
work()
EDIT: I should add that I did my testing with node.js v0.8.7
you can just set:
http.globalAgent.keepAlive = true

Basic HTTP authentication in Node.JS?

I'm trying to write a REST-API server with NodeJS like the one used by Joyent, and everything is ok except I can't verify a normal user's authentication. If I jump to a terminal and do curl -u username:password localhost:8000 -X GET, I can't get the values username:password on the NodeJS http server. If my NodeJS http server is something like
var http = require('http');
http.createServer(function (req, res) {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('Hello World\n');
}).listen(1337, "127.0.0.1");
, shouldn't I get the values username:password somewhere in the req object that comes from the callback ?
How can I get those values without having to use Connect's basic http auth ?
The username:password is contained in the Authorization header as a base64-encoded string.
Try this:
const http = require('http');
http.createServer(function (req, res) {
var header = req.headers.authorization || ''; // get the auth header
var token = header.split(/\s+/).pop() || ''; // and the encoded auth token
var auth = Buffer.from(token, 'base64').toString(); // convert from base64
var parts = auth.split(/:/); // split on colon
var username = parts.shift(); // username is first
var password = parts.join(':'); // everything else is the password
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('username is "' + username + '" and password is "' + password + '"');
}).listen(1337, '127.0.0.1');
From HTTP Authentication: Basic and Digest Access Authentication - Part 2 Basic Authentication Scheme (Pages 4-5)
Basic Authentication in Backus-Naur Form
basic-credentials = base64-user-pass
base64-user-pass = <base64 [4] encoding of user-pass,
except not limited to 76 char/line>
user-pass = userid ":" password
userid = *<TEXT excluding ":">
password = *TEXT
If you're using express, you can use the connect plugin (included with express):
//Load express
var express = require('express');
//User validation
var auth = express.basicAuth(function(user, pass) {
return (user == "super" && pass == "secret");
},'Super duper secret area');
//Password protected area
app.get('/admin', auth, routes.admin);
You can use node-http-digest for basic auth or everyauth, if adding authorization from external services are in you roadmap.
I use this code for my own starter sites with auth.
It does several things:
basic auth
return index.html for / route
serve content without crashing and silent handle the error
allow port parameter when running
minimal amount of logging
Before using the code, npm install express
var express = require("express");
var app = express();
//User validation
var auth = express.basicAuth(function(user, pass) {
return (user == "username" && pass == "password") ? true : false;
},'dev area');
/* serves main page */
app.get("/", auth, function(req, res) {
try{
res.sendfile('index.html')
}catch(e){}
});
/* add your other paths here */
/* serves all the static files */
app.get(/^(.+)$/, auth, function(req, res){
try{
console.log('static file request : ' + req.params);
res.sendfile( __dirname + req.params[0]);
}catch(e){}
});
var port = process.env.PORT || 8080;
app.listen(port, function() {
console.log("Listening on " + port);
});
It can be implemented easily in pure node.js with no dependency, this is my version which is based on this answer for express.js but simplified so you can see the basic idea easily:
const http = require('http');
http.createServer(function (req, res) {
const userpass = Buffer.from(
(req.headers.authorization || '').split(' ')[1] || '',
'base64'
).toString();
if (userpass !== 'username:password') {
res.writeHead(401, { 'WWW-Authenticate': 'Basic realm="nope"' });
res.end('HTTP Error 401 Unauthorized: Access is denied');
return;
}
res.end('You are in! Yay!!');
}).listen(1337, '127.0.0.1');
The restify framework (http://mcavage.github.com/node-restify/) includes an authorization header parser for "basic" and "signature" authentication schemes.
You can use http-auth module
// Authentication module.
var auth = require('http-auth');
var basic = auth.basic({
realm: "Simon Area.",
file: __dirname + "/../data/users.htpasswd" // gevorg:gpass, Sarah:testpass ...
});
// Creating new HTTP server.
http.createServer(basic, function(req, res) {
res.end("Welcome to private area - " + req.user + "!");
}).listen(1337);

Resources