-
Notifications
You must be signed in to change notification settings - Fork 17.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
crypto/tls: permanently broken tls.Conn should not return temporary net.Error #29971
Comments
With TLS connections a Write timeout caused by a SetDeadline permanently breaks the connection. However, the errors are reported as temporary. So there is no way to determine if it really is recoverable. As these were the only kind of Write error that was recovered all Write errors are now fatal to the connection. #494 #506 golang/go#29971
Agreed, we should wrap the net.Error to make Temporary() false. |
Thanks for reporting this issue @jackc and @FiloSottile for the triage and direction! Here is a standalone test adapted from @jackc's report and source code that can be used as a regression test package main
import (
"crypto/tls"
"fmt"
"net"
"net/http"
"net/http/httptest"
"net/url"
"testing"
"time"
)
func TestPermanentlyBrokenConnection(t *testing.T) {
cst := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Hello, World!"))
}))
defer cst.Close()
cstU, _ := url.Parse(cst.URL)
conn, err := tls.Dial("tcp", cstU.Host, &tls.Config{InsecureSkipVerify: true})
if err != nil {
t.Fatalf("Dial err: %v", err)
}
if err = conn.SetDeadline(time.Now()); err != nil {
t.Fatalf("conn.SetDeadline(time.Now()): %v", err)
}
if _, err = conn.Write([]byte("should fail")); err == nil {
t.Fatal("conn.Write succeeded when it should have failed")
}
// Clear deadline
err = conn.SetDeadline(time.Time{})
if err != nil {
t.Fatalf("final conn.SetDeadline(time.Time{}): %v", err)
}
_, err = conn.Write([]byte("This connection is permanently broken"))
if err != nil {
fmt.Println("Write err", err)
}
ne := err.(net.Error)
if ne.Temporary() {
t.Fatal("Got a net.Error that apparently is temporary")
}
} |
Change https://golang.org/cl/227774 mentions this issue: |
Change https://golang.org/cl/227840 mentions this issue: |
What version of Go are you using (
go version
)?Does this issue reproduce with the latest release?
Yes.
What operating system and processor architecture are you using (
go env
)?go env
OutputWhat did you do?
Demo code: https://github.com/jackc/go-tls-deadline-temporary-error
What did you expect to see?
A
net.Error
where Temporary() => false or a non-net.Error
.What did you see instead?
An
net.Error
where Temporary() => true.This is technically documented behavior
After a Write has timed out, the TLS state is corrupt and all future writes will return the same error.
But it means that code that works generically with thenet.Conn
interface cannot rely on temporary errors being (potentially) temporary. It must treat them as permanent.Perhaps the original failed
tls.Conn.Write
could return a non-temporary error.The text was updated successfully, but these errors were encountered: