github.com/Cloud-Foundations/Dominator@v0.3.4/lib/srpc/server.go (about) 1 package srpc 2 3 import ( 4 "bufio" 5 "bytes" 6 "crypto/tls" 7 "crypto/x509" 8 "errors" 9 "fmt" 10 "io" 11 "net/http" 12 "os" 13 "os/exec" 14 "path/filepath" 15 "reflect" 16 "sort" 17 "strings" 18 "sync" 19 "time" 20 21 "github.com/Cloud-Foundations/Dominator/lib/net" 22 "github.com/Cloud-Foundations/Dominator/lib/stringutil" 23 "github.com/Cloud-Foundations/Dominator/lib/x509util" 24 "github.com/Cloud-Foundations/tricorder/go/tricorder" 25 "github.com/Cloud-Foundations/tricorder/go/tricorder/units" 26 ) 27 28 const ( 29 connectString = "200 Connected to Go SRPC" 30 rpcPath = "/_goSRPC_/" // Unsecured endpoint. GOB coder. 31 tlsRpcPath = "/_go_TLS_SRPC_/" // Secured endpoint. GOB coder. 32 jsonRpcPath = "/_SRPC_/unsecured/JSON" 33 jsonTlsRpcPath = "/_SRPC_/TLS/JSON" 34 35 getHostnamePath = rpcPath + "getHostname" 36 listMethodsPath = rpcPath + "listMethods" 37 listPublicMethodsPath = rpcPath + "listPublicMethods" 38 39 doNotUseMethodPowers = "doNotUseMethodPowers" 40 41 methodTypeRaw = iota 42 methodTypeCoder 43 methodTypeRequestReply 44 ) 45 46 type builtinReceiver struct{} // NOTE: GrantMethod allows all access. 47 48 type methodWrapper struct { 49 methodType int 50 public bool 51 fn reflect.Value 52 requestType reflect.Type 53 responseType reflect.Type 54 failedCallsDistribution *tricorder.CumulativeDistribution 55 failedRRCallsDistribution *tricorder.CumulativeDistribution 56 numDeniedCalls uint64 57 numPermittedCalls uint64 58 successfulCallsDistribution *tricorder.CumulativeDistribution 59 successfulRRCallsDistribution *tricorder.CumulativeDistribution 60 } 61 62 type receiverType struct { 63 methods map[string]*methodWrapper 64 blockMethod func(methodName string, 65 authInfo *AuthInformation) (func(), error) 66 grantMethod func(serviceMethod string, authInfo *AuthInformation) bool 67 } 68 69 var ( 70 defaultGrantMethod = func(serviceMethod string, 71 authInfo *AuthInformation) bool { 72 return false 73 } 74 receivers map[string]receiverType = make(map[string]receiverType) 75 serverMetricsDir *tricorder.DirectorySpec 76 bucketer *tricorder.Bucketer 77 serverMetricsMutex sync.Mutex 78 numPanicedCalls uint64 79 numServerConnections uint64 80 numOpenServerConnections uint64 81 numRejectedServerConnections uint64 82 registerBuiltin sync.Once 83 registerBuiltinError error 84 85 computeHostname sync.Once 86 hostname string 87 hostnameError error 88 ) 89 90 // Precompute some reflect types. Can't use the types directly because Typeof 91 // takes an empty interface value. This is annoying. 92 var typeOfConn = reflect.TypeOf((**Conn)(nil)).Elem() 93 var typeOfDecoder = reflect.TypeOf((*Decoder)(nil)).Elem() 94 var typeOfEncoder = reflect.TypeOf((*Encoder)(nil)).Elem() 95 var typeOfError = reflect.TypeOf((*error)(nil)).Elem() 96 97 func init() { 98 http.HandleFunc(getHostnamePath, getHostnameHttpHandler) 99 http.HandleFunc(rpcPath, gobUnsecuredHttpHandler) 100 http.HandleFunc(tlsRpcPath, gobTlsHttpHandler) 101 http.HandleFunc(jsonRpcPath, jsonUnsecuredHttpHandler) 102 http.HandleFunc(jsonTlsRpcPath, jsonTlsHttpHandler) 103 http.HandleFunc(listMethodsPath, listMethodsHttpHandler) 104 http.HandleFunc(listPublicMethodsPath, listPublicMethodsHttpHandler) 105 registerServerMetrics() 106 } 107 108 func getNumPanicedCalls() uint64 { 109 serverMetricsMutex.Lock() 110 defer serverMetricsMutex.Unlock() 111 return numPanicedCalls 112 } 113 114 func registerServerMetrics() { 115 var err error 116 serverMetricsDir, err = tricorder.RegisterDirectory("srpc/server") 117 if err != nil { 118 panic(err) 119 } 120 err = serverMetricsDir.RegisterMetric("num-connections", 121 &numServerConnections, units.None, "number of connection attempts") 122 if err != nil { 123 panic(err) 124 } 125 err = serverMetricsDir.RegisterMetric("num-open-connections", 126 &numOpenServerConnections, units.None, "number of open connections") 127 if err != nil { 128 panic(err) 129 } 130 err = serverMetricsDir.RegisterMetric("num-rejected-connections", 131 &numRejectedServerConnections, units.None, 132 "number of rejected connections") 133 if err != nil { 134 panic(err) 135 } 136 bucketer = tricorder.NewGeometricBucketer(0.1, 1e5) 137 } 138 139 func defaultMethodBlocker(methodName string, 140 authInfo *AuthInformation) (func(), error) { 141 return nil, nil 142 } 143 144 func defaultMethodGranter(serviceMethod string, 145 authInfo *AuthInformation) bool { 146 return defaultGrantMethod(serviceMethod, authInfo) 147 } 148 149 func registerName(name string, rcvr interface{}, 150 options ReceiverOptions) error { 151 registerBuiltin.Do(func() { 152 registerBuiltinError = _registerName("", &builtinReceiver{}, 153 ReceiverOptions{}) 154 }) 155 if registerBuiltinError != nil { 156 return registerBuiltinError 157 } 158 return _registerName(name, rcvr, options) 159 } 160 161 func _registerName(name string, rcvr interface{}, 162 options ReceiverOptions) error { 163 if _, ok := receivers[name]; ok { 164 return fmt.Errorf("SRPC receiver already registered: %s", name) 165 } 166 receiver := receiverType{methods: make(map[string]*methodWrapper)} 167 typeOfReceiver := reflect.TypeOf(rcvr) 168 valueOfReceiver := reflect.ValueOf(rcvr) 169 receiverMetricsDir, err := serverMetricsDir.RegisterDirectory(name) 170 if err != nil { 171 return err 172 } 173 publicMethods := stringutil.ConvertListToMap(options.PublicMethods, false) 174 for index := 0; index < typeOfReceiver.NumMethod(); index++ { 175 method := typeOfReceiver.Method(index) 176 if method.PkgPath != "" { // Method must be exported. 177 continue 178 } 179 methodType := method.Type 180 mVal := getMethod(methodType, valueOfReceiver.Method(index)) 181 if mVal == nil { 182 continue 183 } 184 receiver.methods[method.Name] = mVal 185 if _, ok := publicMethods[method.Name]; ok { 186 mVal.public = true 187 } 188 dir, err := receiverMetricsDir.RegisterDirectory(method.Name) 189 if err != nil { 190 return err 191 } 192 if err := mVal.registerMetrics(dir); err != nil { 193 return err 194 } 195 } 196 if blocker, ok := rcvr.(MethodBlocker); ok { 197 receiver.blockMethod = blocker.BlockMethod 198 } else { 199 receiver.blockMethod = defaultMethodBlocker 200 } 201 if granter, ok := rcvr.(MethodGranter); ok { 202 receiver.grantMethod = granter.GrantMethod 203 } else { 204 receiver.grantMethod = defaultMethodGranter 205 } 206 receivers[name] = receiver 207 startReadingSmallStackMetaData() 208 return nil 209 } 210 211 func (*builtinReceiver) GrantMethod( 212 serviceMethod string, authInfo *AuthInformation) bool { 213 return true 214 } 215 216 func getMethod(methodType reflect.Type, fn reflect.Value) *methodWrapper { 217 if methodType.NumOut() != 1 { 218 return nil 219 } 220 if methodType.Out(0) != typeOfError { 221 return nil 222 } 223 if methodType.NumIn() == 2 { 224 // Method needs two ins: receiver, *Conn. 225 if methodType.In(1) != typeOfConn { 226 return nil 227 } 228 return &methodWrapper{methodType: methodTypeRaw, fn: fn} 229 } 230 if methodType.NumIn() == 4 { 231 if methodType.In(1) != typeOfConn { 232 return nil 233 } 234 // Coder Method needs four ins: receiver, *Conn, Decoder, Encoder. 235 if methodType.In(2) == typeOfDecoder && 236 methodType.In(3) == typeOfEncoder { 237 return &methodWrapper{ 238 methodType: methodTypeCoder, 239 fn: fn, 240 } 241 } 242 // RequestReply Method needs four ins: receiver, *Conn, request, *reply. 243 if methodType.In(3).Kind() == reflect.Ptr { 244 return &methodWrapper{ 245 methodType: methodTypeRequestReply, 246 fn: fn, 247 requestType: methodType.In(2), 248 responseType: methodType.In(3).Elem(), 249 } 250 } 251 } 252 return nil 253 } 254 255 func (m *methodWrapper) registerMetrics(dir *tricorder.DirectorySpec) error { 256 m.failedCallsDistribution = bucketer.NewCumulativeDistribution() 257 err := dir.RegisterMetric("failed-call-durations", 258 m.failedCallsDistribution, units.Millisecond, 259 "duration of failed calls") 260 if err != nil { 261 return err 262 } 263 err = dir.RegisterMetric("num-denied-calls", &m.numDeniedCalls, 264 units.None, "number of denied calls to method") 265 if err != nil { 266 return err 267 } 268 err = dir.RegisterMetric("num-permitted-calls", &m.numPermittedCalls, 269 units.None, "number of permitted calls to method") 270 if err != nil { 271 return err 272 } 273 m.successfulCallsDistribution = bucketer.NewCumulativeDistribution() 274 err = dir.RegisterMetric("successful-call-durations", 275 m.successfulCallsDistribution, units.Millisecond, 276 "duration of successful calls") 277 if err != nil { 278 return err 279 } 280 if m.methodType != methodTypeRequestReply { 281 return nil 282 } 283 m.failedRRCallsDistribution = bucketer.NewCumulativeDistribution() 284 err = dir.RegisterMetric("failed-request-reply-call-durations", 285 m.failedRRCallsDistribution, units.Millisecond, 286 "duration of failed request-reply calls") 287 if err != nil { 288 return err 289 } 290 m.successfulRRCallsDistribution = bucketer.NewCumulativeDistribution() 291 err = dir.RegisterMetric("successful-request-reply-call-durations", 292 m.successfulRRCallsDistribution, units.Millisecond, 293 "duration of successful request-reply calls") 294 if err != nil { 295 return err 296 } 297 return nil 298 } 299 300 func getHostnameHttpHandler(w http.ResponseWriter, req *http.Request) { 301 computeHostname.Do(func() { 302 cmd := exec.Command("hostname", "-f") 303 output, err := cmd.CombinedOutput() 304 if err == nil { 305 hostname = strings.TrimSpace(string(output)) 306 return 307 } 308 hostname, hostnameError = os.Hostname() 309 }) 310 if hostnameError != nil { 311 w.WriteHeader(http.StatusInternalServerError) 312 fmt.Fprintln(w, hostnameError) 313 return 314 } 315 fmt.Fprintln(w, hostname) 316 } 317 318 func gobTlsHttpHandler(w http.ResponseWriter, req *http.Request) { 319 httpHandler(w, req, true, &gobCoder{}) 320 } 321 322 func gobUnsecuredHttpHandler(w http.ResponseWriter, req *http.Request) { 323 httpHandler(w, req, false, &gobCoder{}) 324 } 325 326 func jsonTlsHttpHandler(w http.ResponseWriter, req *http.Request) { 327 httpHandler(w, req, true, &jsonCoder{}) 328 } 329 330 func jsonUnsecuredHttpHandler(w http.ResponseWriter, req *http.Request) { 331 httpHandler(w, req, false, &jsonCoder{}) 332 } 333 334 func httpHandler(w http.ResponseWriter, req *http.Request, doTls bool, 335 makeCoder coderMaker) { 336 serverMetricsMutex.Lock() 337 numServerConnections++ 338 serverMetricsMutex.Unlock() 339 if doTls && serverTlsConfig == nil { 340 w.Header().Set("Content-Type", "text/plain; charset=utf-8") 341 w.WriteHeader(http.StatusNotFound) 342 return 343 } 344 if (tlsRequired && !doTls) || req.Method != "CONNECT" { 345 serverMetricsMutex.Lock() 346 numRejectedServerConnections++ 347 serverMetricsMutex.Unlock() 348 w.Header().Set("Content-Type", "text/plain; charset=utf-8") 349 w.WriteHeader(http.StatusMethodNotAllowed) 350 return 351 } 352 if tlsRequired && req.TLS != nil { 353 if serverTlsConfig == nil || 354 !checkVerifiedChains(req.TLS.VerifiedChains, 355 serverTlsConfig.ClientCAs) { 356 serverMetricsMutex.Lock() 357 numRejectedServerConnections++ 358 serverMetricsMutex.Unlock() 359 w.Header().Set("Content-Type", "text/plain; charset=utf-8") 360 w.WriteHeader(http.StatusUnauthorized) 361 return 362 } 363 } 364 if req.ParseForm() != nil { 365 w.WriteHeader(http.StatusBadRequest) 366 return 367 } 368 hijacker, ok := w.(http.Hijacker) 369 if !ok { 370 w.Header().Set("Content-Type", "text/plain; charset=utf-8") 371 w.WriteHeader(http.StatusInternalServerError) 372 logger.Println("not a hijacker ", req.RemoteAddr) 373 return 374 } 375 unsecuredConn, bufrw, err := hijacker.Hijack() 376 if err != nil { 377 w.Header().Set("Content-Type", "text/plain; charset=utf-8") 378 w.WriteHeader(http.StatusInternalServerError) 379 logger.Println("rpc hijacking ", req.RemoteAddr, ": ", err.Error()) 380 return 381 } 382 myConn := &Conn{ 383 conn: unsecuredConn, 384 localAddr: unsecuredConn.LocalAddr().String(), 385 remoteAddr: unsecuredConn.RemoteAddr().String(), 386 } 387 connType := "unknown" 388 defer func() { 389 myConn.conn.Close() 390 }() 391 if tcpConn, ok := unsecuredConn.(net.TCPConn); ok { 392 connType = "TCP" 393 if err := tcpConn.SetKeepAlive(true); err != nil { 394 logger.Println("error setting keepalive: ", err.Error()) 395 return 396 } 397 if err := tcpConn.SetKeepAlivePeriod(time.Minute * 5); err != nil { 398 logger.Println("error setting keepalive period: ", err.Error()) 399 return 400 } 401 } else { 402 w.Header().Set("Content-Type", "text/plain; charset=utf-8") 403 w.WriteHeader(http.StatusNotAcceptable) 404 logger.Println("non-TCP connection") 405 return 406 } 407 _, err = io.WriteString(unsecuredConn, "HTTP/1.0 "+connectString+"\n\n") 408 if err != nil { 409 logger.Println("error writing connect message: ", err.Error()) 410 return 411 } 412 allowMethodPowers := true 413 if req.Form.Get(doNotUseMethodPowers) == "true" { 414 allowMethodPowers = false 415 } 416 if doTls { 417 var tlsConn *tls.Conn 418 if req.TLS == nil { 419 tlsConn = tls.Server(unsecuredConn, serverTlsConfig) 420 myConn.conn = tlsConn 421 if err := tlsConn.Handshake(); err != nil { 422 serverMetricsMutex.Lock() 423 numRejectedServerConnections++ 424 serverMetricsMutex.Unlock() 425 logger.Println(err) 426 return 427 } 428 connType += "/TLS" 429 } else { 430 if tlsConn, ok = unsecuredConn.(*tls.Conn); !ok { 431 logger.Println("not really a TLS connection") 432 return 433 } 434 connType += "/TLS" 435 } 436 myConn.isEncrypted = true 437 myConn.username, myConn.permittedMethods, myConn.groupList, err = 438 getAuth(tlsConn.ConnectionState(), allowMethodPowers) 439 if err != nil { 440 logger.Println(err) 441 return 442 } 443 myConn.ReadWriter = bufio.NewReadWriter(bufio.NewReader(tlsConn), 444 bufio.NewWriter(tlsConn)) 445 } else { 446 if !allowMethodPowers { 447 myConn.permittedMethods = make(map[string]struct{}) 448 } 449 myConn.ReadWriter = bufrw 450 } 451 logger.Debugf(0, "accepted %s connection from: %s\n", 452 connType, myConn.remoteAddr) 453 serverMetricsMutex.Lock() 454 numOpenServerConnections++ 455 serverMetricsMutex.Unlock() 456 handleConnection(myConn, makeCoder) 457 serverMetricsMutex.Lock() 458 numOpenServerConnections-- 459 serverMetricsMutex.Unlock() 460 } 461 462 func checkVerifiedChains(verifiedChains [][]*x509.Certificate, 463 certPool *x509.CertPool) bool { 464 for _, vChain := range verifiedChains { 465 vSubject := vChain[0].RawIssuer 466 for _, cSubject := range certPool.Subjects() { 467 if bytes.Compare(vSubject, cSubject) == 0 { 468 return true 469 } 470 } 471 } 472 return false 473 } 474 475 func getAuth(state tls.ConnectionState, allowMethodPowers bool) ( 476 string, map[string]struct{}, 477 map[string]struct{}, error) { 478 var username string 479 permittedMethods := make(map[string]struct{}) 480 trustCertMethods := false 481 if fullAuthCaCertPool == nil || 482 checkVerifiedChains(state.VerifiedChains, fullAuthCaCertPool) { 483 trustCertMethods = true 484 } 485 var groupList map[string]struct{} 486 for _, certChain := range state.VerifiedChains { 487 for _, cert := range certChain { 488 var err error 489 if username == "" { 490 username, err = x509util.GetUsername(cert) 491 if err != nil { 492 return "", nil, nil, err 493 } 494 } 495 if len(groupList) < 1 { 496 groupList, err = x509util.GetGroupList(cert) 497 if err != nil { 498 return "", nil, nil, err 499 } 500 } 501 if allowMethodPowers && trustCertMethods { 502 pms, err := x509util.GetPermittedMethods(cert) 503 if err != nil { 504 return "", nil, nil, err 505 } 506 for method := range pms { 507 permittedMethods[method] = struct{}{} 508 } 509 } 510 } 511 } 512 return username, permittedMethods, groupList, nil 513 } 514 515 func handleConnection(conn *Conn, makeCoder coderMaker) { 516 defer conn.callReleaseNotifier() 517 defer conn.Flush() 518 for ; ; conn.Flush() { 519 conn.callReleaseNotifier() 520 serviceMethod, err := conn.ReadString('\n') 521 if err == io.EOF || err == io.ErrUnexpectedEOF { 522 return 523 } 524 if err != nil { 525 logger.Println(err) 526 if _, err := conn.WriteString(err.Error() + "\n"); err != nil { 527 logger.Println(err) 528 return 529 } 530 continue 531 } 532 serviceMethod = strings.TrimSpace(serviceMethod) 533 if serviceMethod == "" { 534 // Received a "ping" request, send response. 535 if _, err := conn.WriteString("\n"); err != nil { 536 logger.Println(err) 537 return 538 } 539 continue 540 } 541 method, err := conn.findMethod(serviceMethod) 542 if err != nil { 543 if _, err := conn.WriteString(err.Error() + "\n"); err != nil { 544 logger.Println(err) 545 return 546 } 547 continue 548 } 549 // Method is OK to call. Tell client and then call method handler. 550 if _, err := conn.WriteString("\n"); err != nil { 551 logger.Println(err) 552 return 553 } 554 if err := conn.Flush(); err != nil { 555 logger.Println(err) 556 return 557 } 558 if err := method.call(conn, makeCoder); err != nil { 559 if err != ErrorCloseClient { 560 logger.Println(err) 561 } 562 return 563 } 564 } 565 } 566 567 func (conn *Conn) callReleaseNotifier() { 568 if releaseNotifier := conn.releaseNotifier; releaseNotifier != nil { 569 releaseNotifier() 570 } 571 conn.releaseNotifier = nil 572 } 573 574 func (conn *Conn) findMethod(serviceMethod string) (*methodWrapper, error) { 575 splitServiceMethod := strings.Split(serviceMethod, ".") 576 if len(splitServiceMethod) != 2 { 577 return nil, errors.New("malformed Service.Method: " + serviceMethod) 578 } 579 serviceName := splitServiceMethod[0] 580 receiver, ok := receivers[serviceName] 581 if !ok { 582 return nil, errors.New("unknown service: " + serviceName) 583 } 584 methodName := splitServiceMethod[1] 585 method, ok := receiver.methods[methodName] 586 if !ok { 587 return nil, errors.New(serviceName + ": unknown method: " + methodName) 588 } 589 if conn.checkMethodAccess(serviceMethod) { 590 conn.haveMethodAccess = true 591 } else if receiver.grantMethod(serviceName, conn.GetAuthInformation()) { 592 conn.haveMethodAccess = true 593 } else { 594 conn.haveMethodAccess = false 595 if !method.public { 596 method.numDeniedCalls++ 597 return nil, ErrorAccessToMethodDenied 598 } 599 } 600 authInfo := conn.GetAuthInformation() 601 if rn, err := receiver.blockMethod(methodName, authInfo); err != nil { 602 return nil, err 603 } else { 604 conn.releaseNotifier = rn 605 } 606 return method, nil 607 } 608 609 // checkMethodAccess implements the built-in authorisation checks. It returns 610 // true if the method is permitted, else false if denied. 611 func (conn *Conn) checkMethodAccess(serviceMethod string) bool { 612 if conn.permittedMethods == nil { 613 return true 614 } 615 for sm := range conn.permittedMethods { 616 if matched, _ := filepath.Match(sm, serviceMethod); matched { 617 return true 618 } 619 } 620 if conn.username != "" { 621 smallStackOwners := getSmallStackOwners() 622 if smallStackOwners != nil { 623 if _, ok := smallStackOwners.users[conn.username]; ok { 624 return true 625 } 626 for _, group := range smallStackOwners.groups { 627 if _, ok := conn.groupList[group]; ok { 628 return true 629 } 630 } 631 } 632 } 633 return false 634 } 635 636 func listMethodsHttpHandler(w http.ResponseWriter, req *http.Request) { 637 writer := bufio.NewWriter(w) 638 defer writer.Flush() 639 methods := make([]string, len(receivers)) 640 for receiverName, receiver := range receivers { 641 for method := range receiver.methods { 642 methods = append(methods, receiverName+"."+method+"\n") 643 } 644 } 645 sort.Strings(methods) 646 for _, method := range methods { 647 writer.WriteString(method) 648 } 649 } 650 651 func listPublicMethodsHttpHandler(w http.ResponseWriter, req *http.Request) { 652 writer := bufio.NewWriter(w) 653 defer writer.Flush() 654 methods := make([]string, len(receivers)) 655 for receiverName, receiver := range receivers { 656 for name, method := range receiver.methods { 657 if !method.public { 658 continue 659 } 660 methods = append(methods, receiverName+"."+name+"\n") 661 } 662 } 663 sort.Strings(methods) 664 for _, method := range methods { 665 writer.WriteString(method) 666 } 667 } 668 669 func (m *methodWrapper) call(conn *Conn, makeCoder coderMaker) error { 670 m.numPermittedCalls++ 671 startTime := time.Now() 672 err := m._call(conn, makeCoder) 673 timeTaken := time.Since(startTime) 674 if err == nil { 675 m.successfulCallsDistribution.Add(timeTaken) 676 } else { 677 m.failedCallsDistribution.Add(timeTaken) 678 } 679 return err 680 } 681 682 func (m *methodWrapper) _call(conn *Conn, makeCoder coderMaker) error { 683 defer func() { 684 if err := recover(); err != nil { 685 serverMetricsMutex.Lock() 686 numPanicedCalls++ 687 serverMetricsMutex.Unlock() 688 panic(err) 689 } 690 }() 691 connValue := reflect.ValueOf(conn) 692 conn.Decoder = makeCoder.MakeDecoder(conn) 693 conn.Encoder = makeCoder.MakeEncoder(conn) 694 switch m.methodType { 695 case methodTypeRaw: 696 returnValues := m.fn.Call([]reflect.Value{connValue}) 697 errInter := returnValues[0].Interface() 698 if errInter != nil { 699 return errInter.(error) 700 } 701 return nil 702 case methodTypeCoder: 703 returnValues := m.fn.Call([]reflect.Value{ 704 connValue, 705 reflect.ValueOf(conn.Decoder), 706 reflect.ValueOf(conn.Encoder), 707 }) 708 errInter := returnValues[0].Interface() 709 if errInter != nil { 710 return errInter.(error) 711 } 712 return nil 713 case methodTypeRequestReply: 714 request := reflect.New(m.requestType) 715 response := reflect.New(m.responseType) 716 if err := conn.Decode(request.Interface()); err != nil { 717 _, err = conn.WriteString(err.Error() + "\n") 718 return err 719 } 720 startTime := time.Now() 721 returnValues := m.fn.Call([]reflect.Value{connValue, request.Elem(), 722 response}) 723 timeTaken := time.Since(startTime) 724 errInter := returnValues[0].Interface() 725 if errInter != nil { 726 m.failedRRCallsDistribution.Add(timeTaken) 727 err := errInter.(error) 728 _, err = conn.WriteString(err.Error() + "\n") 729 return err 730 } 731 m.successfulRRCallsDistribution.Add(timeTaken) 732 if _, err := conn.WriteString("\n"); err != nil { 733 return err 734 } 735 return conn.Encode(response.Interface()) 736 } 737 return errors.New("unknown method type") 738 }