func (p *PushStream) Finish() { p.writeHeader() end := new(frames.DATA) end.StreamID = p.streamID end.Data = []byte{} end.Flags = common.FLAG_FIN p.output <- end p.Close() }
// run is the main control path of // the stream. It is prepared, the // registered handler is called, // and then the stream is cleaned // up and closed. func (s *ResponseStream) Run() error { // Catch any panics. defer func() { if v := recover(); v != nil { if s != nil && s.state != nil && !s.state.Closed() { log.Printf("Encountered stream error: %v (%[1]T)\n", v) } } }() // Make sure Request is prepared. if s.requestBody == nil || s.request.Body == nil { s.requestBody = new(bytes.Buffer) s.request.Body = &common.ReadCloser{s.requestBody} } // Wait until the full request has been received. <-s.ready /*************** *** HANDLER *** ***************/ s.handler.ServeHTTP(s, s.request) // Make sure any queued data has been sent. if err := s.flow.Wait(); err != nil { log.Println(err) } // Close the stream with a SYN_REPLY if // none has been sent, or an empty DATA // frame, if a SYN_REPLY has been sent // already. // If the stream is already closed at // this end, then nothing happens. if !s.unidirectional { if s.state.OpenHere() && !s.wroteHeader { s.header.Set(":status", "200") s.header.Set(":version", "HTTP/1.1") // Create the response SYN_REPLY. synReply := new(frames.SYN_REPLY) synReply.Flags = common.FLAG_FIN synReply.StreamID = s.streamID synReply.Header = make(http.Header) for name, values := range s.header { for _, value := range values { synReply.Header.Add(name, value) } s.header.Del(name) } s.output <- synReply } else if s.state.OpenHere() { // Create the DATA. data := new(frames.DATA) data.StreamID = s.streamID data.Flags = common.FLAG_FIN data.Data = []byte{} s.output <- data } } // Clean up state. s.state.CloseHere() if s.state.Closed() { return s.Close() } return nil }
// 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 } } }