Skip to content

Commit

Permalink
Encode request id in URI path
Browse files Browse the repository at this point in the history
  • Loading branch information
andclt committed Jul 1, 2024
1 parent fe17311 commit cba61c6
Show file tree
Hide file tree
Showing 11 changed files with 145 additions and 15 deletions.
8 changes: 8 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
"node-gyp": "9.4.0"
},
"devDependencies": {
"lambda-runtime": "file:./src/",
"esbuild": "^0.18.3",
"eslint": "8.42.0",
"eslint-config-prettier": "8.8.0",
Expand Down
9 changes: 6 additions & 3 deletions src/RAPIDClient.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ module.exports = class RAPIDClient {
*/
postInvocationResponse(response, id, callback) {
let bodyString = _trySerializeResponse(response);
this.nativeClient.done(id, bodyString);
this.nativeClient.done(encodeURIComponent(id), bodyString);
callback();
}

Expand All @@ -65,7 +65,10 @@ module.exports = class RAPIDClient {
hostname: this.hostname,
method: 'POST',
port: this.port,
path: '/2018-06-01/runtime/invocation/' + id + '/response',
path:
'/2018-06-01/runtime/invocation/' +
encodeURIComponent(id) +
'/response',
highWaterMark: options?.highWaterMark,
},
});
Expand Down Expand Up @@ -108,7 +111,7 @@ module.exports = class RAPIDClient {
let response = Errors.toRapidResponse(error);
let bodyString = _trySerializeResponse(response);
let xrayString = XRayError.formatted(error);
this.nativeClient.error(id, bodyString, xrayString);
this.nativeClient.error(encodeURIComponent(id), bodyString, xrayString);
callback();
}

Expand Down
2 changes: 1 addition & 1 deletion test/unit/ErrorsTest.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
'use strict';

require('should');
let Errors = require('../../src/Errors');
let Errors = require('lambda-runtime/Errors');

describe('Formatted Error Logging', () => {
it('should fall back to a minimal error format when an exception occurs', () => {
Expand Down
1 change: 1 addition & 0 deletions test/unit/FakeTelemetryTarget.js
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ module.exports = class FakeTelemetryTarget {
if (lineLength === 0) {
return '';
}

let lineBytes = Buffer.alloc(lineLength);
let actualLineSize = fs.readSync(
this.readTarget,
Expand Down
2 changes: 1 addition & 1 deletion test/unit/InvokeContextTest.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
require('should');
const sleep = require('util').promisify(setTimeout);

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

describe('Getting remaining invoke time', () => {
it('should reduce by at least elapsed time', async () => {
Expand Down
4 changes: 2 additions & 2 deletions test/unit/LogPatchTest.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
const util = require('util');

let should = require('should');
let LogPatch = require('../../src/LogPatch');
let Errors = require('../../src/Errors');
let LogPatch = require('lambda-runtime/LogPatch');
let Errors = require('lambda-runtime/Errors');
let assert = require('assert');

let {
Expand Down
117 changes: 117 additions & 0 deletions test/unit/RAPIDClientTest.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
/**
* Copyright 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*/

'use strict';

require('should');

let RAPIDClient = require('lambda-runtime/RAPIDClient.js');
let runtimeErrors = require('lambda-runtime/Errors.js');

/**
* Stub request object.
* Provides no-op definitions of the request functions used by the rapid client.
*/
const noOpRequest = Object.freeze({
/* no op, return itself to allow continuations/chaining */
on: () => noOpRequest,
/* no op, return itself to allow continuations/chaining */
end: () => noOpRequest,
});

class StubHttp {
constructor() {
this.lastUsedOptions = {};
this.Agent = class FakeAgent {};
}

request(options, _callback) {
this.lastUsedOptions = options;
return noOpRequest;
}
}

class NoOpNativeHttp {
constructor() {
this.lastRequestId = '';
this.lastErrorRequestId = '';
}

done(requestId) {
this.lastRequestId = requestId;
}

error(requestId) {
this.lastErrorRequestId = requestId;
}
}

class EvilError extends Error {
get name() {
throw 'gotcha';
}
}

const EXPECTED_ERROR_HEADER = 'Lambda-Runtime-Function-Error-Type';

describe('building error requests with the RAPIDClient', () => {
let stubHttp = new StubHttp();
let client = new RAPIDClient('notUsed:1337', stubHttp, new NoOpNativeHttp());

let errors = [
[new Error('generic failure'), 'Error'],
[new runtimeErrors.ImportModuleError(), 'Runtime.ImportModuleError'],
[new runtimeErrors.HandlerNotFound(), 'Runtime.HandlerNotFound'],
[new runtimeErrors.MalformedHandlerName(), 'Runtime.MalformedHandlerName'],
[new runtimeErrors.UserCodeSyntaxError(), 'Runtime.UserCodeSyntaxError'],
[{ data: 'some random object' }, 'object'],
[new EvilError(), 'handled'],
];

describe('the error header in postInitError', () => {
errors.forEach(([error, name]) => {
it(`should be ${name} for ${error.constructor.name}`, () => {
client.postInitError(error);
stubHttp.lastUsedOptions.should.have
.property('headers')
.have.property(EXPECTED_ERROR_HEADER, name);
});
});
});
});

describe('invalid request id works', () => {
const nativeClient = new NoOpNativeHttp();
const client = new RAPIDClient('notUsed:1337', undefined, nativeClient);

[
// Encoding expected:
['#', '%23'],
['%', '%25'],
['/', '%2F'],
['?', '%3F'],
['\x7F', '%7F'],
["<script>alert('1')</script>", "%3Cscript%3Ealert('1')%3C%2Fscript%3E"],
['⚡', '%E2%9A%A1'],

// No encoding:
['.', '.'],
['..', '..'],
['a', 'a'],
[
'59b22c65-fa81-47fb-a6dc-23028a63566f',
'59b22c65-fa81-47fb-a6dc-23028a63566f',
],
].forEach(([requestId, expected]) => {
it(`postInvocationResponse should encode requestId: '${requestId}'`, () => {
client.postInvocationResponse({}, requestId, () => {});
nativeClient.lastRequestId.should.be.equal(expected);
});

it(`postInvocationError should encode requestId: '${requestId}'`, () => {
client.postInvocationError(new Error(), requestId, () => {});
nativeClient.lastErrorRequestId.should.be.equal(expected);
});
});
});
8 changes: 4 additions & 4 deletions test/unit/ResponseStreamTest.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ const ServerMock = require('mock-http-server');
const {
createResponseStream,
tryCallFail,
} = require('../../src/ResponseStream.js');
const { HttpResponseStream } = require('../../src/HttpResponseStream.js');
const { InvalidStreamingOperation } = require('../../src/Errors.js');
const { verbose, vverbose, vvverbose } = require('../../src/VerboseLog').logger(
} = require('lambda-runtime/ResponseStream.js');
const { HttpResponseStream } = require('lambda-runtime/HttpResponseStream.js');
const { InvalidStreamingOperation } = require('lambda-runtime/Errors.js');
const { verbose, vverbose, vvverbose } = require('lambda-runtime/VerboseLog').logger(
'TEST',
);
const Throttle = require('throttle');
Expand Down
4 changes: 2 additions & 2 deletions test/unit/StreamingContextTest.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@
'use strict';

require('should');
const StreamingContext = require('../../src/StreamingContext.js');
const StreamingContext = require('lambda-runtime/StreamingContext.js');
const { PassThrough } = require('stream');
const BeforeExitListener = require('../../src/BeforeExitListener.js');
const BeforeExitListener = require('lambda-runtime/BeforeExitListener.js');

class MockRapidClient {
constructor() {
Expand Down
4 changes: 2 additions & 2 deletions test/unit/UserFunctionTest.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ const {
HandlerNotFound,
ImportModuleError,
MalformedHandlerName,
} = require('../../src/Errors.js');
const UserFunction = require('../../src/UserFunction.js');
} = require('lambda-runtime/Errors.js');
const UserFunction = require('lambda-runtime/UserFunction.js');

const TEST_ROOT = path.join(__dirname, '../');
const HANDLERS_ROOT = path.join(TEST_ROOT, 'handlers');
Expand Down

0 comments on commit cba61c6

Please sign in to comment.