// =================================================================================================
// Copyright 2011 Twitter, Inc.
// -------------------------------------------------------------------------------------------------
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this work except in compliance with the License.
// You may obtain a copy of the License in the LICENSE file, or 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 com.twitter.common.net;
import com.google.common.base.Function;
import com.google.common.testing.TearDown;
import com.google.common.testing.junit4.TearDownTestCase;
import com.twitter.common.base.Closure;
import com.twitter.common.collections.Pair;
import org.easymock.Capture;
import org.easymock.IAnswer;
import org.easymock.IMocksControl;
import org.junit.Before;
import org.junit.Test;
import org.mortbay.jetty.Server;
import org.mortbay.jetty.bio.SocketConnector;
import org.mortbay.jetty.servlet.Context;
import org.mortbay.jetty.servlet.ServletHolder;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URL;
import static org.easymock.EasyMock.*;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
/**
* TODO(John Sirois): add test for error conditions
*
* @author John Sirois
*/
public class UrlResolverUtilTest extends TearDownTestCase {
private static final String REDIRECT_LOCATION = "http://bar";
private IMocksControl control;
private Function<URL, String> urlToUA;
private Closure<Pair<HttpServletRequest, HttpServletResponse>> requestHandler;
private UrlResolverUtil urlResolverUtil;
private HttpServlet servlet;
private String url;
@Before
public void setUp() {
control = createControl();
@SuppressWarnings("unchecked")
Function<URL, String> urlToUA = control.createMock(Function.class);
this.urlToUA = urlToUA;
@SuppressWarnings("unchecked")
Closure<Pair<HttpServletRequest, HttpServletResponse>> handler =
control.createMock(Closure.class);
requestHandler = handler;
urlResolverUtil = new UrlResolverUtil(urlToUA);
servlet = new HttpServlet() {
@Override protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
requestHandler.execute(Pair.of(req, resp));
}
};
}
@Test
public void testUASelection() throws Exception {
url = startServer();
String redirectLocation = "http://bar.com";
expect(urlToUA.apply(new URL(url))).andReturn("foo-agent");
expectRequestAndRedirect(redirectLocation);
control.replay();
String effectiveUrl = urlResolverUtil.getEffectiveUrl(url, null /* no proxy */);
assertEquals(redirectLocation, effectiveUrl);
control.verify();
}
@Test
public void testRelativeRedirect() throws Exception {
url = startServer();
String relativeRedirect = "relatively/speaking";
expect(urlToUA.apply(new URL(url))).andReturn("foo-agent");
expectRequestAndRedirect(relativeRedirect);
control.replay();
String effectiveUrl = urlResolverUtil.getEffectiveUrl(url, null /* no proxy */);
assertEquals(url + relativeRedirect, effectiveUrl);
control.verify();
}
@Test
public void testInvalidRedirect() throws Exception {
url = startServer();
String badRedirect = ":::<<<<<::IAMNOTAVALIDURI";
expect(urlToUA.apply(new URL(url))).andReturn("foo-agent");
expectRequestAndRedirect(badRedirect);
control.replay();
String effectiveUrl = urlResolverUtil.getEffectiveUrl(url, null /* no proxy */);
assertEquals(badRedirect, effectiveUrl);
control.verify();
}
private void expectRequestAndRedirect(final String location) throws Exception {
final Capture<Pair<HttpServletRequest, HttpServletResponse>> requestCapture =
new Capture<Pair<HttpServletRequest, HttpServletResponse>>();
requestHandler.execute(capture(requestCapture));
expectLastCall().andAnswer(new IAnswer<Void>() {
@Override public Void answer() throws Throwable {
assertTrue(requestCapture.hasCaptured());
Pair<HttpServletRequest, HttpServletResponse> pair = requestCapture.getValue();
HttpServletRequest request = pair.getFirst();
assertEquals("HEAD", request.getMethod());
assertEquals("foo-agent", request.getHeader("User-Agent"));
pair.getSecond().sendRedirect(location);
return null;
}
});
}
private String startServer() throws Exception {
final Server server = new Server();
final SocketConnector connector = new SocketConnector();
connector.setHost("127.0.0.1");
connector.setPort(0);
server.addConnector(connector);
Context context = new Context(server, "/", Context.NO_SECURITY);
context.addServlet(new ServletHolder(servlet), "/*");
server.start();
addTearDown(new TearDown() {
@Override public void tearDown() throws Exception {
server.stop();
}
});
return "http://" + connector.getHost() + ":" + connector.getLocalPort() + "/";
}
}