I am developing a react application (chat app) using electron js (for desktops) I want to make Http requests to certain websites, to get URL metadata (opengraph, schema.org, twitterCard, etc). This cannot be done without disabling the webSecurity in electronJS.
a) is it a good idea to disable webSecurity in electron JS ? since users can send others pretty much anything ?
b) I have managed to achieve this using electron net package. I used it in react (renderer process) and it works smoothly, no need to disable webSecurity. however when a invalid URL is provided it throws an exception in main process (net::ERR_NAME_NOT_RESOLVED) which pops a error dialog box. is there a way to catch this exception in the renderer process?
below is how I used electron net package.
const {net} = window.require('electron').remote
function ScrapeMeta(url) {
var promise = new Promise((resolve, reject) => {
const options = {
url: url,
timeout: 2000
};
const request = net.request(options)
request.on('response', (response) => {
var body = '';
response.on('data', function (d) {
body += d;
});
response.on('end', () => {
if (response.statusCode == 200) {
ParseMeta(body)
.then(meta => resolve(meta))
.catch(err => reject(err))
} else {
reject("request failed with " + response.statusCode);
}
})
})
request.end();
})
return promise;
}
What is the best way to achieve this. thanks.
Related
Using interceptBufferProtocol, I can successfully intercept the loadURL event to https://google.com mainWindow.loadURL("https://google.com/"); and replace it with my custom HTML code. The HTML code has an iframe which I am trying to proxy. This can usually be achieved by setting the electron browserWindow proxy but in my case, it fails to work. I set the proxy with the following code:
mainWindow.webContents.session.setProxy({
proxyRules: "http://" + proxy
}, () => {
console.log('Proxy: http://' + proxy)
})
Intercept url code:
ses.protocol.interceptBufferProtocol('https', (req, callback) => {
ses.resolveProxy(req.url, (x) => {
console.log(x)
})
if (req.url == "https://google.com/") {
fs.readFile(path.join(__dirname, "/../../path/stuff.html"), 'utf8', function(err, html) {
callback(Buffer.from(html, 'utf8'));
});
} else {
const request = net.request(req)
request.on('response', res => {
const chunks = []
res.on('data', chunk => {
chunks.push(Buffer.from(chunk))
})
res.on('end', async () => {
const file = Buffer.concat(chunks)
callback(file)
})
})
if (req.uploadData) {
req.uploadData.forEach(part => {
if (part.bytes) {
request.write(part.bytes)
} else if (part.file) {
request.write(fs.readFileSync(part.file))
}
})
}
request.end()
}
})
However, no matter what I do, it appears to use my local IP instead of a proxy. Do I have any options?
The code runs fine without a proxy. I'm trying to run it with one. The problem lies within the .interceptBufferProtocol() function. Any help would be appreciated!
I have very little experience working with Node.js and jQuery and have been searching for the last few hours for a solution. I have an API from openweathermap.com () that returns weather information in the JSON format, and I am trying to pull the temperature value.
I am using Node.js to run a program that can be accessed from any device on the network and I have previously used jQuery on the client to read the file using $.getJSON but am in the process transferring most of my code to the server side to prevent needing a browser open at all times in order for the program to run properly. Obviously you can't use jQuery with node.js but i tried server adaptations for node.js including cheerio, jsdom, and a standard jquery add-on but none of them would do the trick. I can't use XMLHttpRequest or http.get because its being run server side and I can't simply use JSON.parse because it is pulling from a website.
How can I pull the data from the website, store it as an object, and then pull data from it while using just pure javascript?
Here is what I originally had running on the client:
var updateWeather = function(){
$.getJSON('http://api.openweathermap.org/data/2.5/weather?id=5802340&units=imperial&appid=80e9f3ae5074805d4788ec25275ff8a0&units=imperial', function(data) {
socket.emit("outsideTemp",data.main.temp);
});
}
updateWeather();
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.0/jquery.min.js"></script>
</head>
NodeJS natively supports JSON -- so no "special" work needed. I would recommend using an http client that makes our life easier, like axios, but you can do this natively. I have provided two snippets below for you to get started:
Using popular HTTP Client
const axios = require('axios');
axios.get('http://api.openweathermap.org/data/2.5/weather?id=5802340&units=imperial&appid=80e9f3ae5074805d4788ec25275ff8a0&units=imperial').then((res) => {
console.log(res.data)
})
Plain NodeJS (taken from the NodeJS Docs)
const http = require('http');
http.get('http://api.openweathermap.org/data/2.5/weather?id=5802340&units=imperial&appid=80e9f3ae5074805d4788ec25275ff8a0&units=imperial', (res) => {
const { statusCode } = res;
const contentType = res.headers['content-type'];
let error;
if (statusCode !== 200) {
error = new Error('Request Failed.\n' +
`Status Code: ${statusCode}`);
} else if (!/^application\/json/.test(contentType)) {
error = new Error('Invalid content-type.\n' +
`Expected application/json but received ${contentType}`);
}
if (error) {
console.error(error.message);
// Consume response data to free up memory
res.resume();
return;
}
res.setEncoding('utf8');
let rawData = '';
res.on('data', (chunk) => { rawData += chunk; });
res.on('end', () => {
try {
const parsedData = JSON.parse(rawData);
console.log(parsedData);
} catch (e) {
console.error(e.message);
}
});
}).on('error', (e) => {
console.error(`Got error: ${e.message}`);
});
Many people use request / request promise with node
const req = require('request-promise');
req.get({
uri: 'http://api.openweathermap.org/data/2.5/weather?id=5802340&units=imperial&appid=80e9f3ae5074805d4788ec25275ff8a0&units=imperial',
json: true
}).then(e => {console.log(e.coord)});
I am using Node.js 8.0.0 and wanted to update a file on a platform. For this they have an API with very clear ways to use it.
In this case I have to use a PUT method for this, also the right hostname and path, right auth keys and the file itself that must be of Content-Type: multipart/form-data. So I really wanted to use the node https module and try to not install anything else.
I tried using the request http client (https://github.com/request/request)and worked like a charm but as I previously told, would like to use what we already have in Node without installing anything else. I see some working replies here using Request, but no one using Node https module.
Using the https.request I managed to go to the right URL, pass auth, but it always shows me a correlation error (specifically for this platform not something you can Google I guess).
function update(method, path, params) {
return new Promise((resolve, reject) => {
https.request({
method,
host: HOST,
path: path + (params ? '?' + qs.stringify(params) : ''),
auth: `${USER}:${PASS}`,
formData: document,
}, res => {
let body = '';
res
.on('data', message => body += message)
.on('error', (e) => console.log(e))
.on('end', () => resolve(body));
})
.end();
Where: const document = fs.createReadStream(path.resolve(__dirname, '../../src/', 'myfile.xlf'));
And where I call the update function like this:
await operations.update('PUT', '/right/path/to/update', {
id: `${rightId}`,
});
With this code I don't have any auth problem and I can communicate with the platform, in fact if I use other api method (GET, POST) I can obtain statistics and things like that, but in this case explained before, the response have a 400 Bad Request error, that I am sure is a problem with the way I am "trying to send the file".
Using the request http client, I get no errors and managed to update the document with this code:
function update(path, params) {
const url = 'https://' + HOST + path + (params ? '?' + qs.stringify(params) : '');
return new Promise((resolve, reject) => {
try {
resolve(requests.put({
url,
formData: document,
}).auth(USER, PASS));
} catch (err) {
reject(err);
}
});
}
I do web development with an Android (not rooted) phone and look for a method to show the browser (Chrome or Firefox) console messages.
Neither Android Chrome nor Firefox has web inspector/console and I don't found Firefox (working) add-on.
Update: I can't connect my phone to a computer (ADB, Chrome remote tool... are unavailable).
Anybody can hint me a viable solution?
Try https://github.com/liriliri/eruda, very good approximation of Dev Tools.
All you need to do is add this snippet on top of the page:
<script src="//cdn.jsdelivr.net/npm/eruda"></script>
<script>eruda.init();</script>
Edit
A practical solution can be found at https://stackoverflow.com/a/60106504/1025638
Original (self) answer
I don't found a "just work" solution to solve my problem so I made a short tool to send the logs and errors from the browser to a backend server.
It uses a Proxy around window.console object and implements the function window.onerror to post the messages to the server.
I structured the code to use it as an expressjs middleware for reusability.
It isn't perfect and it may not be compatible with
all browsers, but it really helps if there isn't dev tools in a browser.
Anyone can test it through repl.it.
// Use this module as middleware with expressjs compatible server:
//
// In the server:
// consoleWrapperMiddleware(basePath, app)
// basePath: URL path to send browser messages
// app: expressjs application reference
// return: nothing
//
// In the html page:
// <script src="basePath" />
// basePath: URL path to send browser messages
function consoleWrapper(oldConsole, oldOnerror, serverUrl) {
function _post(log) {
const req = new XMLHttpRequest()
req.open('POST', serverUrl, true)
req.setRequestHeader('Content-Type', 'application/json')
req.send(JSON.stringify(log))
}
const console = new Proxy(oldConsole, {
get: (target, propKey, receiver) => {
const origMethod = target[propKey]
return function (...args) {
if (origMethod === undefined) {
const message = 'unknown console method: '+propKey
_post({ level: 'wrap', message: [message]})
return message
}
else {
let result = origMethod.apply(this, args)
_post({ level: origMethod.name, message: args })
return result
}
}
}
})
const onerror = function(msg, url, line, col) {
if (typeof oldOnerror === 'function')
oldOnerror(arguments)
const content = [ msg, url+':'+line+':'+col ]
_post({ level: 'js-err', message: content })
}
return [ console, onerror ]
}
function consoleWrapperMiddleware(basePath, app) {
app.get(basePath, (req, res) => {
res.status(200).send('[ window.console, window.onerror ] = '+consoleWrapper.toString()+'(window.console, window.onerror, location.protocol.concat("//").concat(location.host).concat("'+basePath+'"))')
console.log('Console wrapper sent')
})
app.post(basePath, (req, res) => {
let body = []
req.on('data', (chunk) => {
body.push(chunk)
}).on('end', () => {
body = Buffer.concat(body).toString()
const logMsg = JSON.parse(body)
console.log('['+logMsg.level+']', ...logMsg.message)
res.writeHead(200)
res.end()
})
})
console.log('Log server listening from',basePath)
}
module.exports = consoleWrapperMiddleware
I'm currently experimenting with Kurento Media Server to rebuild the One2Many example with NodeJS, Socket.io and React but I cannot seem to establish a conenction between the publisher and KMS.
The SDP offer is transmitted to KMS and the answer is transmitted to the client. Every ICECandidates from KMS and the client are transmitted too. The video feedback is showing on the app but nothing is sent to the server and there is no errors. Here's is the chrome://webrtc-internals for my app.
The example app is perfectly working with the same Kurento server, I checked every line and I'm doing the same calls on the backend and on the frontend. Here's the chrome://webrtc-internals for the example app.
For reference, here's the code I'm using on the backend (the errors checking have been removed for this example but nothing is raising an error when I'm using it):
io.on('connect', (socket) => {
const socketInfo = {};
socketInfo.webrtcEndpointCreation = new Promise((resolve, reject) => {
socketInfo.webrtcEndpointCreationResolve = resolve;
socketInfo.webrtcEndpointCreationReject = reject;
});
socket.on('broadcast', (infos, callback) => {
kms.client.create('MediaPipeline', (mediaPipelineError, pipeline) => {
mediaPipeline = pipeline;
mediaPipeline.create('WebRtcEndpoint', (webRtcEndpointError, webRtcEndpoint) => {
socketInfo.webRtcEndpoint = webRtcEndpoint;
presenterWebRtc = webRtcEndpoint;
socketInfo.webrtcEndpointCreationResolve();
webRtcEndpoint.on('OnIceCandidate', (event) => {
socket.emit('iceCandidate',
new kms.lib.register.complexTypes.IceCandidate(event.candidate));
});
webRtcEndpoint.processOffer(infos.sdpOffer, (error, sdpAnswer) => {
callback(null, sdpAnswer);
});
webRtcEndpoint.gatherCandidates();
});
});
});
socket.on('iceCandidate', (candidate) => {
socketInfo.webrtcEndpointCreation.then(() => {
socketInfo.webRtcEndpoint.addIceCandidate(candidate);
});
});
});
And this is the client code:
const options = {
localVideo: document.getElementById('video'),
onicecandidate: (candidate) => {
global.socket.emit('iceCandidate', candidate);
}
};
this.kurentoSocket = new WebRtcPeer.WebRtcPeerSendonly(options, (error) => {
this.kurentoSocket.generateOffer((err, sdpOffer) => {
global.socket.on('iceCandidate', (iceCandidate) => {
this.kurentoSocket.addIceCandidate(iceCandidate);
});
global.socket.emit('broadcast', { sdpOffer }, (broadcastErr, sdpAnswer) => {
this.kurentoSocket.processAnswer(sdpAnswer);
});
});
});
I finally found the problem, it was a Backend issue.
I need to create a IceCandidate object with new kms.lib.register.complexTypes.IceCandidate(candidate) from the message sent by the client before adding it. Because of the way promises works, the error was ignored.