@@ -395,11 +395,11 @@ func (cw *chunkWriter) Write(p []byte) (n int, err error) {
395
395
return
396
396
}
397
397
398
- func (cw * chunkWriter ) flush () {
398
+ func (cw * chunkWriter ) flush () error {
399
399
if ! cw .wroteHeader {
400
400
cw .writeHeader (nil )
401
401
}
402
- cw .res .conn .bufw .Flush ()
402
+ return cw .res .conn .bufw .Flush ()
403
403
}
404
404
405
405
func (cw * chunkWriter ) close () {
@@ -443,6 +443,14 @@ type response struct {
443
443
w * bufio.Writer // buffers output in chunks to chunkWriter
444
444
cw chunkWriter
445
445
446
+ // writeTimeoutTimer is set when the server has a WriteTimeout configured
447
+ // and triggers when a write timed out
448
+ // writeDeadline is used to enable direct flushing of writes after the
449
+ // timeout so writers receive an error and can handle it
450
+ writeTimeoutTimer * time.Timer
451
+ writeDeadline bool
452
+ writeDeadlineMu sync.Mutex
453
+
446
454
// handlerHeader is the Header that Handlers get access to,
447
455
// which may be retained and mutated even after WriteHeader.
448
456
// handlerHeader is copied into cw.header at WriteHeader
@@ -1045,6 +1053,9 @@ func (c *conn) readRequest(ctx context.Context) (w *response, err error) {
1045
1053
if isH2Upgrade {
1046
1054
w .closeAfterReply = true
1047
1055
}
1056
+ if d := c .server .WriteTimeout ; d > 0 {
1057
+ w .setWriteTimeout (d )
1058
+ }
1048
1059
w .cw .res = w
1049
1060
w .w = newBufioWriterSize (& w .cw , bufferBeforeChunkingSize )
1050
1061
return w , nil
@@ -1590,6 +1601,16 @@ func (w *response) WriteString(data string) (n int, err error) {
1590
1601
return w .write (len (data ), nil , data )
1591
1602
}
1592
1603
1604
+ // setWriteTimeout lets the response know if the write was supposed to be
1605
+ // timed out, timed out requests will force be flushed on every write
1606
+ func (w * response ) setWriteTimeout (d time.Duration ) {
1607
+ w .writeTimeoutTimer = time .AfterFunc (d , func () {
1608
+ w .writeDeadlineMu .Lock ()
1609
+ w .writeDeadline = true
1610
+ w .writeDeadlineMu .Unlock ()
1611
+ })
1612
+ }
1613
+
1593
1614
// either dataB or dataS is non-zero.
1594
1615
func (w * response ) write (lenData int , dataB []byte , dataS string ) (n int , err error ) {
1595
1616
if w .conn .hijacked () {
@@ -1625,10 +1646,22 @@ func (w *response) write(lenData int, dataB []byte, dataS string) (n int, err er
1625
1646
return 0 , ErrContentLength
1626
1647
}
1627
1648
if dataB != nil {
1628
- return w .w .Write (dataB )
1649
+ n , err = w .w .Write (dataB )
1629
1650
} else {
1630
- return w .w .WriteString (dataS )
1651
+ n , err = w .w .WriteString (dataS )
1631
1652
}
1653
+ if err == nil {
1654
+ w .writeDeadlineMu .Lock ()
1655
+ wd := w .writeDeadline
1656
+ w .writeDeadlineMu .Unlock ()
1657
+
1658
+ if wd {
1659
+ // r.Flush returns no errors, flush manually
1660
+ w .w .Flush ()
1661
+ err = w .cw .flush ()
1662
+ }
1663
+ }
1664
+ return
1632
1665
}
1633
1666
1634
1667
func (w * response ) finishRequest () {
@@ -1643,6 +1676,9 @@ func (w *response) finishRequest() {
1643
1676
w .cw .close ()
1644
1677
w .conn .bufw .Flush ()
1645
1678
1679
+ if w .writeTimeoutTimer != nil {
1680
+ w .writeTimeoutTimer .Stop ()
1681
+ }
1646
1682
w .conn .r .abortPendingRead ()
1647
1683
1648
1684
// Close the body (regardless of w.closeAfterReply) so we can
0 commit comments