github.com/volts-dev/volts@v0.0.0-20240120094013-5e9c65924106/internal/net/net.go (about)

     1  package net
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"net"
     7  	"os"
     8  	"strconv"
     9  	"strings"
    10  )
    11  
    12  // HostPort format addr and port suitable for dial
    13  func HostPort(addr string, port interface{}) string {
    14  	host := addr
    15  	if strings.Count(addr, ":") > 0 {
    16  		host = fmt.Sprintf("[%s]", addr)
    17  	}
    18  	// when port is blank or 0, host is a queue name
    19  	if v, ok := port.(string); ok && v == "" {
    20  		return host
    21  	} else if v, ok := port.(int); ok && v == 0 && net.ParseIP(host) == nil {
    22  		return host
    23  	}
    24  
    25  	return fmt.Sprintf("%s:%v", host, port)
    26  }
    27  
    28  // Listen takes addr:portmin-portmax and binds to the first available port
    29  // Example: Listen("localhost:5000-6000", fn)
    30  func Listen(addr string, fn func(string) (net.Listener, error)) (net.Listener, error) {
    31  
    32  	if strings.Count(addr, ":") == 1 && strings.Count(addr, "-") == 0 {
    33  		return fn(addr)
    34  	}
    35  
    36  	// host:port || host:min-max
    37  	host, ports, err := net.SplitHostPort(addr)
    38  	if err != nil {
    39  		return nil, err
    40  	}
    41  
    42  	// try to extract port range
    43  	prange := strings.Split(ports, "-")
    44  
    45  	// single port
    46  	if len(prange) < 2 {
    47  		return fn(addr)
    48  	}
    49  
    50  	// we have a port range
    51  
    52  	// extract min port
    53  	min, err := strconv.Atoi(prange[0])
    54  	if err != nil {
    55  		return nil, errors.New("unable to extract port range")
    56  	}
    57  
    58  	// extract max port
    59  	max, err := strconv.Atoi(prange[1])
    60  	if err != nil {
    61  		return nil, errors.New("unable to extract port range")
    62  	}
    63  
    64  	// range the ports
    65  	for port := min; port <= max; port++ {
    66  		// try bind to host:port
    67  		ln, err := fn(HostPort(host, port))
    68  		if err == nil {
    69  			return ln, nil
    70  		}
    71  
    72  		// hit max port
    73  		if port == max {
    74  			return nil, err
    75  		}
    76  	}
    77  
    78  	// why are we here?
    79  	return nil, fmt.Errorf("unable to bind to %s", addr)
    80  }
    81  
    82  // Proxy returns the proxy and the address if it exits
    83  func Proxy(service string, address []string) (string, []string, bool) {
    84  	var hasProxy bool
    85  
    86  	// get proxy. we parse out address if present
    87  	if prx := os.Getenv("VOLTS_PROXY"); len(prx) > 0 {
    88  		// default name
    89  		if prx == "service" {
    90  			prx = "go.volts.proxy"
    91  			address = nil
    92  		}
    93  
    94  		// check if its an address
    95  		if v := strings.Split(prx, ":"); len(v) > 1 {
    96  			address = []string{prx}
    97  		}
    98  
    99  		service = prx
   100  		hasProxy = true
   101  
   102  		return service, address, hasProxy
   103  	}
   104  
   105  	if prx := os.Getenv("VOLTS_NETWORK"); len(prx) > 0 {
   106  		// default name
   107  		if prx == "service" {
   108  			prx = "go.volts.network"
   109  		}
   110  		service = prx
   111  		hasProxy = true
   112  	}
   113  
   114  	if prx := os.Getenv("VOLTS_NETWORK_ADDRESS"); len(prx) > 0 {
   115  		address = []string{prx}
   116  		hasProxy = true
   117  	}
   118  
   119  	return service, address, hasProxy
   120  }