summaryrefslogtreecommitdiff
path: root/src/pkg/net/rpc/server.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/pkg/net/rpc/server.go')
-rw-r--r--src/pkg/net/rpc/server.go686
1 files changed, 0 insertions, 686 deletions
diff --git a/src/pkg/net/rpc/server.go b/src/pkg/net/rpc/server.go
deleted file mode 100644
index 6b264b46b..000000000
--- a/src/pkg/net/rpc/server.go
+++ /dev/null
@@ -1,686 +0,0 @@
-// Copyright 2009 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-/*
- Package rpc provides access to the exported methods of an object across a
- network or other I/O connection. A server registers an object, making it visible
- as a service with the name of the type of the object. After registration, exported
- methods of the object will be accessible remotely. A server may register multiple
- objects (services) of different types but it is an error to register multiple
- objects of the same type.
-
- Only methods that satisfy these criteria will be made available for remote access;
- other methods will be ignored:
-
- - the method is exported.
- - the method has two arguments, both exported (or builtin) types.
- - the method's second argument is a pointer.
- - the method has return type error.
-
- In effect, the method must look schematically like
-
- func (t *T) MethodName(argType T1, replyType *T2) error
-
- where T, T1 and T2 can be marshaled by encoding/gob.
- These requirements apply even if a different codec is used.
- (In the future, these requirements may soften for custom codecs.)
-
- The method's first argument represents the arguments provided by the caller; the
- second argument represents the result parameters to be returned to the caller.
- The method's return value, if non-nil, is passed back as a string that the client
- sees as if created by errors.New. If an error is returned, the reply parameter
- will not be sent back to the client.
-
- The server may handle requests on a single connection by calling ServeConn. More
- typically it will create a network listener and call Accept or, for an HTTP
- listener, HandleHTTP and http.Serve.
-
- A client wishing to use the service establishes a connection and then invokes
- NewClient on the connection. The convenience function Dial (DialHTTP) performs
- both steps for a raw network connection (an HTTP connection). The resulting
- Client object has two methods, Call and Go, that specify the service and method to
- call, a pointer containing the arguments, and a pointer to receive the result
- parameters.
-
- The Call method waits for the remote call to complete while the Go method
- launches the call asynchronously and signals completion using the Call
- structure's Done channel.
-
- Unless an explicit codec is set up, package encoding/gob is used to
- transport the data.
-
- Here is a simple example. A server wishes to export an object of type Arith:
-
- package server
-
- type Args struct {
- A, B int
- }
-
- type Quotient struct {
- Quo, Rem int
- }
-
- type Arith int
-
- func (t *Arith) Multiply(args *Args, reply *int) error {
- *reply = args.A * args.B
- return nil
- }
-
- func (t *Arith) Divide(args *Args, quo *Quotient) error {
- if args.B == 0 {
- return errors.New("divide by zero")
- }
- quo.Quo = args.A / args.B
- quo.Rem = args.A % args.B
- return nil
- }
-
- The server calls (for HTTP service):
-
- arith := new(Arith)
- rpc.Register(arith)
- rpc.HandleHTTP()
- l, e := net.Listen("tcp", ":1234")
- if e != nil {
- log.Fatal("listen error:", e)
- }
- go http.Serve(l, nil)
-
- At this point, clients can see a service "Arith" with methods "Arith.Multiply" and
- "Arith.Divide". To invoke one, a client first dials the server:
-
- client, err := rpc.DialHTTP("tcp", serverAddress + ":1234")
- if err != nil {
- log.Fatal("dialing:", err)
- }
-
- Then it can make a remote call:
-
- // Synchronous call
- args := &server.Args{7,8}
- var reply int
- err = client.Call("Arith.Multiply", args, &reply)
- if err != nil {
- log.Fatal("arith error:", err)
- }
- fmt.Printf("Arith: %d*%d=%d", args.A, args.B, reply)
-
- or
-
- // Asynchronous call
- quotient := new(Quotient)
- divCall := client.Go("Arith.Divide", args, quotient, nil)
- replyCall := <-divCall.Done // will be equal to divCall
- // check errors, print, etc.
-
- A server implementation will often provide a simple, type-safe wrapper for the
- client.
-*/
-package rpc
-
-import (
- "bufio"
- "encoding/gob"
- "errors"
- "io"
- "log"
- "net"
- "net/http"
- "reflect"
- "strings"
- "sync"
- "unicode"
- "unicode/utf8"
-)
-
-const (
- // Defaults used by HandleHTTP
- DefaultRPCPath = "/_goRPC_"
- DefaultDebugPath = "/debug/rpc"
-)
-
-// Precompute the reflect type for error. Can't use error directly
-// because Typeof takes an empty interface value. This is annoying.
-var typeOfError = reflect.TypeOf((*error)(nil)).Elem()
-
-type methodType struct {
- sync.Mutex // protects counters
- method reflect.Method
- ArgType reflect.Type
- ReplyType reflect.Type
- numCalls uint
-}
-
-type service struct {
- name string // name of service
- rcvr reflect.Value // receiver of methods for the service
- typ reflect.Type // type of the receiver
- method map[string]*methodType // registered methods
-}
-
-// Request is a header written before every RPC call. It is used internally
-// but documented here as an aid to debugging, such as when analyzing
-// network traffic.
-type Request struct {
- ServiceMethod string // format: "Service.Method"
- Seq uint64 // sequence number chosen by client
- next *Request // for free list in Server
-}
-
-// Response is a header written before every RPC return. It is used internally
-// but documented here as an aid to debugging, such as when analyzing
-// network traffic.
-type Response struct {
- ServiceMethod string // echoes that of the Request
- Seq uint64 // echoes that of the request
- Error string // error, if any.
- next *Response // for free list in Server
-}
-
-// Server represents an RPC Server.
-type Server struct {
- mu sync.RWMutex // protects the serviceMap
- serviceMap map[string]*service
- reqLock sync.Mutex // protects freeReq
- freeReq *Request
- respLock sync.Mutex // protects freeResp
- freeResp *Response
-}
-
-// NewServer returns a new Server.
-func NewServer() *Server {
- return &Server{serviceMap: make(map[string]*service)}
-}
-
-// DefaultServer is the default instance of *Server.
-var DefaultServer = NewServer()
-
-// Is this an exported - upper case - name?
-func isExported(name string) bool {
- rune, _ := utf8.DecodeRuneInString(name)
- return unicode.IsUpper(rune)
-}
-
-// Is this type exported or a builtin?
-func isExportedOrBuiltinType(t reflect.Type) bool {
- for t.Kind() == reflect.Ptr {
- t = t.Elem()
- }
- // PkgPath will be non-empty even for an exported type,
- // so we need to check the type name as well.
- return isExported(t.Name()) || t.PkgPath() == ""
-}
-
-// Register publishes in the server the set of methods of the
-// receiver value that satisfy the following conditions:
-// - exported method
-// - two arguments, both of exported type
-// - the second argument is a pointer
-// - one return value, of type error
-// It returns an error if the receiver is not an exported type or has
-// no suitable methods. It also logs the error using package log.
-// The client accesses each method using a string of the form "Type.Method",
-// where Type is the receiver's concrete type.
-func (server *Server) Register(rcvr interface{}) error {
- return server.register(rcvr, "", false)
-}
-
-// RegisterName is like Register but uses the provided name for the type
-// instead of the receiver's concrete type.
-func (server *Server) RegisterName(name string, rcvr interface{}) error {
- return server.register(rcvr, name, true)
-}
-
-func (server *Server) register(rcvr interface{}, name string, useName bool) error {
- server.mu.Lock()
- defer server.mu.Unlock()
- if server.serviceMap == nil {
- server.serviceMap = make(map[string]*service)
- }
- s := new(service)
- s.typ = reflect.TypeOf(rcvr)
- s.rcvr = reflect.ValueOf(rcvr)
- sname := reflect.Indirect(s.rcvr).Type().Name()
- if useName {
- sname = name
- }
- if sname == "" {
- s := "rpc.Register: no service name for type " + s.typ.String()
- log.Print(s)
- return errors.New(s)
- }
- if !isExported(sname) && !useName {
- s := "rpc.Register: type " + sname + " is not exported"
- log.Print(s)
- return errors.New(s)
- }
- if _, present := server.serviceMap[sname]; present {
- return errors.New("rpc: service already defined: " + sname)
- }
- s.name = sname
-
- // Install the methods
- s.method = suitableMethods(s.typ, true)
-
- if len(s.method) == 0 {
- str := ""
-
- // To help the user, see if a pointer receiver would work.
- method := suitableMethods(reflect.PtrTo(s.typ), false)
- if len(method) != 0 {
- str = "rpc.Register: type " + sname + " has no exported methods of suitable type (hint: pass a pointer to value of that type)"
- } else {
- str = "rpc.Register: type " + sname + " has no exported methods of suitable type"
- }
- log.Print(str)
- return errors.New(str)
- }
- server.serviceMap[s.name] = s
- return nil
-}
-
-// suitableMethods returns suitable Rpc methods of typ, it will report
-// error using log if reportErr is true.
-func suitableMethods(typ reflect.Type, reportErr bool) map[string]*methodType {
- methods := make(map[string]*methodType)
- for m := 0; m < typ.NumMethod(); m++ {
- method := typ.Method(m)
- mtype := method.Type
- mname := method.Name
- // Method must be exported.
- if method.PkgPath != "" {
- continue
- }
- // Method needs three ins: receiver, *args, *reply.
- if mtype.NumIn() != 3 {
- if reportErr {
- log.Println("method", mname, "has wrong number of ins:", mtype.NumIn())
- }
- continue
- }
- // First arg need not be a pointer.
- argType := mtype.In(1)
- if !isExportedOrBuiltinType(argType) {
- if reportErr {
- log.Println(mname, "argument type not exported:", argType)
- }
- continue
- }
- // Second arg must be a pointer.
- replyType := mtype.In(2)
- if replyType.Kind() != reflect.Ptr {
- if reportErr {
- log.Println("method", mname, "reply type not a pointer:", replyType)
- }
- continue
- }
- // Reply type must be exported.
- if !isExportedOrBuiltinType(replyType) {
- if reportErr {
- log.Println("method", mname, "reply type not exported:", replyType)
- }
- continue
- }
- // Method needs one out.
- if mtype.NumOut() != 1 {
- if reportErr {
- log.Println("method", mname, "has wrong number of outs:", mtype.NumOut())
- }
- continue
- }
- // The return type of the method must be error.
- if returnType := mtype.Out(0); returnType != typeOfError {
- if reportErr {
- log.Println("method", mname, "returns", returnType.String(), "not error")
- }
- continue
- }
- methods[mname] = &methodType{method: method, ArgType: argType, ReplyType: replyType}
- }
- return methods
-}
-
-// A value sent as a placeholder for the server's response value when the server
-// receives an invalid request. It is never decoded by the client since the Response
-// contains an error when it is used.
-var invalidRequest = struct{}{}
-
-func (server *Server) sendResponse(sending *sync.Mutex, req *Request, reply interface{}, codec ServerCodec, errmsg string) {
- resp := server.getResponse()
- // Encode the response header
- resp.ServiceMethod = req.ServiceMethod
- if errmsg != "" {
- resp.Error = errmsg
- reply = invalidRequest
- }
- resp.Seq = req.Seq
- sending.Lock()
- err := codec.WriteResponse(resp, reply)
- if debugLog && err != nil {
- log.Println("rpc: writing response:", err)
- }
- sending.Unlock()
- server.freeResponse(resp)
-}
-
-func (m *methodType) NumCalls() (n uint) {
- m.Lock()
- n = m.numCalls
- m.Unlock()
- return n
-}
-
-func (s *service) call(server *Server, sending *sync.Mutex, mtype *methodType, req *Request, argv, replyv reflect.Value, codec ServerCodec) {
- mtype.Lock()
- mtype.numCalls++
- mtype.Unlock()
- function := mtype.method.Func
- // Invoke the method, providing a new value for the reply.
- returnValues := function.Call([]reflect.Value{s.rcvr, argv, replyv})
- // The return value for the method is an error.
- errInter := returnValues[0].Interface()
- errmsg := ""
- if errInter != nil {
- errmsg = errInter.(error).Error()
- }
- server.sendResponse(sending, req, replyv.Interface(), codec, errmsg)
- server.freeRequest(req)
-}
-
-type gobServerCodec struct {
- rwc io.ReadWriteCloser
- dec *gob.Decoder
- enc *gob.Encoder
- encBuf *bufio.Writer
-}
-
-func (c *gobServerCodec) ReadRequestHeader(r *Request) error {
- return c.dec.Decode(r)
-}
-
-func (c *gobServerCodec) ReadRequestBody(body interface{}) error {
- return c.dec.Decode(body)
-}
-
-func (c *gobServerCodec) WriteResponse(r *Response, body interface{}) (err error) {
- if err = c.enc.Encode(r); err != nil {
- return
- }
- if err = c.enc.Encode(body); err != nil {
- return
- }
- return c.encBuf.Flush()
-}
-
-func (c *gobServerCodec) Close() error {
- return c.rwc.Close()
-}
-
-// ServeConn runs the server on a single connection.
-// ServeConn blocks, serving the connection until the client hangs up.
-// The caller typically invokes ServeConn in a go statement.
-// ServeConn uses the gob wire format (see package gob) on the
-// connection. To use an alternate codec, use ServeCodec.
-func (server *Server) ServeConn(conn io.ReadWriteCloser) {
- buf := bufio.NewWriter(conn)
- srv := &gobServerCodec{conn, gob.NewDecoder(conn), gob.NewEncoder(buf), buf}
- server.ServeCodec(srv)
-}
-
-// ServeCodec is like ServeConn but uses the specified codec to
-// decode requests and encode responses.
-func (server *Server) ServeCodec(codec ServerCodec) {
- sending := new(sync.Mutex)
- for {
- service, mtype, req, argv, replyv, keepReading, err := server.readRequest(codec)
- if err != nil {
- if debugLog && err != io.EOF {
- log.Println("rpc:", err)
- }
- if !keepReading {
- break
- }
- // send a response if we actually managed to read a header.
- if req != nil {
- server.sendResponse(sending, req, invalidRequest, codec, err.Error())
- server.freeRequest(req)
- }
- continue
- }
- go service.call(server, sending, mtype, req, argv, replyv, codec)
- }
- codec.Close()
-}
-
-// ServeRequest is like ServeCodec but synchronously serves a single request.
-// It does not close the codec upon completion.
-func (server *Server) ServeRequest(codec ServerCodec) error {
- sending := new(sync.Mutex)
- service, mtype, req, argv, replyv, keepReading, err := server.readRequest(codec)
- if err != nil {
- if !keepReading {
- return err
- }
- // send a response if we actually managed to read a header.
- if req != nil {
- server.sendResponse(sending, req, invalidRequest, codec, err.Error())
- server.freeRequest(req)
- }
- return err
- }
- service.call(server, sending, mtype, req, argv, replyv, codec)
- return nil
-}
-
-func (server *Server) getRequest() *Request {
- server.reqLock.Lock()
- req := server.freeReq
- if req == nil {
- req = new(Request)
- } else {
- server.freeReq = req.next
- *req = Request{}
- }
- server.reqLock.Unlock()
- return req
-}
-
-func (server *Server) freeRequest(req *Request) {
- server.reqLock.Lock()
- req.next = server.freeReq
- server.freeReq = req
- server.reqLock.Unlock()
-}
-
-func (server *Server) getResponse() *Response {
- server.respLock.Lock()
- resp := server.freeResp
- if resp == nil {
- resp = new(Response)
- } else {
- server.freeResp = resp.next
- *resp = Response{}
- }
- server.respLock.Unlock()
- return resp
-}
-
-func (server *Server) freeResponse(resp *Response) {
- server.respLock.Lock()
- resp.next = server.freeResp
- server.freeResp = resp
- server.respLock.Unlock()
-}
-
-func (server *Server) readRequest(codec ServerCodec) (service *service, mtype *methodType, req *Request, argv, replyv reflect.Value, keepReading bool, err error) {
- service, mtype, req, keepReading, err = server.readRequestHeader(codec)
- if err != nil {
- if !keepReading {
- return
- }
- // discard body
- codec.ReadRequestBody(nil)
- return
- }
-
- // Decode the argument value.
- argIsValue := false // if true, need to indirect before calling.
- if mtype.ArgType.Kind() == reflect.Ptr {
- argv = reflect.New(mtype.ArgType.Elem())
- } else {
- argv = reflect.New(mtype.ArgType)
- argIsValue = true
- }
- // argv guaranteed to be a pointer now.
- if err = codec.ReadRequestBody(argv.Interface()); err != nil {
- return
- }
- if argIsValue {
- argv = argv.Elem()
- }
-
- replyv = reflect.New(mtype.ReplyType.Elem())
- return
-}
-
-func (server *Server) readRequestHeader(codec ServerCodec) (service *service, mtype *methodType, req *Request, keepReading bool, err error) {
- // Grab the request header.
- req = server.getRequest()
- err = codec.ReadRequestHeader(req)
- if err != nil {
- req = nil
- if err == io.EOF || err == io.ErrUnexpectedEOF {
- return
- }
- err = errors.New("rpc: server cannot decode request: " + err.Error())
- return
- }
-
- // We read the header successfully. If we see an error now,
- // we can still recover and move on to the next request.
- keepReading = true
-
- dot := strings.LastIndex(req.ServiceMethod, ".")
- if dot < 0 {
- err = errors.New("rpc: service/method request ill-formed: " + req.ServiceMethod)
- return
- }
- serviceName := req.ServiceMethod[:dot]
- methodName := req.ServiceMethod[dot+1:]
-
- // Look up the request.
- server.mu.RLock()
- service = server.serviceMap[serviceName]
- server.mu.RUnlock()
- if service == nil {
- err = errors.New("rpc: can't find service " + req.ServiceMethod)
- return
- }
- mtype = service.method[methodName]
- if mtype == nil {
- err = errors.New("rpc: can't find method " + req.ServiceMethod)
- }
- return
-}
-
-// Accept accepts connections on the listener and serves requests
-// for each incoming connection. Accept blocks; the caller typically
-// invokes it in a go statement.
-func (server *Server) Accept(lis net.Listener) {
- for {
- conn, err := lis.Accept()
- if err != nil {
- log.Fatal("rpc.Serve: accept:", err.Error()) // TODO(r): exit?
- }
- go server.ServeConn(conn)
- }
-}
-
-// Register publishes the receiver's methods in the DefaultServer.
-func Register(rcvr interface{}) error { return DefaultServer.Register(rcvr) }
-
-// RegisterName is like Register but uses the provided name for the type
-// instead of the receiver's concrete type.
-func RegisterName(name string, rcvr interface{}) error {
- return DefaultServer.RegisterName(name, rcvr)
-}
-
-// A ServerCodec implements reading of RPC requests and writing of
-// RPC responses for the server side of an RPC session.
-// The server calls ReadRequestHeader and ReadRequestBody in pairs
-// to read requests from the connection, and it calls WriteResponse to
-// write a response back. The server calls Close when finished with the
-// connection. ReadRequestBody may be called with a nil
-// argument to force the body of the request to be read and discarded.
-type ServerCodec interface {
- ReadRequestHeader(*Request) error
- ReadRequestBody(interface{}) error
- // WriteResponse must be safe for concurrent use by multiple goroutines.
- WriteResponse(*Response, interface{}) error
-
- Close() error
-}
-
-// ServeConn runs the DefaultServer on a single connection.
-// ServeConn blocks, serving the connection until the client hangs up.
-// The caller typically invokes ServeConn in a go statement.
-// ServeConn uses the gob wire format (see package gob) on the
-// connection. To use an alternate codec, use ServeCodec.
-func ServeConn(conn io.ReadWriteCloser) {
- DefaultServer.ServeConn(conn)
-}
-
-// ServeCodec is like ServeConn but uses the specified codec to
-// decode requests and encode responses.
-func ServeCodec(codec ServerCodec) {
- DefaultServer.ServeCodec(codec)
-}
-
-// ServeRequest is like ServeCodec but synchronously serves a single request.
-// It does not close the codec upon completion.
-func ServeRequest(codec ServerCodec) error {
- return DefaultServer.ServeRequest(codec)
-}
-
-// Accept accepts connections on the listener and serves requests
-// to DefaultServer for each incoming connection.
-// Accept blocks; the caller typically invokes it in a go statement.
-func Accept(lis net.Listener) { DefaultServer.Accept(lis) }
-
-// Can connect to RPC service using HTTP CONNECT to rpcPath.
-var connected = "200 Connected to Go RPC"
-
-// ServeHTTP implements an http.Handler that answers RPC requests.
-func (server *Server) ServeHTTP(w http.ResponseWriter, req *http.Request) {
- if req.Method != "CONNECT" {
- w.Header().Set("Content-Type", "text/plain; charset=utf-8")
- w.WriteHeader(http.StatusMethodNotAllowed)
- io.WriteString(w, "405 must CONNECT\n")
- return
- }
- conn, _, err := w.(http.Hijacker).Hijack()
- if err != nil {
- log.Print("rpc hijacking ", req.RemoteAddr, ": ", err.Error())
- return
- }
- io.WriteString(conn, "HTTP/1.0 "+connected+"\n\n")
- server.ServeConn(conn)
-}
-
-// HandleHTTP registers an HTTP handler for RPC messages on rpcPath,
-// and a debugging handler on debugPath.
-// It is still necessary to invoke http.Serve(), typically in a go statement.
-func (server *Server) HandleHTTP(rpcPath, debugPath string) {
- http.Handle(rpcPath, server)
- http.Handle(debugPath, debugHTTP{server})
-}
-
-// HandleHTTP registers an HTTP handler for RPC messages to DefaultServer
-// on DefaultRPCPath and a debugging handler on DefaultDebugPath.
-// It is still necessary to invoke http.Serve(), typically in a go statement.
-func HandleHTTP() {
- DefaultServer.HandleHTTP(DefaultRPCPath, DefaultDebugPath)
-}