The cause of

After the recent project was upgraded from Golang0.9 to Golang1.13, a very special phenomenon occurred in the project. In the APP, the user can access the page normally after logging in, but the user does not log in, and an error is reported.

The process

  1. Charles captured the packet and found that the service returns protobuf data when logged in, and JSON structure when logged out. The service returns the corresponding data type based on the data passed in the cookie. It is preliminarily concluded that cookies cannot be obtained without login

  2. Check the difference between cookies when logged in and when not logged in.

    • Login: serviceToken = ABC; type=protobuf; session=123
    • Not logged in:; type=protobuf; session=123

    The serviceToken is the authentication information after login. If the user does not log in, the data does not exist, but the semicolon does exist. The golang version was initially suspected

  3. With the code unchanged, the service was generated using different versions of Golang and tested using scripts

    
            
    $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);Copy the code

    Confirm golang version problem

  4. Check the source code, in net/ HTTP /cookie.go, you can see

    • Golang1.12 directly splits the cookie strings.split (strings.trimspace (line), “;” ), so the semicolon can be resolved wherever it is
    • In golang1.13 if splitIndex := strings.Index(line, “;”) ); SplitIndex > 0. If the quotes are first, the process ends and the correct cookie value cannot be obtained

    golang1.12

    // 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
             }
             iffilter ! =""&& filter ! = name {continue
             }
             val, ok := parseCookieValue(val, true)
             if! ok {continue
             }
             cookies = append(cookies, &Cookie{Name: name, Value: val})
          }
       }
       return cookies
    }
    Copy the code

    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
             }
             iffilter ! =""&& filter ! = name {continue
             }
             val, ok := parseCookieValue(val, true)
             if! ok {continue
             }
             cookies = append(cookies, &Cookie{Name: name, Value: val})
          }
       }
       return cookies
    }
    Copy the code

conclusion

  1. Calibrated boldness
    • Upgrades are sometimes necessary, with more features and better performance, and if the circumstances are right, you need to be bold
    • Be sure to let the test students, each terminal, various states, various processes over again
    • Once online, you need to keep watching
  2. It is a process of personal growth and improvement to constantly track down problems
  3. Small things need to be perfect, or they can be costly. The way the client wrote the cookie was correct, but not very standard, and I didn’t care when I wrote it. Currently, the old version is unfixable, and if you want to upgrade 1.13, you have to either fix the problems in Golang or add logic to your project to handle cookies, which is a bit of a hassle

The last

If you like my article, you can follow my public account (Programmer Malatang)

Review of previous articles:

  1. Redis implements distributed locking
  2. Golang source BUG tracking
  3. The implementation principle of atomicity, consistency and persistence of transactions
  4. How to exercise your memory
  5. CDN request process details
  6. Thinking about programmer career development
  7. The story of how blogging services were crushed
  8. Common Cache tips
  9. How to effectively connect with third-party payment
  10. Gin framework concise version
  11. Thinking about code review
  12. InnoDB locks and transactions
  13. Markdown editor recommends – Typora