网络代码如何管理Cookie

I was recently migrating over from C# and looking to create some of my old applications. As such I have needed to find a way to manage sessions within Go web requests. I found a solution in the form of this code:

// Jar is session object struct - cookie jar including mutex for syncing
type Jar struct {
    sync.Mutex
    cookies map[string][]*http.Cookie
}

// NewJar is a function for creating cookie jar for use
func NewJar() *Jar {
    jar := new(Jar)
    jar.cookies = make(map[string][]*http.Cookie)
    return jar
}

// SetCookies sets the cookies for the jar
func (jar *Jar) SetCookies(u *url.URL, cookies []*http.Cookie) {
    jar.Lock()
    if _, ok := jar.cookies[u.Host]; ok {
        for _, c := range cookies {
            jar.cookies[u.Host] = append(jar.cookies[u.Host], c)
        }
    } else {
        jar.cookies[u.Host] = cookies
    }
    jar.Unlock()
}

// Cookies returns cookies for each host
func (jar *Jar) Cookies(u *url.URL) []*http.Cookie {
    return jar.cookies[u.Host]
}

// NewJarClient creates new client, utilising a NewJar()
func NewJarClient() *http.Client {

    proxyURL, _ := url.Parse("http://127.0.0.1:8888")

    tr := &http.Transport{
        MaxIdleConns:       10,
        IdleConnTimeout:    30 * time.Second,
        DisableCompression: true,
        Proxy:              http.ProxyURL(proxyURL),
    }

    return &http.Client{
        Jar:       NewJar(),
        Transport: tr,
    }
}

The problem I'm having is in understanding how this works. I create a client doing the following

client := NewJarClient()

but then when I issue networking fuctions using it such as a get request, the cookies automatically carry on and it all works as planned. The problem is Ihave no idea why. I see no mention of methods such as the Cookies one or the SetCookies one ever being called and it seems to just handle each one by magically running the functions. Could someone annotate or explain the given methods line by line or in a way so that they'd make better sense to me coming over from a C# background. Thanks :)

NewJar allocates and returns a new instance of type *Jar, now type *Jar, thanks to the methods defined on it, implements the interface called CookieJar, implicitly.

The http.Client type has a field called Jar which is defined as having the type CookieJar, that means that you can set http.Client.Jar to anything that implements the CookieJar interface, including the *Jar type. The NewJarClient function returns a new *http.Client instance with it's Jar field set to the *Jar instance returned by NewJar.

This allows the returned client value to use *Jar's methods without really knowing that it's a *Jar, it only knows that the value in its Jar field has the same set of methods as those defined by the CookieJar interface.

So the http.Client instance, when sending requests, uses your *Jar by calling its methods providing the parameters and handling the returned values. How your *Jar is used by the client is an implementation detail of the http.Client type and you don't have to worry about that. You just need to make sure that the methods that implement the CookieJar interface do what you want them to do, how and when they are called is up to the client.

But if you're interested in the implementation details of the client anyway, you can check out the source file of http.Client.

Due to misinformation in the form of a few dated blog posts, I came to the impression that I was unable to maintain cookies across requests in go - for some weird reason. Having thought that, I researched and looked into creating my own implementation which ca be seen above. It's been brought to my attention that my implementation is completely broken and flawed and that the standard http library itself can perfectly handle maintaining cookies, simply by including a value for the Jar when creating a client. For example:

jar, _ := cookiejar.New(nil)

proxyURL, _ := url.Parse("http://127.0.0.1:8888")

tr := &http.Transport{
    MaxIdleConns:       10,
    IdleConnTimeout:    30 * time.Second,
    DisableCompression: true,
    Proxy:              http.ProxyURL(proxyURL),
}

c := &http.Client{
    Jar:       jar,
    Transport: tr,
}