Esempio n. 1
0
func (s *Service) runUDP() {
	buff := make([]byte, 65536)
	conn := s.udpListener

	// for UDP, we can proxy the data right here.
	for {
		n, _, err := conn.ReadFromUDP(buff)
		if err != nil {
			// we can't cleanly signal the Read to stop, so we have to
			// string-match this error.
			if err.Error() == "use of closed network connection" {
				// normal shutdown
				return
			} else if err, ok := err.(net.Error); ok && err.Temporary() {
				log.Warnf("WARN: %s", err.Error())
			} else {
				// unexpected error, log it before exiting
				log.Errorf("ERROR: %s", err.Error())
				atomic.AddInt64(&s.Errors, 1)
				return
			}
		}

		if n == 0 {
			continue
		}

		atomic.AddInt64(&s.Rcvd, int64(n))

		backend := s.udpRoundRobin()
		if backend == nil {
			// this could produce a lot of message
			// TODO: log some %, or max rate of messages
			continue
		}

		n, err = conn.WriteTo(buff[:n], backend.udpAddr)
		if err != nil {
			if err, ok := err.(net.Error); ok && err.Temporary() {
				log.Warnf("WARN: %s", err.Error())
				continue
			}

			log.Errorf("ERROR: %s", err.Error())
			atomic.AddInt64(&s.Errors, 1)
		} else {
			atomic.AddInt64(&s.Sent, int64(n))
		}
	}
}
Esempio n. 2
0
func (r *HostRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	reqId := genId()
	req.Header.Set("X-Request-Id", reqId)
	w.Header().Add("X-Request-Id", reqId)

	var err error
	host := req.Host

	if strings.Contains(host, ":") {
		host, _, err = net.SplitHostPort(req.Host)
		if err != nil {
			log.Warnf("%s", err)
		}
	}

	svc := Registry.GetVHostService(host)

	if svc != nil && svc.httpProxy != nil {
		// The vhost has a service registered, give it to the proxy
		svc.ServeHTTP(w, req)
		return
	}

	r.noHostHandler(w, req)
}
Esempio n. 3
0
func (r *HostRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	reqId := genId()
	req.Header.Set("X-Request-Id", reqId)
	w.Header().Add("X-Request-Id", reqId)

	if r.SSLOnly {
		if req.TLS != nil || req.Header.Get("X-Forwarded-Proto") != "https" {
			//TODO: verify RequestURI
			redirLoc := "https://" + req.Host + req.RequestURI
			http.Redirect(w, req, redirLoc, http.StatusMovedPermanently)
			return
		}
	}

	var err error
	host := req.Host

	if strings.Contains(host, ":") {
		host, _, err = net.SplitHostPort(req.Host)
		if err != nil {
			log.Warnf("%s", err)
		}
	}

	svc := Registry.GetVHostService(host)

	if svc != nil && svc.httpProxy != nil {
		// The vhost has a service registered, give it to the proxy
		svc.ServeHTTP(w, req)
		return
	}

	r.noHostHandler(w, req)
}
Esempio n. 4
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)
	}
}
Esempio n. 5
0
func (e *ErrorResponse) fetch(page *ErrorPage) {
	log.Debugf("Fetching error page from %s", page.Location)
	resp, err := e.client.Get(page.Location)
	if err != nil {
		log.Warnf("Could not fetch %s: %s", page.Location, err.Error())
		return
	}
	defer resp.Body.Close()

	// If the StatusCode matches any of our registered codes, it's OK
	for _, code := range page.StatusCodes {
		if resp.StatusCode == code {
			resp.StatusCode = http.StatusOK
			break
		}
	}

	if resp.StatusCode != http.StatusOK {
		log.Warnf("Server returned %d when fetching %s", resp.StatusCode, page.Location)
		return
	}

	header := make(map[string][]string)
	for _, key := range ErrorHeaders {
		if hdr, ok := resp.Header[key]; ok {
			header[key] = hdr
		}
	}
	// set the headers along with the body below

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Warnf("Error reading response from %s: %s", page.Location, err.Error())
		return
	}

	if len(body) > 0 {
		page.SetHeader(header)
		page.SetBody(body)
		return
	}
	log.Warnf("Empty response from %s", page.Location)
}
Esempio n. 6
0
// Return a *Service for this VirtualHost
func (v *VirtualHost) Service() *Service {
	v.Lock()
	defer v.Unlock()

	if len(v.services) == 0 {
		log.Warnf("No Services registered for VirtualHost %s", v.Name)
		return nil
	}

	// start cycling through the services in case one has no backends available
	for i := 1; i <= len(v.services); i++ {
		idx := (v.last + i) % len(v.services)
		if v.services[idx].Available() > 0 {
			v.last = idx
			return v.services[idx]
		}
	}

	// even if all backends are down, return a service so that the request can
	// be processed normally (we may have a custom 502 error page for this)
	return v.services[v.last]
}
Esempio n. 7
0
// Update the running configuration.
func (s *Service) UpdateConfig(cfg client.ServiceConfig) error {
	s.Lock()
	defer s.Unlock()

	if s.ClientTimeout != time.Duration(cfg.ClientTimeout)*time.Millisecond {
		return ErrInvalidServiceUpdate
	}

	if s.Addr != cfg.Addr {
		return ErrInvalidServiceUpdate
	}

	s.CheckInterval = cfg.CheckInterval
	s.Fall = cfg.Fall
	s.Rise = cfg.Rise
	s.ServerTimeout = time.Duration(cfg.ServerTimeout) * time.Millisecond
	s.DialTimeout = time.Duration(cfg.DialTimeout) * time.Millisecond
	s.HTTPSRedirect = cfg.HTTPSRedirect

	if s.Balance != cfg.Balance {
		s.Balance = cfg.Balance
		switch s.Balance {
		case client.RoundRobin:
			s.next = s.roundRobin
		case client.LeastConn:
			s.next = s.leastConn
		default:
			if cfg.Balance != "" {
				log.Warnf("invalid balancing algorithm '%s'", cfg.Balance)
			}
			s.next = s.roundRobin
		}
	}

	return nil
}
Esempio n. 8
0
// Create a Service from a config struct
func NewService(cfg client.ServiceConfig) *Service {
	s := &Service{
		Name:          cfg.Name,
		Addr:          cfg.Addr,
		Balance:       cfg.Balance,
		CheckInterval: cfg.CheckInterval,
		Fall:          cfg.Fall,
		Rise:          cfg.Rise,
		HTTPSRedirect: cfg.HTTPSRedirect,
		VirtualHosts:  cfg.VirtualHosts,
		ClientTimeout: time.Duration(cfg.ClientTimeout) * time.Millisecond,
		ServerTimeout: time.Duration(cfg.ServerTimeout) * time.Millisecond,
		DialTimeout:   time.Duration(cfg.DialTimeout) * time.Millisecond,
		errorPages:    NewErrorResponse(cfg.ErrorPages),
		errPagesCfg:   cfg.ErrorPages,
		Network:       cfg.Network,
	}

	// TODO: insert this into the backends too
	s.dialer = &net.Dialer{
		Timeout:   s.DialTimeout,
		KeepAlive: 30 * time.Second,
	}

	// create our reverse proxy, using our load-balancing Dial method
	proxyTransport := &http.Transport{
		Dial:                s.Dial,
		MaxIdleConnsPerHost: 10,
	}
	s.httpProxy = NewReverseProxy(proxyTransport)
	s.httpProxy.FlushInterval = time.Second
	s.httpProxy.Director = func(req *http.Request) {
		req.URL.Scheme = "http"
	}

	s.httpProxy.OnResponse = []ProxyCallback{logProxyRequest, s.errStats, s.errorPages.CheckResponse}

	if s.CheckInterval == 0 {
		s.CheckInterval = client.DefaultCheckInterval
	}
	if s.Rise == 0 {
		s.Rise = client.DefaultRise
	}
	if s.Fall == 0 {
		s.Fall = client.DefaultFall
	}

	if s.Network == "" {
		s.Network = client.DefaultNet
	}

	for _, b := range cfg.Backends {
		s.add(NewBackend(b))
	}

	switch cfg.Balance {
	case client.RoundRobin:
		s.next = s.roundRobin
	case client.LeastConn:
		s.next = s.leastConn
	default:
		if cfg.Balance != "" {
			log.Warnf("invalid balancing algorithm '%s'", cfg.Balance)
		}
		s.next = s.roundRobin
	}

	return s
}