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 }
736 }
737 }
738
739
740
741
742
743
744
745
746 public void startAdd() {
747 adding = true;
748 readSelector.wakeup();
749 }
750
751 public synchronized SelectionKey registerChannel(SocketChannel channel)
752 throws IOException {
753 return channel.register(readSelector, SelectionKey.OP_READ);
754 }
755
756 public synchronized void finishAdd() {
757 adding = false;
758 this.notify();
759 }
760 }
761
762
763
764
765
766
767
768
769 private void cleanupConnections(boolean force) {
770 if (force || numConnections > thresholdIdleConnections) {
771 long currentTime = System.currentTimeMillis();
772 if (!force && (currentTime - lastCleanupRunTime) < cleanupInterval) {
773 return;
774 }
775 int start = 0;
776 int end = numConnections - 1;
777 if (!force) {
778 start = rand.nextInt(numConnections);
779 end = rand.nextInt(numConnections);
780 int temp;
781 if (end < start) {
782 temp = start;
783 start = end;
784 end = temp;
785 }
786 }
787 int i = start;
788 int numNuked = 0;
789 while (i <= end) {
790 Connection c;
791 synchronized (connectionList) {
792 try {
793 c = connectionList.get(i);
794 } catch (Exception e) {return;}
795 }
796 if (c.timedOut(currentTime)) {
797 if (LOG.isDebugEnabled())
798 LOG.debug(getName() + ": disconnecting client " + c.getHostAddress());
799 closeConnection(c);
800 numNuked++;
801 end--;
802
803 c = null;
804 if (!force && numNuked == maxConnectionsToNuke) break;
805 }
806 else i++;
807 }
808 lastCleanupRunTime = System.currentTimeMillis();
809 }
810 }
811
812 @Override
813 @edu.umd.cs.findbugs.annotations.SuppressWarnings(value="IS2_INCONSISTENT_SYNC",
814 justification="selector access is not synchronized; seems fine but concerned changing " +
815 "it will have per impact")
816 public void run() {
817 LOG.info(getName() + ": starting");
818 while (running) {
819 SelectionKey key = null;
820 try {
821 selector.select();
822 Iterator<SelectionKey> iter = selector.selectedKeys().iterator();
823 while (iter.hasNext()) {
824 key = iter.next();
825 iter.remove();
826 try {
827 if (key.isValid()) {
828 if (key.isAcceptable())
829 doAccept(key);
830 }
831 } catch (IOException ignored) {
832 if (LOG.isTraceEnabled()) LOG.trace("ignored", ignored);
833 }
834 key = null;
835 }
836 } catch (OutOfMemoryError e) {
837 if (errorHandler != null) {
838 if (errorHandler.checkOOME(e)) {
839 LOG.info(getName() + ": exiting on OutOfMemoryError");
840 closeCurrentConnection(key, e);
841 cleanupConnections(true);
842 return;
843 }
844 } else {
845
846
847
848 LOG.warn(getName() + ": OutOfMemoryError in server select", e);
849 closeCurrentConnection(key, e);
850 cleanupConnections(true);
851 try {
852 Thread.sleep(60000);
853 } catch (InterruptedException ex) {
854 LOG.debug("Interrupted while sleeping");
855 return;
856 }
857 }
858 } catch (Exception e) {
859 closeCurrentConnection(key, e);
860 }
861 cleanupConnections(false);
862 }
863
864 LOG.info(getName() + ": stopping");
865
866 synchronized (this) {
867 try {
868 acceptChannel.close();
869 selector.close();
870 } catch (IOException ignored) {
871 if (LOG.isTraceEnabled()) LOG.trace("ignored", ignored);
872 }
873
874 selector= null;
875 acceptChannel= null;
876
877
878 while (!connectionList.isEmpty()) {
879 closeConnection(connectionList.remove(0));
880 }
881 }
882 }
883
884 private void closeCurrentConnection(SelectionKey key, Throwable e) {
885 if (key != null) {
886 Connection c = (Connection)key.attachment();
887 if (c != null) {
888 if (LOG.isDebugEnabled()) {
889 LOG.debug(getName() + ": disconnecting client " + c.getHostAddress() +
890 (e != null ? " on error " + e.getMessage() : ""));
891 }
892 closeConnection(c);
893 key.attach(null);
894 }
895 }
896 }
897
898 InetSocketAddress getAddress() {
899 return address;
900 }
901
902 void doAccept(SelectionKey key) throws IOException, OutOfMemoryError {
903 Connection c;
904 ServerSocketChannel server = (ServerSocketChannel) key.channel();
905
906 SocketChannel channel;
907 while ((channel = server.accept()) != null) {
908 try {
909 channel.configureBlocking(false);
910 channel.socket().setTcpNoDelay(tcpNoDelay);
911 channel.socket().setKeepAlive(tcpKeepAlive);
912 } catch (IOException ioe) {
913 channel.close();
914 throw ioe;
915 }
916
917 Reader reader = getReader();
918 try {
919 reader.startAdd();
920 SelectionKey readKey = reader.registerChannel(channel);
921 c = getConnection(channel, System.currentTimeMillis());
922 readKey.attach(c);
923 synchronized (connectionList) {
924 connectionList.add(numConnections, c);
925 numConnections++;
926 }
927 if (LOG.isDebugEnabled())
928 LOG.debug(getName() + ": connection from " + c.toString() +
929 "; # active connections: " + numConnections);
930 } finally {
931 reader.finishAdd();
932 }
933 }
934 }
935
936 void doRead(SelectionKey key) throws InterruptedException {
937 int count;
938 Connection c = (Connection) key.attachment();
939 if (c == null) {
940 return;
941 }
942 c.setLastContact(System.currentTimeMillis());
943 try {
944 count = c.readAndProcess();
945
946 if (count > 0) {
947 c.setLastContact(System.currentTimeMillis());
948 }
949
950 } catch (InterruptedException ieo) {
951 throw ieo;
952 } catch (Exception e) {
953 if (LOG.isDebugEnabled()) {
954 LOG.debug(getName() + ": Caught exception while reading:", e);
955 }
956 count = -1;
957 }
958 if (count < 0) {
959 if (LOG.isDebugEnabled()) {
960 LOG.debug(getName() + ": DISCONNECTING client " + c.toString() +
961 " because read count=" + count +
962 ". Number of active connections: " + numConnections);
963 }
964 closeConnection(c);
965 }
966 }
967
968 synchronized void doStop() {
969 if (selector != null) {
970 selector.wakeup();
971 Thread.yield();
972 }
973 if (acceptChannel != null) {
974 try {
975 acceptChannel.socket().close();
976 } catch (IOException e) {
977 LOG.info(getName() + ": exception in closing listener socket. " + e);
978 }
979 }
980 readPool.shutdownNow();
981 }
982
983
984
985 Reader getReader() {
986 currentReader = (currentReader + 1) % readers.length;
987 return readers[currentReader];
988 }
989 }
990
991
992 protected class Responder extends Thread {
993 private final Selector writeSelector;
994 private final Set<Connection> writingCons =
995 Collections.newSetFromMap(new ConcurrentHashMap<Connection, Boolean>());
996
997 Responder() throws IOException {
998 this.setName("RpcServer.responder");
999 this.setDaemon(true);
1000 this.setUncaughtExceptionHandler(Threads.LOGGING_EXCEPTION_HANDLER);
1001 writeSelector = Selector.open();
1002 }
1003
1004 @Override
1005 public void run() {
1006 LOG.info(getName() + ": starting");
1007 try {
1008 doRunLoop();
1009 } finally {
1010 LOG.info(getName() + ": stopping");
1011 try {
1012 writeSelector.close();
1013 } catch (IOException ioe) {
1014 LOG.error(getName() + ": couldn't close write selector", ioe);
1015 }
1016 }
1017 }
1018
1019
1020
1021
1022
1023 private void registerWrites() {
1024 Iterator<Connection> it = writingCons.iterator();
1025 while (it.hasNext()) {
1026 Connection c = it.next();
1027 it.remove();
1028 SelectionKey sk = c.channel.keyFor(writeSelector);
1029 try {
1030 if (sk == null) {
1031 try {
1032 c.channel.register(writeSelector, SelectionKey.OP_WRITE, c);
1033 } catch (ClosedChannelException e) {
1034
1035 if (LOG.isTraceEnabled()) LOG.trace("ignored", e);
1036 }
1037 } else {
1038 sk.interestOps(SelectionKey.OP_WRITE);
1039 }
1040 } catch (CancelledKeyException e) {
1041
1042 if (LOG.isTraceEnabled()) LOG.trace("ignored", e);
1043 }
1044 }
1045 }
1046
1047
1048
1049
1050 public void registerForWrite(Connection c) {
1051 if (writingCons.add(c)) {
1052 writeSelector.wakeup();
1053 }
1054 }
1055
1056 private void doRunLoop() {
1057 long lastPurgeTime = 0;
1058 while (running) {
1059 try {
1060 registerWrites();
1061 int keyCt = writeSelector.select(purgeTimeout);
1062 if (keyCt == 0) {
1063 continue;
1064 }
1065
1066 Set<SelectionKey> keys = writeSelector.selectedKeys();
1067 Iterator<SelectionKey> iter = keys.iterator();
1068 while (iter.hasNext()) {
1069 SelectionKey key = iter.next();
1070 iter.remove();
1071 try {
1072 if (key.isValid() && key.isWritable()) {
1073 doAsyncWrite(key);
1074 }
1075 } catch (IOException e) {
1076 LOG.debug(getName() + ": asyncWrite", e);
1077 }
1078 }
1079
1080 lastPurgeTime = purge(lastPurgeTime);
1081
1082 } catch (OutOfMemoryError e) {
1083 if (errorHandler != null) {
1084 if (errorHandler.checkOOME(e)) {
1085 LOG.info(getName() + ": exiting on OutOfMemoryError");
1086 return;
1087 }
1088 } else {
1089
1090
1091
1092
1093
1094 LOG.warn(getName() + ": OutOfMemoryError in server select", e);
1095 try {
1096 Thread.sleep(60000);
1097 } catch (InterruptedException ex) {
1098 LOG.debug("Interrupted while sleeping");
1099 return;
1100 }
1101 }
1102 } catch (Exception e) {
1103 LOG.warn(getName() + ": exception in Responder " +
1104 StringUtils.stringifyException(e), e);
1105 }
1106 }
1107 LOG.info(getName() + ": stopped");
1108 }
1109
1110
1111
1112
1113
1114
1115 private long purge(long lastPurgeTime) {
1116 long now = System.currentTimeMillis();
1117 if (now < lastPurgeTime + purgeTimeout) {
1118 return lastPurgeTime;
1119 }
1120
1121 ArrayList<Connection> conWithOldCalls = new ArrayList<Connection>();
1122
1123 synchronized (writeSelector.keys()) {
1124 for (SelectionKey key : writeSelector.keys()) {
1125 Connection connection = (Connection) key.attachment();
1126 if (connection == null) {
1127 throw new IllegalStateException("Coding error: SelectionKey key without attachment.");
1128 }
1129 Call call = connection.responseQueue.peekFirst();
1130 if (call != null && now > call.timestamp + purgeTimeout) {
1131 conWithOldCalls.add(call.connection);
1132 }
1133 }
1134 }
1135
1136
1137 for (Connection connection : conWithOldCalls) {
1138 closeConnection(connection);
1139 }
1140
1141 return now;
1142 }
1143
1144 private void doAsyncWrite(SelectionKey key) throws IOException {
1145 Connection connection = (Connection) key.attachment();
1146 if (connection == null) {
1147 throw new IOException("doAsyncWrite: no connection");
1148 }
1149 if (key.channel() != connection.channel) {
1150 throw new IOException("doAsyncWrite: bad channel");
1151 }
1152
1153 if (processAllResponses(connection)) {
1154 try {
1155
1156
1157 key.interestOps(0);
1158 } catch (CancelledKeyException e) {
1159
1160
1161
1162
1163
1164 LOG.warn("Exception while changing ops : " + e);
1165 }
1166 }
1167 }
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177 private boolean processResponse(final Call call) throws IOException {
1178 boolean error = true;
1179 try {
1180
1181 long numBytes = channelWrite(call.connection.channel, call.response);
1182 if (numBytes < 0) {
1183 throw new HBaseIOException("Error writing on the socket " +
1184 "for the call:" + call.toShortString());
1185 }
1186 error = false;
1187 } finally {
1188 if (error) {
1189 LOG.debug(getName() + call.toShortString() + ": output error -- closing");
1190 closeConnection(call.connection);
1191 }
1192 }
1193
1194 if (!call.response.hasRemaining()) {
1195 call.done();
1196 return true;
1197 } else {
1198 return false;
1199 }
1200 }
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210 private boolean processAllResponses(final Connection connection) throws IOException {
1211
1212 connection.responseWriteLock.lock();
1213 try {
1214 for (int i = 0; i < 20; i++) {
1215
1216 Call call = connection.responseQueue.pollFirst();
1217 if (call == null) {
1218 return true;
1219 }
1220 if (!processResponse(call)) {
1221 connection.responseQueue.addFirst(call);
1222 return false;
1223 }
1224 }
1225 } finally {
1226 connection.responseWriteLock.unlock();
1227 }
1228
1229 return connection.responseQueue.isEmpty();
1230 }
1231
1232
1233
1234
1235 void doRespond(Call call) throws IOException {
1236 boolean added = false;
1237
1238
1239
1240 if (call.connection.responseQueue.isEmpty() && call.connection.responseWriteLock.tryLock()) {
1241 try {
1242 if (call.connection.responseQueue.isEmpty()) {
1243
1244
1245 if (processResponse(call)) {
1246 return;
1247 }
1248
1249 call.connection.responseQueue.addFirst(call);
1250 added = true;
1251 }
1252 } finally {
1253 call.connection.responseWriteLock.unlock();
1254 }
1255 }
1256
1257 if (!added) {
1258 call.connection.responseQueue.addLast(call);
1259 }
1260 call.responder.registerForWrite(call.connection);
1261
1262
1263 call.timestamp = System.currentTimeMillis();
1264 }
1265 }
1266
1267
1268 @edu.umd.cs.findbugs.annotations.SuppressWarnings(
1269 value="VO_VOLATILE_INCREMENT",
1270 justification="False positive according to http://sourceforge.net/p/findbugs/bugs/1032/")
1271 public class Connection {
1272
1273 private boolean connectionPreambleRead = false;
1274
1275 private boolean connectionHeaderRead = false;
1276 protected SocketChannel channel;
1277 private ByteBuffer data;
1278 private ByteBuffer dataLengthBuffer;
1279 private ByteBuffer preambleBuffer;
1280 protected final ConcurrentLinkedDeque<Call> responseQueue = new ConcurrentLinkedDeque<Call>();
1281 private final Lock responseWriteLock = new ReentrantLock();
1282 private Counter rpcCount = new Counter();
1283 private long lastContact;
1284 private InetAddress addr;
1285 protected Socket socket;
1286
1287
1288 protected String hostAddress;
1289 protected int remotePort;
1290 ConnectionHeader connectionHeader;
1291
1292
1293
1294
1295 private Codec codec;
1296
1297
1298
1299 private CompressionCodec compressionCodec;
1300 BlockingService service;
1301
1302 private AuthMethod authMethod;
1303 private boolean saslContextEstablished;
1304 private boolean skipInitialSaslHandshake;
1305 private ByteBuffer unwrappedData;
1306
1307 private ByteBuffer unwrappedDataLengthBuffer = ByteBuffer.allocate(4);
1308 boolean useSasl;
1309 SaslServer saslServer;
1310 private boolean useWrap = false;
1311
1312 private static final int AUTHORIZATION_FAILED_CALLID = -1;
1313 private final Call authFailedCall = new Call(AUTHORIZATION_FAILED_CALLID, null, null, null,
1314 null, null, this, null, 0, null, null, 0);
1315 private ByteArrayOutputStream authFailedResponse =
1316 new ByteArrayOutputStream();
1317
1318 private static final int SASL_CALLID = -33;
1319 private final Call saslCall = new Call(SASL_CALLID, null, null, null, null, null, this, null,
1320 0, null, null, 0);
1321
1322
1323 private boolean authenticatedWithFallback;
1324
1325 private boolean retryImmediatelySupported = false;
1326
1327 public UserGroupInformation attemptingUser = null;
1328 protected User user = null;
1329 protected UserGroupInformation ugi = null;
1330
1331 public Connection(SocketChannel channel, long lastContact) {
1332 this.channel = channel;
1333 this.lastContact = lastContact;
1334 this.data = null;
1335 this.dataLengthBuffer = ByteBuffer.allocate(4);
1336 this.socket = channel.socket();
1337 this.addr = socket.getInetAddress();
1338 if (addr == null) {
1339 this.hostAddress = "*Unknown*";
1340 } else {
1341 this.hostAddress = addr.getHostAddress();
1342 }
1343 this.remotePort = socket.getPort();
1344 if (socketSendBufferSize != 0) {
1345 try {
1346 socket.setSendBufferSize(socketSendBufferSize);
1347 } catch (IOException e) {
1348 LOG.warn("Connection: unable to set socket send buffer size to " +
1349 socketSendBufferSize);
1350 }
1351 }
1352 }
1353
1354 @Override
1355 public String toString() {
1356 return getHostAddress() + ":" + remotePort;
1357 }
1358
1359 public String getHostAddress() {
1360 return hostAddress;
1361 }
1362
1363 public InetAddress getHostInetAddress() {
1364 return addr;
1365 }
1366
1367 public int getRemotePort() {
1368 return remotePort;
1369 }
1370
1371 public void setLastContact(long lastContact) {
1372 this.lastContact = lastContact;
1373 }
1374
1375 public VersionInfo getVersionInfo() {
1376 if (connectionHeader.hasVersionInfo()) {
1377 return connectionHeader.getVersionInfo();
1378 }
1379 return null;
1380 }
1381
1382
1383 private boolean isIdle() {
1384 return rpcCount.get() == 0;
1385 }
1386
1387
1388 protected void decRpcCount() {
1389 rpcCount.decrement();
1390 }
1391
1392
1393 protected void incRpcCount() {
1394 rpcCount.increment();
1395 }
1396
1397 protected boolean timedOut(long currentTime) {
1398 return isIdle() && currentTime - lastContact > maxIdleTime;
1399 }
1400
1401 private UserGroupInformation getAuthorizedUgi(String authorizedId)
1402 throws IOException {
1403 UserGroupInformation authorizedUgi;
1404 if (authMethod == AuthMethod.DIGEST) {
1405 TokenIdentifier tokenId = HBaseSaslRpcServer.getIdentifier(authorizedId,
1406 secretManager);
1407 authorizedUgi = tokenId.getUser();
1408 if (authorizedUgi == null) {
1409 throw new AccessDeniedException(
1410 "Can't retrieve username from tokenIdentifier.");
1411 }
1412 authorizedUgi.addTokenIdentifier(tokenId);
1413 } else {
1414 authorizedUgi = UserGroupInformation.createRemoteUser(authorizedId);
1415 }
1416 authorizedUgi.setAuthenticationMethod(authMethod.authenticationMethod.getAuthMethod());
1417 return authorizedUgi;
1418 }
1419
1420 private void saslReadAndProcess(ByteBuffer saslToken) throws IOException,
1421 InterruptedException {
1422 if (saslContextEstablished) {
1423 if (LOG.isTraceEnabled())
1424 LOG.trace("Have read input token of size " + saslToken.limit()
1425 + " for processing by saslServer.unwrap()");
1426
1427 if (!useWrap) {
1428 processOneRpc(saslToken);
1429 } else {
1430 byte[] b = saslToken.array();
1431 byte [] plaintextData = saslServer.unwrap(b, saslToken.position(), saslToken.limit());
1432 processUnwrappedData(plaintextData);
1433 }
1434 } else {
1435 byte[] replyToken;
1436 try {
1437 if (saslServer == null) {
1438 switch (authMethod) {
1439 case DIGEST:
1440 if (secretManager == null) {
1441 throw new AccessDeniedException(
1442 "Server is not configured to do DIGEST authentication.");
1443 }
1444 saslServer = Sasl.createSaslServer(AuthMethod.DIGEST
1445 .getMechanismName(), null, SaslUtil.SASL_DEFAULT_REALM,
1446 HBaseSaslRpcServer.getSaslProps(), new SaslDigestCallbackHandler(
1447 secretManager, this));
1448 break;
1449 default:
1450 UserGroupInformation current = UserGroupInformation.getCurrentUser();
1451 String fullName = current.getUserName();
1452 if (LOG.isDebugEnabled()) {
1453 LOG.debug("Kerberos principal name is " + fullName);
1454 }
1455 final String names[] = SaslUtil.splitKerberosName(fullName);
1456 if (names.length != 3) {
1457 throw new AccessDeniedException(
1458 "Kerberos principal name does NOT have the expected "
1459 + "hostname part: " + fullName);
1460 }
1461 current.doAs(new PrivilegedExceptionAction<Object>() {
1462 @Override
1463 public Object run() throws SaslException {
1464 saslServer = Sasl.createSaslServer(AuthMethod.KERBEROS
1465 .getMechanismName(), names[0], names[1],
1466 HBaseSaslRpcServer.getSaslProps(), new SaslGssCallbackHandler());
1467 return null;
1468 }
1469 });
1470 }
1471 if (saslServer == null)
1472 throw new AccessDeniedException(
1473 "Unable to find SASL server implementation for "
1474 + authMethod.getMechanismName());
1475 if (LOG.isDebugEnabled()) {
1476 LOG.debug("Created SASL server with mechanism = " + authMethod.getMechanismName());
1477 }
1478 }
1479 if (LOG.isDebugEnabled()) {
1480 LOG.debug("Have read input token of size " + saslToken.limit()
1481 + " for processing by saslServer.evaluateResponse()");
1482 }
1483 replyToken = saslServer.evaluateResponse(saslToken.array());
1484 } catch (IOException e) {
1485 IOException sendToClient = e;
1486 Throwable cause = e;
1487 while (cause != null) {
1488 if (cause instanceof InvalidToken) {
1489 sendToClient = (InvalidToken) cause;
1490 break;
1491 }
1492 cause = cause.getCause();
1493 }
1494 doRawSaslReply(SaslStatus.ERROR, null, sendToClient.getClass().getName(),
1495 sendToClient.getLocalizedMessage());
1496 metrics.authenticationFailure();
1497 String clientIP = this.toString();
1498
1499 AUDITLOG.warn(AUTH_FAILED_FOR + clientIP + ":" + attemptingUser);
1500 throw e;
1501 }
1502 if (replyToken != null) {
1503 if (LOG.isDebugEnabled()) {
1504 LOG.debug("Will send token of size " + replyToken.length
1505 + " from saslServer.");
1506 }
1507 doRawSaslReply(SaslStatus.SUCCESS, new BytesWritable(replyToken), null,
1508 null);
1509 }
1510 if (saslServer.isComplete()) {
1511 String qop = (String) saslServer.getNegotiatedProperty(Sasl.QOP);
1512 useWrap = qop != null && !"auth".equalsIgnoreCase(qop);
1513 ugi = getAuthorizedUgi(saslServer.getAuthorizationID());
1514 if (LOG.isDebugEnabled()) {
1515 LOG.debug("SASL server context established. Authenticated client: "
1516 + ugi + ". Negotiated QoP is "
1517 + saslServer.getNegotiatedProperty(Sasl.QOP));
1518 }
1519 metrics.authenticationSuccess();
1520 AUDITLOG.info(AUTH_SUCCESSFUL_FOR + ugi);
1521 saslContextEstablished = true;
1522 }
1523 }
1524 }
1525
1526
1527
1528
1529 private void doRawSaslReply(SaslStatus status, Writable rv,
1530 String errorClass, String error) throws IOException {
1531 ByteBufferOutputStream saslResponse = null;
1532 DataOutputStream out = null;
1533 try {
1534
1535
1536 saslResponse = new ByteBufferOutputStream(256);
1537 out = new DataOutputStream(saslResponse);
1538 out.writeInt(status.state);
1539 if (status == SaslStatus.SUCCESS) {
1540 rv.write(out);
1541 } else {
1542 WritableUtils.writeString(out, errorClass);
1543 WritableUtils.writeString(out, error);
1544 }
1545 saslCall.setSaslTokenResponse(saslResponse.getByteBuffer());
1546 saslCall.responder = responder;
1547 saslCall.sendResponseIfReady();
1548 } finally {
1549 if (saslResponse != null) {
1550 saslResponse.close();
1551 }
1552 if (out != null) {
1553 out.close();
1554 }
1555 }
1556 }
1557
1558 private void disposeSasl() {
1559 if (saslServer != null) {
1560 try {
1561 saslServer.dispose();
1562 saslServer = null;
1563 } catch (SaslException ignored) {
1564
1565 }
1566 }
1567 }
1568
1569 private int readPreamble() throws IOException {
1570 if (preambleBuffer == null) {
1571 preambleBuffer = ByteBuffer.allocate(6);
1572 }
1573 int count = channelRead(channel, preambleBuffer);
1574 if (count < 0 || preambleBuffer.remaining() > 0) {
1575 return count;
1576 }
1577
1578 preambleBuffer.flip();
1579 for (int i = 0; i < HConstants.RPC_HEADER.length; i++) {
1580 if (HConstants.RPC_HEADER[i] != preambleBuffer.get(i)) {
1581 return doBadPreambleHandling("Expected HEADER=" +
1582 Bytes.toStringBinary(HConstants.RPC_HEADER) + " but received HEADER=" +
1583 Bytes.toStringBinary(preambleBuffer.array(), 0, HConstants.RPC_HEADER.length) +
1584 " from " + toString());
1585 }
1586 }
1587 int version = preambleBuffer.get(HConstants.RPC_HEADER.length);
1588 byte authbyte = preambleBuffer.get(HConstants.RPC_HEADER.length + 1);
1589 this.authMethod = AuthMethod.valueOf(authbyte);
1590 if (version != CURRENT_VERSION) {
1591 String msg = getFatalConnectionString(version, authbyte);
1592 return doBadPreambleHandling(msg, new WrongVersionException(msg));
1593 }
1594 if (authMethod == null) {
1595 String msg = getFatalConnectionString(version, authbyte);
1596 return doBadPreambleHandling(msg, new BadAuthException(msg));
1597 }
1598 if (isSecurityEnabled && authMethod == AuthMethod.SIMPLE) {
1599 if (allowFallbackToSimpleAuth) {
1600 metrics.authenticationFallback();
1601 authenticatedWithFallback = true;
1602 } else {
1603 AccessDeniedException ae = new AccessDeniedException("Authentication is required");
1604 setupResponse(authFailedResponse, authFailedCall, ae, ae.getMessage());
1605 responder.doRespond(authFailedCall);
1606 throw ae;
1607 }
1608 }
1609 if (!isSecurityEnabled && authMethod != AuthMethod.SIMPLE) {
1610 doRawSaslReply(SaslStatus.SUCCESS, new IntWritable(
1611 SaslUtil.SWITCH_TO_SIMPLE_AUTH), null, null);
1612 authMethod = AuthMethod.SIMPLE;
1613
1614
1615
1616 skipInitialSaslHandshake = true;
1617 }
1618 if (authMethod != AuthMethod.SIMPLE) {
1619 useSasl = true;
1620 }
1621
1622 preambleBuffer = null;
1623 connectionPreambleRead = true;
1624 return count;
1625 }
1626
1627 private int read4Bytes() throws IOException {
1628 if (this.dataLengthBuffer.remaining() > 0) {
1629 return channelRead(channel, this.dataLengthBuffer);
1630 } else {
1631 return 0;
1632 }
1633 }
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643 public int readAndProcess() throws IOException, InterruptedException {
1644
1645 if (!connectionPreambleRead) {
1646 int count = readPreamble();
1647 if (!connectionPreambleRead) {
1648 return count;
1649 }
1650 }
1651
1652
1653 int count = read4Bytes();
1654 if (count < 0 || dataLengthBuffer.remaining() > 0) {
1655 return count;
1656 }
1657
1658
1659 if (!connectionPreambleRead) {
1660 count = readPreamble();
1661 if (!connectionPreambleRead) {
1662 return count;
1663 }
1664
1665 count = read4Bytes();
1666 if (count < 0 || dataLengthBuffer.remaining() > 0) {
1667 return count;
1668 }
1669 }
1670
1671 final boolean useWrap = this.useWrap;
1672 final BlockingService service = this.service;
1673 final boolean headerAndPreambleRead = connectionHeaderRead && connectionPreambleRead;
1674 final boolean canUseRequestTooBig = headerAndPreambleRead &&
1675 VersionInfoUtil.hasMinimumVersion(connectionHeader.getVersionInfo(),
1676 RequestTooBigException.MAJOR_VERSION, RequestTooBigException.MINOR_VERSION);
1677
1678
1679
1680 synchronized(this) {
1681
1682
1683 if (data == null) {
1684 dataLengthBuffer.flip();
1685 int dataLength = dataLengthBuffer.getInt();
1686 if (dataLength == RpcClient.PING_CALL_ID) {
1687 if (!useWrap) {
1688 dataLengthBuffer.clear();
1689 return 0;
1690 }
1691 }
1692 if (dataLength < 0) {
1693 throw new DoNotRetryIOException("Unexpected data length "
1694 + dataLength + "!! from " + getHostAddress());
1695 }
1696
1697 if (dataLength > maxRequestSize) {
1698 String msg = "RPC data length of " + dataLength + " received from "
1699 + getHostAddress() + " is greater than max allowed "
1700 + maxRequestSize + ". Set \"" + MAX_REQUEST_SIZE
1701 + "\" on server to override this limit (not recommended)";
1702 LOG.warn(msg);
1703
1704 if (headerAndPreambleRead) {
1705 incRpcCount();
1706
1707
1708
1709 final ByteBuffer buf = ByteBuffer.allocate(1);
1710 InputStream is = new InputStream() {
1711 @Override
1712 public int read() throws IOException {
1713 channelRead(channel, buf);
1714 buf.flip();
1715 int x = buf.get();
1716 buf.flip();
1717 return x;
1718 }
1719 };
1720 CodedInputStream cis = CodedInputStream.newInstance(is);
1721 int headerSize = cis.readRawVarint32();
1722 Message.Builder builder = RequestHeader.newBuilder();
1723 ProtobufUtil.mergeFrom(builder, cis, headerSize);
1724 RequestHeader header = (RequestHeader) builder.build();
1725
1726
1727 Call reqTooBig = new Call(header.getCallId(), service, null, null, null,
1728 null, this, responder, 0, null, this.addr,0);
1729 metrics.exception(REQUEST_TOO_BIG_EXCEPTION);
1730
1731
1732 if (canUseRequestTooBig) {
1733 setupResponse(null, reqTooBig, REQUEST_TOO_BIG_EXCEPTION, msg);
1734 } else {
1735 setupResponse(null, reqTooBig, new DoNotRetryIOException(), msg);
1736 }
1737
1738
1739 responseWriteLock.lock();
1740 responder.processResponse(reqTooBig);
1741 responseWriteLock.unlock();
1742 }
1743
1744 return -1;
1745 }
1746
1747 data = ByteBuffer.allocate(dataLength);
1748
1749
1750
1751
1752 incRpcCount();
1753 }
1754
1755 count = channelRead(channel, data);
1756
1757 if (count >= 0 && data.remaining() == 0) {
1758 process();
1759 }
1760 }
1761
1762 return count;
1763 }
1764
1765
1766
1767
1768 private void process() throws IOException, InterruptedException {
1769 data.flip();
1770 try {
1771 if (skipInitialSaslHandshake) {
1772 skipInitialSaslHandshake = false;
1773 return;
1774 }
1775
1776 if (useSasl) {
1777 saslReadAndProcess(data);
1778 } else {
1779 processOneRpc(data);
1780 }
1781
1782 } finally {
1783 dataLengthBuffer.clear();
1784 data = null;
1785 }
1786 }
1787
1788 private String getFatalConnectionString(final int version, final byte authByte) {
1789 return "serverVersion=" + CURRENT_VERSION +
1790 ", clientVersion=" + version + ", authMethod=" + authByte +
1791 ", authSupported=" + (authMethod != null) + " from " + toString();
1792 }
1793
1794 private int doBadPreambleHandling(final String msg) throws IOException {
1795 return doBadPreambleHandling(msg, new FatalConnectionException(msg));
1796 }
1797
1798 private int doBadPreambleHandling(final String msg, final Exception e) throws IOException {
1799 LOG.warn(msg);
1800 Call fakeCall = new Call(-1, null, null, null, null, null, this, responder, -1, null, null,0);
1801 setupResponse(null, fakeCall, e, msg);
1802 responder.doRespond(fakeCall);
1803
1804 return -1;
1805 }
1806
1807
1808 private void processConnectionHeader(ByteBuffer buf) throws IOException {
1809 this.connectionHeader = ConnectionHeader.parseFrom(
1810 new ByteBufferInputStream(buf));
1811 String serviceName = connectionHeader.getServiceName();
1812 if (serviceName == null) throw new EmptyServiceNameException();
1813 this.service = getService(services, serviceName);
1814 if (this.service == null) throw new UnknownServiceException(serviceName);
1815 setupCellBlockCodecs(this.connectionHeader);
1816 UserGroupInformation protocolUser = createUser(connectionHeader);
1817 if (!useSasl) {
1818 ugi = protocolUser;
1819 if (ugi != null) {
1820 ugi.setAuthenticationMethod(AuthMethod.SIMPLE.authenticationMethod);
1821 }
1822
1823 if (authenticatedWithFallback) {
1824 LOG.warn("Allowed fallback to SIMPLE auth for " + ugi
1825 + " connecting from " + getHostAddress());
1826 }
1827 AUDITLOG.info(AUTH_SUCCESSFUL_FOR + ugi);
1828 } else {
1829
1830 ugi.setAuthenticationMethod(authMethod.authenticationMethod);
1831
1832
1833
1834 if ((protocolUser != null)
1835 && (!protocolUser.getUserName().equals(ugi.getUserName()))) {
1836 if (authMethod == AuthMethod.DIGEST) {
1837
1838 throw new AccessDeniedException("Authenticated user (" + ugi
1839 + ") doesn't match what the client claims to be ("
1840 + protocolUser + ")");
1841 } else {
1842
1843
1844
1845 UserGroupInformation realUser = ugi;
1846 ugi = UserGroupInformation.createProxyUser(protocolUser
1847 .getUserName(), realUser);
1848
1849 ugi.setAuthenticationMethod(AuthenticationMethod.PROXY);
1850 }
1851 }
1852 }
1853 if (connectionHeader.hasVersionInfo()) {
1854
1855 retryImmediatelySupported = VersionInfoUtil.hasMinimumVersion(getVersionInfo(), 1, 2);
1856
1857 AUDITLOG.info("Connection from " + this.hostAddress + " port: " + this.remotePort
1858 + " with version info: "
1859 + TextFormat.shortDebugString(connectionHeader.getVersionInfo()));
1860 } else {
1861 AUDITLOG.info("Connection from " + this.hostAddress + " port: " + this.remotePort
1862 + " with unknown version info");
1863 }
1864
1865
1866 }
1867
1868
1869
1870
1871
1872 private void setupCellBlockCodecs(final ConnectionHeader header)
1873 throws FatalConnectionException {
1874
1875 if (!header.hasCellBlockCodecClass()) return;
1876 String className = header.getCellBlockCodecClass();
1877 if (className == null || className.length() == 0) return;
1878 try {
1879 this.codec = (Codec)Class.forName(className).getDeclaredConstructor().newInstance();
1880 } catch (Exception e) {
1881 throw new UnsupportedCellCodecException(className, e);
1882 }
1883 if (!header.hasCellBlockCompressorClass()) return;
1884 className = header.getCellBlockCompressorClass();
1885 try {
1886 this.compressionCodec = (CompressionCodec)
1887 Class.forName(className).getDeclaredConstructor().newInstance();
1888 } catch (Exception e) {
1889 throw new UnsupportedCompressionCodecException(className, e);
1890 }
1891 }
1892
1893 private void processUnwrappedData(byte[] inBuf) throws IOException,
1894 InterruptedException {
1895 ReadableByteChannel ch = Channels.newChannel(new ByteArrayInputStream(inBuf));
1896
1897 while (true) {
1898 int count;
1899 if (unwrappedDataLengthBuffer.remaining() > 0) {
1900 count = channelRead(ch, unwrappedDataLengthBuffer);
1901 if (count <= 0 || unwrappedDataLengthBuffer.remaining() > 0)
1902 return;
1903 }
1904
1905 if (unwrappedData == null) {
1906 unwrappedDataLengthBuffer.flip();
1907 int unwrappedDataLength = unwrappedDataLengthBuffer.getInt();
1908
1909 if (unwrappedDataLength == RpcClient.PING_CALL_ID) {
1910 if (LOG.isDebugEnabled())
1911 LOG.debug("Received ping message");
1912 unwrappedDataLengthBuffer.clear();
1913 continue;
1914 }
1915 unwrappedData = ByteBuffer.allocate(unwrappedDataLength);
1916 }
1917
1918 count = channelRead(ch, unwrappedData);
1919 if (count <= 0 || unwrappedData.remaining() > 0)
1920 return;
1921
1922 if (unwrappedData.remaining() == 0) {
1923 unwrappedDataLengthBuffer.clear();
1924 unwrappedData.flip();
1925 processOneRpc(unwrappedData);
1926 unwrappedData = null;
1927 }
1928 }
1929 }
1930
1931
1932 private void processOneRpc(ByteBuffer buf) throws IOException, InterruptedException {
1933 if (connectionHeaderRead) {
1934 processRequest(buf);
1935 } else {
1936 processConnectionHeader(buf);
1937 this.connectionHeaderRead = true;
1938 if (!authorizeConnection()) {
1939
1940
1941 throw new AccessDeniedException("Connection from " + this + " for service " +
1942 connectionHeader.getServiceName() + " is unauthorized for user: " + ugi);
1943 }
1944 this.user = userProvider.create(this.ugi);
1945 }
1946 }
1947
1948
1949
1950
1951
1952
1953
1954 protected void processRequest(ByteBuffer buf) throws IOException, InterruptedException {
1955 long totalRequestSize = buf.limit();
1956 int offset = 0;
1957
1958
1959 CodedInputStream cis = CodedInputStream.newInstance(buf.array(), offset, buf.limit());
1960 int headerSize = cis.readRawVarint32();
1961 offset = cis.getTotalBytesRead();
1962 Message.Builder builder = RequestHeader.newBuilder();
1963 ProtobufUtil.mergeFrom(builder, cis, headerSize);
1964 RequestHeader header = (RequestHeader) builder.build();
1965 offset += headerSize;
1966 int id = header.getCallId();
1967 if (LOG.isTraceEnabled()) {
1968 LOG.trace("RequestHeader " + TextFormat.shortDebugString(header) +
1969 " totalRequestSize: " + totalRequestSize + " bytes");
1970 }
1971
1972
1973 if ((totalRequestSize + callQueueSize.get()) > maxQueueSize) {
1974 final Call callTooBig =
1975 new Call(id, this.service, null, null, null, null, this,
1976 responder, totalRequestSize, null, null, 0);
1977 ByteArrayOutputStream responseBuffer = new ByteArrayOutputStream();
1978 metrics.exception(CALL_QUEUE_TOO_BIG_EXCEPTION);
1979 setupResponse(responseBuffer, callTooBig, CALL_QUEUE_TOO_BIG_EXCEPTION,
1980 "Call queue is full on " + server.getServerName() +
1981 ", is hbase.ipc.server.max.callqueue.size too small?");
1982 responder.doRespond(callTooBig);
1983 return;
1984 }
1985 MethodDescriptor md = null;
1986 Message param = null;
1987 CellScanner cellScanner = null;
1988 try {
1989 if (header.hasRequestParam() && header.getRequestParam()) {
1990 md = this.service.getDescriptorForType().findMethodByName(header.getMethodName());
1991 if (md == null) throw new UnsupportedOperationException(header.getMethodName());
1992 builder = this.service.getRequestPrototype(md).newBuilderForType();
1993 cis.resetSizeCounter();
1994 int paramSize = cis.readRawVarint32();
1995 offset += cis.getTotalBytesRead();
1996 if (builder != null) {
1997 ProtobufUtil.mergeFrom(builder, cis, paramSize);
1998 param = builder.build();
1999 }
2000 offset += paramSize;
2001 } else {
2002
2003 String msg = "Invalid request header: " + TextFormat.shortDebugString(header)
2004 + ", should have param set in it";
2005 LOG.warn(msg);
2006 throw new DoNotRetryIOException(msg);
2007 }
2008 if (header.hasCellBlockMeta()) {
2009 buf.position(offset);
2010 cellScanner = cellBlockBuilder.createCellScanner(this.codec, this.compressionCodec, buf);
2011 }
2012 } catch (Throwable t) {
2013 InetSocketAddress address = getListenerAddress();
2014 String msg = (address != null ? address : "(channel closed)") +
2015 " is unable to read call parameter from client " + getHostAddress();
2016 LOG.warn(msg, t);
2017
2018 metrics.exception(t);
2019
2020
2021 if (t instanceof LinkageError) {
2022 t = new DoNotRetryIOException(t);
2023 }
2024
2025 if (t instanceof UnsupportedOperationException) {
2026 t = new DoNotRetryIOException(t);
2027 }
2028
2029 final Call readParamsFailedCall =
2030 new Call(id, this.service, null, null, null, null, this,
2031 responder, totalRequestSize, null, null, 0);
2032 ByteArrayOutputStream responseBuffer = new ByteArrayOutputStream();
2033 setupResponse(responseBuffer, readParamsFailedCall, t,
2034 msg + "; " + t.getMessage());
2035 responder.doRespond(readParamsFailedCall);
2036 return;
2037 }
2038
2039 TraceInfo traceInfo = header.hasTraceInfo()
2040 ? new TraceInfo(header.getTraceInfo().getTraceId(), header.getTraceInfo().getParentId())
2041 : null;
2042 int timeout = 0;
2043 if (header.hasTimeout() && header.getTimeout() > 0){
2044 timeout = Math.max(minClientRequestTimeout, header.getTimeout());
2045 }
2046 Call call = new Call(id, this.service, md, header, param, cellScanner, this, responder,
2047 totalRequestSize, traceInfo, this.addr, timeout);
2048
2049 if (!scheduler.dispatch(new CallRunner(RpcServer.this, call))) {
2050 callQueueSize.add(-1 * call.getSize());
2051
2052 ByteArrayOutputStream responseBuffer = new ByteArrayOutputStream();
2053 metrics.exception(CALL_QUEUE_TOO_BIG_EXCEPTION);
2054 setupResponse(responseBuffer, call, CALL_QUEUE_TOO_BIG_EXCEPTION,
2055 "Call queue is full on " + server.getServerName() +
2056 ", too many items queued ?");
2057 responder.doRespond(call);
2058 }
2059 }
2060
2061 private boolean authorizeConnection() throws IOException {
2062 try {
2063
2064
2065
2066
2067 if (ugi != null && ugi.getRealUser() != null
2068 && (authMethod != AuthMethod.DIGEST)) {
2069 ProxyUsers.authorize(ugi, this.getHostAddress(), conf);
2070 }
2071 authorize(ugi, connectionHeader, getHostInetAddress());
2072 metrics.authorizationSuccess();
2073 } catch (AuthorizationException ae) {
2074 if (LOG.isDebugEnabled()) {
2075 LOG.debug("Connection authorization failed: " + ae.getMessage(), ae);
2076 }
2077 metrics.authorizationFailure();
2078 setupResponse(authFailedResponse, authFailedCall,
2079 new AccessDeniedException(ae), ae.getMessage());
2080 responder.doRespond(authFailedCall);
2081 return false;
2082 }
2083 return true;
2084 }
2085
2086 protected synchronized void close() {
2087 disposeSasl();
2088 data = null;
2089 if (!channel.isOpen())
2090 return;
2091 try {socket.shutdownOutput();} catch(Exception ignored) {
2092 if (LOG.isTraceEnabled()) {
2093 LOG.trace(ignored);
2094 }
2095 }
2096 if (channel.isOpen()) {
2097 try {channel.close();} catch(Exception ignored) {
2098 if (LOG.isTraceEnabled()) {
2099 LOG.trace(ignored);
2100 }
2101 }
2102 }
2103 try {socket.close();} catch(Exception ignored) {
2104 if (LOG.isTraceEnabled()) {
2105 LOG.trace(ignored);
2106 }
2107 }
2108 }
2109
2110 private UserGroupInformation createUser(ConnectionHeader head) {
2111 UserGroupInformation ugi = null;
2112
2113 if (!head.hasUserInfo()) {
2114 return null;
2115 }
2116 UserInformation userInfoProto = head.getUserInfo();
2117 String effectiveUser = null;
2118 if (userInfoProto.hasEffectiveUser()) {
2119 effectiveUser = userInfoProto.getEffectiveUser();
2120 }
2121 String realUser = null;
2122 if (userInfoProto.hasRealUser()) {
2123 realUser = userInfoProto.getRealUser();
2124 }
2125 if (effectiveUser != null) {
2126 if (realUser != null) {
2127 UserGroupInformation realUserUgi =
2128 UserGroupInformation.createRemoteUser(realUser);
2129 ugi = UserGroupInformation.createProxyUser(effectiveUser, realUserUgi);
2130 } else {
2131 ugi = UserGroupInformation.createRemoteUser(effectiveUser);
2132 }
2133 }
2134 return ugi;
2135 }
2136 }
2137
2138
2139
2140
2141
2142
2143
2144 public static class BlockingServiceAndInterface {
2145 private final BlockingService service;
2146 private final Class<?> serviceInterface;
2147 public BlockingServiceAndInterface(final BlockingService service,
2148 final Class<?> serviceInterface) {
2149 this.service = service;
2150 this.serviceInterface = serviceInterface;
2151 }
2152 public Class<?> getServiceInterface() {
2153 return this.serviceInterface;
2154 }
2155 public BlockingService getBlockingService() {
2156 return this.service;
2157 }
2158 }
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170 public RpcServer(final Server server, final String name,
2171 final List<BlockingServiceAndInterface> services,
2172 final InetSocketAddress bindAddress, Configuration conf,
2173 RpcScheduler scheduler)
2174 throws IOException {
2175
2176 if (conf.getBoolean("hbase.ipc.server.reservoir.enabled", true)) {
2177 this.reservoir = new BoundedByteBufferPool(
2178 conf.getInt("hbase.ipc.server.reservoir.max.buffer.size", 1024 * 1024),
2179 conf.getInt("hbase.ipc.server.reservoir.initial.buffer.size", 16 * 1024),
2180
2181 conf.getInt("hbase.ipc.server.reservoir.initial.max",
2182 conf.getInt(HConstants.REGION_SERVER_HANDLER_COUNT,
2183 HConstants.DEFAULT_REGION_SERVER_HANDLER_COUNT) * 2),
2184
2185 conf.getBoolean("hbase.ipc.server.reservoir.direct.buffer", false));
2186 } else {
2187 reservoir = null;
2188 }
2189 this.server = server;
2190 this.services = services;
2191 this.bindAddress = bindAddress;
2192 this.conf = conf;
2193 this.socketSendBufferSize = 0;
2194 this.maxQueueSize =
2195 this.conf.getInt("hbase.ipc.server.max.callqueue.size", DEFAULT_MAX_CALLQUEUE_SIZE);
2196 this.readThreads = conf.getInt("hbase.ipc.server.read.threadpool.size", 10);
2197 this.maxIdleTime = 2 * conf.getInt("hbase.ipc.client.connection.maxidletime", 1000);
2198 this.maxConnectionsToNuke = conf.getInt("hbase.ipc.client.kill.max", 10);
2199 this.thresholdIdleConnections = conf.getInt("hbase.ipc.client.idlethreshold", 4000);
2200 this.purgeTimeout = conf.getLong("hbase.ipc.client.call.purge.timeout",
2201 2 * HConstants.DEFAULT_HBASE_RPC_TIMEOUT);
2202 this.warnResponseTime = conf.getInt(WARN_RESPONSE_TIME, DEFAULT_WARN_RESPONSE_TIME);
2203 this.warnResponseSize = conf.getInt(WARN_RESPONSE_SIZE, DEFAULT_WARN_RESPONSE_SIZE);
2204 this.minClientRequestTimeout = conf.getInt(MIN_CLIENT_REQUEST_TIMEOUT,
2205 DEFAULT_MIN_CLIENT_REQUEST_TIMEOUT);
2206 this.maxRequestSize = conf.getInt(MAX_REQUEST_SIZE, DEFAULT_MAX_REQUEST_SIZE);
2207
2208
2209 listener = new Listener(name);
2210 this.port = listener.getAddress().getPort();
2211
2212 this.metrics = new MetricsHBaseServer(name, new MetricsHBaseServerWrapperImpl(this));
2213 this.tcpNoDelay = conf.getBoolean("hbase.ipc.server.tcpnodelay", true);
2214 this.tcpKeepAlive = conf.getBoolean("hbase.ipc.server.tcpkeepalive", true);
2215
2216 this.cellBlockBuilder = new CellBlockBuilder(conf);
2217
2218
2219 responder = new Responder();
2220 this.authorize = conf.getBoolean(HADOOP_SECURITY_AUTHORIZATION, false);
2221 this.userProvider = UserProvider.instantiate(conf);
2222 this.isSecurityEnabled = userProvider.isHBaseSecurityEnabled();
2223 if (isSecurityEnabled) {
2224 HBaseSaslRpcServer.init(conf);
2225 }
2226 initReconfigurable(conf);
2227
2228 this.scheduler = scheduler;
2229 this.scheduler.init(new RpcSchedulerContext(this));
2230 }
2231
2232 @Override
2233 public void onConfigurationChange(Configuration newConf) {
2234 initReconfigurable(newConf);
2235 if (scheduler instanceof ConfigurationObserver) {
2236 ((ConfigurationObserver)scheduler).onConfigurationChange(newConf);
2237 }
2238 }
2239
2240 private void initReconfigurable(Configuration confToLoad) {
2241 this.allowFallbackToSimpleAuth = confToLoad.getBoolean(FALLBACK_TO_INSECURE_CLIENT_AUTH, false);
2242 if (isSecurityEnabled && allowFallbackToSimpleAuth) {
2243 LOG.warn("********* WARNING! *********");
2244 LOG.warn("This server is configured to allow connections from INSECURE clients");
2245 LOG.warn("(" + FALLBACK_TO_INSECURE_CLIENT_AUTH + " = true).");
2246 LOG.warn("While this option is enabled, client identities cannot be secured, and user");
2247 LOG.warn("impersonation is possible!");
2248 LOG.warn("For secure operation, please disable SIMPLE authentication as soon as possible,");
2249 LOG.warn("by setting " + FALLBACK_TO_INSECURE_CLIENT_AUTH + " = false in hbase-site.xml");
2250 LOG.warn("****************************");
2251 }
2252 }
2253
2254
2255
2256
2257
2258 protected Connection getConnection(SocketChannel channel, long time) {
2259 return new Connection(channel, time);
2260 }
2261
2262
2263
2264
2265
2266
2267
2268
2269
2270 private void setupResponse(ByteArrayOutputStream response, Call call, Throwable t, String error)
2271 throws IOException {
2272 if (response != null) response.reset();
2273 call.setResponse(null, null, t, error);
2274 }
2275
2276 protected void closeConnection(Connection connection) {
2277 synchronized (connectionList) {
2278 if (connectionList.remove(connection)) {
2279 numConnections--;
2280 }
2281 }
2282 connection.close();
2283 }
2284
2285 Configuration getConf() {
2286 return conf;
2287 }
2288
2289
2290
2291
2292 @Override
2293 public void setSocketSendBufSize(int size) { this.socketSendBufferSize = size; }
2294
2295 @Override
2296 public boolean isStarted() {
2297 return this.started;
2298 }
2299
2300
2301 @Override
2302 public synchronized void start() {
2303 if (started) return;
2304 authTokenSecretMgr = createSecretManager();
2305 if (authTokenSecretMgr != null) {
2306 setSecretManager(authTokenSecretMgr);
2307 authTokenSecretMgr.start();
2308 }
2309 this.authManager = new ServiceAuthorizationManager();
2310 HBasePolicyProvider.init(conf, authManager);
2311 responder.start();
2312 listener.start();
2313 scheduler.start();
2314 started = true;
2315 }
2316
2317 @Override
2318 public synchronized void refreshAuthManager(PolicyProvider pp) {
2319
2320
2321 this.authManager.refresh(this.conf, pp);
2322 }
2323
2324 private AuthenticationTokenSecretManager createSecretManager() {
2325 if (!isSecurityEnabled) return null;
2326 if (server == null) return null;
2327 Configuration conf = server.getConfiguration();
2328 long keyUpdateInterval =
2329 conf.getLong("hbase.auth.key.update.interval", 24*60*60*1000);
2330 long maxAge =
2331 conf.getLong("hbase.auth.token.max.lifetime", 7*24*60*60*1000);
2332 return new AuthenticationTokenSecretManager(conf, server.getZooKeeper(),
2333 server.getServerName().toString(), keyUpdateInterval, maxAge);
2334 }
2335
2336 public SecretManager<? extends TokenIdentifier> getSecretManager() {
2337 return this.secretManager;
2338 }
2339
2340 @SuppressWarnings("unchecked")
2341 public void setSecretManager(SecretManager<? extends TokenIdentifier> secretManager) {
2342 this.secretManager = (SecretManager<TokenIdentifier>) secretManager;
2343 }
2344
2345 public Pair<Message, CellScanner> call(BlockingService service, MethodDescriptor md,
2346 Message param, CellScanner cellScanner, long receiveTime, MonitoredRPCHandler status)
2347 throws IOException {
2348 return call(service, md, param, cellScanner, receiveTime, status, System.currentTimeMillis(),0);
2349 }
2350
2351
2352
2353
2354
2355
2356 @Override
2357 public Pair<Message, CellScanner> call(BlockingService service, MethodDescriptor md,
2358 Message param, CellScanner cellScanner, long receiveTime, MonitoredRPCHandler status,
2359 long startTime, int timeout)
2360 throws IOException {
2361 try {
2362 status.setRPC(md.getName(), new Object[]{param}, receiveTime);
2363
2364 status.setRPCPacket(param);
2365 status.resume("Servicing call");
2366
2367 HBaseRpcController controller = new HBaseRpcControllerImpl(cellScanner);
2368 controller.setCallTimeout(timeout);
2369 Message result = service.callBlockingMethod(md, controller, param);
2370 long endTime = System.currentTimeMillis();
2371 int processingTime = (int) (endTime - startTime);
2372 int qTime = (int) (startTime - receiveTime);
2373 int totalTime = (int) (endTime - receiveTime);
2374 Call call = CurCall.get();
2375 if (LOG.isTraceEnabled()) {
2376 LOG.trace(CurCall.get().toString() +
2377 ", response " + TextFormat.shortDebugString(result) +
2378 " queueTime: " + qTime +
2379 " processingTime: " + processingTime +
2380 " totalTime: " + totalTime);
2381 }
2382
2383 long requestSize = call.getSize();
2384 long responseSize = result.getSerializedSize();
2385 if (call.isClientCellBlockSupported()) {
2386
2387 responseSize += call.getResponseCellSize();
2388 }
2389
2390 metrics.dequeuedCall(qTime);
2391 metrics.processedCall(processingTime);
2392 metrics.totalCall(totalTime);
2393 metrics.receivedRequest(requestSize);
2394 metrics.sentResponse(responseSize);
2395
2396
2397 boolean tooSlow = (processingTime > warnResponseTime && warnResponseTime > -1);
2398 boolean tooLarge = (responseSize > warnResponseSize && warnResponseSize > -1);
2399 if (tooSlow || tooLarge) {
2400
2401
2402 logResponse(param,
2403 md.getName(), md.getName() + "(" + param.getClass().getName() + ")",
2404 (tooLarge ? "TooLarge" : "TooSlow"),
2405 status.getClient(), startTime, processingTime, qTime,
2406 responseSize);
2407 }
2408 return new Pair<Message, CellScanner>(result, controller.cellScanner());
2409 } catch (Throwable e) {
2410
2411
2412
2413 if (e instanceof ServiceException) {
2414 if (e.getCause() == null) {
2415 LOG.debug("Caught a ServiceException with null cause", e);
2416 } else {
2417 e = e.getCause();
2418 }
2419 }
2420
2421
2422 metrics.exception(e);
2423
2424 if (e instanceof LinkageError) throw new DoNotRetryIOException(e);
2425 if (e instanceof IOException) throw (IOException)e;
2426 LOG.error("Unexpected throwable object ", e);
2427 throw new IOException(e.getMessage(), e);
2428 }
2429 }
2430
2431
2432
2433
2434
2435
2436
2437
2438
2439
2440
2441
2442
2443
2444
2445 void logResponse(Message param, String methodName, String call, String tag,
2446 String clientAddress, long startTime, int processingTime, int qTime,
2447 long responseSize)
2448 throws IOException {
2449
2450 Map<String, Object> responseInfo = new HashMap<String, Object>();
2451 responseInfo.put("starttimems", startTime);
2452 responseInfo.put("processingtimems", processingTime);
2453 responseInfo.put("queuetimems", qTime);
2454 responseInfo.put("responsesize", responseSize);
2455 responseInfo.put("client", clientAddress);
2456 responseInfo.put("class", server == null? "": server.getClass().getSimpleName());
2457 responseInfo.put("method", methodName);
2458 responseInfo.put("call", call);
2459
2460 String stringifiedParam = ProtobufUtil.getShortTextFormat(param);
2461 if (stringifiedParam.length() > 150) {
2462
2463 stringifiedParam = truncateTraceLog(stringifiedParam);
2464 }
2465 responseInfo.put("param", stringifiedParam);
2466 if (param instanceof ClientProtos.ScanRequest && rsRpcServices != null) {
2467 ClientProtos.ScanRequest request = ((ClientProtos.ScanRequest) param);
2468 if (request.hasScannerId()) {
2469 long scannerId = request.getScannerId();
2470 String scanDetails = rsRpcServices.getScanDetailsWithId(scannerId);
2471 if (scanDetails != null) {
2472 responseInfo.put("scandetails", scanDetails);
2473 }
2474 }
2475 }
2476 LOG.warn("(response" + tag + "): " + MAPPER.writeValueAsString(responseInfo));
2477 }
2478
2479
2480 @Override
2481 public synchronized void stop() {
2482 LOG.info("Stopping server on " + port);
2483 running = false;
2484 if (authTokenSecretMgr != null) {
2485 authTokenSecretMgr.stop();
2486 authTokenSecretMgr = null;
2487 }
2488 listener.interrupt();
2489 listener.doStop();
2490 responder.interrupt();
2491 scheduler.stop();
2492 notifyAll();
2493 }
2494
2495
2496
2497
2498
2499
2500 @Override
2501 public synchronized void join() throws InterruptedException {
2502 while (running) {
2503 wait();
2504 }
2505 }
2506
2507
2508
2509
2510
2511
2512
2513 @Override
2514 public synchronized InetSocketAddress getListenerAddress() {
2515 if (listener == null) {
2516 return null;
2517 }
2518 return listener.getAddress();
2519 }
2520
2521
2522
2523
2524
2525
2526
2527 @VisibleForTesting
2528 String truncateTraceLog(String strParam) {
2529 if (LOG.isTraceEnabled()) {
2530 int traceLogMaxLength = getConf().getInt(TRACE_LOG_MAX_LENGTH, DEFAULT_TRACE_LOG_MAX_LENGTH);
2531 int truncatedLength =
2532 strParam.length() < traceLogMaxLength ? strParam.length() : traceLogMaxLength;
2533 String truncatedFlag = truncatedLength == strParam.length() ? "" : KEY_WORD_TRUNCATED;
2534 return strParam.subSequence(0, truncatedLength) + truncatedFlag;
2535 }
2536 return strParam.subSequence(0, 150) + KEY_WORD_TRUNCATED;
2537 }
2538
2539
2540
2541
2542
2543 @Override
2544 public void setErrorHandler(HBaseRPCErrorHandler handler) {
2545 this.errorHandler = handler;
2546 }
2547
2548 @Override
2549 public HBaseRPCErrorHandler getErrorHandler() {
2550 return this.errorHandler;
2551 }
2552
2553
2554
2555
2556 @Override
2557 public MetricsHBaseServer getMetrics() {
2558 return metrics;
2559 }
2560
2561 @Override
2562 public void addCallSize(final long diff) {
2563 this.callQueueSize.add(diff);
2564 }
2565
2566
2567
2568
2569
2570
2571
2572
2573
2574
2575 public synchronized void authorize(UserGroupInformation user, ConnectionHeader connection,
2576 InetAddress addr)
2577 throws AuthorizationException {
2578 if (authorize) {
2579 Class<?> c = getServiceInterface(services, connection.getServiceName());
2580 this.authManager.authorize(user != null ? user : null, c, getConf(), addr);
2581 }
2582 }
2583
2584
2585
2586
2587
2588
2589 private static int NIO_BUFFER_LIMIT = 64 * 1024;
2590
2591
2592
2593
2594
2595
2596
2597
2598
2599
2600
2601
2602
2603
2604
2605 protected long channelWrite(GatheringByteChannel channel, BufferChain bufferChain)
2606 throws IOException {
2607 long count = bufferChain.write(channel, NIO_BUFFER_LIMIT);
2608 if (count > 0) this.metrics.sentBytes(count);
2609 return count;
2610 }
2611
2612
2613
2614
2615
2616
2617
2618
2619
2620
2621
2622
2623
2624 protected int channelRead(ReadableByteChannel channel,
2625 ByteBuffer buffer) throws IOException {
2626
2627 int count = (buffer.remaining() <= NIO_BUFFER_LIMIT) ?
2628 channel.read(buffer) : channelIO(channel, null, buffer);
2629 if (count > 0) {
2630 metrics.receivedBytes(count);
2631 }
2632 return count;
2633 }
2634
2635
2636
2637
2638
2639
2640
2641
2642
2643
2644
2645
2646
2647
2648 private static int channelIO(ReadableByteChannel readCh,
2649 WritableByteChannel writeCh,
2650 ByteBuffer buf) throws IOException {
2651
2652 int originalLimit = buf.limit();
2653 int initialRemaining = buf.remaining();
2654 int ret = 0;
2655
2656 while (buf.remaining() > 0) {
2657 try {
2658 int ioSize = Math.min(buf.remaining(), NIO_BUFFER_LIMIT);
2659 buf.limit(buf.position() + ioSize);
2660
2661 ret = (readCh == null) ? writeCh.write(buf) : readCh.read(buf);
2662
2663 if (ret < ioSize) {
2664 break;
2665 }
2666
2667 } finally {
2668 buf.limit(originalLimit);
2669 }
2670 }
2671
2672 int nBytes = initialRemaining - buf.remaining();
2673 return (nBytes > 0) ? nBytes : ret;
2674 }
2675
2676
2677
2678
2679
2680
2681
2682 public static RpcCallContext getCurrentCall() {
2683 return CurCall.get();
2684 }
2685
2686 public static boolean isInRpcCallContext() {
2687 return CurCall.get() != null;
2688 }
2689
2690
2691
2692
2693
2694
2695 public static User getRequestUser() {
2696 RpcCallContext ctx = getCurrentCall();
2697 return ctx == null? null: ctx.getRequestUser();
2698 }
2699
2700
2701
2702
2703
2704 public static String getRequestUserName() {
2705 User user = getRequestUser();
2706 return user == null? null: user.getShortName();
2707 }
2708
2709
2710
2711
2712 public static InetAddress getRemoteAddress() {
2713 RpcCallContext ctx = getCurrentCall();
2714 return ctx == null? null: ctx.getRemoteAddress();
2715 }
2716
2717
2718
2719
2720
2721
2722 static BlockingServiceAndInterface getServiceAndInterface(
2723 final List<BlockingServiceAndInterface> services, final String serviceName) {
2724 for (BlockingServiceAndInterface bs : services) {
2725 if (bs.getBlockingService().getDescriptorForType().getName().equals(serviceName)) {
2726 return bs;
2727 }
2728 }
2729 return null;
2730 }
2731
2732
2733
2734
2735
2736
2737 static Class<?> getServiceInterface(
2738 final List<BlockingServiceAndInterface> services,
2739 final String serviceName) {
2740 BlockingServiceAndInterface bsasi =
2741 getServiceAndInterface(services, serviceName);
2742 return bsasi == null? null: bsasi.getServiceInterface();
2743 }
2744
2745
2746
2747
2748
2749
2750 static BlockingService getService(
2751 final List<BlockingServiceAndInterface> services,
2752 final String serviceName) {
2753 BlockingServiceAndInterface bsasi =
2754 getServiceAndInterface(services, serviceName);
2755 return bsasi == null? null: bsasi.getBlockingService();
2756 }
2757
2758 static MonitoredRPCHandler getStatus() {
2759
2760 MonitoredRPCHandler status = RpcServer.MONITORED_RPC.get();
2761 if (status != null) {
2762 return status;
2763 }
2764 status = TaskMonitor.get().createRPCStatus(Thread.currentThread().getName());
2765 status.pause("Waiting for a call");
2766 RpcServer.MONITORED_RPC.set(status);
2767 return status;
2768 }
2769
2770
2771
2772
2773
2774 public static InetAddress getRemoteIp() {
2775 Call call = CurCall.get();
2776 if (call != null && call.connection != null && call.connection.socket != null) {
2777 return call.connection.socket.getInetAddress();
2778 }
2779 return null;
2780 }
2781
2782
2783
2784
2785
2786
2787
2788
2789
2790
2791
2792
2793 public static void bind(ServerSocket socket, InetSocketAddress address,
2794 int backlog) throws IOException {
2795 try {
2796 socket.bind(address, backlog);
2797 } catch (BindException e) {
2798 BindException bindException =
2799 new BindException("Problem binding to " + address + " : " +
2800 e.getMessage());
2801 bindException.initCause(e);
2802 throw bindException;
2803 } catch (SocketException e) {
2804
2805
2806 if ("Unresolved address".equals(e.getMessage())) {
2807 throw new UnknownHostException("Invalid hostname for server: " +
2808 address.getHostName());
2809 }
2810 throw e;
2811 }
2812 }
2813
2814 @Override
2815 public RpcScheduler getScheduler() {
2816 return scheduler;
2817 }
2818
2819 @Override
2820 public void setRsRpcServices(RSRpcServices rsRpcServices) {
2821 this.rsRpcServices = rsRpcServices;
2822 }
2823 }