start function starts the proxy server on the idle local port. When skipListener is true, no TCP listener is created. The function blocks on ctx.Done and handles cleanup on shutdown.
(ctx context.Context, readyChan chan<- error)
| 1250 | // When skipListener is true, no TCP listener is created. |
| 1251 | // The function blocks on ctx.Done and handles cleanup on shutdown. |
| 1252 | func (p *Proxy) start(ctx context.Context, readyChan chan<- error) error { |
| 1253 | |
| 1254 | if p.skipListener { |
| 1255 | // Info-level notice is emitted once in StartProxy (near the |
| 1256 | // DNS-control log, which is the conventional "startup banner" |
| 1257 | // spot). Here we only want a debug trace for the start() |
| 1258 | // internal entry point so logs aren't duplicated. |
| 1259 | p.logger.Debug("Proxy TCP listener skipped; DNS and session services active") |
| 1260 | readyChan <- nil |
| 1261 | // Block until context is cancelled, then run cleanup. |
| 1262 | <-ctx.Done() |
| 1263 | // Intentionally do NOT close p.session.MC in the skipListener |
| 1264 | // path. The non-skip path above closes MC only after |
| 1265 | // clientConnErrGrp.Wait() guarantees every per-conn producer |
| 1266 | // has exited, so no send-to-closed-channel panic can fire. |
| 1267 | // In skipListener mode there is no clientConnErrGrp — DNS |
| 1268 | // mock producers and any external capture-layer senders are |
| 1269 | // still running up to ctx cancellation and may race a close. |
| 1270 | // Shutdown of downstream consumers is driven by ctx.Done() |
| 1271 | // instead; the Go runtime garbage-collects MC once all |
| 1272 | // references drop. |
| 1273 | p.nsSwitchMutex.Lock() |
| 1274 | if string(p.nsswitchData) != "" { |
| 1275 | if err := p.resetNsSwitchConfig(); err != nil { |
| 1276 | utils.LogError(p.logger, err, "failed to reset the nsswitch config, please retry shutdown or restore the resolver configuration manually from /etc/nsswitch.conf") |
| 1277 | } |
| 1278 | } |
| 1279 | p.nsSwitchMutex.Unlock() |
| 1280 | p.logger.Debug("proxy (skipListener) stopped") |
| 1281 | return nil |
| 1282 | } |
| 1283 | |
| 1284 | // It will listen on all the interfaces |
| 1285 | listener, err := net.Listen("tcp", fmt.Sprintf(":%v", p.Port)) |
| 1286 | if err != nil { |
| 1287 | utils.LogError(p.logger, err, fmt.Sprintf("failed to start proxy on port:%v", p.Port)) |
| 1288 | // Notify failure |
| 1289 | readyChan <- err |
| 1290 | return err |
| 1291 | } |
| 1292 | p.Listener = listener |
| 1293 | p.logger.Debug(fmt.Sprintf("Proxy server is listening on %s", fmt.Sprintf(":%v", listener.Addr()))) |
| 1294 | // Signal that the server is ready |
| 1295 | readyChan <- nil |
| 1296 | defer func(listener net.Listener) { |
| 1297 | err := listener.Close() |
| 1298 | |
| 1299 | if err != nil && !isNetworkClosedErr(err) { |
| 1300 | p.logger.Error("failed to close the listener", zap.Error(err)) |
| 1301 | } |
| 1302 | p.logger.Debug("proxy stopped...") |
| 1303 | }(listener) |
| 1304 | |
| 1305 | clientConnCtx, clientConnCancel := context.WithCancel(ctx) |
| 1306 | clientConnErrGrp, _ := errgroup.WithContext(clientConnCtx) |
| 1307 | |
| 1308 | // Periodically drain attributable buffered mocks WHILE recording is |
| 1309 | // live. The request-driven drains (ResolveRange / DeleteMocksStrictlyBefore) |
no test coverage detected