+// Deadlined socket read/write. https://github.com/golang/go/issues/16100
+
+package godlighty
+
+import (
+ "net"
+ "time"
+)
+
+type DeadlinedConn struct {
+ net.Conn
+ ReadTimeout time.Duration
+ WriteTimeout time.Duration
+ ReadThreshold int64
+ WriteThreshold int64
+ BytesReadFromDeadline int64
+ BytesWrittenFromDeadline int64
+}
+
+func (c *DeadlinedConn) Read(b []byte) (n int, err error) {
+ if c.BytesReadFromDeadline > c.ReadThreshold {
+ c.BytesReadFromDeadline = 0
+ err = c.Conn.SetDeadline(time.Now().Add(c.ReadTimeout))
+ if err != nil {
+ return
+ }
+ }
+ n, err = c.Conn.Read(b)
+ c.BytesReadFromDeadline += int64(n)
+ return
+}
+
+func (c *DeadlinedConn) Write(b []byte) (n int, err error) {
+ if c.BytesWrittenFromDeadline > c.WriteThreshold {
+ c.BytesWrittenFromDeadline = 0
+ err = c.Conn.SetWriteDeadline(time.Now().Add(c.WriteTimeout))
+ if err != nil {
+ return
+ }
+ }
+ n, err = c.Conn.Write(b)
+ c.BytesWrittenFromDeadline += int64(n)
+ return
+}
+
+type DeadlinedListener struct {
+ net.Listener
+ ReadTimeout time.Duration
+ WriteTimeout time.Duration
+}
+
+func (l *DeadlinedListener) Accept() (net.Conn, error) {
+ c, err := l.Listener.Accept()
+ if err != nil {
+ return nil, err
+ }
+ return &DeadlinedConn{
+ Conn: c,
+ ReadTimeout: l.ReadTimeout,
+ WriteTimeout: l.WriteTimeout,
+ ReadThreshold: int64((l.ReadTimeout * 1024) / time.Second),
+ WriteThreshold: int64((l.WriteTimeout * 1024) / time.Second),
+ }, nil
+}
+
+func DeadlinedListen(
+ network, addr string,
+ readTimeout, writeTimeout time.Duration,
+) (net.Listener, error) {
+ l, err := net.Listen(network, addr)
+ if err != nil {
+ return nil, err
+ }
+ return &DeadlinedListener{
+ Listener: l,
+ ReadTimeout: readTimeout,
+ WriteTimeout: writeTimeout,
+ }, nil
+}