/* * 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.IOException; import java.io.PrintWriter; import java.net.SocketException; import java.net.SocketTimeoutException; import org.apache.commons.io.output.StringBuilderWriter; import org.apache.http.Header; import org.apache.http.HttpEntity; import org.apache.http.HttpStatus; import org.apache.http.HttpVersion; import org.apache.http.client.ClientProtocolException; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.conn.ConnectTimeoutException; import org.apache.http.conn.ConnectionPoolTimeoutException; import org.apache.http.conn.HttpHostConnectException; import org.apache.http.entity.ByteArrayEntity; import org.apache.http.entity.ContentType; import org.apache.http.entity.StringEntity; import org.apache.http.message.BasicHttpResponse; import org.apache.http.message.BasicStatusLine; import org.apache.http.util.EntityUtils; import org.esigate.http.BasicCloseableHttpResponse; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Exception thrown when an error occurred retrieving a resource. * <p> * This exception can include a HTTP response (with error page body) or a String body. * * @author Francois-Xavier Bonnet * @author Nicolas Richeton */ public class HttpErrorPage extends Exception { private static final long serialVersionUID = 1L; private static final Logger LOG = LoggerFactory.getLogger(HttpErrorPage.class); private final CloseableHttpResponse httpResponse; private static HttpEntity toMemoryEntity(String content) { return new StringEntity(content, "UTF-8"); } private static HttpEntity toMemoryEntity(Exception exception) { StringBuilderWriter out = new StringBuilderWriter(Parameters.DEFAULT_BUFFER_SIZE); PrintWriter pw = new PrintWriter(out); exception.printStackTrace(pw); String content = out.toString(); try { return toMemoryEntity(content); } finally { pw.close(); } } private static HttpEntity toMemoryEntity(HttpEntity httpEntity) { if (httpEntity == null) { return null; } HttpEntity memoryEntity; try { byte[] content = EntityUtils.toByteArray(httpEntity); ByteArrayEntity byteArrayEntity = new ByteArrayEntity(content, ContentType.get(httpEntity)); Header contentEncoding = httpEntity.getContentEncoding(); if (contentEncoding != null) { byteArrayEntity.setContentEncoding(contentEncoding); } memoryEntity = byteArrayEntity; } catch (IOException e) { StringBuilderWriter out = new StringBuilderWriter(Parameters.DEFAULT_BUFFER_SIZE); PrintWriter pw = new PrintWriter(out); e.printStackTrace(pw); pw.close(); memoryEntity = new StringEntity(out.toString(), ContentType.getOrDefault(httpEntity)); } return memoryEntity; } /** * Create an HTTP error page exception from an Http response. * * @param httpResponse * backend response. */ public HttpErrorPage(CloseableHttpResponse httpResponse) { super(httpResponse.getStatusLine().getStatusCode() + " " + httpResponse.getStatusLine().getReasonPhrase()); this.httpResponse = httpResponse; // Consume the entity and replace it with an in memory Entity httpResponse.setEntity(toMemoryEntity(httpResponse.getEntity())); } /** * Create an HTTP response from a String content wich will be used as the response entity. * * @param statusCode * @param statusMessage * @param content */ public HttpErrorPage(int statusCode, String statusMessage, String content) { super(statusCode + " " + statusMessage); this.httpResponse = HttpErrorPage.generateHttpResponse(statusCode, statusMessage); this.httpResponse.setEntity(toMemoryEntity(content)); } /** * Create an HTTP response from an exception. This exception stack trace will be showed to the end user. * * @param statusCode * @param statusMessage * @param exception */ public HttpErrorPage(int statusCode, String statusMessage, Exception exception) { super(statusCode + " " + statusMessage, exception); this.httpResponse = HttpErrorPage.generateHttpResponse(statusCode, statusMessage); this.httpResponse.setEntity(toMemoryEntity(exception)); } /** * Get HTTP response enclosed in this exception. * * @return HTTP response */ public CloseableHttpResponse getHttpResponse() { return this.httpResponse; } public static CloseableHttpResponse generateHttpResponse(Exception exception) { if (exception instanceof HttpHostConnectException) { return generateHttpResponse(HttpStatus.SC_BAD_GATEWAY, "Connection refused"); } else if (exception instanceof ConnectionPoolTimeoutException) { return generateHttpResponse(HttpStatus.SC_GATEWAY_TIMEOUT, "Connection pool timeout"); } else if (exception instanceof ConnectTimeoutException) { return generateHttpResponse(HttpStatus.SC_GATEWAY_TIMEOUT, "Connect timeout"); } else if (exception instanceof SocketTimeoutException) { return generateHttpResponse(HttpStatus.SC_GATEWAY_TIMEOUT, "Socket timeout"); } else if (exception instanceof SocketException) { return generateHttpResponse(HttpStatus.SC_BAD_GATEWAY, "Socket Exception"); } else if (exception instanceof ClientProtocolException) { String message = exception.getMessage(); if (message == null && exception.getCause() != null) { message = exception.getCause().getMessage(); } return generateHttpResponse(HttpStatus.SC_BAD_GATEWAY, "Protocol error: " + message); } else { LOG.error("Error retrieving URL", exception); return generateHttpResponse(HttpStatus.SC_BAD_GATEWAY, "Error retrieving URL"); } } public static CloseableHttpResponse generateHttpResponse(int statusCode, String statusText) { CloseableHttpResponse result = BasicCloseableHttpResponse.adapt(new BasicHttpResponse(new BasicStatusLine(HttpVersion.HTTP_1_1, statusCode, statusText))); result.setEntity(toMemoryEntity(statusText)); return result; } }