Merge branch 'master' into feeds

This commit is contained in:
makeworld 2020-11-23 21:11:51 -05:00
commit ba28b2b5f9
16 changed files with 376 additions and 59 deletions

View File

@ -5,9 +5,18 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased] ## [Unreleased]
### Added
- Opening local files with `file://` URIs (#103, #117)
### Changed ### 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
- Preformatted text is now grey by default - 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 ## [1.6.0] - 2020-11-04

View File

@ -1,8 +1,6 @@
# Notes # Notes
## Feeds (temp) ## Feeds (temp)
- Support multiple links in Atom feeds
- Display gemini one if it exist, then HTTP(S), then whatever is first
- TODO: remove all logger lines - TODO: remove all logger lines
## Issues ## Issues

View File

@ -116,6 +116,7 @@ Features in *italics* are in the master branch, but not in the latest release.
- [x] Bookmarks - [x] Bookmarks
- [x] Download pages and arbitrary data - [x] Download pages and arbitrary data
- [x] Theming - [x] Theming
- Check out the [user contributed themes](https://github.com/makeworld-the-better-one/amfora/tree/master/contrib/themes)!
- [x] Emoji favicons - [x] Emoji favicons
- See `gemini://mozz.us/files/rfc_gemini_favicon.gmi` for details - See `gemini://mozz.us/files/rfc_gemini_favicon.gmi` for details
- Disabled by default, enable in config - Disabled by default, enable in config

View File

@ -10,4 +10,5 @@ Thank you to the following contributors, who have helped make Amfora great. FOSS
- Timur Ismagilov (@bouncepaw) - Timur Ismagilov (@bouncepaw)
- Matt Caroll (@ohiolab) - Matt Caroll (@ohiolab)
- Patryk Niedźwiedziński (@pniedzwiedzinski) - Patryk Niedźwiedziński (@pniedzwiedzinski)
- Trevor Slocum (@tsclocum) - Trevor Slocum (@tsclocum)
- Mattias Jadelius (@jedthehumanoid)

View File

@ -4,6 +4,7 @@ import (
"fmt" "fmt"
"os" "os"
"github.com/makeworld-the-better-one/amfora/client"
"github.com/makeworld-the-better-one/amfora/config" "github.com/makeworld-the-better-one/amfora/config"
"github.com/makeworld-the-better-one/amfora/display" "github.com/makeworld-the-better-one/amfora/display"
"github.com/makeworld-the-better-one/amfora/feeds" "github.com/makeworld-the-better-one/amfora/feeds"
@ -50,6 +51,8 @@ func main() {
os.Exit(1) os.Exit(1)
} }
client.Init()
display.Init() display.Init()
display.NewTab() display.NewTab()
display.NewTab() // Open extra tab and close it to fully initialize the app and wrapping display.NewTab() // Open extra tab and close it to fully initialize the app and wrapping

View File

@ -6,14 +6,31 @@ import (
"net" "net"
"net/url" "net/url"
"sync" "sync"
"time"
"github.com/makeworld-the-better-one/go-gemini" "github.com/makeworld-the-better-one/go-gemini"
"github.com/mitchellh/go-homedir" "github.com/mitchellh/go-homedir"
"github.com/spf13/viper" "github.com/spf13/viper"
) )
var certCache = make(map[string][][]byte) var (
var certCacheMu = &sync.RWMutex{} certCache = make(map[string][][]byte)
certCacheMu = &sync.RWMutex{}
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) { func clientCert(host string) ([]byte, []byte) {
certCacheMu.RLock() certCacheMu.RLock()
@ -66,18 +83,16 @@ func HasClientCert(host string) bool {
return cert != nil return cert != nil
} }
// Fetch returns response data and an error. func fetch(u string, c *gemini.Client) (*gemini.Response, error) {
// The error text is human friendly and should be displayed.
func Fetch(u string) (*gemini.Response, error) {
parsed, _ := url.Parse(u) parsed, _ := url.Parse(u)
cert, key := clientCert(parsed.Host) cert, key := clientCert(parsed.Host)
var res *gemini.Response var res *gemini.Response
var err error var err error
if cert != nil { if cert != nil {
res, err = gemini.FetchWithCert(u, cert, key) res, err = c.FetchWithCert(u, cert, key)
} else { } else {
res, err = gemini.Fetch(u) res, err = c.Fetch(u)
} }
if err != nil { if err != nil {
return nil, err return nil, err
@ -91,17 +106,27 @@ func Fetch(u string) (*gemini.Response, error) {
return res, err return res, err
} }
// FetchWithProxy is the same as Fetch, but uses a proxy. // Fetch returns response data and an error.
func FetchWithProxy(proxyHostname, proxyPort, u string) (*gemini.Response, 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) parsed, _ := url.Parse(u)
cert, key := clientCert(parsed.Host) cert, key := clientCert(parsed.Host)
var res *gemini.Response var res *gemini.Response
var err error var err error
if cert != nil { 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 { } else {
res, err = gemini.FetchWithHost(net.JoinHostPort(proxyHostname, proxyPort), u) res, err = c.FetchWithHost(net.JoinHostPort(proxyHostname, proxyPort), u)
} }
if err != nil { if err != nil {
return nil, err return nil, err
@ -115,3 +140,13 @@ func FetchWithProxy(proxyHostname, proxyPort, u string) (*gemini.Response, error
return res, nil 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)
}

View File

@ -28,14 +28,15 @@ auto_redirect = false
# #
# The best to define a command is using a string array. # The best to define a command is using a string array.
# Examples: # Examples:
# http = ["firefox"] # http = ['firefox']
# http = ["custom-browser", "--flag", "--option=2"] # http = ['custom-browser', '--flag', '--option=2']
# http = ["/path/with spaces/in it/firefox"] # http = ['/path/with spaces/in it/firefox']
# #
# Using just a string will also work, but it is deprecated, # Note the use of single quotes, so that backslashes will not be escaped.
# and will degrade if you use paths with spaces. # Using just a string will also work, but it is deprecated, and will degrade if
# you use paths with spaces.
http = "default" http = 'default'
# Any URL that will accept a query string can be put here # Any URL that will accept a query string can be put here
search = "gemini://gus.guru/search" search = "gemini://gus.guru/search"
@ -58,7 +59,8 @@ max_width = 100
# 'downloads' is the path to a downloads folder. # 'downloads' is the path to a downloads folder.
# An empty value means the code will find the default downloads folder for your system. # An empty value means the code will find the default downloads folder for your system.
# If the path does not exist it will be created. # If the path does not exist it will be created.
downloads = "" # Note the use of single quotes, so that backslashes will not be escaped.
downloads = ''
# Max size for displayable content in bytes - after that size a download window pops up # Max size for displayable content in bytes - after that size a download window pops up
page_max_size = 2097152 # 2 MiB page_max_size = 2097152 # 2 MiB
@ -71,16 +73,17 @@ emoji_favicons = false
[auth] [auth]
# Authentication settings # Authentication settings
# Note the use of single quotes for values, so that backslashes will not be escaped.
[auth.certs] [auth.certs]
# Client certificates # Client certificates
# Set domain name equal to path to client cert # Set domain name equal to path to client cert
# "example.com" = "mycert.crt" # "example.com" = 'mycert.crt'
[auth.keys] [auth.keys]
# Client certificate keys # Client certificate keys
# Set domain name equal to path to key for the client cert above # Set domain name equal to path to key for the client cert above
# "example.com" = "mycert.key" # "example.com" = 'mycert.key'
[keybindings] [keybindings]
@ -94,18 +97,19 @@ shift_numbers = "!@#$%^&*()"
[url-handlers] [url-handlers]
# Allows setting the commands to run for various URL schemes. # Allows setting the commands to run for various URL schemes.
# E.g. to open FTP URLs with FileZilla set the following key: # E.g. to open FTP URLs with FileZilla set the following key:
# ftp = "filezilla" # ftp = 'filezilla'
# You can set any scheme to "off" or "" to disable handling it, or # You can set any scheme to "off" or "" to disable handling it, or
# just leave the key unset. # just leave the key unset.
# #
# DO NOT use this for setting the HTTP command. # DO NOT use this for setting the HTTP command.
# Use the http setting in the "a-general" section above. # Use the http setting in the "a-general" section above.
# #
# NOTE: These settings are override by the ones in the proxies section. # NOTE: These settings are overrided by the ones in the proxies section.
# Note the use of single quotes, so that backslashes will not be escaped.
# This is a special key that defines the handler for all URL schemes for which # This is a special key that defines the handler for all URL schemes for which
# no handler is defined. # no handler is defined.
other = "off" other = 'off'
[cache] [cache]

View File

@ -60,7 +60,7 @@ var theme = map[string]tcell.Color{
"link_number": tcell.ColorSilver, "link_number": tcell.ColorSilver,
"regular_text": tcell.ColorWhite, "regular_text": tcell.ColorWhite,
"quote_text": tcell.ColorWhite, "quote_text": tcell.ColorWhite,
"preformatted_text": tcell.ColorGrey, "preformatted_text": tcell.Color229, // xterm:Wheat1, #ffffaf
"list_text": tcell.ColorWhite, "list_text": tcell.ColorWhite,
} }

26
contrib/themes/README.md Normal file
View File

@ -0,0 +1,26 @@
# User Contributed Themes
You can use these themes by replacing the `[theme]` section of your config with their contents. Some themes won't display properly on terminals that do not have truecolor support.
## Nord
Contributed by **[@lokesh-krishna](https://github.com/lokesh-krishna)**.
![screenshot of the nord theme](https://user-images.githubusercontent.com/20235646/99020443-a93a1980-2584-11eb-8028-0b95cfcf0fc6.png)
## Dracula
Contributed by **[@crdpa](https://github.com/crdpa)**.
![screenshot of dracula theme](https://user-images.githubusercontent.com/61637474/99983229-5b928d80-2d8a-11eb-8e5c-e5681bb274c5.png)
<details>
<summary>More screenshots</summary>
![screenshot of dracula theme](https://user-images.githubusercontent.com/61637474/99983237-5e8d7e00-2d8a-11eb-8e22-3a3459ae560a.png)
![screenshot of dracula theme](https://user-images.githubusercontent.com/61637474/99983210-53d2e900-2d8a-11eb-9ab7-12dc10c2933a.png)
</details>
## Yours?
Contribute your own theme by opening a PR.

103
contrib/themes/dracula.toml Normal file
View File

@ -0,0 +1,103 @@
[theme]
# This section is for changing the COLORS used in Amfora.
# These colors only apply if 'color' is enabled above.
# Colors can be set using a W3C color name, or a hex value such as "#ffffff".
# Note that not all colors will work on terminals that do not have truecolor support.
# If you want to stick to the standard 16 or 256 colors, you can get
# a list of those here: https://jonasjacek.github.io/colors/
# DO NOT use the names from that site, just the hex codes.
# Definitions:
# bg = background
# fg = foreground
# dl = download
# btn = button
# hdg = heading
# bkmk = bookmark
# modal = a popup window/box in the middle of the screen
# EXAMPLES:
# hdg_1 = "green"
# hdg_2 = "#5f0000"
# Available keys to set:
# bg: background for pages, tab row, app in general
# tab_num: The number/highlight of the tabs at the top
# tab_divider: The color of the divider character between tab numbers: |
# bottombar_label: The color of the prompt that appears when you press space
# bottombar_text: The color of the text you type
# bottombar_bg
bg = "#282a36"
fg = "#f8f8f2"
tab_num = "#50fa7b"
tab_divider = "#f8f8f2"
bottombar_bg = "#282a36"
bottombar_text = "#f8f8f2"
bottombar_label = "#9aedfe"
# hdg_1
# hdg_2
# hdg_3
# amfora_link: A link that Amfora supports viewing. For now this is only gemini://
# foreign_link: HTTP(S), Gopher, etc
# link_number: The silver number that appears to the left of a link
# regular_text: Normal gemini text, and plaintext documents
# quote_text
# preformatted_text
# list_text
hdg_1 = "#5af78e"
hdg_2 = "#9aedfe"
hdg_3 = "#caa9fa"
amfora_link = "#f4f99d"
foreign_link = "#d4d989"
link_number = "#ff5555"
regular_text = "#f8f8f2"
quote_text = "#E6E6E6"
preformatted_text = "#f8f8f2"
list_text = "#f8f8f2"
# btn_bg: The bg color for all modal buttons
# btn_text: The text color for all modal buttons
btn_bg = "#bfbfbf"
btn_text = "#4d4d4d"
dl_choice_modal_bg = "#282a36"
dl_choice_modal_text = "#f8f8f2"
dl_modal_bg = "#282a36"
dl_modal_text = "#f8f8f2"
info_modal_bg = "#282a36"
info_modal_text = "#f8f8f2"
error_modal_bg = "#282a36"
error_modal_text = "#ff5555"
yesno_modal_bg = "#282a36"
yesno_modal_text = "#f1fa8c"
tofu_modal_bg = "#282a36"
tofu_modal_text = "#f8f8f2"
# input_modal_bg
# input_modal_text
# input_modal_field_bg: The bg of the input field, where you type the text
# input_modal_field_text: The color of the text you type
input_modal_bg = "#282a36"
input_modal_text = "#f8f8f2"
input_modal_field_bg = "#4d4d4d"
input_modal_field_text ="#f8f8f2"
# bkmk_modal_bg
# bkmk_modal_text
# bkmk_modal_label
# bkmk_modal_field_bg
# bkmk_modal_field_text
bkmk_modal_bg = "#282a36"
bkmk_modal_text = "#f8f8f2"
bkmk_modal_label = "#f8f8f2"
bkmk_modal_field_bg = "#000000"
bkmk_modal_field_text = "#f8f8f2"

View File

@ -25,14 +25,15 @@ auto_redirect = false
# #
# The best to define a command is using a string array. # The best to define a command is using a string array.
# Examples: # Examples:
# http = ["firefox"] # http = ['firefox']
# http = ["custom-browser", "--flag", "--option=2"] # http = ['custom-browser', '--flag', '--option=2']
# http = ["/path/with spaces/in it/firefox"] # http = ['/path/with spaces/in it/firefox']
# #
# Using just a string will also work, but it is deprecated, # Note the use of single quotes, so that backslashes will not be escaped.
# and will degrade if you use paths with spaces. # Using just a string will also work, but it is deprecated, and will degrade if
# you use paths with spaces.
http = "default" http = 'default'
# Any URL that will accept a query string can be put here # Any URL that will accept a query string can be put here
search = "gemini://gus.guru/search" search = "gemini://gus.guru/search"
@ -55,7 +56,8 @@ max_width = 100
# 'downloads' is the path to a downloads folder. # 'downloads' is the path to a downloads folder.
# An empty value means the code will find the default downloads folder for your system. # An empty value means the code will find the default downloads folder for your system.
# If the path does not exist it will be created. # If the path does not exist it will be created.
downloads = "" # Note the use of single quotes, so that backslashes will not be escaped.
downloads = ''
# Max size for displayable content in bytes - after that size a download window pops up # Max size for displayable content in bytes - after that size a download window pops up
page_max_size = 2097152 # 2 MiB page_max_size = 2097152 # 2 MiB
@ -68,16 +70,17 @@ emoji_favicons = false
[auth] [auth]
# Authentication settings # Authentication settings
# Note the use of single quotes for values, so that backslashes will not be escaped.
[auth.certs] [auth.certs]
# Client certificates # Client certificates
# Set domain name equal to path to client cert # Set domain name equal to path to client cert
# "example.com" = "mycert.crt" # "example.com" = 'mycert.crt'
[auth.keys] [auth.keys]
# Client certificate keys # Client certificate keys
# Set domain name equal to path to key for the client cert above # Set domain name equal to path to key for the client cert above
# "example.com" = "mycert.key" # "example.com" = 'mycert.key'
[keybindings] [keybindings]
@ -91,18 +94,19 @@ shift_numbers = "!@#$%^&*()"
[url-handlers] [url-handlers]
# Allows setting the commands to run for various URL schemes. # Allows setting the commands to run for various URL schemes.
# E.g. to open FTP URLs with FileZilla set the following key: # E.g. to open FTP URLs with FileZilla set the following key:
# ftp = "filezilla" # ftp = 'filezilla'
# You can set any scheme to "off" or "" to disable handling it, or # You can set any scheme to "off" or "" to disable handling it, or
# just leave the key unset. # just leave the key unset.
# #
# DO NOT use this for setting the HTTP command. # DO NOT use this for setting the HTTP command.
# Use the http setting in the "a-general" section above. # Use the http setting in the "a-general" section above.
# #
# NOTE: These settings are override by the ones in the proxies section. # NOTE: These settings are overrided by the ones in the proxies section.
# Note the use of single quotes, so that backslashes will not be escaped.
# This is a special key that defines the handler for all URL schemes for which # This is a special key that defines the handler for all URL schemes for which
# no handler is defined. # no handler is defined.
other = "off" other = 'off'
[cache] [cache]

116
display/file.go Normal file
View File

@ -0,0 +1,116 @@
package display
import (
"fmt"
"io/ioutil"
"mime"
"net/url"
"os"
"path/filepath"
"strings"
"github.com/makeworld-the-better-one/amfora/renderer"
"github.com/makeworld-the-better-one/amfora/structs"
"github.com/spf13/viper"
)
// handleFile handles urls using file:// protocol
func handleFile(u string) (*structs.Page, bool) {
page := &structs.Page{}
uri, err := url.ParseRequestURI(u)
if err != nil {
Error("File Error", "Cannot parse URI: "+err.Error())
return page, false
}
fi, err := os.Stat(uri.Path)
if err != nil {
Error("File Error", "Cannot open local file: "+err.Error())
return page, false
}
switch mode := fi.Mode(); {
case mode.IsDir():
return createDirectoryListing(u)
case mode.IsRegular():
if fi.Size() > viper.GetInt64("a-general.page_max_size") {
Error("File Error", "Cannot open local file, exceeds page max size")
return page, false
}
mimetype := mime.TypeByExtension(filepath.Ext(uri.Path))
if strings.HasSuffix(u, ".gmi") || strings.HasSuffix(u, ".gemini") {
mimetype = "text/gemini"
}
if !strings.HasPrefix(mimetype, "text/") {
Error("File Error", "Cannot open file, not recognized as text.")
return page, false
}
content, err := ioutil.ReadFile(uri.Path)
if err != nil {
Error("File Error", "Cannot open local file: "+err.Error())
return page, false
}
if mimetype == "text/gemini" {
rendered, links := renderer.RenderGemini(string(content), textWidth(), leftMargin(), false)
page = &structs.Page{
Mediatype: structs.TextGemini,
URL: u,
Raw: string(content),
Content: rendered,
Links: links,
Width: termW,
}
} else {
page = &structs.Page{
Mediatype: structs.TextPlain,
URL: u,
Raw: string(content),
Content: renderer.RenderPlainText(string(content), leftMargin()),
Links: []string{},
Width: termW,
}
}
}
return page, true
}
// createDirectoryListing creates a text/gemini page for a directory
// that lists all the files as links.
func createDirectoryListing(u string) (*structs.Page, bool) {
page := &structs.Page{}
uri, err := url.ParseRequestURI(u)
if err != nil {
Error("Directory Error", "Cannot parse URI: "+err.Error())
}
files, err := ioutil.ReadDir(uri.Path)
if err != nil {
Error("Directory error", "Cannot open local directory: "+err.Error())
return page, false
}
content := "Index of " + uri.Path + "\n"
content += "=> ../ ../\n"
for _, f := range files {
separator := ""
if f.IsDir() {
separator = "/"
}
content += fmt.Sprintf("=> %s%s %s%s\n", f.Name(), separator, f.Name(), separator)
}
rendered, links := renderer.RenderGemini(content, textWidth(), leftMargin(), false)
page = &structs.Page{
Mediatype: structs.TextGemini,
URL: u,
Raw: content,
Content: rendered,
Links: links,
Width: termW,
}
return page, true
}

View File

@ -86,7 +86,9 @@ func reformatPage(p *structs.Page) {
case structs.TextGemini: case structs.TextGemini:
// Links are not recorded because they won't change // Links are not recorded because they won't change
proxied := true proxied := true
if strings.HasPrefix(p.URL, "gemini") || strings.HasPrefix(p.URL, "about") { if strings.HasPrefix(p.URL, "gemini") ||
strings.HasPrefix(p.URL, "about") ||
strings.HasPrefix(p.URL, "file") {
proxied = false proxied = false
} }
rendered, _ = renderer.RenderGemini(p.Raw, textWidth(), leftMargin(), proxied) rendered, _ = renderer.RenderGemini(p.Raw, textWidth(), leftMargin(), proxied)
@ -396,7 +398,16 @@ func handleURL(t *tab, u string, numRedirects int) (string, bool) {
usingProxy = true usingProxy = true
} }
if !strings.HasPrefix(u, "http") && !strings.HasPrefix(u, "gemini") { if strings.HasPrefix(u, "file") {
page, ok := handleFile(u)
if !ok {
return ret("", false)
}
setPage(t, page)
return ret(u, true)
}
if !strings.HasPrefix(u, "http") && !strings.HasPrefix(u, "gemini") && !strings.HasPrefix(u, "file") {
// Not a Gemini URL // Not a Gemini URL
if proxy == "" || proxy == "off" { if proxy == "" || proxy == "off" {
// No proxy available // No proxy available
@ -467,24 +478,35 @@ func handleURL(t *tab, u string, numRedirects int) (string, bool) {
return ret("", false) return ret("", false)
} }
var res2 *gemini.Response
var dlErr error
if errors.Is(err, renderer.ErrTooLarge) { if errors.Is(err, renderer.ErrTooLarge) {
// Make new request for downloading purposes // Make new request for downloading purposes
res, clientErr := client.Fetch(u) if usingProxy {
if clientErr != nil && !errors.Is(clientErr, client.ErrTofu) { 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()) Error("URL Fetch Error", err.Error())
return ret("", false) 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) return ret("", false)
} }
if errors.Is(err, renderer.ErrTimedOut) { if errors.Is(err, renderer.ErrTimedOut) {
// Make new request for downloading purposes // Make new request for downloading purposes
res, clientErr := client.Fetch(u) if usingProxy {
if clientErr != nil && !errors.Is(clientErr, client.ErrTofu) { 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()) Error("URL Fetch Error", err.Error())
return ret("", false) 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) return ret("", false)
} }
if err != nil { if err != nil {

2
go.mod
View File

@ -7,7 +7,7 @@ require (
github.com/fsnotify/fsnotify v1.4.9 // indirect github.com/fsnotify/fsnotify v1.4.9 // indirect
github.com/gdamore/tcell v1.3.1-0.20200608133353-cb1e5d6fa606 github.com/gdamore/tcell v1.3.1-0.20200608133353-cb1e5d6fa606
github.com/google/go-cmp v0.5.0 // indirect 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/go-isemoji v1.1.0
github.com/makeworld-the-better-one/progressbar/v3 v3.3.5-0.20200710151429-125743e22b4f github.com/makeworld-the-better-one/progressbar/v3 v3.3.5-0.20200710151429-125743e22b4f
github.com/mitchellh/go-homedir v1.1.0 github.com/mitchellh/go-homedir v1.1.0

4
go.sum
View File

@ -133,8 +133,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/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 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4=
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= 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.3 h1:vpJc1u4LYpEI5h7GcOE2zSfOmpE9gQzt0vEayp/ilWc=
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/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 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/go-isemoji v1.1.0/go.mod h1:FBjkPl9rr0G4vlZCc+Mr+QcnOfGCTbGWYW8/1sp06I0=
github.com/makeworld-the-better-one/gofeed v1.1.1-0.20201123002655-c0c6354134fe h1:i3b9Qy5z23DcXRnrsMYcM5s9Ng5VIidM1xZd+szuTsY= github.com/makeworld-the-better-one/gofeed v1.1.1-0.20201123002655-c0c6354134fe h1:i3b9Qy5z23DcXRnrsMYcM5s9Ng5VIidM1xZd+szuTsY=

View File

@ -5,8 +5,8 @@ import (
"errors" "errors"
"io" "io"
"mime" "mime"
"os"
"strings" "strings"
"time"
"github.com/makeworld-the-better-one/amfora/structs" "github.com/makeworld-the-better-one/amfora/structs"
"github.com/makeworld-the-better-one/go-gemini" "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) 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) _, err := io.CopyN(buf, res.Body, viper.GetInt64("a-general.page_max_size")+1)
res.Body.Close() res.Body.Close()
if err == nil { if err == nil {
// Content was larger than max size // Content was larger than max size
return nil, ErrTooLarge return nil, ErrTooLarge
} else if err != io.EOF { } else if err != io.EOF {
if strings.HasSuffix(err.Error(), "use of closed network connection") { if errors.Is(err, os.ErrDeadlineExceeded) {
// Timed out // Timed out
return nil, ErrTimedOut return nil, ErrTimedOut
} }