/*
* 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.http;
import static org.easymock.EasyMock.expect;
import static org.easymock.EasyMock.replay;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
import org.apache.shindig.auth.BasicSecurityToken;
import org.apache.shindig.auth.SecurityToken;
import org.apache.shindig.common.uri.Uri;
import org.apache.shindig.gadgets.AuthType;
import org.apache.shindig.gadgets.oauth.OAuthArguments;
import org.apache.shindig.gadgets.spec.RequestAuthenticationInfo;
import org.apache.shindig.gadgets.uri.UriCommon.Param;
import org.easymock.EasyMock;
import org.junit.Test;
import java.util.Map;
public class AbstractHttpCacheTest {
protected static final Uri DEFAULT_URI = Uri.parse("http://example.org/file.txt");
protected static final Uri IMAGE_URI = Uri.parse("http://example.org/image.png");
private static final Uri APP_URI = Uri.parse("http://example.org/gadget.xml");
private static final String MODULE_ID = "100";
private static final String SERVICE_NAME = "service";
private static final String TOKEN_NAME = "token";
private static final String CONTAINER_NAME = "container";
private final TestHttpCache cache = new TestHttpCache();
@Test
public void createKeySimple() {
HttpRequest request = new HttpRequest(DEFAULT_URI).setAuthType(AuthType.NONE);
CacheKeyBuilder key = new CacheKeyBuilder()
.setLegacyParam(0, DEFAULT_URI).setLegacyParam(1, AuthType.NONE);
assertEquals(key.build(), cache.createKey(request));
}
private HttpRequest getMockImageRequest(String height, String width, String quality,
String mimeType) {
HttpRequest request = EasyMock.createMock(HttpRequest.class);
expect(request.getUri()).andReturn(IMAGE_URI).anyTimes();
expect(request.getAuthType()).andReturn(AuthType.NONE).anyTimes();
expect(request.getSecurityToken()).andReturn(null).anyTimes();
expect(request.getParam(Param.RESIZE_HEIGHT.getKey())).andReturn(height).anyTimes();
expect(request.getParam(Param.RESIZE_WIDTH.getKey())).andReturn(width).anyTimes();
expect(request.getParam(Param.RESIZE_QUALITY.getKey())).andReturn(quality).anyTimes();
expect(request.getRewriteMimeType()).andReturn(mimeType).anyTimes();
replay(request);
return request;
}
@Test
public void createKeySimpleImageRequest() throws Exception {
// Mock the Request with Image Resize (Quality) params, without rewrite mimeType.
HttpRequest request = getMockImageRequest("100", "80", "70", null);
CacheKeyBuilder key = new CacheKeyBuilder()
.setLegacyParam(0, IMAGE_URI)
.setLegacyParam(1, AuthType.NONE)
.setParam("rh", "100")
.setParam("rw", "80")
.setParam("rq", "70");
assertEquals(key.build(), cache.createKey(request));
}
@Test
public void createKeyImageRequestRewrite() throws Exception {
// Mock the Request with Image Resize (Quality) params and specified rewrite mimeType.
HttpRequest request = getMockImageRequest("100", "80", "70", "image/jpg");
CacheKeyBuilder key = new CacheKeyBuilder()
.setLegacyParam(0, IMAGE_URI)
.setLegacyParam(1, AuthType.NONE)
.setParam("rh", "100")
.setParam("rw", "80")
.setParam("rq", "70")
.setParam("rm", "image/jpg");
assertEquals(key.build(), cache.createKey(request));
}
@Test
public void createKeySignedOwner() throws Exception {
// Using a mock instead of a fake object makes the test less brittle if the interface should
// change.
RequestAuthenticationInfo authInfo = newMockAuthInfo(
true /* isSignOwner */,
false /* isSignViewer */,
ImmutableMap.of("OAUTH_SERVICE_NAME", SERVICE_NAME, "OAUTH_TOKEN_NAME", TOKEN_NAME));
replay(authInfo);
String ownerId = "owner eye dee";
SecurityToken securityToken = new BasicSecurityToken(ownerId, "", "", "",
APP_URI.toString(), MODULE_ID, CONTAINER_NAME, null, null);
HttpRequest request = new HttpRequest(DEFAULT_URI)
.setAuthType(AuthType.SIGNED)
.setOAuthArguments(new OAuthArguments(authInfo))
.setSecurityToken(securityToken);
CacheKeyBuilder key = new CacheKeyBuilder()
.setLegacyParam(0, DEFAULT_URI)
.setLegacyParam(1, AuthType.SIGNED)
.setLegacyParam(2, ownerId)
.setLegacyParam(3, "")
.setLegacyParam(5, APP_URI)
.setLegacyParam(6, MODULE_ID)
.setLegacyParam(7, SERVICE_NAME)
.setLegacyParam(8, TOKEN_NAME);
assertEquals(key.build(), cache.createKey(request));
}
private RequestAuthenticationInfo newMockAuthInfo(boolean isSignOwner, boolean isSignViewer,
Map<String, String> attributesMap) {
RequestAuthenticationInfo authInfo = EasyMock.createNiceMock(RequestAuthenticationInfo.class);
expect(authInfo.getAttributes()).andReturn(attributesMap).anyTimes();
expect(authInfo.getAuthType()).andReturn(AuthType.SIGNED).anyTimes();
expect(authInfo.getHref()).andReturn(DEFAULT_URI).anyTimes();
expect(authInfo.isSignOwner()).andReturn(isSignOwner).anyTimes();
expect(authInfo.isSignViewer()).andReturn(isSignOwner).anyTimes();
return authInfo;
}
@Test
public void createKeySignedViewer() throws Exception {
RequestAuthenticationInfo authInfo = newMockAuthInfo(
false /* isSignOwner */,
true /* isSignViewer */,
ImmutableMap.of("OAUTH_SERVICE_NAME", SERVICE_NAME, "OAUTH_TOKEN_NAME", TOKEN_NAME));
replay(authInfo);
String viewerId = "viewer eye dee";
SecurityToken securityToken = new BasicSecurityToken(
"", viewerId, "", "", APP_URI.toString(), MODULE_ID, CONTAINER_NAME, null, null);
HttpRequest request = new HttpRequest(DEFAULT_URI)
.setAuthType(AuthType.SIGNED)
.setOAuthArguments(new OAuthArguments(authInfo))
.setSecurityToken(securityToken);
CacheKeyBuilder key = new CacheKeyBuilder()
.setLegacyParam(0, DEFAULT_URI)
.setLegacyParam(1, AuthType.SIGNED)
.setLegacyParam(3, null) // The Viewer ID is in this case defaults to null
.setLegacyParam(5, APP_URI)
.setLegacyParam(6, MODULE_ID)
.setLegacyParam(7, SERVICE_NAME)
.setLegacyParam(8, TOKEN_NAME);
assertEquals(key.build(), cache.createKey(request));
}
@Test
public void createKeyWithTokenOwner() throws Exception {
RequestAuthenticationInfo authInfo = newMockAuthInfo(
true /* isSignOwner */,
true /* isSignViewer */,
ImmutableMap.of("OAUTH_SERVICE_NAME", SERVICE_NAME, "OAUTH_TOKEN_NAME", TOKEN_NAME,
"OAUTH_USE_TOKEN", "always"));
replay(authInfo);
String userId = "user id";
SecurityToken securityToken = new BasicSecurityToken(
userId, userId, "", "", APP_URI.toString(), MODULE_ID, CONTAINER_NAME, null, null);
HttpRequest request = new HttpRequest(DEFAULT_URI)
.setAuthType(AuthType.SIGNED)
.setOAuthArguments(new OAuthArguments(authInfo))
.setSecurityToken(securityToken);
CacheKeyBuilder key = new CacheKeyBuilder()
.setLegacyParam(0, DEFAULT_URI)
.setLegacyParam(1, AuthType.SIGNED)
.setLegacyParam(2, userId)
.setLegacyParam(3, userId)
.setLegacyParam(4, userId)
.setLegacyParam(5, APP_URI)
.setLegacyParam(6, MODULE_ID)
.setLegacyParam(7, SERVICE_NAME)
.setLegacyParam(8, TOKEN_NAME);
assertEquals(key.build(), cache.createKey(request));
}
@Test(expected = IllegalArgumentException.class)
public void createKeyWithoutSecurityToken() throws Exception {
RequestAuthenticationInfo authInfo = newMockAuthInfo(
true /* isSignOwner */,
false /* isSignViewer */,
ImmutableMap.<String, String>of());
replay(authInfo);
HttpRequest request = new HttpRequest(DEFAULT_URI)
.setAuthType(AuthType.SIGNED)
.setOAuthArguments(new OAuthArguments(authInfo));
cache.createKey(request);
}
@Test
public void getResponse() {
HttpRequest request = new HttpRequest(DEFAULT_URI);
String key = cache.createKey(request);
HttpResponse response = new HttpResponse("result");
cache.map.put(key, response);
assertEquals(response, cache.getResponse(request));
}
@Test
public void getResponseUsingPost() {
HttpRequest request = new HttpRequest(DEFAULT_URI)
.setMethod("POST");
String key = cache.createKey(request);
HttpResponse response = new HttpResponse("result");
cache.map.put(key, response);
assertNull("Did not return null when method was POST", cache.getResponse(request));
}
@Test
public void getResponseUsingMethodOverride() {
HttpRequest request = new HttpRequest(DEFAULT_URI)
.setMethod("POST")
.addHeader("X-Method-Override", "GET");
String key = cache.createKey(request);
HttpResponse response = new HttpResponse("result");
cache.map.put(key, response);
assertEquals(response, cache.getResponse(request));
}
@Test
public void getResponseIgnoreCache() {
HttpRequest request = new HttpRequest(DEFAULT_URI);
String key = cache.createKey(request);
HttpResponse response = new HttpResponse("result");
cache.map.put(key, response);
request.setIgnoreCache(true);
assertNull("Did not return null when ignoreCache was true", cache.getResponse(request));
}
@Test
public void getResponseNotCacheable() {
HttpRequest request = new HttpRequest(DEFAULT_URI);
HttpResponse response = new HttpResponseBuilder().setStrictNoCache().create();
cache.addResponse(request, response);
assertNull("Did not return null when response was uncacheable", cache.getResponse(request));
}
@Test
public void addResponse() {
HttpRequest request = new HttpRequest(DEFAULT_URI);
HttpResponse response = new HttpResponse("normal");
String key = cache.createKey(request);
assertTrue("response should have been cached", cache.addResponse(request, response));
assertEquals(response, cache.map.get(key));
}
@Test
public void addResponseIgnoreCache() {
HttpRequest request = new HttpRequest(DEFAULT_URI)
.setIgnoreCache(true);
HttpResponse response = new HttpResponse("does not matter");
assertFalse("response should not have been cached", cache.addResponse(request, response));
assertEquals(0, cache.map.size());
}
@Test
public void addResponseNotCacheable() {
HttpRequest request = new HttpRequest(DEFAULT_URI);
HttpResponse response = new HttpResponseBuilder().setStrictNoCache().create();
assertFalse(cache.addResponse(request, response));
assertEquals(0, cache.map.size());
}
@Test
public void addResponseIfModifiedSince() {
HttpRequest request = new HttpRequest(DEFAULT_URI);
HttpResponse response = new HttpResponseBuilder().setHttpStatusCode(HttpResponse.SC_NOT_MODIFIED).create();
assertFalse(cache.addResponse(request, response));
assertEquals(0, cache.map.size());
}
@Test
public void addResponseUsingPost() {
HttpRequest request = new HttpRequest(DEFAULT_URI)
.setMethod("POST");
HttpResponse response = new HttpResponse("does not matter");
assertFalse(cache.addResponse(request, response));
assertEquals(0, cache.map.size());
}
@Test
public void addResponseUsingMethodOverride() {
HttpRequest request = new HttpRequest(DEFAULT_URI)
.setMethod("POST")
.addHeader("X-Method-Override", "GET");
HttpResponse response = new HttpResponse("normal");
String key = cache.createKey(request);
assertTrue(cache.addResponse(request, response));
assertEquals(response, cache.map.get(key));
}
@Test
public void addResponseWithForcedTtl() {
HttpRequest request = new HttpRequest(DEFAULT_URI)
.setCacheTtl(10);
String key = cache.createKey(request);
HttpResponse response = new HttpResponse("result");
assertTrue(cache.addResponse(request, response));
assertEquals("public,max-age=10", cache.map.get(key).getHeader("Cache-Control"));
}
@Test
public void addResponseWithForcedTtlAndStrictNoCache() {
HttpRequest request = new HttpRequest(DEFAULT_URI)
.setCacheTtl(10);
String key = cache.createKey(request);
HttpResponse response = new HttpResponseBuilder()
.setResponseString("result")
.setStrictNoCache()
.create();
assertTrue(cache.addResponse(request, response));
assertEquals("public,max-age=10", cache.map.get(key).getHeader("Cache-Control"));
}
@Test
public void addResponseWithNoCachingHeaders() {
HttpRequest request = new HttpRequest(DEFAULT_URI);
String key = cache.createKey(request);
HttpResponse response = new HttpResponse("no headers");
assertTrue(cache.addResponse(request, response));
assertEquals("no headers", cache.map.get(key).getResponseAsString());
}
@Test
public void removeResponse() {
HttpRequest request = new HttpRequest(DEFAULT_URI);
String key = cache.createKey(request);
HttpResponse response = new HttpResponse("result");
cache.map.put(key, response);
assertEquals(response, cache.removeResponse(request));
assertEquals(0, cache.map.size());
}
@Test
public void removeResponseIsStaled() {
long expiration = System.currentTimeMillis() + 1000L;
HttpRequest request = new HttpRequest(DEFAULT_URI);
String key = cache.createKey(request);
HttpResponse response = new HttpResponseBuilder()
.setExpirationTime(expiration)
.create();
cache.map.put(key, response);
// The cache itself still hold and return staled value,
// caller responsible to decide what to do about it
assertEquals(response, cache.removeResponse(request));
assertEquals(0, cache.map.size());
}
private static class TestHttpCache extends AbstractHttpCache {
protected final Map<String, HttpResponse> map;
public TestHttpCache() {
map = Maps.newHashMap();
}
@Override
public void addResponseImpl(String key, HttpResponse response) {
map.put(key, response);
}
@Override
public HttpResponse getResponseImpl(String key) {
return map.get(key);
}
@Override
public HttpResponse removeResponseImpl(String key) {
return map.remove(key);
}
}
}