github.com/searKing/golang/go@v1.2.74/net/url/host.go (about)

     1  // Copyright 2020 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 url
     6  
     7  import (
     8  	"net"
     9  	"strings"
    10  	"unicode/utf8"
    11  
    12  	"golang.org/x/net/idna"
    13  )
    14  
    15  // cleanHost cleans up the host sent in request's Host header.
    16  //
    17  // It both strips anything after '/' or ' ', and puts the value
    18  // into Punycode form, if necessary.
    19  //
    20  // Ideally we'd clean the Host header according to the spec:
    21  //   https://tools.ietf.org/html/rfc7230#section-5.4 (Host = uri-host [ ":" port ]")
    22  //   https://tools.ietf.org/html/rfc7230#section-2.7 (uri-host -> rfc3986's host)
    23  //   https://tools.ietf.org/html/rfc3986#section-3.2.2 (definition of host)
    24  // But practically, what we are trying to avoid is the situation in
    25  // issue 11206, where a malformed Host header used in the proxy context
    26  // would create a bad request. So it is enough to just truncate at the
    27  // first offending character.
    28  func CleanHost(in string) string {
    29  	if i := strings.IndexAny(in, " /"); i != -1 {
    30  		in = in[:i]
    31  	}
    32  	host, port, err := net.SplitHostPort(in)
    33  	if err != nil { // input was just a host
    34  		a, err := idnaASCII(in)
    35  		if err != nil {
    36  			return in // garbage in, garbage out
    37  		}
    38  		return a
    39  	}
    40  	a, err := idnaASCII(host)
    41  	if err != nil {
    42  		return in // garbage in, garbage out
    43  	}
    44  	return net.JoinHostPort(a, port)
    45  }
    46  
    47  // removeZone removes IPv6 zone identifier from host.
    48  // E.g., "[fe80::1%en0]:8080" to "[fe80::1]:8080"
    49  func RemoveZone(host string) string {
    50  	if !strings.HasPrefix(host, "[") {
    51  		return host
    52  	}
    53  	i := strings.LastIndex(host, "]")
    54  	if i < 0 {
    55  		return host
    56  	}
    57  	j := strings.LastIndex(host[:i], "%")
    58  	if j < 0 {
    59  		return host
    60  	}
    61  	return host[:j] + host[i:]
    62  }
    63  
    64  func idnaASCII(v string) (string, error) {
    65  	// TODO: Consider removing this check after verifying performance is okay.
    66  	// Right now punycode verification, length checks, context checks, and the
    67  	// permissible character tests are all omitted. It also prevents the ToASCII
    68  	// call from salvaging an invalid IDN, when possible. As a result it may be
    69  	// possible to have two IDNs that appear identical to the user where the
    70  	// ASCII-only version causes an error downstream whereas the non-ASCII
    71  	// version does not.
    72  	// Note that for correct ASCII IDNs ToASCII will only do considerably more
    73  	// work, but it will not cause an allocation.
    74  	if isASCII(v) {
    75  		return v, nil
    76  	}
    77  	return idna.Lookup.ToASCII(v)
    78  }
    79  
    80  func isASCII(s string) bool {
    81  	for i := 0; i < len(s); i++ {
    82  		if s[i] >= utf8.RuneSelf {
    83  			return false
    84  		}
    85  	}
    86  	return true
    87  }