DEV Community

dbarun
dbarun

Posted on • Edited on

Go http handlers

A short walk on how to setup a go server using HTTP package provided by itself. Before jumping to framework I recommend you to explore and understand the packages provided by the particular programming language.

Before jumping directly to the code we need to know how go
structure the HTTP package so we need to jump to another code
i.e server.go
[](https://cs.opensource.google/go/go/+/refs/tags/go1.21.0:src/net/http/server.go)

I extract the code from the above link in case if you are too lazy to have another tab

  1. serveMux which type is struct
type ServeMux struct {
    mu    sync.RWMutex
    m     map[string]muxEntry
    es    []muxEntry // slice of entries sorted from longest to shortest.
    hosts bool       // whether any patterns contain hostnames
}

type muxEntry struct {
    h       Handler
    pattern string
}

// NewServeMux allocates and returns a new ServeMux.
func NewServeMux() *ServeMux { return new(ServeMux) }

// DefaultServeMux is the default ServeMux used by Serve.
var DefaultServeMux = &defaultServeMux

var defaultServeMux ServeMux
Enter fullscreen mode Exit fullscreen mode
  1. Handler type Handler interface { ServeHTTP(http.ResponseWriter, *http.Request) signature }

3.Handle i.e func (mux *ServeMux) Handle(path string, handler Handler)

// Handle registers the handler for the given pattern.
// If a handler already exists for pattern, Handle panics.
func (mux *ServeMux) Handle(pattern string, handler Handler) {
    mux.mu.Lock()
    defer mux.mu.Unlock()

    if pattern == "" {
        panic("http: invalid pattern")
    }
    if handler == nil {
        panic("http: nil handler")
    }
    if _, exist := mux.m[pattern]; exist {
        panic("http: multiple registrations for " + pattern)
    }

    if mux.m == nil {
        mux.m = make(map[string]muxEntry)
    }
    e := muxEntry{h: handler, pattern: pattern}
    mux.m[pattern] = e
    if pattern[len(pattern)-1] == '/' {
        mux.es = appendSorted(mux.es, e)
    }

    if pattern[0] != '/' {
        mux.hosts = true
    }
}
Enter fullscreen mode Exit fullscreen mode

4.HandleFunc

func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
    if handler == nil {
        panic("http: nil handler")
    }
    mux.Handle(pattern, HandlerFunc(handler))
}

// Handle registers the handler for the given pattern
// in the DefaultServeMux.
// The documentation for ServeMux explains how patterns are matched.
func Handle(pattern string, handler Handler) { DefaultServeMux.Handle(pattern, handler) }

// HandleFunc registers the handler function for the given pattern
// in the DefaultServeMux.
// The documentation for ServeMux explains how patterns are matched.
func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
    DefaultServeMux.HandleFunc(pattern, handler)
}
Enter fullscreen mode Exit fullscreen mode

lets write the code to set up the server

package main

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

type foo struct {
  l *log.Logger
}

// we can create a constructor function
func newFoo(l *log.Logger) *foo{
   return &(l)
}

/* we need to implement a ServeHTTP function in order to use  http.Handle because it takes handler as argument.Handler is an interface which has ServeHTTP signature ref 2*/

func (f *foo) ServeHTTP(rw http.ResponseWriter, r*http.Request){
   // handle all sort of things
   rw.Write([]byte("Hello"))
   return
}


func main() {

    l := log.New(os.Stdout, "go", log.LstdFlags)
    nf := newFoo(l)

    /* we can create a new serveMux if not in listenAndServe, 
    default sever is use  */
     sm := http.NewServeMux()

// routing using Handle
     sm.Handle("/",nf) 

//routing using HandleFunc 
     sm.HandleFunc("/hf",func(w http.ResponseWriter, r *http.Request) {
        w.Write([]byte("handle by Handle Func"))
        return
    })

    // this server configuartion
    s := &http.Server{
        Addr: ":3000",
        Handler: sm,
        IdleTimeout : 120*time.Second,
        ReadTimeout: 1 * time.Second,
        WriteTimeout: 1*time.Second,
    }

    // we use go routine in order to independently execute 
    // and separate from main go routine
    go func() {

        err := s.ListenAndServe()
        if err != nil {
            l.Fatal(err)
        }
    }()

    /* in order to shutdown the server properly means if we want to shutdown the server it make the existing request respond properly*/
    sigChan := make(chan os.Signal) 
    signal.Notify(sigChan, os.Interrupt)
    signal.Notify(sigChan,os.Kill)

    sig := <- sigChan //this line blocks the waits for signal
    fmt.Println("gracefully shutting down server", sig)
    tc, f :=context.WithTimeout(context.Background(), 30*time.Second)
    f()
    s.Shutdown(tc)

}

---

Enter fullscreen mode Exit fullscreen mode

That's all :)

Top comments (0)