View Javadoc

1   /**
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  
19  package org.apache.hadoop.hbase.ipc;
20  
21  import static org.apache.hadoop.fs.CommonConfigurationKeysPublic.HADOOP_SECURITY_AUTHORIZATION;
22  
23  import com.google.common.annotations.VisibleForTesting;
24  import com.google.common.util.concurrent.ThreadFactoryBuilder;
25  import com.google.protobuf.BlockingService;
26  import com.google.protobuf.CodedInputStream;
27  import com.google.protobuf.CodedOutputStream;
28  import com.google.protobuf.Descriptors.MethodDescriptor;
29  import com.google.protobuf.Message;
30  import com.google.protobuf.ServiceException;
31  import com.google.protobuf.TextFormat;
32  
33  import java.io.ByteArrayInputStream;
34  import java.io.ByteArrayOutputStream;
35  import java.io.DataOutputStream;
36  import java.io.IOException;
37  import java.io.InputStream;
38  import java.net.BindException;
39  import java.net.InetAddress;
40  import java.net.InetSocketAddress;
41  import java.net.ServerSocket;
42  import java.net.Socket;
43  import java.net.SocketException;
44  import java.net.UnknownHostException;
45  import java.nio.ByteBuffer;
46  import java.nio.channels.CancelledKeyException;
47  import java.nio.channels.Channels;
48  import java.nio.channels.ClosedChannelException;
49  import java.nio.channels.GatheringByteChannel;
50  import java.nio.channels.ReadableByteChannel;
51  import java.nio.channels.SelectionKey;
52  import java.nio.channels.Selector;
53  import java.nio.channels.ServerSocketChannel;
54  import java.nio.channels.SocketChannel;
55  import java.nio.channels.WritableByteChannel;
56  import java.security.PrivilegedExceptionAction;
57  import java.util.ArrayList;
58  import java.util.Collections;
59  import java.util.HashMap;
60  import java.util.Iterator;
61  import java.util.LinkedList;
62  import java.util.List;
63  import java.util.Map;
64  import java.util.Random;
65  import java.util.Set;
66  import java.util.concurrent.ConcurrentHashMap;
67  import java.util.concurrent.ConcurrentLinkedDeque;
68  import java.util.concurrent.ExecutorService;
69  import java.util.concurrent.Executors;
70  import java.util.concurrent.locks.Lock;
71  import java.util.concurrent.locks.ReentrantLock;
72  
73  import javax.security.sasl.Sasl;
74  import javax.security.sasl.SaslException;
75  import javax.security.sasl.SaslServer;
76  
77  import org.apache.commons.logging.Log;
78  import org.apache.commons.logging.LogFactory;
79  import org.apache.hadoop.conf.Configuration;
80  import org.apache.hadoop.hbase.CallQueueTooBigException;
81  import org.apache.hadoop.hbase.CellScanner;
82  import org.apache.hadoop.hbase.DoNotRetryIOException;
83  import org.apache.hadoop.hbase.HBaseIOException;
84  import org.apache.hadoop.hbase.HBaseInterfaceAudience;
85  import org.apache.hadoop.hbase.HConstants;
86  import org.apache.hadoop.hbase.Server;
87  import org.apache.hadoop.hbase.classification.InterfaceAudience;
88  import org.apache.hadoop.hbase.classification.InterfaceStability;
89  import org.apache.hadoop.hbase.client.NeedUnmanagedConnectionException;
90  import org.apache.hadoop.hbase.client.VersionInfoUtil;
91  import org.apache.hadoop.hbase.codec.Codec;
92  import org.apache.hadoop.hbase.conf.ConfigurationObserver;
93  import org.apache.hadoop.hbase.exceptions.RegionMovedException;
94  import org.apache.hadoop.hbase.exceptions.RequestTooBigException;
95  import org.apache.hadoop.hbase.io.BoundedByteBufferPool;
96  import org.apache.hadoop.hbase.io.ByteBufferInputStream;
97  import org.apache.hadoop.hbase.io.ByteBufferOutputStream;
98  import org.apache.hadoop.hbase.monitoring.MonitoredRPCHandler;
99  import org.apache.hadoop.hbase.monitoring.TaskMonitor;
100 import org.apache.hadoop.hbase.protobuf.ProtobufUtil;
101 import org.apache.hadoop.hbase.protobuf.generated.ClientProtos;
102 import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.VersionInfo;
103 import org.apache.hadoop.hbase.protobuf.generated.RPCProtos.CellBlockMeta;
104 import org.apache.hadoop.hbase.protobuf.generated.RPCProtos.ConnectionHeader;
105 import org.apache.hadoop.hbase.protobuf.generated.RPCProtos.ExceptionResponse;
106 import org.apache.hadoop.hbase.protobuf.generated.RPCProtos.RequestHeader;
107 import org.apache.hadoop.hbase.protobuf.generated.RPCProtos.ResponseHeader;
108 import org.apache.hadoop.hbase.protobuf.generated.RPCProtos.UserInformation;
109 import org.apache.hadoop.hbase.regionserver.RSRpcServices;
110 import org.apache.hadoop.hbase.security.AccessDeniedException;
111 import org.apache.hadoop.hbase.security.AuthMethod;
112 import org.apache.hadoop.hbase.security.HBasePolicyProvider;
113 import org.apache.hadoop.hbase.security.HBaseSaslRpcServer;
114 import org.apache.hadoop.hbase.security.HBaseSaslRpcServer.SaslDigestCallbackHandler;
115 import org.apache.hadoop.hbase.security.HBaseSaslRpcServer.SaslGssCallbackHandler;
116 import org.apache.hadoop.hbase.security.SaslStatus;
117 import org.apache.hadoop.hbase.security.SaslUtil;
118 import org.apache.hadoop.hbase.security.User;
119 import org.apache.hadoop.hbase.security.UserProvider;
120 import org.apache.hadoop.hbase.security.token.AuthenticationTokenSecretManager;
121 import org.apache.hadoop.hbase.util.Bytes;
122 import org.apache.hadoop.hbase.util.Counter;
123 import org.apache.hadoop.hbase.util.Pair;
124 import org.apache.hadoop.hbase.util.Threads;
125 import org.apache.hadoop.io.BytesWritable;
126 import org.apache.hadoop.io.IntWritable;
127 import org.apache.hadoop.io.Writable;
128 import org.apache.hadoop.io.WritableUtils;
129 import org.apache.hadoop.io.compress.CompressionCodec;
130 import org.apache.hadoop.security.UserGroupInformation;
131 import org.apache.hadoop.security.UserGroupInformation.AuthenticationMethod;
132 import org.apache.hadoop.security.authorize.AuthorizationException;
133 import org.apache.hadoop.security.authorize.PolicyProvider;
134 import org.apache.hadoop.security.authorize.ProxyUsers;
135 import org.apache.hadoop.security.authorize.ServiceAuthorizationManager;
136 import org.apache.hadoop.security.token.SecretManager;
137 import org.apache.hadoop.security.token.SecretManager.InvalidToken;
138 import org.apache.hadoop.security.token.TokenIdentifier;
139 import org.apache.hadoop.util.StringUtils;
140 import org.apache.htrace.TraceInfo;
141 import org.codehaus.jackson.map.ObjectMapper;
142 
143 /**
144  * An RPC server that hosts protobuf described Services.
145  *
146  * An RpcServer instance has a Listener that hosts the socket.  Listener has fixed number
147  * of Readers in an ExecutorPool, 10 by default.  The Listener does an accept and then
148  * round robin a Reader is chosen to do the read.  The reader is registered on Selector.  Read does
149  * total read off the channel and the parse from which it makes a Call.  The call is wrapped in a
150  * CallRunner and passed to the scheduler to be run.  Reader goes back to see if more to be done
151  * and loops till done.
152  *
153  * <p>Scheduler can be variously implemented but default simple scheduler has handlers to which it
154  * has given the queues into which calls (i.e. CallRunner instances) are inserted.  Handlers run
155  * taking from the queue.  They run the CallRunner#run method on each item gotten from queue
156  * and keep taking while the server is up.
157  *
158  * CallRunner#run executes the call.  When done, asks the included Call to put itself on new
159  * queue for Responder to pull from and return result to client.
160  */
161 @InterfaceAudience.LimitedPrivate({HBaseInterfaceAudience.COPROC, HBaseInterfaceAudience.PHOENIX})
162 @InterfaceStability.Evolving
163 public class RpcServer implements RpcServerInterface, ConfigurationObserver {
164   // LOG is being used in CallRunner and the log level is being changed in tests
165   public static final Log LOG = LogFactory.getLog(RpcServer.class);
166   private static final CallQueueTooBigException CALL_QUEUE_TOO_BIG_EXCEPTION
167       = new CallQueueTooBigException();
168 
169   private final boolean authorize;
170   private boolean isSecurityEnabled;
171 
172   public static final byte CURRENT_VERSION = 0;
173 
174   /**
175    * Whether we allow a fallback to SIMPLE auth for insecure clients when security is enabled.
176    */
177   public static final String FALLBACK_TO_INSECURE_CLIENT_AUTH =
178           "hbase.ipc.server.fallback-to-simple-auth-allowed";
179 
180   /**
181    * How many calls/handler are allowed in the queue.
182    */
183   static final int DEFAULT_MAX_CALLQUEUE_LENGTH_PER_HANDLER = 10;
184 
185   /**
186    * The maximum size that we can hold in the RPC queue
187    */
188   private static final int DEFAULT_MAX_CALLQUEUE_SIZE = 1024 * 1024 * 1024;
189 
190   private final CellBlockBuilder cellBlockBuilder;
191 
192   private static final String AUTH_FAILED_FOR = "Auth failed for ";
193   private static final String AUTH_SUCCESSFUL_FOR = "Auth successful for ";
194   private static final Log AUDITLOG = LogFactory.getLog("SecurityLogger." +
195     Server.class.getName());
196   protected SecretManager<TokenIdentifier> secretManager;
197   protected ServiceAuthorizationManager authManager;
198 
199   /** This is set to Call object before Handler invokes an RPC and ybdie
200    * after the call returns.
201    */
202   protected static final ThreadLocal<Call> CurCall = new ThreadLocal<Call>();
203 
204   /** Keeps MonitoredRPCHandler per handler thread. */
205   static final ThreadLocal<MonitoredRPCHandler> MONITORED_RPC
206       = new ThreadLocal<MonitoredRPCHandler>();
207 
208   protected final InetSocketAddress bindAddress;
209   protected int port;                             // port we listen on
210   protected InetSocketAddress address;            // inet address we listen on
211   private int readThreads;                        // number of read threads
212   protected int maxIdleTime;                      // the maximum idle time after
213                                                   // which a client may be
214                                                   // disconnected
215   protected int thresholdIdleConnections;         // the number of idle
216                                                   // connections after which we
217                                                   // will start cleaning up idle
218                                                   // connections
219   int maxConnectionsToNuke;                       // the max number of
220                                                   // connections to nuke
221                                                   // during a cleanup
222 
223   protected MetricsHBaseServer metrics;
224 
225   protected final Configuration conf;
226 
227   private int maxQueueSize;
228   protected int socketSendBufferSize;
229   protected final boolean tcpNoDelay;   // if T then disable Nagle's Algorithm
230   protected final boolean tcpKeepAlive; // if T then use keepalives
231   protected final long purgeTimeout;    // in milliseconds
232 
233   /**
234    * This flag is used to indicate to sub threads when they should go down.  When we call
235    * {@link #start()}, all threads started will consult this flag on whether they should
236    * keep going.  It is set to false when {@link #stop()} is called.
237    */
238   volatile boolean running = true;
239 
240   /**
241    * This flag is set to true after all threads are up and 'running' and the server is then opened
242    * for business by the call to {@link #start()}.
243    */
244   volatile boolean started = false;
245 
246   /**
247    * This is a running count of the size of all outstanding calls by size.
248    */
249   protected final Counter callQueueSize = new Counter();
250 
251   protected final List<Connection> connectionList =
252     Collections.synchronizedList(new LinkedList<Connection>());
253   //maintain a list
254   //of client connections
255   private Listener listener = null;
256   protected Responder responder = null;
257   protected AuthenticationTokenSecretManager authTokenSecretMgr = null;
258   protected int numConnections = 0;
259 
260   protected HBaseRPCErrorHandler errorHandler = null;
261 
262   public static final String MAX_REQUEST_SIZE = "hbase.ipc.max.request.size";
263   private static final RequestTooBigException REQUEST_TOO_BIG_EXCEPTION =
264       new RequestTooBigException();
265 
266   private static final String WARN_RESPONSE_TIME = "hbase.ipc.warn.response.time";
267   private static final String WARN_RESPONSE_SIZE = "hbase.ipc.warn.response.size";
268 
269 
270   /**
271    * Minimum allowable timeout (in milliseconds) in rpc request's header. This
272    * configuration exists to prevent the rpc service regarding this request as timeout immediately.
273    */
274   private static final String MIN_CLIENT_REQUEST_TIMEOUT = "hbase.ipc.min.client.request.timeout";
275   private static final int DEFAULT_MIN_CLIENT_REQUEST_TIMEOUT = 20;
276 
277   /** Default value for above params */
278   public static final int DEFAULT_MAX_REQUEST_SIZE = DEFAULT_MAX_CALLQUEUE_SIZE / 4; // 256M
279   private static final int DEFAULT_WARN_RESPONSE_TIME = 10000; // milliseconds
280   private static final int DEFAULT_WARN_RESPONSE_SIZE = 100 * 1024 * 1024;
281 
282   private static final ObjectMapper MAPPER = new ObjectMapper();
283 
284   protected static final int DEFAULT_TRACE_LOG_MAX_LENGTH = 1000;
285   protected static final String TRACE_LOG_MAX_LENGTH = "hbase.ipc.trace.log.max.length";
286   protected static final String KEY_WORD_TRUNCATED = " <TRUNCATED>";
287 
288   private final int maxRequestSize;
289   private final int warnResponseTime;
290   private final int warnResponseSize;
291 
292   private final int minClientRequestTimeout;
293 
294   private final Server server;
295   private final List<BlockingServiceAndInterface> services;
296 
297   private final RpcScheduler scheduler;
298 
299   private UserProvider userProvider;
300 
301   private final BoundedByteBufferPool reservoir;
302 
303   private volatile boolean allowFallbackToSimpleAuth;
304 
305   /**
306    * Used to get details for scan with a scanner_id<br/>
307    * TODO try to figure out a better way and remove reference from regionserver package later.
308    */
309   private RSRpcServices rsRpcServices;
310 
311   /**
312    * Datastructure that holds all necessary to a method invocation and then afterward, carries
313    * the result.
314    */
315   @InterfaceAudience.LimitedPrivate({HBaseInterfaceAudience.COPROC, HBaseInterfaceAudience.PHOENIX})
316   @InterfaceStability.Evolving
317   public class Call implements RpcCallContext {
318     protected int id;                             // the client's call id
319     protected BlockingService service;
320     protected MethodDescriptor md;
321     protected RequestHeader header;
322     protected Message param;                      // the parameter passed
323     // Optional cell data passed outside of protobufs.
324     protected CellScanner cellScanner;
325     protected Connection connection;              // connection to client
326     protected long timestamp;      // the time received when response is null
327                                    // the time served when response is not null
328     protected int timeout;
329     protected long startTime;
330     protected long deadline;// the deadline to handle this call, if exceed we can drop it.
331 
332     /**
333      * Chain of buffers to send as response.
334      */
335     protected BufferChain response;
336     protected Responder responder;
337 
338     protected long size;                          // size of current call
339     protected boolean isError;
340     protected TraceInfo tinfo;
341     private ByteBuffer cellBlock = null;
342 
343     private User user;
344     private InetAddress remoteAddress;
345 
346     private long responseCellSize = 0;
347     private long responseBlockSize = 0;
348     // cumulative size of serialized exceptions
349     private long exceptionSize = 0;
350     private boolean retryImmediatelySupported;
351 
352     @edu.umd.cs.findbugs.annotations.SuppressWarnings(value="NP_NULL_ON_SOME_PATH",
353         justification="Can't figure why this complaint is happening... see below")
354     Call(int id, final BlockingService service, final MethodDescriptor md, RequestHeader header,
355          Message param, CellScanner cellScanner, Connection connection, Responder responder,
356          long size, TraceInfo tinfo, final InetAddress remoteAddress, int timeout) {
357       this.id = id;
358       this.service = service;
359       this.md = md;
360       this.header = header;
361       this.param = param;
362       this.cellScanner = cellScanner;
363       this.connection = connection;
364       this.timestamp = System.currentTimeMillis();
365       this.response = null;
366       this.responder = responder;
367       this.isError = false;
368       this.size = size;
369       this.tinfo = tinfo;
370       this.user = connection == null? null: connection.user; // FindBugs: NP_NULL_ON_SOME_PATH
371       this.remoteAddress = remoteAddress;
372       this.retryImmediatelySupported =
373           connection == null? null: connection.retryImmediatelySupported;
374       this.timeout = timeout;
375       this.deadline = this.timeout > 0 ? this.timestamp + this.timeout : Long.MAX_VALUE;
376     }
377 
378     /**
379      * Call is done. Execution happened and we returned results to client. It is now safe to
380      * cleanup.
381      */
382     @edu.umd.cs.findbugs.annotations.SuppressWarnings(value="IS2_INCONSISTENT_SYNC",
383         justification="Presume the lock on processing request held by caller is protection enough")
384     void done() {
385       if (this.cellBlock != null && reservoir != null) {
386         // Return buffer to reservoir now we are done with it.
387         reservoir.putBuffer(this.cellBlock);
388         this.cellBlock = null;
389       }
390       this.connection.decRpcCount();  // Say that we're done with this call.
391     }
392 
393     @Override
394     public String toString() {
395       return toShortString() + " param: " +
396         (this.param != null? ProtobufUtil.getShortTextFormat(this.param): "") +
397         " connection: " + connection.toString();
398     }
399 
400     protected RequestHeader getHeader() {
401       return this.header;
402     }
403 
404     public boolean hasPriority() {
405       return this.header.hasPriority();
406     }
407 
408     public int getPriority() {
409       return this.header.getPriority();
410     }
411 
412     /*
413      * Short string representation without param info because param itself could be huge depends on
414      * the payload of a command
415      */
416     String toShortString() {
417       String serviceName = this.connection.service != null ?
418           this.connection.service.getDescriptorForType().getName() : "null";
419       return "callId: " + this.id + " service: " + serviceName +
420           " methodName: " + ((this.md != null) ? this.md.getName() : "n/a") +
421           " size: " + StringUtils.TraditionalBinaryPrefix.long2String(this.size, "", 1) +
422           " connection: " + connection.toString() +
423           " deadline: " + deadline;
424     }
425 
426     String toTraceString() {
427       String serviceName = this.connection.service != null ?
428                            this.connection.service.getDescriptorForType().getName() : "";
429       String methodName = (this.md != null) ? this.md.getName() : "";
430       return serviceName + "." + methodName;
431     }
432 
433     protected synchronized void setSaslTokenResponse(ByteBuffer response) {
434       this.response = new BufferChain(response);
435     }
436 
437     protected synchronized void setResponse(Object m, final CellScanner cells,
438         Throwable t, String errorMsg) {
439       if (this.isError) return;
440       if (t != null) this.isError = true;
441       BufferChain bc = null;
442       try {
443         ResponseHeader.Builder headerBuilder = ResponseHeader.newBuilder();
444         // Presume it a pb Message.  Could be null.
445         Message result = (Message)m;
446         // Call id.
447         headerBuilder.setCallId(this.id);
448         if (t != null) {
449           ExceptionResponse.Builder exceptionBuilder = ExceptionResponse.newBuilder();
450           exceptionBuilder.setExceptionClassName(t.getClass().getName());
451           exceptionBuilder.setStackTrace(errorMsg);
452           exceptionBuilder.setDoNotRetry(t instanceof DoNotRetryIOException ||
453             t instanceof NeedUnmanagedConnectionException);
454           if (t instanceof RegionMovedException) {
455             // Special casing for this exception.  This is only one carrying a payload.
456             // Do this instead of build a generic system for allowing exceptions carry
457             // any kind of payload.
458             RegionMovedException rme = (RegionMovedException)t;
459             exceptionBuilder.setHostname(rme.getHostname());
460             exceptionBuilder.setPort(rme.getPort());
461           }
462           // Set the exception as the result of the method invocation.
463           headerBuilder.setException(exceptionBuilder.build());
464         }
465         // Pass reservoir to buildCellBlock. Keep reference to returne so can add it back to the
466         // reservoir when finished. This is hacky and the hack is not contained but benefits are
467         // high when we can avoid a big buffer allocation on each rpc.
468         this.cellBlock = cellBlockBuilder.buildCellBlock(this.connection.codec,
469           this.connection.compressionCodec, cells, reservoir);
470         if (this.cellBlock != null) {
471           CellBlockMeta.Builder cellBlockBuilder = CellBlockMeta.newBuilder();
472           // Presumes the cellBlock bytebuffer has been flipped so limit has total size in it.
473           cellBlockBuilder.setLength(this.cellBlock.limit());
474           headerBuilder.setCellBlockMeta(cellBlockBuilder.build());
475         }
476         Message header = headerBuilder.build();
477 
478         byte[] b = createHeaderAndMessageBytes(result, header);
479 
480         bc = new BufferChain(ByteBuffer.wrap(b), this.cellBlock);
481 
482         if (connection.useWrap) {
483           bc = wrapWithSasl(bc);
484         }
485       } catch (IOException e) {
486         LOG.warn("Exception while creating response " + e);
487       }
488       this.response = bc;
489     }
490 
491     private byte[] createHeaderAndMessageBytes(Message result, Message header)
492         throws IOException {
493       // Organize the response as a set of bytebuffers rather than collect it all together inside
494       // one big byte array; save on allocations.
495       int headerSerializedSize = 0, resultSerializedSize = 0, headerVintSize = 0,
496           resultVintSize = 0;
497       if (header != null) {
498         headerSerializedSize = header.getSerializedSize();
499         headerVintSize = CodedOutputStream.computeRawVarint32Size(headerSerializedSize);
500       }
501       if (result != null) {
502         resultSerializedSize = result.getSerializedSize();
503         resultVintSize = CodedOutputStream.computeRawVarint32Size(resultSerializedSize);
504       }
505       // calculate the total size
506       int totalSize = headerSerializedSize + headerVintSize
507           + (resultSerializedSize + resultVintSize)
508           + (this.cellBlock == null ? 0 : this.cellBlock.limit());
509       // The byte[] should also hold the totalSize of the header, message and the cellblock
510       byte[] b = new byte[headerSerializedSize + headerVintSize + resultSerializedSize
511           + resultVintSize + Bytes.SIZEOF_INT];
512       // The RpcClient expects the int to be in a format that code be decoded by
513       // the DataInputStream#readInt(). Hence going with the Bytes.toBytes(int)
514       // form of writing int.
515       Bytes.putInt(b, 0, totalSize);
516       CodedOutputStream cos = CodedOutputStream.newInstance(b, Bytes.SIZEOF_INT,
517           b.length - Bytes.SIZEOF_INT);
518       if (header != null) {
519         cos.writeMessageNoTag(header);
520       }
521       if (result != null) {
522         cos.writeMessageNoTag(result);
523       }
524       cos.flush();
525       cos.checkNoSpaceLeft();
526       return b;
527     }
528 
529     private BufferChain wrapWithSasl(BufferChain bc)
530         throws IOException {
531       if (!this.connection.useSasl) return bc;
532       // Looks like no way around this; saslserver wants a byte array.  I have to make it one.
533       // THIS IS A BIG UGLY COPY.
534       byte [] responseBytes = bc.getBytes();
535       byte [] token;
536       // synchronization may be needed since there can be multiple Handler
537       // threads using saslServer to wrap responses.
538       synchronized (connection.saslServer) {
539         token = connection.saslServer.wrap(responseBytes, 0, responseBytes.length);
540       }
541       if (LOG.isTraceEnabled()) {
542         LOG.trace("Adding saslServer wrapped token of size " + token.length
543             + " as call response.");
544       }
545 
546       ByteBuffer bbTokenLength = ByteBuffer.wrap(Bytes.toBytes(token.length));
547       ByteBuffer bbTokenBytes = ByteBuffer.wrap(token);
548       return new BufferChain(bbTokenLength, bbTokenBytes);
549     }
550 
551     @Override
552     public boolean isClientCellBlockSupported() {
553       return this.connection != null && this.connection.codec != null;
554     }
555 
556     @Override
557     public long disconnectSince() {
558       if (!connection.channel.isOpen()) {
559         return System.currentTimeMillis() - timestamp;
560       } else {
561         return -1L;
562       }
563     }
564 
565     @Override
566     public long getResponseExceptionSize() {
567       return exceptionSize;
568     }
569     @Override
570     public void incrementResponseExceptionSize(long exSize) {
571       exceptionSize += exSize;
572     }
573 
574     public long getSize() {
575       return this.size;
576     }
577 
578     @Override
579     public long getResponseCellSize() {
580       return responseCellSize;
581     }
582 
583     @Override
584     public void incrementResponseCellSize(long cellSize) {
585       responseCellSize += cellSize;
586     }
587 
588     @Override
589     public long getResponseBlockSize() {
590       return responseBlockSize;
591     }
592 
593     @Override
594     public void incrementResponseBlockSize(long blockSize) {
595       responseBlockSize += blockSize;
596     }
597 
598     @Override
599     public long getDeadline() {
600       return deadline;
601     }
602 
603     public synchronized void sendResponseIfReady() throws IOException {
604       // set param null to reduce memory pressure
605       this.param = null;
606       this.responder.doRespond(this);
607     }
608 
609     public UserGroupInformation getRemoteUser() {
610       return connection.ugi;
611     }
612 
613     @Override
614     public User getRequestUser() {
615       return user;
616     }
617 
618     @Override
619     public String getRequestUserName() {
620       User user = getRequestUser();
621       return user == null? null: user.getShortName();
622     }
623 
624     @Override
625     public InetAddress getRemoteAddress() {
626       return remoteAddress;
627     }
628 
629     @Override
630     public VersionInfo getClientVersionInfo() {
631       return connection.getVersionInfo();
632     }
633 
634     @Override
635     public boolean isRetryImmediatelySupported() {
636       return retryImmediatelySupported;
637     }
638   }
639 
640   /** Listens on the socket. Creates jobs for the handler threads*/
641   private class Listener extends Thread {
642 
643     private ServerSocketChannel acceptChannel = null; //the accept channel
644     private Selector selector = null; //the selector that we use for the server
645     private Reader[] readers = null;
646     private int currentReader = 0;
647     private Random rand = new Random();
648     private long lastCleanupRunTime = 0; //the last time when a cleanup connec-
649                                          //-tion (for idle connections) ran
650     private long cleanupInterval = 10000; //the minimum interval between
651                                           //two cleanup runs
652     private int backlogLength;
653 
654     private ExecutorService readPool;
655 
656     public Listener(final String name) throws IOException {
657       super(name);
658       backlogLength = conf.getInt("hbase.ipc.server.listen.queue.size", 128);
659       // Create a new server socket and set to non blocking mode
660       acceptChannel = ServerSocketChannel.open();
661       acceptChannel.configureBlocking(false);
662 
663       // Bind the server socket to the binding addrees (can be different from the default interface)
664       bind(acceptChannel.socket(), bindAddress, backlogLength);
665       port = acceptChannel.socket().getLocalPort(); //Could be an ephemeral port
666       address = (InetSocketAddress)acceptChannel.socket().getLocalSocketAddress();
667 
668       // create a selector;
669       selector= Selector.open();
670 
671       readers = new Reader[readThreads];
672       readPool = Executors.newFixedThreadPool(readThreads,
673         new ThreadFactoryBuilder().setNameFormat(
674           "RpcServer.reader=%d,bindAddress=" + bindAddress.getHostName() +
675           ",port=" + port).setDaemon(true)
676         .setUncaughtExceptionHandler(Threads.LOGGING_EXCEPTION_HANDLER).build());
677       for (int i = 0; i < readThreads; ++i) {
678         Reader reader = new Reader();
679         readers[i] = reader;
680         readPool.execute(reader);
681       }
682       LOG.info(getName() + ": started " + readThreads + " reader(s) listening on port=" + port);
683 
684       // Register accepts on the server socket with the selector.
685       acceptChannel.register(selector, SelectionKey.OP_ACCEPT);
686       this.setName("RpcServer.listener,port=" + port);
687       this.setDaemon(true);
688     }
689 
690 
691     private class Reader implements Runnable {
692       private volatile boolean adding = false;
693       private final Selector readSelector;
694 
695       Reader() throws IOException {
696         this.readSelector = Selector.open();
697       }
698       @Override
699       public void run() {
700         try {
701           doRunLoop();
702         } finally {
703           try {
704             readSelector.close();
705           } catch (IOException ioe) {
706             LOG.error(getName() + ": error closing read selector in " + getName(), ioe);
707           }
708         }
709       }
710 
711       private synchronized void doRunLoop() {
712         while (running) {
713           try {
714             readSelector.select();
715             while (adding) {
716               this.wait(1000);
717             }
718 
719             Iterator<SelectionKey> iter = readSelector.selectedKeys().iterator();
720             while (iter.hasNext()) {
721               SelectionKey key = iter.next();
722               iter.remove();
723               if (key.isValid()) {
724                 if (key.isReadable()) {
725                   doRead(key);
726                 }
727               }
728             }
729           } catch (InterruptedException e) {
730             LOG.debug("Interrupted while sleeping");
731           } catch (CancelledKeyException e) {
732             LOG.error(getName() + ": CancelledKeyException in Reader", e);
733           } catch (IOException ex) {
734             LOG.info(getName() + ": IOException in Reader", ex);
735           } catch (OutOfMemoryError e) {
736             if (getErrorHandler() != null) {
737               if (getErrorHandler().checkOOME(e)) {
738                 RpcServer.LOG.info(Thread.currentThread().getName()
739                     + ": exiting on OutOfMemoryError");
740                 return;
741               }
742             } else {
743               // rethrow if no handler
744               throw e;
745             }
746           }
747         }
748       }
749 
750       /**
751        * This gets reader into the state that waits for the new channel
752        * to be registered with readSelector. If it was waiting in select()
753        * the thread will be woken up, otherwise whenever select() is called
754        * it will return even if there is nothing to read and wait
755        * in while(adding) for finishAdd call
756        */
757       public void startAdd() {
758         adding = true;
759         readSelector.wakeup();
760       }
761 
762       public synchronized SelectionKey registerChannel(SocketChannel channel)
763         throws IOException {
764         return channel.register(readSelector, SelectionKey.OP_READ);
765       }
766 
767       public synchronized void finishAdd() {
768         adding = false;
769         this.notify();
770       }
771     }
772 
773     /** cleanup connections from connectionList. Choose a random range
774      * to scan and also have a limit on the number of the connections
775      * that will be cleanedup per run. The criteria for cleanup is the time
776      * for which the connection was idle. If 'force' is true then all
777      * connections will be looked at for the cleanup.
778      * @param force all connections will be looked at for cleanup
779      */
780     private void cleanupConnections(boolean force) {
781       if (force || numConnections > thresholdIdleConnections) {
782         long currentTime = System.currentTimeMillis();
783         if (!force && (currentTime - lastCleanupRunTime) < cleanupInterval) {
784           return;
785         }
786         int start = 0;
787         int end = numConnections - 1;
788         if (!force) {
789           start = rand.nextInt(numConnections);
790           end = rand.nextInt(numConnections);
791           int temp;
792           if (end < start) {
793             temp = start;
794             start = end;
795             end = temp;
796           }
797         }
798         int i = start;
799         int numNuked = 0;
800         while (i <= end) {
801           Connection c;
802           synchronized (connectionList) {
803             try {
804               c = connectionList.get(i);
805             } catch (Exception e) {return;}
806           }
807           if (c.timedOut(currentTime)) {
808             if (LOG.isDebugEnabled())
809               LOG.debug(getName() + ": disconnecting client " + c.getHostAddress());
810             closeConnection(c);
811             numNuked++;
812             end--;
813             //noinspection UnusedAssignment
814             c = null;
815             if (!force && numNuked == maxConnectionsToNuke) break;
816           }
817           else i++;
818         }
819         lastCleanupRunTime = System.currentTimeMillis();
820       }
821     }
822 
823     @Override
824     @edu.umd.cs.findbugs.annotations.SuppressWarnings(value="IS2_INCONSISTENT_SYNC",
825       justification="selector access is not synchronized; seems fine but concerned changing " +
826         "it will have per impact")
827     public void run() {
828       LOG.info(getName() + ": starting");
829       while (running) {
830         SelectionKey key = null;
831         try {
832           selector.select(); // FindBugs IS2_INCONSISTENT_SYNC
833           Iterator<SelectionKey> iter = selector.selectedKeys().iterator();
834           while (iter.hasNext()) {
835             key = iter.next();
836             iter.remove();
837             try {
838               if (key.isValid()) {
839                 if (key.isAcceptable())
840                   doAccept(key);
841               }
842             } catch (IOException ignored) {
843               if (LOG.isTraceEnabled()) LOG.trace("ignored", ignored);
844             }
845             key = null;
846           }
847         } catch (OutOfMemoryError e) {
848           if (errorHandler != null) {
849             if (errorHandler.checkOOME(e)) {
850               LOG.info(getName() + ": exiting on OutOfMemoryError");
851               closeCurrentConnection(key, e);
852               cleanupConnections(true);
853               return;
854             }
855           } else {
856             // we can run out of memory if we have too many threads
857             // log the event and sleep for a minute and give
858             // some thread(s) a chance to finish
859             LOG.warn(getName() + ": OutOfMemoryError in server select", e);
860             closeCurrentConnection(key, e);
861             cleanupConnections(true);
862             try {
863               Thread.sleep(60000);
864             } catch (InterruptedException ex) {
865               LOG.debug("Interrupted while sleeping");
866               return;
867             }
868           }
869         } catch (Exception e) {
870           closeCurrentConnection(key, e);
871         }
872         cleanupConnections(false);
873       }
874 
875       LOG.info(getName() + ": stopping");
876 
877       synchronized (this) {
878         try {
879           acceptChannel.close();
880           selector.close();
881         } catch (IOException ignored) {
882           if (LOG.isTraceEnabled()) LOG.trace("ignored", ignored);
883         }
884 
885         selector= null;
886         acceptChannel= null;
887 
888         // clean up all connections
889         while (!connectionList.isEmpty()) {
890           closeConnection(connectionList.remove(0));
891         }
892       }
893     }
894 
895     private void closeCurrentConnection(SelectionKey key, Throwable e) {
896       if (key != null) {
897         Connection c = (Connection)key.attachment();
898         if (c != null) {
899           if (LOG.isDebugEnabled()) {
900             LOG.debug(getName() + ": disconnecting client " + c.getHostAddress() +
901                 (e != null ? " on error " + e.getMessage() : ""));
902           }
903           closeConnection(c);
904           key.attach(null);
905         }
906       }
907     }
908 
909     InetSocketAddress getAddress() {
910       return address;
911     }
912 
913     void doAccept(SelectionKey key) throws IOException, OutOfMemoryError {
914       Connection c;
915       ServerSocketChannel server = (ServerSocketChannel) key.channel();
916 
917       SocketChannel channel;
918       while ((channel = server.accept()) != null) {
919         try {
920           channel.configureBlocking(false);
921           channel.socket().setTcpNoDelay(tcpNoDelay);
922           channel.socket().setKeepAlive(tcpKeepAlive);
923         } catch (IOException ioe) {
924           channel.close();
925           throw ioe;
926         }
927 
928         Reader reader = getReader();
929         try {
930           reader.startAdd();
931           SelectionKey readKey = reader.registerChannel(channel);
932           c = getConnection(channel, System.currentTimeMillis());
933           readKey.attach(c);
934           synchronized (connectionList) {
935             connectionList.add(numConnections, c);
936             numConnections++;
937           }
938           if (LOG.isDebugEnabled())
939             LOG.debug(getName() + ": connection from " + c.toString() +
940                 "; # active connections: " + numConnections);
941         } finally {
942           reader.finishAdd();
943         }
944       }
945     }
946 
947     void doRead(SelectionKey key) throws InterruptedException {
948       int count;
949       Connection c = (Connection) key.attachment();
950       if (c == null) {
951         return;
952       }
953       c.setLastContact(System.currentTimeMillis());
954       try {
955         count = c.readAndProcess();
956 
957         if (count > 0) {
958           c.setLastContact(System.currentTimeMillis());
959         }
960 
961       } catch (InterruptedException ieo) {
962         throw ieo;
963       } catch (Exception e) {
964         if (LOG.isDebugEnabled()) {
965           LOG.debug(getName() + ": Caught exception while reading:", e);
966         }
967         count = -1; //so that the (count < 0) block is executed
968       }
969       if (count < 0) {
970         if (LOG.isDebugEnabled()) {
971           LOG.debug(getName() + ": DISCONNECTING client " + c.toString() +
972               " because read count=" + count +
973               ". Number of active connections: " + numConnections);
974         }
975         closeConnection(c);
976       }
977     }
978 
979     synchronized void doStop() {
980       if (selector != null) {
981         selector.wakeup();
982         Thread.yield();
983       }
984       if (acceptChannel != null) {
985         try {
986           acceptChannel.socket().close();
987         } catch (IOException e) {
988           LOG.info(getName() + ": exception in closing listener socket. " + e);
989         }
990       }
991       readPool.shutdownNow();
992     }
993 
994     // The method that will return the next reader to work with
995     // Simplistic implementation of round robin for now
996     Reader getReader() {
997       currentReader = (currentReader + 1) % readers.length;
998       return readers[currentReader];
999     }
1000   }
1001 
1002   // Sends responses of RPC back to clients.
1003   protected class Responder extends Thread {
1004     private final Selector writeSelector;
1005     private final Set<Connection> writingCons =
1006         Collections.newSetFromMap(new ConcurrentHashMap<Connection, Boolean>());
1007 
1008     Responder() throws IOException {
1009       this.setName("RpcServer.responder");
1010       this.setDaemon(true);
1011       this.setUncaughtExceptionHandler(Threads.LOGGING_EXCEPTION_HANDLER);
1012       writeSelector = Selector.open(); // create a selector
1013     }
1014 
1015     @Override
1016     public void run() {
1017       LOG.info(getName() + ": starting");
1018       try {
1019         doRunLoop();
1020       } finally {
1021         LOG.info(getName() + ": stopping");
1022         try {
1023           writeSelector.close();
1024         } catch (IOException ioe) {
1025           LOG.error(getName() + ": couldn't close write selector", ioe);
1026         }
1027       }
1028     }
1029 
1030     /**
1031      * Take the list of the connections that want to write, and register them
1032      * in the selector.
1033      */
1034     private void registerWrites() {
1035       Iterator<Connection> it = writingCons.iterator();
1036       while (it.hasNext()) {
1037         Connection c = it.next();
1038         it.remove();
1039         SelectionKey sk = c.channel.keyFor(writeSelector);
1040         try {
1041           if (sk == null) {
1042             try {
1043               c.channel.register(writeSelector, SelectionKey.OP_WRITE, c);
1044             } catch (ClosedChannelException e) {
1045               // ignore: the client went away.
1046               if (LOG.isTraceEnabled()) LOG.trace("ignored", e);
1047             }
1048           } else {
1049             sk.interestOps(SelectionKey.OP_WRITE);
1050           }
1051         } catch (CancelledKeyException e) {
1052           // ignore: the client went away.
1053           if (LOG.isTraceEnabled()) LOG.trace("ignored", e);
1054         }
1055       }
1056     }
1057 
1058     /**
1059      * Add a connection to the list that want to write,
1060      */
1061     public void registerForWrite(Connection c) {
1062       if (writingCons.add(c)) {
1063         writeSelector.wakeup();
1064       }
1065     }
1066 
1067     private void doRunLoop() {
1068       long lastPurgeTime = 0;   // last check for old calls.
1069       while (running) {
1070         try {
1071           registerWrites();
1072           int keyCt = writeSelector.select(purgeTimeout);
1073           if (keyCt == 0) {
1074             continue;
1075           }
1076 
1077           Set<SelectionKey> keys = writeSelector.selectedKeys();
1078           Iterator<SelectionKey> iter = keys.iterator();
1079           while (iter.hasNext()) {
1080             SelectionKey key = iter.next();
1081             iter.remove();
1082             try {
1083               if (key.isValid() && key.isWritable()) {
1084                 doAsyncWrite(key);
1085               }
1086             } catch (IOException e) {
1087               LOG.debug(getName() + ": asyncWrite", e);
1088             }
1089           }
1090 
1091           lastPurgeTime = purge(lastPurgeTime);
1092 
1093         } catch (OutOfMemoryError e) {
1094           if (errorHandler != null) {
1095             if (errorHandler.checkOOME(e)) {
1096               LOG.info(getName() + ": exiting on OutOfMemoryError");
1097               return;
1098             }
1099           } else {
1100             //
1101             // we can run out of memory if we have too many threads
1102             // log the event and sleep for a minute and give
1103             // some thread(s) a chance to finish
1104             //
1105             LOG.warn(getName() + ": OutOfMemoryError in server select", e);
1106             try {
1107               Thread.sleep(60000);
1108             } catch (InterruptedException ex) {
1109               LOG.debug("Interrupted while sleeping");
1110               return;
1111             }
1112           }
1113         } catch (Exception e) {
1114           LOG.warn(getName() + ": exception in Responder " +
1115               StringUtils.stringifyException(e), e);
1116         }
1117       }
1118       LOG.info(getName() + ": stopped");
1119     }
1120 
1121     /**
1122      * If there were some calls that have not been sent out for a
1123      * long time, we close the connection.
1124      * @return the time of the purge.
1125      */
1126     private long purge(long lastPurgeTime) {
1127       long now = System.currentTimeMillis();
1128       if (now < lastPurgeTime + purgeTimeout) {
1129         return lastPurgeTime;
1130       }
1131 
1132       ArrayList<Connection> conWithOldCalls = new ArrayList<Connection>();
1133       // get the list of channels from list of keys.
1134       synchronized (writeSelector.keys()) {
1135         for (SelectionKey key : writeSelector.keys()) {
1136           Connection connection = (Connection) key.attachment();
1137           if (connection == null) {
1138             throw new IllegalStateException("Coding error: SelectionKey key without attachment.");
1139           }
1140           Call call = connection.responseQueue.peekFirst();
1141           if (call != null && now > call.timestamp + purgeTimeout) {
1142             conWithOldCalls.add(call.connection);
1143           }
1144         }
1145       }
1146 
1147       // Seems safer to close the connection outside of the synchronized loop...
1148       for (Connection connection : conWithOldCalls) {
1149         closeConnection(connection);
1150       }
1151 
1152       return now;
1153     }
1154 
1155     private void doAsyncWrite(SelectionKey key) throws IOException {
1156       Connection connection = (Connection) key.attachment();
1157       if (connection == null) {
1158         throw new IOException("doAsyncWrite: no connection");
1159       }
1160       if (key.channel() != connection.channel) {
1161         throw new IOException("doAsyncWrite: bad channel");
1162       }
1163 
1164       if (processAllResponses(connection)) {
1165         try {
1166           // We wrote everything, so we don't need to be told when the socket is ready for
1167           //  write anymore.
1168          key.interestOps(0);
1169         } catch (CancelledKeyException e) {
1170           /* The Listener/reader might have closed the socket.
1171            * We don't explicitly cancel the key, so not sure if this will
1172            * ever fire.
1173            * This warning could be removed.
1174            */
1175           LOG.warn("Exception while changing ops : " + e);
1176         }
1177       }
1178     }
1179 
1180     /**
1181      * Process the response for this call. You need to have the lock on
1182      * {@link org.apache.hadoop.hbase.ipc.RpcServer.Connection#responseWriteLock}
1183      *
1184      * @param call the call
1185      * @return true if we proceed the call fully, false otherwise.
1186      * @throws IOException
1187      */
1188     private boolean processResponse(final Call call) throws IOException {
1189       boolean error = true;
1190       try {
1191         // Send as much data as we can in the non-blocking fashion
1192         long numBytes = channelWrite(call.connection.channel, call.response);
1193         if (numBytes < 0) {
1194           throw new HBaseIOException("Error writing on the socket " +
1195             "for the call:" + call.toShortString());
1196         }
1197         error = false;
1198       } finally {
1199         if (error) {
1200           LOG.debug(getName() + call.toShortString() + ": output error -- closing");
1201           closeConnection(call.connection);
1202         }
1203       }
1204 
1205       if (!call.response.hasRemaining()) {
1206         call.done();
1207         return true;
1208       } else {
1209         return false; // Socket can't take more, we will have to come back.
1210       }
1211     }
1212 
1213     /**
1214      * Process all the responses for this connection
1215      *
1216      * @return true if all the calls were processed or that someone else is doing it.
1217      * false if there * is still some work to do. In this case, we expect the caller to
1218      * delay us.
1219      * @throws IOException
1220      */
1221     private boolean processAllResponses(final Connection connection) throws IOException {
1222       // We want only one writer on the channel for a connection at a time.
1223       connection.responseWriteLock.lock();
1224       try {
1225         for (int i = 0; i < 20; i++) {
1226           // protection if some handlers manage to need all the responder
1227           Call call = connection.responseQueue.pollFirst();
1228           if (call == null) {
1229             return true;
1230           }
1231           if (!processResponse(call)) {
1232             connection.responseQueue.addFirst(call);
1233             return false;
1234           }
1235         }
1236       } finally {
1237         connection.responseWriteLock.unlock();
1238       }
1239 
1240       return connection.responseQueue.isEmpty();
1241     }
1242 
1243     //
1244     // Enqueue a response from the application.
1245     //
1246     void doRespond(Call call) throws IOException {
1247       boolean added = false;
1248 
1249       // If there is already a write in progress, we don't wait. This allows to free the handlers
1250       //  immediately for other tasks.
1251       if (call.connection.responseQueue.isEmpty() && call.connection.responseWriteLock.tryLock()) {
1252         try {
1253           if (call.connection.responseQueue.isEmpty()) {
1254             // If we're alone, we can try to do a direct call to the socket. It's
1255             //  an optimisation to save on context switches and data transfer between cores..
1256             if (processResponse(call)) {
1257               return; // we're done.
1258             }
1259             // Too big to fit, putting ahead.
1260             call.connection.responseQueue.addFirst(call);
1261             added = true; // We will register to the selector later, outside of the lock.
1262           }
1263         } finally {
1264           call.connection.responseWriteLock.unlock();
1265         }
1266       }
1267 
1268       if (!added) {
1269         call.connection.responseQueue.addLast(call);
1270       }
1271       call.responder.registerForWrite(call.connection);
1272 
1273       // set the serve time when the response has to be sent later
1274       call.timestamp = System.currentTimeMillis();
1275     }
1276   }
1277 
1278   /** Reads calls from a connection and queues them for handling. */
1279   @edu.umd.cs.findbugs.annotations.SuppressWarnings(
1280       value="VO_VOLATILE_INCREMENT",
1281       justification="False positive according to http://sourceforge.net/p/findbugs/bugs/1032/")
1282   public class Connection {
1283     // If initial preamble with version and magic has been read or not.
1284     private boolean connectionPreambleRead = false;
1285     // If the connection header has been read or not.
1286     private boolean connectionHeaderRead = false;
1287     protected SocketChannel channel;
1288     private ByteBuffer data;
1289     private ByteBuffer dataLengthBuffer;
1290     private ByteBuffer preambleBuffer;
1291     protected final ConcurrentLinkedDeque<Call> responseQueue = new ConcurrentLinkedDeque<Call>();
1292     private final Lock responseWriteLock = new ReentrantLock();
1293     private Counter rpcCount = new Counter(); // number of outstanding rpcs
1294     private long lastContact;
1295     private InetAddress addr;
1296     protected Socket socket;
1297     // Cache the remote host & port info so that even if the socket is
1298     // disconnected, we can say where it used to connect to.
1299     protected String hostAddress;
1300     protected int remotePort;
1301     ConnectionHeader connectionHeader;
1302 
1303     /**
1304      * Codec the client asked use.
1305      */
1306     private Codec codec;
1307     /**
1308      * Compression codec the client asked us use.
1309      */
1310     private CompressionCodec compressionCodec;
1311     BlockingService service;
1312 
1313     private AuthMethod authMethod;
1314     private boolean saslContextEstablished;
1315     private boolean skipInitialSaslHandshake;
1316     private ByteBuffer unwrappedData;
1317     // When is this set?  FindBugs wants to know!  Says NP
1318     private ByteBuffer unwrappedDataLengthBuffer = ByteBuffer.allocate(4);
1319     boolean useSasl;
1320     SaslServer saslServer;
1321     private boolean useWrap = false;
1322     // Fake 'call' for failed authorization response
1323     private static final int AUTHORIZATION_FAILED_CALLID = -1;
1324     private final Call authFailedCall = new Call(AUTHORIZATION_FAILED_CALLID, null, null, null,
1325         null, null, this, null, 0, null, null, 0);
1326     private ByteArrayOutputStream authFailedResponse =
1327         new ByteArrayOutputStream();
1328     // Fake 'call' for SASL context setup
1329     private static final int SASL_CALLID = -33;
1330     private final Call saslCall = new Call(SASL_CALLID, null, null, null, null, null, this, null,
1331         0, null, null, 0);
1332 
1333     // was authentication allowed with a fallback to simple auth
1334     private boolean authenticatedWithFallback;
1335 
1336     private boolean retryImmediatelySupported = false;
1337 
1338     public UserGroupInformation attemptingUser = null; // user name before auth
1339     protected User user = null;
1340     protected UserGroupInformation ugi = null;
1341 
1342     public Connection(SocketChannel channel, long lastContact) {
1343       this.channel = channel;
1344       this.lastContact = lastContact;
1345       this.data = null;
1346       this.dataLengthBuffer = ByteBuffer.allocate(4);
1347       this.socket = channel.socket();
1348       this.addr = socket.getInetAddress();
1349       if (addr == null) {
1350         this.hostAddress = "*Unknown*";
1351       } else {
1352         this.hostAddress = addr.getHostAddress();
1353       }
1354       this.remotePort = socket.getPort();
1355       if (socketSendBufferSize != 0) {
1356         try {
1357           socket.setSendBufferSize(socketSendBufferSize);
1358         } catch (IOException e) {
1359           LOG.warn("Connection: unable to set socket send buffer size to " +
1360                    socketSendBufferSize);
1361         }
1362       }
1363     }
1364 
1365       @Override
1366     public String toString() {
1367       return getHostAddress() + ":" + remotePort;
1368     }
1369 
1370     public String getHostAddress() {
1371       return hostAddress;
1372     }
1373 
1374     public InetAddress getHostInetAddress() {
1375       return addr;
1376     }
1377 
1378     public int getRemotePort() {
1379       return remotePort;
1380     }
1381 
1382     public void setLastContact(long lastContact) {
1383       this.lastContact = lastContact;
1384     }
1385 
1386     public VersionInfo getVersionInfo() {
1387       if (connectionHeader.hasVersionInfo()) {
1388         return connectionHeader.getVersionInfo();
1389       }
1390       return null;
1391     }
1392 
1393     /* Return true if the connection has no outstanding rpc */
1394     private boolean isIdle() {
1395       return rpcCount.get() == 0;
1396     }
1397 
1398     /* Decrement the outstanding RPC count */
1399     protected void decRpcCount() {
1400       rpcCount.decrement();
1401     }
1402 
1403     /* Increment the outstanding RPC count */
1404     protected void incRpcCount() {
1405       rpcCount.increment();
1406     }
1407 
1408     protected boolean timedOut(long currentTime) {
1409       return isIdle() && currentTime - lastContact > maxIdleTime;
1410     }
1411 
1412     private UserGroupInformation getAuthorizedUgi(String authorizedId)
1413         throws IOException {
1414       UserGroupInformation authorizedUgi;
1415       if (authMethod == AuthMethod.DIGEST) {
1416         TokenIdentifier tokenId = HBaseSaslRpcServer.getIdentifier(authorizedId,
1417             secretManager);
1418         authorizedUgi = tokenId.getUser();
1419         if (authorizedUgi == null) {
1420           throw new AccessDeniedException(
1421               "Can't retrieve username from tokenIdentifier.");
1422         }
1423         authorizedUgi.addTokenIdentifier(tokenId);
1424       } else {
1425         authorizedUgi = UserGroupInformation.createRemoteUser(authorizedId);
1426       }
1427       authorizedUgi.setAuthenticationMethod(authMethod.authenticationMethod.getAuthMethod());
1428       return authorizedUgi;
1429     }
1430 
1431     private void saslReadAndProcess(ByteBuffer saslToken) throws IOException,
1432         InterruptedException {
1433       if (saslContextEstablished) {
1434         if (LOG.isTraceEnabled())
1435           LOG.trace("Have read input token of size " + saslToken.limit()
1436               + " for processing by saslServer.unwrap()");
1437 
1438         if (!useWrap) {
1439           processOneRpc(saslToken);
1440         } else {
1441           byte[] b = saslToken.array();
1442           byte [] plaintextData = saslServer.unwrap(b, saslToken.position(), saslToken.limit());
1443           processUnwrappedData(plaintextData);
1444         }
1445       } else {
1446         byte[] replyToken;
1447         try {
1448           if (saslServer == null) {
1449             switch (authMethod) {
1450             case DIGEST:
1451               if (secretManager == null) {
1452                 throw new AccessDeniedException(
1453                     "Server is not configured to do DIGEST authentication.");
1454               }
1455               saslServer = Sasl.createSaslServer(AuthMethod.DIGEST
1456                   .getMechanismName(), null, SaslUtil.SASL_DEFAULT_REALM,
1457                   HBaseSaslRpcServer.getSaslProps(), new SaslDigestCallbackHandler(
1458                       secretManager, this));
1459               break;
1460             default:
1461               UserGroupInformation current = UserGroupInformation.getCurrentUser();
1462               String fullName = current.getUserName();
1463               if (LOG.isDebugEnabled()) {
1464                 LOG.debug("Kerberos principal name is " + fullName);
1465               }
1466               final String names[] = SaslUtil.splitKerberosName(fullName);
1467               if (names.length != 3) {
1468                 throw new AccessDeniedException(
1469                     "Kerberos principal name does NOT have the expected "
1470                         + "hostname part: " + fullName);
1471               }
1472               current.doAs(new PrivilegedExceptionAction<Object>() {
1473                 @Override
1474                 public Object run() throws SaslException {
1475                   saslServer = Sasl.createSaslServer(AuthMethod.KERBEROS
1476                       .getMechanismName(), names[0], names[1],
1477                       HBaseSaslRpcServer.getSaslProps(), new SaslGssCallbackHandler());
1478                   return null;
1479                 }
1480               });
1481             }
1482             if (saslServer == null)
1483               throw new AccessDeniedException(
1484                   "Unable to find SASL server implementation for "
1485                       + authMethod.getMechanismName());
1486             if (LOG.isDebugEnabled()) {
1487               LOG.debug("Created SASL server with mechanism = " + authMethod.getMechanismName());
1488             }
1489           }
1490           if (LOG.isDebugEnabled()) {
1491             LOG.debug("Have read input token of size " + saslToken.limit()
1492                 + " for processing by saslServer.evaluateResponse()");
1493           }
1494           replyToken = saslServer.evaluateResponse(saslToken.array());
1495         } catch (IOException e) {
1496           IOException sendToClient = e;
1497           Throwable cause = e;
1498           while (cause != null) {
1499             if (cause instanceof InvalidToken) {
1500               sendToClient = (InvalidToken) cause;
1501               break;
1502             }
1503             cause = cause.getCause();
1504           }
1505           doRawSaslReply(SaslStatus.ERROR, null, sendToClient.getClass().getName(),
1506             sendToClient.getLocalizedMessage());
1507           metrics.authenticationFailure();
1508           String clientIP = this.toString();
1509           // attempting user could be null
1510           AUDITLOG.warn(AUTH_FAILED_FOR + clientIP + ":" + attemptingUser);
1511           throw e;
1512         }
1513         if (replyToken != null) {
1514           if (LOG.isDebugEnabled()) {
1515             LOG.debug("Will send token of size " + replyToken.length
1516                 + " from saslServer.");
1517           }
1518           doRawSaslReply(SaslStatus.SUCCESS, new BytesWritable(replyToken), null,
1519               null);
1520         }
1521         if (saslServer.isComplete()) {
1522           String qop = (String) saslServer.getNegotiatedProperty(Sasl.QOP);
1523           useWrap = qop != null && !"auth".equalsIgnoreCase(qop);
1524           ugi = getAuthorizedUgi(saslServer.getAuthorizationID());
1525           if (LOG.isDebugEnabled()) {
1526             LOG.debug("SASL server context established. Authenticated client: "
1527               + ugi + ". Negotiated QoP is "
1528               + saslServer.getNegotiatedProperty(Sasl.QOP));
1529           }
1530           metrics.authenticationSuccess();
1531           AUDITLOG.info(AUTH_SUCCESSFUL_FOR + ugi);
1532           saslContextEstablished = true;
1533         }
1534       }
1535     }
1536 
1537     /**
1538      * No protobuf encoding of raw sasl messages
1539      */
1540     private void doRawSaslReply(SaslStatus status, Writable rv,
1541         String errorClass, String error) throws IOException {
1542       ByteBufferOutputStream saslResponse = null;
1543       DataOutputStream out = null;
1544       try {
1545         // In my testing, have noticed that sasl messages are usually
1546         // in the ballpark of 100-200. That's why the initial capacity is 256.
1547         saslResponse = new ByteBufferOutputStream(256);
1548         out = new DataOutputStream(saslResponse);
1549         out.writeInt(status.state); // write status
1550         if (status == SaslStatus.SUCCESS) {
1551           rv.write(out);
1552         } else {
1553           WritableUtils.writeString(out, errorClass);
1554           WritableUtils.writeString(out, error);
1555         }
1556         saslCall.setSaslTokenResponse(saslResponse.getByteBuffer());
1557         saslCall.responder = responder;
1558         saslCall.sendResponseIfReady();
1559       } finally {
1560         if (saslResponse != null) {
1561           saslResponse.close();
1562         }
1563         if (out != null) {
1564           out.close();
1565         }
1566       }
1567     }
1568 
1569     private void disposeSasl() {
1570       if (saslServer != null) {
1571         try {
1572           saslServer.dispose();
1573           saslServer = null;
1574         } catch (SaslException ignored) {
1575           // Ignored. This is being disposed of anyway.
1576         }
1577       }
1578     }
1579 
1580     private int readPreamble() throws IOException {
1581       if (preambleBuffer == null) {
1582         preambleBuffer = ByteBuffer.allocate(6);
1583       }
1584       int count = channelRead(channel, preambleBuffer);
1585       if (count < 0 || preambleBuffer.remaining() > 0) {
1586         return count;
1587       }
1588       // Check for 'HBas' magic.
1589       preambleBuffer.flip();
1590       for (int i = 0; i < HConstants.RPC_HEADER.length; i++) {
1591         if (HConstants.RPC_HEADER[i] != preambleBuffer.get(i)) {
1592           return doBadPreambleHandling("Expected HEADER=" +
1593               Bytes.toStringBinary(HConstants.RPC_HEADER) + " but received HEADER=" +
1594               Bytes.toStringBinary(preambleBuffer.array(), 0, HConstants.RPC_HEADER.length) +
1595               " from " + toString());
1596         }
1597       }
1598       int version = preambleBuffer.get(HConstants.RPC_HEADER.length);
1599       byte authbyte = preambleBuffer.get(HConstants.RPC_HEADER.length + 1);
1600       this.authMethod = AuthMethod.valueOf(authbyte);
1601       if (version != CURRENT_VERSION) {
1602         String msg = getFatalConnectionString(version, authbyte);
1603         return doBadPreambleHandling(msg, new WrongVersionException(msg));
1604       }
1605       if (authMethod == null) {
1606         String msg = getFatalConnectionString(version, authbyte);
1607         return doBadPreambleHandling(msg, new BadAuthException(msg));
1608       }
1609       if (isSecurityEnabled && authMethod == AuthMethod.SIMPLE) {
1610         if (allowFallbackToSimpleAuth) {
1611           metrics.authenticationFallback();
1612           authenticatedWithFallback = true;
1613         } else {
1614           AccessDeniedException ae = new AccessDeniedException("Authentication is required");
1615           setupResponse(authFailedResponse, authFailedCall, ae, ae.getMessage());
1616           responder.doRespond(authFailedCall);
1617           throw ae;
1618         }
1619       }
1620       if (!isSecurityEnabled && authMethod != AuthMethod.SIMPLE) {
1621         doRawSaslReply(SaslStatus.SUCCESS, new IntWritable(
1622             SaslUtil.SWITCH_TO_SIMPLE_AUTH), null, null);
1623         authMethod = AuthMethod.SIMPLE;
1624         // client has already sent the initial Sasl message and we
1625         // should ignore it. Both client and server should fall back
1626         // to simple auth from now on.
1627         skipInitialSaslHandshake = true;
1628       }
1629       if (authMethod != AuthMethod.SIMPLE) {
1630         useSasl = true;
1631       }
1632 
1633       preambleBuffer = null; // do not need it anymore
1634       connectionPreambleRead = true;
1635       return count;
1636     }
1637 
1638     private int read4Bytes() throws IOException {
1639       if (this.dataLengthBuffer.remaining() > 0) {
1640         return channelRead(channel, this.dataLengthBuffer);
1641       } else {
1642         return 0;
1643       }
1644     }
1645 
1646 
1647     /**
1648      * Read off the wire. If there is not enough data to read, update the connection state with
1649      *  what we have and returns.
1650      * @return Returns -1 if failure (and caller will close connection), else zero or more.
1651      * @throws IOException
1652      * @throws InterruptedException
1653      */
1654     public int readAndProcess() throws IOException, InterruptedException {
1655       // If we have not read the connection setup preamble, look to see if that is on the wire.
1656       if (!connectionPreambleRead) {
1657         int count = readPreamble();
1658         if (!connectionPreambleRead) {
1659           return count;
1660         }
1661       }
1662       // Try and read in an int. It will be length of the data to read (or -1 if a ping). We catch
1663       // the integer length into the 4-byte this.dataLengthBuffer.
1664       int count = read4Bytes();
1665       if (count < 0 || dataLengthBuffer.remaining() > 0) {
1666         return count;
1667       }
1668 
1669       // If we have not read the connection setup preamble, look to see if that is on the wire.
1670       if (!connectionPreambleRead) {
1671         count = readPreamble();
1672         if (!connectionPreambleRead) {
1673           return count;
1674         }
1675 
1676         count = read4Bytes();
1677         if (count < 0 || dataLengthBuffer.remaining() > 0) {
1678           return count;
1679         }
1680       }
1681 
1682       final boolean useWrap = this.useWrap;
1683       final BlockingService service = this.service;
1684       final boolean headerAndPreambleRead = connectionHeaderRead && connectionPreambleRead;
1685       final boolean canUseRequestTooBig = headerAndPreambleRead &&
1686           VersionInfoUtil.hasMinimumVersion(connectionHeader.getVersionInfo(),
1687               RequestTooBigException.MAJOR_VERSION, RequestTooBigException.MINOR_VERSION);
1688 
1689       // we're guarding against data being modified concurrently
1690       // while trying to keep other instance members out of the block
1691       synchronized(this) {
1692         // We have read a length and we have read the preamble.  It is either the connection header
1693         // or it is a request.
1694         if (data == null) {
1695           dataLengthBuffer.flip();
1696           int dataLength = dataLengthBuffer.getInt();
1697           if (dataLength == RpcClient.PING_CALL_ID) {
1698             if (!useWrap) { //covers the !useSasl too
1699               dataLengthBuffer.clear();
1700               return 0;  //ping message
1701             }
1702           }
1703           if (dataLength < 0) { // A data length of zero is legal.
1704             throw new DoNotRetryIOException("Unexpected data length "
1705                 + dataLength + "!! from " + getHostAddress());
1706           }
1707 
1708           if (dataLength > maxRequestSize) {
1709             String msg = "RPC data length of " + dataLength + " received from "
1710                 + getHostAddress() + " is greater than max allowed "
1711                 + maxRequestSize + ". Set \"" + MAX_REQUEST_SIZE
1712                 + "\" on server to override this limit (not recommended)";
1713             LOG.warn(msg);
1714 
1715             if (headerAndPreambleRead) {
1716               incRpcCount();
1717               // Construct InputStream for the non-blocking SocketChannel
1718               // We need the InputStream because we want to read only the request header
1719               // instead of the whole rpc.
1720               final ByteBuffer buf = ByteBuffer.allocate(1);
1721               InputStream is = new InputStream() {
1722                 @Override
1723                 public int read() throws IOException {
1724                   channelRead(channel, buf);
1725                   buf.flip();
1726                   int x = buf.get();
1727                   buf.flip();
1728                   return x;
1729                 }
1730               };
1731               CodedInputStream cis = CodedInputStream.newInstance(is);
1732               int headerSize = cis.readRawVarint32();
1733               Message.Builder builder = RequestHeader.newBuilder();
1734               ProtobufUtil.mergeFrom(builder, cis, headerSize);
1735               RequestHeader header = (RequestHeader) builder.build();
1736 
1737               // Notify the client about the offending request
1738               Call reqTooBig = new Call(header.getCallId(), service, null, null, null,
1739                   null, this, responder, 0, null, this.addr,0);
1740               metrics.exception(REQUEST_TOO_BIG_EXCEPTION);
1741               // Make sure the client recognizes the underlying exception
1742               // Otherwise, throw a DoNotRetryIOException.
1743               if (canUseRequestTooBig) {
1744                 setupResponse(null, reqTooBig, REQUEST_TOO_BIG_EXCEPTION, msg);
1745               } else {
1746                 setupResponse(null, reqTooBig, new DoNotRetryIOException(), msg);
1747               }
1748               // We are going to close the connection, make sure we process the response
1749               // before that. In rare case when this fails, we still close the connection.
1750               responseWriteLock.lock();
1751               responder.processResponse(reqTooBig);
1752               responseWriteLock.unlock();
1753             }
1754             // Close the connection
1755             return -1;
1756           }
1757 
1758           data = ByteBuffer.allocate(dataLength);
1759 
1760           // Increment the rpc count. This counter will be decreased when we write
1761           //  the response.  If we want the connection to be detected as idle properly, we
1762           //  need to keep the inc / dec correct.
1763           incRpcCount();
1764         }
1765 
1766         count = channelRead(channel, data);
1767 
1768         if (count >= 0 && data.remaining() == 0) { // count==0 if dataLength == 0
1769           process();
1770         }
1771       }
1772 
1773       return count;
1774     }
1775 
1776     /**
1777      * Process the data buffer and clean the connection state for the next call.
1778      */
1779     private void process() throws IOException, InterruptedException {
1780       data.flip();
1781       try {
1782         if (skipInitialSaslHandshake) {
1783           skipInitialSaslHandshake = false;
1784           return;
1785         }
1786 
1787         if (useSasl) {
1788           saslReadAndProcess(data);
1789         } else {
1790           processOneRpc(data);
1791         }
1792 
1793       } finally {
1794         dataLengthBuffer.clear(); // Clean for the next call
1795         data = null; // For the GC
1796       }
1797     }
1798 
1799     private String getFatalConnectionString(final int version, final byte authByte) {
1800       return "serverVersion=" + CURRENT_VERSION +
1801       ", clientVersion=" + version + ", authMethod=" + authByte +
1802       ", authSupported=" + (authMethod != null) + " from " + toString();
1803     }
1804 
1805     private int doBadPreambleHandling(final String msg) throws IOException {
1806       return doBadPreambleHandling(msg, new FatalConnectionException(msg));
1807     }
1808 
1809     private int doBadPreambleHandling(final String msg, final Exception e) throws IOException {
1810       LOG.warn(msg);
1811       Call fakeCall = new Call(-1, null, null, null, null, null, this, responder, -1, null, null,0);
1812       setupResponse(null, fakeCall, e, msg);
1813       responder.doRespond(fakeCall);
1814       // Returning -1 closes out the connection.
1815       return -1;
1816     }
1817 
1818     // Reads the connection header following version
1819     private void processConnectionHeader(ByteBuffer buf) throws IOException {
1820       this.connectionHeader = ConnectionHeader.parseFrom(
1821         new ByteBufferInputStream(buf));
1822       String serviceName = connectionHeader.getServiceName();
1823       if (serviceName == null) throw new EmptyServiceNameException();
1824       this.service = getService(services, serviceName);
1825       if (this.service == null) throw new UnknownServiceException(serviceName);
1826       setupCellBlockCodecs(this.connectionHeader);
1827       UserGroupInformation protocolUser = createUser(connectionHeader);
1828       if (!useSasl) {
1829         ugi = protocolUser;
1830         if (ugi != null) {
1831           ugi.setAuthenticationMethod(AuthMethod.SIMPLE.authenticationMethod);
1832         }
1833         // audit logging for SASL authenticated users happens in saslReadAndProcess()
1834         if (authenticatedWithFallback) {
1835           LOG.warn("Allowed fallback to SIMPLE auth for " + ugi
1836               + " connecting from " + getHostAddress());
1837         }
1838         AUDITLOG.info(AUTH_SUCCESSFUL_FOR + ugi);
1839       } else {
1840         // user is authenticated
1841         ugi.setAuthenticationMethod(authMethod.authenticationMethod);
1842         //Now we check if this is a proxy user case. If the protocol user is
1843         //different from the 'user', it is a proxy user scenario. However,
1844         //this is not allowed if user authenticated with DIGEST.
1845         if ((protocolUser != null)
1846             && (!protocolUser.getUserName().equals(ugi.getUserName()))) {
1847           if (authMethod == AuthMethod.DIGEST) {
1848             // Not allowed to doAs if token authentication is used
1849             throw new AccessDeniedException("Authenticated user (" + ugi
1850                 + ") doesn't match what the client claims to be ("
1851                 + protocolUser + ")");
1852           } else {
1853             // Effective user can be different from authenticated user
1854             // for simple auth or kerberos auth
1855             // The user is the real user. Now we create a proxy user
1856             UserGroupInformation realUser = ugi;
1857             ugi = UserGroupInformation.createProxyUser(protocolUser
1858                 .getUserName(), realUser);
1859             // Now the user is a proxy user, set Authentication method Proxy.
1860             ugi.setAuthenticationMethod(AuthenticationMethod.PROXY);
1861           }
1862         }
1863       }
1864       if (connectionHeader.hasVersionInfo()) {
1865         // see if this connection will support RetryImmediatelyException
1866         retryImmediatelySupported = VersionInfoUtil.hasMinimumVersion(getVersionInfo(), 1, 2);
1867 
1868         AUDITLOG.info("Connection from " + this.hostAddress + " port: " + this.remotePort
1869             + " with version info: "
1870             + TextFormat.shortDebugString(connectionHeader.getVersionInfo()));
1871       } else {
1872         AUDITLOG.info("Connection from " + this.hostAddress + " port: " + this.remotePort
1873             + " with unknown version info");
1874       }
1875 
1876 
1877     }
1878 
1879     /**
1880      * Set up cell block codecs
1881      * @throws FatalConnectionException
1882      */
1883     private void setupCellBlockCodecs(final ConnectionHeader header)
1884     throws FatalConnectionException {
1885       // TODO: Plug in other supported decoders.
1886       if (!header.hasCellBlockCodecClass()) return;
1887       String className = header.getCellBlockCodecClass();
1888       if (className == null || className.length() == 0) return;
1889       try {
1890         this.codec = (Codec)Class.forName(className).getDeclaredConstructor().newInstance();
1891       } catch (Exception e) {
1892         throw new UnsupportedCellCodecException(className, e);
1893       }
1894       if (!header.hasCellBlockCompressorClass()) return;
1895       className = header.getCellBlockCompressorClass();
1896       try {
1897         this.compressionCodec = (CompressionCodec)
1898           Class.forName(className).getDeclaredConstructor().newInstance();
1899       } catch (Exception e) {
1900         throw new UnsupportedCompressionCodecException(className, e);
1901       }
1902     }
1903 
1904     private void processUnwrappedData(byte[] inBuf) throws IOException,
1905     InterruptedException {
1906       ReadableByteChannel ch = Channels.newChannel(new ByteArrayInputStream(inBuf));
1907       // Read all RPCs contained in the inBuf, even partial ones
1908       while (true) {
1909         int count;
1910         if (unwrappedDataLengthBuffer.remaining() > 0) {
1911           count = channelRead(ch, unwrappedDataLengthBuffer);
1912           if (count <= 0 || unwrappedDataLengthBuffer.remaining() > 0)
1913             return;
1914         }
1915 
1916         if (unwrappedData == null) {
1917           unwrappedDataLengthBuffer.flip();
1918           int unwrappedDataLength = unwrappedDataLengthBuffer.getInt();
1919 
1920           if (unwrappedDataLength == RpcClient.PING_CALL_ID) {
1921             if (LOG.isDebugEnabled())
1922               LOG.debug("Received ping message");
1923             unwrappedDataLengthBuffer.clear();
1924             continue; // ping message
1925           }
1926           unwrappedData = ByteBuffer.allocate(unwrappedDataLength);
1927         }
1928 
1929         count = channelRead(ch, unwrappedData);
1930         if (count <= 0 || unwrappedData.remaining() > 0)
1931           return;
1932 
1933         if (unwrappedData.remaining() == 0) {
1934           unwrappedDataLengthBuffer.clear();
1935           unwrappedData.flip();
1936           processOneRpc(unwrappedData);
1937           unwrappedData = null;
1938         }
1939       }
1940     }
1941 
1942 
1943     private void processOneRpc(ByteBuffer buf) throws IOException, InterruptedException {
1944       if (connectionHeaderRead) {
1945         processRequest(buf);
1946       } else {
1947         processConnectionHeader(buf);
1948         this.connectionHeaderRead = true;
1949         if (!authorizeConnection()) {
1950           // Throw FatalConnectionException wrapping ACE so client does right thing and closes
1951           // down the connection instead of trying to read non-existent retun.
1952           throw new AccessDeniedException("Connection from " + this + " for service " +
1953             connectionHeader.getServiceName() + " is unauthorized for user: " + ugi);
1954         }
1955         this.user = userProvider.create(this.ugi);
1956       }
1957     }
1958 
1959     /**
1960      * @param buf Has the request header and the request param and optionally encoded data buffer
1961      * all in this one array.
1962      * @throws IOException
1963      * @throws InterruptedException
1964      */
1965     protected void processRequest(ByteBuffer buf) throws IOException, InterruptedException {
1966       long totalRequestSize = buf.limit();
1967       int offset = 0;
1968       // Here we read in the header.  We avoid having pb
1969       // do its default 4k allocation for CodedInputStream.  We force it to use backing array.
1970       CodedInputStream cis = CodedInputStream.newInstance(buf.array(), offset, buf.limit());
1971       int headerSize = cis.readRawVarint32();
1972       offset = cis.getTotalBytesRead();
1973       Message.Builder builder = RequestHeader.newBuilder();
1974       ProtobufUtil.mergeFrom(builder, cis, headerSize);
1975       RequestHeader header = (RequestHeader) builder.build();
1976       offset += headerSize;
1977       int id = header.getCallId();
1978       if (LOG.isTraceEnabled()) {
1979         LOG.trace("RequestHeader " + TextFormat.shortDebugString(header) +
1980           " totalRequestSize: " + totalRequestSize + " bytes");
1981       }
1982       // Enforcing the call queue size, this triggers a retry in the client
1983       // This is a bit late to be doing this check - we have already read in the total request.
1984       if ((totalRequestSize + callQueueSize.get()) > maxQueueSize) {
1985         final Call callTooBig =
1986           new Call(id, this.service, null, null, null, null, this,
1987             responder, totalRequestSize, null, null, 0);
1988         ByteArrayOutputStream responseBuffer = new ByteArrayOutputStream();
1989         metrics.exception(CALL_QUEUE_TOO_BIG_EXCEPTION);
1990         setupResponse(responseBuffer, callTooBig, CALL_QUEUE_TOO_BIG_EXCEPTION,
1991             "Call queue is full on " + server.getServerName() +
1992                 ", is hbase.ipc.server.max.callqueue.size too small?");
1993         responder.doRespond(callTooBig);
1994         return;
1995       }
1996       MethodDescriptor md = null;
1997       Message param = null;
1998       CellScanner cellScanner = null;
1999       try {
2000         if (header.hasRequestParam() && header.getRequestParam()) {
2001           md = this.service.getDescriptorForType().findMethodByName(header.getMethodName());
2002           if (md == null) throw new UnsupportedOperationException(header.getMethodName());
2003           builder = this.service.getRequestPrototype(md).newBuilderForType();
2004           cis.resetSizeCounter();
2005           int paramSize = cis.readRawVarint32();
2006           offset += cis.getTotalBytesRead();
2007           if (builder != null) {
2008             ProtobufUtil.mergeFrom(builder, cis, paramSize);
2009             param = builder.build();
2010           }
2011           offset += paramSize;
2012         } else {
2013           // currently header must have request param, so we directly throw exception here
2014           String msg = "Invalid request header: " + TextFormat.shortDebugString(header)
2015               + ", should have param set in it";
2016           LOG.warn(msg);
2017           throw new DoNotRetryIOException(msg);
2018         }
2019         if (header.hasCellBlockMeta()) {
2020           buf.position(offset);
2021           cellScanner = cellBlockBuilder.createCellScanner(this.codec, this.compressionCodec, buf);
2022         }
2023       } catch (Throwable t) {
2024         InetSocketAddress address = getListenerAddress();
2025         String msg = (address != null ? address : "(channel closed)") +
2026             " is unable to read call parameter from client " + getHostAddress();
2027         LOG.warn(msg, t);
2028 
2029         metrics.exception(t);
2030 
2031         // probably the hbase hadoop version does not match the running hadoop version
2032         if (t instanceof LinkageError) {
2033           t = new DoNotRetryIOException(t);
2034         }
2035         // If the method is not present on the server, do not retry.
2036         if (t instanceof UnsupportedOperationException) {
2037           t = new DoNotRetryIOException(t);
2038         }
2039 
2040         final Call readParamsFailedCall =
2041           new Call(id, this.service, null, null, null, null, this,
2042             responder, totalRequestSize, null, null, 0);
2043         ByteArrayOutputStream responseBuffer = new ByteArrayOutputStream();
2044         setupResponse(responseBuffer, readParamsFailedCall, t,
2045           msg + "; " + t.getMessage());
2046         responder.doRespond(readParamsFailedCall);
2047         return;
2048       }
2049 
2050       TraceInfo traceInfo = header.hasTraceInfo()
2051           ? new TraceInfo(header.getTraceInfo().getTraceId(), header.getTraceInfo().getParentId())
2052           : null;
2053       int timeout = 0;
2054       if (header.hasTimeout() && header.getTimeout() > 0){
2055         timeout = Math.max(minClientRequestTimeout, header.getTimeout());
2056       }
2057       Call call = new Call(id, this.service, md, header, param, cellScanner, this, responder,
2058               totalRequestSize, traceInfo, this.addr, timeout);
2059 
2060       if (!scheduler.dispatch(new CallRunner(RpcServer.this, call))) {
2061         callQueueSize.add(-1 * call.getSize());
2062 
2063         ByteArrayOutputStream responseBuffer = new ByteArrayOutputStream();
2064         metrics.exception(CALL_QUEUE_TOO_BIG_EXCEPTION);
2065         setupResponse(responseBuffer, call, CALL_QUEUE_TOO_BIG_EXCEPTION,
2066             "Call queue is full on " + server.getServerName() +
2067                 ", too many items queued ?");
2068         responder.doRespond(call);
2069       }
2070     }
2071 
2072     private boolean authorizeConnection() throws IOException {
2073       try {
2074         // If auth method is DIGEST, the token was obtained by the
2075         // real user for the effective user, therefore not required to
2076         // authorize real user. doAs is allowed only for simple or kerberos
2077         // authentication
2078         if (ugi != null && ugi.getRealUser() != null
2079             && (authMethod != AuthMethod.DIGEST)) {
2080           ProxyUsers.authorize(ugi, this.getHostAddress(), conf);
2081         }
2082         authorize(ugi, connectionHeader, getHostInetAddress());
2083         metrics.authorizationSuccess();
2084       } catch (AuthorizationException ae) {
2085         if (LOG.isDebugEnabled()) {
2086           LOG.debug("Connection authorization failed: " + ae.getMessage(), ae);
2087         }
2088         metrics.authorizationFailure();
2089         setupResponse(authFailedResponse, authFailedCall,
2090           new AccessDeniedException(ae), ae.getMessage());
2091         responder.doRespond(authFailedCall);
2092         return false;
2093       }
2094       return true;
2095     }
2096 
2097     protected synchronized void close() {
2098       disposeSasl();
2099       data = null;
2100       if (!channel.isOpen())
2101         return;
2102       try {socket.shutdownOutput();} catch(Exception ignored) {
2103         if (LOG.isTraceEnabled()) {
2104           LOG.trace(ignored);
2105         }
2106       }
2107       if (channel.isOpen()) {
2108         try {channel.close();} catch(Exception ignored) {
2109           if (LOG.isTraceEnabled()) {
2110             LOG.trace(ignored);
2111           }
2112         }
2113       }
2114       try {socket.close();} catch(Exception ignored) {
2115         if (LOG.isTraceEnabled()) {
2116           LOG.trace(ignored);
2117         }
2118       }
2119     }
2120 
2121     private UserGroupInformation createUser(ConnectionHeader head) {
2122       UserGroupInformation ugi = null;
2123 
2124       if (!head.hasUserInfo()) {
2125         return null;
2126       }
2127       UserInformation userInfoProto = head.getUserInfo();
2128       String effectiveUser = null;
2129       if (userInfoProto.hasEffectiveUser()) {
2130         effectiveUser = userInfoProto.getEffectiveUser();
2131       }
2132       String realUser = null;
2133       if (userInfoProto.hasRealUser()) {
2134         realUser = userInfoProto.getRealUser();
2135       }
2136       if (effectiveUser != null) {
2137         if (realUser != null) {
2138           UserGroupInformation realUserUgi =
2139               UserGroupInformation.createRemoteUser(realUser);
2140           ugi = UserGroupInformation.createProxyUser(effectiveUser, realUserUgi);
2141         } else {
2142           ugi = UserGroupInformation.createRemoteUser(effectiveUser);
2143         }
2144       }
2145       return ugi;
2146     }
2147   }
2148 
2149   /**
2150    * Datastructure for passing a {@link BlockingService} and its associated class of
2151    * protobuf service interface.  For example, a server that fielded what is defined
2152    * in the client protobuf service would pass in an implementation of the client blocking service
2153    * and then its ClientService.BlockingInterface.class.  Used checking connection setup.
2154    */
2155   public static class BlockingServiceAndInterface {
2156     private final BlockingService service;
2157     private final Class<?> serviceInterface;
2158     public BlockingServiceAndInterface(final BlockingService service,
2159         final Class<?> serviceInterface) {
2160       this.service = service;
2161       this.serviceInterface = serviceInterface;
2162     }
2163     public Class<?> getServiceInterface() {
2164       return this.serviceInterface;
2165     }
2166     public BlockingService getBlockingService() {
2167       return this.service;
2168     }
2169   }
2170 
2171   /**
2172    * Constructs a server listening on the named port and address.
2173    * @param server hosting instance of {@link Server}. We will do authentications if an
2174    * instance else pass null for no authentication check.
2175    * @param name Used keying this rpc servers' metrics and for naming the Listener thread.
2176    * @param services A list of services.
2177    * @param bindAddress Where to listen
2178    * @param conf
2179    * @param scheduler
2180    */
2181   public RpcServer(final Server server, final String name,
2182       final List<BlockingServiceAndInterface> services,
2183       final InetSocketAddress bindAddress, Configuration conf,
2184       RpcScheduler scheduler)
2185       throws IOException {
2186     
2187     if (conf.getBoolean("hbase.ipc.server.reservoir.enabled", true)) {
2188       this.reservoir = new BoundedByteBufferPool(
2189           conf.getInt("hbase.ipc.server.reservoir.max.buffer.size", 1024 * 1024),
2190           conf.getInt("hbase.ipc.server.reservoir.initial.buffer.size", 16 * 1024),
2191           // Make the max twice the number of handlers to be safe.
2192           conf.getInt("hbase.ipc.server.reservoir.initial.max",
2193               conf.getInt(HConstants.REGION_SERVER_HANDLER_COUNT,
2194                   HConstants.DEFAULT_REGION_SERVER_HANDLER_COUNT) * 2),
2195           // By default make direct byte buffers from the buffer pool.
2196           conf.getBoolean("hbase.ipc.server.reservoir.direct.buffer", false));
2197     } else {
2198       reservoir = null;
2199     }
2200     this.server = server;
2201     this.services = services;
2202     this.bindAddress = bindAddress;
2203     this.conf = conf;
2204     this.socketSendBufferSize = 0;
2205     this.maxQueueSize =
2206       this.conf.getInt("hbase.ipc.server.max.callqueue.size", DEFAULT_MAX_CALLQUEUE_SIZE);
2207     this.readThreads = conf.getInt("hbase.ipc.server.read.threadpool.size", 10);
2208     this.maxIdleTime = 2 * conf.getInt("hbase.ipc.client.connection.maxidletime", 1000);
2209     this.maxConnectionsToNuke = conf.getInt("hbase.ipc.client.kill.max", 10);
2210     this.thresholdIdleConnections = conf.getInt("hbase.ipc.client.idlethreshold", 4000);
2211     this.purgeTimeout = conf.getLong("hbase.ipc.client.call.purge.timeout",
2212       2 * HConstants.DEFAULT_HBASE_RPC_TIMEOUT);
2213     this.warnResponseTime = conf.getInt(WARN_RESPONSE_TIME, DEFAULT_WARN_RESPONSE_TIME);
2214     this.warnResponseSize = conf.getInt(WARN_RESPONSE_SIZE, DEFAULT_WARN_RESPONSE_SIZE);
2215     this.minClientRequestTimeout = conf.getInt(MIN_CLIENT_REQUEST_TIMEOUT,
2216         DEFAULT_MIN_CLIENT_REQUEST_TIMEOUT);
2217     this.maxRequestSize = conf.getInt(MAX_REQUEST_SIZE, DEFAULT_MAX_REQUEST_SIZE);
2218 
2219     // Start the listener here and let it bind to the port
2220     listener = new Listener(name);
2221     this.port = listener.getAddress().getPort();
2222 
2223     this.metrics = new MetricsHBaseServer(name, new MetricsHBaseServerWrapperImpl(this));
2224     this.tcpNoDelay = conf.getBoolean("hbase.ipc.server.tcpnodelay", true);
2225     this.tcpKeepAlive = conf.getBoolean("hbase.ipc.server.tcpkeepalive", true);
2226 
2227     this.cellBlockBuilder = new CellBlockBuilder(conf);
2228 
2229     // Create the responder here
2230     responder = new Responder();
2231     this.authorize = conf.getBoolean(HADOOP_SECURITY_AUTHORIZATION, false);
2232     this.userProvider = UserProvider.instantiate(conf);
2233     this.isSecurityEnabled = userProvider.isHBaseSecurityEnabled();
2234     if (isSecurityEnabled) {
2235       HBaseSaslRpcServer.init(conf);
2236     }
2237     initReconfigurable(conf);
2238 
2239     this.scheduler = scheduler;
2240     this.scheduler.init(new RpcSchedulerContext(this));
2241   }
2242 
2243   @Override
2244   public void onConfigurationChange(Configuration newConf) {
2245     initReconfigurable(newConf);
2246     if (scheduler instanceof ConfigurationObserver) {
2247       ((ConfigurationObserver)scheduler).onConfigurationChange(newConf);
2248     }
2249   }
2250 
2251   private void initReconfigurable(Configuration confToLoad) {
2252     this.allowFallbackToSimpleAuth = confToLoad.getBoolean(FALLBACK_TO_INSECURE_CLIENT_AUTH, false);
2253     if (isSecurityEnabled && allowFallbackToSimpleAuth) {
2254       LOG.warn("********* WARNING! *********");
2255       LOG.warn("This server is configured to allow connections from INSECURE clients");
2256       LOG.warn("(" + FALLBACK_TO_INSECURE_CLIENT_AUTH + " = true).");
2257       LOG.warn("While this option is enabled, client identities cannot be secured, and user");
2258       LOG.warn("impersonation is possible!");
2259       LOG.warn("For secure operation, please disable SIMPLE authentication as soon as possible,");
2260       LOG.warn("by setting " + FALLBACK_TO_INSECURE_CLIENT_AUTH + " = false in hbase-site.xml");
2261       LOG.warn("****************************");
2262     }
2263   }
2264 
2265   /**
2266    * Subclasses of HBaseServer can override this to provide their own
2267    * Connection implementations.
2268    */
2269   protected Connection getConnection(SocketChannel channel, long time) {
2270     return new Connection(channel, time);
2271   }
2272 
2273   /**
2274    * Setup response for the RPC Call.
2275    *
2276    * @param response buffer to serialize the response into
2277    * @param call {@link Call} to which we are setting up the response
2278    * @param error error message, if the call failed
2279    * @throws IOException
2280    */
2281   private void setupResponse(ByteArrayOutputStream response, Call call, Throwable t, String error)
2282   throws IOException {
2283     if (response != null) response.reset();
2284     call.setResponse(null, null, t, error);
2285   }
2286 
2287   protected void closeConnection(Connection connection) {
2288     synchronized (connectionList) {
2289       if (connectionList.remove(connection)) {
2290         numConnections--;
2291       }
2292     }
2293     connection.close();
2294   }
2295 
2296   Configuration getConf() {
2297     return conf;
2298   }
2299 
2300   /** Sets the socket buffer size used for responding to RPCs.
2301    * @param size send size
2302    */
2303   @Override
2304   public void setSocketSendBufSize(int size) { this.socketSendBufferSize = size; }
2305 
2306   @Override
2307   public boolean isStarted() {
2308     return this.started;
2309   }
2310 
2311   /** Starts the service.  Must be called before any calls will be handled. */
2312   @Override
2313   public synchronized void start() {
2314     if (started) return;
2315     authTokenSecretMgr = createSecretManager();
2316     if (authTokenSecretMgr != null) {
2317       setSecretManager(authTokenSecretMgr);
2318       authTokenSecretMgr.start();
2319     }
2320     this.authManager = new ServiceAuthorizationManager();
2321     HBasePolicyProvider.init(conf, authManager);
2322     responder.start();
2323     listener.start();
2324     scheduler.start();
2325     started = true;
2326   }
2327 
2328   @Override
2329   public synchronized void refreshAuthManager(PolicyProvider pp) {
2330     // Ignore warnings that this should be accessed in a static way instead of via an instance;
2331     // it'll break if you go via static route.
2332     this.authManager.refresh(this.conf, pp);
2333   }
2334 
2335   private AuthenticationTokenSecretManager createSecretManager() {
2336     if (!isSecurityEnabled) return null;
2337     if (server == null) return null;
2338     Configuration conf = server.getConfiguration();
2339     long keyUpdateInterval =
2340         conf.getLong("hbase.auth.key.update.interval", 24*60*60*1000);
2341     long maxAge =
2342         conf.getLong("hbase.auth.token.max.lifetime", 7*24*60*60*1000);
2343     return new AuthenticationTokenSecretManager(conf, server.getZooKeeper(),
2344         server.getServerName().toString(), keyUpdateInterval, maxAge);
2345   }
2346 
2347   public SecretManager<? extends TokenIdentifier> getSecretManager() {
2348     return this.secretManager;
2349   }
2350 
2351   @SuppressWarnings("unchecked")
2352   public void setSecretManager(SecretManager<? extends TokenIdentifier> secretManager) {
2353     this.secretManager = (SecretManager<TokenIdentifier>) secretManager;
2354   }
2355 
2356   public Pair<Message, CellScanner> call(BlockingService service, MethodDescriptor md,
2357       Message param, CellScanner cellScanner, long receiveTime, MonitoredRPCHandler status)
2358       throws IOException {
2359     return call(service, md, param, cellScanner, receiveTime, status, System.currentTimeMillis(),0);
2360   }
2361 
2362   /**
2363    * This is a server side method, which is invoked over RPC. On success
2364    * the return response has protobuf response payload. On failure, the
2365    * exception name and the stack trace are returned in the protobuf response.
2366    */
2367   @Override
2368   public Pair<Message, CellScanner> call(BlockingService service, MethodDescriptor md,
2369       Message param, CellScanner cellScanner, long receiveTime, MonitoredRPCHandler status,
2370       long startTime, int timeout)
2371   throws IOException {
2372     try {
2373       status.setRPC(md.getName(), new Object[]{param}, receiveTime);
2374       // TODO: Review after we add in encoded data blocks.
2375       status.setRPCPacket(param);
2376       status.resume("Servicing call");
2377       //get an instance of the method arg type
2378       HBaseRpcController controller = new HBaseRpcControllerImpl(cellScanner);
2379       controller.setCallTimeout(timeout);
2380       Message result = service.callBlockingMethod(md, controller, param);
2381       long endTime = System.currentTimeMillis();
2382       int processingTime = (int) (endTime - startTime);
2383       int qTime = (int) (startTime - receiveTime);
2384       int totalTime = (int) (endTime - receiveTime);
2385       Call call = CurCall.get();
2386       if (LOG.isTraceEnabled()) {
2387         LOG.trace(CurCall.get().toString() +
2388             ", response " + TextFormat.shortDebugString(result) +
2389             " queueTime: " + qTime +
2390             " processingTime: " + processingTime +
2391             " totalTime: " + totalTime);
2392       }
2393       // Use the raw request call size for now.
2394       long requestSize = call.getSize();
2395       long responseSize = result.getSerializedSize();
2396       if (call.isClientCellBlockSupported()) {
2397         // Include the payload size in HBaseRpcController
2398         responseSize += call.getResponseCellSize();
2399       }
2400 
2401       metrics.dequeuedCall(qTime);
2402       metrics.processedCall(processingTime);
2403       metrics.totalCall(totalTime);
2404       metrics.receivedRequest(requestSize);
2405       metrics.sentResponse(responseSize);
2406       // log any RPC responses that are slower than the configured warn
2407       // response time or larger than configured warning size
2408       boolean tooSlow = (processingTime > warnResponseTime && warnResponseTime > -1);
2409       boolean tooLarge = (responseSize > warnResponseSize && warnResponseSize > -1);
2410       if (tooSlow || tooLarge) {
2411         // when tagging, we let TooLarge trump TooSmall to keep output simple
2412         // note that large responses will often also be slow.
2413         logResponse(param,
2414             md.getName(), md.getName() + "(" + param.getClass().getName() + ")",
2415             (tooLarge ? "TooLarge" : "TooSlow"),
2416             status.getClient(), startTime, processingTime, qTime,
2417             responseSize);
2418       }
2419       return new Pair<Message, CellScanner>(result, controller.cellScanner());
2420     } catch (Throwable e) {
2421       // The above callBlockingMethod will always return a SE.  Strip the SE wrapper before
2422       // putting it on the wire.  Its needed to adhere to the pb Service Interface but we don't
2423       // need to pass it over the wire.
2424       if (e instanceof ServiceException) {
2425         if (e.getCause() == null) {
2426           LOG.debug("Caught a ServiceException with null cause", e);
2427         } else {
2428           e = e.getCause();
2429         }
2430       }
2431 
2432       // increment the number of requests that were exceptions.
2433       metrics.exception(e);
2434 
2435       if (e instanceof LinkageError) throw new DoNotRetryIOException(e);
2436       if (e instanceof IOException) throw (IOException)e;
2437       LOG.error("Unexpected throwable object ", e);
2438       throw new IOException(e.getMessage(), e);
2439     }
2440   }
2441 
2442   /**
2443    * Logs an RPC response to the LOG file, producing valid JSON objects for
2444    * client Operations.
2445    * @param param The parameters received in the call.
2446    * @param methodName The name of the method invoked
2447    * @param call The string representation of the call
2448    * @param tag  The tag that will be used to indicate this event in the log.
2449    * @param clientAddress   The address of the client who made this call.
2450    * @param startTime       The time that the call was initiated, in ms.
2451    * @param processingTime  The duration that the call took to run, in ms.
2452    * @param qTime           The duration that the call spent on the queue
2453    *                        prior to being initiated, in ms.
2454    * @param responseSize    The size in bytes of the response buffer.
2455    */
2456   void logResponse(Message param, String methodName, String call, String tag,
2457       String clientAddress, long startTime, int processingTime, int qTime,
2458       long responseSize)
2459           throws IOException {
2460     // base information that is reported regardless of type of call
2461     Map<String, Object> responseInfo = new HashMap<String, Object>();
2462     responseInfo.put("starttimems", startTime);
2463     responseInfo.put("processingtimems", processingTime);
2464     responseInfo.put("queuetimems", qTime);
2465     responseInfo.put("responsesize", responseSize);
2466     responseInfo.put("client", clientAddress);
2467     responseInfo.put("class", server == null? "": server.getClass().getSimpleName());
2468     responseInfo.put("method", methodName);
2469     responseInfo.put("call", call);
2470     // The params could be really big, make sure they don't kill us at WARN
2471     String stringifiedParam = ProtobufUtil.getShortTextFormat(param);
2472     if (stringifiedParam.length() > 150) {
2473       // Truncate to 1000 chars if TRACE is on, else to 150 chars
2474       stringifiedParam = truncateTraceLog(stringifiedParam);
2475     }
2476     responseInfo.put("param", stringifiedParam);
2477     if (param instanceof ClientProtos.ScanRequest && rsRpcServices != null) {
2478       ClientProtos.ScanRequest request = ((ClientProtos.ScanRequest) param);
2479       if (request.hasScannerId()) {
2480         long scannerId = request.getScannerId();
2481         String scanDetails = rsRpcServices.getScanDetailsWithId(scannerId);
2482         if (scanDetails != null) {
2483           responseInfo.put("scandetails", scanDetails);
2484         }
2485       }
2486     }
2487     LOG.warn("(response" + tag + "): " + MAPPER.writeValueAsString(responseInfo));
2488   }
2489 
2490   /** Stops the service.  No new calls will be handled after this is called. */
2491   @Override
2492   public synchronized void stop() {
2493     LOG.info("Stopping server on " + port);
2494     running = false;
2495     if (authTokenSecretMgr != null) {
2496       authTokenSecretMgr.stop();
2497       authTokenSecretMgr = null;
2498     }
2499     listener.interrupt();
2500     listener.doStop();
2501     responder.interrupt();
2502     scheduler.stop();
2503     notifyAll();
2504   }
2505 
2506   /** Wait for the server to be stopped.
2507    * Does not wait for all subthreads to finish.
2508    *  See {@link #stop()}.
2509    * @throws InterruptedException e
2510    */
2511   @Override
2512   public synchronized void join() throws InterruptedException {
2513     while (running) {
2514       wait();
2515     }
2516   }
2517 
2518   /**
2519    * Return the socket (ip+port) on which the RPC server is listening to. May return null if
2520    * the listener channel is closed.
2521    * @return the socket (ip+port) on which the RPC server is listening to, or null if this
2522    * information cannot be determined
2523    */
2524   @Override
2525   public synchronized InetSocketAddress getListenerAddress() {
2526     if (listener == null) {
2527       return null;
2528     }
2529     return listener.getAddress();
2530   }
2531 
2532   /**
2533    * Truncate to number of chars decided by conf hbase.ipc.trace.log.max.length
2534    * if TRACE is on else to 150 chars Refer to Jira HBASE-20826 and HBASE-20942
2535    * @param strParam stringifiedParam to be truncated
2536    * @return truncated trace log string
2537    */
2538   @VisibleForTesting
2539   String truncateTraceLog(String strParam) {
2540     if (LOG.isTraceEnabled()) {
2541       int traceLogMaxLength = getConf().getInt(TRACE_LOG_MAX_LENGTH, DEFAULT_TRACE_LOG_MAX_LENGTH);
2542       int truncatedLength =
2543           strParam.length() < traceLogMaxLength ? strParam.length() : traceLogMaxLength;
2544       String truncatedFlag = truncatedLength == strParam.length() ? "" : KEY_WORD_TRUNCATED;
2545       return strParam.subSequence(0, truncatedLength) + truncatedFlag;
2546     }
2547     return strParam.subSequence(0, 150) + KEY_WORD_TRUNCATED;
2548   }
2549 
2550   /**
2551    * Set the handler for calling out of RPC for error conditions.
2552    * @param handler the handler implementation
2553    */
2554   @Override
2555   public void setErrorHandler(HBaseRPCErrorHandler handler) {
2556     this.errorHandler = handler;
2557   }
2558 
2559   @Override
2560   public HBaseRPCErrorHandler getErrorHandler() {
2561     return this.errorHandler;
2562   }
2563 
2564   /**
2565    * Returns the metrics instance for reporting RPC call statistics
2566    */
2567   @Override
2568   public MetricsHBaseServer getMetrics() {
2569     return metrics;
2570   }
2571 
2572   @Override
2573   public void addCallSize(final long diff) {
2574     this.callQueueSize.add(diff);
2575   }
2576 
2577   /**
2578    * Authorize the incoming client connection.
2579    *
2580    * @param user client user
2581    * @param connection incoming connection
2582    * @param addr InetAddress of incoming connection
2583    * @throws org.apache.hadoop.security.authorize.AuthorizationException
2584    *         when the client isn't authorized to talk the protocol
2585    */
2586   public synchronized void authorize(UserGroupInformation user, ConnectionHeader connection,
2587       InetAddress addr)
2588   throws AuthorizationException {
2589     if (authorize) {
2590       Class<?> c = getServiceInterface(services, connection.getServiceName());
2591       this.authManager.authorize(user != null ? user : null, c, getConf(), addr);
2592     }
2593   }
2594 
2595   /**
2596    * When the read or write buffer size is larger than this limit, i/o will be
2597    * done in chunks of this size. Most RPC requests and responses would be
2598    * be smaller.
2599    */
2600   private static int NIO_BUFFER_LIMIT = 64 * 1024; //should not be more than 64KB.
2601 
2602   /**
2603    * This is a wrapper around {@link java.nio.channels.WritableByteChannel#write(java.nio.ByteBuffer)}.
2604    * If the amount of data is large, it writes to channel in smaller chunks.
2605    * This is to avoid jdk from creating many direct buffers as the size of
2606    * buffer increases. This also minimizes extra copies in NIO layer
2607    * as a result of multiple write operations required to write a large
2608    * buffer.
2609    *
2610    * @param channel writable byte channel to write to
2611    * @param bufferChain Chain of buffers to write
2612    * @return number of bytes written
2613    * @throws java.io.IOException e
2614    * @see java.nio.channels.WritableByteChannel#write(java.nio.ByteBuffer)
2615    */
2616   protected long channelWrite(GatheringByteChannel channel, BufferChain bufferChain)
2617   throws IOException {
2618     long count =  bufferChain.write(channel, NIO_BUFFER_LIMIT);
2619     if (count > 0) this.metrics.sentBytes(count);
2620     return count;
2621   }
2622 
2623   /**
2624    * This is a wrapper around {@link java.nio.channels.ReadableByteChannel#read(java.nio.ByteBuffer)}.
2625    * If the amount of data is large, it writes to channel in smaller chunks.
2626    * This is to avoid jdk from creating many direct buffers as the size of
2627    * ByteBuffer increases. There should not be any performance degredation.
2628    *
2629    * @param channel writable byte channel to write on
2630    * @param buffer buffer to write
2631    * @return number of bytes written
2632    * @throws java.io.IOException e
2633    * @see java.nio.channels.ReadableByteChannel#read(java.nio.ByteBuffer)
2634    */
2635   protected int channelRead(ReadableByteChannel channel,
2636                                    ByteBuffer buffer) throws IOException {
2637 
2638     int count = (buffer.remaining() <= NIO_BUFFER_LIMIT) ?
2639            channel.read(buffer) : channelIO(channel, null, buffer);
2640     if (count > 0) {
2641       metrics.receivedBytes(count);
2642     }
2643     return count;
2644   }
2645 
2646   /**
2647    * Helper for {@link #channelRead(java.nio.channels.ReadableByteChannel, java.nio.ByteBuffer)}
2648    * and {@link #channelWrite(GatheringByteChannel, BufferChain)}. Only
2649    * one of readCh or writeCh should be non-null.
2650    *
2651    * @param readCh read channel
2652    * @param writeCh write channel
2653    * @param buf buffer to read or write into/out of
2654    * @return bytes written
2655    * @throws java.io.IOException e
2656    * @see #channelRead(java.nio.channels.ReadableByteChannel, java.nio.ByteBuffer)
2657    * @see #channelWrite(GatheringByteChannel, BufferChain)
2658    */
2659   private static int channelIO(ReadableByteChannel readCh,
2660                                WritableByteChannel writeCh,
2661                                ByteBuffer buf) throws IOException {
2662 
2663     int originalLimit = buf.limit();
2664     int initialRemaining = buf.remaining();
2665     int ret = 0;
2666 
2667     while (buf.remaining() > 0) {
2668       try {
2669         int ioSize = Math.min(buf.remaining(), NIO_BUFFER_LIMIT);
2670         buf.limit(buf.position() + ioSize);
2671 
2672         ret = (readCh == null) ? writeCh.write(buf) : readCh.read(buf);
2673 
2674         if (ret < ioSize) {
2675           break;
2676         }
2677 
2678       } finally {
2679         buf.limit(originalLimit);
2680       }
2681     }
2682 
2683     int nBytes = initialRemaining - buf.remaining();
2684     return (nBytes > 0) ? nBytes : ret;
2685   }
2686 
2687   /**
2688    * Needed for features such as delayed calls.  We need to be able to store the current call
2689    * so that we can complete it later or ask questions of what is supported by the current ongoing
2690    * call.
2691    * @return An RpcCallContext backed by the currently ongoing call (gotten from a thread local)
2692    */
2693   public static RpcCallContext getCurrentCall() {
2694     return CurCall.get();
2695   }
2696 
2697   public static boolean isInRpcCallContext() {
2698     return CurCall.get() != null;
2699   }
2700 
2701   /**
2702    * Returns the user credentials associated with the current RPC request or
2703    * <code>null</code> if no credentials were provided.
2704    * @return A User
2705    */
2706   public static User getRequestUser() {
2707     RpcCallContext ctx = getCurrentCall();
2708     return ctx == null? null: ctx.getRequestUser();
2709   }
2710 
2711   /**
2712    * Returns the username for any user associated with the current RPC
2713    * request or <code>null</code> if no user is set.
2714    */
2715   public static String getRequestUserName() {
2716     User user = getRequestUser();
2717     return user == null? null: user.getShortName();
2718   }
2719 
2720   /**
2721    * @return Address of remote client if a request is ongoing, else null
2722    */
2723   public static InetAddress getRemoteAddress() {
2724     RpcCallContext ctx = getCurrentCall();
2725     return ctx == null? null: ctx.getRemoteAddress();
2726   }
2727 
2728   /**
2729    * @param serviceName Some arbitrary string that represents a 'service'.
2730    * @param services Available service instances
2731    * @return Matching BlockingServiceAndInterface pair
2732    */
2733   static BlockingServiceAndInterface getServiceAndInterface(
2734       final List<BlockingServiceAndInterface> services, final String serviceName) {
2735     for (BlockingServiceAndInterface bs : services) {
2736       if (bs.getBlockingService().getDescriptorForType().getName().equals(serviceName)) {
2737         return bs;
2738       }
2739     }
2740     return null;
2741   }
2742 
2743   /**
2744    * @param serviceName Some arbitrary string that represents a 'service'.
2745    * @param services Available services and their service interfaces.
2746    * @return Service interface class for <code>serviceName</code>
2747    */
2748   static Class<?> getServiceInterface(
2749       final List<BlockingServiceAndInterface> services,
2750       final String serviceName) {
2751     BlockingServiceAndInterface bsasi =
2752         getServiceAndInterface(services, serviceName);
2753     return bsasi == null? null: bsasi.getServiceInterface();
2754   }
2755 
2756   /**
2757    * @param serviceName Some arbitrary string that represents a 'service'.
2758    * @param services Available services and their service interfaces.
2759    * @return BlockingService that goes with the passed <code>serviceName</code>
2760    */
2761   static BlockingService getService(
2762       final List<BlockingServiceAndInterface> services,
2763       final String serviceName) {
2764     BlockingServiceAndInterface bsasi =
2765         getServiceAndInterface(services, serviceName);
2766     return bsasi == null? null: bsasi.getBlockingService();
2767   }
2768 
2769   static MonitoredRPCHandler getStatus() {
2770     // It is ugly the way we park status up in RpcServer.  Let it be for now.  TODO.
2771     MonitoredRPCHandler status = RpcServer.MONITORED_RPC.get();
2772     if (status != null) {
2773       return status;
2774     }
2775     status = TaskMonitor.get().createRPCStatus(Thread.currentThread().getName());
2776     status.pause("Waiting for a call");
2777     RpcServer.MONITORED_RPC.set(status);
2778     return status;
2779   }
2780 
2781   /** Returns the remote side ip address when invoked inside an RPC
2782    *  Returns null incase of an error.
2783    *  @return InetAddress
2784    */
2785   public static InetAddress getRemoteIp() {
2786     Call call = CurCall.get();
2787     if (call != null && call.connection != null && call.connection.socket != null) {
2788       return call.connection.socket.getInetAddress();
2789     }
2790     return null;
2791   }
2792 
2793 
2794   /**
2795    * A convenience method to bind to a given address and report
2796    * better exceptions if the address is not a valid host.
2797    * @param socket the socket to bind
2798    * @param address the address to bind to
2799    * @param backlog the number of connections allowed in the queue
2800    * @throws BindException if the address can't be bound
2801    * @throws UnknownHostException if the address isn't a valid host name
2802    * @throws IOException other random errors from bind
2803    */
2804   public static void bind(ServerSocket socket, InetSocketAddress address,
2805                           int backlog) throws IOException {
2806     try {
2807       socket.bind(address, backlog);
2808     } catch (BindException e) {
2809       BindException bindException =
2810         new BindException("Problem binding to " + address + " : " +
2811             e.getMessage());
2812       bindException.initCause(e);
2813       throw bindException;
2814     } catch (SocketException e) {
2815       // If they try to bind to a different host's address, give a better
2816       // error message.
2817       if ("Unresolved address".equals(e.getMessage())) {
2818         throw new UnknownHostException("Invalid hostname for server: " +
2819                                        address.getHostName());
2820       }
2821       throw e;
2822     }
2823   }
2824 
2825   @Override
2826   public RpcScheduler getScheduler() {
2827     return scheduler;
2828   }
2829 
2830   @Override
2831   public void setRsRpcServices(RSRpcServices rsRpcServices) {
2832     this.rsRpcServices = rsRpcServices;
2833   }
2834 }