gopkg.in/dedis/onet.v2@v2.0.0-20181115163211-c8f3724038a7/processor.go (about) 1 package onet 2 3 import ( 4 "errors" 5 "net/http" 6 "reflect" 7 8 "strings" 9 10 "github.com/dedis/protobuf" 11 "gopkg.in/dedis/onet.v2/log" 12 "gopkg.in/dedis/onet.v2/network" 13 ) 14 15 // ServiceProcessor allows for an easy integration of external messages 16 // into the Services. You have to embed it into your Service-struct as 17 // a pointer. It will process client requests that have been registered 18 // with RegisterMessage. 19 type ServiceProcessor struct { 20 handlers map[string]serviceHandler 21 *Context 22 } 23 24 // serviceHandler stores the handler and the message-type. 25 type serviceHandler struct { 26 handler interface{} 27 msgType reflect.Type 28 streaming bool 29 } 30 31 // NewServiceProcessor initializes your ServiceProcessor. 32 func NewServiceProcessor(c *Context) *ServiceProcessor { 33 return &ServiceProcessor{ 34 handlers: make(map[string]serviceHandler), 35 Context: c, 36 } 37 } 38 39 var errType = reflect.TypeOf((*error)(nil)).Elem() 40 41 // RegisterHandler will store the given handler that will be used by the service. 42 // WebSocket will then forward requests to "ws://service_name/struct_name" 43 // to the given function f, which must be in the following form: 44 // func(msg interface{})(ret interface{}, err error) 45 // 46 // * msg is a pointer to a structure to the message sent. 47 // * ret is a pointer to a struct of the return-message. 48 // * err is an error, it can be nil, or any type that implements error. 49 // 50 // struct_name is stripped of its package-name, so a structure like 51 // network.Body will be converted to Body. 52 func (p *ServiceProcessor) RegisterHandler(f interface{}) error { 53 if err := p.handlerInputCheck(f); err != nil { 54 return err 55 } 56 57 // check output 58 ft := reflect.TypeOf(f) 59 if ft.NumOut() != 2 { 60 return errors.New("Need 2 return values: network.Body and error") 61 } 62 // first output 63 ret := ft.Out(0) 64 if ret.Kind() != reflect.Interface { 65 if ret.Kind() != reflect.Ptr { 66 return errors.New("1st return value must be a *pointer* to a struct or an interface") 67 } 68 if ret.Elem().Kind() != reflect.Struct { 69 return errors.New("1st return value must be a pointer to a *struct* or an interface") 70 } 71 } 72 // second output 73 if !ft.Out(1).Implements(errType) { 74 return errors.New("2nd return value has to implement error, but is: " + 75 ft.Out(1).String()) 76 } 77 78 cr := ft.In(0) 79 log.Lvl4("Registering handler", cr.String()) 80 pm := strings.Split(cr.Elem().String(), ".")[1] 81 p.handlers[pm] = serviceHandler{f, cr.Elem(), false} 82 83 return nil 84 } 85 86 // RegisterStreamingHandler stores a handler that is responsible for streaming 87 // messages to the client via a channel. Websocket will accept requests for 88 // this handler at "ws://service_name/struct_name", where struct_name is 89 // argument of f, which must be in the form: 90 // func(msg interface{})(retChan chan interface{}, closeChan chan bool, err error) 91 // 92 // * msg is a pointer to a structure to the message sent. 93 // * retChan is a channel of a pointer to a struct, everything sent into this 94 // channel will be forwarded to the client, if there are no more messages, 95 // the service should close retChan. 96 // * closeChan is a boolean channel, upon receiving a message on this channel, 97 // the handler must stop sending messages and close retChan. 98 // * err is an error, it can be nil, or any type that implements error. 99 // 100 // struct_name is stripped of its package-name, so a structure like 101 // network.Body will be converted to Body. 102 func (p *ServiceProcessor) RegisterStreamingHandler(f interface{}) error { 103 if err := p.handlerInputCheck(f); err != nil { 104 return err 105 } 106 107 // check output 108 ft := reflect.TypeOf(f) 109 if ft.NumOut() != 3 { 110 return errors.New("Need 3 return values: chan interface{}, chan bool and error") 111 } 112 // first output 113 ret0 := ft.Out(0) 114 if ret0.Kind() != reflect.Chan { 115 return errors.New("1st return value must be a channel") 116 } 117 if ret0.Elem().Kind() != reflect.Interface { 118 if ret0.Elem().Kind() != reflect.Ptr { 119 return errors.New("1st return value must be a channel of a *pointer* to a struct") 120 } 121 if ret0.Elem().Elem().Kind() != reflect.Struct { 122 return errors.New("1st return value must be a channel of a pointer to a *struct*") 123 } 124 } 125 // second output 126 ret1 := ft.Out(1) 127 if ret1.Kind() != reflect.Chan { 128 return errors.New("2nd return value must be a channel") 129 } 130 if ret1.Elem().Kind() != reflect.Bool { 131 return errors.New("2nd return value must be a boolean channel") 132 } 133 // third output 134 if !ft.Out(2).Implements(errType) { 135 return errors.New("3rd return value has to implement error, but is: " + 136 ft.Out(2).String()) 137 } 138 139 cr := ft.In(0) 140 log.Lvl4("Registering streaming handler", cr.String()) 141 pm := strings.Split(cr.Elem().String(), ".")[1] 142 p.handlers[pm] = serviceHandler{f, cr.Elem(), true} 143 144 return nil 145 } 146 147 func (p *ServiceProcessor) handlerInputCheck(f interface{}) error { 148 ft := reflect.TypeOf(f) 149 if ft.Kind() != reflect.Func { 150 return errors.New("Input is not a function") 151 } 152 if ft.NumIn() != 1 { 153 return errors.New("Need one argument: *struct") 154 } 155 cr := ft.In(0) 156 if cr.Kind() != reflect.Ptr { 157 return errors.New("Argument must be a *pointer* to a struct") 158 } 159 if cr.Elem().Kind() != reflect.Struct { 160 return errors.New("Argument must be a pointer to *struct*") 161 } 162 return nil 163 } 164 165 // RegisterHandlers takes a vararg of messages to register and returns 166 // the first error encountered or nil if everything was OK. 167 func (p *ServiceProcessor) RegisterHandlers(procs ...interface{}) error { 168 for _, pr := range procs { 169 if err := p.RegisterHandler(pr); err != nil { 170 return err 171 } 172 } 173 return nil 174 } 175 176 // RegisterStreamingHandlers takes a vararg of messages to register and returns 177 // the first error encountered or nil if everything was OK. 178 func (p *ServiceProcessor) RegisterStreamingHandlers(procs ...interface{}) error { 179 for _, pr := range procs { 180 if err := p.RegisterStreamingHandler(pr); err != nil { 181 return err 182 } 183 } 184 return nil 185 } 186 187 // Process implements the Processor interface and dispatches ClientRequest 188 // messages. 189 func (p *ServiceProcessor) Process(env *network.Envelope) { 190 log.Panic("Cannot handle message.") 191 } 192 193 // NewProtocol is a stub for services that don't want to intervene in the 194 // protocol-handling. 195 func (p *ServiceProcessor) NewProtocol(tn *TreeNodeInstance, conf *GenericConfig) (ProtocolInstance, error) { 196 return nil, nil 197 } 198 199 // StreamingTunnel is used as a tunnel between service processor and its 200 // caller, usually the websocket read-loop. When the tunnel is returned to the 201 // websocket loop, it should read from the out channel and forward the content 202 // to the client. If the client is disconnected, then the close channel should 203 // be closed. The signal exists to notify the service to stop streaming. 204 type StreamingTunnel struct { 205 out chan []byte 206 close chan bool 207 } 208 209 // ProcessClientRequest implementes the Service interface, see the interface 210 // documentation. 211 func (p *ServiceProcessor) ProcessClientRequest(req *http.Request, path string, buf []byte) ([]byte, *StreamingTunnel, error) { 212 mh, ok := p.handlers[path] 213 reply, stopServiceChan, err := func() (interface{}, chan bool, error) { 214 if !ok { 215 err := errors.New("The requested message hasn't been registered: " + path) 216 log.Error(err) 217 return nil, nil, err 218 } 219 msg := reflect.New(mh.msgType).Interface() 220 err := protobuf.DecodeWithConstructors(buf, msg, 221 network.DefaultConstructors(p.Context.server.Suite())) 222 if err != nil { 223 return nil, nil, err 224 } 225 226 to := reflect.TypeOf(mh.handler).In(0) 227 f := reflect.ValueOf(mh.handler) 228 229 arg := reflect.New(to.Elem()) 230 arg.Elem().Set(reflect.ValueOf(msg).Elem()) 231 ret := f.Call([]reflect.Value{arg}) 232 233 if mh.streaming { 234 ierr := ret[2].Interface() 235 if ierr != nil { 236 return nil, nil, ierr.(error) 237 } 238 return ret[0].Interface(), ret[1].Interface().(chan bool), nil 239 } 240 ierr := ret[1].Interface() 241 if ierr != nil { 242 return nil, nil, ierr.(error) 243 } 244 return ret[0].Interface(), nil, nil 245 }() 246 if err != nil { 247 return nil, nil, err 248 } 249 250 if mh.streaming { 251 // We need some buffer space for the intermediate channel that 252 // is responsible for forwarding messages from the service to 253 // the client because we need to keep the select-loop running 254 // to handle channel closures. 255 outChan := make(chan []byte, 100) 256 go func() { 257 inChan := reflect.ValueOf(reply) 258 cases := []reflect.SelectCase{ 259 reflect.SelectCase{Dir: reflect.SelectRecv, Chan: inChan}, 260 } 261 for { 262 chosen, v, ok := reflect.Select(cases) 263 if !ok { 264 log.Lvlf4("publisher is closed for %s, closing outgoing channel", path) 265 close(outChan) 266 return 267 } 268 if chosen == 0 { 269 // Send information down to the client. 270 buf, err = protobuf.Encode(v.Interface()) 271 if err != nil { 272 log.Error(err) 273 close(outChan) 274 return 275 } 276 outChan <- buf 277 } else { 278 panic("no such channel index") 279 } 280 // We don't add a way to explicitly stop the 281 // go-routine, otherwise the service will 282 // block. The service should close the channel 283 // when it has nothing else to say because it 284 // is the producer. Then this go-routine will 285 // be stopped as well. 286 } 287 }() 288 return nil, &StreamingTunnel{outChan, stopServiceChan}, nil 289 } 290 291 buf, err = protobuf.Encode(reply) 292 if err != nil { 293 log.Error(err) 294 return nil, nil, errors.New("") 295 } 296 return buf, nil, nil 297 }