|
4 | 4 | *--------------------------------------------------------------------------------------------*/
|
5 | 5 |
|
6 | 6 | import type { IPayloadData, IXHROverride } from '@microsoft/1ds-post-js';
|
7 |
| -import * as https from 'https'; |
| 7 | +import { streamToBuffer } from 'vs/base/common/buffer'; |
| 8 | +import { CancellationToken } from 'vs/base/common/cancellation'; |
| 9 | +import { IRequestOptions } from 'vs/base/parts/request/common/request'; |
| 10 | +import { IRequestService } from 'vs/platform/request/common/request'; |
8 | 11 | import { AbstractOneDataSystemAppender, IAppInsightsCore } from 'vs/platform/telemetry/common/1dsAppender';
|
9 | 12 |
|
| 13 | +/** |
| 14 | + * Completes a request to submit telemetry to the server utilizing the request service |
| 15 | + * @param options The options which will be used to make the request |
| 16 | + * @param requestService The request service |
| 17 | + * @returns An object containing the headers, statusCode, and responseData |
| 18 | + */ |
| 19 | +async function makeTelemetryRequest(options: IRequestOptions, requestService: IRequestService) { |
| 20 | + const response = await requestService.request(options, CancellationToken.None); |
| 21 | + const responseData = (await streamToBuffer(response.stream)).toString(); |
| 22 | + const statusCode = response.res.statusCode ?? 200; |
| 23 | + const headers = response.res.headers as Record<string, any>; |
| 24 | + return { |
| 25 | + headers, |
| 26 | + statusCode, |
| 27 | + responseData |
| 28 | + }; |
| 29 | +} |
| 30 | + |
| 31 | +/** |
| 32 | + * Complete a request to submit telemetry to the server utilizing the https module. Only used when the request service is not available |
| 33 | + * @param options The options which will be used to make the request |
| 34 | + * @param httpsModule The https node module |
| 35 | + * @returns An object containing the headers, statusCode, and responseData |
| 36 | + */ |
| 37 | +function makeLegacyTelemetryRequest(options: IRequestOptions, httpsModule: typeof import('https')) { |
| 38 | + const httpsOptions = { |
| 39 | + method: options.type, |
| 40 | + headers: options.headers |
| 41 | + }; |
| 42 | + const req = httpsModule.request(options.url ?? '', httpsOptions, res => { |
| 43 | + res.on('data', function (responseData) { |
| 44 | + return { |
| 45 | + headers: res.headers as Record<string, any>, |
| 46 | + statusCode: res.statusCode ?? 200, |
| 47 | + responseData: responseData.toString() |
| 48 | + }; |
| 49 | + }); |
| 50 | + // On response with error send status of 0 and a blank response to oncomplete so we can retry events |
| 51 | + res.on('error', function (err) { |
| 52 | + throw err; |
| 53 | + }); |
| 54 | + }); |
| 55 | + req.write(options.data); |
| 56 | + req.end(); |
| 57 | + return; |
| 58 | +} |
| 59 | + |
10 | 60 |
|
11 | 61 | export class OneDataSystemAppender extends AbstractOneDataSystemAppender {
|
12 | 62 |
|
13 | 63 | constructor(
|
| 64 | + requestService: IRequestService | undefined, |
14 | 65 | isInternalTelemetry: boolean,
|
15 | 66 | eventPrefix: string,
|
16 | 67 | defaultData: { [key: string]: any } | null,
|
17 | 68 | iKeyOrClientFactory: string | (() => IAppInsightsCore), // allow factory function for testing
|
18 | 69 | ) {
|
| 70 | + let httpsModule: typeof import('https') | undefined; |
| 71 | + if (!requestService) { |
| 72 | + httpsModule = require('https'); |
| 73 | + } |
19 | 74 | // Override the way events get sent since node doesn't have XHTMLRequest
|
20 | 75 | const customHttpXHROverride: IXHROverride = {
|
21 | 76 | sendPOST: (payload: IPayloadData, oncomplete) => {
|
22 |
| - const options = { |
23 |
| - method: 'POST', |
| 77 | + |
| 78 | + const telemetryRequestData = typeof payload.data === 'string' ? payload.data : new TextDecoder().decode(payload.data); |
| 79 | + const requestOptions: IRequestOptions = { |
| 80 | + type: 'POST', |
24 | 81 | headers: {
|
25 | 82 | ...payload.headers,
|
26 | 83 | 'Content-Type': 'application/json',
|
27 |
| - 'Content-Length': Buffer.byteLength(payload.data) |
28 |
| - } |
| 84 | + 'Content-Length': Buffer.byteLength(payload.data).toString() |
| 85 | + }, |
| 86 | + url: payload.urlString, |
| 87 | + data: telemetryRequestData |
29 | 88 | };
|
| 89 | + |
30 | 90 | try {
|
31 |
| - const req = https.request(payload.urlString, options, res => { |
32 |
| - res.on('data', function (responseData) { |
33 |
| - oncomplete(res.statusCode ?? 200, res.headers as Record<string, any>, responseData.toString()); |
| 91 | + if (requestService) { |
| 92 | + makeTelemetryRequest(requestOptions, requestService).then(({ statusCode, headers, responseData }) => { |
| 93 | + oncomplete(statusCode, headers, responseData); |
34 | 94 | });
|
35 |
| - // On response with error send status of 0 and a blank response to oncomplete so we can retry events |
36 |
| - res.on('error', function (err) { |
37 |
| - oncomplete(0, {}); |
38 |
| - }); |
39 |
| - }); |
40 |
| - req.write(payload.data); |
41 |
| - req.end(); |
| 95 | + } else { |
| 96 | + if (!httpsModule) { |
| 97 | + throw new Error('https module is undefined'); |
| 98 | + } |
| 99 | + makeLegacyTelemetryRequest(requestOptions, httpsModule); |
| 100 | + } |
42 | 101 | } catch {
|
43 | 102 | // If it errors out, send status of 0 and a blank response to oncomplete so we can retry events
|
44 | 103 | oncomplete(0, {});
|
|
0 commit comments