Bicopy copies all of the data between the two connections and will close them after one or both of them are done writing. If the context is canceled, both of the connections will be closed.
(ctx context.Context, c1, c2 io.ReadWriteCloser)
| 10 | // after one or both of them are done writing. If the context is canceled, both |
| 11 | // of the connections will be closed. |
| 12 | func Bicopy(ctx context.Context, c1, c2 io.ReadWriteCloser) { |
| 13 | ctx, cancel := context.WithCancel(ctx) |
| 14 | defer cancel() |
| 15 | |
| 16 | defer func() { |
| 17 | _ = c1.Close() |
| 18 | _ = c2.Close() |
| 19 | }() |
| 20 | |
| 21 | var wg sync.WaitGroup |
| 22 | copyFunc := func(dst io.WriteCloser, src io.Reader) { |
| 23 | defer func() { |
| 24 | wg.Done() |
| 25 | // If one side of the copy fails, ensure the other one exits as |
| 26 | // well. |
| 27 | cancel() |
| 28 | }() |
| 29 | _, _ = io.Copy(dst, src) |
| 30 | } |
| 31 | |
| 32 | wg.Add(2) |
| 33 | go copyFunc(c1, c2) |
| 34 | go copyFunc(c2, c1) |
| 35 | |
| 36 | // Convert waitgroup to a channel so we can also wait on the context. |
| 37 | done := make(chan struct{}) |
| 38 | go func() { |
| 39 | defer close(done) |
| 40 | wg.Wait() |
| 41 | }() |
| 42 | |
| 43 | select { |
| 44 | case <-ctx.Done(): |
| 45 | case <-done: |
| 46 | } |
| 47 | } |