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.common.JspHelper; 039import org.apache.hadoop.hdfs.server.datanode.BlockScanner; 040import org.apache.hadoop.hdfs.server.datanode.DataNode; 041import org.apache.hadoop.hdfs.server.namenode.FileChecksumServlets; 042import org.apache.hadoop.hdfs.server.namenode.StreamFile; 043import org.apache.hadoop.http.HttpConfig; 044import org.apache.hadoop.http.HttpServer2; 045import org.apache.hadoop.net.NetUtils; 046import org.apache.hadoop.security.authorize.AccessControlList; 047import org.apache.hadoop.security.ssl.SSLFactory; 048 049import java.io.Closeable; 050import java.io.IOException; 051import java.net.InetSocketAddress; 052import java.net.SocketAddress; 053import java.net.URI; 054import java.nio.channels.ServerSocketChannel; 055import java.security.GeneralSecurityException; 056 057import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_ADMIN; 058import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_DATANODE_HTTPS_ADDRESS_DEFAULT; 059import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_DATANODE_HTTPS_ADDRESS_KEY; 060import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_DATANODE_HTTP_ADDRESS_KEY; 061 062public class DatanodeHttpServer implements Closeable { 063 private final HttpServer2 infoServer; 064 private final EventLoopGroup bossGroup; 065 private final EventLoopGroup workerGroup; 066 private final ServerSocketChannel externalHttpChannel; 067 private final ServerBootstrap httpServer; 068 private final SSLFactory sslFactory; 069 private final ServerBootstrap httpsServer; 070 private final Configuration conf; 071 private final Configuration confForCreate; 072 private InetSocketAddress httpAddress; 073 private InetSocketAddress httpsAddress; 074 075 static final Log LOG = LogFactory.getLog(DatanodeHttpServer.class); 076 077 public DatanodeHttpServer(final Configuration conf, 078 final DataNode datanode, 079 final ServerSocketChannel externalHttpChannel) 080 throws IOException { 081 this.conf = conf; 082 083 Configuration confForInfoServer = new Configuration(conf); 084 confForInfoServer.setInt(HttpServer2.HTTP_MAX_THREADS, 10); 085 HttpServer2.Builder builder = new HttpServer2.Builder() 086 .setName("datanode") 087 .setConf(confForInfoServer) 088 .setACL(new AccessControlList(conf.get(DFS_ADMIN, " "))) 089 .hostName(getHostnameForSpnegoPrincipal(confForInfoServer)) 090 .addEndpoint(URI.create("http://localhost:0")) 091 .setFindPort(true); 092 093 this.infoServer = builder.build(); 094 this.infoServer.setAttribute(HttpServer2.CONF_CONTEXT_ATTRIBUTE, conf); 095 096 this.infoServer.addInternalServlet(null, "/streamFile/*", StreamFile.class); 097 this.infoServer.addInternalServlet(null, "/getFileChecksum/*", 098 FileChecksumServlets.GetServlet.class); 099 100 this.infoServer.setAttribute("datanode", datanode); 101 this.infoServer.setAttribute(JspHelper.CURRENT_CONF, conf); 102 this.infoServer.addServlet(null, "/blockScannerReport", 103 BlockScanner.Servlet.class); 104 105 this.infoServer.start(); 106 final InetSocketAddress jettyAddr = infoServer.getConnectorAddress(0); 107 108 this.confForCreate = new Configuration(conf); 109 confForCreate.set(FsPermission.UMASK_LABEL, "000"); 110 111 this.bossGroup = new NioEventLoopGroup(); 112 this.workerGroup = new NioEventLoopGroup(); 113 this.externalHttpChannel = externalHttpChannel; 114 HttpConfig.Policy policy = DFSUtil.getHttpPolicy(conf); 115 116 if (policy.isHttpEnabled()) { 117 this.httpServer = new ServerBootstrap().group(bossGroup, workerGroup) 118 .childHandler(new ChannelInitializer<SocketChannel>() { 119 @Override 120 protected void initChannel(SocketChannel ch) throws Exception { 121 ChannelPipeline p = ch.pipeline(); 122 p.addLast(new HttpRequestDecoder(), 123 new HttpResponseEncoder(), 124 new ChunkedWriteHandler(), 125 new URLDispatcher(jettyAddr, conf, confForCreate)); 126 } 127 }); 128 if (externalHttpChannel == null) { 129 httpServer.channel(NioServerSocketChannel.class); 130 } else { 131 httpServer.channelFactory(new ChannelFactory<NioServerSocketChannel>() { 132 @Override 133 public NioServerSocketChannel newChannel() { 134 return new NioServerSocketChannel(externalHttpChannel) { 135 // The channel has been bounded externally via JSVC, 136 // thus bind() becomes a no-op. 137 @Override 138 protected void doBind(SocketAddress localAddress) throws Exception {} 139 }; 140 } 141 }); 142 } 143 } else { 144 this.httpServer = null; 145 } 146 147 if (policy.isHttpsEnabled()) { 148 this.sslFactory = new SSLFactory(SSLFactory.Mode.SERVER, conf); 149 try { 150 sslFactory.init(); 151 } catch (GeneralSecurityException e) { 152 throw new IOException(e); 153 } 154 this.httpsServer = new ServerBootstrap().group(bossGroup, workerGroup) 155 .channel(NioServerSocketChannel.class) 156 .childHandler(new ChannelInitializer<SocketChannel>() { 157 @Override 158 protected void initChannel(SocketChannel ch) throws Exception { 159 ChannelPipeline p = ch.pipeline(); 160 p.addLast( 161 new SslHandler(sslFactory.createSSLEngine()), 162 new HttpRequestDecoder(), 163 new HttpResponseEncoder(), 164 new ChunkedWriteHandler(), 165 new URLDispatcher(jettyAddr, conf, confForCreate)); 166 } 167 }); 168 } else { 169 this.httpsServer = null; 170 this.sslFactory = null; 171 } 172 } 173 174 public InetSocketAddress getHttpAddress() { 175 return httpAddress; 176 } 177 178 public InetSocketAddress getHttpsAddress() { 179 return httpsAddress; 180 } 181 182 public void start() { 183 if (httpServer != null) { 184 ChannelFuture f = httpServer.bind(DataNode.getInfoAddr(conf)); 185 f.syncUninterruptibly(); 186 httpAddress = (InetSocketAddress) f.channel().localAddress(); 187 LOG.info("Listening HTTP traffic on " + httpAddress); 188 } 189 190 if (httpsServer != null) { 191 InetSocketAddress secInfoSocAddr = NetUtils.createSocketAddr(conf.getTrimmed( 192 DFS_DATANODE_HTTPS_ADDRESS_KEY, DFS_DATANODE_HTTPS_ADDRESS_DEFAULT)); 193 ChannelFuture f = httpsServer.bind(secInfoSocAddr); 194 f.syncUninterruptibly(); 195 httpsAddress = (InetSocketAddress) f.channel().localAddress(); 196 LOG.info("Listening HTTPS traffic on " + httpsAddress); 197 } 198 } 199 200 @Override 201 public void close() throws IOException { 202 bossGroup.shutdownGracefully(); 203 workerGroup.shutdownGracefully(); 204 if (sslFactory != null) { 205 sslFactory.destroy(); 206 } 207 if (externalHttpChannel != null) { 208 externalHttpChannel.close(); 209 } 210 try { 211 infoServer.stop(); 212 } catch (Exception e) { 213 throw new IOException(e); 214 } 215 } 216 217 private static String getHostnameForSpnegoPrincipal(Configuration conf) { 218 String addr = conf.getTrimmed(DFS_DATANODE_HTTP_ADDRESS_KEY, null); 219 if (addr == null) { 220 addr = conf.getTrimmed(DFS_DATANODE_HTTPS_ADDRESS_KEY, 221 DFS_DATANODE_HTTPS_ADDRESS_DEFAULT); 222 } 223 InetSocketAddress inetSocker = NetUtils.createSocketAddr(addr); 224 return inetSocker.getHostString(); 225 } 226}