Skip to content

Commit fe8890e

Browse files
committed
fix: fix the issue which the server-side did not report failure after a panic occurred during request processing
1 parent 11073fe commit fe8890e

File tree

3 files changed

+72
-13
lines changed

3 files changed

+72
-13
lines changed

pkg/remote/trans/default_server_handler.go

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,14 @@ func (t *svrTransHandler) Write(ctx context.Context, conn net.Conn, sendMsg remo
8989
func (t *svrTransHandler) Read(ctx context.Context, conn net.Conn, recvMsg remote.Message) (nctx context.Context, err error) {
9090
var bufReader remote.ByteBuffer
9191
defer func() {
92+
if r := recover(); r != nil {
93+
stack := string(debug.Stack())
94+
panicErr := kerrors.ErrPanic.WithCauseAndStack(fmt.Errorf("[happened in Read] %s", r), stack)
95+
rpcStats := rpcinfo.AsMutableRPCStats(recvMsg.RPCInfo().Stats())
96+
rpcStats.SetPanicked(panicErr)
97+
err = remote.NewTransError(remote.ProtocolError, panicErr)
98+
nctx = ctx
99+
}
92100
t.ext.ReleaseBuffer(bufReader, err)
93101
rpcinfo.Record(ctx, recvMsg.RPCInfo(), stats.ReadFinish, err)
94102
}()
@@ -133,9 +141,8 @@ func (t *svrTransHandler) OnRead(ctx context.Context, conn net.Conn) (err error)
133141
var sendMsg remote.Message
134142
closeConnOutsideIfErr := true
135143
defer func() {
136-
panicErr := recover()
137-
var wrapErr error
138-
if panicErr != nil {
144+
var panicErr error
145+
if r := recover(); r != nil {
139146
stack := string(debug.Stack())
140147
if conn != nil {
141148
ri := rpcinfo.GetRPCInfo(ctx)
@@ -144,10 +151,9 @@ func (t *svrTransHandler) OnRead(ctx context.Context, conn net.Conn) (err error)
144151
} else {
145152
klog.CtxErrorf(ctx, "KITEX: panic happened, error=%v\nstack=%s", panicErr, stack)
146153
}
147-
if err != nil {
148-
wrapErr = kerrors.ErrPanic.WithCauseAndStack(fmt.Errorf("[happened in OnRead] %s, last error=%s", panicErr, err.Error()), stack)
149-
} else {
150-
wrapErr = kerrors.ErrPanic.WithCauseAndStack(fmt.Errorf("[happened in OnRead] %s", panicErr), stack)
154+
panicErr = kerrors.ErrPanic.WithCauseAndStack(fmt.Errorf("[happened in OnRead] %s", panicErr), stack)
155+
if err == nil {
156+
err = panicErr
151157
}
152158
}
153159
t.finishTracer(ctx, ri, err, panicErr)
@@ -158,10 +164,9 @@ func (t *svrTransHandler) OnRead(ctx context.Context, conn net.Conn) (err error)
158164
if rpcinfo.PoolEnabled() {
159165
t.opt.InitOrResetRPCInfoFunc(ri, conn.RemoteAddr())
160166
}
161-
if wrapErr != nil {
162-
err = wrapErr
163-
}
164167
if err != nil && !closeConnOutsideIfErr {
168+
// when error is not nil, outside will close conn,
169+
// set err to nil to indicate that this kind of error does not require closing the connection
165170
err = nil
166171
}
167172
}()
@@ -186,7 +191,7 @@ func (t *svrTransHandler) OnRead(ctx context.Context, conn net.Conn) (err error)
186191
// reply processing
187192
var methodInfo serviceinfo.MethodInfo
188193
if methodInfo, err = GetMethodInfo(ri, svcInfo); err != nil {
189-
// it won't be err, because the method has been checked in decode, err check here just do defensive inspection
194+
// it won't be error, because the method has been checked in decode, err check here just do defensive inspection
190195
t.writeErrorReplyIfNeeded(ctx, recvMsg, conn, err, ri, true)
191196
// for proxy case, need read actual remoteAddr, error print must exec after writeErrorReplyIfNeeded,
192197
// t.OnError(ctx, err, conn) will be executed at outer function when transServer close the conn

pkg/remote/trans/default_server_handler_test.go

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"context"
2121
"errors"
2222
"net"
23+
"strings"
2324
"testing"
2425

2526
"github.com/golang/mock/gomock"
@@ -211,6 +212,59 @@ func TestSvrTransHandlerReadErr(t *testing.T) {
211212
test.Assert(t, errors.Is(err, mockErr))
212213
}
213214

215+
func TestSvrTransHandlerReadPanic(t *testing.T) {
216+
ctrl := gomock.NewController(t)
217+
defer ctrl.Finish()
218+
219+
mockTracer := stats.NewMockTracer(ctrl)
220+
mockTracer.EXPECT().Start(gomock.Any()).DoAndReturn(func(ctx context.Context) context.Context { return ctx }).AnyTimes()
221+
mockTracer.EXPECT().Finish(gomock.Any()).DoAndReturn(func(ctx context.Context) {
222+
err := rpcinfo.GetRPCInfo(ctx).Stats().Error()
223+
test.Assert(t, err != nil)
224+
}).AnyTimes()
225+
226+
buf := remote.NewReaderWriterBuffer(1024)
227+
ext := &MockExtension{
228+
NewWriteByteBufferFunc: func(ctx context.Context, conn net.Conn, msg remote.Message) remote.ByteBuffer {
229+
return buf
230+
},
231+
NewReadByteBufferFunc: func(ctx context.Context, conn net.Conn, msg remote.Message) remote.ByteBuffer {
232+
return buf
233+
},
234+
}
235+
236+
tracerCtl := &rpcinfo.TraceController{}
237+
tracerCtl.Append(mockTracer)
238+
opt := &remote.ServerOption{
239+
Codec: &MockCodec{
240+
EncodeFunc: func(ctx context.Context, msg remote.Message, out remote.ByteBuffer) error {
241+
return nil
242+
},
243+
DecodeFunc: func(ctx context.Context, msg remote.Message, in remote.ByteBuffer) error {
244+
panic("mock")
245+
},
246+
},
247+
SvcSearcher: svcSearcher,
248+
TargetSvcInfo: svcInfo,
249+
TracerCtl: tracerCtl,
250+
InitOrResetRPCInfoFunc: func(ri rpcinfo.RPCInfo, addr net.Addr) rpcinfo.RPCInfo {
251+
rpcinfo.AsMutableEndpointInfo(ri.From()).SetAddress(addr)
252+
return ri
253+
},
254+
}
255+
ri := rpcinfo.NewRPCInfo(rpcinfo.EmptyEndpointInfo(), rpcinfo.FromBasicInfo(&rpcinfo.EndpointBasicInfo{}),
256+
rpcinfo.NewInvocation("", ""), nil, rpcinfo.NewRPCStats())
257+
ctx := rpcinfo.NewCtxWithRPCInfo(context.Background(), ri)
258+
259+
svrHandler, err := NewDefaultSvrTransHandler(opt, ext)
260+
test.Assert(t, err == nil)
261+
pl := remote.NewTransPipeline(svrHandler)
262+
svrHandler.SetPipeline(pl)
263+
err = svrHandler.OnRead(ctx, &mocks.Conn{})
264+
test.Assert(t, err != nil)
265+
test.Assert(t, strings.Contains(err.Error(), "panic"))
266+
}
267+
214268
func TestSvrTransHandlerOnReadHeartbeat(t *testing.T) {
215269
ctrl := gomock.NewController(t)
216270
defer ctrl.Finish()

pkg/remote/trans/netpoll/server_handler_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -172,8 +172,8 @@ func TestInvokeErr(t *testing.T) {
172172
test.Assert(t, isInvoked)
173173
}
174174

175-
// TestPanicAfterRead test server_handler not panic after read
176-
func TestPanicAfterRead(t *testing.T) {
175+
// TestPipelineNilPanic test server_handler that TransPipeline is nil
176+
func TestPipelineNilPanic(t *testing.T) {
177177
// 1. prepare mock data
178178
var isWriteBufFlushed bool
179179
var isReaderBufReleased bool

0 commit comments

Comments
 (0)