Skip to content

Use electron-proxy-agent #878

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Nov 30, 2017
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 32 additions & 27 deletions gui/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ const notify = require('electron-main-notification')
const debounce = require('lodash.debounce')
const path = require('path')

const setupProxy = require('./src/main/proxy')

const autoLaunch = require('./src/main/autolaunch')
const lastFiles = require('./src/main/lastfiles')
const tray = require('./src/main/tray')
Expand Down Expand Up @@ -221,35 +223,38 @@ if (shouldExit) {
}

app.on('ready', () => {
log.info('Loading CLI...')
desktop = new Desktop(process.env.COZY_DESKTOP_DIR)
i18n.init(app)
tray.init(app, toggleWindow)
lastFiles.init(desktop)
log.trace('Setting up tray WM...')
trayWindow = new TrayWM(app, desktop)
log.trace('Setting up help WM...')
helpWindow = new HelpWM(app, desktop)
log.trace('Setting up onboarding WM...')
onboardingWindow = new OnboardingWM(app, desktop)
onboardingWindow.onOnboardingDone(() => {
onboardingWindow.hide()
trayWindow.show().then(() => startSync())
})
log.trace('Setting up updater WM...')
updaterWindow = new UpdaterWM(app, desktop)
updaterWindow.onUpToDate(() => {
updaterWindow.hide()
showWindowStartApp()
})
updaterWindow.checkForUpdates()
const {session} = require('electron')
setupProxy(app, session, () => {
log.info('Loading CLI...')
desktop = new Desktop(process.env.COZY_DESKTOP_DIR)
i18n.init(app)
tray.init(app, toggleWindow)
lastFiles.init(desktop)
log.trace('Setting up tray WM...')
trayWindow = new TrayWM(app, desktop)
log.trace('Setting up help WM...')
helpWindow = new HelpWM(app, desktop)
log.trace('Setting up onboarding WM...')
onboardingWindow = new OnboardingWM(app, desktop)
onboardingWindow.onOnboardingDone(() => {
onboardingWindow.hide()
trayWindow.show().then(() => startSync())
})
log.trace('Setting up updater WM...')
updaterWindow = new UpdaterWM(app, desktop)
updaterWindow.onUpToDate(() => {
updaterWindow.hide()
showWindowStartApp()
})
updaterWindow.checkForUpdates()

// Os X wants all application to have a menu
Menu.setApplicationMenu(buildAppMenu(app))
// Os X wants all application to have a menu
Menu.setApplicationMenu(buildAppMenu(app))

// On OS X it's common to re-create a window in the app when the
// dock icon is clicked and there are no other windows open.
app.on('activate', showWindow)
// On OS X it's common to re-create a window in the app when the
// dock icon is clicked and there are no other windows open.
app.on('activate', showWindow)
})
})

// Don't quit the app when all windows are closed, keep the tray icon
Expand Down
5 changes: 4 additions & 1 deletion gui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,12 +61,15 @@
"auto-launch-patched": "5.0.2",
"babel-polyfill": "^6.23.0",
"cozy-desktop": "file:../cli/",
"electron-fetch": "^1.1.0",
"electron-main-notification": "1.0.1",
"electron-positioner": "^3.0.0",
"electron-proxy-agent": "^1.0.2",
"electron-updater": "1.15.0",
"lnk": "^1.1.0",
"lodash.debounce": "4.0.8",
"node-uuid": "^1.4.8"
"node-uuid": "^1.4.8",
"yargs": "^10.0.3"
},
"devDependencies": {
"chokidar-cli": "^1.2.0",
Expand Down
8 changes: 7 additions & 1 deletion gui/src/main/onboarding.window.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,13 @@ module.exports = class OnboardingWM extends WindowManager {
},
(err) => {
log.error(err)
event.sender.send('registration-error', translate('Address No cozy instance at this address!'))
if (err.code.match(/PROXY/)) {
session.defaultSession.resolveProxy(cozyUrl, (p) => {
event.sender.send('registration-error', translate('Address Proxy issue') + p)
})
} else {
event.sender.send('registration-error', translate('Address No cozy instance at this address!'))
}
}
)
}
Expand Down
107 changes: 107 additions & 0 deletions gui/src/main/proxy.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
/* eslint standard/no-callback-literal: 0 */

const ElectronProxyAgent = require('electron-proxy-agent')
const url = require('url')
const http = require('http')
const https = require('https')

const log = require('cozy-desktop').default.logger({
component: 'GUI:proxy'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes me think that CLI won't have proxy support. Not a priority though. We'll just need to make it clear in case we bring it back.

})

const config = require('yargs')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not reuse commander since it was already used in CLI? Is it obsolete in some way? But same a CLI-related comment above: not a priority (just out of curiosity).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was in the CLI package and I am scared of the dark voodoo between cli & gui if they use the same package 😃

.env('COZY_DRIVE')
.conflicts('proxy-script', 'proxy-rules')
.describe('proxy-script', 'The URL associated with the PAC file.')
.describe('proxy-rules', 'Rules indicating which proxies to use.')
.describe('proxy-bypassrules', 'Rules indicating which URLs should bypass the proxy settings. ' +
'See https://github.com/electron/electron/blob/master/docs/api/session.md#sessetproxyconfig-callback')
.default('proxy-ntlm-domains', '*')
.describe('proxy-ntlm-domains', 'A comma-separated list of servers for which integrated authentication is enabled. ' +
'Dynamically sets whether to always send credentials for HTTP NTLM or Negotiate authentication.')
.describe('login-by-realm', 'comma-separated list of realm:user:password')
.help('help')
.parse()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe move to main later?


log.warn({config}, 'argv')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

log.debug?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(not repeating, there are a few others below)


const printCertificate = (certif) => `Certificate(${certif.issuerName} ${certif.subjectName})`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn't really print anything.. format*? stringify?


module.exports = (app, session, doneSetup) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we really export anything?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yep, this cant be initialized until app.on('ready')

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Of course, we are exporting a function... Sorry, my bad :)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(My brain was messing up with requirejs-like syntax...)

const loginByRealm = {}
if (config['login-by-realm']) {
config['login-by-realm'].split(',').forEach((lbr) => {
const [realm, username, password] = lbr.split(':')
loginByRealm[realm] = [username, password]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Make sure auth doesn't break when password contains a colon?

const [realm, username, passwordParts] = lbr.split(':')
loginByRealm[realm] = [username, passwordParts.join(':')]

})
}

if (config['proxy-ntlm-domains']) {
session.defaultSession.allowNTLMCredentialsForDomains(config['proxy-ntlm-domains'])
}

session.defaultSession.setCertificateVerifyProc((request, callback) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this for debug only?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yep. Hopefully will give us more data for the bug we were speaking about tonight.

const {hostname, certificate, verificationResult, errorCode} = request
if (verificationResult < 0) {
log.warn({hostname, certificate: printCertificate(certificate), verificationResult, errorCode}, 'Certificate Verification Error')
} else {
log.debug({hostname, certificate: printCertificate(certificate), verificationResult, errorCode}, 'Certificate Validated')
}
callback(-3) // use chrome validation
})

app.on('select-client-certificate', (event, webContents, url, list, callback) => {
log.debug({url}, 'select-client-certificate')
callback()
})

app.on('certificate-error', (event, webContents, url, error, certificate, callback) => {
log.warn({url, error, certificate: printCertificate(certificate)}, 'App Certificate Error')
callback(false)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why false?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need to callback(false) to not accept any certificate.

})

app.on('login', (event, webContents, request, authInfo, callback) => {
log.warn({request: request.method + ' ' + request.url, authInfo}, 'Login event')
const auth = loginByRealm[authInfo.realm]
if (auth) {
event.preventDefault()
callback(...auth)
} else {
callback()
}
})

const electronFetch = require('electron-fetch')
global.fetch = (url, opts = {}) => {
opts.session = session.defaultSession
return electronFetch(url, opts)
}
http.Agent.globalAgent = http.globalAgent = https.globalAgent = new ElectronProxyAgent(session.defaultSession)
const _httpRequest = http.request
http.request = function (options, cb) {
log.warn(options, 'USING RAW HTTP REQUEST')
options.agent = options.agent || http.globalAgent
options.headers = options.headers || {}
options.headers.host = options.hostname
return _httpRequest.call(http, options, cb)
}
const _httpsRequest = https.request
https.request = function (options, cb) {
log.warn(options, 'USING RAW HTTPS REQUEST')
if (typeof options === 'string') {
options = url.parse(options)
} else {
options = Object.assign({}, options)
}
options.agent = options.agent || https.globalAgent
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why doesn't https.request need the same options.headers.host patching as in http.request?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, thanks for the explanation!

return _httpsRequest.call(https, options, cb)
}

if (config['proxy-script'] || config['proxy-rules']) {
session.defaultSession.setProxy({
pacScript: config['proxy-script'],
proxyRules: config['proxy-rules'],
proxyBypassRules: config['proxy-bypassrules']
}, doneSetup)
} else doneSetup()
}
Loading