Golang源碼COOKIE Bug追查

原由

最近的項目從golang0.9升級到golang1.13後,項目中出現了很特殊的現象,在APP裏,用戶登陸後訪問頁面正常,用戶不登陸,報錯。php

處理過程

  1. Charles抓包發現,登陸的狀況下,服務返回的是protobuf的數據,未登陸狀況下返回的是json結構。服務是根據cookie中傳入的數據來返回對應的數據類型。初步判定未登陸狀況下沒法獲取到cookie程序員

  2. 檢查登陸和未登陸狀況下cookie的區別。golang

    • 登陸:serviceToken=abc;type=protobuf;session=123
    • 未登陸:;type=protobuf;session=123

    serviceToken是登陸後的驗證信息,用戶若是未登陸,數據不存在,可是分號卻存在。初步懷疑是golang版本引發json

  3. 在代碼不變的狀況下,使用不一樣golang版本生成服務,使用腳本進行測試緩存

    <?php
    $url = "http://10.220.130.8:8081/in/app/sync";
    //$url = "http://in-go.buy.mi.com/in/app/sync";
    $cookie = "; xmuuid=XMGUEST-FCF117BF-4D1B-272F-829D-25E19826D4F8;type=protobuf";
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    curl_setopt($ch, CURLOPT_COOKIE, $cookie);
    $output = curl_exec($ch);
    curl_close($ch);
    var_dump($output,11) ;
    複製代碼

    肯定是golang版本問題cookie

  4. 查看源碼,在net/http/cookie.go中,能夠看到session

    • golang1.12將cookie直接作了分割strings.Split(strings.TrimSpace(line), ";"),因此不管分號在什麼位置都能解析出來
    • 在golang1.13中 if splitIndex := strings.Index(line, ";"); splitIndex > 0 ,使用這種切割方式,若是引號位於第一個,整個獲取過程便結束了,沒法得到正確的cookie值

    golang1.12app

    // readCookies parses all "Cookie" values from the header h and
    // returns the successfully parsed Cookies.
    //
    // if filter isn't empty, only cookies of that name are returned
    func readCookies(h Header, filter string) []*Cookie {
       lines, ok := h["Cookie"]
       if !ok {
          return []*Cookie{}
       }
    
       cookies := []*Cookie{}
       for _, line := range lines {
          parts := strings.Split(strings.TrimSpace(line), ";")
          if len(parts) == 1 && parts[0] == "" {
             continue
          }
          // Per-line attributes
          for i := 0; i < len(parts); i++ {
             parts[i] = strings.TrimSpace(parts[i])
             if len(parts[i]) == 0 {
                continue
             }
             name, val := parts[i], ""
             if j := strings.Index(name, "="); j >= 0 {
                name, val = name[:j], name[j+1:]
             }
             if !isCookieNameValid(name) {
                continue
             }
             if filter != "" && filter != name {
                continue
             }
             val, ok := parseCookieValue(val, true)
             if !ok {
                continue
             }
             cookies = append(cookies, &Cookie{Name: name, Value: val})
          }
       }
       return cookies
    }
    複製代碼

    golang1.13框架

    // readCookies parses all "Cookie" values from the header h and
    // returns the successfully parsed Cookies.
    //
    // if filter isn't empty, only cookies of that name are returned
    func readCookies(h Header, filter string) []*Cookie {
       lines := h["Cookie"]
       if len(lines) == 0 {
          return []*Cookie{}
       }
    
       cookies := make([]*Cookie, 0, len(lines)+strings.Count(lines[0], ";"))
       for _, line := range lines {
          line = strings.TrimSpace(line)
          var part string
          for len(line) > 0 { // continue since we have rest
             if splitIndex := strings.Index(line, ";"); splitIndex > 0 {
                part, line = line[:splitIndex], line[splitIndex+1:]
             } else {
                part, line = line, ""
             }
             part = strings.TrimSpace(part)
             if len(part) == 0 {
                continue
             }
             name, val := part, ""
             if j := strings.Index(part, "="); j >= 0 {
                name, val = name[:j], name[j+1:]
             }
             if !isCookieNameValid(name) {
                continue
             }
             if filter != "" && filter != name {
                continue
             }
             val, ok := parseCookieValue(val, true)
             if !ok {
                continue
             }
             cookies = append(cookies, &Cookie{Name: name, Value: val})
          }
       }
       return cookies
    }
    複製代碼

總結

  1. 膽大心細
    • 升級這種操做有時候是必然要作的,新版會有更多的新特性,帶來更好的性能,狀況合適的話,須要大膽去作
    • 必定要讓測試同窗,將各個終端,各類狀態,各類流程過一遍
    • 上線以後,須要繼續觀察
  2. 遇到問題,須要不斷追查,這是一個自我成長和提高的過程
  3. 小的方面也須要完美,不然會致使很大的代價。客戶端寫cookie的方法雖然沒錯,但也不是很標準,當時寫的時候也沒在乎。目前老的版本已經沒法修復,若是想升級1.13,要麼修復golang裏的問題,要麼在項目裏添加處理cookie的邏輯,處理起來都有些麻煩

最後

你們若是喜歡個人文章,能夠關注個人公衆號(程序員麻辣燙)curl

往期文章回顧:

  1. Redis實現分佈式鎖
  2. Golang源碼BUG追查
  3. 事務原子性、一致性、持久性的實現原理
  4. 如何鍛鍊本身的記憶力
  5. CDN請求過程詳解
  6. 關於程序員職業發展的思考
  7. 記博客服務被壓垮的歷程
  8. 經常使用緩存技巧
  9. 如何高效對接第三方支付
  10. Gin框架簡潔版
  11. 關於代碼review的思考
  12. InnoDB鎖與事務簡析
  13. Markdown編輯器推薦-typora
相關文章
相關標籤/搜索