Module 1 · Foundations that hold everything up

Lesson 01 — HTTP in depth

Methods, status codes with intent, CORS, secure cookies, caching and TLS — verified with curl.

Published
In this lesson
  1. Exercise 1 — Anatomy with
  2. Exercise 2 — Design an endpoint's contract
  3. Exercise 3 — Correct codes in DRF
  4. Exercise 4 — CORS in practice
  5. Exercise 5 — Secure session cookies
  6. Exercise 6 — HTTP caching and 304
  7. Exercise 7 — Reason like an API designer (discussion)
  8. Exercise 8 — HTTPS and TLS (theory, no code)
  9. Submission

Do them in order. Submit your answers in the chat (pasting commands and trimmed output). Don't look at solutions.md until you've submitted. Feedback is part of the learning.

Setup (2 min):

bash
cd ~/dev/ticketflow && source .venv/bin/activate
pip install django-cors-headers && pip freeze > requirements.txt

Add to config/settings.py:

python
INSTALLED_APPS = [ ..., "corsheaders", ]
MIDDLEWARE = [ "corsheaders.middleware.CorsMiddleware", ... ]  # as early as possible
CORS_ALLOWED_ORIGINS = ["http://localhost:5173"]   # the student's hypothetical web app

Exercise 1 — Anatomy with curl -v (warm-up)

With the server running (python manage.py runserver):

  1. Run curl -v http://127.0.0.1:8000/api/health/ and identify in the output: request line, status line, three response headers and the body.
  2. Repeat with curl -v -H "Accept: text/html" http://127.0.0.1:8000/api/health/. What changes and why?

Deliverable: the 4 answers, one line each.

Exercise 2 — Design an endpoint's contract

Design (on paper/chat only, no code) the complete request and response for reserving seats of event 42:

  • Request: method, path, the 3 headers you consider essential, and the JSON body.
  • Happy-path response: code + 2 headers + body.
  • Response when seat 1051 is already taken: code + body.

Hint: there's a status code designed exactly for the "state clash" case.

Exercise 3 — Correct codes in DRF

This code is wrong in two ways (status code and method semantics). Fix it and explain each mistake:

python
@api_view(["POST"])
def cancel_reservation(request, pk):
    reservation = Reservation.objects.get(pk=pk)
    reservation.status = "cancelled"
    reservation.save()
    return Response({"status": "cancelled"})   # ← no explicit status=

Hint: what code does someone who just cancelled expect? And what if the reservation doesn't exist (you'll see it when you test it)?

Exercise 4 — CORS in practice

With the setup from above and the server running:

bash
curl -i -X OPTIONS http://127.0.0.1:8000/api/health/ \
  -H "Origin: http://localhost:5173" \
  -H "Access-Control-Request-Method: POST" \
  -H "Access-Control-Request-Headers: authorization,content-type"
  1. Which status code and Access-Control-* headers do you see in the response?
  2. Repeat with Origin: http://evil.example. What changes?
  3. Add Idempotency-Key to the allowed headers list (CORS_ALLOW_HEADERS), restart, and repeat 1. What changed in the response headers?

Deliverable: trimmed outputs + your explanation of what you just tested (this is exactly what a browser does before a POST from another origin).

Exercise 5 — Secure session cookies

  1. With django.contrib.sessions active (it's there by default), log in via the admin or create a session manually, and observe the Set-Cookie header of the sessionid with curl -i.
  2. In settings.py set SESSION_COOKIE_SECURE = True and SESSION_COOKIE_SAMESITE = "Strict". Restart and repeat with curl http://... (careful: no https). Does the cookie arrive? Why?
  3. Write one sentence on what protection each attribute adds (HttpOnly, Secure, SameSite).

Exercise 6 — HTTP caching and 304

Add ETag-based validation support to a GET endpoint (e.g. health or a future listing):

python
from django.utils.cache import patch_response_headers

@api_view(["GET"])
def events_list(request):
    data = [{"id": 1, "name": "Concert"}, {"id": 2, "name": "Play"}]
    resp = Response(data)
    patch_response_headers(resp, cache_timeout=60)   # Cache-Control, ETag...
    return resp
  1. Test it: curl -i http://127.0.0.1:8000/api/events/ and note the ETag.
  2. Repeat with curl -i -H 'If-None-Match: <your-etag>' http://127.0.0.1:8000/api/events/. Which code do you get, and what body?
  3. What exactly did the server and the client save compared to the first response?

Exercise 7 — Reason like an API designer (discussion)

For each case pick method + status code and justify in 1-2 sentences (this is what interviews ask):

  1. Extending the expiry of reservation 1051 (a "renew for 10 minutes" operation).
  2. Marking a whole event as "cancelled" (replacing its state).
  3. Checking availability of event 42.
  4. Submitting the purchase confirmation (it triggers a queue process; it doesn't return the tickets yet).

Exercise 8 — HTTPS and TLS (theory, no code)

Answer in your own words:

  1. Which three guarantees does TLS give, and which real attacker defeats each one?
  2. What changes in the handshake with TLS 1.3 vs 1.2, and why does it matter for latency?
  3. Your API sits behind Nginx. Where does TLS end, and why is this called "TLS termination"? What does it imply for internal traffic?

Submission

Paste your answers in the chat (with trimmed outputs). I'll grade them one by one, we'll mark the mistakes and I'll update the course state. Then you have the final self-assessment and we move on to networking.