skipper/fetch.go

187 lines
4.0 KiB
Go

package main
import "git.tebibyte.media/sashakoshka/skipper/dom"
import "git.tebibyte.media/sashakoshka/skipper/input"
import "git.tebibyte.media/sashakoshka/skipper/bookmarks"
import "fmt"
import "net/url"
import "strings"
import "git.sr.ht/~yotam/go-gemini"
func fetchBackward () {
if page.loading { return }
location := page.history.Backward()
if location != nil {
go fetchNoTrace(location)
}
}
func fetchForward () {
if page.loading { return }
location := page.history.Forward()
if location != nil {
go fetchNoTrace(location)
}
}
func fetch (location *url.URL) {
fetchBack(location, false, true)
}
func fetchStringUrl (stringLocation string) {
location, _ := url.Parse(stringLocation)
fetch(location)
}
func fetchNoTrace (location *url.URL) {
fetchBack(location, false, false)
}
func fetchSource (location *url.URL) {
fetchBack(location, true, false)
}
func fetchBack (location *url.URL, viewSource bool, addToHistory bool) {
if page.loading { return }
page.loading = true
page.readingInput = false
page.readingUrlInput = false
input.SelectNone()
page.currentUrl = location
if addToHistory {
page.history.Add(location)
}
if location.Scheme == "about" {
switch location.Opaque {
case "skipper":
fetchString(aboutGemtext, page.currentUrl)
case "home":
fetchString(homeGemtext, page.currentUrl)
case "bookmarks":
fetchString(bookmarks.Gemtext(), page.currentUrl)
default:
fetchString (
"# Error\n\n" +
"That page does not exist.",
page.currentUrl)
}
page.loading = false
redraw()
application.Draw()
return
}
redraw()
application.Draw()
response, err := client.Fetch(location.String())
body := response.Body
defer func () {
if body != nil {
body.Close()
}
} ()
if err != nil {
fetchString (
"# Error\n\n" +
"There was an error connecting to " +
location.String() + "\n\n" +
"> " + err.Error() + "\n",
page.currentUrl)
response.Status = 0
}
simpleStatus := gemini.SimplifyStatus(response.Status)
if simpleStatus == gemini.StatusRedirect {
page.redirectCounter ++
} else {
page.redirectCounter = 0
}
switch simpleStatus {
case 0:
case gemini.StatusInput:
fetchString("# " + response.Meta, page.currentUrl)
page.input.Reset()
page.input.Obscure = response.Status == 11
page.input.Select()
page.readingInput = true
case gemini.StatusSuccess:
if body != nil {
meta := strings.Split(response.Meta, ";")
var mime string
if len(meta) >= 1 {
mime = meta[0]
}
if viewSource {
setDocument(dom.ParseSource(body, page.currentUrl))
} else if mime == "text/gemini" {
setDocument(dom.ParseDocument(body, page.currentUrl))
} else {
setDocument(dom.ParseSource(body, page.currentUrl))
}
}
case gemini.StatusRedirect:
if page.redirectCounter > 8 {
fetchString (fmt.Sprintf (
"# Too many redirects\n\n" +
"Enough! The site has redirected us too many " +
"times."),
page.currentUrl)
break
}
location, err := url.Parse(response.Meta)
if err != nil {
fetchString (fmt.Sprintf (
"# Error\n\n" +
"The site responded with an invalid redirect:\n\n" +
"%s",
err.Error()), page.currentUrl)
} else {
page.history.Remove()
defer func () {
go fetchBack(location, viewSource, true)
} ()
}
case gemini.StatusTemporaryFailure, gemini.StatusPermanentFailure:
fetchString (fmt.Sprintf (
"# Error %d\n\n" +
"The site responded with an error:\n\n" +
"> %s",
response.Status, response.Meta), page.currentUrl)
default:
fetchString (fmt.Sprintf (
"# Unhandled response status %d\n\n" +
"Skipper doesn't know how to handle this type of " +
"response yet.",
response.Status), page.currentUrl)
}
page.loading = false
redraw()
application.Draw()
}
func fetchString (data string, location *url.URL) {
setDocument(dom.ParseDocument(strings.NewReader(data), location))
}
func setDocument (document *dom.Document) {
page.document = document
page.scroll = 0
application.SetTitle(document.Title() + " - Skipper")
}