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  }