@@ -130,6 +130,12 @@ type Server struct {
130
130
// If nil, a default scheduler is chosen.
131
131
NewWriteScheduler func () WriteScheduler
132
132
133
+ // CountError, if non-nil, is called on HTTP/2 server 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
// Internal state. This is a pointer (rather than embedded directly)
134
140
// so that we don't embed a Mutex in this struct, which will make the
135
141
// struct non-copyable, which might break some callers.
@@ -1399,7 +1405,7 @@ func (sc *serverConn) processFrame(f Frame) error {
1399
1405
// First frame received must be SETTINGS.
1400
1406
if ! sc .sawFirstSettings {
1401
1407
if _ , ok := f .(* SettingsFrame ); ! ok {
1402
- return ConnectionError (ErrCodeProtocol )
1408
+ return sc . countError ( "first_settings" , ConnectionError (ErrCodeProtocol ) )
1403
1409
}
1404
1410
sc .sawFirstSettings = true
1405
1411
}
@@ -1424,7 +1430,7 @@ func (sc *serverConn) processFrame(f Frame) error {
1424
1430
case * PushPromiseFrame :
1425
1431
// A client cannot push. Thus, servers MUST treat the receipt of a PUSH_PROMISE
1426
1432
// frame as a connection error (Section 5.4.1) of type PROTOCOL_ERROR.
1427
- return ConnectionError (ErrCodeProtocol )
1433
+ return sc . countError ( "push_promise" , ConnectionError (ErrCodeProtocol ) )
1428
1434
default :
1429
1435
sc .vlogf ("http2: server ignoring frame: %v" , f .Header ())
1430
1436
return nil
@@ -1444,7 +1450,7 @@ func (sc *serverConn) processPing(f *PingFrame) error {
1444
1450
// identifier field value other than 0x0, the recipient MUST
1445
1451
// respond with a connection error (Section 5.4.1) of type
1446
1452
// PROTOCOL_ERROR."
1447
- return ConnectionError (ErrCodeProtocol )
1453
+ return sc . countError ( "ping_on_stream" , ConnectionError (ErrCodeProtocol ) )
1448
1454
}
1449
1455
if sc .inGoAway && sc .goAwayCode != ErrCodeNo {
1450
1456
return nil
@@ -1463,7 +1469,7 @@ func (sc *serverConn) processWindowUpdate(f *WindowUpdateFrame) error {
1463
1469
// or PRIORITY on a stream in this state MUST be
1464
1470
// treated as a connection error (Section 5.4.1) of
1465
1471
// type PROTOCOL_ERROR."
1466
- return ConnectionError (ErrCodeProtocol )
1472
+ return sc . countError ( "stream_idle" , ConnectionError (ErrCodeProtocol ) )
1467
1473
}
1468
1474
if st == nil {
1469
1475
// "WINDOW_UPDATE can be sent by a peer that has sent a
@@ -1474,7 +1480,7 @@ func (sc *serverConn) processWindowUpdate(f *WindowUpdateFrame) error {
1474
1480
return nil
1475
1481
}
1476
1482
if ! st .flow .add (int32 (f .Increment )) {
1477
- return streamError (f .StreamID , ErrCodeFlowControl )
1483
+ return sc . countError ( "bad_flow" , streamError (f .StreamID , ErrCodeFlowControl ) )
1478
1484
}
1479
1485
default : // connection-level flow control
1480
1486
if ! sc .flow .add (int32 (f .Increment )) {
@@ -1495,7 +1501,7 @@ func (sc *serverConn) processResetStream(f *RSTStreamFrame) error {
1495
1501
// identifying an idle stream is received, the
1496
1502
// recipient MUST treat this as a connection error
1497
1503
// (Section 5.4.1) of type PROTOCOL_ERROR.
1498
- return ConnectionError (ErrCodeProtocol )
1504
+ return sc . countError ( "reset_idle_stream" , ConnectionError (ErrCodeProtocol ) )
1499
1505
}
1500
1506
if st != nil {
1501
1507
st .cancelCtx ()
@@ -1547,15 +1553,15 @@ func (sc *serverConn) processSettings(f *SettingsFrame) error {
1547
1553
// Why is the peer ACKing settings we never sent?
1548
1554
// The spec doesn't mention this case, but
1549
1555
// hang up on them anyway.
1550
- return ConnectionError (ErrCodeProtocol )
1556
+ return sc . countError ( "ack_mystery" , ConnectionError (ErrCodeProtocol ) )
1551
1557
}
1552
1558
return nil
1553
1559
}
1554
1560
if f .NumSettings () > 100 || f .HasDuplicates () {
1555
1561
// This isn't actually in the spec, but hang up on
1556
1562
// suspiciously large settings frames or those with
1557
1563
// duplicate entries.
1558
- return ConnectionError (ErrCodeProtocol )
1564
+ return sc . countError ( "settings_big_or_dups" , ConnectionError (ErrCodeProtocol ) )
1559
1565
}
1560
1566
if err := f .ForeachSetting (sc .processSetting ); err != nil {
1561
1567
return err
@@ -1622,7 +1628,7 @@ func (sc *serverConn) processSettingInitialWindowSize(val uint32) error {
1622
1628
// control window to exceed the maximum size as a
1623
1629
// connection error (Section 5.4.1) of type
1624
1630
// FLOW_CONTROL_ERROR."
1625
- return ConnectionError (ErrCodeFlowControl )
1631
+ return sc . countError ( "setting_win_size" , ConnectionError (ErrCodeFlowControl ) )
1626
1632
}
1627
1633
}
1628
1634
return nil
@@ -1655,7 +1661,7 @@ func (sc *serverConn) processData(f *DataFrame) error {
1655
1661
// or PRIORITY on a stream in this state MUST be
1656
1662
// treated as a connection error (Section 5.4.1) of
1657
1663
// type PROTOCOL_ERROR."
1658
- return ConnectionError (ErrCodeProtocol )
1664
+ return sc . countError ( "data_on_idle" , ConnectionError (ErrCodeProtocol ) )
1659
1665
}
1660
1666
1661
1667
// "If a DATA frame is received whose stream is not in "open"
@@ -1672,7 +1678,7 @@ func (sc *serverConn) processData(f *DataFrame) error {
1672
1678
// and return any flow control bytes since we're not going
1673
1679
// to consume them.
1674
1680
if sc .inflow .available () < int32 (f .Length ) {
1675
- return streamError (id , ErrCodeFlowControl )
1681
+ return sc . countError ( "data_flow" , streamError (id , ErrCodeFlowControl ) )
1676
1682
}
1677
1683
// Deduct the flow control from inflow, since we're
1678
1684
// going to immediately add it back in
@@ -1685,7 +1691,7 @@ func (sc *serverConn) processData(f *DataFrame) error {
1685
1691
// Already have a stream error in flight. Don't send another.
1686
1692
return nil
1687
1693
}
1688
- return streamError (id , ErrCodeStreamClosed )
1694
+ return sc . countError ( "closed" , streamError (id , ErrCodeStreamClosed ) )
1689
1695
}
1690
1696
if st .body == nil {
1691
1697
panic ("internal error: should have a body in this state" )
@@ -1697,20 +1703,20 @@ func (sc *serverConn) processData(f *DataFrame) error {
1697
1703
// RFC 7540, sec 8.1.2.6: A request or response is also malformed if the
1698
1704
// value of a content-length header field does not equal the sum of the
1699
1705
// DATA frame payload lengths that form the body.
1700
- return streamError (id , ErrCodeProtocol )
1706
+ return sc . countError ( "send_too_much" , streamError (id , ErrCodeProtocol ) )
1701
1707
}
1702
1708
if f .Length > 0 {
1703
1709
// Check whether the client has flow control quota.
1704
1710
if st .inflow .available () < int32 (f .Length ) {
1705
- return streamError (id , ErrCodeFlowControl )
1711
+ return sc . countError ( "flow_on_data_length" , streamError (id , ErrCodeFlowControl ) )
1706
1712
}
1707
1713
st .inflow .take (int32 (f .Length ))
1708
1714
1709
1715
if len (data ) > 0 {
1710
1716
wrote , err := st .body .Write (data )
1711
1717
if err != nil {
1712
1718
sc .sendWindowUpdate (nil , int (f .Length )- wrote )
1713
- return streamError (id , ErrCodeStreamClosed )
1719
+ return sc . countError ( "body_write_err" , streamError (id , ErrCodeStreamClosed ) )
1714
1720
}
1715
1721
if wrote != len (data ) {
1716
1722
panic ("internal error: bad Writer" )
@@ -1796,7 +1802,7 @@ func (sc *serverConn) processHeaders(f *MetaHeadersFrame) error {
1796
1802
// stream identifier MUST respond with a connection error
1797
1803
// (Section 5.4.1) of type PROTOCOL_ERROR.
1798
1804
if id % 2 != 1 {
1799
- return ConnectionError (ErrCodeProtocol )
1805
+ return sc . countError ( "headers_even" , ConnectionError (ErrCodeProtocol ) )
1800
1806
}
1801
1807
// A HEADERS frame can be used to create a new stream or
1802
1808
// send a trailer for an open one. If we already have a stream
@@ -1813,7 +1819,7 @@ func (sc *serverConn) processHeaders(f *MetaHeadersFrame) error {
1813
1819
// this state, it MUST respond with a stream error (Section 5.4.2) of
1814
1820
// type STREAM_CLOSED.
1815
1821
if st .state == stateHalfClosedRemote {
1816
- return streamError (id , ErrCodeStreamClosed )
1822
+ return sc . countError ( "headers_half_closed" , streamError (id , ErrCodeStreamClosed ) )
1817
1823
}
1818
1824
return st .processTrailerHeaders (f )
1819
1825
}
@@ -1824,7 +1830,7 @@ func (sc *serverConn) processHeaders(f *MetaHeadersFrame) error {
1824
1830
// receives an unexpected stream identifier MUST respond with
1825
1831
// a connection error (Section 5.4.1) of type PROTOCOL_ERROR.
1826
1832
if id <= sc .maxClientStreamID {
1827
- return ConnectionError (ErrCodeProtocol )
1833
+ return sc . countError ( "stream_went_down" , ConnectionError (ErrCodeProtocol ) )
1828
1834
}
1829
1835
sc .maxClientStreamID = id
1830
1836
@@ -1841,14 +1847,14 @@ func (sc *serverConn) processHeaders(f *MetaHeadersFrame) error {
1841
1847
if sc .curClientStreams + 1 > sc .advMaxStreams {
1842
1848
if sc .unackedSettings == 0 {
1843
1849
// They should know better.
1844
- return streamError (id , ErrCodeProtocol )
1850
+ return sc . countError ( "over_max_streams" , streamError (id , ErrCodeProtocol ) )
1845
1851
}
1846
1852
// Assume it's a network race, where they just haven't
1847
1853
// received our last SETTINGS update. But actually
1848
1854
// this can't happen yet, because we don't yet provide
1849
1855
// a way for users to adjust server parameters at
1850
1856
// runtime.
1851
- return streamError (id , ErrCodeRefusedStream )
1857
+ return sc . countError ( "over_max_streams_race" , streamError (id , ErrCodeRefusedStream ) )
1852
1858
}
1853
1859
1854
1860
initialState := stateOpen
@@ -1858,7 +1864,7 @@ func (sc *serverConn) processHeaders(f *MetaHeadersFrame) error {
1858
1864
st := sc .newStream (id , 0 , initialState )
1859
1865
1860
1866
if f .HasPriority () {
1861
- if err := checkPriority (f .StreamID , f .Priority ); err != nil {
1867
+ if err := sc . checkPriority (f .StreamID , f .Priority ); err != nil {
1862
1868
return err
1863
1869
}
1864
1870
sc .writeSched .AdjustStream (st .id , f .Priority )
@@ -1902,15 +1908,15 @@ func (st *stream) processTrailerHeaders(f *MetaHeadersFrame) error {
1902
1908
sc := st .sc
1903
1909
sc .serveG .check ()
1904
1910
if st .gotTrailerHeader {
1905
- return ConnectionError (ErrCodeProtocol )
1911
+ return sc . countError ( "dup_trailers" , ConnectionError (ErrCodeProtocol ) )
1906
1912
}
1907
1913
st .gotTrailerHeader = true
1908
1914
if ! f .StreamEnded () {
1909
- return streamError (st .id , ErrCodeProtocol )
1915
+ return sc . countError ( "trailers_not_ended" , streamError (st .id , ErrCodeProtocol ) )
1910
1916
}
1911
1917
1912
1918
if len (f .PseudoFields ()) > 0 {
1913
- return streamError (st .id , ErrCodeProtocol )
1919
+ return sc . countError ( "trailers_pseudo" , streamError (st .id , ErrCodeProtocol ) )
1914
1920
}
1915
1921
if st .trailer != nil {
1916
1922
for _ , hf := range f .RegularFields () {
@@ -1919,7 +1925,7 @@ func (st *stream) processTrailerHeaders(f *MetaHeadersFrame) error {
1919
1925
// TODO: send more details to the peer somehow. But http2 has
1920
1926
// no way to send debug data at a stream level. Discuss with
1921
1927
// HTTP folk.
1922
- return streamError (st .id , ErrCodeProtocol )
1928
+ return sc . countError ( "trailers_bogus" , streamError (st .id , ErrCodeProtocol ) )
1923
1929
}
1924
1930
st .trailer [key ] = append (st .trailer [key ], hf .Value )
1925
1931
}
@@ -1928,13 +1934,13 @@ func (st *stream) processTrailerHeaders(f *MetaHeadersFrame) error {
1928
1934
return nil
1929
1935
}
1930
1936
1931
- func checkPriority (streamID uint32 , p PriorityParam ) error {
1937
+ func ( sc * serverConn ) checkPriority (streamID uint32 , p PriorityParam ) error {
1932
1938
if streamID == p .StreamDep {
1933
1939
// Section 5.3.1: "A stream cannot depend on itself. An endpoint MUST treat
1934
1940
// this as a stream error (Section 5.4.2) of type PROTOCOL_ERROR."
1935
1941
// Section 5.3.3 says that a stream can depend on one of its dependencies,
1936
1942
// so it's only self-dependencies that are forbidden.
1937
- return streamError (streamID , ErrCodeProtocol )
1943
+ return sc . countError ( "priority" , streamError (streamID , ErrCodeProtocol ) )
1938
1944
}
1939
1945
return nil
1940
1946
}
@@ -1943,7 +1949,7 @@ func (sc *serverConn) processPriority(f *PriorityFrame) error {
1943
1949
if sc .inGoAway {
1944
1950
return nil
1945
1951
}
1946
- if err := checkPriority (f .StreamID , f .PriorityParam ); err != nil {
1952
+ if err := sc . checkPriority (f .StreamID , f .PriorityParam ); err != nil {
1947
1953
return err
1948
1954
}
1949
1955
sc .writeSched .AdjustStream (f .StreamID , f .PriorityParam )
@@ -2013,13 +2019,13 @@ func (sc *serverConn) newWriterAndRequest(st *stream, f *MetaHeadersFrame) (*res
2013
2019
// "All HTTP/2 requests MUST include exactly one valid
2014
2020
// value for the :method, :scheme, and :path
2015
2021
// pseudo-header fields"
2016
- return nil , nil , streamError (f .StreamID , ErrCodeProtocol )
2022
+ return nil , nil , sc . countError ( "bad_path_method" , streamError (f .StreamID , ErrCodeProtocol ) )
2017
2023
}
2018
2024
2019
2025
bodyOpen := ! f .StreamEnded ()
2020
2026
if rp .method == "HEAD" && bodyOpen {
2021
2027
// HEAD requests can't have bodies
2022
- return nil , nil , streamError (f .StreamID , ErrCodeProtocol )
2028
+ return nil , nil , sc . countError ( "head_body" , streamError (f .StreamID , ErrCodeProtocol ) )
2023
2029
}
2024
2030
2025
2031
rp .header = make (http.Header )
@@ -2102,7 +2108,7 @@ func (sc *serverConn) newWriterAndRequestNoBody(st *stream, rp requestParam) (*r
2102
2108
var err error
2103
2109
url_ , err = url .ParseRequestURI (rp .path )
2104
2110
if err != nil {
2105
- return nil , nil , streamError (st .id , ErrCodeProtocol )
2111
+ return nil , nil , sc . countError ( "bad_path" , streamError (st .id , ErrCodeProtocol ) )
2106
2112
}
2107
2113
requestURI = rp .path
2108
2114
}
@@ -2985,3 +2991,31 @@ func h1ServerKeepAlivesDisabled(hs *http.Server) bool {
2985
2991
}
2986
2992
return false
2987
2993
}
2994
+
2995
+ func (sc * serverConn ) countError (name string , err error ) error {
2996
+ if sc == nil || sc .srv == nil {
2997
+ return err
2998
+ }
2999
+ f := sc .srv .CountError
3000
+ if f == nil {
3001
+ return err
3002
+ }
3003
+ var typ string
3004
+ var code ErrCode
3005
+ switch e := err .(type ) {
3006
+ case ConnectionError :
3007
+ typ = "conn"
3008
+ code = ErrCode (e )
3009
+ case StreamError :
3010
+ typ = "stream"
3011
+ code = ErrCode (e .Code )
3012
+ default :
3013
+ return err
3014
+ }
3015
+ codeStr := errCodeName [code ]
3016
+ if codeStr == "" {
3017
+ codeStr = strconv .Itoa (int (code ))
3018
+ }
3019
+ f (fmt .Sprintf ("%s_%s_%s" , typ , codeStr , name ))
3020
+ return err
3021
+ }
0 commit comments