// Vulcan implements Getter interface that is used by controllers to issue concurrent get requests with failover func (p *ReverseProxy) Get(w http.ResponseWriter, hosts []string, query client.MultiDict, auth *netutils.BasicAuth) error { req, err := http.NewRequest("GET", "http://localhost", nil) if err != nil { return err } if query != nil { parameters := url.Values{} for key, values := range query { for i, _ := range values { parameters.Add(key, values[i]) } } req.URL.RawQuery = parameters.Encode() } if auth != nil { req.SetBasicAuth(auth.Username, auth.Password) } upstreams, err := command.NewUpstreamsFromUrls(hosts) if err != nil { return err } req.Body = &Buffer{&bytes.Reader{}} cmd := &command.Forward{ Failover: &command.Failover{Active: true}, Upstreams: upstreams, } endpoints := command.EndpointsFromUpstreams(cmd.Upstreams) _, err = p.proxyRequest(w, req, cmd, endpoints) return err }
// Main request handler, accepts requests, round trips it to the upstream // proxies back the response. func (p *ReverseProxy) ServeHTTP(w http.ResponseWriter, req *http.Request) { p.metrics.Requests.Mark(1) glog.Infof("Serving Request %s %s", req.Method, req.RequestURI) // Ask controller for instructions cmdI, err := p.controller.GetInstructions(req) if err != nil { glog.Errorf("Error getting instructions: %s", err) p.replyError(err, w, req) return } switch cmd := cmdI.(type) { case *command.Reply: // reply command provides the exact response // for the client. Proxy responds and hangs up. glog.Infof("Got Reply command: %v", cmd) p.metrics.CmdReply.Mark(1) p.replyCommand(cmd, w, req) return case *command.Forward: // Forward command contains list of upstreams // instructions for the failover and request modification glog.Infof("Got Forward command %v", cmd) p.metrics.CmdForward.Mark(1) // Get upstreams ready to process the request retrySeconds, err := p.rateLimit(cmd) if err != nil { p.replyError(err, w, req) return } if retrySeconds != 0 { p.replyError(&command.RetryError{Seconds: retrySeconds}, w, req) return } endpoints := command.EndpointsFromUpstreams(cmd.Upstreams) requestBytes, err := p.proxyRequest(w, req, cmd, endpoints) if err != nil { glog.Error("Failed to proxy to all upstreams:", err) p.replyError(&command.AllUpstreamsDownError{}, w, req) return } p.updateRates(requestBytes, cmd) return } p.replyError(fmt.Errorf("Internal logic error"), w, req) }