2 tofuproxy -- HTTP proxy with TLS certificates management
3 Copyright (C) 2021 Sergey Matveev <stargrave@stargrave.org>
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, version 3 of the License.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>.
29 "github.com/dustin/go-humanize"
30 "go.stargrave.org/tofuproxy/fifos"
31 "go.stargrave.org/tofuproxy/rounds"
35 transport = http.Transport{
36 DialContext: (&net.Dialer{
38 KeepAlive: time.Minute,
40 MaxIdleConns: http.DefaultTransport.(*http.Transport).MaxIdleConns,
41 IdleConnTimeout: http.DefaultTransport.(*http.Transport).IdleConnTimeout * 2,
42 TLSHandshakeTimeout: time.Minute,
43 DialTLSContext: dialTLS,
44 ForceAttemptHTTP2: true,
46 proxyHeaders = map[string]struct{}{
56 w http.ResponseWriter,
60 func roundTrip(w http.ResponseWriter, req *http.Request) {
61 host := strings.TrimSuffix(req.URL.Host, ":443")
62 for _, round := range []Round{
66 rounds.RoundRedditOld,
67 rounds.RoundHabrImage,
69 if cont, _ := round(host, nil, w, req); !cont {
74 resp, err := transport.RoundTrip(req)
76 fifos.SinkErr <- fmt.Sprintf("%s\t%s", req.URL.Host, err.Error())
77 http.Error(w, err.Error(), http.StatusBadGateway)
81 for k, vs := range resp.Header {
82 if _, ok := proxyHeaders[k]; ok {
85 for _, v := range vs {
90 for _, round := range []Round{
91 rounds.RoundDenyFonts,
92 rounds.RoundTranscodeWebP,
93 rounds.RoundTranscodeJXL,
94 rounds.RoundTranscodeAVIF,
95 rounds.RoundRedirectHTML,
97 cont, err := round(host, resp, w, req)
99 http.Error(w, err.Error(), http.StatusBadGateway)
107 for h := range proxyHeaders {
108 if v := resp.Header.Get(h); v != "" {
112 w.WriteHeader(resp.StatusCode)
113 n, err := io.Copy(w, resp.Body)
115 log.Printf("Error during %s: %+v\n", req.URL, err)
123 resp.Header.Get("Content-Type"),
124 humanize.IBytes(uint64(n)),
126 if resp.StatusCode == http.StatusOK {
129 fifos.SinkOther <- msg