Go http handlers
Tags: go, webdev, beginners, backend
Published: 2023-08-29 01:01:11

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)](url)

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
```

2. **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
	}
}
```

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)
}
```

---

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)

}

---

```
That's all :)