I started playing with nodejs yesterday afternoon. This morning I started writing a proxy server and have the following requirements once a request has been received:
Perform auth check (through request())
If auth check evaluates to true, do actual request (proxy)
Else, redirect
All works correctly bar the actual proxy request (assigned to proxy var). It either is not being called, or at least, the response is not being pipped back to the request. Or something else, which I feel could be to do with the asynchronous behavior of nodejs.
Additional note: "Win!" is output on the console.
Any thoughts are welcomed.
var server = httpProxy.createServer(function(request, response, proxy) {
var requestHostname = request.headers['x-forwarded-host'];
var configFile = './config/'+requestHostname+'.js';
if(path.existsSync(configFile))
{
var config = require(configFile);
var authProxy = requester({
url: config.proxyRequest.url+config.proxyRequest.defaultPath,
port: 443,
method: request.method
}, function(error, proxyResp, body) {
if(config.methods.authCheck(body))
{
console.log('Win!');
proxy = requester({
url: 'http://www.google.com',
port: 443,
method: request.method
});
// Pipe request and response back
request.pipe(proxy);
proxy.pipe(response);
}
else
{
response.writeHead(300, 'Forbidden', {
'Location': globalConf.portalUrl
});
response.end();
}
});
}
else
{
response.writeHeader(400);
response.write('404: The requested URL '+requestHostname+' does not exist.');
response.end();
}
response.addListener('end', function() {
console.log('Ending it');
})
});
The problem is that you are assigning a return value to authProxy and proxy. You should call requester without assigning a value, just:
requester( object, callback );
I'm not sure what requester returns in your code, but when calling async functions, you don't typically expect a return value, handle everything else in the callback passed as parameter. Maybe if you add a sample of that code (requester function), things will be clearer :)
Related
I have to do request from node to Yii2 api. It doesn't throw any errors, but doesn't return anything either. When I do request to Yii2 api method directly in browser, value is returned. Here is my request in route in node:
router.get('', function (req, res) {
var parameter = 20;
request({
url: 'http://**.**.**.***:8000/web/index.php?r=api/get-value',
parameter: parameter,
method: 'GET'
}, function(error, response, body) {
if(error || response.statusCode != 200)
throw error;
res.send(body);
});
});
module.exports = router;
And here is method/endpoint in Yii2 controllers/apiController.php:
public function actionGetValue($inverterId) {
return $inverterId * 2;
}
Any suggestions what could be wrong/missing?
You can use the following
var http = require('http');
var client = http.createClient(8000, 'localhost');
var request = client.request('GET', '/web/index.php?r=api/get-value');
request.write("stuff");
request.end();
request.on("response", function (response) {
// handle the response
});
Resource Link:
Http request with node?
Sending http request in node.js
or Another full example:
Get requests
Now we’ll set up a super simple test to make sure it’s working. If it’s not still running, run your simple Node server so that it’s listening on http://localhost:8000. In a separate file in the same directory as your http-request.js where your new module lives, add a file called test-http.js with the following contents:
// test-http.js
'use strict';
const
request = require('./http-request'),
config = {
method: 'GET',
hostname: 'localhost',
path: '/',
port: 8000
};
request(config).then(res => {
console.log('success');
console.log(res);
}, err => {
console.log('error');
console.log(err);
});
This will import our module, run a request according to the configured options, and console log either the response, or an error if one is thrown. You can run that file by navigating to its directory in the command line, and typing the following:
$ node test-http.js
You should see the following response:
success
{ data: 'Cake, and grief counseling, will be available at the conclusion of the test.' }
Resource Link:
https://webcake.co/sending-http-requests-from-a-node-application/
Okay, shame on me, I did not check, what's going on in public function beforeAction($action) in apiController.php - since request to endpoint getValue() is done from the "outside", it falls under a condition, that does not allow further actions and returns false - that's why response wasn't changing no matter what was done/set in getValue().
UPDATE: I had a mistake on my http request endpoint. I had not set the appropriate authentication options so that fixed a lot of errors possibly this specific one.
My question is similar to one here:
Node.js Lambda function returns "The response is invalid" back to Alexa Service Simulator from REST call
However the solution to that question does not solve my problem. So I make an http request call to an xsjs service in Hana cloud. I am getting the 'response is invalid' error message. I can't see why. Here is my function:
// Create a web request and handle the response.
function httpGet(query, callback) {
console.log("/n QUERY: "+ query);
var host = 'datacloudyd070518trial.hanatrial.ondemand.com';
var path = '/LocationInformation/getLocationInfo.xsjs?location=';
var hostname = 'https://' + host + path + query;
var auth = 'user1:D1anafer';
var req = http.request({'hostname': hostname,
'auth': auth
}, (res) => {
var body = '';
res.on('data', (d) => {
body += JSON.stringify(d);
});
res.on('end', function () {
callback(body);
});
});
req.end();
req.on('error', (e) => {
console.error(e);
});
}
And the function that calls it:
'getNewsIntent': function () {
//self = this;
httpGet(location, function (response) {
// Parse the response into a JSON object ready to be formatted.
//var output = JSON.parse(response);
//output = output['change'];
var output = response;
var cardTitle = location;
var cardContent = output;
alexa.emit(':tellWithCard', output, cardTitle, cardContent);
});
},
Thank You
-Diana
Inside your AWS account go to your Lambda function and click on the monitoring tab, where you should see "View Logs in Cloudwatch" in the right hand corner. If you click that link and you should see the errors that are being produced.
You can also use console.log() to log any information being returned from your REST api, which will be logged in cloudwatch and can help you see where your errors are.
This is just a guess from the top of my head. To really help some detailed error message would be required like mentioned about.
But just a guess: Your http.request() is using the http module (https://nodejs.org/api/http.html) and your are accessing the a https resource. If so there is a https (https://nodejs.org/api/https.html) module or use something like axios https://www.npmjs.com/package/axios or requestjs (https://github.com/request/request) this will handle both.
Like I said just a blind guess without detailed error message and seeing your require statements but I am happy to dive deeper if you happen to have details.
HTH
Your callback from the Lambda has to return a valid status code and body. Like this:
let payload = {
statusCode: 400,
body: JSON.stringify('body'),
headers: {"Access-Control-Allow-Origin": "*"}
};
callback(null, payload);
On top of that, to call this from client side code, you have to pass the CORS header back.
I'm making a testbench with Test'em and Mocha (that run on node.js) in order to test a PHP website.
What I want is to request some URL (e.g http://www.my-website/test.php) and get the http status code as well as the content returned.
I'm doing it with the node.js Request module.
The problem is:
I need to be authenticated to access this page, otherwise I'm
redirected to the login page.
So, does it exist a way to log in my application through Node.js and keep the session open to be able to chain tests on any pages I want?
I was thinking on get the PHPSESSID on login request if it is possible. Do you thing it is a good direction ?
Any help would be much appreciated.
Thank you, have a nice day :)
Michaël
If you set jar: true in your options or use your own custom cookie jar, then request will remember cookies set by the server so that you can keep your session between requests.
mscdex thanks for your answer! But unfortunately it did not work for me :/
hyubs thanks to you too.
Finally I carried on to use Mocha + Request.
Basically what I did is:
Connect through a POST request to the login page and get the PHPSESSID cookie that is returned in the response header.
Pass the cookie in the header in the next requests that target a URL where you have to be logged.
Here is my code :
var params = {
email: 'your_username',
password: 'your_password'
};
var paramsString = JSON.stringify(params);
// Login to the application
request.post('http://localhost/biings/front-end/rest/auth',
{
headers: {
"Content-Type" : "application/json",
'Content-Length' : paramsString.length
},
body: paramsString,
},function (error, response, body) {
// get the PHPSESSID (the last one) that is returned in the header. Sometimes more than one is returned
var sessionCookie = response.headers['set-cookie'][response.headers['set-cookie'].length - 1];
sessionCookie = sessionCookie.split(';');
sessionCookie = sessionCookie[0];
// Write it in a file (this is a quick trick to access it globally)
// e.g.: PHPSESSID=ao9a1j0timv9nmuj2ntt363d92 (write it simply as a string)
fs.writeFile('sessionCookie.txt', sessionCookie, function (err)
{
if(err)
{
return console.log(err);
}
});
});
// don't care about this it() function (it's for Mocha)
it("test 1", function(done)
{
// Get the cookie
fs.readFile('sessionCookie.txt','utf8', function (err, data)
{
if(err)
{
throw err;
}
else
{
// Launch a request that includes the cookie in the header
request.get('http://localhost/biings/front-end/rest/group',
{
headers: {"Cookie" : data},
}, function (error, response, body) {
// Check your request reaches the right page
expect(response.statusCode).equals(200);
console.log(body);
done();
});
}
});
});
It works like a charm for me.
Tell me if you see something wrong or which could be optimized :)
Michaël
Instead of using the request module, use headless browsers like PhantomJS and zombie.js. You can even emulate user interaction with these.
I'm working on testing my node.js code with Zombie.js. I have the following api, which is in POST method:
/api/names
and following code in my test/person.js file:
it('Test Retreiving Names Via Browser', function(done){
this.timeout(10000);
var url = host + "/api/names";
var browser = new zombie.Browser();
browser.visit(url, function(err, _browser, status){
if(browser.error)
{
console.log("Invalid url!!! " + url);
}
else
{
console.log("Valid url!!!" + ". Status " + status);
}
done();
});
});
Now, when I execute the command mocha from my terminal, it gets into browser.error condition. However, if I set my API to get method, it works as expected and gets into Valid Url (else part). I guess this is because of having my API in post method.
PS: I don't have any Form created to execute the queries on button click as I'm developing a back-end for mobile.
Any help on how to execute APIs with POST method would be appreciated.
Zombie is more for interacting with actual webpages, and in the case of post requests actual forms.
For your test use the request module and manually craft the post request yourself
var request = require('request')
var should = require('should')
describe('URL names', function () {
it('Should give error on invalid url', function(done) {
// assume the following url is invalid
var url = 'http://localhost:5000/api/names'
var opts = {
url: url,
method: 'post'
}
request(opts, function (err, res, body) {
// you will need to customize the assertions below based on your server
// if server returns an actual error
should.exist(err)
// maybe you want to check the status code
res.statusCode.should.eql(404, 'wrong status code returned from server')
done()
})
})
it('Should not give error on valid url', function(done) {
// assume the following url is valid
var url = 'http://localhost:5000/api/foo'
var opts = {
url: url,
method: 'post'
}
request(opts, function (err, res, body) {
// you will need to customize the assertions below based on your server
// if server returns an actual error
should.not.exist(err)
// maybe you want to check the status code
res.statusCode.should.eql(200, 'wrong status code returned from server')
done()
})
})
})
For the example code above you will need the request and should modules
npm install --save-dev request should
I probably have some issues with the asyncness of Node.js.
rest.js
var Shred = require("shred");
var shred = new Shred();
module.exports = {
Request: function (ressource,datacont) {
var req = shred.get({
url: 'ip'+ressource,
headers: {
Accept: 'application/json',
},
on: {
// You can use response codes as events
200: function(response) {
// Shred will automatically JSON-decode response bodies that have a
// JSON Content-Type
if (datacont === undefined){
return response.content.data;
//console.log(response.content.data);
}
else return response.content.data[datacont];
},
// Any other response means something's wrong
response: function(response) {
return "Oh no!";
}
}
});
}
}
other.js
var rest = require('./rest.js');
console.log(rest.Request('/system'));
The problem ist if I call the request from the other.js I always get 'undefined'. If I uncomment the console.log in rest.js then the right response of the http request is written to the console. I think the problem is that the value is returned before the actual response of the request is there. Does anyone know how to fix that?
Best,
dom
First off, it is useful to strip down the code you have.
Request: function (ressource, datacont) {
var req = shred.get({
// ...
on: {
// ...
}
});
}
Your Request function never returns anything at all, so when you call it and console.log the result, it will always print undefined. Your request handlers for the various status codes call return, but those returns are inside of the individual handler functions, not inside Request.
You are correct about the asynchronous nature of Node though. It is impossible for you to return the result of the request, because the request will still be in progress when your function returns. Basically when you run Request, you are starting the request, but it can finish at any time in the future. The way this is handled in JavaScript is with callback functions.
Request: function (ressource, datacont, callback) {
var req = shred.get({
// ...
on: {
200: function(response){
callback(null, response);
},
response: function(response){
callback(response, null);
}
}
});
}
// Called like this:
var rest = require('./rest.js');
rest.Request('/system', undefined, function(err, data){
console.log(err, data);
})
You pass a third argument to Request which is a function to call when the request has finished. The standard Node format for callbacks that can fail is function(err, data){ so in this case on success you pass null because there is no error, and you pass the response as the data. If there is any status code, then you can consider it an error or whatever you want.