KNOX-1157 - Scoped rewrite rules are treated as global rules in some cases (Wei Han...
[knox.git] / gateway-provider-rewrite / src / test / java / org / apache / knox / gateway / filter / rewrite / api / UrlRewriteProcessorTest.java
1 /**
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 */
18 package org.apache.knox.gateway.filter.rewrite.api;
19
20 import static org.hamcrest.CoreMatchers.is;
21 import static org.hamcrest.CoreMatchers.notNullValue;
22 import static org.hamcrest.MatcherAssert.assertThat;
23 import static org.junit.Assert.assertEquals;
24 import static org.junit.Assert.assertTrue;
25
26 import java.io.FileNotFoundException;
27 import java.io.IOException;
28 import java.io.InputStream;
29 import java.io.InputStreamReader;
30 import java.io.Reader;
31 import java.net.URI;
32 import java.net.URISyntaxException;
33 import java.net.URL;
34 import java.util.ArrayList;
35 import java.util.HashMap;
36 import java.util.Map;
37
38 import javax.servlet.http.HttpServletRequest;
39 import javax.servlet.http.HttpServletResponse;
40
41 import org.apache.knox.gateway.util.urltemplate.Expander;
42 import org.apache.knox.gateway.util.urltemplate.Matcher;
43 import org.apache.knox.gateway.util.urltemplate.Parser;
44 import org.apache.knox.gateway.util.urltemplate.Template;
45 import org.easymock.EasyMock;
46 import org.junit.Test;
47
48 public class UrlRewriteProcessorTest {
49
50 private static URL getTestResourceUrl( String name ) throws FileNotFoundException {
51 name = UrlRewriteProcessorTest.class.getName().replaceAll( "\\.", "/" ) + "/" + name;
52 URL url = ClassLoader.getSystemResource( name );
53 if( url == null ) {
54 throw new FileNotFoundException( name );
55 }
56 return url;
57 }
58
59 private static InputStream getTestResourceStream( String name ) throws IOException {
60 URL url = getTestResourceUrl( name );
61 InputStream stream = url.openStream();
62 return stream;
63 }
64
65 private static Reader getTestResourceReader( String name, String charset ) throws IOException {
66 return new InputStreamReader( getTestResourceStream( name ), charset );
67 }
68
69 @Test
70 public void testBasicPathRewrite() throws IOException, URISyntaxException {
71 UrlRewriteEnvironment environment = EasyMock.createNiceMock( UrlRewriteEnvironment.class );
72 HttpServletRequest request = EasyMock.createNiceMock( HttpServletRequest.class );
73 HttpServletResponse response = EasyMock.createNiceMock( HttpServletResponse.class );
74 EasyMock.replay( environment, request, response );
75
76 UrlRewriteProcessor processor = new UrlRewriteProcessor();
77 UrlRewriteRulesDescriptor config = UrlRewriteRulesDescriptorFactory.load(
78 "xml", getTestResourceReader( "rewrite.xml", "UTF-8" ) );
79 processor.initialize( environment, config );
80
81 Template inputUrl = Parser.parseLiteral( "test-scheme://test-host:1/test-input-path" );
82 Template outputUrl = processor.rewrite( null, inputUrl, UrlRewriter.Direction.IN, null );
83
84 assertThat( "Expect rewrite to produce a new URL",
85 outputUrl, notNullValue() );
86 assertThat(
87 "Expect rewrite to contain the correct path.",
88 outputUrl.toString(), is( "test-scheme://test-host:1/test-output-path" ) );
89 processor.destroy();
90 }
91
92 @Test
93 public void testMultipleIdenticalRewriteOutputRules() throws IOException, URISyntaxException {
94 UrlRewriteEnvironment environment = EasyMock.createNiceMock( UrlRewriteEnvironment.class );
95 HttpServletRequest request = EasyMock.createNiceMock( HttpServletRequest.class );
96 HttpServletResponse response = EasyMock.createNiceMock( HttpServletResponse.class );
97 EasyMock.replay( environment, request, response );
98
99 UrlRewriteProcessor processor = new UrlRewriteProcessor();
100 UrlRewriteRulesDescriptor config = UrlRewriteRulesDescriptorFactory.load(
101 "xml", getTestResourceReader( "rewrite-with-same-rules.xml", "UTF-8" ) );
102 processor.initialize( environment, config );
103
104 Template inputUrl = Parser.parseLiteral( "scheme://input-mock-host:42/test-input-path" );
105 Template outputUrl = processor.rewrite( null, inputUrl, UrlRewriter.Direction.OUT, null );
106
107 assertThat( "Expect rewrite to produce a new URL",
108 outputUrl, notNullValue() );
109 // Should always pick the first one.
110 assertThat(
111 "Expect rewrite to contain the correct path.",
112 outputUrl.toString(), is( "output-mock-scheme-1://output-mock-host-1:42/test-input-path" ) );
113
114 inputUrl = Parser.parseLiteral( "mock-scheme://input-mock-host:42/no-query" );
115 outputUrl = processor.rewrite( null, inputUrl, UrlRewriter.Direction.OUT, null );
116
117 assertThat(
118 "Expect rewrite to contain the correct path.",
119 outputUrl.toString(), is( "mock-scheme://output-mock-host-3:42/no-query" ) );
120
121 outputUrl = processor.rewrite( null, inputUrl, UrlRewriter.Direction.OUT, "test-rule-4" );
122
123 assertThat(
124 "Expect rewrite to contain the correct path.",
125 outputUrl.toString(), is( "mock-scheme://output-mock-host-4:42/no-query" ) );
126
127 processor.destroy();
128 }
129
130 @Test
131 public void testIdenticalRewriteOutputRulesWithScopes() throws IOException, URISyntaxException {
132 UrlRewriteEnvironment environment = EasyMock.createNiceMock( UrlRewriteEnvironment.class );
133 HttpServletRequest request = EasyMock.createNiceMock( HttpServletRequest.class );
134 HttpServletResponse response = EasyMock.createNiceMock( HttpServletResponse.class );
135 ArrayList<String> roles = new ArrayList<>();
136 roles.add("service-1");
137 EasyMock.expect(environment.resolve("service.role")).andReturn(roles).anyTimes();
138 EasyMock.replay( environment, request, response );
139
140 UrlRewriteProcessor processor = new UrlRewriteProcessor();
141 UrlRewriteRulesDescriptor config = UrlRewriteRulesDescriptorFactory.load(
142 "xml", getTestResourceReader( "rewrite-with-same-rules-different-scope.xml", "UTF-8" ) );
143 processor.initialize( environment, config );
144
145 Template inputUrl = Parser.parseLiteral( "scheme://input-mock-host:42/test-input-path" );
146 Template outputUrl = processor.rewrite( environment, inputUrl, UrlRewriter.Direction.OUT, null );
147
148 assertThat( "Expect rewrite to produce a new URL",
149 outputUrl, notNullValue() );
150 assertThat(
151 "Expect rewrite to contain the correct path.",
152 outputUrl.toString(), is( "output-mock-scheme-2://output-mock-host-2:42/test-input-path" ) );
153
154 inputUrl = Parser.parseLiteral( "mock-scheme://input-mock-host:42/no-query" );
155 outputUrl = processor.rewrite( environment, inputUrl, UrlRewriter.Direction.OUT, null );
156
157 roles.remove(0);
158 roles.add("service-2");
159
160 assertThat(
161 "Expect rewrite to contain the correct path.",
162 outputUrl.toString(), is( "mock-scheme://output-mock-host-5:42/no-query" ) );
163
164 outputUrl = processor.rewrite( environment, inputUrl, UrlRewriter.Direction.OUT, "service-2/test-rule-4" );
165
166 //no scope information should pick the first one
167 assertThat(
168 "Expect rewrite to contain the correct path.",
169 outputUrl.toString(), is( "mock-scheme://output-mock-host-4:42/no-query" ) );
170
171 outputUrl = processor.rewrite( null, inputUrl, UrlRewriter.Direction.OUT, "service-2/test-rule-4" );
172
173 assertThat(
174 "Expect rewrite to contain the correct path.",
175 outputUrl.toString(), is( "mock-scheme://output-mock-host-4:42/no-query" ) );
176
177 //Test the IN direction
178 inputUrl = Parser.parseLiteral( "scheme://input-mock-host:42/test-input-path" );
179 outputUrl = processor.rewrite( environment, inputUrl, UrlRewriter.Direction.IN, null );
180
181 assertThat( "Expect rewrite to produce a new URL",
182 outputUrl, notNullValue() );
183 assertThat(
184 "Expect rewrite to contain the correct path.",
185 outputUrl.toString(), is( "input-mock-scheme-2://input-mock-host-2:42/test-input-path" ) );
186
187 //Test the scenario where input could match two different rules
188 inputUrl = Parser.parseLiteral( "/foo/bar" );
189
190 roles.remove(0);
191 roles.add("service-1");
192 outputUrl = processor.rewrite( environment, inputUrl, UrlRewriter.Direction.OUT, null);
193
194 assertThat(
195 "Expect rewrite to contain the correct path.",
196 outputUrl.toString(), is( "/foo/service-1" ) );
197
198 roles.remove(0);
199 roles.add("service-2");
200
201 outputUrl = processor.rewrite( environment, inputUrl, UrlRewriter.Direction.OUT, null);
202
203 assertThat(
204 "Expect rewrite to contain the correct path.",
205 outputUrl.toString(), is( "/foo/service-2" ) );
206
207 processor.destroy();
208 }
209
210 @Test
211 public void testRewriteViaRuleNameWithAmbiguousRules() throws IOException, URISyntaxException {
212 UrlRewriteEnvironment environment = EasyMock.createNiceMock( UrlRewriteEnvironment.class );
213 HttpServletRequest request = EasyMock.createNiceMock( HttpServletRequest.class );
214 HttpServletResponse response = EasyMock.createNiceMock( HttpServletResponse.class );
215 EasyMock.replay( environment, request, response );
216
217 UrlRewriteProcessor processor = new UrlRewriteProcessor();
218 UrlRewriteRulesDescriptor config = UrlRewriteRulesDescriptorFactory.load(
219 "xml", getTestResourceReader( "rewrite-with-same-rules.xml", "UTF-8" ) );
220 processor.initialize( environment, config );
221
222 Template inputUrl = Parser.parseLiteral( "input-mock-scheme-1://input-mock-host-1:42/test-input-path" );
223 Template outputUrl = processor.rewrite( null, inputUrl, UrlRewriter.Direction.OUT, "test-rule-2" );
224
225 assertThat( "Expect rewrite to produce a new URL",
226 outputUrl, notNullValue() );
227 assertThat(
228 "Expect rewrite to contain the correct path.",
229 outputUrl.toString(), is( "output-mock-scheme-2://output-mock-host-2:42/test-input-path" ) );
230
231 outputUrl = processor.rewrite( null, inputUrl, UrlRewriter.Direction.OUT, "test-rule-1" );
232
233 assertThat( "Expect rewrite to produce a new URL",
234 outputUrl, notNullValue() );
235 assertThat(
236 "Expect rewrite to contain the correct path.",
237 outputUrl.toString(), is( "output-mock-scheme-1://output-mock-host-1:42/test-input-path" ) );
238
239 processor.destroy();
240 }
241
242 @Test
243 public void testRewriteViaRuleWithComplexFlow() throws Exception {
244 UrlRewriteEnvironment environment = EasyMock.createNiceMock( UrlRewriteEnvironment.class );
245 HttpServletRequest request = EasyMock.createNiceMock( HttpServletRequest.class );
246 HttpServletResponse response = EasyMock.createNiceMock( HttpServletResponse.class );
247 EasyMock.replay( environment, request, response );
248
249 UrlRewriteProcessor processor = new UrlRewriteProcessor();
250 UrlRewriteRulesDescriptor config = UrlRewriteRulesDescriptorFactory.load(
251 "xml", getTestResourceReader( "rewrite.xml", "UTF-8" ) );
252 processor.initialize( environment, config );
253
254 Template inputUrl;
255 Template outputUrl;
256
257 inputUrl = Parser.parseLiteral( "test-scheme://test-host:777/test-path" );
258 outputUrl = processor.rewrite( null, inputUrl, UrlRewriter.Direction.IN, "test-rule-with-complex-flow" );
259 assertThat(
260 "Expect rewrite to contain the correct path.",
261 outputUrl.toString(), is( "test-scheme-output://test-host-output:42/test-path-output/test-path" ) );
262
263 inputUrl = Parser.parseLiteral( "test-scheme://test-host:42/~/test-path" );
264 outputUrl = processor.rewrite( null, inputUrl, UrlRewriter.Direction.IN, "test-rule-with-complex-flow" );
265 assertThat(
266 "Expect rewrite to contain the correct path.",
267 outputUrl.toString(), is( "test-scheme-output://test-host-output:777/test-path-output/test-home/test-path" ) );
268
269 processor.destroy();
270 }
271
272 @Test
273 public void testRewriteViaRuleWithWildcardTemplateAndOptionalQuery() throws Exception {
274 UrlRewriteEnvironment environment = EasyMock.createNiceMock( UrlRewriteEnvironment.class );
275 HttpServletRequest request = EasyMock.createNiceMock( HttpServletRequest.class );
276 HttpServletResponse response = EasyMock.createNiceMock( HttpServletResponse.class );
277 EasyMock.replay( environment, request, response );
278
279 UrlRewriteProcessor processor = new UrlRewriteProcessor();
280 UrlRewriteRulesDescriptor config = UrlRewriteRulesDescriptorFactory.load(
281 "xml", getTestResourceReader( "rewrite.xml", "UTF-8" ) );
282 processor.initialize( environment, config );
283
284 Template inputUrl;
285 Template outputUrl;
286
287 inputUrl = Parser.parseLiteral( "test-scheme-input://test-host-input:42/test-path-input-one/test-path-input-two?test-query-name=test-query-value" );
288 outputUrl = processor.rewrite( null, inputUrl, UrlRewriter.Direction.OUT, "test-rule-2" );
289
290 assertThat(
291 "Expect rewrite to contain the correct path and query.",
292 outputUrl.toString(), is( "test-scheme-output://test-host-output:777/test-path-output/test-path-input-one/test-path-input-two?test-query-name=test-query-value" ) );
293
294 inputUrl = Parser.parseLiteral( "test-scheme-input://test-host-input:42/test-path-input-one/test-path-input-two" );
295 outputUrl = processor.rewrite( null, inputUrl, UrlRewriter.Direction.OUT, "test-rule-2" );
296
297 assertThat(
298 "Expect rewrite to contain the correct path.",
299 outputUrl.toString(), is( "test-scheme-output://test-host-output:777/test-path-output/test-path-input-one/test-path-input-two" ) );
300
301 processor.destroy();
302 }
303
304 /**
305 * Tests the rewrite pattern used for re-writing Solr urls passed through Knox.
306 *
307 * @throws Exception
308 */
309 @Test
310 public void testSolrRewrite() throws Exception {
311 URI inputUri, outputUri;
312 Matcher<Void> matcher;
313 Matcher<Void>.Match match;
314 Template input, pattern, template;
315
316 inputUri = new URI(
317 "https://hortonworks.sandbox.hdp.24.test:8443/gateway/sandbox/solr/TestCollection/select?q=*.*&wt=json&indent=true");
318
319 input = Parser.parseLiteral(inputUri.toString());
320 pattern = Parser.parseTemplate("*://*:*/**/solr/{collection=**}/{query=**}?{**}");
321 template = Parser.parseTemplate("http://sandbox.hortonworks.com/solr/{collection=**}/{query=**}?{**}");
322
323 matcher = new Matcher<Void>();
324 matcher.add(pattern, null);
325 match = matcher.match(input);
326
327 outputUri = Expander.expand(template, match.getParams(), null);
328
329 final String reWrittenScheme = outputUri.getScheme();
330 assertEquals("http", reWrittenScheme);
331
332 final String reWrittenHost = outputUri.getHost();
333 assertEquals("sandbox.hortonworks.com", reWrittenHost);
334
335 final String reWrittenPath = outputUri.getPath();
336 assertEquals("/solr/TestCollection/select", reWrittenPath);
337
338 // Whole thing is (non-deterministicly ordered around the &s):
339 // "q=*.*&wt=json&indent=true"
340 final String reWrittenQuery = outputUri.getQuery();
341
342 // Check individual parameters are present, and have the right value.
343 final Map<String, String> reWrittenParams = mapUrlParameters(reWrittenQuery);
344 assertTrue(reWrittenParams.containsKey("q"));
345 assertEquals("*.*", reWrittenParams.get("q"));
346 assertTrue(reWrittenParams.containsKey("wt"));
347 assertEquals("json", reWrittenParams.get("wt"));
348 assertEquals("true", reWrittenParams.get("indent"));
349 }
350
351
352 @Test
353 public void testSolrRewriteDefaultPort() throws Exception {
354 URI inputUri, outputUri;
355 Matcher<Void> matcher;
356 Matcher<Void>.Match match;
357 Template input, pattern, template;
358
359 inputUri = new URI(
360 "https://hortonworks.sandbox.hdp.24.test/gateway/sandbox/solr/TestCollection/select?q=*.*&wt=json&indent=true");
361
362 input = Parser.parseLiteral(inputUri.toString());
363 pattern = Parser.parseTemplate("*://*:*/**/solr/{collection=**}/{query=**}?{**}");
364 template = Parser.parseTemplate("http://sandbox.hortonworks.com/solr/{collection=**}/{query=**}?{**}");
365
366 matcher = new Matcher<Void>();
367 matcher.add(pattern, null);
368 match = matcher.match(input);
369
370 outputUri = Expander.expand(template, match.getParams(), null);
371
372 final String reWrittenScheme = outputUri.getScheme();
373 assertEquals("http", reWrittenScheme);
374
375 final String reWrittenHost = outputUri.getHost();
376 assertEquals("sandbox.hortonworks.com", reWrittenHost);
377
378 final String reWrittenPath = outputUri.getPath();
379 assertEquals("/solr/TestCollection/select", reWrittenPath);
380
381 // Whole thing is (non-deterministicly ordered around the &s):
382 // "q=*.*&wt=json&indent=true"
383 final String reWrittenQuery = outputUri.getQuery();
384
385 // Check individual parameters are present, and have the right value.
386 final Map<String, String> reWrittenParams = mapUrlParameters(reWrittenQuery);
387 assertTrue(reWrittenParams.containsKey("q"));
388 assertEquals("*.*", reWrittenParams.get("q"));
389 assertTrue(reWrittenParams.containsKey("wt"));
390 assertEquals("json", reWrittenParams.get("wt"));
391 assertEquals("true", reWrittenParams.get("indent"));
392 }
393
394
395 /**
396 * Turn a string containing URL parameters, e.g.
397 *
398 * <pre>
399 * a=b&c=d&e=f
400 * </pre>
401 *
402 * into a map such as
403 * <table>
404 * <tr>
405 * <th>Key</th>
406 * <th>Value</th>
407 * </tr>
408 * <tr>
409 * <td>a</td>
410 * <td>b</td>
411 * </tr>
412 * <tr>
413 * <td>c</td>
414 * <td>d</td>
415 * </tr>
416 * </table>
417 *
418 * @param urlParameters the URL parameter string. Expected to contain something of the form
419 * "a=b&c=d" etc (i.e. Key=Value separated by &).
420 * @return a map, with the key-values pairs representing the URL parameters.
421 */
422 private Map<String, String> mapUrlParameters(String urlParameters) {
423 final Map<String, String> map = new HashMap<>();
424 for (String pair : urlParameters.split("&")) {
425 String[] kv = pair.split("=");
426 map.put(kv[0], kv[1]);
427 }
428 return map;
429 }
430
431
432 }