-
Notifications
You must be signed in to change notification settings - Fork 8
/
response_proxy_buf.go
150 lines (133 loc) · 4.04 KB
/
response_proxy_buf.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
// Copyright 2015-present, Cyrill @ Schumacher.fm and the CoreStore contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package caddyesi
import (
"bufio"
"io"
"net"
"net/http"
"strconv"
)
type responseBufferWriter interface {
http.ResponseWriter
TriggerRealWrite(addContentLength int)
}
// responseWrapBuffer wraps an http.ResponseWriter, returning a proxy which only writes
// into the provided io.Writer.
func responseWrapBuffer(buf io.Writer, w http.ResponseWriter) responseBufferWriter {
_, cn := w.(http.CloseNotifier)
_, fl := w.(http.Flusher)
_, hj := w.(http.Hijacker)
_, rf := w.(io.ReaderFrom)
bw := bufferedWriter{
rw: w,
buf: buf,
header: make(http.Header),
}
if cn && fl && hj && rf {
return &bufferedFancyWriter{bw}
}
if fl {
return &bufferedFlushWriter{bw}
}
return &bw
}
// bufferedWriter wraps a http.ResponseWriter that implements the minimal
// http.ResponseWriter interface.
type bufferedWriter struct {
rw http.ResponseWriter
buf io.Writer
header http.Header
// addContentLength rewrites the Content-Length header to the correct
// returned length. Value can also be negative when the error message in an
// Tag tag is shorter than the length of the Tag tag.
addContentLength int
code int
wroteHeader bool
// writeReal does not write to the buffer and writes directly to the original
// rw.
writeReal bool
}
func (b *bufferedWriter) TriggerRealWrite(addContentLength int) {
b.writeReal = true
b.addContentLength = addContentLength
}
func (b *bufferedWriter) Header() http.Header {
return b.header
}
func (b *bufferedWriter) WriteHeader(code int) {
// WriteHeader gets called before TriggerRealWrite
if b.code == 0 {
b.code = code
}
}
// Write does not write to the client instead it writes in the underlying
// buffer.
func (b *bufferedWriter) Write(p []byte) (int, error) {
if !b.writeReal {
return b.buf.Write(p)
}
const clName = "Content-Length"
if !b.wroteHeader {
b.wroteHeader = true
if b.addContentLength != 0 {
clRaw := b.header.Get(clName)
cl, _ := strconv.Atoi(clRaw) // ignoring that err ... for now
b.header.Set(clName, strconv.Itoa(cl+b.addContentLength))
}
for k, v := range b.header {
b.rw.Header()[k] = v
}
b.rw.WriteHeader(b.code)
}
return b.rw.Write(p)
}
// bufferedFancyWriter is a writer that additionally satisfies
// http.CloseNotifier, http.Flusher, http.Hijacker, and io.ReaderFrom. It exists
// for the common case of wrapping the http.ResponseWriter that package http
// gives you, in order to make the proxied object support the full method set of
// the proxied object.
type bufferedFancyWriter struct {
bufferedWriter
}
func (f *bufferedFancyWriter) CloseNotify() <-chan bool {
cn := f.bufferedWriter.rw.(http.CloseNotifier)
return cn.CloseNotify()
}
func (f *bufferedFancyWriter) Flush() {
fl := f.bufferedWriter.rw.(http.Flusher)
fl.Flush()
}
func (f *bufferedFancyWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
hj := f.bufferedWriter.rw.(http.Hijacker)
return hj.Hijack()
}
func (f *bufferedFancyWriter) Push(target string, opts *http.PushOptions) error {
if p, ok := f.bufferedWriter.rw.(http.Pusher); ok {
return p.Push(target, opts)
}
return nil
}
// ReadFrom writes r into the underlying buffer
func (f *bufferedFancyWriter) ReadFrom(r io.Reader) (int64, error) {
return io.Copy(&f.bufferedWriter, r)
}
// bufferedFlushWriter implements only http.Flusher mostly used
type bufferedFlushWriter struct {
bufferedWriter
}
func (f *bufferedFlushWriter) Flush() {
fl := f.bufferedWriter.rw.(http.Flusher)
fl.Flush()
}