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,
}