解决Node.js request报错HPE_INVALID_CONSTANT
我们爬虫的下载器基于Node.js,网页请求使用 request/request 库,今天反馈一个页面 http://www. sxicpa.org.cn/view.php? nav=32&page=1 无法下载,经过调试发现request在请求时报错,错误如下:
{
Error: Parse Error
at Socket.socketOnData (_http_client.js:442:20)
at Socket.emit (events.js:189:13)
at addChunk (_stream_readable.js:284:12)
at readableAddChunk (_stream_readable.js:265:11)
at Socket.Readable.push (_stream_readable.js:220:10)
at TCP.onStreamRead (internal/stream_base_commons.js:94:17)
bytesParsed: 676,
code: 'HPE_INVALID_CONSTANT'
}
网上google了一下,HPE_INVALID_CONSTANT这个错误码表示HTTP协议错误,基本原因都是WEB服务器返回的响应不太符合HTTP规范。
使用curl -v http://www. sxicpa.org.cn/view.php? nav=32&page=1 验证了一下,输出如下:
* TCP_NODELAY set
* Connected to www.sxicpa.org.cn (202.99.223.101) port 80 (#0)
> GET /view.php?nav=32 HTTP/1.1
> Host: www.sxicpa.org.cn
> User-Agent: curl/7.54.0
> Accept: */*
< HTTP/1.1 200 OK
< Server: nginx
< Date: Wed, 27 Mar 2019 03:54:14 GMT
< Content-Type: text/html; charset=utf-8
< Transfer-Encoding: chunked
< Connection: keep-alive
< Vary: Accept-Encoding
<html xmlns="http://www.w3.org/1999/xhtml" lang="gb2312">
</body>
* Leftovers after chunking: 996 bytes
* Connection #0 to host www.sxicpa.org.cn left intact
</html>
与正常网页对比了一下,发现最后多了一行“Leftovers after chunking: 996 bytes”,意思是chunk内容和chunk长度不匹配,最后的</html>应该就是多出来的字符。
在浏览器中,这个页面可以正常显示,猜测应该是做了兼容性处理,因此我们也考虑在Node.js中做兼容性处理。
request库基于Node.js标准库http和https封装,如果在解析响应头和响应内容时出现上述错误,我们可以忽略该错误,然后把已经解析完的响应内容作为最终结果,有结果总比没结果好,具体实现代码如下:
const request = require('request')
function sendRequest(requestOptions){
return new Promise((resolve, reject) => {
let lastError
let lastResponse
const reqWrapper = request(requestOptions, function(error, response, body) {
// request内部使用该标记控制只调用一次回调函数,
// 因为我们需要在出错后仍然能拿到解析完的响应内容,所以将此标记重置为false
this._callbackCalled = false
if (error) {
if (!lastResponse) {
// 在服务器返回响应前出错,报错并结束处理
reject(error)
} else {
// 在解析响应过程中报错,暂时保存该错误
lastError = error
} else {
// 响应解析完毕
if(!lastError || lastError.code === 'HPE_INVALID_CONSTANT'){
// 如果之前没有报错,或者是兼容性错误,则返回成功
resolve({response, body})
} else {
// 其他错误,则报错
reject(lastError)
if (lastError) {
// 在创建请求时出错,报错并结束处理
reject(lastError)
return
reqWrapper.on('response', res => {
// 收到服务器响应
lastResponse = res
reqWrapper.on('abort', () => {
// 被中断,报错并结束处理
if (lastError) {
reject(lastError)
} else {