buffer.go 1.6 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061
  1. /*
  2. Package to allow go applications to immediately start
  3. listening on a socket, unix, tcp, udp but hold connections
  4. until the application has booted and is ready to accept them
  5. */
  6. package listenbuffer
  7. import (
  8. "fmt"
  9. "net"
  10. "time"
  11. )
  12. // NewListenBuffer returns a listener listening on addr with the protocol. It sets the
  13. // timeout to wait on first connection before an error is returned
  14. func NewListenBuffer(proto, addr string, activate chan struct{}, timeout time.Duration) (net.Listener, error) {
  15. wrapped, err := net.Listen(proto, addr)
  16. if err != nil {
  17. return nil, err
  18. }
  19. return &defaultListener{
  20. wrapped: wrapped,
  21. activate: activate,
  22. timeout: timeout,
  23. }, nil
  24. }
  25. type defaultListener struct {
  26. wrapped net.Listener // the real listener to wrap
  27. ready bool // is the listner ready to start accpeting connections
  28. activate chan struct{}
  29. timeout time.Duration // how long to wait before we consider this an error
  30. }
  31. func (l *defaultListener) Close() error {
  32. return l.wrapped.Close()
  33. }
  34. func (l *defaultListener) Addr() net.Addr {
  35. return l.wrapped.Addr()
  36. }
  37. func (l *defaultListener) Accept() (net.Conn, error) {
  38. // if the listen has been told it is ready then we can go ahead and
  39. // start returning connections
  40. if l.ready {
  41. return l.wrapped.Accept()
  42. }
  43. select {
  44. case <-time.After(l.timeout):
  45. // close the connection so any clients are disconnected
  46. l.Close()
  47. return nil, fmt.Errorf("timeout (%s) reached waiting for listener to become ready", l.timeout.String())
  48. case <-l.activate:
  49. l.ready = true
  50. return l.Accept()
  51. }
  52. panic("unreachable")
  53. }