Day 31 — The OSI Model in Practice
Learning Objectives
- Map Go's
netpackage 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?"