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
相关文章
相关标签/搜索