๐ Go HTTP: Your Gateway to the Web World
Analogy: Think of HTTP like a postal service for the internet. When you send a letter, you write it, put it in an envelope with addresses, add stamps (headers), and the post office delivers it. The recipient reads it and sends a reply back. Thatโs exactly how HTTP works between computers!
๐ฌ HTTP Client Operations: Sending Letters to the Internet
Imagine you want to send a letter to a friend (a server). In Go, the net/http package is your postal service counter!
The Simple Way: Just Send It!
package main
import (
"fmt"
"io"
"net/http"
)
func main() {
// Ask for something (GET request)
resp, err := http.Get("https://api.example.com/hello")
if err != nil {
fmt.Println("Oops! Letter lost:", err)
return
}
defer resp.Body.Close()
// Read the reply
body, _ := io.ReadAll(resp.Body)
fmt.Println("Reply:", string(body))
}
Sending Data: POST is Like Mailing a Package
import (
"bytes"
"net/http"
)
func sendPackage() {
// Your package content
data := []byte(`{"name": "Gopher", "age": 10}`)
// Send the package
resp, err := http.Post(
"https://api.example.com/users",
"application/json",
bytes.NewBuffer(data),
)
if err != nil {
return
}
defer resp.Body.Close()
}
Custom Requests: The VIP Letter Service
Sometimes you need more control, like choosing express delivery!
func customRequest() {
client := &http.Client{}
req, _ := http.NewRequest(
"PUT",
"https://api.example.com/update",
bytes.NewBuffer([]byte(`{"status":"active"}`)),
)
resp, _ := client.Do(req)
defer resp.Body.Close()
}
๐ HTTP Headers and Cookies: The Envelope Details
Headers are like special instructions written ON the envelope. Cookies are like membership cards you show every time!
Adding Headers: Writing Special Instructions
func requestWithHeaders() {
client := &http.Client{}
req, _ := http.NewRequest("GET",
"https://api.example.com/secret", nil)
// Add special instructions
req.Header.Set("Authorization", "Bearer my-token")
req.Header.Set("Content-Type", "application/json")
req.Header.Add("Accept-Language", "en-US")
resp, _ := client.Do(req)
defer resp.Body.Close()
// Read server's headers
serverType := resp.Header.Get("Server")
fmt.Println("Server:", serverType)
}
Cookies: Your Internet Membership Card
func handleCookies() {
// Create a cookie jar to store cookies
jar, _ := cookiejar.New(nil)
client := &http.Client{Jar: jar}
// First visit - server gives you a cookie
client.Get("https://shop.example.com/login")
// Next visit - cookie sent automatically!
client.Get("https://shop.example.com/cart")
// Manual cookie creation
cookie := &http.Cookie{
Name: "session_id",
Value: "abc123",
}
req, _ := http.NewRequest("GET",
"https://shop.example.com", nil)
req.AddCookie(cookie)
}
๐ URL Query Parameters: Questions in the Address
Query parameters are like adding specific questions to your letterโs address: โ123 Main St, apt 4B, floor 2โ
graph TD A["Base URL"] --> B["?"] B --> C["key1=value1"] C --> D["&"] D --> E["key2=value2"] F["Result: url.com?key1=value1&key2=value2"]
Building URLs with Questions
import "net/url"
func buildURL() {
baseURL := "https://api.example.com/search"
// Create URL with questions
params := url.Values{}
params.Add("query", "golang")
params.Add("page", "1")
params.Add("sort", "recent")
fullURL := baseURL + "?" + params.Encode()
// Result: https://api.example.com/search?
// query=golang&page=1&sort=recent
fmt.Println(fullURL)
}
Reading Query Parameters
func parseQuery() {
rawURL := "https://example.com/search?name=Go&version=1.21"
parsed, _ := url.Parse(rawURL)
// Get all questions and answers
queries := parsed.Query()
name := queries.Get("name") // "Go"
version := queries.Get("version") // "1.21"
}
๐ฆ net/url Package: Your Address Parser
The net/url package is like a smart address reader. It breaks down any web address into understandable parts!
graph TD A["Full URL"] --> B["Scheme: https"] A --> C["Host: example.com"] A --> D["Path: /api/users"] A --> E["Query: id=123"] A --> F["Fragment: section1"]
Breaking Down an Address
import "net/url"
func parseAddress() {
rawURL := "https://user:pass@example.com:8080/path?q=1#top"
u, _ := url.Parse(rawURL)
fmt.Println("Scheme:", u.Scheme) // https
fmt.Println("Host:", u.Host) // example.com:8080
fmt.Println("Path:", u.Path) // /path
fmt.Println("Query:", u.RawQuery) // q=1
fmt.Println("Fragment:", u.Fragment) // top
fmt.Println("User:", u.User.Username()) // user
}
Building Addresses Safely
func buildSafeURL() {
u := &url.URL{
Scheme: "https",
Host: "api.example.com",
Path: "/users/search",
}
q := u.Query()
q.Set("name", "John Doe") // Spaces handled!
q.Set("city", "New York")
u.RawQuery = q.Encode()
fmt.Println(u.String())
// https://api.example.com/users/search?
// city=New+York&name=John+Doe
}
๐ HTTP Server Basics: Building Your Own Post Office
Now YOU become the post office! You receive letters and send replies.
The Simplest Server
package main
import (
"fmt"
"net/http"
)
func main() {
// When someone knocks on /hello door
http.HandleFunc("/hello", func(w http.ResponseWriter,
r *http.Request) {
fmt.Fprintf(w, "Hello, visitor!")
})
// Open the post office on port 8080
fmt.Println("Server starting on :8080")
http.ListenAndServe(":8080", nil)
}
Multiple Doors (Routes)
func main() {
http.HandleFunc("/", homeHandler)
http.HandleFunc("/users", usersHandler)
http.HandleFunc("/api/data", apiHandler)
http.ListenAndServe(":8080", nil)
}
func homeHandler(w http.ResponseWriter, r *http.Request) {
// Check what type of letter arrived
switch r.Method {
case "GET":
fmt.Fprintf(w, "Welcome home!")
case "POST":
fmt.Fprintf(w, "Data received!")
}
}
Reading Incoming Mail
func readRequest(w http.ResponseWriter, r *http.Request) {
// Who sent it?
fmt.Println("From:", r.RemoteAddr)
// What method?
fmt.Println("Method:", r.Method)
// Any headers?
auth := r.Header.Get("Authorization")
// Query parameters?
name := r.URL.Query().Get("name")
// Body content?
body, _ := io.ReadAll(r.Body)
fmt.Println("Message:", string(body))
}
โฐ HTTP Context Cancellation: โNever Mind, Forget That Letter!โ
Sometimes you change your mind. Context lets you cancel requests!
graph TD A["Start Request"] --> B{Context OK?} B -->|Yes| C["Continue Working"] C --> B B -->|Cancelled!| D["Stop Everything"] D --> E["Clean Up"]
Cancel After Some Time
import (
"context"
"net/http"
"time"
)
func cancelableRequest() {
// "Cancel if not done in 5 seconds"
ctx, cancel := context.WithTimeout(
context.Background(),
5*time.Second,
)
defer cancel() // Always clean up!
req, _ := http.NewRequestWithContext(
ctx,
"GET",
"https://slow-api.example.com",
nil,
)
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
// Check if cancelled
if ctx.Err() == context.DeadlineExceeded {
fmt.Println("Too slow! Cancelled.")
}
return
}
defer resp.Body.Close()
}
Manual Cancellation
func manualCancel() {
ctx, cancel := context.WithCancel(
context.Background(),
)
// In another goroutine, cancel when needed
go func() {
time.Sleep(2 * time.Second)
cancel() // "Stop everything!"
}()
req, _ := http.NewRequestWithContext(
ctx, "GET", "https://api.example.com", nil,
)
// Request will be cancelled after 2 seconds
}
โฑ๏ธ HTTP Timeouts: Donโt Wait Forever!
Timeouts are like telling the post office: โIf no reply in 10 minutes, forget it!โ
Client Timeouts
func clientWithTimeout() {
client := &http.Client{
Timeout: 10 * time.Second, // Total timeout
}
resp, err := client.Get("https://api.example.com")
if err != nil {
// Might be timeout!
fmt.Println("Request failed:", err)
return
}
defer resp.Body.Close()
}
Fine-Grained Control
import "net"
func advancedTimeouts() {
transport := &http.Transport{
DialContext: (&net.Dialer{
Timeout: 5 * time.Second, // Connect
KeepAlive: 30 * time.Second,
}).DialContext,
TLSHandshakeTimeout: 5 * time.Second,
ResponseHeaderTimeout: 10 * time.Second,
IdleConnTimeout: 90 * time.Second,
}
client := &http.Client{
Transport: transport,
Timeout: 30 * time.Second, // Total
}
client.Get("https://api.example.com")
}
Server Timeouts
func serverWithTimeouts() {
server := &http.Server{
Addr: ":8080",
ReadTimeout: 5 * time.Second, // Read request
WriteTimeout: 10 * time.Second, // Write response
IdleTimeout: 120 * time.Second, // Keep-alive
}
server.ListenAndServe()
}
๐ HTTP Middleware: The Security Guards and Helpers
Middleware are like helpful people who check your letter BEFORE it reaches the final destination. They can check ID, stamp timestamps, or even translate!
graph TD A["Request Arrives"] --> B["Middleware 1: Logging"] B --> C["Middleware 2: Auth Check"] C --> D{Allowed?} D -->|Yes| E["Your Handler"] D -->|No| F["Reject: 401"] E --> G["Response"] G --> B
Creating Middleware
// Middleware is a function that wraps a handler
func loggingMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
// Before: Log the arrival
fmt.Printf("[%s] %s %s\n",
time.Now().Format("15:04:05"),
r.Method,
r.URL.Path,
)
// Pass to next handler
next.ServeHTTP(w, r)
// After: Could log response time
},
)
}
Authentication Middleware
func authMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
token := r.Header.Get("Authorization")
if token == "" {
http.Error(w, "No token!",
http.StatusUnauthorized)
return
}
if !isValidToken(token) {
http.Error(w, "Bad token!",
http.StatusForbidden)
return
}
// Token OK! Continue
next.ServeHTTP(w, r)
},
)
}
Chaining Middleware Together
func main() {
// Your actual handler
finalHandler := http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Secret data!")
},
)
// Wrap with middleware (order matters!)
// Request goes: logging -> auth -> handler
wrapped := loggingMiddleware(
authMiddleware(finalHandler),
)
http.Handle("/secret", wrapped)
http.ListenAndServe(":8080", nil)
}
CORS Middleware Example
func corsMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
// Allow cross-origin requests
w.Header().Set(
"Access-Control-Allow-Origin", "*",
)
w.Header().Set(
"Access-Control-Allow-Methods",
"GET, POST, PUT, DELETE",
)
// Handle preflight
if r.Method == "OPTIONS" {
w.WriteHeader(http.StatusOK)
return
}
next.ServeHTTP(w, r)
},
)
}
๐ฏ Quick Reference: Your HTTP Toolbox
| Task | Tool | Example |
|---|---|---|
| Simple GET | http.Get() |
http.Get("url") |
| POST data | http.Post() |
http.Post("url", "type", body) |
| Custom request | http.NewRequest() |
Full control |
| Add headers | req.Header.Set() |
Auth, Content-Type |
| Handle cookies | cookiejar.Jar |
Auto cookie management |
| Parse URL | url.Parse() |
Break down address |
| Build URL | url.Values |
Safe query building |
| Start server | http.ListenAndServe() |
Open for business |
| Cancel request | context.WithTimeout() |
Prevent hanging |
| Set timeout | client.Timeout |
Donโt wait forever |
| Add middleware | Wrap handlers | Logging, auth, CORS |
๐ Youโre Now an HTTP Master!
Youโve learned:
- โ How to send requests (GET, POST, custom)
- โ Adding special instructions (headers) and membership cards (cookies)
- โ Building smart addresses with query parameters
- โ
Taking apart URLs with
net/url - โ Creating your own web server
- โ Cancelling slow requests with context
- โ Setting timeouts so you never wait forever
- โ Building middleware chains for security and logging
Remember: HTTP is just sending letters on the internet. Now go build something amazing! ๐
