Chapter 5: Advanced topics

Sessions

face Josiah Wang

It can be tedious to have to enter your username and password over and over every time to make a request.

This is where sessions come in handy. Sessions offer a persistent connection to the server. You can log in once and then keep the same connection for your remaining requests.

In the requests package, a Session instance offers the same get(), put(), etc. methods as earlier. So you can just use the Session instance to make your HTTP requests. The session instance ‘remembers’ whatever information you gave it (for example, authentication credentials or some parameters).

with requests.Session() as session:
    username = "cobra"
    password = "hiss"
    session.auth = (username, password)
    first_response = session.get("http://localhost:5000/greeting")
    second_response = session.get("http://localhost:5000/greeting")
    third_response = session.get("http://localhost:5000/greeting")

If you examine the output from your requests, you will find that the server actually ‘remembers’ your identity and how many times you visited! (Note the “welcome back” message)

>>> print(first_response.json())
{'message': 'Welcome, cobra!'}
>>> print(second_response.json())
{'message': 'Welcome back, cobra! This is visit #2.'}
>>> print(third_response.json())
{'message': 'Welcome back, cobra! This is visit #3.'}

How the server actually ‘remembers’ is by passing some cookies back to you (the client) 🍪🍪. These cookies are in a form of dictionary (key, value) pairs. The cookies store all the information it needs for later use. So you (the client) will pass the cookies back to the server when you ‘continue’ your interaction with it later in the same session.

In our example, if you examine the headers of the first response, you will see that the server has instructed you to "Set-Cookie" to some given session data (in which the server actually stores your username and your number of visits).

>>> print(first_response.headers)
{'Content-Type': 'application/json', 'Content-Length': '35', 'Vary': 'Cookie', 
'Set-Cookie': 'session=eyJ1c2VybmFtZSI6ImNvYnJhIiwidmlzaXQiOjF9.YZaTHQ.OR2ZuTCLFhYlbq0ne7jdx9CbbpU; HttpOnly; Path=/', 
'Server': 'Werkzeug/2.0.2 Python/3.8.10', 'Date': 'Wed, 17 Nov 2021 20:42:27 GMT'}

In your second request, you dutifully passed the same "Cookie" back to the server in the HTTP request header. The server then retrieves the session information from the cookie, and gets hold of your username and number of visits that it stored earlier. It then updates the number of visits and passes back a new set of cookies to you. Notice that the cookie looks slightly different since the number of visits have changed.

>>> print(second_response.request.headers)
{'User-Agent': 'python-requests/2.22.0', 'Accept-Encoding': 'gzip, deflate', 
'Accept': '*/*', 'Connection': 'keep-alive', 'Cookie': 
'session=eyJ1c2VybmFtZSI6ImNvYnJhIiwidmlzaXQiOjF9.YZaTHQ.OR2ZuTCLFhYlbq0ne7jdx9CbbpU', 
'Authorization': 'Basic Y29icmE6aGlzcw=='}
>>> print(second_response.headers)
{'Content-Type': 'application/json', 'Content-Length': '58', 'Vary': 'Cookie', 
'Set-Cookie': 'session=eyJ1c2VybmFtZSI6ImNvYnJhIiwidmlzaXQiOjJ9.YZaTHQ.asPFCOqXYwYU9EVUscsPyowPuuc; HttpOnly; Path=/', 
'Server': 'Werkzeug/2.0.2 Python/3.8.10', 'Date': 'Wed, 17 Nov 2021 20:42:27 GMT'}

Some people may argue that using sessions/cookies breaks the statelessness constraint of REST APIs (that each request must be independent and self-sufficient). We will not go into this debate - sometimes you need to compromise!