go-micro.dev/v5@v5.12.0/server/rpc_server.go (about) 1 package server 2 3 import ( 4 "context" 5 "io" 6 "net" 7 "runtime/debug" 8 "sort" 9 "strconv" 10 "strings" 11 "sync" 12 "time" 13 14 "github.com/pkg/errors" 15 16 "go-micro.dev/v5/broker" 17 "go-micro.dev/v5/codec" 18 log "go-micro.dev/v5/logger" 19 "go-micro.dev/v5/metadata" 20 "go-micro.dev/v5/registry" 21 "go-micro.dev/v5/transport" 22 "go-micro.dev/v5/transport/headers" 23 "go-micro.dev/v5/util/addr" 24 "go-micro.dev/v5/util/backoff" 25 mnet "go-micro.dev/v5/util/net" 26 "go-micro.dev/v5/util/socket" 27 ) 28 29 type rpcServer struct { 30 opts Options 31 // Subscribe to service name 32 subscriber broker.Subscriber 33 // Goal: 34 // router Router 35 router *router 36 exit chan chan error 37 38 handlers map[string]Handler 39 subscribers map[Subscriber][]broker.Subscriber 40 // Graceful exit 41 wg *sync.WaitGroup 42 // Cached service 43 rsvc *registry.Service 44 45 sync.RWMutex 46 // Marks the serve as started 47 started bool 48 // Used for first registration 49 registered bool 50 } 51 52 // NewRPCServer will create a new default RPC server. 53 func NewRPCServer(opts ...Option) Server { 54 options := NewOptions(opts...) 55 router := newRpcRouter() 56 router.hdlrWrappers = options.HdlrWrappers 57 router.subWrappers = options.SubWrappers 58 59 return &rpcServer{ 60 opts: options, 61 router: router, 62 handlers: make(map[string]Handler), 63 subscribers: make(map[Subscriber][]broker.Subscriber), 64 exit: make(chan chan error), 65 wg: wait(options.Context), 66 } 67 } 68 69 func (s *rpcServer) Init(opts ...Option) error { 70 s.Lock() 71 defer s.Unlock() 72 73 for _, opt := range opts { 74 opt(&s.opts) 75 } 76 77 // update router if its the default 78 if s.opts.Router == nil { 79 r := newRpcRouter() 80 r.hdlrWrappers = s.opts.HdlrWrappers 81 r.serviceMap = s.router.serviceMap 82 r.subWrappers = s.opts.SubWrappers 83 s.router = r 84 } 85 86 s.rsvc = nil 87 88 return nil 89 } 90 91 // ServeConn serves a single connection. 92 func (s *rpcServer) ServeConn(sock transport.Socket) { 93 logger := s.opts.Logger 94 95 // Global error tracking 96 var gerr error 97 98 // Keep track of Connection: close header 99 var closeConn bool 100 101 // Streams are multiplexed on Micro-Stream or Micro-Id header 102 pool := socket.NewPool() 103 104 // Waitgroup to wait for processing to finish 105 // A double waitgroup is used to block the global waitgroup incase it is 106 // empty, but only wait for the local routines to finish with the local waitgroup. 107 wg := NewWaitGroup(s.getWg()) 108 109 defer func() { 110 // Only wait if there's no error 111 if gerr != nil { 112 select { 113 case <-s.exit: 114 default: 115 // EOF is expected if the client closes the connection 116 if !errors.Is(gerr, io.EOF) { 117 logger.Logf(log.ErrorLevel, "error while serving connection: %v", gerr) 118 } 119 } 120 } else { 121 wg.Wait() 122 } 123 124 // Close all the sockets for this connection 125 pool.Close() 126 127 // Close underlying socket 128 if err := sock.Close(); err != nil { 129 logger.Logf(log.ErrorLevel, "failed to close socket: %v", err) 130 } 131 132 // recover any panics 133 if r := recover(); r != nil { 134 logger.Log(log.ErrorLevel, "panic recovered: ", r) 135 logger.Log(log.ErrorLevel, string(debug.Stack())) 136 } 137 }() 138 139 for { 140 msg := transport.Message{ 141 Header: make(map[string]string), 142 } 143 144 // Close connection if Connection: close header was set 145 if closeConn { 146 return 147 } 148 149 // Process inbound messages one at a time 150 if err := sock.Recv(&msg); err != nil { 151 // Set a global error and return. 152 // We're saying we essentially can't 153 // use the socket anymore 154 gerr = errors.Wrapf(err, "%s-%s | %s", s.opts.Name, s.opts.Id, sock.Remote()) 155 156 return 157 } 158 159 // Keep track of when to close the connection 160 if c := msg.Header["Connection"]; c == "close" { 161 closeConn = true 162 } 163 164 // Check the message header for micro message header, if so handle 165 // as micro event 166 if t := msg.Header[headers.Message]; len(t) > 0 { 167 // Process the event 168 ev := newEvent(msg) 169 170 if err := s.HandleEvent(ev.Topic())(ev); err != nil { 171 msg.Header[headers.Error] = err.Error() 172 logger.Logf(log.ErrorLevel, "failed to handle event: %v", err) 173 } 174 // Write back some 200 175 if err := sock.Send(&transport.Message{Header: msg.Header}); err != nil { 176 gerr = err 177 break 178 } 179 180 continue 181 } 182 183 // business as usual 184 185 // use Micro-Stream as the stream identifier 186 // in the event its blank we'll always process 187 // on the same socket 188 var ( 189 stream bool 190 id string 191 ) 192 193 if s := getHeader(headers.Stream, msg.Header); len(s) > 0 { 194 id = s 195 stream = true 196 } else { 197 // If there's no stream id then its a standard request 198 // use the Micro-Id 199 id = msg.Header[headers.ID] 200 } 201 202 // Check if we have an existing socket 203 psock, ok := pool.Get(id) 204 205 // If we don't have a socket and its a stream 206 // Check if its a last stream EOS error 207 if !ok && stream && msg.Header[headers.Error] == errLastStreamResponse.Error() { 208 closeConn = true 209 pool.Release(psock) 210 211 continue 212 } 213 214 // Got an existing socket already 215 if ok { 216 // we're starting processing 217 wg.Add(1) 218 219 // Pass the message to that existing socket 220 if err := psock.Accept(&msg); err != nil { 221 // Release the socket if there's an error 222 pool.Release(psock) 223 } 224 225 wg.Done() 226 227 continue 228 } 229 230 // No socket was found so its new 231 // Set the local and remote values 232 psock.SetLocal(sock.Local()) 233 psock.SetRemote(sock.Remote()) 234 235 // Load the socket with the current message 236 if err := psock.Accept(&msg); err != nil { 237 logger.Logf(log.ErrorLevel, "Socket failed to accept message: %v", err) 238 } 239 240 // Now walk the usual path 241 242 // We use this Timeout header to set a server deadline 243 to := msg.Header["Timeout"] 244 // We use this Content-Type header to identify the codec needed 245 contentType := msg.Header["Content-Type"] 246 247 // Copy the message headers 248 header := make(map[string]string, len(msg.Header)) 249 for k, v := range msg.Header { 250 header[k] = v 251 } 252 253 // Set local/remote ips 254 header["Local"] = sock.Local() 255 header["Remote"] = sock.Remote() 256 257 // Create new context with the metadata 258 ctx := metadata.NewContext(context.Background(), header) 259 260 // Set the timeout from the header if we have it 261 if len(to) > 0 { 262 if n, err := strconv.ParseUint(to, 10, 64); err == nil { 263 var cancel context.CancelFunc 264 265 ctx, cancel = context.WithTimeout(ctx, time.Duration(n)) 266 defer cancel() 267 } 268 } 269 270 // If there's no content type default it 271 if len(contentType) == 0 { 272 msg.Header["Content-Type"] = DefaultContentType 273 contentType = DefaultContentType 274 } 275 276 // Setup old protocol 277 cf := setupProtocol(&msg) 278 279 // No legacy codec needed 280 if cf == nil { 281 var err error 282 // Try get a new codec 283 if cf, err = s.newCodec(contentType); err != nil { 284 // No codec found so send back an error 285 if err = sock.Send(&transport.Message{ 286 Header: map[string]string{ 287 "Content-Type": "text/plain", 288 }, 289 Body: []byte(err.Error()), 290 }); err != nil { 291 gerr = err 292 } 293 294 pool.Release(psock) 295 296 continue 297 } 298 } 299 300 // Create a new rpc codec based on the pseudo socket and codec 301 rcodec := newRPCCodec(&msg, psock, cf) 302 // Check the protocol as well 303 protocol := rcodec.String() 304 305 // Internal request 306 request := rpcRequest{ 307 service: getHeader(headers.Request, msg.Header), 308 method: getHeader(headers.Method, msg.Header), 309 endpoint: getHeader(headers.Endpoint, msg.Header), 310 contentType: contentType, 311 codec: rcodec, 312 header: msg.Header, 313 body: msg.Body, 314 socket: psock, 315 stream: stream, 316 } 317 318 // Internal response 319 response := rpcResponse{ 320 header: make(map[string]string), 321 socket: psock, 322 codec: rcodec, 323 } 324 325 // Wait for two coroutines to exit 326 // Serve the request and process the outbound messages 327 wg.Add(2) 328 329 // Process the outbound messages from the socket 330 go func(psock *socket.Socket) { 331 defer func() { 332 if r := recover(); r != nil { 333 logger.Log(log.ErrorLevel, "panic recovered in outbound goroutine: ", r) 334 logger.Log(log.ErrorLevel, string(debug.Stack())) 335 } 336 // TODO: don't hack this but if its grpc just break out of the stream 337 // We do this because the underlying connection is h2 and its a stream 338 if protocol == "grpc" { 339 if err := sock.Close(); err != nil { 340 logger.Logf(log.ErrorLevel, "Failed to close socket: %v", err) 341 } 342 } 343 344 s.deferer(pool, psock, wg) 345 }() 346 347 for { 348 // Get the message from our internal handler/stream 349 m := new(transport.Message) 350 if err := psock.Process(m); err != nil { 351 return 352 } 353 354 // Send the message back over the socket 355 if err := sock.Send(m); err != nil { 356 return 357 } 358 } 359 }(psock) 360 361 // Serve the request in a go routine as this may be a stream 362 go func(psock *socket.Socket) { 363 defer func() { 364 if r := recover(); r != nil { 365 logger.Log(log.ErrorLevel, "panic recovered in serveReq goroutine: ", r) 366 logger.Log(log.ErrorLevel, string(debug.Stack())) 367 } 368 s.deferer(pool, psock, wg) 369 }() 370 371 s.serveReq(ctx, msg, &request, &response, rcodec) 372 }(psock) 373 } 374 } 375 376 func (s *rpcServer) NewHandler(h interface{}, opts ...HandlerOption) Handler { 377 return s.router.NewHandler(h, opts...) 378 } 379 380 func (s *rpcServer) Handle(h Handler) error { 381 s.Lock() 382 defer s.Unlock() 383 384 if err := s.router.Handle(h); err != nil { 385 return err 386 } 387 388 s.handlers[h.Name()] = h 389 390 return nil 391 } 392 393 func (s *rpcServer) Register() error { 394 config := s.Options() 395 logger := config.Logger 396 397 // Registry function used to register the service 398 regFunc := s.newRegFuc(config) 399 400 // Directly register if service was cached 401 rsvc := s.getCachedService() 402 if rsvc != nil { 403 if err := regFunc(rsvc); err != nil { 404 return errors.Wrap(err, "failed to register service") 405 } 406 407 return nil 408 } 409 410 // Only cache service if host IP valid 411 addr, cacheService, err := s.getAddr(config) 412 if err != nil { 413 return err 414 } 415 416 node := ®istry.Node{ 417 // TODO: node id should be set better. Add native option to specify 418 // host id through either config or ENV. Also look at logging of name. 419 Id: config.Name + "-" + config.Id, 420 Address: addr, 421 Metadata: s.newNodeMetedata(config), 422 } 423 424 service := ®istry.Service{ 425 Name: config.Name, 426 Version: config.Version, 427 Nodes: []*registry.Node{node}, 428 Endpoints: s.getEndpoints(), 429 } 430 431 registered := s.isRegistered() 432 if !registered { 433 logger.Logf(log.InfoLevel, "Registry [%s] Registering node: %s", config.Registry.String(), node.Id) 434 } 435 436 // Register the service 437 if err := regFunc(service); err != nil { 438 return errors.Wrap(err, "failed to register service") 439 } 440 441 // Already registered? don't need to register subscribers 442 if registered { 443 return nil 444 } 445 446 s.Lock() 447 defer s.Unlock() 448 449 s.registered = true 450 451 // Cache service 452 if cacheService { 453 s.rsvc = service 454 } 455 456 // Set what we're advertising 457 s.opts.Advertise = addr 458 459 return nil 460 } 461 462 func (s *rpcServer) Deregister() error { 463 config := s.Options() 464 logger := config.Logger 465 466 addr, _, err := s.getAddr(config) 467 if err != nil { 468 return err 469 } 470 471 // TODO: there should be a better way to do this than reconstruct the service 472 // Edge case is that if service is not cached 473 node := ®istry.Node{ 474 // TODO: also update node id naming 475 Id: config.Name + "-" + config.Id, 476 Address: addr, 477 } 478 479 service := ®istry.Service{ 480 Name: config.Name, 481 Version: config.Version, 482 Nodes: []*registry.Node{node}, 483 } 484 485 logger.Logf(log.InfoLevel, "Registry [%s] Deregistering node: %s", config.Registry.String(), node.Id) 486 487 if err := config.Registry.Deregister(service); err != nil { 488 return err 489 } 490 491 s.Lock() 492 defer s.Unlock() 493 494 s.rsvc = nil 495 496 if !s.registered { 497 return nil 498 } 499 500 s.registered = false 501 502 // close the subscriber 503 if s.subscriber != nil { 504 if err := s.subscriber.Unsubscribe(); err != nil { 505 logger.Logf(log.ErrorLevel, "Failed to unsubscribe service from service name topic: %v", err) 506 } 507 508 s.subscriber = nil 509 } 510 511 for sb, subs := range s.subscribers { 512 for i, sub := range subs { 513 logger.Logf(log.InfoLevel, "Unsubscribing %s from topic: %s", node.Id, sub.Topic()) 514 515 if err := sub.Unsubscribe(); err != nil { 516 logger.Logf(log.ErrorLevel, 517 "Failed to unsubscribe subscriber nr. %d from topic %s: %v", 518 i+1, 519 sub.Topic(), 520 err) 521 } 522 } 523 524 s.subscribers[sb] = nil 525 } 526 527 return nil 528 } 529 530 func (s *rpcServer) Start() error { 531 if s.isStarted() { 532 return nil 533 } 534 535 config := s.Options() 536 logger := config.Logger 537 538 // start listening on the listener 539 listener, err := config.Transport.Listen(config.Address, config.ListenOptions...) 540 if err != nil { 541 return err 542 } 543 544 logger.Logf(log.InfoLevel, "Transport [%s] Listening on %s", config.Transport.String(), listener.Addr()) 545 546 // swap address 547 addr := s.swapAddr(config, listener.Addr()) 548 549 // connect to the broker 550 brokerName := config.Broker.String() 551 if err = config.Broker.Connect(); err != nil { 552 logger.Logf(log.ErrorLevel, "Broker [%s] connect error: %v", brokerName, err) 553 return err 554 } 555 556 logger.Logf(log.InfoLevel, "Broker [%s] Connected to %s", brokerName, config.Broker.Address()) 557 558 // Use RegisterCheck func before register 559 if err = s.opts.RegisterCheck(s.opts.Context); err != nil { 560 logger.Logf(log.ErrorLevel, "Server %s-%s register check error: %s", config.Name, config.Id, err) 561 } else if err = s.Register(); err != nil { 562 // Perform initial registration 563 logger.Logf(log.ErrorLevel, "Server %s-%s register error: %s", config.Name, config.Id, err) 564 } 565 566 exit := make(chan bool) 567 568 // Listen for connections 569 go s.listen(listener, exit) 570 571 // Keep the service registered to registry 572 go s.registrar(listener, addr, config, exit) 573 574 s.setStarted(true) 575 576 return nil 577 } 578 579 func (s *rpcServer) Stop() error { 580 if !s.isStarted() { 581 return nil 582 } 583 584 ch := make(chan error) 585 s.exit <- ch 586 587 err := <-ch 588 589 s.setStarted(false) 590 591 return err 592 } 593 594 func (s *rpcServer) String() string { 595 return "mucp" 596 } 597 598 // newRegFuc will create a new registry function used to register the service. 599 func (s *rpcServer) newRegFuc(config Options) func(service *registry.Service) error { 600 return func(service *registry.Service) error { 601 rOpts := []registry.RegisterOption{registry.RegisterTTL(config.RegisterTTL)} 602 603 var regErr error 604 605 // Attempt to register. If registration fails, back off and try again. 606 // TODO: see if we can improve the retry mechanism. Maybe retry lib, maybe config values 607 for i := 0; i < 3; i++ { 608 if regErr = config.Registry.Register(service, rOpts...); regErr != nil { 609 time.Sleep(backoff.Do(i + 1)) 610 continue 611 } 612 break 613 } 614 615 if regErr != nil { 616 return regErr 617 } 618 619 s.Lock() 620 defer s.Unlock() 621 // Router can exchange messages on broker 622 // Subscribe to the topic with its own name 623 if err := s.subscribeServer(config); err != nil { 624 return errors.Wrap(err, "failed to subscribe to service name topic") 625 } 626 // Subscribe for all of the subscribers 627 s.reSubscribe(config) 628 629 return nil 630 } 631 } 632 633 // getAddr will take the advertise or service address, and return it. 634 func (s *rpcServer) getAddr(config Options) (string, bool, error) { 635 // Use advertise address if provided, else use service address 636 advt := config.Address 637 if len(config.Advertise) > 0 { 638 advt = config.Advertise 639 } 640 641 // Use explicit host and port if possible 642 host, port := advt, "" 643 644 if cnt := strings.Count(advt, ":"); cnt >= 1 { 645 // ipv6 address in format [host]:port or ipv4 host:port 646 h, p, err := net.SplitHostPort(advt) 647 if err != nil { 648 return "", false, err 649 } 650 651 host, port = h, p 652 } 653 654 validHost := net.ParseIP(host) != nil 655 656 addr, err := addr.Extract(host) 657 if err != nil { 658 return "", false, err 659 } 660 661 // mq-rpc(eg. nats) doesn't need the port. its addr is queue name. 662 if port != "" { 663 addr = mnet.HostPort(addr, port) 664 } 665 666 return addr, validHost, nil 667 } 668 669 // newNodeMetedata creates a new metadata map with default values. 670 func (s *rpcServer) newNodeMetedata(config Options) metadata.Metadata { 671 md := metadata.Copy(config.Metadata) 672 673 // TODO: revisit this for v5 674 md["transport"] = config.Transport.String() 675 md["broker"] = config.Broker.String() 676 md["server"] = s.String() 677 md["registry"] = config.Registry.String() 678 md["protocol"] = "mucp" 679 680 return md 681 } 682 683 // getEndpoints takes the list of handlers and subscribers and adds them to 684 // a single endpoints list. 685 func (s *rpcServer) getEndpoints() []*registry.Endpoint { 686 s.RLock() 687 defer s.RUnlock() 688 689 var handlerList []string 690 691 for n, e := range s.handlers { 692 // Only advertise non internal handlers 693 if !e.Options().Internal { 694 handlerList = append(handlerList, n) 695 } 696 } 697 698 // Maps are ordered randomly, sort the keys for consistency 699 // TODO: replace with generic version 700 sort.Strings(handlerList) 701 702 var subscriberList []Subscriber 703 704 for e := range s.subscribers { 705 // Only advertise non internal subscribers 706 if !e.Options().Internal { 707 subscriberList = append(subscriberList, e) 708 } 709 } 710 711 sort.Slice(subscriberList, func(i, j int) bool { 712 return subscriberList[i].Topic() > subscriberList[j].Topic() 713 }) 714 715 endpoints := make([]*registry.Endpoint, 0, len(handlerList)+len(subscriberList)) 716 717 for _, n := range handlerList { 718 endpoints = append(endpoints, s.handlers[n].Endpoints()...) 719 } 720 721 for _, e := range subscriberList { 722 endpoints = append(endpoints, e.Endpoints()...) 723 } 724 725 return endpoints 726 } 727 728 func (s *rpcServer) listen(listener transport.Listener, exit chan bool) { 729 for { 730 // Start listening for connections 731 // This will block until either exit signal given or error occurred 732 err := listener.Accept(s.ServeConn) 733 734 // TODO: listen for messages 735 // msg := broker.Exchange(service).Consume() 736 737 select { 738 // check if we're supposed to exit 739 case <-exit: 740 return 741 // check the error and backoff 742 default: 743 if err != nil { 744 s.opts.Logger.Logf(log.ErrorLevel, "Accept error: %v", err) 745 time.Sleep(time.Second) 746 747 continue 748 } 749 } 750 751 return 752 } 753 } 754 755 // registrar is responsible for keeping the service registered to the registry. 756 func (s *rpcServer) registrar(listener transport.Listener, addr string, config Options, exit chan bool) { 757 logger := config.Logger 758 759 // Only process if it exists 760 ticker := new(time.Ticker) 761 if s.opts.RegisterInterval > time.Duration(0) { 762 ticker = time.NewTicker(s.opts.RegisterInterval) 763 } 764 765 // Return error chan 766 var ch chan error 767 768 Loop: 769 for { 770 select { 771 // Register self on interval 772 case <-ticker.C: 773 registered := s.isRegistered() 774 775 rerr := s.opts.RegisterCheck(s.opts.Context) 776 if rerr != nil && registered { 777 logger.Logf(log.ErrorLevel, 778 "Server %s-%s register check error: %s, deregister it", 779 config.Name, 780 config.Id, 781 rerr) 782 // deregister self in case of error 783 if err := s.Deregister(); err != nil { 784 logger.Logf(log.ErrorLevel, "Server %s-%s deregister error: %s", config.Name, config.Id, err) 785 } 786 } else if rerr != nil && !registered { 787 logger.Logf(log.ErrorLevel, "Server %s-%s register check error: %s", config.Name, config.Id, rerr) 788 continue 789 } 790 791 if err := s.Register(); err != nil { 792 logger.Logf(log.ErrorLevel, "Server %s-%s register error: %s", config.Name, config.Id, err) 793 } 794 795 // Wait for exit signal 796 case ch = <-s.exit: 797 ticker.Stop() 798 close(exit) 799 800 break Loop 801 } 802 } 803 804 // Shutting down, deregister 805 if s.isRegistered() { 806 if err := s.Deregister(); err != nil { 807 logger.Logf(log.ErrorLevel, "Server %s-%s deregister error: %s", config.Name, config.Id, err) 808 } 809 } 810 811 // Wait for requests to finish 812 if swg := s.getWg(); swg != nil { 813 swg.Wait() 814 } 815 816 // Close transport listener 817 ch <- listener.Close() 818 819 brokerName := config.Broker.String() 820 logger.Logf(log.InfoLevel, "Broker [%s] Disconnected from %s", brokerName, config.Broker.Address()) 821 822 // Disconnect the broker 823 if err := config.Broker.Disconnect(); err != nil { 824 logger.Logf(log.ErrorLevel, "Broker [%s] Disconnect error: %v", brokerName, err) 825 } 826 827 // Swap back address 828 s.setOptsAddr(addr) 829 } 830 831 func (s *rpcServer) serveReq(ctx context.Context, 832 msg transport.Message, 833 req *rpcRequest, 834 resp *rpcResponse, 835 rcodec codec.Codec) { 836 logger := s.opts.Logger 837 router := s.getRouter() 838 839 // serve the actual request using the request router 840 if serveRequestError := router.ServeRequest(ctx, req, resp); serveRequestError != nil { 841 // write an error response 842 writeError := rcodec.Write(&codec.Message{ 843 Header: msg.Header, 844 Error: serveRequestError.Error(), 845 Type: codec.Error, 846 }, nil) 847 848 // if the server request is an EOS error we let the socket know 849 // sometimes the socket is already closed on the other side, so we can ignore that error 850 alreadyClosed := errors.Is(serveRequestError, errLastStreamResponse) && errors.Is(writeError, io.EOF) 851 852 // could not write error response 853 if writeError != nil && !alreadyClosed { 854 logger.Logf(log.DebugLevel, "rpc: unable to write error response: %v", writeError) 855 } 856 } 857 } 858 859 func (s *rpcServer) deferer(pool *socket.Pool, psock *socket.Socket, wg *waitGroup) { 860 pool.Release(psock) 861 wg.Done() 862 863 logger := s.opts.Logger 864 if r := recover(); r != nil { 865 logger.Log(log.ErrorLevel, "panic recovered: ", r) 866 logger.Log(log.ErrorLevel, string(debug.Stack())) 867 } 868 } 869 870 func (s *rpcServer) getRouter() Router { 871 router := Router(s.router) 872 873 // if not nil use the router specified 874 if s.opts.Router != nil { 875 // create a wrapped function 876 handler := func(ctx context.Context, req Request, rsp interface{}) error { 877 return s.opts.Router.ServeRequest(ctx, req, rsp.(Response)) 878 } 879 880 // execute the wrapper for it 881 for i := len(s.opts.HdlrWrappers); i > 0; i-- { 882 handler = s.opts.HdlrWrappers[i-1](handler) 883 } 884 885 // set the router 886 router = rpcRouter{h: handler} 887 } 888 889 return router 890 }