github.com/tickoalcantara12/micro/v3@v3.0.0-20221007104245-9d75b9bcbab9/util/net/net.go (about)

     1  // Licensed under the Apache License, Version 2.0 (the "License");
     2  // you may not use this file except in compliance with the License.
     3  // You may obtain a copy of the License at
     4  //
     5  //     https://www.apache.org/licenses/LICENSE-2.0
     6  //
     7  // Unless required by applicable law or agreed to in writing, software
     8  // distributed under the License is distributed on an "AS IS" BASIS,
     9  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    10  // See the License for the specific language governing permissions and
    11  // limitations under the License.
    12  //
    13  // Original source: github.com/micro/go-micro/v3/util/net/net.go
    14  
    15  package net
    16  
    17  import (
    18  	"errors"
    19  	"fmt"
    20  	"net"
    21  	"strconv"
    22  	"strings"
    23  )
    24  
    25  // HostPort format addr and port suitable for dial
    26  func HostPort(addr string, port interface{}) string {
    27  	host := addr
    28  	if strings.Count(addr, ":") > 0 {
    29  		host = fmt.Sprintf("[%s]", addr)
    30  	}
    31  	// when port is blank or 0, host is a queue name
    32  	if v, ok := port.(string); ok && v == "" {
    33  		return host
    34  	} else if v, ok := port.(int); ok && v == 0 && net.ParseIP(host) == nil {
    35  		return host
    36  	}
    37  
    38  	return fmt.Sprintf("%s:%v", host, port)
    39  }
    40  
    41  // Listen takes addr:portmin-portmax and binds to the first available port
    42  // Example: Listen("localhost:5000-6000", fn)
    43  func Listen(addr string, fn func(string) (net.Listener, error)) (net.Listener, error) {
    44  
    45  	if strings.Count(addr, ":") == 1 && strings.Count(addr, "-") == 0 {
    46  		return fn(addr)
    47  	}
    48  
    49  	// host:port || host:min-max
    50  	host, ports, err := net.SplitHostPort(addr)
    51  	if err != nil {
    52  		return nil, err
    53  	}
    54  
    55  	// try to extract port range
    56  	prange := strings.Split(ports, "-")
    57  
    58  	// single port
    59  	if len(prange) < 2 {
    60  		return fn(addr)
    61  	}
    62  
    63  	// we have a port range
    64  
    65  	// extract min port
    66  	min, err := strconv.Atoi(prange[0])
    67  	if err != nil {
    68  		return nil, errors.New("unable to extract port range")
    69  	}
    70  
    71  	// extract max port
    72  	max, err := strconv.Atoi(prange[1])
    73  	if err != nil {
    74  		return nil, errors.New("unable to extract port range")
    75  	}
    76  
    77  	// range the ports
    78  	for port := min; port <= max; port++ {
    79  		// try bind to host:port
    80  		ln, err := fn(HostPort(host, port))
    81  		if err == nil {
    82  			return ln, nil
    83  		}
    84  
    85  		// hit max port
    86  		if port == max {
    87  			return nil, err
    88  		}
    89  	}
    90  
    91  	// why are we here?
    92  	return nil, fmt.Errorf("unable to bind to %s", addr)
    93  }