Search code examples
gomux

Handling dynamic sub-domains


Let's say my website is http://soccer.com and I want to support an unlimited amount of subdomains, eg.:

  • http://cronaldo.soccer.com
  • http://messi.soccer.com
  • http://neymar.soccer.com
  • http://muller.soccer.com
  • ...

I also want to have a few reserved sub-domains, eg.:

  • http://admin.soccer.com
  • http://help.soccer.com
  • ...

While the player's subdomains will be handled by the same logic, the reserved ones won't. So I'll need 2 routes or 2 routers?

Here's what I have:

package main

import (
    "fmt"
    "net/http"
    "log"
    "html/template"
    "strings"
)

type Mux struct {
    http.Handler
}

func (mux Mux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    domainParts := strings.Split(r.Host, ".")
    fmt.Println("Here: " + domainParts[0])
    if domainParts[0] == "admin" {
        // assign route?
        mux.ServeHTTP(w, r)
    } else if domainParts[0] == "help" {
        // assign route?
        mux.ServeHTTP(w, r)
    } else if isSubDomainValid(domainParts[0]) {
        // assign route for player page?
        mux.ServeHTTP(w, r)
    } else {
        // Handle 404
        http.Error(w, "Not found", 404)
    }
}

func main() {
  mux := http.NewServeMux()
  mux.HandleFunc("/", HomeHandler)

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

func HomeHandler(w http.ResponseWriter, r *http.Request) {
  t, err := template.ParseFiles("index.html")
  if err != nil {
    log.Print("template parsing error: ", err)
  }

  err = t.Execute(w, nil)
  if err != nil {
    log.Print("template executing error: ", err)
  }

}

But I don't think I'm doing this right.

fmt.Println("Here: " + domainParts[0]) never shows up, leading me to believe that ServeHTTP() is never called.

I'm just a beginner in Go, so I might be missing some concept

Thank you


Solution

  • You are on the right track. Create a http.ServeMux for each kind of domain and call those muxes from the if / else statement in Mux.ServeHTTP. Also, use a Mux as the root handler.

    type Mux struct {
        help, admin, sub, main *http.ServeMux
    }
    
    func (mux *Mux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
        if isMainDomain(r.Host) {
            mux.main.ServeHTTP(w, r)
            return
        }
        domainParts := strings.Split(r.Host, ".")
        if domainParts[0] == "admin" {
            mux.admin.ServeHTTP(w, r)
        } else if domainParts[0] == "help" {
            mux.help.ServeHTTP(w, r)
        } else if isSubDomainValid(domainParts[0]) {
            mux.sub.ServeHTTP(w, r)
        } else {
            http.Error(w, "Not found", 404)
        }
    }
    
    func main() {
        mux := &Mux{
            help:  http.NewServeMux(),
            admin: http.NewServeMux(),
            sub:   http.NewServeMux(),
            main:  http.NewServeMux(),
        }
    
        mux.help.HandleFunc("/", helpHomeHandler)
        mux.admin.HandleFunc("/", adminHomeHandler)
        mux.sub.HandleFunc("/", defaultSubdomainHomeHandler)
        mux.main.HandleFunc("/", mainHomeHandler)
    
        http.ListenAndServe(":8080", mux) // <-- use Mux value as root handler
    }