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}