@@ -425,19 +425,14 @@ type response struct {
425
425
reqBody io.ReadCloser
426
426
cancelCtx context.CancelFunc // when ServeHTTP exits
427
427
wroteHeader bool // a non-1xx header has been (logically) written
428
- wroteContinue bool // 100 Continue response was written
429
428
wants10KeepAlive bool // HTTP/1.0 w/ Connection "keep-alive"
430
429
wantsClose bool // HTTP request has Connection "close"
431
430
432
- // canWriteContinue is an atomic boolean that says whether or
433
- // not a 100 Continue header can be written to the
434
- // connection.
435
- // writeContinueMu must be held while writing the header.
436
- // These two fields together synchronize the body reader (the
437
- // expectContinueReader, which wants to write 100 Continue)
438
- // against the main writer.
439
- canWriteContinue atomic.Bool
440
- writeContinueMu sync.Mutex
431
+ // writeContinue synchronizes the body reader (the
432
+ // expectContinueReader, which wants to write 100 Continue
433
+ // to the connection) against the main writer.
434
+ writeContinue sync.Once
435
+ wroteContinue atomic.Bool // 100 Continue response was written
441
436
442
437
w * bufio.Writer // buffers output in chunks to chunkWriter
443
438
cw chunkWriter
@@ -917,15 +912,12 @@ func (ecr *expectContinueReader) Read(p []byte) (n int, err error) {
917
912
return 0 , ErrBodyReadAfterClose
918
913
}
919
914
w := ecr .resp
920
- if ! w .wroteContinue && w .canWriteContinue .Load () && ! w .conn .hijacked () {
921
- w .wroteContinue = true
922
- w .writeContinueMu .Lock ()
923
- if w .canWriteContinue .Load () {
915
+ if ! w .conn .hijacked () {
916
+ w .writeContinue .Do (func () {
917
+ w .wroteContinue .Store (true )
924
918
w .conn .bufw .WriteString ("HTTP/1.1 100 Continue\r \n \r \n " )
925
919
w .conn .bufw .Flush ()
926
- w .canWriteContinue .Store (false )
927
- }
928
- w .writeContinueMu .Unlock ()
920
+ })
929
921
}
930
922
n , err = ecr .readCloser .Read (p )
931
923
if err == io .EOF {
@@ -1165,10 +1157,8 @@ func (w *response) WriteHeader(code int) {
1165
1157
// so it takes the non-informational path.
1166
1158
if code >= 100 && code <= 199 && code != StatusSwitchingProtocols {
1167
1159
// Prevent a potential race with an automatically-sent 100 Continue triggered by Request.Body.Read()
1168
- if code == 100 && w .canWriteContinue .Load () {
1169
- w .writeContinueMu .Lock ()
1170
- w .canWriteContinue .Store (false )
1171
- w .writeContinueMu .Unlock ()
1160
+ if code == 100 {
1161
+ w .disableContinue ()
1172
1162
}
1173
1163
1174
1164
writeStatusLine (w .conn .bufw , w .req .ProtoAtLeast (1 , 1 ), code , w .statusBuf [:])
@@ -1383,7 +1373,7 @@ func (cw *chunkWriter) writeHeader(p []byte) {
1383
1373
1384
1374
switch bdy := w .req .Body .(type ) {
1385
1375
case * expectContinueReader :
1386
- if bdy .resp .wroteContinue {
1376
+ if bdy .resp .wroteContinue . Load () {
1387
1377
discard = true
1388
1378
}
1389
1379
case * body :
@@ -1625,15 +1615,7 @@ func (w *response) write(lenData int, dataB []byte, dataS string) (n int, err er
1625
1615
return 0 , ErrHijacked
1626
1616
}
1627
1617
1628
- if w .canWriteContinue .Load () {
1629
- // Body reader wants to write 100 Continue but hasn't yet.
1630
- // Tell it not to. The store must be done while holding the lock
1631
- // because the lock makes sure that there is not an active write
1632
- // this very moment.
1633
- w .writeContinueMu .Lock ()
1634
- w .canWriteContinue .Store (false )
1635
- w .writeContinueMu .Unlock ()
1636
- }
1618
+ w .disableContinue ()
1637
1619
1638
1620
if ! w .wroteHeader {
1639
1621
w .WriteHeader (StatusOK )
@@ -1679,6 +1661,11 @@ func (w *response) finishRequest() {
1679
1661
}
1680
1662
}
1681
1663
1664
+ // disableContinue tells body reader not to write 100 Continue.
1665
+ func (w * response ) disableContinue () {
1666
+ w .writeContinue .Do (func () {})
1667
+ }
1668
+
1682
1669
// shouldReuseConnection reports whether the underlying TCP connection can be reused.
1683
1670
// It must only be called after the handler is done executing.
1684
1671
func (w * response ) shouldReuseConnection () bool {
@@ -1905,6 +1892,7 @@ func (c *conn) serve(ctx context.Context) {
1905
1892
if inFlightResponse != nil {
1906
1893
inFlightResponse .conn .r .abortPendingRead ()
1907
1894
inFlightResponse .reqBody .Close ()
1895
+ inFlightResponse .disableContinue ()
1908
1896
}
1909
1897
c .close ()
1910
1898
c .setState (c .rwc , StateClosed , runHooks )
@@ -2016,7 +2004,6 @@ func (c *conn) serve(ctx context.Context) {
2016
2004
if req .ProtoAtLeast (1 , 1 ) && req .ContentLength != 0 {
2017
2005
// Wrap the Body reader with one that replies on the connection
2018
2006
req .Body = & expectContinueReader {readCloser : req .Body , resp : w }
2019
- w .canWriteContinue .Store (true )
2020
2007
}
2021
2008
} else if req .Header .get ("Expect" ) != "" {
2022
2009
w .sendExpectationFailed ()
@@ -2046,6 +2033,7 @@ func (c *conn) serve(ctx context.Context) {
2046
2033
return
2047
2034
}
2048
2035
w .finishRequest ()
2036
+ w .disableContinue ()
2049
2037
c .rwc .SetWriteDeadline (time.Time {})
2050
2038
if ! w .shouldReuseConnection () {
2051
2039
if w .requestBodyLimitHit || w .closedRequestBodyEarly () {
0 commit comments