tumblrandom: do some redirect-based stuff to avoid timeouts while fetching

This commit is contained in:
Luke Granger-Brown 2024-09-09 00:28:21 +00:00
parent 8c75a20274
commit 93efb988cb

View file

@ -4,6 +4,7 @@ import (
"context" "context"
"crypto/rand" "crypto/rand"
"crypto/sha256" "crypto/sha256"
"encoding/base64"
"encoding/hex" "encoding/hex"
"encoding/json" "encoding/json"
"errors" "errors"
@ -19,6 +20,7 @@ import (
"os/signal" "os/signal"
"path/filepath" "path/filepath"
"strings" "strings"
"sync"
"golang.org/x/oauth2" "golang.org/x/oauth2"
) )
@ -202,6 +204,11 @@ type likesResponse struct {
} `json:"response"` } `json:"response"`
} }
var (
refreshBuf map[string][]Post
refreshBufMu sync.Mutex
)
func (a *app) refreshLikes(rw http.ResponseWriter, r *http.Request) { func (a *app) refreshLikes(rw http.ResponseWriter, r *http.Request) {
ctx := r.Context() ctx := r.Context()
u, ok := user(ctx) u, ok := user(ctx)
@ -214,39 +221,88 @@ func (a *app) refreshLikes(rw http.ResponseWriter, r *http.Request) {
var likes []Post var likes []Post
const urlPrefix = "https://api.tumblr.com" const urlPrefix = "https://api.tumblr.com"
urlSuffix := "/v2/user/likes" refreshSess := r.FormValue("refresh_session")
for { if refreshSess == "" {
req, err := http.NewRequestWithContext(ctx, "GET", urlPrefix+urlSuffix, nil) refreshSessBytes := make([]byte, 32)
if err != nil { if _, err := rand.Read(refreshSessBytes); err != nil {
http.Error(rw, fmt.Sprintf("formulating likes request: %v", err), http.StatusInternalServerError) http.Error(rw, fmt.Sprintf("generating refresh session token: %v", err), http.StatusInternalServerError)
return return
} }
fmt.Println(req.URL) refreshSess = base64.RawURLEncoding.EncodeToString(refreshSessBytes)
resp, err := client.Do(req) refreshBufMu.Lock()
if err != nil { _, ok := refreshBuf[refreshSess]
http.Error(rw, fmt.Sprintf("performing likes request: %v", err), http.StatusInternalServerError) refreshBufMu.Unlock()
if ok {
http.Error(rw, fmt.Sprintf("randomness isn't random enough"), http.StatusInternalServerError)
return return
} }
defer resp.Body.Close() } else {
if resp.StatusCode != http.StatusOK { refreshBufMu.Lock()
errBody, _ := io.ReadAll(resp.Body) likes2, ok := refreshBuf[refreshSess]
http.Error(rw, fmt.Sprintf("likes request status %v\n\n%v", resp.StatusCode, string(errBody)), http.StatusInternalServerError) refreshBufMu.Unlock()
if !ok {
http.Error(rw, fmt.Sprintf("refresh session %q is missing", refreshSess), http.StatusBadRequest)
return return
} }
likes = likes2
var r likesResponse
if err := json.NewDecoder(resp.Body).Decode(&r); err != nil {
http.Error(rw, fmt.Sprintf("decoding likes body as json: %v", err), http.StatusInternalServerError)
return
}
likes = append(likes, r.Response.LikedPosts...)
urlSuffix = r.Response.Links.Next.Href
if urlSuffix == "" {
break
}
} }
urlSuffix := r.FormValue("url_suffix")
if urlSuffix == "" {
urlSuffix = "/v2/user/likes"
}
req, err := http.NewRequestWithContext(ctx, "GET", urlPrefix+urlSuffix, nil)
if err != nil {
http.Error(rw, fmt.Sprintf("formulating likes request: %v", err), http.StatusInternalServerError)
return
}
fmt.Println(req.URL)
resp, err := client.Do(req)
if err != nil {
http.Error(rw, fmt.Sprintf("performing likes request: %v", err), http.StatusInternalServerError)
return
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
errBody, _ := io.ReadAll(resp.Body)
http.Error(rw, fmt.Sprintf("likes request status %v\n\n%v", resp.StatusCode, string(errBody)), http.StatusInternalServerError)
return
}
var lr likesResponse
if err := json.NewDecoder(resp.Body).Decode(&lr); err != nil {
http.Error(rw, fmt.Sprintf("decoding likes body as json: %v", err), http.StatusInternalServerError)
return
}
likes = append(likes, lr.Response.LikedPosts...)
urlSuffix = lr.Response.Links.Next.Href
if urlSuffix != "" {
fmt.Fprintf(rw, `<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<form method="POST" id="theForm">
<input type="hidden" name="url_suffix" value="%q">
<input type="hidden" name="refresh_session" value="%q">
<input type="submit" id="theSubmit">
</form>
<script>
document.querySelector('#theSubmit').disabled = true;
document.querySelector('#theForm').submit();
</script>
</body>
</html>
`, urlSuffix, refreshSess)
return
}
refreshBufMu.Lock()
delete(refreshBuf, refreshSess)
refreshBufMu.Unlock()
u.Likes = likes u.Likes = likes
if err := u.save(); err != nil { if err := u.save(); err != nil {
http.Error(rw, fmt.Sprintf("saving likes: %v", err), http.StatusInternalServerError) http.Error(rw, fmt.Sprintf("saving likes: %v", err), http.StatusInternalServerError)