github.com/SlyMarbo/spdy@v0.0.0-20160217225201-b5edf18e002b/spdy3/io.go (about) 1 package spdy3 2 3 import ( 4 "runtime" 5 6 "github.com/SlyMarbo/spdy/common" 7 "github.com/SlyMarbo/spdy/spdy3/frames" 8 ) 9 10 // readFrames is the main processing loop, where frames 11 // are read from the connection and processed individually. 12 // Returning from readFrames begins the cleanup and exit 13 // process for this connection. 14 func (c *Conn) readFrames() { 15 // Ensure no panics happen. 16 defer func() { 17 if v := recover(); v != nil { 18 if !c.Closed() { 19 log.Printf("Encountered receive error: %v (%[1]T)\n", v) 20 } 21 } 22 }() 23 24 for { 25 // This is the mechanism for handling too many benign errors. 26 // By default MaxBenignErrors is 0, which ignores errors. 27 too_many := c.numBenignErrors > common.MaxBenignErrors && common.MaxBenignErrors > 0 28 if c.criticalCheck(too_many, 0, "Ending connection for benign error buildup") { 29 return 30 } 31 32 // ReadFrame takes care of the frame parsing for us. 33 c.refreshReadTimeout() 34 frame, err := frames.ReadFrame(c.buf, c.Subversion) 35 if err != nil { 36 c.handleReadWriteError(err) 37 return 38 } 39 40 debug.Printf("Receiving %s:\n", frame.Name()) // Print frame type. 41 42 // Decompress the frame's headers, if there are any. 43 err = frame.Decompress(c.decompressor) 44 if c.criticalCheck(err != nil, 0, "Decompression: %v", err) { 45 return 46 } 47 48 debug.Println(frame) // Print frame once the content's been decompressed. 49 50 if c.processFrame(frame) { 51 return 52 } 53 } 54 } 55 56 // send is run in a separate goroutine. It's used 57 // to ensure clear interleaving of frames and to 58 // provide assurances of priority and structure. 59 func (c *Conn) send() { 60 // Catch any panics. 61 defer func() { 62 if v := recover(); v != nil { 63 if !c.Closed() { 64 log.Printf("Encountered send error: %v (%[1]T)\n", v) 65 } 66 } 67 }() 68 69 for i := 1; ; i++ { 70 if i >= 5 { 71 i = 0 // Once per 5 frames, pick randomly. 72 } 73 74 var frame common.Frame 75 if i == 0 { // Ignore priority. 76 frame = c.selectFrameToSend(false) 77 } else { // Normal selection. 78 frame = c.selectFrameToSend(true) 79 } 80 81 if frame == nil { 82 c.Close() 83 return 84 } 85 86 // Process connection-level flow control. 87 if c.Subversion > 0 { 88 c.connectionWindowLock.Lock() 89 if frame, ok := frame.(*frames.DATA); ok { 90 size := int64(len(frame.Data)) 91 constrained := false 92 sending := size 93 if sending > c.connectionWindowSize { 94 sending = c.connectionWindowSize 95 constrained = true 96 } 97 if sending < 0 { 98 sending = 0 99 } 100 101 c.connectionWindowSize -= sending 102 103 if constrained { 104 // Chop off what we can send now. 105 partial := new(frames.DATA) 106 partial.Flags = frame.Flags 107 partial.StreamID = frame.StreamID 108 partial.Data = make([]byte, int(sending)) 109 copy(partial.Data, frame.Data[:sending]) 110 frame.Data = frame.Data[sending:] 111 112 // Buffer this frame and try again. 113 if c.dataBuffer == nil { 114 c.dataBuffer = []*frames.DATA{frame} 115 } else { 116 buffer := make([]*frames.DATA, 1, len(c.dataBuffer)+1) 117 buffer[0] = frame 118 buffer = append(buffer, c.dataBuffer...) 119 c.dataBuffer = buffer 120 } 121 122 frame = partial 123 } 124 } 125 c.connectionWindowLock.Unlock() 126 } 127 128 // Compress any name/value header blocks. 129 err := frame.Compress(c.compressor) 130 if err != nil { 131 log.Printf("Error in compression: %v (type %T).\n", err, frame) 132 c.Close() 133 return 134 } 135 136 debug.Printf("Sending %s:\n", frame.Name()) 137 debug.Println(frame) 138 139 // Leave the specifics of writing to the 140 // connection up to the frame. 141 c.refreshWriteTimeout() 142 if _, err = frame.WriteTo(c.conn); err != nil { 143 c.handleReadWriteError(err) 144 return 145 } 146 } 147 } 148 149 // selectFrameToSend follows the specification's guidance 150 // on frame priority, sending frames with higher priority 151 // (a smaller number) first. If the given boolean is false, 152 // this priority is temporarily ignored, which can be used 153 // when high load is ignoring low-priority frames. 154 func (c *Conn) selectFrameToSend(prioritise bool) (frame common.Frame) { 155 if c.Closed() { 156 return nil 157 } 158 159 // Try buffered DATA frames first. 160 if c.Subversion > 0 { 161 if c.dataBuffer != nil { 162 if len(c.dataBuffer) == 0 { 163 c.dataBuffer = nil 164 } else { 165 first := c.dataBuffer[0] 166 if c.connectionWindowSize >= int64(8+len(first.Data)) { 167 if len(c.dataBuffer) > 1 { 168 c.dataBuffer = c.dataBuffer[1:] 169 } else { 170 c.dataBuffer = nil 171 } 172 return first 173 } 174 } 175 } 176 } 177 178 // Then in priority order. 179 if prioritise { 180 for i := 0; i < 8; i++ { 181 select { 182 case frame = <-c.output[i]: 183 return frame 184 default: 185 } 186 } 187 188 // No frames are immediately pending, so if the 189 // cection is being closed, cease sending 190 // safely. 191 c.sendingLock.Lock() 192 if c.sending != nil { 193 close(c.sending) 194 c.sendingLock.Unlock() 195 runtime.Goexit() 196 } 197 c.sendingLock.Unlock() 198 } 199 200 // Wait for any frame. 201 select { 202 case frame = <-c.output[0]: 203 return frame 204 case frame = <-c.output[1]: 205 return frame 206 case frame = <-c.output[2]: 207 return frame 208 case frame = <-c.output[3]: 209 return frame 210 case frame = <-c.output[4]: 211 return frame 212 case frame = <-c.output[5]: 213 return frame 214 case frame = <-c.output[6]: 215 return frame 216 case frame = <-c.output[7]: 217 return frame 218 case _ = <-c.stop: 219 return nil 220 } 221 }