-/*
-tofuproxy -- HTTP proxy with TLS certificates management
-Copyright (C) 2021 Sergey Matveev <stargrave@stargrave.org>
-
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, version 3 of the License.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program. If not, see <http://www.gnu.org/licenses/>.
-*/
+// tofuproxy -- flexible HTTP/HTTPS proxy, TLS terminator, X.509 TOFU
+// manager, WARC/geminispace browser
+// Copyright (C) 2021-2024 Sergey Matveev <stargrave@stargrave.org>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, version 3 of the License.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
package tofuproxy
"time"
"github.com/dustin/go-humanize"
+ "go.stargrave.org/tofuproxy/caches"
"go.stargrave.org/tofuproxy/fifos"
"go.stargrave.org/tofuproxy/rounds"
+ ttls "go.stargrave.org/tofuproxy/tls"
)
var (
MaxIdleConns: http.DefaultTransport.(*http.Transport).MaxIdleConns,
IdleConnTimeout: http.DefaultTransport.(*http.Transport).IdleConnTimeout * 2,
TLSHandshakeTimeout: time.Minute,
- DialTLSContext: dialTLS,
+ DialTLSContext: ttls.DialTLS,
ForceAttemptHTTP2: true,
}
proxyHeaders = map[string]struct{}{
) (bool, error)
func roundTrip(w http.ResponseWriter, req *http.Request) {
- fifos.SinkReq <- fmt.Sprintf("%s %s", req.Method, req.URL.String())
+ defer req.Body.Close()
+ fifos.LogReq <- fmt.Sprintf("%s %s", req.Method, req.URL)
host := strings.TrimSuffix(req.URL.Host, ":443")
for _, round := range []Round{
- rounds.RoundNoHead,
+ rounds.RoundGemini,
+ rounds.RoundWARC,
rounds.RoundDenySpy,
rounds.RoundRedditOld,
rounds.RoundHabrImage,
reqFlags := []string{}
unauthorized := false
+
+ caches.HTTPAuthCacheM.RLock()
+ if creds, ok := caches.HTTPAuthCache[req.URL.Host]; ok {
+ req.SetBasicAuth(creds[0], creds[1])
+ unauthorized = true
+ }
+ caches.HTTPAuthCacheM.RUnlock()
+
Retry:
resp, err := transport.RoundTrip(req)
if err != nil {
- fifos.SinkErr <- fmt.Sprintf("%s\t%s", req.URL.Host, err.Error())
+ fifos.LogErr <- fmt.Sprintf("%s\t%s", req.URL.Host, err.Error())
http.Error(w, err.Error(), http.StatusBadGateway)
return
}
if resp.StatusCode == http.StatusUnauthorized {
resp.Body.Close()
- authCacheM.Lock()
+ caches.HTTPAuthCacheM.Lock()
if unauthorized {
- delete(authCache, req.URL.Host)
+ delete(caches.HTTPAuthCache, req.URL.Host)
} else {
unauthorized = true
- if creds, ok := authCache[req.URL.Host]; ok {
- authCacheM.Unlock()
- req.SetBasicAuth(creds[0], creds[1])
- goto Retry
- }
}
- fifos.SinkOther <- fmt.Sprintf("%s\tauthorization required", req.URL.Host)
+ fifos.LogVarious <- fmt.Sprintf(
+ "%s %s\tHTTP authorization required", req.Method, req.URL.Host,
+ )
user, pass, err := authDialog(host, resp.Header.Get("WWW-Authenticate"))
if err != nil {
- authCacheM.Unlock()
- fifos.SinkErr <- fmt.Sprintf("%s\t%s", req.URL.Host, err.Error())
+ caches.HTTPAuthCacheM.Unlock()
+ fifos.LogErr <- fmt.Sprintf("%s\t%s", req.URL.Host, err.Error())
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
- authCache[req.URL.Host] = [2]string{user, pass}
- authCacheM.Unlock()
+ caches.HTTPAuthCache[req.URL.Host] = [2]string{user, pass}
+ caches.HTTPAuthCacheM.Unlock()
req.SetBasicAuth(user, pass)
+ fifos.LogHTTPAuth <- fmt.Sprintf("%s %s\t%s", req.Method, req.URL, user)
goto Retry
}
if unauthorized {
resp.Body.Close()
msg := fmt.Sprintf(
"%s %s\t%s\t%s\t%s\t%s",
- req.Method,
- req.URL.String(),
+ req.Method, req.URL,
resp.Status,
resp.Header.Get("Content-Type"),
humanize.IBytes(uint64(n)),
strings.Join(reqFlags, ","),
)
if resp.StatusCode == http.StatusOK {
- fifos.SinkOK <- msg
+ fifos.LogOK <- msg
} else {
- fifos.SinkOther <- msg
+ fifos.LogNonOK <- msg
}
}