I'm currently dabbling in Go and I'm having trouble figuring out the following:

I am trying to proxy a MySQL connection through my application intending to do some introspection on both the requests coming from the client and responses coming from the server. I did the exact same thing for a Redis connection, which works well.

Now the issue is, if I simply do

go io.Copy(serverConnection, clientConnection)
go io.Copy(clientConnection, serverConnection)

This works fine. I can use a mysql client to connect to my proxy and issue queries to and receive responses from the MySQL server.

io.MultiWriter

The implementation (simplified) looks like this (and works this way):

package main

import (
    "fmt"
    "io"
    "log"
    "net"
)

func main () {
    port := 21001

    listenAddr, err := net.ResolveTCPAddr("tcp", fmt.Sprintf("127.0.0.1:%d", port))

    if err != nil {
        log.Fatalln(err)
    }

    targetAddr, err := net.ResolveTCPAddr("tcp", "127.0.0.1:11001")

    if err != nil {
        log.Fatalln(err)
    }

    listener, err := net.ListenTCP("tcp", listenAddr)

    if err != nil {
        log.Fatalln(err)
    }

    for {
        clientConnection, err := listener.AcceptTCP()

        if err != nil {
            log.Fatalln(err)
        }

        go func () {
            serverConnection, err := net.DialTCP("tcp", nil, targetAddr)

            if err != nil {
                return
            }

            connectionDone := make(chan bool)

            go monitorClientConnection(clientConnection, serverConnection, connectionDone)
            go monitorServerConnection(serverConnection, clientConnection, connectionDone)

            _ = <-connectionDone
            _ = <-connectionDone

            fmt.Println("MySQL connection closed")
        }()
    }
}

func monitorClientConnection(clientConnection *net.TCPConn, serverConnection *net.TCPConn, connectionDone chan bool) {
    go func() {
        _, err := io.Copy(serverConnection, clientConnection)

        fmt.Println(err)

        _ = serverConnection.Close()

        connectionDone <- true
    }()
}

func monitorServerConnection(serverConnection *net.TCPConn, clientConnection *net.TCPConn, connectionDone chan bool) {
    go func() {
        _, err := io.Copy(clientConnection, serverConnection)

        fmt.Println(err)

        _ = clientConnection.Close()

        connectionDone <- true
    }()
}
Where it starts to fail

If I change the implementation of the monitoring functions from (client and server are identical, just reversed):

func monitorServerConnection(serverConnection *net.TCPConn, clientConnection *net.TCPConn, connectionDone chan bool) {
    go func() {
        _, err := io.Copy(clientConnection, serverConnection)

        fmt.Println(err)

        _ = clientConnection.Close()

        connectionDone <- true
    }()
}

to

func monitorServerConnection(serverConnection *net.TCPConn, clientConnection *net.TCPConn, connectionDone chan bool) {
    _, w := io.Pipe() // The reader here would be used to consume the stream, left out for simplicity

    go func() {
        mw := io.MultiWriter(clientConnection, w)

        _, err := io.Copy(mw, serverConnection)

        fmt.Println(err)

        _ = clientConnection.Close()

        connectionDone <- true
    }()
}

It just hangs. This is the exact same approach that works fine for the Redis proxy (the Redis Serialization Protocol is plaintext, newline delimited, could be a factor maybe?).

What I'd like to know
io.MultiWriter