Commit 4c56c30b authored by Rob Pike's avatar Rob Pike

rpc: don't panic on write error.

The mechanism to record the error in the call is already in place.
Fixes #2382.

R=golang-dev, dsymonds, bradfitz, r
CC=golang-dev
https://golang.org/cl/5307043
parent fc3ce349
...@@ -85,7 +85,8 @@ func (client *Client) send(c *Call) { ...@@ -85,7 +85,8 @@ func (client *Client) send(c *Call) {
client.request.Seq = c.seq client.request.Seq = c.seq
client.request.ServiceMethod = c.ServiceMethod client.request.ServiceMethod = c.ServiceMethod
if err := client.codec.WriteRequest(&client.request, c.Args); err != nil { if err := client.codec.WriteRequest(&client.request, c.Args); err != nil {
panic("rpc: client encode error: " + err.String()) c.Error = err
c.done()
} }
} }
...@@ -251,10 +252,10 @@ func (client *Client) Close() os.Error { ...@@ -251,10 +252,10 @@ func (client *Client) Close() os.Error {
// the same Call object. If done is nil, Go will allocate a new channel. // the same Call object. If done is nil, Go will allocate a new channel.
// If non-nil, done must be buffered or Go will deliberately crash. // If non-nil, done must be buffered or Go will deliberately crash.
func (client *Client) Go(serviceMethod string, args interface{}, reply interface{}, done chan *Call) *Call { func (client *Client) Go(serviceMethod string, args interface{}, reply interface{}, done chan *Call) *Call {
c := new(Call) call := new(Call)
c.ServiceMethod = serviceMethod call.ServiceMethod = serviceMethod
c.Args = args call.Args = args
c.Reply = reply call.Reply = reply
if done == nil { if done == nil {
done = make(chan *Call, 10) // buffered. done = make(chan *Call, 10) // buffered.
} else { } else {
...@@ -266,14 +267,14 @@ func (client *Client) Go(serviceMethod string, args interface{}, reply interface ...@@ -266,14 +267,14 @@ func (client *Client) Go(serviceMethod string, args interface{}, reply interface
log.Panic("rpc: done channel is unbuffered") log.Panic("rpc: done channel is unbuffered")
} }
} }
c.Done = done call.Done = done
if client.shutdown { if client.shutdown {
c.Error = ErrShutdown call.Error = ErrShutdown
c.done() call.done()
return c return call
} }
client.send(c) client.send(call)
return c return call
} }
// Call invokes the named function, waits for it to complete, and returns its error status. // Call invokes the named function, waits for it to complete, and returns its error status.
......
...@@ -467,6 +467,32 @@ func TestCountMallocsOverHTTP(t *testing.T) { ...@@ -467,6 +467,32 @@ func TestCountMallocsOverHTTP(t *testing.T) {
fmt.Printf("mallocs per HTTP rpc round trip: %d\n", countMallocs(dialHTTP, t)) fmt.Printf("mallocs per HTTP rpc round trip: %d\n", countMallocs(dialHTTP, t))
} }
type writeCrasher struct{}
func (writeCrasher) Close() os.Error {
return nil
}
func (writeCrasher) Read(p []byte) (int, os.Error) {
return 0, os.EOF
}
func (writeCrasher) Write(p []byte) (int, os.Error) {
return 0, os.NewError("fake write failure")
}
func TestClientWriteError(t *testing.T) {
c := NewClient(writeCrasher{})
res := false
err := c.Call("foo", 1, &res)
if err == nil {
t.Fatal("expected error")
}
if err.String() != "fake write failure" {
t.Error("unexpected value of error:", err)
}
}
func benchmarkEndToEnd(dial func() (*Client, os.Error), b *testing.B) { func benchmarkEndToEnd(dial func() (*Client, os.Error), b *testing.B) {
b.StopTimer() b.StopTimer()
once.Do(startServer) once.Do(startServer)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment