github.com/annchain/OG@v0.0.9/wserver/server.go (about)

     1  // Copyright © 2019 Annchain Authors <EMAIL ADDRESS>
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  // Package wserver provides building simple websocket server with message push.
    15  package wserver
    16  
    17  import (
    18  	"context"
    19  	"encoding/json"
    20  	"fmt"
    21  	"github.com/annchain/OG/arefactor/common/goroutine"
    22  	types2 "github.com/annchain/OG/arefactor/og/types"
    23  	"github.com/annchain/OG/og/types"
    24  	"github.com/annchain/OG/status"
    25  	"github.com/gin-gonic/gin"
    26  	"github.com/gorilla/websocket"
    27  	"github.com/sirupsen/logrus"
    28  	"net/http"
    29  	"time"
    30  )
    31  
    32  const (
    33  	serverDefaultWSPath   = "/ws"
    34  	serverDefaultPushPath = "/push"
    35  
    36  	messageTypeNewUnit   = "new_unit"
    37  	messageTypeConfirmed = "confirmed"
    38  	messageTypeNewTx     = "new_tx"
    39  )
    40  
    41  var defaultUpgrader = &websocket.Upgrader{
    42  	ReadBufferSize:  1024,
    43  	WriteBufferSize: 1024,
    44  	CheckOrigin: func(*http.Request) bool {
    45  		return true
    46  	},
    47  }
    48  
    49  // Server defines parameters for running websocket server.
    50  type Server struct {
    51  	// Address for server to listen on
    52  	Addr string
    53  
    54  	// Path for websocket request, default "/ws".
    55  	WSPath string
    56  
    57  	// Path for push message, default "/push".
    58  	PushPath string
    59  
    60  	// Upgrader is for upgrade connection to websocket connection using
    61  	// "github.com/gorilla/websocket".
    62  	//
    63  	// If Upgrader is nil, default upgrader will be used. Default upgrader is
    64  	// set ReadBufferSize and WriteBufferSize to 1024, and CheckOrigin always
    65  	// returns true.
    66  	Upgrader *websocket.Upgrader
    67  
    68  	// Authorize push request. Message will be sent if it returns true,
    69  	// otherwise the request will be discarded. Default nil and push request
    70  	// will always be accepted.
    71  	PushAuth func(r *http.Request) bool
    72  
    73  	// To receive new tx events
    74  	NewTxReceivedChan chan types.Txi
    75  
    76  	// to receive confirmation events
    77  	BatchConfirmedChan chan map[types2.Hash]types.Txi
    78  
    79  	wh     *websocketHandler
    80  	ph     *pushHandler
    81  	engine *gin.Engine
    82  	server *http.Server
    83  	quit   chan bool
    84  }
    85  
    86  func (s *Server) GetBenchmarks() map[string]interface{} {
    87  	return map[string]interface{}{
    88  		"newtx":   len(s.NewTxReceivedChan),
    89  		"batchtx": len(s.BatchConfirmedChan),
    90  	}
    91  }
    92  
    93  // ListenAndServe listens on the TCP network address and handle websocket
    94  // request.
    95  func (s *Server) Serve() {
    96  	if err := s.server.ListenAndServe(); err != nil {
    97  		// cannot panic, because this probably is an intentional close
    98  		logrus.WithError(err).Info("websocket server")
    99  	}
   100  }
   101  
   102  // Push filters connections by userID and event, then write message
   103  func (s *Server) Push(event, message string) (int, error) {
   104  	return s.ph.push(event, message)
   105  }
   106  
   107  // NewServer creates a new Server.
   108  func NewServer(addr string) *Server {
   109  	s := &Server{
   110  		Addr:               addr,
   111  		WSPath:             serverDefaultWSPath,
   112  		PushPath:           serverDefaultPushPath,
   113  		NewTxReceivedChan:  make(chan types.Txi, 10000),
   114  		BatchConfirmedChan: make(chan map[types2.Hash]types.Txi, 1000),
   115  		quit:               make(chan bool),
   116  	}
   117  
   118  	e2c := NewEvent2Cons()
   119  
   120  	// websocket request handler
   121  	wh := websocketHandler{
   122  		upgrader:   defaultUpgrader,
   123  		event2Cons: e2c,
   124  	}
   125  	if s.Upgrader != nil {
   126  		wh.upgrader = s.Upgrader
   127  	}
   128  	s.wh = &wh
   129  
   130  	// push request handler
   131  	ph := pushHandler{
   132  		event2Cons: e2c,
   133  	}
   134  	if s.PushAuth != nil {
   135  		ph.authFunc = s.PushAuth
   136  	}
   137  	s.ph = &ph
   138  
   139  	engine := gin.Default()
   140  	engine.GET(s.WSPath, wh.Handle)
   141  	engine.GET(s.PushPath, ph.Handle)
   142  
   143  	s.server = &http.Server{
   144  		Addr:    s.Addr,
   145  		Handler: engine,
   146  	}
   147  	return s
   148  }
   149  
   150  func (s *Server) Start() {
   151  	goroutine.New(s.Serve)
   152  	goroutine.New(s.WatchNewTxs)
   153  }
   154  
   155  func (s *Server) Stop() {
   156  	close(s.quit)
   157  	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
   158  	defer cancel()
   159  	if err := s.server.Shutdown(ctx); err != nil {
   160  		logrus.WithError(err).Info("server Shutdown")
   161  	}
   162  	logrus.Info("server exiting")
   163  }
   164  
   165  func (s *Server) Name() string {
   166  	return fmt.Sprintf("websocket Server at %s", s.Addr)
   167  }
   168  func (s *Server) WatchNewTxs() {
   169  	ticker := time.NewTicker(time.Millisecond * 300)
   170  	defer ticker.Stop()
   171  	var uidata *UIData
   172  	var blockdbData *BlockDbUIData
   173  	for {
   174  		select {
   175  		case tx := <-s.NewTxReceivedChan:
   176  			if status.ArchiveMode {
   177  				if blockdbData == nil {
   178  					blockdbData = &BlockDbUIData{
   179  						//Type: messageTypeNewTx,
   180  					}
   181  				}
   182  
   183  				blockdbData.Nodes = append(blockdbData.Nodes, types.TxiSmallCaseMarshal{tx})
   184  			}
   185  
   186  			//if ac,ok := tx.(*tx_types.Archive);ok {
   187  			//	data := base64.StdEncoding.EncodeToString(ac.Data)
   188  			//	var a tx_types.Archive
   189  			//	a= *ac
   190  			//	a.Data = []byte(data)
   191  			//	blockData.Nodes = append(blockData.Nodes, types.TxiSmallCaseMarshal{&a})
   192  			//}else {
   193  			//blockData.Nodes = append(blockData.Nodes, types.TxiSmallCaseMarshal{tx})
   194  			//}
   195  
   196  			if uidata == nil {
   197  				uidata = &UIData{
   198  					Type: messageTypeNewUnit,
   199  					//Nodes: []Node{},
   200  					//Edges: []Edge{},
   201  				}
   202  			}
   203  			uidata.AddToBatch(tx, true)
   204  		case batch := <-s.BatchConfirmedChan:
   205  			// first publish all pending txs
   206  			if status.ArchiveMode {
   207  				s.publishNewTxs(blockdbData)
   208  				blockdbData = nil
   209  			}
   210  			s.publishTxs(uidata)
   211  			uidata = nil
   212  
   213  			// then publish batch
   214  			s.publishBatch(batch)
   215  		case <-ticker.C:
   216  			if status.ArchiveMode {
   217  				s.publishNewTxs(blockdbData)
   218  				blockdbData = nil
   219  			}
   220  			s.publishTxs(uidata)
   221  			uidata = nil
   222  
   223  		case <-s.quit:
   224  			break
   225  		}
   226  	}
   227  }
   228  
   229  func (s *Server) publishTxs(uidata *UIData) {
   230  	if uidata == nil {
   231  		return
   232  	}
   233  	bs, err := json.Marshal(uidata)
   234  	if err != nil {
   235  		logrus.WithError(err).Error("Failed to marshal ws message")
   236  		return
   237  	}
   238  	logrus.WithField("len ", len(bs)).WithField("nodeCount", len(uidata.Nodes)).Trace("push to ws")
   239  	s.Push(messageTypeNewUnit, string(bs))
   240  }
   241  func (s *Server) publishBatch(elders map[types2.Hash]types.Txi) {
   242  	logrus.WithFields(logrus.Fields{
   243  		"len": len(elders),
   244  	}).Trace("push confirmation to ws")
   245  
   246  	uiData := UIData{
   247  		Type:  messageTypeConfirmed,
   248  		Nodes: []Node{},
   249  	}
   250  
   251  	for _, tx := range elders {
   252  		uiData.AddToBatch(tx, false)
   253  	}
   254  
   255  	bs, err := json.Marshal(uiData)
   256  	if err != nil {
   257  		logrus.WithError(err).Error("Failed to marshal ws message")
   258  		return
   259  	}
   260  	s.Push(messageTypeConfirmed, string(bs))
   261  
   262  }
   263  
   264  func (s *Server) publishNewTxs(data *BlockDbUIData) {
   265  	if data == nil {
   266  		return
   267  	}
   268  	bs, err := json.Marshal(data)
   269  	if err != nil {
   270  		logrus.WithError(err).Error("Failed to marshal ws message")
   271  		return
   272  	}
   273  	logrus.WithField("data ", string(bs)).WithField("len ", len(bs)).WithField("nodeCount", len(data.Nodes)).Trace("push to ws")
   274  	s.Push(messageTypeNewTx, string(bs))
   275  }