Refactor HTTP connection requests

This commit is contained in:
Zaid Abdulla committed 2020-12-04 15:33:35 -08:00
1 parent fa1e40e370
commit bded263adf
3 files changed
+53 -15

No files matched your search

+30 -15
View File
@@ -62,7 +62,6 @@ async function extractAuth(
function httpGet( function httpGet(
options: http.RequestOptions, options: http.RequestOptions,
timeout,
_debug: boolean, _debug: boolean,
deviceId: string deviceId: string
): Promise<{ statusCode: number; headers: http.IncomingHttpHeaders }> { ): Promise<{ statusCode: number; headers: http.IncomingHttpHeaders }> {
@@ -77,15 +76,12 @@ function httpGet(
} }
}) })
.on("error", (err) => { .on("error", (err) => {
req.abort(); req.destroy();
reject(new Error("Device is offline")); reject(err);
if (_debug) debug.outgoingHttpRequestError(req, deviceId, options, err); if (_debug) debug.outgoingHttpRequestError(req, deviceId, options, err);
}) })
.on("socket", (socket) => { .on("timeout", () => {
socket.setTimeout(timeout); req.destroy();
socket.on("timeout", () => {
req.abort();
});
}); });
}); });
} }
@@ -105,6 +101,7 @@ export async function httpConnectionRequest(
options.agent = new http.Agent({ options.agent = new http.Agent({
maxSockets: 1, maxSockets: 1,
keepAlive: true, keepAlive: true,
timeout: timeout,
}); });
let authHeader: Record<string, string>; let authHeader: Record<string, string>;
@@ -146,24 +143,42 @@ export async function httpConnectionRequest(
} }
} }
let res = await httpGet(opts, timeout, _debug, deviceId); let res: { statusCode: number; headers: http.IncomingHttpHeaders };
try {
res = await httpGet(opts, _debug, deviceId);
} catch (err) {
// Workaround for some devices unexpectedly closing the connection // Workaround for some devices unexpectedly closing the connection
if (res.statusCode === 0 && authHeader) if (authHeader) {
res = await httpGet(opts, timeout, _debug, deviceId); try {
if (res.statusCode === 0) return "Device is offline"; res = await httpGet(opts, _debug, deviceId);
} catch (err) {
return `Connection request error: ${err.message}`;
}
}
if (err["code"] === "ECONNRESET" || err["code"] === "ECONNREFUSED")
return "Device is offline";
return `Connection request error: ${err.message}`;
}
if (res.statusCode === 200 || res.statusCode === 204) return ""; if (res.statusCode === 200 || res.statusCode === 204) return "";
// When a Connection Request is received for the Virtual CWMP Device and the Proxied
// Device is offline the CPE Proxier MUST respond with an HTTP 503 failure
if (res.statusCode === 503) return "Device is offline";
if (res.statusCode === 401 && res.headers["www-authenticate"]) { if (res.statusCode === 401 && res.headers["www-authenticate"]) {
authHeader = auth.parseWwwAuthenticateHeader( authHeader = auth.parseWwwAuthenticateHeader(
res.headers["www-authenticate"] res.headers["www-authenticate"]
); );
[username, password, authExp] = await extractAuth(authExp, false); [username, password, authExp] = await extractAuth(authExp, false);
} else { } else {
return `Unexpected response code from device: ${res.statusCode}`; return `Connection request error: Unexpected status code ${res.statusCode}`;
} }
} }
return "Incorrect connection request credentials";
return "Connection request error: Incorrect connection request credentials";
} }
export async function udpConnectionRequest( export async function udpConnectionRequest(
+15
View File
@@ -394,12 +394,19 @@ export function listener(
.insertTasks(task) .insertTasks(task)
.then(async () => { .then(async () => {
const lastInform = Date.now(); const lastInform = Date.now();
const socketTimeout: number = request.socket["timeout"];
// Disable socket timeout while waiting for session
if (socketTimeout) request.socket.setTimeout(0);
const notInSession = await apiFunctions.awaitSessionEnd( const notInSession = await apiFunctions.awaitSessionEnd(
deviceId, deviceId,
30000 30000
); );
await cache.del(`${deviceId}_tasks_faults_operations`); await cache.del(`${deviceId}_tasks_faults_operations`);
if (urlParts.query.connection_request == null) { if (urlParts.query.connection_request == null) {
if (socketTimeout) request.socket.setTimeout(socketTimeout);
response.writeHead(202, { response.writeHead(202, {
"Content-Type": "application/json", "Content-Type": "application/json",
}); });
@@ -408,6 +415,7 @@ export function listener(
} }
if (!notInSession) { if (!notInSession) {
if (socketTimeout) request.socket.setTimeout(socketTimeout);
response.writeHead(202, "Task queued but not processed", { response.writeHead(202, "Task queued but not processed", {
"Content-Type": "application/json", "Content-Type": "application/json",
}); });
@@ -418,6 +426,7 @@ export function listener(
const status = await apiFunctions.connectionRequest(deviceId); const status = await apiFunctions.connectionRequest(deviceId);
if (status) { if (status) {
if (socketTimeout) request.socket.setTimeout(socketTimeout);
response.writeHead(202, status, { response.writeHead(202, status, {
"Content-Type": "application/json", "Content-Type": "application/json",
}); });
@@ -436,6 +445,7 @@ export function listener(
onlineThreshold onlineThreshold
); );
if (!sessionStarted) { if (!sessionStarted) {
if (socketTimeout) request.socket.setTimeout(socketTimeout);
response.writeHead(202, "Task queued but not processed", { response.writeHead(202, "Task queued but not processed", {
"Content-Type": "application/json", "Content-Type": "application/json",
}); });
@@ -448,6 +458,7 @@ export function listener(
120000 120000
); );
if (!sessionEnded) { if (!sessionEnded) {
if (socketTimeout) request.socket.setTimeout(socketTimeout);
response.writeHead(202, "Task queued but not processed", { response.writeHead(202, "Task queued but not processed", {
"Content-Type": "application/json", "Content-Type": "application/json",
}); });
@@ -467,6 +478,10 @@ export function listener(
); );
const [t, f] = await Promise.all([prom1, prom2]); const [t, f] = await Promise.all([prom1, prom2]);
// Restore socket timeout
if (socketTimeout) request.socket.setTimeout(socketTimeout);
if (f) { if (f) {
response.writeHead(202, "Task faulted", { response.writeHead(202, "Task faulted", {
"Content-Type": "application/json", "Content-Type": "application/json",
+8
View File
@@ -531,6 +531,11 @@ router.post("/devices/:id/tasks", async (ctx) => {
} }
) as number; ) as number;
const socketTimeout: number = ctx.socket["timeout"];
// Disable socket timeout while waiting for postTasks()
if (socketTimeout) ctx.socket.setTimeout(0);
const res = await apiFunctions.postTasks( const res = await apiFunctions.postTasks(
ctx.params.id, ctx.params.id,
ctx.request.body, ctx.request.body,
@@ -538,6 +543,9 @@ router.post("/devices/:id/tasks", async (ctx) => {
device device
); );
// Restore socket timeout
if (socketTimeout) ctx.socket.setTimeout(socketTimeout);
log.tasks = res.tasks.map((t) => t._id).join(","); log.tasks = res.tasks.map((t) => t._id).join(",");
logger.accessInfo(log); logger.accessInfo(log);