diff --git a/connect/connection.go b/connect/connection.go index c035d5515285c6b0faa415786d194f8daf001c5a..31b74642253db0b3b8c5328e4956d4b39d27d69e 100644 --- a/connect/connection.go +++ b/connect/connection.go @@ -4,6 +4,7 @@ import ( "git.xx.network/elixxir/grpc-web-go-client/grpcweb" jww "github.com/spf13/jwalterweatherman" "google.golang.org/grpc" + "time" ) const ( @@ -49,6 +50,7 @@ type Connection interface { // Close closes the underlying connection Close() error + IsOnline() (time.Duration, bool) clientConnHelpers } diff --git a/connect/grpcConn.go b/connect/grpcConn.go index 2dc9e6e9d7bb4862481fa58ba0fafbe2fbe59a47..e9b0aba129b953345474bc3ee9ae46db3f69d468 100644 --- a/connect/grpcConn.go +++ b/connect/grpcConn.go @@ -8,6 +8,7 @@ import ( "google.golang.org/grpc" "google.golang.org/grpc/connectivity" "math" + "net" "sync/atomic" "time" ) @@ -148,3 +149,22 @@ func (gc *grpcConn) isAlive() bool { return state == connectivity.Idle || state == connectivity.Connecting || state == connectivity.Ready } + +func (gc *grpcConn) IsOnline() (time.Duration, bool) { + addr := gc.h.GetAddress() + start := time.Now() + conn, err := net.DialTimeout("tcp", addr, gc.h.params.PingTimeout) + if err != nil { + // If we cannot connect, mark the connection as failed + jww.DEBUG.Printf("Failed to verify connectivity for address %s", addr) + return 0, false + } + // Attempt to close the connection + if conn != nil { + errClose := conn.Close() + if errClose != nil { + jww.DEBUG.Printf("Failed to close connection for address %s", addr) + } + } + return time.Since(start), true +} diff --git a/connect/host.go b/connect/host.go index 681f27e61b36bc0887f7b7cab2f206a0463806f0..3c5ee9b3905ec22830efa980429e494e70258d5e 100644 --- a/connect/host.go +++ b/connect/host.go @@ -22,7 +22,6 @@ import ( "gitlab.com/xx_network/primitives/rateLimiting" "google.golang.org/grpc/credentials" "math" - "net" "strings" "sync" "sync/atomic" @@ -251,22 +250,7 @@ func (h *Host) conditionalDisconnect(count uint64) { // before the timeout by attempting to dial a tcp connection // Returns how long the ping took, and whether it was successful func (h *Host) IsOnline() (time.Duration, bool) { - addr := h.GetAddress() - start := time.Now() - conn, err := net.DialTimeout("tcp", addr, h.params.PingTimeout) - if err != nil { - // If we cannot connect, mark the connection as failed - jww.DEBUG.Printf("Failed to verify connectivity for address %s", addr) - return 0, false - } - // Attempt to close the connection - if conn != nil { - errClose := conn.Close() - if errClose != nil { - jww.DEBUG.Printf("Failed to close connection for address %s", addr) - } - } - return time.Since(start), true + return h.connection.IsOnline() } // send checks that the host has a connection and sends if it does. diff --git a/connect/webConn.go b/connect/webConn.go index c19d4350b33192df68e4615b29b944f22e4c4f5c..82bf288f3fcec205d731e0db21ff417cca700747 100644 --- a/connect/webConn.go +++ b/connect/webConn.go @@ -1,11 +1,15 @@ package connect import ( + "crypto/tls" "fmt" "git.xx.network/elixxir/grpc-web-go-client/grpcweb" "github.com/pkg/errors" jww "github.com/spf13/jwalterweatherman" "google.golang.org/grpc" + "net/http" + "net/http/httptrace" + "strings" "time" ) @@ -163,3 +167,56 @@ func (wc *webConn) isAlive() bool { } return wc.connection.IsAlive() } + +func (wc *webConn) IsOnline() (time.Duration, bool) { + addr := wc.h.GetAddress() + start := time.Now() + tr := &http.Transport{ + TLSClientConfig: &tls.Config{ + InsecureSkipVerify: true, + }, + } + client := http.Client{ + Transport: tr, + Timeout: wc.h.params.PingTimeout, + } + req, err := http.NewRequest("GET", addr, nil) + if err != nil { + fmt.Print("Failed to initiate request ", err) + } + + trace := &httptrace.ClientTrace{ + DNSDone: func(dnsInfo httptrace.DNSDoneInfo) { + fmt.Println("DNS Info: %+v\n", dnsInfo) + }, + GotConn: func(connInfo httptrace.GotConnInfo) { + fmt.Println("Got Conn: %+v\n", connInfo) + }, + GotFirstResponseByte: func() { + fmt.Println("Got first byte!") + }, + } + + // IMPORTANT - enables better HTTP(S) discovery, because many browsers block CORS by default. + req.Header.Add("js.fetch:mode", "no-cors") + fmt.Println("(GO request): ", fmt.Sprintf("%+v", req)) + + req = req.WithContext(httptrace.WithClientTrace(req.Context(), trace)) + if _, err := client.Do(req); err != nil { + fmt.Println(err) + fmt.Println("(GO error): ", err.Error()) + + // TODO: Get more exception strings for major browsers + errString := strings.ToLower(err.Error()) + if strings.Contains(errString, "exceeded while awaiting") || + strings.Contains(errString, "ssl") || + strings.Contains(errString, "cors") || + strings.Contains(errString, "invalid") || + strings.Contains(errString, "protocol") { + return time.Since(start), true + } else { + return time.Since(start), false + } + } + return time.Since(start), true +}