1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.eclipse.jetty.server.handler;
20
21 import java.io.IOException;
22 import java.net.InetSocketAddress;
23 import java.net.SocketException;
24 import java.net.SocketTimeoutException;
25 import java.nio.channels.ClosedChannelException;
26 import java.nio.channels.SelectionKey;
27 import java.nio.channels.SocketChannel;
28 import java.util.Arrays;
29 import java.util.concurrent.ConcurrentHashMap;
30 import java.util.concurrent.ConcurrentMap;
31 import java.util.concurrent.CountDownLatch;
32 import java.util.concurrent.TimeUnit;
33 import javax.servlet.ServletException;
34 import javax.servlet.http.HttpServletRequest;
35 import javax.servlet.http.HttpServletResponse;
36
37 import org.eclipse.jetty.http.HttpMethods;
38 import org.eclipse.jetty.http.HttpParser;
39 import org.eclipse.jetty.io.AsyncEndPoint;
40 import org.eclipse.jetty.io.Buffer;
41 import org.eclipse.jetty.io.ConnectedEndPoint;
42 import org.eclipse.jetty.io.Connection;
43 import org.eclipse.jetty.io.EndPoint;
44 import org.eclipse.jetty.io.nio.AsyncConnection;
45 import org.eclipse.jetty.io.nio.IndirectNIOBuffer;
46 import org.eclipse.jetty.io.nio.SelectChannelEndPoint;
47 import org.eclipse.jetty.io.nio.SelectorManager;
48 import org.eclipse.jetty.server.AbstractHttpConnection;
49 import org.eclipse.jetty.server.Handler;
50 import org.eclipse.jetty.server.Request;
51 import org.eclipse.jetty.server.Server;
52 import org.eclipse.jetty.util.HostMap;
53 import org.eclipse.jetty.util.TypeUtil;
54 import org.eclipse.jetty.util.component.LifeCycle;
55 import org.eclipse.jetty.util.log.Log;
56 import org.eclipse.jetty.util.log.Logger;
57 import org.eclipse.jetty.util.thread.ThreadPool;
58
59
60
61
62
63
64 public class ConnectHandler extends HandlerWrapper
65 {
66 private static final Logger LOG = Log.getLogger(ConnectHandler.class);
67 private final SelectorManager _selectorManager = new Manager();
68 private volatile int _connectTimeout = 5000;
69 private volatile int _writeTimeout = 30000;
70 private volatile ThreadPool _threadPool;
71 private volatile boolean _privateThreadPool;
72 private HostMap<String> _white = new HostMap<String>();
73 private HostMap<String> _black = new HostMap<String>();
74
75 public ConnectHandler()
76 {
77 this(null);
78 }
79
80 public ConnectHandler(String[] white, String[] black)
81 {
82 this(null, white, black);
83 }
84
85 public ConnectHandler(Handler handler)
86 {
87 setHandler(handler);
88 }
89
90 public ConnectHandler(Handler handler, String[] white, String[] black)
91 {
92 setHandler(handler);
93 set(white, _white);
94 set(black, _black);
95 }
96
97
98
99
100 public int getConnectTimeout()
101 {
102 return _connectTimeout;
103 }
104
105
106
107
108 public void setConnectTimeout(int connectTimeout)
109 {
110 _connectTimeout = connectTimeout;
111 }
112
113
114
115
116 public int getWriteTimeout()
117 {
118 return _writeTimeout;
119 }
120
121
122
123
124 public void setWriteTimeout(int writeTimeout)
125 {
126 _writeTimeout = writeTimeout;
127 }
128
129 @Override
130 public void setServer(Server server)
131 {
132 super.setServer(server);
133
134 server.getContainer().update(this, null, _selectorManager, "selectManager");
135
136 if (_privateThreadPool)
137 server.getContainer().update(this, null, _privateThreadPool, "threadpool", true);
138 else
139 _threadPool = server.getThreadPool();
140 }
141
142
143
144
145 public ThreadPool getThreadPool()
146 {
147 return _threadPool;
148 }
149
150
151
152
153 public void setThreadPool(ThreadPool threadPool)
154 {
155 if (getServer() != null)
156 getServer().getContainer().update(this, _privateThreadPool ? _threadPool : null, threadPool, "threadpool", true);
157 _privateThreadPool = threadPool != null;
158 _threadPool = threadPool;
159 }
160
161 @Override
162 protected void doStart() throws Exception
163 {
164 super.doStart();
165
166 if (_threadPool == null)
167 {
168 _threadPool = getServer().getThreadPool();
169 _privateThreadPool = false;
170 }
171 if (_threadPool instanceof LifeCycle && !((LifeCycle)_threadPool).isRunning())
172 ((LifeCycle)_threadPool).start();
173
174 _selectorManager.start();
175 }
176
177 @Override
178 protected void doStop() throws Exception
179 {
180 _selectorManager.stop();
181
182 ThreadPool threadPool = _threadPool;
183 if (_privateThreadPool && _threadPool != null && threadPool instanceof LifeCycle)
184 ((LifeCycle)threadPool).stop();
185
186 super.doStop();
187 }
188
189 @Override
190 public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
191 {
192 if (HttpMethods.CONNECT.equalsIgnoreCase(request.getMethod()))
193 {
194 LOG.debug("CONNECT request for {}", request.getRequestURI());
195 try
196 {
197 handleConnect(baseRequest, request, response, request.getRequestURI());
198 }
199 catch(Exception e)
200 {
201 LOG.warn("ConnectHandler "+baseRequest.getUri()+" "+ e);
202 LOG.debug(e);
203 }
204 }
205 else
206 {
207 super.handle(target, baseRequest, request, response);
208 }
209 }
210
211
212
213
214
215
216
217
218
219
220
221
222
223 protected void handleConnect(Request baseRequest, HttpServletRequest request, HttpServletResponse response, String serverAddress) throws ServletException, IOException
224 {
225 boolean proceed = handleAuthentication(request, response, serverAddress);
226 if (!proceed)
227 return;
228
229 String host = serverAddress;
230 int port = 80;
231 int colon = serverAddress.indexOf(':');
232 if (colon > 0)
233 {
234 host = serverAddress.substring(0, colon);
235 port = Integer.parseInt(serverAddress.substring(colon + 1));
236 }
237
238 if (!validateDestination(host))
239 {
240 LOG.info("ProxyHandler: Forbidden destination " + host);
241 response.setStatus(HttpServletResponse.SC_FORBIDDEN);
242 baseRequest.setHandled(true);
243 return;
244 }
245
246 SocketChannel channel;
247
248 try
249 {
250 channel = connectToServer(request,host,port);
251 }
252 catch (SocketException se)
253 {
254 LOG.info("ConnectHandler: SocketException " + se.getMessage());
255 response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
256 baseRequest.setHandled(true);
257 return;
258 }
259 catch (SocketTimeoutException ste)
260 {
261 LOG.info("ConnectHandler: SocketTimeoutException" + ste.getMessage());
262 response.setStatus(HttpServletResponse.SC_GATEWAY_TIMEOUT);
263 baseRequest.setHandled(true);
264 return;
265 }
266 catch (IOException ioe)
267 {
268 LOG.info("ConnectHandler: IOException" + ioe.getMessage());
269 response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
270 baseRequest.setHandled(true);
271 return;
272 }
273
274
275
276
277
278
279 AbstractHttpConnection httpConnection = AbstractHttpConnection.getCurrentConnection();
280 Buffer headerBuffer = ((HttpParser)httpConnection.getParser()).getHeaderBuffer();
281 Buffer bodyBuffer = ((HttpParser)httpConnection.getParser()).getBodyBuffer();
282 int length = headerBuffer == null ? 0 : headerBuffer.length();
283 length += bodyBuffer == null ? 0 : bodyBuffer.length();
284 IndirectNIOBuffer buffer = null;
285 if (length > 0)
286 {
287 buffer = new IndirectNIOBuffer(length);
288 if (headerBuffer != null)
289 {
290 buffer.put(headerBuffer);
291 headerBuffer.clear();
292 }
293 if (bodyBuffer != null)
294 {
295 buffer.put(bodyBuffer);
296 bodyBuffer.clear();
297 }
298 }
299
300 ConcurrentMap<String, Object> context = new ConcurrentHashMap<String, Object>();
301 prepareContext(request, context);
302
303 ClientToProxyConnection clientToProxy = prepareConnections(context, channel, buffer);
304
305
306 response.setStatus(HttpServletResponse.SC_OK);
307
308
309 baseRequest.getConnection().getGenerator().setPersistent(true);
310
311
312 response.getOutputStream().close();
313
314 upgradeConnection(request, response, clientToProxy);
315 }
316
317 private ClientToProxyConnection prepareConnections(ConcurrentMap<String, Object> context, SocketChannel channel, Buffer buffer)
318 {
319 AbstractHttpConnection httpConnection = AbstractHttpConnection.getCurrentConnection();
320 ProxyToServerConnection proxyToServer = newProxyToServerConnection(context, buffer);
321 ClientToProxyConnection clientToProxy = newClientToProxyConnection(context, channel, httpConnection.getEndPoint(), httpConnection.getTimeStamp());
322 clientToProxy.setConnection(proxyToServer);
323 proxyToServer.setConnection(clientToProxy);
324 return clientToProxy;
325 }
326
327
328
329
330
331
332
333
334
335
336
337
338 protected boolean handleAuthentication(HttpServletRequest request, HttpServletResponse response, String address) throws ServletException, IOException
339 {
340 return true;
341 }
342
343 protected ClientToProxyConnection newClientToProxyConnection(ConcurrentMap<String, Object> context, SocketChannel channel, EndPoint endPoint, long timeStamp)
344 {
345 return new ClientToProxyConnection(context, channel, endPoint, timeStamp);
346 }
347
348 protected ProxyToServerConnection newProxyToServerConnection(ConcurrentMap<String, Object> context, Buffer buffer)
349 {
350 return new ProxyToServerConnection(context, buffer);
351 }
352
353
354 private SocketChannel connectToServer(HttpServletRequest request, String host, int port) throws IOException
355 {
356 SocketChannel channel = connect(request, host, port);
357 channel.configureBlocking(false);
358 return channel;
359 }
360
361
362
363
364
365
366
367
368
369
370 protected SocketChannel connect(HttpServletRequest request, String host, int port) throws IOException
371 {
372 SocketChannel channel = SocketChannel.open();
373
374 if (channel == null)
375 {
376 throw new IOException("unable to connect to " + host + ":" + port);
377 }
378
379 try
380 {
381
382 LOG.debug("Establishing connection to {}:{}", host, port);
383 channel.socket().setTcpNoDelay(true);
384 channel.socket().connect(new InetSocketAddress(host, port), getConnectTimeout());
385 LOG.debug("Established connection to {}:{}", host, port);
386 return channel;
387 }
388 catch (IOException x)
389 {
390 LOG.debug("Failed to establish connection to " + host + ":" + port, x);
391 try
392 {
393 channel.close();
394 }
395 catch (IOException xx)
396 {
397 LOG.ignore(xx);
398 }
399 throw x;
400 }
401 }
402
403 protected void prepareContext(HttpServletRequest request, ConcurrentMap<String, Object> context)
404 {
405 }
406
407 private void upgradeConnection(HttpServletRequest request, HttpServletResponse response, Connection connection) throws IOException
408 {
409
410
411 request.setAttribute("org.eclipse.jetty.io.Connection", connection);
412 response.setStatus(HttpServletResponse.SC_SWITCHING_PROTOCOLS);
413 LOG.debug("Upgraded connection to {}", connection);
414 }
415
416 private void register(SocketChannel channel, ProxyToServerConnection proxyToServer) throws IOException
417 {
418 _selectorManager.register(channel, proxyToServer);
419 proxyToServer.waitReady(_connectTimeout);
420 }
421
422
423
424
425
426
427
428
429
430
431
432 protected int read(EndPoint endPoint, Buffer buffer, ConcurrentMap<String, Object> context) throws IOException
433 {
434 return endPoint.fill(buffer);
435 }
436
437
438
439
440
441
442
443
444
445
446 protected int write(EndPoint endPoint, Buffer buffer, ConcurrentMap<String, Object> context) throws IOException
447 {
448 if (buffer == null)
449 return 0;
450
451 int length = buffer.length();
452 final StringBuilder debug = LOG.isDebugEnabled()?new StringBuilder():null;
453 int flushed = endPoint.flush(buffer);
454 if (debug!=null)
455 debug.append(flushed);
456
457
458 while (buffer.length()>0 && !endPoint.isOutputShutdown())
459 {
460 if (!endPoint.isBlocking())
461 {
462 boolean ready = endPoint.blockWritable(getWriteTimeout());
463 if (!ready)
464 throw new IOException("Write timeout");
465 }
466 flushed = endPoint.flush(buffer);
467 if (debug!=null)
468 debug.append("+").append(flushed);
469 }
470
471 LOG.debug("Written {}/{} bytes {}", debug, length, endPoint);
472 buffer.compact();
473 return length;
474 }
475
476 private class Manager extends SelectorManager
477 {
478 @Override
479 protected SelectChannelEndPoint newEndPoint(SocketChannel channel, SelectSet selectSet, SelectionKey key) throws IOException
480 {
481 SelectChannelEndPoint endp = new SelectChannelEndPoint(channel, selectSet, key, channel.socket().getSoTimeout());
482 endp.setConnection(selectSet.getManager().newConnection(channel,endp, key.attachment()));
483 endp.setMaxIdleTime(_writeTimeout);
484 return endp;
485 }
486
487 @Override
488 public AsyncConnection newConnection(SocketChannel channel, AsyncEndPoint endpoint, Object attachment)
489 {
490 ProxyToServerConnection proxyToServer = (ProxyToServerConnection)attachment;
491 proxyToServer.setTimeStamp(System.currentTimeMillis());
492 proxyToServer.setEndPoint(endpoint);
493 return proxyToServer;
494 }
495
496 @Override
497 protected void endPointOpened(SelectChannelEndPoint endpoint)
498 {
499 ProxyToServerConnection proxyToServer = (ProxyToServerConnection)endpoint.getSelectionKey().attachment();
500 proxyToServer.ready();
501 }
502
503 @Override
504 public boolean dispatch(Runnable task)
505 {
506 return _threadPool.dispatch(task);
507 }
508
509 @Override
510 protected void endPointClosed(SelectChannelEndPoint endpoint)
511 {
512 }
513
514 @Override
515 protected void endPointUpgraded(ConnectedEndPoint endpoint, Connection oldConnection)
516 {
517 }
518 }
519
520
521
522 public class ProxyToServerConnection implements AsyncConnection
523 {
524 private final CountDownLatch _ready = new CountDownLatch(1);
525 private final Buffer _buffer = new IndirectNIOBuffer(4096);
526 private final ConcurrentMap<String, Object> _context;
527 private volatile Buffer _data;
528 private volatile ClientToProxyConnection _toClient;
529 private volatile long _timestamp;
530 private volatile AsyncEndPoint _endPoint;
531
532 public ProxyToServerConnection(ConcurrentMap<String, Object> context, Buffer data)
533 {
534 _context = context;
535 _data = data;
536 }
537
538 @Override
539 public String toString()
540 {
541 StringBuilder builder = new StringBuilder("ProxyToServer");
542 builder.append("(:").append(_endPoint.getLocalPort());
543 builder.append("<=>:").append(_endPoint.getRemotePort());
544 return builder.append(")").toString();
545 }
546
547 public Connection handle() throws IOException
548 {
549 LOG.debug("{}: begin reading from server", this);
550 try
551 {
552 writeData();
553
554 while (true)
555 {
556 int read = read(_endPoint, _buffer, _context);
557
558 if (read == -1)
559 {
560 LOG.debug("{}: server closed connection {}", this, _endPoint);
561
562 if (_endPoint.isOutputShutdown() || !_endPoint.isOpen())
563 closeClient();
564 else
565 _toClient.shutdownOutput();
566
567 break;
568 }
569
570 if (read == 0)
571 break;
572
573 LOG.debug("{}: read from server {} bytes {}", this, read, _endPoint);
574 int written = write(_toClient._endPoint, _buffer, _context);
575 LOG.debug("{}: written to {} {} bytes", this, _toClient, written);
576 }
577 return this;
578 }
579 catch (ClosedChannelException x)
580 {
581 LOG.debug(x);
582 throw x;
583 }
584 catch (IOException x)
585 {
586 LOG.warn(this + ": unexpected exception", x);
587 close();
588 throw x;
589 }
590 catch (RuntimeException x)
591 {
592 LOG.warn(this + ": unexpected exception", x);
593 close();
594 throw x;
595 }
596 finally
597 {
598 LOG.debug("{}: end reading from server", this);
599 }
600 }
601
602 public void onInputShutdown() throws IOException
603 {
604
605 }
606
607 private void writeData() throws IOException
608 {
609
610
611
612 synchronized (this)
613 {
614 if (_data != null)
615 {
616 try
617 {
618 int written = write(_endPoint, _data, _context);
619 LOG.debug("{}: written to server {} bytes", this, written);
620 }
621 finally
622 {
623
624
625
626 _data = null;
627 }
628 }
629 }
630 }
631
632 public void setConnection(ClientToProxyConnection connection)
633 {
634 _toClient = connection;
635 }
636
637 public long getTimeStamp()
638 {
639 return _timestamp;
640 }
641
642 public void setTimeStamp(long timestamp)
643 {
644 _timestamp = timestamp;
645 }
646
647 public void setEndPoint(AsyncEndPoint endpoint)
648 {
649 _endPoint = endpoint;
650 }
651
652 public boolean isIdle()
653 {
654 return false;
655 }
656
657 public boolean isSuspended()
658 {
659 return false;
660 }
661
662 public void onClose()
663 {
664 }
665
666 public void ready()
667 {
668 _ready.countDown();
669 }
670
671 public void waitReady(long timeout) throws IOException
672 {
673 try
674 {
675 _ready.await(timeout, TimeUnit.MILLISECONDS);
676 }
677 catch (final InterruptedException x)
678 {
679 throw new IOException()
680 {{
681 initCause(x);
682 }};
683 }
684 }
685
686 public void closeClient() throws IOException
687 {
688 _toClient.closeClient();
689 }
690
691 public void closeServer() throws IOException
692 {
693 _endPoint.close();
694 }
695
696 public void close()
697 {
698 try
699 {
700 closeClient();
701 }
702 catch (IOException x)
703 {
704 LOG.debug(this + ": unexpected exception closing the client", x);
705 }
706
707 try
708 {
709 closeServer();
710 }
711 catch (IOException x)
712 {
713 LOG.debug(this + ": unexpected exception closing the server", x);
714 }
715 }
716
717 public void shutdownOutput() throws IOException
718 {
719 writeData();
720 _endPoint.shutdownOutput();
721 }
722
723 public void onIdleExpired(long idleForMs)
724 {
725 try
726 {
727 shutdownOutput();
728 }
729 catch(Exception e)
730 {
731 LOG.debug(e);
732 close();
733 }
734 }
735 }
736
737 public class ClientToProxyConnection implements AsyncConnection
738 {
739 private final Buffer _buffer = new IndirectNIOBuffer(4096);
740 private final ConcurrentMap<String, Object> _context;
741 private final SocketChannel _channel;
742 private final EndPoint _endPoint;
743 private final long _timestamp;
744 private volatile ProxyToServerConnection _toServer;
745 private boolean _firstTime = true;
746
747 public ClientToProxyConnection(ConcurrentMap<String, Object> context, SocketChannel channel, EndPoint endPoint, long timestamp)
748 {
749 _context = context;
750 _channel = channel;
751 _endPoint = endPoint;
752 _timestamp = timestamp;
753 }
754
755 @Override
756 public String toString()
757 {
758 StringBuilder builder = new StringBuilder("ClientToProxy");
759 builder.append("(:").append(_endPoint.getLocalPort());
760 builder.append("<=>:").append(_endPoint.getRemotePort());
761 return builder.append(")").toString();
762 }
763
764 public Connection handle() throws IOException
765 {
766 LOG.debug("{}: begin reading from client", this);
767 try
768 {
769 if (_firstTime)
770 {
771 _firstTime = false;
772 register(_channel, _toServer);
773 LOG.debug("{}: registered channel {} with connection {}", this, _channel, _toServer);
774 }
775
776 while (true)
777 {
778 int read = read(_endPoint, _buffer, _context);
779
780 if (read == -1)
781 {
782 LOG.debug("{}: client closed connection {}", this, _endPoint);
783
784 if (_endPoint.isOutputShutdown() || !_endPoint.isOpen())
785 closeServer();
786 else
787 _toServer.shutdownOutput();
788
789 break;
790 }
791
792 if (read == 0)
793 break;
794
795 LOG.debug("{}: read from client {} bytes {}", this, read, _endPoint);
796 int written = write(_toServer._endPoint, _buffer, _context);
797 LOG.debug("{}: written to {} {} bytes", this, _toServer, written);
798 }
799 return this;
800 }
801 catch (ClosedChannelException x)
802 {
803 LOG.debug(x);
804 closeServer();
805 throw x;
806 }
807 catch (IOException x)
808 {
809 LOG.warn(this + ": unexpected exception", x);
810 close();
811 throw x;
812 }
813 catch (RuntimeException x)
814 {
815 LOG.warn(this + ": unexpected exception", x);
816 close();
817 throw x;
818 }
819 finally
820 {
821 LOG.debug("{}: end reading from client", this);
822 }
823 }
824
825 public void onInputShutdown() throws IOException
826 {
827
828 }
829
830 public long getTimeStamp()
831 {
832 return _timestamp;
833 }
834
835 public boolean isIdle()
836 {
837 return false;
838 }
839
840 public boolean isSuspended()
841 {
842 return false;
843 }
844
845 public void onClose()
846 {
847 }
848
849 public void setConnection(ProxyToServerConnection connection)
850 {
851 _toServer = connection;
852 }
853
854 public void closeClient() throws IOException
855 {
856 _endPoint.close();
857 }
858
859 public void closeServer() throws IOException
860 {
861 _toServer.closeServer();
862 }
863
864 public void close()
865 {
866 try
867 {
868 closeClient();
869 }
870 catch (IOException x)
871 {
872 LOG.debug(this + ": unexpected exception closing the client", x);
873 }
874
875 try
876 {
877 closeServer();
878 }
879 catch (IOException x)
880 {
881 LOG.debug(this + ": unexpected exception closing the server", x);
882 }
883 }
884
885 public void shutdownOutput() throws IOException
886 {
887 _endPoint.shutdownOutput();
888 }
889
890 public void onIdleExpired(long idleForMs)
891 {
892 try
893 {
894 shutdownOutput();
895 }
896 catch(Exception e)
897 {
898 LOG.debug(e);
899 close();
900 }
901 }
902 }
903
904
905
906
907
908
909 public void addWhite(String entry)
910 {
911 add(entry, _white);
912 }
913
914
915
916
917
918
919 public void addBlack(String entry)
920 {
921 add(entry, _black);
922 }
923
924
925
926
927
928
929 public void setWhite(String[] entries)
930 {
931 set(entries, _white);
932 }
933
934
935
936
937
938
939 public void setBlack(String[] entries)
940 {
941 set(entries, _black);
942 }
943
944
945
946
947
948
949
950
951 protected void set(String[] entries, HostMap<String> hostMap)
952 {
953 hostMap.clear();
954
955 if (entries != null && entries.length > 0)
956 {
957 for (String addrPath : entries)
958 {
959 add(addrPath, hostMap);
960 }
961 }
962 }
963
964
965
966
967
968
969
970
971 private void add(String entry, HostMap<String> hostMap)
972 {
973 if (entry != null && entry.length() > 0)
974 {
975 entry = entry.trim();
976 if (hostMap.get(entry) == null)
977 {
978 hostMap.put(entry, entry);
979 }
980 }
981 }
982
983
984
985
986
987
988
989 public boolean validateDestination(String host)
990 {
991 if (_white.size() > 0)
992 {
993 Object whiteObj = _white.getLazyMatches(host);
994 if (whiteObj == null)
995 {
996 return false;
997 }
998 }
999
1000 if (_black.size() > 0)
1001 {
1002 Object blackObj = _black.getLazyMatches(host);
1003 if (blackObj != null)
1004 {
1005 return false;
1006 }
1007 }
1008
1009 return true;
1010 }
1011
1012 @Override
1013 public void dump(Appendable out, String indent) throws IOException
1014 {
1015 dumpThis(out);
1016 if (_privateThreadPool)
1017 dump(out, indent, Arrays.asList(_threadPool, _selectorManager), TypeUtil.asList(getHandlers()), getBeans());
1018 else
1019 dump(out, indent, Arrays.asList(_selectorManager), TypeUtil.asList(getHandlers()), getBeans());
1020 }
1021 }