Skip to content

Commit 4098016

Browse files
authored
Encode request id in URI path (#113)
1 parent fe17311 commit 4098016

11 files changed

+145
-15
lines changed

package-lock.json

+8
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@
5959
"node-gyp": "9.4.0"
6060
},
6161
"devDependencies": {
62+
"lambda-runtime": "file:./src/",
6263
"esbuild": "^0.18.3",
6364
"eslint": "8.42.0",
6465
"eslint-config-prettier": "8.8.0",

src/RAPIDClient.js

+6-3
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ module.exports = class RAPIDClient {
4444
*/
4545
postInvocationResponse(response, id, callback) {
4646
let bodyString = _trySerializeResponse(response);
47-
this.nativeClient.done(id, bodyString);
47+
this.nativeClient.done(encodeURIComponent(id), bodyString);
4848
callback();
4949
}
5050

@@ -65,7 +65,10 @@ module.exports = class RAPIDClient {
6565
hostname: this.hostname,
6666
method: 'POST',
6767
port: this.port,
68-
path: '/2018-06-01/runtime/invocation/' + id + '/response',
68+
path:
69+
'/2018-06-01/runtime/invocation/' +
70+
encodeURIComponent(id) +
71+
'/response',
6972
highWaterMark: options?.highWaterMark,
7073
},
7174
});
@@ -108,7 +111,7 @@ module.exports = class RAPIDClient {
108111
let response = Errors.toRapidResponse(error);
109112
let bodyString = _trySerializeResponse(response);
110113
let xrayString = XRayError.formatted(error);
111-
this.nativeClient.error(id, bodyString, xrayString);
114+
this.nativeClient.error(encodeURIComponent(id), bodyString, xrayString);
112115
callback();
113116
}
114117

test/unit/ErrorsTest.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
'use strict';
66

77
require('should');
8-
let Errors = require('../../src/Errors');
8+
let Errors = require('lambda-runtime/Errors');
99

1010
describe('Formatted Error Logging', () => {
1111
it('should fall back to a minimal error format when an exception occurs', () => {

test/unit/FakeTelemetryTarget.js

+1
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ module.exports = class FakeTelemetryTarget {
112112
if (lineLength === 0) {
113113
return '';
114114
}
115+
115116
let lineBytes = Buffer.alloc(lineLength);
116117
let actualLineSize = fs.readSync(
117118
this.readTarget,

test/unit/InvokeContextTest.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
require('should');
88
const sleep = require('util').promisify(setTimeout);
99

10-
let InvokeContext = require('../../src/InvokeContext');
10+
let InvokeContext = require('lambda-runtime/InvokeContext');
1111

1212
describe('Getting remaining invoke time', () => {
1313
it('should reduce by at least elapsed time', async () => {

test/unit/LogPatchTest.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@
77
const util = require('util');
88

99
let should = require('should');
10-
let LogPatch = require('../../src/LogPatch');
11-
let Errors = require('../../src/Errors');
10+
let LogPatch = require('lambda-runtime/LogPatch');
11+
let Errors = require('lambda-runtime/Errors');
1212
let assert = require('assert');
1313

1414
let {

test/unit/RAPIDClientTest.js

+117
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
/**
2+
* Copyright 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
*/
4+
5+
'use strict';
6+
7+
require('should');
8+
9+
let RAPIDClient = require('lambda-runtime/RAPIDClient.js');
10+
let runtimeErrors = require('lambda-runtime/Errors.js');
11+
12+
/**
13+
* Stub request object.
14+
* Provides no-op definitions of the request functions used by the rapid client.
15+
*/
16+
const noOpRequest = Object.freeze({
17+
/* no op, return itself to allow continuations/chaining */
18+
on: () => noOpRequest,
19+
/* no op, return itself to allow continuations/chaining */
20+
end: () => noOpRequest,
21+
});
22+
23+
class StubHttp {
24+
constructor() {
25+
this.lastUsedOptions = {};
26+
this.Agent = class FakeAgent {};
27+
}
28+
29+
request(options, _callback) {
30+
this.lastUsedOptions = options;
31+
return noOpRequest;
32+
}
33+
}
34+
35+
class NoOpNativeHttp {
36+
constructor() {
37+
this.lastRequestId = '';
38+
this.lastErrorRequestId = '';
39+
}
40+
41+
done(requestId) {
42+
this.lastRequestId = requestId;
43+
}
44+
45+
error(requestId) {
46+
this.lastErrorRequestId = requestId;
47+
}
48+
}
49+
50+
class EvilError extends Error {
51+
get name() {
52+
throw 'gotcha';
53+
}
54+
}
55+
56+
const EXPECTED_ERROR_HEADER = 'Lambda-Runtime-Function-Error-Type';
57+
58+
describe('building error requests with the RAPIDClient', () => {
59+
let stubHttp = new StubHttp();
60+
let client = new RAPIDClient('notUsed:1337', stubHttp, new NoOpNativeHttp());
61+
62+
let errors = [
63+
[new Error('generic failure'), 'Error'],
64+
[new runtimeErrors.ImportModuleError(), 'Runtime.ImportModuleError'],
65+
[new runtimeErrors.HandlerNotFound(), 'Runtime.HandlerNotFound'],
66+
[new runtimeErrors.MalformedHandlerName(), 'Runtime.MalformedHandlerName'],
67+
[new runtimeErrors.UserCodeSyntaxError(), 'Runtime.UserCodeSyntaxError'],
68+
[{ data: 'some random object' }, 'object'],
69+
[new EvilError(), 'handled'],
70+
];
71+
72+
describe('the error header in postInitError', () => {
73+
errors.forEach(([error, name]) => {
74+
it(`should be ${name} for ${error.constructor.name}`, () => {
75+
client.postInitError(error);
76+
stubHttp.lastUsedOptions.should.have
77+
.property('headers')
78+
.have.property(EXPECTED_ERROR_HEADER, name);
79+
});
80+
});
81+
});
82+
});
83+
84+
describe('invalid request id works', () => {
85+
const nativeClient = new NoOpNativeHttp();
86+
const client = new RAPIDClient('notUsed:1337', undefined, nativeClient);
87+
88+
[
89+
// Encoding expected:
90+
['#', '%23'],
91+
['%', '%25'],
92+
['/', '%2F'],
93+
['?', '%3F'],
94+
['\x7F', '%7F'],
95+
["<script>alert('1')</script>", "%3Cscript%3Ealert('1')%3C%2Fscript%3E"],
96+
['⚡', '%E2%9A%A1'],
97+
98+
// No encoding:
99+
['.', '.'],
100+
['..', '..'],
101+
['a', 'a'],
102+
[
103+
'59b22c65-fa81-47fb-a6dc-23028a63566f',
104+
'59b22c65-fa81-47fb-a6dc-23028a63566f',
105+
],
106+
].forEach(([requestId, expected]) => {
107+
it(`postInvocationResponse should encode requestId: '${requestId}'`, () => {
108+
client.postInvocationResponse({}, requestId, () => {});
109+
nativeClient.lastRequestId.should.be.equal(expected);
110+
});
111+
112+
it(`postInvocationError should encode requestId: '${requestId}'`, () => {
113+
client.postInvocationError(new Error(), requestId, () => {});
114+
nativeClient.lastErrorRequestId.should.be.equal(expected);
115+
});
116+
});
117+
});

test/unit/ResponseStreamTest.js

+4-4
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,10 @@ const ServerMock = require('mock-http-server');
99
const {
1010
createResponseStream,
1111
tryCallFail,
12-
} = require('../../src/ResponseStream.js');
13-
const { HttpResponseStream } = require('../../src/HttpResponseStream.js');
14-
const { InvalidStreamingOperation } = require('../../src/Errors.js');
15-
const { verbose, vverbose, vvverbose } = require('../../src/VerboseLog').logger(
12+
} = require('lambda-runtime/ResponseStream.js');
13+
const { HttpResponseStream } = require('lambda-runtime/HttpResponseStream.js');
14+
const { InvalidStreamingOperation } = require('lambda-runtime/Errors.js');
15+
const { verbose, vverbose, vvverbose } = require('lambda-runtime/VerboseLog').logger(
1616
'TEST',
1717
);
1818
const Throttle = require('throttle');

test/unit/StreamingContextTest.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@
55
'use strict';
66

77
require('should');
8-
const StreamingContext = require('../../src/StreamingContext.js');
8+
const StreamingContext = require('lambda-runtime/StreamingContext.js');
99
const { PassThrough } = require('stream');
10-
const BeforeExitListener = require('../../src/BeforeExitListener.js');
10+
const BeforeExitListener = require('lambda-runtime/BeforeExitListener.js');
1111

1212
class MockRapidClient {
1313
constructor() {

test/unit/UserFunctionTest.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ const {
1010
HandlerNotFound,
1111
ImportModuleError,
1212
MalformedHandlerName,
13-
} = require('../../src/Errors.js');
14-
const UserFunction = require('../../src/UserFunction.js');
13+
} = require('lambda-runtime/Errors.js');
14+
const UserFunction = require('lambda-runtime/UserFunction.js');
1515

1616
const TEST_ROOT = path.join(__dirname, '../');
1717
const HANDLERS_ROOT = path.join(TEST_ROOT, 'handlers');

0 commit comments

Comments
 (0)