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