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