예제 #1
0
파일: backend.go 프로젝트: joeshaw/shuttle
// 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.Printf("Copy error: %s", err)
	}
	if err := src.Close(); err != nil {
		atomic.AddInt64(errors, 1)
		log.Printf("Close error: %s", err)
	}
	srcClosed <- true
}
예제 #2
0
파일: config.go 프로젝트: joeshaw/shuttle
func loadConfig() {
	for _, cfgPath := range []string{stateConfig, defaultConfig} {
		if cfgPath == "" {
			continue
		}

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

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

		if err := Registry.UpdateConfig(cfg); err != nil {
			log.Printf("Unable to load config: error: %s", err)
		}
	}
}
예제 #3
0
파일: service.go 프로젝트: joeshaw/shuttle
// Stop the Service's Accept loop by closing the Listener,
// and stop all backends for this service.
func (s *Service) stop() {
	s.Lock()
	defer s.Unlock()

	log.Printf("Stopping Listener for %s on %s:%s", s.Name, s.Network, s.Addr)
	for _, backend := range s.Backends {
		backend.Stop()
	}

	switch s.Network {
	case "tcp", "tcp4", "tcp6":
		// the service may have been bad, and the listener failed
		if s.tcpListener == nil {
			return
		}

		err := s.tcpListener.Close()
		if err != nil {
			log.Println(err)
		}

	case "udp", "udp4", "udp6":
		if s.udpListener == nil {
			return
		}
		err := s.udpListener.Close()
		if err != nil {
			log.Println(err)
		}
	}

}
예제 #4
0
파일: service.go 프로젝트: joeshaw/shuttle
// Add or replace a Backend in this service
func (s *Service) add(backend *Backend) {
	s.Lock()
	defer s.Unlock()

	log.Printf("Adding %s backend %s{%s} for %s at %s", backend.Network, backend.Name, backend.Addr, s.Name, s.Addr)
	backend.up = true
	backend.rwTimeout = s.ServerTimeout
	backend.dialTimeout = s.DialTimeout
	backend.checkInterval = time.Duration(s.CheckInterval) * time.Millisecond

	// We may add some allowed protocol bridging in the future, but for now just fail
	if s.Network[:3] != backend.Network[:3] {
		log.Errorf("ERROR: backend %s cannot use network '%s'", backend.Name, backend.Network)
	}

	// replace an existing backend if we have it.
	for i, b := range s.Backends {
		if b.Name == backend.Name {
			b.Stop()
			s.Backends[i] = backend
			backend.Start()
			return
		}
	}

	s.Backends = append(s.Backends, backend)

	backend.Start()
}
예제 #5
0
파일: main.go 프로젝트: carriercomm/shuttle
func main() {
	if debug {
		log.DefaultLogger.Level = log.DEBUG
	}

	if version {
		println(buildVersion)
		return
	}

	log.Printf("Starting shuttle %s", buildVersion)
	loadConfig()

	var wg sync.WaitGroup
	wg.Add(1)
	go startAdminHTTPServer(&wg)

	if httpAddr != "" {
		wg.Add(1)
		go startHTTPServer(&wg)
	}

	if httpsAddr != "" {
		wg.Add(1)
		go startHTTPSServer(&wg)
	}
	wg.Wait()
}
예제 #6
0
파일: http.go 프로젝트: joeshaw/shuttle
// Start the HTTP Router frontend.
// Takes a channel to notify when the listener is started
// to safely synchronize tests.
func (r *HostRouter) Start(ready chan bool) {
	//FIXME: poor locking strategy
	r.Lock()
	var err error
	r.listener, err = newTimeoutListener("tcp", r.server.Addr, 300*time.Second)
	if err != nil {
		log.Errorf("%s", err)
		r.Unlock()
		return
	}

	listener := r.listener
	if r.Scheme == "https" {
		listener = tls.NewListener(listener, r.server.TLSConfig)
	}

	r.Unlock()

	log.Printf("%s server listening at %s", strings.ToUpper(r.Scheme), r.server.Addr)
	if ready != nil {
		close(ready)
	}

	// This will log a closed connection error every time we Stop
	// but that's mostly a testing issue.
	log.Errorf("%s", r.server.Serve(listener))
}
예제 #7
0
파일: http.go 프로젝트: joeshaw/shuttle
func logProxyRequest(pr *ProxyRequest) bool {
	// TODO: we may to be able to switch this off
	if pr == nil || pr.Request == nil {
		return true
	}

	var id, method, clientIP, url, backend, agent string
	var status int

	duration := pr.FinishTime.Sub(pr.StartTime)

	id = pr.Request.Header.Get("X-Request-Id")
	method = pr.Request.Method
	url = pr.Request.Host + pr.Request.RequestURI
	agent = pr.Request.UserAgent()
	status = pr.Response.StatusCode

	clientIP = pr.Request.Header.Get("X-Forwarded-For")
	if clientIP == "" {
		clientIP = pr.Request.RemoteAddr
	}

	if pr.Response != nil && pr.Response.Request != nil && pr.Response.Request.URL != nil {
		backend = pr.Response.Request.URL.Host
	}

	err := fmt.Sprintf("%v", pr.ProxyError)

	fmtStr := "id=%s method=%s client-ip=%s url=%s backend=%s status=%d duration=%s agent=%s, err=%s"

	log.Printf(fmtStr, id, method, clientIP, url, backend, status, duration, agent, err)
	return true
}
예제 #8
0
파일: registry.go 프로젝트: joeshaw/shuttle
func (v *VirtualHost) Remove(svc *Service) {
	v.Lock()
	defer v.Unlock()

	found := -1
	for i, s := range v.services {
		if s.Name == svc.Name {
			found = i
			break
		}
	}

	if found < 0 {
		log.Debugf("Service %s not found under VirtualHost %s", svc.Name, v.Name)
		return
	}

	// safe way to get the backends info for logging
	svcCfg := svc.Config()

	// Now removing this Service
	for _, backend := range svcCfg.Backends {
		log.Printf("Removing backend http://%s from VirtualHost %s", backend.Addr, v.Name)
	}

	v.services = append(v.services[:found], v.services[found+1:]...)
}
예제 #9
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.Printf("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("id=%s transfer error: %s", req.Header.Get("X-Request-Id"), err)
	}
}
예제 #10
0
파일: service.go 프로젝트: joeshaw/shuttle
// Fill out and verify service
func (s *Service) start() (err error) {
	s.Lock()
	defer s.Unlock()

	if s.Backends == nil {
		s.Backends = make([]*Backend, 0)
	}

	switch s.Network {
	case "tcp", "tcp4", "tcp6":
		log.Printf("Starting TCP listener for %s on %s", s.Name, s.Addr)

		s.tcpListener, err = newTimeoutListener(s.Network, s.Addr, s.ClientTimeout)
		if err != nil {
			return err
		}

		go s.runTCP()
	case "udp", "udp4", "udp6":
		log.Printf("Starting UDP listener for %s on %s", s.Name, s.Addr)

		laddr, err := net.ResolveUDPAddr(s.Network, s.Addr)
		if err != nil {
			return err
		}
		s.udpListener, err = net.ListenUDP(s.Network, laddr)
		if err != nil {
			return err
		}

		go s.runUDP()
	default:
		return fmt.Errorf("Error: unknown network '%s'", s.Network)
	}

	return nil
}
예제 #11
0
파일: service.go 프로젝트: joeshaw/shuttle
// Remove a Backend by name
func (s *Service) remove(name string) bool {
	s.Lock()
	defer s.Unlock()

	for i, b := range s.Backends {
		if b.Name == name {
			log.Printf("Removing %s backend %s{%s} for %s at %s", b.Network, b.Name, b.Addr, s.Name, s.Addr)
			last := len(s.Backends) - 1
			deleted := b
			s.Backends[i], s.Backends[last] = s.Backends[last], nil
			s.Backends = s.Backends[:last]
			deleted.Stop()
			return true
		}
	}
	return false
}
예제 #12
0
파일: registry.go 프로젝트: joeshaw/shuttle
// Insert a service
// do nothing if the service already is registered
func (v *VirtualHost) Add(svc *Service) {
	v.Lock()
	defer v.Unlock()
	for _, s := range v.services {
		if s.Name == svc.Name {
			log.Debugf("Service %s already registered in VirtualHost %s", svc.Name, v.Name)
			return
		}
	}

	// TODO: is this the best place to log these?
	svcCfg := svc.Config()
	for _, backend := range svcCfg.Backends {
		log.Printf("Adding backend http://%s to VirtualHost %s", backend.Addr, v.Name)
	}
	v.services = append(v.services, svc)
}