Example #1
0
// This does the actual data transfer.
// The broker only closes the Read side.
func broker(dst, src net.Conn, srcClosed chan bool, written, errors *int64) {
	_, err := io.Copy(dst, src)
	if err != nil {
		atomic.AddInt64(errors, 1)
		log.Errorf("ERROR: Copy error: %s", err)
	}
	if err := src.Close(); err != nil {
		atomic.AddInt64(errors, 1)
		log.Errorf("ERROR: Close error: %s", err)
	}
	srcClosed <- true
}
Example #2
0
func loadConfig() {
	for _, cfgPath := range []string{stateConfig, defaultConfig} {
		if cfgPath == "" {
			continue
		}

		cfgData, err := ioutil.ReadFile(cfgPath)
		if err != nil {
			log.Warnln("WARN: Reading config ", err)
			continue
		}

		var cfg client.Config
		err = json.Unmarshal(cfgData, &cfg)
		if err != nil {
			log.Warnln("WARN: Config error:", err)
			continue
		}
		log.Debug("DEBUG: Loaded config from:", cfgPath)

		if err := Registry.UpdateConfig(cfg); err != nil {
			log.Errorf("ERROR: Unable to load config: %s", err)
		}
	}
}
Example #3
0
func NewBackend(cfg client.BackendConfig) *Backend {
	b := &Backend{
		Name:      cfg.Name,
		Addr:      cfg.Addr,
		CheckAddr: cfg.CheckAddr,
		Weight:    cfg.Weight,
		Network:   cfg.Network,
		stopCheck: make(chan interface{}),
	}

	// don't want a weight of 0
	if b.Weight == 0 {
		b.Weight = 1
	}

	if b.Network == "" {
		b.Network = "tcp"
	}

	switch b.Network {
	case "udp", "udp4", "udp6":
		var err error
		b.udpAddr, err = net.ResolveUDPAddr(b.Network, b.Addr)
		if err != nil {
			log.Errorf("ERROR: %s", err.Error())
			b.up = false
		}
	}

	return b
}
Example #4
0
// Add a new service to the Registry.
// Do not replace an existing service.
func (s *ServiceRegistry) AddService(svcCfg client.ServiceConfig) error {
	s.Lock()
	defer s.Unlock()

	log.Debug("DEBUG: Adding service:", svcCfg.Name)
	if _, ok := s.svcs[svcCfg.Name]; ok {
		log.Debug("DEBUG: Service already exists:", svcCfg.Name)
		return ErrDuplicateService
	}

	s.setServiceDefaults(&svcCfg)
	svcCfg = svcCfg.SetDefaults()

	service := NewService(svcCfg)
	err := service.start()
	if err != nil {
		log.Errorf("ERROR: Unable to start service '%s'", svcCfg.Name)
		return err
	}

	s.svcs[service.Name] = service

	svcCfg.VirtualHosts = filterEmpty(svcCfg.VirtualHosts)
	for _, name := range svcCfg.VirtualHosts {
		vhost := s.vhosts[name]
		if vhost == nil {
			vhost = &VirtualHost{Name: name}
			s.vhosts[name] = vhost
		}
		vhost.Add(service)
	}

	return nil
}
Example #5
0
// This probably shouldn't be called ServeHTTP anymore
func (p *ReverseProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request, addrs []string) {

	pr := &ProxyRequest{
		ResponseWriter: rw,
		Request:        req,
		Backends:       addrs,
	}

	for _, f := range p.OnRequest {
		cont := f(pr)
		if !cont {
			return
		}
	}

	pr.StartTime = time.Now()
	res, err := p.doRequest(pr)

	pr.Response = res
	pr.ProxyError = err
	pr.FinishTime = time.Now()

	if err != nil {
		log.Errorf("ERROR: HTTP proxy error - %v", err)

		// We want to ensure that we have a non-nil response even on error for
		// the OnResponse callbacks. If the Callback chain completes, this will
		// be written to the client.
		res = &http.Response{
			Header:     make(map[string][]string),
			StatusCode: http.StatusBadGateway,
			Status:     http.StatusText(http.StatusBadGateway),
			// this ensures Body isn't nil
			Body: ioutil.NopCloser(bytes.NewReader(nil)),
		}
		pr.Response = res
	}

	for _, h := range hopHeaders {
		res.Header.Del(h)
	}

	copyHeader(rw.Header(), res.Header)

	for _, f := range p.OnResponse {
		cont := f(pr)
		if !cont {
			return
		}
	}

	// calls all completed with true, write the Response back to the client.
	defer res.Body.Close()
	rw.WriteHeader(res.StatusCode)
	_, err = p.copyResponse(rw, res.Body)
	if err != nil {
		log.Warnf("WARN: id=%s transfer error: %s", req.Header.Get("X-Request-Id"), err)
	}
}
Example #6
0
func deleteService(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)

	err := Registry.RemoveService(vars["service"])
	if err != nil {
		log.Errorf("ERROR: %s", err)
		http.Error(w, err.Error(), http.StatusNotFound)
		return
	}
	go writeStateConfig()
	w.Write(marshal(Registry.Config()))
}
Example #7
0
func getBackendStats(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	serviceName := vars["service"]
	backendName := vars["backend"]

	backend, err := Registry.BackendStats(serviceName, backendName)
	if err != nil {
		log.Errorf("ERROR: %s", err)
		http.Error(w, err.Error(), http.StatusNotFound)
		return
	}

	w.Write(marshal(backend))
}
Example #8
0
// Update the global config state, including services and backends.
// This does not remove any Services, but will add or update any provided in
// the config.
func (s *ServiceRegistry) UpdateConfig(cfg client.Config) error {

	// Set globals
	// TODO: we might need to unset something
	// TODO: this should remove services and backends to match the submitted config

	if cfg.Balance != "" {
		s.cfg.Balance = cfg.Balance
	}
	if cfg.CheckInterval != 0 {
		s.cfg.CheckInterval = cfg.CheckInterval
	}
	if cfg.Fall != 0 {
		s.cfg.Fall = cfg.Fall
	}
	if cfg.Rise != 0 {
		s.cfg.Rise = cfg.Rise
	}
	if cfg.ClientTimeout != 0 {
		s.cfg.ClientTimeout = cfg.ClientTimeout
	}
	if cfg.ServerTimeout != 0 {
		s.cfg.ServerTimeout = cfg.ServerTimeout
	}
	if cfg.DialTimeout != 0 {
		s.cfg.DialTimeout = cfg.DialTimeout
	}

	// apply the https rediect flag
	if httpsRedirect {
		s.cfg.HTTPSRedirect = true
	}

	invalidPorts := []string{
		// FIXME: lookup bound addresses some other way.  We may have multiple
		//        http listeners, as well as all listening Services.
		// listenAddr[strings.Index(listenAddr, ":")+1:],
		adminListenAddr[strings.Index(adminListenAddr, ":")+1:],
	}

	errors := &multiError{}

	for _, svc := range cfg.Services {
		for _, port := range invalidPorts {
			if strings.HasSuffix(svc.Addr, port) {
				// TODO: report conflicts between service listeners
				errors.Add(fmt.Errorf("Port conflict: %s port %s already bound by shuttle", svc.Name, port))
				continue
			}
		}

		// Add a new service, or update an existing one.
		if Registry.GetService(svc.Name) == nil {
			if err := Registry.AddService(svc); err != nil {
				log.Errorf("ERROR: Unable to add service %s - %s", svc.Name, err.Error())
				errors.Add(err)
				continue
			}
		} else if err := Registry.UpdateService(svc); err != nil {
			log.Errorf("ERROR: Unable to update service %s - %s", svc.Name, err.Error())
			errors.Add(err)
			continue
		}
	}

	go writeStateConfig()

	if errors.Len() == 0 {
		return nil
	}
	return errors
}