Skip to content

Commit

Permalink
Merge pull request #1129 from Microsoft/jwilaby/#1057-proxy-support
Browse files Browse the repository at this point in the history
Fixes #1057 - Proxy support
  • Loading branch information
Justin Wilaby committed Nov 28, 2018
2 parents 87eaf4a + 3d54b5b commit 6fc6eb8
Show file tree
Hide file tree
Showing 27 changed files with 235 additions and 26,537 deletions.
272 changes: 144 additions & 128 deletions package-lock.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion packages/app/client/package.json
Expand Up @@ -66,7 +66,7 @@
"jsdom-global": "^3.0.2",
"mini-css-extract-plugin": "^0.4.0",
"node-sass": "4.9.0",
"npm-run-all": "^4.1.3",
"npm-run-all": "^4.1.5",
"react-app-rewired": "^1.4.1",
"react-dev-utils": "^5.0.0",
"react-hot-loader": "^4.1.3",
Expand Down
10,458 changes: 0 additions & 10,458 deletions packages/app/main/package-lock.json

This file was deleted.

2 changes: 1 addition & 1 deletion packages/app/main/package.json
Expand Up @@ -88,7 +88,7 @@
"jest": "^23.0.0",
"license-list": "^0.1.3",
"ncp": "2.0.0",
"npm-run-all": "^4.1.3",
"npm-run-all": "^4.1.5",
"through2": "^2.0.3",
"tslint": "^5.10.0",
"typescript": "3.1.1",
Expand Down
1 change: 1 addition & 0 deletions packages/app/main/src/commands/electronCommands.spec.ts
@@ -1,3 +1,4 @@
import '../fetchProxy';
import { SharedConstants } from '@bfemulator/app-shared';
import { CommandRegistryImpl } from '@bfemulator/sdk-shared';
import * as Electron from 'electron';
Expand Down
1 change: 1 addition & 0 deletions packages/app/main/src/commands/emulatorCommands.spec.ts
@@ -1,3 +1,4 @@
import '../fetchProxy';
import { combineReducers, createStore } from 'redux';
import * as BotActions from '../botData/actions/botActions';
import { bot } from '../botData/reducers/bot';
Expand Down
35 changes: 35 additions & 0 deletions packages/app/main/src/fetchProxy.spec.ts
@@ -0,0 +1,35 @@
let mockFetchArgs: {input: RequestInfo, init?: any};
jest.mock('node-fetch', () => {
return async(input, init) => {
mockFetchArgs = { input, init };
return {
ok: true,
json: async () => ({}),
text: async () => '{}',
};
};
});

import './fetchProxy';

describe('fetch proxy support', () => {
it('should add the https-proxy-agent when the HTTPS_PROXY env var exists', async () => {
process.env.HTTPS_PROXY = 'https://proxy';
await fetch('https://some.api.com');
expect(mockFetchArgs.init.agent).not.toBeNull();
});

it('should not add the https-proxy-agent when the HTTPS_PROXY env var exists but the NO_PROXY omits the url', () => {
process.env.HTTPS_PROXY = 'https://proxy';
process.env.NO_PROXY = 'localhost';
fetch('https://localhost').catch();
expect(mockFetchArgs.init).toBeUndefined();
});

it('should not add the http-proxy-agent when the HTTPS_PROXY is omitted', () => {
delete process.env.HTTPS_PROXY;
delete process.env.NO_PROXY;
fetch('https://localhost').catch();
expect(mockFetchArgs.init).toBeUndefined();
});
});
24 changes: 24 additions & 0 deletions packages/app/main/src/fetchProxy.ts
@@ -0,0 +1,24 @@
// ------------------------------------------------------------------
// Proxy support
const nodeFetch = require('node-fetch');

declare function fetch(input: RequestInfo, init?: RequestInit): Promise<Response>;

(global as any).fetch = function (...args: any[]) {
const [urlOrRequest, requestInit = {}] = args;
// No Proxy
const url: string = typeof urlOrRequest === 'string' ? urlOrRequest : urlOrRequest.url;
if (!process.env.HTTPS_PROXY || (process.env.NO_PROXY && url.includes(process.env.NO_PROXY))) {
return nodeFetch(...args);
}
// URL is first param attach the proxy
// to the RequestInit
const HttpsProxyAgent = require('https-proxy-agent');
const agent = new HttpsProxyAgent(process.env.HTTPS_PROXY);
if (typeof urlOrRequest === 'string') {
requestInit.agent = agent;
} else {
urlOrRequest.agent = agent;
}
return nodeFetch(urlOrRequest, requestInit);
};
3 changes: 1 addition & 2 deletions packages/app/main/src/main.ts
Expand Up @@ -30,7 +30,7 @@
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//

import './fetchProxy';
import * as Electron from 'electron';
import { app, Menu, systemPreferences } from 'electron';

Expand Down Expand Up @@ -64,7 +64,6 @@ export let mainWindow: Window;
export let windowManager: WindowManager;

const store = getStore();

// -----------------------------------------------------------------------------

(process as NodeJS.EventEmitter).on('uncaughtException', (error: Error) => {
Expand Down
2 changes: 1 addition & 1 deletion packages/app/main/src/protocolHandler.spec.ts
Expand Up @@ -12,7 +12,7 @@ jest.mock('./globals', () => ({
getGlobal: () => ({}),
setGlobal: () => null
}));

import './fetchProxy';
import {
Protocol,
ProtocolHandler,
Expand Down
1 change: 0 additions & 1 deletion packages/app/main/src/restServer.ts
Expand Up @@ -34,7 +34,6 @@
import { BotEmulator, Conversation } from '@bfemulator/emulator-core';
import CORS from 'restify-cors-middleware';
import * as Restify from 'restify';
import fetch from 'node-fetch';

import { mainWindow } from './main';
import { emulator } from './emulator';
Expand Down
@@ -1,3 +1,4 @@
import '../fetchProxy';
import { AzureAuthWorkflowService } from './azureAuthWorkflowService';
import { BrowserWindow } from 'electron';

Expand Down
1 change: 0 additions & 1 deletion packages/app/main/src/services/azureAuthWorkflowService.ts
Expand Up @@ -32,7 +32,6 @@
//

import { BrowserWindow } from 'electron';
import fetch from 'node-fetch';
import uuidv4 from 'uuid/v4';
import * as jwt from 'jsonwebtoken';

Expand Down
10 changes: 1 addition & 9 deletions packages/app/main/src/services/conversationService.spec.ts
@@ -1,5 +1,5 @@
import '../fetchProxy';
let mockFetchArgs: MockFetch;
import * as nodeFetch from 'node-fetch';

jest.mock('node-fetch', () => {
const fetch = (url, opts) => {
Expand Down Expand Up @@ -37,7 +37,6 @@ describe('The ConversationService should call "fetch" with the expected paramete
expect(url).toBe('http://localhost/emulator/abcdef/users');

const { body, headers, method } = opts;
expect(headers instanceof nodeFetch.Headers).toBeTruthy();
expect(headers === headers).toBeTruthy();
expect(method).toBe('POST');
const members = JSON.parse(body);
Expand All @@ -52,7 +51,6 @@ describe('The ConversationService should call "fetch" with the expected paramete
expect(url).toBe('http://localhost/emulator/abcdef/users');

const { body, headers, method } = opts;
expect(headers instanceof nodeFetch.Headers).toBeTruthy();
expect(headers === headers).toBeTruthy();
expect(method).toBe('DELETE');
const users = JSON.parse(body);
Expand All @@ -66,7 +64,6 @@ describe('The ConversationService should call "fetch" with the expected paramete
expect(url).toBe('http://localhost/emulator/abcdef/users');

const { body, headers, method } = opts;
expect(headers instanceof nodeFetch.Headers).toBeTruthy();
expect(headers === headers).toBeTruthy();
expect(method).toBe('DELETE');
expect(body).toBeFalsy();
Expand All @@ -79,7 +76,6 @@ describe('The ConversationService should call "fetch" with the expected paramete
expect(url).toBe('http://localhost/emulator/abcdef/contacts');

const { body, headers, method } = opts;
expect(headers instanceof nodeFetch.Headers).toBeTruthy();
expect(headers === headers).toBeTruthy();
expect(method).toBe('POST');
expect(body).toBeFalsy();
Expand All @@ -92,7 +88,6 @@ describe('The ConversationService should call "fetch" with the expected paramete
expect(url).toBe('http://localhost/emulator/abcdef/contacts');

const { body, headers, method } = opts;
expect(headers instanceof nodeFetch.Headers).toBeTruthy();
expect(headers === headers).toBeTruthy();
expect(method).toBe('DELETE');
expect(body).toBeFalsy();
Expand All @@ -105,7 +100,6 @@ describe('The ConversationService should call "fetch" with the expected paramete
expect(url).toBe('http://localhost/emulator/abcdef/typing');

const { body, headers, method } = opts;
expect(headers instanceof nodeFetch.Headers).toBeTruthy();
expect(headers === headers).toBeTruthy();
expect(method).toBe('POST');
expect(body).toBeFalsy();
Expand All @@ -118,7 +112,6 @@ describe('The ConversationService should call "fetch" with the expected paramete
expect(url).toBe('http://localhost/emulator/abcdef/ping');

const { body, headers, method } = opts;
expect(headers instanceof nodeFetch.Headers).toBeTruthy();
expect(headers === headers).toBeTruthy();
expect(method).toBe('POST');
expect(body).toBeFalsy();
Expand All @@ -131,7 +124,6 @@ describe('The ConversationService should call "fetch" with the expected paramete
expect(url).toBe('http://localhost/emulator/abcdef/userdata');

const { body, headers, method } = opts;
expect(headers instanceof nodeFetch.Headers).toBeTruthy();
expect(headers === headers).toBeTruthy();
expect(method).toBe('DELETE');
expect(body).toBeFalsy();
Expand Down
6 changes: 2 additions & 4 deletions packages/app/main/src/services/conversationService.ts
@@ -1,8 +1,6 @@
import fetch, { Headers } from 'node-fetch';

export const headers = new Headers({
export const headers = {
'Content-Accept': 'application/json'
});
};

export class ConversationService {

Expand Down
19 changes: 6 additions & 13 deletions packages/app/main/src/services/luisApiService.spec.ts
@@ -1,5 +1,7 @@
import '../fetchProxy';

const mockArmToken = 'bm90aGluZw.eyJ1cG4iOiJnbGFzZ293QHNjb3RsYW5kLmNvbSJ9.7gjdshgfdsk98458205jfds9843fjds';
const mockReq: RequestInit = { headers: { Authorization: `Bearer ${mockArmToken}` } };
const mockReq: RequestInit = { headers: { Authorization: `Bearer ${ mockArmToken }` } };
const mockResponses = [
'hfdjg459846gjfdhgfdshjg',
{ 'error': { 'statusCode': 401, 'message': 'Oh Noes!' } },
Expand Down Expand Up @@ -95,26 +97,17 @@ describe('The LuisApiService class', () => {

expect(mockArgsPassedToFetch[1]).toEqual({
'url': 'https://australiaeast.api.cognitive.microsoft.com/luis/api/v2.0/apps/',
'headers': {
'headers': {},
'method': 'get'
}
headers: jasmine.any(Object)
});

expect(mockArgsPassedToFetch[2]).toEqual({
'url': 'https://westeurope.api.cognitive.microsoft.com/luis/api/v2.0/apps/',
'headers': {
'headers': {},
'method': 'get'
}
headers: jasmine.any(Object)
});

expect(mockArgsPassedToFetch[3]).toEqual({
'url': 'https://westus.api.cognitive.microsoft.com/luis/api/v2.0/apps/',
'headers': {
'headers': {},
'method': 'get'
}
headers: jasmine.any(Object)
});
});
});
5 changes: 2 additions & 3 deletions packages/app/main/src/services/luisApiService.ts
Expand Up @@ -33,7 +33,6 @@

import { LuisModel, LuisRegion, ServiceCodes } from '@bfemulator/app-shared';
import { ILuisService, ServiceTypes } from 'botframework-config/lib/schema';
import fetch, { Headers, Response } from 'node-fetch';

export class LuisApi {
public static* getServices(armToken: string): IterableIterator<any> {
Expand Down Expand Up @@ -87,10 +86,10 @@ export class LuisApi {

public static async getApplicationsForRegion(region: LuisRegion, key: string): Promise<LuisModel[] | { error: any }> {
const url = `https://${region}.api.cognitive.microsoft.com/luis/api/v2.0/apps/`;
const headers = new Headers({
const headers = {
'Content-Accept': 'application/json',
'Ocp-Apim-Subscription-Key': key
});
};

const response: Response = await fetch(url, { headers, method: 'get' } as any);
if (!response.ok) {
Expand Down
1 change: 1 addition & 0 deletions packages/app/main/src/services/qnaApiService.spec.ts
@@ -1,3 +1,4 @@
import '../fetchProxy';
const mockArmToken = 'bm90aGluZw.eyJ1cG4iOiJnbGFzZ293QHNjb3RsYW5kLmNvbSJ9.7gjdshgfdsk98458205jfds9843fjds';
const mockReq: RequestInit = { headers: { Authorization: `Bearer ${mockArmToken}` } };
const mockResponses = [
Expand Down
1 change: 0 additions & 1 deletion packages/app/main/src/services/qnaApiService.ts
@@ -1,5 +1,4 @@
import { QnaMakerService } from 'botframework-config/lib/models';
import fetch, { Headers, Response } from 'node-fetch';
import { ServiceCodes } from '@bfemulator/app-shared';

export interface Subscription {
Expand Down
1 change: 0 additions & 1 deletion packages/emulator/core/package.json
Expand Up @@ -57,7 +57,6 @@
"http-status-codes": "^1.3.0",
"jsonwebtoken": "^8.3.0",
"moment": "^2.22.1",
"node-fetch": "2.2.0",
"npmlog": "^4.1.2",
"on-error-resume-next": "^1.0.0",
"rsa-pem-from-mod-exp": "^0.8.4",
Expand Down
15 changes: 7 additions & 8 deletions packages/emulator/core/src/botEmulator.ts
Expand Up @@ -32,7 +32,6 @@
//

import * as Restify from 'restify';
import fetch from 'node-fetch';
import Attachments from './facility/attachments';
import BotState from './facility/botState';
import ConsoleLogService from './facility/consoleLogService';
Expand Down Expand Up @@ -64,13 +63,13 @@ export interface ServiceUrlProvider {
}

export interface Facilities {
attachments: Attachments,
botState: BotState,
conversations: ConversationSet,
endpoints: EndpointSet,
logger: Logger,
users: Users,
locale?: string
attachments: Attachments;
botState: BotState;
conversations: ConversationSet;
endpoints: EndpointSet;
logger: Logger;
users: Users;
locale?: string;
}

export default class BotEmulator {
Expand Down
5 changes: 2 additions & 3 deletions packages/emulator/core/src/utils/oauthLinkEncoder.ts
Expand Up @@ -36,7 +36,6 @@ import GenericActivity from '../types/activity/generic';
import AttachmentContentTypes from '../types/attachment/contentTypes';
import OAuthCard from '../types/card/oAuth';
import uniqueId from '../utils/uniqueId';
import fetch, { Headers, Response } from 'node-fetch';
import Attachment from '../types/attachment';
import BotEmulator from '../botEmulator';
import { StringProvider } from './stringProvider';
Expand Down Expand Up @@ -112,9 +111,9 @@ export default class OAuthLinkEncoder {

let serializedState = JSON.stringify(tokenExchangeState);
let state = Buffer.from(serializedState).toString('base64');
const headers = new Headers({
const headers = {
'Authorization': this.authorizationHeader
});
};

const url = 'https://api.botframework.com/api/botsignin/GetSignInUrl?state=' +
state + '&emulatorUrl=' + this.emulatorUrl + '&code_challenge=' + codeChallenge;
Expand Down
3 changes: 2 additions & 1 deletion packages/emulator/core/tsconfig.json
Expand Up @@ -5,7 +5,8 @@
// Emit declaration only when we are building with Babel
// "emitDeclarationOnly": true,
"lib": [
"es2016"
"es2016",
"dom"
],
"module": "commonjs",
"moduleResolution": "node",
Expand Down

0 comments on commit 6fc6eb8

Please sign in to comment.