X-Git-Url: http://www.git.stargrave.org/?a=blobdiff_plain;f=trip.go;h=73807a7967b418499fd38839559efa470170ddcc;hb=aca0f719ffa95e51420a625813f2f4cbf1d5397c;hp=27d4e9e30beacd8239b3827d74fe45a97526d8af;hpb=5de2203a3d1fb3fe2c6ac61cf07441634d3151ae;p=tofuproxy.git diff --git a/trip.go b/trip.go index 27d4e9e..73807a7 100644 --- a/trip.go +++ b/trip.go @@ -1,5 +1,6 @@ /* -tofuproxy -- HTTP proxy with TLS certificates management +tofuproxy -- flexible HTTP/HTTPS proxy, TLS terminator, X.509 TOFU + manager, WARC/geminispace browser Copyright (C) 2021 Sergey Matveev This program is free software: you can redistribute it and/or modify @@ -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 @@ -116,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 } }