/******************************************************************************* * Copyright (c) 2011 Subgraph. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Subgraph - initial API and implementation ******************************************************************************/ package com.subgraph.vega.internal.http.requests; import java.io.IOException; import java.io.InputStream; import java.util.concurrent.Callable; import java.util.logging.Logger; import java.util.zip.GZIPInputStream; import org.apache.http.Header; import org.apache.http.HeaderElement; import org.apache.http.HttpEntity; import org.apache.http.HttpHost; import org.apache.http.HttpRequest; import org.apache.http.HttpResponse; import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpUriRequest; import org.apache.http.entity.ByteArrayEntity; import org.apache.http.protocol.ExecutionContext; import org.apache.http.protocol.HTTP; import org.apache.http.protocol.HttpContext; import com.subgraph.vega.api.html.IHTMLParser; import com.subgraph.vega.api.http.requests.IHttpRequestEngineConfig; import com.subgraph.vega.api.http.requests.IHttpResponse; import com.subgraph.vega.api.http.requests.IHttpResponseProcessor; class RequestTask implements Callable<IHttpResponse> { private final static Logger logger = Logger.getLogger("request-engine"); private final HttpClient client; private final RateLimiter rateLimit; private final HttpUriRequest request; private final HttpContext context; private final IHttpRequestEngineConfig config; private final IHTMLParser htmlParser; RequestTask(HttpClient client, RateLimiter rateLimit, HttpUriRequest request, HttpContext context, IHttpRequestEngineConfig config, IHTMLParser htmlParser) { this.client = client; this.rateLimit = rateLimit; this.request = request; this.context = context; this.config = config; this.htmlParser = htmlParser; } @Override public IHttpResponse call() throws Exception { if(config.getForceIdentityEncoding()) request.setHeader(HTTP.CONTENT_ENCODING, HTTP.IDENTITY_CODING); if(rateLimit != null) rateLimit.maybeDelayRequest(); final long start = System.currentTimeMillis(); final HttpResponse httpResponse = client.execute(request, context); final long elapsed = System.currentTimeMillis() - start; final HttpEntity entity = httpResponse.getEntity(); if(entity != null) { if(config.getMaximumResponseKilobytes() > 0 && entity.getContentLength() > (config.getMaximumResponseKilobytes() * 1024)) { logger.warning("Aborting request "+ request.getURI().toString() +" because response length "+ entity.getContentLength() + " exceeds maximum length of "+ config.getMaximumResponseKilobytes() +" kb."); request.abort(); httpResponse.setEntity(createEmptyEntity()); } final HttpEntity newEntity = processEntity(httpResponse, entity); httpResponse.setEntity(newEntity); } final HttpHost host = (HttpHost) context.getAttribute(ExecutionContext.HTTP_TARGET_HOST); final HttpRequest sentRequest = (HttpRequest) context.getAttribute(HttpRequestEngine.VEGA_SENT_REQUEST); final IHttpResponse response = new EngineHttpResponse( request.getURI(), host, (sentRequest == null) ? (request) : (sentRequest), httpResponse, elapsed, htmlParser ); for(IHttpResponseProcessor p: config.getResponseProcessors()) { p.processResponse(response.getOriginalRequest(), response, context); } return response; } private HttpEntity processEntity(HttpResponse response, HttpEntity entity) throws IOException { if(entity == null) return null; if(isGzipEncoded(entity) && config.getDecompressGzipEncoding()) return processGzipEncodedEntity(response, entity); final InputStream input = entity.getContent(); if(input == null) { return createEmptyEntity(); } String contentType = (entity.getContentType() == null) ? (null) : (entity.getContentType().getValue()); String contentEncoding = (entity.getContentEncoding() == null) ? (null) : (entity.getContentEncoding().getValue()); RepeatableStreamingEntity e = new RepeatableStreamingEntity(input, entity.getContentLength(), false, entity.isChunked(), contentType, contentEncoding); if(config.getMaximumResponseKilobytes() > 0) { e.setMaximumInputKilobytes(config.getMaximumResponseKilobytes()); } return e; } private HttpEntity createEmptyEntity() { return new ByteArrayEntity(new byte[0]); } private HttpEntity processGzipEncodedEntity(HttpResponse response, HttpEntity entity) throws IOException { final InputStream input = entity.getContent(); if(input == null) { response.setHeader(HTTP.CONTENT_LEN, "0"); return new ByteArrayEntity(new byte[0]); } final InputStream gzipInput = new GZIPInputStream(input); response.removeHeaders(HTTP.CONTENT_ENCODING); String contentType = (entity.getContentType() == null) ? (null) : (entity.getContentType().getValue()); RepeatableStreamingEntity newEntity = new RepeatableStreamingEntity(gzipInput, -1, true, entity.isChunked(), contentType, null); response.setHeader(HTTP.CONTENT_LEN, Long.toString(newEntity.getContentLength())); if(config.getMaximumResponseKilobytes() > 0) { newEntity.setMaximumInputKilobytes(config.getMaximumResponseKilobytes()); } return newEntity; } private boolean isGzipEncoded(HttpEntity entity) { if(entity == null) return false; final Header ceh = entity.getContentEncoding(); if(ceh == null) return false; for(HeaderElement element : ceh.getElements()) { if("gzip".equalsIgnoreCase(element.getName())) return true; } return false; } }