Welcome To Golang By Example

Cookies in Go (Golang)

What is cookie

Cookies are a way to store information at the client end. The client can be a browser, a mobile application, or anything which makes an HTTP request. Cookies are basically some files that are stored in the cache memory of your browser. When you are browsing any website which supports cookies will drop some kind of information related to your activities in the cookie. This information could be anything. Cookies in short store historical information about the user activities. This information is stored on the client’s computer. Since a cookie is stored in a file,  hence this information is not lost even when the user closes a browser window or restarts the computer. A cookie can also store the login information. In fact, login information such as tokens is generally stored in cookies only. Cookies are stored per domain. Cookies stored locally belonging to a particular domain are sent in each request to that domain. They are sent in each request as part of headers. So essentially cookie is nothing but a header.

You can read about HTTP cookie in general here – https://en.wikipedia.org/wiki/HTTP_cookie

Cookies can be send

A cookie in golang is represented as below

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.

Let’s see two things in details related to cookies

Set a cookie in golang

We already mentioned that a cookie is just a header.  So for setting a particular cookie, we need to just set that header. 
There are two cases

Let’s see each of these in detail as well

Set a cookie while making a 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

In the above program, HTTP Client added two cookies. Both these cookies will be sent in the call to google.com.

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

HTTP client uses this jar in two ways

For more information about CookieJar in golang, you can refer to this link https://golangbyexample.com/cookiejar-golang/

Set a cookie while responding to an incoming request

This is the case where golang acts as an HTTP server. http.ResponseWriter struct provides a convenient method to set a cookie. Below is the signature of the method

func SetCookie(w ResponseWriter, cookie *Cookie)

This method is used to set cookies on a ResponseWriter. It adds a Set-Cookie header to the response headers.  This Set-Cookie header is used to send the cookie that is to be set at the client end or browser end. This cookie then will be sent back to the server when the client makes subsequent calls to the server. 

Below is the program for the same.

package main
import (
    "net/http"
)
func main() {
    docHandler := http.HandlerFunc(docHandler)
    http.Handle("/doc", docHandler)
    http.ListenAndServe(":8080", nil)
}
func docHandler(w http.ResponseWriter, r *http.Request) {
    cookie := &http.Cookie{
        Name:   "id",
        Value:  "abcd",
        MaxAge: 300,
    }
    http.SetCookie(w, cookie)
    w.WriteHeader(200)
    w.Write([]byte("Doc Get Successful"))
    return
}

Run the above program using

go run main.go

The server will start running  on port 8080

Now make the API call localhost:8080/doc from a browser. The server is sending the below Set-Cookie in the response

Set-Cookie: id=abcd; Max-Age=300

The same is also visible in the response headers of the API call. See screenshot below

For more details around the Set-Cookie header please refer to this link. This link contains all the details to understand the Set-Cookie header in golang.

Read a cookie in golang

net/http Request struct provides a convenient method to read a particular cookie given its name. Below is the signature of that method. https://golang.org/pkg/net/http/#Request.Cookie

func (r *Request) Cookie(name string) (*Cookie, error)

To print all cookies,  we can iterate over the Cookies method of http.Request struct. We can use a range keyword for that.

for _, c := range r.Cookies() {
     fmt.Println(c)
}

Below is the program for the same to illustrate the Cookie and Cookies method of the http.Request struct

package main

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

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

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

func docHandler(w http.ResponseWriter, r *http.Request) {
	fmt.Println("Cookies in API Call:")

	tokenCookie, err := r.Cookie("token")
	if err != nil {
		log.Fatalf("Error occured while reading cookie")
	}
	fmt.Println("\nPrinting cookie with name as token")
	fmt.Println(tokenCookie)

	fmt.Println("\nPrinting all cookies")
	for _, c := range r.Cookies() {
		fmt.Println(c)
	}
	fmt.Println()
	w.WriteHeader(200)
	w.Write([]byte("Doc Get Successful"))
	return
}

Run the program above and make the below curl call

curl -X GET localhost:8080/doc --cookie "id=abcd; token=some_token"

The curl call is passing two cookie name-value pairs

It will give below output

Cookies in API Call:

Printing cookie with name as token
token=some_token

Printing all cookies
id=abcd
token=some_token

This is how we print a particular cookie with a given name “token”

tokenCookie, err := r.Cookie("token")

It prints as seen from the output

token=some_token

This is how we print all the cookies

for _, c := range r.Cookies() {
     fmt.Println(c)
}

It outputs both the cookies name-value pairs that we had sent in the curl call

id=abcd
token=some_token

That was all about 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