From 31ae0f5bc93e2a6d80a808ebc718a4235dfd24e2 Mon Sep 17 00:00:00 2001 From: ShenLin <773933146@qq.com> Date: Sat, 29 Aug 2026 01:10:01 +0800 Subject: [PATCH] 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> --- drivers/onedrive_sharelink/util.go | 53 ++++++++++++++----------- drivers/onedrive_sharelink/util_test.go | 46 +++++++++++++++++++++ 2 files changed, 75 insertions(+), 24 deletions(-) create mode 100644 drivers/onedrive_sharelink/util_test.go diff --git a/drivers/onedrive_sharelink/util.go b/drivers/onedrive_sharelink/util.go index 13785939f..a0f7450d3 100644 --- a/drivers/onedrive_sharelink/util.go +++ b/drivers/onedrive_sharelink/util.go @@ -2,7 +2,6 @@ package onedrive_sharelink import ( "context" - "crypto/tls" "encoding/json" "fmt" "io" @@ -13,30 +12,28 @@ import ( "time" "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" "golang.org/x/net/html" ) // NewNoRedirectClient creates an HTTP client that doesn't follow redirects func NewNoRedirectCLient() *http.Client { - return &http.Client{ - Timeout: time.Hour * 48, - 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 - }, + client := internalNet.NewHttpClient() + client.CheckRedirect = func(req *http.Request, via []*http.Request) error { + return http.ErrUseLastResponse } + return client } // 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 - 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 { return "", err } @@ -95,16 +92,18 @@ func getCookiesWithPassword(link, password string) (string, error) { "__VIEWSTATEENCRYPTED": []string{""}, } - client := &http.Client{ - CheckRedirect: func(req *http.Request, via []*http.Request) error { - return http.ErrUseLastResponse - }, - } + client := NewNoRedirectCLient() // 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 { 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 cookie := resp.Cookies() @@ -153,6 +152,7 @@ func (d *OnedriveSharelink) getHeaders(ctx context.Context) (http.Header, error) if err != nil { return nil, err } + defer answerNoRedirect.Body.Close() redirectUrl := answerNoRedirect.Header.Get("Location") log.Debugln("redirectUrl:", redirectUrl) if redirectUrl == "" { @@ -169,7 +169,7 @@ func (d *OnedriveSharelink) getHeaders(ctx context.Context) (http.Header, error) header.Set("authority", u.Host) return header, nil } else { - cookie, err := getCookiesWithPassword(d.ShareLinkURL, d.ShareLinkPassword) + cookie, err := getCookiesWithPassword(ctx, d.ShareLinkURL, d.ShareLinkPassword) if err != nil { return nil, err } @@ -197,6 +197,7 @@ func (d *OnedriveSharelink) getFiles(ctx context.Context, path string) ([]Item, if err != nil { return nil, err } + _ = answerNoRedirect.Body.Close() redirectUrl = answerNoRedirect.Header.Get("Location") } else { header = d.Headers @@ -205,6 +206,7 @@ func (d *OnedriveSharelink) getFiles(ctx context.Context, path string) ([]Item, if err != nil { return nil, err } + _ = answerNoRedirect.Body.Close() redirectUrl = answerNoRedirect.Header.Get("Location") } 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"} - client := &http.Client{} + client := base.HttpClient 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 { return nil, err } @@ -326,7 +328,10 @@ func (d *OnedriveSharelink) getFiles(ctx context.Context, path string) ([]Item, log.Debugln("nextHref:", nextHref) graphqlReqNEW := GraphQLNEWRequest{} 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 resp, err := client.Do(req) diff --git a/drivers/onedrive_sharelink/util_test.go b/drivers/onedrive_sharelink/util_test.go new file mode 100644 index 000000000..e7756844a --- /dev/null +++ b/drivers/onedrive_sharelink/util_test.go @@ -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") + } +}