Example #1
0
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)
		}
	}
}
Example #2
0
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")
}