/* Copyright 2010 Google 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.
*
* $Id: HttpFetchStrategy.java 534 2010-03-12 23:32:23Z dchung@google.com $
*/
package com.google.dataconnector.client.fetchrequest;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.StatusLine;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpHead;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicHeader;
import org.apache.log4j.Logger;
import com.google.dataconnector.client.StrategyException;
import com.google.dataconnector.client.FetchRequestHandler.Strategy;
import com.google.dataconnector.protocol.proto.SdcFrame.FetchReply;
import com.google.dataconnector.protocol.proto.SdcFrame.FetchRequest;
import com.google.dataconnector.protocol.proto.SdcFrame.MessageHeader;
import com.google.inject.Inject;
import com.google.protobuf.ByteString;
/**
TODO(dchung): javadoc.
*/
public class HttpFetchStrategy implements Strategy {
private static Logger LOG = Logger.getLogger(HttpFetchStrategy.class);
// Local fields.
private final DefaultHttpClient httpClient = new DefaultHttpClient();
@Inject
public HttpFetchStrategy() {
// Default constructor.
}
/**
* Executes an HTTP GET/POST and return the response.
* @param request The request.
* @return The HTTP response.
* @throws StrategyException
*/
HttpResponse getHttpResponse(FetchRequest request) throws StrategyException {
HttpRequestBase httpMethod = null;
try {
httpMethod = getMethod(request);
if (httpMethod != null) {
copyHeaders(request, httpMethod);
return httpClient.execute(httpMethod);
} else {
throw new StrategyException(request.getId() + ": Unknown method.");
}
} catch (IOException e) {
String method = (httpMethod != null) ? httpMethod.getMethod() : "Unknown";
throw new StrategyException(request.getId() + ": while executing HTTP " +
method + ": ", e);
}
}
/**
* Based on the inbound request type header, determine the correct http
* method to use. If a method cannot be determined (or not specified),
* HTTP GET is attempted.
*/
HttpRequestBase getMethod(FetchRequest request) {
String method = null;
for (MessageHeader h : request.getHeadersList()) {
if ("x-sdc-http-method".equalsIgnoreCase(h.getKey())) {
method = h.getValue().toUpperCase();
}
}
LOG.info(request.getId() + ": method=" + method + ", resource=" + request.getResource() +
((request.hasContents()) ? ", payload_size=" + request.getContents().size() : ""));
if (method == null) {
LOG.info(request.getId() + ": No http method specified. Default to GET.");
method = "GET";
}
if ("GET".equals(method)) {
return new HttpGet(request.getResource());
}
if ("POST".equals(method)) {
HttpPost httpPost = new HttpPost(request.getResource());
if (request.hasContents()) {
LOG.debug(request.getId() + ": Content = " + new String(request.getContents().toByteArray()));
httpPost.setEntity(new ByteArrayEntity(request.getContents().toByteArray()));
}
return httpPost;
}
if ("PUT".equals(method)) {
HttpPut httpPut = new HttpPut(request.getResource());
if (request.hasContents()) {
LOG.debug(request.getId() + ": Content = " + new String(request.getContents().toByteArray()));
httpPut.setEntity(new ByteArrayEntity(request.getContents().toByteArray()));
}
return httpPut;
}
if ("DELETE".equals(method)) {
return new HttpDelete(request.getResource());
}
if ("HEAD".equals(method)) {
return new HttpHead(request.getResource());
}
LOG.info(request.getId() + ": Unknown method " + method);
return null;
}
/**
* Copies the headers from the inbound request proto to the actual http request.
*/
void copyHeaders(FetchRequest request, HttpRequestBase httpRequest) throws IOException {
for (MessageHeader h : request.getHeadersList()) {
if ("x-sdc-agent-cookie".equalsIgnoreCase(h.getKey())) {
// set the cookie
continue;
}
Header httpHeader = new BasicHeader(h.getKey(), h.getValue());
LOG.debug(request.getId() + ": Header = " + h.getKey() + ", " + h.getValue());
httpRequest.addHeader(httpHeader);
}
// Tell the server to close down for keep-alive connections.
httpRequest.addHeader(new BasicHeader("Connection", "close"));
}
/**
* Implements the strategy method of processing the request and filling in the
* reply with results of processing.
*
* @param request The request.
* @param replyBuilder The reply to fill in.
*/
@Override
public void process(FetchRequest request, FetchReply.Builder replyBuilder)
throws StrategyException {
HttpResponse response = getHttpResponse(request);
StatusLine statusLine = response.getStatusLine();
int statusCode = statusLine.getStatusCode();
replyBuilder.setStatus(statusCode);
HttpEntity entity = response.getEntity();
if (entity != null) {
try {
ByteArrayOutputStream buff = new ByteArrayOutputStream();
entity.writeTo(buff);
buff.flush();
buff.close();
if (buff.size() > 0) {
replyBuilder.setContents(ByteString.copyFrom(buff.toByteArray()));
}
} catch (IOException e) {
throw new StrategyException(request.getId() + " while copying content:", e);
}
}
// Copy the headers
for (Header h : response.getAllHeaders()) {
replyBuilder.addHeaders(MessageHeader.newBuilder()
.setKey(h.getName()).setValue(h.getValue()).build());
}
LOG.info(request.getId() + ": Got response from resource:" + statusLine);
}
}