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  
21  package org.waarp.openr66.protocol.http.restv2;
22  
23  import io.cdap.http.ChannelPipelineModifier;
24  import io.cdap.http.NettyHttpService;
25  import io.cdap.http.SSLConfig;
26  import io.netty.channel.ChannelOption;
27  import io.netty.channel.ChannelPipeline;
28  import io.netty.handler.codec.http.HttpObjectAggregator;
29  import io.netty.handler.codec.http.cors.CorsConfig;
30  import io.netty.handler.codec.http.cors.CorsConfigBuilder;
31  import io.netty.handler.codec.http.cors.CorsHandler;
32  import org.waarp.common.crypto.ssl.WaarpSecureKeyStore;
33  import org.waarp.common.logging.WaarpLogger;
34  import org.waarp.common.logging.WaarpLoggerFactory;
35  import org.waarp.gateway.kernel.rest.RestConfiguration;
36  import org.waarp.gateway.kernel.rest.RestConfiguration.CRUD;
37  import org.waarp.openr66.protocol.configuration.Configuration;
38  import org.waarp.openr66.protocol.http.restv2.dbhandlers.AbstractRestDbHandler;
39  import org.waarp.openr66.protocol.http.restv2.dbhandlers.HostConfigHandler;
40  import org.waarp.openr66.protocol.http.restv2.dbhandlers.HostIdHandler;
41  import org.waarp.openr66.protocol.http.restv2.dbhandlers.HostsHandler;
42  import org.waarp.openr66.protocol.http.restv2.dbhandlers.LimitsHandler;
43  import org.waarp.openr66.protocol.http.restv2.dbhandlers.RuleIdHandler;
44  import org.waarp.openr66.protocol.http.restv2.dbhandlers.RulesHandler;
45  import org.waarp.openr66.protocol.http.restv2.dbhandlers.ServerHandler;
46  import org.waarp.openr66.protocol.http.restv2.dbhandlers.SpooledHandler;
47  import org.waarp.openr66.protocol.http.restv2.dbhandlers.TransferIdHandler;
48  import org.waarp.openr66.protocol.http.restv2.dbhandlers.TransfersHandler;
49  import org.waarp.openr66.protocol.http.restv2.resthandlers.RestExceptionHandler;
50  import org.waarp.openr66.protocol.http.restv2.resthandlers.RestHandlerHook;
51  import org.waarp.openr66.protocol.http.restv2.resthandlers.RestSignatureHandler;
52  import org.waarp.openr66.protocol.http.restv2.resthandlers.RestVersionHandler;
53  import org.waarp.openr66.protocol.networkhandler.ssl.NetworkSslServerInitializer;
54  
55  import java.io.File;
56  import java.util.ArrayList;
57  import java.util.Collection;
58  import java.util.Collections;
59  
60  import static io.netty.handler.codec.http.HttpHeaderNames.*;
61  import static io.netty.handler.codec.http.HttpMethod.*;
62  import static org.waarp.openr66.protocol.http.rest.HttpRestR66Handler.RESTHANDLERS.*;
63  import static org.waarp.openr66.protocol.http.restv2.RestConstants.*;
64  
65  /**
66   * This class is called to initialize the RESTv2 API.
67   */
68  public final class RestServiceInitializer {
69  
70    private static final String ROUTER = "router";
71  
72    /**
73     * The logger for all unexpected errors during the service initialization.
74     */
75    private static final WaarpLogger logger =
76        WaarpLoggerFactory.getLogger(RestServiceInitializer.class);
77  
78    /**
79     * This is the {@link NettyHttpService} in charge of handling the RESTv2
80     * API.
81     */
82    private static NettyHttpService restService;
83  
84    /**
85     * This is a static class that should never be instantiated with a
86     * constructor.
87     */
88    private RestServiceInitializer() {
89      throw new UnsupportedOperationException(
90          getClass().getName() + " cannot be instantiated.");
91    }
92  
93    /**
94     * The list of all {@link AbstractRestDbHandler} used by the API.
95     */
96    public static final Collection<AbstractRestDbHandler> handlers =
97        new ArrayList<AbstractRestDbHandler>();
98  
99    /**
100    * Fills the list of {@link AbstractRestDbHandler} with all handlers
101    * activated in the API configuration.
102    *
103    * @param config The REST API configuration object.
104    */
105   private static void initHandlers(final RestConfiguration config) {
106     final byte hostsCRUD = config.getResthandlersCrud()[DbHostAuth.ordinal()];
107     final byte rulesCRUD = config.getResthandlersCrud()[DbRule.ordinal()];
108     final byte transferCRUD =
109         config.getResthandlersCrud()[DbTaskRunner.ordinal()];
110     final byte spooledCRUD = CRUD.READ.mask;
111     final byte configCRUD =
112         config.getResthandlersCrud()[DbHostConfiguration.ordinal()];
113     final byte limitCRUD = config.getResthandlersCrud()[Bandwidth.ordinal()];
114     final int serverCRUD = config.getResthandlersCrud()[Business.ordinal()] +
115                            config.getResthandlersCrud()[Config.ordinal()] +
116                            config.getResthandlersCrud()[Information.ordinal()] +
117                            config.getResthandlersCrud()[Log.ordinal()] +
118                            config.getResthandlersCrud()[Server.ordinal()] +
119                            config.getResthandlersCrud()[Control.ordinal()];
120 
121     if (hostsCRUD != 0) {
122       handlers.add(new HostsHandler(hostsCRUD));
123       handlers.add(new HostIdHandler(hostsCRUD));
124     }
125     if (rulesCRUD != 0) {
126       handlers.add(new RulesHandler(rulesCRUD));
127       handlers.add(new RuleIdHandler(rulesCRUD));
128     }
129     if (transferCRUD != 0) {
130       handlers.add(new TransfersHandler(transferCRUD));
131       handlers.add(new TransferIdHandler(transferCRUD));
132     }
133     if (configCRUD != 0) {
134       handlers.add(new HostConfigHandler(configCRUD));
135     }
136     if (limitCRUD != 0) {
137       handlers.add(new LimitsHandler(limitCRUD));
138     }
139     if (serverCRUD != 0) {
140       handlers.add(new ServerHandler(config.getResthandlersCrud()));
141     }
142     if (spooledCRUD != 0) {
143       handlers.add(new SpooledHandler(spooledCRUD));
144     }
145   }
146 
147   /**
148    * Builds and returns a {@link CorsConfig} to be used by the {@link
149    * CorsHandler} to allow the REST API to
150    * support CORS.
151    *
152    * @return The configuration used for dealing with CORS requests.
153    */
154   private static CorsConfig corsConfig() {
155     final CorsConfigBuilder builder = CorsConfigBuilder.forAnyOrigin();
156 
157     builder.exposeHeaders(ALLOW, "transferURI", "hostURI", "ruleURI");
158     builder.allowedRequestHeaders(AUTHORIZATION, AUTH_USER, AUTH_TIMESTAMP,
159                                   AUTH_SIGNATURE, CONTENT_TYPE);
160     builder.allowedRequestMethods(GET, POST, PUT, DELETE, OPTIONS);
161     builder.maxAge(600);
162     return builder.build();
163   }
164 
165   /**
166    * Initializes the RESTv2 service with the given {@link RestConfiguration}.
167    *
168    * @param config The REST API configuration object.
169    */
170   public static void initRestService(final RestConfiguration config) {
171     initHandlers(config);
172 
173     final NettyHttpService.Builder restServiceBuilder =
174         NettyHttpService.builder("R66_RESTv2").setPort(config.getRestPort())
175                         .setHost(config.getRestAddress())
176                         .setExecThreadPoolSize(20).setHttpChunkLimit(
177                             Configuration.configuration.getMaxGlobalMemory())
178                         .setChannelConfig(ChannelOption.SO_REUSEADDR, true)
179                         .setChildChannelConfig(ChannelOption.TCP_NODELAY, false)
180                         .setChildChannelConfig(ChannelOption.SO_REUSEADDR, true)
181                         .setHttpHandlers(handlers).setHandlerHooks(
182                             Collections.singleton(
183                                 new RestHandlerHook(config.isRestAuthenticated(),
184                                                     config.getHmacSha256(),
185                                                     config.getRestTimeLimit())))
186                         .setExceptionHandler(new RestExceptionHandler())
187                         .setExecThreadKeepAliveSeconds(-1L)
188                         .setChannelPipelineModifier(
189                             new ChannelPipelineModifier() {
190                               @Override
191                               public final void modify(
192                                   final ChannelPipeline channelPipeline) {
193                                 channelPipeline.addBefore(ROUTER, "aggregator",
194                                                           new HttpObjectAggregator(
195                                                               Configuration.configuration.getMaxGlobalMemory()));
196                                 channelPipeline.addBefore(ROUTER,
197                                                           RestVersionHandler.HANDLER_NAME,
198                                                           new RestVersionHandler(
199                                                               config));
200                                 channelPipeline.addBefore(
201                                     RestVersionHandler.HANDLER_NAME, "cors",
202                                     new CorsHandler(corsConfig()));
203                                 if (config.isRestAuthenticated() &&
204                                     config.isRestSignature()) {
205                                   channelPipeline.addAfter(ROUTER, "signature",
206                                                            new RestSignatureHandler(
207                                                                config.getHmacSha256()));
208                                 }
209 
210                                 // Removes the HTTP compressor which causes problems
211                                 // on systems running java6 or earlier
212                                 final double JRE_version = Double.parseDouble(
213                                     System.getProperty(
214                                         "java.specification.version"));
215                                 if (JRE_version <= 1.6) {
216                                   logger.info(
217                                       "Removed REST HTTP compressor due to incompatibility " +
218                                       "with the Java Runtime version");
219                                   channelPipeline.remove("compressor");
220                                 }
221                               }
222                             });
223 
224     if (config.isRestSsl()) {
225       final WaarpSecureKeyStore keyStore =
226           NetworkSslServerInitializer.getWaarpSecureKeyStore();
227       final String keyStoreFilename = keyStore.getKeyStoreFilename();
228       final String keyStorePass = new String(keyStore.getKeyStorePassword());
229       final String certificatePassword =
230           new String(keyStore.getCertificatePassword());
231 
232       restServiceBuilder.enableSSL(
233           SSLConfig.builder(new File(keyStoreFilename), keyStorePass)
234                    .setCertificatePassword(certificatePassword).build());
235     }
236 
237     restService = restServiceBuilder.build();
238     try {
239       restService.start();
240     } catch (final Throwable t) {
241       logger.error(t);
242       throw new ExceptionInInitializerError(
243           "FATAL ERROR_TASK : Failed to initialize RESTv2 service");
244     }
245   }
246 
247   /**
248    * Stops the REST service.
249    */
250   public static void stopRestService() {
251     if (restService != null) {
252       try {
253         restService.stop();
254       } catch (final Throwable e) {
255         logger.error("Exception caught during RESTv2 service shutdown", e);
256       }
257     } else {
258       logger.warn("Error RESTv2 service is not running, cannot stop");
259     }
260   }
261 }