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