Skip to content

Commit db9eeff

Browse files
authored
Updated OptPath semantics (#62)
* Updated OptPath semantics * Updates after PR reviews
1 parent c26728d commit db9eeff

5 files changed

Lines changed: 242 additions & 69 deletions

File tree

go.mod

Lines changed: 19 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,18 @@ require (
77
github.com/andreburgaud/crypt2go v1.8.0
88
github.com/djthorpe/go-errors v1.0.3
99
github.com/djthorpe/go-tablewriter v0.0.11
10-
github.com/mutablelogic/go-server v1.6.13
10+
github.com/mutablelogic/go-server v1.6.18
1111
github.com/stretchr/testify v1.11.1
1212
github.com/xdg-go/pbkdf2 v1.0.0
13-
go.opentelemetry.io/otel v1.41.0
14-
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.41.0
15-
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.41.0
16-
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.41.0
17-
go.opentelemetry.io/otel/sdk v1.41.0
18-
go.opentelemetry.io/otel/trace v1.41.0
19-
golang.org/x/crypto v0.48.0
20-
golang.org/x/oauth2 v0.35.0
21-
golang.org/x/term v0.40.0
13+
go.opentelemetry.io/otel v1.42.0
14+
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.42.0
15+
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.42.0
16+
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.42.0
17+
go.opentelemetry.io/otel/sdk v1.42.0
18+
go.opentelemetry.io/otel/trace v1.42.0
19+
golang.org/x/crypto v0.49.0
20+
golang.org/x/oauth2 v0.36.0
21+
golang.org/x/term v0.41.0
2222
)
2323

2424
require (
@@ -28,18 +28,19 @@ require (
2828
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
2929
github.com/go-logr/logr v1.4.3 // indirect
3030
github.com/go-logr/stdr v1.2.2 // indirect
31+
github.com/google/jsonschema-go v0.4.2 // indirect
3132
github.com/google/uuid v1.6.0 // indirect
3233
github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 // indirect
33-
github.com/mattn/go-runewidth v0.0.20 // indirect
34+
github.com/mattn/go-runewidth v0.0.21 // indirect
3435
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
3536
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
36-
go.opentelemetry.io/otel/metric v1.41.0 // indirect
37-
go.opentelemetry.io/proto/otlp v1.9.0 // indirect
38-
golang.org/x/net v0.51.0 // indirect
39-
golang.org/x/sys v0.41.0 // indirect
40-
golang.org/x/text v0.34.0 // indirect
41-
google.golang.org/genproto/googleapis/api v0.0.0-20260226221140-a57be14db171 // indirect
42-
google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171 // indirect
37+
go.opentelemetry.io/otel/metric v1.42.0 // indirect
38+
go.opentelemetry.io/proto/otlp v1.10.0 // indirect
39+
golang.org/x/net v0.52.0 // indirect
40+
golang.org/x/sys v0.42.0 // indirect
41+
golang.org/x/text v0.35.0 // indirect
42+
google.golang.org/genproto/googleapis/api v0.0.0-20260319201613-d00831a3d3e7 // indirect
43+
google.golang.org/genproto/googleapis/rpc v0.0.0-20260319201613-d00831a3d3e7 // indirect
4344
google.golang.org/grpc v1.79.3 // indirect
4445
google.golang.org/protobuf v1.36.11 // indirect
4546
gopkg.in/yaml.v3 v3.0.1 // indirect

go.sum

Lines changed: 41 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek
2727
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
2828
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
2929
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
30+
github.com/google/jsonschema-go v0.4.2 h1:tmrUohrwoLZZS/P3x7ex0WAVknEkBZM46iALbcqoRA8=
31+
github.com/google/jsonschema-go v0.4.2/go.mod h1:r5quNTdLOYEz95Ru18zA0ydNbBuYoo9tgaYcxEYhJVE=
3032
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
3133
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
3234
github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 h1:HWRh5R2+9EifMyIHV7ZV+MIZqgz+PMpZ14Jynv3O2Zs=
@@ -37,10 +39,12 @@ github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
3739
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
3840
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
3941
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
40-
github.com/mattn/go-runewidth v0.0.20 h1:WcT52H91ZUAwy8+HUkdM3THM6gXqXuLJi9O3rjcQQaQ=
41-
github.com/mattn/go-runewidth v0.0.20/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs=
42+
github.com/mattn/go-runewidth v0.0.21 h1:jJKAZiQH+2mIinzCJIaIG9Be1+0NR+5sz/lYEEjdM8w=
43+
github.com/mattn/go-runewidth v0.0.21/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs=
4244
github.com/mutablelogic/go-server v1.6.13 h1:eefS5dnp0dICM1TAYH8wcs4+vfkH4Mvjo35oFKoTx7A=
4345
github.com/mutablelogic/go-server v1.6.13/go.mod h1:jswZsmiSn53d62ZqOA25n/yNHj4jJWsMIbDwf+0oyZw=
46+
github.com/mutablelogic/go-server v1.6.18 h1:t+rLcl1wuImxYgzKCB7/laNLa9adY2oUktPtSL4pyug=
47+
github.com/mutablelogic/go-server v1.6.18/go.mod h1:4kpT+CFI+ioq2O59Sk5snHgFgMFbzpwka2SZ0Q+1ZcA=
4448
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
4549
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
4650
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
@@ -51,44 +55,45 @@ github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=
5155
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
5256
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
5357
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
54-
go.opentelemetry.io/otel v1.41.0 h1:YlEwVsGAlCvczDILpUXpIpPSL/VPugt7zHThEMLce1c=
55-
go.opentelemetry.io/otel v1.41.0/go.mod h1:Yt4UwgEKeT05QbLwbyHXEwhnjxNO6D8L5PQP51/46dE=
56-
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.41.0 h1:ao6Oe+wSebTlQ1OEht7jlYTzQKE+pnx/iNywFvTbuuI=
57-
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.41.0/go.mod h1:u3T6vz0gh/NVzgDgiwkgLxpsSF6PaPmo2il0apGJbls=
58-
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.41.0 h1:mq/Qcf28TWz719lE3/hMB4KkyDuLJIvgJnFGcd0kEUI=
59-
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.41.0/go.mod h1:yk5LXEYhsL2htyDNJbEq7fWzNEigeEdV5xBF/Y+kAv0=
60-
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.41.0 h1:inYW9ZhgqiDqh6BioM7DVHHzEGVq76Db5897WLGZ5Go=
61-
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.41.0/go.mod h1:Izur+Wt8gClgMJqO/cZ8wdeeMryJ/xxiOVgFSSfpDTY=
62-
go.opentelemetry.io/otel/metric v1.41.0 h1:rFnDcs4gRzBcsO9tS8LCpgR0dxg4aaxWlJxCno7JlTQ=
63-
go.opentelemetry.io/otel/metric v1.41.0/go.mod h1:xPvCwd9pU0VN8tPZYzDZV/BMj9CM9vs00GuBjeKhJps=
64-
go.opentelemetry.io/otel/sdk v1.41.0 h1:YPIEXKmiAwkGl3Gu1huk1aYWwtpRLeskpV+wPisxBp8=
65-
go.opentelemetry.io/otel/sdk v1.41.0/go.mod h1:ahFdU0G5y8IxglBf0QBJXgSe7agzjE4GiTJ6HT9ud90=
66-
go.opentelemetry.io/otel/sdk/metric v1.41.0 h1:siZQIYBAUd1rlIWQT2uCxWJxcCO7q3TriaMlf08rXw8=
67-
go.opentelemetry.io/otel/sdk/metric v1.41.0/go.mod h1:HNBuSvT7ROaGtGI50ArdRLUnvRTRGniSUZbxiWxSO8Y=
68-
go.opentelemetry.io/otel/trace v1.41.0 h1:Vbk2co6bhj8L59ZJ6/xFTskY+tGAbOnCtQGVVa9TIN0=
69-
go.opentelemetry.io/otel/trace v1.41.0/go.mod h1:U1NU4ULCoxeDKc09yCWdWe+3QoyweJcISEVa1RBzOis=
70-
go.opentelemetry.io/proto/otlp v1.9.0 h1:l706jCMITVouPOqEnii2fIAuO3IVGBRPV5ICjceRb/A=
71-
go.opentelemetry.io/proto/otlp v1.9.0/go.mod h1:xE+Cx5E/eEHw+ISFkwPLwCZefwVjY+pqKg1qcK03+/4=
58+
go.opentelemetry.io/otel v1.42.0 h1:lSQGzTgVR3+sgJDAU/7/ZMjN9Z+vUip7leaqBKy4sho=
59+
go.opentelemetry.io/otel v1.42.0/go.mod h1:lJNsdRMxCUIWuMlVJWzecSMuNjE7dOYyWlqOXWkdqCc=
60+
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.42.0 h1:THuZiwpQZuHPul65w4WcwEnkX2QIuMT+UFoOrygtoJw=
61+
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.42.0/go.mod h1:J2pvYM5NGHofZ2/Ru6zw/TNWnEQp5crgyDeSrYpXkAw=
62+
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.42.0 h1:zWWrB1U6nqhS/k6zYB74CjRpuiitRtLLi68VcgmOEto=
63+
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.42.0/go.mod h1:2qXPNBX1OVRC0IwOnfo1ljoid+RD0QK3443EaqVlsOU=
64+
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.42.0 h1:uLXP+3mghfMf7XmV4PkGfFhFKuNWoCvvx5wP/wOXo0o=
65+
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.42.0/go.mod h1:v0Tj04armyT59mnURNUJf7RCKcKzq+lgJs6QSjHjaTc=
66+
go.opentelemetry.io/otel/metric v1.42.0 h1:2jXG+3oZLNXEPfNmnpxKDeZsFI5o4J+nz6xUlaFdF/4=
67+
go.opentelemetry.io/otel/metric v1.42.0/go.mod h1:RlUN/7vTU7Ao/diDkEpQpnz3/92J9ko05BIwxYa2SSI=
68+
go.opentelemetry.io/otel/sdk v1.42.0 h1:LyC8+jqk6UJwdrI/8VydAq/hvkFKNHZVIWuslJXYsDo=
69+
go.opentelemetry.io/otel/sdk v1.42.0/go.mod h1:rGHCAxd9DAph0joO4W6OPwxjNTYWghRWmkHuGbayMts=
70+
go.opentelemetry.io/otel/sdk/metric v1.42.0 h1:D/1QR46Clz6ajyZ3G8SgNlTJKBdGp84q9RKCAZ3YGuA=
71+
go.opentelemetry.io/otel/trace v1.42.0 h1:OUCgIPt+mzOnaUTpOQcBiM/PLQ/Op7oq6g4LenLmOYY=
72+
go.opentelemetry.io/otel/trace v1.42.0/go.mod h1:f3K9S+IFqnumBkKhRJMeaZeNk9epyhnCmQh/EysQCdc=
73+
go.opentelemetry.io/proto/otlp v1.10.0 h1:IQRWgT5srOCYfiWnpqUYz9CVmbO8bFmKcwYxpuCSL2g=
74+
go.opentelemetry.io/proto/otlp v1.10.0/go.mod h1:/CV4QoCR/S9yaPj8utp3lvQPoqMtxXdzn7ozvvozVqk=
7275
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
7376
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
74-
golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts=
75-
golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos=
76-
golang.org/x/net v0.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo=
77-
golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y=
78-
golang.org/x/oauth2 v0.35.0 h1:Mv2mzuHuZuY2+bkyWXIHMfhNdJAdwW3FuWeCPYN5GVQ=
79-
golang.org/x/oauth2 v0.35.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
80-
golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k=
81-
golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
82-
golang.org/x/term v0.40.0 h1:36e4zGLqU4yhjlmxEaagx2KuYbJq3EwY8K943ZsHcvg=
83-
golang.org/x/term v0.40.0/go.mod h1:w2P8uVp06p2iyKKuvXIm7N/y0UCRt3UfJTfZ7oOpglM=
84-
golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk=
85-
golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA=
77+
golang.org/x/crypto v0.49.0 h1:+Ng2ULVvLHnJ/ZFEq4KdcDd/cfjrrjjNSXNzxg0Y4U4=
78+
golang.org/x/crypto v0.49.0/go.mod h1:ErX4dUh2UM+CFYiXZRTcMpEcN8b/1gxEuv3nODoYtCA=
79+
golang.org/x/net v0.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0=
80+
golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw=
81+
golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs=
82+
golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q=
83+
golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo=
84+
golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
85+
golang.org/x/term v0.41.0 h1:QCgPso/Q3RTJx2Th4bDLqML4W6iJiaXFq2/ftQF13YU=
86+
golang.org/x/term v0.41.0/go.mod h1:3pfBgksrReYfZ5lvYM0kSO0LIkAl4Yl2bXOkKP7Ec2A=
87+
golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8=
88+
golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA=
8689
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
8790
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
88-
google.golang.org/genproto/googleapis/api v0.0.0-20260226221140-a57be14db171 h1:tu/dtnW1o3wfaxCOjSLn5IRX4YDcJrtlpzYkhHhGaC4=
89-
google.golang.org/genproto/googleapis/api v0.0.0-20260226221140-a57be14db171/go.mod h1:M5krXqk4GhBKvB596udGL3UyjL4I1+cTbK0orROM9ng=
90-
google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171 h1:ggcbiqK8WWh6l1dnltU4BgWGIGo+EVYxCaAPih/zQXQ=
91-
google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
91+
google.golang.org/genproto/googleapis/api v0.0.0-20260311181403-84a4fc48630c h1:OyQPd6I3pN/9gDxz6L13kYGJgqkpdrAohJRBeXyxlgI=
92+
google.golang.org/genproto/googleapis/api v0.0.0-20260319201613-d00831a3d3e7 h1:41r6JMbpzBMen0R/4TZeeAmGXSJC7DftGINUodzTkPI=
93+
google.golang.org/genproto/googleapis/api v0.0.0-20260319201613-d00831a3d3e7/go.mod h1:EIQZ5bFCfRQDV4MhRle7+OgjNtZ6P1PiZBgAKuxXu/Y=
94+
google.golang.org/genproto/googleapis/rpc v0.0.0-20260311181403-84a4fc48630c h1:xgCzyF2LFIO/0X2UAoVRiXKU5Xg6VjToG4i2/ecSswk=
95+
google.golang.org/genproto/googleapis/rpc v0.0.0-20260319201613-d00831a3d3e7 h1:ndE4FoJqsIceKP2oYSnUZqhTdYufCYYkqwtFzfrhI7w=
96+
google.golang.org/genproto/googleapis/rpc v0.0.0-20260319201613-d00831a3d3e7/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
9297
google.golang.org/grpc v1.79.3 h1:sybAEdRIEtvcD68Gx7dmnwjZKlyfuc61Dyo9pGXXkKE=
9398
google.golang.org/grpc v1.79.3/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
9499
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=

pkg/bitwarden/client.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ func (c *Client) Login(opts ...RequestOpt) error {
147147
request.Device = c.session.Device
148148
if payload, err := client.NewFormRequest(request, client.ContentTypeJson); err != nil {
149149
return err
150-
} else if err := c.client.Do(payload, &response, client.OptReqEndpoint(identityUrl), client.OptPath("connect/token")); err != nil {
150+
} else if err := c.client.Do(payload, &response, client.OptReqEndpoint(identityUrl), client.OptPath("connect", "token")); err != nil {
151151
return err
152152
}
153153

requestopts.go

Lines changed: 51 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import (
44
"fmt"
55
"net/http"
66
"net/url"
7-
"path"
87
"strings"
98

109
// Package imports
@@ -44,26 +43,46 @@ func OptReqEndpoint(value string) RequestOpt {
4443
}
4544
}
4645

47-
// OptAbsPath sets the absolute path for a request
46+
// OptAbsPath replaces the request path with the provided path segments.
47+
//
48+
// Each argument is treated as exactly one path segment. Leading and trailing
49+
// slashes on each argument are ignored for path joining, but any slash or dot
50+
// segment within an argument is preserved as data and percent-encoded in the
51+
// outbound request.
52+
//
53+
// For example, OptAbsPath("a/b", "c") results in a request target of
54+
// "/a%2Fb/c", not "/a/b/c".
4855
func OptAbsPath(value ...any) RequestOpt {
4956
return func(r *requestOpts) error {
5057
// Make a copy
5158
url := *r.URL
5259
// Clean up and append path
53-
url.Path = absolutePath(strings.TrimPrefix(join(value, PathSeparator), PathSeparator))
60+
url.Path = absolutePath(pathSegments(value)...)
61+
url.RawPath = absolutePath(escapedPathSegments(value)...)
5462
// Set new path
5563
r.URL = &url
5664
return nil
5765
}
5866
}
5967

60-
// OptPath appends path elements onto a request
68+
// OptPath appends the provided path segments onto the current request path.
69+
//
70+
// Each argument is treated as exactly one path segment. Leading and trailing
71+
// slashes on each argument are ignored for path joining, but any slash or dot
72+
// segment within an argument is preserved as data and percent-encoded in the
73+
// outbound request.
74+
//
75+
// For example, OptPath("a/b", "c") results in a request target of
76+
// "/a%2Fb/c", not "/a/b/c".
6177
func OptPath(value ...any) RequestOpt {
6278
return func(r *requestOpts) error {
6379
// Make a copy
6480
url := *r.URL
81+
basePath := strings.Trim(url.Path, PathSeparator)
82+
baseRawPath := strings.Trim(url.EscapedPath(), PathSeparator)
6583
// Clean up and append path
66-
url.Path = absolutePath(strings.Trim(url.Path, PathSeparator), strings.TrimPrefix(join(value, PathSeparator), PathSeparator))
84+
url.Path = absolutePath(append([]string{basePath}, pathSegments(value)...)...)
85+
url.RawPath = absolutePath(append([]string{baseRawPath}, escapedPathSegments(value)...)...)
6786
// Set new path
6887
r.URL = &url
6988
return nil
@@ -143,21 +162,39 @@ func OptReqTransport(fn func(http.RoundTripper) http.RoundTripper) RequestOpt {
143162
///////////////////////////////////////////////////////////////////////////////
144163
// PRIVATE METHODS
145164

146-
func join(values []any, sep string) string {
165+
func pathSegments(values []any) []string {
147166
if len(values) == 0 {
148-
return ""
167+
return nil
149168
}
150-
str := make([]string, len(values))
151-
for i, v := range values {
152-
str[i] = fmt.Sprint(v)
169+
segments := make([]string, 0, len(values))
170+
for _, value := range values {
171+
segments = append(segments, strings.Trim(fmt.Sprint(value), PathSeparator))
153172
}
154-
return strings.Join(str, sep)
173+
return segments
174+
}
175+
176+
func escapedPathSegments(values []any) []string {
177+
if len(values) == 0 {
178+
return nil
179+
}
180+
segments := make([]string, 0, len(values))
181+
for _, value := range values {
182+
segment := strings.Trim(fmt.Sprint(value), PathSeparator)
183+
segments = append(segments, url.PathEscape(segment))
184+
}
185+
return segments
155186
}
156187

157188
func absolutePath(elem ...string) string {
158-
joined := path.Join(elem...)
159-
if joined == "." {
189+
parts := make([]string, 0, len(elem))
190+
for _, part := range elem {
191+
part = strings.Trim(part, PathSeparator)
192+
if part != "" {
193+
parts = append(parts, part)
194+
}
195+
}
196+
if len(parts) == 0 {
160197
return PathSeparator
161198
}
162-
return PathSeparator + joined
199+
return PathSeparator + strings.Join(parts, PathSeparator)
163200
}

0 commit comments

Comments
 (0)