Issue 52: warn when host header is used

git-svn-id: http://jclouds.googlecode.com/svn/trunk@1083 3d8758e0-26b5-11de-8745-db77d3ebf521
This commit is contained in:
adrian.f.cole 2009-06-07 11:06:29 +00:00
parent 9e9d8ecd1c
commit 0c9b3accda
2 changed files with 233 additions and 224 deletions

View File

@ -24,135 +24,150 @@
package org.jclouds.gae; package org.jclouds.gae;
import static com.google.appengine.api.urlfetch.FetchOptions.Builder.disallowTruncate; import static com.google.appengine.api.urlfetch.FetchOptions.Builder.disallowTruncate;
import com.google.appengine.api.urlfetch.*;
import com.google.common.annotations.VisibleForTesting;
import com.google.inject.Inject;
import org.apache.commons.io.IOUtils;
import org.jclouds.http.*;
import org.jclouds.http.internal.BaseHttpFutureCommandClient;
import java.io.*; import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException; import java.net.MalformedURLException;
import java.net.URL; import java.net.URL;
import java.util.List; import java.util.List;
import org.apache.commons.io.IOUtils;
import org.jclouds.http.HttpConstants;
import org.jclouds.http.HttpFutureCommand;
import org.jclouds.http.HttpFutureCommandClient;
import org.jclouds.http.HttpRequest;
import org.jclouds.http.HttpRequestFilter;
import org.jclouds.http.HttpResponse;
import org.jclouds.http.internal.BaseHttpFutureCommandClient;
import com.google.appengine.api.urlfetch.HTTPHeader;
import com.google.appengine.api.urlfetch.HTTPMethod;
import com.google.appengine.api.urlfetch.HTTPRequest;
import com.google.appengine.api.urlfetch.HTTPResponse;
import com.google.appengine.api.urlfetch.URLFetchService;
import com.google.common.annotations.VisibleForTesting;
import com.google.inject.Inject;
/** /**
* Google App Engine version of {@link HttpFutureCommandClient} * Google App Engine version of {@link HttpFutureCommandClient}
* *
* @author Adrian Cole * @author Adrian Cole
*/ */
public class URLFetchServiceClient extends BaseHttpFutureCommandClient { public class URLFetchServiceClient extends BaseHttpFutureCommandClient {
private final URLFetchService urlFetchService; private final URLFetchService urlFetchService;
@Inject @Inject
public URLFetchServiceClient(URL target, URLFetchService urlFetchService) public URLFetchServiceClient(URL target, URLFetchService urlFetchService)
throws MalformedURLException { throws MalformedURLException {
super(target); super(target);
this.urlFetchService = urlFetchService; this.urlFetchService = urlFetchService;
} }
public void submit(HttpFutureCommand<?> command) { public void submit(HttpFutureCommand<?> command) {
HttpRequest request = command.getRequest(); HttpRequest request = command.getRequest();
HTTPResponse gaeResponse = null;
try {
for (HttpRequestFilter filter : requestFilters) {
filter.filter(request);
}
HttpResponse response = null;
for (; ;) {
logger.trace("%1$s - converting request %2$s", target, request);
HTTPRequest gaeRequest = convert(request);
if (logger.isTraceEnabled())
logger.trace(
"%1$s - submitting request %2$s, headers: %3$s",
target, gaeRequest.getURL(),
headersAsString(gaeRequest.getHeaders()));
gaeResponse = this.urlFetchService.fetch(gaeRequest);
if (logger.isTraceEnabled())
logger
.trace(
"%1$s - received response code %2$s, headers: %3$s",
target, gaeResponse.getResponseCode(),
headersAsString(gaeResponse.getHeaders()));
response = convert(gaeResponse);
int statusCode = response.getStatusCode();
if (statusCode >= 500 && httpRetryHandler.retryRequest(command, response))
continue;
break;
}
handleResponse(command, response);
} catch (Exception e) {
if (gaeResponse != null && gaeResponse.getContent() != null) {
logger.error(e,
"error encountered during the execution: %1$s%n%2$s",
gaeResponse, new String(gaeResponse.getContent()));
}
command.setException(e);
}
}
String headersAsString(List<HTTPHeader> headers) { HTTPResponse gaeResponse = null;
StringBuilder builder = new StringBuilder(""); try {
for (HTTPHeader header : headers) for (HttpRequestFilter filter : requestFilters) {
builder.append("[").append(header.getName()).append("=").append( filter.filter(request);
header.getValue()).append("],"); }
return builder.toString(); String hostHeader = request.getFirstHeaderOrNull(HttpConstants.HOST);
} if (hostHeader != null) {
logger
.warn(
"Note that as of GAE SDK version 1.2.1, host headers are stripped. you passed %1$s",
hostHeader);
}
HttpResponse response = null;
for (;;) {
logger.trace("%1$s - converting request %2$s", target, request);
HTTPRequest gaeRequest = convert(request);
if (logger.isTraceEnabled())
logger.trace("%1$s - submitting request %2$s, headers: %3$s", target, gaeRequest
.getURL(), headersAsString(gaeRequest.getHeaders()));
gaeResponse = this.urlFetchService.fetch(gaeRequest);
if (logger.isTraceEnabled())
logger.info("%1$s - received response code %2$s, headers: %3$s", target, gaeResponse
.getResponseCode(), headersAsString(gaeResponse.getHeaders()));
response = convert(gaeResponse);
int statusCode = response.getStatusCode();
if (statusCode >= 500 && httpRetryHandler.retryRequest(command, response))
continue;
break;
}
handleResponse(command, response);
} catch (Exception e) {
if (gaeResponse != null && gaeResponse.getContent() != null) {
logger.error(e, "error encountered during the execution: %1$s%n%2$s", gaeResponse,
new String(gaeResponse.getContent()));
}
command.setException(e);
}
}
/** String headersAsString(List<HTTPHeader> headers) {
* byte [] content is replayable and the only content type supportable by StringBuilder builder = new StringBuilder("");
* GAE. As such, we convert the original request content to a byte array. for (HTTPHeader header : headers)
*/ builder.append("[").append(header.getName()).append("=").append(header.getValue()).append(
@VisibleForTesting "],");
void changeRequestContentToBytes(HttpRequest request) throws IOException { return builder.toString();
Object content = request.getPayload(); }
if (content == null || content instanceof byte[]) {
return;
} else if (content instanceof String) {
String string = (String) content;
request.setPayload(string.getBytes());
} else if (content instanceof InputStream || content instanceof File) {
InputStream i = content instanceof InputStream ? (InputStream) content
: new FileInputStream((File) content);
try {
request.setPayload(IOUtils.toByteArray(i));
} finally {
IOUtils.closeQuietly(i);
}
} else {
throw new UnsupportedOperationException("Content not supported "
+ content.getClass());
}
} /**
* byte [] content is replayable and the only content type supportable by GAE. As such, we
* convert the original request content to a byte array.
*/
@VisibleForTesting
void changeRequestContentToBytes(HttpRequest request) throws IOException {
Object content = request.getPayload();
if (content == null || content instanceof byte[]) {
return;
} else if (content instanceof String) {
String string = (String) content;
request.setPayload(string.getBytes());
} else if (content instanceof InputStream || content instanceof File) {
InputStream i = content instanceof InputStream ? (InputStream) content
: new FileInputStream((File) content);
try {
request.setPayload(IOUtils.toByteArray(i));
} finally {
IOUtils.closeQuietly(i);
}
} else {
throw new UnsupportedOperationException("Content not supported " + content.getClass());
}
@VisibleForTesting }
HttpResponse convert(HTTPResponse gaeResponse) {
HttpResponse response = new HttpResponse();
response.setStatusCode(gaeResponse.getResponseCode());
for (HTTPHeader header : gaeResponse.getHeaders()) {
response.getHeaders().put(header.getName(), header.getValue());
}
if (gaeResponse.getContent() != null) {
response.setContent(new ByteArrayInputStream(gaeResponse
.getContent()));
}
return response;
}
@VisibleForTesting @VisibleForTesting
HTTPRequest convert(HttpRequest request) throws IOException { HttpResponse convert(HTTPResponse gaeResponse) {
URL url = new URL(target, request.getUri()); HttpResponse response = new HttpResponse();
HTTPRequest gaeRequest = new HTTPRequest(url, HTTPMethod response.setStatusCode(gaeResponse.getResponseCode());
.valueOf(request.getMethod()), disallowTruncate()); for (HTTPHeader header : gaeResponse.getHeaders()) {
for (String header : request.getHeaders().keySet()) { response.getHeaders().put(header.getName(), header.getValue());
for (String value : request.getHeaders().get(header)) }
gaeRequest.addHeader(new HTTPHeader(header, value)); if (gaeResponse.getContent() != null) {
} response.setContent(new ByteArrayInputStream(gaeResponse.getContent()));
if (request.getPayload() != null) { }
changeRequestContentToBytes(request); return response;
gaeRequest.setPayload((byte[]) request.getPayload()); }
}
return gaeRequest; @VisibleForTesting
} HTTPRequest convert(HttpRequest request) throws IOException {
URL url = new URL(target, request.getUri());
HTTPRequest gaeRequest = new HTTPRequest(url, HTTPMethod.valueOf(request.getMethod()),
disallowTruncate().doNotFollowRedirects());
for (String header : request.getHeaders().keySet()) {
for (String value : request.getHeaders().get(header))
gaeRequest.addHeader(new HTTPHeader(header, value));
}
if (request.getPayload() != null) {
changeRequestContentToBytes(request);
gaeRequest.setPayload((byte[]) request.getPayload());
}
return gaeRequest;
}
} }

View File

@ -57,129 +57,123 @@ import com.google.appengine.api.urlfetch.URLFetchService;
*/ */
@Test @Test
public class URLFetchServiceClientTest { public class URLFetchServiceClientTest {
URLFetchServiceClient client; URLFetchServiceClient client;
URL url; URL url;
@BeforeTest @BeforeTest
void setupClient() throws MalformedURLException { void setupClient() throws MalformedURLException {
url = new URL("http://localhost:80"); url = new URL("http://localhost:80");
client = new URLFetchServiceClient(url, client = new URLFetchServiceClient(url, createNiceMock(URLFetchService.class));
createNiceMock(URLFetchService.class)); }
}
@Test @Test
void testConvertWithHeaders() { void testConvertWithHeaders() {
HTTPResponse gaeResponse = createMock(HTTPResponse.class); HTTPResponse gaeResponse = createMock(HTTPResponse.class);
expect(gaeResponse.getResponseCode()).andReturn(200); expect(gaeResponse.getResponseCode()).andReturn(200);
List<HTTPHeader> headers = new ArrayList<HTTPHeader>(); List<HTTPHeader> headers = new ArrayList<HTTPHeader>();
headers.add(new HTTPHeader(HttpHeaders.CONTENT_TYPE, "text/xml")); headers.add(new HTTPHeader(HttpHeaders.CONTENT_TYPE, "text/xml"));
expect(gaeResponse.getHeaders()).andReturn(headers); expect(gaeResponse.getHeaders()).andReturn(headers);
expect(gaeResponse.getContent()).andReturn(null).atLeastOnce(); expect(gaeResponse.getContent()).andReturn(null).atLeastOnce();
replay(gaeResponse); replay(gaeResponse);
HttpResponse response = client.convert(gaeResponse); HttpResponse response = client.convert(gaeResponse);
assertEquals(response.getStatusCode(), 200); assertEquals(response.getStatusCode(), 200);
assertEquals(response.getContent(), null); assertEquals(response.getContent(), null);
assertEquals(response.getHeaders().size(), 1); assertEquals(response.getHeaders().size(), 1);
assertEquals(response.getFirstHeaderOrNull(HttpHeaders.CONTENT_TYPE), assertEquals(response.getFirstHeaderOrNull(HttpHeaders.CONTENT_TYPE), "text/xml");
"text/xml"); }
}
@Test @Test
void testConvertWithContent() throws IOException { void testConvertWithContent() throws IOException {
HTTPResponse gaeResponse = createMock(HTTPResponse.class); HTTPResponse gaeResponse = createMock(HTTPResponse.class);
expect(gaeResponse.getResponseCode()).andReturn(200); expect(gaeResponse.getResponseCode()).andReturn(200);
List<HTTPHeader> headers = new ArrayList<HTTPHeader>(); List<HTTPHeader> headers = new ArrayList<HTTPHeader>();
headers.add(new HTTPHeader(HttpHeaders.CONTENT_TYPE, "text/xml")); headers.add(new HTTPHeader(HttpHeaders.CONTENT_TYPE, "text/xml"));
expect(gaeResponse.getHeaders()).andReturn(headers); expect(gaeResponse.getHeaders()).andReturn(headers);
expect(gaeResponse.getContent()).andReturn("hello".getBytes()) expect(gaeResponse.getContent()).andReturn("hello".getBytes()).atLeastOnce();
.atLeastOnce(); replay(gaeResponse);
replay(gaeResponse); HttpResponse response = client.convert(gaeResponse);
HttpResponse response = client.convert(gaeResponse); assertEquals(response.getStatusCode(), 200);
assertEquals(response.getStatusCode(), 200); assertEquals(IOUtils.toString(response.getContent()), "hello");
assertEquals(IOUtils.toString(response.getContent()), "hello"); assertEquals(response.getHeaders().size(), 1);
assertEquals(response.getHeaders().size(), 1); assertEquals(response.getFirstHeaderOrNull(HttpHeaders.CONTENT_TYPE), "text/xml");
assertEquals(response.getFirstHeaderOrNull(HttpHeaders.CONTENT_TYPE), }
"text/xml");
}
@Test @Test
void testConvertRequestGetsTargetAndUri() throws IOException { void testConvertRequestGetsTargetAndUri() throws IOException {
HttpRequest request = new HttpRequest("GET", "foo"); HttpRequest request = new HttpRequest("GET", "foo");
HTTPRequest gaeRequest = client.convert(request); HTTPRequest gaeRequest = client.convert(request);
assertEquals(gaeRequest.getURL().getPath(), "/foo"); assertEquals(gaeRequest.getURL().getPath(), "/foo");
} }
@Test @Test
void testConvertRequestSetsFetchOptions() throws IOException { void testConvertRequestSetsFetchOptions() throws IOException {
HttpRequest request = new HttpRequest("GET", "foo"); HttpRequest request = new HttpRequest("GET", "foo");
HTTPRequest gaeRequest = client.convert(request); HTTPRequest gaeRequest = client.convert(request);
assert gaeRequest.getFetchOptions() != null; assert gaeRequest.getFetchOptions() != null;
} }
@Test @Test
void testConvertRequestSetsHeaders() throws IOException { void testConvertRequestSetsHeaders() throws IOException {
HttpRequest request = new HttpRequest("GET", "foo"); HttpRequest request = new HttpRequest("GET", "foo");
request.getHeaders().put("foo", "bar"); request.getHeaders().put("foo", "bar");
HTTPRequest gaeRequest = client.convert(request); HTTPRequest gaeRequest = client.convert(request);
assertEquals(gaeRequest.getHeaders().get(0).getName(), "foo"); assertEquals(gaeRequest.getHeaders().get(0).getName(), "foo");
assertEquals(gaeRequest.getHeaders().get(0).getValue(), "bar"); assertEquals(gaeRequest.getHeaders().get(0).getValue(), "bar");
} }
@Test @Test
void testConvertRequestNoContent() throws IOException { void testConvertRequestNoContent() throws IOException {
HttpRequest request = new HttpRequest("GET", "foo"); HttpRequest request = new HttpRequest("GET", "foo");
HTTPRequest gaeRequest = client.convert(request); HTTPRequest gaeRequest = client.convert(request);
assert gaeRequest.getPayload() == null; assert gaeRequest.getPayload() == null;
assertEquals(gaeRequest.getHeaders().size(), 0); assertEquals(gaeRequest.getHeaders().size(), 0);
} }
@Test @Test
void testConvertRequestStringContent() throws IOException { void testConvertRequestStringContent() throws IOException {
HttpRequest request = new HttpRequest("GET", "foo"); HttpRequest request = new HttpRequest("GET", "foo");
request.setPayload("hoot!"); request.setPayload("hoot!");
testHoot(request); testHoot(request);
} }
@Test @Test
void testConvertRequestInputStreamContent() throws IOException { void testConvertRequestInputStreamContent() throws IOException {
HttpRequest request = new HttpRequest("GET", "foo"); HttpRequest request = new HttpRequest("GET", "foo");
request.setPayload(IOUtils.toInputStream("hoot!")); request.setPayload(IOUtils.toInputStream("hoot!"));
testHoot(request); testHoot(request);
} }
@Test @Test
void testConvertRequestBytesContent() throws IOException { void testConvertRequestBytesContent() throws IOException {
HttpRequest request = new HttpRequest("GET", "foo"); HttpRequest request = new HttpRequest("GET", "foo");
request.setPayload("hoot!".getBytes()); request.setPayload("hoot!".getBytes());
testHoot(request); testHoot(request);
} }
@Test(expectedExceptions = UnsupportedOperationException.class) @Test(expectedExceptions = UnsupportedOperationException.class)
void testConvertRequestBadContent() throws IOException { void testConvertRequestBadContent() throws IOException {
HttpRequest request = new HttpRequest("GET", "foo"); HttpRequest request = new HttpRequest("GET", "foo");
request.setPayload(new Date()); request.setPayload(new Date());
client.convert(request); client.convert(request);
} }
@Test @Test
@Parameters("basedir") @Parameters("basedir")
void testConvertRequestFileContent(String basedir) throws IOException { void testConvertRequestFileContent(String basedir) throws IOException {
File file = new File(basedir, "target/testfiles/hoot"); File file = new File(basedir, "target/testfiles/hoot");
file.getParentFile().mkdirs(); file.getParentFile().mkdirs();
IOUtils.write("hoot!", new FileOutputStream(file)); IOUtils.write("hoot!", new FileOutputStream(file));
HttpRequest request = new HttpRequest("GET", "foo"); HttpRequest request = new HttpRequest("GET", "foo");
request.setPayload(file); request.setPayload(file);
testHoot(request); testHoot(request);
} }
private void testHoot(HttpRequest request) throws IOException {
request.getHeaders().put(HttpHeaders.CONTENT_TYPE,"text/plain");
HTTPRequest gaeRequest = client.convert(request);
assertEquals(gaeRequest.getHeaders().get(0).getName(),
HttpHeaders.CONTENT_TYPE);
assertEquals(gaeRequest.getHeaders().get(0).getValue(), "text/plain");
assertEquals(new String(gaeRequest.getPayload()), "hoot!");
}
private void testHoot(HttpRequest request) throws IOException {
request.getHeaders().put(HttpHeaders.CONTENT_TYPE, "text/plain");
HTTPRequest gaeRequest = client.convert(request);
assertEquals(gaeRequest.getHeaders().get(0).getName(), HttpHeaders.CONTENT_TYPE);
assertEquals(gaeRequest.getHeaders().get(0).getValue(), "text/plain");
assertEquals(new String(gaeRequest.getPayload()), "hoot!");
}
} }