]> Sergey Matveev's repositories - tofuproxy.git/blobdiff - trip.go
WARC
[tofuproxy.git] / trip.go
diff --git a/trip.go b/trip.go
index 27d4e9e30beacd8239b3827d74fe45a97526d8af..b217750205cf26561be27e65f47ce20bb0ef3e2c 100644 (file)
--- a/trip.go
+++ b/trip.go
@@ -1,5 +1,5 @@
 /*
-tofuproxy -- HTTP proxy with TLS certificates management
+tofuproxy -- flexible HTTP/WARC 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
@@ -27,6 +27,7 @@ import (
        "time"
 
        "github.com/dustin/go-humanize"
+       "go.stargrave.org/tofuproxy/caches"
        "go.stargrave.org/tofuproxy/fifos"
        "go.stargrave.org/tofuproxy/rounds"
 )
@@ -58,10 +59,12 @@ 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.RoundWARC,
                rounds.RoundDenySpy,
                rounds.RoundRedditOld,
                rounds.RoundHabrImage,
@@ -71,13 +74,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
@@ -116,16 +161,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
        }
 }