gopkg.in/dedis/onet.v2@v2.0.0-20181115163211-c8f3724038a7/service.go (about) 1 package onet 2 3 import ( 4 "crypto/sha256" 5 "errors" 6 "fmt" 7 "net/http" 8 "os" 9 "path" 10 "strconv" 11 "sync" 12 13 bolt "github.com/coreos/bbolt" 14 "gopkg.in/dedis/onet.v2/log" 15 "gopkg.in/dedis/onet.v2/network" 16 "gopkg.in/satori/go.uuid.v1" 17 ) 18 19 func init() { 20 network.RegisterMessage(GenericConfig{}) 21 } 22 23 // Service is a generic interface to define any type of services. 24 // A Service has multiple roles: 25 // * Processing websocket client requests with ProcessClientRequests 26 // * Handling onet information to ProtocolInstances created with 27 // NewProtocol 28 // * Handling any kind of messages between Services between different hosts with 29 // the Processor interface 30 type Service interface { 31 // NewProtocol is called upon a ProtocolInstance's first message when Onet needs 32 // to instantiate the protocol. A Service is expected to manually create 33 // the ProtocolInstance it is using. If a Service returns (nil,nil), that 34 // means this Service lets Onet handle the protocol instance. 35 NewProtocol(*TreeNodeInstance, *GenericConfig) (ProtocolInstance, error) 36 // ProcessClientRequest is called when a message from an external 37 // client is received by the websocket for this service. The message is 38 // forwarded to the corresponding handler keyed by the path. If the 39 // handler is a normal one, i.e., a request-response handler, it 40 // returns a message in the first return value and the second 41 // (StreamingTunnel) will be set to nil. If the handler is a streaming 42 // handler, the first return value is set to nil but the second 43 // (StreamingTunnel) will exist. It should be used to stream messages 44 // to the client. See the StreamingTunnel documentation on how it 45 // should be used. The returned error will be formatted as a websocket 46 // error code 4000, using the string form of the error as the message. 47 ProcessClientRequest(req *http.Request, handler string, msg []byte) (reply []byte, tunnel *StreamingTunnel, err error) 48 // Processor makes a Service being able to handle any kind of packets 49 // directly from the network. It is used for inter service communications, 50 // which are mostly single packets with no or little interactions needed. If 51 // a complex logic is used for these messages, it's best to put that logic 52 // into a ProtocolInstance that the Service will launch, since there's nicer 53 // utilities for ProtocolInstance. 54 network.Processor 55 } 56 57 // NewServiceFunc is the type of a function that is used to instantiate a given Service 58 // A service is initialized with a Server (to send messages to someone). 59 type NewServiceFunc func(c *Context) (Service, error) 60 61 // ServiceID is a type to represent a uuid for a Service 62 type ServiceID uuid.UUID 63 64 // String returns the string representation of this ServiceID 65 func (s ServiceID) String() string { 66 return uuid.UUID(s).String() 67 } 68 69 // Equal returns true if and only if s2 equals this ServiceID. 70 func (s ServiceID) Equal(s2 ServiceID) bool { 71 return uuid.Equal(uuid.UUID(s), uuid.UUID(s2)) 72 } 73 74 // IsNil returns true iff the ServiceID is Nil 75 func (s ServiceID) IsNil() bool { 76 return s.Equal(ServiceID(uuid.Nil)) 77 } 78 79 // NilServiceID is the empty ServiceID 80 var NilServiceID = ServiceID(uuid.Nil) 81 82 // GenericConfig is a config that can hold any type of specific configs for 83 // protocols. It is passed down to the service NewProtocol function. 84 type GenericConfig struct { 85 Data []byte 86 } 87 88 // A serviceFactory is used to register a NewServiceFunc 89 type serviceFactory struct { 90 constructors []serviceEntry 91 mutex sync.RWMutex 92 } 93 94 // A serviceEntry holds all references to a service 95 type serviceEntry struct { 96 constructor NewServiceFunc 97 serviceID ServiceID 98 name string 99 } 100 101 // ServiceFactory is the global service factory to instantiate Services 102 var ServiceFactory = serviceFactory{ 103 constructors: []serviceEntry{}, 104 } 105 106 // Register takes a name and a function, then creates a ServiceID out of it and stores the 107 // mapping and the creation function. 108 func (s *serviceFactory) Register(name string, fn NewServiceFunc) (ServiceID, error) { 109 if !s.ServiceID(name).Equal(NilServiceID) { 110 return NilServiceID, fmt.Errorf("service %s already registered", name) 111 } 112 id := ServiceID(uuid.NewV5(uuid.NamespaceURL, name)) 113 s.mutex.Lock() 114 defer s.mutex.Unlock() 115 s.constructors = append(s.constructors, serviceEntry{ 116 constructor: fn, 117 serviceID: id, 118 name: name, 119 }) 120 return id, nil 121 } 122 123 // Unregister - mainly for tests 124 func (s *serviceFactory) Unregister(name string) error { 125 s.mutex.Lock() 126 defer s.mutex.Unlock() 127 index := -1 128 for i, c := range s.constructors { 129 if c.name == name { 130 index = i 131 break 132 } 133 } 134 if index < 0 { 135 return errors.New("Didn't find service " + name) 136 } 137 s.constructors = append(s.constructors[:index], s.constructors[index+1:]...) 138 return nil 139 } 140 141 // RegisterNewService is a wrapper around service factory 142 func RegisterNewService(name string, fn NewServiceFunc) (ServiceID, error) { 143 return ServiceFactory.Register(name, fn) 144 } 145 146 // UnregisterService removes a service from the global pool. 147 func UnregisterService(name string) error { 148 return ServiceFactory.Unregister(name) 149 } 150 151 // registeredServiceIDs returns all the services registered 152 func (s *serviceFactory) registeredServiceIDs() []ServiceID { 153 s.mutex.RLock() 154 defer s.mutex.RUnlock() 155 var ids = make([]ServiceID, 0, len(s.constructors)) 156 for _, c := range s.constructors { 157 ids = append(ids, c.serviceID) 158 } 159 return ids 160 } 161 162 // RegisteredServiceNames returns all the names of the services registered 163 func (s *serviceFactory) RegisteredServiceNames() []string { 164 s.mutex.RLock() 165 defer s.mutex.RUnlock() 166 var names = make([]string, 0, len(s.constructors)) 167 for _, n := range s.constructors { 168 names = append(names, n.name) 169 } 170 return names 171 } 172 173 // ServiceID returns the ServiceID out of the name of the service 174 func (s *serviceFactory) ServiceID(name string) ServiceID { 175 s.mutex.RLock() 176 defer s.mutex.RUnlock() 177 for _, c := range s.constructors { 178 if name == c.name { 179 return c.serviceID 180 } 181 } 182 return NilServiceID 183 } 184 185 // Name returns the Name out of the ID 186 func (s *serviceFactory) Name(id ServiceID) string { 187 s.mutex.RLock() 188 defer s.mutex.RUnlock() 189 for _, c := range s.constructors { 190 if id.Equal(c.serviceID) { 191 return c.name 192 } 193 } 194 return "" 195 } 196 197 // start launches a new service 198 func (s *serviceFactory) start(name string, con *Context) (Service, error) { 199 s.mutex.RLock() 200 defer s.mutex.RUnlock() 201 for _, c := range s.constructors { 202 if name == c.name { 203 return c.constructor(con) 204 } 205 } 206 return nil, errors.New("Didn't find service " + name) 207 } 208 209 // serviceManager is the place where all instantiated services are stored 210 // It gives access to: all the currently running services 211 type serviceManager struct { 212 // the actual services 213 services map[ServiceID]Service 214 // making sure we're not racing for services 215 servicesMutex sync.Mutex 216 // the onet host 217 server *Server 218 // a bbolt database for all services 219 db *bolt.DB 220 dbPath string 221 // should the db be deleted on close? 222 delDb bool 223 // the dispatcher can take registration of Processors 224 network.Dispatcher 225 } 226 227 // newServiceManager will create a serviceStore out of all the registered Service 228 func newServiceManager(srv *Server, o *Overlay, dbPath string, delDb bool) *serviceManager { 229 services := make(map[ServiceID]Service) 230 s := &serviceManager{ 231 services: services, 232 server: srv, 233 dbPath: dbPath, 234 delDb: delDb, 235 Dispatcher: network.NewRoutineDispatcher(), 236 } 237 238 s.updateDbFileName() 239 240 db, err := openDb(s.dbFileName()) 241 if err != nil { 242 log.Panic("Failed to create new database: " + err.Error()) 243 } 244 s.db = db 245 246 for name, inst := range protocols.instantiators { 247 log.Lvl4("Registering global protocol", name) 248 srv.ProtocolRegister(name, inst) 249 } 250 251 ids := ServiceFactory.registeredServiceIDs() 252 for _, id := range ids { 253 name := ServiceFactory.Name(id) 254 log.Lvl3("Starting service", name) 255 256 cont := newContext(srv, o, id, s) 257 258 srvc, err := ServiceFactory.start(name, cont) 259 if err != nil { 260 log.Fatalf("Trying to instantiate service %v: %v", name, err) 261 } 262 log.Lvl3("Started Service", name) 263 s.servicesMutex.Lock() 264 services[id] = srvc 265 s.servicesMutex.Unlock() 266 srv.WebSocket.registerService(name, srvc) 267 } 268 log.Lvl3(srv.Address(), "instantiated all services") 269 srv.statusReporterStruct.RegisterStatusReporter("Db", s) 270 return s 271 } 272 273 // openDb opens a database at `path`. It creates the database if it does not exist. 274 // The caller must ensure that all parent directories exist. 275 func openDb(path string) (*bolt.DB, error) { 276 db, err := bolt.Open(path, 0600, nil) 277 if err != nil { 278 return nil, err 279 } 280 return db, nil 281 } 282 283 func (s *serviceManager) dbFileNameOld() string { 284 pub, _ := s.server.ServerIdentity.Public.MarshalBinary() 285 return path.Join(s.dbPath, fmt.Sprintf("%x.db", pub)) 286 } 287 288 func (s *serviceManager) dbFileName() string { 289 pub, _ := s.server.ServerIdentity.Public.MarshalBinary() 290 h := sha256.New() 291 h.Write(pub) 292 return path.Join(s.dbPath, fmt.Sprintf("%x.db", h.Sum(nil))) 293 } 294 295 // updateDbFileName checks if the old database file name exists, if it does, it 296 // will rename it to the new file name. 297 func (s *serviceManager) updateDbFileName() { 298 if _, err := os.Stat(s.dbFileNameOld()); err == nil { 299 // we assume the new name does not exist 300 log.Lvl2("Renaming database from", s.dbFileNameOld(), "to", s.dbFileName()) 301 if err := os.Rename(s.dbFileNameOld(), s.dbFileName()); err != nil { 302 log.Error(err) 303 } 304 } 305 } 306 307 // Process implements the Processor interface: service manager will relay 308 // messages to the right Service. 309 func (s *serviceManager) Process(env *network.Envelope) { 310 // will launch a go routine for that message 311 s.Dispatch(env) 312 } 313 314 // closeDatabase closes the database. 315 // It also removes the database file if the path is not default (i.e. testing config) 316 func (s *serviceManager) closeDatabase() error { 317 if s.db != nil { 318 err := s.db.Close() 319 if err != nil { 320 log.Error("Close database failed with: " + err.Error()) 321 } 322 } 323 324 if s.delDb { 325 err := os.Remove(s.dbFileName()) 326 if err != nil { 327 return err 328 } 329 } 330 return nil 331 } 332 333 // GetStatus is a function that returns the status report of the server. 334 func (s *serviceManager) GetStatus() *Status { 335 if s.db == nil { 336 return &Status{Field: map[string]string{"Open": "false"}} 337 } 338 st := s.db.Stats() 339 return &Status{Field: map[string]string{ 340 "Open": "true", 341 "FreePageN": strconv.Itoa(st.FreePageN), 342 "PendingPageN": strconv.Itoa(st.PendingPageN), 343 "FreeAlloc": strconv.Itoa(st.FreeAlloc), 344 "FreelistInuse": strconv.Itoa(st.FreelistInuse), 345 "TxN": strconv.Itoa(st.TxN), 346 "OpenTxN": strconv.Itoa(st.OpenTxN), 347 "Tx.PageCount": strconv.Itoa(st.TxStats.PageCount), 348 "Tx.PageAlloc": strconv.Itoa(st.TxStats.PageAlloc), 349 "Tx.CursorCount": strconv.Itoa(st.TxStats.CursorCount), 350 "Tx.NodeCount": strconv.Itoa(st.TxStats.NodeCount), 351 "Tx.NodeDeref": strconv.Itoa(st.TxStats.NodeDeref), 352 "Tx.Rebalance": strconv.Itoa(st.TxStats.Rebalance), 353 "Tx.RebalanceTime": st.TxStats.RebalanceTime.String(), 354 "Tx.Split": strconv.Itoa(st.TxStats.Split), 355 "Tx.Spill": strconv.Itoa(st.TxStats.Spill), 356 "Tx.SpillTime": st.TxStats.SpillTime.String(), 357 "Tx.Write": strconv.Itoa(st.TxStats.Write), 358 "Tx.WriteTime": st.TxStats.WriteTime.String(), 359 }} 360 } 361 362 // registerProcessor the processor to the service manager and tells the host to dispatch 363 // this message to the service manager. The service manager will then dispatch 364 // the message in a go routine. XXX This is needed because we need to have 365 // messages for service dispatched in asynchronously regarding the protocols. 366 // This behavior with go routine is fine for the moment but for better 367 // performance / memory / resilience, it may be changed to a real queuing 368 // system later. 369 func (s *serviceManager) registerProcessor(p network.Processor, msgType network.MessageTypeID) { 370 // delegate message to host so the host will pass the message to ourself 371 s.server.RegisterProcessor(s, msgType) 372 // handle the message ourselves (will be launched in a go routine) 373 s.Dispatcher.RegisterProcessor(p, msgType) 374 } 375 376 func (s *serviceManager) registerProcessorFunc(msgType network.MessageTypeID, fn func(*network.Envelope)) { 377 // delegate message to host so the host will pass the message to ourself 378 s.server.RegisterProcessor(s, msgType) 379 // handle the message ourselves (will be launched in a go routine) 380 s.Dispatcher.RegisterProcessorFunc(msgType, fn) 381 382 } 383 384 // availableServices returns a list of all services available to the serviceManager. 385 // If no services are instantiated, it returns an empty list. 386 func (s *serviceManager) availableServices() (ret []string) { 387 s.servicesMutex.Lock() 388 defer s.servicesMutex.Unlock() 389 for id := range s.services { 390 ret = append(ret, ServiceFactory.Name(id)) 391 } 392 return 393 } 394 395 // service returns the service implementation being registered to this name or 396 // nil if no service by this name is available. 397 func (s *serviceManager) service(name string) Service { 398 id := ServiceFactory.ServiceID(name) 399 if id.Equal(NilServiceID) { 400 return nil 401 } 402 s.servicesMutex.Lock() 403 defer s.servicesMutex.Unlock() 404 ser, ok := s.services[id] 405 if !ok { 406 log.Error("this service is not instantiated") 407 return nil 408 } 409 return ser 410 } 411 412 func (s *serviceManager) serviceByID(id ServiceID) (Service, bool) { 413 var serv Service 414 var ok bool 415 s.servicesMutex.Lock() 416 defer s.servicesMutex.Unlock() 417 if serv, ok = s.services[id]; !ok { 418 return nil, false 419 } 420 return serv, true 421 } 422 423 // newProtocol contains the logic of how and where a ProtocolInstance is 424 // created. If the token's ServiceID is nil, then onet handles the creation of 425 // the PI. If the corresponding service returns (nil,nil), then onet handles 426 // the creation of the PI. Otherwise the service is responsible for setting up 427 // the PI. 428 func (s *serviceManager) newProtocol(tni *TreeNodeInstance, config *GenericConfig) (pi ProtocolInstance, err error) { 429 si, ok := s.serviceByID(tni.Token().ServiceID) 430 defaultHandle := func() (ProtocolInstance, error) { return s.server.protocolInstantiate(tni.Token().ProtoID, tni) } 431 if !ok { 432 // let onet handle it 433 return defaultHandle() 434 } 435 436 defer func() { 437 if r := recover(); r != nil { 438 pi = nil 439 err = fmt.Errorf("could not create new protocol: %v", r) 440 return 441 } 442 }() 443 444 pi, err = si.NewProtocol(tni, config) 445 if pi == nil && err == nil { 446 return defaultHandle() 447 } 448 return 449 }