I am using the conn.SetReadDeadline
method to set the read timeout for conn
, When conn.Read
waits for more than the specified time, it will return and return an error of type *net.OpError
. This error is returned by the net
package after wrapping all non-io.EOF
errors.
I can get the error before wrapping with Unwrap()
. A timeout error is an error of type *poll.DeadlineExceededError
. I use statements like this in my code to handle timeout errors precisely.
import "internal/poll"
_, err = conn.Read(p)
if err != nil {
if pe, ok := err.(*net.OpError); ok {
err = pe.Unwrap()
if timeout, ok := err.(*poll.DeadlineExceededError); ok {
log.Error(fmt.Sprintf("%T, %s", timeout, timeout))
}
}
return
}
I received an use of internal package internal/poll not allowed
error while running the program. The compiler tells me that internal packages cannot be used.
I googled and found a solution to delete the internal
folder, is this the final solution? Will there be a better solution?
CodePudding user response:
The os
package exports that error as os.ErrDeadlineExceeded
(check the source code). You can try :
if errors.Is(err, os.ErrDeadlineExceeded) {
log.Error("Timeout error")
}
[edit] actually, after reading @Brit's comment, this is the documented way to check for that error. See the doc for Conn.SetDeadline()
:
If the deadline is exceeded a call to Read or Write or to other I/O methods will return an error that wraps os.ErrDeadlineExceeded. This can be tested using errors.Is(err, os.ErrDeadlineExceeded).
Another indication that an error is a "timeout" error is if it has a Timeout() bool
method, which returns true
when called.
This is part of the net.Error
interface (although this interface has an extra method, which is documented as deprecated), and implemented by the net.OpError
type (and also by the internal poll.DeadlineExceededError
type).
There are several functions (net.OpError.IsTimeout()
, os.SyscallError.IsTimeout()
and the public function os.IsTimeout()
for example) that implement a timeout checking by directly casting the error value to a timeout
interface.
If you want to deal with errors that can be wrapped one into another, you may want to implement your own isTimeout()
check using errors.As(...)
:
// isTimeout : use errors.As() to unwrap errors and check if a sub error is a Timeout error
func isTimeout(err error) bool {
var terr interface{ Timeout() bool }
return errors.As(err, &terr) && terr.Timeout()
}
https://go.dev/play/p/OhhKY3XsGjZ
note that :
the difference between the isTimeout()
function above and the errors.Is(err, os.ErrDeadlineExceeded)
call is that the latter will try to match exacly an ErrDeadlineExceeded
(which is mostly triggered by setting SetDeadline()
on some file-or-conn-like object), while the latter one may return true for errors that try to advertise "I'm a timeout error" for other reasons (e.g : an HTTP "408 Request timeout" response)
note : all links above refer to go 1.18.3 . Depending on the version of go you are using, you may have to adapt some of the code above (for example : the os.ErrDeadlineExceeded
was added in go1.15 )