fix(drivers/onedrive_sharelink): reuse shared HTTP clients (#2961)

fix(onedrive_sharelink): reuse shared HTTP clients

- Reuse shared client settings for redirect and GraphQL requests
- Propagate request contexts through password and pagination requests
- Close OneDrive response bodies that were missing cleanup
- Add tests for shared client settings and redirect handling

Signed-off-by: jyxjjj <16695261+jyxjjj@users.noreply.github.com>
Co-authored-by: Codex <267193182+codex@users.noreply.github.com>
This commit is contained in:
ShenLin
2026-08-29 01:10:01 +08:00
committed by GitHub
parent 3943180898
commit 31ae0f5bc9
2 changed files with 75 additions and 24 deletions
+28 -23
View File
@@ -2,7 +2,6 @@ package onedrive_sharelink
import ( import (
"context" "context"
"crypto/tls"
"encoding/json" "encoding/json"
"fmt" "fmt"
"io" "io"
@@ -13,30 +12,28 @@ import (
"time" "time"
"github.com/OpenListTeam/OpenList/v4/drivers/base" "github.com/OpenListTeam/OpenList/v4/drivers/base"
"github.com/OpenListTeam/OpenList/v4/internal/conf" internalNet "github.com/OpenListTeam/OpenList/v4/internal/net"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"golang.org/x/net/html" "golang.org/x/net/html"
) )
// NewNoRedirectClient creates an HTTP client that doesn't follow redirects // NewNoRedirectClient creates an HTTP client that doesn't follow redirects
func NewNoRedirectCLient() *http.Client { func NewNoRedirectCLient() *http.Client {
return &http.Client{ client := internalNet.NewHttpClient()
Timeout: time.Hour * 48, client.CheckRedirect = func(req *http.Request, via []*http.Request) error {
Transport: &http.Transport{
Proxy: http.ProxyFromEnvironment,
TLSClientConfig: &tls.Config{InsecureSkipVerify: conf.Conf.TlsInsecureSkipVerify},
},
// Prevent following redirects
CheckRedirect: func(req *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse return http.ErrUseLastResponse
},
} }
return client
} }
// getCookiesWithPassword fetches cookies required for authenticated access using the provided password // getCookiesWithPassword fetches cookies required for authenticated access using the provided password
func getCookiesWithPassword(link, password string) (string, error) { func getCookiesWithPassword(ctx context.Context, link, password string) (string, error) {
// Send GET request // Send GET request
resp, err := http.Get(link) req, err := http.NewRequestWithContext(ctx, http.MethodGet, link, nil)
if err != nil {
return "", err
}
resp, err := base.HttpClient.Do(req)
if err != nil { if err != nil {
return "", err return "", err
} }
@@ -95,16 +92,18 @@ func getCookiesWithPassword(link, password string) (string, error) {
"__VIEWSTATEENCRYPTED": []string{""}, "__VIEWSTATEENCRYPTED": []string{""},
} }
client := &http.Client{ client := NewNoRedirectCLient()
CheckRedirect: func(req *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse
},
}
// Send the POST request, preventing redirects // Send the POST request, preventing redirects
resp, err = client.PostForm(newURL, data) req, err = http.NewRequestWithContext(ctx, http.MethodPost, newURL, strings.NewReader(data.Encode()))
if err != nil { if err != nil {
return "", err return "", err
} }
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
resp, err = client.Do(req)
if err != nil {
return "", err
}
defer resp.Body.Close()
// Extract the desired cookie value // Extract the desired cookie value
cookie := resp.Cookies() cookie := resp.Cookies()
@@ -153,6 +152,7 @@ func (d *OnedriveSharelink) getHeaders(ctx context.Context) (http.Header, error)
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer answerNoRedirect.Body.Close()
redirectUrl := answerNoRedirect.Header.Get("Location") redirectUrl := answerNoRedirect.Header.Get("Location")
log.Debugln("redirectUrl:", redirectUrl) log.Debugln("redirectUrl:", redirectUrl)
if redirectUrl == "" { if redirectUrl == "" {
@@ -169,7 +169,7 @@ func (d *OnedriveSharelink) getHeaders(ctx context.Context) (http.Header, error)
header.Set("authority", u.Host) header.Set("authority", u.Host)
return header, nil return header, nil
} else { } else {
cookie, err := getCookiesWithPassword(d.ShareLinkURL, d.ShareLinkPassword) cookie, err := getCookiesWithPassword(ctx, d.ShareLinkURL, d.ShareLinkPassword)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -197,6 +197,7 @@ func (d *OnedriveSharelink) getFiles(ctx context.Context, path string) ([]Item,
if err != nil { if err != nil {
return nil, err return nil, err
} }
_ = answerNoRedirect.Body.Close()
redirectUrl = answerNoRedirect.Header.Get("Location") redirectUrl = answerNoRedirect.Header.Get("Location")
} else { } else {
header = d.Headers header = d.Headers
@@ -205,6 +206,7 @@ func (d *OnedriveSharelink) getFiles(ctx context.Context, path string) ([]Item,
if err != nil { if err != nil {
return nil, err return nil, err
} }
_ = answerNoRedirect.Body.Close()
redirectUrl = answerNoRedirect.Header.Get("Location") redirectUrl = answerNoRedirect.Header.Get("Location")
} }
redirectSplitURL := strings.Split(redirectUrl, "/") redirectSplitURL := strings.Split(redirectUrl, "/")
@@ -289,9 +291,9 @@ func (d *OnedriveSharelink) getFiles(ctx context.Context, path string) ([]Item,
} }
tempHeader["Content-Type"] = []string{"application/json;odata=verbose"} tempHeader["Content-Type"] = []string{"application/json;odata=verbose"}
client := &http.Client{} client := base.HttpClient
postUrl := strings.Join(redirectSplitURL[:len(redirectSplitURL)-3], "/") + "/_api/v2.1/graphql" postUrl := strings.Join(redirectSplitURL[:len(redirectSplitURL)-3], "/") + "/_api/v2.1/graphql"
req, err = http.NewRequest(http.MethodPost, postUrl, strings.NewReader(graphqlVar)) req, err = http.NewRequestWithContext(ctx, http.MethodPost, postUrl, strings.NewReader(graphqlVar))
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -326,7 +328,10 @@ func (d *OnedriveSharelink) getFiles(ctx context.Context, path string) ([]Item,
log.Debugln("nextHref:", nextHref) log.Debugln("nextHref:", nextHref)
graphqlReqNEW := GraphQLNEWRequest{} graphqlReqNEW := GraphQLNEWRequest{}
postUrl = strings.Join(redirectSplitURL[:len(redirectSplitURL)-3], "/") + "/_api/web/GetListUsingPath(DecodedUrl=@a1)/RenderListDataAsStream" + nextHref postUrl = strings.Join(redirectSplitURL[:len(redirectSplitURL)-3], "/") + "/_api/web/GetListUsingPath(DecodedUrl=@a1)/RenderListDataAsStream" + nextHref
req, _ = http.NewRequest(http.MethodPost, postUrl, strings.NewReader(renderListDataAsStreamVar)) req, err = http.NewRequestWithContext(ctx, http.MethodPost, postUrl, strings.NewReader(renderListDataAsStreamVar))
if err != nil {
return nil, err
}
req.Header = tempHeader req.Header = tempHeader
resp, err := client.Do(req) resp, err := client.Do(req)
+46
View File
@@ -0,0 +1,46 @@
package onedrive_sharelink
import (
"net/http"
"net/http/httptest"
"reflect"
"testing"
internalNet "github.com/OpenListTeam/OpenList/v4/internal/net"
)
func TestNoRedirectClientUsesSharedSettings(t *testing.T) {
client := NewNoRedirectCLient()
sharedClient := internalNet.NewHttpClient()
if client.Timeout != sharedClient.Timeout {
t.Fatalf("expected shared timeout %s, got %s", sharedClient.Timeout, client.Timeout)
}
if reflect.TypeOf(client.Transport) != reflect.TypeOf(sharedClient.Transport) {
t.Fatalf("expected shared transport type %T, got %T", sharedClient.Transport, client.Transport)
}
}
func TestNoRedirectClientStopsRedirects(t *testing.T) {
targetRequested := false
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path == "/target" {
targetRequested = true
w.WriteHeader(http.StatusNoContent)
return
}
http.Redirect(w, r, "/target", http.StatusFound)
}))
defer server.Close()
resp, err := NewNoRedirectCLient().Get(server.URL)
if err != nil {
t.Fatal(err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusFound {
t.Fatalf("expected redirect response, got %d", resp.StatusCode)
}
if targetRequested {
t.Fatal("redirect target was requested")
}
}