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