Server

Start HTTP, HTTPS, and mTLS servers with graceful shutdown.

HTTP Server

Basic HTTP Server

Start an HTTP server:

ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
defer cancel()

if err := a.Start(ctx); err != nil {
    log.Fatal(err)
}

Custom Address

Configure the listen address via options when creating the app. Default is :8080 for HTTP and :8443 for TLS/mTLS:

// Localhost only
a, err := app.New(
    app.WithServiceName("my-api"),
    app.WithHost("127.0.0.1"),
    app.WithPort(8080),
)
// ...
a.Start(ctx)

// All interfaces (default)
a, err := app.New(
    app.WithServiceName("my-api"),
    app.WithPort(8080),
)
// ...
a.Start(ctx)

HTTPS Server

Start HTTPS Server

Configure TLS at construction with WithTLS, then start the server (default port 8443; use WithPort(443) to override):

a := app.MustNew(
    app.WithServiceName("my-api"),
    app.WithTLS("server.crt", "server.key"),
)
// ... register routes ...

ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
defer cancel()

if err := a.Start(ctx); err != nil {
    log.Fatal(err)
}

Generate Self-Signed Certificate

For development:

openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365 -nodes

mTLS Server

Start mTLS Server

Configure mTLS at construction with WithMTLS, then start the server:

// Load server certificate
serverCert, err := tls.LoadX509KeyPair("server.crt", "server.key")
if err != nil {
    log.Fatal(err)
}

// Load CA certificate for client validation
caCert, err := os.ReadFile("ca.crt")
if err != nil {
    log.Fatal(err)
}
caCertPool := x509.NewCertPool()
caCertPool.AppendCertsFromPEM(caCert)

a := app.MustNew(
    app.WithServiceName("my-api"),
    app.WithMTLS(serverCert,
        app.WithClientCAs(caCertPool),
        app.WithMinVersion(tls.VersionTLS13),
    ), // default port 8443; use WithPort(443) to override
)
// ... register routes ...

ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
defer cancel()

if err := a.Start(ctx); err != nil {
    log.Fatal(err)
}

Client Authorization

Authorize clients based on certificate by adding WithAuthorize to WithMTLS:

a := app.MustNew(
    app.WithServiceName("my-api"),
    app.WithPort(8443),
    app.WithMTLS(serverCert,
        app.WithClientCAs(caCertPool),
        app.WithAuthorize(func(cert *x509.Certificate) (string, bool) {
            principal := cert.Subject.CommonName
            if principal == "" {
                return "", false
            }
            return principal, true
        }),
    ),
)
// ...
if err := a.Start(ctx); err != nil { ... }

Graceful Shutdown

Signal-Based Shutdown

Use signal.NotifyContext for graceful shutdown:

ctx, cancel := signal.NotifyContext(
    context.Background(),
    os.Interrupt,
    syscall.SIGTERM,
)
defer cancel()

if err := a.Start(ctx); err != nil {
    log.Fatal(err)
}

Shutdown Process

When context is canceled:

  1. Server stops accepting new connections
  2. OnShutdown hooks execute (LIFO order)
  3. Server waits for in-flight requests (up to shutdown timeout)
  4. Observability components shut down (metrics, tracing)
  5. OnStop hooks execute (best-effort)
  6. Process exits

Shutdown Timeout

Configure shutdown timeout:

a, err := app.New(
    app.WithServer(
        app.WithShutdownTimeout(30 * time.Second),
    ),
)

Default: 30 seconds

Complete Examples

HTTP with Graceful Shutdown

package main

import (
    "context"
    "log"
    "os"
    "os/signal"
    "syscall"
    
    "rivaas.dev/app"
)

func main() {
    a := app.MustNew(
        app.WithServiceName("api"),
    )
    
    a.GET("/", homeHandler)
    
    ctx, cancel := signal.NotifyContext(
        context.Background(),
        os.Interrupt,
        syscall.SIGTERM,
    )
    defer cancel()
    
    log.Println("Server starting on :8080")
    if err := a.Start(ctx); err != nil {
        log.Fatal(err)
    }
}

HTTPS with mTLS

package main

import (
    "context"
    "crypto/tls"
    "crypto/x509"
    "log"
    "os"
    "os/signal"
    "syscall"
    
    "rivaas.dev/app"
)

func main() {
    serverCert, err := tls.LoadX509KeyPair("server.crt", "server.key")
    if err != nil {
        log.Fatal(err)
    }
    
    caCert, err := os.ReadFile("ca.crt")
    if err != nil {
        log.Fatal(err)
    }
    caCertPool := x509.NewCertPool()
    caCertPool.AppendCertsFromPEM(caCert)
    
    a := app.MustNew(
        app.WithServiceName("secure-api"),
        app.WithMTLS(serverCert,
            app.WithClientCAs(caCertPool),
            app.WithMinVersion(tls.VersionTLS13),
        ), // default port 8443
    )
    a.GET("/", homeHandler)
    
    ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
    defer cancel()
    
    log.Println("mTLS server starting on :8443 (default)")
    if err := a.Start(ctx); err != nil {
        log.Fatal(err)
    }
}

Next Steps