]> Sergey Matveev's repositories - tofuproxy.git/blobdiff - trip.go
Raised copyright years
[tofuproxy.git] / trip.go
diff --git a/trip.go b/trip.go
index 019c4081b93ce4ca6cae1e9b44fd7bebacc83fa8..1b9a84538cc89de6dc143d49d5fecaf936451dac 100644 (file)
--- a/trip.go
+++ b/trip.go
@@ -1,6 +1,7 @@
 /*
-tofuproxy -- HTTP proxy with TLS certificates management
-Copyright (C) 2021 Sergey Matveev <stargrave@stargrave.org>
+tofuproxy -- flexible HTTP/HTTPS proxy, TLS terminator, X.509 TOFU
+             manager, WARC/geminispace browser
+Copyright (C) 2021-2022 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
@@ -27,8 +28,10 @@ import (
        "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 (
@@ -40,7 +43,7 @@ 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{}{
@@ -58,10 +61,13 @@ type Round func(
 ) (bool, error)
 
 func roundTrip(w http.ResponseWriter, req *http.Request) {
+       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.RoundLog,
+               rounds.RoundGemini,
+               rounds.RoundWARC,
                rounds.RoundDenySpy,
                rounds.RoundRedditOld,
                rounds.RoundHabrImage,
@@ -71,13 +77,55 @@ func roundTrip(w http.ResponseWriter, req *http.Request) {
                }
        }
 
+       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()
+               caches.HTTPAuthCacheM.Lock()
+               if unauthorized {
+                       delete(caches.HTTPAuthCache, req.URL.Host)
+               } else {
+                       unauthorized = true
+               }
+               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 {
+                       caches.HTTPAuthCacheM.Unlock()
+                       fifos.LogErr <- fmt.Sprintf("%s\t%s", req.URL.Host, err.Error())
+                       http.Error(w, err.Error(), http.StatusInternalServerError)
+                       return
+               }
+               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 {
+               reqFlags = append(reqFlags, "auth")
+       }
+       if resp.TLS != nil && resp.TLS.NegotiatedProtocol != "" {
+               reqFlags = append(reqFlags, resp.TLS.NegotiatedProtocol)
+       }
+
        for k, vs := range resp.Header {
                if _, ok := proxyHeaders[k]; ok {
                        continue
@@ -91,6 +139,7 @@ func roundTrip(w http.ResponseWriter, req *http.Request) {
                rounds.RoundDenyFonts,
                rounds.RoundTranscodeWebP,
                rounds.RoundTranscodeJXL,
+               rounds.RoundTranscodeAVIF,
                rounds.RoundRedirectHTML,
        } {
                cont, err := round(host, resp, w, req)
@@ -115,16 +164,16 @@ func roundTrip(w http.ResponseWriter, req *http.Request) {
        }
        resp.Body.Close()
        msg := fmt.Sprintf(
-               "%s %s\t%s\t%s\t%s",
-               req.Method,
-               req.URL.String(),
+               "%s %s\t%s\t%s\t%s\t%s",
+               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
        }
 }