mirror of
				https://github.com/go-micro/go-micro.git
				synced 2025-10-30 23:27:41 +02:00 
			
		
		
		
	remove web
This commit is contained in:
		
							
								
								
									
										259
									
								
								web/options.go
									
									
									
									
									
								
							
							
						
						
									
										259
									
								
								web/options.go
									
									
									
									
									
								
							| @@ -1,259 +0,0 @@ | ||||
| package web | ||||
|  | ||||
| import ( | ||||
| 	"context" | ||||
| 	"crypto/tls" | ||||
| 	"net/http" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/micro/cli/v2" | ||||
| 	"github.com/micro/go-micro/v2" | ||||
| 	"github.com/micro/go-micro/v2/registry" | ||||
| ) | ||||
|  | ||||
| //Options for web | ||||
| type Options struct { | ||||
| 	Name      string | ||||
| 	Version   string | ||||
| 	Id        string | ||||
| 	Metadata  map[string]string | ||||
| 	Address   string | ||||
| 	Advertise string | ||||
|  | ||||
| 	Action func(*cli.Context) | ||||
| 	Flags  []cli.Flag | ||||
|  | ||||
| 	RegisterTTL      time.Duration | ||||
| 	RegisterInterval time.Duration | ||||
|  | ||||
| 	// RegisterCheck runs a check function before registering the service | ||||
| 	RegisterCheck func(context.Context) error | ||||
|  | ||||
| 	Server  *http.Server | ||||
| 	Handler http.Handler | ||||
|  | ||||
| 	// Alternative Options | ||||
| 	Context context.Context | ||||
|  | ||||
| 	Registry registry.Registry | ||||
| 	Service  micro.Service | ||||
|  | ||||
| 	Secure      bool | ||||
| 	TLSConfig   *tls.Config | ||||
| 	BeforeStart []func() error | ||||
| 	BeforeStop  []func() error | ||||
| 	AfterStart  []func() error | ||||
| 	AfterStop   []func() error | ||||
|  | ||||
| 	// Static directory | ||||
| 	StaticDir string | ||||
|  | ||||
| 	Signal bool | ||||
| } | ||||
|  | ||||
| func newOptions(opts ...Option) Options { | ||||
| 	opt := Options{ | ||||
| 		Name:             DefaultName, | ||||
| 		Version:          DefaultVersion, | ||||
| 		Id:               DefaultId, | ||||
| 		Address:          DefaultAddress, | ||||
| 		RegisterTTL:      DefaultRegisterTTL, | ||||
| 		RegisterInterval: DefaultRegisterInterval, | ||||
| 		StaticDir:        DefaultStaticDir, | ||||
| 		Service:          micro.NewService(), | ||||
| 		Context:          context.TODO(), | ||||
| 		Signal:           true, | ||||
| 	} | ||||
|  | ||||
| 	for _, o := range opts { | ||||
| 		o(&opt) | ||||
| 	} | ||||
|  | ||||
| 	if opt.RegisterCheck == nil { | ||||
| 		opt.RegisterCheck = DefaultRegisterCheck | ||||
| 	} | ||||
|  | ||||
| 	return opt | ||||
| } | ||||
|  | ||||
| // Name of Web | ||||
| func Name(n string) Option { | ||||
| 	return func(o *Options) { | ||||
| 		o.Name = n | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Icon specifies an icon url to load in the UI | ||||
| func Icon(ico string) Option { | ||||
| 	return func(o *Options) { | ||||
| 		if o.Metadata == nil { | ||||
| 			o.Metadata = make(map[string]string) | ||||
| 		} | ||||
| 		o.Metadata["icon"] = ico | ||||
| 	} | ||||
| } | ||||
|  | ||||
| //Id for Unique server id | ||||
| func Id(id string) Option { | ||||
| 	return func(o *Options) { | ||||
| 		o.Id = id | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Version of the service | ||||
| func Version(v string) Option { | ||||
| 	return func(o *Options) { | ||||
| 		o.Version = v | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Metadata associated with the service | ||||
| func Metadata(md map[string]string) Option { | ||||
| 	return func(o *Options) { | ||||
| 		o.Metadata = md | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Address to bind to - host:port | ||||
| func Address(a string) Option { | ||||
| 	return func(o *Options) { | ||||
| 		o.Address = a | ||||
| 	} | ||||
| } | ||||
|  | ||||
| //Advertise The address to advertise for discovery - host:port | ||||
| func Advertise(a string) Option { | ||||
| 	return func(o *Options) { | ||||
| 		o.Advertise = a | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Context specifies a context for the service. | ||||
| // Can be used to signal shutdown of the service. | ||||
| // Can be used for extra option values. | ||||
| func Context(ctx context.Context) Option { | ||||
| 	return func(o *Options) { | ||||
| 		o.Context = ctx | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Registry used for discovery | ||||
| func Registry(r registry.Registry) Option { | ||||
| 	return func(o *Options) { | ||||
| 		o.Registry = r | ||||
| 	} | ||||
| } | ||||
|  | ||||
| //RegisterTTL Register the service with a TTL | ||||
| func RegisterTTL(t time.Duration) Option { | ||||
| 	return func(o *Options) { | ||||
| 		o.RegisterTTL = t | ||||
| 	} | ||||
| } | ||||
|  | ||||
| //RegisterInterval Register the service with at interval | ||||
| func RegisterInterval(t time.Duration) Option { | ||||
| 	return func(o *Options) { | ||||
| 		o.RegisterInterval = t | ||||
| 	} | ||||
| } | ||||
|  | ||||
| //Handler for custom handler | ||||
| func Handler(h http.Handler) Option { | ||||
| 	return func(o *Options) { | ||||
| 		o.Handler = h | ||||
| 	} | ||||
| } | ||||
|  | ||||
| //Server for custom Server | ||||
| func Server(srv *http.Server) Option { | ||||
| 	return func(o *Options) { | ||||
| 		o.Server = srv | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // MicroService sets the micro.Service used internally | ||||
| func MicroService(s micro.Service) Option { | ||||
| 	return func(o *Options) { | ||||
| 		o.Service = s | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Flags sets the command flags. | ||||
| func Flags(flags ...cli.Flag) Option { | ||||
| 	return func(o *Options) { | ||||
| 		o.Flags = append(o.Flags, flags...) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Action sets the command action. | ||||
| func Action(a func(*cli.Context)) Option { | ||||
| 	return func(o *Options) { | ||||
| 		o.Action = a | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // BeforeStart is executed before the server starts. | ||||
| func BeforeStart(fn func() error) Option { | ||||
| 	return func(o *Options) { | ||||
| 		o.BeforeStart = append(o.BeforeStart, fn) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // BeforeStop is executed before the server stops. | ||||
| func BeforeStop(fn func() error) Option { | ||||
| 	return func(o *Options) { | ||||
| 		o.BeforeStop = append(o.BeforeStop, fn) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // AfterStart is executed after server start. | ||||
| func AfterStart(fn func() error) Option { | ||||
| 	return func(o *Options) { | ||||
| 		o.AfterStart = append(o.AfterStart, fn) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // AfterStop is executed after server stop. | ||||
| func AfterStop(fn func() error) Option { | ||||
| 	return func(o *Options) { | ||||
| 		o.AfterStop = append(o.AfterStop, fn) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Secure Use secure communication. If TLSConfig is not specified we use InsecureSkipVerify and generate a self signed cert | ||||
| func Secure(b bool) Option { | ||||
| 	return func(o *Options) { | ||||
| 		o.Secure = b | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // TLSConfig to be used for the transport. | ||||
| func TLSConfig(t *tls.Config) Option { | ||||
| 	return func(o *Options) { | ||||
| 		o.TLSConfig = t | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // StaticDir sets the static file directory. This defaults to ./html | ||||
| func StaticDir(d string) Option { | ||||
| 	return func(o *Options) { | ||||
| 		o.StaticDir = d | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // RegisterCheck run func before registry service | ||||
| func RegisterCheck(fn func(context.Context) error) Option { | ||||
| 	return func(o *Options) { | ||||
| 		o.RegisterCheck = fn | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // HandleSignal toggles automatic installation of the signal handler that | ||||
| // traps TERM, INT, and QUIT.  Users of this feature to disable the signal | ||||
| // handler, should control liveness of the service through the context. | ||||
| func HandleSignal(b bool) Option { | ||||
| 	return func(o *Options) { | ||||
| 		o.Signal = b | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										542
									
								
								web/service.go
									
									
									
									
									
								
							
							
						
						
									
										542
									
								
								web/service.go
									
									
									
									
									
								
							| @@ -1,542 +0,0 @@ | ||||
| package web | ||||
|  | ||||
| import ( | ||||
| 	"crypto/tls" | ||||
| 	"fmt" | ||||
| 	"net" | ||||
| 	"net/http" | ||||
| 	"os" | ||||
| 	"os/signal" | ||||
| 	"path/filepath" | ||||
| 	"strings" | ||||
| 	"sync" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/micro/cli/v2" | ||||
| 	"github.com/micro/go-micro/v2" | ||||
| 	"github.com/micro/go-micro/v2/logger" | ||||
| 	"github.com/micro/go-micro/v2/registry" | ||||
| 	maddr "github.com/micro/go-micro/v2/util/addr" | ||||
| 	authutil "github.com/micro/go-micro/v2/util/auth" | ||||
| 	"github.com/micro/go-micro/v2/util/backoff" | ||||
| 	mhttp "github.com/micro/go-micro/v2/util/http" | ||||
| 	mnet "github.com/micro/go-micro/v2/util/net" | ||||
| 	signalutil "github.com/micro/go-micro/v2/util/signal" | ||||
| 	mls "github.com/micro/go-micro/v2/util/tls" | ||||
| ) | ||||
|  | ||||
| type service struct { | ||||
| 	opts Options | ||||
|  | ||||
| 	mux *http.ServeMux | ||||
| 	srv *registry.Service | ||||
|  | ||||
| 	sync.RWMutex | ||||
| 	running bool | ||||
| 	static  bool | ||||
| 	exit    chan chan error | ||||
| } | ||||
|  | ||||
| func newService(opts ...Option) Service { | ||||
| 	options := newOptions(opts...) | ||||
| 	s := &service{ | ||||
| 		opts:   options, | ||||
| 		mux:    http.NewServeMux(), | ||||
| 		static: true, | ||||
| 	} | ||||
| 	s.srv = s.genSrv() | ||||
| 	return s | ||||
| } | ||||
|  | ||||
| func (s *service) genSrv() *registry.Service { | ||||
| 	var host string | ||||
| 	var port string | ||||
| 	var err error | ||||
|  | ||||
| 	// default host:port | ||||
| 	if len(s.opts.Address) > 0 { | ||||
| 		host, port, err = net.SplitHostPort(s.opts.Address) | ||||
| 		if err != nil { | ||||
| 			logger.Fatal(err) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// check the advertise address first | ||||
| 	// if it exists then use it, otherwise | ||||
| 	// use the address | ||||
| 	if len(s.opts.Advertise) > 0 { | ||||
| 		host, port, err = net.SplitHostPort(s.opts.Advertise) | ||||
| 		if err != nil { | ||||
| 			logger.Fatal(err) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	addr, err := maddr.Extract(host) | ||||
| 	if err != nil { | ||||
| 		logger.Fatal(err) | ||||
| 	} | ||||
|  | ||||
| 	if strings.Count(addr, ":") > 0 { | ||||
| 		addr = "[" + addr + "]" | ||||
| 	} | ||||
|  | ||||
| 	return ®istry.Service{ | ||||
| 		Name:    s.opts.Name, | ||||
| 		Version: s.opts.Version, | ||||
| 		Nodes: []*registry.Node{{ | ||||
| 			Id:       s.opts.Id, | ||||
| 			Address:  fmt.Sprintf("%s:%s", addr, port), | ||||
| 			Metadata: s.opts.Metadata, | ||||
| 		}}, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (s *service) run(exit chan bool) { | ||||
| 	s.RLock() | ||||
| 	if s.opts.RegisterInterval <= time.Duration(0) { | ||||
| 		s.RUnlock() | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	t := time.NewTicker(s.opts.RegisterInterval) | ||||
| 	s.RUnlock() | ||||
|  | ||||
| 	for { | ||||
| 		select { | ||||
| 		case <-t.C: | ||||
| 			s.register() | ||||
| 		case <-exit: | ||||
| 			t.Stop() | ||||
| 			return | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (s *service) register() error { | ||||
| 	s.Lock() | ||||
| 	defer s.Unlock() | ||||
|  | ||||
| 	if s.srv == nil { | ||||
| 		return nil | ||||
| 	} | ||||
| 	// default to service registry | ||||
| 	r := s.opts.Service.Client().Options().Registry | ||||
| 	// switch to option if specified | ||||
| 	if s.opts.Registry != nil { | ||||
| 		r = s.opts.Registry | ||||
| 	} | ||||
|  | ||||
| 	// service node need modify, node address maybe changed | ||||
| 	srv := s.genSrv() | ||||
| 	srv.Endpoints = s.srv.Endpoints | ||||
| 	s.srv = srv | ||||
|  | ||||
| 	// use RegisterCheck func before register | ||||
| 	if err := s.opts.RegisterCheck(s.opts.Context); err != nil { | ||||
| 		if logger.V(logger.ErrorLevel, logger.DefaultLogger) { | ||||
| 			logger.Errorf("Server %s-%s register check error: %s", s.opts.Name, s.opts.Id, err) | ||||
| 		} | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	var regErr error | ||||
|  | ||||
| 	// try three times if necessary | ||||
| 	for i := 0; i < 3; i++ { | ||||
| 		// attempt to register | ||||
| 		if err := r.Register(s.srv, registry.RegisterTTL(s.opts.RegisterTTL)); err != nil { | ||||
| 			// set the error | ||||
| 			regErr = err | ||||
| 			// backoff then retry | ||||
| 			time.Sleep(backoff.Do(i + 1)) | ||||
| 			continue | ||||
| 		} | ||||
| 		// success so nil error | ||||
| 		regErr = nil | ||||
| 		break | ||||
| 	} | ||||
|  | ||||
| 	return regErr | ||||
| } | ||||
|  | ||||
| func (s *service) deregister() error { | ||||
| 	s.Lock() | ||||
| 	defer s.Unlock() | ||||
|  | ||||
| 	if s.srv == nil { | ||||
| 		return nil | ||||
| 	} | ||||
| 	// default to service registry | ||||
| 	r := s.opts.Service.Client().Options().Registry | ||||
| 	// switch to option if specified | ||||
| 	if s.opts.Registry != nil { | ||||
| 		r = s.opts.Registry | ||||
| 	} | ||||
| 	return r.Deregister(s.srv) | ||||
| } | ||||
|  | ||||
| func (s *service) start() error { | ||||
| 	s.Lock() | ||||
| 	defer s.Unlock() | ||||
|  | ||||
| 	if s.running { | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	for _, fn := range s.opts.BeforeStart { | ||||
| 		if err := fn(); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	l, err := s.listen("tcp", s.opts.Address) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	s.opts.Address = l.Addr().String() | ||||
| 	srv := s.genSrv() | ||||
| 	srv.Endpoints = s.srv.Endpoints | ||||
| 	s.srv = srv | ||||
|  | ||||
| 	var h http.Handler | ||||
|  | ||||
| 	if s.opts.Handler != nil { | ||||
| 		h = s.opts.Handler | ||||
| 	} else { | ||||
| 		h = s.mux | ||||
| 		var r sync.Once | ||||
|  | ||||
| 		// register the html dir | ||||
| 		r.Do(func() { | ||||
| 			// static dir | ||||
| 			static := s.opts.StaticDir | ||||
| 			if s.opts.StaticDir[0] != '/' { | ||||
| 				dir, _ := os.Getwd() | ||||
| 				static = filepath.Join(dir, static) | ||||
| 			} | ||||
|  | ||||
| 			// set static if no / handler is registered | ||||
| 			if s.static { | ||||
| 				_, err := os.Stat(static) | ||||
| 				if err == nil { | ||||
| 					if logger.V(logger.InfoLevel, logger.DefaultLogger) { | ||||
| 						logger.Infof("Enabling static file serving from %s", static) | ||||
| 					} | ||||
| 					s.mux.Handle("/", http.FileServer(http.Dir(static))) | ||||
| 				} | ||||
| 			} | ||||
| 		}) | ||||
| 	} | ||||
|  | ||||
| 	var httpSrv *http.Server | ||||
| 	if s.opts.Server != nil { | ||||
| 		httpSrv = s.opts.Server | ||||
| 	} else { | ||||
| 		httpSrv = &http.Server{} | ||||
| 	} | ||||
|  | ||||
| 	httpSrv.Handler = h | ||||
|  | ||||
| 	go httpSrv.Serve(l) | ||||
|  | ||||
| 	for _, fn := range s.opts.AfterStart { | ||||
| 		if err := fn(); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	s.exit = make(chan chan error, 1) | ||||
| 	s.running = true | ||||
|  | ||||
| 	go func() { | ||||
| 		ch := <-s.exit | ||||
| 		ch <- l.Close() | ||||
| 	}() | ||||
|  | ||||
| 	if logger.V(logger.InfoLevel, logger.DefaultLogger) { | ||||
| 		logger.Infof("Listening on %v", l.Addr().String()) | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (s *service) stop() error { | ||||
| 	s.Lock() | ||||
| 	defer s.Unlock() | ||||
|  | ||||
| 	if !s.running { | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	for _, fn := range s.opts.BeforeStop { | ||||
| 		if err := fn(); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	ch := make(chan error, 1) | ||||
| 	s.exit <- ch | ||||
| 	s.running = false | ||||
|  | ||||
| 	if logger.V(logger.InfoLevel, logger.DefaultLogger) { | ||||
| 		logger.Info("Stopping") | ||||
| 	} | ||||
|  | ||||
| 	for _, fn := range s.opts.AfterStop { | ||||
| 		if err := fn(); err != nil { | ||||
| 			if chErr := <-ch; chErr != nil { | ||||
| 				return chErr | ||||
| 			} | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return <-ch | ||||
| } | ||||
|  | ||||
| func (s *service) Client() *http.Client { | ||||
| 	rt := mhttp.NewRoundTripper( | ||||
| 		mhttp.WithRegistry(s.opts.Registry), | ||||
| 	) | ||||
| 	return &http.Client{ | ||||
| 		Transport: rt, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (s *service) Handle(pattern string, handler http.Handler) { | ||||
| 	var seen bool | ||||
| 	s.RLock() | ||||
| 	for _, ep := range s.srv.Endpoints { | ||||
| 		if ep.Name == pattern { | ||||
| 			seen = true | ||||
| 			break | ||||
| 		} | ||||
| 	} | ||||
| 	s.RUnlock() | ||||
|  | ||||
| 	// if its unseen then add an endpoint | ||||
| 	if !seen { | ||||
| 		s.Lock() | ||||
| 		s.srv.Endpoints = append(s.srv.Endpoints, ®istry.Endpoint{ | ||||
| 			Name: pattern, | ||||
| 		}) | ||||
| 		s.Unlock() | ||||
| 	} | ||||
|  | ||||
| 	// disable static serving | ||||
| 	if pattern == "/" { | ||||
| 		s.Lock() | ||||
| 		s.static = false | ||||
| 		s.Unlock() | ||||
| 	} | ||||
|  | ||||
| 	// register the handler | ||||
| 	s.mux.Handle(pattern, handler) | ||||
| } | ||||
|  | ||||
| func (s *service) HandleFunc(pattern string, handler func(http.ResponseWriter, *http.Request)) { | ||||
|  | ||||
| 	var seen bool | ||||
| 	s.RLock() | ||||
| 	for _, ep := range s.srv.Endpoints { | ||||
| 		if ep.Name == pattern { | ||||
| 			seen = true | ||||
| 			break | ||||
| 		} | ||||
| 	} | ||||
| 	s.RUnlock() | ||||
|  | ||||
| 	if !seen { | ||||
| 		s.Lock() | ||||
| 		s.srv.Endpoints = append(s.srv.Endpoints, ®istry.Endpoint{ | ||||
| 			Name: pattern, | ||||
| 		}) | ||||
| 		s.Unlock() | ||||
| 	} | ||||
|  | ||||
| 	// disable static serving | ||||
| 	if pattern == "/" { | ||||
| 		s.Lock() | ||||
| 		s.static = false | ||||
| 		s.Unlock() | ||||
| 	} | ||||
|  | ||||
| 	s.mux.HandleFunc(pattern, handler) | ||||
| } | ||||
|  | ||||
| func (s *service) Init(opts ...Option) error { | ||||
| 	s.Lock() | ||||
|  | ||||
| 	for _, o := range opts { | ||||
| 		o(&s.opts) | ||||
| 	} | ||||
|  | ||||
| 	serviceOpts := []micro.Option{} | ||||
|  | ||||
| 	if len(s.opts.Flags) > 0 { | ||||
| 		serviceOpts = append(serviceOpts, micro.Flags(s.opts.Flags...)) | ||||
| 	} | ||||
|  | ||||
| 	if s.opts.Registry != nil { | ||||
| 		serviceOpts = append(serviceOpts, micro.Registry(s.opts.Registry)) | ||||
| 	} | ||||
|  | ||||
| 	s.Unlock() | ||||
|  | ||||
| 	serviceOpts = append(serviceOpts, micro.Action(func(ctx *cli.Context) error { | ||||
| 		s.Lock() | ||||
| 		defer s.Unlock() | ||||
|  | ||||
| 		if ttl := ctx.Int("register_ttl"); ttl > 0 { | ||||
| 			s.opts.RegisterTTL = time.Duration(ttl) * time.Second | ||||
| 		} | ||||
|  | ||||
| 		if interval := ctx.Int("register_interval"); interval > 0 { | ||||
| 			s.opts.RegisterInterval = time.Duration(interval) * time.Second | ||||
| 		} | ||||
|  | ||||
| 		if name := ctx.String("server_name"); len(name) > 0 { | ||||
| 			s.opts.Name = name | ||||
| 		} | ||||
|  | ||||
| 		if ver := ctx.String("server_version"); len(ver) > 0 { | ||||
| 			s.opts.Version = ver | ||||
| 		} | ||||
|  | ||||
| 		if id := ctx.String("server_id"); len(id) > 0 { | ||||
| 			s.opts.Id = id | ||||
| 		} | ||||
|  | ||||
| 		if addr := ctx.String("server_address"); len(addr) > 0 { | ||||
| 			s.opts.Address = addr | ||||
| 		} | ||||
|  | ||||
| 		if adv := ctx.String("server_advertise"); len(adv) > 0 { | ||||
| 			s.opts.Advertise = adv | ||||
| 		} | ||||
|  | ||||
| 		if s.opts.Action != nil { | ||||
| 			s.opts.Action(ctx) | ||||
| 		} | ||||
|  | ||||
| 		return nil | ||||
| 	})) | ||||
|  | ||||
| 	s.RLock() | ||||
| 	// pass in own name and version | ||||
| 	if s.opts.Service.Name() == "" { | ||||
| 		serviceOpts = append(serviceOpts, micro.Name(s.opts.Name)) | ||||
| 	} | ||||
| 	serviceOpts = append(serviceOpts, micro.Version(s.opts.Version)) | ||||
| 	s.RUnlock() | ||||
|  | ||||
| 	s.opts.Service.Init(serviceOpts...) | ||||
|  | ||||
| 	s.Lock() | ||||
| 	srv := s.genSrv() | ||||
| 	srv.Endpoints = s.srv.Endpoints | ||||
| 	s.srv = srv | ||||
| 	s.Unlock() | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (s *service) Run() error { | ||||
| 	// generate an auth account | ||||
| 	srvID := s.opts.Service.Server().Options().Id | ||||
| 	srvName := s.Options().Name | ||||
| 	if err := authutil.Generate(srvID, srvName, s.opts.Service.Options().Auth); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	if err := s.start(); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	if err := s.register(); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	// start reg loop | ||||
| 	ex := make(chan bool) | ||||
| 	go s.run(ex) | ||||
|  | ||||
| 	ch := make(chan os.Signal, 1) | ||||
| 	if s.opts.Signal { | ||||
| 		signal.Notify(ch, signalutil.Shutdown()...) | ||||
| 	} | ||||
|  | ||||
| 	select { | ||||
| 	// wait on kill signal | ||||
| 	case sig := <-ch: | ||||
| 		if logger.V(logger.InfoLevel, logger.DefaultLogger) { | ||||
| 			logger.Infof("Received signal %s", sig) | ||||
| 		} | ||||
| 	// wait on context cancel | ||||
| 	case <-s.opts.Context.Done(): | ||||
| 		if logger.V(logger.InfoLevel, logger.DefaultLogger) { | ||||
| 			logger.Info("Received context shutdown") | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// exit reg loop | ||||
| 	close(ex) | ||||
|  | ||||
| 	if err := s.deregister(); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	return s.stop() | ||||
| } | ||||
|  | ||||
| // Options returns the options for the given service | ||||
| func (s *service) Options() Options { | ||||
| 	return s.opts | ||||
| } | ||||
|  | ||||
| func (s *service) listen(network, addr string) (net.Listener, error) { | ||||
| 	var l net.Listener | ||||
| 	var err error | ||||
|  | ||||
| 	// TODO: support use of listen options | ||||
| 	if s.opts.Secure || s.opts.TLSConfig != nil { | ||||
| 		config := s.opts.TLSConfig | ||||
|  | ||||
| 		fn := func(addr string) (net.Listener, error) { | ||||
| 			if config == nil { | ||||
| 				hosts := []string{addr} | ||||
|  | ||||
| 				// check if its a valid host:port | ||||
| 				if host, _, err := net.SplitHostPort(addr); err == nil { | ||||
| 					if len(host) == 0 { | ||||
| 						hosts = maddr.IPs() | ||||
| 					} else { | ||||
| 						hosts = []string{host} | ||||
| 					} | ||||
| 				} | ||||
|  | ||||
| 				// generate a certificate | ||||
| 				cert, err := mls.Certificate(hosts...) | ||||
| 				if err != nil { | ||||
| 					return nil, err | ||||
| 				} | ||||
| 				config = &tls.Config{Certificates: []tls.Certificate{cert}} | ||||
| 			} | ||||
| 			return tls.Listen(network, addr, config) | ||||
| 		} | ||||
|  | ||||
| 		l, err = mnet.Listen(addr, fn) | ||||
| 	} else { | ||||
| 		fn := func(addr string) (net.Listener, error) { | ||||
| 			return net.Listen(network, addr) | ||||
| 		} | ||||
|  | ||||
| 		l, err = mnet.Listen(addr, fn) | ||||
| 	} | ||||
|  | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	return l, nil | ||||
| } | ||||
| @@ -1,300 +0,0 @@ | ||||
| package web | ||||
|  | ||||
| import ( | ||||
| 	"crypto/tls" | ||||
| 	"fmt" | ||||
| 	"io/ioutil" | ||||
| 	"net/http" | ||||
| 	"os" | ||||
| 	"os/signal" | ||||
| 	"syscall" | ||||
| 	"testing" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/micro/go-micro/v2/registry" | ||||
| 	"github.com/micro/go-micro/v2/registry/memory" | ||||
| ) | ||||
|  | ||||
| func TestService(t *testing.T) { | ||||
| 	var ( | ||||
| 		beforeStartCalled bool | ||||
| 		afterStartCalled  bool | ||||
| 		beforeStopCalled  bool | ||||
| 		afterStopCalled   bool | ||||
| 		str               = `<html><body><h1>Hello World</h1></body></html>` | ||||
| 		fn                = func(w http.ResponseWriter, r *http.Request) { fmt.Fprint(w, str) } | ||||
| 		reg               = memory.NewRegistry() | ||||
| 	) | ||||
|  | ||||
| 	beforeStart := func() error { | ||||
| 		beforeStartCalled = true | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	afterStart := func() error { | ||||
| 		afterStartCalled = true | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	beforeStop := func() error { | ||||
| 		beforeStopCalled = true | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	afterStop := func() error { | ||||
| 		afterStopCalled = true | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	service := NewService( | ||||
| 		Name("go.micro.web.test"), | ||||
| 		Registry(reg), | ||||
| 		BeforeStart(beforeStart), | ||||
| 		AfterStart(afterStart), | ||||
| 		BeforeStop(beforeStop), | ||||
| 		AfterStop(afterStop), | ||||
| 	) | ||||
|  | ||||
| 	service.HandleFunc("/", fn) | ||||
|  | ||||
| 	errCh := make(chan error, 1) | ||||
| 	go func() { | ||||
| 		errCh <- service.Run() | ||||
| 		close(errCh) | ||||
| 	}() | ||||
|  | ||||
| 	var s []*registry.Service | ||||
|  | ||||
| 	eventually(func() bool { | ||||
| 		var err error | ||||
| 		s, err = reg.GetService("go.micro.web.test") | ||||
| 		return err == nil | ||||
| 	}, t.Fatal) | ||||
|  | ||||
| 	if have, want := len(s), 1; have != want { | ||||
| 		t.Fatalf("Expected %d but got %d services", want, have) | ||||
| 	} | ||||
|  | ||||
| 	rsp, err := http.Get(fmt.Sprintf("http://%s", s[0].Nodes[0].Address)) | ||||
| 	if err != nil { | ||||
| 		t.Fatal(err) | ||||
| 	} | ||||
| 	defer rsp.Body.Close() | ||||
|  | ||||
| 	b, err := ioutil.ReadAll(rsp.Body) | ||||
| 	if err != nil { | ||||
| 		t.Fatal(err) | ||||
| 	} | ||||
|  | ||||
| 	if string(b) != str { | ||||
| 		t.Errorf("Expected %s got %s", str, string(b)) | ||||
| 	} | ||||
|  | ||||
| 	callbackTests := []struct { | ||||
| 		subject string | ||||
| 		have    interface{} | ||||
| 	}{ | ||||
| 		{"beforeStartCalled", beforeStartCalled}, | ||||
| 		{"afterStartCalled", afterStartCalled}, | ||||
| 	} | ||||
|  | ||||
| 	for _, tt := range callbackTests { | ||||
| 		if tt.have != true { | ||||
| 			t.Errorf("unexpected %s: want true, have false", tt.subject) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	select { | ||||
| 	case err := <-errCh: | ||||
| 		if err != nil { | ||||
| 			t.Fatalf("service.Run():%v", err) | ||||
| 		} | ||||
| 	case <-time.After(time.Duration(time.Second)): | ||||
| 		if len(os.Getenv("IN_TRAVIS_CI")) == 0 { | ||||
| 			t.Logf("service.Run() survived a client request without an error") | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	ch := make(chan os.Signal, 1) | ||||
| 	signal.Notify(ch, syscall.SIGTERM) | ||||
| 	p, _ := os.FindProcess(os.Getpid()) | ||||
| 	p.Signal(syscall.SIGTERM) | ||||
|  | ||||
| 	<-ch | ||||
|  | ||||
| 	select { | ||||
| 	case err := <-errCh: | ||||
| 		if err != nil { | ||||
| 			t.Fatalf("service.Run():%v", err) | ||||
| 		} else { | ||||
| 			if len(os.Getenv("IN_TRAVIS_CI")) == 0 { | ||||
| 				t.Log("service.Run() nil return on syscall.SIGTERM") | ||||
| 			} | ||||
| 		} | ||||
| 	case <-time.After(time.Duration(time.Second)): | ||||
| 		if len(os.Getenv("IN_TRAVIS_CI")) == 0 { | ||||
| 			t.Logf("service.Run() survived a client request without an error") | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	eventually(func() bool { | ||||
| 		_, err := reg.GetService("go.micro.web.test") | ||||
| 		return err == registry.ErrNotFound | ||||
| 	}, t.Error) | ||||
|  | ||||
| 	callbackTests = []struct { | ||||
| 		subject string | ||||
| 		have    interface{} | ||||
| 	}{ | ||||
| 		{"beforeStopCalled", beforeStopCalled}, | ||||
| 		{"afterStopCalled", afterStopCalled}, | ||||
| 	} | ||||
|  | ||||
| 	for _, tt := range callbackTests { | ||||
| 		if tt.have != true { | ||||
| 			t.Errorf("unexpected %s: want true, have false", tt.subject) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| func TestOptions(t *testing.T) { | ||||
| 	var ( | ||||
| 		name             = "service-name" | ||||
| 		id               = "service-id" | ||||
| 		version          = "service-version" | ||||
| 		address          = "service-addr:8080" | ||||
| 		advertise        = "service-adv:8080" | ||||
| 		reg              = memory.NewRegistry() | ||||
| 		registerTTL      = 123 * time.Second | ||||
| 		registerInterval = 456 * time.Second | ||||
| 		handler          = http.NewServeMux() | ||||
| 		metadata         = map[string]string{"key": "val"} | ||||
| 		secure           = true | ||||
| 	) | ||||
|  | ||||
| 	service := NewService( | ||||
| 		Name(name), | ||||
| 		Id(id), | ||||
| 		Version(version), | ||||
| 		Address(address), | ||||
| 		Advertise(advertise), | ||||
| 		Registry(reg), | ||||
| 		RegisterTTL(registerTTL), | ||||
| 		RegisterInterval(registerInterval), | ||||
| 		Handler(handler), | ||||
| 		Metadata(metadata), | ||||
| 		Secure(secure), | ||||
| 	) | ||||
|  | ||||
| 	opts := service.Options() | ||||
|  | ||||
| 	tests := []struct { | ||||
| 		subject string | ||||
| 		want    interface{} | ||||
| 		have    interface{} | ||||
| 	}{ | ||||
| 		{"name", name, opts.Name}, | ||||
| 		{"version", version, opts.Version}, | ||||
| 		{"id", id, opts.Id}, | ||||
| 		{"address", address, opts.Address}, | ||||
| 		{"advertise", advertise, opts.Advertise}, | ||||
| 		{"registry", reg, opts.Registry}, | ||||
| 		{"registerTTL", registerTTL, opts.RegisterTTL}, | ||||
| 		{"registerInterval", registerInterval, opts.RegisterInterval}, | ||||
| 		{"handler", handler, opts.Handler}, | ||||
| 		{"metadata", metadata["key"], opts.Metadata["key"]}, | ||||
| 		{"secure", secure, opts.Secure}, | ||||
| 	} | ||||
|  | ||||
| 	for _, tc := range tests { | ||||
| 		if tc.want != tc.have { | ||||
| 			t.Errorf("unexpected %s: want %v, have %v", tc.subject, tc.want, tc.have) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func eventually(pass func() bool, fail func(...interface{})) { | ||||
| 	tick := time.NewTicker(10 * time.Millisecond) | ||||
| 	defer tick.Stop() | ||||
|  | ||||
| 	timeout := time.After(time.Second) | ||||
|  | ||||
| 	for { | ||||
| 		select { | ||||
| 		case <-timeout: | ||||
| 			fail("timed out") | ||||
| 			return | ||||
| 		case <-tick.C: | ||||
| 			if pass() { | ||||
| 				return | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestTLS(t *testing.T) { | ||||
| 	var ( | ||||
| 		str    = `<html><body><h1>Hello World</h1></body></html>` | ||||
| 		fn     = func(w http.ResponseWriter, r *http.Request) { fmt.Fprint(w, str) } | ||||
| 		secure = true | ||||
| 		reg    = memory.NewRegistry() | ||||
| 	) | ||||
|  | ||||
| 	service := NewService( | ||||
| 		Name("go.micro.web.test"), | ||||
| 		Secure(secure), | ||||
| 		Registry(reg), | ||||
| 	) | ||||
|  | ||||
| 	service.HandleFunc("/", fn) | ||||
|  | ||||
| 	errCh := make(chan error, 1) | ||||
| 	go func() { | ||||
| 		errCh <- service.Run() | ||||
| 		close(errCh) | ||||
| 	}() | ||||
|  | ||||
| 	var s []*registry.Service | ||||
|  | ||||
| 	eventually(func() bool { | ||||
| 		var err error | ||||
| 		s, err = reg.GetService("go.micro.web.test") | ||||
| 		return err == nil | ||||
| 	}, t.Fatal) | ||||
|  | ||||
| 	if have, want := len(s), 1; have != want { | ||||
| 		t.Fatalf("Expected %d but got %d services", want, have) | ||||
| 	} | ||||
|  | ||||
| 	tr := &http.Transport{ | ||||
| 		TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, | ||||
| 	} | ||||
| 	client := &http.Client{Transport: tr} | ||||
| 	rsp, err := client.Get(fmt.Sprintf("https://%s", s[0].Nodes[0].Address)) | ||||
| 	if err != nil { | ||||
| 		t.Fatal(err) | ||||
| 	} | ||||
| 	defer rsp.Body.Close() | ||||
|  | ||||
| 	b, err := ioutil.ReadAll(rsp.Body) | ||||
| 	if err != nil { | ||||
| 		t.Fatal(err) | ||||
| 	} | ||||
|  | ||||
| 	if string(b) != str { | ||||
| 		t.Errorf("Expected %s got %s", str, string(b)) | ||||
| 	} | ||||
|  | ||||
| 	select { | ||||
| 	case err := <-errCh: | ||||
| 		if err != nil { | ||||
| 			t.Fatalf("service.Run():%v", err) | ||||
| 		} | ||||
| 	case <-time.After(time.Duration(time.Second)): | ||||
| 		if len(os.Getenv("IN_TRAVIS_CI")) == 0 { | ||||
| 			t.Logf("service.Run() survived a client request without an error") | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| } | ||||
							
								
								
									
										45
									
								
								web/web.go
									
									
									
									
									
								
							
							
						
						
									
										45
									
								
								web/web.go
									
									
									
									
									
								
							| @@ -1,45 +0,0 @@ | ||||
| // Package web provides web based micro services | ||||
| package web | ||||
|  | ||||
| import ( | ||||
| 	"context" | ||||
| 	"net/http" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/google/uuid" | ||||
| ) | ||||
|  | ||||
| // Service is a web service with service discovery built in | ||||
| type Service interface { | ||||
| 	Client() *http.Client | ||||
| 	Init(opts ...Option) error | ||||
| 	Options() Options | ||||
| 	Handle(pattern string, handler http.Handler) | ||||
| 	HandleFunc(pattern string, handler func(http.ResponseWriter, *http.Request)) | ||||
| 	Run() error | ||||
| } | ||||
|  | ||||
| //Option for web | ||||
| type Option func(o *Options) | ||||
|  | ||||
| //Web basic Defaults | ||||
| var ( | ||||
| 	// For serving | ||||
| 	DefaultName    = "go-web" | ||||
| 	DefaultVersion = "latest" | ||||
| 	DefaultId      = uuid.New().String() | ||||
| 	DefaultAddress = ":0" | ||||
|  | ||||
| 	// for registration | ||||
| 	DefaultRegisterTTL      = time.Second * 90 | ||||
| 	DefaultRegisterInterval = time.Second * 30 | ||||
|  | ||||
| 	// static directory | ||||
| 	DefaultStaticDir     = "html" | ||||
| 	DefaultRegisterCheck = func(context.Context) error { return nil } | ||||
| ) | ||||
|  | ||||
| // NewService returns a new web.Service | ||||
| func NewService(opts ...Option) Service { | ||||
| 	return newService(opts...) | ||||
| } | ||||
| @@ -1,72 +0,0 @@ | ||||
| package web_test | ||||
|  | ||||
| import ( | ||||
| 	"context" | ||||
| 	"fmt" | ||||
| 	"sync" | ||||
| 	"testing" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/micro/cli/v2" | ||||
| 	"github.com/micro/go-micro/v2" | ||||
| 	"github.com/micro/go-micro/v2/logger" | ||||
| 	"github.com/micro/go-micro/v2/web" | ||||
| ) | ||||
|  | ||||
| func TestWeb(t *testing.T) { | ||||
| 	for i := 0; i < 10; i++ { | ||||
| 		fmt.Println("Test nr", i) | ||||
| 		testFunc() | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func testFunc() { | ||||
| 	ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond*250) | ||||
| 	defer cancel() | ||||
|  | ||||
| 	s := micro.NewService( | ||||
| 		micro.Name("test"), | ||||
| 		micro.Context(ctx), | ||||
| 		micro.HandleSignal(false), | ||||
| 		micro.Flags( | ||||
| 			&cli.StringFlag{ | ||||
| 				Name: "test.timeout", | ||||
| 			}, | ||||
| 			&cli.BoolFlag{ | ||||
| 				Name: "test.v", | ||||
| 			}, | ||||
| 			&cli.StringFlag{ | ||||
| 				Name: "test.run", | ||||
| 			}, | ||||
| 			&cli.StringFlag{ | ||||
| 				Name: "test.testlogfile", | ||||
| 			}, | ||||
| 		), | ||||
| 	) | ||||
| 	w := web.NewService( | ||||
| 		web.MicroService(s), | ||||
| 		web.Context(ctx), | ||||
| 		web.HandleSignal(false), | ||||
| 	) | ||||
| 	//s.Init() | ||||
| 	//w.Init() | ||||
|  | ||||
| 	var wg sync.WaitGroup | ||||
| 	wg.Add(2) | ||||
| 	go func() { | ||||
| 		defer wg.Done() | ||||
| 		err := s.Run() | ||||
| 		if err != nil { | ||||
| 			logger.Errorf("micro run error: %v", err) | ||||
| 		} | ||||
| 	}() | ||||
| 	go func() { | ||||
| 		defer wg.Done() | ||||
| 		err := w.Run() | ||||
| 		if err != nil { | ||||
| 			logger.Errorf("web run error: %v", err) | ||||
| 		} | ||||
| 	}() | ||||
|  | ||||
| 	wg.Wait() | ||||
| } | ||||
		Reference in New Issue
	
	Block a user