github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/rpc/client.go (about) 1 // Copyright 2012, 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package rpc 5 6 import ( 7 "strings" 8 9 "github.com/juju/errors" 10 ) 11 12 var ErrShutdown = errors.New("connection is shut down") 13 14 func IsShutdownErr(err error) bool { 15 return errors.Cause(err) == ErrShutdown 16 } 17 18 // Call represents an active RPC. 19 type Call struct { 20 Request 21 Params interface{} 22 Response interface{} 23 Error error 24 Done chan *Call 25 } 26 27 // RequestError represents an error returned from an RPC request. 28 type RequestError struct { 29 Message string 30 Code string 31 } 32 33 func (e *RequestError) Error() string { 34 if e.Code != "" { 35 return e.Message + " (" + e.Code + ")" 36 } 37 return e.Message 38 } 39 40 func (e *RequestError) ErrorCode() string { 41 return e.Code 42 } 43 44 func (conn *Conn) send(call *Call) { 45 conn.sending.Lock() 46 defer conn.sending.Unlock() 47 48 // Register this call. 49 conn.mutex.Lock() 50 if conn.dead == nil { 51 panic("rpc: call made when connection not started") 52 } 53 if conn.closing || conn.shutdown { 54 call.Error = ErrShutdown 55 conn.mutex.Unlock() 56 call.done() 57 return 58 } 59 conn.reqId++ 60 reqId := conn.reqId 61 conn.clientPending[reqId] = call 62 conn.mutex.Unlock() 63 64 // Encode and send the request. 65 hdr := &Header{ 66 RequestId: reqId, 67 Request: call.Request, 68 Version: 1, 69 } 70 params := call.Params 71 if params == nil { 72 params = struct{}{} 73 } 74 75 if err := conn.codec.WriteMessage(hdr, params); err != nil { 76 conn.mutex.Lock() 77 call = conn.clientPending[reqId] 78 delete(conn.clientPending, reqId) 79 conn.mutex.Unlock() 80 if call != nil { 81 call.Error = err 82 call.done() 83 } 84 } 85 } 86 87 func (conn *Conn) handleResponse(hdr *Header) error { 88 reqId := hdr.RequestId 89 conn.mutex.Lock() 90 call := conn.clientPending[reqId] 91 delete(conn.clientPending, reqId) 92 conn.mutex.Unlock() 93 94 var err error 95 switch { 96 case call == nil: 97 // We've got no pending call. That usually means that 98 // WriteHeader partially failed, and call was already 99 // removed; response is a server telling us about an 100 // error reading request body. We should still attempt 101 // to read error body, but there's no one to give it to. 102 err = conn.readBody(nil, false) 103 case hdr.Error != "": 104 // Report rpcreflect.NoSuchMethodError with CodeNotImplemented. 105 if strings.HasPrefix(hdr.Error, "no such request ") && hdr.ErrorCode == "" { 106 hdr.ErrorCode = codeNotImplemented 107 } 108 // We've got an error response. Give this to the request; 109 // any subsequent requests will get the ReadResponseBody 110 // error if there is one. 111 call.Error = &RequestError{ 112 Message: hdr.Error, 113 Code: hdr.ErrorCode, 114 } 115 err = conn.readBody(nil, false) 116 call.done() 117 default: 118 err = conn.readBody(call.Response, false) 119 call.done() 120 } 121 return errors.Annotate(err, "error handling response") 122 } 123 124 func (call *Call) done() { 125 select { 126 case call.Done <- call: 127 // ok 128 default: 129 // We don't want to block here. It is the caller's responsibility to make 130 // sure the channel has enough buffer space. See comment in Go(). 131 logger.Errorf("discarding Call reply due to insufficient Done chan capacity") 132 } 133 } 134 135 // Call invokes the named action on the object of the given type with the given 136 // id. The returned values will be stored in response, which should be a pointer. 137 // If the action fails remotely, the error will have a cause of type RequestError. 138 // The params value may be nil if no parameters are provided; the response value 139 // may be nil to indicate that any result should be discarded. 140 func (conn *Conn) Call(req Request, params, response interface{}) error { 141 call := &Call{ 142 Request: req, 143 Params: params, 144 Response: response, 145 Done: make(chan *Call, 1), 146 } 147 conn.send(call) 148 result := <-call.Done 149 return errors.Trace(result.Error) 150 }