Skip to content

Commit 986fc17

Browse files
hanoj-budimeHanoj Budime
and
Hanoj Budime
authored
✨ Custom Headers (#610)
Co-authored-by: Hanoj Budime <[email protected]>
1 parent 253083b commit 986fc17

File tree

9 files changed

+220
-6
lines changed

9 files changed

+220
-6
lines changed

app/custom_headers.html

+94
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<title>RUM Integ Test</title>
5+
<script src="./loader_custom_headers.js"></script>
6+
<link
7+
rel="icon"
8+
type="image/png"
9+
href="https://awsmedia.s3.amazonaws.com/favicon.ico"
10+
/>
11+
12+
<script>
13+
// Common to all test pages
14+
function dispatch() {
15+
cwr('dispatch');
16+
}
17+
18+
function clearRequestResponse() {
19+
document.getElementById('request_url').innerText = '';
20+
document.getElementById('request_header').innerText = '';
21+
document.getElementById('request_body').innerText = '';
22+
23+
document.getElementById('response_status').innerText = '';
24+
document.getElementById('response_header').innerText = '';
25+
document.getElementById('response_body').innerText = '';
26+
}
27+
28+
function disable() {
29+
cwr('disable');
30+
}
31+
32+
function enable() {
33+
cwr('enable');
34+
}
35+
</script>
36+
37+
<style>
38+
table {
39+
border-collapse: collapse;
40+
margin-top: 10px;
41+
margin-bottom: 10px;
42+
}
43+
44+
td,
45+
th {
46+
border: 1px solid black;
47+
text-align: left;
48+
padding: 8px;
49+
}
50+
</style>
51+
</head>
52+
<body>
53+
<p id="welcome">This application is used for RUM integ testing.</p>
54+
55+
<button id="disable" onclick="disable()">Disable</button>
56+
<button id="enable" onclick="enable()">Enable</button>
57+
<hr />
58+
<button id="dispatch" onclick="dispatch()">Dispatch</button>
59+
<button id="testButton">Test Button</button>
60+
61+
<hr />
62+
<span id="request"></span>
63+
<span id="response"></span>
64+
<table>
65+
<tr>
66+
<td>Request URL</td>
67+
<td id="request_url"></td>
68+
</tr>
69+
<tr>
70+
<td>Request Header</td>
71+
<td id="request_header"></td>
72+
</tr>
73+
<tr>
74+
<td>Request Body</td>
75+
<td id="request_body"></td>
76+
</tr>
77+
</table>
78+
<table>
79+
<tr>
80+
<td>Response Status Code</td>
81+
<td id="response_status"></td>
82+
</tr>
83+
<tr>
84+
<td>Response Header</td>
85+
<td id="response_header"></td>
86+
</tr>
87+
<tr>
88+
<td>Response Body</td>
89+
<td id="response_body"></td>
90+
</tr>
91+
</table>
92+
<img src="blank.png" alt="sample" />
93+
</body>
94+
</html>

docs/configuration.md

+1
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ For example, the config object may look similar to the following:
4343
| 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. |
4444
| eventCacheSize | Number | `200` | The maximum number of events the cache can contain before dropping events. |
4545
| sessionLengthSeconds | Number | `1800` | The duration of a session (in seconds). |
46+
| 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.<br/><br/>For more details, see: [MDN - Request Headers](https://developer.mozilla.org/en-US/docs/Glossary/Request_header). |
4647

4748
## CookieAttributes
4849

src/dispatch/DataPlaneClient.ts

+5-2
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ import {
44
AwsCredentialIdentityProvider,
55
AwsCredentialIdentity,
66
HttpResponse,
7-
RequestPresigningArguments
7+
RequestPresigningArguments,
8+
HeaderBag
89
} from '@aws-sdk/types';
910
import { Sha256 } from '@aws-crypto/sha256-js';
1011
import { HttpHandler, HttpRequest } from '@aws-sdk/protocol-http';
@@ -47,6 +48,7 @@ export declare type DataPlaneClientConfig = {
4748
| AwsCredentialIdentityProvider
4849
| AwsCredentialIdentity
4950
| undefined;
51+
headers?: HeaderBag;
5052
};
5153

5254
export class DataPlaneClient {
@@ -118,7 +120,8 @@ export class DataPlaneClient {
118120
port: Number(this.config.endpoint.port) || undefined,
119121
headers: {
120122
'content-type': contentType,
121-
host: this.config.endpoint.host
123+
host: this.config.endpoint.host,
124+
...this.config.headers
122125
},
123126
hostname: this.config.endpoint.hostname,
124127
path: `${path}/appmonitors/${putRumEventsRequest.AppMonitorDetails.id}`,

src/dispatch/Dispatch.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ export class Dispatch {
3939
private buildClient: ClientBuilder;
4040
private config: Config;
4141
private disableCodes = ['403', '404'];
42+
private headers: any;
4243

4344
constructor(
4445
region: string,
@@ -52,6 +53,7 @@ export class Dispatch {
5253
this.enabled = true;
5354
this.buildClient = config.clientBuilder || this.defaultClientBuilder;
5455
this.config = config;
56+
this.headers = config.headers;
5557
this.startDispatchTimer();
5658
if (config.signing) {
5759
this.rum = {
@@ -265,7 +267,8 @@ export class Dispatch {
265267
beaconRequestHandler: new BeaconHttpHandler(),
266268
endpoint,
267269
region,
268-
credentials
270+
credentials,
271+
headers: this.headers
269272
});
270273
};
271274
}

src/dispatch/__tests__/DataPlaneClient.test.ts

+35-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { FetchHttpHandler } from '@aws-sdk/fetch-http-handler';
44
import { DataPlaneClient } from '../DataPlaneClient';
55
import { HttpRequest } from '@aws-sdk/protocol-http';
66
import { advanceTo } from 'jest-date-mock';
7+
import { HeaderBag } from '@aws-sdk/types';
78

89
const beaconHandler = jest.fn(() => Promise.resolve());
910
jest.mock('../BeaconHttpHandler', () => ({
@@ -21,6 +22,7 @@ jest.mock('@aws-sdk/fetch-http-handler', () => ({
2122
interface Config {
2223
signing: boolean;
2324
endpoint: URL;
25+
headers?: HeaderBag;
2426
}
2527

2628
const defaultConfig = { signing: true, endpoint: Utils.AWS_RUM_ENDPOINT };
@@ -33,7 +35,8 @@ const createDataPlaneClient = (
3335
beaconRequestHandler: new BeaconHttpHandler(),
3436
endpoint: config.endpoint,
3537
region: Utils.AWS_RUM_REGION,
36-
credentials: config.signing ? Utils.createAwsCredentials() : undefined
38+
credentials: config.signing ? Utils.createAwsCredentials() : undefined,
39+
headers: config.headers ? config.headers : undefined
3740
});
3841
};
3942

@@ -345,4 +348,35 @@ describe('DataPlaneClient tests', () => {
345348
expect(signedRequest.query['X-Amz-SignedHeaders']).toEqual(undefined);
346349
expect(signedRequest.query['X-Amz-Signature']).toEqual(undefined);
347350
});
351+
352+
test('when the headers contains in config', async () => {
353+
// Init
354+
const endpoint = new URL(`${Utils.AWS_RUM_ENDPOINT}${'prod'}`);
355+
const headers = {
356+
Authorization: `Bearer token`,
357+
'x-api-key': 'a1b2c3d4e5f6',
358+
'content-type': 'application/json'
359+
};
360+
const client: DataPlaneClient = createDataPlaneClient({
361+
...defaultConfig,
362+
endpoint,
363+
headers,
364+
signing: false
365+
});
366+
367+
// Run
368+
await client.sendFetch(Utils.PUT_RUM_EVENTS_REQUEST);
369+
370+
// Assert
371+
const signedRequest: HttpRequest = (
372+
fetchHandler.mock.calls[0] as any
373+
)[0];
374+
console.log('signedRequest :>> ', signedRequest);
375+
expect(signedRequest.headers['Authorization']).toEqual(
376+
headers['Authorization']
377+
);
378+
expect(signedRequest.headers['x-api-key']).toEqual(
379+
headers['x-api-key']
380+
);
381+
});
348382
});

src/loader/loader-custom-headers.js

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { loader } from './loader';
2+
import { showRequestClientBuilder } from '../test-utils/mock-http-custom-headers';
3+
4+
const token =
5+
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c';
6+
loader(
7+
'cwr',
8+
'a1b2c3d4-c3f5-1a2b-b2b4-012345678910',
9+
'1.0',
10+
'us-west-2',
11+
'./rum_javascript_telemetry.js',
12+
{
13+
userIdRetentionDays: 1,
14+
dispatchInterval: 0,
15+
allowCookies: false,
16+
eventPluginsToLoad: [],
17+
telemetries: [],
18+
clientBuilder: showRequestClientBuilder,
19+
signing: false,
20+
endpoint:
21+
'https://api-id.execute-api.region.amazonaws.com/api/dataplane',
22+
headers: {
23+
Authorization: `Bearer ${token}`,
24+
'x-api-key': 'a1b2c3d4e5f6',
25+
'content-type': 'application/json'
26+
}
27+
}
28+
);

src/orchestration/Orchestration.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ import { EventCache } from '../event-cache/EventCache';
1717
import { ClientBuilder, Dispatch } from '../dispatch/Dispatch';
1818
import {
1919
AwsCredentialIdentityProvider,
20-
AwsCredentialIdentity
20+
AwsCredentialIdentity,
21+
HeaderBag
2122
} from '@aws-sdk/types';
2223
import { NavigationPlugin } from '../plugins/event-plugins/NavigationPlugin';
2324
import { ResourcePlugin } from '../plugins/event-plugins/ResourcePlugin';
@@ -160,6 +161,7 @@ export interface Config {
160161
useBeacon: boolean;
161162
userIdRetentionDays: number;
162163
alias?: string;
164+
headers?: HeaderBag;
163165
}
164166

165167
export interface PartialConfig
+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import { HttpHandler, HttpRequest, HttpResponse } from '@aws-sdk/protocol-http';
2+
import { ClientBuilder } from '../dispatch/Dispatch';
3+
import { DataPlaneClient } from '../dispatch/DataPlaneClient';
4+
import { logRequestToPage, logResponseToPage } from './http-handler-utils';
5+
6+
/**
7+
* Returns data plane service client with a mocked request handler.
8+
*
9+
* @param endpoint Service endpoint.
10+
* @param region Service region.
11+
* @param credentials AWS credentials.
12+
*/
13+
const token =
14+
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c';
15+
export const showRequestClientBuilder: ClientBuilder = (
16+
endpoint,
17+
region,
18+
credentials
19+
) => {
20+
return new DataPlaneClient({
21+
fetchRequestHandler: new ShowMockRequestHandler(),
22+
beaconRequestHandler: new ShowMockRequestHandler(),
23+
endpoint,
24+
region,
25+
credentials,
26+
headers: {
27+
Authorization: `Bearer ${token}`,
28+
'x-api-key': 'a1b2c3d4e5f6',
29+
'content-type': 'application/json'
30+
}
31+
});
32+
};
33+
34+
class ShowMockRequestHandler implements HttpHandler {
35+
handle(request: HttpRequest): Promise<{ response: HttpResponse }> {
36+
const response: HttpResponse = {
37+
statusCode: 202,
38+
headers: {
39+
'content-type': 'application/json',
40+
'Access-Control-Allow-Origin': '*'
41+
},
42+
body: undefined
43+
};
44+
logRequestToPage(request);
45+
logResponseToPage(response);
46+
return Promise.resolve({ response });
47+
}
48+
}

webpack/webpack.dev.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@ module.exports = merge(common, {
3131
loader_remote_config: './src/loader/loader-remote-config.js',
3232
loader_spa: './src/loader/loader-spa.js',
3333
loader_custom_events: './src/loader/loader-custom-events.js',
34-
loader_alias: './src/loader/loader-alias.js'
34+
loader_alias: './src/loader/loader-alias.js',
35+
loader_custom_headers: './src/loader/loader-custom-headers.js'
3536
},
3637
resolve: {
3738
extensions: ['.ts', '.js', '.json']

0 commit comments

Comments
 (0)