github.com/searKing/golang/go@v1.2.117/net/http/transport.host.go (about)

     1  // Copyright 2023 The searKing Author. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package http
     6  
     7  import (
     8  	"fmt"
     9  	"net/http"
    10  
    11  	"github.com/searKing/golang/go/net/http/httphost"
    12  	"github.com/searKing/golang/go/net/http/httpproxy"
    13  	"github.com/searKing/golang/go/net/resolver"
    14  	time_ "github.com/searKing/golang/go/time"
    15  )
    16  
    17  // RequestWithHostTarget replace Host in url.Url by resolver.Host
    18  // replace Host in req if replaceHostInRequest is true
    19  func RequestWithHostTarget(req *http.Request, target *httphost.Host) *http.Request {
    20  	if target == nil {
    21  		return req
    22  	}
    23  	return req.WithContext(httphost.WithHost(req.Context(), target))
    24  }
    25  
    26  // HostFuncFromContext builds a host function from the given string, which should
    27  // represent a Target that can be used as a host. It performs basic
    28  // sanitization of the Target retrieved in context of Request, and returns any error encountered.
    29  func HostFuncFromContext(req *http.Request) error {
    30  	host := httphost.ContextHost(req.Context())
    31  	// load host from environment if host not set
    32  	if host == nil || host.HostTarget == "" {
    33  		return nil
    34  	}
    35  	if req.URL == nil {
    36  		return nil
    37  	}
    38  
    39  	if host.HostTarget == "" {
    40  		return nil
    41  	}
    42  
    43  	// replace host of host if target of host if resolved
    44  	address, err := resolver.ResolveOneAddr(req.Context(), host.HostTarget)
    45  	if err != nil {
    46  		return err
    47  	}
    48  	if address.Addr != "" {
    49  		req.URL.Host = address.Addr
    50  	}
    51  	host.HostTargetAddrResolved = address
    52  	if host.ReplaceHostInRequest {
    53  		req.Host = req.URL.Host
    54  	}
    55  	return nil
    56  }
    57  
    58  // DefaultTransportWithDynamicHost is the default implementation of Transport and is
    59  // used by DefaultClientWithDynamicHost. It establishes network connections as needed
    60  // and caches them for reuse by subsequent calls.
    61  var DefaultTransportWithDynamicHost = RoundTripperWithTarget(http.DefaultTransport)
    62  
    63  // DefaultClientWithDynamicHost is the default Client with DefaultTransportWithDynamicHost.
    64  var DefaultClientWithDynamicHost = &http.Client{
    65  	Transport: DefaultTransportWithDynamicHost,
    66  }
    67  
    68  // RoundTripperWithTarget wraps http.RoundTripper with request url replaced by Target resolved by resolver.
    69  // Target is as like gRPC Naming for service discovery.
    70  func RoundTripperWithTarget(rt http.RoundTripper) http.RoundTripper {
    71  	return RoundTripFunc(func(req *http.Request) (resp *http.Response, err error) {
    72  		err = HostFuncFromContext(req)
    73  		if err != nil {
    74  			return nil, err
    75  		}
    76  		var cost time_.Cost
    77  		cost.Start()
    78  		defer func() {
    79  			if host := httphost.ContextHost(req.Context()); host != nil {
    80  				_ = resolver.ResolveDone(req.Context(), host.HostTarget, resolver.DoneInfo{
    81  					Err:      err,
    82  					Addr:     host.HostTargetAddrResolved,
    83  					Duration: cost.Elapse(),
    84  				})
    85  				if err != nil && host.HostTargetAddrResolved.Addr != "" {
    86  					var s string
    87  					if host.HostTarget != "" {
    88  						s = fmt.Sprintf(" in target(%s)", host.HostTarget)
    89  					}
    90  					err = fmt.Errorf("->http_host(%s)%s: %w", host.HostTargetAddrResolved.Addr, s, err)
    91  				}
    92  			}
    93  
    94  			if proxy := httpproxy.ContextProxy(req.Context()); proxy != nil {
    95  				_ = resolver.ResolveDone(req.Context(), proxy.ProxyTarget, resolver.DoneInfo{
    96  					Err:      err,
    97  					Addr:     proxy.ProxyAddrResolved,
    98  					Duration: cost.Elapse(),
    99  				})
   100  				if err != nil && proxy.ProxyAddrResolved.Addr != "" {
   101  					var s string
   102  					if proxy.ProxyTarget != "" {
   103  						s = fmt.Sprintf(" in target(%s)", proxy.ProxyTarget)
   104  					}
   105  					err = fmt.Errorf("->http_proxy(%s)%s: %w", proxy.ProxyAddrResolved.Addr, s, err)
   106  				}
   107  			}
   108  		}()
   109  		return rt.RoundTrip(req)
   110  	})
   111  }
   112  
   113  // DefaultTransportWithDynamicHostAndProxy is the default implementation of Transport and is
   114  // used by DefaultClientWithDynamicHostAndProxy. It establishes network connections as needed
   115  // and caches them for reuse by subsequent calls.
   116  var DefaultTransportWithDynamicHostAndProxy = RoundTripperWithTarget(DefaultTransportWithDynamicProxy)
   117  
   118  // DefaultClientWithDynamicHostAndProxy is the default Client with DefaultTransportWithDynamicHostAndProxy.
   119  var DefaultClientWithDynamicHostAndProxy = &http.Client{
   120  	Transport: DefaultTransportWithDynamicHostAndProxy,
   121  }