Ignore session cookie on Inform
Some devices preserve cookies set in a previous session and pass them on to the next session. The presence of a session cookie in the Inform request causes an unnecessary DB query and, in some edge cases, an Invalid Session error.
This commit is contained in:
1 parent
bc2d258f9c
commit
ca83704958
2 files changed
+111
-142
No files matched your search
+109
-139
@@ -878,27 +878,6 @@ async function sendAcsRequest(
|
|||||||
return writeResponse(sessionContext, res);
|
return writeResponse(sessionContext, res);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getSession(
|
|
||||||
connection: Socket,
|
|
||||||
sessionId: string
|
|
||||||
): Promise<SessionContext> {
|
|
||||||
let sessionContext = currentSessions.get(connection);
|
|
||||||
if (sessionContext) {
|
|
||||||
currentSessions.delete(connection);
|
|
||||||
return sessionContext;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!sessionId) return null;
|
|
||||||
|
|
||||||
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
||||||
|
|
||||||
const sessionContextString = await cache.pop(`session_${sessionId}`);
|
|
||||||
if (!sessionContextString) return null;
|
|
||||||
sessionContext = await session.deserialize(sessionContextString);
|
|
||||||
connection.setTimeout(sessionContext.timeout * 1000);
|
|
||||||
return sessionContext;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only needed to prevent tree shaking from removing the remoteAddress
|
// Only needed to prevent tree shaking from removing the remoteAddress
|
||||||
// workaround in onConnection function.
|
// workaround in onConnection function.
|
||||||
const remoteAddressWorkaround = new WeakMap<Socket, string>();
|
const remoteAddressWorkaround = new WeakMap<Socket, string>();
|
||||||
@@ -1045,13 +1024,13 @@ async function reportBadState(sessionContext: SessionContext): Promise<void> {
|
|||||||
sessionContext: sessionContext,
|
sessionContext: sessionContext,
|
||||||
});
|
});
|
||||||
const httpResponse = sessionContext.httpResponse;
|
const httpResponse = sessionContext.httpResponse;
|
||||||
currentSessions.delete(httpResponse.socket);
|
|
||||||
const body = "Bad session state";
|
const body = "Bad session state";
|
||||||
httpResponse.setHeader("Content-Length", Buffer.byteLength(body));
|
httpResponse.setHeader("Content-Length", Buffer.byteLength(body));
|
||||||
httpResponse.writeHead(400, { Connection: "close" });
|
httpResponse.writeHead(400, { Connection: "close" });
|
||||||
if (sessionContext.debug)
|
if (sessionContext.debug)
|
||||||
debug.outgoingHttpResponse(httpResponse, sessionContext.deviceId, body);
|
debug.outgoingHttpResponse(httpResponse, sessionContext.deviceId, body);
|
||||||
httpResponse.end(body);
|
httpResponse.end(body);
|
||||||
|
return endSession(sessionContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function responseUnauthorized(
|
async function responseUnauthorized(
|
||||||
@@ -1323,6 +1302,48 @@ export function listener(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function clientError(
|
||||||
|
httpRequest: IncomingMessage,
|
||||||
|
httpResponse: ServerResponse,
|
||||||
|
sessionContext: SessionContext,
|
||||||
|
body: string,
|
||||||
|
msg: string
|
||||||
|
): Promise<void> {
|
||||||
|
let debugEnabled = false;
|
||||||
|
let deviceId: string = null;
|
||||||
|
|
||||||
|
if (sessionContext) {
|
||||||
|
debugEnabled = sessionContext.debug;
|
||||||
|
deviceId = sessionContext.deviceId;
|
||||||
|
} else {
|
||||||
|
const cacheSnapshot = await localCache.getCurrentSnapshot();
|
||||||
|
debugEnabled = !!localCache.getConfig(
|
||||||
|
cacheSnapshot,
|
||||||
|
"cwmp.debug",
|
||||||
|
{
|
||||||
|
remoteAddress: getRequestOrigin(httpRequest).remoteAddress,
|
||||||
|
},
|
||||||
|
Date.now(),
|
||||||
|
(e) => {
|
||||||
|
if (Array.isArray(e) && e[0] === "FUNC" && e[1] === "REMOTE_ADDRESS")
|
||||||
|
return getRequestOrigin(httpRequest).remoteAddress;
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
httpResponse.setHeader("Content-Length", Buffer.byteLength(msg));
|
||||||
|
httpResponse.writeHead(400, { Connection: "close" });
|
||||||
|
|
||||||
|
if (debugEnabled) {
|
||||||
|
debug.incomingHttpRequest(httpRequest, deviceId, body);
|
||||||
|
debug.outgoingHttpResponse(httpResponse, deviceId, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
httpResponse.end(msg);
|
||||||
|
if (sessionContext) await endSession(sessionContext);
|
||||||
|
}
|
||||||
|
|
||||||
function decodeString(buffer: Buffer, charset: string): string {
|
function decodeString(buffer: Buffer, charset: string): string {
|
||||||
try {
|
try {
|
||||||
return buffer.toString(charset as BufferEncoding);
|
return buffer.toString(charset as BufferEncoding);
|
||||||
@@ -1410,15 +1431,14 @@ async function listenerAsync(
|
|||||||
// Request aborted
|
// Request aborted
|
||||||
if (!body) return;
|
if (!body) return;
|
||||||
|
|
||||||
const newConnection = !currentSessions.has(httpRequest.socket);
|
let sessionContext = currentSessions.get(httpRequest.socket);
|
||||||
|
|
||||||
const sessionContext = await getSession(httpRequest.socket, sessionId);
|
|
||||||
|
|
||||||
if (sessionContext) {
|
if (sessionContext) {
|
||||||
|
currentSessions.delete(httpRequest.socket);
|
||||||
sessionContext.httpRequest = httpRequest;
|
sessionContext.httpRequest = httpRequest;
|
||||||
sessionContext.httpResponse = httpResponse;
|
sessionContext.httpResponse = httpResponse;
|
||||||
if (
|
if (
|
||||||
(newConnection && sessionContext.sessionId !== sessionId) ||
|
sessionContext.sessionId !== sessionId ||
|
||||||
sessionContext.lastActivity + sessionContext.timeout * 1000 < Date.now()
|
sessionContext.lastActivity + sessionContext.timeout * 1000 < Date.now()
|
||||||
) {
|
) {
|
||||||
logger.accessError({
|
logger.accessError({
|
||||||
@@ -1426,29 +1446,14 @@ async function listenerAsync(
|
|||||||
sessionContext: sessionContext,
|
sessionContext: sessionContext,
|
||||||
});
|
});
|
||||||
|
|
||||||
const _body = "Invalid session";
|
return clientError(
|
||||||
httpResponse.setHeader("Content-Length", Buffer.byteLength(_body));
|
httpRequest,
|
||||||
httpResponse.writeHead(400, { Connection: "close" });
|
|
||||||
if (sessionContext.debug) {
|
|
||||||
debug.outgoingHttpResponse(
|
|
||||||
httpResponse,
|
httpResponse,
|
||||||
sessionContext.deviceId,
|
sessionContext,
|
||||||
_body
|
body.toString(),
|
||||||
|
"Invalid session"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
httpResponse.end(_body);
|
|
||||||
await endSession(sessionContext);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (newConnection && sessionContext.authState !== 1)
|
|
||||||
sessionContext.authState = 0;
|
|
||||||
} else if (stats.concurrentRequests > MAX_CONCURRENT_REQUESTS) {
|
|
||||||
// Check again just in case device included old session ID
|
|
||||||
// from the previous session
|
|
||||||
httpResponse.writeHead(503, { "Retry-after": 60, Connection: "close" });
|
|
||||||
httpResponse.end("503 Service Unavailable");
|
|
||||||
stats.droppedRequests += 1;
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let charset: string;
|
let charset: string;
|
||||||
@@ -1468,6 +1473,16 @@ async function listenerAsync(
|
|||||||
const bodyStr = decodeString(body, charset);
|
const bodyStr = decodeString(body, charset);
|
||||||
|
|
||||||
if (bodyStr == null) {
|
if (bodyStr == null) {
|
||||||
|
if (!sessionContext && sessionId) {
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, 100));
|
||||||
|
const sessionContextString = await cache.pop(`session_${sessionId}`);
|
||||||
|
if (sessionContextString) {
|
||||||
|
sessionContext = await session.deserialize(sessionContextString);
|
||||||
|
sessionContext.httpRequest = httpRequest;
|
||||||
|
sessionContext.httpResponse = httpResponse;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const msg = `Unknown encoding '${charset}'`;
|
const msg = `Unknown encoding '${charset}'`;
|
||||||
logger.accessError({
|
logger.accessError({
|
||||||
message: "XML parse error",
|
message: "XML parse error",
|
||||||
@@ -1477,97 +1492,58 @@ async function listenerAsync(
|
|||||||
httpResponse: httpResponse,
|
httpResponse: httpResponse,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
httpResponse.setHeader("Content-Length", Buffer.byteLength(msg));
|
return clientError(
|
||||||
httpResponse.writeHead(400, { Connection: "close" });
|
|
||||||
if (sessionContext) {
|
|
||||||
if (sessionContext.debug) {
|
|
||||||
debug.incomingHttpRequest(
|
|
||||||
httpRequest,
|
httpRequest,
|
||||||
sessionContext.deviceId,
|
httpResponse,
|
||||||
body.toString()
|
sessionContext,
|
||||||
|
body.toString(),
|
||||||
|
msg
|
||||||
);
|
);
|
||||||
debug.outgoingHttpResponse(httpResponse, sessionContext.deviceId, msg);
|
|
||||||
}
|
|
||||||
await endSession(sessionContext);
|
|
||||||
} else {
|
|
||||||
const cacheSnapshot = await localCache.getCurrentSnapshot();
|
|
||||||
const d = !!localCache.getConfig(
|
|
||||||
cacheSnapshot,
|
|
||||||
"cwmp.debug",
|
|
||||||
{
|
|
||||||
remoteAddress: getRequestOrigin(httpRequest).remoteAddress,
|
|
||||||
},
|
|
||||||
Date.now(),
|
|
||||||
(e) => {
|
|
||||||
if (Array.isArray(e) && e[0] === "FUNC" && e[1] === "REMOTE_ADDRESS")
|
|
||||||
return getRequestOrigin(httpRequest).remoteAddress;
|
|
||||||
return e;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
if (d) {
|
|
||||||
debug.incomingHttpRequest(httpRequest, null, body.toString());
|
|
||||||
debug.outgoingHttpResponse(httpResponse, null, msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
httpResponse.end(msg);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const parseWarnings = [];
|
const parseWarnings = [];
|
||||||
let rpc: SoapMessage;
|
let rpc: SoapMessage;
|
||||||
try {
|
try {
|
||||||
rpc = soap.request(
|
rpc = soap.request(bodyStr, parseWarnings);
|
||||||
bodyStr,
|
} catch (err) {
|
||||||
sessionContext ? sessionContext.cwmpVersion : null,
|
if (!sessionContext && sessionId) {
|
||||||
parseWarnings
|
await new Promise((resolve) => setTimeout(resolve, 100));
|
||||||
);
|
const sessionContextString = await cache.pop(`session_${sessionId}`);
|
||||||
} catch (error) {
|
if (sessionContextString) {
|
||||||
|
sessionContext = await session.deserialize(sessionContextString);
|
||||||
|
sessionContext.httpRequest = httpRequest;
|
||||||
|
sessionContext.httpResponse = httpResponse;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
logger.accessError({
|
logger.accessError({
|
||||||
message: "XML parse error",
|
message: "XML parse error",
|
||||||
parseError: error.message.trim(),
|
parseError: err.message,
|
||||||
sessionContext: sessionContext || {
|
sessionContext: sessionContext || {
|
||||||
httpRequest: httpRequest,
|
httpRequest: httpRequest,
|
||||||
httpResponse: httpResponse,
|
httpResponse: httpResponse,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
httpResponse.setHeader("Content-Length", Buffer.byteLength(error.message));
|
|
||||||
httpResponse.writeHead(400, { Connection: "close" });
|
return clientError(
|
||||||
if (sessionContext) {
|
|
||||||
if (sessionContext.debug) {
|
|
||||||
debug.incomingHttpRequest(
|
|
||||||
httpRequest,
|
httpRequest,
|
||||||
sessionContext.deviceId,
|
|
||||||
bodyStr
|
|
||||||
);
|
|
||||||
debug.outgoingHttpResponse(
|
|
||||||
httpResponse,
|
httpResponse,
|
||||||
sessionContext.deviceId,
|
sessionContext,
|
||||||
error.message
|
bodyStr,
|
||||||
|
err.message
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
await endSession(sessionContext);
|
|
||||||
} else {
|
if (!sessionContext && sessionId && rpc.cpeRequest?.name !== "Inform") {
|
||||||
const cacheSnapshot = await localCache.getCurrentSnapshot();
|
await new Promise((resolve) => setTimeout(resolve, 100));
|
||||||
const d = !!localCache.getConfig(
|
const sessionContextString = await cache.pop(`session_${sessionId}`);
|
||||||
cacheSnapshot,
|
if (sessionContextString) {
|
||||||
"cwmp.debug",
|
sessionContext = await session.deserialize(sessionContextString);
|
||||||
{
|
sessionContext.httpRequest = httpRequest;
|
||||||
remoteAddress: getRequestOrigin(httpRequest).remoteAddress,
|
sessionContext.httpResponse = httpResponse;
|
||||||
},
|
httpRequest.socket.setTimeout(sessionContext.timeout * 1000);
|
||||||
Date.now(),
|
if (sessionContext.authState !== 1) sessionContext.authState = 0;
|
||||||
(e) => {
|
|
||||||
if (Array.isArray(e) && e[0] === "FUNC" && e[1] === "REMOTE_ADDRESS")
|
|
||||||
return getRequestOrigin(httpRequest).remoteAddress;
|
|
||||||
return e;
|
|
||||||
}
|
}
|
||||||
);
|
|
||||||
if (d) {
|
|
||||||
debug.incomingHttpRequest(httpRequest, null, bodyStr);
|
|
||||||
debug.outgoingHttpResponse(httpResponse, null, error.message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
httpResponse.end(error.message);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sessionContext)
|
if (sessionContext)
|
||||||
@@ -1576,33 +1552,27 @@ async function listenerAsync(
|
|||||||
if (!(rpc.cpeRequest && rpc.cpeRequest.name === "Inform")) {
|
if (!(rpc.cpeRequest && rpc.cpeRequest.name === "Inform")) {
|
||||||
logger.accessError({
|
logger.accessError({
|
||||||
message: "Invalid session",
|
message: "Invalid session",
|
||||||
sessionContext: sessionContext || {
|
sessionContext: {
|
||||||
httpRequest: httpRequest,
|
httpRequest: httpRequest,
|
||||||
httpResponse: httpResponse,
|
httpResponse: httpResponse,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const _body = "Invalid session";
|
|
||||||
httpResponse.setHeader("Content-Length", Buffer.byteLength(_body));
|
return clientError(
|
||||||
httpResponse.writeHead(400, { Connection: "close" });
|
httpRequest,
|
||||||
const cacheSnapshot = await localCache.getCurrentSnapshot();
|
httpResponse,
|
||||||
const d = !!localCache.getConfig(
|
null,
|
||||||
cacheSnapshot,
|
bodyStr,
|
||||||
"cwmp.debug",
|
"Invalid session"
|
||||||
{
|
|
||||||
remoteAddress: getRequestOrigin(httpRequest).remoteAddress,
|
|
||||||
},
|
|
||||||
Date.now(),
|
|
||||||
(e) => {
|
|
||||||
if (Array.isArray(e) && e[0] === "FUNC" && e[1] === "REMOTE_ADDRESS")
|
|
||||||
return getRequestOrigin(httpRequest).remoteAddress;
|
|
||||||
return e;
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
if (d) {
|
|
||||||
debug.incomingHttpRequest(httpRequest, null, bodyStr);
|
|
||||||
debug.outgoingHttpResponse(httpResponse, null, _body);
|
|
||||||
}
|
}
|
||||||
httpResponse.end(_body);
|
|
||||||
|
if (stats.concurrentRequests > MAX_CONCURRENT_REQUESTS) {
|
||||||
|
// Check again just in case device included old session ID
|
||||||
|
// from the previous session
|
||||||
|
httpResponse.writeHead(503, { "Retry-after": 60, Connection: "close" });
|
||||||
|
httpResponse.end("503 Service Unavailable");
|
||||||
|
stats.droppedRequests += 1;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+2
-3
@@ -827,14 +827,13 @@ function fault(xml: Element): CpeFault {
|
|||||||
|
|
||||||
export function request(
|
export function request(
|
||||||
body: string,
|
body: string,
|
||||||
cwmpVersion: string,
|
|
||||||
warn: Record<string, unknown>[]
|
warn: Record<string, unknown>[]
|
||||||
): SoapMessage {
|
): SoapMessage {
|
||||||
warnings = warn;
|
warnings = warn;
|
||||||
|
|
||||||
const rpc = {
|
const rpc = {
|
||||||
id: null,
|
id: null,
|
||||||
cwmpVersion: cwmpVersion,
|
cwmpVersion: null,
|
||||||
sessionTimeout: null,
|
sessionTimeout: null,
|
||||||
cpeRequest: null,
|
cpeRequest: null,
|
||||||
cpeFault: null,
|
cpeFault: null,
|
||||||
@@ -877,7 +876,7 @@ export function request(
|
|||||||
|
|
||||||
const methodElement = bodyElement.children[0];
|
const methodElement = bodyElement.children[0];
|
||||||
|
|
||||||
if (!rpc.cwmpVersion && methodElement.localName !== "Fault") {
|
if (methodElement.localName === "Inform") {
|
||||||
let namespace, namespaceHref;
|
let namespace, namespaceHref;
|
||||||
for (const e of [methodElement, bodyElement, envelope]) {
|
for (const e of [methodElement, bodyElement, envelope]) {
|
||||||
namespace = namespace || e.namespace;
|
namespace = namespace || e.namespace;
|
||||||
|
|||||||
Reference in new issue
Block a user