問題描述
從某天起,在大羣裏有 KOL 用戶反饋:在微信客戶端裏打開商品詳情頁會出現頁面尺寸放大的現象:html
出現的頻次不肯定,不能穩定復現,且在北京、長沙幾個地域均出現了該狀況。前端
技術前提
出現這種現象的前提,是由於前端配置了頁面的 viewport meta 標籤,並使用 JS 動態根據設備尺寸和像素比來縮放頁面,以適配不一樣屏幕的手機設備。在下文中,該段技術前提代碼統稱爲『配置』。web
排查步驟
排查的難度在於:沒法穩定復現,因此只能不斷排查觸發條件。chrome
第一種假設:配置失敗安全
- 首先想到的是,這段配置並不在全部業務邏輯的最開始,是不是因爲這以前的業務邏輯報錯致使配置失效?因而將這段配置提高到業務邏輯最前面。失敗。
- 再次觀察問題描述,發現出現放大的狀況,頁面並無標題,是不是因爲設置標題出了問題(由於在微信裏設置頁面標題必須用 iframe 的 trick 的方式)?因而去掉了在微信裏設置標題的業務代碼。失敗。
- 經歷了兩次業務邏輯排查失敗,考慮從更高層面分析,是不是業務代碼形成的影響?因爲頁面中大多數核心邏輯的錯誤都在 Promise 的流程中被 catch 住,並且頁面級的 sentry 日誌收集方案由於外網不支持,並無保留上報的錯誤日誌,最終可以分析的數據幾乎爲零。所以搭建了個最精簡的頁面,只包含配置代碼和頁面樣式。依舊失敗。
- 沒有了業務代碼,是不是配置自己有問題?因而在代碼里加上了 try catch,發現並無報錯。失敗。
這幾回失敗沒有找到問題的緣由,但至少驗證了:不是業務代碼的錯誤影響;不是自己配置語法錯誤的影響。那麼,頗有可能這個大的假設『配置失敗』是錯誤的,至少配置的語法沒有錯誤,由於均檢測到頁面 viewport 的值配置上了。因而開始另外一種假設(實際上也許並無接下來的假設,由於當時準備放棄選擇另外一種配置方案,然而巨大的遷移成本讓咱們望而怯步,幸虧沒有這麼作)。微信
第二種假設:配置成功,但沒有生效工具
- 沒有生效的緣由有不少,是不是語法兼容性的問題?嘗試用
content
,setAttribute
,document.write
多種方法配置 viewport。失敗。
- 是不是執行時序的問題?設置了頁面加載完成後配置、延時配置。失敗。
- 是不是微信內置的 webview 的兼容問題?因爲問題出現時正好遇上微信客戶端的一波升級,強烈懷疑下聯繫了微信 webview 的開發人員,反覆嘗試後沒法復現。失敗。但提出因爲之後的微信 webview 使用與 safari 同樣的 WK 內核,可使用 safari 嘗試復現一下。果真發現 safari 下也有相似的問題偶發,漸漸排除了對微信的懷疑。
- 是不是劫持致使的?在某次不經意發現 chrome 下也有該問題出現,而且發現有非公司域名的 js 腳本注入。因而嘗試在測試頁面加上外鏈的劫持腳本。因爲安全策略,http 協議的腳本沒法在 https 的域名下加載執行,所以看起來也沒對配置有影響。失敗。
至此,看起來配置是設置成功了,但在某些條件下依舊『沒法生效』。在全部條件的排除後線索斷了,問題依舊沒法穩定復現。不過進一步總結了問題的幾個規律:頁面被放大、頁面無標題、fixed 定位的頁面元素失效,都是頁面級的影響。並且基本每次有新上線都會出現。測試
一次偶然的狀況,再一次在 chrome 下碰到了劫持的狀況,因而仔細分析了一下頁面,不看不知道:flex
顯然,這不是簡單的往頁面裏注入 JS 腳本的劫持,而是整個頁面級的劫持,將原有頁面包在了 iframe 容器裏,外層的頁面則是劫持者的頁面,而這個頁面的 viewport 顯然沒有通過配置的,因此產生了放大。一樣因爲設置頁面標題的代碼在 iframe 內部,只能給 iframe 的窗口設置標題。再者 fixed 的定位限定在了 iframe 內部,天然不會『貼』在窗口上。一切的現象都可以獲得解釋,那麼問題來了:若是真是被劫持了,那麼 https 的頁面究竟怎樣被劫持的呢?調試
第三種假設:頁面被劫持
- 現象都能解釋的通,但仍是得確認一下。因而在測試頁面裏判斷是否咱們的頁面被封裝在了 iframe 裏,這裏只用檢測『自己』和『頂層』的頁面是不是同一個。果真當出現問題的時候,二者並不是同一個頁面,那就存在 iframe 形式的劫持。成功。
- 那麼是如何被劫持的呢?是否頁面入口是 http 的?不幸的是,頁面入口是支持 http 的,只是在 JS 的業務邏輯裏會將 http 轉成 https 從新請求一遍頁面,這是方便可是不安全的,並且目前也有從 http 過來的流量,看起來像掩耳盜鈴。但實際的測試頁是 https 的,一樣有問題的發生,因此即使頁面 http 是開放的,也不是此次的緣由。失敗。
- 那是不是頁面有引用 http 的靜態文件致使的呢?由於網上查到有人反饋運營商會劫持並修改 https 頁面中引用的 http 的靜態文件,從而達到間接劫持的效果。但測試頁一樣駁回了這種想法,由於頁面是最簡單的邏輯,全部的代碼都是內聯而非地址引用的。失敗。
- 到底是哪一個環節被劫持了呢?毫無頭緒下只能求助 @flex 爸爸了。在梳理了整個請求流程後,發現前端全部的靜態文件,包括入口的 html 都上線到了 cdn 上,請求到了 cdn 後,cdn 是用 http 來回源的,多是這段路徑受到了劫持。這正好與『基本每次上線都會出現』的現象一致。在配置好回源的 https 協議後,問題總算再也不出現。至此,整個排查也告一段落。
回頭看來,這並不是一次目標明確的排查,整個過程在盲目的懷疑中徘徊了好久,其中也有不少因素制約着,好比沒有完善的錯誤日誌收集體系、沒有 webview 的調試工具,前端的視野限制了咱們的思惟。但也有一些手段起了很大的幫助,好比搭建了一個最精簡的測試環境,便於收斂分析;也關注了一些細節,好比詳細分析了問題的場景和現象,進而協助目標的定位。
結果是簡單的,過程仍是蠻使人回味。仍是那句話:排除全部不可能,剩下的必定是事情的真相。