@azure/monitor-opentelemetry
Version:
Azure Monitor OpenTelemetry (Node.js)
141 lines • 5.46 kB
JavaScript
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
import url from "url";
import { redirectPolicyName } from "@azure/core-rest-pipeline";
import { diag } from "@opentelemetry/api";
import { QuickpulseClient } from "../../../generated/index.js";
import { isSameRegisteredDomain } from "../redirectUtils.js";
const applicationInsightsResource = "https://monitor.azure.com/.default";
/**
* Quickpulse sender class
* @internal
*/
export class QuickpulseSender {
quickpulseClient;
instrumentationKey;
endpointUrl;
credential;
credentialScopes;
constructor(options) {
// Build endpoint using provided configuration or default values
this.endpointUrl = options.endpointUrl;
const clientOptions = {
endpoint: this.endpointUrl,
};
this.instrumentationKey = options.instrumentationKey;
this.credential = options.credential;
// Configure credential scopes
if (options.credentialScopes) {
this.credentialScopes = Array.isArray(options.credentialScopes)
? options.credentialScopes
: [options.credentialScopes];
}
else {
this.credentialScopes = [applicationInsightsResource];
}
if (options.credential) {
clientOptions.credentials = { scopes: this.credentialScopes };
}
this.quickpulseClient = this.createQuickpulseClient(clientOptions);
}
createQuickpulseClient(clientOptions) {
const client = new QuickpulseClient(this.credential, clientOptions);
// Handle redirects in HTTP Sender
client.pipeline.removePolicy({ name: redirectPolicyName });
return client;
}
/**
* isSubscribed Quickpulse service
* @internal
*/
async isSubscribed(optionalParams) {
try {
let responseHeaders = {};
const body = await this.quickpulseClient.isSubscribed(this.instrumentationKey, {
...optionalParams,
onResponse: (rawResponse) => {
responseHeaders = rawResponse.headers.toJSON();
},
});
return {
...body,
xMsQpsSubscribed: responseHeaders["x-ms-qps-subscribed"],
xMsQpsConfigurationEtag: responseHeaders["x-ms-qps-configuration-etag"],
xMsQpsServicePollingIntervalHint: responseHeaders["x-ms-qps-service-polling-interval-hint"],
xMsQpsServiceEndpointRedirectV2: responseHeaders["x-ms-qps-service-endpoint-redirect-v2"],
};
}
catch (error) {
const restError = error;
diag.info("Failed to ping Quickpulse service", restError.message);
}
return;
}
/**
* publish Quickpulse service
* @internal
*/
async publish(optionalParams) {
try {
let responseHeaders = {};
const body = await this.quickpulseClient.publish(this.instrumentationKey, {
...optionalParams,
onResponse: (rawResponse) => {
responseHeaders = rawResponse.headers.toJSON();
},
});
return {
...body,
xMsQpsSubscribed: responseHeaders["x-ms-qps-subscribed"],
xMsQpsConfigurationEtag: responseHeaders["x-ms-qps-configuration-etag"],
};
}
catch (error) {
const restError = error;
diag.warn("Failed to post Quickpulse service", restError.message);
}
return;
}
/**
* Apply a server-issued Live Metrics redirect (`x-ms-qps-service-endpoint-redirect-v2`) by
* re-pointing the underlying client at the new host.
*
* Cross-origin redirects are refused (no state mutated) when the target is neither the configured
* Live Metrics host nor under a known Azure Monitor ingestion domain suffix. Refusing them is
* required to prevent an attacker-controlled redirect from causing the bearer auth policy to
* attach a freshly-signed AAD token (scope `https://monitor.azure.com/.default`) — and the
* telemetry body — to a foreign host on the next ping/publish call.
*/
handlePermanentRedirect(location) {
if (location) {
let locUrl;
try {
locUrl = new url.URL(location);
}
catch {
return;
}
if (!locUrl.host) {
return;
}
let currentHost = "";
try {
currentHost = new url.URL(this.endpointUrl).host;
}
catch {
currentHost = "";
}
if (!isSameRegisteredDomain(currentHost, locUrl.host)) {
diag.error(`Refusing cross-origin Live Metrics redirect to https://${locUrl.host}: target is neither the configured endpoint host nor under a known Azure Monitor ingestion domain.`);
return;
}
this.endpointUrl = "https://" + locUrl.host;
// Recreate the client so subsequent requests use the new endpoint
this.quickpulseClient = this.createQuickpulseClient({
endpoint: this.endpointUrl,
credentials: { scopes: this.credentialScopes },
});
}
}
}
//# sourceMappingURL=sender.js.map