View Javadoc
1   /*
2    * This file is part of Waarp Project (named also Waarp or GG).
3    *
4    *  Copyright (c) 2019, Waarp SAS, and individual contributors by the @author
5    *  tags. See the COPYRIGHT.txt in the distribution for a full listing of
6    * individual contributors.
7    *
8    *  All Waarp Project is free software: you can redistribute it and/or
9    * modify it under the terms of the GNU General Public License as published by
10   * the Free Software Foundation, either version 3 of the License, or (at your
11   * option) any later version.
12   *
13   * Waarp is distributed in the hope that it will be useful, but WITHOUT ANY
14   * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
15   * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
16   *
17   *  You should have received a copy of the GNU General Public License along with
18   * Waarp . If not, see <http://www.gnu.org/licenses/>.
19   */
20  package org.waarp.openr66.proxy.network;
21  
22  import io.netty.bootstrap.Bootstrap;
23  import io.netty.channel.Channel;
24  import io.netty.channel.ChannelFuture;
25  import io.netty.channel.ChannelPipelineException;
26  import io.netty.channel.group.ChannelGroup;
27  import io.netty.channel.group.DefaultChannelGroup;
28  import org.waarp.common.crypto.ssl.WaarpSslUtility;
29  import org.waarp.common.logging.SysErrLogger;
30  import org.waarp.common.logging.WaarpLogger;
31  import org.waarp.common.logging.WaarpLoggerFactory;
32  import org.waarp.common.utility.WaarpNettyUtil;
33  import org.waarp.common.utility.WaarpShutdownHook;
34  import org.waarp.common.utility.WaarpSystemUtil;
35  import org.waarp.openr66.protocol.exception.OpenR66Exception;
36  import org.waarp.openr66.protocol.exception.OpenR66ProtocolNetworkException;
37  import org.waarp.openr66.protocol.exception.OpenR66ProtocolNoConnectionException;
38  import org.waarp.openr66.proxy.network.ssl.NetworkSslServerHandler;
39  import org.waarp.openr66.proxy.network.ssl.NetworkSslServerInitializerProxy;
40  
41  import java.net.ConnectException;
42  import java.net.SocketAddress;
43  
44  import static org.waarp.openr66.protocol.configuration.Configuration.*;
45  
46  /**
47   * This class handles Network Transaction connections
48   */
49  public class NetworkTransaction {
50    /**
51     * Internal Logger
52     */
53    private static final WaarpLogger logger =
54        WaarpLoggerFactory.getLogger(NetworkTransaction.class);
55  
56    private final Bootstrap clientBootstrap;
57    private final Bootstrap clientSslBootstrap;
58    private final ChannelGroup networkChannelGroup;
59  
60    public NetworkTransaction() {
61      networkChannelGroup = new DefaultChannelGroup("NetworkChannels",
62                                                    configuration.getSubTaskGroup()
63                                                                 .next());
64      final NetworkServerInitializerProxy networkServerInitializerProxy =
65          new NetworkServerInitializerProxy(false);
66      clientBootstrap = new Bootstrap();
67      WaarpNettyUtil.setBootstrap(clientBootstrap,
68                                  configuration.getNetworkWorkerGroup(),
69                                  (int) configuration.getTimeoutCon(),
70                                  configuration.getBlockSize() + 64, false);
71      clientBootstrap.handler(networkServerInitializerProxy);
72      clientSslBootstrap = new Bootstrap();
73      if (configuration.isUseSSL() && configuration.getHostSslId() != null) {
74        final NetworkSslServerInitializerProxy networkSslServerInitializerProxy =
75            new NetworkSslServerInitializerProxy(true);
76        WaarpNettyUtil.setBootstrap(clientSslBootstrap,
77                                    configuration.getNetworkWorkerGroup(),
78                                    (int) configuration.getTimeoutCon(),
79                                    configuration.getBlockSize() + 64, false);
80        clientSslBootstrap.handler(networkSslServerInitializerProxy);
81      } else {
82        if (configuration.isWarnOnStartup()) {
83          logger.warn("No SSL support configured");
84        } else {
85          logger.info("No SSL support configured");
86        }
87      }
88    }
89  
90    /**
91     * Create a connection to the specified socketAddress with multiple retries
92     *
93     * @param socketAddress
94     * @param isSSL
95     *
96     * @return the Channel
97     */
98    public final Channel createConnectionWithRetry(
99        final SocketAddress socketAddress, final boolean isSSL) {
100     Channel channel = null;
101     OpenR66Exception lastException = null;
102     for (int i = 0; i < RETRYNB; i++) {
103       try {
104         channel = createConnection(socketAddress, isSSL);
105         break;
106       } catch (final OpenR66ProtocolNoConnectionException e1) {
107         lastException = e1;
108         channel = null;
109         break;
110       } catch (final OpenR66ProtocolNetworkException e1) {
111         // Can retry
112         lastException = e1;
113         channel = null;
114         try {
115           Thread.sleep(WAITFORNETOP * 5);
116         } catch (final InterruptedException e) {//NOSONAR
117           SysErrLogger.FAKE_LOGGER.ignoreLog(e);
118           break;
119         }
120       }
121     }
122     if (channel == null) {
123       if (lastException != null) {
124         logger.info("Proxy Cannot connect : {}", lastException.getMessage());
125       } else {
126         logger.info("Proxy Cannot connect!");
127       }
128     } else if (lastException != null) {
129       logger.debug("Proxy Connection retried since {}",
130                    lastException.getMessage());
131     } else {
132       logger.info("Proxy Connected");
133     }
134     return channel;
135   }
136 
137   /**
138    * Create a connection to the specified socketAddress
139    *
140    * @param socketAddress
141    * @param isSSL
142    *
143    * @return the channel
144    *
145    * @throws OpenR66ProtocolNetworkException
146    * @throws OpenR66ProtocolNoConnectionException
147    */
148   private Channel createConnection(final SocketAddress socketAddress,
149                                    final boolean isSSL)
150       throws OpenR66ProtocolNetworkException,
151              OpenR66ProtocolNoConnectionException {
152     Channel channel = null;
153     boolean ok = false;
154     // check valid limit on server side only (could be the initiator but not a client)
155     boolean valid = false;
156     for (int i = 0; i < RETRYNB * 2; i++) {
157       if (configuration.getConstraintLimitHandler().checkConstraintsSleep(i)) {
158         logger.info("Proxy Constraints exceeded: {}", i);
159       } else {
160         logger.debug("Proxy Constraints NOT exceeded");
161         valid = true;
162         break;
163       }
164     }
165     if (!valid) {
166       // Limit is locally exceeded
167       logger.debug("Proxy Overloaded local system");
168       throw new OpenR66ProtocolNetworkException(
169           "Proxy Cannot connect to remote server due to local overload");
170     }
171     try {
172       channel = createNewConnection(socketAddress, isSSL);
173       ok = true;
174     } finally {
175       if (!ok) {
176         if (channel != null) {
177           if (channel.isOpen()) {
178             WaarpSslUtility.closingSslChannel(channel);
179           }
180           channel = null;
181         }
182       }
183     }
184     return channel;
185   }
186 
187   /**
188    * @param socketServerAddress
189    * @param isSSL
190    *
191    * @return the channel
192    *
193    * @throws OpenR66ProtocolNetworkException
194    * @throws OpenR66ProtocolNoConnectionException
195    */
196   private Channel createNewConnection(final SocketAddress socketServerAddress,
197                                       final boolean isSSL)
198       throws OpenR66ProtocolNetworkException,
199 
200              OpenR66ProtocolNoConnectionException {
201     ChannelFuture channelFuture = null;
202     for (int i = 0; i < RETRYNB; i++) {
203       try {
204         if (isSSL) {
205           if (configuration.getHostSslId() != null) {
206             channelFuture = clientSslBootstrap.connect(socketServerAddress);
207           } else {
208             throw new OpenR66ProtocolNoConnectionException("No SSL support");
209           }
210         } else {
211           channelFuture = clientBootstrap.connect(socketServerAddress);
212         }
213       } catch (final ChannelPipelineException e) {
214         throw new OpenR66ProtocolNoConnectionException(
215             "Proxy Cannot connect to remote server due to a channel exception");
216       }
217       try {
218         channelFuture.await(configuration.getTimeoutCon() / 3);
219       } catch (final InterruptedException e1) {//NOSONAR
220         SysErrLogger.FAKE_LOGGER.ignoreLog(e1);
221       }
222       if (channelFuture.isSuccess()) {
223         final Channel channel = channelFuture.channel();
224         if (isSSL && !NetworkSslServerHandler.isSslConnectedChannel(channel)) {
225           logger.info("Proxy KO CONNECT since SSL handshake is over");
226           channel.close();
227           throw new OpenR66ProtocolNoConnectionException(
228               "Proxy Cannot finish connect to remote server");
229         }
230         networkChannelGroup.add(channel);
231         return channel;
232       } else {
233         try {
234           Thread.sleep(WAITFORNETOP * 2);
235         } catch (final InterruptedException e) {//NOSONAR
236           SysErrLogger.FAKE_LOGGER.ignoreLog(e);
237         }
238         if (!channelFuture.isDone()) {
239           throw new OpenR66ProtocolNoConnectionException(
240               "Proxy Cannot connect to remote server due to interruption");
241         }
242         if (channelFuture.cause() instanceof ConnectException) {
243           logger.debug("Proxy KO CONNECT: {}",
244                        channelFuture.cause().getMessage());
245           throw new OpenR66ProtocolNoConnectionException(
246               "Proxy Cannot connect to remote server", channelFuture.cause());
247         } else {
248           logger.debug("Proxy KO CONNECT but retry", channelFuture.cause());
249         }
250       }
251     }
252     final Throwable cause = channelFuture == null? null : channelFuture.cause();
253     throw new OpenR66ProtocolNetworkException(
254         "Proxy Cannot connect to remote server", cause);
255   }
256 
257   /**
258    * Close all Network Transaction
259    */
260   public final void closeAll() {
261     logger.debug("Proxy close All Network Channels");
262     try {
263       Thread.sleep(RETRYINMS * 2);
264     } catch (final InterruptedException e) {//NOSONAR
265       SysErrLogger.FAKE_LOGGER.ignoreLog(e);
266     }
267     if (!configuration.isServer()) {
268       WaarpShutdownHook.shutdownHook.launchFinalExit();
269     }
270     for (final Channel channel : networkChannelGroup) {
271       WaarpSslUtility.closingSslChannel(channel);
272     }
273     WaarpNettyUtil.awaitOrInterrupted(networkChannelGroup.close());
274     try {
275       Thread.sleep(WAITFORNETOP);
276     } catch (final InterruptedException e) {//NOSONAR
277       SysErrLogger.FAKE_LOGGER.ignoreLog(e);
278     }
279     configuration.clientStop();
280     logger.debug("Proxy Last action before exit");
281     WaarpSystemUtil.stopLogger(false);
282   }
283 
284   /**
285    * @return The number of Network Channels
286    */
287   public final int getNumberClients() {
288     return networkChannelGroup.size();
289   }
290 }