(ctx context.Context, logger *zap.Logger, reqBuf []byte, clientConn, destConn net.Conn, mocks chan<- *models.Mock, opts models.OutgoingOptions)
| 57 | } |
| 58 | |
| 59 | func encodeGeneric(ctx context.Context, logger *zap.Logger, reqBuf []byte, clientConn, destConn net.Conn, mocks chan<- *models.Mock, opts models.OutgoingOptions) error { |
| 60 | |
| 61 | // Forward initial request buffer to destination immediately. |
| 62 | _, err := destConn.Write(reqBuf) |
| 63 | if err != nil { |
| 64 | utils.LogError(logger, err, "failed to write request message to the destination server") |
| 65 | return err |
| 66 | } |
| 67 | |
| 68 | // If recording is already paused, pure passthrough. |
| 69 | if memoryguard.IsRecordingPaused() { |
| 70 | return forwardBidirectional(clientConn, destConn) |
| 71 | } |
| 72 | |
| 73 | // Capture channels — background goroutine reads these to create mocks. |
| 74 | // Buffered to absorb brief processing delays without blocking forwarding. |
| 75 | clientCapChan := make(chan []byte, 256) |
| 76 | destCapChan := make(chan []byte, 256) |
| 77 | |
| 78 | // Seed initial request buffer into capture. |
| 79 | initialBuf := make([]byte, len(reqBuf)) |
| 80 | copy(initialBuf, reqBuf) |
| 81 | clientCapChan <- initialBuf |
| 82 | |
| 83 | // Start background mock creator. |
| 84 | go createGenericMocksAsync(ctx, logger, clientCapChan, destCapChan) |
| 85 | |
| 86 | // Forward bidirectionally at wire speed with non-blocking capture tee. |
| 87 | // Each tee writer shares a closeOnce with the deferred close so the |
| 88 | // channel is closed exactly once (by whichever fires first: capture |
| 89 | // stop or connection end). |
| 90 | clientCloseOnce := &sync.Once{} |
| 91 | destCloseOnce := &sync.Once{} |
| 92 | |
| 93 | done := make(chan struct{}, 2) |
| 94 | go func() { |
| 95 | defer clientCloseOnce.Do(func() { close(clientCapChan) }) |
| 96 | tee := &captureTeeWriter{dest: destConn, ch: clientCapChan, closeOnce: clientCloseOnce} |
| 97 | _, _ = io.Copy(tee, clientConn) |
| 98 | done <- struct{}{} |
| 99 | }() |
| 100 | go func() { |
| 101 | defer destCloseOnce.Do(func() { close(destCapChan) }) |
| 102 | tee := &captureTeeWriter{dest: clientConn, ch: destCapChan, closeOnce: destCloseOnce} |
| 103 | _, _ = io.Copy(tee, destConn) |
| 104 | done <- struct{}{} |
| 105 | }() |
| 106 | <-done |
| 107 | <-done |
| 108 | |
| 109 | return nil |
| 110 | } |
| 111 | |
| 112 | // forwardBidirectional does raw TCP passthrough without any capture. |
| 113 | func forwardBidirectional(clientConn, destConn net.Conn) error { |
no test coverage detected