/*
* Copyright 2017 LINE Corporation
*
* LINE Corporation 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 com.linecorp.armeria.testing.server.webapp;
import static com.linecorp.armeria.common.SerializationFormat.NONE;
import static org.assertj.core.api.Assertions.assertThat;
import java.io.File;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.regex.Pattern;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.junit.ClassRule;
import org.junit.Test;
import com.linecorp.armeria.client.ClientFactory;
import com.linecorp.armeria.client.SessionOption;
import com.linecorp.armeria.client.SessionOptions;
import com.linecorp.armeria.client.http.HttpClient;
import com.linecorp.armeria.client.http.HttpClientFactory;
import com.linecorp.armeria.common.http.AggregatedHttpMessage;
import com.linecorp.armeria.server.Server;
import com.linecorp.armeria.server.Service;
import com.linecorp.armeria.testing.server.SelfSignedCertificateRule;
import com.linecorp.armeria.testing.server.ServerRule;
import io.netty.handler.codec.http.HttpHeaderNames;
import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
/**
* Tests a web application container {@link Service}.
*/
public abstract class WebAppContainerTest {
private static final Pattern CR_OR_LF = Pattern.compile("[\\r\\n]");
/**
* The self-signed certificate that is used for testing a TLS connection.
*/
@ClassRule
public static final SelfSignedCertificateRule certificate = new SelfSignedCertificateRule();
/**
* Returns the doc-base directory of the test web application.
*/
public static File webAppRoot() {
URL url = WebAppContainerTest.class.getProtectionDomain().getCodeSource().getLocation();
File f;
try {
f = new File(url.toURI());
} catch (URISyntaxException ignored) {
f = new File(url.getPath());
}
final File buildDir;
if (f.isDirectory()) {
// f is: testing/build/resources/main
buildDir = f.getParentFile().getParentFile();
} else {
// f is: testing/build/libs/armeria-testing-*.jar
assert f.isFile();
buildDir = f.getParentFile().getParentFile();
}
assert buildDir.getPath().endsWith("testing" + File.separatorChar + "build");
final File webAppRoot = new File(
buildDir.getParentFile(), "src" + File.separatorChar + "main" + File.separatorChar + "webapp");
assert webAppRoot.isDirectory();
return webAppRoot;
}
/**
* Returns the {@link ServerRule} that provides the {@link Server} that serves the {@link Service}s this
* test runs against.
*/
protected abstract ServerRule server();
@Test
public void jsp() throws Exception {
try (CloseableHttpClient hc = HttpClients.createMinimal()) {
try (CloseableHttpResponse res = hc.execute(new HttpGet(server().uri("/jsp/index.jsp")))) {
assertThat(res.getStatusLine().toString()).isEqualTo("HTTP/1.1 200 OK");
assertThat(res.getFirstHeader(HttpHeaderNames.CONTENT_TYPE.toString()).getValue())
.startsWith("text/html");
final String actualContent = CR_OR_LF.matcher(EntityUtils.toString(res.getEntity()))
.replaceAll("");
assertThat(actualContent).isEqualTo(
"<html><body>" +
"<p>Hello, Armerian World!</p>" +
"<p>Have you heard about the class 'io.netty.buffer.ByteBuf'?</p>" +
"<p>Context path: </p>" + // ROOT context path
"<p>Request URI: /index.jsp</p>" +
"<p>Scheme: http</p>" +
"</body></html>");
}
}
}
@Test
public void japanesePath() throws Exception {
try (CloseableHttpClient hc = HttpClients.createMinimal()) {
try (CloseableHttpResponse res = hc.execute(new HttpGet(server().uri("/jsp/日本語/index.jsp")))) {
assertThat(res.getStatusLine().toString()).isEqualTo("HTTP/1.1 200 OK");
assertThat(res.getFirstHeader(HttpHeaderNames.CONTENT_TYPE.toString()).getValue())
.startsWith("text/html");
final String actualContent = CR_OR_LF.matcher(EntityUtils.toString(res.getEntity()))
.replaceAll("");
assertThat(actualContent).isEqualTo(
"<html><body>" +
"<p>Hello, Armerian World!</p>" +
"<p>Have you heard about the class 'io.netty.buffer.ByteBuf'?</p>" +
"<p>Context path: </p>" + // ROOT context path
"<p>Request URI: /%E6%97%A5%E6%9C%AC%E8%AA%9E/index.jsp</p>" +
"<p>Servlet Path: /日本語/index.jsp</p>" +
"</body></html>");
}
}
}
@Test
public void https() throws Exception {
ClientFactory clientFactory =
new HttpClientFactory(SessionOptions.of(
SessionOption.TRUST_MANAGER_FACTORY.newValue(InsecureTrustManagerFactory.INSTANCE)));
HttpClient client = clientFactory.newClient(server().httpsUri(NONE, "/"), HttpClient.class);
AggregatedHttpMessage response = client.get("/jsp/index.jsp").aggregate().get();
final String actualContent = CR_OR_LF.matcher(response.content().toStringUtf8())
.replaceAll("");
assertThat(actualContent).isEqualTo(
"<html><body>" +
"<p>Hello, Armerian World!</p>" +
"<p>Have you heard about the class 'io.netty.buffer.ByteBuf'?</p>" +
"<p>Context path: </p>" + // ROOT context path
"<p>Request URI: /index.jsp</p>" +
"<p>Scheme: https</p>" +
"</body></html>");
}
@Test
public void getWithQueryString() throws Exception {
try (CloseableHttpClient hc = HttpClients.createMinimal()) {
try (CloseableHttpResponse res = hc.execute(
new HttpGet(server().uri("/jsp/query_string.jsp?foo=%31&bar=%32")))) {
assertThat(res.getStatusLine().toString()).isEqualTo("HTTP/1.1 200 OK");
assertThat(res.getFirstHeader(HttpHeaderNames.CONTENT_TYPE.toString()).getValue())
.startsWith("text/html");
final String actualContent = CR_OR_LF.matcher(EntityUtils.toString(res.getEntity()))
.replaceAll("");
assertThat(actualContent).isEqualTo(
"<html><body>" +
"<p>foo is 1</p>" +
"<p>bar is 2</p>" +
"</body></html>");
}
}
}
@Test
public void postWithQueryString() throws Exception {
try (CloseableHttpClient hc = HttpClients.createMinimal()) {
final HttpPost post = new HttpPost(server().uri("/jsp/query_string.jsp?foo=3"));
post.setEntity(new UrlEncodedFormEntity(
Collections.singletonList(new BasicNameValuePair("bar", "4")), StandardCharsets.UTF_8));
try (CloseableHttpResponse res = hc.execute(post)) {
assertThat(res.getStatusLine().toString()).isEqualTo("HTTP/1.1 200 OK");
assertThat(res.getFirstHeader(HttpHeaderNames.CONTENT_TYPE.toString()).getValue())
.startsWith("text/html");
final String actualContent = CR_OR_LF.matcher(EntityUtils.toString(res.getEntity()))
.replaceAll("");
assertThat(actualContent).isEqualTo(
"<html><body>" +
"<p>foo is 3</p>" +
"<p>bar is 4</p>" +
"</body></html>");
}
}
}
@Test
public void echoPost() throws Exception {
try (CloseableHttpClient hc = HttpClients.createMinimal()) {
final HttpPost post = new HttpPost(server().uri("/jsp/echo_post.jsp"));
post.setEntity(new StringEntity("test"));
try (CloseableHttpResponse res = hc.execute(post)) {
assertThat(res.getStatusLine().toString()).isEqualTo("HTTP/1.1 200 OK");
assertThat(res.getFirstHeader(HttpHeaderNames.CONTENT_TYPE.toString()).getValue())
.startsWith("text/html");
final String actualContent = CR_OR_LF.matcher(EntityUtils.toString(res.getEntity()))
.replaceAll("");
assertThat(actualContent).isEqualTo(
"<html><body>" +
"<p>Check request body</p>" +
"<p>test</p>" +
"</body></html>");
}
}
}
@Test
public void echoPostWithEmptyBody() throws Exception {
try (CloseableHttpClient hc = HttpClients.createMinimal()) {
final HttpPost post = new HttpPost(server().uri("/jsp/echo_post.jsp"));
try (CloseableHttpResponse res = hc.execute(post)) {
assertThat(res.getStatusLine().toString()).isEqualTo("HTTP/1.1 200 OK");
assertThat(res.getFirstHeader(HttpHeaderNames.CONTENT_TYPE.toString()).getValue())
.startsWith("text/html");
final String actualContent = CR_OR_LF.matcher(EntityUtils.toString(res.getEntity()))
.replaceAll("");
assertThat(actualContent).isEqualTo(
"<html><body>" +
"<p>Check request body</p>" +
"<p></p>" +
"</body></html>");
}
}
}
@Test
public void addressesAndPorts_127001() throws Exception {
try (CloseableHttpClient hc = HttpClients.createMinimal()) {
try (CloseableHttpResponse res = hc.execute(
new HttpGet(server().uri("/jsp/addrs_and_ports.jsp")))) {
assertThat(res.getStatusLine().toString()).isEqualTo("HTTP/1.1 200 OK");
assertThat(res.getFirstHeader(HttpHeaderNames.CONTENT_TYPE.toString()).getValue())
.startsWith("text/html");
final String actualContent = CR_OR_LF.matcher(EntityUtils.toString(res.getEntity()))
.replaceAll("");
assertThat(actualContent).matches(
"<html><body>" +
"<p>RemoteAddr: 127\\.0\\.0\\.1</p>" +
"<p>RemoteHost: 127\\.0\\.0\\.1</p>" +
"<p>RemotePort: [1-9][0-9]+</p>" +
"<p>LocalAddr: (?!null)[^<]+</p>" +
"<p>LocalName: " + server().server().defaultHostname() + "</p>" +
"<p>LocalPort: " + server().httpPort() + "</p>" +
"<p>ServerName: 127\\.0\\.0\\.1</p>" +
"<p>ServerPort: " + server().httpPort() + "</p>" +
"</body></html>");
}
}
}
@Test
public void addressesAndPorts_localhost() throws Exception {
try (CloseableHttpClient hc = HttpClients.createMinimal()) {
HttpGet request = new HttpGet(server().uri("/jsp/addrs_and_ports.jsp"));
request.setHeader("Host", "localhost:1111");
try (CloseableHttpResponse res = hc.execute(request)) {
assertThat(res.getStatusLine().toString()).isEqualTo("HTTP/1.1 200 OK");
assertThat(res.getFirstHeader(HttpHeaderNames.CONTENT_TYPE.toString()).getValue())
.startsWith("text/html");
final String actualContent = CR_OR_LF.matcher(EntityUtils.toString(res.getEntity()))
.replaceAll("");
assertThat(actualContent).matches(
"<html><body>" +
"<p>RemoteAddr: 127\\.0\\.0\\.1</p>" +
"<p>RemoteHost: 127\\.0\\.0\\.1</p>" +
"<p>RemotePort: [1-9][0-9]+</p>" +
"<p>LocalAddr: (?!null)[^<]+</p>" +
"<p>LocalName: " + server().server().defaultHostname() + "</p>" +
"<p>LocalPort: " + server().httpPort() + "</p>" +
"<p>ServerName: localhost</p>" +
"<p>ServerPort: 1111</p>" +
"</body></html>");
}
}
}
}