最近做一个服务转发功能,但是对端服务可能没有开启或已经宕机,因此在转发前需要判断此次转发的服务是否是有效的(或者转发后能分辨此次响应是否正常),如果是不存在的服务下次转发需要屏蔽掉。
封装了一个request方法:
const request = require('request');
async function GetResponse(url) {
return new Promise((resolve, reject) => {
request({
url: url,
method: "GET",
encoding: null, // setting null can avoid some encoding trouble
headers: {
//'Accept-Encoding': 'gzip, deflate'
}
}, function (error, response, body) {
if (body){
resolve(response);
} else if (error){
reject(error);
}
});
// .on('socket', function (sok){
// console.log(sok);
// sok.on('data', function (fin){
// console.log(fin);
// });
// });
});
}
此方法使用了promise方式,可达到等待的目的,如果请求还未返回则进行阻塞,直到请求url有响应了(即使是error)才会继续后面的逻辑。调用时需要在一个async方法中调用:
let result = await GetResponse("http://www.baidu.com");
如果请求成功,则callback中的error为null,body内容为响应体(和response['body']一致,因此建议返回response对象,自行获取响应体,期间还可以判断响应状态等信息);
如果请求失败,callback中的error则为错误对象,message中可以获取具体错误信息。
================================================================================================
问题:最近测试时,使用了两个域名,一个http://www.baidu.com,一个自己编的并且用浏览器访问确实是不存在的,却发现错误的域名竟然也能正确返回,并且状态码也是200,statusMessage也是OK。
先了解一下一次http请求的基本流程:
- 根据域名和 DNS 解析到服务器的IP地址 (DNS + CDN)
- 通过ARP协议获得IP地址对应的物理机器的MAC地址
- 浏览器对服务器发起 TCP 3 次握手
- 建立 TCP 连接后发起 HTTP 请求报文
- 服务器响应 HTTP 请求,将响应报文返回给浏览器
- 短连接情况下,请求结束则通过 TCP 四次挥手关闭连接,长连接在没有访问服务器的若干时间后,进行连接的关闭
- 浏览器得到响应信息中的 HTML 代码, 并请求 HTML 代码中的资源(如js、css、图片等)
- 浏览器对页面进行渲染并呈现给用户
问题出在第一步域名解析上,不同地区的运营商会有一层代理,去做DNS解析,如果解析不成功,则会返回一个解析失败的空白页,此页面就是response中的body,以天津为例:我使用上述方法请求http://www.nickDaDa.com,callback中error是null,而且获取到了body是一个byte数组。我们在转发router中使用另一种方法转发:
req.pipe(request({ url: 'http://www.nickDaDa.com' })).pipe(res); //request is external package, req&res is router param
使用postman做测试,就会发现,它返回的是一个页面:
<html>
<script language=javascript type="text/javascript">
window.location.replace("http://tjdnserror2.wo.com.cn:8080?HOST="
+ location.hostname + "&R="
+ location.pathname + "&" + location.search.substr(location.search.indexOf("\?")+1));
</script>
<noscript>
<meta http-equiv="refresh" content="0;URL=http://tjdnserror2.wo.com.cn:8080">
</noscript>
<head>
<title>Redirect</title>
</head>
<body bgcolor="white" text="black"></body>
</html>
因此,转发时只能是用IP:PORT的方式进行测试(也可以先使用DNS自己解析域名,获取到对应IP),这样就能跳过DNS解析这一步,如果服务不可达,则直接返回error对象,进行错误处理即可。