github.com/btccom/go-micro/v2@v2.9.3/broker/http.go (about)

     1  // Package http provides a http based message broker
     2  package broker
     3  
     4  import (
     5  	"bytes"
     6  	"context"
     7  	"crypto/tls"
     8  	"errors"
     9  	"fmt"
    10  	"io"
    11  	"io/ioutil"
    12  	"math/rand"
    13  	"net"
    14  	"net/http"
    15  	"net/url"
    16  	"runtime"
    17  	"sync"
    18  	"time"
    19  
    20  	"github.com/google/uuid"
    21  	"github.com/btccom/go-micro/v2/codec/json"
    22  	merr "github.com/btccom/go-micro/v2/errors"
    23  	"github.com/btccom/go-micro/v2/registry"
    24  	"github.com/btccom/go-micro/v2/registry/cache"
    25  	maddr "github.com/btccom/go-micro/v2/util/addr"
    26  	mnet "github.com/btccom/go-micro/v2/util/net"
    27  	mls "github.com/btccom/go-micro/v2/util/tls"
    28  	"golang.org/x/net/http2"
    29  )
    30  
    31  // HTTP Broker is a point to point async broker
    32  type httpBroker struct {
    33  	id      string
    34  	address string
    35  	opts    Options
    36  
    37  	mux *http.ServeMux
    38  
    39  	c *http.Client
    40  	r registry.Registry
    41  
    42  	sync.RWMutex
    43  	subscribers map[string][]*httpSubscriber
    44  	running     bool
    45  	exit        chan chan error
    46  
    47  	// offline message inbox
    48  	mtx   sync.RWMutex
    49  	inbox map[string][][]byte
    50  }
    51  
    52  type httpSubscriber struct {
    53  	opts  SubscribeOptions
    54  	id    string
    55  	topic string
    56  	fn    Handler
    57  	svc   *registry.Service
    58  	hb    *httpBroker
    59  }
    60  
    61  type httpEvent struct {
    62  	m   *Message
    63  	t   string
    64  	err error
    65  }
    66  
    67  var (
    68  	DefaultPath      = "/"
    69  	DefaultAddress   = "127.0.0.1:0"
    70  	serviceName      = "micro.http.broker"
    71  	broadcastVersion = "ff.http.broadcast"
    72  	registerTTL      = time.Minute
    73  	registerInterval = time.Second * 30
    74  )
    75  
    76  func init() {
    77  	rand.Seed(time.Now().Unix())
    78  }
    79  
    80  func newTransport(config *tls.Config) *http.Transport {
    81  	if config == nil {
    82  		config = &tls.Config{
    83  			InsecureSkipVerify: true,
    84  		}
    85  	}
    86  
    87  	dialTLS := func(network string, addr string) (net.Conn, error) {
    88  		return tls.Dial(network, addr, config)
    89  	}
    90  
    91  	t := &http.Transport{
    92  		Proxy: http.ProxyFromEnvironment,
    93  		Dial: (&net.Dialer{
    94  			Timeout:   30 * time.Second,
    95  			KeepAlive: 30 * time.Second,
    96  		}).Dial,
    97  		TLSHandshakeTimeout: 10 * time.Second,
    98  		DialTLS:             dialTLS,
    99  	}
   100  	runtime.SetFinalizer(&t, func(tr **http.Transport) {
   101  		(*tr).CloseIdleConnections()
   102  	})
   103  
   104  	// setup http2
   105  	http2.ConfigureTransport(t)
   106  
   107  	return t
   108  }
   109  
   110  func newHttpBroker(opts ...Option) Broker {
   111  	options := Options{
   112  		Codec:    json.Marshaler{},
   113  		Context:  context.TODO(),
   114  		Registry: registry.DefaultRegistry,
   115  	}
   116  
   117  	for _, o := range opts {
   118  		o(&options)
   119  	}
   120  
   121  	// set address
   122  	addr := DefaultAddress
   123  
   124  	if len(options.Addrs) > 0 && len(options.Addrs[0]) > 0 {
   125  		addr = options.Addrs[0]
   126  	}
   127  
   128  	h := &httpBroker{
   129  		id:          uuid.New().String(),
   130  		address:     addr,
   131  		opts:        options,
   132  		r:           options.Registry,
   133  		c:           &http.Client{Transport: newTransport(options.TLSConfig)},
   134  		subscribers: make(map[string][]*httpSubscriber),
   135  		exit:        make(chan chan error),
   136  		mux:         http.NewServeMux(),
   137  		inbox:       make(map[string][][]byte),
   138  	}
   139  
   140  	// specify the message handler
   141  	h.mux.Handle(DefaultPath, h)
   142  
   143  	// get optional handlers
   144  	if h.opts.Context != nil {
   145  		handlers, ok := h.opts.Context.Value("http_handlers").(map[string]http.Handler)
   146  		if ok {
   147  			for pattern, handler := range handlers {
   148  				h.mux.Handle(pattern, handler)
   149  			}
   150  		}
   151  	}
   152  
   153  	return h
   154  }
   155  
   156  func (h *httpEvent) Ack() error {
   157  	return nil
   158  }
   159  
   160  func (h *httpEvent) Error() error {
   161  	return h.err
   162  }
   163  
   164  func (h *httpEvent) Message() *Message {
   165  	return h.m
   166  }
   167  
   168  func (h *httpEvent) Topic() string {
   169  	return h.t
   170  }
   171  
   172  func (h *httpSubscriber) Options() SubscribeOptions {
   173  	return h.opts
   174  }
   175  
   176  func (h *httpSubscriber) Topic() string {
   177  	return h.topic
   178  }
   179  
   180  func (h *httpSubscriber) Unsubscribe() error {
   181  	return h.hb.unsubscribe(h)
   182  }
   183  
   184  func (h *httpBroker) saveMessage(topic string, msg []byte) {
   185  	h.mtx.Lock()
   186  	defer h.mtx.Unlock()
   187  
   188  	// get messages
   189  	c := h.inbox[topic]
   190  
   191  	// save message
   192  	c = append(c, msg)
   193  
   194  	// max length 64
   195  	if len(c) > 64 {
   196  		c = c[:64]
   197  	}
   198  
   199  	// save inbox
   200  	h.inbox[topic] = c
   201  }
   202  
   203  func (h *httpBroker) getMessage(topic string, num int) [][]byte {
   204  	h.mtx.Lock()
   205  	defer h.mtx.Unlock()
   206  
   207  	// get messages
   208  	c, ok := h.inbox[topic]
   209  	if !ok {
   210  		return nil
   211  	}
   212  
   213  	// more message than requests
   214  	if len(c) >= num {
   215  		msg := c[:num]
   216  		h.inbox[topic] = c[num:]
   217  		return msg
   218  	}
   219  
   220  	// reset inbox
   221  	h.inbox[topic] = nil
   222  
   223  	// return all messages
   224  	return c
   225  }
   226  
   227  func (h *httpBroker) subscribe(s *httpSubscriber) error {
   228  	h.Lock()
   229  	defer h.Unlock()
   230  
   231  	if err := h.r.Register(s.svc, registry.RegisterTTL(registerTTL)); err != nil {
   232  		return err
   233  	}
   234  
   235  	h.subscribers[s.topic] = append(h.subscribers[s.topic], s)
   236  	return nil
   237  }
   238  
   239  func (h *httpBroker) unsubscribe(s *httpSubscriber) error {
   240  	h.Lock()
   241  	defer h.Unlock()
   242  
   243  	//nolint:prealloc
   244  	var subscribers []*httpSubscriber
   245  
   246  	// look for subscriber
   247  	for _, sub := range h.subscribers[s.topic] {
   248  		// deregister and skip forward
   249  		if sub == s {
   250  			_ = h.r.Deregister(sub.svc)
   251  			continue
   252  		}
   253  		// keep subscriber
   254  		subscribers = append(subscribers, sub)
   255  	}
   256  
   257  	// set subscribers
   258  	h.subscribers[s.topic] = subscribers
   259  
   260  	return nil
   261  }
   262  
   263  func (h *httpBroker) run(l net.Listener) {
   264  	t := time.NewTicker(registerInterval)
   265  	defer t.Stop()
   266  
   267  	for {
   268  		select {
   269  		// heartbeat for each subscriber
   270  		case <-t.C:
   271  			h.RLock()
   272  			for _, subs := range h.subscribers {
   273  				for _, sub := range subs {
   274  					_ = h.r.Register(sub.svc, registry.RegisterTTL(registerTTL))
   275  				}
   276  			}
   277  			h.RUnlock()
   278  		// received exit signal
   279  		case ch := <-h.exit:
   280  			ch <- l.Close()
   281  			h.RLock()
   282  			for _, subs := range h.subscribers {
   283  				for _, sub := range subs {
   284  					_ = h.r.Deregister(sub.svc)
   285  				}
   286  			}
   287  			h.RUnlock()
   288  			return
   289  		}
   290  	}
   291  }
   292  
   293  func (h *httpBroker) ServeHTTP(w http.ResponseWriter, req *http.Request) {
   294  	if req.Method != "POST" {
   295  		err := merr.BadRequest("go.micro.broker", "Method not allowed")
   296  		http.Error(w, err.Error(), http.StatusMethodNotAllowed)
   297  		return
   298  	}
   299  	defer req.Body.Close()
   300  
   301  	req.ParseForm()
   302  
   303  	b, err := ioutil.ReadAll(req.Body)
   304  	if err != nil {
   305  		errr := merr.InternalServerError("go.micro.broker", "Error reading request body: %v", err)
   306  		w.WriteHeader(500)
   307  		w.Write([]byte(errr.Error()))
   308  		return
   309  	}
   310  
   311  	var m *Message
   312  	if err = h.opts.Codec.Unmarshal(b, &m); err != nil {
   313  		errr := merr.InternalServerError("go.micro.broker", "Error parsing request body: %v", err)
   314  		w.WriteHeader(500)
   315  		w.Write([]byte(errr.Error()))
   316  		return
   317  	}
   318  
   319  	topic := m.Header["Micro-Topic"]
   320  	//delete(m.Header, ":topic")
   321  
   322  	if len(topic) == 0 {
   323  		errr := merr.InternalServerError("go.micro.broker", "Topic not found")
   324  		w.WriteHeader(500)
   325  		w.Write([]byte(errr.Error()))
   326  		return
   327  	}
   328  
   329  	p := &httpEvent{m: m, t: topic}
   330  	id := req.Form.Get("id")
   331  
   332  	//nolint:prealloc
   333  	var subs []Handler
   334  
   335  	h.RLock()
   336  	for _, subscriber := range h.subscribers[topic] {
   337  		if id != subscriber.id {
   338  			continue
   339  		}
   340  		subs = append(subs, subscriber.fn)
   341  	}
   342  	h.RUnlock()
   343  
   344  	// execute the handler
   345  	for _, fn := range subs {
   346  		p.err = fn(p)
   347  	}
   348  }
   349  
   350  func (h *httpBroker) Address() string {
   351  	h.RLock()
   352  	defer h.RUnlock()
   353  	return h.address
   354  }
   355  
   356  func (h *httpBroker) Connect() error {
   357  	h.RLock()
   358  	if h.running {
   359  		h.RUnlock()
   360  		return nil
   361  	}
   362  	h.RUnlock()
   363  
   364  	h.Lock()
   365  	defer h.Unlock()
   366  
   367  	var l net.Listener
   368  	var err error
   369  
   370  	if h.opts.Secure || h.opts.TLSConfig != nil {
   371  		config := h.opts.TLSConfig
   372  
   373  		fn := func(addr string) (net.Listener, error) {
   374  			if config == nil {
   375  				hosts := []string{addr}
   376  
   377  				// check if its a valid host:port
   378  				if host, _, err := net.SplitHostPort(addr); err == nil {
   379  					if len(host) == 0 {
   380  						hosts = maddr.IPs()
   381  					} else {
   382  						hosts = []string{host}
   383  					}
   384  				}
   385  
   386  				// generate a certificate
   387  				cert, err := mls.Certificate(hosts...)
   388  				if err != nil {
   389  					return nil, err
   390  				}
   391  				config = &tls.Config{Certificates: []tls.Certificate{cert}}
   392  			}
   393  			return tls.Listen("tcp", addr, config)
   394  		}
   395  
   396  		l, err = mnet.Listen(h.address, fn)
   397  	} else {
   398  		fn := func(addr string) (net.Listener, error) {
   399  			return net.Listen("tcp", addr)
   400  		}
   401  
   402  		l, err = mnet.Listen(h.address, fn)
   403  	}
   404  
   405  	if err != nil {
   406  		return err
   407  	}
   408  
   409  	addr := h.address
   410  	h.address = l.Addr().String()
   411  
   412  	go http.Serve(l, h.mux)
   413  	go func() {
   414  		h.run(l)
   415  		h.Lock()
   416  		h.opts.Addrs = []string{addr}
   417  		h.address = addr
   418  		h.Unlock()
   419  	}()
   420  
   421  	// get registry
   422  	reg := h.opts.Registry
   423  	if reg == nil {
   424  		reg = registry.DefaultRegistry
   425  	}
   426  	// set cache
   427  	h.r = cache.New(reg)
   428  
   429  	// set running
   430  	h.running = true
   431  	return nil
   432  }
   433  
   434  func (h *httpBroker) Disconnect() error {
   435  	h.RLock()
   436  	if !h.running {
   437  		h.RUnlock()
   438  		return nil
   439  	}
   440  	h.RUnlock()
   441  
   442  	h.Lock()
   443  	defer h.Unlock()
   444  
   445  	// stop cache
   446  	rc, ok := h.r.(cache.Cache)
   447  	if ok {
   448  		rc.Stop()
   449  	}
   450  
   451  	// exit and return err
   452  	ch := make(chan error)
   453  	h.exit <- ch
   454  	err := <-ch
   455  
   456  	// set not running
   457  	h.running = false
   458  	return err
   459  }
   460  
   461  func (h *httpBroker) Init(opts ...Option) error {
   462  	h.RLock()
   463  	if h.running {
   464  		h.RUnlock()
   465  		return errors.New("cannot init while connected")
   466  	}
   467  	h.RUnlock()
   468  
   469  	h.Lock()
   470  	defer h.Unlock()
   471  
   472  	for _, o := range opts {
   473  		o(&h.opts)
   474  	}
   475  
   476  	if len(h.opts.Addrs) > 0 && len(h.opts.Addrs[0]) > 0 {
   477  		h.address = h.opts.Addrs[0]
   478  	}
   479  
   480  	if len(h.id) == 0 {
   481  		h.id = "go.micro.http.broker-" + uuid.New().String()
   482  	}
   483  
   484  	// get registry
   485  	reg := h.opts.Registry
   486  	if reg == nil {
   487  		reg = registry.DefaultRegistry
   488  	}
   489  
   490  	// get cache
   491  	if rc, ok := h.r.(cache.Cache); ok {
   492  		rc.Stop()
   493  	}
   494  
   495  	// set registry
   496  	h.r = cache.New(reg)
   497  
   498  	// reconfigure tls config
   499  	if c := h.opts.TLSConfig; c != nil {
   500  		h.c = &http.Client{
   501  			Transport: newTransport(c),
   502  		}
   503  	}
   504  
   505  	return nil
   506  }
   507  
   508  func (h *httpBroker) Options() Options {
   509  	return h.opts
   510  }
   511  
   512  func (h *httpBroker) Publish(topic string, msg *Message, opts ...PublishOption) error {
   513  	// create the message first
   514  	m := &Message{
   515  		Header: make(map[string]string),
   516  		Body:   msg.Body,
   517  	}
   518  
   519  	for k, v := range msg.Header {
   520  		m.Header[k] = v
   521  	}
   522  
   523  	m.Header["Micro-Topic"] = topic
   524  
   525  	// encode the message
   526  	b, err := h.opts.Codec.Marshal(m)
   527  	if err != nil {
   528  		return err
   529  	}
   530  
   531  	// save the message
   532  	h.saveMessage(topic, b)
   533  
   534  	// now attempt to get the service
   535  	h.RLock()
   536  	s, err := h.r.GetService(serviceName)
   537  	if err != nil {
   538  		h.RUnlock()
   539  		return err
   540  	}
   541  	h.RUnlock()
   542  
   543  	pub := func(node *registry.Node, t string, b []byte) error {
   544  		scheme := "http"
   545  
   546  		// check if secure is added in metadata
   547  		if node.Metadata["secure"] == "true" {
   548  			scheme = "https"
   549  		}
   550  
   551  		vals := url.Values{}
   552  		vals.Add("id", node.Id)
   553  
   554  		uri := fmt.Sprintf("%s://%s%s?%s", scheme, node.Address, DefaultPath, vals.Encode())
   555  		r, err := h.c.Post(uri, "application/json", bytes.NewReader(b))
   556  		if err != nil {
   557  			return err
   558  		}
   559  
   560  		// discard response body
   561  		io.Copy(ioutil.Discard, r.Body)
   562  		r.Body.Close()
   563  		return nil
   564  	}
   565  
   566  	srv := func(s []*registry.Service, b []byte) {
   567  		for _, service := range s {
   568  			var nodes []*registry.Node
   569  
   570  			for _, node := range service.Nodes {
   571  				// only use nodes tagged with broker http
   572  				if node.Metadata["broker"] != "http" {
   573  					continue
   574  				}
   575  
   576  				// look for nodes for the topic
   577  				if node.Metadata["topic"] != topic {
   578  					continue
   579  				}
   580  
   581  				nodes = append(nodes, node)
   582  			}
   583  
   584  			// only process if we have nodes
   585  			if len(nodes) == 0 {
   586  				continue
   587  			}
   588  
   589  			switch service.Version {
   590  			// broadcast version means broadcast to all nodes
   591  			case broadcastVersion:
   592  				var success bool
   593  
   594  				// publish to all nodes
   595  				for _, node := range nodes {
   596  					// publish async
   597  					if err := pub(node, topic, b); err == nil {
   598  						success = true
   599  					}
   600  				}
   601  
   602  				// save if it failed to publish at least once
   603  				if !success {
   604  					h.saveMessage(topic, b)
   605  				}
   606  			default:
   607  				// select node to publish to
   608  				node := nodes[rand.Int()%len(nodes)]
   609  
   610  				// publish async to one node
   611  				if err := pub(node, topic, b); err != nil {
   612  					// if failed save it
   613  					h.saveMessage(topic, b)
   614  				}
   615  			}
   616  		}
   617  	}
   618  
   619  	// do the rest async
   620  	go func() {
   621  		// get a third of the backlog
   622  		messages := h.getMessage(topic, 8)
   623  		delay := (len(messages) > 1)
   624  
   625  		// publish all the messages
   626  		for _, msg := range messages {
   627  			// serialize here
   628  			srv(s, msg)
   629  
   630  			// sending a backlog of messages
   631  			if delay {
   632  				time.Sleep(time.Millisecond * 100)
   633  			}
   634  		}
   635  	}()
   636  
   637  	return nil
   638  }
   639  
   640  func (h *httpBroker) Subscribe(topic string, handler Handler, opts ...SubscribeOption) (Subscriber, error) {
   641  	var err error
   642  	var host, port string
   643  	options := NewSubscribeOptions(opts...)
   644  
   645  	// parse address for host, port
   646  	host, port, err = net.SplitHostPort(h.Address())
   647  	if err != nil {
   648  		return nil, err
   649  	}
   650  
   651  	addr, err := maddr.Extract(host)
   652  	if err != nil {
   653  		return nil, err
   654  	}
   655  
   656  	var secure bool
   657  
   658  	if h.opts.Secure || h.opts.TLSConfig != nil {
   659  		secure = true
   660  	}
   661  
   662  	// register service
   663  	node := &registry.Node{
   664  		Id:      topic + "-" + h.id,
   665  		Address: mnet.HostPort(addr, port),
   666  		Metadata: map[string]string{
   667  			"secure": fmt.Sprintf("%t", secure),
   668  			"broker": "http",
   669  			"topic":  topic,
   670  		},
   671  	}
   672  
   673  	// check for queue group or broadcast queue
   674  	version := options.Queue
   675  	if len(version) == 0 {
   676  		version = broadcastVersion
   677  	}
   678  
   679  	service := &registry.Service{
   680  		Name:    serviceName,
   681  		Version: version,
   682  		Nodes:   []*registry.Node{node},
   683  	}
   684  
   685  	// generate subscriber
   686  	subscriber := &httpSubscriber{
   687  		opts:  options,
   688  		hb:    h,
   689  		id:    node.Id,
   690  		topic: topic,
   691  		fn:    handler,
   692  		svc:   service,
   693  	}
   694  
   695  	// subscribe now
   696  	if err := h.subscribe(subscriber); err != nil {
   697  		return nil, err
   698  	}
   699  
   700  	// return the subscriber
   701  	return subscriber, nil
   702  }
   703  
   704  func (h *httpBroker) String() string {
   705  	return "http"
   706  }
   707  
   708  // NewBroker returns a new http broker
   709  func NewBroker(opts ...Option) Broker {
   710  	return newHttpBroker(opts...)
   711  }