The initial TCP handshake will fail without a connected network interface, and at that point there's no chance of success. What's it doing for the next 19.99 seconds?
The Go's HTTP client waits for the full timeout duration before returning an error when there is no network connection, because the client does not know the state of the network when it initiates the request. It only knows that it has not received a response within the specified timeout period.
The client is waiting for the operating system's TCP stack to return an error when it fails to establish a connection, and that can take some time because of various factors like the network configuration, the operating system's TCP/IP implementation, etc.
I do not know of a direct way to set a faster timeout for the case where there's no network connection in Go's HTTP client.
Another approach would be to combining the use of a context with a timeout and a separate goroutine that checks the network connectivity status. The goroutine would cancel the context if it detects that there's no network connection.
The request would use NewRequestWithContext
For instance:
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
defer cancel()
// Check network connection in a separate goroutine.
go func() {
if !isNetworkAvailable() {
cancel()
}
}()
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
if err != nil {
log.Fatal(err)
}
response, err := client.Do(req)
if err != nil {
log.Fatal(err)
}
You would need to implement isNetworkAvailable()
: a function to check if there is a network connection available. This can be platform-specific, and on iOS you would likely need to use a system API to check the network status.
Note that this approach might not work perfectly in all scenarios. For example, the network could become available just after isNetworkAvailable()
checks it, but before the HTTP request is made. In this case, the request would be canceled even though the network is available. But it might help in cases where the network is known to be unavailable for a longer period.
For instance:
import (
"net"
"time"
)
func isNetworkAvailable() bool {
timeout := 2 * time.Second
conn, err := net.DialTimeout("tcp", "8.8.8.8:53", timeout)
if err != nil {
return false
}
if conn != nil {
defer conn.Close()
}
return true
}
This function tries to establish a TCP connection to 8.8.8.8 on port 53 (the port used by DNS servers) with a timeout of 2 seconds.
(as JimB adds in the comments: "All you can do is try to make some network calls with shorter timeouts")
If the function can't establish a connection within that time, it assumes there's no network connection and returns false.
If it can establish a connection, it closes the connection and returns true.