/*
* Copyright 2010 Proofpoint, Inc.
*
* Licensed 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 com.proofpoint.http.server;
import com.google.common.base.Charsets;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ListMultimap;
import com.google.common.io.ByteStreams;
import com.google.common.io.Files;
import com.google.common.net.HttpHeaders;
import com.google.common.net.MediaType;
import com.google.inject.Binder;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.Module;
import com.google.inject.Scopes;
import com.google.inject.multibindings.Multibinder;
import com.proofpoint.configuration.ConfigurationFactory;
import com.proofpoint.configuration.ConfigurationModule;
import com.proofpoint.event.client.NullEventModule;
import com.proofpoint.http.client.HttpClient;
import com.proofpoint.http.client.HttpStatus;
import com.proofpoint.http.client.HttpUriBuilder;
import com.proofpoint.http.client.StatusResponseHandler.StatusResponse;
import com.proofpoint.http.client.StringResponseHandler.StringResponse;
import com.proofpoint.http.client.jetty.JettyHttpClient;
import com.proofpoint.log.Logging;
import com.proofpoint.node.ApplicationNameModule;
import com.proofpoint.node.NodeInfo;
import com.proofpoint.node.testing.TestingNodeModule;
import com.proofpoint.reporting.ReportingModule;
import com.proofpoint.testing.FileUtils;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.BeforeSuite;
import org.testng.annotations.Test;
import org.weakref.jmx.testing.TestingMBeanModule;
import javax.servlet.Filter;
import javax.servlet.Servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.util.Map;
import java.util.Map.Entry;
import static com.google.common.net.MediaType.PLAIN_TEXT_UTF_8;
import static com.proofpoint.http.client.HttpUriBuilder.uriBuilderFrom;
import static com.proofpoint.http.client.Request.Builder.prepareGet;
import static com.proofpoint.http.client.StatusResponseHandler.createStatusResponseHandler;
import static com.proofpoint.http.client.StringResponseHandler.createStringResponseHandler;
import static com.proofpoint.http.server.HttpServerBinder.httpServerBinder;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.assertNull;
import static org.testng.Assert.assertTrue;
public class TestHttpServerModule
{
private File tempDir;
@BeforeSuite
public void setupSuite()
{
Logging.initialize();
}
@BeforeMethod
public void setup()
throws IOException
{
tempDir = Files.createTempDir().getCanonicalFile(); // getCanonicalFile needed to get around Issue 365 (http://code.google.com/p/guava-libraries/issues/detail?id=365)
}
@AfterMethod
public void tearDown()
throws IOException
{
FileUtils.deleteRecursively(tempDir);
}
@Test
public void testCanConstructServer()
throws Exception
{
Map<String, String> properties = new ImmutableMap.Builder<String, String>()
.put("http-server.http.port", "0")
.put("http-server.log.path", new File(tempDir, "http-request.log").getAbsolutePath())
.build();
ConfigurationFactory configFactory = new ConfigurationFactory(properties);
Injector injector = Guice.createInjector(new HttpServerModule(),
new ApplicationNameModule("test-application"),
new TestingNodeModule(),
new ConfigurationModule(configFactory),
new NullEventModule(),
new TestingMBeanModule(),
new ReportingModule(),
new Module()
{
@Override
public void configure(Binder binder)
{
binder.bind(Servlet.class).annotatedWith(TheServlet.class).to(DummyServlet.class);
binder.bind(Servlet.class).annotatedWith(TheAdminServlet.class).to(DummyServlet.class);
}
});
HttpServer server = injector.getInstance(HttpServer.class);
assertNotNull(server);
}
@Test
public void testHttpServerUri()
throws Exception
{
Map<String, String> properties = new ImmutableMap.Builder<String, String>()
.put("http-server.http.port", "0")
.put("http-server.log.path", new File(tempDir, "http-request.log").getAbsolutePath())
.build();
ConfigurationFactory configFactory = new ConfigurationFactory(properties);
Injector injector = Guice.createInjector(new HttpServerModule(),
new ApplicationNameModule("test-application"),
new TestingNodeModule(),
new ConfigurationModule(configFactory),
new NullEventModule(),
new TestingMBeanModule(),
new ReportingModule(),
new Module()
{
@Override
public void configure(Binder binder)
{
binder.bind(Servlet.class).annotatedWith(TheServlet.class).to(DummyServlet.class);
binder.bind(Servlet.class).annotatedWith(TheAdminServlet.class).to(DummyServlet.class);
}
});
NodeInfo nodeInfo = injector.getInstance(NodeInfo.class);
HttpServer server = injector.getInstance(HttpServer.class);
assertNotNull(server);
server.start();
try {
HttpServerInfo httpServerInfo = injector.getInstance(HttpServerInfo.class);
assertNotNull(httpServerInfo);
assertNotNull(httpServerInfo.getHttpUri());
assertEquals(httpServerInfo.getHttpUri().getScheme(), "http");
assertEquals(httpServerInfo.getHttpUri().getHost(), nodeInfo.getInternalIp().getHostAddress());
assertNull(httpServerInfo.getHttpsUri());
}
catch (Exception e) {
server.stop();
}
}
@Test
public void testServer()
throws Exception
{
Map<String, String> properties = new ImmutableMap.Builder<String, String>()
.put("http-server.http.port", "0")
.put("http-server.log.path", new File(tempDir, "http-request.log").getAbsolutePath())
.build();
ConfigurationFactory configFactory = new ConfigurationFactory(properties);
Injector injector = Guice.createInjector(new HttpServerModule(),
new ApplicationNameModule("test-application"),
new TestingNodeModule(),
new ConfigurationModule(configFactory),
new NullEventModule(),
new TestingMBeanModule(),
new ReportingModule(),
new Module()
{
@Override
public void configure(Binder binder)
{
binder.bind(Servlet.class).annotatedWith(TheServlet.class).to(DummyServlet.class);
binder.bind(Servlet.class).annotatedWith(TheAdminServlet.class).to(DummyServlet.class);
Multibinder.newSetBinder(binder, Filter.class, TheServlet.class).addBinding().to(DummyFilter.class).in(Scopes.SINGLETON);
httpServerBinder(binder).bindResource("/", "webapp/user").withWelcomeFile("user-welcome.txt");
httpServerBinder(binder).bindResource("/", "webapp/user2");
httpServerBinder(binder).bindResource("path", "webapp/user").withWelcomeFile("user-welcome.txt");
httpServerBinder(binder).bindResource("path", "webapp/user2");
}
});
HttpServerInfo httpServerInfo = injector.getInstance(HttpServerInfo.class);
HttpServer server = injector.getInstance(HttpServer.class);
server.start();
try (HttpClient client = new JettyHttpClient()) {
// test servlet bound correctly
URI httpUri = httpServerInfo.getHttpUri();
StatusResponse response = client.execute(prepareGet().setUri(httpUri).build(), createStatusResponseHandler());
assertEquals(response.getStatusCode(), HttpServletResponse.SC_OK);
// test filter bound correctly
response = client.execute(prepareGet().setUri(httpUri.resolve("/filter")).build(), createStatusResponseHandler());
assertEquals(response.getStatusCode(), HttpServletResponse.SC_PAYMENT_REQUIRED);
assertEquals(response.getStatusMessage(), "filtered");
// test http resources
assertResource(httpUri, client, "", "welcome user!");
assertResource(httpUri, client, "user-welcome.txt", "welcome user!");
assertResource(httpUri, client, "user.txt", "user");
assertResource(httpUri, client, "user2.txt", "user2");
assertResource(httpUri, client, "path", "welcome user!");
assertResource(httpUri, client, "path/", "welcome user!");
assertResource(httpUri, client, "path/user-welcome.txt", "welcome user!");
assertResource(httpUri, client, "path/user.txt", "user");
assertResource(httpUri, client, "path/user2.txt", "user2");
}
finally {
server.stop();
}
}
private void assertResource(URI baseUri, HttpClient client, String path, String contents)
{
HttpUriBuilder uriBuilder = uriBuilderFrom(baseUri);
StringResponse data = client.execute(prepareGet().setUri(uriBuilder.appendPath(path).build()).build(), createStringResponseHandler());
assertEquals(data.getStatusCode(), HttpStatus.OK.code());
MediaType contentType = MediaType.parse(data.getHeader(HttpHeaders.CONTENT_TYPE));
assertTrue(PLAIN_TEXT_UTF_8.is(contentType), "Expected text/plain but got " + contentType);
assertEquals(data.getBody().trim(), contents);
}
private static final class EchoServlet extends HttpServlet
{
private int responseStatusCode = 300;
private final ListMultimap<String, String> responseHeaders = ArrayListMultimap.create();
public String responseBody;
private String remoteAddress;
@Override
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException
{
ByteStreams.copy(request.getInputStream(), ByteStreams.nullOutputStream());
remoteAddress = request.getRemoteAddr();
for (Entry<String, String> entry : responseHeaders.entries()) {
response.addHeader(entry.getKey(), entry.getValue());
}
response.setStatus(responseStatusCode);
if (responseBody != null) {
response.getOutputStream().write(responseBody.getBytes(Charsets.UTF_8));
}
}
}
}