@@ -130,6 +130,12 @@ type Transport struct {
130
130
// Defaults to 15s.
131
131
PingTimeout time.Duration
132
132
133
+ // CountError, if non-nil, is called on HTTP/2 transport errors.
134
+ // It's intended to increment a metric for monitoring, such
135
+ // as an expvar or Prometheus metric.
136
+ // The errType consists of only ASCII word characters.
137
+ CountError func (errType string )
138
+
133
139
// t1, if non-nil, is the standard library Transport using
134
140
// this transport. Its settings are used (but not its
135
141
// RoundTrip method, etc).
@@ -943,6 +949,9 @@ func (cc *ClientConn) Close() error {
943
949
// closes the client connection immediately. In-flight requests are interrupted.
944
950
func (cc * ClientConn ) closeForLostPing () error {
945
951
err := errors .New ("http2: client connection lost" )
952
+ if f := cc .t .CountError ; f != nil {
953
+ f ("conn_close_lost_ping" )
954
+ }
946
955
return cc .closeForError (err )
947
956
}
948
957
@@ -1830,6 +1839,33 @@ func (rl *clientConnReadLoop) cleanup() {
1830
1839
cc .mu .Unlock ()
1831
1840
}
1832
1841
1842
+ // countReadFrameError calls Transport.CountError with a string
1843
+ // representing err.
1844
+ func (cc * ClientConn ) countReadFrameError (err error ) {
1845
+ f := cc .t .CountError
1846
+ if f == nil || err == nil {
1847
+ return
1848
+ }
1849
+ if ce , ok := err .(ConnectionError ); ok {
1850
+ errCode := ErrCode (ce )
1851
+ f (fmt .Sprintf ("read_frame_conn_error_%s" , errCode .stringToken ()))
1852
+ return
1853
+ }
1854
+ if errors .Is (err , io .EOF ) {
1855
+ f ("read_frame_eof" )
1856
+ return
1857
+ }
1858
+ if errors .Is (err , io .ErrUnexpectedEOF ) {
1859
+ f ("read_frame_unexpected_eof" )
1860
+ return
1861
+ }
1862
+ if errors .Is (err , ErrFrameTooLarge ) {
1863
+ f ("read_frame_too_large" )
1864
+ return
1865
+ }
1866
+ f ("read_frame_other" )
1867
+ }
1868
+
1833
1869
func (rl * clientConnReadLoop ) run () error {
1834
1870
cc := rl .cc
1835
1871
rl .closeWhenIdle = cc .t .disableKeepAlives () || cc .singleUse
@@ -1860,6 +1896,7 @@ func (rl *clientConnReadLoop) run() error {
1860
1896
}
1861
1897
continue
1862
1898
} else if err != nil {
1899
+ cc .countReadFrameError (err )
1863
1900
return err
1864
1901
}
1865
1902
if VerboseLogs {
@@ -2352,6 +2389,10 @@ func (rl *clientConnReadLoop) processGoAway(f *GoAwayFrame) error {
2352
2389
if f .ErrCode != 0 {
2353
2390
// TODO: deal with GOAWAY more. particularly the error code
2354
2391
cc .vlogf ("transport got GOAWAY with error code = %v" , f .ErrCode )
2392
+ if fn := cc .t .CountError ; fn != nil {
2393
+ fn ("recv_goaway_" + f .ErrCode .stringToken ())
2394
+ }
2395
+
2355
2396
}
2356
2397
cc .setGoAway (f )
2357
2398
return nil
@@ -2466,9 +2507,9 @@ func (rl *clientConnReadLoop) processResetStream(f *RSTStreamFrame) error {
2466
2507
if f .ErrCode == ErrCodeProtocol {
2467
2508
rl .cc .SetDoNotReuse ()
2468
2509
serr .Cause = errFromPeer
2469
- // TODO(bradfitz): increment a varz here, once Transport
2470
- // takes an optional interface-typed field that expvar.Map.Add
2471
- // implements.
2510
+ }
2511
+ if fn := cs . cc . t . CountError ; fn != nil {
2512
+ fn ( "recv_rststream_" + f . ErrCode . stringToken ())
2472
2513
}
2473
2514
cs .resetErr = serr
2474
2515
close (cs .peerReset )
0 commit comments