github.com/cloudwego/hertz@v0.9.3/pkg/protocol/http1/client.go (about) 1 /* 2 * Copyright 2022 CloudWeGo Authors 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 * 16 * The MIT License (MIT) 17 * 18 * Copyright (c) 2015-present Aliaksandr Valialkin, VertaMedia, Kirill Danshin, Erik Dubbelboer, FastHTTP Authors 19 * 20 * Permission is hereby granted, free of charge, to any person obtaining a copy 21 * of this software and associated documentation files (the "Software"), to deal 22 * in the Software without restriction, including without limitation the rights 23 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 24 * copies of the Software, and to permit persons to whom the Software is 25 * furnished to do so, subject to the following conditions: 26 * 27 * The above copyright notice and this permission notice shall be included in 28 * all copies or substantial portions of the Software. 29 * 30 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 31 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 32 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 33 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 34 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 35 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 36 * THE SOFTWARE. 37 * 38 * This file may have been modified by CloudWeGo authors. All CloudWeGo 39 * Modifications are Copyright 2022 CloudWeGo Authors. 40 */ 41 42 package http1 43 44 import ( 45 "bytes" 46 "context" 47 "crypto/tls" 48 "errors" 49 "io" 50 "net" 51 "strings" 52 "sync" 53 "sync/atomic" 54 "syscall" 55 "time" 56 57 "github.com/cloudwego/hertz/internal/bytesconv" 58 "github.com/cloudwego/hertz/internal/bytestr" 59 "github.com/cloudwego/hertz/internal/nocopy" 60 "github.com/cloudwego/hertz/pkg/app/client/retry" 61 "github.com/cloudwego/hertz/pkg/common/config" 62 errs "github.com/cloudwego/hertz/pkg/common/errors" 63 "github.com/cloudwego/hertz/pkg/common/hlog" 64 "github.com/cloudwego/hertz/pkg/common/timer" 65 "github.com/cloudwego/hertz/pkg/network" 66 "github.com/cloudwego/hertz/pkg/network/dialer" 67 "github.com/cloudwego/hertz/pkg/protocol" 68 "github.com/cloudwego/hertz/pkg/protocol/client" 69 "github.com/cloudwego/hertz/pkg/protocol/consts" 70 "github.com/cloudwego/hertz/pkg/protocol/http1/proxy" 71 reqI "github.com/cloudwego/hertz/pkg/protocol/http1/req" 72 respI "github.com/cloudwego/hertz/pkg/protocol/http1/resp" 73 ) 74 75 var ( 76 errConnectionClosed = errs.NewPublic("the server closed connection before returning the first response byte. " + 77 "Make sure the server returns 'Connection: close' response header before closing the connection") 78 79 errTimeout = errs.New(errs.ErrTimeout, errs.ErrorTypePublic, "host client") 80 ) 81 82 // HostClient balances http requests among hosts listed in Addr. 83 // 84 // HostClient may be used for balancing load among multiple upstream hosts. 85 // While multiple addresses passed to HostClient.Addr may be used for balancing 86 // load among them, it would be better using LBClient instead, since HostClient 87 // may unevenly balance load among upstream hosts. 88 // 89 // It is forbidden copying HostClient instances. Create new instances instead. 90 // 91 // It is safe calling HostClient methods from concurrently running goroutines. 92 type HostClient struct { 93 noCopy nocopy.NoCopy //lint:ignore U1000 until noCopy is used 94 95 *ClientOptions 96 97 // Comma-separated list of upstream HTTP server host addresses, 98 // which are passed to Dialer in a round-robin manner. 99 // 100 // Each address may contain port if default dialer is used. 101 // For example, 102 // 103 // - foobar.com:80 104 // - foobar.com:443 105 // - foobar.com:8080 106 Addr string 107 IsTLS bool 108 ProxyURI *protocol.URI 109 110 clientName atomic.Value 111 lastUseTime uint32 112 113 connsLock sync.Mutex 114 connsCount int 115 conns []*clientConn 116 connsWait *wantConnQueue 117 118 addrsLock sync.Mutex 119 addrs []string 120 addrIdx uint32 121 122 tlsConfigMap map[string]*tls.Config 123 tlsConfigMapLock sync.Mutex 124 125 pendingRequests int32 126 127 connsCleanerRun bool 128 129 closed chan struct{} 130 } 131 132 func (c *HostClient) SetDynamicConfig(dc *client.DynamicConfig) { 133 c.Addr = dc.Addr 134 c.ProxyURI = dc.ProxyURI 135 c.IsTLS = dc.IsTLS 136 137 // start observation after setting addr to avoid race 138 if c.StateObserve != nil { 139 go func() { 140 t := time.NewTicker(c.ObservationInterval) 141 for { 142 select { 143 case <-c.closed: 144 return 145 case <-t.C: 146 c.StateObserve(c) 147 } 148 } 149 }() 150 } 151 } 152 153 type clientConn struct { 154 c network.Conn 155 156 createdTime time.Time 157 lastUseTime time.Time 158 } 159 160 var startTimeUnix = time.Now().Unix() 161 162 // LastUseTime returns time the client was last used 163 func (c *HostClient) LastUseTime() time.Time { 164 n := atomic.LoadUint32(&c.lastUseTime) 165 return time.Unix(startTimeUnix+int64(n), 0) 166 } 167 168 // Get returns the status code and body of url. 169 // 170 // The contents of dst will be replaced by the body and returned, if the dst 171 // is too small a new slice will be allocated. 172 // 173 // The function follows redirects. Use Do* for manually handling redirects. 174 func (c *HostClient) Get(ctx context.Context, dst []byte, url string) (statusCode int, body []byte, err error) { 175 return client.GetURL(ctx, dst, url, c) 176 } 177 178 func (c *HostClient) ConnectionCount() (count int) { 179 c.connsLock.Lock() 180 count = len(c.conns) 181 c.connsLock.Unlock() 182 return 183 } 184 185 func (c *HostClient) WantConnectionCount() (count int) { 186 return c.connsWait.len() 187 } 188 189 func (c *HostClient) ConnPoolState() config.ConnPoolState { 190 c.connsLock.Lock() 191 defer c.connsLock.Unlock() 192 cps := config.ConnPoolState{ 193 PoolConnNum: len(c.conns), 194 TotalConnNum: c.connsCount, 195 Addr: c.Addr, 196 } 197 198 if c.connsWait != nil { 199 cps.WaitConnNum = c.connsWait.len() 200 } 201 return cps 202 } 203 204 // GetTimeout returns the status code and body of url. 205 // 206 // The contents of dst will be replaced by the body and returned, if the dst 207 // is too small a new slice will be allocated. 208 // 209 // The function follows redirects. Use Do* for manually handling redirects. 210 // 211 // errTimeout error is returned if url contents couldn't be fetched 212 // during the given timeout. 213 func (c *HostClient) GetTimeout(ctx context.Context, dst []byte, url string, timeout time.Duration) (statusCode int, body []byte, err error) { 214 return client.GetURLTimeout(ctx, dst, url, timeout, c) 215 } 216 217 // GetDeadline returns the status code and body of url. 218 // 219 // The contents of dst will be replaced by the body and returned, if the dst 220 // is too small a new slice will be allocated. 221 // 222 // The function follows redirects. Use Do* for manually handling redirects. 223 // 224 // errTimeout error is returned if url contents couldn't be fetched 225 // until the given deadline. 226 func (c *HostClient) GetDeadline(ctx context.Context, dst []byte, url string, deadline time.Time) (statusCode int, body []byte, err error) { 227 return client.GetURLDeadline(ctx, dst, url, deadline, c) 228 } 229 230 // Post sends POST request to the given url with the given POST arguments. 231 // 232 // The contents of dst will be replaced by the body and returned, if the dst 233 // is too small a new slice will be allocated. 234 // 235 // The function follows redirects. Use Do* for manually handling redirects. 236 // 237 // Empty POST body is sent if postArgs is nil. 238 func (c *HostClient) Post(ctx context.Context, dst []byte, url string, postArgs *protocol.Args) (statusCode int, body []byte, err error) { 239 return client.PostURL(ctx, dst, url, postArgs, c) 240 } 241 242 // A wantConnQueue is a queue of wantConns. 243 // 244 // inspired by net/http/transport.go 245 type wantConnQueue struct { 246 // This is a queue, not a deque. 247 // It is split into two stages - head[headPos:] and tail. 248 // popFront is trivial (headPos++) on the first stage, and 249 // pushBack is trivial (append) on the second stage. 250 // If the first stage is empty, popFront can swap the 251 // first and second stages to remedy the situation. 252 // 253 // This two-stage split is analogous to the use of two lists 254 // in Okasaki's purely functional queue but without the 255 // overhead of reversing the list when swapping stages. 256 head []*wantConn 257 headPos int 258 tail []*wantConn 259 } 260 261 // A wantConn records state about a wanted connection 262 // (that is, an active call to getConn). 263 // The conn may be gotten by dialing or by finding an idle connection, 264 // or a cancellation may make the conn no longer wanted. 265 // These three options are racing against each other and use 266 // wantConn to coordinate and agree about the winning outcome. 267 // 268 // inspired by net/http/transport.go 269 type wantConn struct { 270 ready chan struct{} 271 mu sync.Mutex // protects conn, err, close(ready) 272 conn *clientConn 273 err error 274 } 275 276 // DoTimeout performs the given request and waits for response during 277 // the given timeout duration. 278 // 279 // Request must contain at least non-zero RequestURI with full url (including 280 // scheme and host) or non-zero Host header + RequestURI. 281 // 282 // The function doesn't follow redirects. Use Get* for following redirects. 283 // 284 // Response is ignored if resp is nil. 285 // 286 // errTimeout is returned if the response wasn't returned during 287 // the given timeout. 288 // 289 // ErrNoFreeConns is returned if all HostClient.MaxConns connections 290 // to the host are busy. 291 // 292 // It is recommended obtaining req and resp via AcquireRequest 293 // and AcquireResponse in performance-critical code. 294 // 295 // Warning: DoTimeout does not terminate the request itself. The request will 296 // continue in the background and the response will be discarded. 297 // If requests take too long and the connection pool gets filled up please 298 // try setting a ReadTimeout. 299 func (c *HostClient) DoTimeout(ctx context.Context, req *protocol.Request, resp *protocol.Response, timeout time.Duration) error { 300 return client.DoTimeout(ctx, req, resp, timeout, c) 301 } 302 303 // DoDeadline performs the given request and waits for response until 304 // the given deadline. 305 // 306 // Request must contain at least non-zero RequestURI with full url (including 307 // scheme and host) or non-zero Host header + RequestURI. 308 // 309 // The function doesn't follow redirects. Use Get* for following redirects. 310 // 311 // Response is ignored if resp is nil. 312 // 313 // errTimeout is returned if the response wasn't returned until 314 // the given deadline. 315 // 316 // ErrNoFreeConns is returned if all HostClient.MaxConns connections 317 // to the host are busy. 318 // 319 // It is recommended obtaining req and resp via AcquireRequest 320 // and AcquireResponse in performance-critical code. 321 func (c *HostClient) DoDeadline(ctx context.Context, req *protocol.Request, resp *protocol.Response, deadline time.Time) error { 322 return client.DoDeadline(ctx, req, resp, deadline, c) 323 } 324 325 // DoRedirects performs the given http request and fills the given http response, 326 // following up to maxRedirectsCount redirects. When the redirect count exceeds 327 // maxRedirectsCount, ErrTooManyRedirects is returned. 328 // 329 // Request must contain at least non-zero RequestURI with full url (including 330 // scheme and host) or non-zero Host header + RequestURI. 331 // 332 // Client determines the server to be requested in the following order: 333 // 334 // - from RequestURI if it contains full url with scheme and host; 335 // - from Host header otherwise. 336 // 337 // Response is ignored if resp is nil. 338 // 339 // ErrNoFreeConns is returned if all DefaultMaxConnsPerHost connections 340 // to the requested host are busy. 341 // 342 // It is recommended obtaining req and resp via AcquireRequest 343 // and AcquireResponse in performance-critical code. 344 func (c *HostClient) DoRedirects(ctx context.Context, req *protocol.Request, resp *protocol.Response, maxRedirectsCount int) error { 345 _, _, err := client.DoRequestFollowRedirects(ctx, req, resp, req.URI().String(), maxRedirectsCount, c) 346 return err 347 } 348 349 // Do performs the given http request and sets the corresponding response. 350 // 351 // Request must contain at least non-zero RequestURI with full url (including 352 // scheme and host) or non-zero Host header + RequestURI. 353 // 354 // The function doesn't follow redirects. Use Get* for following redirects. 355 // 356 // Response is ignored if resp is nil. 357 // 358 // ErrNoFreeConns is returned if all HostClient.MaxConns connections 359 // to the host are busy. 360 // 361 // It is recommended obtaining req and resp via AcquireRequest 362 // and AcquireResponse in performance-critical code. 363 func (c *HostClient) Do(ctx context.Context, req *protocol.Request, resp *protocol.Response) error { 364 var ( 365 err error 366 canIdempotentRetry bool 367 isDefaultRetryFunc = true 368 attempts uint = 0 369 connAttempts uint = 0 370 maxAttempts uint = 1 371 isRequestRetryable client.RetryIfFunc = client.DefaultRetryIf 372 ) 373 retryCfg := c.ClientOptions.RetryConfig 374 if retryCfg != nil { 375 maxAttempts = retryCfg.MaxAttemptTimes 376 } 377 378 if c.ClientOptions.RetryIfFunc != nil { 379 isRequestRetryable = c.ClientOptions.RetryIfFunc 380 // if the user has provided a custom retry function, the canIdempotentRetry has no meaning anymore. 381 // User will have full control over the retry logic through the custom retry function. 382 isDefaultRetryFunc = false 383 } 384 385 atomic.AddInt32(&c.pendingRequests, 1) 386 req.Options().StartRequest() 387 for { 388 select { 389 case <-ctx.Done(): 390 req.CloseBodyStream() //nolint:errcheck 391 return ctx.Err() 392 default: 393 } 394 395 canIdempotentRetry, err = c.do(req, resp) 396 // If there is no custom retry and err is equal to nil, the loop simply exits. 397 if err == nil && isDefaultRetryFunc { 398 if connAttempts != 0 { 399 hlog.SystemLogger().Warnf("Client connection attempt times: %d, url: %s. "+ 400 "This is mainly because the connection in pool is closed by peer in advance. "+ 401 "If this number is too high which indicates that long-connection are basically unavailable, "+ 402 "try to change the request to short-connection.\n", connAttempts, req.URI().FullURI()) 403 } 404 break 405 } 406 407 // This connection is closed by the peer when it is in the connection pool. 408 // 409 // This case is possible if the server closes the idle 410 // keep-alive connection on timeout. 411 // 412 // Apache and nginx usually do this. 413 if canIdempotentRetry && client.DefaultRetryIf(req, resp, err) && errors.Is(err, errs.ErrBadPoolConn) { 414 connAttempts++ 415 continue 416 } 417 418 if isDefaultRetryFunc { 419 break 420 } 421 422 attempts++ 423 if attempts >= maxAttempts { 424 break 425 } 426 427 // Check whether this request should be retried 428 if !isRequestRetryable(req, resp, err) { 429 break 430 } 431 432 wait := retry.Delay(attempts, err, retryCfg) 433 // Retry after wait time 434 time.Sleep(wait) 435 } 436 atomic.AddInt32(&c.pendingRequests, -1) 437 438 if err == io.EOF { 439 err = errConnectionClosed 440 } 441 return err 442 } 443 444 // PendingRequests returns the current number of requests the client 445 // is executing. 446 // 447 // This function may be used for balancing load among multiple HostClient 448 // instances. 449 func (c *HostClient) PendingRequests() int { 450 return int(atomic.LoadInt32(&c.pendingRequests)) 451 } 452 453 func (c *HostClient) do(req *protocol.Request, resp *protocol.Response) (bool, error) { 454 nilResp := false 455 if resp == nil { 456 nilResp = true 457 resp = protocol.AcquireResponse() 458 } 459 460 canIdempotentRetry, err := c.doNonNilReqResp(req, resp) 461 462 if nilResp { 463 protocol.ReleaseResponse(resp) 464 } 465 466 return canIdempotentRetry, err 467 } 468 469 type requestConfig struct { 470 dialTimeout time.Duration 471 readTimeout time.Duration 472 writeTimeout time.Duration 473 } 474 475 func (c *HostClient) preHandleConfig(o *config.RequestOptions) requestConfig { 476 rc := requestConfig{ 477 dialTimeout: c.DialTimeout, 478 readTimeout: c.ReadTimeout, 479 writeTimeout: c.WriteTimeout, 480 } 481 if o.ReadTimeout() > 0 { 482 rc.readTimeout = o.ReadTimeout() 483 } 484 485 if o.WriteTimeout() > 0 { 486 rc.writeTimeout = o.WriteTimeout() 487 } 488 489 if o.DialTimeout() > 0 { 490 rc.dialTimeout = o.DialTimeout() 491 } 492 493 return rc 494 } 495 496 func updateReqTimeout(reqTimeout, compareTimeout time.Duration, before time.Time) (shouldCloseConn bool, timeout time.Duration) { 497 if reqTimeout <= 0 { 498 return false, compareTimeout 499 } 500 left := reqTimeout - time.Since(before) 501 if left <= 0 { 502 return true, 0 503 } 504 505 if compareTimeout <= 0 { 506 return false, left 507 } 508 509 if left > compareTimeout { 510 return false, compareTimeout 511 } 512 513 return false, left 514 } 515 516 func (c *HostClient) doNonNilReqResp(req *protocol.Request, resp *protocol.Response) (bool, error) { 517 if req == nil { 518 panic("BUG: req cannot be nil") 519 } 520 if resp == nil { 521 panic("BUG: resp cannot be nil") 522 } 523 524 atomic.StoreUint32(&c.lastUseTime, uint32(time.Now().Unix()-startTimeUnix)) 525 526 rc := c.preHandleConfig(req.Options()) 527 528 // Free up resources occupied by response before sending the request, 529 // so the GC may reclaim these resources (e.g. response body). 530 // backing up SkipBody in case it was set explicitly 531 customSkipBody := resp.SkipBody 532 resp.Reset() 533 resp.SkipBody = customSkipBody 534 535 if c.DisablePathNormalizing { 536 req.URI().DisablePathNormalizing = true 537 } 538 reqTimeout := req.Options().RequestTimeout() 539 begin := req.Options().StartTime() 540 541 dialTimeout := rc.dialTimeout 542 if (reqTimeout > 0 && reqTimeout < dialTimeout) || dialTimeout == 0 { 543 dialTimeout = reqTimeout 544 } 545 cc, inPool, err := c.acquireConn(dialTimeout) 546 // if getting connection error, fast fail 547 if err != nil { 548 return false, err 549 } 550 conn := cc.c 551 552 usingProxy := false 553 if c.ProxyURI != nil && bytes.Equal(req.Scheme(), bytestr.StrHTTP) { 554 usingProxy = true 555 proxy.SetProxyAuthHeader(&req.Header, c.ProxyURI) 556 } 557 558 resp.ParseNetAddr(conn) 559 560 shouldClose, timeout := updateReqTimeout(reqTimeout, rc.writeTimeout, begin) 561 if shouldClose { 562 c.closeConn(cc) 563 return false, errTimeout 564 } 565 566 if err = conn.SetWriteTimeout(timeout); err != nil { 567 c.closeConn(cc) 568 // try another connection if retry is enabled 569 return true, err 570 } 571 572 resetConnection := false 573 if c.MaxConnDuration > 0 && time.Since(cc.createdTime) > c.MaxConnDuration && !req.ConnectionClose() { 574 req.SetConnectionClose() 575 resetConnection = true 576 } 577 578 userAgentOld := req.Header.UserAgent() 579 if len(userAgentOld) == 0 { 580 req.Header.SetUserAgentBytes(c.getClientName()) 581 } 582 zw := c.acquireWriter(conn) 583 584 if !usingProxy { 585 err = reqI.Write(req, zw) 586 } else { 587 err = reqI.ProxyWrite(req, zw) 588 } 589 if resetConnection { 590 req.Header.ResetConnectionClose() 591 } 592 593 if err == nil { 594 err = zw.Flush() 595 } 596 // error happened when writing request, close the connection, and try another connection if retry is enabled 597 if err != nil { 598 defer c.closeConn(cc) 599 600 errNorm, ok := conn.(network.ErrorNormalization) 601 if ok { 602 err = errNorm.ToHertzError(err) 603 } 604 605 if !errors.Is(err, errs.ErrConnectionClosed) { 606 return true, err 607 } 608 609 // set a protection timeout to avoid infinite loop. 610 if conn.SetReadTimeout(time.Second) != nil { 611 return true, err 612 } 613 614 // Only if the connection is closed while writing the request. Try to parse the response and return. 615 // In this case, the request/response is considered as successful. 616 // Otherwise, return the former error. 617 zr := c.acquireReader(conn) 618 defer zr.Release() 619 if respI.ReadHeaderAndLimitBody(resp, zr, c.MaxResponseBodySize) == nil { 620 return false, nil 621 } 622 623 if inPool { 624 err = errs.ErrBadPoolConn 625 } 626 627 return true, err 628 } 629 630 shouldClose, timeout = updateReqTimeout(reqTimeout, rc.readTimeout, begin) 631 if shouldClose { 632 c.closeConn(cc) 633 return false, errTimeout 634 } 635 636 // Set Deadline every time, since golang has fixed the performance issue 637 // See https://github.com/golang/go/issues/15133#issuecomment-271571395 for details 638 if err = conn.SetReadTimeout(timeout); err != nil { 639 c.closeConn(cc) 640 // try another connection if retry is enabled 641 return true, err 642 } 643 644 if customSkipBody || req.Header.IsHead() || req.Header.IsConnect() { 645 resp.SkipBody = true 646 } 647 if c.DisableHeaderNamesNormalizing { 648 resp.Header.DisableNormalizing() 649 } 650 zr := c.acquireReader(conn) 651 652 // errs.ErrBadPoolConn error are returned when the 653 // 1 byte peek read fails, and we're actually anticipating a response. 654 // Usually this is just due to the inherent keep-alive shut down race, 655 // where the server closed the connection at the same time the client 656 // wrote. The underlying err field is usually io.EOF or some 657 // ECONNRESET sort of thing which varies by platform. 658 _, err = zr.Peek(1) 659 if err != nil { 660 zr.Release() //nolint:errcheck 661 c.closeConn(cc) 662 if inPool && (err == io.EOF || err == syscall.ECONNRESET) { 663 return true, errs.ErrBadPoolConn 664 } 665 // if this is not a pooled connection, 666 // we should not retry to avoid getting stuck in an endless retry loop. 667 errNorm, ok := conn.(network.ErrorNormalization) 668 if ok { 669 err = errNorm.ToHertzError(err) 670 } 671 return false, err 672 } 673 674 // init here for passing in ReadBodyStream's closure 675 // and this value will be assigned after reading Response's Header 676 // 677 // This is to solve the circular dependency problem of Response and BodyStream 678 shouldCloseConn := false 679 680 if !c.ResponseBodyStream { 681 err = respI.ReadHeaderAndLimitBody(resp, zr, c.MaxResponseBodySize) 682 } else { 683 err = respI.ReadBodyStream(resp, zr, c.MaxResponseBodySize, func(shouldClose bool) error { 684 if shouldCloseConn || shouldClose { 685 c.closeConn(cc) 686 } else { 687 c.releaseConn(cc) 688 } 689 return nil 690 }) 691 } 692 693 if err != nil { 694 zr.Release() //nolint:errcheck 695 c.closeConn(cc) 696 // Don't retry in case of ErrBodyTooLarge since we will just get the same again. 697 retry := !errors.Is(err, errs.ErrBodyTooLarge) 698 return retry, err 699 } 700 701 zr.Release() //nolint:errcheck 702 703 shouldCloseConn = resetConnection || req.ConnectionClose() || resp.ConnectionClose() 704 705 // In stream mode, we still can close/release the connection immediately if there is no content on the wire. 706 if c.ResponseBodyStream && resp.BodyStream() != protocol.NoResponseBody { 707 return false, err 708 } 709 710 if shouldCloseConn { 711 c.closeConn(cc) 712 } else { 713 c.releaseConn(cc) 714 } 715 716 return false, err 717 } 718 719 func (c *HostClient) Close() error { 720 close(c.closed) 721 return nil 722 } 723 724 // SetMaxConns sets up the maximum number of connections which may be established to all hosts listed in Addr. 725 func (c *HostClient) SetMaxConns(newMaxConns int) { 726 c.connsLock.Lock() 727 c.MaxConns = newMaxConns 728 c.connsLock.Unlock() 729 } 730 731 func (c *HostClient) acquireConn(dialTimeout time.Duration) (cc *clientConn, inPool bool, err error) { 732 createConn := false 733 startCleaner := false 734 735 var n int 736 c.connsLock.Lock() 737 n = len(c.conns) 738 if n == 0 { 739 maxConns := c.MaxConns 740 if maxConns <= 0 { 741 maxConns = consts.DefaultMaxConnsPerHost 742 } 743 if c.connsCount < maxConns { 744 c.connsCount++ 745 createConn = true 746 if !c.connsCleanerRun { 747 startCleaner = true 748 c.connsCleanerRun = true 749 } 750 } 751 } else { 752 n-- 753 cc = c.conns[n] 754 c.conns[n] = nil 755 c.conns = c.conns[:n] 756 } 757 c.connsLock.Unlock() 758 759 if cc != nil { 760 return cc, true, nil 761 } 762 if !createConn { 763 if c.MaxConnWaitTimeout <= 0 { 764 return nil, true, errs.ErrNoFreeConns 765 } 766 767 timeout := c.MaxConnWaitTimeout 768 769 // wait for a free connection 770 tc := timer.AcquireTimer(timeout) 771 defer timer.ReleaseTimer(tc) 772 773 w := &wantConn{ 774 ready: make(chan struct{}, 1), 775 } 776 defer func() { 777 if err != nil { 778 w.cancel(c, err) 779 } 780 }() 781 782 // Note: In the case of setting MaxConnWaitTimeout, if the number 783 // of connections in the connection pool exceeds the maximum 784 // number of connections and needs to establish a connection while 785 // waiting, the dialtimeout on the hostclient is used instead of 786 // the dialtimeout in request options. 787 c.queueForIdle(w) 788 789 select { 790 case <-w.ready: 791 return w.conn, true, w.err 792 case <-tc.C: 793 return nil, true, errs.ErrNoFreeConns 794 } 795 } 796 797 if startCleaner { 798 go c.connsCleaner() 799 } 800 801 conn, err := c.dialHostHard(dialTimeout) 802 if err != nil { 803 c.decConnsCount() 804 return nil, false, err 805 } 806 cc = acquireClientConn(conn) 807 808 return cc, false, nil 809 } 810 811 func (c *HostClient) queueForIdle(w *wantConn) { 812 c.connsLock.Lock() 813 defer c.connsLock.Unlock() 814 if c.connsWait == nil { 815 c.connsWait = &wantConnQueue{} 816 } 817 c.connsWait.clearFront() 818 c.connsWait.pushBack(w) 819 } 820 821 func (c *HostClient) dialConnFor(w *wantConn) { 822 conn, err := c.dialHostHard(c.DialTimeout) 823 if err != nil { 824 w.tryDeliver(nil, err) 825 c.decConnsCount() 826 return 827 } 828 829 cc := acquireClientConn(conn) 830 delivered := w.tryDeliver(cc, nil) 831 if !delivered { 832 // not delivered, return idle connection 833 c.releaseConn(cc) 834 } 835 } 836 837 // CloseIdleConnections closes any connections which were previously 838 // connected from previous requests but are now sitting idle in a 839 // "keep-alive" state. It does not interrupt any connections currently 840 // in use. 841 func (c *HostClient) CloseIdleConnections() { 842 c.connsLock.Lock() 843 scratch := append([]*clientConn{}, c.conns...) 844 for i := range c.conns { 845 c.conns[i] = nil 846 } 847 c.conns = c.conns[:0] 848 c.connsLock.Unlock() 849 850 for _, cc := range scratch { 851 c.closeConn(cc) 852 } 853 } 854 855 func (c *HostClient) ShouldRemove() bool { 856 c.connsLock.Lock() 857 defer c.connsLock.Unlock() 858 return c.connsCount == 0 859 } 860 861 func (c *HostClient) connsCleaner() { 862 var ( 863 scratch []*clientConn 864 maxIdleConnDuration = c.MaxIdleConnDuration 865 ) 866 if maxIdleConnDuration <= 0 { 867 maxIdleConnDuration = consts.DefaultMaxIdleConnDuration 868 } 869 for { 870 currentTime := time.Now() 871 872 // Determine idle connections to be closed. 873 c.connsLock.Lock() 874 conns := c.conns 875 n := len(conns) 876 i := 0 877 878 for i < n && currentTime.Sub(conns[i].lastUseTime) > maxIdleConnDuration { 879 i++ 880 } 881 sleepFor := maxIdleConnDuration 882 if i < n { 883 // + 1 so we actually sleep past the expiration time and not up to it. 884 // Otherwise the > check above would still fail. 885 sleepFor = maxIdleConnDuration - currentTime.Sub(conns[i].lastUseTime) + 1 886 } 887 scratch = append(scratch[:0], conns[:i]...) 888 if i > 0 { 889 m := copy(conns, conns[i:]) 890 for i = m; i < n; i++ { 891 conns[i] = nil 892 } 893 c.conns = conns[:m] 894 } 895 c.connsLock.Unlock() 896 897 // Close idle connections. 898 for i, cc := range scratch { 899 c.closeConn(cc) 900 scratch[i] = nil 901 } 902 903 // Determine whether to stop the connsCleaner. 904 c.connsLock.Lock() 905 mustStop := c.connsCount == 0 906 if mustStop { 907 c.connsCleanerRun = false 908 } 909 c.connsLock.Unlock() 910 if mustStop { 911 break 912 } 913 914 time.Sleep(sleepFor) 915 } 916 } 917 918 func (c *HostClient) closeConn(cc *clientConn) { 919 c.decConnsCount() 920 cc.c.Close() 921 releaseClientConn(cc) 922 } 923 924 func (c *HostClient) decConnsCount() { 925 if c.MaxConnWaitTimeout <= 0 { 926 c.connsLock.Lock() 927 c.connsCount-- 928 c.connsLock.Unlock() 929 return 930 } 931 932 c.connsLock.Lock() 933 defer c.connsLock.Unlock() 934 dialed := false 935 if q := c.connsWait; q != nil && q.len() > 0 { 936 for q.len() > 0 { 937 w := q.popFront() 938 if w.waiting() { 939 go c.dialConnFor(w) 940 dialed = true 941 break 942 } 943 } 944 } 945 if !dialed { 946 c.connsCount-- 947 } 948 } 949 950 func acquireClientConn(conn network.Conn) *clientConn { 951 v := clientConnPool.Get() 952 if v == nil { 953 v = &clientConn{} 954 } 955 cc := v.(*clientConn) 956 cc.c = conn 957 cc.createdTime = time.Now() 958 return cc 959 } 960 961 func releaseClientConn(cc *clientConn) { 962 // Reset all fields. 963 *cc = clientConn{} 964 clientConnPool.Put(cc) 965 } 966 967 var clientConnPool sync.Pool 968 969 func (c *HostClient) releaseConn(cc *clientConn) { 970 cc.lastUseTime = time.Now() 971 if c.MaxConnWaitTimeout <= 0 { 972 c.connsLock.Lock() 973 c.conns = append(c.conns, cc) 974 c.connsLock.Unlock() 975 return 976 } 977 978 // try to deliver an idle connection to a *wantConn 979 c.connsLock.Lock() 980 defer c.connsLock.Unlock() 981 delivered := false 982 if q := c.connsWait; q != nil && q.len() > 0 { 983 for q.len() > 0 { 984 w := q.popFront() 985 if w.waiting() { 986 delivered = w.tryDeliver(cc, nil) 987 break 988 } 989 } 990 } 991 if !delivered { 992 c.conns = append(c.conns, cc) 993 } 994 } 995 996 func (c *HostClient) acquireWriter(conn network.Conn) network.Writer { 997 return conn 998 } 999 1000 func (c *HostClient) acquireReader(conn network.Conn) network.Reader { 1001 return conn 1002 } 1003 1004 func newClientTLSConfig(c *tls.Config, addr string) *tls.Config { 1005 if c == nil { 1006 c = &tls.Config{} 1007 } else { 1008 c = c.Clone() 1009 } 1010 1011 if c.ClientSessionCache == nil { 1012 c.ClientSessionCache = tls.NewLRUClientSessionCache(0) 1013 } 1014 1015 if len(c.ServerName) == 0 { 1016 serverName := tlsServerName(addr) 1017 if serverName == "*" { 1018 c.InsecureSkipVerify = true 1019 } else { 1020 c.ServerName = serverName 1021 } 1022 } 1023 return c 1024 } 1025 1026 func tlsServerName(addr string) string { 1027 if !strings.Contains(addr, ":") { 1028 return addr 1029 } 1030 host, _, err := net.SplitHostPort(addr) 1031 if err != nil { 1032 return "*" 1033 } 1034 return host 1035 } 1036 1037 func (c *HostClient) nextAddr() string { 1038 c.addrsLock.Lock() 1039 if c.addrs == nil { 1040 c.addrs = strings.Split(c.Addr, ",") 1041 } 1042 addr := c.addrs[0] 1043 if len(c.addrs) > 1 { 1044 addr = c.addrs[c.addrIdx%uint32(len(c.addrs))] 1045 c.addrIdx++ 1046 } 1047 c.addrsLock.Unlock() 1048 return addr 1049 } 1050 1051 func (c *HostClient) dialHostHard(dialTimeout time.Duration) (conn network.Conn, err error) { 1052 // attempt to dial all the available hosts before giving up. 1053 1054 c.addrsLock.Lock() 1055 n := len(c.addrs) 1056 c.addrsLock.Unlock() 1057 1058 if n == 0 { 1059 // It looks like c.addrs isn't initialized yet. 1060 n = 1 1061 } 1062 1063 deadline := time.Now().Add(dialTimeout) 1064 for n > 0 { 1065 addr := c.nextAddr() 1066 tlsConfig := c.cachedTLSConfig(addr) 1067 conn, err = dialAddr(addr, c.Dialer, c.DialDualStack, tlsConfig, dialTimeout, c.ProxyURI, c.IsTLS) 1068 if err == nil { 1069 return conn, nil 1070 } 1071 if time.Since(deadline) >= 0 { 1072 break 1073 } 1074 n-- 1075 } 1076 return nil, err 1077 } 1078 1079 func (c *HostClient) cachedTLSConfig(addr string) *tls.Config { 1080 var cfgAddr string 1081 if c.ProxyURI != nil && bytes.Equal(c.ProxyURI.Scheme(), bytestr.StrHTTPS) { 1082 cfgAddr = bytesconv.B2s(c.ProxyURI.Host()) 1083 } 1084 1085 if c.IsTLS && cfgAddr == "" { 1086 cfgAddr = addr 1087 } 1088 1089 if cfgAddr == "" { 1090 return nil 1091 } 1092 1093 c.tlsConfigMapLock.Lock() 1094 if c.tlsConfigMap == nil { 1095 c.tlsConfigMap = make(map[string]*tls.Config) 1096 } 1097 cfg := c.tlsConfigMap[cfgAddr] 1098 if cfg == nil { 1099 cfg = newClientTLSConfig(c.TLSConfig, cfgAddr) 1100 c.tlsConfigMap[cfgAddr] = cfg 1101 } 1102 c.tlsConfigMapLock.Unlock() 1103 1104 return cfg 1105 } 1106 1107 func dialAddr(addr string, dial network.Dialer, dialDualStack bool, tlsConfig *tls.Config, timeout time.Duration, proxyURI *protocol.URI, isTLS bool) (network.Conn, error) { 1108 var conn network.Conn 1109 var err error 1110 if dial == nil { 1111 hlog.SystemLogger().Warn("HostClient: no dialer specified, trying to use default dialer") 1112 dial = dialer.DefaultDialer() 1113 } 1114 dialFunc := dial.DialConnection 1115 1116 // addr has already been added port, no need to do it here 1117 if proxyURI != nil { 1118 // use tcp connection first, proxy will AddTLS to it 1119 conn, err = dialFunc("tcp", string(proxyURI.Host()), timeout, nil) 1120 } else { 1121 conn, err = dialFunc("tcp", addr, timeout, tlsConfig) 1122 } 1123 1124 if err != nil { 1125 return nil, err 1126 } 1127 if conn == nil { 1128 panic("BUG: dial.DialConnection returned (nil, nil)") 1129 } 1130 1131 if proxyURI != nil { 1132 conn, err = proxy.SetupProxy(conn, addr, proxyURI, tlsConfig, isTLS, dial) 1133 } 1134 1135 // conn must be nil when got error, so doesn't need to close it 1136 if err != nil { 1137 return nil, err 1138 } 1139 return conn, nil 1140 } 1141 1142 func (c *HostClient) getClientName() []byte { 1143 v := c.clientName.Load() 1144 var clientName []byte 1145 if v == nil { 1146 clientName = []byte(c.Name) 1147 if len(clientName) == 0 && !c.NoDefaultUserAgentHeader { 1148 clientName = bytestr.DefaultUserAgent 1149 } 1150 c.clientName.Store(clientName) 1151 } else { 1152 clientName = v.([]byte) 1153 } 1154 return clientName 1155 } 1156 1157 // waiting reports whether w is still waiting for an answer (connection or error). 1158 func (w *wantConn) waiting() bool { 1159 select { 1160 case <-w.ready: 1161 return false 1162 default: 1163 return true 1164 } 1165 } 1166 1167 // tryDeliver attempts to deliver conn, err to w and reports whether it succeeded. 1168 func (w *wantConn) tryDeliver(conn *clientConn, err error) bool { 1169 w.mu.Lock() 1170 defer w.mu.Unlock() 1171 1172 if w.conn != nil || w.err != nil { 1173 return false 1174 } 1175 w.conn = conn 1176 w.err = err 1177 if w.conn == nil && w.err == nil { 1178 panic("hertz: internal error: misuse of tryDeliver") 1179 } 1180 close(w.ready) 1181 return true 1182 } 1183 1184 // cancel marks w as no longer wanting a result (for example, due to cancellation). 1185 // If a connection has been delivered already, cancel returns it with c.releaseConn. 1186 func (w *wantConn) cancel(c *HostClient, err error) { 1187 w.mu.Lock() 1188 if w.conn == nil && w.err == nil { 1189 close(w.ready) // catch misbehavior in future delivery 1190 } 1191 1192 conn := w.conn 1193 w.conn = nil 1194 w.err = err 1195 w.mu.Unlock() 1196 1197 if conn != nil { 1198 c.releaseConn(conn) 1199 } 1200 } 1201 1202 // len returns the number of items in the queue. 1203 func (q *wantConnQueue) len() int { 1204 return len(q.head) - q.headPos + len(q.tail) 1205 } 1206 1207 // pushBack adds w to the back of the queue. 1208 func (q *wantConnQueue) pushBack(w *wantConn) { 1209 q.tail = append(q.tail, w) 1210 } 1211 1212 // popFront removes and returns the wantConn at the front of the queue. 1213 func (q *wantConnQueue) popFront() *wantConn { 1214 if q.headPos >= len(q.head) { 1215 if len(q.tail) == 0 { 1216 return nil 1217 } 1218 // Pick up tail as new head, clear tail. 1219 q.head, q.headPos, q.tail = q.tail, 0, q.head[:0] 1220 } 1221 1222 w := q.head[q.headPos] 1223 q.head[q.headPos] = nil 1224 q.headPos++ 1225 return w 1226 } 1227 1228 // peekFront returns the wantConn at the front of the queue without removing it. 1229 func (q *wantConnQueue) peekFront() *wantConn { 1230 if q.headPos < len(q.head) { 1231 return q.head[q.headPos] 1232 } 1233 if len(q.tail) > 0 { 1234 return q.tail[0] 1235 } 1236 return nil 1237 } 1238 1239 // cleanFront pops any wantConns that are no longer waiting from the head of the 1240 // queue, reporting whether any were popped. 1241 func (q *wantConnQueue) clearFront() (cleaned bool) { 1242 for { 1243 w := q.peekFront() 1244 if w == nil || w.waiting() { 1245 return cleaned 1246 } 1247 q.popFront() 1248 cleaned = true 1249 } 1250 } 1251 1252 func NewHostClient(c *ClientOptions) client.HostClient { 1253 hc := &HostClient{ 1254 ClientOptions: c, 1255 closed: make(chan struct{}), 1256 } 1257 1258 return hc 1259 } 1260 1261 type ClientOptions struct { 1262 // Client name. Used in User-Agent request header. 1263 Name string 1264 1265 // NoDefaultUserAgentHeader when set to true, causes the default 1266 // User-Agent header to be excluded from the Request. 1267 NoDefaultUserAgentHeader bool 1268 1269 // Callback for establishing new connection to the host. 1270 // 1271 // Default Dialer is used if not set. 1272 Dialer network.Dialer 1273 1274 // Timeout for establishing new connections to hosts. 1275 // 1276 // Default DialTimeout is used if not set. 1277 DialTimeout time.Duration 1278 1279 // Attempt to connect to both ipv4 and ipv6 host addresses 1280 // if set to true. 1281 // 1282 // This option is used only if default TCP dialer is used, 1283 // i.e. if Dialer is blank. 1284 // 1285 // By default client connects only to ipv4 addresses, 1286 // since unfortunately ipv6 remains broken in many networks worldwide :) 1287 DialDualStack bool 1288 1289 // Whether to use TLS (aka SSL or HTTPS) for host connections. 1290 // Optional TLS config. 1291 TLSConfig *tls.Config 1292 1293 // Maximum number of connections which may be established to all hosts 1294 // listed in Addr. 1295 // 1296 // You can change this value while the HostClient is being used 1297 // using HostClient.SetMaxConns(value) 1298 // 1299 // DefaultMaxConnsPerHost is used if not set. 1300 MaxConns int 1301 1302 // Keep-alive connections are closed after this duration. 1303 // 1304 // By default connection duration is unlimited. 1305 MaxConnDuration time.Duration 1306 1307 // Idle keep-alive connections are closed after this duration. 1308 // 1309 // By default idle connections are closed 1310 // after DefaultMaxIdleConnDuration. 1311 MaxIdleConnDuration time.Duration 1312 1313 // Maximum duration for full response reading (including body). 1314 // 1315 // By default response read timeout is unlimited. 1316 ReadTimeout time.Duration 1317 1318 // Maximum duration for full request writing (including body). 1319 // 1320 // By default request write timeout is unlimited. 1321 WriteTimeout time.Duration 1322 1323 // Maximum response body size. 1324 // 1325 // The client returns errBodyTooLarge if this limit is greater than 0 1326 // and response body is greater than the limit. 1327 // 1328 // By default response body size is unlimited. 1329 MaxResponseBodySize int 1330 1331 // Header names are passed as-is without normalization 1332 // if this option is set. 1333 // 1334 // Disabled header names' normalization may be useful only for proxying 1335 // responses to other clients expecting case-sensitive header names. 1336 // 1337 // By default request and response header names are normalized, i.e. 1338 // The first letter and the first letters following dashes 1339 // are uppercased, while all the other letters are lowercased. 1340 // Examples: 1341 // 1342 // * HOST -> Host 1343 // * content-type -> Content-Type 1344 // * cONTENT-lenGTH -> Content-Length 1345 DisableHeaderNamesNormalizing bool 1346 1347 // Path values are sent as-is without normalization 1348 // 1349 // Disabled path normalization may be useful for proxying incoming requests 1350 // to servers that are expecting paths to be forwarded as-is. 1351 // 1352 // By default path values are normalized, i.e. 1353 // extra slashes are removed, special characters are encoded. 1354 DisablePathNormalizing bool 1355 1356 // Maximum duration for waiting for a free connection. 1357 // 1358 // By default will not wait, return ErrNoFreeConns immediately 1359 MaxConnWaitTimeout time.Duration 1360 1361 // ResponseBodyStream enables response body streaming 1362 ResponseBodyStream bool 1363 1364 // All configurations related to retry 1365 RetryConfig *retry.Config 1366 1367 RetryIfFunc client.RetryIfFunc 1368 1369 // Observe hostclient state 1370 StateObserve config.HostClientStateFunc 1371 1372 // StateObserve execution interval 1373 ObservationInterval time.Duration 1374 }