diff --git a/src/languageservice/services/schemaRequestHandler.ts b/src/languageservice/services/schemaRequestHandler.ts index a13c22d7..44d82210 100644 --- a/src/languageservice/services/schemaRequestHandler.ts +++ b/src/languageservice/services/schemaRequestHandler.ts @@ -88,7 +88,13 @@ export const schemaRequestHandler = async ( } // Send the HTTP(S) schema content request and return the result - const headers = { 'Accept-Encoding': 'gzip, deflate' }; + const version = (typeof process !== 'undefined' && process.env.YAML_LANGUAGE_SERVER_VERSION) || 'unknown'; + const nodeVersion = typeof process !== 'undefined' && process.versions?.node ? ` node/${process.versions.node}` : ''; + const platform = typeof process !== 'undefined' && process.platform ? ` (${process.platform})` : ''; + const headers = { + 'Accept-Encoding': 'gzip, deflate', + 'User-Agent': `yaml-language-server/${version} (RedHat)${nodeVersion}${platform}`, + }; return xhr({ url: uri, followRedirects: 5, headers }).then( (response) => { return response.responseText; diff --git a/test/schemaRequestHandler.test.ts b/test/schemaRequestHandler.test.ts index e2ce0725..eb75c1d0 100644 --- a/test/schemaRequestHandler.test.ts +++ b/test/schemaRequestHandler.test.ts @@ -5,6 +5,8 @@ import { schemaRequestHandler } from '../src/languageservice/services/schemaRequestHandler'; import * as sinon from 'sinon'; +import * as request from 'request-light'; +import { XHRResponse } from 'request-light'; import { Connection } from 'vscode-languageserver'; import { URI } from 'vscode-uri'; import * as chai from 'chai'; @@ -66,4 +68,85 @@ describe('Schema Request Handler Tests', () => { expect(result).to.be.equal('{some: "json"}'); }); }); + + describe('HTTP(S) schema requests', () => { + const sandbox = sinon.createSandbox(); + let xhrStub: sinon.SinonStub; + const connection = {} as Connection; + + beforeEach(() => { + xhrStub = sandbox.stub(request, 'xhr'); + xhrStub.resolves({ responseText: '{"$schema":"http://json-schema.org/draft-07/schema"}', status: 200 } as XHRResponse); + }); + + afterEach(() => { + sandbox.restore(); + delete process.env.YAML_LANGUAGE_SERVER_VERSION; + }); + + it('should send correct User-Agent with version, Node runtime and platform', async () => { + process.env.YAML_LANGUAGE_SERVER_VERSION = '1.0.0-test'; + await schemaRequestHandler(connection, 'https://example.com/schema.json', [], URI.parse(''), false, testFileSystem, false); + + expect(xhrStub).calledOnce; + const { headers } = xhrStub.firstCall.args[0]; + expect(headers['User-Agent']).to.equal( + `yaml-language-server/1.0.0-test (RedHat) node/${process.versions.node} (${process.platform})` + ); + }); + + it('should fall back to "unknown" version when YAML_LANGUAGE_SERVER_VERSION is not set', async () => { + delete process.env.YAML_LANGUAGE_SERVER_VERSION; + await schemaRequestHandler(connection, 'https://example.com/schema.json', [], URI.parse(''), false, testFileSystem, false); + + const { headers } = xhrStub.firstCall.args[0]; + expect(headers['User-Agent']).to.match(/^yaml-language-server\/unknown \(RedHat\)/); + }); + + it('should send User-Agent on http:// URIs as well as https://', async () => { + process.env.YAML_LANGUAGE_SERVER_VERSION = '2.0.0'; + await schemaRequestHandler(connection, 'http://example.com/schema.json', [], URI.parse(''), false, testFileSystem, false); + + const { headers } = xhrStub.firstCall.args[0]; + expect(headers['User-Agent']).to.match(/^yaml-language-server\/2\.0\.0 \(RedHat\)/); + }); + + it('should preserve Accept-Encoding header alongside User-Agent', async () => { + await schemaRequestHandler(connection, 'https://example.com/schema.json', [], URI.parse(''), false, testFileSystem, false); + + const { headers } = xhrStub.firstCall.args[0]; + expect(headers['Accept-Encoding']).to.equal('gzip, deflate'); + }); + + it('should return the response text on success', async () => { + const result = await schemaRequestHandler( + connection, + 'https://example.com/schema.json', + [], + URI.parse(''), + false, + testFileSystem, + false + ); + expect(result).to.equal('{"$schema":"http://json-schema.org/draft-07/schema"}'); + }); + + it('should reject with responseText on xhr error', async () => { + xhrStub.rejects({ responseText: 'Not Found', status: 404 } as XHRResponse); + try { + await schemaRequestHandler( + connection, + 'https://example.com/schema.json', + [], + URI.parse(''), + false, + testFileSystem, + false + ); + expect.fail('Expected promise to be rejected'); + } catch (err) { + expect(err).to.equal('Not Found'); + } + }); + }); });