mirror of
				https://github.com/caddyserver/caddy.git
				synced 2025-10-31 02:27:19 -04:00 
			
		
		
		
	core: Wrap listeners with type that can pipe
(Does not apply to PacketConn or quic.EarlyListener types.)
This commit is contained in:
		
							parent
							
								
									754fe4f7b4
								
							
						
					
					
						commit
						17f9f974f8
					
				| @ -15,7 +15,7 @@ import ( | |||||||
| func ListenTimeout(network, addr string, keepAlivePeriod time.Duration) (net.Listener, error) { | func ListenTimeout(network, addr string, keepAlivePeriod time.Duration) (net.Listener, error) { | ||||||
| 	// check to see if plugin provides listener | 	// check to see if plugin provides listener | ||||||
| 	if ln, err := getListenerFromPlugin(network, addr); err != nil || ln != nil { | 	if ln, err := getListenerFromPlugin(network, addr); err != nil || ln != nil { | ||||||
| 		return ln, err | 		return acceptPipe(ln), err | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	lnKey := listenerKey(network, addr) | 	lnKey := listenerKey(network, addr) | ||||||
| @ -29,7 +29,7 @@ func ListenTimeout(network, addr string, keepAlivePeriod time.Duration) (net.Lis | |||||||
| 			} | 			} | ||||||
| 			return nil, err | 			return nil, err | ||||||
| 		} | 		} | ||||||
| 		return &sharedListener{Listener: ln, key: lnKey}, nil | 		return &sharedListener{Listener: acceptPipe(ln), key: lnKey}, nil | ||||||
| 	}) | 	}) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
|  | |||||||
| @ -14,11 +14,12 @@ import ( | |||||||
| func ListenTimeout(network, addr string, keepalivePeriod time.Duration) (net.Listener, error) { | func ListenTimeout(network, addr string, keepalivePeriod time.Duration) (net.Listener, error) { | ||||||
| 	// check to see if plugin provides listener | 	// check to see if plugin provides listener | ||||||
| 	if ln, err := getListenerFromPlugin(network, addr); err != nil || ln != nil { | 	if ln, err := getListenerFromPlugin(network, addr); err != nil || ln != nil { | ||||||
| 		return ln, err | 		return pipeable(ln), err | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	config := &net.ListenConfig{Control: reusePort, KeepAlive: keepalivePeriod} | 	config := &net.ListenConfig{Control: reusePort, KeepAlive: keepalivePeriod} | ||||||
| 	return config.Listen(context.Background(), network, addr) | 	ln, err := config.Listen(context.Background(), network, addr) | ||||||
|  | 	return pipeable(ln), err | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func reusePort(network, address string, conn syscall.RawConn) error { | func reusePort(network, address string, conn syscall.RawConn) error { | ||||||
|  | |||||||
							
								
								
									
										62
									
								
								listeners.go
									
									
									
									
									
								
							
							
						
						
									
										62
									
								
								listeners.go
									
									
									
									
									
								
							| @ -45,6 +45,68 @@ func Listen(network, addr string) (net.Listener, error) { | |||||||
| 	return ListenTimeout(network, addr, 0) | 	return ListenTimeout(network, addr, 0) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | // pipeableListener wraps an underlying listener so | ||||||
|  | // that connections can be given to a server that is | ||||||
|  | // calling Accept(). | ||||||
|  | type pipeableListener struct { | ||||||
|  | 	net.Listener | ||||||
|  | 	bridge chan connAccept | ||||||
|  | 	done   chan struct{} | ||||||
|  | 	closed *int32 // accessed atomically | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (pln pipeableListener) Accept() (net.Conn, error) { | ||||||
|  | 	accept := <-pln.bridge | ||||||
|  | 	return accept.conn, accept.err | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (pln pipeableListener) Close() error { | ||||||
|  | 	if atomic.CompareAndSwapInt32(pln.closed, 0, 1) { | ||||||
|  | 		close(pln.done) | ||||||
|  | 	} | ||||||
|  | 	return pln.Listener.Close() | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // pump pipes real connections from the underlying listener's | ||||||
|  | // Accept() up to the callers of our own Accept(). | ||||||
|  | func (pln pipeableListener) pump() { | ||||||
|  | 	for { | ||||||
|  | 		select { | ||||||
|  | 		case <-pln.done: | ||||||
|  | 			return | ||||||
|  | 		default: | ||||||
|  | 			pln.Pipe(pln.Listener.Accept()) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Pipe gives a connection (or an error) to an active Accept() call | ||||||
|  | // on this listener. | ||||||
|  | func (pln pipeableListener) Pipe(conn net.Conn, err error) { | ||||||
|  | 	pln.bridge <- connAccept{conn, err} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // pipeable wraps listener so that it can be given connections | ||||||
|  | // for its caller/server to Accept() and use. | ||||||
|  | func pipeable(listener net.Listener) net.Listener { | ||||||
|  | 	if listener == nil { | ||||||
|  | 		return listener // don't start a goroutine | ||||||
|  | 	} | ||||||
|  | 	pln := pipeableListener{ | ||||||
|  | 		Listener: listener, | ||||||
|  | 		bridge:   make(chan connAccept), | ||||||
|  | 		done:     make(chan struct{}), | ||||||
|  | 		closed:   new(int32), | ||||||
|  | 	} | ||||||
|  | 	go pln.pump() | ||||||
|  | 	return pln | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | type connAccept struct { | ||||||
|  | 	conn net.Conn | ||||||
|  | 	err  error | ||||||
|  | } | ||||||
|  | 
 | ||||||
| // getListenerFromPlugin returns a listener on the given network and address | // getListenerFromPlugin returns a listener on the given network and address | ||||||
| // if a plugin has registered the network name. It may return (nil, nil) if | // if a plugin has registered the network name. It may return (nil, nil) if | ||||||
| // no plugin can provide a listener. | // no plugin can provide a listener. | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user