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  }