func Route(cache etcache.Cache, r *http.Request, etcdKey string, modulos uint64) (io.ReadCloser, error) { bucket := HashResource(r.URL.Path) % modulos shard := fmt.Sprint(bucket, "-", fmt.Sprint(modulos)) _master, err := cache.Get(path.Join(etcdKey, shard), false, false) if err != nil { return nil, err } master := _master.Node.Value httpClient := &http.Client{} // `Do` will complain if r.RequestURI is set so we unset it r.RequestURI = "" r.URL.Scheme = "http" r.URL.Host = strings.TrimPrefix(master, "http://") resp, err := httpClient.Do(r) if err != nil { return nil, err } if resp.StatusCode != http.StatusOK { return nil, fmt.Errorf("Failed request (%s) to %s.", resp.Status, r.URL.String()) } return resp.Body, nil }
// Multicast enables the Ogre Magi to rapidly cast his spells, giving them // greater potency. // Multicast sends a request to every host it finds under a key and returns a // ReadCloser for each one. func Multicast(cache etcache.Cache, r *http.Request, etcdKey string) ([]*http.Response, error) { _endpoints, err := cache.Get(etcdKey, false, true) if err != nil { return nil, err } endpoints := _endpoints.Node.Nodes if len(endpoints) == 0 { return nil, ErrNoHosts } // If the request has a body we need to store it in memory because it needs // to be sent to multiple endpoints and Reader (the type of r.Body) is // single use. var body []byte if r.ContentLength != 0 { body, err = ioutil.ReadAll(r.Body) if err != nil { return nil, err } } var resps []*http.Response errors := make(chan error, len(endpoints)) var lock sync.Mutex var wg sync.WaitGroup wg.Add(len(endpoints)) for _, node := range endpoints { go func(node *etcd.Node) { defer wg.Done() httpClient := &http.Client{} // First make a request, taking some values from the previous request. url := node.Value + r.URL.Path + "?" + r.URL.RawQuery req, err := http.NewRequest(r.Method, url, ioutil.NopCloser(bytes.NewReader(body))) if err != nil { errors <- err return } // Send the request resp, err := httpClient.Do(req) if err != nil { errors <- err return } if resp.StatusCode != http.StatusOK { errors <- fmt.Errorf("Failed request (%s) to %s.", resp.Status, r.URL.String()) return } // Append the request to the response slice. lock.Lock() resps = append(resps, resp) lock.Unlock() }(node) } wg.Wait() close(errors) for err := range errors { if err != nil { return nil, err } } return resps, nil }