-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathstreamconn.go
More file actions
176 lines (150 loc) · 3.85 KB
/
Copy pathstreamconn.go
File metadata and controls
176 lines (150 loc) · 3.85 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
package samizdat
import (
"io"
"net"
"sync"
"time"
)
// streamConn wraps an io.ReadWriteCloser (typically an HTTP/2 stream body)
// as a net.Conn. It implements all required net.Conn methods, delegating
// Read/Write to the underlying stream and supporting deadline-based timeouts.
type streamConn struct {
rwc io.ReadWriteCloser
localAddr net.Addr
remoteAddr net.Addr
shaper *Shaper
destination string
readDeadline *deadlineTimer
writeDeadline *deadlineTimer
mu sync.Mutex
closed bool
}
// newStreamConn creates a net.Conn backed by the given ReadWriteCloser.
func newStreamConn(rwc io.ReadWriteCloser, localAddr, remoteAddr net.Addr, destination string, shaper *Shaper) *streamConn {
return &streamConn{
rwc: rwc,
localAddr: localAddr,
remoteAddr: remoteAddr,
destination: destination,
shaper: shaper,
readDeadline: newDeadlineTimer(),
writeDeadline: newDeadlineTimer(),
}
}
func (sc *streamConn) Read(b []byte) (int, error) {
if err := sc.readDeadline.wait(); err != nil {
return 0, err
}
return sc.rwc.Read(b)
}
func (sc *streamConn) Write(b []byte) (int, error) {
if err := sc.writeDeadline.wait(); err != nil {
return 0, err
}
if sc.shaper != nil {
return sc.shaper.Write(sc.rwc, b)
}
return sc.rwc.Write(b)
}
func (sc *streamConn) Close() error {
sc.mu.Lock()
defer sc.mu.Unlock()
if sc.closed {
return nil
}
sc.closed = true
sc.readDeadline.stop()
sc.writeDeadline.stop()
return sc.rwc.Close()
}
func (sc *streamConn) LocalAddr() net.Addr { return sc.localAddr }
func (sc *streamConn) RemoteAddr() net.Addr { return sc.remoteAddr }
func (sc *streamConn) SetDeadline(t time.Time) error {
sc.readDeadline.set(t)
sc.writeDeadline.set(t)
return nil
}
func (sc *streamConn) SetReadDeadline(t time.Time) error {
sc.readDeadline.set(t)
return nil
}
func (sc *streamConn) SetWriteDeadline(t time.Time) error {
sc.writeDeadline.set(t)
return nil
}
// CloseWrite performs a half-close on the write side of the connection,
// signaling EOF to the remote peer while keeping the read side open.
// This is critical for protocols like TLS where the server sends remaining
// data after the client signals it's done writing.
func (sc *streamConn) CloseWrite() error {
sc.mu.Lock()
defer sc.mu.Unlock()
if sc.closed {
return nil
}
if cw, ok := sc.rwc.(interface{ CloseWrite() error }); ok {
return cw.CloseWrite()
}
return nil
}
// deadlineTimer supports net.Conn deadline semantics.
type deadlineTimer struct {
mu sync.Mutex
timer *time.Timer
expired bool
}
func newDeadlineTimer() *deadlineTimer {
return &deadlineTimer{}
}
// set configures the deadline. A zero time clears the deadline.
func (dt *deadlineTimer) set(t time.Time) {
dt.mu.Lock()
defer dt.mu.Unlock()
dt.expired = false
if dt.timer != nil {
dt.timer.Stop()
dt.timer = nil
}
if t.IsZero() {
return
}
d := time.Until(t)
if d <= 0 {
dt.expired = true
return
}
dt.timer = time.AfterFunc(d, func() {
dt.mu.Lock()
dt.expired = true
dt.mu.Unlock()
})
}
func (dt *deadlineTimer) stop() {
dt.mu.Lock()
defer dt.mu.Unlock()
if dt.timer != nil {
dt.timer.Stop()
}
}
// wait returns a timeout error if the deadline has expired, nil otherwise.
func (dt *deadlineTimer) wait() error {
dt.mu.Lock()
expired := dt.expired
dt.mu.Unlock()
if expired {
return &timeoutError{}
}
return nil
}
// timeoutError implements the net.Error interface for deadline timeouts.
type timeoutError struct{}
func (e *timeoutError) Error() string { return "i/o timeout" }
func (e *timeoutError) Timeout() bool { return true }
func (e *timeoutError) Temporary() bool { return true }
// streamAddr implements net.Addr for H2 stream connections.
type streamAddr struct {
network string
address string
}
func (a *streamAddr) Network() string { return a.network }
func (a *streamAddr) String() string { return a.address }