tumblrandom: do some redirect-based stuff to avoid timeouts while fetching
This commit is contained in:
parent
8c75a20274
commit
93efb988cb
1 changed files with 81 additions and 25 deletions
|
@ -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)
|
||||||
|
|
Loading…
Reference in a new issue