Remove go-micro/web
This commit is contained in:
parent
1439b101ec
commit
8e126e4fc1
236
web/options.go
236
web/options.go
@ -1,236 +0,0 @@
|
|||||||
package web
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"crypto/tls"
|
|
||||||
"net/http"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/micro/go-micro/v3/registry"
|
|
||||||
"github.com/micro/go-micro/v3/registry/memory"
|
|
||||||
)
|
|
||||||
|
|
||||||
//Options for web
|
|
||||||
type Options struct {
|
|
||||||
Name string
|
|
||||||
Version string
|
|
||||||
Id string
|
|
||||||
Metadata map[string]string
|
|
||||||
Address string
|
|
||||||
Advertise string
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
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,
|
|
||||||
Context: context.TODO(),
|
|
||||||
Signal: true,
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, o := range opts {
|
|
||||||
o(&opt)
|
|
||||||
}
|
|
||||||
|
|
||||||
if opt.Registry == nil {
|
|
||||||
opt.Registry = memory.NewRegistry()
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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
|
|
||||||
}
|
|
||||||
}
|
|
458
web/service.go
458
web/service.go
@ -1,458 +0,0 @@
|
|||||||
package web
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/tls"
|
|
||||||
"fmt"
|
|
||||||
"net"
|
|
||||||
"net/http"
|
|
||||||
"os"
|
|
||||||
"os/signal"
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/micro/go-micro/v3/logger"
|
|
||||||
"github.com/micro/go-micro/v3/registry"
|
|
||||||
maddr "github.com/micro/go-micro/v3/util/addr"
|
|
||||||
"github.com/micro/go-micro/v3/util/backoff"
|
|
||||||
mnet "github.com/micro/go-micro/v3/util/net"
|
|
||||||
signalutil "github.com/micro/go-micro/v3/util/signal"
|
|
||||||
mls "github.com/micro/go-micro/v3/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.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
|
|
||||||
|
|
||||||
// register options
|
|
||||||
rOpts := []registry.RegisterOption{
|
|
||||||
registry.RegisterTTL(s.opts.RegisterTTL),
|
|
||||||
}
|
|
||||||
|
|
||||||
// try three times if necessary
|
|
||||||
for i := 0; i < 3; i++ {
|
|
||||||
// attempt to register
|
|
||||||
if err := r.Register(s.srv, rOpts...); 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.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) 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)
|
|
||||||
}
|
|
||||||
|
|
||||||
srv := s.genSrv()
|
|
||||||
srv.Endpoints = s.srv.Endpoints
|
|
||||||
s.srv = srv
|
|
||||||
s.Unlock()
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *service) Run() error {
|
|
||||||
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/v3/registry"
|
|
||||||
"github.com/micro/go-micro/v3/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")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
44
web/web.go
44
web/web.go
@ -1,44 +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 {
|
|
||||||
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,43 +0,0 @@
|
|||||||
package web_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"sync"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/micro/go-micro/v3/logger"
|
|
||||||
"github.com/micro/go-micro/v3/web"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestWeb(t *testing.T) {
|
|
||||||
for i := 0; i < 3; i++ {
|
|
||||||
fmt.Println("Test nr", i)
|
|
||||||
testFunc()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func testFunc() {
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond*250)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
w := web.NewService(
|
|
||||||
web.Context(ctx),
|
|
||||||
web.HandleSignal(false),
|
|
||||||
)
|
|
||||||
//s.Init()
|
|
||||||
//w.Init()
|
|
||||||
|
|
||||||
var wg sync.WaitGroup
|
|
||||||
wg.Add(1)
|
|
||||||
go func() {
|
|
||||||
defer wg.Done()
|
|
||||||
err := w.Run()
|
|
||||||
if err != nil {
|
|
||||||
logger.Errorf("web run error: %v", err)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
wg.Wait()
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user