mirror of
https://github.com/linka-cloud/grpc.git
synced 2025-06-22 09:12:28 +00:00
add registry base interface, mdns, noop implementations, add resolver, client
This commit is contained in:
167
utils/addr/addr.go
Normal file
167
utils/addr/addr.go
Normal file
@ -0,0 +1,167 @@
|
||||
package addr
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
)
|
||||
|
||||
var (
|
||||
privateBlocks []*net.IPNet
|
||||
)
|
||||
|
||||
func init() {
|
||||
for _, b := range []string{"10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16", "100.64.0.0/10", "fd00::/8"} {
|
||||
if _, block, err := net.ParseCIDR(b); err == nil {
|
||||
privateBlocks = append(privateBlocks, block)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func isPrivateIP(ipAddr string) bool {
|
||||
ip := net.ParseIP(ipAddr)
|
||||
for _, priv := range privateBlocks {
|
||||
if priv.Contains(ip) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsLocal tells us whether an ip is local
|
||||
func IsLocal(addr string) bool {
|
||||
// extract the host
|
||||
host, _, err := net.SplitHostPort(addr)
|
||||
if err == nil {
|
||||
addr = host
|
||||
}
|
||||
|
||||
// check if its localhost
|
||||
if addr == "localhost" {
|
||||
return true
|
||||
}
|
||||
|
||||
// check against all local ips
|
||||
for _, ip := range IPs() {
|
||||
if addr == ip {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// Extract returns a real ip
|
||||
func Extract(addr string) (string, error) {
|
||||
// if addr specified then its returned
|
||||
if len(addr) > 0 && (addr != "0.0.0.0" && addr != "[::]" && addr != "::") {
|
||||
return addr, nil
|
||||
}
|
||||
|
||||
ifaces, err := net.Interfaces()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("Failed to get interfaces! Err: %v", err)
|
||||
}
|
||||
|
||||
//nolint:prealloc
|
||||
var addrs []net.Addr
|
||||
var loAddrs []net.Addr
|
||||
for _, iface := range ifaces {
|
||||
ifaceAddrs, err := iface.Addrs()
|
||||
if err != nil {
|
||||
// ignore error, interface can disappear from system
|
||||
continue
|
||||
}
|
||||
if iface.Flags&net.FlagLoopback != 0 {
|
||||
loAddrs = append(loAddrs, ifaceAddrs...)
|
||||
continue
|
||||
}
|
||||
addrs = append(addrs, ifaceAddrs...)
|
||||
}
|
||||
addrs = append(addrs, loAddrs...)
|
||||
|
||||
var ipAddr string
|
||||
var publicIP string
|
||||
|
||||
for _, rawAddr := range addrs {
|
||||
var ip net.IP
|
||||
switch addr := rawAddr.(type) {
|
||||
case *net.IPAddr:
|
||||
ip = addr.IP
|
||||
case *net.IPNet:
|
||||
ip = addr.IP
|
||||
default:
|
||||
continue
|
||||
}
|
||||
|
||||
if !isPrivateIP(ip.String()) {
|
||||
publicIP = ip.String()
|
||||
continue
|
||||
}
|
||||
|
||||
ipAddr = ip.String()
|
||||
break
|
||||
}
|
||||
|
||||
// return private ip
|
||||
if len(ipAddr) > 0 {
|
||||
a := net.ParseIP(ipAddr)
|
||||
if a == nil {
|
||||
return "", fmt.Errorf("ip addr %s is invalid", ipAddr)
|
||||
}
|
||||
return a.String(), nil
|
||||
}
|
||||
|
||||
// return public or virtual ip
|
||||
if len(publicIP) > 0 {
|
||||
a := net.ParseIP(publicIP)
|
||||
if a == nil {
|
||||
return "", fmt.Errorf("ip addr %s is invalid", publicIP)
|
||||
}
|
||||
return a.String(), nil
|
||||
}
|
||||
|
||||
return "", fmt.Errorf("No IP address found, and explicit IP not provided")
|
||||
}
|
||||
|
||||
// IPs returns all known ips
|
||||
func IPs() []string {
|
||||
ifaces, err := net.Interfaces()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var ipAddrs []string
|
||||
|
||||
for _, i := range ifaces {
|
||||
addrs, err := i.Addrs()
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, addr := range addrs {
|
||||
var ip net.IP
|
||||
switch v := addr.(type) {
|
||||
case *net.IPNet:
|
||||
ip = v.IP
|
||||
case *net.IPAddr:
|
||||
ip = v.IP
|
||||
}
|
||||
|
||||
if ip == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// dont skip ipv6 addrs
|
||||
/*
|
||||
ip = ip.To4()
|
||||
if ip == nil {
|
||||
continue
|
||||
}
|
||||
*/
|
||||
|
||||
ipAddrs = append(ipAddrs, ip.String())
|
||||
}
|
||||
}
|
||||
|
||||
return ipAddrs
|
||||
}
|
58
utils/addr/addr_test.go
Normal file
58
utils/addr/addr_test.go
Normal file
@ -0,0 +1,58 @@
|
||||
package addr
|
||||
|
||||
import (
|
||||
"net"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestIsLocal(t *testing.T) {
|
||||
testData := []struct {
|
||||
addr string
|
||||
expect bool
|
||||
}{
|
||||
{"localhost", true},
|
||||
{"localhost:8080", true},
|
||||
{"127.0.0.1", true},
|
||||
{"127.0.0.1:1001", true},
|
||||
{"80.1.1.1", false},
|
||||
}
|
||||
|
||||
for _, d := range testData {
|
||||
res := IsLocal(d.addr)
|
||||
if res != d.expect {
|
||||
t.Fatalf("expected %t got %t", d.expect, res)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestExtractor(t *testing.T) {
|
||||
testData := []struct {
|
||||
addr string
|
||||
expect string
|
||||
parse bool
|
||||
}{
|
||||
{"127.0.0.1", "127.0.0.1", false},
|
||||
{"10.0.0.1", "10.0.0.1", false},
|
||||
{"", "", true},
|
||||
{"0.0.0.0", "", true},
|
||||
{"[::]", "", true},
|
||||
}
|
||||
|
||||
for _, d := range testData {
|
||||
addr, err := Extract(d.addr)
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error %v", err)
|
||||
}
|
||||
|
||||
if d.parse {
|
||||
ip := net.ParseIP(addr)
|
||||
if ip == nil {
|
||||
t.Error("Unexpected nil IP")
|
||||
}
|
||||
|
||||
} else if addr != d.expect {
|
||||
t.Errorf("Expected %s got %s", d.expect, addr)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
16
utils/backoff/backoff.go
Normal file
16
utils/backoff/backoff.go
Normal file
@ -0,0 +1,16 @@
|
||||
// Package backoff provides backoff functionality
|
||||
package backoff
|
||||
|
||||
import (
|
||||
"math"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Do is a function x^e multiplied by a factor of 0.1 second.
|
||||
// Result is limited to 2 minute.
|
||||
func Do(attempts int) time.Duration {
|
||||
if attempts > 13 {
|
||||
return 2 * time.Minute
|
||||
}
|
||||
return time.Duration(math.Pow(float64(attempts), math.E)) * time.Millisecond * 100
|
||||
}
|
120
utils/net/net.go
Normal file
120
utils/net/net.go
Normal file
@ -0,0 +1,120 @@
|
||||
package net
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// HostPort format addr and port suitable for dial
|
||||
func HostPort(addr string, port interface{}) string {
|
||||
host := addr
|
||||
if strings.Count(addr, ":") > 0 {
|
||||
host = fmt.Sprintf("[%s]", addr)
|
||||
}
|
||||
// when port is blank or 0, host is a queue name
|
||||
if v, ok := port.(string); ok && v == "" {
|
||||
return host
|
||||
} else if v, ok := port.(int); ok && v == 0 && net.ParseIP(host) == nil {
|
||||
return host
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s:%v", host, port)
|
||||
}
|
||||
|
||||
// Listen takes addr:portmin-portmax and binds to the first available port
|
||||
// Example: Listen("localhost:5000-6000", fn)
|
||||
func Listen(addr string, fn func(string) (net.Listener, error)) (net.Listener, error) {
|
||||
|
||||
if strings.Count(addr, ":") == 1 && strings.Count(addr, "-") == 0 {
|
||||
return fn(addr)
|
||||
}
|
||||
|
||||
// host:port || host:min-max
|
||||
host, ports, err := net.SplitHostPort(addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// try to extract port range
|
||||
prange := strings.Split(ports, "-")
|
||||
|
||||
// single port
|
||||
if len(prange) < 2 {
|
||||
return fn(addr)
|
||||
}
|
||||
|
||||
// we have a port range
|
||||
|
||||
// extract min port
|
||||
min, err := strconv.Atoi(prange[0])
|
||||
if err != nil {
|
||||
return nil, errors.New("unable to extract port range")
|
||||
}
|
||||
|
||||
// extract max port
|
||||
max, err := strconv.Atoi(prange[1])
|
||||
if err != nil {
|
||||
return nil, errors.New("unable to extract port range")
|
||||
}
|
||||
|
||||
// range the ports
|
||||
for port := min; port <= max; port++ {
|
||||
// try bind to host:port
|
||||
ln, err := fn(HostPort(host, port))
|
||||
if err == nil {
|
||||
return ln, nil
|
||||
}
|
||||
|
||||
// hit max port
|
||||
if port == max {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// why are we here?
|
||||
return nil, fmt.Errorf("unable to bind to %s", addr)
|
||||
}
|
||||
|
||||
// Proxy returns the proxy and the address if it exits
|
||||
func Proxy(service string, address []string) (string, []string, bool) {
|
||||
var hasProxy bool
|
||||
|
||||
// get proxy. we parse out address if present
|
||||
if prx := os.Getenv("MICRO_PROXY"); len(prx) > 0 {
|
||||
// default name
|
||||
if prx == "service" {
|
||||
prx = "go.micro.proxy"
|
||||
address = nil
|
||||
}
|
||||
|
||||
// check if its an address
|
||||
if v := strings.Split(prx, ":"); len(v) > 1 {
|
||||
address = []string{prx}
|
||||
}
|
||||
|
||||
service = prx
|
||||
hasProxy = true
|
||||
|
||||
return service, address, hasProxy
|
||||
}
|
||||
|
||||
if prx := os.Getenv("MICRO_NETWORK"); len(prx) > 0 {
|
||||
// default name
|
||||
if prx == "service" {
|
||||
prx = "go.micro.network"
|
||||
}
|
||||
service = prx
|
||||
hasProxy = true
|
||||
}
|
||||
|
||||
if prx := os.Getenv("MICRO_NETWORK_ADDRESS"); len(prx) > 0 {
|
||||
address = []string{prx}
|
||||
hasProxy = true
|
||||
}
|
||||
|
||||
return service, address, hasProxy
|
||||
}
|
62
utils/net/net_test.go
Normal file
62
utils/net/net_test.go
Normal file
@ -0,0 +1,62 @@
|
||||
package net
|
||||
|
||||
import (
|
||||
"net"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestListen(t *testing.T) {
|
||||
fn := func(addr string) (net.Listener, error) {
|
||||
return net.Listen("tcp", addr)
|
||||
}
|
||||
|
||||
// try to create a number of listeners
|
||||
for i := 0; i < 10; i++ {
|
||||
l, err := Listen("localhost:10000-11000", fn)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer l.Close()
|
||||
}
|
||||
|
||||
// TODO nats case test
|
||||
// natsAddr := "_INBOX.bID2CMRvlNp0vt4tgNBHWf"
|
||||
// Expect addr DO NOT has extra ":" at the end!
|
||||
|
||||
}
|
||||
|
||||
// TestProxyEnv checks whether we have proxy/network settings in env
|
||||
func TestProxyEnv(t *testing.T) {
|
||||
service := "foo"
|
||||
address := []string{"bar"}
|
||||
|
||||
s, a, ok := Proxy(service, address)
|
||||
if ok {
|
||||
t.Fatal("Should not have proxy", s, a, ok)
|
||||
}
|
||||
|
||||
test := func(key, val, expectSrv, expectAddr string) {
|
||||
// set env
|
||||
os.Setenv(key, val)
|
||||
|
||||
s, a, ok := Proxy(service, address)
|
||||
if !ok {
|
||||
t.Fatal("Expected proxy")
|
||||
}
|
||||
if len(expectSrv) > 0 && s != expectSrv {
|
||||
t.Fatal("Expected proxy service", expectSrv, "got", s)
|
||||
}
|
||||
if len(expectAddr) > 0 {
|
||||
if len(a) == 0 || a[0] != expectAddr {
|
||||
t.Fatal("Expected proxy address", expectAddr, "got", a)
|
||||
}
|
||||
}
|
||||
|
||||
os.Unsetenv(key)
|
||||
}
|
||||
|
||||
test("MICRO_PROXY", "service", "go.micro.proxy", "")
|
||||
test("MICRO_NETWORK", "service", "go.micro.network", "")
|
||||
test("MICRO_NETWORK_ADDRESS", "10.0.0.1:8081", "", "10.0.0.1:8081")
|
||||
}
|
Reference in New Issue
Block a user