/* TODO: Implement more probes */ func (s *Server) probe_backends(probe time.Duration) { transport := http.Transport{Dial: dialTimeout} client := &http.Client{ Transport: &transport, } for { time.Sleep(probe) for vhost, backends := range s.proxy { fmt.Println(backends) err := s.populate_proxies(vhost) if err != nil { utils.Log(fmt.Sprintf("Cleaned entries from vhost: %s", vhost)) continue } is_dead := make(map[string]bool) removed := 0 for backend := range backends { backend = backend - removed utils.Log(fmt.Sprintf( "vhost: %s backends: %s", vhost, s.proxy[vhost][backend].Backend)) if is_dead[s.proxy[vhost][backend].Backend] == true { utils.Log(fmt.Sprintf("Removing dead backend: %s", s.proxy[vhost][backend].Backend)) s.mu.Lock() s.proxy[vhost] = s.proxy[vhost][:backend+copy(s.proxy[vhost][backend:], s.proxy[vhost][backend+1:])] s.vcount[vhost][s.proxy[vhost][backend].Backend]-- s.mu.Unlock() removed++ continue } _, err := client.Get(s.proxy[vhost][backend].Backend) if err != nil { utils.Log(fmt.Sprintf("Removing dead backend: %s with error %s", s.proxy[vhost][backend].Backend, err)) is_dead[s.proxy[vhost][backend].Backend] = true s.mu.Lock() s.proxy[vhost] = s.proxy[vhost][:backend+copy(s.proxy[vhost][backend:], s.proxy[vhost][backend+1:])] s.vcount[vhost][s.proxy[vhost][backend].Backend]-- s.mu.Unlock() removed++ } else { utils.Log(fmt.Sprintf("Alive: %s", s.proxy[vhost][backend].Backend)) } } } transport.CloseIdleConnections() } }
/* TODO: Implement more balance algorithms */ func (s *Server) Next(vhost string) http.Handler { s.mu.Lock() defer s.mu.Unlock() s.backend[vhost]++ total := len(s.proxy[vhost]) if s.backend[vhost] >= total { s.backend[vhost] = 0 } utils.Log(fmt.Sprintf( "Using backend: %s Url: %s", s.proxy[vhost][s.backend[vhost]].Backend, vhost)) return s.proxy[vhost][s.backend[vhost]].handler }
func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { if h := s.handler(r); h != nil { client := xff(r) utils.Log(fmt.Sprintf("Request from: %s Url: %s", client, r.Host)) r.Header.Add("X-Forwarded-For", client) r.Header.Add("X-Real-IP", client) h.ServeHTTP(w, r) // probe should act here fmt.Println(w.Header()) return } http.Error(w, "Not found.", http.StatusNotFound) }
func (s *Server) handler(req *http.Request) http.Handler { vhost := req.Host if i := strings.Index(vhost, ":"); i >= 0 { vhost = vhost[:i] } _, ok := s.proxy[vhost] if !ok { err := s.populate_proxies(vhost) if err != nil { utils.Log(fmt.Sprintf("%s for vhost %s", err, vhost)) return nil } } return s.Next(vhost) }
func main() { utils.Log("Starting Hot Potato Router...") probe_interval, _ := strconv.Atoi(cfg.Options["hpr"]["probe_interval"]) if probe_interval == 0 { probe_interval = 10 } s, err := http_server.NewServer(time.Duration(probe_interval) * time.Second) utils.CheckPanic(err, "Unable to spawn") if cfg.Options["hpr"]["https_addr"] != "" { cert, err := tls.LoadX509KeyPair(cfg.Options["hpr"]["cert_file"], cfg.Options["hpr"]["key_file"]) utils.CheckPanic(err, "Unable to load certificate") c := &tls.Config{Certificates: []tls.Certificate{cert}} l := tls.NewListener(http_server.Listen(cfg.Options["hpr"]["https_addr"]), c) go func() { utils.CheckPanic(http.Serve(l, s), "Problem with https server") }() } utils.CheckPanic( http.Serve(http_server.Listen(cfg.Options["hpr"]["http_addr"]), s), "Problem with http server") }
func (s *Server) populate_proxies(vhost string) (err error) { s.mu.RLock() defer s.mu.RUnlock() f, _ := rc.ZRange(fmt.Sprintf("hpr-backends::%s", vhost), 0, -1, true) if len(f) == 0 { if len(s.proxy[vhost]) > 0 { delete(s.proxy, vhost) delete(s.backend, vhost) delete(s.vcount, vhost) } return errors.New("Backend list is empty") } var url string for _, be := range f { count, err := strconv.Atoi(be) if err != nil { url = be continue } backend := fmt.Sprintf("http://%s", url) for r := 1; r <= count; r++ { if r <= s.vcount[vhost][backend] { continue } s.proxy[vhost] = append(s.proxy[vhost], Proxy{backend, makeHandler(url)}) v, ready := s.vcount[vhost] if !ready { v = make(map[string]int) s.vcount[vhost] = v } s.vcount[vhost][backend]++ utils.Log(fmt.Sprintf("Backend %s with %d handlers for %s", backend, s.vcount[vhost][backend], vhost)) } } return }