aboutsummaryrefslogtreecommitdiff
path: root/prototypes/tls-autodetect.go
diff options
context:
space:
mode:
authorPřemysl Janouch <p@janouch.name>2018-07-22 13:45:32 +0200
committerPřemysl Janouch <p@janouch.name>2018-07-22 13:46:28 +0200
commit525734eeb346f4051eeac16be7ee34d181caab2d (patch)
tree91df49069c237ed42591bb8af1014ab35b0bbbf9 /prototypes/tls-autodetect.go
parent6caa4ab92831f46ecc6e466de4c24d03176b831b (diff)
downloadhaven-525734eeb346f4051eeac16be7ee34d181caab2d.tar.gz
haven-525734eeb346f4051eeac16be7ee34d181caab2d.tar.xz
haven-525734eeb346f4051eeac16be7ee34d181caab2d.zip
tls-autodetect: fix client-initiated shutdown
Diffstat (limited to 'prototypes/tls-autodetect.go')
-rw-r--r--prototypes/tls-autodetect.go57
1 files changed, 29 insertions, 28 deletions
diff --git a/prototypes/tls-autodetect.go b/prototypes/tls-autodetect.go
index 50017d5..aaff3d9 100644
--- a/prototypes/tls-autodetect.go
+++ b/prototypes/tls-autodetect.go
@@ -80,14 +80,15 @@ type connCloseWrite interface {
}
type client struct {
- transport net.Conn // underlying connection
- tls *tls.Conn // TLS, if detected
- conn connCloseWrite // high-level connection
- inQ []byte // unprocessed input
- outQ []byte // unprocessed output
- writing bool // whether a writing goroutine is running
- inShutdown bool // whether we're closing connection
- killTimer *time.Timer // timeout
+ transport net.Conn // underlying connection
+ tls *tls.Conn // TLS, if detected
+ conn connCloseWrite // high-level connection
+ inQ []byte // unprocessed input
+ outQ []byte // unprocessed output
+ reading bool // whether a reading goroutine is running
+ writing bool // whether a writing goroutine is running
+ closing bool // whether we're closing the connection
+ killTimer *time.Timer // timeout
}
type preparedEvent struct {
@@ -164,7 +165,7 @@ func forceShutdown(reason string) {
// --- Client ------------------------------------------------------------------
func (c *client) send(line string) {
- if !c.inShutdown {
+ if !c.closing {
c.outQ = append(c.outQ, (line + "\r\n")...)
c.flushOutQ()
}
@@ -172,7 +173,7 @@ func (c *client) send(line string) {
// Tear down the client connection, trying to do so in a graceful manner.
func (c *client) closeLink() {
- if c.inShutdown {
+ if c.closing {
return
}
if c.conn == nil {
@@ -180,13 +181,13 @@ func (c *client) closeLink() {
return
}
- // Since we send this goodbye, we don't need to call CloseWrite.
+ // Since we send this goodbye, we don't need to call CloseWrite here.
c.send("Goodbye")
c.killTimer = time.AfterFunc(3*time.Second, func() {
timeouts <- c
})
- c.inShutdown = true
+ c.closing = true
}
// Close the connection and forget about the client.
@@ -218,6 +219,7 @@ func (c *client) onPrepared(host string, isTLS bool) {
// TODO: Save the host in the client structure.
go read(c)
+ c.reading = true
}
// Handle the results from trying to read from the client connection.
@@ -236,28 +238,34 @@ func (c *client) onRead(data []byte, readErr error) {
}
// TODO: Inform the client about the inQ overrun in the farewell message.
- // TODO: We should stop receiving any more data from this client.
+ // TODO: We should stop receiving any more data from this client, or at
+ // least stop extending the inQ if we don't want to signal tho goroutine.
if len(c.inQ) > 8192 {
c.closeLink()
return
}
- if readErr == io.EOF {
- if c.inShutdown {
+ if readErr != nil {
+ c.reading = false
+
+ if readErr != io.EOF {
+ log.Println(readErr)
+ c.destroy()
+ } else if c.closing {
+ // Disregarding whether a clean shutdown has happened or not.
+ log.Println("client finished shutdown")
c.destroy()
} else {
+ log.Println("client EOF")
c.closeLink()
}
- } else if readErr != nil {
- log.Println(readErr)
- c.destroy()
}
}
// Spawn a goroutine to flush the outQ if possible and necessary. If the
// connection is not ready yet, it needs to be retried as soon as it becomes.
func (c *client) flushOutQ() {
- if c.conn != nil && !c.writing {
+ if !c.writing && c.conn != nil {
go write(c, c.outQ)
c.writing = true
}
@@ -273,14 +281,8 @@ func (c *client) onWrite(written int, writeErr error) {
c.destroy()
} else if len(c.outQ) > 0 {
c.flushOutQ()
- } else if c.inShutdown {
- if c.conn != nil {
- // FIXME: This is only correct for when /we/ initiate the shutdown,
- // otherwise we should perhaps just Close. Though even if we
- // Close, there's a/ no writer to fail on it, and b/ the reader
- // has already exited, too, which is why the client stays alive
- // up until the timeout. It seems that in that case we need to
- // call c.destroy().
+ } else if c.closing {
+ if c.reading {
c.conn.CloseWrite()
} else {
c.destroy()
@@ -341,7 +343,6 @@ func prepare(client *client) {
}
prepared <- preparedEvent{client, host, isTLS}
-
}
func read(client *client) {