github.com/spotify/syslog-redirector-golang@v0.0.0-20140320174030-4859f03d829a/src/pkg/net/rpc/client.go (about) 1 // Copyright 2009 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package rpc 6 7 import ( 8 "bufio" 9 "encoding/gob" 10 "errors" 11 "io" 12 "log" 13 "net" 14 "net/http" 15 "sync" 16 ) 17 18 // ServerError represents an error that has been returned from 19 // the remote side of the RPC connection. 20 type ServerError string 21 22 func (e ServerError) Error() string { 23 return string(e) 24 } 25 26 var ErrShutdown = errors.New("connection is shut down") 27 28 // Call represents an active RPC. 29 type Call struct { 30 ServiceMethod string // The name of the service and method to call. 31 Args interface{} // The argument to the function (*struct). 32 Reply interface{} // The reply from the function (*struct). 33 Error error // After completion, the error status. 34 Done chan *Call // Strobes when call is complete. 35 } 36 37 // Client represents an RPC Client. 38 // There may be multiple outstanding Calls associated 39 // with a single Client, and a Client may be used by 40 // multiple goroutines simultaneously. 41 type Client struct { 42 mutex sync.Mutex // protects pending, seq, request 43 sending sync.Mutex 44 request Request 45 seq uint64 46 codec ClientCodec 47 pending map[uint64]*Call 48 closing bool 49 shutdown bool 50 } 51 52 // A ClientCodec implements writing of RPC requests and 53 // reading of RPC responses for the client side of an RPC session. 54 // The client calls WriteRequest to write a request to the connection 55 // and calls ReadResponseHeader and ReadResponseBody in pairs 56 // to read responses. The client calls Close when finished with the 57 // connection. ReadResponseBody may be called with a nil 58 // argument to force the body of the response to be read and then 59 // discarded. 60 type ClientCodec interface { 61 // WriteRequest must be safe for concurrent use by multiple goroutines. 62 WriteRequest(*Request, interface{}) error 63 ReadResponseHeader(*Response) error 64 ReadResponseBody(interface{}) error 65 66 Close() error 67 } 68 69 func (client *Client) send(call *Call) { 70 client.sending.Lock() 71 defer client.sending.Unlock() 72 73 // Register this call. 74 client.mutex.Lock() 75 if client.shutdown || client.closing { 76 call.Error = ErrShutdown 77 client.mutex.Unlock() 78 call.done() 79 return 80 } 81 seq := client.seq 82 client.seq++ 83 client.pending[seq] = call 84 client.mutex.Unlock() 85 86 // Encode and send the request. 87 client.request.Seq = seq 88 client.request.ServiceMethod = call.ServiceMethod 89 err := client.codec.WriteRequest(&client.request, call.Args) 90 if err != nil { 91 client.mutex.Lock() 92 call = client.pending[seq] 93 delete(client.pending, seq) 94 client.mutex.Unlock() 95 if call != nil { 96 call.Error = err 97 call.done() 98 } 99 } 100 } 101 102 func (client *Client) input() { 103 var err error 104 var response Response 105 for err == nil { 106 response = Response{} 107 err = client.codec.ReadResponseHeader(&response) 108 if err != nil { 109 break 110 } 111 seq := response.Seq 112 client.mutex.Lock() 113 call := client.pending[seq] 114 delete(client.pending, seq) 115 client.mutex.Unlock() 116 117 switch { 118 case call == nil: 119 // We've got no pending call. That usually means that 120 // WriteRequest partially failed, and call was already 121 // removed; response is a server telling us about an 122 // error reading request body. We should still attempt 123 // to read error body, but there's no one to give it to. 124 err = client.codec.ReadResponseBody(nil) 125 if err != nil { 126 err = errors.New("reading error body: " + err.Error()) 127 } 128 case response.Error != "": 129 // We've got an error response. Give this to the request; 130 // any subsequent requests will get the ReadResponseBody 131 // error if there is one. 132 call.Error = ServerError(response.Error) 133 err = client.codec.ReadResponseBody(nil) 134 if err != nil { 135 err = errors.New("reading error body: " + err.Error()) 136 } 137 call.done() 138 default: 139 err = client.codec.ReadResponseBody(call.Reply) 140 if err != nil { 141 call.Error = errors.New("reading body " + err.Error()) 142 } 143 call.done() 144 } 145 } 146 // Terminate pending calls. 147 client.sending.Lock() 148 client.mutex.Lock() 149 client.shutdown = true 150 closing := client.closing 151 if err == io.EOF { 152 if closing { 153 err = ErrShutdown 154 } else { 155 err = io.ErrUnexpectedEOF 156 } 157 } 158 for _, call := range client.pending { 159 call.Error = err 160 call.done() 161 } 162 client.mutex.Unlock() 163 client.sending.Unlock() 164 if debugLog && err != io.EOF && !closing { 165 log.Println("rpc: client protocol error:", err) 166 } 167 } 168 169 func (call *Call) done() { 170 select { 171 case call.Done <- call: 172 // ok 173 default: 174 // We don't want to block here. It is the caller's responsibility to make 175 // sure the channel has enough buffer space. See comment in Go(). 176 if debugLog { 177 log.Println("rpc: discarding Call reply due to insufficient Done chan capacity") 178 } 179 } 180 } 181 182 // NewClient returns a new Client to handle requests to the 183 // set of services at the other end of the connection. 184 // It adds a buffer to the write side of the connection so 185 // the header and payload are sent as a unit. 186 func NewClient(conn io.ReadWriteCloser) *Client { 187 encBuf := bufio.NewWriter(conn) 188 client := &gobClientCodec{conn, gob.NewDecoder(conn), gob.NewEncoder(encBuf), encBuf} 189 return NewClientWithCodec(client) 190 } 191 192 // NewClientWithCodec is like NewClient but uses the specified 193 // codec to encode requests and decode responses. 194 func NewClientWithCodec(codec ClientCodec) *Client { 195 client := &Client{ 196 codec: codec, 197 pending: make(map[uint64]*Call), 198 } 199 go client.input() 200 return client 201 } 202 203 type gobClientCodec struct { 204 rwc io.ReadWriteCloser 205 dec *gob.Decoder 206 enc *gob.Encoder 207 encBuf *bufio.Writer 208 } 209 210 func (c *gobClientCodec) WriteRequest(r *Request, body interface{}) (err error) { 211 if err = c.enc.Encode(r); err != nil { 212 return 213 } 214 if err = c.enc.Encode(body); err != nil { 215 return 216 } 217 return c.encBuf.Flush() 218 } 219 220 func (c *gobClientCodec) ReadResponseHeader(r *Response) error { 221 return c.dec.Decode(r) 222 } 223 224 func (c *gobClientCodec) ReadResponseBody(body interface{}) error { 225 return c.dec.Decode(body) 226 } 227 228 func (c *gobClientCodec) Close() error { 229 return c.rwc.Close() 230 } 231 232 // DialHTTP connects to an HTTP RPC server at the specified network address 233 // listening on the default HTTP RPC path. 234 func DialHTTP(network, address string) (*Client, error) { 235 return DialHTTPPath(network, address, DefaultRPCPath) 236 } 237 238 // DialHTTPPath connects to an HTTP RPC server 239 // at the specified network address and path. 240 func DialHTTPPath(network, address, path string) (*Client, error) { 241 var err error 242 conn, err := net.Dial(network, address) 243 if err != nil { 244 return nil, err 245 } 246 io.WriteString(conn, "CONNECT "+path+" HTTP/1.0\n\n") 247 248 // Require successful HTTP response 249 // before switching to RPC protocol. 250 resp, err := http.ReadResponse(bufio.NewReader(conn), &http.Request{Method: "CONNECT"}) 251 if err == nil && resp.Status == connected { 252 return NewClient(conn), nil 253 } 254 if err == nil { 255 err = errors.New("unexpected HTTP response: " + resp.Status) 256 } 257 conn.Close() 258 return nil, &net.OpError{ 259 Op: "dial-http", 260 Net: network + " " + address, 261 Addr: nil, 262 Err: err, 263 } 264 } 265 266 // Dial connects to an RPC server at the specified network address. 267 func Dial(network, address string) (*Client, error) { 268 conn, err := net.Dial(network, address) 269 if err != nil { 270 return nil, err 271 } 272 return NewClient(conn), nil 273 } 274 275 func (client *Client) Close() error { 276 client.mutex.Lock() 277 if client.shutdown || client.closing { 278 client.mutex.Unlock() 279 return ErrShutdown 280 } 281 client.closing = true 282 client.mutex.Unlock() 283 return client.codec.Close() 284 } 285 286 // Go invokes the function asynchronously. It returns the Call structure representing 287 // the invocation. The done channel will signal when the call is complete by returning 288 // the same Call object. If done is nil, Go will allocate a new channel. 289 // If non-nil, done must be buffered or Go will deliberately crash. 290 func (client *Client) Go(serviceMethod string, args interface{}, reply interface{}, done chan *Call) *Call { 291 call := new(Call) 292 call.ServiceMethod = serviceMethod 293 call.Args = args 294 call.Reply = reply 295 if done == nil { 296 done = make(chan *Call, 10) // buffered. 297 } else { 298 // If caller passes done != nil, it must arrange that 299 // done has enough buffer for the number of simultaneous 300 // RPCs that will be using that channel. If the channel 301 // is totally unbuffered, it's best not to run at all. 302 if cap(done) == 0 { 303 log.Panic("rpc: done channel is unbuffered") 304 } 305 } 306 call.Done = done 307 client.send(call) 308 return call 309 } 310 311 // Call invokes the named function, waits for it to complete, and returns its error status. 312 func (client *Client) Call(serviceMethod string, args interface{}, reply interface{}) error { 313 call := <-client.Go(serviceMethod, args, reply, make(chan *Call, 1)).Done 314 return call.Error 315 }