How to server static files with virtual hosts functioality in Go

Issue

How can I server static files (with FileServer) for a virtual host in Go?

If I have my own handler function, the task seems to be easily solvable [1]:

package main

import (
    "fmt"
    "net/http"
)

func main() {
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, "Hello, world!")
    })
    http.HandleFunc("qa.example.com/", func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, "Hello, improved world!")
    })
    http.ListenAndServe(":8080", nil)
}

But what if I need to serve static files (with FileServer)
for a virtual host?

This

r.PathPrefix("qa.example.com/").Handler(http.FileServer(http.Dir("/static/qa/")))

does not work — it is just ignored.

What am I doing wrong?
Is this approach generally wrong?

Solution

package main

import (
    "embed"
    "fmt"
    "net/http"
    "strings"
    "time"
)

//go:embed *.go
var f embed.FS

func main() {
    // embed.Fs defaule modtime use now or env value.
    now := time.Now()
    // use mapping host to http.FileSystem
    vhosts := make(map[string]http.FileSystem)
    vhosts["qa.example.com"] = http.FS(f)    // from embed.FS
    vhosts["my.example.com"] = http.Dir(".") // from http.Dir

    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, "Hello, world!")
    })
    http.HandleFunc("/static/", func(w http.ResponseWriter, r *http.Request) {
        // find host
        fs, ok := vhosts[r.Host]
        if !ok {
            w.WriteHeader(404)
            w.Write([]byte("404 not found vhost"))
            return
        }
        // open file from http.FileSystem
        file, err := fs.Open(strings.TrimPrefix(r.URL.Path, "/static/"))
        if err != nil {
            // reference go1.18.3/net/http/fs.go toHTTPError function hander file error.
            w.Write([]byte("check err is 403 or 404 or 500"))
            return
        }
        stat, _ := file.Stat()
        // fix embed modtime is zero.
        modtime := stat.ModTime()
        if modtime.IsZero() {
            modtime = now
        }
        // response
        http.ServeContent(w, r, stat.Name(), modtime, file)

    })

    http.ListenAndServe(":8080", nil)
}

run test exec command curl -H "Host: my.example.com" 127.0.0.1:8080/static/01.go, 01.go replacte your static filename.

Answered By – eudore

Answer Checked By – Cary Denson (GoLangFix Admin)

Leave a Reply

Your email address will not be published.