0%

Golang http malformed HTTP response 探究

问题

处理请求返回内容的时候, 遇到 error malformed HTTP response "<html>" 异常信息

探究

根据目前的测试情况,得出的结论是:

  1. 在请求返回结果为 html 页面时,proxy 需要带上 http:// 前缀。
  2. 在请求返回结果为 api 接口时,proxy 需要去掉 http:// 前缀。

待后续多次验证。

经验证: 以上结论 错误

请求网址 返回结果类型 proxy格式 proxy是否带http 请求结果
http://ip38.com/ html http://27.203.219.181:8060 ok,已验证
http://ip38.com/ html 27.203.219.181:8060 请求正常,代理未生效,已验证
https://www.cnblogs.com/ html http://27.203.219.181:8060 error,malformed HTTP response "<html>",已验证
https://www.cnblogs.com/ html 27.203.219.181:8060 ok,已验证
https://p.3.cn/prices/mgets json http://27.203.219.181:8060 error,malformed HTTP response "<html>",已验证
https://p.3.cn/prices/mgets json 27.203.219.181:8060 ok,已验证

即:

  • 请求 http://ip38.com/ 时,需要使用 proxy格式为 http://27.203.219.181:8060 才能访问成功。
  • 请求 https://www.cnblogs.com/ 时,需要使用proxy格式为 27.203.219.181:8060 才能访问成功。
  • 请求 https://p.3.cn/prices/mgets 时,需要使用proxy格式为 27.203.219.181:8060 才能访问成功。

我的一种解法

项目中在测试使用代理的代码段:

代码中的 req.XReq() 是对 imroc/req: a golang http request library for humans 的封装。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
    // 使用了代理ip

//为防止使用代理ip请求超时太长,设置默认超时时间为30s
if _, ok := rh["time-out"]; !ok {
// 请求头中 time-out 参数不存在
rh["time-out"] = 30
}

is_malformedHttp := false

HandleProxy:
for _, ag := range rAgents {
// 表示在使用proxy ip时,是否遇到返回错误信息为 malformed HTTP response "<html>" 的情况

if is_malformedHttp {
// 将代理ip的 http:// 去掉
ag = GetHostAndPort(ag)
fmt.Printf("[info] remove http for proxy ip: %s .\n", ag)
}

rh["proxy"] = ag
fmt.Printf("[info] use proxy to request: %s .\n", ag)

resData, errMsg = req.XReq(rUrl, rh, rp)
if errMsg != nil {
fmt.Printf("[info] the proxy result error: %s .\n", errMsg.Error())

if !is_malformedHttp {
// 当 is_malf 为 false时,才对 mailformed http response 处理
if strings.Contains(errMsg.Error(), "malformed HTTP response") {
fmt.Printf("[error] the proxy ip format error: %s .\n", errMsg.Error())
is_malformedHttp = true
time.Sleep(1 * time.Second)
goto HandleProxy
}
}
continue
} else {
fmt.Printf("[info] the proxy ok %s .\n", ag)
break
}
}

实现逻辑是:

当传入的代理ip格式为 http://27.203.219.181:8060 ,先发起请求,如果请求报错,判断错误信息是否包含 malformed HTTP response ,如果包含,则认为是代理ip的格式错误,需要对代理ip处理。

通过 goto 语句,直接跳回到 for 循环之外,对代理ip做格式化处理,截取到 host:port 的格式再次去请求。

为了防止第二次请求时再遇到 malformed HTTP response 错误的话,又会 gotofor 循环之外,所以这里加一个标记 is_malformedHttp ,标明代理ip出现格式问题时是否被处理过。

后经测试验证,这种方法无效。


验证结果:

  1. 当代理格式为 ["http://192.168.1.100:8080","192.168.1.120:8080"] 时,proxy1第一次请求异常,经“处理”后请求仍异常,proxy2第一次请求异常。请求结束。
  2. 当代理格式为 ["192.168.1.100:8080","192.168.1.120:8080"] 时,proxy1第一次请求正常。请求结束。

猜测

为什么在代理Ip经处理后的请求,仍然会出错呢?

个人怀疑是 defer 导致的。

defer语句调用一个函数,这个函数执行会推迟,直到外围的函数返回,或者外围函数运行到最后,或者相应的goroutine panic.

在请求之后,需要 deferclose 请求。而 defer 又有延后性,也就导致了在 for 循环中的多次请求之间都是有关联的,第一次的请求对象并没有被关闭就继续进行了后续的请求,直到函数结束,请求才被关闭掉。

也就导致了,即使第二次请求时的代理ip去掉了 http:// ,请求仍然会失败。


之后如何处理

  1. 先用 http://27.203.219.181:8060 的代理格式去请求,当遇到 malformed HTTP response 错误时,直接抛出异常,等待下次请求。
  2. 在第二次请求时,将代理ip的格式更改为 27.203.219.181:8060 传入,再去执行请求操作。

相关

推荐

推荐两个请求类:


以上,仅做记录。由于暂未查询到问题原因,后续持续更新。

如有疑问或需要技术讨论,请留言或发邮件到 service@itfanr.cc