Skip to content

Commit c2ca9bc

Browse files
authored
GH-39837: [Go][Flight] Allow cloning existing cookies in middleware (#39838)
### Rationale for this change This is needed for apache/arrow-adbc#1194 to facilitate better connection handling for flight clients in ADBC by copying the existing cookies over when creating a sub-client. ### What changes are included in this PR? Creating a `Clone` method on the `CookieMiddleware` so that a user can create and hold a reference to a specific cookie middleware instance and then create new ones on the fly that copy over the existing cookies at that moment. ### Are these changes tested? Yes. ### Are there any user-facing changes? No * Closes: #39837 Authored-by: Matt Topol <zotthewizard@gmail.com> Signed-off-by: Matt Topol <zotthewizard@gmail.com>
1 parent 87dd4c4 commit c2ca9bc

2 files changed

Lines changed: 84 additions & 0 deletions

File tree

go/arrow/flight/cookie_middleware.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import (
2323
"sync"
2424
"time"
2525

26+
"golang.org/x/exp/maps"
2627
"google.golang.org/grpc/metadata"
2728
)
2829

@@ -40,11 +41,34 @@ func NewClientCookieMiddleware() ClientMiddleware {
4041
return CreateClientMiddleware(&clientCookieMiddleware{jar: make(map[string]http.Cookie)})
4142
}
4243

44+
func NewCookieMiddleware() CookieMiddleware {
45+
return &clientCookieMiddleware{jar: make(map[string]http.Cookie)}
46+
}
47+
48+
// CookieMiddleware is a go-routine safe middleware for flight clients
49+
// which properly handles Set-Cookie headers for storing cookies.
50+
// This can be passed into `CreateClientMiddleware` to create a new
51+
// middleware object. You can also clone it to create middleware for a
52+
// new client which starts with the same cookies.
53+
type CookieMiddleware interface {
54+
CustomClientMiddleware
55+
// Clone creates a new CookieMiddleware that starts out with the same
56+
// cookies that this one already has. This is useful when creating a
57+
// new client connection for the same server.
58+
Clone() CookieMiddleware
59+
}
60+
4361
type clientCookieMiddleware struct {
4462
jar map[string]http.Cookie
4563
mx sync.Mutex
4664
}
4765

66+
func (cc *clientCookieMiddleware) Clone() CookieMiddleware {
67+
cc.mx.Lock()
68+
defer cc.mx.Unlock()
69+
return &clientCookieMiddleware{jar: maps.Clone(cc.jar)}
70+
}
71+
4872
func (cc *clientCookieMiddleware) StartCall(ctx context.Context) context.Context {
4973
cc.mx.Lock()
5074
defer cc.mx.Unlock()

go/arrow/flight/cookie_middleware_test.go

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,3 +239,63 @@ func TestCookieExpiration(t *testing.T) {
239239
cookieMiddleware.expectedCookies = map[string]string{}
240240
makeReq(client, t)
241241
}
242+
243+
func TestCookiesClone(t *testing.T) {
244+
cookieMiddleware := &serverAddCookieMiddleware{}
245+
246+
s := flight.NewServerWithMiddleware([]flight.ServerMiddleware{
247+
flight.CreateServerMiddleware(cookieMiddleware),
248+
})
249+
s.Init("localhost:0")
250+
f := &flightServer{}
251+
s.RegisterFlightService(f)
252+
253+
go s.Serve()
254+
defer s.Shutdown()
255+
256+
makeReq := func(c flight.Client, t *testing.T) {
257+
flightStream, err := c.ListFlights(context.Background(), &flight.Criteria{})
258+
assert.NoError(t, err)
259+
260+
for {
261+
_, err := flightStream.Recv()
262+
if err != nil {
263+
if errors.Is(err, io.EOF) {
264+
break
265+
}
266+
assert.NoError(t, err)
267+
}
268+
}
269+
}
270+
271+
credsOpt := grpc.WithTransportCredentials(insecure.NewCredentials())
272+
cookies := flight.NewCookieMiddleware()
273+
client1, err := flight.NewClientWithMiddleware(s.Addr().String(), nil,
274+
[]flight.ClientMiddleware{flight.CreateClientMiddleware(cookies)}, credsOpt)
275+
require.NoError(t, err)
276+
defer client1.Close()
277+
278+
// set cookies
279+
cookieMiddleware.cookies = []*http.Cookie{
280+
{Name: "foo", Value: "bar"},
281+
{Name: "foo2", Value: "bar2", MaxAge: 1},
282+
}
283+
makeReq(client1, t)
284+
285+
// validate set
286+
cookieMiddleware.expectedCookies = map[string]string{
287+
"foo": "bar", "foo2": "bar2",
288+
}
289+
makeReq(client1, t)
290+
291+
client2, err := flight.NewClientWithMiddleware(s.Addr().String(), nil,
292+
[]flight.ClientMiddleware{flight.CreateClientMiddleware(cookies.Clone())}, credsOpt)
293+
require.NoError(t, err)
294+
defer client2.Close()
295+
296+
// validate clone worked
297+
cookieMiddleware.expectedCookies = map[string]string{
298+
"foo": "bar", "foo2": "bar2",
299+
}
300+
makeReq(client2, t)
301+
}

0 commit comments

Comments
 (0)