-
Notifications
You must be signed in to change notification settings - Fork 48
/
local-debugger.js
135 lines (115 loc) · 4.92 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
125
126
127
128
129
130
131
132
133
134
135
/*
* 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.
*/
/* ## DEPRECATION NOTICE
This script has been deprecated and is no longer supported.
Please use the [ASK Toolkit for VS Code]
(https://marketplace.visualstudio.com/items?itemName=ask-toolkit.alexa-skills-kit-toolkit),
which provides a more end-to-end integration with Visual Studio Code. If you
use another editor/IDE, please check out the [ASK SDK Local Debug package at npm]
(https://www.npmjs.com/package/ask-sdk-local-debug).
*/
const net = require('net');
const fs = require('fs');
const localDebugger = net.createServer();
const httpHeaderDelimeter = '\r\n';
const httpBodyDelimeter = '\r\n\r\n';
const defaultHandlerName = 'handler';
const host = 'localhost';
const defaultPort = 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, () => {
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(httpBodyDelimeter).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${httpHeaderDelimeter}Content-Type: application/json;charset=UTF-8${httpHeaderDelimeter}Content-Length: ${response.length}${httpBodyDelimeter}${response}`);
});
});
});
/**
* Validates user specified port number is in legal range [0, 65535].
* Defaults to 0.
*/
function getAndValidatePortNumber() {
const portNumberArgument = Number(getArgument('portNumber', defaultPort));
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', defaultHandlerName);
}
/**
* 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];
}