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 }