@marcel91200 / caddy Mirror · read-only
Fork 0

Fast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS

Mirrored from https://github.com/Slazee/caddy.git

Synced Nov 11, 01:00 UTC

master
go 164 lines 5.44 KB
Raw
1 // Copyright 2015 Matthew Holt and The Caddy Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 package timeouts
16
17 import (
18 "net/http"
19 "time"
20
21 "go.uber.org/zap"
22
23 "github.com/caddyserver/caddy/v2"
24 "github.com/caddyserver/caddy/v2/modules/caddyhttp"
25 )
26
27 func init() {
28 caddy.RegisterModule(Timeouts{})
29 }
30
31 // Timeouts is a middleware that applies read/write idle timeouts, minimum
32 // transfer rates, and a write chunk size cap to routes matching this
33 // handler. They take precedence over the server-wide equivalents, and
34 // over those of any enclosing timeouts handler, for as long as this
35 // handler is running. The server-wide read_timeout and write_timeout
36 // still cap the whole transfer.
37 type Timeouts struct {
38 // How long to allow a read from the request body to stall before
39 // aborting the connection, reset on every successful read (like the
40 // server-wide read_idle_timeout, but scoped to routes matching this
41 // handler). If zero, the server-wide setting applies.
42 // EXPERIMENTAL. Subject to change/removal.
43 ReadTimeout time.Duration `json:"read_timeout,omitempty"`
44
45 // ReadMinRate requires the client to sustain at least this many
46 // bytes/second, averaged from the start of the read, or the
47 // connection is aborted (Apache mod_reqtimeout's MinRate). Only
48 // takes effect if ReadTimeout is also set.
49 // EXPERIMENTAL. Subject to change/removal.
50 ReadMinRate int64 `json:"read_min_rate,omitempty"`
51
52 // How long to allow a write to the client to stall before aborting
53 // the connection, reset on every successful write (like the
54 // server-wide write_idle_timeout, but scoped to routes matching this
55 // handler). If zero, the server-wide setting applies.
56 // EXPERIMENTAL. Subject to change/removal.
57 WriteTimeout time.Duration `json:"write_timeout,omitempty"`
58
59 // WriteMinRate is like ReadMinRate, but for writes to the client.
60 // Only takes effect if WriteTimeout is also set.
61 // EXPERIMENTAL. Subject to change/removal.
62 WriteMinRate int64 `json:"write_min_rate,omitempty"`
63
64 // MaxWriteChunk bounds how many bytes a single underlying write
65 // operation is allowed to cover, so WriteTimeout/WriteMinRate can
66 // actually apply between chunks of a large response instead of
67 // being bounded by one deadline for the whole thing. If zero, the
68 // server-wide setting is used, or caddyhttp.DefaultMaxWriteChunk if
69 // there is none.
70 // EXPERIMENTAL. Subject to change/removal.
71 MaxWriteChunk int `json:"max_write_chunk,omitempty"`
72
73 logger *zap.Logger
74 }
75
76 // CaddyModule returns the Caddy module information.
77 func (Timeouts) CaddyModule() caddy.ModuleInfo {
78 return caddy.ModuleInfo{
79 ID: "http.handlers.timeouts",
80 New: func() caddy.Module { return new(Timeouts) },
81 }
82 }
83
84 func (t *Timeouts) Provision(ctx caddy.Context) error {
85 t.logger = ctx.Logger()
86 return nil
87 }
88
89 func (t Timeouts) ServeHTTP(w http.ResponseWriter, r *http.Request, next caddyhttp.Handler) error {
90 if t.ReadTimeout <= 0 && t.WriteTimeout <= 0 {
91 return next.ServeHTTP(w, r)
92 }
93
94 // if the request already has idle timeouts, from the server or an
95 // enclosing route, override them instead of wrapping again: the
96 // wrapper closest to the connection sets the deadline last, so it
97 // would take precedence over ours
98 idleReader, idleWriter := caddyhttp.IdleTimeoutsFromContext(r.Context())
99 var wrapped bool
100 var rc *http.ResponseController
101 start := time.Now()
102
103 if t.ReadTimeout > 0 && r.Body != nil {
104 if idleReader != nil {
105 defer idleReader.Override(t.ReadTimeout, t.ReadMinRate)()
106 } else {
107 //nolint:bodyclose
108 rc = http.NewResponseController(w)
109 idleReader = &caddyhttp.IdleTimeoutReader{
110 ReadCloser: r.Body,
111 Ctrl: rc,
112 Deadline: caddyhttp.IdleDeadline{
113 Start: start,
114 Timeout: t.ReadTimeout,
115 MinRate: t.ReadMinRate,
116 },
117 Logger: t.logger,
118 DrainDeadline: r.ProtoMajor == 1 && r.ContentLength != 0,
119 ClearBetweenReads: r.ProtoMajor == 2,
120 }
121 defer idleReader.HandlerDone()
122 r.Body = idleReader
123 wrapped = true
124 }
125 }
126 if t.WriteTimeout > 0 {
127 if idleWriter != nil {
128 defer idleWriter.Override(t.WriteTimeout, t.WriteMinRate, t.MaxWriteChunk)()
129 } else {
130 if rc == nil {
131 //nolint:bodyclose
132 rc = http.NewResponseController(w)
133 }
134 idleWriter = &caddyhttp.IdleTimeoutWriter{
135 ResponseWriterWrapper: &caddyhttp.ResponseWriterWrapper{ResponseWriter: w},
136 Ctrl: rc,
137 Deadline: caddyhttp.IdleDeadline{
138 Start: start,
139 Timeout: t.WriteTimeout,
140 MinRate: t.WriteMinRate,
141 },
142 MaxChunk: t.MaxWriteChunk,
143 Logger: t.logger,
144 ClearBetweenWrites: r.ProtoMajor == 2,
145 }
146 defer idleWriter.HandlerDone()
147 w = idleWriter
148 wrapped = true
149 }
150 }
151
152 // let nested timeouts handlers override the wrappers added here
153 if wrapped {
154 r = r.WithContext(caddyhttp.ContextWithIdleTimeouts(r.Context(), idleReader, idleWriter))
155 }
156
157 return next.ServeHTTP(w, r)
158 }
159
160 // Interface guards
161 var (
162 _ caddy.Provisioner = (*Timeouts)(nil)
163 _ caddyhttp.MiddlewareHandler = (*Timeouts)(nil)
164 )