/*************************************************************************
* Copyright 2009-2016 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.
*
* This file may incorporate work covered under the following copyright
* and permission notice:
*
* Software License Agreement (BSD License)
*
* Copyright (c) 2008, Regents of the University of California
* All rights reserved.
*
* Redistribution and use of this software in source and binary forms,
* with or without modification, are permitted provided that the
* following conditions are met:
*
* Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE. USERS OF THIS SOFTWARE ACKNOWLEDGE
* THE POSSIBLE PRESENCE OF OTHER OPEN SOURCE LICENSED MATERIAL,
* COPYRIGHTED MATERIAL OR PATENTED MATERIAL IN THIS SOFTWARE,
* AND IF ANY SUCH MATERIAL IS DISCOVERED THE PARTY DISCOVERING
* IT MAY INFORM DR. RICH WOLSKI AT THE UNIVERSITY OF CALIFORNIA,
* SANTA BARBARA WHO WILL THEN ASCERTAIN THE MOST APPROPRIATE REMEDY,
* WHICH IN THE REGENTS' DISCRETION MAY INCLUDE, WITHOUT LIMITATION,
* REPLACEMENT OF THE CODE SO IDENTIFIED, LICENSING OF THE CODE SO
* IDENTIFIED, OR WITHDRAWAL OF THE CODE CAPABILITY TO THE EXTENT
* NEEDED TO COMPLY WITH ANY SUCH LICENSES OR RIGHTS.
************************************************************************/
package com.eucalyptus.ws;
import java.net.SocketAddress;
import com.eucalyptus.auth.AuthContextSupplier;
import static com.eucalyptus.util.RestrictedTypes.findPolicyVendor;
import static com.eucalyptus.util.RestrictedTypes.getIamActionByMessageType;
import java.net.URI;
import java.util.*;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.annotation.Nullable;
import com.google.common.base.*;
import com.google.common.base.Objects;
import org.apache.log4j.Logger;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.buffer.ChannelBuffers;
import org.jboss.netty.channel.*;
import org.jboss.netty.handler.codec.http.*;
import org.jboss.netty.handler.execution.ExecutionHandler;
import org.jboss.netty.handler.execution.OrderedMemoryAwareThreadPoolExecutor;
import org.jboss.netty.handler.ssl.SslHandler;
import org.jboss.netty.handler.stream.ChunkedWriteHandler;
import org.jboss.netty.handler.timeout.IdleState;
import org.jboss.netty.handler.timeout.IdleStateAwareChannelHandler;
import org.jboss.netty.handler.timeout.IdleStateEvent;
import org.jboss.netty.handler.timeout.IdleStateHandler;
import org.jboss.netty.util.HashedWheelTimer;
import com.eucalyptus.auth.Permissions;
import com.eucalyptus.auth.principal.User;
import com.eucalyptus.binding.BindingManager;
import com.eucalyptus.bootstrap.Bootstrap;
import com.eucalyptus.bootstrap.Hosts;
import com.eucalyptus.component.*;
import com.eucalyptus.component.annotation.ComponentMessage;
import com.eucalyptus.component.id.Eucalyptus;
import com.eucalyptus.context.Context;
import com.eucalyptus.context.Contexts;
import com.eucalyptus.context.ServiceStateException;
import com.eucalyptus.crypto.util.SslSetup;
import com.eucalyptus.empyrean.ServiceTransitionType;
import com.eucalyptus.http.MappingHttpMessage;
import com.eucalyptus.http.MappingHttpRequest;
import com.eucalyptus.records.Logs;
import com.eucalyptus.system.Ats;
import com.eucalyptus.system.Threads;
import com.eucalyptus.util.Exceptions;
import com.eucalyptus.util.async.AsyncRequestHandler;
import com.eucalyptus.ws.handlers.BindingHandler;
import com.eucalyptus.ws.handlers.InternalWsSecHandler;
import com.eucalyptus.ws.handlers.QueryTimestampHandler;
import com.eucalyptus.ws.handlers.SoapMarshallingHandler;
import com.eucalyptus.ws.handlers.http.NioHttpDecoder;
import com.eucalyptus.ws.handlers.http.HttpResponseHeaderHandler;
import com.eucalyptus.ws.handlers.AddressingHandler;
import com.eucalyptus.ws.handlers.SoapHandler;
import com.eucalyptus.ws.server.NioServerHandler;
import com.eucalyptus.ws.server.ServiceAccessLoggingHandler;
import com.eucalyptus.ws.server.ServiceContextHandler;
import com.eucalyptus.ws.server.ServiceHackeryHandler;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import edu.ucsb.eucalyptus.msgs.BaseMessage;
public class Handlers {
private static Logger LOG = Logger.getLogger( Handlers.class );
private static final ExecutionHandler pipelineExecutionHandler = new ExecutionHandler( new OrderedMemoryAwareThreadPoolExecutor( StackConfiguration.SERVER_POOL_MAX_THREADS, 0, 0, 30L, TimeUnit.SECONDS, Threads.threadFactory( "web-services-pipeline-%d" ) ) );
private static final ExecutionHandler serviceExecutionHandler = new ExecutionHandler( new OrderedMemoryAwareThreadPoolExecutor( StackConfiguration.SERVER_POOL_MAX_THREADS, 0, 0, 30L, TimeUnit.SECONDS, Threads.threadFactory( "web-services-exec-%d" ) ) );
private static final ChannelHandler queryTimestampHandler = new QueryTimestampHandler( );
private static final ChannelHandler soapMarshallingHandler = new SoapMarshallingHandler( );
private static final ChannelHandler internalWsSecHandler = new InternalWsSecHandler( );
private static final ChannelHandler soapHandler = new SoapHandler( );
private static final ChannelHandler addressingHandler = new AddressingHandler( );
private static final ChannelHandler bindingHandler = new BindingHandler( BindingHandler.context( BindingManager.getDefaultBinding( ) ) );
private static final HashedWheelTimer timer = new HashedWheelTimer( ) { { this.start( ); } }; //TODO:GRZE: configurable
enum ServerPipelineFactory implements ChannelPipelineFactory {
INSTANCE;
@Override
public ChannelPipeline getPipeline( ) throws Exception {
ChannelPipeline pipeline = Channels.pipeline( );
pipeline.addLast( "ssl", Handlers.newSslHandler( ) );
for ( final Map.Entry<String, ChannelHandler> e : Handlers.channelMonitors( TimeUnit.SECONDS, StackConfiguration.PIPELINE_IDLE_TIMEOUT_SECONDS ).entrySet( ) ) {
pipeline.addLast( e.getKey( ), e.getValue( ) );
}
pipeline.addLast( "decoder", Handlers.newHttpDecoder( ) );
pipeline.addLast( "encoder", Handlers.newHttpResponseEncoder( ) );
pipeline.addLast( "chunkedWriter", Handlers.newChunkedWriteHandler( ) );
pipeline.addLast( "http-response-headers", Handlers.newHttpResponseHeaderHandler( ) );
pipeline.addLast( "fence", Handlers.bootstrapFence( ) );
pipeline.addLast( "pipeline-filter", Handlers.newNioServerHandler( ) );
if ( StackConfiguration.ASYNC_PIPELINE ) {
pipeline.addLast( "async-pipeline-execution-handler", Handlers.pipelineExecutionHandler( ) );
}
return pipeline;
}
}
private static NioServerHandler newNioServerHandler( ) {
return new NioServerHandler( );
}
public static ChannelHandler newHttpResponseHeaderHandler( ) { return new HttpResponseHeaderHandler( ); }
private static ChannelHandler newChunkedWriteHandler( ) {
return new ChunkedWriteHandler( );
}
private static ChannelHandler newHttpResponseEncoder( ) {
return new HttpResponseEncoder( );
}
private static ChannelHandler newHttpDecoder( ) {
return new NioHttpDecoder( );
}
public static ChannelPipelineFactory serverPipelineFactory( ) {
return ServerPipelineFactory.INSTANCE;
}
@ChannelHandler.Sharable
enum BootstrapStateCheck implements ChannelUpstreamHandler {
INSTANCE;
@Override
public void handleUpstream( final ChannelHandlerContext ctx, final ChannelEvent e ) throws Exception {
if ( !Bootstrap.isFinished( ) ) {
//TODO:GRZE: do nothing for the moment, not envouh info here.
// throw new ServiceNotReadyException( "System has not yet completed booting." );
ctx.sendUpstream( e );
} else {
ctx.sendUpstream( e );
}
}
}
public static ChannelHandler bootstrapFence( ) {
return BootstrapStateCheck.INSTANCE;
}
public static final Map<String,ChannelHandler> extraMonitors = Maps.newConcurrentMap();
public static Map<String, ChannelHandler> channelMonitors( final TimeUnit unit, final int timeout ) {
return new HashMap<String, ChannelHandler>( 2 + extraMonitors.size() ) {
{
this.put( "idlehandler", new IdleStateHandler( Handlers.timer, 0, 0, timeout, unit ) );
this.put( "idlecloser", new IdleStateAwareChannelHandler() {
@Override
public void channelIdle( ChannelHandlerContext ctx, IdleStateEvent e ) {
if ( e.getState() == IdleState.ALL_IDLE ) {
e.getChannel().close();
}
}
} );
this.putAll( extraMonitors );
}
};
}
public static ChannelHandler newSslHandler( ) {
return new NioSslHandler( );
}
public static ChannelHandler newHttpChunkAggregator( ) {
return new HttpChunkAggregator( StackConfiguration.CLIENT_HTTP_CHUNK_BUFFER_MAX );
}
public static ChannelHandler newQueryHttpChunkAggregator( ) {
return new HttpChunkAggregator( StackConfiguration.PIPELINE_MAX_QUERY_REQUEST_SIZE );
}
public static ChannelHandler addressingHandler( ) {//caching
return addressingHandler;
}
public static ChannelHandler newAddressingHandler( final String addressingPrefix ) {//caching
return new AddressingHandler( addressingPrefix );
}
private static final LoadingCache<String, BindingHandler> bindingHandlers = CacheBuilder.newBuilder().build(
new CacheLoader<String, BindingHandler>() {
@Override
public BindingHandler load( String bindingName ) {
String maybeBindingName = "";
if ( BindingManager.isRegisteredBinding( bindingName ) ) {
return new BindingHandler( BindingHandler.context( BindingManager.getBinding( bindingName ) ) );
} else if ( BindingManager.isRegisteredBinding( maybeBindingName = BindingManager.sanitizeNamespace( bindingName ) ) ) {
return new BindingHandler( BindingHandler.context( BindingManager.getBinding( maybeBindingName ) ) );
} else {
throw Exceptions.trace( "Failed to find registerd binding for name: " + bindingName
+ ". Also tried looking for sanitized name: "
+ maybeBindingName );
}
}
});
public static ChannelHandler bindingHandler( ) {
return bindingHandler;
}
public static ChannelHandler bindingHandler( final String bindingName ) {
return bindingHandlers.getUnchecked( bindingName );
}
public static ChannelHandler soapMarshalling( ) {
return soapMarshallingHandler;
}
public static ChannelHandler soapHandler( ) {
return soapHandler;
}
private static class NioSslHandler extends SslHandler {
private final AtomicBoolean first = new AtomicBoolean( true );
NioSslHandler( ) {
super( SslSetup.getServerEngine( ) );
}
private static List<String> httpVerbPrefix = Lists.newArrayList( HttpMethod.CONNECT.getName( ).substring( 0, 3 ),
HttpMethod.GET.getName( ).substring( 0, 3 ),
HttpMethod.PUT.getName( ).substring( 0, 3 ),
HttpMethod.POST.getName( ).substring( 0, 3 ),
HttpMethod.HEAD.getName( ).substring( 0, 3 ),
HttpMethod.OPTIONS.getName( ).substring( 0, 3 ),
HttpMethod.DELETE.getName( ).substring( 0, 3 ),
HttpMethod.TRACE.getName( ).substring( 0, 3 ) );
private static boolean maybeSsl( final ChannelBuffer buffer ) {
buffer.markReaderIndex( );
final StringBuffer sb = new StringBuffer( );
for ( int lineLength = 0; lineLength++ < 3; sb.append( ( char ) buffer.readByte( ) ) );
buffer.resetReaderIndex( );
return !httpVerbPrefix.contains( sb.toString( ) );
}
@Override
public void handleUpstream( final ChannelHandlerContext ctx, final ChannelEvent e ) throws Exception {
Object o = null;
if ( ( e instanceof MessageEvent )
&& this.first.compareAndSet( true, false )
&& ( ( o = ( ( MessageEvent ) e ).getMessage( ) ) instanceof ChannelBuffer )
&& !maybeSsl( ( ChannelBuffer ) o ) ) {
ctx.getPipeline( ).removeFirst( );
ctx.sendUpstream( e );
} else if ( !( e instanceof MessageEvent ) ) {
ctx.sendUpstream( e );
} else {
super.handleUpstream( ctx, e );
}
}
}
public static ChannelHandler internalServiceStateHandler( ) {
return ServiceStateChecksHandler.INSTANCE;
}
@ChannelHandler.Sharable
public enum ServiceStateChecksHandler implements ChannelUpstreamHandler {
INSTANCE {
@Override
public void handleUpstream( final ChannelHandlerContext ctx, final ChannelEvent e ) throws Exception {
final MappingHttpRequest request = MappingHttpMessage.extractMessage( e );
final BaseMessage msg = BaseMessage.extractMessage( e );
if ( msg != null ) {
try {
final Class<? extends ComponentId> compClass = ComponentMessages.lookup( msg );
ComponentId compId = ComponentIds.lookup( compClass );
if ( compId.isAlwaysLocal( ) || Topology.isEnabledLocally( compClass ) ) {
ctx.sendUpstream( e );
} else {
Handlers.sendRedirect( ctx, e, compClass, request );
}
} catch ( final NoSuchElementException ex ) {
LOG.warn( "Failed to find reverse component mapping for message type: " + msg.getClass( ) );
ctx.sendUpstream( e );
} catch ( final Exception ex ) {
Logs.extreme( ).error( ex, ex );
ctx.sendUpstream( e );
}
} else {
ctx.sendUpstream( e );
}
}
}
}
public static ChannelHandler internalEpochHandler( ) {
return MessageEpochChecks.INSTANCE;
}
@ChannelHandler.Sharable
enum MessageEpochChecks implements ChannelUpstreamHandler {
INSTANCE {
@Override
public void handleUpstream( final ChannelHandlerContext ctx, final ChannelEvent e ) throws Exception {
final MappingHttpRequest request = MappingHttpMessage.extractMessage( e );
final BaseMessage msg = BaseMessage.extractMessage( e );
if ( msg != null ) {
try {
if ( msg instanceof ServiceTransitionType && !Hosts.isCoordinator( ) ) {
//TODO:GRZE: extra epoch check and redirect
Topology.touch( ( ServiceTransitionType ) msg );
ctx.sendUpstream( e );
} else if ( Topology.check( msg ) ) {
ctx.sendUpstream( e );
} else {
final Class<? extends ComponentId> compClass = ComponentMessages.lookup( msg );
Handlers.sendRedirect( ctx, e, compClass, request );
}
} catch ( final Exception ex ) {
Logs.extreme( ).error( ex, ex );
ctx.sendUpstream( e );
}
}
}
};
}
private static final class ComponentMessageCheckHandler implements ChannelUpstreamHandler {
@Nullable
private final Class<? extends ComponentId> componentIdClass;
private ComponentMessageCheckHandler( final Class<? extends ComponentId> componentIdClass ) {
this.componentIdClass = componentIdClass == null ?
null :
ComponentIds.lookup( componentIdClass ).toApiClass( );
}
@Override
public void handleUpstream( final ChannelHandlerContext channelHandlerContext,
final ChannelEvent channelEvent ) throws Exception {
if ( channelEvent instanceof MessageEvent && componentIdClass != null ) {
final BaseMessage message = BaseMessage.extractMessage( channelEvent );
final ComponentMessage componentMessage = message==null ? null :
Ats.inClassHierarchy( message.getClass() ).get( ComponentMessage.class );
if ( message != null && (componentMessage == null || !componentIdClass.equals( componentMessage.value() ) ) ) {
LOG.warn( String.format("Message %s does not match pipeline component %s",
message.getClass(),
componentIdClass.getSimpleName() ) );
final BaseMessage baseMessage = BaseMessage.extractMessage( channelEvent );
if ( baseMessage != null ) {
Contexts.clear( Contexts.lookup( baseMessage.getCorrelationId()) );
}
throw new WebServicesException( HttpResponseStatus.FORBIDDEN );
}
}
channelHandlerContext.sendUpstream( channelEvent );
}
}
static void sendRedirect( final ChannelHandlerContext ctx, final ChannelEvent e, final Class<? extends ComponentId> compClass, final MappingHttpRequest request ) {
e.getFuture( ).cancel( );
String redirectUri = null;
if ( Topology.isEnabled( compClass ) ) {//have an enabled service, lets use that
final URI serviceUri = ServiceUris.remote( Topology.lookup( compClass ) );
redirectUri = serviceUri.toASCIIString( ) + request.getServicePath().replace( serviceUri.getPath( ), "" );
} else if ( Topology.isEnabled( Eucalyptus.class ) ) {//can't find service info, redirect via clc master
final URI serviceUri = ServiceUris.remote( Topology.lookup( Eucalyptus.class ) );
redirectUri = serviceUri.toASCIIString( ).replace( Eucalyptus.INSTANCE.getServicePath( ), "" ) + request.getServicePath().replace( serviceUri.getPath( ), "" );
}
HttpResponse response = null;
if ( redirectUri == null || isRedirectLoop( request, redirectUri ) ) {
response = new DefaultHttpResponse( HttpVersion.HTTP_1_1, HttpResponseStatus.SERVICE_UNAVAILABLE );
if ( Logs.isDebug( ) ) {
String errorMessage = "Failed to lookup service for " + Components.lookup( compClass ).getName( )
+ " for path "
+ request.getServicePath()
+ ".\nCurrent state: \n\t"
+ Joiner.on( "\n\t" ).join( Topology.enabledServices( ) );
byte[] errorBytes = Exceptions.string( new ServiceStateException( errorMessage ) ).getBytes( );
response.setContent( ChannelBuffers.wrappedBuffer( errorBytes ) );
}
} else {
response = new DefaultHttpResponse( HttpVersion.HTTP_1_1, HttpResponseStatus.TEMPORARY_REDIRECT );
if( request.getQuery() != null ) {
redirectUri += "?" + request.getQuery( );
}
response.setHeader( HttpHeaders.Names.LOCATION, redirectUri );
}
response.setHeader( HttpHeaders.Names.CONNECTION, HttpHeaders.Values.CLOSE );
if ( ctx.getChannel( ).isConnected( ) ) {
ctx.getChannel( )
.write( response )
.addListener( ChannelFutureListener.CLOSE );
}
}
public static ChannelHandler internalOnlyHandler( ) {
return InternalOnlyHandler.INSTANCE;
}
@ChannelHandler.Sharable
enum SocketLoggingHandler implements Runnable, ChannelUpstreamHandler, ChannelDownstreamHandler {
INSTANCE;
private final Joiner.MapJoiner joiner = Joiner.on(' ').useForNull( "null" ).withKeyValueSeparator( ":" );
private final Maps.EntryTransformer<String,Object,Object> transformer = new Maps.EntryTransformer<String, Object, Object>() {
@Override
public Object transformEntry( @Nullable String key, @Nullable Object value ) {
if ( key.startsWith( "time." ) ) {
return new Date( ( Long ) value ).toString();
} else if ( key.startsWith( "silent" ) ) {
return "";
} else {
return "" + value;
}
}
};
private final Object NULLPROXY = new Object();
SocketLoggingHandler() {
Threads.newThread( this, "Socket Monitoring" );
}
private static final ConcurrentMap<Channel,ConcurrentMap<String,Object>> channelDetails = Maps.newConcurrentMap();
@Override
public void run() {
while ( !Bootstrap.isShuttingDown() ) {
try {
TimeUnit.SECONDS.sleep( 10 );
} catch ( InterruptedException e ) {
return;
}
for ( Map<String, Object> info : channelDetails.values() ) {
LOG.info(joiner.join( info ));
}
}
}
@Override
public void handleDownstream( ChannelHandlerContext ctx, ChannelEvent e ) throws Exception {
try {
Map<String,Object> info = getChannelInfo( ctx );
if ( e instanceof ChannelStateEvent ) {
downstreamChannelStateEvent( ( ChannelStateEvent ) e, info );
}
} catch ( Exception e1 ) {
LOG.debug( e1 );
}
ctx.sendDownstream( e );
}
@Override
public void handleUpstream( final ChannelHandlerContext ctx, final ChannelEvent e ) throws Exception {
try {
ConcurrentMap<String,Object> info = getChannelInfo( ctx );
if ( e instanceof ChannelStateEvent ) {
upstreamChannelStateEvent( ( ChannelStateEvent ) e, info );
}
if ( ctx.getPipeline().getLast() instanceof AsyncRequestHandler ) {
AsyncRequestHandler requestHandler = ( AsyncRequestHandler ) ctx.getPipeline().getLast();
info.putIfAbsent( "pipeline", ctx.getPipeline().getClass() );
info.putIfAbsent( "silent.requesthandler", requestHandler );
if ( requestHandler != null && requestHandler.getRequest() != null && requestHandler.getRequest().get() != null ) {
info.putIfAbsent( "request.message", Objects.firstNonNull( requestHandler.getRequest().get(), NULLPROXY ).getClass() );
}
} else if ( e instanceof ExceptionEvent ) {//upstream only
info.putIfAbsent( "exception", ( ( ExceptionEvent ) e ).getCause() );
} else if ( e instanceof MessageEvent ) {
final Object message = Objects.firstNonNull( ( ( MessageEvent ) e ).getMessage(), NULLPROXY );
info.putIfAbsent( "silent.request", message );
info.putIfAbsent( "request.type", message.getClass() );
}
} catch ( Exception e1 ) {
LOG.debug( e1 );
}
ctx.sendUpstream( e );
}
private ConcurrentMap<String, Object> getChannelInfo( ChannelHandlerContext ctx ) {
ConcurrentMap<String,Object> info = channelDetails.putIfAbsent( ctx.getChannel(), Maps.<String, Object>newConcurrentMap() );
info = (info == null)?channelDetails.get( ctx.getChannel() ):info;
info.putIfAbsent( "time.start", System.currentTimeMillis() );
return info;
}
private void downstreamChannelStateEvent( ChannelStateEvent e, Map<String, Object> info ) {
ChannelStateEvent stateEvent = ( ChannelStateEvent ) e;
ChannelState state = stateEvent.getState();
switch( state ) {
case OPEN:
if ( Boolean.FALSE.equals( stateEvent.getValue() ) ) {
info.put( "time.close-requested", System.currentTimeMillis() );
}
break;
case BOUND:{
SocketAddress addr = ( SocketAddress ) stateEvent.getValue();
if ( addr != null ) {
info.put( "address.local-specified", addr );
info.put( "time.bind-requested", System.currentTimeMillis() );
} else {
info.put( "time.unbind-requested", System.currentTimeMillis() );
}
break;
}
case CONNECTED:{
SocketAddress addr = ( SocketAddress ) stateEvent.getValue();
if ( addr != null ) {
info.put( "address.local-specified", addr );
info.put( "time.connect-requested", System.currentTimeMillis() );
} else {
info.put( "time.disconnect-requested", System.currentTimeMillis() );
}
break;
}
case INTEREST_OPS:break;
}
}
private void upstreamChannelStateEvent( ChannelStateEvent e, Map<String, Object> info ) {
ChannelStateEvent stateEvent = ( ChannelStateEvent ) e;
ChannelState state = stateEvent.getState();
switch( state ) {
case OPEN: {
if ( Boolean.TRUE.equals( stateEvent.getValue() ) ) {
info.put( "time.opened", System.currentTimeMillis() );
} else {
info.put( "time.closed", System.currentTimeMillis() );
channelDetails.remove( e.getChannel() );
Map<String, Object> transformed = Maps.transformEntries( info, transformer );
LOG.info( e.getChannel().getId() + ": " + Joiner.on( " " ).withKeyValueSeparator( "=" ).join( transformed ) );
}
break;
}
case BOUND: {
SocketAddress addr = ( SocketAddress ) stateEvent.getValue();
if ( addr != null ) {
info.put( "address.local", addr );
info.put( "time.bound", System.currentTimeMillis() );
} else {
info.put( "time.unbound", System.currentTimeMillis() );
}
break;
}
case CONNECTED: {
SocketAddress addr = ( SocketAddress ) stateEvent.getValue();
if ( addr != null ) {
info.put( "address.local", addr );
info.put( "time.connected", System.currentTimeMillis() );
} else {
info.put( "time.disconnected", System.currentTimeMillis() );
}
break;
}
case INTEREST_OPS:break;
}
}
}
@ChannelHandler.Sharable
enum InternalOnlyHandler implements ChannelUpstreamHandler {
INSTANCE;
@Override
public void handleUpstream( final ChannelHandlerContext ctx, final ChannelEvent e ) throws Exception {
final MappingHttpMessage request = MappingHttpMessage.extractMessage( e );
final BaseMessage msg = BaseMessage.extractMessage( e );
if ( ( request != null ) && ( msg != null ) ) {
final Context context = Contexts.lookup( request.getCorrelationId( ) );
final User user = context.getUser( );
final AuthContextSupplier userContext = context.getAuthContext( );
if ( user.isSystemAdmin( ) ||
( context.isImpersonated( ) && isImpersonationSupported( msg ) ) ||
( ( user.isSystemUser( ) || ServiceOperations.isUserOperation( msg ) ) &&
Permissions.isAuthorized( findPolicyVendor( msg.getClass( ) ), "", "", null, getIamActionByMessageType( msg ), userContext ) ) ) {
ctx.sendUpstream( e );
} else {
Contexts.clear( Contexts.lookup( msg.getCorrelationId( ) ) );
throw new WebServicesException( HttpResponseStatus.FORBIDDEN );
}
} else {
ctx.sendUpstream( e );
}
}
}
public static void addComponentHandlers( final Class<? extends ComponentId> componentIdClass,
final ChannelPipeline pipeline ) {
pipeline.addLast( "msg-component-check", new ComponentMessageCheckHandler( componentIdClass ) );
}
public static void addSystemHandlers( final ChannelPipeline pipeline ) {
pipeline.addLast( "service-state-check", internalServiceStateHandler( ) );
pipeline.addLast( "service-specific-mangling", ServiceHackeryHandler.INSTANCE );
if ( StackConfiguration.ASYNC_OPERATIONS ) {
pipeline.addLast( "async-operations-execution-handler", serviceExecutionHandler( ) );
}
pipeline.addLast( "service-request-handler", ServiceAccessLoggingHandler.INSTANCE );
pipeline.addLast( "service-sink", new ServiceContextHandler( ) );
}
public static void addInternalSystemHandlers( ChannelPipeline pipeline ) {
pipeline.addLast( "internal-only-restriction", internalOnlyHandler( ) );
pipeline.addLast( "msg-epoch-check", internalEpochHandler( ) );
}
public static ExecutionHandler pipelineExecutionHandler( ) {
return pipelineExecutionHandler;
}
public static ExecutionHandler serviceExecutionHandler( ) {
return serviceExecutionHandler;
}
public static ChannelHandler queryTimestamphandler( ) {
return queryTimestampHandler;
}
public static ChannelHandler internalWsSecHandler( ) {
return internalWsSecHandler;
}
private static boolean isRedirectLoop( final MappingHttpRequest request,
final String uri ) {
final String requestHost = request.getHeader( HttpHeaders.Names.HOST );
final String requestPath = request.getServicePath( );
return
requestHost != null &&
requestPath != null &&
uri.contains( requestHost + requestPath );
}
private static boolean isImpersonationSupported( final BaseMessage message ) {
boolean impersonationSupported = false;
final ComponentMessage componentMessage = message==null ? null :
Ats.inClassHierarchy( message.getClass() ).get( ComponentMessage.class );
if ( componentMessage != null ) {
impersonationSupported = ComponentIds.lookup( componentMessage.value( ) ).isImpersonationSupported( );
}
return impersonationSupported;
}
}