From c53c36a4d0bd487dab339b65faa185582f4b26c0 Mon Sep 17 00:00:00 2001 From: makeworld Date: Mon, 23 Nov 2020 21:09:48 -0500 Subject: [PATCH] =?UTF-8?q?=E2=AC=86=EF=B8=8F=20=20go-gemini=20v0.9.3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Timeout when header takes too long, downloading uses proxies if appropriate --- CHANGELOG.md | 5 ++++- amfora.go | 3 +++ client/client.go | 54 +++++++++++++++++++++++++++++++++++++--------- display/private.go | 23 ++++++++++++++------ go.mod | 2 +- go.sum | 4 ++-- renderer/page.go | 9 ++------ 7 files changed, 73 insertions(+), 27 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a3d27f1..9a607f5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,11 +9,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Opening local files with `file://` URIs (#103, #117) ### Changed -- Updated [go-gemini](https://github.com/makeworld-the-better-one/go-gemini) to v0.9.1 to support CN-only wildcard certs +- Updated [go-gemini](https://github.com/makeworld-the-better-one/go-gemini) to v0.9.3 + - Supports CN-only wildcard certs + - Time out when header takes too long - Preformatted text is now light yellow by default ### Fixed - Single quotes are used in the default config for commands and paths so that Windows paths with backslashes will be parsed correctly +- Downloading now uses proxies when appropriate ## [1.6.0] - 2020-11-04 diff --git a/amfora.go b/amfora.go index 8c58a73..46c6643 100644 --- a/amfora.go +++ b/amfora.go @@ -4,6 +4,7 @@ import ( "fmt" "os" + "github.com/makeworld-the-better-one/amfora/client" "github.com/makeworld-the-better-one/amfora/config" "github.com/makeworld-the-better-one/amfora/display" ) @@ -43,6 +44,8 @@ func main() { os.Exit(1) } + client.Init() + display.Init() display.NewTab() display.NewTab() // Open extra tab and close it to fully initialize the app and wrapping diff --git a/client/client.go b/client/client.go index 2ec65d5..c7eee10 100644 --- a/client/client.go +++ b/client/client.go @@ -5,13 +5,29 @@ import ( "io/ioutil" "net" "net/url" + "time" "github.com/makeworld-the-better-one/go-gemini" "github.com/mitchellh/go-homedir" "github.com/spf13/viper" ) -var certCache = make(map[string][][]byte) +var ( + certCache = make(map[string][][]byte) + fetchClient *gemini.Client + dlClient *gemini.Client // For downloading +) + +func Init() { + fetchClient = &gemini.Client{ + ConnectTimeout: 10 * time.Second, // Default is 15 + ReadTimeout: time.Duration(viper.GetInt("a-general.page_max_time")) * time.Second, + } + dlClient = &gemini.Client{ + ConnectTimeout: 10 * time.Second, // Default is 15 + // No read timeout, download can take as long as it needs + } +} func clientCert(host string) ([]byte, []byte) { if cert := certCache[host]; cert != nil { @@ -53,18 +69,16 @@ func HasClientCert(host string) bool { return cert != nil } -// Fetch returns response data and an error. -// The error text is human friendly and should be displayed. -func Fetch(u string) (*gemini.Response, error) { +func fetch(u string, c *gemini.Client) (*gemini.Response, error) { parsed, _ := url.Parse(u) cert, key := clientCert(parsed.Host) var res *gemini.Response var err error if cert != nil { - res, err = gemini.FetchWithCert(u, cert, key) + res, err = c.FetchWithCert(u, cert, key) } else { - res, err = gemini.Fetch(u) + res, err = c.Fetch(u) } if err != nil { return nil, err @@ -78,17 +92,27 @@ func Fetch(u string) (*gemini.Response, error) { return res, err } -// FetchWithProxy is the same as Fetch, but uses a proxy. -func FetchWithProxy(proxyHostname, proxyPort, u string) (*gemini.Response, error) { +// Fetch returns response data and an error. +// The error text is human friendly and should be displayed. +func Fetch(u string) (*gemini.Response, error) { + return fetch(u, fetchClient) +} + +// Download is the same as Fetch but with no read timeout. +func Download(u string) (*gemini.Response, error) { + return fetch(u, dlClient) +} + +func fetchWithProxy(proxyHostname, proxyPort, u string, c *gemini.Client) (*gemini.Response, error) { parsed, _ := url.Parse(u) cert, key := clientCert(parsed.Host) var res *gemini.Response var err error if cert != nil { - res, err = gemini.FetchWithHostAndCert(net.JoinHostPort(proxyHostname, proxyPort), u, cert, key) + res, err = c.FetchWithHostAndCert(net.JoinHostPort(proxyHostname, proxyPort), u, cert, key) } else { - res, err = gemini.FetchWithHost(net.JoinHostPort(proxyHostname, proxyPort), u) + res, err = c.FetchWithHost(net.JoinHostPort(proxyHostname, proxyPort), u) } if err != nil { return nil, err @@ -102,3 +126,13 @@ func FetchWithProxy(proxyHostname, proxyPort, u string) (*gemini.Response, error return res, nil } + +// FetchWithProxy is the same as Fetch, but uses a proxy. +func FetchWithProxy(proxyHostname, proxyPort, u string) (*gemini.Response, error) { + return fetchWithProxy(proxyHostname, proxyPort, u, fetchClient) +} + +// DownloadWithProxy is the same as FetchWithProxy but with no read timeout. +func DownloadWithProxy(proxyHostname, proxyPort, u string) (*gemini.Response, error) { + return fetchWithProxy(proxyHostname, proxyPort, u, dlClient) +} diff --git a/display/private.go b/display/private.go index 67b54cf..771aa48 100644 --- a/display/private.go +++ b/display/private.go @@ -452,24 +452,35 @@ func handleURL(t *tab, u string, numRedirects int) (string, bool) { return ret("", false) } + var res2 *gemini.Response + var dlErr error + if errors.Is(err, renderer.ErrTooLarge) { // Make new request for downloading purposes - res, clientErr := client.Fetch(u) - if clientErr != nil && !errors.Is(clientErr, client.ErrTofu) { + if usingProxy { + res2, dlErr = client.DownloadWithProxy(proxyHostname, proxyPort, u) + } else { + res2, dlErr = client.Download(u) + } + if dlErr != nil && !errors.Is(dlErr, client.ErrTofu) { Error("URL Fetch Error", err.Error()) return ret("", false) } - go dlChoice("That page is too large. What would you like to do?", u, res) + go dlChoice("That page is too large. What would you like to do?", u, res2) return ret("", false) } if errors.Is(err, renderer.ErrTimedOut) { // Make new request for downloading purposes - res, clientErr := client.Fetch(u) - if clientErr != nil && !errors.Is(clientErr, client.ErrTofu) { + if usingProxy { + res2, dlErr = client.DownloadWithProxy(proxyHostname, proxyPort, u) + } else { + res2, dlErr = client.Download(u) + } + if dlErr != nil && !errors.Is(dlErr, client.ErrTofu) { Error("URL Fetch Error", err.Error()) return ret("", false) } - go dlChoice("Loading that page timed out. What would you like to do?", u, res) + go dlChoice("Loading that page timed out. What would you like to do?", u, res2) return ret("", false) } if err != nil { diff --git a/go.mod b/go.mod index e633377..4fe7054 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ require ( github.com/fsnotify/fsnotify v1.4.9 // indirect github.com/gdamore/tcell v1.3.1-0.20200608133353-cb1e5d6fa606 github.com/google/go-cmp v0.5.0 // indirect - github.com/makeworld-the-better-one/go-gemini v0.9.1 + github.com/makeworld-the-better-one/go-gemini v0.9.3 github.com/makeworld-the-better-one/go-isemoji v1.1.0 github.com/makeworld-the-better-one/progressbar/v3 v3.3.5-0.20200710151429-125743e22b4f github.com/mitchellh/go-homedir v1.1.0 diff --git a/go.sum b/go.sum index 1bf7e3f..d536110 100644 --- a/go.sum +++ b/go.sum @@ -125,8 +125,8 @@ github.com/lucasb-eyer/go-colorful v1.0.3 h1:QIbQXiugsb+q10B+MI+7DI1oQLdmnep86tW github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/makeworld-the-better-one/go-gemini v0.9.1 h1:/Vc6Y4Y1aOi4lZIBA1wDe+4N2xAI8EQ0CIjip2NUQkk= -github.com/makeworld-the-better-one/go-gemini v0.9.1/go.mod h1:P7/FbZ+IEIbA/d+A0Y3w2GNgD8SA2AcNv7aDGJbaWG4= +github.com/makeworld-the-better-one/go-gemini v0.9.3 h1:vpJc1u4LYpEI5h7GcOE2zSfOmpE9gQzt0vEayp/ilWc= +github.com/makeworld-the-better-one/go-gemini v0.9.3/go.mod h1:P7/FbZ+IEIbA/d+A0Y3w2GNgD8SA2AcNv7aDGJbaWG4= github.com/makeworld-the-better-one/go-isemoji v1.1.0 h1:wZBHOKB5zAIgaU2vaWnXFDDhatebB8TySrNVxjVV84g= github.com/makeworld-the-better-one/go-isemoji v1.1.0/go.mod h1:FBjkPl9rr0G4vlZCc+Mr+QcnOfGCTbGWYW8/1sp06I0= github.com/makeworld-the-better-one/progressbar/v3 v3.3.5-0.20200710151429-125743e22b4f h1:YEUlTs5gb35UlBLTgqrub9axWTYB3d7/8TxrkJDZpRI= diff --git a/renderer/page.go b/renderer/page.go index d7afb80..0bcd72a 100644 --- a/renderer/page.go +++ b/renderer/page.go @@ -5,8 +5,8 @@ import ( "errors" "io" "mime" + "os" "strings" - "time" "github.com/makeworld-the-better-one/amfora/structs" "github.com/makeworld-the-better-one/go-gemini" @@ -63,18 +63,13 @@ func MakePage(url string, res *gemini.Response, width, leftMargin int, proxied b } buf := new(bytes.Buffer) - go func() { - time.Sleep(time.Duration(viper.GetInt("a-general.page_max_time")) * time.Second) - res.Body.Close() - }() - _, err := io.CopyN(buf, res.Body, viper.GetInt64("a-general.page_max_size")+1) res.Body.Close() if err == nil { // Content was larger than max size return nil, ErrTooLarge } else if err != io.EOF { - if strings.HasSuffix(err.Error(), "use of closed network connection") { + if errors.Is(err, os.ErrDeadlineExceeded) { // Timed out return nil, ErrTimedOut }