예제 #1
0
파일: conn.go 프로젝트: mashuai/http2
func (conn *Conn) WriteLoop() (err error) {
	Debug("start conn.WriteLoop()")
	for frame := range conn.WriteChan {
		Notice("%v %v", Red("send"), util.Indent(frame.String()))

		// TODO: ここで connection レベルの WindowSize を見る
		err = frame.Write(conn.RW)
		if err != nil {
			Error("%v", err)
			return err
		}
	}
	return
}
예제 #2
0
파일: conn.go 프로젝트: mashuai/http2
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")
}