Skip to content

Commit b6b3bda

Browse files
committed
http2: add support for Transport reading trailers from the server
Tests in the main go repo. Updates golang/go#13557 Change-Id: Ide7be9e24b603008faf9f3ea55e936b86ea1c42b Reviewed-on: https://go-review.googlesource.com/17930 Reviewed-by: Blake Mizerany <[email protected]>
1 parent b4be494 commit b6b3bda

File tree

1 file changed

+81
-5
lines changed

1 file changed

+81
-5
lines changed

http2/transport.go

Lines changed: 81 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,13 @@ type clientStream struct {
159159

160160
peerReset chan struct{} // closed on peer reset
161161
resetErr error // populated before peerReset is closed
162+
163+
// owned by clientConnReadLoop:
164+
headersDone bool // got HEADERS w/ END_HEADERS
165+
trailersDone bool // got second HEADERS frame w/ END_HEADERS
166+
167+
trailer http.Header // accumulated trailers
168+
resTrailer http.Header // client's Response.Trailer
162169
}
163170

164171
// awaitRequestCancel runs in its own goroutine and waits for the user's
@@ -918,18 +925,42 @@ func (rl *clientConnReadLoop) processHeaderBlockFragment(frag []byte, streamID u
918925
// server got our RST_STREAM.
919926
return nil
920927
}
928+
if cs.headersDone {
929+
rl.hdec.SetEmitFunc(cs.onNewTrailerField)
930+
} else {
931+
rl.hdec.SetEmitFunc(rl.onNewHeaderField)
932+
}
921933
_, err := rl.hdec.Write(frag)
922934
if err != nil {
923-
return err
935+
return ConnectionError(ErrCodeCompression)
924936
}
925937
if !headersEnded {
926938
rl.continueStreamID = cs.ID
927939
return nil
928940
}
929-
930941
// HEADERS (or CONTINUATION) are now over.
931942
rl.continueStreamID = 0
932943

944+
if !cs.headersDone {
945+
cs.headersDone = true
946+
} else {
947+
// We're dealing with trailers. (and specifically the
948+
// final frame of headers)
949+
if cs.trailersDone {
950+
// Too many HEADERS frames for this stream.
951+
return ConnectionError(ErrCodeProtocol)
952+
}
953+
cs.trailersDone = true
954+
if !streamEnded {
955+
// We expect that any header block fragment
956+
// frame for trailers with END_HEADERS also
957+
// has END_STREAM.
958+
return ConnectionError(ErrCodeProtocol)
959+
}
960+
rl.endStream(cs)
961+
return nil
962+
}
963+
933964
if rl.reqMalformed != nil {
934965
cs.resc <- resAndError{err: rl.reqMalformed}
935966
rl.cc.writeStreamReset(cs.ID, ErrCodeProtocol, rl.reqMalformed)
@@ -970,6 +1001,7 @@ func (rl *clientConnReadLoop) processHeaderBlockFragment(frag []byte, streamID u
9701001
}
9711002
}
9721003

1004+
cs.resTrailer = res.Trailer
9731005
rl.activeRes[cs.ID] = cs
9741006
cs.resc <- resAndError{res: res}
9751007
rl.nextRes = nil // unused now; will be reset next HEADERS frame
@@ -1076,12 +1108,24 @@ func (rl *clientConnReadLoop) processData(f *DataFrame) error {
10761108
}
10771109

10781110
if f.StreamEnded() {
1079-
cs.bufPipe.CloseWithError(io.EOF)
1080-
delete(rl.activeRes, cs.ID)
1111+
rl.endStream(cs)
10811112
}
10821113
return nil
10831114
}
10841115

1116+
func (rl *clientConnReadLoop) endStream(cs *clientStream) {
1117+
// TODO: check that any declared content-length matches, like
1118+
// server.go's (*stream).endStream method.
1119+
cs.bufPipe.closeWithErrorAndCode(io.EOF, cs.copyTrailers)
1120+
delete(rl.activeRes, cs.ID)
1121+
}
1122+
1123+
func (cs *clientStream) copyTrailers() {
1124+
for k, vv := range cs.trailer {
1125+
cs.resTrailer[k] = vv
1126+
}
1127+
}
1128+
10851129
func (rl *clientConnReadLoop) processGoAway(f *GoAwayFrame) error {
10861130
cc := rl.cc
10871131
cc.t.connPool().MarkDead(cc)
@@ -1203,6 +1247,7 @@ func (rl *clientConnReadLoop) onNewHeaderField(f hpack.HeaderField) {
12031247
if VerboseLogs {
12041248
cc.logf("Header field: %+v", f)
12051249
}
1250+
// TODO: enforce max header list size like server.
12061251
isPseudo := strings.HasPrefix(f.Name, ":")
12071252
if isPseudo {
12081253
if rl.sawRegHeader {
@@ -1226,7 +1271,38 @@ func (rl *clientConnReadLoop) onNewHeaderField(f hpack.HeaderField) {
12261271
}
12271272
} else {
12281273
rl.sawRegHeader = true
1229-
rl.nextRes.Header.Add(http.CanonicalHeaderKey(f.Name), f.Value)
1274+
key := http.CanonicalHeaderKey(f.Name)
1275+
if key == "Trailer" {
1276+
t := rl.nextRes.Trailer
1277+
if t == nil {
1278+
t = make(http.Header)
1279+
rl.nextRes.Trailer = t
1280+
}
1281+
foreachHeaderElement(f.Value, func(v string) {
1282+
t[http.CanonicalHeaderKey(v)] = nil
1283+
})
1284+
} else {
1285+
rl.nextRes.Header.Add(key, f.Value)
1286+
}
1287+
}
1288+
}
1289+
1290+
func (cs *clientStream) onNewTrailerField(f hpack.HeaderField) {
1291+
isPseudo := strings.HasPrefix(f.Name, ":")
1292+
if isPseudo {
1293+
// TODO: Bogus. report an error later when we close their body.
1294+
// drop for now.
1295+
return
1296+
}
1297+
key := http.CanonicalHeaderKey(f.Name)
1298+
if _, ok := cs.resTrailer[key]; ok {
1299+
if cs.trailer == nil {
1300+
cs.trailer = make(http.Header)
1301+
}
1302+
const tooBig = 1000 // TODO: arbitrary; use max header list size limits
1303+
if cur := cs.trailer[key]; len(cur) < tooBig {
1304+
cs.trailer[key] = append(cur, f.Value)
1305+
}
12301306
}
12311307
}
12321308

0 commit comments

Comments
 (0)