iOS QQ 内置浏览器 AJAX 失效的两个原因及解决方法

文章目录

今天总算解决了网站的两个 BUG。症状表现都是,用 iOS 上的手机 QQ 内置浏览器,AJAX(通过 jQuery 或是原生 XMLHttpRequest 使用)出现错误。如果不用手机 QQ 内置浏览器,用 iOS 系统自带 Safari,甚至微信内置浏览器,都能正常工作。一个是 GET 请求,另一个是带数据(请求体)的 POST 请求。

后来研究发现,理论上在别的环境下也可能有错,尤其是旧版本的 Safari 等,这是后话了。我们进入正题。

HTTPS 下 GET 请求失败

我的网站大多是强制使用 HTTPS 的,HTTP 请求都会 301 重定向到 HTTPS 页面去。我在 iOS QQ 发了一个网址,点击进去,发现通过 XMLHttpRequest 的 GET 请求失败。通过 status 属性得到状态号是 0,就好像 XMLHttpRequest 没有初始化或连接突然中断一样。换用 jQuery 提交 GET 请求,问题依旧。

在我这里,解决方法是如下的 HTML 代码:

<script>
if(window.location.protocol == 'http:') window.location.protocol = 'https:';
</script>

实际上是用 JavaScript 代码判断协议,如果发现是 HTTP 协议,就跳转(重定向)到 HTTPS 协议相应的网址。

那我访问的明明是 https:,怎么会成了 http:?仔细观察,我发现自己在 iOS QQ 发的网址没有带协议(或者带 http:// 道理也是一样的),则默认使用 HTTP 协议。尽管可以发现实际访问了 HTTPS 的文档,但浏览器仍然认为是 HTTP 协议:如果用 JS 获取相应的标识,你会得到 HTTP;或者也可以获取相对路径资源的 src,也得到 HTTP;使用 QQ 内置浏览器的复制网址功能,得到的也是 HTTP 的网址。这又是怎么回事?最终我做了个实验发现,是 HSTS 的缘故。推测在 iOS QQ 内置浏览器中,HSTS 并不像其他浏览器那样产生重定向,那么通过 http: 网址进入时,虽然实际访问了 https: 的文档和资源,URL 模式(scheme)却仍然保留着为 http。而 http: 和 https: 的资源不属于同一个源(Origin),就会产生意料之外的跨源问题(参见同源策略),导致 AJAX 请求失败。

带数据 POST 请求失败

我另一个网站遇到的问题是,在 iOS QQ 内置浏览器上,POST 请求失败。相关的程序报告了 xhr.responseText:undefined, type:error 错误,错误码 -1001。于是我自己构造了相关请求,发现把 data 设为 null 时,可以提交;一旦我传上 POST 参数,就会出现前述错误。

这个问题困扰了我好几天。网上有说是因为 XMLHttpRequest 没有初始化,说要定时,延迟发送请求;试过,似乎不起作用。后来我看人家同样的网站内容,却没有问题。那会不会是 HTTPS 的问题呢?可我再找了一个别人的 HTTPS 网站,也是正常的。我就再分析 HTTP 请求头(Request Header)和响应头(Response Header)。虽然我想是没有跨源,但我加上了跨源的相关头也无济于事。突然发现我的响应头名称是小写的,而非一般的单词首字母大写的写法,查询得知这是 SPDY 和 HTTP/2 的规范特性。于是使用“iOS QQ post http2”的关键词查询,看到一篇眼熟的文章:《谈谈Nginx 的HTTP/2 POST Bug》。就是它让我解决了这个问题。(后来翻了 Nginx 的错误日志,发现每次 AJAX 请求失败都有 worker process 23415 exited on signal 11 的提示)

文章的问题与我遇到的相似,尽管它提到的受影响范围更大一些,我只专注于观察 iOS QQ 内置浏览器去了。说原因是 Nginx 的实现与 HTTP/2 规范不符,不能正确接收 POST 请求的 DATA 帧,而浏览器可能不会重试,最终导致 POST 请求失败。至于我,早就没在用那旧版本的 Nginx,想了一下发觉是打了 CloudFlare 提供的兼容 SPDY 的补丁,才造成此问题。

我的解决方案就是重新编译安装 Nginx,不要那个鬼补丁了。如果你也遇到相似的问题,还是先看一下自己的 Nginx 版本是不是在 1.10.2 以下,要不要升级版本。新版本已经默认能正确接受 DATA 帧。

以上就是我遇到的问题和解决方法。造成相关问题的原因可能不止一种,如果你从别处过来却没能解决问题,还请继续探寻,或者在下方留言交流。

全部为采集文章,文中的 联系方式 均不是 本人 的!

发表评论