我分析了 chromium 源代码以获得一些见解。我的 C++ 新手能力只能达到一定水平。
在此代码块中检测客户端或平台的用户代理(文件:useragent.cc)。
std::string BuildUserAgentFromProduct(const std::string& product) {
std::string os_info;
base::StringAppendF(
&os_info,
"%s%s",
getUserAgentPlatform().c_str(),
BuildOSCpuInfo().c_str());
return BuildUserAgentFromOSAndProduct(os_info, product);
}
您可以在代码块中看到 BuildOSCpuInfo(),它负责添加基于平台的操作系统相关信息,可以在此处找到
std::string android_build_codename = base::SysInfo::GetAndroidBuildCodename();
std::string android_device_name = base::SysInfo::HardwareModelName(); // this line in particular adds the ONEPLUS A3003
但是这个函数(BuildUserAgentFromProduct())并不直接在负责发送http请求的net模块中使用。
当我研究 net(http) 模块的代码时,我发现他们正在获取 useragent* 并通过一系列字符串操作和空白修剪功能对其进行处理。 http_request_headers.cc 中的 AddHeadersFromString() 是将 useragent 字符串添加到请求标头的接口。
注意*:但我认为标头数据不是来自 useragent.cc,因为我无法在任何地方找到对此函数的调用。但我在这里可能是错的。
**我相信这是 OSInfo 值被修改的地方。任何未识别的空白字符或原始格式错误的空白字符都可能给出此结果。
注意**:我无法测试并证明上面的说法,因为 Chromium 中使用的 String 有一个名为 StringPiece 的包装器( *wrapper 只是我正在使用的一个术语,从技术上讲它可以是以不同的方式调用,我不知道。)。而且我不知道如何用 C++ 为 StringPiece 编写代码。
但下面给出了一个非常简单的例子来说明它是如何出错的。
int main()
{
std::string s = " ONEPLUS\rA3003\rBuild/OPR6.170623.013";
std::string delimiter = "\r\n"; //this is the delimeter used in chromium source code.
std::string token = s.substr(0, s.find(delimiter,0));
std::cout << token << std::endl;
return 0;
}
https://www.onlinegdb.com/SkTrbFJDz https://www.onlinegdb.com/SkTrbFJDz
之所以初始的用户代理字符串有值,而后续的http请求没有值,与android中chrome应用程序的架构有关。当页面最初加载时,这些值实际上是由 chrome 应用程序(一个非常大的 java 代码库,但我认为我们需要查看的核心文件是 LoadUrlParams.java)设置的,它具有发送 http 请求的不同实现(此处useragent 不是由相同的 net(http) 模块修剪的,而是由 Java 实现处理的),这种情况仅在第一次加载期间发生。但任何其他后续调用都使用浏览器的 net(http) 模块。
文件参考链接:
我只是包含这个答案来给出可能发生问题的原因之一。如果我有更多时间,我会看看是否可以以某种方式进行测试并证明这一点。
最后要注意的是,这个答案没有给出任何解决问题的解决方案。它只是给出了原因。
[Update]
一种非常廉价的技巧是查看 navigator.useragent 是否具有 oneplus 值并在请求上设置 ajax 标头并发送它。这将覆盖浏览器添加用户代理标头的机制。
XMLHttpRequest.setRequestHeader(header, value)