tls-autodetect: fix client-initiated shutdown
This commit is contained in:
parent
6caa4ab928
commit
525734eeb3
@ -85,8 +85,9 @@ type client struct {
|
|||||||
conn connCloseWrite // high-level connection
|
conn connCloseWrite // high-level connection
|
||||||
inQ []byte // unprocessed input
|
inQ []byte // unprocessed input
|
||||||
outQ []byte // unprocessed output
|
outQ []byte // unprocessed output
|
||||||
|
reading bool // whether a reading goroutine is running
|
||||||
writing bool // whether a writing goroutine is running
|
writing bool // whether a writing goroutine is running
|
||||||
inShutdown bool // whether we're closing connection
|
closing bool // whether we're closing the connection
|
||||||
killTimer *time.Timer // timeout
|
killTimer *time.Timer // timeout
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -164,7 +165,7 @@ func forceShutdown(reason string) {
|
|||||||
// --- Client ------------------------------------------------------------------
|
// --- Client ------------------------------------------------------------------
|
||||||
|
|
||||||
func (c *client) send(line string) {
|
func (c *client) send(line string) {
|
||||||
if !c.inShutdown {
|
if !c.closing {
|
||||||
c.outQ = append(c.outQ, (line + "\r\n")...)
|
c.outQ = append(c.outQ, (line + "\r\n")...)
|
||||||
c.flushOutQ()
|
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.
|
// Tear down the client connection, trying to do so in a graceful manner.
|
||||||
func (c *client) closeLink() {
|
func (c *client) closeLink() {
|
||||||
if c.inShutdown {
|
if c.closing {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if c.conn == nil {
|
if c.conn == nil {
|
||||||
@ -180,13 +181,13 @@ func (c *client) closeLink() {
|
|||||||
return
|
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.send("Goodbye")
|
||||||
c.killTimer = time.AfterFunc(3*time.Second, func() {
|
c.killTimer = time.AfterFunc(3*time.Second, func() {
|
||||||
timeouts <- c
|
timeouts <- c
|
||||||
})
|
})
|
||||||
|
|
||||||
c.inShutdown = true
|
c.closing = true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close the connection and forget about the client.
|
// 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.
|
// TODO: Save the host in the client structure.
|
||||||
go read(c)
|
go read(c)
|
||||||
|
c.reading = true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle the results from trying to read from the client connection.
|
// 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: 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 {
|
if len(c.inQ) > 8192 {
|
||||||
c.closeLink()
|
c.closeLink()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if readErr == io.EOF {
|
if readErr != nil {
|
||||||
if c.inShutdown {
|
c.reading = false
|
||||||
c.destroy()
|
|
||||||
} else {
|
if readErr != io.EOF {
|
||||||
c.closeLink()
|
|
||||||
}
|
|
||||||
} else if readErr != nil {
|
|
||||||
log.Println(readErr)
|
log.Println(readErr)
|
||||||
c.destroy()
|
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()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Spawn a goroutine to flush the outQ if possible and necessary. If the
|
// 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.
|
// connection is not ready yet, it needs to be retried as soon as it becomes.
|
||||||
func (c *client) flushOutQ() {
|
func (c *client) flushOutQ() {
|
||||||
if c.conn != nil && !c.writing {
|
if !c.writing && c.conn != nil {
|
||||||
go write(c, c.outQ)
|
go write(c, c.outQ)
|
||||||
c.writing = true
|
c.writing = true
|
||||||
}
|
}
|
||||||
@ -273,14 +281,8 @@ func (c *client) onWrite(written int, writeErr error) {
|
|||||||
c.destroy()
|
c.destroy()
|
||||||
} else if len(c.outQ) > 0 {
|
} else if len(c.outQ) > 0 {
|
||||||
c.flushOutQ()
|
c.flushOutQ()
|
||||||
} else if c.inShutdown {
|
} else if c.closing {
|
||||||
if c.conn != nil {
|
if c.reading {
|
||||||
// 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().
|
|
||||||
c.conn.CloseWrite()
|
c.conn.CloseWrite()
|
||||||
} else {
|
} else {
|
||||||
c.destroy()
|
c.destroy()
|
||||||
@ -341,7 +343,6 @@ func prepare(client *client) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
prepared <- preparedEvent{client, host, isTLS}
|
prepared <- preparedEvent{client, host, isTLS}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func read(client *client) {
|
func read(client *client) {
|
||||||
|
Loading…
Reference in New Issue
Block a user