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  }