I came up with a clever / elegant solution to this and I would like your feedback. This approach minimizes the http.Server & http.handleFunc structs needed to listen on both local interfaces.
func ListenWithDualListener() { dual, err := multilistener.NewLocalLoopback("8080") if err != nil { panic(err) } defer dual.Close() fmt.Printf("Serving HTTP %+v\n", dual.AllAddr()) fmt.Printf("Preferred Addr: %+v\n", dual.Addr()) if serveErr := http.Serve(dual, nil); serveErr != nil { fmt.Println("http.Serve error:", serveErr) } see https://github.com/tonymet/dualstack/blob/master/multilistener/multilistener.go On Thursday, January 11, 2024 at 12:27:22 AM UTC-8 p...@morth.org wrote: > Hi, > > Unfortunately, the only way to avoid having two sockets in this scenario, > is to listen to only one of 127.0.0.1 or ::1 and live with that it's only > one address family. > As you might've noticed, some tools still struggle with connecting to ::1 > so listening to 127.0.0.1 is the most safe option. > Most client will try to connect to both ::1 and 127.0.0.1 when given > localhost as target. In the case of browsers, they usually do it in > parallel and use the quickest succeeding connection (this is called Happy > Eyeballs if you wish to Google it). > > The reason it works this way is that you bind the socket to an address, > either ::1 or 127.0.0.1. There's no address that binds to multiple address > families, except for :: that listen on all interfaces. > > Regards, > Per Johansson > > On Wednesday, January 10, 2024 at 9:54:12 PM UTC+1 Tony M wrote: > >> How do I listen on all* local / loopback* ipv4 /ipv6 interfaces? >> net.Listen('tcp', ':9999') listens on all interfaces. For my application >> (oauth token listener) I only want to listen to local / loopback >> >> go doc Net.listen: >> >> >> * if the host in the address parameter is empty or a literal >> unspecified IP address, Listen listens on all available unicast and >> anycast IP addresses of the local system.* >> >> My current workarounds are not ideal : >> * implement MultiListener interface with 2 listeners\ >> * Listen on 2 net.Listen listeners with 2 http.Serve servers >> >> >> Example test with netstat showing the issue. >> SEE EXAMPLE CODE BELOW >> >> # Listen :9999 >> $ go run . -i all & >> $ netstat -tulnp |grep v2 >> tcp6 0 0 :::9999 :::* >> LISTEN 28107/v2 >> >> # listen [127.0.0.1]:9999 >> $ go run . -i ipv4 & >> $ netstat -tulnp |grep v2 >> tcp 0 0 127.0.0.1:9999 0.0.0.0:* >> LISTEN 29244/v2 >> >> # listen [::1]:9999 >> $ go run . -i ipv6 >> $ netstat -tulnp |grep v2 >> tcp6 0 0 ::1:9999 :::* >> LISTEN 30163/v2 >> >> Telnet v4 does not work with v6 listen: >> $ go run . -i ipv6 >> $ telnet 127.0.0.1 9999 >> Trying 127.0.0.1... >> telnet: Unable to connect to remote host: Connection refused >> >> >> Example Code >> >> package main >> >> import ( >> "flag" >> "fmt" >> "net" >> "time" >> ) >> >> func main() { >> optListen := flag.String("i", "all", "all / ipv6, ipv4") >> >> addrMap := map[string]string{ >> "all": ":9999", >> "ipv6": "[::1]:9999", >> "ipv4": "[127.0.0.1]:9999", >> } >> >> flag.Parse() >> >> fmt.Printf("Listen: %s\n", *optListen) >> addr, ok := addrMap[*optListen] >> if !ok { >> panic("not found") >> } >> if l1, err := net.Listen("tcp", addr); err == nil { >> fmt.Printf("l1: %s\n", l1.Addr().String()) >> } else { >> panic(err) >> } >> >> time.Sleep(30 * time.Second) >> } >> >> >> -- You received this message because you are subscribed to the Google Groups "golang-nuts" group. To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+unsubscr...@googlegroups.com. To view this discussion visit https://groups.google.com/d/msgid/golang-nuts/b1e5c091-ddd6-42df-8bd6-dc4761094e0fn%40googlegroups.com.