func (stream *Stream) Read(f Frame) { Debug("stream (%d) recv (%v)", stream.ID, f.Header().Type) switch frame := f.(type) { case *HeadersFrame: // Decode Headers header := stream.DecodeHeader(frame.HeaderBlockFragment) frame.Headers = header for name, values := range header { for _, value := range values { stream.Bucket.Headers.Add(name, value) } } if frame.Header().Flags&END_STREAM == END_STREAM { go stream.CallBack(stream) } case *DataFrame: length := int32(frame.Header().Length) stream.WindowUpdate(length) _, err := stream.Bucket.Body.Write(frame.Data) if err != nil { Fatal("%v", err) } if frame.Header().Flags&END_STREAM == END_STREAM { stream.CallBack(stream) } case *RstStreamFrame: Debug("close stream by RST_STREAM") Error("RST_STREAM(%v)", frame.ErrorCode) stream.Close() case *PingFrame: Debug("response to PING") pong := NewPingFrame(ACK, stream.ID, frame.OpaqueData) stream.Write(pong) case *WindowUpdateFrame: Info("Window Update %d byte stream(%v)", frame.WindowSizeIncrement, stream.ID) stream.Window.UpdatePeer(int32(frame.WindowSizeIncrement)) case *ContinuationFrame: // Decode Headers header := stream.DecodeHeader(frame.HeaderBlockFragment) frame.Headers = header for name, values := range header { for _, value := range values { stream.Bucket.Headers.Add(name, value) } } if frame.Header().Flags&END_STREAM == END_STREAM { go stream.CallBack(stream) } } }
func (conn *Conn) ReadLoop() { Debug("start conn.ReadLoop()") for { // コネクションからフレームを読み込む frame, err := ReadFrame(conn.RW, conn.Settings) if err != nil { Error("%v", err) h2Error, ok := err.(*H2Error) if ok { conn.GoAway(0, h2Error) } break } if frame != nil { Notice("%v %v", Green("recv"), util.Indent(frame.String())) } streamID := frame.Header().StreamID types := frame.Header().Type // CONNECTION LEVEL if streamID == 0 { if types == DataFrameType || types == HeadersFrameType || types == PriorityFrameType || types == RstStreamFrameType || types == PushPromiseFrameType || types == ContinuationFrameType { msg := fmt.Sprintf("%s FRAME for Stream ID 0", types) Error("%v", msg) conn.GoAway(0, &H2Error{PROTOCOL_ERROR, msg}) break // TODO: check this flow is correct or not } // SETTINGS frame を受け取った場合 if types == SettingsFrameType { settingsFrame, ok := frame.(*SettingsFrame) if !ok { Error("invalid settings frame %v", frame) return } conn.HandleSettings(settingsFrame) } // Connection Level Window Update if types == WindowUpdateFrameType { windowUpdateFrame, ok := frame.(*WindowUpdateFrame) if !ok { Error("invalid window update frame %v", frame) return } Debug("connection window size increment(%v)", int32(windowUpdateFrame.WindowSizeIncrement)) conn.Window.UpdatePeer(int32(windowUpdateFrame.WindowSizeIncrement)) } // respond to PING if types == PingFrameType { // ignore ack if frame.Header().Flags != ACK { conn.PingACK([]byte("pong ")) // should be 8 byte } continue } // handle GOAWAY with close connection if types == GoAwayFrameType { Debug("stop conn.ReadLoop() by GOAWAY") break } } // STREAM LEVEL if streamID > 0 { if types == SettingsFrameType || types == PingFrameType || types == GoAwayFrameType { msg := fmt.Sprintf("%s FRAME for Stream ID not 0", types) Error("%v", msg) conn.GoAway(0, &H2Error{PROTOCOL_ERROR, msg}) break // TODO: check this flow is correct or not } // DATA frame なら winodw を消費 if types == DataFrameType { length := int32(frame.Header().Length) conn.WindowConsume(length) } // 新しいストリーム ID なら対応するストリームを生成 stream, ok := conn.Streams[streamID] if !ok { // create stream with streamID stream = conn.NewStream(streamID) conn.Streams[streamID] = stream // update last stream id if streamID > conn.LastStreamID { conn.LastStreamID = streamID } } // stream の state を変える err = stream.ChangeState(frame, RECV) if err != nil { Error("%v", err) h2Error, ok := err.(*H2Error) if ok { conn.GoAway(0, h2Error) } break } // stream が close ならリストから消す if stream.State == CLOSED { // ただし、1 秒は window update が来てもいいように待つ // TODO: atomic にする go func(streamID uint32) { <-time.After(1 * time.Second) Info("remove stream(%d) from conn.Streams[]", streamID) conn.Streams[streamID] = nil }(streamID) } // ストリームにフレームを渡す stream.ReadChan <- frame } } Debug("stop the readloop") }