github.com/grailbio/base@v0.0.11/web/webutil/host.go (about)

     1  // Copyright 2018 GRAIL, Inc. All rights reserved.
     2  // Use of this source code is governed by the Apache-2.0
     3  // license that can be found in the LICENSE file.
     4  
     5  package webutil
     6  
     7  import (
     8  	"net"
     9  	"strings"
    10  )
    11  
    12  // CanonicalizeHost accepts a server host name as typically encountered by web
    13  // server and generally obtained from the http request Host header field and
    14  // canonicalizes it into:
    15  // fqhp   - fully qualified host+domain with a port - ie: host.domain:port
    16  // fqh    - fully qualified host+domain without a port - ie: host.domain
    17  // host   - the hostname, i.e. the first component of a . separated name
    18  // domain - the second and subsequent components of a . separated name
    19  // port   - the port.
    20  //
    21  // It is generally used for constructing redirects, determine/scoping
    22  // cookie names etc.
    23  //
    24  // The server name can be in any of the forms accepted by a web browser:
    25  // 1. [<protocol>://]<ip-addr>[:port]
    26  // 2. [<protocol>://]<name>[:<port>]
    27  // 3. [<protocol>://]<name>[.<subdomain>]+[:<port>]
    28  // The defaultDomain and defaultPort, if not "", will be used if there is
    29  // no domain or port in hostName.
    30  func CanonicalizeHost(hostName, defaultDomain, defaultPort string) (protocol, fqhp, fqh, host, domain, port string, hosterr error) {
    31  
    32  	if idx := strings.Index(hostName, "//"); idx >= 0 {
    33  		protocol = hostName[:idx]
    34  		hostName = hostName[idx+len("//"):]
    35  	}
    36  
    37  	h, p, err := net.SplitHostPort(hostName)
    38  	if err == nil {
    39  		// There is a port.
    40  		port = p
    41  		host = h
    42  	} else {
    43  		// There is no port, or there is some kind of other error, so
    44  		// we append a port and split again to see if it's an error.
    45  		_, _, err := net.SplitHostPort(net.JoinHostPort(hostName, "80"))
    46  		if err != nil {
    47  			hosterr = err
    48  			host = ""
    49  			return
    50  		}
    51  		host = hostName
    52  	}
    53  	if len(port) == 0 {
    54  		port = defaultPort
    55  	}
    56  	// host without port and port are now determined.
    57  
    58  	jhp := func(h, p string) string {
    59  		if len(p) > 0 {
    60  			return net.JoinHostPort(h, p)
    61  		}
    62  		return h
    63  	}
    64  
    65  	if net.ParseIP(host) != nil {
    66  		// an IP address
    67  		fqhp = jhp(host, port)
    68  		fqh = host
    69  		host = host
    70  		return
    71  	}
    72  
    73  	if dot := strings.Index(host, "."); dot >= 0 {
    74  		// we have a domain...
    75  		fqhp = jhp(host, port)
    76  		fqh = host
    77  		host, domain = host[:dot], host[dot+1:]
    78  		return
    79  	}
    80  	// use default domain
    81  	domain = strings.TrimPrefix(defaultDomain, ".")
    82  	if len(domain) > 0 {
    83  		fqh = host + "." + domain
    84  		fqhp = jhp(fqh, port)
    85  		return
    86  	}
    87  	fqhp = jhp(host, port)
    88  	fqh = host
    89  	return
    90  }