timeoutlistener.go 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
  1. // Copyright (C) 2019-2023 Nicola Murino
  2. //
  3. // This program is free software: you can redistribute it and/or modify
  4. // it under the terms of the GNU Affero General Public License as published
  5. // by the Free Software Foundation, version 3.
  6. //
  7. // This program is distributed in the hope that it will be useful,
  8. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. // GNU Affero General Public License for more details.
  11. //
  12. // You should have received a copy of the GNU Affero General Public License
  13. // along with this program. If not, see <https://www.gnu.org/licenses/>.
  14. package util
  15. import (
  16. "net"
  17. "sync/atomic"
  18. "time"
  19. )
  20. type listener struct {
  21. net.Listener
  22. ReadTimeout time.Duration
  23. WriteTimeout time.Duration
  24. }
  25. func (l *listener) Accept() (net.Conn, error) {
  26. c, err := l.Listener.Accept()
  27. if err != nil {
  28. return nil, err
  29. }
  30. tc := &Conn{
  31. Conn: c,
  32. ReadTimeout: l.ReadTimeout,
  33. WriteTimeout: l.WriteTimeout,
  34. ReadThreshold: int32((l.ReadTimeout * 1024) / time.Second),
  35. WriteThreshold: int32((l.WriteTimeout * 1024) / time.Second),
  36. }
  37. tc.BytesReadFromDeadline.Store(0)
  38. tc.BytesWrittenFromDeadline.Store(0)
  39. return tc, nil
  40. }
  41. // Conn wraps a net.Conn, and sets a deadline for every read
  42. // and write operation.
  43. type Conn struct {
  44. net.Conn
  45. ReadTimeout time.Duration
  46. WriteTimeout time.Duration
  47. ReadThreshold int32
  48. WriteThreshold int32
  49. BytesReadFromDeadline atomic.Int32
  50. BytesWrittenFromDeadline atomic.Int32
  51. }
  52. func (c *Conn) Read(b []byte) (n int, err error) {
  53. if c.BytesReadFromDeadline.Load() > c.ReadThreshold {
  54. c.BytesReadFromDeadline.Store(0)
  55. // we set both read and write deadlines here otherwise after the request
  56. // is read writing the response fails with an i/o timeout error
  57. err = c.Conn.SetDeadline(time.Now().Add(c.ReadTimeout))
  58. if err != nil {
  59. return 0, err
  60. }
  61. }
  62. n, err = c.Conn.Read(b)
  63. c.BytesReadFromDeadline.Add(int32(n))
  64. return
  65. }
  66. func (c *Conn) Write(b []byte) (n int, err error) {
  67. if c.BytesWrittenFromDeadline.Load() > c.WriteThreshold {
  68. c.BytesWrittenFromDeadline.Store(0)
  69. // we extend the read deadline too, not sure it's necessary,
  70. // but it doesn't hurt
  71. err = c.Conn.SetDeadline(time.Now().Add(c.WriteTimeout))
  72. if err != nil {
  73. return
  74. }
  75. }
  76. n, err = c.Conn.Write(b)
  77. c.BytesWrittenFromDeadline.Add(int32(n))
  78. return
  79. }
  80. func newListener(network, addr string, readTimeout, writeTimeout time.Duration) (net.Listener, error) {
  81. l, err := net.Listen(network, addr)
  82. if err != nil {
  83. return nil, err
  84. }
  85. tl := &listener{
  86. Listener: l,
  87. ReadTimeout: readTimeout,
  88. WriteTimeout: writeTimeout,
  89. }
  90. return tl, nil
  91. }