我分析了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()
但是这个函数(BuildUserAgentFromProduct())并没有直接在负责发送http请求的net模块中使用。
当我调查了net(http)模块的代码后,我发现他们通过一系列的字符串操作和去除空格功能获取了useragent*并进行处理。http_request_headers.cc中的AddHeadersFromString()是将useragent字符串添加到请求头的接口。
注:*但我认为头部数据不来自useragent.cc,因为我无法在任何地方找到对该函数的调用。但我可能在这里错了。
**我认为这是修改OSInfo值的地方。任何未被识别或格式错误的空格字符都会导致此结果。
注:**我无法测试上述语句并证明它,因为Chromium中使用的字符串有一个名为StringPiece的包装器(*wrapper仅是我使用的一个术语,技术上可以以不同的方式调用),而我不知道如何编写StringPiece的c++代码。
但下面给出了一个非常简单的可能出错的示例。
int main()
{
std::string s = " ONEPLUS\rA3003\rBuild/OPR6.170623.013";
std::string delimiter = "\r\n";
std::string token = s.substr(0, s.find(delimiter,0));
std::cout << token << std::endl;
return 0;
}
https://www.onlinegdb.com/SkTrbFJDz
初始用户代理字符串具有值,而后续的http请求没有该值的原因在于Chrome应用程序在Android中的架构。当页面最初加载时,实际上是由Chrome应用程序(一个非常大的Java代码库,但我认为我们需要查看的核心文件是LoadUrlParams.java)设置这些值,它具有不同的发送HTTP请求的实现(这里用户代理并不是由相同的net(http)模块修剪而是由Java实现来处理),这仅发生在第一次加载期间。但任何其他后续调用都使用浏览器的net(http)模块。
文件参考链接:
https://cs.chromium.org/chromium/src/content/common/user_agent.cc?sq=package:chromium&dr=CSs&l=80
https://cs.chromium.org/chromium/src/net/http/http_request_headers.cc?type=cs&q=AddHeadersFromString&l=155
https://cs.chromium.org/chromium/src/content/public/android/java/src/org/chromium/content_public/browser/LoadUrlParams.java?q=createLoadDataParamsWithBaseUrl&dr=CSs
我只是提供一个可能存在问题的原因,如果我有更多时间,我会尝试运行测试并证明这一点。最后需要注意的是,此答案没有解决问题的解决方案。它只是说明了原因。
[更新]
一个非常便宜的技巧是检查navigator.useragent是否具有oneplus值,并设置请求的ajax标头并发送它。这将覆盖浏览器添加用户代理标头的机制。
XMLHttpRequest.setRequestHeader(header, value)