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