Sumo Logic中出现ERR_HTTP_HEADERS_SENT错误,请求排查原因
[ERR_HTTP_HEADERS_SENT] 错误根源定位与修复
错误核心本质
ERR_HTTP_HEADERS_SENT 是因为HTTP响应已经发送给客户端后,代码仍尝试修改响应头(比如设置Cookie)或再次发送响应。结合你提供的三段代码,问题根源在于回调函数被重复调用,导致后续操作多次触发响应头修改。
具体代码问题分析
1. 重试逻辑导致重复回调
这段代码是触发重复回调的关键:
awsService.sendEvent(req, payload, function(err, resp) { if (err) { if (tries <= 3) { tries++; return sendEvent(req, payload, tries, cb); } } return cb(); });
问题点:当err存在且tries <=3时,代码递归调用sendEvent,但当前回调函数的后续代码return cb()依然会执行。也就是说,无论重试是否触发,cb()都会被调用一次,加上重试后sendEvent内部的回调,最终导致cb被多次调用。
2. 最外层回调重复执行响应操作
最外层回调包含res.cookie操作,一旦cb被多次调用,这段代码就会重复执行:
sendEvent(req, payload, 1, function (err, response) { if (err) { req.mtShout.error('[ANALYTICS ERROR]', err); } if (!_.get(req, ['currentClient','oneTrust', 'enabled']) || ( _.get(req, ['optanonConsent', 'groups']) && req.optanonConsent.groups.indexOf(req.currentClient.oneTrust.categories.performance + ':1') > -1 ) ) { res.cookie(COOKIE_NAME, payload.session_id, { maxAge: config.server.raCookieMaxAge, secure: secureCookieFlag, httpOnly: true }); } req.raSession = payload.session_id; });
问题点:如果res.cookie被执行两次,而第一次执行时响应已经通过res.send/res.json等方式发送给客户端,第二次执行res.cookie就会触发ERR_HTTP_HEADERS_SENT错误。
3. 底层sendEvent函数的潜在隐患
let sendEvent = function (req, payload, cb) { if (process.env.NODE_LOCAL === 'true' && !process.env.DEBUG) { req.mtShout.debug('No QUEUE Submission because this is local ENV'); return cb(null, true); } sqs.getQueueUrl({QueueName: awsConfig.queueName}, function(e, d){ if (e) { req.mtShout.error("Error when putting RA message onto SQS: ", e); e = new Error("Failed to put RA event onto SQS queue"); cb(e, null); // 缺少return,若后续代码变更可能导致cb被多次调用 } else { sqs.sendMessage({ MessageBody: JSON.stringify(payload), QueueUrl: d.QueueUrl }, function(err, data) { if(err) { req.mtShout.error("Error when putting RA message onto SQS: ", err); err = new Error("Failed to put RA event onto SQS queue"); cb(err, null); // 同样缺少return } else { req.mtShout.debug('Successfully queued ' + payload.event_name + ' RA event', data); cb(null, true); } }); } }); };
问题点:错误分支调用cb后没有加return,当前逻辑下不会触发问题,但后续代码迭代时可能引发cb多次调用的风险。
修复方案
修复重试逻辑的重复回调
调整重试逻辑,确保cb只被调用一次:
awsService.sendEvent(req, payload, function(err, resp) { if (err) { if (tries <= 3) { tries++; sendEvent(req, payload, tries, cb); return; // 终止当前函数,避免执行后续的cb() } } cb(err); // 统一在最后调用cb,错误时传入err,无错时传入undefined });
补充底层sendEvent的return语句
在错误分支调用cb后添加return,保证逻辑严谨性:
// 修复sqs.getQueueUrl错误分支 if (e) { req.mtShout.error("Error when putting RA message onto SQS: ", e); e = new Error("Failed to put RA event onto SQS queue"); return cb(e, null); } // 修复sqs.sendMessage错误分支 if(err) { req.mtShout.error("Error when putting RA message onto SQS: ", err); err = new Error("Failed to put RA event onto SQS queue"); return cb(err, null); }
验证响应发送时机
确保执行res.cookie的代码在响应发送(res.send/res.json等)之前完成,避免在响应已发送后修改响应头。
内容的提问来源于stack exchange,提问作者HMech
相关产品推荐
相关产品推荐

