Your original email was ambiguous and said "when a connection is closed", so I gave an answer for when `connTwo` is closed. But your actual question was "when `connOne`" is closed, if I'm understanding correctly given the program you posted. So given the program above, if `dst` is closed you should observe that one goroutine has terminated, correct? Then the question is, "what about that other goroutine"?
The problem is that that other goroutine's Copy is sat in a loop like this:
for {
read bytes from src (blocked here)
write bytes to dst
}
`src` in this case has not been closed, so the only way you will notice if `dst` has closed is if you read from `src`, either by closing `src` and returning (via io.EOF) or reading bytes from `src` which then get written to `dst` and cause a "the network connection is broken" style of error, which io.Copy will return.
TL;DR: when the io.Copy(A, B) finishes, call A.Close() and the io.Copy(B, A) will terminate. Calling A.Close() is like forwarding the B.Read()'s io.EOF error.