// 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) } }
func (b *Backend) check() { if b.CheckAddr == "" { return } up := true if c, e := net.DialTimeout("tcp", b.CheckAddr, b.dialTimeout); e == nil { c.(*net.TCPConn).SetLinger(0) c.Close() } else { log.Warnf("WARN: Backend check for %s failed with error:", e) up = false } b.Lock() defer b.Unlock() if up { log.Debugf("DEBUG: Check OK for %s/%s", b.Name, b.CheckAddr) b.fallCount = 0 b.riseCount++ b.checkOK++ if b.riseCount >= b.rise { if !b.up { log.Warnf("WARN: Marking backend %s Up", b.Name) } b.up = true } } else { log.Debugf("DEBUG: Check failed for %s/%s", b.Name, b.CheckAddr) b.riseCount = 0 b.fallCount++ b.checkFail++ if b.fallCount >= b.fall { if b.up { log.Warnf("WARN: Marking backend %s Down", b.Name) } b.up = false } } }
// Return a *Service for this VirtualHost func (v *VirtualHost) Service() *Service { v.Lock() defer v.Unlock() if len(v.services) == 0 { log.Warnf("WARN: 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] }
// Replace the service's configuration, or update its list of backends. // Replacing a configuration will shutdown the existing service, and start a // new one, which will cause the listening socket to be temporarily // unavailable. func (s *ServiceRegistry) UpdateService(newCfg client.ServiceConfig) error { s.Lock() defer s.Unlock() log.Debug("DEBUG: Updating Service:", newCfg.Name) service, ok := s.svcs[newCfg.Name] if !ok { log.Debug("DEBUG: Service not found:", newCfg.Name) return ErrNoService } currentCfg := service.Config() newCfg = currentCfg.Merge(newCfg) if err := service.UpdateConfig(newCfg); err != nil { return err } // Lots of looping here (including fetching the Config, but the cardinality // of Backends shouldn't be very large, and the default RoundRobin balancing // is much simpler with a slice. // we're going to update just the backends for this config // get a map of what's already running currentBackends := make(map[string]client.BackendConfig) for _, backendCfg := range currentCfg.Backends { currentBackends[backendCfg.Name] = backendCfg } // Keep existing backends when they have equivalent config. // Update changed backends, and add new ones. for _, newBackend := range newCfg.Backends { current, ok := currentBackends[newBackend.Name] if ok && current.Equal(newBackend) { log.Debugf("DEBUG: Backend %s/%s unchanged", service.Name, current.Name) // no change for this one delete(currentBackends, current.Name) continue } // we need to remove and re-add this backend log.Warnf("WARN: Updating Backend %s/%s", service.Name, newBackend.Name) service.remove(newBackend.Name) service.add(NewBackend(newBackend)) delete(currentBackends, newBackend.Name) } // remove any left over backends for name := range currentBackends { log.Debugf("DEBUG: Removing Backend %s/%s", service.Name, name) service.remove(name) } if currentCfg.Equal(newCfg) { log.Debugf("DEBUG: Service Unchanged %s", service.Name) return nil } // replace error pages if there's any change if !reflect.DeepEqual(service.errPagesCfg, newCfg.ErrorPages) { log.Debugf("DEBUG: Updating ErrorPages") service.errPagesCfg = newCfg.ErrorPages service.errorPages.Update(newCfg.ErrorPages) } s.updateVHosts(service, filterEmpty(newCfg.VirtualHosts)) return nil }