-
Notifications
You must be signed in to change notification settings - Fork 254
Expand file tree
/
Copy pathjson-schema-content-provider.ts
More file actions
120 lines (109 loc) · 4.64 KB
/
json-schema-content-provider.ts
File metadata and controls
120 lines (109 loc) · 4.64 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
/*---------------------------------------------------------------------------------------------
* Copyright (c) Red Hat, Inc. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { TextDocumentContentProvider, Uri, workspace, window, WorkspaceConfiguration } from 'vscode';
import { xhr, configure as configureHttpRequests, getErrorStatusDescription, XHRResponse } from 'request-light';
import { SchemaExtensionAPI } from './schema-extension-api';
export interface IJSONSchemaCache {
getETag(schemaUri: string): string | undefined;
putSchema(schemaUri: string, eTag: string, schemaContent: string): Promise<void>;
getSchema(schemaUri: string): Promise<string | undefined>;
}
export class JSONSchemaDocumentContentProvider implements TextDocumentContentProvider {
constructor(private readonly schemaCache: IJSONSchemaCache, private readonly schemaApi: SchemaExtensionAPI) {}
async provideTextDocumentContent(uri: Uri): Promise<string> {
if (uri.fragment) {
const origUri = uri.fragment;
const schemaUri = Uri.parse(origUri);
// handle both 'http' and 'https'
if (origUri.startsWith('http')) {
return getJsonSchemaContent(origUri, this.schemaCache);
} else if (this.schemaApi.hasProvider(schemaUri.scheme)) {
let content = this.schemaApi.requestCustomSchemaContent(origUri);
content = await Promise.resolve(content);
// prettify JSON
if (content.indexOf('\n') === -1) {
content = JSON.stringify(JSON.parse(content), null, 2);
}
return content;
} else {
window.showErrorMessage(`Cannot Load content for: ${origUri}. Unknown schema: '${schemaUri.scheme}'`);
return null;
}
} else {
window.showErrorMessage(`Cannot Load content for: '${uri.toString()}' `);
return null;
}
}
}
export async function getJsonSchemaContent(uri: string, schemaCache: IJSONSchemaCache): Promise<string> {
const cachedETag = schemaCache.getETag(uri);
const httpSettings = workspace.getConfiguration('http');
if (requestShouldBeProxied(uri, httpSettings)) {
configureHttpRequests(httpSettings.proxy, httpSettings.proxyStrictSSL);
}
const headers: { [key: string]: string } = { 'Accept-Encoding': 'gzip, deflate' };
if (cachedETag) {
headers['If-None-Match'] = cachedETag;
}
return xhr({ url: uri, followRedirects: 5, headers })
.then(async (response) => {
// cache only if server supports 'etag' header
const etag = response.headers['etag'];
if (typeof etag === 'string') {
await schemaCache.putSchema(uri, etag, response.responseText);
}
return response.responseText;
})
.then((text) => {
return text;
})
.catch(async (error: XHRResponse) => {
// content not changed, return cached
if (error.status === 304) {
const content = await schemaCache.getSchema(uri);
// ensure that we return content even if cache doesn't have it
if (content === undefined) {
console.error(`Cannot read cached content for: ${uri}, trying to load again`);
delete headers['If-None-Match'];
return xhr({ url: uri, followRedirects: 5, headers })
.then((response) => {
return response.responseText;
})
.catch((err: XHRResponse) => {
return createReject(err);
});
}
return content;
}
// in case of some error, like internet connection issue, check if cached version exist and return it
if (schemaCache.getETag(uri)) {
const content = schemaCache.getSchema(uri);
if (content) {
return content;
}
}
return createReject(error);
});
}
function createReject(error: XHRResponse): Promise<string> {
return Promise.reject(error.responseText || getErrorStatusDescription(error.status) || error.toString());
}
export function requestShouldBeProxied(uri: string, httpSettings: WorkspaceConfiguration): boolean {
const proxy = httpSettings.get<string>('proxy');
const noProxy = httpSettings.get<string[]>('noProxy');
// proxy not configured
if (!proxy) return false;
// proxy configured, no items in noProxy
if (!noProxy.length) return true;
const noProxyEntries = noProxy.map((item) => item.trim());
const uriAuthority = Uri.parse(uri).authority;
return !noProxyEntries.some((entry) => {
if (entry.startsWith('*.')) {
return uriAuthority.endsWith(entry.substring(1));
} else {
return uriAuthority === entry;
}
});
}