github.com/cloudwego/kitex@v0.9.0/pkg/remote/trans/nphttp2/grpc/http2_client.go (about) 1 /* 2 * 3 * Copyright 2014 gRPC authors. 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 * 17 * This file may have been modified by CloudWeGo authors. All CloudWeGo 18 * Modifications are Copyright 2021 CloudWeGo Authors. 19 */ 20 21 package grpc 22 23 import ( 24 "context" 25 "io" 26 "math" 27 "net" 28 "runtime/debug" 29 "strconv" 30 "strings" 31 "sync" 32 "sync/atomic" 33 "time" 34 35 "github.com/cloudwego/netpoll" 36 "golang.org/x/net/http2" 37 "golang.org/x/net/http2/hpack" 38 39 "github.com/cloudwego/kitex/pkg/gofunc" 40 "github.com/cloudwego/kitex/pkg/klog" 41 "github.com/cloudwego/kitex/pkg/remote/trans/nphttp2/codes" 42 "github.com/cloudwego/kitex/pkg/remote/trans/nphttp2/grpc/grpcframe" 43 "github.com/cloudwego/kitex/pkg/remote/trans/nphttp2/grpc/syscall" 44 "github.com/cloudwego/kitex/pkg/remote/trans/nphttp2/metadata" 45 "github.com/cloudwego/kitex/pkg/remote/trans/nphttp2/peer" 46 "github.com/cloudwego/kitex/pkg/remote/trans/nphttp2/status" 47 ) 48 49 // http2Client implements the ClientTransport interface with HTTP2. 50 type http2Client struct { 51 lastRead int64 // Keep this field 64-bit aligned. Accessed atomically. 52 ctx context.Context 53 cancel context.CancelFunc 54 conn net.Conn // underlying communication channel 55 loopy *loopyWriter 56 remoteAddr net.Addr 57 localAddr net.Addr 58 scheme string 59 60 readerDone chan struct{} // sync point to enable testing. 61 writerDone chan struct{} // sync point to enable testing. 62 // goAway is closed to notify the upper layer (i.e., addrConn.transportMonitor) 63 // that the server sent GoAway on this transport. 64 goAway chan struct{} 65 66 framer *framer 67 // controlBuf delivers all the control related tasks (e.g., window 68 // updates, reset streams, and various settings) to the controller. 69 // Do not access controlBuf with mu held. 70 controlBuf *controlBuffer 71 fc *trInFlow 72 73 kp ClientKeepalive 74 keepaliveEnabled bool 75 76 initialWindowSize uint32 77 78 // configured by peer through SETTINGS_MAX_HEADER_LIST_SIZE 79 maxSendHeaderListSize *uint32 80 81 bdpEst *bdpEstimator 82 83 maxConcurrentStreams uint32 84 streamQuota int64 85 streamsQuotaAvailable chan struct{} 86 waitingStreams uint32 87 nextID uint32 88 89 // Do not access controlBuf with mu held. 90 mu sync.Mutex // guard the following variables 91 state transportState 92 activeStreams map[uint32]*Stream 93 // prevGoAway ID records the Last-Stream-ID in the previous GOAway frame. 94 prevGoAwayID uint32 95 96 goAwayReason GoAwayReason 97 // A condition variable used to signal when the keepalive goroutine should 98 // go dormant. The condition for dormancy is based on the number of active 99 // streams and the `PermitWithoutStream` keepalive client parameter. And 100 // since the number of active streams is guarded by the above mutex, we use 101 // the same for this condition variable as well. 102 kpDormancyCond *sync.Cond 103 // A boolean to track whether the keepalive goroutine is dormant or not. 104 // This is checked before attempting to signal the above condition 105 // variable. 106 kpDormant bool 107 onGoAway func(GoAwayReason) 108 onClose func() 109 110 bufferPool *bufferPool 111 } 112 113 // newHTTP2Client constructs a connected ClientTransport to addr based on HTTP2 114 // and starts to receive messages on it. Non-nil error returns if construction 115 // fails. 116 func newHTTP2Client(ctx context.Context, conn net.Conn, opts ConnectOptions, 117 remoteService string, onGoAway func(GoAwayReason), onClose func(), 118 ) (_ *http2Client, err error) { 119 scheme := "http" 120 if opts.TLSConfig != nil { 121 scheme = "https" 122 } 123 ctx, cancel := context.WithCancel(ctx) 124 defer func() { 125 if err != nil { 126 cancel() 127 } 128 }() 129 130 kp := opts.KeepaliveParams 131 // Validate keepalive parameters. 132 if kp.Time == 0 { 133 kp.Time = defaultClientKeepaliveTime 134 } 135 if kp.Timeout == 0 { 136 kp.Timeout = defaultClientKeepaliveTimeout 137 } 138 keepaliveEnabled := false 139 if kp.Time != Infinity { 140 if err = syscall.SetTCPUserTimeout(conn, kp.Timeout); err != nil { 141 return nil, connectionErrorf(false, err, "transport: failed to set TCP_USER_TIMEOUT: %v", err) 142 } 143 keepaliveEnabled = true 144 } 145 146 dynamicWindow := true 147 icwz := initialWindowSize 148 if opts.InitialConnWindowSize >= defaultWindowSize { 149 icwz = opts.InitialConnWindowSize 150 dynamicWindow = false 151 } 152 153 writeBufSize := defaultWriteBufferSize 154 readBufSize := defaultReadBufferSize 155 if opts.WriteBufferSize > 0 { 156 writeBufSize = opts.WriteBufferSize 157 } 158 if opts.ReadBufferSize > 0 { 159 readBufSize = opts.ReadBufferSize 160 } 161 maxHeaderListSize := defaultClientMaxHeaderListSize 162 if opts.MaxHeaderListSize != nil { 163 maxHeaderListSize = *opts.MaxHeaderListSize 164 } 165 t := &http2Client{ 166 ctx: ctx, 167 conn: conn, 168 cancel: cancel, 169 remoteAddr: conn.RemoteAddr(), 170 localAddr: conn.LocalAddr(), 171 scheme: scheme, 172 readerDone: make(chan struct{}), 173 writerDone: make(chan struct{}), 174 goAway: make(chan struct{}), 175 framer: newFramer(conn, writeBufSize, readBufSize, maxHeaderListSize), 176 fc: &trInFlow{limit: icwz}, 177 activeStreams: make(map[uint32]*Stream), 178 kp: kp, 179 keepaliveEnabled: keepaliveEnabled, 180 initialWindowSize: initialWindowSize, 181 nextID: 1, 182 maxConcurrentStreams: defaultMaxStreamsClient, 183 streamQuota: defaultMaxStreamsClient, 184 streamsQuotaAvailable: make(chan struct{}, 1), 185 onGoAway: onGoAway, 186 onClose: onClose, 187 bufferPool: newBufferPool(), 188 } 189 t.controlBuf = newControlBuffer(t.ctx.Done()) 190 if opts.InitialWindowSize >= defaultWindowSize { 191 t.initialWindowSize = opts.InitialWindowSize 192 dynamicWindow = false 193 } 194 if dynamicWindow { 195 t.bdpEst = &bdpEstimator{ 196 bdp: initialWindowSize, 197 updateFlowControl: t.updateFlowControl, 198 } 199 } 200 t.loopy = newLoopyWriter(clientSide, t.framer, t.controlBuf, t.bdpEst) 201 202 // Start the reader goroutine for incoming message. Each transport has 203 // a dedicated goroutine which reads HTTP2 frame from network. Then it 204 // dispatches the frame to the corresponding stream entity. 205 if npconn, ok := t.conn.(netpoll.Connection); ok { 206 npconn.SetOnRequest(func(ctx context.Context, connection netpoll.Connection) error { 207 t.reader() 208 return nil 209 }) 210 } else { 211 gofunc.RecoverGoFuncWithInfo(ctx, t.reader, gofunc.NewBasicInfo(remoteService, conn.RemoteAddr().String())) 212 } 213 214 if t.keepaliveEnabled { 215 t.kpDormancyCond = sync.NewCond(&t.mu) 216 gofunc.RecoverGoFuncWithInfo(ctx, t.keepalive, gofunc.NewBasicInfo(remoteService, conn.RemoteAddr().String())) 217 } 218 219 // Send connection preface to server. 220 n, err := t.conn.Write(ClientPreface) 221 if err != nil { 222 t.Close() 223 return nil, connectionErrorf(true, err, "transport: failed to write client preface: %v", err) 224 } 225 if n != ClientPrefaceLen { 226 t.Close() 227 return nil, connectionErrorf(true, err, "transport: preface mismatch, wrote %d bytes; want %d", n, ClientPrefaceLen) 228 } 229 230 ss := []http2.Setting{ 231 { 232 ID: http2.SettingInitialWindowSize, 233 Val: uint32(t.initialWindowSize), 234 }, 235 } 236 err = t.framer.WriteSettings(ss...) 237 if err != nil { 238 t.Close() 239 return nil, connectionErrorf(true, err, "transport: failed to write initial settings frame: %v", err) 240 } 241 242 // Adjust the connection flow control window if needed. 243 if delta := uint32(icwz - defaultWindowSize); delta > 0 { 244 if err := t.framer.WriteWindowUpdate(0, delta); err != nil { 245 t.Close() 246 return nil, connectionErrorf(true, err, "transport: failed to write window update: %v", err) 247 } 248 } 249 250 if err := t.framer.writer.Flush(); err != nil { 251 return nil, err 252 } 253 gofunc.RecoverGoFuncWithInfo(ctx, func() { 254 err := t.loopy.run(conn.RemoteAddr().String()) 255 if err != nil { 256 klog.CtxErrorf(ctx, "KITEX: grpc client loopyWriter.run returning, error=%v", err) 257 } 258 // If it's a connection error, let reader goroutine handle it 259 // since there might be data in the buffers. 260 if _, ok := err.(net.Error); !ok { 261 t.conn.Close() 262 } 263 close(t.writerDone) 264 }, gofunc.NewBasicInfo(remoteService, conn.RemoteAddr().String())) 265 266 return t, nil 267 } 268 269 type preAllocatedStreamFields struct { 270 recvBuffer *recvBuffer 271 writeQuota *writeQuota 272 } 273 274 var ( 275 preallocateChan = make(chan preAllocatedStreamFields, 256) 276 preallocateInit sync.Once 277 ) 278 279 func fillStreamFields(s *Stream) { 280 preallocateInit.Do(func() { 281 go preallocateForStream() 282 }) 283 var fields preAllocatedStreamFields 284 select { 285 case fields = <-preallocateChan: 286 default: // won't block even the producer is not fast enough 287 fields = allocateStreamFields() 288 } 289 s.buf = fields.recvBuffer 290 s.wq = fields.writeQuota 291 s.wq.done = s.done 292 } 293 294 func preallocateForStream() { 295 defer func() { 296 if err := recover(); err != nil { 297 klog.Warnf("grpc.preallocateForStream panic, error=%v, stack=%s", err, debug.Stack()) 298 } 299 }() 300 for { 301 preallocateChan <- allocateStreamFields() 302 } 303 } 304 305 func allocateStreamFields() preAllocatedStreamFields { 306 return preAllocatedStreamFields{ 307 recvBuffer: newRecvBuffer(), 308 writeQuota: newWriteQuota(defaultWriteQuota, nil), 309 } 310 } 311 312 func (t *http2Client) newStream(ctx context.Context, callHdr *CallHdr) *Stream { 313 s := &Stream{ 314 ctx: ctx, 315 ct: t, 316 done: make(chan struct{}), 317 method: callHdr.Method, 318 sendCompress: callHdr.SendCompress, 319 headerChan: make(chan struct{}), 320 contentSubtype: callHdr.ContentSubtype, 321 } 322 fillStreamFields(s) 323 s.requestRead = func(n int) { 324 t.adjustWindow(s, uint32(n)) 325 } 326 s.trReader = &transportReader{ 327 reader: &recvBufferReader{ 328 ctx: s.ctx, 329 ctxDone: s.ctx.Done(), 330 recv: s.buf, 331 closeStream: func(err error) { 332 t.CloseStream(s, err) 333 }, 334 freeBuffer: t.bufferPool.put, 335 }, 336 windowHandler: func(n int) { 337 t.updateWindow(s, uint32(n)) 338 }, 339 } 340 return s 341 } 342 343 // TODO: mesh headers 344 func (t *http2Client) createHeaderFields(ctx context.Context, callHdr *CallHdr) []hpack.HeaderField { 345 hfLen := 7 // :method, :scheme, :path, :authority, content-type, user-agent, te 346 headerFields := make([]hpack.HeaderField, 0, hfLen) 347 headerFields = append(headerFields, hpack.HeaderField{Name: ":method", Value: "POST"}) 348 headerFields = append(headerFields, hpack.HeaderField{Name: ":scheme", Value: t.scheme}) 349 headerFields = append(headerFields, hpack.HeaderField{Name: ":path", Value: callHdr.Method}) 350 headerFields = append(headerFields, hpack.HeaderField{Name: ":authority", Value: callHdr.Host}) 351 headerFields = append(headerFields, hpack.HeaderField{Name: "content-type", Value: contentType(callHdr.ContentSubtype)}) 352 headerFields = append(headerFields, hpack.HeaderField{Name: "user-agent", Value: defaultUserAgent}) 353 headerFields = append(headerFields, hpack.HeaderField{Name: "te", Value: "trailers"}) 354 if callHdr.PreviousAttempts > 0 { 355 headerFields = append(headerFields, hpack.HeaderField{Name: "grpc-previous-rpc-attempts", Value: strconv.Itoa(callHdr.PreviousAttempts)}) 356 } 357 358 if callHdr.SendCompress != "" { 359 headerFields = append(headerFields, hpack.HeaderField{Name: "grpc-encoding", Value: callHdr.SendCompress}) 360 headerFields = append(headerFields, hpack.HeaderField{Name: "grpc-accept-encoding", Value: callHdr.SendCompress}) 361 } 362 if dl, ok := ctx.Deadline(); ok { 363 // Send out timeout regardless its value. The server can detect timeout context by itself. 364 // TODO(mmukhi): Perhaps this field should be updated when actually writing out to the wire. 365 timeout := time.Until(dl) 366 headerFields = append(headerFields, hpack.HeaderField{Name: "grpc-timeout", Value: encodeTimeout(timeout)}) 367 } 368 if md, added, ok := metadata.FromOutgoingContextRaw(ctx); ok { 369 var k string 370 for k, vv := range md { 371 // HTTP doesn't allow you to set pseudoheaders after non pseudoheaders were set. 372 if isReservedHeader(k) { 373 continue 374 } 375 for _, v := range vv { 376 headerFields = append(headerFields, hpack.HeaderField{Name: k, Value: encodeMetadataHeader(k, v)}) 377 } 378 } 379 for _, vv := range added { 380 for i, v := range vv { 381 if i%2 == 0 { 382 k = v 383 continue 384 } 385 // HTTP doesn't allow you to set pseudoheaders after non pseudoheaders were set. 386 if isReservedHeader(k) { 387 continue 388 } 389 headerFields = append(headerFields, hpack.HeaderField{Name: strings.ToLower(k), Value: encodeMetadataHeader(k, v)}) 390 } 391 } 392 } 393 return headerFields 394 } 395 396 func (t *http2Client) setPeer(ctx context.Context) { 397 peer, ok := peer.GetPeerFromContext(ctx) 398 if ok { 399 peer.Addr = t.remoteAddr 400 } 401 } 402 403 // NewStream creates a stream and registers it into the transport as "active" 404 // streams. 405 func (t *http2Client) NewStream(ctx context.Context, callHdr *CallHdr) (_ *Stream, err error) { 406 t.setPeer(ctx) 407 s := t.newStream(ctx, callHdr) 408 cleanup := func(err error) { 409 if s.swapState(streamDone) == streamDone { 410 // If it was already done, return. 411 return 412 } 413 // The stream was unprocessed by the server. 414 atomic.StoreUint32(&s.unprocessed, 1) 415 s.write(recvMsg{err: err}) 416 close(s.done) 417 // If headerChan isn't closed, then close it. 418 if atomic.CompareAndSwapUint32(&s.headerChanClosed, 0, 1) { 419 close(s.headerChan) 420 } 421 } 422 hdr := &headerFrame{ 423 hf: t.createHeaderFields(ctx, callHdr), 424 endStream: false, 425 initStream: func(id uint32) error { 426 t.mu.Lock() 427 if state := t.state; state != reachable { 428 t.mu.Unlock() 429 // Do a quick cleanup. 430 err := error(errStreamDrain) 431 if state == closing { 432 err = ErrConnClosing 433 } 434 cleanup(err) 435 return err 436 } 437 // If the keepalive goroutine has gone dormant, wake it up. 438 if t.kpDormant { 439 t.kpDormancyCond.Signal() 440 } 441 t.mu.Unlock() 442 return nil 443 }, 444 onOrphaned: cleanup, 445 wq: s.wq, 446 } 447 firstTry := true 448 var ch chan struct{} 449 checkForStreamQuota := func(it interface{}) bool { 450 if t.streamQuota <= 0 { // Can go negative if server decreases it. 451 if firstTry { 452 t.waitingStreams++ 453 } 454 ch = t.streamsQuotaAvailable 455 return false 456 } 457 if !firstTry { 458 t.waitingStreams-- 459 } 460 t.streamQuota-- 461 h := it.(*headerFrame) 462 h.streamID = t.nextID 463 t.nextID += 2 464 s.id = h.streamID 465 s.fc = &inFlow{limit: uint32(t.initialWindowSize)} 466 t.mu.Lock() 467 if t.activeStreams == nil { // Can be niled from Close(). 468 t.mu.Unlock() 469 return false // Don't create a stream if the transport is already closed. 470 } 471 t.activeStreams[s.id] = s 472 t.mu.Unlock() 473 if t.streamQuota > 0 && t.waitingStreams > 0 { 474 select { 475 case t.streamsQuotaAvailable <- struct{}{}: 476 default: 477 } 478 } 479 return true 480 } 481 var hdrListSizeErr error 482 checkForHeaderListSize := func(it interface{}) bool { 483 if t.maxSendHeaderListSize == nil { 484 return true 485 } 486 hdrFrame := it.(*headerFrame) 487 var sz int64 488 for _, f := range hdrFrame.hf { 489 if sz += int64(f.Size()); sz > int64(*t.maxSendHeaderListSize) { 490 hdrListSizeErr = status.Errorf(codes.Internal, "header list size to send violates the maximum size (%d bytes) set by server", *t.maxSendHeaderListSize) 491 return false 492 } 493 } 494 return true 495 } 496 for { 497 success, err := t.controlBuf.executeAndPut(func(it interface{}) bool { 498 return checkForHeaderListSize(it) && checkForStreamQuota(it) 499 }, hdr) 500 if err != nil { 501 return nil, err 502 } 503 if success { 504 break 505 } 506 if hdrListSizeErr != nil { 507 return nil, hdrListSizeErr 508 } 509 firstTry = false 510 select { 511 case <-ch: 512 case <-s.ctx.Done(): 513 return nil, ContextErr(s.ctx.Err()) 514 case <-t.goAway: 515 return nil, errStreamDrain 516 case <-t.ctx.Done(): 517 return nil, ErrConnClosing 518 } 519 } 520 return s, nil 521 } 522 523 // CloseStream clears the footprint of a stream when the stream is not needed any more. 524 // This must not be executed in reader's goroutine. 525 func (t *http2Client) CloseStream(s *Stream, err error) { 526 var ( 527 rst bool 528 rstCode http2.ErrCode 529 ) 530 if err != nil { 531 rst = true 532 rstCode = http2.ErrCodeCancel 533 } 534 t.closeStream(s, err, rst, rstCode, status.Convert(err), nil, false) 535 } 536 537 func (t *http2Client) closeStream(s *Stream, err error, rst bool, rstCode http2.ErrCode, st *status.Status, mdata map[string][]string, eosReceived bool) { 538 // Set stream status to done. 539 if s.swapState(streamDone) == streamDone { 540 // If it was already done, return. If multiple closeStream calls 541 // happen simultaneously, wait for the first to finish. 542 <-s.done 543 return 544 } 545 // status and trailers can be updated here without any synchronization because the stream goroutine will 546 // only read it after it sees an io.EOF error from read or write and we'll write those errors 547 // only after updating this. 548 s.status = st 549 if len(mdata) > 0 { 550 s.trailer = mdata 551 } 552 if err != nil { 553 // This will unblock reads eventually. 554 s.write(recvMsg{err: err}) 555 } 556 // If headerChan isn't closed, then close it. 557 if atomic.CompareAndSwapUint32(&s.headerChanClosed, 0, 1) { 558 s.noHeaders = true 559 close(s.headerChan) 560 } 561 cleanup := &cleanupStream{ 562 streamID: s.id, 563 onWrite: func() { 564 t.mu.Lock() 565 if t.activeStreams != nil { 566 delete(t.activeStreams, s.id) 567 } 568 t.mu.Unlock() 569 }, 570 rst: rst, 571 rstCode: rstCode, 572 } 573 addBackStreamQuota := func(interface{}) bool { 574 t.streamQuota++ 575 if t.streamQuota > 0 && t.waitingStreams > 0 { 576 select { 577 case t.streamsQuotaAvailable <- struct{}{}: 578 default: 579 } 580 } 581 return true 582 } 583 t.controlBuf.executeAndPut(addBackStreamQuota, cleanup) 584 // This will unblock write. 585 close(s.done) 586 } 587 588 // Close kicks off the shutdown process of the transport. This should be called 589 // only once on a transport. Once it is called, the transport should not be 590 // accessed any more. 591 // 592 // This method blocks until the addrConn that initiated this transport is 593 // re-connected. This happens because t.onClose() begins reconnect logic at the 594 // addrConn level and blocks until the addrConn is successfully connected. 595 func (t *http2Client) Close() error { 596 t.mu.Lock() 597 // Make sure we only Close once. 598 if t.state == closing { 599 t.mu.Unlock() 600 return nil 601 } 602 // Call t.onClose before setting the state to closing to prevent the client 603 // from attempting to create new streams ASAP. 604 if t.onClose != nil { 605 t.onClose() 606 } 607 t.state = closing 608 streams := t.activeStreams 609 t.activeStreams = nil 610 if t.kpDormant { 611 // If the keepalive goroutine is blocked on this condition variable, we 612 // should unblock it so that the goroutine eventually exits. 613 t.kpDormancyCond.Signal() 614 } 615 t.mu.Unlock() 616 t.controlBuf.finish() 617 t.cancel() 618 err := t.conn.Close() 619 // Notify all active streams. 620 for _, s := range streams { 621 t.closeStream(s, ErrConnClosing, false, http2.ErrCodeNo, status.New(codes.Unavailable, ErrConnClosing.Desc), nil, false) 622 } 623 return err 624 } 625 626 // GracefulClose sets the state to draining, which prevents new streams from 627 // being created and causes the transport to be closed when the last active 628 // stream is closed. If there are no active streams, the transport is closed 629 // immediately. This does nothing if the transport is already draining or 630 // closing. 631 func (t *http2Client) GracefulClose() { 632 t.mu.Lock() 633 // Make sure we move to draining only from active. 634 if t.state == draining || t.state == closing { 635 t.mu.Unlock() 636 return 637 } 638 t.state = draining 639 active := len(t.activeStreams) 640 t.mu.Unlock() 641 if active == 0 { 642 t.Close() 643 return 644 } 645 t.controlBuf.put(&incomingGoAway{}) 646 } 647 648 // Write formats the data into HTTP2 data frame(s) and sends it out. The caller 649 // should proceed only if Write returns nil. 650 func (t *http2Client) Write(s *Stream, hdr, data []byte, opts *Options) error { 651 if opts.Last { 652 // If it's the last message, update stream state. 653 if !s.compareAndSwapState(streamActive, streamWriteDone) { 654 return errStreamDone 655 } 656 } else if s.getState() != streamActive { 657 return errStreamDone 658 } 659 df := &dataFrame{ 660 streamID: s.id, 661 endStream: opts.Last, 662 h: hdr, 663 d: data, 664 } 665 if len(hdr) == 0 && len(data) != 0 { 666 df.dcache = data 667 } 668 if hdr != nil || data != nil { // If it's not an empty data frame, check quota. 669 if err := s.wq.get(int32(len(hdr) + len(data))); err != nil { 670 return err 671 } 672 } 673 return t.controlBuf.put(df) 674 } 675 676 func (t *http2Client) getStream(f http2.Frame) *Stream { 677 t.mu.Lock() 678 s := t.activeStreams[f.Header().StreamID] 679 t.mu.Unlock() 680 return s 681 } 682 683 // adjustWindow sends out extra window update over the initial window size 684 // of stream if the application is requesting data larger in size than 685 // the window. 686 func (t *http2Client) adjustWindow(s *Stream, n uint32) { 687 if w := s.fc.maybeAdjust(n); w > 0 { 688 t.controlBuf.put(&outgoingWindowUpdate{streamID: s.id, increment: w}) 689 } 690 } 691 692 // updateFlowControl updates the incoming flow control windows 693 // for the transport and the stream based on the current bdp 694 // estimation. 695 func (t *http2Client) updateFlowControl(n uint32) { 696 updateIWS := func(interface{}) bool { 697 t.initialWindowSize = n 698 t.mu.Lock() 699 for _, s := range t.activeStreams { 700 s.fc.newLimit(n) 701 } 702 t.mu.Unlock() 703 return true 704 } 705 t.controlBuf.executeAndPut(updateIWS, &outgoingWindowUpdate{streamID: 0, increment: t.fc.newLimit(n)}) 706 t.controlBuf.put(&outgoingSettings{ 707 ss: []http2.Setting{ 708 { 709 ID: http2.SettingInitialWindowSize, 710 Val: n, 711 }, 712 }, 713 }) 714 } 715 716 // updateWindow adjusts the inbound quota for the stream. 717 // Window updates will be sent out when the cumulative quota 718 // exceeds the corresponding threshold. 719 func (t *http2Client) updateWindow(s *Stream, n uint32) { 720 if w := s.fc.onRead(n); w > 0 { 721 t.controlBuf.put(&outgoingWindowUpdate{streamID: s.id, increment: w}) 722 } 723 } 724 725 func (t *http2Client) handleData(f *grpcframe.DataFrame) { 726 size := f.Header().Length 727 var sendBDPPing bool 728 if t.bdpEst != nil { 729 sendBDPPing = t.bdpEst.add(size) 730 } 731 // Decouple connection's flow control from application's read. 732 // An update on connection's flow control should not depend on 733 // whether user application has read the data or not. Such a 734 // restriction is already imposed on the stream's flow control, 735 // and therefore the sender will be blocked anyways. 736 // Decoupling the connection flow control will prevent other 737 // active(fast) streams from starving in presence of slow or 738 // inactive streams. 739 // 740 if w := t.fc.onData(size); w > 0 { 741 t.controlBuf.put(&outgoingWindowUpdate{ 742 streamID: 0, 743 increment: w, 744 }) 745 } 746 if sendBDPPing { 747 // Avoid excessive ping detection (e.g. in an L7 proxy) 748 // by sending a window update prior to the BDP ping. 749 750 if w := t.fc.reset(); w > 0 { 751 t.controlBuf.put(&outgoingWindowUpdate{ 752 streamID: 0, 753 increment: w, 754 }) 755 } 756 757 t.controlBuf.put(bdpPing) 758 } 759 // Select the right stream to dispatch. 760 s := t.getStream(f) 761 if s == nil { 762 return 763 } 764 if size > 0 { 765 if err := s.fc.onData(size); err != nil { 766 t.closeStream(s, io.EOF, true, http2.ErrCodeFlowControl, status.New(codes.Internal, err.Error()), nil, false) 767 return 768 } 769 if f.Header().Flags.Has(http2.FlagDataPadded) { 770 if w := s.fc.onRead(size - uint32(len(f.Data()))); w > 0 { 771 t.controlBuf.put(&outgoingWindowUpdate{s.id, w}) 772 } 773 } 774 // TODO(bradfitz, zhaoq): A copy is required here because there is no 775 // guarantee f.Data() is consumed before the arrival of next frame. 776 // Can this copy be eliminated? 777 if len(f.Data()) > 0 { 778 buffer := t.bufferPool.get() 779 buffer.Reset() 780 buffer.Write(f.Data()) 781 s.write(recvMsg{buffer: buffer}) 782 } 783 } 784 // The server has closed the stream without sending trailers. Record that 785 // the read direction is closed, and set the status appropriately. 786 if f.FrameHeader.Flags.Has(http2.FlagDataEndStream) { 787 t.closeStream(s, io.EOF, false, http2.ErrCodeNo, status.New(codes.Internal, "server closed the stream without sending trailers"), nil, true) 788 } 789 } 790 791 func (t *http2Client) handleRSTStream(f *http2.RSTStreamFrame) { 792 s := t.getStream(f) 793 if s == nil { 794 return 795 } 796 if f.ErrCode == http2.ErrCodeRefusedStream { 797 // The stream was unprocessed by the server. 798 atomic.StoreUint32(&s.unprocessed, 1) 799 } 800 statusCode, ok := http2ErrConvTab[f.ErrCode] 801 if !ok { 802 klog.Warnf("transport: http2Client.handleRSTStream found no mapped gRPC status for the received nhttp2 error %v", f.ErrCode) 803 statusCode = codes.Unknown 804 } 805 if statusCode == codes.Canceled { 806 if d, ok := s.ctx.Deadline(); ok && !d.After(time.Now()) { 807 // Our deadline was already exceeded, and that was likely the cause 808 // of this cancelation. Alter the status code accordingly. 809 statusCode = codes.DeadlineExceeded 810 } 811 } 812 t.closeStream(s, io.EOF, false, http2.ErrCodeNo, status.Newf(statusCode, "stream terminated by RST_STREAM with error code: %v", f.ErrCode), nil, false) 813 } 814 815 func (t *http2Client) handleSettings(f *grpcframe.SettingsFrame, isFirst bool) { 816 if f.IsAck() { 817 return 818 } 819 var maxStreams *uint32 820 var ss []http2.Setting 821 var updateFuncs []func() 822 f.ForeachSetting(func(s http2.Setting) error { 823 switch s.ID { 824 case http2.SettingMaxConcurrentStreams: 825 maxStreams = new(uint32) 826 *maxStreams = s.Val 827 case http2.SettingMaxHeaderListSize: 828 updateFuncs = append(updateFuncs, func() { 829 t.maxSendHeaderListSize = new(uint32) 830 *t.maxSendHeaderListSize = s.Val 831 }) 832 default: 833 ss = append(ss, s) 834 } 835 return nil 836 }) 837 if isFirst && maxStreams == nil { 838 maxStreams = new(uint32) 839 *maxStreams = math.MaxUint32 840 } 841 sf := &incomingSettings{ 842 ss: ss, 843 } 844 if maxStreams != nil { 845 updateStreamQuota := func() { 846 delta := int64(*maxStreams) - int64(t.maxConcurrentStreams) 847 t.maxConcurrentStreams = *maxStreams 848 t.streamQuota += delta 849 if delta > 0 && t.waitingStreams > 0 { 850 close(t.streamsQuotaAvailable) // wake all of them up. 851 t.streamsQuotaAvailable = make(chan struct{}, 1) 852 } 853 } 854 updateFuncs = append(updateFuncs, updateStreamQuota) 855 } 856 t.controlBuf.executeAndPut(func(interface{}) bool { 857 for _, f := range updateFuncs { 858 f() 859 } 860 return true 861 }, sf) 862 } 863 864 func (t *http2Client) handlePing(f *http2.PingFrame) { 865 if f.IsAck() { 866 // Maybe it's a BDP ping. 867 if t.bdpEst != nil { 868 t.bdpEst.calculate(f.Data) 869 } 870 return 871 } 872 pingAck := &ping{ack: true} 873 copy(pingAck.data[:], f.Data[:]) 874 t.controlBuf.put(pingAck) 875 } 876 877 func (t *http2Client) handleGoAway(f *grpcframe.GoAwayFrame) { 878 t.mu.Lock() 879 if t.state == closing { 880 t.mu.Unlock() 881 return 882 } 883 if f.ErrCode == http2.ErrCodeEnhanceYourCalm { 884 klog.Infof("Client received GoAway with http2.ErrCodeEnhanceYourCalm.") 885 } 886 id := f.LastStreamID 887 if id > 0 && id%2 != 1 { 888 t.mu.Unlock() 889 t.Close() 890 return 891 } 892 // A client can receive multiple GoAways from the server (see 893 // https://github.com/grpc/grpc-go/issues/1387). The idea is that the first 894 // GoAway will be sent with an ID of MaxInt32 and the second GoAway will be 895 // sent after an RTT delay with the ID of the last stream the server will 896 // process. 897 // 898 // Therefore, when we get the first GoAway we don't necessarily close any 899 // streams. While in case of second GoAway we close all streams created after 900 // the GoAwayId. This way streams that were in-flight while the GoAway from 901 // server was being sent don't get killed. 902 select { 903 case <-t.goAway: // t.goAway has been closed (i.e.,multiple GoAways). 904 // If there are multiple GoAways the first one should always have an ID greater than the following ones. 905 if id > t.prevGoAwayID { 906 t.mu.Unlock() 907 t.Close() 908 return 909 } 910 default: 911 t.setGoAwayReason(f) 912 close(t.goAway) 913 defer t.controlBuf.put(&incomingGoAway{}) // Defer as t.mu is currently held. 914 // Notify the clientconn about the GOAWAY before we set the state to 915 // draining, to allow the client to stop attempting to create streams 916 // before disallowing new streams on this connection. 917 if t.onGoAway != nil { 918 t.onGoAway(t.goAwayReason) 919 } 920 t.state = draining 921 } 922 // All streams with IDs greater than the GoAwayId 923 // and smaller than the previous GoAway ID should be killed. 924 upperLimit := t.prevGoAwayID 925 if upperLimit == 0 { // This is the first GoAway Frame. 926 upperLimit = math.MaxUint32 // Kill all streams after the GoAway ID. 927 } 928 for streamID, stream := range t.activeStreams { 929 if streamID > id && streamID <= upperLimit { 930 // The stream was unprocessed by the server. 931 atomic.StoreUint32(&stream.unprocessed, 1) 932 t.closeStream(stream, errStreamDrain, false, http2.ErrCodeNo, statusGoAway, nil, false) 933 } 934 } 935 t.prevGoAwayID = id 936 active := len(t.activeStreams) 937 t.mu.Unlock() 938 if active == 0 { 939 t.Close() 940 } 941 } 942 943 // setGoAwayReason sets the value of t.goAwayReason based 944 // on the GoAway frame received. 945 // It expects a lock on transport's mutext to be held by 946 // the caller. 947 func (t *http2Client) setGoAwayReason(f *grpcframe.GoAwayFrame) { 948 t.goAwayReason = GoAwayNoReason 949 switch f.ErrCode { 950 case http2.ErrCodeEnhanceYourCalm: 951 if string(f.DebugData()) == "too_many_pings" { 952 t.goAwayReason = GoAwayTooManyPings 953 } 954 } 955 } 956 957 func (t *http2Client) GetGoAwayReason() GoAwayReason { 958 t.mu.Lock() 959 defer t.mu.Unlock() 960 return t.goAwayReason 961 } 962 963 func (t *http2Client) handleWindowUpdate(f *http2.WindowUpdateFrame) { 964 t.controlBuf.put(&incomingWindowUpdate{ 965 streamID: f.Header().StreamID, 966 increment: f.Increment, 967 }) 968 } 969 970 // operateHeaders takes action on the decoded headers. 971 func (t *http2Client) operateHeaders(frame *grpcframe.MetaHeadersFrame) { 972 s := t.getStream(frame) 973 if s == nil { 974 return 975 } 976 endStream := frame.StreamEnded() 977 atomic.StoreUint32(&s.bytesReceived, 1) 978 initialHeader := atomic.LoadUint32(&s.headerChanClosed) == 0 979 980 if !initialHeader && !endStream { 981 // As specified by gRPC over HTTP2, a HEADERS frame (and associated CONTINUATION frames) can only appear at the start or end of a stream. Therefore, second HEADERS frame must have EOS bit set. 982 st := status.New(codes.Internal, "a HEADERS frame cannot appear in the middle of a stream") 983 t.closeStream(s, st.Err(), true, http2.ErrCodeProtocol, st, nil, false) 984 return 985 } 986 987 state := &decodeState{} 988 // Initialize isGRPC value to be !initialHeader, since if a gRPC Response-Headers has already been received, then it means that the peer is speaking gRPC and we are in gRPC mode. 989 state.data.isGRPC = !initialHeader 990 if err := state.decodeHeader(frame); err != nil { 991 t.closeStream(s, err, true, http2.ErrCodeProtocol, status.Convert(err), nil, endStream) 992 return 993 } 994 995 // If headerChan hasn't been closed yet 996 if atomic.CompareAndSwapUint32(&s.headerChanClosed, 0, 1) { 997 s.headerValid = true 998 if !endStream { 999 // These values can be set without any synchronization because 1000 // stream goroutine will read it only after seeing a closed 1001 // headerChan which we'll close after setting this. 1002 s.recvCompress = state.data.encoding 1003 if len(state.data.mdata) > 0 { 1004 s.header = state.data.mdata 1005 } 1006 } else { 1007 // HEADERS frame block carries a Trailers-Only. 1008 s.noHeaders = true 1009 } 1010 close(s.headerChan) 1011 } 1012 1013 if !endStream { 1014 return 1015 } 1016 1017 // if client received END_STREAM from server while stream was still active, send RST_STREAM 1018 rst := s.getState() == streamActive 1019 s.SetBizStatusErr(state.bizStatusErr()) 1020 t.closeStream(s, io.EOF, rst, http2.ErrCodeNo, state.status(), state.data.mdata, true) 1021 } 1022 1023 // reader runs as a separate goroutine in charge of reading data from network 1024 // connection. 1025 // 1026 // TODO(zhaoq): currently one reader per transport. Investigate whether this is 1027 // optimal. 1028 // TODO(zhaoq): Check the validity of the incoming frame sequence. 1029 func (t *http2Client) reader() { 1030 defer close(t.readerDone) 1031 // Check the validity of server preface. 1032 frame, err := t.framer.ReadFrame() 1033 if err != nil { 1034 // TODO(emma): comment this log temporarily, because when use short connection, 'resource temporarily unavailable' error will happen 1035 // if the log need to be output, connection info should be appended 1036 // klog.Errorf("KITEX: grpc readFrame failed, error=%s", err.Error()) 1037 t.Close() // this kicks off resetTransport, so must be last before return 1038 return 1039 } 1040 t.conn.SetReadDeadline(time.Time{}) // reset deadline once we get the settings frame (we didn't time out, yay!) 1041 if t.keepaliveEnabled { 1042 atomic.StoreInt64(&t.lastRead, time.Now().UnixNano()) 1043 } 1044 sf, ok := frame.(*grpcframe.SettingsFrame) 1045 if !ok { 1046 t.Close() // this kicks off resetTransport, so must be last before return 1047 return 1048 } 1049 t.handleSettings(sf, true) 1050 1051 // loop to keep reading incoming messages on this transport. 1052 for { 1053 t.controlBuf.throttle() 1054 frame, err := t.framer.ReadFrame() 1055 if t.keepaliveEnabled { 1056 atomic.StoreInt64(&t.lastRead, time.Now().UnixNano()) 1057 } 1058 if err != nil { 1059 // Abort an active stream if the http2.Framer returns a 1060 // http2.StreamError. This can happen only if the server's response 1061 // is malformed http2. 1062 if se, ok := err.(http2.StreamError); ok { 1063 t.mu.Lock() 1064 s := t.activeStreams[se.StreamID] 1065 t.mu.Unlock() 1066 if s != nil { 1067 // use error detail to provide better err message 1068 code := http2ErrConvTab[se.Code] 1069 err := t.framer.ErrorDetail() 1070 var msg string 1071 if err != nil { 1072 msg = err.Error() 1073 } 1074 t.closeStream(s, status.New(code, msg).Err(), true, http2.ErrCodeProtocol, status.New(code, msg), nil, false) 1075 } 1076 continue 1077 } else { 1078 // Transport error. 1079 // TODO(emma): comment this log temporarily, because when use short connection, 'resource temporarily unavailable' error will happen 1080 // if the log need to be output, connection info should be appended 1081 // klog.Errorf("KITEX: grpc readFrame failed, error=%s", err.Error()) 1082 t.Close() 1083 return 1084 } 1085 } 1086 switch frame := frame.(type) { 1087 case *grpcframe.MetaHeadersFrame: 1088 t.operateHeaders(frame) 1089 case *grpcframe.DataFrame: 1090 t.handleData(frame) 1091 case *http2.RSTStreamFrame: 1092 t.handleRSTStream(frame) 1093 case *grpcframe.SettingsFrame: 1094 t.handleSettings(frame, false) 1095 case *http2.PingFrame: 1096 t.handlePing(frame) 1097 case *grpcframe.GoAwayFrame: 1098 t.handleGoAway(frame) 1099 case *http2.WindowUpdateFrame: 1100 t.handleWindowUpdate(frame) 1101 default: 1102 klog.Warnf("transport: http2Client.reader got unhandled frame type %v.", frame) 1103 } 1104 t.framer.reader.Release() 1105 } 1106 } 1107 1108 func minTime(a, b time.Duration) time.Duration { 1109 if a < b { 1110 return a 1111 } 1112 return b 1113 } 1114 1115 // keepalive running in a separate goroutune makes sure the connection is alive by sending pings. 1116 func (t *http2Client) keepalive() { 1117 p := &ping{data: [8]byte{}} 1118 // True iff a ping has been sent, and no data has been received since then. 1119 outstandingPing := false 1120 // Amount of time remaining before which we should receive an ACK for the 1121 // last sent ping. 1122 timeoutLeft := time.Duration(0) 1123 // Records the last value of t.lastRead before we go block on the timer. 1124 // This is required to check for read activity since then. 1125 prevNano := time.Now().UnixNano() 1126 timer := time.NewTimer(t.kp.Time) 1127 for { 1128 select { 1129 case <-timer.C: 1130 lastRead := atomic.LoadInt64(&t.lastRead) 1131 if lastRead > prevNano { 1132 // There has been read activity since the last time we were here. 1133 outstandingPing = false 1134 // Next timer should fire at kp.Time seconds from lastRead time. 1135 timer.Reset(time.Duration(lastRead) + t.kp.Time - time.Duration(time.Now().UnixNano())) 1136 prevNano = lastRead 1137 continue 1138 } 1139 if outstandingPing && timeoutLeft <= 0 { 1140 t.Close() 1141 return 1142 } 1143 t.mu.Lock() 1144 if t.state == closing { 1145 // If the transport is closing, we should exit from the 1146 // keepalive goroutine here. If not, we could have a race 1147 // between the call to Signal() from Close() and the call to 1148 // Wait() here, whereby the keepalive goroutine ends up 1149 // blocking on the condition variable which will never be 1150 // signalled again. 1151 t.mu.Unlock() 1152 return 1153 } 1154 if len(t.activeStreams) < 1 && !t.kp.PermitWithoutStream { 1155 // If a ping was sent out previously (because there were active 1156 // streams at that point) which wasn't acked and its timeout 1157 // hadn't fired, but we got here and are about to go dormant, 1158 // we should make sure that we unconditionally send a ping once 1159 // we awaken. 1160 outstandingPing = false 1161 t.kpDormant = true 1162 t.kpDormancyCond.Wait() 1163 } 1164 t.kpDormant = false 1165 t.mu.Unlock() 1166 1167 // We get here either because we were dormant and a new stream was 1168 // created which unblocked the Wait() call, or because the 1169 // keepalive timer expired. In both cases, we need to send a ping. 1170 if !outstandingPing { 1171 t.controlBuf.put(p) 1172 timeoutLeft = t.kp.Timeout 1173 outstandingPing = true 1174 } 1175 // The amount of time to sleep here is the minimum of kp.Time and 1176 // timeoutLeft. This will ensure that we wait only for kp.Time 1177 // before sending out the next ping (for cases where the ping is 1178 // acked). 1179 sleepDuration := minTime(t.kp.Time, timeoutLeft) 1180 timeoutLeft -= sleepDuration 1181 timer.Reset(sleepDuration) 1182 case <-t.ctx.Done(): 1183 if !timer.Stop() { 1184 <-timer.C 1185 } 1186 return 1187 } 1188 } 1189 } 1190 1191 func (t *http2Client) Error() <-chan struct{} { 1192 return t.ctx.Done() 1193 } 1194 1195 func (t *http2Client) GoAway() <-chan struct{} { 1196 return t.goAway 1197 } 1198 1199 func (t *http2Client) RemoteAddr() net.Addr { return t.remoteAddr } 1200 func (t *http2Client) LocalAddr() net.Addr { return t.localAddr } 1201 1202 // IsActive return the connection's state, check if it's reachable. 1203 func (t *http2Client) IsActive() bool { 1204 t.mu.Lock() 1205 defer t.mu.Unlock() 1206 return t.state == reachable 1207 }