MCPcopy
hub / github.com/grpc/grpc-go / handleGoAway

Method handleGoAway

internal/transport/http2_client.go:1361–1436  ·  view source on GitHub ↗
(f *http2.GoAwayFrame)

Source from the content-addressed store, hash-verified

1359}
1360
1361func (t *http2Client) handleGoAway(f *http2.GoAwayFrame) error {
1362 t.mu.Lock()
1363 if t.state == closing {
1364 t.mu.Unlock()
1365 return nil
1366 }
1367 if f.ErrCode == http2.ErrCodeEnhanceYourCalm && string(f.DebugData()) == "too_many_pings" {
1368 // When a client receives a GOAWAY with error code ENHANCE_YOUR_CALM and debug
1369 // data equal to ASCII "too_many_pings", it should log the occurrence at a log level that is
1370 // enabled by default and double the configure KEEPALIVE_TIME used for new connections
1371 // on that channel.
1372 logger.Errorf("Client received GoAway with error code ENHANCE_YOUR_CALM and debug data equal to ASCII \"too_many_pings\".")
1373 }
1374 id := f.LastStreamID
1375 if id > 0 && id%2 == 0 {
1376 t.mu.Unlock()
1377 return connectionErrorf(true, nil, "received goaway with non-zero even-numbered stream id: %v", id)
1378 }
1379 // A client can receive multiple GoAways from the server (see
1380 // https://github.com/grpc/grpc-go/issues/1387). The idea is that the first
1381 // GoAway will be sent with an ID of MaxInt32 and the second GoAway will be
1382 // sent after an RTT delay with the ID of the last stream the server will
1383 // process.
1384 //
1385 // Therefore, when we get the first GoAway we don't necessarily close any
1386 // streams. While in case of second GoAway we close all streams created after
1387 // the GoAwayId. This way streams that were in-flight while the GoAway from
1388 // server was being sent don't get killed.
1389 select {
1390 case <-t.goAway: // t.goAway has been closed (i.e.,multiple GoAways).
1391 // If there are multiple GoAways the first one should always have an ID greater than the following ones.
1392 if id > t.prevGoAwayID {
1393 t.mu.Unlock()
1394 return connectionErrorf(true, nil, "received goaway with stream id: %v, which exceeds stream id of previous goaway: %v", id, t.prevGoAwayID)
1395 }
1396 default:
1397 t.setGoAwayReason(f)
1398 close(t.goAway)
1399 defer t.controlBuf.put(&incomingGoAway{}) // Defer as t.mu is currently held.
1400 // Notify the clientconn about the GOAWAY before we set the state to
1401 // draining, to allow the client to stop attempting to create streams
1402 // before disallowing new streams on this connection.
1403 if t.state != draining {
1404 t.onClose(GoAwayInfo{Reason: t.goAwayReason, GoAwayCode: t.goAwayCode})
1405 t.state = draining
1406 }
1407 }
1408 // All streams with IDs greater than the GoAwayId
1409 // and smaller than the previous GoAway ID should be killed.
1410 upperLimit := t.prevGoAwayID
1411 if upperLimit == 0 { // This is the first GoAway Frame.
1412 upperLimit = math.MaxUint32 // Kill all streams after the GoAway ID.
1413 }
1414
1415 t.prevGoAwayID = id
1416 if len(t.activeStreams) == 0 {
1417 t.mu.Unlock()
1418 return connectionErrorf(true, nil, "received goaway and there are no active streams")

Callers 1

readerMethod · 0.95

Calls 7

setGoAwayReasonMethod · 0.95
closeStreamMethod · 0.95
connectionErrorfFunction · 0.85
ErrorfMethod · 0.65
LockMethod · 0.45
UnlockMethod · 0.45
putMethod · 0.45

Tested by

no test coverage detected