Get list of blocked requests in Puppeteer - puppeteer

I am trying to get a list of requests that are blocked by the browser (HTTP on HTTPS sites). I tried
page.on('requestfailed', request => {
console.log(request.url());
console.log('failed');
});
but it did not provide the requests. The only requests I saw were those of URLs that do not exist anymore. The blocked (HTTP) request do also not trigger the request event.
page.on('request', request => {
console.log(request.url());
}
Is there another event I can listen to?
EDIT:
I can see that something failed when I use
page._client.on('Network.loadingFailed', async event => {
const request = await page._networkManager._requestIdToRequest.get(event.requestId);
console.log(event);
console.log(request);
});
but the request var is undefined. So I don't know which request failed.

Found out how to solve this
page._client.on('Network.loadingFailed', async event => {
if (requestToBySend[event.requestId] !== undefined) {
let reason = '';
if (event.blockedReason !== undefined) {
reason = event.blockedReason;
} else {
reason = event.errorText;
}
console.log('blocked: ' + requestToBySend[event.requestId] + '; reason: ' + reason);
}
});
page._client.on('Network.requestWillBeSent', async event => {
requestToBySend[event.requestId] = event.request.url;
});
The requestId is known on requestWillbeSent event. I just store those ids to look them up when I need them.
The Chrome events I use can be found here: https://chromedevtools.github.io/devtools-protocol/tot/Network#event-loadingFailed

Related

Using the looker embedsdk the connect() promise never resolves using an sso url

Using the looker embedsdk the connect() promise never resolves using an sso url.
Using the debugger in my dev environment I can see at this point in the #looker/chatty code there is a switch against evt.data.action which is always undefined. The debug in the line above fires many times but I just see:
looker:chatty:host window received +0ms undefined undefined
In the debugger I can see evt.data = {"type":"page:changed","page":{"type":"dashboard","absoluteUrl":"https://company.looker.com:19999/embed/dashboards/104?embed_domain=http://localhost:3100","url":"/embed/dashboards/104?embed_domain=http://localhost:3100"}}
So evt.data.data and evt.data.action are both undefined.
The iframe loads the dashboard fine. I’ve added localhost:3100 to the embed allow list and I’m including ?embed_domain=http://localhost:3100&sdk=2 in the target url when creating the sso url.
This is my code:
const [embed, setEmbed] = useState<LookerEmbedDashboard>();
const embedUrl = useLookerSSOEmbed(dashboard);
const embedCtrRef = useCallback(
(el) => {
if (el && embedUrl) {
el.innerHTML = '';
LookerEmbedSDK.init('https://company.looker.com:19999');
LookerEmbedSDK.createDashboardWithUrl(embedUrl)
.appendTo(el)
.build()
.connect()
.then((dashboard) => {
setEmbed(dashboard);
console.log('dashboard', dashboard);
})
.catch((error) => console.log('error', error));
}
},
[embedUrl]
);
return <EmbedContainer ref={embedCtrRef}></EmbedContainer>;
};
This is the part of the code in #looker/chatty host.js That it dies in. The single case in the switch never hits since evt.data.action is always undefinded:
var windowListener = function (evt) {
if (!_this.isValidMsg(evt)) {
// don't reject here, since that breaks the promise resolution chain
ChattyHost._debug('window received invalid', evt);
return;
}
ChattyHost._debug('window received', evt.data.action, evt.data.data);
switch (evt.data.action) {
case client_messages_1.ChattyClientMessages.Syn:
if (_this._port) {
// If targetOrigin is set and we receive another Syn, the frame has potentially
// navigated to another valid webpage and we should re-connect
if ((_this._targetOrigin && _this._targetOrigin === '*') ||
_this._targetOrigin === evt.origin) {
ChattyHost._debug('reconnecting to', evt.origin);
_this._port.close();
}
else {
ChattyHost._debug('rejected new connection from', evt.origin);
return;
}
}
_this._port = evt.ports[0];
_this._port.onmessage = eventListener;
_this.sendMsg(host_messages_1.ChattyHostMessages.SynAck);
_this._state = ChattyHostStates.SynAck;
break;
}
};

How to bypass CSP(Content-Security-Policy) using puppeteer's API page.addScriptTag?

scenario:
I use puppeteer launched chrome in headless mode, and call page.addScriptTag with an cross-domain javascript file. Now if the opening site has csp set and restricts only same origin javascript tags, how can I bypass this using puppeteer API?
Use:
await page.setBypassCSP(true)
Documentation
This is my first stackoverflow contribution so have mercy on me. I found this work around to allow you to get past CSP, Here.
The basic idea is that you intercept page requests and use a library like node-fetch to make the request and disable the CSP header when passing it back to chrome.
Here's the snippet that initially came from the github issue tracker.
Replace "example.com" with the website that needs to have CSP disabled.
const fetch = require('node-fetch')
const requestInterceptor = async (request) => {
try {
const url = request.url()
const requestHeaders = request.headers()
const acceptHeader = requestHeaders.accept || ''
if (url.includes("example.com") && (acceptHeader.includes('text/html'))) {
const cookiesList = await page.cookies(url)
const cookies = cookiesList.map(cookie => `${cookie.name}=${cookie.value}`).join('; ')
delete requestHeaders['x-devtools-emulate-network-conditions-client-id']
if (requestHeaders.Cookie) {
requestHeaders.cookie = requestHeaders.Cookie
delete requestHeaders.Cookie
}
const theseHeaders = Object.assign({'cookie': cookies}, requestHeaders, {'accept-language': 'en-US,en'})
const init = {
body: request.postData(),
headers: theseHeaders,
method: request.method(),
follow: 20,
}
const result = await fetch(
url,
init,
)
const resultHeaders = {}
result.headers.forEach((value, name) => {
if (name.toLowerCase() !== 'content-security-policy') {
resultHeaders[name] = value
} else {
console.log('CSP', `omitting CSP`, {originalCSP: value})
}
})
const buffer = await result.buffer()
await request.respond({
body: buffer,
resultHeaders,
status: result.status,
})
} else {
request.continue();
}
} catch (e) {
console.log("Error while disabling CSP", e);
request.abort();
}
}
await page.setRequestInterception(true)
page.on('request', requestInterceptor)

Login Service implementation in angularjs not working

This is my controller which is calling the login service
mod.controller("loginCtrl",function($scope,loginService,$http)
{
$scope.Userlogin = function()
{
var User = {
userid :$scope.uname,
pass:$scope.pass
};
var res = UserloginService(User);
console.log(res);
alert("login_succ");
}
});
And this is the login service code which takes the User variable and checks for username & password
mod.service("loginService",function($http,$q) {
UserloginService = function(User) {
var deffered = $q.defer();
$http({
method:'POST',
url:'http://localhost:8080/WebApplication4_1/login.htm',
data:User
}).then(function(data) {
deffered.resolve(data);
}).error(function(status) {
deffered.reject({
status:status
});
});
return deffered.promise;
// var response = $http({
//
// method:"post",
// url:"http://localhost:8080/WebApplication4_1/login.htm",
// data:JSON.stringify(User),
// dataType:"json"
// });
// return "Name";
}
});
I have created a rest api using springs which upon passing json return back the username and password in json like this
Console shows me this error for angular
You need to enable CORS for your application for guidance see this link
https://htet101.wordpress.com/2014/01/22/cors-with-angularjs-and-spring-rest/
I prefer to use Factory to do what you're trying to do, which would be something like this:
MyApp.factory('MyService', ["$http", function($http) {
var urlBase = "http://localhost:3000";
return {
getRecent: function(numberOfItems) {
return $http.get(urlBase+"/things/recent?limit="+numberOfItems);
},
getSomethingElse: function(url) {
return $http.get(urlBase+"/other/things")
},
search: function (searchTerms) {
return $http.get(urlBase+"/search?q="+searchTerms);
}
}
}]);
And then in your controller you can import MyService and then use it in this way:
MyService.getRecent(10).then(function(res) {
$scope.things = res.data;
});
This is a great way to handle it, because you're putting the .then in your controller and you are able to control the state of the UI during a loading state if you'd like, like this:
// initialize the loading var, set to false
$scope.loading = false;
// create a reuseable update function, and inside use a promise for the ajax call,
// which is running inside the `Factory`
$scope.updateList = function() {
$scope.loading = true;
MyService.getRecent(10).then(function(res) {
$scope.loading = false;
$scope.things = res.data;
});
};
$scope.updateList();
The error in the console shows two issues with your code:
CORS is not enabled in your api. To fix this you need to enable CORS using Access-Control-Allow-Origin header to your rest api.
Unhandled rejection error, as the way you are handling errors with '.error()' method is deprecated.
'Promise.error()' method is deprecated according to this and this commit in Angular js github repo.
Hence you need to change the way you are handling errors as shown below :
$http().then(successCallback, errorCallback);
function successCallback (res) {
return res;
}
function errorCallback (err) {
return err;
}
One more thing in your code which can be avoided is you have defined a new promise and resolving it using $q methods, which is not required. $http itself returns a promise by default, which you need not define again inside it to use it as a Promise. You can directly use $http.then().

WebRTC: Connecting multiple listeners to one client, one at a time

I have been trying to get WebRTC to function with a broadcaster and multiple listeners but am stuck when it comes to transferal descriptions and candidates via signalling (with nodejs & socket.io).
I can get the process working between two browsers with a simple nodejs socket app which simply broadcasts the descriptions and candidates to other already connected clients, but when I attempt to store a description and connect with a newly opened browser, nothing happens.
What I basically need to understand is what do I need to provide to one browser, in order for it to begin communicating with another? The project I am working on requires the ability for listeners to join rooms, authenticate, and begin listening to whatever media is being sent.
Below is my client side code:
var audioContext = new webkitAudioContext()
var client = null
var configuration =
{
'iceServers':
[{
'url': 'stun:stun.example.org'
}]
}
$(function ()
{
window.RTCPeerConnection = window.RTCPeerConnection || window.webkitRTCPeerConnection || window.mozRTCPeerConnection
client = new RTCPeerConnection(configuration, { optional:[ { RtpDataChannels: true } ]})
client.onnegotiationneeded = function ()
{
console.log('Negotiation needed')
createOffer()
}
client.onicecandidate = function (event)
{
console.log('onicecandidate')
socket.emit('candidate', JSON.stringify({ 'candidate': event.candidate }))
}
client.onaddstream = function (event)
{
console.log('onaddstream')
$('#player').attr('src', URL.createObjectURL(event.stream))
player.play()
}
socket.on('candidate', function (event)
{
candidate(event)
})
socket.on('description', function (message)
{
if(!client) { return }
client.setRemoteDescription(new RTCSessionDescription(message.sdp), function () {
if (client.remoteDescription.type == 'offer')
client.createAnswer(function (description)
{
client.setLocalDescription(description, function ()
{
socket.emit('description', JSON.stringify({ 'sdp':client.localDescription }))
})
}, function (err)
{
console.log('error: ' + err)
})
}, function(err)
{
console.log('error: ' + err)
})
})
addStream()
})
function createOffer ()
{
if(!client) { return; }
client.createOffer(function (description)
{
console.log(description)
client.setLocalDescription(description, function ()
{
socket.emit('description', JSON.stringify({ 'sdp': client.localDescription }))
console.log('set local description')
})
})
}
function candidate (message)
{
if(message.candidate)
{
console.log('candidate')
client.addIceCandidate(new RTCIceCandidate(message.candidate))
}
}
function addStream ()
{
navigator.webkitGetUserMedia({audio: true, video: false}, function(stream)
{
client.addStream(stream)
})
}
And my signalling part of my server as it currently stands:
io.on 'connection', (socket) ->
socket.on 'description', (data) ->
parsed = JSON.parse data
socket.broadcast.emit 'description', parsed
socket.on 'candidate', (candidate) ->
parsed = JSON.parse candidate
socket.broadcast.emit 'candidate', parsed
I'd appreciate any insight into this. Thanks.
The "PeerConnection" as the name indicates can be used with only one other peer. You cannot cache the offer SDP generated by one PeerConnection instance to use it with more than one other peers.
In your case, you must create a PeerConnection for each browser that you want to send/receive audio and video from and then exchange the corresponding SDP offer and answers with those browsers via your signaling mechanism.
Please feel free to go through some of the links I have mentioned here to understand how WebRTC works.

Is there any workaround to make use of html5 localstorage on both http and https?

I need to store some data client side and this data is too large to store it in a cookie. LocalStorage seemed like the perfect way of doing this but the thing is that the website that i will be using this has some parts that work on https and others with just http and as local storage can't access data from https that you set with http this doesn't seem like a viable solution anymore.
Any idea if there is any solution to this? Any other alternatives?
Store all data on one domain, e.g. https://my.domain.org/.
On https protocols, simply use localStorage.setItem('key', 'value') to save the data.
On http protocols, embed a https frame, and use postMessage to save the data:
Demo: http://jsfiddle.net/gK7ce/4/ (with the helper page being located at http://jsfiddle.net/gK7ce/3/).
// Script at https://my.domain.org/postMessage
window.addEventListener('message', function(event) {
// Domain restriction (to not leak variables to any page..)
if (event.origin == 'http://my.domain.org' ||
event.origin == 'https://my.domain.org') {
var data = JSON.parse(event.data);
if ('setItem' in data) {
localStorage.setItem(data.setItem, data.value);
} else if ('getItem' in data) {
var gotItem = localStorage.getItem(data.getItem);
// See below
event.source.postMessage(
'#localStorage#' + data.identifier +
(gotItem === null ? 'null#' : '#' + gotItem),
event.origin
);
} else if ('removeItem' in data) {
localStorage.removeItem(data.removeItem);
}
}
}, false);
On the http(s) page, the frame can be embedded as follows (replace https://my.mydomain.com with the actual URL. Note that you can simply get a reference to the frame, and use the src attribute):
<iframe name="myPostMessage" src="https://my.domain.org/postMessage" style="display:none;"></iframe>
// Example: Set the data
function LSsetItem(key, value) {
var obj = {
setItem: key,
value: value
};
frames['myPostMessage'].postMessage(JSON.stringify(obj), 'https://my.domain.com');
}
LSsetItem('key', 'value');
Note that the method is asynchronous, because of postMessage. An implementation of the getItem method has to be implemented differently:
var callbacks = {};
window.addEventListener('message', function(event) {
if (event.source === frames['myPostMessage']) {
var data = /^#localStorage#(\d+)(null)?#([\S\s]*)/.exec(event.data);
if (data) {
if (callbacks[data[1]]) {
// null and "null" are distinguished by our pattern
callbacks[data[1]](data[2] === 'null' ? null : data[3]);
}
delete callbacks[data[1]];
}
}
}, false);
function LSgetItem(key, callback) {
var identifier = new Date().getTime();
var obj = {
identifier: identifier,
getItem: key
};
callbacks[identifier] = callback;
frames['myPostMessage'].postMessage(JSON.stringify(obj), 'https://my.domain.com');
}
// Usage:
LSgetItem('key', function(value) {
console.log('Value: ' + value);
});
Note that each callback is stored in a hash. Each message also contains an identifier, so that the window which receives the message calls the correct corresponding callback.
For completeness, here's the LSremoveItem method:
function LSremoveItem(key) {
var obj = {
removeItem: key
};
frames['myPostMessage'].postMessage(JSON.stringify(obj), 'https://my.domain.com');
}