func newHPACKDecoder() *hpackDecoder { d := &hpackDecoder{} d.h = hpack.NewDecoder(http2InitHeaderTableSize, func(f hpack.HeaderField) { switch f.Name { case "content-type": if !strings.Contains(f.Value, "application/grpc") { d.err = StreamErrorf(codes.FailedPrecondition, "transport: received the unexpected header") return } case "grpc-encoding": d.state.encoding = f.Value case "grpc-status": code, err := strconv.Atoi(f.Value) if err != nil { d.err = StreamErrorf(codes.Internal, "transport: malformed grpc-status: %v", err) return } d.state.statusCode = codes.Code(code) case "grpc-message": d.state.statusDesc = f.Value case "grpc-timeout": d.state.timeoutSet = true var err error d.state.timeout, err = timeoutDecode(f.Value) if err != nil { d.err = StreamErrorf(codes.Internal, "transport: malformed time-out: %v", err) return } case ":path": d.state.method = f.Value default: if !isReservedHeader(f.Name) { if f.Name == "user-agent" { i := strings.LastIndex(f.Value, " ") if i == -1 { // There is no application user agent string being set. return } // Extract the application user agent string. f.Value = f.Value[:i] } if d.state.mdata == nil { d.state.mdata = make(map[string][]string) } k, v, err := metadata.DecodeKeyValue(f.Name, f.Value) if err != nil { grpclog.Printf("Failed to decode (%q, %q): %v", f.Name, f.Value, err) return } d.state.mdata[k] = append(d.state.mdata[k], v) } } }) return d }
// readLoop runs in its own goroutine and reads and dispatches frames. func (cc *ClientConn) readLoop() { rl := &clientConnReadLoop{ cc: cc, activeRes: make(map[uint32]*clientStream), } rl.hdec = hpack.NewDecoder(initialHeaderTableSize, rl.onNewHeaderField) defer rl.cleanup() cc.readerErr = rl.run() if ce, ok := cc.readerErr.(ConnectionError); ok { cc.wmu.Lock() cc.fr.WriteGoAway(0, ErrCode(ce), nil) cc.wmu.Unlock() } }
func (app *h2i) readFrames() error { for { f, err := app.framer.ReadFrame() if err != nil { return fmt.Errorf("ReadFrame: %v", err) } app.logf("%v", f) switch f := f.(type) { case *http2.PingFrame: app.logf(" Data = %q", f.Data) case *http2.SettingsFrame: f.ForeachSetting(func(s http2.Setting) error { app.logf(" %v", s) app.peerSetting[s.ID] = s.Val return nil }) case *http2.WindowUpdateFrame: app.logf(" Window-Increment = %v\n", f.Increment) case *http2.GoAwayFrame: app.logf(" Last-Stream-ID = %d; Error-Code = %v (%d)\n", f.LastStreamID, f.ErrCode, f.ErrCode) case *http2.DataFrame: app.logf(" %q", f.Data()) case *http2.HeadersFrame: if f.HasPriority() { app.logf(" PRIORITY = %v", f.Priority) } if app.hdec == nil { // TODO: if the user uses h2i to send a SETTINGS frame advertising // something larger, we'll need to respect SETTINGS_HEADER_TABLE_SIZE // and stuff here instead of using the 4k default. But for now: tableSize := uint32(4 << 10) app.hdec = hpack.NewDecoder(tableSize, app.onNewHeaderField) } app.hdec.Write(f.HeaderBlockFragment()) } } }