Skip to content

Instantly share code, notes, and snippets.

@dstroot
Forked from enricofoltran/main.go
Last active April 10, 2023 13:22
Show Gist options
  • Save dstroot/faef20a6c863b5fa8e3438f6055e36cd to your computer and use it in GitHub Desktop.
Save dstroot/faef20a6c863b5fa8e3438f6055e36cd to your computer and use it in GitHub Desktop.

Revisions

  1. Dan Stroot revised this gist Jan 3, 2020. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion main.go
    Original file line number Diff line number Diff line change
    @@ -15,7 +15,7 @@ func main() {
    router := newRouter()
    server := newServer(
    listenAddr,
    (middlewares{tracing(func() string { return fmt.Sprintf("%d", time.Now().UnixNano()) }), logging(logger)}).apply(router),
    (middlewares{ logging(logger), tracing(func() string { return fmt.Sprintf("%d", time.Now().UnixNano()) })}).apply(router),
    logger,
    )

  2. Dan Stroot revised this gist Jan 8, 2018. 2 changed files with 4 additions and 4 deletions.
    4 changes: 2 additions & 2 deletions main.go
    Original file line number Diff line number Diff line change
    @@ -13,14 +13,14 @@ func main() {
    // create a logger, router and server
    logger := log.New(os.Stdout, "http: ", log.LstdFlags)
    router := newRouter()
    server := NewServer(
    server := newServer(
    listenAddr,
    (middlewares{tracing(func() string { return fmt.Sprintf("%d", time.Now().UnixNano()) }), logging(logger)}).apply(router),
    logger,
    )

    // run our server
    if err := server.Run(); err != nil {
    if err := server.run(); err != nil {
    log.Fatal(err)
    }
    }
    4 changes: 2 additions & 2 deletions server.go
    Original file line number Diff line number Diff line change
    @@ -22,7 +22,7 @@ type Server struct {
    }

    // NewServer creates a new HTTP Server
    func NewServer(port string, h http.Handler, l *log.Logger) *Server {
    func newServer(port string, h http.Handler, l *log.Logger) *Server {
    return &Server{
    server: &http.Server{
    Addr: ":" + port,
    @@ -37,7 +37,7 @@ func NewServer(port string, h http.Handler, l *log.Logger) *Server {
    }

    // Run starts the HTTP server
    func (s *Server) Run() error {
    func (s *Server) run() error {

    // Get hostname
    hostname, err := os.Hostname()
  3. Dan Stroot revised this gist Jan 8, 2018. 5 changed files with 188 additions and 116 deletions.
    31 changes: 31 additions & 0 deletions handlers.go
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,31 @@
    package main

    import (
    "io"
    "net/http"
    "sync/atomic"
    )

    func index() http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    if r.URL.Path != "/" {
    http.NotFound(w, r)
    return
    }
    w.Header().Set("X-Content-Type-Options", "nosniff")
    w.Header().Set("Content-Type", "text/plain; charset=utf-8")
    io.WriteString(w, "Hello, World!")
    })
    }

    func healthz() http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    if atomic.LoadInt32(&healthy) == 1 {
    w.Header().Set("X-Content-Type-Options", "nosniff")
    w.Header().Set("Content-Type", "application/json; charset=utf-8")
    io.WriteString(w, `{"alive": true}`)
    return
    }
    w.WriteHeader(http.StatusServiceUnavailable)
    })
    }
    129 changes: 13 additions & 116 deletions main.go
    Original file line number Diff line number Diff line change
    @@ -1,129 +1,26 @@
    package main

    import (
    "context"
    "flag"
    "fmt"
    "log"
    "net/http"
    "os"
    "os/signal"
    "sync/atomic"
    "time"
    )

    type key int

    const (
    requestIDKey key = 0
    )

    var (
    listenAddr string
    healthy int32
    )

    func main() {
    flag.StringVar(&listenAddr, "listen-addr", ":5000", "server listen address")
    flag.Parse()
    listenAddr := "5000"

    // create a logger, router and server
    logger := log.New(os.Stdout, "http: ", log.LstdFlags)
    logger.Println("Server is starting...")

    router := http.NewServeMux()
    router.Handle("/", index())
    router.Handle("/healthz", healthz())

    nextRequestID := func() string {
    return fmt.Sprintf("%d", time.Now().UnixNano())
    }

    server := &http.Server{
    Addr: listenAddr,
    Handler: tracing(nextRequestID)(logging(logger)(router)),
    ErrorLog: logger,
    ReadTimeout: 5 * time.Second,
    WriteTimeout: 10 * time.Second,
    IdleTimeout: 15 * time.Second,
    }

    done := make(chan bool)
    quit := make(chan os.Signal, 1)
    signal.Notify(quit, os.Interrupt)

    go func() {
    <-quit
    logger.Println("Server is shutting down...")
    atomic.StoreInt32(&healthy, 0)

    ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
    defer cancel()

    server.SetKeepAlivesEnabled(false)
    if err := server.Shutdown(ctx); err != nil {
    logger.Fatalf("Could not gracefully shutdown the server: %v\n", err)
    }
    close(done)
    }()

    logger.Println("Server is ready to handle requests at", listenAddr)
    atomic.StoreInt32(&healthy, 1)
    if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
    logger.Fatalf("Could not listen on %s: %v\n", listenAddr, err)
    }

    <-done
    logger.Println("Server stopped")
    }

    func index() http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    if r.URL.Path != "/" {
    http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
    return
    }
    w.Header().Set("Content-Type", "text/plain; charset=utf-8")
    w.Header().Set("X-Content-Type-Options", "nosniff")
    w.WriteHeader(http.StatusOK)
    fmt.Fprintln(w, "Hello, World!")
    })
    }

    func healthz() http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    if atomic.LoadInt32(&healthy) == 1 {
    w.WriteHeader(http.StatusNoContent)
    return
    }
    w.WriteHeader(http.StatusServiceUnavailable)
    })
    }

    func logging(logger *log.Logger) func(http.Handler) http.Handler {
    return func(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    defer func() {
    requestID, ok := r.Context().Value(requestIDKey).(string)
    if !ok {
    requestID = "unknown"
    }
    logger.Println(requestID, r.Method, r.URL.Path, r.RemoteAddr, r.UserAgent())
    }()
    next.ServeHTTP(w, r)
    })
    }
    }

    func tracing(nextRequestID func() string) func(http.Handler) http.Handler {
    return func(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    requestID := r.Header.Get("X-Request-Id")
    if requestID == "" {
    requestID = nextRequestID()
    }
    ctx := context.WithValue(r.Context(), requestIDKey, requestID)
    w.Header().Set("X-Request-Id", requestID)
    next.ServeHTTP(w, r.WithContext(ctx))
    })
    router := newRouter()
    server := NewServer(
    listenAddr,
    (middlewares{tracing(func() string { return fmt.Sprintf("%d", time.Now().UnixNano()) }), logging(logger)}).apply(router),
    logger,
    )

    // run our server
    if err := server.Run(); err != nil {
    log.Fatal(err)
    }
    }
    }
    52 changes: 52 additions & 0 deletions middleware.go
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,52 @@
    package main

    import (
    "context"
    "log"
    "net/http"
    )

    type key int

    const (
    requestIDKey key = 0
    )

    type middleware func(http.Handler) http.Handler
    type middlewares []middleware

    func (mws middlewares) apply(hdlr http.Handler) http.Handler {
    if len(mws) == 0 {
    return hdlr
    }
    return mws[1:].apply(mws[0](hdlr))
    }

    func logging(logger *log.Logger) func(http.Handler) http.Handler {
    return func(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    defer func() {
    requestID, ok := r.Context().Value(requestIDKey).(string)
    if !ok {
    requestID = "unknown"
    }
    logger.Println(requestID, r.Method, r.URL.Path, r.RemoteAddr, r.UserAgent())
    }()
    next.ServeHTTP(w, r)
    })
    }
    }

    func tracing(nextRequestID func() string) func(http.Handler) http.Handler {
    return func(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    requestID := r.Header.Get("X-Request-Id")
    if requestID == "" {
    requestID = nextRequestID()
    }
    ctx := context.WithValue(r.Context(), requestIDKey, requestID)
    w.Header().Set("X-Request-Id", requestID)
    next.ServeHTTP(w, r.WithContext(ctx))
    })
    }
    }
    15 changes: 15 additions & 0 deletions router.go
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,15 @@
    package main

    import (
    "net/http"
    )

    func newRouter() *http.ServeMux {
    router := http.NewServeMux()

    // routes
    router.Handle("/", index())
    router.Handle("/healthz", healthz())

    return router
    }
    77 changes: 77 additions & 0 deletions server.go
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,77 @@
    package main

    import (
    "context"
    "fmt"
    "log"
    "net/http"
    "os"
    "os/signal"
    "sync/atomic"
    "syscall"
    "time"
    )

    var (
    healthy int32
    )

    // Server implements our HTTP server
    type Server struct {
    server *http.Server
    }

    // NewServer creates a new HTTP Server
    func NewServer(port string, h http.Handler, l *log.Logger) *Server {
    return &Server{
    server: &http.Server{
    Addr: ":" + port,
    Handler: h, // pass in mux/router
    ErrorLog: l,
    ReadTimeout: 5 * time.Second,
    WriteTimeout: 10 * time.Second,
    IdleTimeout: 30 * time.Second,
    MaxHeaderBytes: 1 << 20,
    },
    }
    }

    // Run starts the HTTP server
    func (s *Server) Run() error {

    // Get hostname
    hostname, err := os.Hostname()
    if err != nil {
    return err
    }

    done := make(chan bool)
    quit := make(chan os.Signal, 1)
    signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)

    go func() {
    <-quit
    fmt.Println("")
    s.server.ErrorLog.Printf("%s - Shutdown signal received...\n", hostname)
    atomic.StoreInt32(&healthy, 0)

    ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
    defer cancel()

    s.server.SetKeepAlivesEnabled(false)
    if err := s.server.Shutdown(ctx); err != nil {
    s.server.ErrorLog.Fatalf("Could not gracefully shutdown the server: %v\n", err)
    }
    close(done)
    }()

    s.server.ErrorLog.Printf("%s - Starting server on port %v", hostname, s.server.Addr)
    atomic.StoreInt32(&healthy, 1)
    if err := s.server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
    s.server.ErrorLog.Fatalf("Could not listen on %s: %v\n", s.server.Addr, err)
    }

    <-done
    s.server.ErrorLog.Printf("%s - Server gracefully stopped.\n", hostname)
    return nil
    }
  4. Enrico Foltran revised this gist Jan 7, 2018. 1 changed file with 5 additions and 2 deletions.
    7 changes: 5 additions & 2 deletions main.go
    Original file line number Diff line number Diff line change
    @@ -47,6 +47,7 @@ func main() {
    IdleTimeout: 15 * time.Second,
    }

    done := make(chan bool)
    quit := make(chan os.Signal, 1)
    signal.Notify(quit, os.Interrupt)

    @@ -55,21 +56,23 @@ func main() {
    logger.Println("Server is shutting down...")
    atomic.StoreInt32(&healthy, 0)

    ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
    ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
    defer cancel()

    server.SetKeepAlivesEnabled(false)
    if err := server.Shutdown(ctx); err != nil {
    logger.Fatalf("Could not gracefully shutdown the server: %v\n", err)
    }
    close(done)
    }()

    logger.Println("Server is ready to handle requests at", listenAddr)
    atomic.StoreInt32(&healthy, 1)
    if err := server.ListenAndServe(); err != http.ErrServerClosed {
    if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
    logger.Fatalf("Could not listen on %s: %v\n", listenAddr, err)
    }

    <-done
    logger.Println("Server stopped")
    }

  5. Enrico Foltran revised this gist Jan 7, 2018. 1 changed file with 2 additions and 2 deletions.
    4 changes: 2 additions & 2 deletions main.go
    Original file line number Diff line number Diff line change
    @@ -52,15 +52,15 @@ func main() {

    go func() {
    <-quit
    logger.Println("Server is shoutting down...")
    logger.Println("Server is shutting down...")
    atomic.StoreInt32(&healthy, 0)

    ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
    defer cancel()

    server.SetKeepAlivesEnabled(false)
    if err := server.Shutdown(ctx); err != nil {
    logger.Fatalf("Could not gracefully shoutdown the server: %v\n", err)
    logger.Fatalf("Could not gracefully shutdown the server: %v\n", err)
    }
    }()

  6. Enrico Foltran created this gist Jan 7, 2018.
    126 changes: 126 additions & 0 deletions main.go
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,126 @@
    package main

    import (
    "context"
    "flag"
    "fmt"
    "log"
    "net/http"
    "os"
    "os/signal"
    "sync/atomic"
    "time"
    )

    type key int

    const (
    requestIDKey key = 0
    )

    var (
    listenAddr string
    healthy int32
    )

    func main() {
    flag.StringVar(&listenAddr, "listen-addr", ":5000", "server listen address")
    flag.Parse()

    logger := log.New(os.Stdout, "http: ", log.LstdFlags)
    logger.Println("Server is starting...")

    router := http.NewServeMux()
    router.Handle("/", index())
    router.Handle("/healthz", healthz())

    nextRequestID := func() string {
    return fmt.Sprintf("%d", time.Now().UnixNano())
    }

    server := &http.Server{
    Addr: listenAddr,
    Handler: tracing(nextRequestID)(logging(logger)(router)),
    ErrorLog: logger,
    ReadTimeout: 5 * time.Second,
    WriteTimeout: 10 * time.Second,
    IdleTimeout: 15 * time.Second,
    }

    quit := make(chan os.Signal, 1)
    signal.Notify(quit, os.Interrupt)

    go func() {
    <-quit
    logger.Println("Server is shoutting down...")
    atomic.StoreInt32(&healthy, 0)

    ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
    defer cancel()

    server.SetKeepAlivesEnabled(false)
    if err := server.Shutdown(ctx); err != nil {
    logger.Fatalf("Could not gracefully shoutdown the server: %v\n", err)
    }
    }()

    logger.Println("Server is ready to handle requests at", listenAddr)
    atomic.StoreInt32(&healthy, 1)
    if err := server.ListenAndServe(); err != http.ErrServerClosed {
    logger.Fatalf("Could not listen on %s: %v\n", listenAddr, err)
    }

    logger.Println("Server stopped")
    }

    func index() http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    if r.URL.Path != "/" {
    http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
    return
    }
    w.Header().Set("Content-Type", "text/plain; charset=utf-8")
    w.Header().Set("X-Content-Type-Options", "nosniff")
    w.WriteHeader(http.StatusOK)
    fmt.Fprintln(w, "Hello, World!")
    })
    }

    func healthz() http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    if atomic.LoadInt32(&healthy) == 1 {
    w.WriteHeader(http.StatusNoContent)
    return
    }
    w.WriteHeader(http.StatusServiceUnavailable)
    })
    }

    func logging(logger *log.Logger) func(http.Handler) http.Handler {
    return func(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    defer func() {
    requestID, ok := r.Context().Value(requestIDKey).(string)
    if !ok {
    requestID = "unknown"
    }
    logger.Println(requestID, r.Method, r.URL.Path, r.RemoteAddr, r.UserAgent())
    }()
    next.ServeHTTP(w, r)
    })
    }
    }

    func tracing(nextRequestID func() string) func(http.Handler) http.Handler {
    return func(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    requestID := r.Header.Get("X-Request-Id")
    if requestID == "" {
    requestID = nextRequestID()
    }
    ctx := context.WithValue(r.Context(), requestIDKey, requestID)
    w.Header().Set("X-Request-Id", requestID)
    next.ServeHTTP(w, r.WithContext(ctx))
    })
    }
    }