]> git.proxmox.com Git - ceph.git/blame - ceph/src/arrow/java/flight/flight-core/src/test/java/org/apache/arrow/flight/client/TestCookieHandling.java
import quincy 17.2.0
[ceph.git] / ceph / src / arrow / java / flight / flight-core / src / test / java / org / apache / arrow / flight / client / TestCookieHandling.java
CommitLineData
1d09f67e
TL
1/*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18package org.apache.arrow.flight.client;
19
20import java.io.IOException;
21
22import org.apache.arrow.flight.CallHeaders;
23import org.apache.arrow.flight.CallInfo;
24import org.apache.arrow.flight.CallStatus;
25import org.apache.arrow.flight.Criteria;
26import org.apache.arrow.flight.ErrorFlightMetadata;
27import org.apache.arrow.flight.FlightClient;
28import org.apache.arrow.flight.FlightInfo;
29import org.apache.arrow.flight.FlightMethod;
30import org.apache.arrow.flight.FlightProducer;
31import org.apache.arrow.flight.FlightServer;
32import org.apache.arrow.flight.FlightServerMiddleware;
33import org.apache.arrow.flight.FlightTestUtil;
34import org.apache.arrow.flight.NoOpFlightProducer;
35import org.apache.arrow.flight.RequestContext;
36import org.apache.arrow.memory.BufferAllocator;
37import org.apache.arrow.memory.RootAllocator;
38import org.apache.arrow.util.AutoCloseables;
39import org.junit.After;
40import org.junit.Assert;
41import org.junit.Before;
42import org.junit.Ignore;
43import org.junit.Test;
44
45/**
46 * Tests for correct handling of cookies from the FlightClient using {@link ClientCookieMiddleware}.
47 */
48public class TestCookieHandling {
49 private static final String SET_COOKIE_HEADER = "Set-Cookie";
50 private static final String COOKIE_HEADER = "Cookie";
51 private BufferAllocator allocator;
52 private FlightServer server;
53 private FlightClient client;
54
55 private ClientCookieMiddlewareTestFactory testFactory = new ClientCookieMiddlewareTestFactory();
56 private ClientCookieMiddleware cookieMiddleware = new ClientCookieMiddleware(testFactory);
57
58 @Before
59 public void setup() throws Exception {
60 allocator = new RootAllocator(Long.MAX_VALUE);
61 startServerAndClient();
62 }
63
64 @After
65 public void cleanup() throws Exception {
66 testFactory = new ClientCookieMiddlewareTestFactory();
67 cookieMiddleware = testFactory.onCallStarted(new CallInfo(FlightMethod.DO_ACTION));
68 AutoCloseables.close(client, server, allocator);
69 client = null;
70 server = null;
71 allocator = null;
72 }
73
74 @Test
75 public void basicCookie() {
76 CallHeaders headersToSend = new ErrorFlightMetadata();
77 headersToSend.insert(SET_COOKIE_HEADER, "k=v");
78 cookieMiddleware = testFactory.onCallStarted(new CallInfo(FlightMethod.DO_ACTION));
79 cookieMiddleware.onHeadersReceived(headersToSend);
80 Assert.assertEquals("k=v", cookieMiddleware.getValidCookiesAsString());
81 }
82
83 @Test
84 public void cookieStaysAfterMultipleRequests() {
85 CallHeaders headersToSend = new ErrorFlightMetadata();
86 headersToSend.insert(SET_COOKIE_HEADER, "k=v");
87 cookieMiddleware = testFactory.onCallStarted(new CallInfo(FlightMethod.DO_ACTION));
88 cookieMiddleware.onHeadersReceived(headersToSend);
89 Assert.assertEquals("k=v", cookieMiddleware.getValidCookiesAsString());
90
91 headersToSend = new ErrorFlightMetadata();
92 cookieMiddleware = testFactory.onCallStarted(new CallInfo(FlightMethod.DO_ACTION));
93 cookieMiddleware.onHeadersReceived(headersToSend);
94 Assert.assertEquals("k=v", cookieMiddleware.getValidCookiesAsString());
95
96 headersToSend = new ErrorFlightMetadata();
97 cookieMiddleware = testFactory.onCallStarted(new CallInfo(FlightMethod.DO_ACTION));
98 cookieMiddleware.onHeadersReceived(headersToSend);
99 Assert.assertEquals("k=v", cookieMiddleware.getValidCookiesAsString());
100 }
101
102 @Ignore
103 @Test
104 public void cookieAutoExpires() {
105 CallHeaders headersToSend = new ErrorFlightMetadata();
106 headersToSend.insert(SET_COOKIE_HEADER, "k=v; Max-Age=2");
107 cookieMiddleware = testFactory.onCallStarted(new CallInfo(FlightMethod.DO_ACTION));
108 cookieMiddleware.onHeadersReceived(headersToSend);
109 // Note: using max-age changes cookie version from 0->1, which quotes values.
110 Assert.assertEquals("k=\"v\"", cookieMiddleware.getValidCookiesAsString());
111
112 headersToSend = new ErrorFlightMetadata();
113 cookieMiddleware = testFactory.onCallStarted(new CallInfo(FlightMethod.DO_ACTION));
114 cookieMiddleware.onHeadersReceived(headersToSend);
115 Assert.assertEquals("k=\"v\"", cookieMiddleware.getValidCookiesAsString());
116
117 try {
118 Thread.sleep(5000);
119 } catch (InterruptedException ignored) {
120 }
121
122 // Verify that the k cookie was discarded because it expired.
123 Assert.assertTrue(cookieMiddleware.getValidCookiesAsString().isEmpty());
124 }
125
126 @Test
127 public void cookieExplicitlyExpires() {
128 CallHeaders headersToSend = new ErrorFlightMetadata();
129 headersToSend.insert(SET_COOKIE_HEADER, "k=v; Max-Age=2");
130 cookieMiddleware = testFactory.onCallStarted(new CallInfo(FlightMethod.DO_ACTION));
131 cookieMiddleware.onHeadersReceived(headersToSend);
132 // Note: using max-age changes cookie version from 0->1, which quotes values.
133 Assert.assertEquals("k=\"v\"", cookieMiddleware.getValidCookiesAsString());
134
135 // Note: The JDK treats Max-Age < 0 as not expired and treats 0 as expired.
136 // This violates the RFC, which states that less than zero and zero should both be expired.
137 headersToSend = new ErrorFlightMetadata();
138 headersToSend.insert(SET_COOKIE_HEADER, "k=v; Max-Age=0");
139 cookieMiddleware = testFactory.onCallStarted(new CallInfo(FlightMethod.DO_ACTION));
140 cookieMiddleware.onHeadersReceived(headersToSend);
141
142 // Verify that the k cookie was discarded because the server told the client it is expired.
143 Assert.assertTrue(cookieMiddleware.getValidCookiesAsString().isEmpty());
144 }
145
146 @Ignore
147 @Test
148 public void cookieExplicitlyExpiresWithMaxAgeMinusOne() {
149 CallHeaders headersToSend = new ErrorFlightMetadata();
150 headersToSend.insert(SET_COOKIE_HEADER, "k=v; Max-Age=2");
151 cookieMiddleware = testFactory.onCallStarted(new CallInfo(FlightMethod.DO_ACTION));
152 cookieMiddleware.onHeadersReceived(headersToSend);
153 // Note: using max-age changes cookie version from 0->1, which quotes values.
154 Assert.assertEquals("k=\"v\"", cookieMiddleware.getValidCookiesAsString());
155
156 headersToSend = new ErrorFlightMetadata();
157
158 // The Java HttpCookie class has a bug where it uses a -1 maxAge to indicate
159 // a persistent cookie, when the RFC spec says this should mean the cookie expires immediately.
160 headersToSend.insert(SET_COOKIE_HEADER, "k=v; Max-Age=-1");
161 cookieMiddleware = testFactory.onCallStarted(new CallInfo(FlightMethod.DO_ACTION));
162 cookieMiddleware.onHeadersReceived(headersToSend);
163
164 // Verify that the k cookie was discarded because the server told the client it is expired.
165 Assert.assertTrue(cookieMiddleware.getValidCookiesAsString().isEmpty());
166 }
167
168 @Test
169 public void changeCookieValue() {
170 CallHeaders headersToSend = new ErrorFlightMetadata();
171 headersToSend.insert(SET_COOKIE_HEADER, "k=v");
172 cookieMiddleware.onHeadersReceived(headersToSend);
173 Assert.assertEquals("k=v", cookieMiddleware.getValidCookiesAsString());
174
175 headersToSend = new ErrorFlightMetadata();
176 headersToSend.insert(SET_COOKIE_HEADER, "k=v2");
177 cookieMiddleware.onHeadersReceived(headersToSend);
178 Assert.assertEquals("k=v2", cookieMiddleware.getValidCookiesAsString());
179 }
180
181 @Test
182 public void multipleCookiesWithSetCookie() {
183 CallHeaders headersToSend = new ErrorFlightMetadata();
184 headersToSend.insert(SET_COOKIE_HEADER, "firstKey=firstVal");
185 headersToSend.insert(SET_COOKIE_HEADER, "secondKey=secondVal");
186 cookieMiddleware.onHeadersReceived(headersToSend);
187 Assert.assertEquals("firstKey=firstVal; secondKey=secondVal", cookieMiddleware.getValidCookiesAsString());
188 }
189
190 @Test
191 public void cookieStaysAfterMultipleRequestsEndToEnd() {
192 client.handshake();
193 Assert.assertEquals("k=v", testFactory.clientCookieMiddleware.getValidCookiesAsString());
194 client.handshake();
195 Assert.assertEquals("k=v", testFactory.clientCookieMiddleware.getValidCookiesAsString());
196 client.listFlights(Criteria.ALL);
197 Assert.assertEquals("k=v", testFactory.clientCookieMiddleware.getValidCookiesAsString());
198 }
199
200 /**
201 * A server middleware component that injects SET_COOKIE_HEADER into the outgoing headers.
202 */
203 static class SetCookieHeaderInjector implements FlightServerMiddleware {
204 private final Factory factory;
205
206 public SetCookieHeaderInjector(Factory factory) {
207 this.factory = factory;
208 }
209
210 @Override
211 public void onBeforeSendingHeaders(CallHeaders outgoingHeaders) {
212 if (!factory.receivedCookieHeader) {
213 outgoingHeaders.insert(SET_COOKIE_HEADER, "k=v");
214 }
215 }
216
217 @Override
218 public void onCallCompleted(CallStatus status) {
219
220 }
221
222 @Override
223 public void onCallErrored(Throwable err) {
224
225 }
226
227 static class Factory implements FlightServerMiddleware.Factory<SetCookieHeaderInjector> {
228 private boolean receivedCookieHeader = false;
229
230 @Override
231 public SetCookieHeaderInjector onCallStarted(CallInfo info, CallHeaders incomingHeaders,
232 RequestContext context) {
233 receivedCookieHeader = null != incomingHeaders.get(COOKIE_HEADER);
234 return new SetCookieHeaderInjector(this);
235 }
236 }
237 }
238
239 public static class ClientCookieMiddlewareTestFactory extends ClientCookieMiddleware.Factory {
240
241 private ClientCookieMiddleware clientCookieMiddleware;
242
243 @Override
244 public ClientCookieMiddleware onCallStarted(CallInfo info) {
245 this.clientCookieMiddleware = new ClientCookieMiddleware(this);
246 return this.clientCookieMiddleware;
247 }
248 }
249
250 private void startServerAndClient() throws IOException {
251 final FlightProducer flightProducer = new NoOpFlightProducer() {
252 public void listFlights(CallContext context, Criteria criteria,
253 StreamListener<FlightInfo> listener) {
254 listener.onCompleted();
255 }
256 };
257
258 this.server = FlightTestUtil.getStartedServer((location) -> FlightServer
259 .builder(allocator, location, flightProducer)
260 .middleware(FlightServerMiddleware.Key.of("test"), new SetCookieHeaderInjector.Factory())
261 .build());
262
263 this.client = FlightClient.builder(allocator, server.getLocation())
264 .intercept(testFactory)
265 .build();
266 }
267}