How can response header can be modified? - node.js

i have a problem that break my mind since 2 days.
request( {url: url } , function(err,res, body){ res.headers['...'] = ...; return res }).pipe(response);
I thought this code change the header from the first response then put him on the second response. but NOT, all attempt fail. I try and I try but NOTHING, NOTHING WORKK.
Look, I'm really in peace and open-minded.
that's my code clear and concise :
modify_header(err,res,body){
var header = res.headers;
header['x-frame-options'] = null;
header['Set-Cookie'] = 'HttpOnly;';
return res;
}
request_src(req,response){
let isabsolute = this.decode_href(req.url);
if(!isabsolute) {
request.get({ url : this.url+req.url , headers : this.headers },this.modify_header).pipe(response);
}else{
request.get({ url : isabsolute , headers : this.headers },this.modify_header).pipe(response);
}
return false;
}
request_src(req,response) is a function called in http.createServer so, req & response are just the request from clients.
then, when i do request.get( {url:this.url ... I send client's request to an another site, like a proxy. but i need to change the header between the "other site" and the client. and believe me for sure, function modify_header modify nothing.
no, i lie just a little, when i set header['x-frame-options'] = null;res.headers is equals to null. that's ok
BUT, in the browser (client side) It just doesn't work that way. 'x-frame-options' is deny or something that's not mine (the same for cookie).
can you help me please, I pull out my hair since 2 days and this isn't good for me.
thank you.

The issue is that callbacks actually happen after a request is complete, which is way after the pipe has already begun streaming data to the response. You probably want to hook into the response event, which would look something like this:
request.get({ url: this.url + req.url, headers: this.headers })
.on('response', function (resp) {
resp.headers['my-custom-header'] = 'blah';
})
.pipe(response);
The response event is emitted before streaming to the destination (which in this case, is back to the original caller), so you should be able to do what you want using this method.

my code looks like that right now :
modify_header(res){
var header = res.headers;
header['x-frame-options'] = null;
header['Set-Cookie'] = 'HttpOnly;';
return res;
}
request_src(req,response){
console.log('original: ',req.url);
let isabsolute = this.decode_href(req.url);
if(!isabsolute) {
request.get({ url : this.url+req.url , headers : this.headers })
.on('response',this.modify_header)
.pipe(response);
}else{
request.get({ url : isabsolute , headers : this.headers})
.on('response',this.modify_header)
.pipe(response);
}
return false;
}

Related

How to use the full request URL in AWS Lambda to execute logic only on certain pages

I have a website running on www.mywebsite.com. The files are hosted in an S3 bucket in combination with cloudFront. Recently, I have added a new part to the site, which is supposed to be only for private access, so I wanted to put some form of protection on there. The rest of the site, however, should remain public. My goal is for the site to be accessible for everyone, but as soon as someone gets to the new part, they should not see any source files, and be prompted for a username/password combination.
The URL of the new part would be for example www.mywebsite.com/private/index.html ,...
I found that an AWS Lambda function (with node.js) is good for this, and it kind of works. I have managed to authenticate everything in the entire website, but I can't figure out how to get it to work on only the pages that contain for example '/private/*' in the full URL name. The lambda function I wrote looks like this:
'use strict';
exports.handler = (event, context, callback) => {
// Get request and request headers
const request = event.Records[0].cf.request;
const headers = request.headers;
if (!request.uri.toLowerCase().indexOf("/private/") > -1) {
// Continue request processing if authentication passed
callback(null, request);
return;
}
// Configure authentication
const authUser = 'USER';
const authPass = 'PASS';
// Construct the Basic Auth string
const authString = 'Basic ' + new Buffer(authUser + ':' + authPass).toString('base64');
// Require Basic authentication
if (typeof headers.authorization == 'undefined' || headers.authorization[0].value != authString) {
const body = 'Unauthorized';
const response = {
status: '401',
statusDescription: 'Unauthorized',
body: body,
headers: {
'www-authenticate': [{key: 'WWW-Authenticate', value:'Basic'}]
},
};
callback(null, response);
}
// Continue request processing if authentication passed
callback(null, request);
};
The part that doesn't work is the following part:
if (!request.uri.toLowerCase().indexOf("/private/") > -1) {
// Continue request processing if authentication passed
callback(null, request);
return;
}
My guess is that the request.uri does not contain what I expected it to contain, but I can't seem to figure out what does contain what I need.
My guess is that the request.uri does not contain what I expected it to contain, but I can't seem to figure out what does contain what I need.
If you're using a Lambda#Edge function (appears you are). Then you can view the Request Event structure here: https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/lambda-event-structure.html#lambda-event-structure-request
You can see the actual value of the request URI field by using console.log and checking the respective logs in Cloudwatch.
The problem might be this line:
if (!request.uri.toLowerCase().indexOf("/private/") > -1) {
If you're strictly looking to check if a JavaScript string contains another string in it, you probably want to do this instead:
if (!request.uri.toLowerCase().indexOf("/private/") !== -1) {
Or better yet, using more modern JS:
if (!request.uri.toLowerCase().includes("/private/")) {

HttpClientModule http.get not working

I picked up this previous working app (Angular2) and find that it is not working (Angular4) as expected now.
Module was used (it may not matter):
import { HttpModule, JsonpModule } from '#angular/http';
Module being used now:
import { HttpClientModule} from '#angular/common/http';
Trying to get a list of records from the backend (Node.js, Express, and MongoDB) as below.
listResource(resType: string, parameters: string[]) {
console.log("***listResource");
let headers = new HttpHeaders().set("X-CustomHeader", "custom header value");
headers.append('Content-Type', 'application/fhir+json');
headers.append("'Access-Control-Allow-Origin", "*");
headers.append("Accept", "application/fhir+json");
headers.append("USER_KEY", "QIF83Fjoe4sYxdQsah3h"); //TOUCHSTONE KEY
let urlString = this.baseUrl + resType + "/list"; // + queryString;
console.log("List resource URL string: [" + urlString + "]");
return (this.httpClient.get(urlString, { headers })
.map((res: Response) => res.json()))
.catch((error: any) => Observable.throw(error.json().error || 'Server error from Observable http.get call')); //...errors if any
}
when my component is loaded, the above listResource will be called as below.
ngOnInit() {
//Get the initial 25 latest patient
//this.progressBar = true;
this.currentPage = 0;
this.patientList = [];
this.globalSvc.gPatient = [];
console.log("***OnInit");
this.restSvc.listResource("Patient", ["identifier=*", "family=*", "given=*"]).subscribe(
data => {
console.log("Response data: " + JSON.stringify(data));
},
(err: HttpErrorResponse) => {
if (err.error instanceof Error) {
// A client-side or network error occurred. Handle it accordingly.
console.log('An error occurred:', err.error.message);
} else {
// The backend returned an unsuccessful response code.
// The response body may contain clues as to what went wrong,
console.log(`Backend returned code ${err.status}, body was: ${err.error}`);
}
});
}
Below is the output from Chrome console. Of course, I don't get any good response. It seems to me the Chrome browser sends CORS option and the server responds correctly, then the browser doesn't send the actual GET.
If I send the REST API request from PostMan directly which doesn't have CORS, I get the expected good response from the server. Hence, it seems to me the server is ok.
Questions:
Any idea how to debug or fix it?
Will this relate to CORS on both Angular client and Node.js server?
The ${err.status} and ${err.error} are "undefined" in Chrome console. How can I find the actual error?
console.log(Backend returned code ${err.status}, body was: ${err.error});
Update 1 based on Grey's suggestion on the immutable header and const.
The GET is returning data now.
headers.append() does not alter the headers, it returns a new Headers (because Headers is immutable).
So, instead of
let headers = new HttpHeaders().set("X-CustomHeader", "custom header value");
headers.append('Content-Type', 'application/fhir+json');
headers.append("'Access-Control-Allow-Origin", "*");
headers.append("Accept", "application/fhir+json");
headers.append("USER_KEY", "QIF83Fjoe4sYxdQsah3h"); //TOUCHSTONE KEY
you need to do something like:
let headers = new HttpHeaders().set("X-CustomHeader", "custom header value")
.append('Content-Type', 'application/fhir+json')
.append("'Access-Control-Allow-Origin", "*")
.append("Accept", "application/fhir+json")
.append("USER_KEY", "QIF83Fjoe4sYxdQsah3h");
Oh, and that should actually be const headers =, rather than let headers =

angular2-rc1 http.post not working correctly

I am creating one angular2 app in which I am using http in my service to make POST call to mongoDB.
When I am making a POST call for the first time it is working fine i.e. entry is inserted into database correctly but when I am sending the data for second time it is not getting inserted.
If I am reloading the page after first insertion then its working fine.
I did some debugging and found that during second request my req.body is blank.
Here is my code:
page.service.ts
savePage(page: Object) {
this.headers.append('Content-Type', 'application/json');
let url = this.baseUrl+'/pm/pages/';
let data={};
data["data"]=page;
console.log(data); //this is printing both times correctly
//on second request data is blank where as it works correctly for first time
return this.http.post(url, JSON.stringify(data),{headers: this.headers})
.map((res: Response) => res.json()).catch(this.handleError);
}
Here is my data in req.body shown in node services.
First Request:
body:{
data:{
name: 'wtwetwet',
desc: 'wetwetwetetwte',
isPublic: true,
createdBy: 'Bhushan'
}
}
Second Request
body: {}
any inputs?
It looks more like a backend thing. You should however include the code that subscribes on this http call.
By the way, why are you using RC1? Angular 2 is now on RC5.
I finally realised that my method used to set content-type each time it was getting called.
Thus changing code from :
savePage(page: Object) {
this.headers.append('Content-Type', 'application/json');
let url = this.baseUrl+'/pm/pages/';
let data={};
data["data"]=page;
return this.http.post(url, JSON.stringify(data),{headers: this.headers})
.map((res: Response) => res.json()).catch(this.handleError);
}
to:
savePage(page: Object) {
this.headers=new Headers();
this.headers.append('Content-Type', 'application/json');
let url = this.baseUrl+'/pm/pages/';
let data={};
data["data"]=page;
return this.http.post(url, JSON.stringify(data),{headers: this.headers})
.map((res: Response) => res.json()).catch(this.handleError);
}
did the trick for me.
Actually we need to set headers only once while making a rest call,but in my code it was already set thus creating new Headers Object helped me clearing any previously set configurations.
Thanks for the help guys.

What is the correct syntax for winjs.xhr data

I tried to use winjs.xhr to POST some data to a URL with no success. I got it working by essentially doing the same thing with XMLHttpRequest. This just doesn't feel right, as winjs.xhr, I thought, wraps XMLHttpRequest anyway. Can anyone explain how I do this in winjs.xhr?
Not working winjs.xhr code
Passing everything in as a URL encoded string
var url = "http://localhost/paramecho.php";
var targetUri = "http://localhost/paramecho.php";
var formParams = "username=foo&password=bar" //prefixing with a '?' makes no difference
//ends up with same response passing an object(below) or string (above)
//var formObj = {username: "foo", password: "bar"}
WinJS.xhr({
type: "post",
url: targetUri,
data: formParams
}).then(function (xhr) {
console.log(xhr.responseText);
});
I end up with my receiving PHP file getting none of the parameters, as though I'd sent no data in the first place.
I tried a few things but the code above is the simplest example. If I was to pass an object into the data parameter it behaves the same way (commented out). I've used a FormData object as well as a plain JSON object.
I changed my app manifest to have the correct network capabilities - and the working example below was done in the same app, so I'm confident it's not capability-related.
Working version using XMLHttpRequest
var username = "foo";
var password = "bar";
var request = new XMLHttpRequest();
try {
request.open("POST", "http://localhost/paramecho.php", false);
request.setRequestHeader('Content-type', "application/x-www-form-urlencoded");
request.send("username=" + encodeURIComponent(username) + "&password=" + encodeURIComponent(password));
console.log(request.responseText);
} catch (e) {
console.log("networkError " + e.message + " " + e.description);
}
And this successfully calls my PHP server-side function, with the parameters I expected.
So, the question is...how do I achieve what I have working in XMLHttpRequest with winjs.xhr? It feels like winjs.xhr the way this is supposed to work (I'm a newbie at Windows 8 app development so I'm happy to be corrected)
You're completely right WiredPrairie. Passing in the header is all that is needed - I think I'd assumed that was the default for a post.
Working version:
WinJS.xhr({
type: "post",
url: targetUri,
data: formParams,
headers: {"Content-type": "application/x-www-form-urlencoded"}
}).then(function (xhr) {
console.log(xhr.responseText);
});

check on server side if youtube video exist

How to check if youtube video exists on node.js app server side:
var youtubeId = "adase268_";
// pseudo code
youtubeVideoExist = function (youtubeId){
return true; // if youtube video exists
}
You don't need to use the youtube API per-se, you can look for the thumbnail image:
Valid video = 200 - OK:
http://img.youtube.com/vi/gC4j-V585Ug/0.jpg
Invalid video = 404 - Not found:
http://img.youtube.com/vi/gC4j-V58xxx/0.jpg
I thought I could make this work from the browser since you can load images from a third-party site without security problems. But testing it, it's failing to report the 404 as an error, probably because the content body is still a valid image. Since you're using node, you should be able to look at the HTTP response code directly.
I can't think of an approach that doesn't involve making a separate HTTP request to the video link to see if it exists or not unless you know beforehand of a set of video IDs that are inactive,dead, or wrong.
Here's an example of something that might work for you. I can't readily tell if you're using this as a standalone script or as part of a web server. The example below assumes the latter, assuming you call a web server on /video?123videoId and have it respond or do something depending on whether or not the video with that ID exists. It uses Node's request library, which you can install with npm install request:
var request = require('request');
// Your route here. Example on what route may look like if called on /video?id=123videoId
app.get('/video', function(req, response, callback){
var videoId = 'adase268_'; // Could change to something like request.params['id']
request.get('https://www.youtube.com/watch?v='+videoId, function(error, response, body){
if(response.statusCode === 404){
// Video doesn't exist. Do what you need to do here.
}
else{
// Video exists.
// Can handle other HTTP response codes here if you like.
}
});
});
// You could refactor the above to take out the 'request.get()', wrap it in a function
// that takes a callback and re-use in multiple routes, depending on your problem.
#rodrigomartell is on the right track, in that your check function will need to make an HTTP call; however, just checking the youtube.com URL won't work in most cases. You'll get back a 404 if the videoID is a malformed ID (i.e. less than 11 characters or using characters not valid in their scheme), but if it's a properly formed videoID that just happens to not correspond to a video, you'll still get back a 200. It would be better to use an API request, like this (note that it might be easier to use the request-json library instead of just the request library):
request = require('request-json');
var client = request.newClient('https://www.googleapis.com/youtube/v3/');
youtubeVideoExist = function (youtubeId){
var apikey ='YOUR_API_KEY'; // register for a javascript API key at the Google Developer's Console ... https://console.developers.google.com/
client.get('videos/?part=id&id='+youtubeId+'&key='+apikey, function(err, res, body) {
if (body.items.length) {
return true; // if youtube video exists
}
else {
return false;
}
});
};
Using youtube-feeds module. Works fast (~200ms) and no need API_KEY
youtube = require("youtube-feeds");
existsFunc = function(youtubeId, callback) {
youtube.video(youtubeId, function(err, result) {
var exists;
exists = result.id === youtubeId;
console.log("youtubeId");
console.log(youtubeId);
console.log("exists");
console.log(exists);
callback (exists);
});
};
var notExistentYoutubeId = "y0srjasdkfjcKC4eY"
existsFunc (notExistentYoutubeId, console.log)
var existentYoutubeId = "y0srjcKC4eY"
existsFunc (existentYoutubeId, console.log)
output:
❯ node /pathToFileWithCodeAbove/FileWithCodeAbove.js
youtubeId
y0srjcKC4eY
exists
true
true
youtubeId
y0srjasdkfjcKC4eY
exists
false
false
All you need is to look for the thumbnail image. In NodeJS it would be something like
var http = require('http');
function isValidYoutubeID(youtubeID) {
var options = {
method: 'HEAD',
host: 'img.youtube.com',
path: '/vi/' + youtubeID + '/0.jpg'
};
var req = http.request(options, function(res) {
if (res.statusCode == 200){
console.log("Valid Youtube ID");
} else {
console.log("Invalid Youtube ID");
}
});
req.end();
}
API_KEY is not needed. It is quite fast because there is only header check for statusCode 200/404 and image is not loaded.

Resources