Go HTTP Server
The HTTP server in Go is built around the net/http
package, which provides a robust and efficient foundation for building web applications. Go's HTTP server is designed for high performance and concurrency, making it ideal for building scalable web services and APIs. Understanding HTTP server fundamentals is crucial for building any web application in Go. This comprehensive guide will teach you everything you need to know about creating and managing HTTP servers in Go.
Understanding HTTP Servers in Go
What Is an HTTP Server?
An HTTP server in Go is a program that listens for HTTP requests and responds with HTTP responses. It provides:
- Request Processing - Handling incoming HTTP requests
- Response Generation - Creating and sending HTTP responses
- Concurrent Handling - Processing multiple requests simultaneously
- Middleware Support - Implementing cross-cutting concerns
- Configuration Management - Server settings and optimization
HTTP Server Architecture
Request-Response Cycle
Every HTTP request goes through a complete lifecycle from reception to response.
Handler Functions
Functions that process HTTP requests and generate responses.
Server Configuration
Settings that control server behavior and performance.
Basic HTTP Server
Creating a Simple HTTP Server
The http.ListenAndServe
Function
The simplest way to create an HTTP server in Go.
Handler Functions
Functions that implement the http.Handler
interface.
package main
import (
"fmt"
"net/http"
"time"
)
func main() {
// Basic HTTP server examples
fmt.Println("Basic HTTP server examples:")
// Simple HTTP server
func simpleHTTPServer() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello, World! Welcome to Go HTTP Server")
})
http.HandleFunc("/time", func(w http.ResponseWriter, r *http.Request) {
currentTime := time.Now().Format("2006-01-02 15:04:05")
fmt.Fprintf(w, "Current time: %s", currentTime)
})
fmt.Println("Server starting on :8080")
http.ListenAndServe(":8080", nil)
}
// Start server in goroutine to avoid blocking
go simpleHTTPServer()
time.Sleep(2 * time.Second)
// HTTP server with custom handler
func customHandlerServer() {
type CustomHandler struct {
name string
}
func (h *CustomHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello from %s handler! Path: %s", h.name, r.URL.Path)
}
handler := &CustomHandler{name: "Custom"}
http.Handle("/custom/", handler)
fmt.Println("Custom handler server starting on :8081")
http.ListenAndServe(":8081", nil)
}
// Start custom handler server
go customHandlerServer()
time.Sleep(2 * time.Second)
// HTTP server with multiple handlers
func multipleHandlersServer() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Welcome to the homepage!")
})
http.HandleFunc("/about", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "About page: This is a Go HTTP server")
})
http.HandleFunc("/contact", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Contact page: Get in touch with us")
})
http.HandleFunc("/api/status", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
fmt.Fprintf(w, `{"status": "ok", "message": "Server is running"}`)
})
fmt.Println("Multiple handlers server starting on :8082")
http.ListenAndServe(":8082", nil)
}
// Start multiple handlers server
go multipleHandlersServer()
time.Sleep(2 * time.Second)
fmt.Println("All servers started successfully!")
}
HTTP Handler Interface
The http.Handler
Interface
Understanding the handler interface and its methods.
Handler Function Types
Different types of handler functions in Go.
package main
import (
"fmt"
"net/http"
"time"
)
func main() {
// HTTP handler interface examples
fmt.Println("HTTP handler interface examples:")
// HandlerFunc type
func handlerFuncExample() {
var handler http.HandlerFunc = func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "HandlerFunc: Path is %s", r.URL.Path)
}
http.Handle("/handlerfunc/", handler)
fmt.Println("HandlerFunc server starting on :8083")
http.ListenAndServe(":8083", nil)
}
// Start HandlerFunc server
go handlerFuncExample()
time.Sleep(2 * time.Second)
// Custom handler struct
func customHandlerStructExample() {
type UserHandler struct {
users map[string]string
}
func (h *UserHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case "GET":
h.handleGetUser(w, r)
case "POST":
h.handleCreateUser(w, r)
default:
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
}
}
func (h *UserHandler) handleGetUser(w http.ResponseWriter, r *http.Request) {
userID := r.URL.Path[len("/users/"):]
if user, exists := h.users[userID]; exists {
fmt.Fprintf(w, "User %s: %s", userID, user)
} else {
http.Error(w, "User not found", http.StatusNotFound)
}
}
func (h *UserHandler) handleCreateUser(w http.ResponseWriter, r *http.Request) {
userID := r.URL.Path[len("/users/"):]
h.users[userID] = "New User"
fmt.Fprintf(w, "User %s created successfully", userID)
}
handler := &UserHandler{
users: make(map[string]string),
}
http.Handle("/users/", handler)
fmt.Println("Custom handler struct server starting on :8084")
http.ListenAndServe(":8084", nil)
}
// Start custom handler struct server
go customHandlerStructExample()
time.Sleep(2 * time.Second)
// Handler with middleware
func handlerWithMiddlewareExample() {
func loggingMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
next.ServeHTTP(w, r)
fmt.Printf("Request: %s %s took %v\n", r.Method, r.URL.Path, time.Since(start))
})
}
func authMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
token := r.Header.Get("Authorization")
if token != "Bearer valid-token" {
http.Error(w, "Unauthorized", http.StatusUnauthorized)
return
}
next.ServeHTTP(w, r)
})
}
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Protected resource: %s", r.URL.Path)
})
protectedHandler := authMiddleware(loggingMiddleware(handler))
http.Handle("/protected/", protectedHandler)
fmt.Println("Handler with middleware server starting on :8085")
http.ListenAndServe(":8085", nil)
}
// Start handler with middleware server
go handlerWithMiddlewareExample()
time.Sleep(2 * time.Second)
fmt.Println("All handler examples completed!")
}
Request Handling
Understanding HTTP Requests
The http.Request
Type
Understanding the structure and fields of HTTP requests.
Request Methods and Headers
Working with HTTP methods and headers.
package main
import (
"fmt"
"io"
"net/http"
"time"
)
func main() {
// Request handling examples
fmt.Println("Request handling examples:")
// Basic request handling
func basicRequestHandling() {
http.HandleFunc("/request", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Method: %s\n", r.Method)
fmt.Fprintf(w, "URL: %s\n", r.URL.String())
fmt.Fprintf(w, "Host: %s\n", r.Host)
fmt.Fprintf(w, "RemoteAddr: %s\n", r.RemoteAddr)
fmt.Fprintf(w, "UserAgent: %s\n", r.UserAgent())
})
fmt.Println("Basic request handling server starting on :8086")
http.ListenAndServe(":8086", nil)
}
// Start basic request handling server
go basicRequestHandling()
time.Sleep(2 * time.Second)
// Request headers handling
func requestHeadersHandling() {
http.HandleFunc("/headers", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Request Headers:\n")
for name, values := range r.Header {
for _, value := range values {
fmt.Fprintf(w, "%s: %s\n", name, value)
}
}
})
fmt.Println("Request headers handling server starting on :8087")
http.ListenAndServe(":8087", nil)
}
// Start request headers handling server
go requestHeadersHandling()
time.Sleep(2 * time.Second)
// Request body handling
func requestBodyHandling() {
http.HandleFunc("/body", func(w http.ResponseWriter, r *http.Request) {
if r.Method != "POST" {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}
body, err := io.ReadAll(r.Body)
if err != nil {
http.Error(w, "Error reading body", http.StatusBadRequest)
return
}
defer r.Body.Close()
fmt.Fprintf(w, "Request body: %s\n", string(body))
fmt.Fprintf(w, "Content-Type: %s\n", r.Header.Get("Content-Type"))
fmt.Fprintf(w, "Content-Length: %d\n", r.ContentLength)
})
fmt.Println("Request body handling server starting on :8088")
http.ListenAndServe(":8088", nil)
}
// Start request body handling server
go requestBodyHandling()
time.Sleep(2 * time.Second)
// Query parameters handling
func queryParametersHandling() {
http.HandleFunc("/search", func(w http.ResponseWriter, r *http.Request) {
query := r.URL.Query()
fmt.Fprintf(w, "Query parameters:\n")
for key, values := range query {
for _, value := range values {
fmt.Fprintf(w, "%s = %s\n", key, value)
}
}
// Get specific parameter
if name := query.Get("name"); name != "" {
fmt.Fprintf(w, "Name parameter: %s\n", name)
}
// Get multiple values for same parameter
if tags := query["tag"]; len(tags) > 0 {
fmt.Fprintf(w, "Tags: %v\n", tags)
}
})
fmt.Println("Query parameters handling server starting on :8089")
http.ListenAndServe(":8089", nil)
}
// Start query parameters handling server
go queryParametersHandling()
time.Sleep(2 * time.Second)
// Form data handling
func formDataHandling() {
http.HandleFunc("/form", func(w http.ResponseWriter, r *http.Request) {
if r.Method != "POST" {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}
// Parse form data
err := r.ParseForm()
if err != nil {
http.Error(w, "Error parsing form", http.StatusBadRequest)
return
}
fmt.Fprintf(w, "Form data:\n")
for key, values := range r.Form {
for _, value := range values {
fmt.Fprintf(w, "%s = %s\n", key, value)
}
}
// Get specific form field
if name := r.FormValue("name"); name != "" {
fmt.Fprintf(w, "Name: %s\n", name)
}
// Get multiple values for same field
if hobbies := r.Form["hobby"]; len(hobbies) > 0 {
fmt.Fprintf(w, "Hobbies: %v\n", hobbies)
}
})
fmt.Println("Form data handling server starting on :8090")
http.ListenAndServe(":8090", nil)
}
// Start form data handling server
go formDataHandling()
time.Sleep(2 * time.Second)
fmt.Println("All request handling examples completed!")
}
Response Generation
Understanding HTTP Responses
The http.ResponseWriter
Interface
Understanding how to generate HTTP responses.
Response Headers and Status Codes
Setting response headers and status codes.
package main
import (
"fmt"
"net/http"
"time"
)
func main() {
// Response generation examples
fmt.Println("Response generation examples:")
// Basic response generation
func basicResponseGeneration() {
http.HandleFunc("/response", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, "Basic response generated successfully")
})
fmt.Println("Basic response generation server starting on :8091")
http.ListenAndServe(":8091", nil)
}
// Start basic response generation server
go basicResponseGeneration()
time.Sleep(2 * time.Second)
// Response headers
func responseHeaders() {
http.HandleFunc("/headers", func(w http.ResponseWriter, r *http.Request) {
// Set custom headers
w.Header().Set("Content-Type", "application/json")
w.Header().Set("X-Custom-Header", "Custom Value")
w.Header().Set("X-Response-Time", time.Now().Format(time.RFC3339))
// Set multiple values for same header
w.Header().Add("X-Multiple-Values", "Value1")
w.Header().Add("X-Multiple-Values", "Value2")
fmt.Fprintf(w, `{"message": "Response with custom headers"}`)
})
fmt.Println("Response headers server starting on :8092")
http.ListenAndServe(":8092", nil)
}
// Start response headers server
go responseHeaders()
time.Sleep(2 * time.Second)
// HTTP status codes
func httpStatusCodes() {
http.HandleFunc("/status/200", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, "Status 200: OK")
})
http.HandleFunc("/status/201", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusCreated)
fmt.Fprintf(w, "Status 201: Created")
})
http.HandleFunc("/status/400", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusBadRequest)
fmt.Fprintf(w, "Status 400: Bad Request")
})
http.HandleFunc("/status/404", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusNotFound)
fmt.Fprintf(w, "Status 404: Not Found")
})
http.HandleFunc("/status/500", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusInternalServerError)
fmt.Fprintf(w, "Status 500: Internal Server Error")
})
fmt.Println("HTTP status codes server starting on :8093")
http.ListenAndServe(":8093", nil)
}
// Start HTTP status codes server
go httpStatusCodes()
time.Sleep(2 * time.Second)
// JSON response
func jsonResponse() {
type User struct {
ID int `json:"id"`
Name string `json:"name"`
Email string `json:"email"`
}
http.HandleFunc("/json", func(w http.ResponseWriter, r *http.Request) {
user := User{
ID: 1,
Name: "John Doe",
Email: "[email protected]",
}
w.Header().Set("Content-Type", "application/json")
fmt.Fprintf(w, `{"id": %d, "name": "%s", "email": "%s"}`,
user.ID, user.Name, user.Email)
})
fmt.Println("JSON response server starting on :8094")
http.ListenAndServe(":8094", nil)
}
// Start JSON response server
go jsonResponse()
time.Sleep(2 * time.Second)
// File response
func fileResponse() {
http.HandleFunc("/download", func(w http.ResponseWriter, r *http.Request) {
filename := "example.txt"
content := "This is an example file content for download."
w.Header().Set("Content-Type", "text/plain")
w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=%s", filename))
w.Header().Set("Content-Length", fmt.Sprintf("%d", len(content)))
fmt.Fprintf(w, content)
})
fmt.Println("File response server starting on :8095")
http.ListenAndServe(":8095", nil)
}
// Start file response server
go fileResponse()
time.Sleep(2 * time.Second)
// Redirect response
func redirectResponse() {
http.HandleFunc("/redirect", func(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, "/target", http.StatusMovedPermanently)
})
http.HandleFunc("/target", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Redirected successfully!")
})
fmt.Println("Redirect response server starting on :8096")
http.ListenAndServe(":8096", nil)
}
// Start redirect response server
go redirectResponse()
time.Sleep(2 * time.Second)
fmt.Println("All response generation examples completed!")
}
Server Configuration
Advanced Server Configuration
The http.Server
Type
Using the Server struct for advanced configuration.
Server Settings and Optimization
Configuring server performance and behavior.
package main
import (
"context"
"fmt"
"net/http"
"time"
)
func main() {
// Server configuration examples
fmt.Println("Server configuration examples:")
// Basic server configuration
func basicServerConfiguration() {
server := &http.Server{
Addr: ":8097",
Handler: nil,
ReadTimeout: 10 * time.Second,
WriteTimeout: 10 * time.Second,
}
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Server with custom configuration")
})
fmt.Println("Basic server configuration starting on :8097")
server.ListenAndServe()
}
// Start basic server configuration
go basicServerConfiguration()
time.Sleep(2 * time.Second)
// Advanced server configuration
func advancedServerConfiguration() {
server := &http.Server{
Addr: ":8098",
Handler: nil,
ReadTimeout: 15 * time.Second,
WriteTimeout: 15 * time.Second,
IdleTimeout: 60 * time.Second,
MaxHeaderBytes: 1 << 20, // 1 MB
}
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Advanced server configuration")
})
fmt.Println("Advanced server configuration starting on :8098")
server.ListenAndServe()
}
// Start advanced server configuration
go advancedServerConfiguration()
time.Sleep(2 * time.Second)
// Server with custom handler
func serverWithCustomHandler() {
mux := http.NewServeMux()
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Custom handler server")
})
mux.HandleFunc("/api/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "API endpoint")
})
server := &http.Server{
Addr: ":8099",
Handler: mux,
ReadTimeout: 10 * time.Second,
WriteTimeout: 10 * time.Second,
}
fmt.Println("Server with custom handler starting on :8099")
server.ListenAndServe()
}
// Start server with custom handler
go serverWithCustomHandler()
time.Sleep(2 * time.Second)
// Server with graceful shutdown
func serverWithGracefulShutdown() {
mux := http.NewServeMux()
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Server with graceful shutdown")
})
server := &http.Server{
Addr: ":8100",
Handler: mux,
ReadTimeout: 10 * time.Second,
WriteTimeout: 10 * time.Second,
}
go func() {
fmt.Println("Server with graceful shutdown starting on :8100")
if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
fmt.Printf("Server error: %v\n", err)
}
}()
// Simulate shutdown after some time
time.Sleep(2 * time.Second)
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
if err := server.Shutdown(ctx); err != nil {
fmt.Printf("Server shutdown error: %v\n", err)
} else {
fmt.Println("Server shutdown gracefully")
}
}
// Start server with graceful shutdown
serverWithGracefulShutdown()
fmt.Println("All server configuration examples completed!")
}
What You've Learned
Congratulations! You now have a comprehensive understanding of Go's HTTP server capabilities:
Basic HTTP Server
- Creating simple HTTP servers with
http.ListenAndServe
- Understanding handler functions and the
http.Handler
interface - Implementing custom handlers and middleware
Request Handling
- Working with HTTP requests and their properties
- Handling request headers, body, and query parameters
- Processing form data and different content types
Response Generation
- Generating HTTP responses with proper status codes
- Setting response headers and content types
- Creating JSON responses and file downloads
Server Configuration
- Using the
http.Server
struct for advanced configuration - Implementing graceful shutdown and timeout settings
- Optimizing server performance and behavior
Key Concepts
http.Handler
- Interface for handling HTTP requestshttp.ResponseWriter
- Interface for writing HTTP responseshttp.Request
- Structure representing HTTP requestshttp.Server
- Advanced server configuration- Middleware - Cross-cutting concerns in request processing
Next Steps
You now have a solid foundation in Go's HTTP server capabilities. In the next section, we'll explore routing and middleware patterns, which will help you organize and structure your web applications effectively.
Understanding HTTP server fundamentals is crucial for building any web application in Go. These concepts form the foundation for all the more advanced web development techniques we'll cover in the coming sections.
Ready to learn about routing and middleware? Let's explore URL routing patterns and learn how to implement middleware for cross-cutting concerns!