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)) } } }
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) }
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) }
// 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) } }
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) }
// 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] }
// 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 }
// 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 }