diff --git a/app/custom_headers.html b/app/custom_headers.html
new file mode 100644
index 00000000..7170c191
--- /dev/null
+++ b/app/custom_headers.html
@@ -0,0 +1,94 @@
+
+
+
+ RUM Integ Test
+
+
+
+
+
+
+
+
+ This application is used for RUM integ testing.
+
+ Disable
+ Enable
+
+ Dispatch
+ Test Button
+
+
+
+
+
+
+ Request URL
+
+
+
+ Request Header
+
+
+
+ Request Body
+
+
+
+
+
+ Response Status Code
+
+
+
+ Response Header
+
+
+
+ Response Body
+
+
+
+
+
+
diff --git a/docs/configuration.md b/docs/configuration.md
index ab846212..ba141042 100644
--- a/docs/configuration.md
+++ b/docs/configuration.md
@@ -43,6 +43,7 @@ For example, the config object may look similar to the following:
| dispatchInterval | Number | `5000` | The frequency (in milliseconds) in which the webclient will dispatch a batch of RUM events. RUM events are first cached and then automatically dispatched at this set interval. |
| eventCacheSize | Number | `200` | The maximum number of events the cache can contain before dropping events. |
| sessionLengthSeconds | Number | `1800` | The duration of a session (in seconds). |
+| headers | Object | `{}` | The **headers** configuration is optional and allows you to include custom headers in an HTTP request. For example, you can use it to pass `Authorization` and `x-api-key` headers. For more details, see: [MDN - Request Headers](https://developer.mozilla.org/en-US/docs/Glossary/Request_header). |
## CookieAttributes
diff --git a/src/dispatch/DataPlaneClient.ts b/src/dispatch/DataPlaneClient.ts
index d46eb597..64044675 100644
--- a/src/dispatch/DataPlaneClient.ts
+++ b/src/dispatch/DataPlaneClient.ts
@@ -4,7 +4,8 @@ import {
AwsCredentialIdentityProvider,
AwsCredentialIdentity,
HttpResponse,
- RequestPresigningArguments
+ RequestPresigningArguments,
+ HeaderBag
} from '@aws-sdk/types';
import { Sha256 } from '@aws-crypto/sha256-js';
import { HttpHandler, HttpRequest } from '@aws-sdk/protocol-http';
@@ -47,6 +48,7 @@ export declare type DataPlaneClientConfig = {
| AwsCredentialIdentityProvider
| AwsCredentialIdentity
| undefined;
+ headers?: HeaderBag;
};
export class DataPlaneClient {
@@ -118,7 +120,8 @@ export class DataPlaneClient {
port: Number(this.config.endpoint.port) || undefined,
headers: {
'content-type': contentType,
- host: this.config.endpoint.host
+ host: this.config.endpoint.host,
+ ...this.config.headers
},
hostname: this.config.endpoint.hostname,
path: `${path}/appmonitors/${putRumEventsRequest.AppMonitorDetails.id}`,
diff --git a/src/dispatch/Dispatch.ts b/src/dispatch/Dispatch.ts
index 4c111c89..ffd74fa7 100644
--- a/src/dispatch/Dispatch.ts
+++ b/src/dispatch/Dispatch.ts
@@ -39,6 +39,7 @@ export class Dispatch {
private buildClient: ClientBuilder;
private config: Config;
private disableCodes = ['403', '404'];
+ private headers: any;
constructor(
region: string,
@@ -52,6 +53,7 @@ export class Dispatch {
this.enabled = true;
this.buildClient = config.clientBuilder || this.defaultClientBuilder;
this.config = config;
+ this.headers = config.headers;
this.startDispatchTimer();
if (config.signing) {
this.rum = {
@@ -265,7 +267,8 @@ export class Dispatch {
beaconRequestHandler: new BeaconHttpHandler(),
endpoint,
region,
- credentials
+ credentials,
+ headers: this.headers
});
};
}
diff --git a/src/dispatch/__tests__/DataPlaneClient.test.ts b/src/dispatch/__tests__/DataPlaneClient.test.ts
index 0b4b4777..9409b05a 100644
--- a/src/dispatch/__tests__/DataPlaneClient.test.ts
+++ b/src/dispatch/__tests__/DataPlaneClient.test.ts
@@ -4,6 +4,7 @@ import { FetchHttpHandler } from '@aws-sdk/fetch-http-handler';
import { DataPlaneClient } from '../DataPlaneClient';
import { HttpRequest } from '@aws-sdk/protocol-http';
import { advanceTo } from 'jest-date-mock';
+import { HeaderBag } from '@aws-sdk/types';
const beaconHandler = jest.fn(() => Promise.resolve());
jest.mock('../BeaconHttpHandler', () => ({
@@ -21,6 +22,7 @@ jest.mock('@aws-sdk/fetch-http-handler', () => ({
interface Config {
signing: boolean;
endpoint: URL;
+ headers?: HeaderBag;
}
const defaultConfig = { signing: true, endpoint: Utils.AWS_RUM_ENDPOINT };
@@ -33,7 +35,8 @@ const createDataPlaneClient = (
beaconRequestHandler: new BeaconHttpHandler(),
endpoint: config.endpoint,
region: Utils.AWS_RUM_REGION,
- credentials: config.signing ? Utils.createAwsCredentials() : undefined
+ credentials: config.signing ? Utils.createAwsCredentials() : undefined,
+ headers: config.headers ? config.headers : undefined
});
};
@@ -345,4 +348,35 @@ describe('DataPlaneClient tests', () => {
expect(signedRequest.query['X-Amz-SignedHeaders']).toEqual(undefined);
expect(signedRequest.query['X-Amz-Signature']).toEqual(undefined);
});
+
+ test('when the headers contains in config', async () => {
+ // Init
+ const endpoint = new URL(`${Utils.AWS_RUM_ENDPOINT}${'prod'}`);
+ const headers = {
+ Authorization: `Bearer token`,
+ 'x-api-key': 'a1b2c3d4e5f6',
+ 'content-type': 'application/json'
+ };
+ const client: DataPlaneClient = createDataPlaneClient({
+ ...defaultConfig,
+ endpoint,
+ headers,
+ signing: false
+ });
+
+ // Run
+ await client.sendFetch(Utils.PUT_RUM_EVENTS_REQUEST);
+
+ // Assert
+ const signedRequest: HttpRequest = (
+ fetchHandler.mock.calls[0] as any
+ )[0];
+ console.log('signedRequest :>> ', signedRequest);
+ expect(signedRequest.headers['Authorization']).toEqual(
+ headers['Authorization']
+ );
+ expect(signedRequest.headers['x-api-key']).toEqual(
+ headers['x-api-key']
+ );
+ });
});
diff --git a/src/loader/loader-custom-headers.js b/src/loader/loader-custom-headers.js
new file mode 100644
index 00000000..1b119a6f
--- /dev/null
+++ b/src/loader/loader-custom-headers.js
@@ -0,0 +1,28 @@
+import { loader } from './loader';
+import { showRequestClientBuilder } from '../test-utils/mock-http-custom-headers';
+
+const token =
+ 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c';
+loader(
+ 'cwr',
+ 'a1b2c3d4-c3f5-1a2b-b2b4-012345678910',
+ '1.0',
+ 'us-west-2',
+ './rum_javascript_telemetry.js',
+ {
+ userIdRetentionDays: 1,
+ dispatchInterval: 0,
+ allowCookies: false,
+ eventPluginsToLoad: [],
+ telemetries: [],
+ clientBuilder: showRequestClientBuilder,
+ signing: false,
+ endpoint:
+ 'https://api-id.execute-api.region.amazonaws.com/api/dataplane',
+ headers: {
+ Authorization: `Bearer ${token}`,
+ 'x-api-key': 'a1b2c3d4e5f6',
+ 'content-type': 'application/json'
+ }
+ }
+);
diff --git a/src/orchestration/Orchestration.ts b/src/orchestration/Orchestration.ts
index 0e593052..df079423 100644
--- a/src/orchestration/Orchestration.ts
+++ b/src/orchestration/Orchestration.ts
@@ -17,7 +17,8 @@ import { EventCache } from '../event-cache/EventCache';
import { ClientBuilder, Dispatch } from '../dispatch/Dispatch';
import {
AwsCredentialIdentityProvider,
- AwsCredentialIdentity
+ AwsCredentialIdentity,
+ HeaderBag
} from '@aws-sdk/types';
import { NavigationPlugin } from '../plugins/event-plugins/NavigationPlugin';
import { ResourcePlugin } from '../plugins/event-plugins/ResourcePlugin';
@@ -160,6 +161,7 @@ export interface Config {
useBeacon: boolean;
userIdRetentionDays: number;
alias?: string;
+ headers?: HeaderBag;
}
export interface PartialConfig
diff --git a/src/test-utils/mock-http-custom-headers.ts b/src/test-utils/mock-http-custom-headers.ts
new file mode 100644
index 00000000..93f13d2d
--- /dev/null
+++ b/src/test-utils/mock-http-custom-headers.ts
@@ -0,0 +1,48 @@
+import { HttpHandler, HttpRequest, HttpResponse } from '@aws-sdk/protocol-http';
+import { ClientBuilder } from '../dispatch/Dispatch';
+import { DataPlaneClient } from '../dispatch/DataPlaneClient';
+import { logRequestToPage, logResponseToPage } from './http-handler-utils';
+
+/**
+ * Returns data plane service client with a mocked request handler.
+ *
+ * @param endpoint Service endpoint.
+ * @param region Service region.
+ * @param credentials AWS credentials.
+ */
+const token =
+ 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c';
+export const showRequestClientBuilder: ClientBuilder = (
+ endpoint,
+ region,
+ credentials
+) => {
+ return new DataPlaneClient({
+ fetchRequestHandler: new ShowMockRequestHandler(),
+ beaconRequestHandler: new ShowMockRequestHandler(),
+ endpoint,
+ region,
+ credentials,
+ headers: {
+ Authorization: `Bearer ${token}`,
+ 'x-api-key': 'a1b2c3d4e5f6',
+ 'content-type': 'application/json'
+ }
+ });
+};
+
+class ShowMockRequestHandler implements HttpHandler {
+ handle(request: HttpRequest): Promise<{ response: HttpResponse }> {
+ const response: HttpResponse = {
+ statusCode: 202,
+ headers: {
+ 'content-type': 'application/json',
+ 'Access-Control-Allow-Origin': '*'
+ },
+ body: undefined
+ };
+ logRequestToPage(request);
+ logResponseToPage(response);
+ return Promise.resolve({ response });
+ }
+}
diff --git a/webpack/webpack.dev.js b/webpack/webpack.dev.js
index 8e9562d4..ef698f24 100644
--- a/webpack/webpack.dev.js
+++ b/webpack/webpack.dev.js
@@ -31,7 +31,8 @@ module.exports = merge(common, {
loader_remote_config: './src/loader/loader-remote-config.js',
loader_spa: './src/loader/loader-spa.js',
loader_custom_events: './src/loader/loader-custom-events.js',
- loader_alias: './src/loader/loader-alias.js'
+ loader_alias: './src/loader/loader-alias.js',
+ loader_custom_headers: './src/loader/loader-custom-headers.js'
},
resolve: {
extensions: ['.ts', '.js', '.json']