1
// Copyright 2012, 2013 Canonical Ltd.
2
// Licensed under the AGPLv3, see LICENCE file for details.
9
"launchpad.net/juju-core/log"
12
var ErrShutdown = errors.New("connection is shut down")
14
// Call represents an active RPC.
25
// RequestError represents an error returned from an RPC request.
26
type RequestError struct {
31
func (e *RequestError) Error() string {
32
m := "request error: " + e.Message
34
m += " (" + e.Code + ")"
39
func (e *RequestError) ErrorCode() string {
43
func (conn *Conn) send(call *Call) {
45
defer conn.sending.Unlock()
47
// Register this call.
50
panic("rpc: call made when connection not started")
52
if conn.closing || conn.shutdown {
53
call.Error = ErrShutdown
60
conn.clientPending[reqId] = call
63
// Encode and send the request.
68
Request: call.Request,
74
if err := conn.codec.WriteMessage(hdr, params); err != nil {
76
call = conn.clientPending[reqId]
77
delete(conn.clientPending, reqId)
86
func (conn *Conn) handleResponse(hdr *Header) error {
87
reqId := hdr.RequestId
89
call := conn.clientPending[reqId]
90
delete(conn.clientPending, reqId)
96
// We've got no pending call. That usually means that
97
// WriteHeader partially failed, and call was already
98
// removed; response is a server telling us about an
99
// error reading request body. We should still attempt
100
// to read error body, but there's no one to give it to.
101
err = conn.readBody(nil, false)
102
case hdr.Error != "":
103
// We've got an error response. Give this to the request;
104
// any subsequent requests will get the ReadResponseBody
105
// error if there is one.
106
call.Error = &RequestError{
110
err = conn.readBody(nil, false)
113
err = conn.readBody(call.Response, false)
119
func (call *Call) done() {
121
case call.Done <- call:
124
// We don't want to block here. It is the caller's responsibility to make
125
// sure the channel has enough buffer space. See comment in Go().
126
log.Errorf("rpc: discarding Call reply due to insufficient Done chan capacity")
130
// Call invokes the named action on the object of the given type with
131
// the given id. The returned values will be stored in response, which
132
// should be a pointer. If the action fails remotely, the returned
133
// error will be of type RequestError. The params value may be nil if
134
// no parameters are provided; the response value may be nil to indicate
135
// that any result should be discarded.
136
func (conn *Conn) Call(objType, id, action string, params, response interface{}) error {
137
call := <-conn.Go(objType, id, action, params, response, make(chan *Call, 1)).Done
141
// Go invokes the request asynchronously. It returns the Call structure representing
142
// the invocation. The done channel will signal when the call is complete by returning
143
// the same Call object. If done is nil, Go will allocate a new channel.
144
// If non-nil, done must be buffered or Go will deliberately panic.
145
func (conn *Conn) Go(objType, id, request string, args, response interface{}, done chan *Call) *Call {
147
done = make(chan *Call, 1)
149
// If caller passes done != nil, it must arrange that
150
// done has enough buffer for the number of simultaneous
151
// RPCs that will be using that channel. If the channel
152
// is totally unbuffered, it's best not to run at all.
154
panic("launchpad.net/juju-core/rpc: done channel is unbuffered")