How to get HTTP status code of a page opened in a webview in Electron - electron

I want to open a remote web app in electron's webview, but this app is sometimes down and return 503 response. The problem is that I can't detect any HTTP errors from electron, so that I can do something about it from my side.
Here is a sample of my code :
webviewObj = document.createElement('webview');
webviewObj.addEventListener('did-fail-load', (e) => {
// Is not fired for HTTP errors
});
webviewObj.addEventListener('did-finish-load', (e) => {
// No info about HTTP status code
});
webviewObj.src = "https://web-app.com";
In an old version of electron, the webview had an event did-get-response-details that gives httpResponseCode, but it got deprecated, and I could not find an alternative.
Thanks for your help.

you can use this API https://www.electronjs.org/docs/api/web-contents#event-did-navigate
win.webContents.on('did-navigate', (_event: any, _url: string, httpResponseCode: number) => {
if (httpResponseCode >= 400) {
// what you want to do
}
});

Related

InAppBrowser OAUTH didFailLoadWithError 1004 "Could not connect to the server." "<allow-navigation> not set for url"

I see that this question is similar to another question (webView:didFailLoadWithError -1004: Could not connect to the server while connecting google plus in Phonegap ios) , but somehow different because I've gone over the code line-by-line and it is doing the samething, but it is still not working for me. Perhaps also because I am on different versions: iOS 9.3.2 on an iPhone 5S), Cordova 6.1.1, and cordova-plugin-inappbrowser 1.3.0.
My code works well on my Android, but not on the iPhone. Code is as follows:
var googleapi = {
authorize: function(options) {
var deferred = $.Deferred();
var authUrl = GOOGLE_CLIENT_API_URL + $.param({
client_id: options.client_id,
redirect_uri: options.redirect_uri,
response_type: 'code',
scope: options.scope
});
console.log("authUrl: " + authUrl);
var authWindow = window.open(authUrl, "_blank", "location=no,toolbar=no"); // for iOS add 'toolbar=no'
//The recommendation is to use the redirect_uri "urn:ietf:wg:oauth:2.0:oob"
//which sets the authorization code in the browser's title. However, we can't
//access the title of the InAppBrowser.
//
//Instead, we pass a bogus redirect_uri of "http://localhost", which means the
//authorization code will get set in the url. We can access the url in the
//loadstart and loadstop events. So if we bind the loadstart event, we can
//find the authorization code and close the InAppBrowser after the user
//has granted us access to their data.
//
// To clear the authorization, go to https://accounts.google.com/IssuedAuthSubTokens.
$(authWindow).on('loadstart', function(e) {
var url = e.originalEvent.url;
var code = /\?code=(.+)$/.exec(url);
var error = /\?error=(.+)$/.exec(url);
if(code || error) {
authWindow.close();
}
if (code) {
//Exchange the authorization code for an access token
$.post('https://accounts.google.com/o/oauth2/token', {
code: code[1],
client_id: options.client_id,
client_secret: options.client_secret,
redirect_uri: options.redirect_uri,
grant_type: 'authorization_code'
}).done(function(data) {
// use the token we got back from oauth to setup the api.
gapi.auth.setToken(data);
// load the drive api.
loadDriveApi();
deferred.resolve(data);
}).fail(function(response) {
console.log("Posting code to Google failed. No OAuth token will be returned.");
deferred.reject(response.responseJSON);
});
} else if (error) {
//The user denied access to the app
console.log("Error retrieving code from Google.");
deferred.reject({
error: error[1]
});
}
});
return deferred.promise();
}
};
function checkAuth() {
if(device.platform === 'browser') {
console.log("calling gapi.auth.authorize()");
gapi.auth.authorize(
{
'client_id' : CLIENT_ID,
'scope' : SCOPES.join(' '),
'immediate' : true
}, handleAuthResult);
} else {
// because this is called only after deviceready(), InAppBrowser is initialized by now:
console.log("using the InAppBrowser plugin to authenticate.");
window.open = cordova.InAppBrowser.open;
googleapi.authorize(
{
'client_id' : CLIENT_ID,
'client_secret' : CLIENT_SECRET,
'redirect_uri' : REDIRECT_URI,
'scope' : SCOPES.join(' ')
}, handleAuthResult);
}
}
/**
* Handle response from authorization server.
*
* #param {Object} authResult Authorization result.
*/
function handleAuthResult(authResult) {
var authMenuItem = document.getElementById("menuitemenablegoogledrivebackup");
if (authResult && !authResult.error) {
// If already authorized, change menu option to allow user to deny Authorization
authMenuItem.innerHTML = l("Disable Google Drive Backup");
loadDriveApi();
} else {
alert("Authorization Error: " + authResult.error);
console.log("inside handleAuthResult, authResult.error: " + authResult.error);
// Show auth menu item, allowing the user to initiate authorization
authMenuItem.innerHTML = l("Enable Google Drive Backup");
// use the InAppBrowser to display the authorization window:
// var authWindow = window.open(authUrl, '_blank', 'location=no,toolbar=no');
// or?
// gapi.auth.authorize(
// {
// client_id: CLIENT_ID,
// scope: SCOPES.join(' '),
// immediate: false
// }, handleAuthResult)
}
}
/**
* Load Drive API client library.
*/
function loadDriveApi() {
try {
gapi.client.load('drive', 'v2', null).then(function(resp) {
console.log("Google Drive API v2 loaded successfully.");
}, function(reason) {
alert('Google Drive API v2 FAILED to load: ' + reason.result.error.message);
console.log('Google Drive aPI v2 FAILED to load: ' + reason.result.error.message);
});
} catch(err) {
alert(err.message);
console.log("Google Drive API v2 FAILED to load. Exception: " + err.message);
}
}
From debugging, I see that the Android version calls the window.open() call, which goes through the loadstart handler first once, with the original URL, but it contains no code, and no error, so it just passes through. Then the redirect_url comes up, on a second call to the loadstart handler (is this by the InAppBrowser?) but this time it has the shorter redirect_url with the code appended, so the code is then successfully used to get the token on the "$.post" call. However, on iOS, there is no second call to the loadstart handler.
When I run it in the Chrome debugger, I get no errors, just silent failure. In the XCode debugger, I get errors as follows:
2016-06-09 20:47:27.014 APass2[675:398271] Setting the WebView's frame
to {{0, 0}, {320, 524}} 2016-06-09 20:47:27.015 APass2[675:398271]
Setting the WebView's frame to {{0, 0}, {320, 568}} 2016-06-09
20:47:27.026 APass2[675:398271] THREAD WARNING: ['InAppBrowser'] took
'39.259033' ms. Plugin should use a background thread. 2016-06-09
20:47:27.749 APass2[675:398271] webView:didFailLoadWithError - -1004:
Could not connect to the server. 2016-06-09 20:47:28.955
APass2[675:398271] ERROR Internal navigation rejected -
not set for
url='https://content.googleapis.com/static/proxy.html?jsh=m%3B%2F_%2Fscs%2Fapps-static%2F_%2Fjs%2Fk%3Doz.gapi.en.joG9nQvYxYQ.O%2Fm%3D__features__%2Fam%3DAQ%2Frt%3Dj%2Fd%3D1%2Frs%3DAGLTcCPyXDgCg_S7GlvvvMpztuAZ6V0pEA#parent=file%3A%2F%2F&rpctoken=1268129019'
None of my success or fail callbacks is called.
Please help!!! I'm totally at a loss now.
Thanks,
Edward
First of all, by looking at the InAppBrowser documentation, I learned that there is also a "loaderror" event. Only on iOS, the call to the inAppBrowser.open() was resulting in "loaderror" handler being called. Inside the "loaderror" handler, I was also able to grab the url, just as the original code did on "loadstart". Debugging simultaneously in Chrome and Safari I was able to see that the url was exactly the same in the "loaderror" as in the "loadstart" handler, and the parsing for code and error worked exactly the same way. So, in the first cut, I hacked it that way and got to the next phase (success - sort of). Then I hit another error related to <access-navigation>. Googling that much more, I found that there is a configuration setting available in config.xml in the root of your project.
Lots more Googling pointed me at someone who said to use <allow-navigation href="*" />
Clearly, I was unhappy with that broad a security hole.
So, the bottom line is that I needed to add the urls that the Google api needs to access to the config.xml file as follows:
<allow-navigation href="https://accounts.google.com/*" />
<allow-navigation href="https://content.googleapis.com/*" />
I still need to clean up the code, and probably simplify the error handling in the "loaderror" handler, but I have got it working now!
Most frustrating of all is that this setting is not necessary at all on Android, so I had no reason to suspect this was the problem.
Thank you to those of you who took the time to look at this!
Edward

Ionic Angular. $http and geolocation.getCurrentPosition do not appear to work in iOS simulator

I'm developing a simple iOS app using Ionic.
I have 2 bits of functionality that do not appear to work in the iOS simulator that are working fine when I run the app in chrome.
Firstly, I am using the $http service in a factory to pull in some data:
var harbours = function () {
var deferred = $q.defer();
$http({
method: 'GET',
url: '../data/harbourData.json'
}).success(function (data, status, headers, config) {
deferred.resolve(data);
}).error(function(data, status, headers, config) {
deferred.reject(status);
});
return deferred.promise;
}
.....
return {
all: harbours
....
}
and then set it in my scope in the controller with:
var harboursPromise = Harbours.all();
harboursPromise.then(function(response){
console.log('response from controller', response);
$scope.harbours = response.harbours;
});
This does not appear to execute as I do not get the data in my template, whereas I do when running the app in chrome.
Secondly, geolocation. This piece of code is working fine in the browser but not when I build and run the app in the simulator:
$scope.position = 'Trying to find your location...'
if (navigator.geolocation){
navigator.geolocation.getCurrentPosition(function(position){
$scope.$apply(function(){
$scope.position = position.coords.latitude+", "+position.coords.longitude;
})
})
} else {
$scope.position = "Sorry, we can't get your location";
}
In the running app, it appears to have access to navigator.geolocation as $scope.position does not get changed to "Sorry, we can't get your location". I'm sure there is something fundamentally wrong that I am / am not doing. Can anyone point me in the right direction, please?
Thank you.

ionic framework bad request when calling web service over 3G

I'm developing iOS app using ionic framework and I have one problem when I try to call web service by using 3G network.
here is my service in UserService:
function getUserStat(user_id){
var request = $http({ method: "get",
url: "http://www.example.com/user.php",
params: {
action: "stat",
user_id:user_id
},
data: {
}
});
return(request.then(handleSuccess, handleError));
}
function handleError( response ) {
// The API response from the server should be returned in a
// nomralized format. However, if the request was not handled by the
// server (or what not handles properly - ex. server error), then we
// may have to normalize it on our end, as best we can.
if (!angular.isObject( response.data ) || !response.data.message) {
return( $q.reject("An unknown error occurred.") );
}
// Otherwise, use expected error message.
return( $q.reject( response.data.message ) );
}
// I transform the successful response, unwrapping the application data
// from the API response payload.
function handleSuccess( response ) {
return( response.data );
}
the getUserStat() function will return json back.
here is my controller
UserService.getUserStat($scope.user_id).then(function(data){
alert("Result: " + JSON.stringify(data));
});
in my control I just show the json.
I build this code to my iPhone and test it over WIFI network, everything work fine. If i update the serverside, UserService.getUserStat in controller will show update. but the problem is when I test it on 3G network, iPhone always show the old json returned from the server (even I change server side data).
any idea to solve this problem?
Thank you
I had a similar problem when I tried to upload a camera photo to my data server.when i tested the app on my local WIFI it worked perfectly but when I tested it outside i noticed it fails to upload the file. eventualy the problem was that since the internet outside is much slower the app moved to another view without finish the upload action.
so for example if your controller looks something like this:
.controller('Ctrl1', function(webService, $scope, $state) {
UserService.getUserStat($scope.user_id).then(function(data){
alert("Result: " + JSON.stringify(data));
});
$state.go('app.posts');
});
it should be like this:
.controller('Ctrl1', function(webService, $scope, $state) {
UserService.getUserStat($scope.user_id).then(function(data){
alert("Result: " + JSON.stringify(data));
})
.finally(function() {
$state.go('app.posts');
});
});

$http error handling in AngularJS

$http in my AngularJS project not able to recognize 40X(401,403,405...) errors on iOS.
I am using 1.2.10 AngularJS version and Cordova version 3.4.0.
Below is the code I am using:
TE_SERVICES.factory('hello',function ($http,$rootScope) {
return {
loginUser: function(userCredentials,successCallback,errorCallback){
$http({
method: "GET",
url: "data/example.json",
headers: {"Authorization":'Basic '+userCredentials},
}).then(function(response){
successCallback(response.data);
console.log("Success------"+JSON.stringify(response))
},function(data, status, headers, config){
errorCallback(data);
console.log("Error------"+JSON.stringify(data)+" "+status)
})
}
}
});
hello.loginUser($rootScope.encodedUserCredencials,function(persons) {
// success handler
}, function(data) {
// error handler
console.log(data.status+"===="+status)
});
data.status is returning 0 and status returns undefined.
Please help me to resolve this issue.
Tried to include the domain in whitelist on IOS.But no solution :( It still gives the same response.
But the same code works absolutely fine in Android.
Please help me to resolve this issue.
Thanks in advance :)
So you r using the $http from angular. Do you use the error callback or the second function in the then callback ?
Example
$http.get("someUrl")
.success(function(response){}) // if http code == 200
.error(function(response){}) // else
Or with then, that can take 2 functions. The first is the onSuccess, the second the onError function.
$http.get("someUrl")
.then(function(response){
// if http code == 200
},
function(response){
// else
});
The response parameter does also contain the error codes.
Consider using a $httpInterceptor to handle all errorcodes at the same place, instead handling them in every http callback.
UPDATE:
It seems, that the angular doc is incomplete/wrong for the success callback.
It doesnt pass 4 parameter there. It does pass a response object that contains all the information about request+response and the passed data.
Update to the edit:
Dont write callbacks by yourself. Use angular promises:
TE_SERVICES.factory('hello',function ($http,$rootScope) {
return {
loginUser: function(userCredentials){
return $http({
method: "GET",
url: "data/example.json",
headers: {"Authorization":'Basic '+userCredentials},
}).then(function(response){
return response.data;
},function(response){
return response;
});
}
}
});
hello.loginUser($rootScope.encodedUserCredencials)
.then(function(persons) { // success handler
}, function(data) { // error handler
console.log(data);
});
Try this and tell me if the console.log logs something.
I had exactly the same problem. Cordova app, angular js, IPhone and 401 requests are not received in angular js http interceptor. They work fine on android devices.
My finding was that IPhone browser is handling those at a higher lever and trying to use WWW-Authenticate information to do authentication. This is why the response does not get to angular.
The only solution I found, was to change my service to return 400 instead of 401 in case of an api request. In this case I return 400 with an error message that I handle on client side.
I hope this helps.
My issue with the 403 status code was that my backend returned a response with status 403 but the body of a response did not contain a JSON string. It contained just a string - Authentication Failed.
The rejection variable was an object Error.
I encoded the body and the rejection variable contains a valid response error object.
To handle HTTP errors I use interceptors.
$httpProvider.interceptors.push(function($q, $location, redirect, HTTP_CODES) {
return {
'responseError': function(rejection) {
if (rejection.status === HTTP_CODES.FORBIDDEN) {
redirect('/login', $location.url());
}
return $q.reject(rejection);
}
};
});

401 (Unauthorized) only in release mode. Debugging, everything works perfectly! Why?

I am creating an extension for Google Chrome and I'm having trouble authenticating with Twitter.
This extension is published in this link:
As you can see, I am also consuming Dropbox API (which also works with OAuth 1.0) and it works perfectly!
To work with OAuth use a library called jsOAuth available at this link.
When the user clicks on the "Twitter", a window (popup) appears to be made ​​authentic:
//Request Windows token
chrome.windows.create({url: url, type:"popup"}, function(win){
chrome.tabs.executeScript(win.tabs[0].id, { file: "/scripts/jquery-1.7.1.min.js" }, function() {
chrome.tabs.executeScript(win.tabs[0].id, { file: "/scripts/querystring-0.9.0-min.js" }, function() {
chrome.tabs.executeScript(this.args[0], { file: "/scripts/services/TwitterPage.js" });
});
});
});
url = _https://api.twitter.com/oauth/authorize?oauth_token=XXX&oauth_token_secret=YYY&oauth_callback_confirmed=true_
TwitterPage.js code
$(document).ready(function() {
$("#allow").click(function(){
var token = $.QueryString("oauth_token");
var secret = $.QueryString("oauth_token_secret");
var data = { oauth_token: token, oauth_secret: secret };
chrome.extension.sendRequest(data);
});
});
Then the authentication window is displayed
Full link: http://i.imgur.com/tikh4.png
As you can see in the above code, a request is sent to my extension.
Following is the code that captures this request:
chrome.extension.onRequest.addListener(function(request, sender, sendResponse) {
chrome.windows.remove(sender.tab.windowId, fetchAccessToken);
});
fetchAccessToken function:
fetchAccessToken = function() {
oauthObj.fetchAccessToken(function(){
console.log("This code is only executed when debug step by step")
}, failureHandler);
}
Looking at the console, the error: GET https://api.twitter.com/oauth/access_token 401 (Unauthorized) is displayed
Full image: http://i.stack.imgur.com/8MgNw.png
Questions
What is wrong?
Step by step debugging, authentication is performed successfully!?! Why?
The GET /oauth/access_token is being requested twice. One succeeds and the other doesn't. It is probably getting the 401 because the request_token is only valid once. If you stop it from executing twice it should be fine.
On a side note you are including oauth_callback even while getting an access_token. This is not preferred.

Resources