HTTP/REST 是目前最主流的前后端接口设计,在测试、线上环境里截获 HTTP 请求可以有效诊断接口请求错误、响应性能、网络环境对页面响应的影响、用户路径分析等。本文从截获 APP HTTP 请求开始讲起,介绍如何分析错误的 HTTP 响应,以及分析响应的性能,穿插实战中找到的问题案例。系列文章将一步步介绍精细化分析。
APP 端和服务器端的交互是非常重要的一个环节,能够获取交互信息可以有效提高测试分析的效率,找到服务端的问题、定位 APP 代码问题,甚至用来分析用户路径、流量等,是提高产品质量的重要工具。
说到 HTTP 抓包,第一个想到的肯定是代理软件,例如 Charles 和 Fiddler,搜一下,教程很多,用过之后缺点非常多:
理想的情况应该是在 APP 内部有一个机制截获 APP 的 HTTP 请求响应,log 下来,然后能够方便获取做进一步分析。这样一来 APP 自然就会 log 自己的流量;同时因为是 APP 内部,所以 https 不再是问题;数据可以灵活获取、分析;而且可以根据不同配置(测试环境、用户环境)采取不同的 log 策略,用来不同的分析(测试时全 log,分析错误、性能、稳定性等;用户环境打点、报告问题请求响应)。
然而只有有些 HTTP 库,比如 okhttp,提供了比较方便的 log 接口(Interceptor),很多 http 请求都没有这样的 log 接口。另外 APP 自己会用很多三方库里也会发出请求,三方库代码不好改。最后,监控点如果需要人工维护,成本也很高。
Appetizer 通过插桩技术解决这以上所有问题,插桩是对 APK 的字节码(Dex 文件)进行修改,根据规则截获 APP 以及三方库内发送的 HTTP 请求,并 log HTTP 请求和响应,相比系统代理,插桩截获有以下优势:
APP 中常用的 HTTP 请求库有 HttpURLConnection(HUC), Apache HTTP Client, OkHttp, Retrofit, Volley,还有一些 “快速开发框架”(实际内部封装了这些库)。HUC 是java.net.HttpURLConnection
提供的功能,是 Java 自带的标准库,通常是第三方库用来请求 HTTP 的(这样就可以只依赖标准库)。Apache 是曾经 Android 推荐的 HTTP 库,从 5.0 开始已经不再推荐,不过有一些老的三方库、控件库依然使用着 Apache。OkHttp 是 Square 公司开发的方便好用的现代的 HTTP 请求库,目前使用非常流行,Retrofit 也是依赖于 OkHttp,OkHttp 有 2.x(包名com.squareup.okhttp
), 3.x(包名okhttp3.*
)两个版本,从 5.0+ 开始,标准库的 HUC 实际内部实现是 okhttp。最后 Volley 是 Google 官方出的一套小而巧的异步请求库,
其实是个框架,底层可以对接其他 HTTP 库,官方提供了 Apache 和 HttpURLConnection 的支持。此外国内外有很多 “所谓的 HTTP 库” 其实都是对这几个库的封装。
下图总结了一下常见的库的依赖关系,还有一些比较上层的封装库和开发框架:
Appetizer 本身通过字节码插桩的方式来截获 APP 调用 HTTP 库的 API。Appetizer 截获最底层的 HUC, Apache 和 okhttp 2.x 和 3.x,从而支持所有依赖于这几个底层库的框架,如果有我们没有提到的 HTTP 库,请在评论区告知。
对于 okhttp 截获比较简单,因为 okhttp 提供了天然的Interceptor接口,Appetizer 如果探测到 APP 使用了 okhttp,则在程序开头注册一个自己的 interceptor。而 Apache HTTP Client 和 HUC 就没有这么方便的机制,于是 Appetizer 会扫描整个 APP 的代码,为每处调用加上 log 代码,例如:
URLConnection conn = new URL("https://appetizer.io").openConnection();
// Appetizer搜索到openConnection 的调用,在这里插入log代码,记录conn的内容(请求头、响应内容等)
conn.getResponseCode();
插桩技术相比较于需要接入 SDK 的方式(比如 Facebook Stetho 和一些监控库)有以下优点:
有了截获的 HTTP 数据后,我们可以开始分析,精细化 HTTP 分析对于开发、测试以至于产品分析都有非常重要的用处。我们会分几期介绍基于 HTTP 数据的各种分析。
最常见的是对错误返回码系列的分析,4xx 是 client 端的问题,5xx 是 server 端的问题。
对于 4xx 的返回码(如下图),Appetizer 会给出完整的请求 URL(包括请求参数),以及请求 Header(见折叠),通常这类情况是由于构造的的 URL 有问题,例如参数问题,或者是一些字符串转码方面的问题。Appetizer 1.2.0 推出后,报告页面右上角会多一个按钮,用于导出这个问题请求的 cURL 命令行,可以轻松导入 postman 进行进一步测试
如果返回码是 5xx,那就是后端同学的锅了,同样,可以产生了 cURL 命令行发给后端同学去反复测试这个接口。
如何接入 Appetizer 插桩服务?
使用我们的图形化界面客户端,参考:https://testerhome.com/topics/8162
使用 Python 客户端(考虑使用脚本插桩或者接入 Jenkins): https://github.com/appetizerio/insights.py
除了 GET,POST,其他 HTTP 方法,比如 PUT 支持吗?
我们支持 HTTP 协议支持的所有 HTTP 方法,包括更为小众的 PATCH (例如 zhihu 用了),HEAD 等。所以只要是 REST API 都可以用 Appetizer 来截获分析。
APP 可以加固么?
不可以,加固后的 APP 不能修改,开发包可以考虑只混淆
APP 可以混淆么?
插桩本身支持混淆后的 APP,但是有些截获功能是通过寻找目标库的 API 实现的,混淆后有些库的 API 被去除或者改名会导致部分库无法识别,无法截获。目前如果使用了 okhttp,最好保证库不会被混淆,可以在 proguard 规则中增加:
-keepattributes Signature
-keepattributes Annotation
-keep class okhttp3.** { *; }
-keep interface okhttp3.** { *; }
-dontwarn okhttp3.**
-dontwarn okio.**
ReactNative/Weex 等 H5 技术开发的 APP 支持么?
经测试是完全支持的。ReactNative 的 HTTP 库用的是 OkHttp,在我们支持范围内。
Kotlin 开发的 APP 支持么?
经测试是完全支持的。
webview 控件里的页面请求能截获吗?
正在开发中
插桩截获功能对性能的影响怎样?
插桩代码的运行代价是记录 HTTP 请求,Appetizer 只记录请求响应的 header 内容,并不记录 body,从而性能影响很小,记录单个请求耗时最多 1ms。同时,因为 HTTP 请求都是在 APP 的子线程上完成的,所以不会造成任何主线程卡顿
使用了一些会发出 HTTP 请求的 C++ 库 (.so) 能截获吗?
目前不支持,插桩仅限 Java 代码。例如使用了 Lua 解释器、C++ 的视频/音频播放器目前都暂不支持,如果希望支持,请在评论区留言
支持手游吗?
同上。手游的 HTTP 是从 C++ 库里发出的
支持非 HTTP 协议,例如 RTP 之类的
暂不支持
iOS 呢?
参考光哥的文