Welcome back to the second part of our exploration into building web servers using GoLang! In the previous blog post, we discussed creating a basic web server using the http.ListenAndServe function.

However, this does not give us more control over the server config and to gain more control and flexibility over the server’s behaviour, you’re in for a treat. In this article, we’ll delve into the world of custom servers by utilizing the http.Server struct.

The code is very straightforward,

package main

import "net/http"

func main() {
    // Creating an instance of http.Server
    s := http.Server{
        Addr:    ":8080",
        Handler: nil, // You can replace this with your own handler logic
    }

    // Starting the server
    s.ListenAndServe()
}

You can find the server struct here: https://pkg.go.dev/net/http#Server

and the ListenAndServe method on the server struct can be found here: https://pkg.go.dev/net/http#Server.ListenAndServe

This is the exact same method that we saw in the previous blog post.

Basically instead of passing Addr and Handler as an input and then implicitly creating Server struct, we are defining the struct beforehand.

Let’s take a look at this server struct,

type Server struct {
    Addr                          string
    Handler                       Handler
    DisableGeneralOptionsHandler  bool
    TLSConfig                     *tls.Config
    ReadTimeout                   time.Duration
    ReadHeaderTimeout             time.Duration
    WriteTimeout                  time.Duration
    IdleTimeout                   time.Duration
    MaxHeaderBytes                int
    TLSNextProto                  map[string]func(*Server, *tls.Conn, Handler)
    ConnState                     func(net.Conn, ConnState)
    ErrorLog                      *log.Logger
    BaseContext                   func(net.Listener) context.Context
    ConnContext                   func(ctx context.Context, c net.Conn) context.Context
    inShutdown                    atomic.Bool
    disableKeepAlives             atomic.Bool
    nextProtoOnce                 sync.Once
    nextProtoErr                  error
    mu                            sync.Mutex
    listeners                     map[*net.Listener]struct{}
    activeConn                    map[*conn]struct{}
    onShutdown                    []func()
    listenerGroup                 sync.WaitGroup
}

Along with Addr and Handler You can now set various timeouts, context, logger, etc

Similar to a previous blog post, as you run this code, you will still get 404 As we have not defined any handler as such this server is technically useless but a good example to understand the internals.

In the upcoming blog post, we will see how to define a handler and make this server more usable.

Till then, Happy Coding!!!