Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Register a non-standard LSP content request for resolving schemas on the client side #359

Merged
merged 2 commits into from
Nov 26, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 23 additions & 4 deletions src/languageservice/services/schemaRequestHandler.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,30 @@
import { URI } from 'vscode-uri';
import { IConnection } from 'vscode-languageserver';
import { IConnection, WorkspaceFolder } from 'vscode-languageserver';
import { xhr, XHRResponse, getErrorStatusDescription } from 'request-light';
import * as fs from 'fs';

import { CustomSchemaContentRequest } from '../../requestTypes';
import { CustomSchemaContentRequest, VSCodeContentRequest } from '../../requestTypes';
import { isRelativePath, relativeToAbsolutePath } from '../utils/paths';

/**
* Handles schema content requests given the schema URI
* @param uri can be a local file, vscode request, http(s) request or a custom request
*/
export const schemaRequestHandler = (connection: IConnection, uri: string): Promise<string> => {
export const schemaRequestHandler = (
connection: IConnection,
uri: string,
workspaceFolders: WorkspaceFolder[],
workspaceRoot: URI,
useVSCodeContentRequest: boolean
): Promise<string> => {
if (!uri) {
return Promise.reject('No schema specified');
}

// If the requested schema URI is a relative file path
// Convert it into a proper absolute path URI
if (isRelativePath(uri)) {
uri = relativeToAbsolutePath(this.workspaceFolders, this.workspaceRoot, uri);
uri = relativeToAbsolutePath(workspaceFolders, workspaceRoot, uri);
}

let scheme = URI.parse(uri).scheme.toLowerCase();
Expand Down Expand Up @@ -47,6 +53,19 @@ export const schemaRequestHandler = (connection: IConnection, uri: string): Prom

// HTTP(S) requests are sent and the response result is either the schema content or an error
if (scheme === 'http' || scheme === 'https') {
// If we are running inside of VSCode we need to make a content request. This content request
// will make it so that schemas behind VPN's will resolve correctly
if (useVSCodeContentRequest) {
return connection.sendRequest(VSCodeContentRequest.type, uri).then(
(responseText) => {
return responseText;
},
(error) => {
return Promise.reject(error.message);
}
) as Promise<string>;
}

// Send the HTTP(S) schema content request and return the result
const headers = { 'Accept-Encoding': 'gzip, deflate' };
return xhr({ url: uri, followRedirects: 5, headers }).then(
Expand Down
4 changes: 4 additions & 0 deletions src/requestTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ export namespace DynamicCustomSchemaRequestRegistration {
export const type: NotificationType<{}, {}> = new NotificationType('yaml/registerCustomSchemaRequest');
}

export namespace VSCodeContentRequestRegistration {
export const type: NotificationType<{}, {}> = new NotificationType('yaml/registerContentRequest');
}

export namespace VSCodeContentRequest {
export const type: RequestType<{}, {}, {}, {}> = new RequestType('vscode/content');
}
Expand Down
24 changes: 22 additions & 2 deletions src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,13 @@ import {
CustomSchemaRequest,
SchemaModificationNotification,
ISchemaAssociations,
VSCodeContentRequestRegistration,
} from './requestTypes';
import { schemaRequestHandler } from './languageservice/services/schemaRequestHandler';
import { isRelativePath, relativeToAbsolutePath, workspaceFoldersChanged } from './languageservice/utils/paths';
import { URI } from 'vscode-uri';
import { KUBERNETES_SCHEMA_URL, JSON_SCHEMASTORE_URL } from './languageservice/utils/schemaUrls';
import { schemaRequestHandler } from './languageservice/services/schemaRequestHandler';

// eslint-disable-next-line @typescript-eslint/no-explicit-any
nls.config(process.env['VSCODE_NLS_CONFIG'] as any);

Expand Down Expand Up @@ -136,6 +138,7 @@ let workspaceFolders: WorkspaceFolder[] = [];
let clientDynamicRegisterSupport = false;
let hierarchicalDocumentSymbolSupport = false;
let hasWorkspaceFolderCapability = false;
let useVSCodeContentRequest = false;

/****************************
* Reusable helper functions
Expand Down Expand Up @@ -373,7 +376,15 @@ console.error = connection.console.error.bind(connection.console);
// for open, change and close text document events
documents.listen(connection);

const schemaRequestService = schemaRequestHandler.bind(this, connection);
/**
* Handles schema content requests given the schema URI
* @param uri can be a local file, vscode request, http(s) request or a custom request
*/
const schemaRequestHandlerWrapper = (connection: IConnection, uri: string): Promise<string> => {
return schemaRequestHandler(connection, uri, workspaceFolders, workspaceRoot, useVSCodeContentRequest);
};

const schemaRequestService = schemaRequestHandlerWrapper.bind(this, connection);

export const customLanguageService = getCustomLanguageService(schemaRequestService, workspaceContext);

Expand Down Expand Up @@ -456,6 +467,15 @@ connection.onNotification(DynamicCustomSchemaRequestRegistration.type, () => {
customLanguageService.registerCustomSchemaProvider(schemaProvider);
});

/**
* Received a notification from the client that it can accept content requests
* This means that the server sends schemas back to the client side to get resolved rather
* than resolving them on the extension side
*/
connection.onNotification(VSCodeContentRequestRegistration.type, () => {
useVSCodeContentRequest = true;
});

/**
* Run when the editor configuration is changed
* The client syncs the 'yaml', 'http.proxy', 'http.proxyStrictSSL' settings sections
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import * as sinon from 'sinon';
import * as fs from 'fs';
import { IConnection } from 'vscode-languageserver';
import * as assert from 'assert';
import { URI } from 'vscode-uri';

suite('Schema Request Handler Tests', () => {
suite('schemaRequestHandler', () => {
Expand All @@ -23,7 +24,7 @@ suite('Schema Request Handler Tests', () => {
});
test('Should care Win URI', async () => {
const connection = <IConnection>{};
const resultPromise = schemaRequestHandler(connection, 'c:\\some\\window\\path\\scheme.json');
const resultPromise = schemaRequestHandler(connection, 'c:\\some\\window\\path\\scheme.json', [], URI.parse(''), false);
assert.ok(readFileStub.calledOnceWith('c:\\some\\window\\path\\scheme.json'));
readFileStub.callArgWith(2, undefined, '{some: "json"}');
const result = await resultPromise;
Expand All @@ -32,7 +33,7 @@ suite('Schema Request Handler Tests', () => {

test('UNIX URI should works', async () => {
const connection = <IConnection>{};
const resultPromise = schemaRequestHandler(connection, '/some/unix/path/');
const resultPromise = schemaRequestHandler(connection, '/some/unix/path/', [], URI.parse(''), false);
readFileStub.callArgWith(2, undefined, '{some: "json"}');
const result = await resultPromise;
assert.equal(result, '{some: "json"}');
Expand Down