001/**
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.apache.hadoop.hdfs.server.datanode.web;
019
020import io.netty.bootstrap.ChannelFactory;
021import io.netty.bootstrap.ServerBootstrap;
022import io.netty.channel.ChannelFuture;
023import io.netty.channel.ChannelInitializer;
024import io.netty.channel.ChannelPipeline;
025import io.netty.channel.EventLoopGroup;
026import io.netty.channel.nio.NioEventLoopGroup;
027import io.netty.channel.socket.SocketChannel;
028import io.netty.channel.socket.nio.NioServerSocketChannel;
029import io.netty.handler.codec.http.HttpRequestDecoder;
030import io.netty.handler.codec.http.HttpResponseEncoder;
031import io.netty.handler.ssl.SslHandler;
032import io.netty.handler.stream.ChunkedWriteHandler;
033import org.apache.commons.logging.Log;
034import org.apache.commons.logging.LogFactory;
035import org.apache.hadoop.conf.Configuration;
036import org.apache.hadoop.fs.permission.FsPermission;
037import org.apache.hadoop.hdfs.DFSUtil;
038import org.apache.hadoop.hdfs.server.datanode.DataNode;
039import org.apache.hadoop.http.HttpConfig;
040import org.apache.hadoop.net.NetUtils;
041import org.apache.hadoop.security.ssl.SSLFactory;
042
043import java.io.Closeable;
044import java.io.IOException;
045import java.net.InetSocketAddress;
046import java.net.SocketAddress;
047import java.nio.channels.ServerSocketChannel;
048import java.security.GeneralSecurityException;
049
050import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_DATANODE_HTTPS_ADDRESS_DEFAULT;
051import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_DATANODE_HTTPS_ADDRESS_KEY;
052
053public class DatanodeHttpServer implements Closeable {
054  private final EventLoopGroup bossGroup;
055  private final EventLoopGroup workerGroup;
056  private final ServerSocketChannel externalHttpChannel;
057  private final ServerBootstrap httpServer;
058  private final SSLFactory sslFactory;
059  private final ServerBootstrap httpsServer;
060  private final Configuration conf;
061  private final Configuration confForCreate;
062  private InetSocketAddress httpAddress;
063  private InetSocketAddress httpsAddress;
064
065  static final Log LOG = LogFactory.getLog(DatanodeHttpServer.class);
066
067  public DatanodeHttpServer(final Configuration conf, final InetSocketAddress
068    jettyAddr, final ServerSocketChannel externalHttpChannel)
069    throws IOException {
070    this.conf = conf;
071    this.confForCreate = new Configuration(conf);
072    confForCreate.set(FsPermission.UMASK_LABEL, "000");
073
074    this.bossGroup = new NioEventLoopGroup();
075    this.workerGroup = new NioEventLoopGroup();
076    this.externalHttpChannel = externalHttpChannel;
077    HttpConfig.Policy policy = DFSUtil.getHttpPolicy(conf);
078
079    if (policy.isHttpEnabled()) {
080      this.httpServer = new ServerBootstrap().group(bossGroup, workerGroup)
081        .childHandler(new ChannelInitializer<SocketChannel>() {
082        @Override
083        protected void initChannel(SocketChannel ch) throws Exception {
084          ChannelPipeline p = ch.pipeline();
085          p.addLast(new HttpRequestDecoder(),
086            new HttpResponseEncoder(),
087            new ChunkedWriteHandler(),
088            new URLDispatcher(jettyAddr, conf, confForCreate));
089        }
090      });
091      if (externalHttpChannel == null) {
092        httpServer.channel(NioServerSocketChannel.class);
093      } else {
094        httpServer.channelFactory(new ChannelFactory<NioServerSocketChannel>() {
095          @Override
096          public NioServerSocketChannel newChannel() {
097            return new NioServerSocketChannel(externalHttpChannel) {
098              // The channel has been bounded externally via JSVC,
099              // thus bind() becomes a no-op.
100              @Override
101              protected void doBind(SocketAddress localAddress) throws Exception {}
102            };
103          }
104        });
105      }
106    } else {
107      this.httpServer = null;
108    }
109
110    if (policy.isHttpsEnabled()) {
111      this.sslFactory = new SSLFactory(SSLFactory.Mode.SERVER, conf);
112      try {
113        sslFactory.init();
114      } catch (GeneralSecurityException e) {
115        throw new IOException(e);
116      }
117      this.httpsServer = new ServerBootstrap().group(bossGroup, workerGroup)
118        .channel(NioServerSocketChannel.class)
119        .childHandler(new ChannelInitializer<SocketChannel>() {
120          @Override
121          protected void initChannel(SocketChannel ch) throws Exception {
122            ChannelPipeline p = ch.pipeline();
123            p.addLast(
124              new SslHandler(sslFactory.createSSLEngine()),
125              new HttpRequestDecoder(),
126              new HttpResponseEncoder(),
127              new ChunkedWriteHandler(),
128              new URLDispatcher(jettyAddr, conf, confForCreate));
129          }
130        });
131    } else {
132      this.httpsServer = null;
133      this.sslFactory = null;
134    }
135  }
136
137  public InetSocketAddress getHttpAddress() {
138    return httpAddress;
139  }
140
141  public InetSocketAddress getHttpsAddress() {
142    return httpsAddress;
143  }
144
145  public void start() {
146    if (httpServer != null) {
147      ChannelFuture f = httpServer.bind(DataNode.getInfoAddr(conf));
148      f.syncUninterruptibly();
149      httpAddress = (InetSocketAddress) f.channel().localAddress();
150      LOG.info("Listening HTTP traffic on " + httpAddress);
151    }
152
153    if (httpsServer != null) {
154      InetSocketAddress secInfoSocAddr = NetUtils.createSocketAddr(conf.getTrimmed(
155        DFS_DATANODE_HTTPS_ADDRESS_KEY, DFS_DATANODE_HTTPS_ADDRESS_DEFAULT));
156      ChannelFuture f = httpsServer.bind(secInfoSocAddr);
157      f.syncUninterruptibly();
158      httpsAddress = (InetSocketAddress) f.channel().localAddress();
159      LOG.info("Listening HTTPS traffic on " + httpsAddress);
160    }
161  }
162
163  @Override
164  public void close() throws IOException {
165    bossGroup.shutdownGracefully();
166    workerGroup.shutdownGracefully();
167    if (sslFactory != null) {
168      sslFactory.destroy();
169    }
170    if (externalHttpChannel != null) {
171      externalHttpChannel.close();
172    }
173  }
174}