@@ -159,6 +159,13 @@ type clientStream struct {
159
159
160
160
peerReset chan struct {} // closed on peer reset
161
161
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
162
169
}
163
170
164
171
// awaitRequestCancel runs in its own goroutine and waits for the user's
@@ -918,18 +925,42 @@ func (rl *clientConnReadLoop) processHeaderBlockFragment(frag []byte, streamID u
918
925
// server got our RST_STREAM.
919
926
return nil
920
927
}
928
+ if cs .headersDone {
929
+ rl .hdec .SetEmitFunc (cs .onNewTrailerField )
930
+ } else {
931
+ rl .hdec .SetEmitFunc (rl .onNewHeaderField )
932
+ }
921
933
_ , err := rl .hdec .Write (frag )
922
934
if err != nil {
923
- return err
935
+ return ConnectionError ( ErrCodeCompression )
924
936
}
925
937
if ! headersEnded {
926
938
rl .continueStreamID = cs .ID
927
939
return nil
928
940
}
929
-
930
941
// HEADERS (or CONTINUATION) are now over.
931
942
rl .continueStreamID = 0
932
943
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
+
933
964
if rl .reqMalformed != nil {
934
965
cs .resc <- resAndError {err : rl .reqMalformed }
935
966
rl .cc .writeStreamReset (cs .ID , ErrCodeProtocol , rl .reqMalformed )
@@ -970,6 +1001,7 @@ func (rl *clientConnReadLoop) processHeaderBlockFragment(frag []byte, streamID u
970
1001
}
971
1002
}
972
1003
1004
+ cs .resTrailer = res .Trailer
973
1005
rl .activeRes [cs .ID ] = cs
974
1006
cs .resc <- resAndError {res : res }
975
1007
rl .nextRes = nil // unused now; will be reset next HEADERS frame
@@ -1076,12 +1108,24 @@ func (rl *clientConnReadLoop) processData(f *DataFrame) error {
1076
1108
}
1077
1109
1078
1110
if f .StreamEnded () {
1079
- cs .bufPipe .CloseWithError (io .EOF )
1080
- delete (rl .activeRes , cs .ID )
1111
+ rl .endStream (cs )
1081
1112
}
1082
1113
return nil
1083
1114
}
1084
1115
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
+
1085
1129
func (rl * clientConnReadLoop ) processGoAway (f * GoAwayFrame ) error {
1086
1130
cc := rl .cc
1087
1131
cc .t .connPool ().MarkDead (cc )
@@ -1203,6 +1247,7 @@ func (rl *clientConnReadLoop) onNewHeaderField(f hpack.HeaderField) {
1203
1247
if VerboseLogs {
1204
1248
cc .logf ("Header field: %+v" , f )
1205
1249
}
1250
+ // TODO: enforce max header list size like server.
1206
1251
isPseudo := strings .HasPrefix (f .Name , ":" )
1207
1252
if isPseudo {
1208
1253
if rl .sawRegHeader {
@@ -1226,7 +1271,38 @@ func (rl *clientConnReadLoop) onNewHeaderField(f hpack.HeaderField) {
1226
1271
}
1227
1272
} else {
1228
1273
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
+ }
1230
1306
}
1231
1307
}
1232
1308
0 commit comments