no more streaming
This commit is contained in:
@@ -15,14 +15,16 @@ app.use(express.json({
|
|||||||
// Request timeout middleware
|
// Request timeout middleware
|
||||||
app.use((req, res, next) => {
|
app.use((req, res, next) => {
|
||||||
// Set request timeout
|
// Set request timeout
|
||||||
req.setTimeout(config.requestTimeout, () => {
|
const timeoutHandle = setTimeout(() => {
|
||||||
logger.error({
|
logger.error({
|
||||||
method: req.method,
|
method: req.method,
|
||||||
url: req.url,
|
url: req.url,
|
||||||
timeout: config.requestTimeout,
|
timeout: config.requestTimeout,
|
||||||
|
headersSent: res.headersSent,
|
||||||
|
finished: res.finished,
|
||||||
}, 'Request timeout');
|
}, 'Request timeout');
|
||||||
|
|
||||||
if (!res.headersSent) {
|
if (!res.headersSent && !res.finished) {
|
||||||
res.status(504).json({
|
res.status(504).json({
|
||||||
jsonrpc: '2.0',
|
jsonrpc: '2.0',
|
||||||
error: {
|
error: {
|
||||||
@@ -32,6 +34,15 @@ app.use((req, res, next) => {
|
|||||||
id: req.body?.id,
|
id: req.body?.id,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}, config.requestTimeout);
|
||||||
|
|
||||||
|
// Clear timeout when response finishes
|
||||||
|
res.on('finish', () => {
|
||||||
|
clearTimeout(timeoutHandle);
|
||||||
|
});
|
||||||
|
|
||||||
|
res.on('close', () => {
|
||||||
|
clearTimeout(timeoutHandle);
|
||||||
});
|
});
|
||||||
|
|
||||||
next();
|
next();
|
||||||
|
|||||||
@@ -172,26 +172,45 @@ class RPCProxy {
|
|||||||
let clientCloseReason = null;
|
let clientCloseReason = null;
|
||||||
let responseCompleted = false;
|
let responseCompleted = false;
|
||||||
|
|
||||||
req.on('close', () => {
|
// Use 'aborted' event which is more reliable for detecting client disconnects
|
||||||
|
req.on('aborted', () => {
|
||||||
if (!clientClosed) {
|
if (!clientClosed) {
|
||||||
|
clientClosed = true;
|
||||||
|
clientCloseReason = 'request_aborted';
|
||||||
|
const elapsedMs = Date.now() - startTime;
|
||||||
|
|
||||||
|
logger.warn({
|
||||||
|
requestId,
|
||||||
|
reason: clientCloseReason,
|
||||||
|
headers: req.headers,
|
||||||
|
userAgent: req.headers['user-agent'],
|
||||||
|
contentLength: req.headers['content-length'],
|
||||||
|
method: requestBody.method,
|
||||||
|
elapsedMs,
|
||||||
|
responseCompleted,
|
||||||
|
}, 'Client aborted request');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// The 'close' event can fire for various reasons, not just client disconnect
|
||||||
|
// Only consider it a client disconnect if the response hasn't been completed
|
||||||
|
req.on('close', () => {
|
||||||
|
// Only log as client disconnect if response wasn't completed
|
||||||
|
if (!clientClosed && !responseCompleted && !res.headersSent) {
|
||||||
clientClosed = true;
|
clientClosed = true;
|
||||||
clientCloseReason = 'connection_closed';
|
clientCloseReason = 'connection_closed';
|
||||||
const elapsedMs = Date.now() - startTime;
|
const elapsedMs = Date.now() - startTime;
|
||||||
|
|
||||||
// Immediate close (0-1ms) with no response is likely a client timeout or abort
|
// Only log as error if it happens very quickly (< 100ms)
|
||||||
if (elapsedMs <= 1 && !responseCompleted) {
|
if (elapsedMs < 100) {
|
||||||
logger.error({
|
logger.debug({
|
||||||
requestId,
|
requestId,
|
||||||
reason: clientCloseReason,
|
reason: clientCloseReason,
|
||||||
headers: req.headers,
|
|
||||||
userAgent: req.headers['user-agent'],
|
|
||||||
contentLength: req.headers['content-length'],
|
|
||||||
method: requestBody.method,
|
method: requestBody.method,
|
||||||
elapsedMs,
|
elapsedMs,
|
||||||
responseCompleted,
|
responseCompleted,
|
||||||
xForwardedFor: req.headers['x-forwarded-for'],
|
headersSent: res.headersSent,
|
||||||
xRealIp: req.headers['x-real-ip'],
|
}, 'Request closed early (may be normal behavior)');
|
||||||
}, 'Client aborted connection immediately - likely timeout or network issue');
|
|
||||||
} else {
|
} else {
|
||||||
logger.warn({
|
logger.warn({
|
||||||
requestId,
|
requestId,
|
||||||
@@ -225,17 +244,16 @@ class RPCProxy {
|
|||||||
|
|
||||||
// Also track response close events
|
// Also track response close events
|
||||||
res.on('close', () => {
|
res.on('close', () => {
|
||||||
if (!clientClosed) {
|
if (!responseCompleted) {
|
||||||
clientClosed = true;
|
|
||||||
clientCloseReason = 'response_closed';
|
|
||||||
logger.warn({
|
logger.warn({
|
||||||
requestId,
|
requestId,
|
||||||
reason: clientCloseReason,
|
reason: 'response_closed',
|
||||||
finished: res.finished,
|
finished: res.finished,
|
||||||
headersSent: res.headersSent,
|
headersSent: res.headersSent,
|
||||||
method: requestBody.method,
|
method: requestBody.method,
|
||||||
elapsedMs: Date.now() - startTime,
|
elapsedMs: Date.now() - startTime,
|
||||||
}, 'Response connection closed');
|
clientClosed,
|
||||||
|
}, 'Response connection closed before completion');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -337,13 +355,8 @@ class RPCProxy {
|
|||||||
clientCloseReason: getClientCloseReason(),
|
clientCloseReason: getClientCloseReason(),
|
||||||
elapsedBeforeRequest: Date.now() - startTime,
|
elapsedBeforeRequest: Date.now() - startTime,
|
||||||
}, 'Client closed before upstream request could be made');
|
}, 'Client closed before upstream request could be made');
|
||||||
return {
|
// Don't return early - still try to make the request in case our detection was wrong
|
||||||
statusCode: 0,
|
// The actual response sending will handle the client closed state
|
||||||
data: '',
|
|
||||||
size: 0,
|
|
||||||
latency: Date.now() - startTime,
|
|
||||||
contentEncoding: null,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.debug({
|
logger.debug({
|
||||||
|
|||||||
Reference in New Issue
Block a user