If you are an active Internet user and like to shop online or, for example, use online learning platforms, you have probably come across a message asking you to accept the use of cookies when you first log in.
In this topic, we will learn what these ubiquitous cookies are, why our future web application may need them, and how to use them in Ktor.
What is a cookie and what is its use?
A cookie is a small piece of data created by the server and stored on the user's side and then returned without any changes and additions, thus allowing us to save certain status information on the user's device.
Schematically, the operation of cookies can be depicted as follows:
- First, the client requesting access sends a request to the server.
- Then, the server sends a response to the request, which contains cookies.
- The following client request contains the received cookie.
A cookie itself is a name-value pair and zero or more attributes, each of which is also a name-value pair. We'll talk more about attributes further.
For example, mysterious_cookie=MysteriousText. In this example, the mysterious_cookie is the name, and the MysteriousText is the value.
If you are interested, you can study for yourself which cookies are sent by the sites you visit. To do this, you need to go to the console in the developer tools in your browser and enter document.cookie. If you have tried to do this, you will notice that some cookies represent a kind of gibberish. It's because of cookies encoding. Commonly cookies encode through Base64, but Ktor uses URI encoding by default and has some other encoding strategies.
So what function do these cookies fulfill? For example, you spent a few hours choosing new sneakers and put a few pairs in the cart in an online store, but accidentally closed the store tab. When you opened the tab again and saw that the sneakers were still in the cart. All thanks to cookies. The server sent data to your device that such an item was put in the cart. And when you opened the site again, the server received this information from your device via a cookie and displayed the sneakers in the cart.
Cookies can also be used for authorization, accelerated page loading, personalization, advertising, and so on.
Cookie Attributes
Now we know that the cookies contain some important data for some important purposes as name-value pairs. Every day you visit dozens of various sites that send dozens or even hundreds of cookies to your browser. Some of these cookies become obsolete over time, and some contain confidential information, so it's necessary to manage them in some way. To solve this problem, the server adds some attributes to a responding cookie. Let's find out what cookie attributes are.
- Expires attribute: defines the lifetime of the cookie specified on a certain date and time. For example
"some_cookie=someValue; expires=Sat, 21 Mar 2030 05:20:10 GMT". - Max-Age attribute: defines the same as the expiration date attribute, but indicates it in seconds. For example
"some_cookie=someValue; max-age=10800". - Domain attribute: defines the host to which the cookie will be sent. For example, the attribute
"some_cookie=someValue; domain=example.com"means that the cookie will be added only when interacting with the domain example.com. - Path attribute: is very similar to the domain attribute but does not define the domain as a whole but specific paths. For example, if we specify
"some_cookie=someValue; path=/someway"then the cookie will be used only on the path"/someway"or"/someway/otherway"but not on along the path"/otherway". - Secure attribute: allows us to transfer cookies only by encrypted transmission and only through secure connections. This attribute is either specified or not. For example, if
"some_cookie=someValue; secure"is defined, then the attribute is active. If not, then the cookie properties remain default. - HttpOnly attribute: restricts access to cookies in any other way, except for HTTP requests. For example, if
"some_cookie=someValue; HttpOnly"is defined, then the attribute is active. If not, then the cookie properties remain default. - Custom attributes. It's also important to say that we can add custom attributes to our cookie (at least in Ktor). For example, in
"some_cookie=someValue; greetings=Hello"we define "greetings" attribute with a "Hello" value.
Working with cookies in Ktor
As we have already said, cookies are generated by the server, sent to the client and stored, and then, when necessary, sent back to the server.
Therefore, we can access cookies both through the client's request and through the server's response:
// return a String value of "some_cookie" cookie
call.request.cookies.get("some_cookie")
// return "some_cookie" as Cookie object
call.response.cookies.get("some_cookie")
In the request case, we get a String with a cookie value or null. In the response case, an object of the Cookie class or null.
To add our cookie to the response, we need to use the append method, to which we pass a Cookie object or cookie parameters. In the following example, we specified two necessary parameters: the cookie's key and value, and the optional cookie's path.
get("/vanilla") {
call.response.cookies.append("SOME_COOKIE", "VANILLA", path = "/vanilla")
call.respondText("We sent vanilla cookie!!!")
}
get("/chocolate") {
call.response.cookies.append("SOME_COOKIE", "Chocolate")
call.respondText("We sent chocolate cookie!!!")
}
In the code example above, we add two cookies to the server responses, with "flavors" of vanilla and chocolate.
To check if everything worked out for us, let's use Postman. By sending relevant requests via Postman, we will be able to see our vanilla and chocolate cookies in the Cookies header:
In the image above, we see that Postman catches our Chocolate cookie.
Processing Cookies via the Session plugin
We are already familiar with the Session plugin and know that it allows us to work with Cookies. Let's figure out how it works.
First, let's create a couple of data class for our future sessions:
data class UserSession(val name: String, val surName: String)
data class CartSession(val cart: MutableList<String>)
Now, we will configure our future cookies by calling the cookie function inside the installation block:
install(Sessions) {
cookie<UserSession>("user_session") {
cookie.path = "/login"
}
cookie<CartSession>("cart_session")
}
Finally, we will prepare our requests and responses:
routing {
get("/login") {
call.sessions.set(UserSession("John", "Matrix"))
call.sessions.set(CartSession(mutableListOf()))
}
get("/cottonCandy") {
val cartSession = call.sessions.get<CartSession>()
call.sessions.set(cartSession!!.cart.add("cotton candy"))
}
get("/iceCream") {
val cartSession = call.sessions.get<CartSession>()
call.sessions.set(cartSession!!.cart.add("ice cream"))
}
}
The code above creates two sessions when sending a request to "/login". When sending a request to "/cottonCandy" and "/iceCream", it adds one product at a time to our improvised basket.
To check whether our cookies are being sent and what information they contain, we will again use Postman. By sending a GET request to the "/login" path, we will receive the following data in the Headers section:
Here, we see two cookies, "user_session" and "cart_session", which contain information about our sessions. These are the contents of the data classes we created.
Now let's try to send a request to the "/iceCream" path:
We received only one cookie, because we specified "/login"as the path attribute for the "user_session" cookie, so it was not sent. You can also notice that the value of "cart_session" cookie has also changed. This happened because "ice cream" was added to our shopping cart when responding to the request.
Now you can try by yourself to create different data classes and add different attributes to cookies, and see what happens with the help of Postman.
Conclusion
Let's sum up some small results:
- Cookies are pieces of data representing a key-value pair and used to store information on the client's side.
- Most often, cookies are used to save user identification data, store data about actions performed by the user (for example, adding products to the cart), to store individual user settings, and also allow you to remember individual user preferences in order, for example, to offer him specific products.
- Now you can configure your own cookies yourself.