/************************************************************************* * Copyright 2009-2013 Eucalyptus Systems, Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see http://www.gnu.org/licenses/. * * Please contact Eucalyptus Systems, Inc., 6755 Hollister Ave., Goleta * CA 93117, USA or visit http://www.eucalyptus.com/licenses/ if you need * additional information or have any questions. ************************************************************************/ package com.eucalyptus.objectstorage.pipeline; import java.nio.charset.Charset; import org.apache.log4j.Logger; import org.jboss.netty.channel.Channel; import org.jboss.netty.channel.ChannelFuture; import org.jboss.netty.channel.ChannelFutureListener; import org.jboss.netty.channel.ChannelHandlerContext; import org.jboss.netty.channel.Channels; import org.jboss.netty.channel.ExceptionEvent; import org.jboss.netty.channel.SimpleChannelUpstreamHandler; import org.jboss.netty.handler.codec.http.HttpHeaders; import org.jboss.netty.handler.codec.http.HttpResponseStatus; import org.jboss.netty.handler.codec.http.HttpVersion; import com.eucalyptus.context.Context; import com.eucalyptus.context.Contexts; import com.eucalyptus.http.MappingHttpRequest; import com.eucalyptus.http.MappingHttpResponse; import com.eucalyptus.objectstorage.exceptions.ObjectStorageException; import com.eucalyptus.objectstorage.exceptions.s3.InvalidAccessKeyIdException; import com.eucalyptus.objectstorage.exceptions.s3.SignatureDoesNotMatchException; import com.eucalyptus.objectstorage.msgs.ObjectStorageErrorMessageType; import com.eucalyptus.ws.WebServicesException; import com.google.common.base.Strings; import com.google.common.util.concurrent.Runnables; public class ObjectStorageRESTExceptionHandler extends SimpleChannelUpstreamHandler { private static final Logger LOG = Logger.getLogger(ObjectStorageRESTExceptionHandler.class); private static final String CODE_UNKNOWN = "UNKNOWN"; private static final Charset UTF_8 = Charset.forName("UTF-8"); @Override public void exceptionCaught(final ChannelHandlerContext ctx, final ExceptionEvent e) throws Exception { final Channel ch = e.getChannel(); Throwable cause = e.getCause(); if (cause.getCause() != null) { // wrapped exception cause = cause.getCause(); } // Get the request ID from the context and clear the context. If you cant log an exception and move on String requestId = null; HttpVersion httpVersion = HttpVersion.HTTP_1_0; Runnable cleanup = Runnables.doNothing( ); try { if (ch != null) { Context context = Contexts.lookup(ch); requestId = context.getCorrelationId(); MappingHttpRequest request = context.getHttpRequest( ); httpVersion = request != null ? request.getProtocolVersion( ) : httpVersion; cleanup = () -> Contexts.clear( context ); } } catch (Exception ex) { LOG.trace("Error getting request ID or clearing context", ex); } // Populate the error response fields final HttpResponseStatus status; final String code; final String resource; if (cause instanceof ObjectStorageException) { ObjectStorageException walrusEx = (ObjectStorageException) cause; status = walrusEx.getStatus(); code = walrusEx.getCode(); resource = walrusEx.getResource(); } else if (cause instanceof WebServicesException) { WebServicesException webEx = (WebServicesException) cause; status = webEx.getStatus(); code = CODE_UNKNOWN; resource = null; } else { status = HttpResponseStatus.INTERNAL_SERVER_ERROR; code = CODE_UNKNOWN; resource = null; } final ObjectStorageErrorMessageType errorResponse = new ObjectStorageErrorMessageType( ); errorResponse.setResource( Strings.nullToEmpty( resource ) ); errorResponse.setMessage( Strings.nullToEmpty( cause.getMessage() ) ); errorResponse.setCode( Strings.nullToEmpty( code ) ); errorResponse.setRequestId( Strings.nullToEmpty( requestId ) ); errorResponse.setStatus( status ); if ( cause instanceof InvalidAccessKeyIdException ) { errorResponse.setAccessKeyId( ( (InvalidAccessKeyIdException) cause ).getAccessKeyId( ) ); errorResponse.setResource( null ); } if (cause instanceof SignatureDoesNotMatchException) { SignatureDoesNotMatchException ex = (SignatureDoesNotMatchException) cause; errorResponse.setAccessKeyId( ex.getAccessKeyId( ) ); errorResponse.setResource( null ); errorResponse.setStringToSign( Strings.nullToEmpty( ex.getStringToSign( ) ) ); errorResponse.setSignatureProvided( Strings.nullToEmpty( ex.getSignatureProvided( ) ) ); if (ex.getStringToSign() != null) errorResponse.setStringToSignBytes( stringToByteString( ex.getStringToSign() ) ); errorResponse.setCanonicalRequest( Strings.nullToEmpty( ex.getCanonicalRequest( ) ) ); if (ex.getCanonicalRequest() != null) errorResponse.setCanonicalRequestBytes( stringToByteString( ex.getCanonicalRequest() ) ); } if ( ctx.getChannel( ).isConnected( ) ) { final MappingHttpResponse response = new MappingHttpResponse( httpVersion ); response.setStatus( status ); response.setMessage( errorResponse ); response.setCorrelationId( requestId ); errorResponse.setCorrelationId( requestId ); final ChannelFuture writeFuture = Channels.future(ctx.getChannel()); writeFuture.addListener(ChannelFutureListener.CLOSE); response.setHeader(HttpHeaders.Names.CONNECTION, HttpHeaders.Values.CLOSE); Channels.write( ctx, writeFuture, response ); cleanup.run( ); } else { ctx.sendDownstream( e ); } } private static String stringToByteString(String in) { StringBuilder sb = new StringBuilder(); byte[] b = in.getBytes(UTF_8); for(int i=0; i<b.length; i++) sb.append(b[i]).append(" "); sb.deleteCharAt(sb.length()-1); return sb.toString(); } }