func (svr *Server) Connect(info *api.HubInfo, stream api.Hub_ConnectServer) error {
	log.Printf("debug: Hub.Connect: start: %s", info.Host)
	defer log.Printf("debug: Hub.Connect: end: %s", info.Host)
	ctx := stream.Context()
	if !strings.HasSuffix(info.Host, "."+svr.BaseDomain) {
		return errors.New("invalid subdomain")
	}
	subdomain := strings.TrimSuffix(info.Host, "."+svr.BaseDomain)
	if !validSubdomain.MatchString(subdomain) {
		return errors.New("invalid subdomain")
	}
	host := subdomain + "." + svr.BaseDomain
	closeHub, err := svr.newHub(host, stream)
	if err != nil {
		return err
	}
	defer closeHub()
	<-ctx.Done()
	return nil
}
Connect handles Hub.Connect gRPC call.
package server
import (
	"log"
	"net/http"
	"regexp"
	"strings"
	"sync"
	google_protobuf "github.com/golang/protobuf/ptypes/empty"
	"github.com/pkg/errors"
	"github.com/speps/go-hashids"
	"github.com/yurinandayona-com/kuma/api"
	"golang.org/x/net/context"
	"google.golang.org/grpc/credentials"
	"google.golang.org/grpc/metadata"
	"google.golang.org/grpc/peer"
)
var (
	validSubdomain = regexp.MustCompile(`^[a-zA-Z0-9]+$`)
)
type Config struct {
	BaseDomain string
	HashID     *hashids.HashID
}
type Server struct {
	sync.RWMutex
	*Config
	nextHubID int64
	hubHosts  map[string]int64
	hubs      map[int64]*hub
}
func New(cfg *Config) (*Server, error) {
	svr := &Server{
		Config: cfg,
		hubHosts: make(map[string]int64),
		hubs:     make(map[int64]*hub),
	}
	return svr, nil
}
func (svr *Server) getHubFromHost(host string) (*hub, bool) {
	svr.RLock()
	defer svr.RUnlock()
	hubID, ok := svr.hubHosts[host]
	if !ok {
		return nil, ok
	}
	hub, ok := svr.hubs[hubID]
	return hub, ok
}
func (svr *Server) getHubFromID(hubID int64) (*hub, bool) {
	svr.RLock()
	defer svr.RUnlock()
	hub, ok := svr.hubs[hubID]
	return hub, ok
}
func (svr *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	host := r.Host
	if hub, ok := svr.getHubFromHost(host); ok {
		hub.ServeHTTP(w, r)
	} else {
		http.Error(w, "kuma: host not found", http.StatusNotFound)
	}
}
func (svr *Server) Prepare(ctx context.Context, config *api.HubConfig) (*api.HubInfo, error) {
	log.Printf("debug: Hub.Prepare: start: %s", config.Subdomain)
	defer log.Printf("debug: Hub.Prepare: end: %s", config.Subdomain)
	if !validSubdomain.MatchString(config.Subdomain) {
		return nil, errors.New("invalid subdomain")
	}
	host := config.Subdomain + "." + svr.BaseDomain
	return &api.HubInfo{Host: host}, nil
}