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