785
}
786
}
787
788
+func TestIdleTimeoutWriter_HandlerDeadline(t *testing.T) {
789
+ const idle = time.Hour
790
+ now := time.Now()
791
+ earlier := now.Add(30 * time.Minute)
792
+ later := now.Add(2 * time.Hour)
793
+ hard := now.Add(20 * time.Minute)
794
+
795
+ // the values recorded for each SetWriteDeadline call
796
+ const (
797
+ idleSet = "idle"
798
+ zeroSet = "zero"
799
+ earlierSet = "earlier"
800
+ laterSet = "later"
801
+ hardSet = "hard"
802
+ otherSet = "other"
803
+ )
804
+
805
+ for i, tc := range []struct {
806
+ name string
807
+ clear bool
808
+ hard bool
809
+ deadlines []time.Time
810
+ expected []string
811
+ }{
812
+ {
813
+ name: "earlier handler deadline caps the idle reset",
814
+ deadlines: []time.Time{earlier},
815
+ expected: []string{earlierSet, earlierSet},
816
+ },
817
+ {
818
+ name: "clearing puts back the handler deadline",
819
+ clear: true,
820
+ deadlines: []time.Time{earlier},
821
+ expected: []string{earlierSet, earlierSet, earlierSet},
822
+ },
823
+ {
824
+ name: "later handler deadline doesn't loosen the idle timeout",
825
+ deadlines: []time.Time{later},
826
+ expected: []string{laterSet, idleSet},
827
+ },
828
+ {
829
+ name: "clearing puts back a later handler deadline",
830
+ clear: true,
831
+ deadlines: []time.Time{later},
832
+ expected: []string{laterSet, idleSet, laterSet},
833
+ },
834
+ {
835
+ name: "zero handler deadline removes the ceiling",
836
+ deadlines: []time.Time{earlier, {}},
837
+ expected: []string{earlierSet, zeroSet, idleSet},
838
+ },
839
+ {
840
+ name: "earlier hard deadline stays the ceiling",
841
+ clear: true,
842
+ hard: true,
843
+ deadlines: []time.Time{earlier},
844
+ expected: []string{earlierSet, hardSet, hardSet},
845
+ },
846
+ } {
847
+ rec := &writeDeadlineRecorder{ResponseRecorder: httptest.NewRecorder()}
848
+ w := &IdleTimeoutWriter{
849
+ ResponseWriterWrapper: &ResponseWriterWrapper{ResponseWriter: rec},
850
+ Ctrl: http.NewResponseController(rec),
851
+ Deadline: IdleDeadline{Timeout: idle},
852
+ Logger: zap.NewNop(),
853
+ ClearBetweenWrites: tc.clear,
854
+ }
855
+ if tc.hard {
856
+ w.Deadline.HardDeadline = hard
857
+ }
858
+
859
+ // handlers usually reach the writer through other wrappers
860
+ ctrl := http.NewResponseController(&ResponseWriterWrapper{ResponseWriter: w})
861
+ for _, d := range tc.deadlines {
862
+ require.NoError(t, ctrl.SetWriteDeadline(d))
863
+ }
864
+
865
+ before := time.Now()
866
+ _, err := w.Write([]byte("x"))
867
+ require.NoError(t, err)
868
+ after := time.Now()
869
+
870
+ var actual []string
871
+ for _, d := range rec.deadlines {
872
+ switch {
873
+ case d.IsZero():
874
+ actual = append(actual, zeroSet)
875
+ case d.Equal(earlier):
876
+ actual = append(actual, earlierSet)
877
+ case d.Equal(later):
878
+ actual = append(actual, laterSet)
879
+ case d.Equal(hard):
880
+ actual = append(actual, hardSet)
881
+ case !d.Before(before.Add(idle)) && !d.After(after.Add(idle)):
882
+ actual = append(actual, idleSet)
883
+ default:
884
+ actual = append(actual, otherSet)
885
+ }
886
+ }
887
+ assert.Equal(t, tc.expected, actual, "Test %d (%s)", i, tc.name)
888
+ }
889
+}
890
+
891
// readFromCounter is a fake http.ResponseWriter that records how many
892
// times ReadFrom was called on it and the size of the largest call, so
893
// tests can assert on chunking behavior deterministically instead of