Fix offline detection (#1907)
* Fix offline detection 8.8.8.8 blocks our requests. We can use microsoft.com instead, if our app is checking using microsoft, well, if microsoft is down we've learned that the app will have problems anyways , so if that is down it doesnt matter as much. * fix variable name * respond to linter * respond to PR feedback, fix whitespace and ??
This commit is contained in:
Родитель
ddac92e166
Коммит
b1e85ced25
|
@ -68,7 +68,7 @@
|
|||
"run-script-os": "^1.1.6",
|
||||
"semver": "^7.6.2",
|
||||
"shelljs": "^0.8.5",
|
||||
"typescript": "4.4.4",
|
||||
"typescript": "^5.5.4",
|
||||
"vscode-extension-telemetry": "^0.4.3",
|
||||
"vscode-test": "^1.6.1"
|
||||
},
|
||||
|
@ -5553,7 +5553,7 @@
|
|||
"run-script-os": "^1.1.6",
|
||||
"semver": "^7.6.2",
|
||||
"shelljs": "^0.8.5",
|
||||
"typescript": "4.4.4",
|
||||
"typescript": "^5.5.4",
|
||||
"vscode-extension-telemetry": "^0.4.3",
|
||||
"vscode-test": "^1.6.1"
|
||||
}
|
||||
|
|
|
@ -1882,7 +1882,7 @@ util-deprecate@~1.0.1:
|
|||
run-script-os "^1.1.6"
|
||||
semver "^7.6.2"
|
||||
shelljs "^0.8.5"
|
||||
typescript "4.4.4"
|
||||
typescript "^5.5.4"
|
||||
vscode-extension-telemetry "^0.4.3"
|
||||
vscode-test "^1.6.1"
|
||||
optionalDependencies:
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
* The .NET Foundation licenses this file to you under the MIT license.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import * as cp from 'child_process';
|
||||
import * as dns from 'dns';
|
||||
import * as os from 'os';
|
||||
import path = require('path');
|
||||
|
||||
|
@ -14,8 +13,10 @@ import {
|
|||
DotnetAcquisitionScriptOutput,
|
||||
DotnetAcquisitionTimeoutError,
|
||||
DotnetAcquisitionUnexpectedError,
|
||||
OffilneDetectionLogicTriggered,
|
||||
DotnetOfflineFailure,
|
||||
EventBasedError,
|
||||
EventCancellationError,
|
||||
} from '../EventStream/EventStreamEvents';
|
||||
|
||||
import { timeoutConstants } from '../Utils/ErrorHandler'
|
||||
|
@ -31,6 +32,7 @@ import { IDotnetInstallationContext } from './IDotnetInstallationContext';
|
|||
import { IInstallScriptAcquisitionWorker } from './IInstallScriptAcquisitionWorker';
|
||||
import { DotnetInstall } from './DotnetInstall';
|
||||
import { DotnetInstallMode } from './DotnetInstallMode';
|
||||
import { WebRequestWorker } from '../Utils/WebRequestWorker';
|
||||
/* tslint:disable:no-any */
|
||||
/* tslint:disable:only-arrow-functions */
|
||||
|
||||
|
@ -47,28 +49,6 @@ You will need to restart VS Code after these changes. If PowerShell is still not
|
|||
this.fileUtilities = new FileUtilities();
|
||||
}
|
||||
|
||||
private async isOnline(installContext : IDotnetInstallationContext) : Promise<boolean>
|
||||
{
|
||||
const googleDNS = '8.8.8.8';
|
||||
const expectedDNSResolutionTime = Math.max(installContext.timeoutSeconds * 10, 100); // Assumption: DNS resolution should take less than 1/100 of the time it'd take to download .NET.
|
||||
// ... 100 ms is there as a default to prevent the dns resolver from throwing a runtime error if the user sets timeoutSeconds to 0.
|
||||
|
||||
const dnsResolver = new dns.Resolver({ timeout: expectedDNSResolutionTime });
|
||||
await Promise.resolve(dnsResolver.resolve(googleDNS, function(error : any)
|
||||
{
|
||||
if (error)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
})).then(function(dnsRes)
|
||||
{
|
||||
return dnsRes;
|
||||
});
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public async installDotnet(installContext: IDotnetInstallationContext, install : DotnetInstall): Promise<void>
|
||||
{
|
||||
const winOS = os.platform() === 'win32';
|
||||
|
@ -99,7 +79,7 @@ You will need to restart VS Code after these changes. If PowerShell is still not
|
|||
}
|
||||
if (error)
|
||||
{
|
||||
if (!(await this.isOnline(installContext)))
|
||||
if (!(await WebRequestWorker.isOnline(installContext.timeoutSeconds, this.eventStream)))
|
||||
{
|
||||
const offlineError = new EventBasedError('DotnetOfflineFailure', 'No internet connection detected: Cannot install .NET');
|
||||
this.eventStream.post(new DotnetOfflineFailure(offlineError, install));
|
||||
|
|
|
@ -388,6 +388,10 @@ export class UserManualInstallFailure extends SuppressedAcquisitionError {
|
|||
eventName = 'UserManualInstallFailure';
|
||||
}
|
||||
|
||||
export class OffilneDetectionLogicTriggered extends SuppressedAcquisitionError {
|
||||
eventName = 'OffilneDetectionLogicTriggered';
|
||||
}
|
||||
|
||||
export class DotnetInstallationValidationMissed extends SuppressedAcquisitionError {
|
||||
eventName = 'DotnetInstallationValidationMissed';
|
||||
}
|
||||
|
@ -496,11 +500,16 @@ export abstract class DotnetAcquisitionVersionError extends DotnetAcquisitionErr
|
|||
}
|
||||
|
||||
public getProperties(telemetry = false): { [id: string]: string } | undefined {
|
||||
return {ErrorMessage : this.error.message,
|
||||
AcquisitionErrorInstallId : this.install?.installId ?? 'null',
|
||||
...InstallToStrings(this.install!),
|
||||
return this.install ? {ErrorMessage : this.error.message,
|
||||
AcquisitionErrorInstallId : this.install.installId ?? 'null',
|
||||
...InstallToStrings(this.install),
|
||||
ErrorName : this.error.name,
|
||||
StackTrace : this.error.stack ? this.error.stack : ''};
|
||||
StackTrace : this.error.stack ?? ''}
|
||||
:
|
||||
{ErrorMessage : this.error.message,
|
||||
AcquisitionErrorInstallId : 'null',
|
||||
ErrorName : this.error.name,
|
||||
StackTrace : this.error.stack ?? ''};
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -7,13 +7,25 @@ import axiosRetry from 'axios-retry';
|
|||
import { HttpsProxyAgent } from 'https-proxy-agent';
|
||||
import { getProxySettings } from 'get-proxy-settings';
|
||||
import { AxiosCacheInstance, buildMemoryStorage, setupCache } from 'axios-cache-interceptor';
|
||||
import {DiskIsFullError, DotnetDownloadFailure, EventBasedError, SuppressedAcquisitionError, WebRequestError, WebRequestSent } from '../EventStream/EventStreamEvents';
|
||||
import { getInstallFromContext } from './InstallIdUtilities';
|
||||
|
||||
import * as dns from 'dns';
|
||||
import * as fs from 'fs';
|
||||
import { promisify } from 'util';
|
||||
import stream = require('stream');
|
||||
|
||||
import { IAcquisitionWorkerContext } from '../Acquisition/IAcquisitionWorkerContext';
|
||||
import { IEventStream } from '../EventStream/EventStream';
|
||||
import {
|
||||
DiskIsFullError,
|
||||
DotnetDownloadFailure,
|
||||
DotnetOfflineFailure,
|
||||
EventBasedError,
|
||||
EventCancellationError,
|
||||
OffilneDetectionLogicTriggered,
|
||||
SuppressedAcquisitionError,
|
||||
WebRequestError,
|
||||
WebRequestSent
|
||||
} from '../EventStream/EventStreamEvents';
|
||||
import { getInstallFromContext } from './InstallIdUtilities';
|
||||
/* tslint:disable:no-any */
|
||||
|
||||
export class WebRequestWorker
|
||||
|
@ -72,9 +84,15 @@ export class WebRequestWorker
|
|||
throw new EventBasedError('AxiosGetFailedWithInvalidURL', `Request to the url ${this.url} failed, as the URL is invalid.`);
|
||||
}
|
||||
const timeoutCancelTokenHook = new AbortController();
|
||||
const timeout = setTimeout(() =>
|
||||
const timeout = setTimeout(async () =>
|
||||
{
|
||||
timeoutCancelTokenHook.abort();
|
||||
if(!(await WebRequestWorker.isOnline(this.websiteTimeoutMs / 1000, this.context.eventStream)))
|
||||
{
|
||||
const offlineError = new EventBasedError('DotnetOfflineFailure', 'No internet connection detected: Cannot install .NET');
|
||||
this.context.eventStream.post(new DotnetOfflineFailure(offlineError, null));
|
||||
throw offlineError;
|
||||
}
|
||||
const formattedError = new Error(`TIMEOUT: The request to ${this.url} timed out at ${this.websiteTimeoutMs} ms. This only occurs if your internet
|
||||
or the url are experiencing connection difficulties; not if the server is being slow to respond. Check your connection, the url, and or increase the timeout value here: https://github.com/dotnet/vscode-dotnet-runtime/blob/main/Documentation/troubleshooting-runtime.md#install-script-timeouts`);
|
||||
this.context.eventStream.post(new WebRequestError(new EventBasedError('WebRequestError', formattedError.message, formattedError.stack), null));
|
||||
|
@ -95,6 +113,24 @@ export class WebRequestWorker
|
|||
return this.makeWebRequest(true, retriesCount);
|
||||
}
|
||||
|
||||
|
||||
public static async isOnline(timeoutSec : number, eventStream : IEventStream) : Promise<boolean>
|
||||
{
|
||||
const microsoftServer = 'www.microsoft.com';
|
||||
const expectedDNSResolutionTimeMs = Math.max(timeoutSec * 100, 100); // Assumption: DNS resolution should take less than 1/10 of the time it'd take to download .NET.
|
||||
// ... 100 ms is there as a default to prevent the dns resolver from throwing a runtime error if the user sets timeoutSeconds to 0.
|
||||
|
||||
const dnsResolver = new dns.promises.Resolver({ timeout: expectedDNSResolutionTimeMs });
|
||||
const couldConnect = await dnsResolver.resolve(microsoftServer).then(() =>
|
||||
{
|
||||
return true;
|
||||
}).catch((error : any) =>
|
||||
{
|
||||
eventStream.post(new OffilneDetectionLogicTriggered((error as EventCancellationError), `DNS resolution failed at microsoft.com, ${JSON.stringify(error)}.`));
|
||||
return false;
|
||||
});
|
||||
return couldConnect;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param urlInQuestion
|
||||
|
|
Загрузка…
Ссылка в новой задаче