-
Notifications
You must be signed in to change notification settings - Fork 6
/
local-debugger.js
124 lines (107 loc) · 4.4 KB
/
local-debugger.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
/*
* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
* http://www.apache.org/licenses/LICENSE-2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
const net = require('net');
const fs = require('fs');
const localDebugger = net.createServer();
const HTTP_HEADER_DELIMITER = '\r\n';
const HTTP_BODY_DELIMITER = '\r\n\r\n';
const DEFAULT_HANDLER_NAME = 'handler';
const HOST_NAME = 'localhost';
const DEFAULT_PORT = 0;
/**
* Resolves the skill invoker class dependency from the user provided
* skill entry file.
*/
// eslint-disable-next-line import/no-dynamic-require
const skillInvoker = require(getAndValidateSkillInvokerFile());
const portNumber = getAndValidatePortNumber();
const lambdaHandlerName = getLambdaHandlerName();
/**
* Starts listening on the port for incoming skill requests.
*/
localDebugger.listen(portNumber, HOST_NAME, () => {
console.log(`Starting server on port: ${localDebugger.address().port}.`);
});
/**
* For a new incoming skill request a new socket connection is established.
* From the data received on the socket the request body is extracted, parsed into
* JSON and passed to the skill invoker's lambda handler.
* The response from the lambda handler is parsed as a HTTP 200 message format as specified
* here - https://developer.amazon.com/docs/custom-skills/request-and-response-json-reference.html#http-header-1
* The response is written onto the socket connection.
*/
localDebugger.on('connection', (socket) => {
console.log(`Connection from: ${socket.remoteAddress}:${socket.remotePort}`);
socket.on('data', (data) => {
const body = JSON.parse(data.toString().split(HTTP_BODY_DELIMITER).pop());
console.log(`Request envelope: ${JSON.stringify(body)}`);
skillInvoker[lambdaHandlerName](body, null, (_invokeErr, response) => {
response = JSON.stringify(response);
console.log(`Response envelope: ${response}`);
socket.write(`HTTP/1.1 200 OK${HTTP_HEADER_DELIMITER}Content-Type: application/json;charset=UTF-8${HTTP_HEADER_DELIMITER}Content-Length: ${response.length}${HTTP_BODY_DELIMITER}${response}`);
});
});
});
/**
* Validates user specified port number is in legal range [0, 65535].
* Defaults to 0.
*/
function getAndValidatePortNumber() {
const portNumberArgument = Number(getArgument('portNumber', DEFAULT_PORT));
if (!Number.isInteger(portNumberArgument)) {
throw new Error(`Port number has to be an integer - ${portNumberArgument}.`);
}
if (portNumberArgument < 0 || portNumberArgument > 65535) {
throw new Error(`Port out of legal range: ${portNumberArgument}. The port number should be in the range [0, 65535]`);
}
if (portNumberArgument === 0) {
console.log('The TCP server will listen on a port that is free.'
+ 'Check logs to find out what port number is being used');
}
return portNumberArgument;
}
/**
* Gets the lambda handler name.
* Defaults to "handler".
*/
function getLambdaHandlerName() {
return getArgument('lambdaHandler', DEFAULT_HANDLER_NAME);
}
/**
* Validates that the skill entry file exists on the path specified.
* This is a required field.
*/
// eslint-disable-next-line consistent-return
function getAndValidateSkillInvokerFile() {
const fileNameArgument = getArgument('skillEntryFile');
if (!fs.existsSync(fileNameArgument)) {
throw new Error(`File not found: ${fileNameArgument}`);
}
return fileNameArgument;
}
/**
* Helper function to fetch the value for a given argument
* @param {argumentName} argumentName name of the argument for which the value needs to be fetched
* @param {defaultValue} defaultValue default value of the argument that is returned if the value doesn't exist
*/
function getArgument(argumentName, defaultValue) {
const index = process.argv.indexOf(`--${argumentName}`);
if (index === -1 || typeof process.argv[index + 1] === 'undefined') {
if (defaultValue === undefined) {
throw new Error(`Required argument - ${argumentName} not provided.`);
} else {
return defaultValue;
}
}
return process.argv[index + 1];
}