Basic Http & File Server in Go


Golang has a built-in net/http package that allows us to build a http server like Nginx or Apache. I'm going through serveral levels on how to build it.

The Basics

package main

import "net/http"

func main() {
  http.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
    w.Write([]byte("Hello World"))
  })
  http.ListenAndServe(":8000", nil)
}

After we run go main.go the server runs on port 8000 displaying Hello World. This program is composed of several parts:

  • An http.HandleFunc() handles http requests and returns responses accordingly.
  • The http.ListenAndServe() listens to port 8000 to receives requests.

However, we only respond Hello World to all responses. How about we serve some HTML pages accordingly?

Serving static files

The way we serve static files are to render files according to reqesuted path.

package main

import (
    "bufio"
    "net/http"
    "os"
    "strings"
)

type MyHandler struct {
    http.Handler
}

func ContentType(path string) (contentType string) {
    if strings.HasSuffix(path, ".css") {
        contentType = "text/css"
    } else if strings.HasSuffix(path, ".html") {
        contentType = "text/html"
    } else if strings.HasSuffix(path, ".js") {
        contentType = "application/javascript"
    } else if strings.HasSuffix(path, ".png") {
        contentType = "image/png"
    } else {
        contentType = "text/plain"
    }
    return
}

// We create a custom handler
func (this *MyHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
    path := "./public" + req.URL.Path
    // Open a file, which does not render it but keep it ready for read
    f, err := os.Open(path)

    // if a file exists, check its content type, or return 404
    if err == nil {
        // read the content to buffer in order to save memory
        bufferedReader := bufio.NewReader(f)
        // check content type of the file according to its suffix
        w.Header().Add("Content Type", ContentType(path))
        // write the file content to the response
        bufferedReader.WriteTo(w)
    } else {
        w.WriteHeader(404)
        w.Write([]byte("404 - " + http.StatusText(404)))
    }
}

func main() {
    // use the custom handler
    http.Handle("/", new(MyHandler))
    http.ListenAndServe(":8000", nil)
}

Serving static files, an easier way

The code above is actually built-in in Go library, so we can put it like:

package main

import "net/http"

func main() {
  http.ListenAndServe(":8000", http.FileServer(http.Dir("public")))
}

And we're done.