Meteor: what is the final Location/URL after following redirects? - javascript

Meteor's HTTP package is a wrapper around mikeal's request, and it supports the followRedirects option. But how can one find out what the final URL is, after the 3xx redirect responses have been followed (and the request didn't fail because of lack of a cookie jar)?
With request, the final URL is in response.request.href. But with Meteor... ?
Here's the Meteor code:
if (Meteor.isServer) {
Meteor.startup(function () {
var url = 'http://google.com';
var result = HTTP.call("HEAD", url, {
followRedirects: true
});
console.log(result); // nothing here hints at the final URL
});
}

I've created a package that does this - http-more.
Turns out Meteor doesn't pass back the request object within the response, and given the history of rejected PRs concerning enhancements to the HTTP package, I've just implemented that option separately.

Related

Node request() not being sent

I'm using the npm request library and am running into an issue where the request is never sent if I call express's res.send() after calling request. I realize the request callback won't fire if I close the connection, but I'm not even seeing the request being sent in the first place.
This code is being executed on RunKit (formerly TonicDev), an online code editor that allows code execution via endpoints. I'm not seeing this issue on my local machine, so it seems like it may have to do with RunKit. Anyone have any ideas as to what's going on here or how I might work around this?
You can execute the code yourself by going to:
https://runkit.com/gragland/58056bc6e9d9ed00130c84d5 and clicking the endpoint link at the top.
// Helper to return a RunKit compatible express app (runkit.com/tonic/express-endpoint)
var tonicExpress = require("#runkit/tonic/express-endpoint/1.0.0")
// Provide the exports object to the tonicExpress helper
var app = tonicExpress(module.exports)
var request = require('request')
app.get("/", function(req, res){
var request_number = 9
request({
// To see if request is sent go to: https://requestb.in/1coqbqn1?inspect
url: 'http://requestb.in/1coqbqn1',
method: 'POST',
json: {
request_number: request_number,
message: 'hello'
}
})
// The line below has to be commented out for the above request to be sent
// I don't care about the request callback() firing, I just want the request to be sent
res.send('Done')
})

Node.js JavaScript Basic Get Request

Just installed node.js, and I'm having trouble sending basic get requests. I used to run things in chrome/firefox's console but wanted to branch out. What I am trying to do (as a test) is send a get request to a webpage, and have it print out some text on it.
In chrome's console, I would do something like this:
$.get("http://stackoverflow.com/questions/1801160/can-i-use-jquery-with-node-js", function(data) {
console.log($(data).find(".question-hyperlink")[0].innerHTML);
});
In node.js, how would I do that? I've tried requiring a few things and gone off a few examples but none of them worked.
Later on, I'll also need to add parameters to get and post requests, so if that involves something different, could you show how to send the request with the parameters {"dog":"bark"}? And say it returned the JSON {"cat":"meow"}, how would I read/get that?
You can install the request module with:
npm install request
And, then do this in your node.js code:
const request = require('request');
request.get("http://stackoverflow.com/questions/1801160/can-i-use-jquery-with-node-js", function(err, response, body) {
if (err) {
// deal with error here
} else {
// you can access the body parameter here to see the HTML
console.log(body);
}
});
The request module supports all sorts of optional parameters you can specify as part of your request for everything from custom headers to authentication to query parameters. You can see how to do all those things in the doc.
If you want to parse and search the HTML with a DOM like interface, you can use the cheerio module.
npm install request
npm install cheerio
And, then use this code:
const request = require('request');
const cheerio = require('cheerio');
request.get("http://stackoverflow.com/questions/1801160/can-i-use-jquery-with-node-js", function(err, response, body) {
if (err) {
// deal with error here
} else {
// you can access the body parameter here to see the HTML
let $ = cheerio.load(body);
console.log($.find(".question-hyperlink").html());
}
});

How should AngularJS handle 403 error in $http.post due to outdated XSRF token?

An AngularJS version 1.4.8 app is getting an unhandled 403 error when its login form sends data to a backend REST authentication service after the user's browser has been left open for many (16 in this case) hours. Upon deeper analysis, the root cause is that the client AngularJS app has outdated cookies for XSRF-TOKEN and JSESSIONID, which causes the backend Spring Security to reject the request to the public /login1 service because Spring thinks the request is cross site request forgery.
The problem can be resolved manually if the user closes all browser windows and then re-opens a new browser window before making the request again. But this is not an acceptable user experience. I have read the AngularJS documentation at this link, and I see that I can add an errorCallback function, but how specifically should i re-write the function to handle the 403 error?
Here is the original this.logForm() method in the authorization service, which you can see does not handle 403 errors:
this.logForm = function(isValid) {
if (isValid) {
var usercredentials = {type:"resultmessage", name: this.credentials.username, encpwd: this.credentials.password };
$http.post('/login1', usercredentials)
.then(
function(response, $cookies) {
if(response.data.content=='login1success'){// do some stuff
} else {// do other stuff
}
}
);
}
};
Here is my very rough attempt at a revised version of the this.logForm() method attempting to handle a 403 error following the example in the AngularJS documentation:
this.logForm = function(isValid) {
if (isValid) {
var usercredentials = {type:"resultmessage", name: this.credentials.username, encpwd: this.credentials.password };
$http({ method: 'POST', url: '/login1', usercredentials })
.then(
function successCallback(response, $cookies) {
// this callback will be called asynchronously when the response is available
if(response.data.content=='login1success'){// do some stuff
} else {// do other stuff
}
},
function errorCallback(response, status) {// is status a valid parameter to place here to get the error code?
// called asynchronously if an error occurs or server returns response with an error status.
if(status == 403){
this.clearCookies();
// try to call this POST method again, but how? And how avoid infinite loop?
}
}
);
}
};
What specific changes need to be made to the code above to handle the 403 error due to server-perceived XSRF-TOKEN and JSESSIONID issues? And how can the post be called a second time after deleting the cookies without leading to an infinite loop in the case where deleting the cookies does not resolve the 403 error?
I am also looking into global approaches to error handling, but there is a combination of public and secure backend REST services, which would need to be handled separately, leading to complexity. This login form is the first point of user entry, and I want to handle it separately before looking at global approaches which would retain a separate handling of the login form using methods developed in reply to this OP.
You could restructure your http calls to auto retry, and use promises in your controllers (or whatever)
var httpPostRetry = function(url, usercredentials) {
var promise = new Promise(function(resolve, reject) {
var retries = 0;
var postRetry = function(url, usercredentials) {
if (retries < 3) {
$http({ method: 'POST', url: '/login1', usercredentials })
.then(function(result) {
resolve(result);
}).catch(function(result) {
retries ++;
postRetry(url, usercredentials);
});
} else {
reject(result);
}
};
}.bind(this));
return promise;
}
and then you would call
httpPostRetry(bla, bla).then(function(result) {
// one of the 3 tries must of succeeded
}).catch(function(result) {
// tried 3 times and failed each time
});
To handle specific http errors you can broadcast that specific error and handle that case in a specific controller. Or use a service to encapsulate the status and have some other part of your code handle the UI flow for that error.
$rootScope.$broadcast('unauthorized http error', { somedata: {} });
Does this help?
Have a look at the angular-http-auth module and how things are done there. I think one key element you would want to use is a http interceptor.
For purposes of global error handling, authentication, or any kind of
synchronous or asynchronous pre-processing of request or
postprocessing of responses, it is desirable to be able to intercept
requests before they are handed to the server and responses before
they are handed over to the application code that initiated these
requests. The interceptors leverage the promise APIs to fulfill this
need for both synchronous and asynchronous pre-processing.
After playing around with interceptors you can look at the angular-http-auth http buffer and the way they handle rejected requests there. If their interceptor receives a responseError, they add the config object - which basically stores all information about your request - to a buffer, and then any time they want they can manipulate elements in that buffer. You could easily adept their code to manipulate the config's xsrfHeaderName, xsrfCookieName, or parameters on your behalf when you receive a 403.
I hope that helps a little.

filter outgoing requests in node.js for logging

I am building an Express app which on certain requests has to make its own HTTP calls. I could use Superagent, request or node's own http.request.
Thing is, I need to log all of those server originating requests and their respective responses. Calling log.info before each and every of those seems silly.
How can you add a pre-filter for all outgoing HTTP calls, and ideally access both req and res?
NOTE: I am not interested in logging requests coming in to the server I am building, only in the requests that the server itself kicks off. Think of my server as a client to another black box server.
What you can do is patch http and https and proxy the request method. This way you can have a global handler that will catch the req & res objects.
var http = require('http');
var https = require('https');
var patch = function(object) {
var original = object.request;
// We proxy the request method
object.request = function(options, callback) {
// And we also proxy the callback to get res
var newCallback = function() {
var res = arguments[0];
// You can log res here
console.log("RES",res.statusCode);
callback.apply(this,arguments);
}
var req = original(options, newCallback);
// You can log your req object here.
console.log(req.method,req.path);
return req;
}
}
patch(http);
patch(https);
http.get("http://www.google.com/index.html", function(res) {
console.log("Got response");
}).on('error', function(e) {
console.log("Got error: " + e.message);
});
Edit: This might work if you use the request npm package as well, as it might just rely on the built-in node.js http.request method anyways.
What server are you going to use for you app?
I would definally bring up such functionality on to server level. Take a look how heroku router is doing it. You can track all of needed information using some of their addons: papertrail, or newrelic ( or use them separately for you app ).
https://papertrailapp.com/
http://newrelic.com/
I like out-of-box solutions in this case, no need extend your app logic for logging such information.
If you want to have your own solution, you can setup nginx to monitor request/response info.
http://nginx.com/resources/admin-guide/logging-and-monitoring/

Call function in nodejs from angular application

I'm having an angular app(angular-seed app) which should call a function in nodejs(web-server.js).
The function in nodejs is just calls a batch file.
If I understood this correctly you want a click on the client-side (angular app) to call a batch file on the server side. You can do this in several ways depending on your requirements, but basically you want the client-side to send a http-request to the server (either with ajax call or form submit) and process this on the server that will call the batch file.
Client-side
On the client-side you need to have a button that uses the angular ng-click directive:
<button ng-click="batchfile()">Click me!</button>
In your angular controller you'll need to use the $http service to make a HTTP GET request to your server on some particular url. What that url is depends how you've set up your express app. Something like this:
function MyCtrl($scope, $http) {
// $http is injected by angular's IOC implementation
// other functions and controller stuff is here...
// this is called when button is clicked
$scope.batchfile = function() {
$http.get('/performbatch').success(function() {
// url was called successfully, do something
// maybe indicate in the UI that the batch file is
// executed...
});
}
}
You can validate that this HTTP GET request is made by using e.g. your browser's developer tools such as Google Chrome's network tab or a http packet sniffer such as fiddler.
Server-side
EDIT: I incorrectly assumed that angular-seed was using expressjs, which it doesn't. See basti1302's answer on how to set it up server-side "vanilla style" node.js. If you're using express you can continue below.
On the server side you need to set up the url in your express app that will perform the batch file call. Since we let the client-side above make a simple HTTP GET request to /performbatch we'll set it up that way:
app.get('/performbatch', function(req, res){
// is called when /performbatch is requested from any client
// ... call the function that executes the batch file from your node app
});
Calling the batch file is done in some ways but you can read the stackoverflow answer here for a solution:
node.js shell command execution
Hope this helps
The OP didn't mention express so I'll provide an alternative for the server side (Node.js part) without using any additional frameworks (which would require installing it via npm). This solution uses just node core:
web-server.js:
'use strict';
var http = require('http')
var spawn = require('child_process').spawn
var url = require('url')
function onRequest(request, response) {
console.log('received request')
var path = url.parse(request.url).pathname
console.log('requested path: ' + path)
if (path === '/performbatch') {
// call your already existing function here or start the batch file like this:
response.statusCode = 200
response.write('Starting batch file...\n')
spawn('whatever.bat')
response.write('Batch file started.')
} else {
response.statusCode = 400
response.write('Could not process your request, sorry.')
}
response.end()
}
http.createServer(onRequest).listen(8888)
Assuming you are on Windows, I would at first use a batch file like this to test it:
whatever.bat:
REM Append a timestamp to out.txt
time /t >> out.txt
For the client side, there is nothing to add to Spoike's solution.

Categories