cloudflare/cloudflared

Public

mirrored from https://github.com/cloudflare/cloudflaredAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
2018.10.0

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

h2mux/muxedstream.go

289lines · modecode

1package h2mux
2
3import (
4 "bytes"
5 "io"
6 "sync"
7)
8
9type ReadWriteLengther interface {
10 io.ReadWriter
11 Reset()
12 Len() int
13}
14
15type ReadWriteClosedCloser interface {
16 io.ReadWriteCloser
17 Closed() bool
18}
19
20type MuxedStream struct {
21 Headers []Header
22
23 streamID uint32
24
25 responseHeadersReceived chan struct{}
26
27 readBuffer ReadWriteClosedCloser
28 receiveWindow uint32
29 // current window size limit. Exponentially increase it when it's exhausted
30 receiveWindowCurrentMax uint32
31 // limit set in http2 spec. 2^31-1
32 receiveWindowMax uint32
33
34 // nonzero if a WINDOW_UPDATE frame for a stream needs to be sent
35 windowUpdate uint32
36
37 writeLock sync.Mutex
38 // The zero value for Buffer is an empty buffer ready to use.
39 writeBuffer ReadWriteLengther
40
41 sendWindow uint32
42
43 readyList *ReadyList
44 headersSent bool
45 writeHeaders []Header
46 // true if the write end of this stream has been closed
47 writeEOF bool
48 // true if we have sent EOF to the peer
49 sentEOF bool
50 // true if the peer sent us an EOF
51 receivedEOF bool
52
53 // dictionary that was used to compress the stream
54 receivedUseDict bool
55 method string
56 contentType string
57 path string
58 dictionaries h2Dictionaries
59 readBufferLock sync.RWMutex
60}
61
62func (s *MuxedStream) Read(p []byte) (n int, err error) {
63 if s.dictionaries.read != nil {
64 s.readBufferLock.RLock()
65 b := s.readBuffer
66 s.readBufferLock.RUnlock()
67 return b.Read(p)
68 }
69 return s.readBuffer.Read(p)
70}
71
72func (s *MuxedStream) Write(p []byte) (n int, err error) {
73 ok := assignDictToStream(s, p)
74 if !ok {
75 s.writeLock.Lock()
76 }
77 defer s.writeLock.Unlock()
78 if s.writeEOF {
79 return 0, io.EOF
80 }
81 n, err = s.writeBuffer.Write(p)
82 if n != len(p) || err != nil {
83 return n, err
84 }
85 s.writeNotify()
86 return n, nil
87}
88
89func (s *MuxedStream) Close() error {
90 // TUN-115: Close the write buffer before the read buffer.
91 // In the case of shutdown, read will not get new data, but the write buffer can still receive
92 // new data. Closing read before write allows application to race between a failed read and a
93 // successful write, even though this close should appear to be atomic.
94 // This can't happen the other way because reads may succeed after a failed write; if we read
95 // past EOF the application will block until we close the buffer.
96 err := s.CloseWrite()
97 if err != nil {
98 if s.CloseRead() == nil {
99 // don't bother the caller with errors if at least one close succeeded
100 return nil
101 }
102 return err
103 }
104 return s.CloseRead()
105}
106
107func (s *MuxedStream) CloseRead() error {
108 return s.readBuffer.Close()
109}
110
111func (s *MuxedStream) CloseWrite() error {
112 s.writeLock.Lock()
113 defer s.writeLock.Unlock()
114 if s.writeEOF {
115 return io.EOF
116 }
117 s.writeEOF = true
118 if c, ok := s.writeBuffer.(io.Closer); ok {
119 c.Close()
120 }
121 s.writeNotify()
122 return nil
123}
124
125func (s *MuxedStream) WriteHeaders(headers []Header) error {
126 s.writeLock.Lock()
127 defer s.writeLock.Unlock()
128 if s.writeHeaders != nil {
129 return ErrStreamHeadersSent
130 }
131
132 if s.dictionaries.write != nil {
133 dictWriter := s.dictionaries.write.getDictWriter(s, headers)
134 if dictWriter != nil {
135 s.writeBuffer = dictWriter
136 }
137
138 }
139
140 s.writeHeaders = headers
141 s.headersSent = false
142 s.writeNotify()
143 return nil
144}
145
146func (s *MuxedStream) getReceiveWindow() uint32 {
147 s.writeLock.Lock()
148 defer s.writeLock.Unlock()
149 return s.receiveWindow
150}
151
152func (s *MuxedStream) getSendWindow() uint32 {
153 s.writeLock.Lock()
154 defer s.writeLock.Unlock()
155 return s.sendWindow
156}
157
158// writeNotify must happen while holding writeLock.
159func (s *MuxedStream) writeNotify() {
160 s.readyList.Signal(s.streamID)
161}
162
163// Call by muxreader when it gets a WindowUpdateFrame. This is an update of the peer's
164// receive window (how much data we can send).
165func (s *MuxedStream) replenishSendWindow(bytes uint32) {
166 s.writeLock.Lock()
167 s.sendWindow += bytes
168 s.writeNotify()
169 s.writeLock.Unlock()
170}
171
172// Call by muxreader when it receives a data frame
173func (s *MuxedStream) consumeReceiveWindow(bytes uint32) bool {
174 s.writeLock.Lock()
175 defer s.writeLock.Unlock()
176 // received data size is greater than receive window/buffer
177 if s.receiveWindow < bytes {
178 return false
179 }
180 s.receiveWindow -= bytes
181 if s.receiveWindow < s.receiveWindowCurrentMax/2 {
182 // exhausting client send window (how much data client can send)
183 if s.receiveWindowCurrentMax < s.receiveWindowMax {
184 s.receiveWindowCurrentMax <<= 1
185 }
186 s.windowUpdate += s.receiveWindowCurrentMax - s.receiveWindow
187 s.writeNotify()
188 }
189 return true
190}
191
192// receiveEOF should be called when the peer indicates no more data will be sent.
193// Returns true if the socket is now closed (i.e. the write side is already closed).
194func (s *MuxedStream) receiveEOF() (closed bool) {
195 s.writeLock.Lock()
196 defer s.writeLock.Unlock()
197 s.receivedEOF = true
198 s.CloseRead()
199 return s.writeEOF && s.writeBuffer.Len() == 0
200}
201
202func (s *MuxedStream) gotReceiveEOF() bool {
203 s.writeLock.Lock()
204 defer s.writeLock.Unlock()
205 return s.receivedEOF
206}
207
208// MuxedStreamReader implements io.ReadCloser for the read end of the stream.
209// This is useful for passing to functions that close the object after it is done reading,
210// but you still want to be able to write data afterwards (e.g. http.Client).
211type MuxedStreamReader struct {
212 *MuxedStream
213}
214
215func (s MuxedStreamReader) Read(p []byte) (n int, err error) {
216 return s.MuxedStream.Read(p)
217}
218
219func (s MuxedStreamReader) Close() error {
220 return s.MuxedStream.CloseRead()
221}
222
223// streamChunk represents a chunk of data to be written.
224type streamChunk struct {
225 streamID uint32
226 // true if a HEADERS frame should be sent
227 sendHeaders bool
228 headers []Header
229 // nonzero if a WINDOW_UPDATE frame should be sent
230 windowUpdate uint32
231 // true if data frames should be sent
232 sendData bool
233 eof bool
234 buffer bytes.Buffer
235}
236
237// getChunk atomically extracts a chunk of data to be written by MuxWriter.
238// The data returned will not exceed the send window for this stream.
239func (s *MuxedStream) getChunk() *streamChunk {
240 s.writeLock.Lock()
241 defer s.writeLock.Unlock()
242
243 chunk := &streamChunk{
244 streamID: s.streamID,
245 sendHeaders: !s.headersSent,
246 headers: s.writeHeaders,
247 windowUpdate: s.windowUpdate,
248 sendData: !s.sentEOF,
249 eof: s.writeEOF && uint32(s.writeBuffer.Len()) <= s.sendWindow,
250 }
251
252 // Copies at most s.sendWindow bytes
253 writeLen, _ := io.CopyN(&chunk.buffer, s.writeBuffer, int64(s.sendWindow))
254 s.sendWindow -= uint32(writeLen)
255 s.receiveWindow += s.windowUpdate
256 s.windowUpdate = 0
257 s.headersSent = true
258
259 // if this chunk contains the end of the stream, close the stream now
260 if chunk.sendData && chunk.eof {
261 s.sentEOF = true
262 }
263
264 return chunk
265}
266
267func (c *streamChunk) sendHeadersFrame() bool {
268 return c.sendHeaders
269}
270
271func (c *streamChunk) sendWindowUpdateFrame() bool {
272 return c.windowUpdate > 0
273}
274
275func (c *streamChunk) sendDataFrame() bool {
276 return c.sendData
277}
278
279func (c *streamChunk) nextDataFrame(frameSize int) (payload []byte, endStream bool) {
280 payload = c.buffer.Next(frameSize)
281 if c.buffer.Len() == 0 {
282 // this is the last data frame in this chunk
283 c.sendData = false
284 if c.eof {
285 endStream = true
286 }
287 }
288 return
289}
290