@@ -27,6 +27,7 @@ import (
2727 "github.com/AdguardTeam/golibs/service"
2828 "github.com/AdguardTeam/golibs/syncutil"
2929 "github.com/AdguardTeam/golibs/timeutil"
30+ "github.com/AdguardTeam/golibs/validate"
3031 "github.com/ameshkov/dnscrypt/v2"
3132 "github.com/miekg/dns"
3233 gocache "github.com/patrickmn/go-cache"
@@ -183,9 +184,9 @@ type Proxy struct {
183184 // udpOOBSize is the size of the out-of-band data for UDP connections.
184185 udpOOBSize int
185186
186- // bindRetryNum is the number of retries for binding to an address for
187+ // bindRetryCount is the number of retries for binding to an address for
187188 // listening. Zero means one attempt and no retries.
188- bindRetryNum uint
189+ bindRetryCount uint
189190
190191 // bindRetryIvl is the interval between attempts to bind to an address for
191192 // listening.
@@ -215,6 +216,8 @@ type Proxy struct {
215216// New creates a new Proxy with the specified configuration. c must not be nil.
216217//
217218// TODO(e.burkov): Cover with tests.
219+ //
220+ // TODO(e.burkov): Add context.
218221func New (c * Config ) (p * Proxy , err error ) {
219222 p = & Proxy {
220223 Config : * c ,
@@ -256,12 +259,6 @@ func New(c *Config) (p *Proxy, err error) {
256259 return nil , err
257260 }
258261
259- // TODO(s.chzhen): Consider moving to [Proxy.validateConfig].
260- err = p .validateBasicAuth ()
261- if err != nil {
262- return nil , fmt .Errorf ("basic auth: %w" , err )
263- }
264-
265262 p .initCache ()
266263
267264 if p .MaxGoroutines > 0 {
@@ -281,7 +278,7 @@ func New(c *Config) (p *Proxy, err error) {
281278 }
282279
283280 if bindRetries := c .BindRetryConfig ; bindRetries != nil && bindRetries .Enabled {
284- p .bindRetryNum = bindRetries .Count
281+ p .bindRetryCount = bindRetries .Count
285282 p .bindRetryIvl = bindRetries .Interval
286283 }
287284
@@ -290,6 +287,7 @@ func New(c *Config) (p *Proxy, err error) {
290287 return nil , fmt .Errorf ("setting up DNS64: %w" , err )
291288 }
292289
290+ // TODO(e.burkov): Clone all mutable fields of Config.
293291 p .RatelimitWhitelist = slices .Clone (p .RatelimitWhitelist )
294292 slices .SortFunc (p .RatelimitWhitelist , netip .Addr .Compare )
295293
@@ -324,11 +322,7 @@ func (p *Proxy) validateBasicAuth() (err error) {
324322 return nil
325323 }
326324
327- if len (conf .HTTPSListenAddr ) == 0 {
328- return errors .Error ("no https addrs" )
329- }
330-
331- return nil
325+ return validate .NotEmptySlice ("https listen addrs" , conf .HTTPSListenAddr )
332326}
333327
334328// Returns true if proxy is started. It is safe for concurrent use.
@@ -359,12 +353,15 @@ func (p *Proxy) Start(ctx context.Context) (err error) {
359353 return err
360354 }
361355
362- err = p .configureListeners (ctx )
356+ err = p .startListeners (ctx )
363357 if err != nil {
364- return fmt .Errorf ("configuring listeners: %w" , err )
358+ closeErr := errors .Join (p .closeListeners (nil )... )
359+
360+ return fmt .Errorf ("configuring listeners: %w" , errors .WithDeferred (err , closeErr ))
365361 }
366362
367- p .startListeners ()
363+ p .serveListeners ()
364+
368365 p .started = true
369366
370367 return nil
@@ -390,7 +387,8 @@ func closeAll[C io.Closer](errs []error, closers ...C) (appended []error) {
390387 return errs
391388}
392389
393- // Shutdown implements the [service.Interface] for *Proxy.
390+ // Shutdown implements the [service.Interface] for *Proxy. It also closes the
391+ // configured upstream configurations.
394392func (p * Proxy ) Shutdown (ctx context.Context ) (err error ) {
395393 p .logger .InfoContext (ctx , "stopping server" )
396394
@@ -404,65 +402,75 @@ func (p *Proxy) Shutdown(ctx context.Context) (err error) {
404402 return nil
405403 }
406404
407- errs := closeAll (nil , p .tcpListen ... )
405+ errs := p .closeListeners (nil )
406+
407+ for _ , u := range []* UpstreamConfig {
408+ p .UpstreamConfig ,
409+ p .PrivateRDNSUpstreamConfig ,
410+ p .Fallbacks ,
411+ } {
412+ if u != nil {
413+ errs = closeAll (errs , u )
414+ }
415+ }
416+
417+ p .started = false
418+
419+ p .logger .InfoContext (ctx , "stopped dns proxy server" )
420+
421+ err = errors .Join (errs ... )
422+ if err != nil {
423+ return fmt .Errorf ("stopping dns proxy server: %w" , err )
424+ }
425+
426+ return nil
427+ }
428+
429+ // closeListeners closes all acrive listeners and returns the occurred errors.
430+ func (p * Proxy ) closeListeners (errs []error ) (res []error ) {
431+ res = errs
432+
433+ res = closeAll (res , p .tcpListen ... )
408434 p .tcpListen = nil
409435
410- errs = closeAll (errs , p .udpListen ... )
436+ res = closeAll (res , p .udpListen ... )
411437 p .udpListen = nil
412438
413- errs = closeAll (errs , p .tlsListen ... )
439+ res = closeAll (res , p .tlsListen ... )
414440 p .tlsListen = nil
415441
416442 if p .httpsServer != nil {
417- errs = closeAll (errs , p .httpsServer )
443+ res = closeAll (res , p .httpsServer )
418444 p .httpsServer = nil
419445
420446 // No need to close these since they're closed by httpsServer.Close().
421447 p .httpsListen = nil
422448 }
423449
424450 if p .h3Server != nil {
425- errs = closeAll (errs , p .h3Server )
451+ res = closeAll (res , p .h3Server )
426452 p .h3Server = nil
427453 }
428454
429- errs = closeAll (errs , p .h3Listen ... )
455+ res = closeAll (res , p .h3Listen ... )
430456 p .h3Listen = nil
431457
432- errs = closeAll (errs , p .quicListen ... )
458+ res = closeAll (res , p .quicListen ... )
433459 p .quicListen = nil
434460
435- errs = closeAll (errs , p .quicTransports ... )
461+ res = closeAll (res , p .quicTransports ... )
436462 p .quicTransports = nil
437463
438- errs = closeAll (errs , p .quicConns ... )
464+ res = closeAll (res , p .quicConns ... )
439465 p .quicConns = nil
440466
441- errs = closeAll (errs , p .dnsCryptUDPListen ... )
467+ res = closeAll (res , p .dnsCryptUDPListen ... )
442468 p .dnsCryptUDPListen = nil
443469
444- errs = closeAll (errs , p .dnsCryptTCPListen ... )
470+ res = closeAll (res , p .dnsCryptTCPListen ... )
445471 p .dnsCryptTCPListen = nil
446472
447- for _ , u := range []* UpstreamConfig {
448- p .UpstreamConfig ,
449- p .PrivateRDNSUpstreamConfig ,
450- p .Fallbacks ,
451- } {
452- if u != nil {
453- errs = closeAll (errs , u )
454- }
455- }
456-
457- p .started = false
458-
459- p .logger .InfoContext (ctx , "stopped dns proxy server" )
460-
461- if len (errs ) > 0 {
462- return fmt .Errorf ("stopping dns proxy server: %w" , errors .Join (errs ... ))
463- }
464-
465- return nil
473+ return res
466474}
467475
468476// addrFunc provides the address from the given A.
0 commit comments