Skip to content

Commit

Permalink
Merge pull request #81 from FlowFuse/271-proxy-support
Browse files Browse the repository at this point in the history
Proxy support
  • Loading branch information
Steve-Mcl authored Jun 14, 2024
2 parents 47b6c79 + 73206a7 commit 8fb4a4d
Show file tree
Hide file tree
Showing 7 changed files with 8,376 additions and 1,186 deletions.
4 changes: 3 additions & 1 deletion .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,13 @@
"extends": [
"standard"
],
"plugins": [ "eslint-plugin-html", "no-only-tests" ],
"parserOptions": {
"ecmaVersion": 12
},
"rules": {
"indent": ["error", 4],
"object-shorthand": ["error"]
"object-shorthand": ["error"],
"no-only-tests/no-only-tests": "error"
}
}
75 changes: 75 additions & 0 deletions lib/utils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
const proxyFromEnv = require('proxy-from-env')

module.exports = {
getWSProxyAgent,
getHTTPProxyAgent
}

/**
* Get a specific proxy agent for a WebSocket connection. This should be applied to the `wsOptions.agent` property
*
* NOTE: This utility function is specifically designed for the MQTT instances where the proxy is set based on the http based EndPoint
* that the instance will use to make a connection. As such, the proxy URL is determined based on the `wsEndPoint` provided in
* conjunction with env vars `http_proxy`, `https_proxy` and `no_proxy`.
*
* More Info:
* `wsOptions.agent` is expected to be an HTTP or HTTPS agent based on the request protocol
* http/ws requests use env var `http_proxy` and the HttpProxyAgent
* https/wss requests use env var `https_proxy` and the HttpsProxyAgent
* REF: https://github.com/TooTallNate/proxy-agents/tree/main/packages/proxy-agent#maps-proxy-protocols-to-httpagent-implementations
*
* @param {String} url - WebSocket url
* @param {import('http').AgentOptions} proxyOptions - proxy options
* @returns {import('https-proxy-agent').HttpsProxyAgent | import('http-proxy-agent').HttpProxyAgent | null}
*/
function getWSProxyAgent (url, proxyOptions) {
if (!url) {
return null
}
const _url = new URL(url)
const isHTTPBased = _url.protocol === 'ws:' || _url.protocol === 'http:'
const isHTTPSBased = _url.protocol === 'wss:' || _url.protocol === 'https:'
if (!isHTTPBased && !isHTTPSBased) {
return null
}

// replace ^ws with http so that getProxyForUrl can return the correct http*_proxy for ws/wss
const proxyUrl = proxyFromEnv.getProxyForUrl(url.replace(/^ws/, 'http'))

if (proxyUrl && isHTTPSBased) {
const HttpsAgent = require('https-proxy-agent').HttpsProxyAgent
return new HttpsAgent(proxyUrl, proxyOptions)
}
if (proxyUrl && isHTTPBased) {
const HttpAgent = require('http-proxy-agent').HttpProxyAgent
return new HttpAgent(proxyUrl, proxyOptions)
}
return null
}

/**
* Get proxy agent for HTTP or HTTPS got instance. This should be applied to the `agent` property of the got instance options
*
* NOTE: This utility function is specifically designed for the GOT instances where the proxy is set based on the `httpEndPoint`
* that the instance will use to make requests. As such, the proxy URL is determined based on the `httpEndPoint` provided
* in conjunction with env vars `http_proxy`, `https_proxy` and `no_proxy`.
* @param {String} url - http or https URL
* @param {import('http').AgentOptions} proxyOptions - proxy options
* @returns {{http: import('http-proxy-agent').HttpProxyAgent | undefined, https: import('https-proxy-agent').HttpsProxyAgent | undefined}}
*/
function getHTTPProxyAgent (url, proxyOptions) {
const agent = {}
if (url) {
const _url = new URL(url)
const proxyUrl = proxyFromEnv.getProxyForUrl(url)
if (proxyUrl && _url.protocol === 'http:') {
const HttpAgent = require('http-proxy-agent').HttpProxyAgent
agent.http = new HttpAgent(proxyUrl, proxyOptions)
}
if (proxyUrl && _url.protocol === 'https:') {
const HttpsAgent = require('https-proxy-agent').HttpsProxyAgent
agent.https = new HttpsAgent(proxyUrl, proxyOptions)
}
}
return agent
}
15 changes: 14 additions & 1 deletion nodes/project-link.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@ module.exports = function (RED) {

// Imports
const crypto = require('crypto')
const got = require('got')
const { default: GOT } = require('got')
const MQTT = require('mqtt')
const urlModule = require('url')
const utils = require('../lib/utils.js')

// Constants
const API_VERSION = 'v1'
Expand Down Expand Up @@ -631,6 +632,14 @@ module.exports = function (RED) {
const parsedURL = urlModule.parse(brokerURL)
const newURL = new URL(brokerURL)
parsedURL.hostname = newURL.hostname

// wsOptions.agent is expected to be an HTTP or HTTPS agent based on the request protocol
if (process.env.all_proxy || process.env.http_proxy || process.env.https_proxy) {
options.wsOptions = {
agent: utils.getWSProxyAgent(brokerURL)
}
}

client = MQTT.connect(parsedURL, options)
clients.push(client) // add to clients array for containment and auto cleanup of multiple clients
on('connect', onConnect)
Expand Down Expand Up @@ -1056,6 +1065,10 @@ module.exports = function (RED) {
}
RED.nodes.registerType('project link call', ProjectLinkCallNode)

const got = GOT.extend({
agent: utils.getHTTPProxyAgent(RED.settings.flowforge.forgeURL, { timeout: 4000 })
})

// Endpoint for querying list of projects in node UI
RED.httpAdmin.get('/nr-project-link/projects', RED.auth.needsPermission('flows.write'), async function (_req, res) {
const url = `${RED.settings.flowforge.forgeURL}/api/${API_VERSION}/teams/${RED.settings.flowforge.teamID}/projects`
Expand Down
Loading

0 comments on commit 8fb4a4d

Please sign in to comment.