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 095 this.infoServer.addInternalServlet(null, "/streamFile/*", StreamFile.class); 096 this.infoServer.addInternalServlet(null, "/getFileChecksum/*", 097 FileChecksumServlets.GetServlet.class); 098 099 this.infoServer.setAttribute("datanode", datanode); 100 this.infoServer.setAttribute(JspHelper.CURRENT_CONF, conf); 101 this.infoServer.addServlet(null, "/blockScannerReport", 102 BlockScanner.Servlet.class); 103 104 this.infoServer.start(); 105 final InetSocketAddress jettyAddr = infoServer.getConnectorAddress(0); 106 107 this.confForCreate = new Configuration(conf); 108 confForCreate.set(FsPermission.UMASK_LABEL, "000"); 109 110 this.bossGroup = new NioEventLoopGroup(); 111 this.workerGroup = new NioEventLoopGroup(); 112 this.externalHttpChannel = externalHttpChannel; 113 HttpConfig.Policy policy = DFSUtil.getHttpPolicy(conf); 114 115 if (policy.isHttpEnabled()) { 116 this.httpServer = new ServerBootstrap().group(bossGroup, workerGroup) 117 .childHandler(new ChannelInitializer<SocketChannel>() { 118 @Override 119 protected void initChannel(SocketChannel ch) throws Exception { 120 ChannelPipeline p = ch.pipeline(); 121 p.addLast(new HttpRequestDecoder(), 122 new HttpResponseEncoder(), 123 new ChunkedWriteHandler(), 124 new URLDispatcher(jettyAddr, conf, confForCreate)); 125 } 126 }); 127 if (externalHttpChannel == null) { 128 httpServer.channel(NioServerSocketChannel.class); 129 } else { 130 httpServer.channelFactory(new ChannelFactory<NioServerSocketChannel>() { 131 @Override 132 public NioServerSocketChannel newChannel() { 133 return new NioServerSocketChannel(externalHttpChannel) { 134 // The channel has been bounded externally via JSVC, 135 // thus bind() becomes a no-op. 136 @Override 137 protected void doBind(SocketAddress localAddress) throws Exception {} 138 }; 139 } 140 }); 141 } 142 } else { 143 this.httpServer = null; 144 } 145 146 if (policy.isHttpsEnabled()) { 147 this.sslFactory = new SSLFactory(SSLFactory.Mode.SERVER, conf); 148 try { 149 sslFactory.init(); 150 } catch (GeneralSecurityException e) { 151 throw new IOException(e); 152 } 153 this.httpsServer = new ServerBootstrap().group(bossGroup, workerGroup) 154 .channel(NioServerSocketChannel.class) 155 .childHandler(new ChannelInitializer<SocketChannel>() { 156 @Override 157 protected void initChannel(SocketChannel ch) throws Exception { 158 ChannelPipeline p = ch.pipeline(); 159 p.addLast( 160 new SslHandler(sslFactory.createSSLEngine()), 161 new HttpRequestDecoder(), 162 new HttpResponseEncoder(), 163 new ChunkedWriteHandler(), 164 new URLDispatcher(jettyAddr, conf, confForCreate)); 165 } 166 }); 167 } else { 168 this.httpsServer = null; 169 this.sslFactory = null; 170 } 171 } 172 173 public InetSocketAddress getHttpAddress() { 174 return httpAddress; 175 } 176 177 public InetSocketAddress getHttpsAddress() { 178 return httpsAddress; 179 } 180 181 public void start() { 182 if (httpServer != null) { 183 ChannelFuture f = httpServer.bind(DataNode.getInfoAddr(conf)); 184 f.syncUninterruptibly(); 185 httpAddress = (InetSocketAddress) f.channel().localAddress(); 186 LOG.info("Listening HTTP traffic on " + httpAddress); 187 } 188 189 if (httpsServer != null) { 190 InetSocketAddress secInfoSocAddr = NetUtils.createSocketAddr(conf.getTrimmed( 191 DFS_DATANODE_HTTPS_ADDRESS_KEY, DFS_DATANODE_HTTPS_ADDRESS_DEFAULT)); 192 ChannelFuture f = httpsServer.bind(secInfoSocAddr); 193 f.syncUninterruptibly(); 194 httpsAddress = (InetSocketAddress) f.channel().localAddress(); 195 LOG.info("Listening HTTPS traffic on " + httpsAddress); 196 } 197 } 198 199 @Override 200 public void close() throws IOException { 201 bossGroup.shutdownGracefully(); 202 workerGroup.shutdownGracefully(); 203 if (sslFactory != null) { 204 sslFactory.destroy(); 205 } 206 if (externalHttpChannel != null) { 207 externalHttpChannel.close(); 208 } 209 try { 210 infoServer.stop(); 211 } catch (Exception e) { 212 throw new IOException(e); 213 } 214 } 215 216 private static String getHostnameForSpnegoPrincipal(Configuration conf) { 217 String addr = conf.getTrimmed(DFS_DATANODE_HTTP_ADDRESS_KEY, null); 218 if (addr == null) { 219 addr = conf.getTrimmed(DFS_DATANODE_HTTPS_ADDRESS_KEY, 220 DFS_DATANODE_HTTPS_ADDRESS_DEFAULT); 221 } 222 InetSocketAddress inetSocker = NetUtils.createSocketAddr(addr); 223 return inetSocker.getHostString(); 224 } 225}