/*************************************************************************
* (c) Copyright 2016 Hewlett Packard Enterprise Development Company LP
*
* 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/.
************************************************************************/
package com.eucalyptus.ws;
import java.net.URI;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import javax.net.ssl.SSLEngine;
import com.eucalyptus.binding.BindingManager;
import com.eucalyptus.crypto.util.SslSetup;
import com.eucalyptus.http.MappingHttpRequest;
import com.eucalyptus.util.Exceptions;
import com.eucalyptus.ws.handlers.IoAddressingHandler;
import com.eucalyptus.ws.handlers.IoBindingHandler;
import com.eucalyptus.ws.handlers.IoInternalHmacHandler;
import com.eucalyptus.ws.handlers.IoInternalWsSecHandler;
import com.eucalyptus.ws.handlers.IoMessageWrapperHandler;
import com.eucalyptus.ws.handlers.IoSoapMarshallingHandler;
import com.eucalyptus.ws.handlers.IoSoapHandler;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.Maps;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.ChannelOutboundHandlerAdapter;
import io.netty.channel.ChannelPromise;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpRequestEncoder;
import io.netty.handler.codec.http.HttpResponseDecoder;
import io.netty.handler.ssl.SslHandler;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import io.netty.handler.timeout.IdleStateHandler;
/**
*
*/
public class IoHandlers {
public static final Map<String,ChannelHandler> extraMonitors = Maps.newConcurrentMap();
private static final ChannelHandler soapHandler = new IoSoapHandler( );
private static final ChannelHandler soapMarshallingHandler = new IoSoapMarshallingHandler( );
private static final ChannelHandler internalWsSecHandler = new IoInternalWsSecHandler( );
private static final ChannelHandler internalHmacHandler = new IoInternalHmacHandler( );
private static final ChannelHandler addressingHandler = new IoAddressingHandler( );
private static final ChannelHandler ioMessageHandler = new IoMessageWrapperHandler( );
private static final ChannelHandler bindingHandler = new IoBindingHandler( IoBindingHandler.context( BindingManager.getDefaultBinding( ) ) );
public static ChannelHandler internalHttpsHandler( ) { return new SslHandler( SslSetup.getClientEngine( ) ); };
public static ChannelHandler httpRequestEncoder( ) {
return new HttpRequestEncoder( );
}
public static ChannelHandler httpResponseDecoder( ) {
return new HttpResponseDecoder( );
}
public static ChannelHandler newHttpChunkAggregator( ) {
return new HttpObjectAggregator( StackConfiguration.CLIENT_HTTP_CHUNK_BUFFER_MAX );
}
public static ChannelHandler newQueryHttpChunkAggregator( ) {
return new HttpObjectAggregator( StackConfiguration.PIPELINE_MAX_QUERY_REQUEST_SIZE );
}
public static ChannelHandler soapHandler( ) {
return soapHandler;
}
public static ChannelHandler soapMarshalling( ) {
return soapMarshallingHandler;
}
public static ChannelHandler internalWsSecHandler( ) {
return internalWsSecHandler;
}
public static ChannelHandler getInternalHmacHandler( ) {
return internalHmacHandler;
}
public static ChannelHandler addressingHandler( ) {
return addressingHandler;
}
public static ChannelHandler addressingHandler( final String prefix ) {
return new IoAddressingHandler( prefix );
}
public static ChannelHandler bindingHandler( ) {
return bindingHandler;
}
public static ChannelHandler bindingHandler( final String bindingName ) {
return bindingHandlers.getUnchecked( bindingName );
}
public static ChannelHandler ioMessageWrappingHandler( ) {
return ioMessageHandler;
}
private static final LoadingCache<String, IoBindingHandler> bindingHandlers = CacheBuilder.newBuilder().build(
new CacheLoader<String, IoBindingHandler>() {
@Override
public IoBindingHandler load( String bindingName ) {
String maybeBindingName = "";
if ( BindingManager.isRegisteredBinding( bindingName ) ) {
return new IoBindingHandler( IoBindingHandler.context( BindingManager.getBinding( bindingName ) ) );
} else if ( BindingManager.isRegisteredBinding( maybeBindingName = BindingManager.sanitizeNamespace( bindingName ) ) ) {
return new IoBindingHandler( IoBindingHandler.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 Map<String, ChannelHandler> channelMonitors( final TimeUnit unit, final long timeout ) {
final Map<String, ChannelHandler> monitors = Maps.newHashMap( );
monitors.put( "idlehandler", new IdleStateHandler( 0L, 0L, timeout, unit ) );
monitors.put( "idlecloser", new ChannelInboundHandlerAdapter() {
@Override
public void userEventTriggered( final ChannelHandlerContext ctx, final Object evt ) throws Exception {
if ( evt instanceof IdleStateEvent ) {
IdleStateEvent e = (IdleStateEvent) evt;
if ( e.state() == IdleState.ALL_IDLE ) {
ctx.channel( ).close( );
}
}
}
} );
monitors.putAll( extraMonitors );
return monitors;
}
public abstract static class ClientSslHandler extends ChannelOutboundHandlerAdapter {
private final String sslHandlerName;
public ClientSslHandler( final String sslHandlerName ) {
this.sslHandlerName = sslHandlerName;
}
protected abstract SSLEngine createSSLEngine( final String peerHost, final int peerPort );
@Override
public void write( final ChannelHandlerContext ctx, final Object msg, final ChannelPromise promise ) throws Exception {
if ( msg instanceof MappingHttpRequest ) {
ctx.pipeline( ).remove( this );
if ( isHttps( (MappingHttpRequest)msg ) ) {
final URI uri = URI.create( ( (MappingHttpRequest) msg ).getUri( ) );
final SslHandler sslHandler =
new SslHandler( createSSLEngine( uri.getHost( ), uri.getPort( )==-1?443:uri.getPort( ) ) );
ctx.pipeline( ).addFirst( sslHandlerName, sslHandler );
}
}
super.write( ctx, msg, promise );
}
private boolean isHttps( final MappingHttpRequest o ) {
return !o.getUri( ).startsWith( "http://" );
}
}
}