Welcome To Golang By Example

Set cookie http in Go (Golang)

Overview

A cookie in golang is represented as below in golang

https://golang.org/src/net/http/cookie.go

type Cookie struct {
	Name  string
	Value string

	Path       string    // optional
	Domain     string    // optional
	Expires    time.Time // optional
	RawExpires string    // for reading cookies only

	// MaxAge=0 means no 'Max-Age' attribute specified.
	// MaxAge<0 means delete cookie now, equivalently 'Max-Age: 0'
	// MaxAge>0 means Max-Age attribute present and given in seconds
	MaxAge   int
	Secure   bool
	HttpOnly bool
	SameSite SameSite
	Raw      string
	Unparsed []string // Raw text of unparsed attribute-value pairs
}

See https://tools.ietf.org/html/rfc6265 for details of each of the fields of the above cookie.

When it comes to the setting of cookies, there are two cases

The second use case is needed when let’s say you generate an authentication token in the first call by entering username and password, and you want that token to be passed in each subsequent call in the cookie. We will use a cookie jar for this use case. 

Let’s see both the use cases one by one. 

Set cookie for one of the request

This is the case where golang acts as an HTTP client. AddCookie method of net/http package can be used add a cookie. If we call this method for two different names and value,  then both that name and value will be added to the resultant cookie

package main
import (
    "fmt"
    "log"
    "net/http"
    "net/http/cookiejar"
)
var client http.Client
func init() {
    jar, err := cookiejar.New(nil)
    if err != nil {
        log.Fatalf("Got error while creating cookie jar %s", err.Error())
    }
    client = http.Client{
        Jar: jar,
    }
}
func main() {
    cookie := &http.Cookie{
        Name:   "token",
        Value:  "some_token",
        MaxAge: 300,
    }
    cookie2 := &http.Cookie{
        Name:   "clicked",
        Value:  "true",
        MaxAge: 300,
    }
    req, err := http.NewRequest("GET", "http://google.com", nil)
    if err != nil {
        log.Fatalf("Got error %s", err.Error())
    }
    req.AddCookie(cookie)
    req.AddCookie(cookie2)
    for _, c := range req.Cookies() {
        fmt.Println(c)
    }
    resp, err := client.Do(req)
    if err != nil {
        log.Fatalf("Error occured. Error is: %s", err.Error())
    }
    defer resp.Body.Close()
    fmt.Printf("StatusCode: %d\n", resp.StatusCode)
}

Output

token=some_token
clicked=true
StatusCode: 200

We first need to create the instance of the cookie as below

https://golang.org/src/net/http/cookie.go

cookie := &http.Cookie{
    Name:   "token",
    Value:  "some_token",
    MaxAge: 300,
}

Once the cookie is created then use the AddCookie method to add it to the HTTP request object

req.AddCookie(cookie)

In the above program, HTTP Client added two cookies with the below name-value pair.

Both these cookies will be sent in the call to google.com.

Set cookie and send that cookie for all requests to that domain

HTTP client in golang lets you specify a CookieJar that can be used for storing and sending the cookies while making external HTTP requests. As the name suggests, think of it as a jar that contains cookies.

https://golang.org/pkg/net/http/#Client

Below is the structure of the net/http Client struct. It contains an instance variable named Jar of type CookieJar which is an interface

type Client struct {
    Transport RoundTripper

    CheckRedirect func(req *Request, via []*Request) error

    Jar CookieJar

    Timeout time.Duration
}

Below is the CookieJar interface

type CookieJar interface {
    SetCookies(u *url.URL, cookies []*Cookie)
    Cookies(u *url.URL) []*Cookie
}

net/http provides a default cookie jar implementation that implements the above CookieJar interface. We will use it while initializing our net/http Client

https://golang.org/pkg/net/http/cookiejar/#Jar

You can also provide your custom cookie jar during the initialization of net/http Client struct which implements the above CookieJar interface.

HTTP client uses this jar in two ways

Let’s see an example where the client will add a cookie while making HTTP requests. This cookie will be sent in all subsequent requests to the same domain.

In this example, we will see how the client can set the cookie in the cookie jar. For that let’s first create a client. Here is the program for the same

Here is the client code

Client

go.mod

module sample.com/client
go 1.16

client.go

package main

import (
	"fmt"
	"log"
	"net/http"
	"net/http/cookiejar"
	"net/url"
)

var client http.Client

func init() {
	jar, err := cookiejar.New(nil)
	if err != nil {
		log.Fatalf("Got error while creating cookie jar %s", err.Error())
	}

	client = http.Client{
		Jar: jar,
	}
}

func main() {
	req, err := http.NewRequest("GET", "http://localhost:8080/doc", nil)
	if err != nil {
		log.Fatalf("Got error %s", err.Error())
	}
	cookie := &http.Cookie{
		Name:   "token",
		Value:  "some_token",
		MaxAge: 300,
	}
	urlObj, _ := url.Parse("http://localhost:8080/")
	client.Jar.SetCookies(urlObj, []*http.Cookie{cookie})
	resp, err := client.Do(req)
	if err != nil {
		log.Fatalf("Error occured. Error is: %s", err.Error())
	}
	defer resp.Body.Close()

	fmt.Printf("StatusCode: %d\n", resp.StatusCode)

	req, err = http.NewRequest("GET", "http://localhost:8080/doc/id", nil)
	if err != nil {
		log.Fatalf("Got error %s", err.Error())
	}

	resp, err = client.Do(req)
	if err != nil {
		log.Fatalf("Error occured. Error is: %s", err.Error())
	}
	defer resp.Body.Close()

	fmt.Printf("StatusCode: %d\n", resp.StatusCode)
}

In the above client program, we are creating an HTTP client with a cookie Jar

jar, err := cookiejar.New(nil)
if err != nil {
     log.Fatalf("Got error while creating cookie jar %s", err.Error())
}

client = http.Client{
     Jar: jar,
}

we are adding a cookie to the Cookie Jar

cookie := &http.Cookie{
		Name:   "token",
		Value:  "some_token",
		MaxAge: 300,
}
urlObj, _ := url.Parse("http://localhost:8080/")
client.Jar.SetCookies(urlObj, []*http.Cookie{cookie})

To test the above code and to illustrate that cookies added to the cookie jar in the first call are indeed sent in the subsequent call as well, we need to create the server as well which will print the incoming cookies.

Server

The server listens to port 8080 and has two APIs. These are the two APIs which is being hit by the client above.

In both the APIs we are printing the cookies which it receives in the incoming header

go.mod

module sample.com/server

go 1.16

server.go

package main

import (
	"fmt"
	"net/http"
)

func main() {
	docHandler := http.HandlerFunc(docHandler)
	http.Handle("/doc", docHandler)

	docGetID := http.HandlerFunc(docGetID)
	http.Handle("/doc/id", docGetID)

	http.ListenAndServe(":8080", nil)
}

func docHandler(w http.ResponseWriter, r *http.Request) {
	fmt.Println("Cookie in First API Call")
	for _, c := range r.Cookies() {
		fmt.Println(c)
	}
	fmt.Println()
	w.WriteHeader(200)
	w.Write([]byte("Doc Get Successful"))
	return
}

func docGetID(w http.ResponseWriter, r *http.Request) {
	fmt.Println("Cookie in Second API Call")
	for _, c := range r.Cookies() {
		fmt.Println(c)
	}
	w.WriteHeader(200)
	w.Write([]byte("Doc Get ID Successful"))
	return
}

Now run the server

go run server.go

and client

go run client.go

Notice the output at the server end

Cookie in First API Call
token=some_token

Cookie in Second API Call
token=some_token

The same cookie is being sent automatically in both the first and second call which the client will make to the server. How does it work out of the box? This is because CookieJar comes into the picture. The golang HTTP client checks the Cookie Jar before making the HTTP call. It then sends this cookie.

This is all about setting cookies in golang. Hope you have liked the tutorial. Please share feedback in the comments.

Also, check out our Golang advance tutorial Series – Golang Advance Tutorial