Frontend Labs
⋯

Day 31 — The OSI Model in Practice

Learning Objectives

  • Map Go's net package to OSI layers
  • Trace a complete request from DNS to data delivery
  • Understand where your code operates and where the kernel handles things

OSI Model — Where Go Code Lives

Layer 7 — Application   → net/http, WebSocket, gRPC
Layer 6 — Presentation  → encoding/json, crypto/tls (TLS record layer)
Layer 5 — Session       → TLS handshake, HTTP/2 stream management
Layer 4 — Transport     → net.Conn (TCP), net.PacketConn (UDP)
Layer 3 — Network       → net.IP, net.IPNet (Go reads; kernel routes)
Layer 2 — Data Link     → Kernel handles; Go doesn't touch
Layer 1 — Physical      → Hardware; Go doesn't touch

Your proxy sits at L4-L7:
  Client → [L4 TCP] → Proxy → [L7 HTTP] → Backend

Exercise: Full Connection Trace

package main

import (
    "bufio"
    "fmt"
    "net"
    "time"
)

func traceConnection(host string, port string) {
    target := host + ":" + port

    // Step 1: DNS resolution
    t0 := time.Now()
    addrs, err := net.LookupHost(host)
    dnsLatency := time.Since(t0)
    if err != nil {
        fmt.Printf("DNS FAIL: %v\n", err)
        return
    }
    fmt.Printf("[DNS]  %s → %v (%v)\n", host, addrs, dnsLatency)

    // Step 2: TCP connection (3-way handshake)
    t1 := time.Now()
    conn, err := net.DialTimeout("tcp", target, 5*time.Second)
    tcpLatency := time.Since(t1)
    if err != nil {
        fmt.Printf("TCP FAIL: %v\n", err)
        return
    }
    defer conn.Close()
    fmt.Printf("[TCP]  connected to %s (%v) — local=%s\n", conn.RemoteAddr(), tcpLatency, conn.LocalAddr())

    // Step 3: Raw HTTP/1.0 request (L7 — application protocol over TCP)
    t2 := time.Now()
    fmt.Fprintf(conn, "GET / HTTP/1.0\r\nHost: %s\r\nConnection: close\r\n\r\n", host)

    // Step 4: Read response
    reader := bufio.NewReader(conn)
    statusLine, _ := reader.ReadString('\n')
    httpLatency := time.Since(t2)
    fmt.Printf("[HTTP] response: %s", statusLine)
    fmt.Printf("[HTTP] first-byte latency: %v\n", httpLatency)
    fmt.Printf("[TOTAL] %.1fms DNS + %.1fms TCP + %.1fms TTFB\n",
        float64(dnsLatency.Microseconds())/1000,
        float64(tcpLatency.Microseconds())/1000,
        float64(httpLatency.Microseconds())/1000,
    )
}

func main() {
    fmt.Println("=== Tracing connection to example.com ===")
    traceConnection("example.com", "80")
}

Real-World Context

DNS latency is often the biggest surprise in production. A 50ms DNS lookup before every new connection = 50ms added to cold-start latency. This is why your load balancer (Week 7) caches DNS results with TTL. "Why is our p99 latency 300ms?" is often "why does DNS resolution take 300ms for 1% of requests?"


Generated from week5.md · 8 Oct 2026 16:36