/*
* 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 org.esigate;
import java.io.*;
import java.net.SocketTimeoutException;
import java.util.Date;
import java.util.HashMap;
import java.util.Properties;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.zip.GZIPOutputStream;
import junit.framework.Assert;
import junit.framework.TestCase;
import org.apache.http.HttpEntity;
import org.apache.http.HttpRequest;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.ProtocolVersion;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.conn.HttpClientConnectionManager;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.InputStreamEntity;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.cookie.BasicClientCookie;
import org.apache.http.message.BasicHeader;
import org.apache.http.message.BasicHttpResponse;
import org.apache.http.util.EntityUtils;
import org.esigate.esi.EsiRenderer;
import org.esigate.events.Event;
import org.esigate.events.EventDefinition;
import org.esigate.events.EventManager;
import org.esigate.events.IEventListener;
import org.esigate.events.impl.FetchEvent;
import org.esigate.extension.DefaultCharset;
import org.esigate.http.DateUtils;
import org.esigate.http.HttpClientRequestExecutor;
import org.esigate.http.HttpResponseUtils;
import org.esigate.http.IncomingRequest;
import org.esigate.tags.BlockRenderer;
import org.esigate.tags.TemplateRenderer;
import org.esigate.test.TestUtils;
import org.esigate.test.conn.IResponseHandler;
import org.esigate.test.conn.MockConnectionManager;
import org.esigate.test.http.HttpResponseBuilder;
import org.esigate.util.UriUtils;
public class DriverTest extends TestCase {
private IncomingRequest.Builder request;
private MockConnectionManager mockConnectionManager;
@Override
protected void setUp() {
mockConnectionManager = new MockConnectionManager();
MockRequestExecutor provider = MockRequestExecutor.createMockDriver("mock");
provider.addResource("/testBlock",
"abc some<!--$beginblock$A$-->some text goes here<!--$endblock$A$--> cdf hello");
provider.addResource("/testTemplateFullPage",
"some <!--$beginparam$key$-->some hidden text goes here<!--$endparam$key$--> printed");
provider.addResource("/testTemplate",
"abc some<!--$begintemplate$A$-->some text goes here<!--$endtemplate$A$--> cdf hello");
request = TestUtils.createIncomingRequest();
}
public void testRenderBlock() throws IOException, HttpErrorPage {
CloseableHttpResponse response =
DriverFactory.getInstance("mock").render("/testBlock", request.build(),
new BlockRenderer("A", "/testBlock"));
assertEquals("some text goes here", HttpResponseUtils.toString(response));
response =
DriverFactory.getInstance("mock").render("$(vartestBlock)", request.build(),
new BlockRenderer("A", "$(vartestBlock)"));
assertEquals("some text goes here", HttpResponseUtils.toString(response));
response =
DriverFactory.getInstance("mock").render("/$(vartest)$(varBlock)", request.build(),
new BlockRenderer("A", "/$(vartest)$(varBlock)"));
assertEquals("some text goes here", HttpResponseUtils.toString(response));
}
public void testRenderTemplateFullPage() throws IOException, HttpErrorPage {
HashMap<String, String> params = new HashMap<>();
params.put("key", "'value'");
params.put("some other key", "'another value'");
CloseableHttpResponse response =
DriverFactory.getInstance("mock").render("/testTemplateFullPage", request.build(),
new TemplateRenderer(null, params, "/testTemplateFullPage"));
String result = HttpResponseUtils.toString(response);
assertFalse(result.contains("key"));
assertTrue(result.contains("'value'"));
assertFalse(result.contains("some other key"));
assertEquals("some 'value' printed", result);
}
public void testRenderTemplate() throws IOException, HttpErrorPage {
IncomingRequest incomingRequest = request.build();
CloseableHttpResponse response =
DriverFactory.getInstance("mock").render("/testTemplate", incomingRequest,
new TemplateRenderer("A", null, "/testTemplate"));
assertEquals("some text goes here", HttpResponseUtils.toString(response));
response =
DriverFactory.getInstance("mock").render("/test$(varTemplate)", incomingRequest,
new TemplateRenderer("A", null, "/test$(varTemplate)"));
assertEquals("some text goes here", HttpResponseUtils.toString(response));
}
public void testHeadersPreservedWhenError500() throws Exception {
Properties properties = new Properties();
properties.put(Parameters.REMOTE_URL_BASE.getName(), "http://localhost");
HttpResponse response =
new BasicHttpResponse(new ProtocolVersion("HTTP", 1, 1), HttpStatus.SC_INTERNAL_SERVER_ERROR,
"Internal Server Error");
response.addHeader("Content-type", "Text/html;Charset=UTF-8");
response.addHeader("Dummy", "dummy");
HttpEntity httpEntity = new StringEntity("Error", "UTF-8");
response.setEntity(httpEntity);
mockConnectionManager.setResponse(response);
Driver driver = createMockDriver(properties, mockConnectionManager);
CloseableHttpResponse driverResponse;
try {
driverResponse = driver.proxy("/", request.build());
fail("We should get an HttpErrorPage");
} catch (HttpErrorPage e) {
driverResponse = e.getHttpResponse();
}
int statusCode = driverResponse.getStatusLine().getStatusCode();
assertEquals("Status code", HttpStatus.SC_INTERNAL_SERVER_ERROR, statusCode);
assertTrue("Header 'Dummy'", driverResponse.containsHeader("Dummy"));
}
public void testHeadersFilteredWhenError500() throws Exception {
Properties properties = new Properties();
properties.put(Parameters.REMOTE_URL_BASE.getName(), "http://localhost");
HttpResponse response =
new BasicHttpResponse(new ProtocolVersion("HTTP", 1, 1), HttpStatus.SC_INTERNAL_SERVER_ERROR,
"Internal Server Error");
response.addHeader("Content-type", "Text/html;Charset=UTF-8");
response.addHeader("Transfer-Encoding", "dummy");
HttpEntity httpEntity = new StringEntity("Error", "UTF-8");
response.setEntity(httpEntity);
mockConnectionManager.setResponse(response);
Driver driver = createMockDriver(properties, mockConnectionManager);
CloseableHttpResponse driverResponse;
try {
driverResponse = driver.proxy("/", request.build());
fail("We should get an HttpErrorPage");
} catch (HttpErrorPage e) {
driverResponse = e.getHttpResponse();
}
int statusCode = driverResponse.getStatusLine().getStatusCode();
assertEquals("Status code", HttpStatus.SC_INTERNAL_SERVER_ERROR, statusCode);
assertFalse("Header 'Transfer-Encoding'", driverResponse.containsHeader("Transfer-Encoding"));
}
public void testSpecialCharacterInErrorPage() throws Exception {
Properties properties = new Properties();
properties.put(Parameters.REMOTE_URL_BASE.getName(), "http://localhost");
HttpResponse response =
new BasicHttpResponse(new ProtocolVersion("HTTP", 1, 1), HttpStatus.SC_INTERNAL_SERVER_ERROR,
"Internal Server Error");
response.addHeader("Content-type", "Text/html;Charset=UTF-8");
HttpEntity httpEntity = new StringEntity("é", "UTF-8");
response.setEntity(httpEntity);
mockConnectionManager.setResponse(response);
Driver driver = createMockDriver(properties, mockConnectionManager);
CloseableHttpResponse driverResponse;
try {
driverResponse = driver.proxy("/", request.build());
fail("We should get an HttpErrorPage");
} catch (HttpErrorPage e) {
driverResponse = e.getHttpResponse();
}
assertEquals("é", HttpResponseUtils.toString(driverResponse));
}
public void testGzipErrorPage() throws Exception {
Properties properties = new Properties();
properties.put(Parameters.REMOTE_URL_BASE.getName(), "http://localhost");
HttpResponse response =
new BasicHttpResponse(new ProtocolVersion("HTTP", 1, 1), HttpStatus.SC_INTERNAL_SERVER_ERROR,
"Internal Server Error");
response.addHeader("Content-type", "Text/html;Charset=UTF-8");
response.addHeader("Content-encoding", "gzip");
ByteArrayOutputStream baos = new ByteArrayOutputStream();
GZIPOutputStream gzos = new GZIPOutputStream(baos);
byte[] uncompressedBytes = "é".getBytes("UTF-8");
gzos.write(uncompressedBytes, 0, uncompressedBytes.length);
gzos.close();
byte[] compressedBytes = baos.toByteArray();
ByteArrayEntity httpEntity = new ByteArrayEntity(compressedBytes);
httpEntity.setContentType("Text/html;Charset=UTF-8");
httpEntity.setContentEncoding("gzip");
response.setEntity(httpEntity);
mockConnectionManager.setResponse(response);
Driver driver = createMockDriver(properties, mockConnectionManager);
CloseableHttpResponse driverResponse;
try {
driverResponse = driver.proxy("/", request.build());
fail("We should get an HttpErrorPage");
} catch (HttpErrorPage e) {
driverResponse = e.getHttpResponse();
}
assertEquals("é", HttpResponseUtils.toString(driverResponse));
}
private Driver createMockDriver(Properties properties, HttpClientConnectionManager connectionManager) {
return createMockDriver(properties, connectionManager, "tested");
}
private Driver createMockDriver(Properties properties, HttpClientConnectionManager connectionManager, String name) {
Driver driver =
Driver.builder()
.setName(name)
.setProperties(properties)
.setRequestExecutorBuilder(
HttpClientRequestExecutor.builder().setConnectionManager(connectionManager)).build();
DriverFactory.put(name, driver);
return driver;
}
public void testRewriteRedirectResponse() throws Exception {
Properties properties = new Properties();
properties.put(Parameters.REMOTE_URL_BASE.getName(), "http://www.foo.com:8080/");
properties.put(Parameters.PRESERVE_HOST, "false");
request = TestUtils.createIncomingRequest("http://www.bar.com/foo/");
HttpResponse response =
new BasicHttpResponse(new ProtocolVersion("HTTP", 1, 1), HttpStatus.SC_MOVED_TEMPORARILY, "Found");
response.addHeader("Location", "http://www.foo.com:8080/somewhere/");
mockConnectionManager.setResponse(response);
Driver driver = createMockDriver(properties, mockConnectionManager);
CloseableHttpResponse driverResponse = driver.proxy("/foo/", request.build());
assertEquals("http://www.bar.com/somewhere/", driverResponse.getFirstHeader("Location").getValue());
}
/**
* 0000174: Redirect location with default port specified are incorrectly rewritten when preserveHost=true
* <p>
* http://www.esigate.org/mantisbt/view.php?id=174
*
* <p>
* Issue with default ports, which results in invalid url creation.
*
* @throws Exception
*/
public void testRewriteRedirectResponseWithDefaultPortSpecifiedInLocation() throws Exception {
Properties properties = new Properties();
properties.put(Parameters.REMOTE_URL_BASE, "http://www.foo.com:8080");
properties.put(Parameters.PRESERVE_HOST, "true");
HttpResponse response =
new BasicHttpResponse(new ProtocolVersion("HTTP", 1, 1), HttpStatus.SC_MOVED_TEMPORARILY, "Found");
// The backend server sets the port even if default (OK it should not
// but some servers do it)
response.addHeader("Location", "http://www.foo.com:80/foo/bar");
mockConnectionManager.setResponse(response);
Driver driver = createMockDriver(properties, mockConnectionManager);
request = TestUtils.createIncomingRequest("http://www.foo.com:80/foo");
// HttpClientHelper will use the Host
// header to rewrite the request sent to the backend
// http://www.foo.com/foo
CloseableHttpResponse driverResponse = driver.proxy("/foo", request.build());
// The test initially failed with an invalid Location:
// http://www.foo.com:80:80/foo/bar
assertEquals("http://www.foo.com:80/foo/bar", driverResponse.getFirstHeader("Location").getValue());
}
/**
* Ensure default ports are not added by esigate.
*
* @throws Exception
*/
public void testRewriteRedirectResponseWithLocation() throws Exception {
Properties properties = new Properties();
properties.put(Parameters.REMOTE_URL_BASE, "http://127.0.0.1");
properties.put(Parameters.PRESERVE_HOST, "true");
mockConnectionManager.setResponseHandler(new IResponseHandler() {
@Override
public HttpResponse execute(final HttpRequest httpRequest) throws IOException {
if (!httpRequest.getLastHeader("Host").getValue().equals("www.foo.com")) {
throw new IllegalArgumentException("Host must be www.foo.com");
}
return new HttpResponseBuilder().status(HttpStatus.SC_MOVED_TEMPORARILY).entity("Found")
.header("Location", "http://www.foo.com").build();
}
});
Driver driver = createMockDriver(properties, mockConnectionManager);
IncomingRequest request1 = TestUtils.createIncomingRequest("http://www.foo.com:80").build();
assertEquals("www.foo.com", request1.getLastHeader("Host").getValue());
CloseableHttpResponse driverResponse = driver.proxy("", request1);
assertEquals("http://www.foo.com", driverResponse.getFirstHeader("Location").getValue());
}
/**
* Test for "Consumes http entity on redirection" https://github.com/esigate/esigate/pull/154 (no cache)
*
* @throws IOException
* @throws HttpErrorPage
*/
public void testRedirectDoesNotLeakConnection() throws IOException, HttpErrorPage {
Properties properties = new Properties();
properties.put(Parameters.REMOTE_URL_BASE, "http://127.0.0.1");
properties.put(Parameters.PRESERVE_HOST, "true");
properties.put(Parameters.USE_CACHE, "false");
final AtomicInteger closeCount = new AtomicInteger();
mockConnectionManager.setResponseHandler(new IResponseHandler() {
@Override
public HttpResponse execute(final HttpRequest httpRequest) throws IOException {
switch (UriUtils.getPath(httpRequest.getRequestLine().getUri())) {
case "/redirect":
InputStream in = new ByteArrayInputStream("found".getBytes()) {
@Override
public void close() throws IOException {
closeCount.incrementAndGet();
}
};
return new HttpResponseBuilder().status(HttpStatus.SC_MOVED_TEMPORARILY)
.entity(new InputStreamEntity(in)).header("Location", "http://www.foo.com/content").build();
case "/content":
return new HttpResponseBuilder().status(HttpStatus.SC_OK).entity("some content").build();
default:
return new HttpResponseBuilder().status(HttpStatus.SC_OK)
.entity("<html><body><esi:include src='$(PROVIDER{tested})/redirect' /></body></html>")
.header("Content-Type", "text/html").build();
}
}
});
Driver driver = createMockDriver(properties, mockConnectionManager);
int requestCount = 100;
for (int i = 0; i < requestCount; i++) {
IncomingRequest request1 = TestUtils.createIncomingRequest("http://www.foo.com/page").build();
CloseableHttpResponse driverResponse = driver.proxy("/page", request1);
assertEquals("<html><body>some content</body></html>", EntityUtils.toString(driverResponse.getEntity()));
}
assertEquals(requestCount, closeCount.get());
assertFalse("All the connections should have been closed", mockConnectionManager.isOpen());
}
/**
* Test for "Consumes http entity on redirection" https://github.com/esigate/esigate/pull/154 (with cache actived)
*
* @throws IOException
* @throws HttpErrorPage
*/
public void testRedirectDoesNotLeakConnectionWithCacheEnable() throws IOException, HttpErrorPage {
Properties properties = new Properties();
properties.put(Parameters.REMOTE_URL_BASE, "http://127.0.0.1");
properties.put(Parameters.PRESERVE_HOST, "true");
final AtomicInteger closeCount = new AtomicInteger();
mockConnectionManager.setResponseHandler(new IResponseHandler() {
@Override
public HttpResponse execute(final HttpRequest httpRequest) throws IOException {
switch (UriUtils.getPath(httpRequest.getRequestLine().getUri())) {
case "/redirect":
InputStream in = new ByteArrayInputStream("found".getBytes()) {
@Override
public void close() throws IOException {
closeCount.incrementAndGet();
}
};
return new HttpResponseBuilder().status(HttpStatus.SC_MOVED_TEMPORARILY)
.entity(new InputStreamEntity(in)).header("Location", "http://www.foo.com/content").build();
case "/content":
return new HttpResponseBuilder().status(HttpStatus.SC_OK).entity("some content").build();
default:
return new HttpResponseBuilder().status(HttpStatus.SC_OK)
.entity("<html><body><esi:include src='$(PROVIDER{tested})/redirect' /></body></html>")
.header("Content-Type", "text/html").build();
}
}
});
Driver driver = createMockDriver(properties, mockConnectionManager);
int requestCount = 100;
for (int i = 0; i < requestCount; i++) {
IncomingRequest request1 = TestUtils.createIncomingRequest("http://www.foo.com/page").build();
CloseableHttpResponse driverResponse = driver.proxy("/page", request1);
assertEquals("<html><body>some content</body></html>", EntityUtils.toString(driverResponse.getEntity()));
}
assertEquals(requestCount, closeCount.get());
assertFalse("All the connections should have been closed", mockConnectionManager.isOpen());
}
/**
* <p>
* Test bug Bug 142 (1st case).
* <p>
* 0000142: Error with 304 backend responses
* <p>
* NPE in Apache HTTP CLient cache
* <p>
* See :http://www.esigate.org/mantisbt/view.php?id=142
*
* @throws Exception
*/
public void testBug142() throws Exception {
Properties properties = new Properties();
properties.put(Parameters.REMOTE_URL_BASE.getName(), "http://www.foo.com/");
properties.put(Parameters.TTL.getName(), "43200");
properties.put(Parameters.PRESERVE_HOST.getName(), "true");
properties.put(Parameters.USE_CACHE.getName(), true);
HttpResponse response =
new BasicHttpResponse(new ProtocolVersion("HTTP", 1, 1), HttpStatus.SC_NOT_MODIFIED, "Not Modified");
response.addHeader("Etag", "b5e3f57c0e84fc7a197b849fdfd3d407");
response.addHeader("Date", "Thu, 13 Dec 2012 07:28:01 GMT");
mockConnectionManager.setResponse(response);
Driver driver = createMockDriver(properties, mockConnectionManager);
// First request
request = TestUtils.createIncomingRequest("http://www.bar.com/foo/");
request.addHeader("If-None-Match", "b5e3f57c0e84fc7a197b849fdfd3d407");
request.addHeader("Accept", "text/html,application/xhtml+xml,application/xml");
request.addHeader("If-Modified-Since", "Fri, 15 Jun 2012 21:06:25 GMT");
request.addHeader("Cache-Control", "max-age=0");
CloseableHttpResponse driverResponse = driver.proxy("/foo/", request.build());
assertEquals(HttpStatus.SC_NOT_MODIFIED, driverResponse.getStatusLine().getStatusCode());
// Second request
request = TestUtils.createIncomingRequest("http://www.bar.com/foo/");
request.addHeader("If-None-Match", "b5e3f57c0e84fc7a197b849fdfd3d407");
request.addHeader("Accept", "text/html,application/xhtml+xml,application/xml");
request.addHeader("If-Modified-Since", "Fri, 15 Jun 2012 21:06:25 GMT");
request.addHeader("Cache-Control", "max-age=0");
driverResponse = driver.proxy("/foo/", request.build());
assertEquals(HttpStatus.SC_NOT_MODIFIED, driverResponse.getStatusLine().getStatusCode());
}
/**
* <p>
* Test bug Bug 142 (2nd case).
* <p>
* 0000142: Error with 304 backend responses
* <p>
* NPE in Apache HTTP CLient cache
* <p>
* See http://www.esigate.org/mantisbt/view.php?id=142
*
* @throws Exception
*/
public void testBug142SecondCase() throws Exception {
Properties properties = new Properties();
properties.put(Parameters.REMOTE_URL_BASE.getName(), "http://www.foo.com/");
properties.put(Parameters.TTL.getName(), "43200");
HttpResponse response =
new BasicHttpResponse(new ProtocolVersion("HTTP", 1, 1), HttpStatus.SC_NOT_MODIFIED, "Not Modified");
response.addHeader("Etag", "a86ecd6cc6d361776ed05f063921aa34");
response.addHeader("Date", "Thu, 13 Dec 2012 08:55:37 GMT");
response.addHeader("Cache-Control", "max-age=2051, public");
response.addHeader("Expires", "Thu, 13 Dec 2012 09:29:48 GMT");
response.addHeader("Vary", "Accept-Encoding");
mockConnectionManager.setResponse(response);
Driver driver = createMockDriver(properties, mockConnectionManager);
// First request
request = TestUtils.createIncomingRequest("http://www.bar142-2.com/foobar142-2/");
request.addHeader("If-None-Match", "a86ecd6cc6d361776ed05f063921aa34");
CloseableHttpResponse driverResponse = driver.proxy("/foobar142-2/", request.build());
assertEquals(HttpStatus.SC_NOT_MODIFIED, driverResponse.getStatusLine().getStatusCode());
}
/**
* <p>
* Test bug Bug 155
* <p>
* 0000155: 304 returned when not using If-XXX headers
* <p>
* When requesting a full response, a full response must be sent even if the cache has cached a 304 response.
* <p>
* See http://www.esigate.org/mantisbt/view.php?id=155
*
* @throws Exception
*/
public void testBug155() throws Exception {
Properties properties = new Properties();
properties.put(Parameters.REMOTE_URL_BASE.getName(), "http://www.foo.com/");
properties.put(Parameters.TTL.getName(), "43200");
properties.put(Parameters.PRESERVE_HOST.getName(), "true");
properties.put(Parameters.USE_CACHE.getName(), true);
HttpResponse response =
new BasicHttpResponse(new ProtocolVersion("HTTP", 1, 1), HttpStatus.SC_NOT_MODIFIED, "Not Modified");
response.addHeader("Etag", "a86ecd6cc6d361776ed05f063921aa34");
response.addHeader("Date", "Thu, 13 Dec 2012 08:55:37 GMT");
response.addHeader("Cache-Control", "max-age=2051, public, must-revalidate, proxy-revalidate");
response.addHeader("Expires", "Thu, 13 Dec 2012 09:29:48 GMT");
response.addHeader("Set-Cookie",
"w3tc_referrer=http%3A%2F%2Fblog.richeton.com%2Fcategory%2Fcomputer%2Fwat%2F; path=/");
mockConnectionManager.setResponse(response);
Driver driver = createMockDriver(properties, mockConnectionManager);
// First request
request = TestUtils.createIncomingRequest("http://www.bar142-2.com/foobar142-2/");
request.addHeader("If-None-Match", "a86ecd6cc6d361776ed05f063921aa34");
request.addHeader("Accept", "text/html,application/xhtml+xml,application/xml");
request.addHeader("Cache-Control", "max-age=0");
request.addHeader("Accept-Encoding", "gzip, deflate");
CloseableHttpResponse driverResponse = driver.proxy("/foobar142-2/", request.build());
assertEquals(HttpStatus.SC_NOT_MODIFIED, driverResponse.getStatusLine().getStatusCode());
response = new BasicHttpResponse(new ProtocolVersion("HTTP", 1, 1), HttpStatus.SC_OK, "Ok");
response.addHeader("Etag", "a86ecd6cc6d361776ed05f063921aa34");
response.addHeader("Date", "Thu, 13 Dec 2012 08:55:37 GMT");
response.addHeader("Cache-Control", "max-age=2051, public, must-revalidate, proxy-revalidate");
response.addHeader("Expires", "Thu, 13 Dec 2012 09:29:48 GMT");
response.addHeader("Set-Cookie",
"w3tc_referrer=http%3A%2F%2Fblog.richeton.com%2Fcategory%2Fcomputer%2Fwat%2F; path=/");
response.setEntity(new StringEntity("test"));
mockConnectionManager.setResponse(response);
// First request
request = TestUtils.createIncomingRequest("http://www.bar142-2.com/foobar142-2/");
request.addHeader("Accept", "text/html,application/xhtml+xml,application/xml");
request.addHeader("Cache-Control", "max-age=0");
request.addHeader("Accept-Encoding", "gzip, deflate");
driverResponse = driver.proxy("/foobar142-2/", request.build());
assertEquals(HttpStatus.SC_OK, driverResponse.getStatusLine().getStatusCode());
assertNotNull(driverResponse.getEntity());
}
/**
* 0000162: Cookie forwarding (Browser->Server) does not work with preserveHost
* <p>
* This test is to ensure behavior with preserve host off (already working as of bug 162).
*
* @throws Exception
* @see <a href="http://www.esigate.org/mantisbt/view.php?id=162">0000162</a>
*/
public void testBug162PreserveHostOff() throws Exception {
Properties properties = new Properties();
properties.put(Parameters.REMOTE_URL_BASE.getName(), "http://localhost.mydomain.fr/");
properties.put(Parameters.PRESERVE_HOST.getName(), "false");
mockConnectionManager = new MockConnectionManager() {
@Override
public HttpResponse execute(HttpRequest httpRequest) {
Assert.assertEquals("localhost.mydomain.fr", httpRequest.getFirstHeader("Host").getValue());
Assert.assertTrue("Cookie must be forwarded", httpRequest.containsHeader("Cookie"));
return new BasicHttpResponse(new ProtocolVersion("HTTP", 1, 1), HttpStatus.SC_OK, "OK");
}
};
Driver driver = createMockDriver(properties, mockConnectionManager);
request =
TestUtils.createIncomingRequest("http://test.mydomain.fr/foobar/").addCookie(
new BasicClientCookie("TEST_cookie", "233445436436346"));
driver.proxy("/foobar/", request.build());
}
/**
* 0000162: Cookie forwarding (Browser->Server) does not work with preserveHost
* <p>
* This test ensure cookies are forwarded with preserveHost. Specific code is Cookie preparation in
* DefaultCookieManager#rewriteForServer().
* <p>
* Warning: HttpClient is not using Host header to validate cookies.
*
* @throws Exception
* @see <a href="http://www.esigate.org/mantisbt/view.php?id=162">0000162</a>
*/
public void testBug162PreserveHostOn() throws Exception {
Properties properties = new Properties();
properties.put(Parameters.REMOTE_URL_BASE.getName(), "http://localhost.mydomain.fr/");
properties.put(Parameters.PRESERVE_HOST.getName(), "true");
mockConnectionManager = new MockConnectionManager() {
@Override
public HttpResponse execute(HttpRequest httpRequest) {
Assert.assertEquals("test.mydomain.fr", httpRequest.getFirstHeader("Host").getValue());
Assert.assertTrue("Cookie must be forwarded", httpRequest.containsHeader("Cookie"));
return new BasicHttpResponse(new ProtocolVersion("HTTP", 1, 1), HttpStatus.SC_OK, "OK");
}
};
Driver driver = createMockDriver(properties, mockConnectionManager);
BasicClientCookie cookie = new BasicClientCookie("TEST_cookie", "233445436436346");
request = TestUtils.createIncomingRequest("http://test.mydomain.fr/foobar/").addCookie(cookie);
driver.proxy("/foobar/", request.build());
// same test without forcing the host header
request = TestUtils.createIncomingRequest("http://test.mydomain.fr/foobar/").addCookie(cookie);
}
/**
* This test ensure Fetch events are fired when cache is disabled.
* <p>
* It uses {@link DefaultCharset} extension which processes the Contet-Type header on post-fetch events.
*
* @throws Exception
*/
public void testBug185() throws Exception {
Properties properties = new Properties();
properties.put(Parameters.REMOTE_URL_BASE.getName(), "http://www.foo.com/");
properties.put(Parameters.EXTENSIONS.getName(), DefaultCharset.class.getName());
properties.put(Parameters.USE_CACHE.getName(), "false");
HttpResponse response =
new BasicHttpResponse(new ProtocolVersion("HTTP", 1, 1), HttpStatus.SC_OK, "Not Modified");
response.addHeader("Content-Type", "text/html");
mockConnectionManager.setResponse(response);
Driver driver = createMockDriver(properties, mockConnectionManager);
// Request
request = TestUtils.createIncomingRequest("http://www.bar142-2.com/foobar142-2/");
CloseableHttpResponse driverResponse = driver.proxy("/foobar142-2/", request.build());
assertEquals(HttpStatus.SC_OK, driverResponse.getStatusLine().getStatusCode());
assertEquals("text/html; charset=ISO-8859-1", driverResponse.getHeaders("Content-Type")[0].getValue());
// Same test with cache enabled
properties.put(Parameters.USE_CACHE.getName(), "true");
response = new BasicHttpResponse(new ProtocolVersion("HTTP", 1, 1), HttpStatus.SC_OK, "Not Modified");
response.addHeader("Content-Type", "text/html");
mockConnectionManager.setResponse(response);
driver = createMockDriver(properties, mockConnectionManager);
// Request
request = TestUtils.createIncomingRequest("http://www.bar142-2.com/foobar142-2/");
driverResponse = driver.proxy("/foobar142-2/", request.build());
assertEquals(HttpStatus.SC_OK, driverResponse.getStatusLine().getStatusCode());
assertEquals("text/html; charset=ISO-8859-1", driverResponse.getHeaders("Content-Type")[0].getValue());
}
/**
* 0000135: Special characters are lost when including a fragment with no charset specified into UTF-8 page.
*
* @throws Exception
* @see <a href="http://www.esigate.org/mantisbt/view.php?id=135">0000135</a>
*/
public void testSpecialCharacterInIncludeNoCharset() throws Exception {
String now = DateUtils.formatDate(new Date());
// Create master application
Properties properties = new Properties();
properties.put(Parameters.REMOTE_URL_BASE.getName(), "http://localhost");
HttpResponse response = new BasicHttpResponse(new ProtocolVersion("HTTP", 1, 1), HttpStatus.SC_OK, "Ok");
response.addHeader("Date", now);
response.addHeader("Content-type", "Text/html;Charset=UTF-8");
HttpEntity httpEntity = new StringEntity("à<esi:include src=\"$(PROVIDER{provider})/\"/>à", "UTF-8");
response.setEntity(httpEntity);
mockConnectionManager.setResponse(response);
Driver driver = createMockDriver(properties, mockConnectionManager);
// Create provider application
properties = new Properties();
properties.put(Parameters.REMOTE_URL_BASE.getName(), "http://localhost");
mockConnectionManager = new MockConnectionManager();
response = new BasicHttpResponse(new ProtocolVersion("HTTP", 1, 1), HttpStatus.SC_OK, "Ok");
response.addHeader("Date", now);
response.addHeader("Content-type", "text/html");
httpEntity = new ByteArrayEntity("é".getBytes("ISO-8859-1"), ContentType.create("text/html", (String) null));
response.setEntity(httpEntity);
mockConnectionManager.setResponse(response);
createMockDriver(properties, mockConnectionManager, "provider");
// Do the include and check the result
CloseableHttpResponse driverResponse = driver.proxy("/", request.build(), new EsiRenderer());
assertEquals("àéà", HttpResponseUtils.toString(driverResponse));
}
/**
* 0000161: Cookie domain validation too strict with preserveHost.
*
* @see <a href="http://www.esigate.org/mantisbt/view.php?id=161">0000161</a>
*
* @throws Exception
*/
public void testBug161SetCookie() throws Exception {
Properties properties = new Properties();
properties.put(Parameters.REMOTE_URL_BASE.getName(), "http://localhost/");
properties.put(Parameters.PRESERVE_HOST.getName(), "true");
BasicHttpResponse response = new BasicHttpResponse(new ProtocolVersion("HTTP", 1, 1), HttpStatus.SC_OK, "Ok");
response.addHeader("Date", "Thu, 13 Dec 2012 08:55:37 GMT");
response.addHeader("Set-Cookie", "mycookie=123456; domain=.mydomain.fr; path=/");
response.setEntity(new StringEntity("test"));
mockConnectionManager.setResponse(response);
Driver driver = createMockDriver(properties, mockConnectionManager);
request = TestUtils.createIncomingRequest("http://test.mydomain.fr/foobar/");
IncomingRequest incomingRequest = request.build();
driver.proxy("/foobar/", incomingRequest);
assertTrue("Set-Cookie must be forwarded.", incomingRequest.getNewCookies().length > 0);
}
/**
* 0000154: Warn on staleWhileRevalidate configuration issue. http://www.esigate.org/mantisbt/view.php?id=154
*/
public void testConfigStaleWhileRevalidateWith0WorkerThreadsThrowsConfigurationException() {
Properties properties = new Properties();
properties.put(Parameters.REMOTE_URL_BASE, "http://localhost/");
properties.put(Parameters.STALE_WHILE_REVALIDATE, "600");
try {
createMockDriver(properties, mockConnectionManager);
fail("We should have had a ConfigurationException");
} catch (ConfigurationException e) {
// This is exactly what we want
}
}
/**
* 0000141: Socket read timeout causes a stacktrace and may leak connection
* http://www.esigate.org/mantisbt/view.php?id=141
*
* The warning will not be fixed in HttpClient but the leak is fixed.
*
* @throws Exception
*/
public void testSocketReadTimeoutWithCacheAndGzipDoesNotLeak() throws Exception {
Properties properties = new Properties();
properties.put(Parameters.REMOTE_URL_BASE, "http://localhost/");
properties.put(Parameters.USE_CACHE, "true");
BasicHttpResponse response = new BasicHttpResponse(new ProtocolVersion("HTTP", 1, 1), HttpStatus.SC_OK, "Ok");
response.addHeader("Date", DateUtils.formatDate(new Date()));
response.addHeader("Cache-control", "public, max-age=1000");
response.addHeader("Content-Encoding", "gzip");
response.setEntity(new InputStreamEntity(new InputStream() {
@Override
public int read() throws IOException {
throw new SocketTimeoutException("Read timed out");
}
}, 1000));
mockConnectionManager.setResponse(response);
Driver driver = createMockDriver(properties, mockConnectionManager);
request = TestUtils.createIncomingRequest("http://test.mydomain.fr/");
request.addHeader("Accept-Encoding", "gzip, deflate");
try {
driver.proxy("/", request.build());
fail("We should have had a SocketTimeoutException");
} catch (HttpErrorPage e) {
// That is what we expect
assertEquals(HttpStatus.SC_GATEWAY_TIMEOUT, e.getHttpResponse().getStatusLine().getStatusCode());
}
assertFalse("All the connections should have been closed", mockConnectionManager.isOpen());
}
public void testForwardCookiesWithPortsAndPreserveHost() throws Exception {
Properties properties = new Properties();
properties.put(Parameters.REMOTE_URL_BASE.getName(), "http://localhost:8080/");
properties.put(Parameters.PRESERVE_HOST.getName(), "true");
mockConnectionManager = new MockConnectionManager() {
@Override
public HttpResponse execute(HttpRequest httpRequest) {
Assert.assertNotNull("Cookie should be forwarded", httpRequest.getFirstHeader("Cookie"));
Assert.assertEquals("JSESSIONID=926E1C6A52804A625DFB0139962D4E13", httpRequest.getFirstHeader("Cookie")
.getValue());
return new BasicHttpResponse(new ProtocolVersion("HTTP", 1, 1), HttpStatus.SC_OK, "OK");
}
};
Driver driver = createMockDriver(properties, mockConnectionManager);
BasicClientCookie cookie = new BasicClientCookie("_JSESSIONID", "926E1C6A52804A625DFB0139962D4E13");
request = TestUtils.createIncomingRequest("http://127.0.0.1:8081/foobar.jsp").addCookie(cookie);
driver.proxy("/foobar.jsp", request.build());
}
public void testForwardCookiesWithPorts() throws Exception {
Properties properties = new Properties();
properties.put(Parameters.REMOTE_URL_BASE.getName(), "http://localhost:8080/");
properties.put(Parameters.PRESERVE_HOST.getName(), "false");
mockConnectionManager = new MockConnectionManager() {
@Override
public HttpResponse execute(HttpRequest httpRequest) {
Assert.assertNotNull(httpRequest.getFirstHeader("Cookie"));
Assert.assertEquals("JSESSIONID=926E1C6A52804A625DFB0139962D4E13", httpRequest.getFirstHeader("Cookie")
.getValue());
return new BasicHttpResponse(new ProtocolVersion("HTTP", 1, 1), HttpStatus.SC_OK, "OK");
}
};
Driver driver = createMockDriver(properties, mockConnectionManager);
BasicClientCookie cookie = new BasicClientCookie("_JSESSIONID", "926E1C6A52804A625DFB0139962D4E13");
request = TestUtils.createIncomingRequest("http://127.0.0.1:8081/foobar.jsp").addCookie(cookie);
driver.proxy("/foobar.jsp", request.build());
}
/**
* Cookies from external URLs calls should also be forwarded.
*
* @see <a href="http://www.esigate.org/mantisbt/view.php?id=255">0000255: forwardCookies does not work with
* esi:include if not using a provider variable</a>
* @throws Exception
*/
public void testForwardCookiesExternalUrl() throws Exception {
Properties properties = new Properties();
properties.put(Parameters.REMOTE_URL_BASE, "http://localhost:8080/");
properties.put(Parameters.PRESERVE_HOST, "true");
mockConnectionManager = new MockConnectionManager() {
@Override
public HttpResponse execute(HttpRequest httpRequest) {
BasicHttpResponse response =
new BasicHttpResponse(new ProtocolVersion("HTTP", 1, 1), HttpStatus.SC_OK, "OK");
response.addHeader(new BasicHeader("Set-Cookie", "name1=value1;domain=www.external.server"));
return response;
}
};
Driver driver = createMockDriver(properties, mockConnectionManager);
request = TestUtils.createIncomingRequest("http://localhost:8080/foo/foobar.jsp");
IncomingRequest incomingRequest = request.build();
driver.proxy("http://www.external.server/foo/foobar.jsp", incomingRequest);
Assert.assertEquals(1, incomingRequest.getNewCookies().length);
request = TestUtils.createIncomingRequest("http://localhost:8080/foo/foobar.jsp");
incomingRequest = request.build();
driver.proxy("http://anotherdomain.external.server/foo/foobar.jsp", incomingRequest);
Assert.assertEquals(0, incomingRequest.getNewCookies().length);
}
public void testRewriteCookiePath() throws Exception {
Properties properties = new Properties();
properties.put(Parameters.REMOTE_URL_BASE.getName(), "http://localhost:8080/");
properties.put(Parameters.PRESERVE_HOST.getName(), "true");
mockConnectionManager = new MockConnectionManager() {
@Override
public HttpResponse execute(HttpRequest httpRequest) {
BasicHttpResponse response =
new BasicHttpResponse(new ProtocolVersion("HTTP", 1, 1), HttpStatus.SC_OK, "OK");
response.addHeader(new BasicHeader("Set-Cookie", "name1=value1;Path=/foo"));
return response;
}
};
Driver driver = createMockDriver(properties, mockConnectionManager);
request = TestUtils.createIncomingRequest("http://localhost:8081/foo/foobar.jsp");
IncomingRequest incomingRequest = request.build();
driver.proxy("/foo/foobar.jsp", incomingRequest);
Assert.assertEquals(1, incomingRequest.getNewCookies().length);
Assert.assertEquals("/foo", incomingRequest.getNewCookies()[0].getPath());
}
public void testRewriteCookiePathNotMatching() throws Exception {
Properties properties = new Properties();
properties.put(Parameters.REMOTE_URL_BASE.getName(), "http://localhost:8080/");
properties.put(Parameters.PRESERVE_HOST.getName(), "true");
mockConnectionManager = new MockConnectionManager() {
@Override
public HttpResponse execute(HttpRequest httpRequest) {
BasicHttpResponse response =
new BasicHttpResponse(new ProtocolVersion("HTTP", 1, 1), HttpStatus.SC_OK, "OK");
response.addHeader(new BasicHeader("Set-Cookie", "name1=value1;Path=/bar"));
return response;
}
};
Driver driver = createMockDriver(properties, mockConnectionManager);
request = TestUtils.createIncomingRequest("http://localhost:8081/foo/foobar.jsp");
IncomingRequest incomingRequest = request.build();
driver.proxy("/bar/foobar.jsp", incomingRequest);
Assert.assertEquals(1, incomingRequest.getNewCookies().length);
Assert.assertEquals("/", incomingRequest.getNewCookies()[0].getPath());
}
/**
* 0000231: ESIgate should be enable to mashup elements for an error/404 page.
*
* @see "http://www.esigate.org/mantisbt/view.php?id=231"
*
* @throws Exception
*/
public void testBug231RenderingOnProxyError() throws Exception {
// Configuration
Properties properties = new Properties();
properties.put(Parameters.REMOTE_URL_BASE.getName(), "http://localhost.mydomain.fr/");
properties.put(Parameters.PRESERVE_HOST.getName(), "true");
// Setup server responses.
mockConnectionManager = new MockConnectionManager() {
@Override
public HttpResponse execute(HttpRequest httpRequest) {
// The main page
if (httpRequest.getRequestLine().getUri().equals("/foobar/")) {
return new HttpResponseBuilder()
.entity(new StringEntity("<esi:include src=\"http://test.mydomain.fr/esi/\"/>",
ContentType.TEXT_HTML)).status(HttpStatus.SC_BAD_REQUEST).build();
}
// The ESI fragment
if (httpRequest.getRequestLine().getUri().equals("/esi/")) {
try {
return new HttpResponseBuilder().entity("OK").build();
} catch (UnsupportedEncodingException e) {
Assert.fail("Unexpected exception" + e.getMessage());
}
}
// Other unexpected request ? -> Fail
Assert.fail("Unexpected request " + httpRequest.getRequestLine().getUri());
return null;
}
};
// Build driver and request.
Driver driver = createMockDriver(properties, mockConnectionManager);
request = IncomingRequest.builder("http://test.mydomain.fr/foobar/");
try {
// Perform call with ESI rendering.
driver.proxy("/foobar/", request.build(), new EsiRenderer());
fail("HttpErrorPage expected");
} catch (HttpErrorPage errorPage) {
// Ensure request was esi-processed.
HttpResponse response = errorPage.getHttpResponse();
Assert.assertEquals("OK", EntityUtils.toString(response.getEntity()));
}
}
/**
* FetchEvent#getHttpResponse().getStatusLine().getUri() is not the same as
* FetchEvent#getHttpRequest().getOriginal().getRequestLine().getUri() #23
*
* @see <a href="https://github.com/esigate/esigate/issues/23">https://github.com/esigate/esigate/issues/23</a>
*
* @throws Exception
*/
public void testPreserveHostInUriInOutgoingRequest() throws Exception {
Properties properties = new Properties();
properties.put(Parameters.REMOTE_URL_BASE.getName(), "http://localhost");
properties.put(Parameters.PRESERVE_HOST, "true");
HttpResponse response = TestUtils.createHttpResponse().entity("test").build();
mockConnectionManager.setResponse(response);
Driver driver = createMockDriver(properties, mockConnectionManager);
IEventListener eventListener = new IEventListener() {
@Override
public boolean event(EventDefinition id, Event event) {
FetchEvent fetchEvent = (FetchEvent) event;
// uri of the incoming request
assertEquals("http://foo.com/test", fetchEvent.getHttpRequest().getOriginal().getRequestLine().getUri());
// uri of the outgoing request
// FIXME
// assertEquals("http://foo.com/test", fetchEvent.getHttpRequest().getRequestLine().getUri());
return false;
}
};
driver.getEventManager().register(EventManager.EVENT_FETCH_PRE, eventListener);
driver.getEventManager().register(EventManager.EVENT_FETCH_POST, eventListener);
IncomingRequest incomingRequest = TestUtils.createIncomingRequest("http://foo.com/test").build();
driver.proxy("/test", incomingRequest);
}
}