Skip to content

Commit 12e98f5

Browse files
JoinPaths: don't add "/" before the query string (Azure#19278)
* JoinPaths: do not unconditionally add a forward slash before the query string Preserve a trailing slash if the last path element has it Fixes Azure#19245 * azblob GetSASURL: don't add a forward slash before the query string * slight change based on url.JoinPath also fix container.GetSASURL Co-authored-by: Joel Hendrix <jhendrix@microsoft.com>
1 parent f2ce733 commit 12e98f5

File tree

7 files changed

+72
-31
lines changed

7 files changed

+72
-31
lines changed

sdk/azcore/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
### Bugs Fixed
1010
* Don't retry a request if the `Retry-After` delay is greater than the configured `RetryOptions.MaxRetryDelay`.
11+
* `runtime.JoinPaths`: do not unconditionally add a forward slash before the query string
1112

1213
### Other Changes
1314
* Removed logging URL from retry policy as it's redundant.

sdk/azcore/runtime/request.go

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import (
1515
"fmt"
1616
"io"
1717
"mime/multipart"
18+
"path"
1819
"reflect"
1920
"strings"
2021
"time"
@@ -56,19 +57,23 @@ func JoinPaths(root string, paths ...string) string {
5657
root, qps = splitPath[0], splitPath[1]
5758
}
5859

59-
for i := 0; i < len(paths); i++ {
60-
root = strings.TrimRight(root, "/")
61-
paths[i] = strings.TrimLeft(paths[i], "/")
62-
root += "/" + paths[i]
60+
p := path.Join(paths...)
61+
// path.Join will remove any trailing slashes.
62+
// if one was provided, preserve it.
63+
if strings.HasSuffix(paths[len(paths)-1], "/") && !strings.HasSuffix(p, "/") {
64+
p += "/"
6365
}
6466

6567
if qps != "" {
66-
if !strings.HasSuffix(root, "/") {
67-
root += "/"
68-
}
69-
return root + "?" + qps
68+
p = p + "?" + qps
69+
}
70+
71+
if strings.HasSuffix(root, "/") && strings.HasPrefix(p, "/") {
72+
root = root[:len(root)-1]
73+
} else if !strings.HasSuffix(root, "/") && !strings.HasPrefix(p, "/") {
74+
p = "/" + p
7075
}
71-
return root
76+
return root + p
7277
}
7378

7479
// EncodeByteArray will base-64 encode the byte slice v.

sdk/azcore/runtime/request_test.go

Lines changed: 50 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -544,17 +544,59 @@ func TestRequestSetBodyContentLengthHeader(t *testing.T) {
544544
}
545545

546546
func TestJoinPaths(t *testing.T) {
547-
if path := JoinPaths(""); path != "" {
548-
t.Fatalf("unexpected path %s", path)
547+
type joinTest struct {
548+
root string
549+
paths []string
550+
expected string
549551
}
550-
expected := "http://test.contoso.com/path/one/path/two/path/three/path/four/"
551-
if path := JoinPaths("http://test.contoso.com/", "/path/one", "path/two", "/path/three/", "path/four/"); path != expected {
552-
t.Fatalf("got %s, expected %s", path, expected)
552+
553+
tests := []joinTest{
554+
{
555+
root: "",
556+
paths: nil,
557+
expected: "",
558+
},
559+
{
560+
root: "/",
561+
paths: nil,
562+
expected: "/",
563+
},
564+
{
565+
root: "http://test.contoso.com/",
566+
paths: []string{"/path/one", "path/two", "/path/three/", "path/four/"},
567+
expected: "http://test.contoso.com/path/one/path/two/path/three/path/four/",
568+
},
569+
{
570+
root: "http://test.contoso.com",
571+
paths: []string{"path/one", "path/two", "/path/three/", "path/four/"},
572+
expected: "http://test.contoso.com/path/one/path/two/path/three/path/four/",
573+
},
574+
{
575+
root: "http://test.contoso.com/?qp1=abc&qp2=def",
576+
paths: []string{"/path/one", "path/two"},
577+
expected: "http://test.contoso.com/path/one/path/two?qp1=abc&qp2=def",
578+
},
579+
{
580+
root: "http://test.contoso.com?qp1=abc&qp2=def",
581+
paths: []string{"path/one", "path/two/"},
582+
expected: "http://test.contoso.com/path/one/path/two/?qp1=abc&qp2=def",
583+
},
584+
{
585+
root: "http://test.contoso.com/?qp1=abc&qp2=def",
586+
paths: []string{"path/one", "path/two/"},
587+
expected: "http://test.contoso.com/path/one/path/two/?qp1=abc&qp2=def",
588+
},
589+
{
590+
root: "http://test.contoso.com/?qp1=abc&qp2=def",
591+
paths: []string{"/path/one", "path/two/"},
592+
expected: "http://test.contoso.com/path/one/path/two/?qp1=abc&qp2=def",
593+
},
553594
}
554595

555-
expected = "http://test.contoso.com/path/one/path/two/?qp1=abc&qp2=def"
556-
if path := JoinPaths("http://test.contoso.com/?qp1=abc&qp2=def", "/path/one", "path/two"); path != expected {
557-
t.Fatalf("got %s, expected %s", path, expected)
596+
for _, tt := range tests {
597+
if path := JoinPaths(tt.root, tt.paths...); path != tt.expected {
598+
t.Fatalf("got %s, expected %s", path, tt.expected)
599+
}
558600
}
559601
}
560602

sdk/storage/azblob/CHANGELOG.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88

99
### Bugs Fixed
1010

11+
* `GetSASURL()`: for container and blob clients, don't add a forward slash before the query string
12+
1113
### Other Changes
1214

1315
## 0.5.0 (2022-09-29)
@@ -18,7 +20,7 @@
1820

1921
### Features Added
2022

21-
* Added [UserDelegationCredential](https://learn.microsoft.com/rest/api/storageservices/create-user-delegation-sas) which resolves [#18976](https://github.com/Azure/azure-sdk-for-go/issues/18976), [#16916](https://github.com/Azure/azure-sdk-for-go/issues/16916), [#18977](https://github.com/Azure/azure-sdk-for-go/issues/18977)
23+
* Added [UserDelegationCredential](https://learn.microsoft.com/rest/api/storageservices/create-user-delegation-sas) which resolves [#18976](https://github.com/Azure/azure-sdk-for-go/issues/18976), [#16916](https://github.com/Azure/azure-sdk-for-go/issues/16916), [#18977](https://github.com/Azure/azure-sdk-for-go/issues/18977)
2224
* Added [Restore Container API](https://learn.microsoft.com/rest/api/storageservices/restore-container).
2325

2426
### Bugs Fixed

sdk/storage/azblob/blob/client.go

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import (
1111
"errors"
1212
"io"
1313
"os"
14-
"strings"
1514
"sync"
1615
"time"
1716

@@ -261,11 +260,7 @@ func (b *Client) GetSASURL(permissions sas.BlobPermissions, start time.Time, exp
261260
return "", err
262261
}
263262

264-
endpoint := b.URL()
265-
if !strings.HasSuffix(endpoint, "/") {
266-
endpoint += "/"
267-
}
268-
endpoint += "?" + qps.Encode()
263+
endpoint := b.URL() + "?" + qps.Encode()
269264

270265
return endpoint, nil
271266
}

sdk/storage/azblob/container/client.go

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import (
1010
"context"
1111
"errors"
1212
"net/http"
13-
"strings"
1413
"time"
1514

1615
"github.com/Azure/azure-sdk-for-go/sdk/azcore"
@@ -317,11 +316,7 @@ func (c *Client) GetSASURL(permissions sas.ContainerPermissions, start time.Time
317316
return "", err
318317
}
319318

320-
endpoint := c.URL()
321-
if !strings.HasSuffix(endpoint, "/") {
322-
endpoint += "/"
323-
}
324-
endpoint += "?" + qps.Encode()
319+
endpoint := c.URL() + "?" + qps.Encode()
325320

326321
return endpoint, nil
327322
}

sdk/storage/azblob/service/client.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,14 @@ package service
99
import (
1010
"context"
1111
"errors"
12-
"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/blob"
1312
"net/http"
1413
"strings"
1514
"time"
1615

1716
"github.com/Azure/azure-sdk-for-go/sdk/azcore"
1817
"github.com/Azure/azure-sdk-for-go/sdk/azcore/policy"
1918
"github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime"
19+
"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/blob"
2020
"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/container"
2121
"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/internal/base"
2222
"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/internal/exported"
@@ -260,6 +260,7 @@ func (s *Client) GetSASURL(resources sas.AccountResourceTypes, permissions sas.A
260260

261261
endpoint := s.URL()
262262
if !strings.HasSuffix(endpoint, "/") {
263+
// add a trailing slash to be consistent with the portal
263264
endpoint += "/"
264265
}
265266
endpoint += "?" + qps.Encode()

0 commit comments

Comments
 (0)