org.dcache.xrootd.core.XrootdRequestHandler.java Source code

Java tutorial

Introduction

Here is the source code for org.dcache.xrootd.core.XrootdRequestHandler.java

Source

/**
 * Copyright (C) 2011-2018 dCache.org <support@dcache.org>
 *
 * This file is part of xrootd4j.
 *
 * xrootd4j is free software: you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * xrootd4j 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with xrootd4j.  If not, see http://www.gnu.org/licenses/.
 */
package org.dcache.xrootd.core;

import com.google.common.net.InetAddresses;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.codec.haproxy.HAProxyMessage;
import io.netty.handler.codec.haproxy.HAProxyProxiedProtocol;
import io.netty.util.ReferenceCountUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.net.InetSocketAddress;
import java.util.Objects;

import org.dcache.xrootd.protocol.messages.AuthenticationRequest;
import org.dcache.xrootd.protocol.messages.CloseRequest;
import org.dcache.xrootd.protocol.messages.DirListRequest;
import org.dcache.xrootd.protocol.messages.EndSessionRequest;
import org.dcache.xrootd.protocol.messages.ErrorResponse;
import org.dcache.xrootd.protocol.messages.LocateRequest;
import org.dcache.xrootd.protocol.messages.LoginRequest;
import org.dcache.xrootd.protocol.messages.MkDirRequest;
import org.dcache.xrootd.protocol.messages.MvRequest;
import org.dcache.xrootd.protocol.messages.OkResponse;
import org.dcache.xrootd.protocol.messages.OpenRequest;
import org.dcache.xrootd.protocol.messages.PrepareRequest;
import org.dcache.xrootd.protocol.messages.ProtocolRequest;
import org.dcache.xrootd.protocol.messages.QueryRequest;
import org.dcache.xrootd.protocol.messages.ReadRequest;
import org.dcache.xrootd.protocol.messages.ReadVRequest;
import org.dcache.xrootd.protocol.messages.RmDirRequest;
import org.dcache.xrootd.protocol.messages.RmRequest;
import org.dcache.xrootd.protocol.messages.SetRequest;
import org.dcache.xrootd.protocol.messages.StatRequest;
import org.dcache.xrootd.protocol.messages.StatxRequest;
import org.dcache.xrootd.protocol.messages.SyncRequest;
import org.dcache.xrootd.protocol.messages.WriteRequest;
import org.dcache.xrootd.protocol.messages.XrootdRequest;
import org.dcache.xrootd.protocol.messages.XrootdResponse;

import static org.dcache.xrootd.protocol.XrootdProtocol.*;

/**
 * A ChannelInboundHandler to dispatch xrootd events to handler methods.
 *
 * Default response to all request messages from a client is
 * kXR_Unsupported. Sub-classes may override handler methods to
 * implement request handling.
 *
 * Releases the reference to XrootdRequest if the handler method throws
 * an exception or returns a response. If the handler returns null the
 * subclass assumes responsibility to release the request, typically
 * by passing it on the next ChannelHandler in the pipeline.
 */
public class XrootdRequestHandler extends ChannelInboundHandlerAdapter {
    private static final Logger _log = LoggerFactory.getLogger(XrootdRequestHandler.class);

    private boolean _isHealthCheck;

    private InetSocketAddress _destinationAddress;

    private InetSocketAddress _sourceAddress;

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        _destinationAddress = (InetSocketAddress) ctx.channel().localAddress();
        _sourceAddress = (InetSocketAddress) ctx.channel().remoteAddress();
        super.channelActive(ctx);
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        if (msg instanceof XrootdRequest) {
            requestReceived(ctx, (XrootdRequest) msg);
        } else if (msg instanceof HAProxyMessage) {
            HAProxyMessage proxyMessage = (HAProxyMessage) msg;
            switch (proxyMessage.command()) {
            case LOCAL:
                _isHealthCheck = true;
                break;
            case PROXY:
                String sourceAddress = proxyMessage.sourceAddress();
                String destinationAddress = proxyMessage.destinationAddress();
                InetSocketAddress localAddress = (InetSocketAddress) ctx.channel().localAddress();
                if (proxyMessage.proxiedProtocol() == HAProxyProxiedProtocol.TCP4
                        || proxyMessage.proxiedProtocol() == HAProxyProxiedProtocol.TCP6) {
                    if (Objects.equals(destinationAddress, localAddress.getAddress().getHostAddress())) {
                        /* Workaround for what looks like a bug in HAProxy - health checks should
                         * generate a LOCAL command, but it appears they do actually use PROXY.
                         */
                        _isHealthCheck = true;
                    } else {
                        _destinationAddress = new InetSocketAddress(InetAddresses.forString(destinationAddress),
                                proxyMessage.destinationPort());
                        _sourceAddress = new InetSocketAddress(InetAddresses.forString(sourceAddress),
                                proxyMessage.sourcePort());
                    }
                }
                break;
            }
            ctx.fireChannelRead(msg);
        } else {
            ctx.fireChannelRead(msg);
        }
    }

    protected void requestReceived(ChannelHandlerContext ctx, XrootdRequest req) {
        try {
            Object response;
            switch (req.getRequestId()) {
            case kXR_auth:
                response = doOnAuthentication(ctx, (AuthenticationRequest) req);
                break;
            case kXR_login:
                response = doOnLogin(ctx, (LoginRequest) req);
                break;
            case kXR_open:
                response = doOnOpen(ctx, (OpenRequest) req);
                break;
            case kXR_stat:
                response = doOnStat(ctx, (StatRequest) req);
                break;
            case kXR_statx:
                response = doOnStatx(ctx, (StatxRequest) req);
                break;
            case kXR_read:
                response = doOnRead(ctx, (ReadRequest) req);
                break;
            case kXR_readv:
                response = doOnReadV(ctx, (ReadVRequest) req);
                break;
            case kXR_write:
                response = doOnWrite(ctx, (WriteRequest) req);
                break;
            case kXR_sync:
                response = doOnSync(ctx, (SyncRequest) req);
                break;
            case kXR_close:
                response = doOnClose(ctx, (CloseRequest) req);
                break;
            case kXR_protocol:
                response = doOnProtocolRequest(ctx, (ProtocolRequest) req);
                break;
            case kXR_rm:
                response = doOnRm(ctx, (RmRequest) req);
                break;
            case kXR_rmdir:
                response = doOnRmDir(ctx, (RmDirRequest) req);
                break;
            case kXR_mkdir:
                response = doOnMkDir(ctx, (MkDirRequest) req);
                break;
            case kXR_mv:
                response = doOnMv(ctx, (MvRequest) req);
                break;
            case kXR_dirlist:
                response = doOnDirList(ctx, (DirListRequest) req);
                break;
            case kXR_prepare:
                response = doOnPrepare(ctx, (PrepareRequest) req);
                break;
            case kXR_locate:
                response = doOnLocate(ctx, (LocateRequest) req);
                break;
            case kXR_query:
                response = doOnQuery(ctx, (QueryRequest) req);
                break;
            case kXR_set:
                response = doOnSet(ctx, (SetRequest) req);
                break;
            case kXR_endsess:
                response = doOnEndSession(ctx, (EndSessionRequest) req);
                break;
            default:
                response = unsupported(ctx, req);
                break;
            }
            if (response != null) {
                respond(ctx, response);
            } else {
                req = null; // Do not release reference
            }
        } catch (XrootdException e) {
            respond(ctx, withError(req, e.getError(), e.getMessage()));
        } catch (RuntimeException e) {
            _log.error(
                    "xrootd server error while processing " + req + " (please report this to support@dcache.org)",
                    e);
            respond(ctx,
                    withError(req, kXR_ServerError, String.format("Internal server error (%s)", e.getMessage())));
        } finally {
            ReferenceCountUtil.release(req);
        }
    }

    protected <T extends XrootdRequest> OkResponse<T> withOk(T req) {
        return new OkResponse<>(req);
    }

    protected <T extends XrootdRequest> ErrorResponse<T> withError(T req, int errorCode, String errMsg) {
        return new ErrorResponse<>(req, errorCode, errMsg);
    }

    protected ChannelFuture respond(ChannelHandlerContext ctx, Object response) {
        return ctx.writeAndFlush(response).addListener(ChannelFutureListener.FIRE_EXCEPTION_ON_FAILURE);
    }

    protected <T extends XrootdRequest> XrootdResponse<T> unsupported(ChannelHandlerContext ctx, T msg)
            throws XrootdException {
        _log.warn("Unsupported request: " + msg);
        throw new XrootdException(kXR_Unsupported, "Request " + msg.getRequestId() + " not supported");
    }

    protected Object doOnLogin(ChannelHandlerContext ctx, LoginRequest msg) throws XrootdException {
        return unsupported(ctx, msg);
    }

    protected Object doOnAuthentication(ChannelHandlerContext ctx, AuthenticationRequest msg)
            throws XrootdException {
        return unsupported(ctx, msg);
    }

    protected Object doOnOpen(ChannelHandlerContext ctx, OpenRequest msg) throws XrootdException {
        return unsupported(ctx, msg);
    }

    protected Object doOnStat(ChannelHandlerContext ctx, StatRequest msg) throws XrootdException {
        return unsupported(ctx, msg);
    }

    protected Object doOnStatx(ChannelHandlerContext ctx, StatxRequest msg) throws XrootdException {
        return unsupported(ctx, msg);
    }

    protected Object doOnRead(ChannelHandlerContext ctx, ReadRequest msg) throws XrootdException {
        return unsupported(ctx, msg);
    }

    protected Object doOnReadV(ChannelHandlerContext ctx, ReadVRequest msg) throws XrootdException {
        return unsupported(ctx, msg);
    }

    protected Object doOnWrite(ChannelHandlerContext ctx, WriteRequest msg) throws XrootdException {
        return unsupported(ctx, msg);
    }

    protected Object doOnSync(ChannelHandlerContext ctx, SyncRequest msg) throws XrootdException {
        return unsupported(ctx, msg);
    }

    protected Object doOnClose(ChannelHandlerContext ctx, CloseRequest msg) throws XrootdException {
        return unsupported(ctx, msg);
    }

    protected Object doOnProtocolRequest(ChannelHandlerContext ctx, ProtocolRequest msg) throws XrootdException {
        return unsupported(ctx, msg);
    }

    protected Object doOnRm(ChannelHandlerContext ctx, RmRequest msg) throws XrootdException {
        return unsupported(ctx, msg);
    }

    protected Object doOnRmDir(ChannelHandlerContext ctx, RmDirRequest msg) throws XrootdException {
        return unsupported(ctx, msg);
    }

    protected Object doOnMkDir(ChannelHandlerContext ctx, MkDirRequest msg) throws XrootdException {
        return unsupported(ctx, msg);
    }

    protected Object doOnMv(ChannelHandlerContext ctx, MvRequest msg) throws XrootdException {
        return unsupported(ctx, msg);
    }

    protected Object doOnDirList(ChannelHandlerContext ctx, DirListRequest msg) throws XrootdException {
        return unsupported(ctx, msg);
    }

    protected Object doOnPrepare(ChannelHandlerContext ctx, PrepareRequest msg) throws XrootdException {
        return unsupported(ctx, msg);
    }

    protected Object doOnLocate(ChannelHandlerContext ctx, LocateRequest msg) throws XrootdException {
        return unsupported(ctx, msg);
    }

    protected Object doOnQuery(ChannelHandlerContext ctx, QueryRequest msg) throws XrootdException {
        return unsupported(ctx, msg);
    }

    protected Object doOnSet(ChannelHandlerContext ctx, SetRequest request) throws XrootdException {
        return unsupported(ctx, request);
    }

    protected Object doOnEndSession(ChannelHandlerContext ctx, EndSessionRequest request) throws XrootdException {
        return unsupported(ctx, request);
    }

    /**
     * The socket address the client connected to. May be the local address
     * of the channel, but could also be an address on a proxy server
     * between the client and the server.
     */
    protected InetSocketAddress getDestinationAddress() {
        return _destinationAddress;
    }

    /**
     * The socket address the client connected from. May be the remote address
     * of the channel, but in case a proxy is in between the client and the
     * server, the source address will be a different from the remote address.
     */
    protected InetSocketAddress getSourceAddress() {
        return _sourceAddress;
    }

    /**
     * True if this looks like a health check connection from a proxy server.
     */
    protected boolean isHealthCheck() {
        return _isHealthCheck;
    }
}