func getService(w http.ResponseWriter, r *http.Request) { r.ParseForm() service := r.Form.Get("service") var s []*registry.Service var err error if len(service) == 0 { s, err = (*cmd.DefaultOptions().Registry).ListServices() } else { s, err = (*cmd.DefaultOptions().Registry).GetService(service) } if err != nil { http.Error(w, err.Error(), 500) return } if s == nil || (len(service) > 0 && (len(s) == 0 || len(s[0].Name) == 0)) { http.Error(w, "Service not found", 404) return } b, err := json.Marshal(s) if err != nil { http.Error(w, err.Error(), 500) return } w.Header().Set("Content-Type", "application/json") w.Header().Set("Content-Length", strconv.Itoa(len(b))) w.Write(b) }
func web(ctx *cli.Context) { opts := []gweb.Option{ gweb.Name("go.micro.web.discovery"), gweb.Handler(whandler.Router()), } opts = append(opts, helper.WebOpts(ctx)...) templateDir := "discovery/templates" if dir := ctx.GlobalString("html_dir"); len(dir) > 0 { templateDir = dir } whandler.Init( templateDir, proto.NewDiscoveryClient("go.micro.srv.discovery", *cmd.DefaultOptions().Client), proto2.NewRegistryClient("go.micro.srv.discovery", *cmd.DefaultOptions().Client), ) service := gweb.NewService(opts...) if err := service.Run(); err != nil { log.Fatal(err) } }
func brokerHandler(w http.ResponseWriter, r *http.Request) { if r.Method != "GET" { http.Error(w, "Method not allowed", 405) return } r.ParseForm() topic := r.Form.Get("topic") if len(topic) == 0 { http.Error(w, "Topic not specified", 400) return } ws, err := upgrader.Upgrade(w, r, nil) if err != nil { log.Error(err.Error()) return } once.Do(func() { (*cmd.DefaultOptions().Broker).Init() (*cmd.DefaultOptions().Broker).Connect() }) c := &conn{ topic: topic, ws: ws, } go c.writeLoop() c.readLoop() }
// TODO: stream via HTTP func streamService(c *cli.Context) { if len(c.Args()) < 2 { fmt.Println("require service and method") return } service := c.Args()[0] method := c.Args()[1] var request map[string]interface{} json.Unmarshal([]byte(strings.Join(c.Args()[2:], " ")), &request) req := (*cmd.DefaultOptions().Client).NewJsonRequest(service, method, request) stream, err := (*cmd.DefaultOptions().Client).Stream(context.Background(), req) if err != nil { fmt.Printf("error calling %s.%s: %v\n", service, method, err) return } if err := stream.Send(request); err != nil { fmt.Printf("error sending to %s.%s: %v\n", service, method, err) return } for { var response map[string]interface{} if err := stream.Recv(&response); err != nil { fmt.Printf("error receiving from %s.%s: %v\n", service, method, err) return } b, _ := json.MarshalIndent(response, "", "\t") fmt.Println(string(b)) // artificial delay time.Sleep(time.Millisecond * 10) } }
func QueryService(c *cli.Context, args []string) ([]byte, error) { if len(args) < 2 { return nil, errors.New("require service and method") } var req, service, method string service = args[0] method = args[1] if len(args) > 2 { req = strings.Join(args[2:], " ") } // empty request if len(req) == 0 { req = `{}` } var request map[string]interface{} var response json.RawMessage if p := c.GlobalString("proxy_address"); len(p) > 0 { request = map[string]interface{}{ "service": service, "method": method, "request": req, } b, err := json.Marshal(request) if err != nil { return nil, err } if err := post(p+"/rpc", b, &response); err != nil { return nil, err } } else { d := json.NewDecoder(strings.NewReader(req)) d.UseNumber() if err := d.Decode(&request); err != nil { return nil, err } creq := (*cmd.DefaultOptions().Client).NewJsonRequest(service, method, request) err := (*cmd.DefaultOptions().Client).Call(context.Background(), creq, &response) if err != nil { return nil, fmt.Errorf("error calling %s.%s: %v\n", service, method, err) } } var out bytes.Buffer defer out.Reset() if err := json.Indent(&out, response, "", "\t"); err != nil { return nil, err } return out.Bytes(), nil }
func registryHandler(w http.ResponseWriter, r *http.Request) { r.ParseForm() svc := r.Form.Get("service") if len(svc) > 0 { s, err := (*cmd.DefaultOptions().Registry).GetService(svc) if err != nil { http.Error(w, "Error occurred:"+err.Error(), 500) return } if len(s) == 0 { http.Error(w, "Not found", 404) return } if r.Header.Get("Content-Type") == "application/json" { b, err := json.Marshal(map[string]interface{}{ "services": s, }) if err != nil { http.Error(w, "Error occurred:"+err.Error(), 500) return } w.Header().Set("Content-Type", "application/json") w.Write(b) return } render(w, r, serviceTemplate, s) return } services, err := (*cmd.DefaultOptions().Registry).ListServices() if err != nil { http.Error(w, "Error occurred:"+err.Error(), 500) return } sort.Sort(sortedServices{services}) if r.Header.Get("Content-Type") == "application/json" { b, err := json.Marshal(map[string]interface{}{ "services": services, }) if err != nil { http.Error(w, "Error occurred:"+err.Error(), 500) return } w.Header().Set("Content-Type", "application/json") w.Write(b) return } render(w, r, registryTemplate, services) }
func addService(w http.ResponseWriter, r *http.Request) { r.ParseForm() b, err := ioutil.ReadAll(r.Body) if err != nil { http.Error(w, err.Error(), 500) return } defer r.Body.Close() var opts []registry.RegisterOption // parse ttl if ttl := r.Form.Get("ttl"); len(ttl) > 0 { d, err := time.ParseDuration(ttl) if err == nil { opts = append(opts, registry.RegisterTTL(d)) } } var service *registry.Service err = json.Unmarshal(b, &service) if err != nil { http.Error(w, err.Error(), 500) return } err = (*cmd.DefaultOptions().Registry).Register(service, opts...) if err != nil { http.Error(w, err.Error(), 500) return } }
func (c *conn) writeLoop() { ticker := time.NewTicker(pingTime) subscriber, err := (*cmd.DefaultOptions().Broker).Subscribe(c.topic, func(p broker.Publication) error { b, err := json.Marshal(p.Message()) if err != nil { return nil } return c.write(websocket.TextMessage, b) }) defer func() { subscriber.Unsubscribe() ticker.Stop() c.ws.Close() }() if err != nil { log.Error(err.Error()) return } for _ = range ticker.C { if err := c.write(websocket.PingMessage, []byte{}); err != nil { return } } }
// run healthchecker func (s *Sidecar) run(exit chan bool) { parts := strings.Split(s.address, ":") host := strings.Join(parts[:len(parts)-1], ":") port, _ := strconv.Atoi(parts[len(parts)-1]) id := s.name + "-" + uuid.NewUUID().String() node := ®istry.Node{ Id: id, Address: host, Port: port, } service := ®istry.Service{ Name: s.name, Nodes: []*registry.Node{node}, } log.Printf("Registering %s", node.Id) (*cmd.DefaultOptions().Registry).Register(service) if len(s.hcUrl) == 0 { return } log.Print("Starting sidecar healthchecker") go s.hcLoop(service, exit) <-exit }
func (c *conn) readLoop() { defer func() { c.close() c.ws.Close() }() // set read limit/deadline c.ws.SetReadLimit(readLimit) c.ws.SetReadDeadline(time.Now().Add(readDeadline)) // set close handler ch := c.ws.CloseHandler() c.ws.SetCloseHandler(func(code int, text string) error { err := ch(code, text) c.close() return err }) // set pong handler c.ws.SetPongHandler(func(string) error { c.ws.SetReadDeadline(time.Now().Add(readDeadline)) return nil }) for { _, message, err := c.ws.ReadMessage() if err != nil { return } (*cmd.DefaultOptions().Broker).Publish(c.topic, &broker.Message{ Header: map[string]string{"Content-Type": c.cType}, Body: message, }) } }
func (s *server) proxy() http.Handler { sel := selector.NewSelector( selector.Registry((*cmd.DefaultOptions().Registry)), ) director := func(r *http.Request) { parts := strings.Split(r.URL.Path, "/") if len(parts) < 2 { return } if !re.MatchString(parts[1]) { return } next, err := sel.Select(Namespace + "." + parts[1]) if err != nil { return } r.URL.Scheme = "http" s, err := next() if err != nil { return } r.URL.Host = fmt.Sprintf("%s:%d", s.Address, s.Port) r.URL.Path = "/" + strings.Join(parts[2:], "/") } return &httputil.ReverseProxy{ Director: director, } }
func (s *Sidecar) run() { parts := strings.Split(s.address, ":") host := strings.Join(parts[:len(parts)-1], ":") port, _ := strconv.Atoi(parts[len(parts)-1]) id := s.name + "-" + uuid.NewUUID().String() node := ®istry.Node{ Id: id, Address: host, Port: port, } service := ®istry.Service{ Name: s.name, Nodes: []*registry.Node{node}, } log.Infof("Registering %s", node.Id) (*cmd.DefaultOptions().Registry).Register(service) if len(s.hcUrl) > 0 { log.Info("Starting sidecar healthchecker") exitCh := make(chan bool, 1) go s.hcLoop(service, exitCh) defer func() { exitCh <- true }() } ch := make(chan os.Signal, 1) signal.Notify(ch, syscall.SIGTERM, syscall.SIGINT, syscall.SIGKILL) <-ch }
func ListServices(c *cli.Context) ([]byte, error) { var rsp []*registry.Service var err error if p := c.GlobalString("proxy_address"); len(p) > 0 { if err := get(p+"/registry", &rsp); err != nil { return nil, err } } else { rsp, err = (*cmd.DefaultOptions().Registry).ListServices() if err != nil { return nil, err } } sort.Sort(sortedServices{rsp}) var services []string for _, service := range rsp { services = append(services, service.Name) } return []byte(strings.Join(services, "\n")), nil }
func DeregisterService(c *cli.Context, args []string) ([]byte, error) { if len(args) == 0 { return nil, errors.New("require service definition") } req := strings.Join(args, " ") if p := c.GlobalString("proxy_address"); len(p) > 0 { if err := del(p+"/registry", []byte(req), nil); err != nil { return nil, err } return []byte("ok"), nil } var service *registry.Service d := json.NewDecoder(strings.NewReader(req)) d.UseNumber() if err := d.Decode(&service); err != nil { return nil, err } if err := (*cmd.DefaultOptions().Registry).Deregister(service); err != nil { return nil, err } return []byte("ok"), nil }
// API handler is the default handler which takes api.Request and returns api.Response func (a *apiHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { request, err := requestToProto(r) if err != nil { er := errors.InternalServerError("go.micro.api", err.Error()) w.Header().Set("Content-Type", "application/json") w.WriteHeader(500) w.Write([]byte(er.Error())) return } // get service and method service, method := pathToReceiver(a.Namespace, r.URL.Path) // create request and response req := (*cmd.DefaultOptions().Client).NewRequest(service, method, request) rsp := &api.Response{} // create the context from headers ctx := helper.RequestToContext(r) if err := (*cmd.DefaultOptions().Client).Call(ctx, req, rsp); err != nil { w.Header().Set("Content-Type", "application/json") ce := errors.Parse(err.Error()) switch ce.Code { case 0: w.WriteHeader(500) default: w.WriteHeader(int(ce.Code)) } w.Write([]byte(ce.Error())) return } for _, header := range rsp.GetHeader() { for _, val := range header.Values { w.Header().Add(header.Key, val) } } if len(w.Header().Get("Content-Type")) == 0 { w.Header().Set("Content-Type", "application/json") } w.WriteHeader(int(rsp.StatusCode)) w.Write([]byte(rsp.Body)) }
func getService(c *cli.Context) { if !c.Args().Present() { fmt.Println("Service required") return } service, err := (*cmd.DefaultOptions().Registry).GetService(c.Args().First()) if err != nil { fmt.Println(err.Error()) return } if len(service) == 0 { fmt.Println("Service not found") return } fmt.Printf("service %s\n", service[0].Name) for _, serv := range service { fmt.Println("\nversion ", serv.Version) fmt.Println("\nId\tAddress\tPort\tMetadata") for _, node := range serv.Nodes { var meta []string for k, v := range node.Metadata { meta = append(meta, k+"="+v) } fmt.Printf("%s\t%s\t%d\t%s\n", node.Id, node.Address, node.Port, strings.Join(meta, ",")) } } for _, e := range service[0].Endpoints { var request, response string var meta []string for k, v := range e.Metadata { meta = append(meta, k+"="+v) } if e.Request != nil && len(e.Request.Values) > 0 { request = "{\n" for _, v := range e.Request.Values { request += formatEndpoint(v, 0) } request += "}" } else { request = "{}" } if e.Response != nil && len(e.Response.Values) > 0 { response = "{\n" for _, v := range e.Response.Values { response += formatEndpoint(v, 0) } response += "}" } else { response = "{}" } fmt.Printf("\nEndpoint: %s\nMetadata: %s\n", e.Name, strings.Join(meta, ",")) fmt.Printf("Request: %s\n\nResponse: %s\n", request, response) } }
// Proxy is a reverse proxy used by the micro web and api func Proxy(ns string, ws bool) http.Handler { return &proxy{ Namespace: ns, Selector: selector.NewSelector( selector.Registry((*cmd.DefaultOptions().Registry)), ), re: regexp.MustCompile("^[a-zA-Z0-9]+$"), ws: ws, } }
func queryService(c *cli.Context) { if len(c.Args()) < 2 { fmt.Println("require service and method") return } service := c.Args()[0] method := c.Args()[1] var request map[string]interface{} var response map[string]interface{} json.Unmarshal([]byte(strings.Join(c.Args()[2:], " ")), &request) req := (*cmd.DefaultOptions().Client).NewJsonRequest(service, method, request) err := (*cmd.DefaultOptions().Client).Call(context.Background(), req, &response) if err != nil { fmt.Printf("error calling %s.%s: %v\n", service, method, err) return } b, _ := json.MarshalIndent(response, "", "\t") fmt.Println(string(b)) }
func queryService(c *cli.Context) { if len(c.Args()) < 2 { fmt.Println("require service and method") return } service := c.Args()[0] method := c.Args()[1] var request map[string]interface{} var response map[string]interface{} if p := c.GlobalString("proxy_address"); len(p) > 0 { request = map[string]interface{}{ "service": service, "method": method, "request": []byte(strings.Join(c.Args()[2:], " ")), } b, err := json.Marshal(request) if err != nil { fmt.Println(err.Error()) return } if err := post(p+"/rpc", b, &response); err != nil { fmt.Println(err.Error()) return } } else { json.Unmarshal([]byte(strings.Join(c.Args()[2:], " ")), &request) req := (*cmd.DefaultOptions().Client).NewJsonRequest(service, method, request) err := (*cmd.DefaultOptions().Client).Call(context.Background(), req, &response) if err != nil { fmt.Printf("error calling %s.%s: %v\n", service, method, err) return } } b, _ := json.MarshalIndent(response, "", "\t") fmt.Println(string(b)) }
func Broker(w http.ResponseWriter, r *http.Request) { if r.Method != "GET" { http.Error(w, "Method not allowed", 405) return } r.ParseForm() topic := r.Form.Get("topic") if len(topic) == 0 { http.Error(w, "Topic not specified", 400) return } queue := r.Form.Get("queue") ws, err := upgrader.Upgrade(w, r, nil) if err != nil { log.Print(err.Error()) return } once.Do(func() { (*cmd.DefaultOptions().Broker).Init() (*cmd.DefaultOptions().Broker).Connect() }) cType := r.Header.Get("Content-Type") if len(cType) == 0 { cType = contentType } c := &conn{ cType: cType, topic: topic, queue: queue, exit: make(chan bool), ws: ws, } go c.writeLoop() c.readLoop() }
func listServices(c *cli.Context) { rsp, err := (*cmd.DefaultOptions().Registry).ListServices() if err != nil { fmt.Println(err.Error()) return } ss := sortedServices{rsp} sort.Sort(ss) for _, service := range ss.services { fmt.Println(service.Name) } }
func restHandler(w http.ResponseWriter, r *http.Request) { request, err := requestToProto(r) if err != nil { er := errors.InternalServerError("go.micro.api", err.Error()) w.Header().Set("Content-Type", "application/json") w.WriteHeader(500) w.Write([]byte(er.Error())) return } service, method := pathToReceiver(r.URL.Path) req := (*cmd.DefaultOptions().Client).NewRequest(service, method, request) rsp := &api.Response{} if err := (*cmd.DefaultOptions().Client).Call(context.Background(), req, rsp); err != nil { w.Header().Set("Content-Type", "application/json") ce := errors.Parse(err.Error()) switch ce.Code { case 0: w.WriteHeader(500) default: w.WriteHeader(int(ce.Code)) } w.Write([]byte(ce.Error())) return } for _, header := range rsp.GetHeader() { for _, val := range header.Values { w.Header().Add(header.Key, val) } } if len(w.Header().Get("Content-Type")) == 0 { w.Header().Set("Content-Type", "application/json") } w.WriteHeader(int(rsp.StatusCode)) w.Write([]byte(rsp.Body)) }
func (s *Sidecar) hcLoop(service *registry.Service, exitCh chan bool) { tick := time.NewTicker(time.Second * 30) registered := true for { select { case <-tick.C: _, err := s.hc() if err != nil && registered { log.Infof("Healthcheck error. Deregistering %v", service.Nodes[0].Id) (*cmd.DefaultOptions().Registry).Deregister(service) registered = false } else if err == nil && !registered { log.Infof("Healthcheck success. Registering %v", service.Nodes[0].Id) (*cmd.DefaultOptions().Registry).Register(service) registered = true } case <-exitCh: return } } }
func queryHandler(w http.ResponseWriter, r *http.Request) { services, err := (*cmd.DefaultOptions().Registry).ListServices() if err != nil { http.Error(w, "Error occurred:"+err.Error(), 500) return } sort.Sort(sortedServices{services}) serviceMap := make(map[string][]*registry.Endpoint) for _, service := range services { s, err := (*cmd.DefaultOptions().Registry).GetService(service.Name) if err != nil { continue } if len(s) == 0 { continue } serviceMap[service.Name] = s[0].Endpoints } if r.Header.Get("Content-Type") == "application/json" { b, err := json.Marshal(map[string]interface{}{ "services": services, }) if err != nil { http.Error(w, "Error occurred:"+err.Error(), 500) return } w.Header().Set("Content-Type", "application/json") w.Write(b) return } render(w, r, queryTemplate, serviceMap) }
func queryHealth(c *cli.Context) { if !c.Args().Present() { fmt.Println("require service name") return } service, err := (*cmd.DefaultOptions().Registry).GetService(c.Args().First()) if err != nil { fmt.Println(err.Error()) return } if service == nil || len(service) == 0 { fmt.Println("Service not found") return } req := (*cmd.DefaultOptions().Client).NewRequest(service[0].Name, "Debug.Health", &proto.HealthRequest{}) fmt.Printf("service %s\n\n", service[0].Name) for _, serv := range service { fmt.Println("\nversion ", serv.Version) fmt.Println("\nnode\t\taddress:port\t\tstatus") for _, node := range serv.Nodes { address := node.Address if node.Port > 0 { address = fmt.Sprintf("%s:%d", address, node.Port) } rsp := &proto.HealthResponse{} err := (*cmd.DefaultOptions().Client).CallRemote(context.Background(), address, req, rsp) var status string if err != nil { status = err.Error() } else { status = rsp.Status } fmt.Printf("%s\t\t%s:%d\t\t%s\n", node.Id, node.Address, node.Port, status) } } }
func deregisterService(c *cli.Context) { if len(c.Args()) != 1 { fmt.Println("require service definition") return } var service *registry.Service if err := json.Unmarshal([]byte(c.Args().First()), &service); err != nil { fmt.Println(err.Error()) return } if err := (*cmd.DefaultOptions().Registry).Deregister(service); err != nil { fmt.Println(err.Error()) return } }
func (s *srv) proxy() http.Handler { sel := selector.NewSelector( selector.Registry((*cmd.DefaultOptions().Registry)), ) director := func(r *http.Request) { kill := func() { r.URL.Host = "" r.URL.Path = "" r.URL.Scheme = "" r.Host = "" r.RequestURI = "" } parts := strings.Split(r.URL.Path, "/") if len(parts) < 2 { kill() return } if !re.MatchString(parts[1]) { kill() return } next, err := sel.Select(Namespace + "." + parts[1]) if err != nil { kill() return } s, err := next() if err != nil { kill() return } r.Header.Set(BasePathHeader, "/"+parts[1]) r.URL.Host = fmt.Sprintf("%s:%d", s.Address, s.Port) r.URL.Path = "/" + strings.Join(parts[2:], "/") r.URL.Scheme = "http" r.Host = r.URL.Host } return &proxy{ Default: &httputil.ReverseProxy{Director: director}, Director: director, } }
func (c *conn) readLoop() { defer func() { c.ws.Close() }() c.ws.SetReadLimit(readLimit) c.ws.SetReadDeadline(time.Now().Add(readDeadline)) c.ws.SetPongHandler(func(string) error { c.ws.SetReadDeadline(time.Now().Add(readDeadline)) return nil }) for { _, message, err := c.ws.ReadMessage() if err != nil { return } (*cmd.DefaultOptions().Broker).Publish(c.topic, &broker.Message{Body: message}) } }
func delService(w http.ResponseWriter, r *http.Request) { r.ParseForm() b, err := ioutil.ReadAll(r.Body) if err != nil { http.Error(w, err.Error(), 500) return } defer r.Body.Close() var service *registry.Service err = json.Unmarshal(b, &service) if err != nil { http.Error(w, err.Error(), 500) return } err = (*cmd.DefaultOptions().Registry).Deregister(service) if err != nil { http.Error(w, err.Error(), 500) return } }
func indexHandler(w http.ResponseWriter, r *http.Request) { services, err := (*cmd.DefaultOptions().Registry).ListServices() if err != nil { http.Error(w, "Error occurred:"+err.Error(), 500) return } var webServices []string for _, s := range services { if strings.Index(s.Name, Namespace) == 0 { webServices = append(webServices, strings.Replace(s.Name, Namespace+".", "", 1)) } } type templateData struct { HasWebServices bool WebServices []string } data := templateData{len(webServices) > 0, webServices} render(w, r, indexTemplate, data) }