// send is run in a separate goroutine. It's used // to ensure clear interleaving of frames and to // provide assurances of priority and structure. func (c *Conn) send() { // Catch any panics. defer func() { if v := recover(); v != nil { if !c.Closed() { log.Printf("Encountered send error: %v (%[1]T)\n", v) } } }() // Enter the processing loop. i := 1 for { // Once per 5 frames, pick randomly. var frame common.Frame if i == 0 { // Ignore priority. frame = c.selectFrameToSend(false) } else { // Normal selection. frame = c.selectFrameToSend(true) } i++ if i >= 5 { i = 0 } if frame == nil { c.Close() return } // Compress any name/value header blocks. err := frame.Compress(c.compressor) if err != nil { log.Printf("Error in compression: %v (%T).\n", err, frame) return } debug.Printf("Sending %s:\n", frame.Name()) debug.Println(frame) // Leave the specifics of writing to the // connection up to the frame. c.refreshWriteTimeout() _, err = frame.WriteTo(c.conn) if err != nil { c.handleReadWriteError(err) return } } }
// send is run in a separate goroutine. It's used // to ensure clear interleaving of frames and to // provide assurances of priority and structure. func (c *Conn) send() { // Catch any panics. defer func() { if v := recover(); v != nil { if !c.Closed() { log.Printf("Encountered send error: %v (%[1]T)\n", v) } } }() for i := 1; ; i++ { if i >= 5 { i = 0 // Once per 5 frames, pick randomly. } var frame common.Frame if i == 0 { // Ignore priority. frame = c.selectFrameToSend(false) } else { // Normal selection. frame = c.selectFrameToSend(true) } if frame == nil { c.Close() return } // Process connection-level flow control. if c.Subversion > 0 { c.connectionWindowLock.Lock() if frame, ok := frame.(*frames.DATA); ok { size := int64(len(frame.Data)) constrained := false sending := size if sending > c.connectionWindowSize { sending = c.connectionWindowSize constrained = true } if sending < 0 { sending = 0 } c.connectionWindowSize -= sending if constrained { // Chop off what we can send now. partial := new(frames.DATA) partial.Flags = frame.Flags partial.StreamID = frame.StreamID partial.Data = make([]byte, int(sending)) copy(partial.Data, frame.Data[:sending]) frame.Data = frame.Data[sending:] // Buffer this frame and try again. if c.dataBuffer == nil { c.dataBuffer = []*frames.DATA{frame} } else { buffer := make([]*frames.DATA, 1, len(c.dataBuffer)+1) buffer[0] = frame buffer = append(buffer, c.dataBuffer...) c.dataBuffer = buffer } frame = partial } } c.connectionWindowLock.Unlock() } // Compress any name/value header blocks. err := frame.Compress(c.compressor) if err != nil { log.Printf("Error in compression: %v (type %T).\n", err, frame) c.Close() return } debug.Printf("Sending %s:\n", frame.Name()) debug.Println(frame) // Leave the specifics of writing to the // connection up to the frame. c.refreshWriteTimeout() if _, err = frame.WriteTo(c.conn); err != nil { c.handleReadWriteError(err) return } } }