From 4b87d2c05852f78d46a030d4fa8dd2e41bc682ed Mon Sep 17 00:00:00 2001 From: Minja Malesevic Date: Wed, 25 Jun 2025 15:58:04 +0200 Subject: [PATCH 1/2] Add useNetModule to newHttpRequest --- .../rendererIntegrationTests/testAppMain.js | 1 + src/electronPlatform.js | 3 +- src/httpRequest.js | 38 ++++++++++++++----- tsconfig.json | 5 ++- typings.d.ts | 1 + 5 files changed, 36 insertions(+), 12 deletions(-) diff --git a/src/__tests__/rendererIntegrationTests/testAppMain.js b/src/__tests__/rendererIntegrationTests/testAppMain.js index 79998cc..d356556 100644 --- a/src/__tests__/rendererIntegrationTests/testAppMain.js +++ b/src/__tests__/rendererIntegrationTests/testAppMain.js @@ -10,6 +10,7 @@ const ldOptions = { logger: ldElectron.createConsoleLogger('debug'), streaming: args.streaming, useReport: true, + useNetModule: args.useNetModule === true, }; const testConfig = { diff --git a/src/electronPlatform.js b/src/electronPlatform.js index d7aa387..f60c513 100644 --- a/src/electronPlatform.js +++ b/src/electronPlatform.js @@ -9,7 +9,8 @@ function makeElectronPlatform(options) { const ret = {}; - ret.httpRequest = (method, url, headers, body) => newHttpRequest(method, url, headers, body, tlsParams); + ret.httpRequest = (method, url, headers, body) => + newHttpRequest(method, url, headers, body, tlsParams, options && options.useNetModule); ret.httpAllowsPost = () => true; diff --git a/src/httpRequest.js b/src/httpRequest.js index 1eef92b..eb9b22b 100644 --- a/src/httpRequest.js +++ b/src/httpRequest.js @@ -5,19 +5,20 @@ const http = require('http'); const https = require('https'); const url = require('url'); -function newHttpRequest(method, requestUrl, headers, body, tlsParams) { +let electronNet; +try { + electronNet = require('electron').net; +} catch (_) { + // Not in Electron or outside main process +} + +function newHttpRequest(method, requestUrl, headers, body, tlsParams, useNetModule) { const urlParams = url.parse(requestUrl); const isHttps = urlParams.protocol === 'https:'; - const requestParams = Object.assign({}, isHttps ? tlsParams : {}, urlParams, { - method: method, - headers: headers, - body: body, - }); - let request; const p = new Promise((resolve, reject) => { - request = (isHttps ? https : http).request(requestParams, res => { + const onResponse = res => { let resBody = ''; res.on('data', chunk => { resBody += chunk; @@ -29,12 +30,31 @@ function newHttpRequest(method, requestUrl, headers, body, tlsParams) { body: resBody, }); }); - }); + }; + + if (useNetModule && electronNet) { + request = electronNet.request({ method, url: requestUrl }); + + for (const [key, value] of Object.entries(headers)) { + request.setHeader(key, value); + } + + request.on('response', onResponse); + } else { + const requestParams = Object.assign({}, isHttps ? tlsParams : {}, urlParams, { + method: method, + headers: headers, + body: body, + }); + + request = (isHttps ? https : http).request(requestParams, onResponse); + } request.on('error', reject); if (body) { request.write(body); } + request.end(); }); diff --git a/tsconfig.json b/tsconfig.json index 9b535e9..6740cf5 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -5,10 +5,11 @@ "lib": [ "es6", "dom" - ] + ], + "types": [] }, "files": [ "typings.d.ts", "test-types.ts" ] -} \ No newline at end of file +} diff --git a/typings.d.ts b/typings.d.ts index ed1a5fc..f8932c1 100644 --- a/typings.d.ts +++ b/typings.d.ts @@ -60,6 +60,7 @@ declare module 'launchdarkly-electron-client-sdk' { * Initialization options for the LaunchDarkly Electron SDK. */ export interface LDOptions extends LDOptionsBase { + useNetModule?: boolean; } /** From 1abc76786d49a1c14ed060ef2c85676cef74f596 Mon Sep 17 00:00:00 2001 From: Minja Malesevic Date: Wed, 25 Jun 2025 16:05:44 +0200 Subject: [PATCH 2/2] Added init test --- src/__tests__/LDClient-useNetModule-test.js | 81 +++++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 src/__tests__/LDClient-useNetModule-test.js diff --git a/src/__tests__/LDClient-useNetModule-test.js b/src/__tests__/LDClient-useNetModule-test.js new file mode 100644 index 0000000..53be97c --- /dev/null +++ b/src/__tests__/LDClient-useNetModule-test.js @@ -0,0 +1,81 @@ +import * as LDClient from '../index'; +import * as packageJson from '../../package.json'; + +import { TestHttpHandlers, TestHttpServer, withCloseable } from 'launchdarkly-js-test-helpers'; + +describe('LDClient', () => { + const envName = 'UNKNOWN_ENVIRONMENT_ID'; + const user = { key: 'user' }; + + it('should exist', () => { + expect(LDClient).toBeDefined(); + }); + + it('should report correct version', () => { + expect(LDClient.version).toEqual(packageJson.version); + }); + + describe('initialization', () => { + it('should initialize successfully', async () => { + await withCloseable(TestHttpServer.start, async server => { + const data = { flag: { value: 3 } }; + server.byDefault(TestHttpHandlers.respondJson(data)); + + const client = LDClient.initializeInMain(envName, user, { + baseUrl: server.url, + sendEvents: false, + useNetModule: true, + }); + await withCloseable(client, async () => { + await client.waitForInitialization(); + + expect(client.variation('flag')).toEqual(3); + }); + }); + }); + + it('sends correct User-Agent in request', async () => { + await withCloseable(TestHttpServer.start, async server => { + const data = { flag: { value: 3 } }; + server.byDefault(TestHttpHandlers.respondJson(data)); + + const client = LDClient.initializeInMain(envName, user, { + baseUrl: server.url, + sendEvents: false, + useNetModule: true, + }); + await withCloseable(client, async () => { + await client.waitForInitialization(); + + expect(server.requests.length()).toEqual(1); + const req = await server.nextRequest(); + expect(req.headers['x-launchdarkly-user-agent']).toMatch(/^ElectronClient\/1\./); + }); + }); + }); + }); + + describe('track()', () => { + it('should not warn when tracking an arbitrary custom event', async () => { + const logger = { + debug: jest.fn(), + info: jest.fn(), + warn: jest.fn(), + error: jest.fn(), + }; + const client = LDClient.initializeInMain(envName, user, { + bootstrap: {}, + sendEvents: false, + logger: logger, + useNetModule: true, + }); + await withCloseable(client, async () => { + await client.waitForInitialization(); + + client.track('whatever'); + expect(logger.warn).not.toHaveBeenCalled(); + expect(logger.error).not.toHaveBeenCalled(); + }); + }); + }); +});