1// Copyright 2017 The Prometheus Authors
2// Licensed under the Apache License, Version 2.0 (the "License");
3// you may not use this file except in compliance with the License.
4// You may obtain a copy of the License at
5//
6// http://www.apache.org/licenses/LICENSE-2.0
7//
8// Unless required by applicable law or agreed to in writing, software
9// distributed under the License is distributed on an "AS IS" BASIS,
10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11// See the License for the specific language governing permissions and
12// limitations under the License.
13
14package promhttp
15
16import (
17	"bufio"
18	"io"
19	"net"
20	"net/http"
21)
22
23const (
24	closeNotifier = 1 << iota
25	flusher
26	hijacker
27	readerFrom
28	pusher
29)
30
31type delegator interface {
32	http.ResponseWriter
33
34	Status() int
35	Written() int64
36}
37
38type responseWriterDelegator struct {
39	http.ResponseWriter
40
41	status             int
42	written            int64
43	wroteHeader        bool
44	observeWriteHeader func(int)
45}
46
47func (r *responseWriterDelegator) Status() int {
48	return r.status
49}
50
51func (r *responseWriterDelegator) Written() int64 {
52	return r.written
53}
54
55func (r *responseWriterDelegator) WriteHeader(code int) {
56	r.status = code
57	r.wroteHeader = true
58	r.ResponseWriter.WriteHeader(code)
59	if r.observeWriteHeader != nil {
60		r.observeWriteHeader(code)
61	}
62}
63
64func (r *responseWriterDelegator) Write(b []byte) (int, error) {
65	if !r.wroteHeader {
66		r.WriteHeader(http.StatusOK)
67	}
68	n, err := r.ResponseWriter.Write(b)
69	r.written += int64(n)
70	return n, err
71}
72
73type closeNotifierDelegator struct{ *responseWriterDelegator }
74type flusherDelegator struct{ *responseWriterDelegator }
75type hijackerDelegator struct{ *responseWriterDelegator }
76type readerFromDelegator struct{ *responseWriterDelegator }
77type pusherDelegator struct{ *responseWriterDelegator }
78
79func (d closeNotifierDelegator) CloseNotify() <-chan bool {
80	//lint:ignore SA1019 http.CloseNotifier is deprecated but we don't want to
81	//remove support from client_golang yet.
82	return d.ResponseWriter.(http.CloseNotifier).CloseNotify()
83}
84func (d flusherDelegator) Flush() {
85	d.ResponseWriter.(http.Flusher).Flush()
86}
87func (d hijackerDelegator) Hijack() (net.Conn, *bufio.ReadWriter, error) {
88	return d.ResponseWriter.(http.Hijacker).Hijack()
89}
90func (d readerFromDelegator) ReadFrom(re io.Reader) (int64, error) {
91	if !d.wroteHeader {
92		d.WriteHeader(http.StatusOK)
93	}
94	n, err := d.ResponseWriter.(io.ReaderFrom).ReadFrom(re)
95	d.written += n
96	return n, err
97}
98func (d pusherDelegator) Push(target string, opts *http.PushOptions) error {
99	return d.ResponseWriter.(http.Pusher).Push(target, opts)
100}
101
102var pickDelegator = make([]func(*responseWriterDelegator) delegator, 32)
103
104func init() {
105	// TODO(beorn7): Code generation would help here.
106	pickDelegator[0] = func(d *responseWriterDelegator) delegator { // 0
107		return d
108	}
109	pickDelegator[closeNotifier] = func(d *responseWriterDelegator) delegator { // 1
110		return closeNotifierDelegator{d}
111	}
112	pickDelegator[flusher] = func(d *responseWriterDelegator) delegator { // 2
113		return flusherDelegator{d}
114	}
115	pickDelegator[flusher+closeNotifier] = func(d *responseWriterDelegator) delegator { // 3
116		return struct {
117			*responseWriterDelegator
118			http.Flusher
119			http.CloseNotifier
120		}{d, flusherDelegator{d}, closeNotifierDelegator{d}}
121	}
122	pickDelegator[hijacker] = func(d *responseWriterDelegator) delegator { // 4
123		return hijackerDelegator{d}
124	}
125	pickDelegator[hijacker+closeNotifier] = func(d *responseWriterDelegator) delegator { // 5
126		return struct {
127			*responseWriterDelegator
128			http.Hijacker
129			http.CloseNotifier
130		}{d, hijackerDelegator{d}, closeNotifierDelegator{d}}
131	}
132	pickDelegator[hijacker+flusher] = func(d *responseWriterDelegator) delegator { // 6
133		return struct {
134			*responseWriterDelegator
135			http.Hijacker
136			http.Flusher
137		}{d, hijackerDelegator{d}, flusherDelegator{d}}
138	}
139	pickDelegator[hijacker+flusher+closeNotifier] = func(d *responseWriterDelegator) delegator { // 7
140		return struct {
141			*responseWriterDelegator
142			http.Hijacker
143			http.Flusher
144			http.CloseNotifier
145		}{d, hijackerDelegator{d}, flusherDelegator{d}, closeNotifierDelegator{d}}
146	}
147	pickDelegator[readerFrom] = func(d *responseWriterDelegator) delegator { // 8
148		return readerFromDelegator{d}
149	}
150	pickDelegator[readerFrom+closeNotifier] = func(d *responseWriterDelegator) delegator { // 9
151		return struct {
152			*responseWriterDelegator
153			io.ReaderFrom
154			http.CloseNotifier
155		}{d, readerFromDelegator{d}, closeNotifierDelegator{d}}
156	}
157	pickDelegator[readerFrom+flusher] = func(d *responseWriterDelegator) delegator { // 10
158		return struct {
159			*responseWriterDelegator
160			io.ReaderFrom
161			http.Flusher
162		}{d, readerFromDelegator{d}, flusherDelegator{d}}
163	}
164	pickDelegator[readerFrom+flusher+closeNotifier] = func(d *responseWriterDelegator) delegator { // 11
165		return struct {
166			*responseWriterDelegator
167			io.ReaderFrom
168			http.Flusher
169			http.CloseNotifier
170		}{d, readerFromDelegator{d}, flusherDelegator{d}, closeNotifierDelegator{d}}
171	}
172	pickDelegator[readerFrom+hijacker] = func(d *responseWriterDelegator) delegator { // 12
173		return struct {
174			*responseWriterDelegator
175			io.ReaderFrom
176			http.Hijacker
177		}{d, readerFromDelegator{d}, hijackerDelegator{d}}
178	}
179	pickDelegator[readerFrom+hijacker+closeNotifier] = func(d *responseWriterDelegator) delegator { // 13
180		return struct {
181			*responseWriterDelegator
182			io.ReaderFrom
183			http.Hijacker
184			http.CloseNotifier
185		}{d, readerFromDelegator{d}, hijackerDelegator{d}, closeNotifierDelegator{d}}
186	}
187	pickDelegator[readerFrom+hijacker+flusher] = func(d *responseWriterDelegator) delegator { // 14
188		return struct {
189			*responseWriterDelegator
190			io.ReaderFrom
191			http.Hijacker
192			http.Flusher
193		}{d, readerFromDelegator{d}, hijackerDelegator{d}, flusherDelegator{d}}
194	}
195	pickDelegator[readerFrom+hijacker+flusher+closeNotifier] = func(d *responseWriterDelegator) delegator { // 15
196		return struct {
197			*responseWriterDelegator
198			io.ReaderFrom
199			http.Hijacker
200			http.Flusher
201			http.CloseNotifier
202		}{d, readerFromDelegator{d}, hijackerDelegator{d}, flusherDelegator{d}, closeNotifierDelegator{d}}
203	}
204	pickDelegator[pusher] = func(d *responseWriterDelegator) delegator { // 16
205		return pusherDelegator{d}
206	}
207	pickDelegator[pusher+closeNotifier] = func(d *responseWriterDelegator) delegator { // 17
208		return struct {
209			*responseWriterDelegator
210			http.Pusher
211			http.CloseNotifier
212		}{d, pusherDelegator{d}, closeNotifierDelegator{d}}
213	}
214	pickDelegator[pusher+flusher] = func(d *responseWriterDelegator) delegator { // 18
215		return struct {
216			*responseWriterDelegator
217			http.Pusher
218			http.Flusher
219		}{d, pusherDelegator{d}, flusherDelegator{d}}
220	}
221	pickDelegator[pusher+flusher+closeNotifier] = func(d *responseWriterDelegator) delegator { // 19
222		return struct {
223			*responseWriterDelegator
224			http.Pusher
225			http.Flusher
226			http.CloseNotifier
227		}{d, pusherDelegator{d}, flusherDelegator{d}, closeNotifierDelegator{d}}
228	}
229	pickDelegator[pusher+hijacker] = func(d *responseWriterDelegator) delegator { // 20
230		return struct {
231			*responseWriterDelegator
232			http.Pusher
233			http.Hijacker
234		}{d, pusherDelegator{d}, hijackerDelegator{d}}
235	}
236	pickDelegator[pusher+hijacker+closeNotifier] = func(d *responseWriterDelegator) delegator { // 21
237		return struct {
238			*responseWriterDelegator
239			http.Pusher
240			http.Hijacker
241			http.CloseNotifier
242		}{d, pusherDelegator{d}, hijackerDelegator{d}, closeNotifierDelegator{d}}
243	}
244	pickDelegator[pusher+hijacker+flusher] = func(d *responseWriterDelegator) delegator { // 22
245		return struct {
246			*responseWriterDelegator
247			http.Pusher
248			http.Hijacker
249			http.Flusher
250		}{d, pusherDelegator{d}, hijackerDelegator{d}, flusherDelegator{d}}
251	}
252	pickDelegator[pusher+hijacker+flusher+closeNotifier] = func(d *responseWriterDelegator) delegator { //23
253		return struct {
254			*responseWriterDelegator
255			http.Pusher
256			http.Hijacker
257			http.Flusher
258			http.CloseNotifier
259		}{d, pusherDelegator{d}, hijackerDelegator{d}, flusherDelegator{d}, closeNotifierDelegator{d}}
260	}
261	pickDelegator[pusher+readerFrom] = func(d *responseWriterDelegator) delegator { // 24
262		return struct {
263			*responseWriterDelegator
264			http.Pusher
265			io.ReaderFrom
266		}{d, pusherDelegator{d}, readerFromDelegator{d}}
267	}
268	pickDelegator[pusher+readerFrom+closeNotifier] = func(d *responseWriterDelegator) delegator { // 25
269		return struct {
270			*responseWriterDelegator
271			http.Pusher
272			io.ReaderFrom
273			http.CloseNotifier
274		}{d, pusherDelegator{d}, readerFromDelegator{d}, closeNotifierDelegator{d}}
275	}
276	pickDelegator[pusher+readerFrom+flusher] = func(d *responseWriterDelegator) delegator { // 26
277		return struct {
278			*responseWriterDelegator
279			http.Pusher
280			io.ReaderFrom
281			http.Flusher
282		}{d, pusherDelegator{d}, readerFromDelegator{d}, flusherDelegator{d}}
283	}
284	pickDelegator[pusher+readerFrom+flusher+closeNotifier] = func(d *responseWriterDelegator) delegator { // 27
285		return struct {
286			*responseWriterDelegator
287			http.Pusher
288			io.ReaderFrom
289			http.Flusher
290			http.CloseNotifier
291		}{d, pusherDelegator{d}, readerFromDelegator{d}, flusherDelegator{d}, closeNotifierDelegator{d}}
292	}
293	pickDelegator[pusher+readerFrom+hijacker] = func(d *responseWriterDelegator) delegator { // 28
294		return struct {
295			*responseWriterDelegator
296			http.Pusher
297			io.ReaderFrom
298			http.Hijacker
299		}{d, pusherDelegator{d}, readerFromDelegator{d}, hijackerDelegator{d}}
300	}
301	pickDelegator[pusher+readerFrom+hijacker+closeNotifier] = func(d *responseWriterDelegator) delegator { // 29
302		return struct {
303			*responseWriterDelegator
304			http.Pusher
305			io.ReaderFrom
306			http.Hijacker
307			http.CloseNotifier
308		}{d, pusherDelegator{d}, readerFromDelegator{d}, hijackerDelegator{d}, closeNotifierDelegator{d}}
309	}
310	pickDelegator[pusher+readerFrom+hijacker+flusher] = func(d *responseWriterDelegator) delegator { // 30
311		return struct {
312			*responseWriterDelegator
313			http.Pusher
314			io.ReaderFrom
315			http.Hijacker
316			http.Flusher
317		}{d, pusherDelegator{d}, readerFromDelegator{d}, hijackerDelegator{d}, flusherDelegator{d}}
318	}
319	pickDelegator[pusher+readerFrom+hijacker+flusher+closeNotifier] = func(d *responseWriterDelegator) delegator { // 31
320		return struct {
321			*responseWriterDelegator
322			http.Pusher
323			io.ReaderFrom
324			http.Hijacker
325			http.Flusher
326			http.CloseNotifier
327		}{d, pusherDelegator{d}, readerFromDelegator{d}, hijackerDelegator{d}, flusherDelegator{d}, closeNotifierDelegator{d}}
328	}
329}
330
331func newDelegator(w http.ResponseWriter, observeWriteHeaderFunc func(int)) delegator {
332	d := &responseWriterDelegator{
333		ResponseWriter:     w,
334		observeWriteHeader: observeWriteHeaderFunc,
335	}
336
337	id := 0
338	//lint:ignore SA1019 http.CloseNotifier is deprecated but we don't want to
339	//remove support from client_golang yet.
340	if _, ok := w.(http.CloseNotifier); ok {
341		id += closeNotifier
342	}
343	if _, ok := w.(http.Flusher); ok {
344		id += flusher
345	}
346	if _, ok := w.(http.Hijacker); ok {
347		id += hijacker
348	}
349	if _, ok := w.(io.ReaderFrom); ok {
350		id += readerFrom
351	}
352	if _, ok := w.(http.Pusher); ok {
353		id += pusher
354	}
355
356	return pickDelegator[id](d)
357}
358