/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.shindig.gadgets.servlet;
import static org.easymock.EasyMock.expect;
import java.util.List;
import org.apache.shindig.common.uri.Uri;
import org.apache.shindig.common.uri.UriBuilder;
import org.apache.shindig.gadgets.GadgetException;
import org.apache.shindig.gadgets.http.HttpRequest;
import org.apache.shindig.gadgets.http.HttpResponse;
import org.apache.shindig.gadgets.http.HttpResponseBuilder;
import org.apache.shindig.gadgets.uri.ConcatUriManager;
import org.apache.shindig.gadgets.uri.UriStatus;
import org.junit.Before;
import org.junit.Test;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import java.util.Map;
import java.util.concurrent.AbstractExecutorService;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
public class ConcatProxyServletTest extends ServletTestFixture {
private static final String REQUEST_DOMAIN = "example.org";
private static final Uri URL1 = Uri.parse("http://example.org/1.js");
private static final Uri URL2 = Uri.parse("http://example.org/2.js");
private static final Uri URL3 = Uri.parse("http://example.org/3.js");
private static final String SCRT1 = "var v1 = 1;";
private static final String SCRT2 = "var v2 = { \"a-b\": 1 , c: \"hello!,\" };";
private static final String SCRT3 = "var v3 = \"world\";";
private static final String SCRT1_ESCAPED = "var v1 = 1;";
private static final String SCRT2_ESCAPED =
"var v2 = { \\\"a-b\\\": 1 , c: \\\"hello!,\\\" };";
private static final String SCRT3_ESCAPED = "var v3 = \\\"world\\\";";
private final ConcatProxyServlet servlet = new ConcatProxyServlet();
private TestConcatUriManager uriManager;
private final ExecutorService sequentialExecutor = Executors.newSingleThreadExecutor();
private final ExecutorService threadedExecutor = Executors.newCachedThreadPool();
@Before
public void setUp() throws Exception {
servlet.setRequestPipeline(pipeline);
uriManager = new TestConcatUriManager();
servlet.setConcatUriManager(uriManager);
expect(request.getHeader("Host")).andReturn(REQUEST_DOMAIN).anyTimes();
expect(lockedDomainService.isSafeForOpenProxy(REQUEST_DOMAIN))
.andReturn(true).anyTimes();
expectGetAndReturnData(URL1, SCRT1);
expectGetAndReturnData(URL2, SCRT2);
expectGetAndReturnData(URL3, SCRT3);
}
private void expectGetAndReturnData(Uri url, String data) throws Exception {
HttpRequest req = new HttpRequest(url);
HttpResponse resp = new HttpResponseBuilder().setResponse(data.getBytes()).create();
expect(pipeline.execute(req)).andReturn(resp).anyTimes();
}
/**
* Simulate the added comments by concat
* @param data - concatenated data
* @param url - data source url
* @return data with added comments
*/
private String addComment(String data, String url) {
String res = "/* ---- Start " + url + " ---- */\r\n"
+ data + "/* ---- End " + url + " ---- */\r\n";
return res;
}
private String addErrComment(String url, int code) {
return "/* ---- Error " + code + " (" + url + ") ---- */\r\n";
}
/**
* Simulate the asJSON result of one script
* @param url - the script url
* @param data - the script escaped content
* @return simulated hash mapping
*/
private String addVar(String url, String data) {
return '\"' + url + "\":\"" + data +"\",\r\n";
}
/**
* Run a concat test by fetching resources as configured by given Executor
* @param result - expected concat results
* @param uris - list of uris to concat
* @throws Exception
*/
private void runConcat(ExecutorService exec, String result, String tok, Uri... uris)
throws Exception {
expectRequestWithUris(Lists.newArrayList(uris), tok);
// Run the servlet
servlet.setExecutor(exec);
servlet.doGet(request, recorder);
verify();
assertEquals(result, recorder.getResponseAsString());
assertEquals(200, recorder.getHttpStatusCode());
}
@Test
public void testSimpleConcat() throws Exception {
String results = addComment(SCRT1, URL1.toString()) + addComment(SCRT2,URL2.toString());
runConcat(sequentialExecutor, results, null, URL1, URL2);
}
@Test
public void testSimpleConcatThreaded() throws Exception {
String results = addComment(SCRT1, URL1.toString()) + addComment(SCRT2,URL2.toString());
runConcat(threadedExecutor, results, null, URL1, URL2);
}
@Test
public void testThreeConcat() throws Exception {
String results = addComment(SCRT1, URL1.toString()) + addComment(SCRT2,URL2.toString())
+ addComment(SCRT3, URL3.toString());
runConcat(sequentialExecutor, results, null, URL1, URL2, URL3);
}
@Test
public void testThreeConcatThreaded() throws Exception {
String results = addComment(SCRT1, URL1.toString()) + addComment(SCRT2,URL2.toString())
+ addComment(SCRT3, URL3.toString());
runConcat(threadedExecutor, results, null, URL1, URL2, URL3);
}
@Test
public void testConcatBadException() throws Exception {
final Uri URL4 = Uri.parse("http://example.org/4.js");
HttpRequest req = new HttpRequest(URL4);
expect(pipeline.execute(req)).andThrow(
new GadgetException(GadgetException.Code.HTML_PARSE_ERROR)).anyTimes();
expectRequestWithUris(Lists.newArrayList(URL1, URL4));
// Run the servlet
servlet.doGet(request, recorder);
verify();
String results = addComment(SCRT1, URL1.toString())
+ "HTML_PARSE_ERROR concat(http://example.org/4.js) null";
assertEquals(results, recorder.getResponseAsString());
assertEquals(400, recorder.getHttpStatusCode());
}
@Test
public void testConcat404() throws Exception {
String url = "http://nobodyhome.com/";
HttpRequest req = new HttpRequest(Uri.parse(url));
HttpResponse resp = new HttpResponseBuilder().setHttpStatusCode(404).create();
expect(pipeline.execute(req)).andReturn(resp).anyTimes();
expectRequestWithUris(Lists.newArrayList(URL1, Uri.parse(url)));
servlet.doGet(request, recorder);
verify();
String results = addComment(SCRT1, URL1.toString()) + addErrComment(url,404);
assertEquals(results, recorder.getResponseAsString());
assertEquals(200, recorder.getHttpStatusCode());
}
@Test
public void testAsJsonConcat() throws Exception {
String results = "_js={\r\n"
+ addVar(URL1.toString(), SCRT1_ESCAPED)
+ addVar(URL2.toString(), SCRT2_ESCAPED)
+ "};\r\n";
runConcat(sequentialExecutor, results, "_js", URL1, URL2);
}
@Test
public void testThreeAsJsonConcat() throws Exception {
String results = "_js={\r\n"
+ addVar(URL1.toString(), SCRT1_ESCAPED)
+ addVar(URL2.toString(), SCRT2_ESCAPED)
+ addVar(URL3.toString(), SCRT3_ESCAPED)
+ "};\r\n";
runConcat(sequentialExecutor, results, "_js", URL1, URL2, URL3);
}
@Test
public void testBadJsonVarConcat() throws Exception {
expectRequestWithUris(Lists.<Uri>newArrayList(), "bad code;");
servlet.doGet(request, recorder);
verify();
String results = "/* ---- Error 400, Bad json variable name bad code; ---- */\r\n";
assertEquals(results, recorder.getResponseAsString());
assertEquals(400, recorder.getHttpStatusCode());
}
@Test
public void testAsJsonConcat404() throws Exception {
final Uri URL4 = Uri.parse("http://example.org/4.js");
HttpRequest req = new HttpRequest(URL4);
HttpResponse resp = new HttpResponseBuilder().setHttpStatusCode(404).create();
expect(pipeline.execute(req)).andReturn(resp).anyTimes();
String results = "_js={\r\n"
+ addVar(URL1.toString(), SCRT1_ESCAPED)
+ "/* ---- Error 404 (http://example.org/4.js) ---- */\r\n"
+ "};\r\n";
runConcat(sequentialExecutor, results, "_js", URL1, URL4);
}
@Test
public void testAsJsonConcatException() throws Exception {
final Uri URL4 = Uri.parse("http://example.org/4.js");
HttpRequest req = new HttpRequest(URL4);
expect(pipeline.execute(req)).andThrow(
new GadgetException(GadgetException.Code.FAILED_TO_RETRIEVE_CONTENT)).anyTimes();
expectRequestWithUris(Lists.newArrayList(URL1, URL4), "_js");
servlet.doGet(request, recorder);
verify();
String results = "_js={\r\n"
+ addVar(URL1.toString(), SCRT1_ESCAPED)
+ "FAILED_TO_RETRIEVE_CONTENT concat(http://example.org/4.js) null};\r\n";
assertEquals(results, recorder.getResponseAsString());
assertEquals(400, recorder.getHttpStatusCode());
}
@Test
public void testAsJsonConcatBadException() throws Exception {
final Uri URL4 = Uri.parse("http://example.org/4.js");
HttpRequest req = new HttpRequest(URL4);
expect(pipeline.execute(req)).andThrow(
new GadgetException(GadgetException.Code.HTML_PARSE_ERROR)).anyTimes();
String results = "_js={\r\n"
+ addVar(URL1.toString(), SCRT1_ESCAPED)
+ "HTML_PARSE_ERROR concat(http://example.org/4.js) null};\r\n";
expectRequestWithUris(Lists.newArrayList(URL1, URL4), "_js");
// Run the servlet
servlet.doGet(request, recorder);
verify();
assertEquals(results, recorder.getResponseAsString());
assertEquals(400, recorder.getHttpStatusCode());
}
private void expectRequestWithUris(List<Uri> uris) {
expectRequestWithUris(uris, null);
}
private void expectRequestWithUris(List<Uri> uris, String tok) {
expect(request.getScheme()).andReturn("http").anyTimes();
expect(request.getServerPort()).andReturn(80).anyTimes();
expect(request.getServerName()).andReturn("example.com").anyTimes();
expect(request.getRequestURI()).andReturn("/path").anyTimes();
expect(request.getQueryString()).andReturn("").anyTimes();
replay();
Uri uri = new UriBuilder(request).toUri();
uriManager.expect(uri, uris, tok);
}
private static class TestConcatUriManager implements ConcatUriManager {
private final Map<Uri, ConcatUri> uriMap;
private TestConcatUriManager() {
this.uriMap = Maps.newHashMap();
}
public List<ConcatData> make(
List<ConcatUri> resourceUris, boolean isAdjacent) {
// Not used by ConcatProxyServlet
throw new UnsupportedOperationException();
}
public ConcatUri process(Uri uri) {
return uriMap.get(uri);
}
private void expect(Uri orig, UriStatus status, Type type, List<Uri> uris, String json) {
uriMap.put(orig, new ConcatUri(status, uris, json, type, null));
}
private void expect(Uri orig, List<Uri> uris, String tok) {
expect(orig, UriStatus.VALID_UNVERSIONED, Type.JS, uris, tok);
}
}
}