添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

解决Node.js request报错HPE_INVALID_CONSTANT

我们爬虫的下载器基于Node.js,网页请求使用 request/request 库,今天反馈一个页面 sxicpa.org.cn/view.php? 无法下载,经过调试发现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 sxicpa.org.cn/view.php? 验证了一下,输出如下:

* 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 {