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.protocol.localhandler.packet;
21  
22  import com.fasterxml.jackson.databind.node.ObjectNode;
23  import io.netty.buffer.ByteBuf;
24  import org.waarp.common.json.JsonHandler;
25  import org.waarp.common.logging.WaarpLogger;
26  import org.waarp.common.logging.WaarpLoggerFactory;
27  import org.waarp.common.utility.WaarpNettyUtil;
28  import org.waarp.common.utility.WaarpStringUtils;
29  import org.waarp.openr66.context.ErrorCode;
30  import org.waarp.openr66.protocol.configuration.Configuration;
31  import org.waarp.openr66.protocol.configuration.PartnerConfiguration;
32  import org.waarp.openr66.protocol.exception.OpenR66ProtocolPacketException;
33  import org.waarp.openr66.protocol.localhandler.LocalChannelReference;
34  
35  /**
36   * Request class
37   * <p>
38   * header = "rulename MODETRANS" middle = way+"FILENAME BLOCKSIZE RANK specialId
39   * code (optional length)" end =
40   * "fileInformation"
41   * <p>
42   * or
43   * <p>
44   * header = "{rule:rulename, mode:MODETRANS}" middle = way{filename:FILENAME,
45   * block:BLOCKSIZE, rank:RANK,
46   * id:specialId, code:code, length:length}" end = "fileInformation"
47   */
48  public class RequestPacket extends AbstractLocalPacket {
49    /**
50     * Internal Logger
51     */
52    private static final WaarpLogger logger =
53        WaarpLoggerFactory.getLogger(RequestPacket.class);
54    private static final String NOT_ENOUGH_DATA = "Not enough data";
55  
56    public enum TRANSFERMODE {
57      UNKNOWNMODE, SENDMODE, RECVMODE, SENDMD5MODE, RECVMD5MODE, SENDTHROUGHMODE,
58      RECVTHROUGHMODE, SENDMD5THROUGHMODE, RECVMD5THROUGHMODE
59    }
60  
61    protected enum FIELDS {
62      rule, mode, filename, block, rank, id, code, length, limit
63    }
64  
65    protected static final byte REQVALIDATE = 0;
66  
67    protected static final byte REQANSWERVALIDATE = 1;
68  
69    protected final String rulename;
70  
71    protected final int mode;
72  
73    protected String filename;
74  
75    protected final int blocksize;
76  
77    protected int rank;
78  
79    protected long specialId;
80  
81    protected byte way;
82  
83    protected char code;
84  
85    protected long originalSize;
86  
87    protected long limit;
88  
89    protected final String transferInformation;
90  
91    protected String separator = PartnerConfiguration.getSEPARATOR_FIELD();
92  
93    /**
94     * @param mode
95     *
96     * @return the same mode (RECV or SEND) in MD5 version
97     */
98    public static int getModeMD5(final int mode) {
99      switch (mode) {
100       case 1:
101       case 2:
102       case 5:
103       case 6:
104         return mode + 2;
105       default:
106         // nothing
107     }
108     return mode;
109   }
110 
111   /**
112    * @param mode
113    *
114    * @return true if this mode is a RECV(MD5) mode
115    */
116   public static boolean isRecvMode(final int mode) {
117     return mode == TRANSFERMODE.RECVMODE.ordinal() ||
118            mode == TRANSFERMODE.RECVMD5MODE.ordinal() ||
119            mode == TRANSFERMODE.RECVTHROUGHMODE.ordinal() ||
120            mode == TRANSFERMODE.RECVMD5THROUGHMODE.ordinal();
121   }
122 
123   /**
124    * @param mode
125    * @param isRequested
126    *
127    * @return True if this mode is a THROUGH (MD5) mode
128    */
129   public static boolean isSendThroughMode(final int mode,
130                                           final boolean isRequested) {
131     return !isRequested && isSendThroughMode(mode) ||
132            isRequested && isRecvThroughMode(mode);
133   }
134 
135   /**
136    * @param mode
137    *
138    * @return True if this mode is a SEND THROUGH (MD5) mode
139    */
140   public static boolean isSendThroughMode(final int mode) {
141     return mode == TRANSFERMODE.SENDTHROUGHMODE.ordinal() ||
142            mode == TRANSFERMODE.SENDMD5THROUGHMODE.ordinal();
143   }
144 
145   /**
146    * @param mode
147    * @param isRequested
148    *
149    * @return True if this mode is a THROUGH (MD5) mode
150    */
151   public static boolean isRecvThroughMode(final int mode,
152                                           final boolean isRequested) {
153     return !isRequested && isRecvThroughMode(mode) ||
154            isRequested && isSendThroughMode(mode);
155   }
156 
157   /**
158    * @param mode
159    *
160    * @return True if this mode is a RECV THROUGH (MD5) mode
161    */
162   public static boolean isRecvThroughMode(final int mode) {
163     return mode == TRANSFERMODE.RECVTHROUGHMODE.ordinal() ||
164            mode == TRANSFERMODE.RECVMD5THROUGHMODE.ordinal();
165   }
166 
167   public static boolean isSendMode(final int mode) {
168     return !isRecvMode(mode);
169   }
170 
171   /**
172    * @param mode
173    *
174    * @return True if this mode is a THROUGH mode (with or without MD5)
175    */
176   public static boolean isThroughMode(final int mode) {
177     return mode >= TRANSFERMODE.SENDTHROUGHMODE.ordinal() &&
178            mode <= TRANSFERMODE.RECVMD5THROUGHMODE.ordinal();
179   }
180 
181   /**
182    * @param mode
183    *
184    * @return true if this mode is a MD5 mode
185    */
186   public static boolean isMD5Mode(final int mode) {
187     return mode == TRANSFERMODE.RECVMD5MODE.ordinal() ||
188            mode == TRANSFERMODE.SENDMD5MODE.ordinal() ||
189            mode == TRANSFERMODE.SENDMD5THROUGHMODE.ordinal() ||
190            mode == TRANSFERMODE.RECVMD5THROUGHMODE.ordinal();
191   }
192 
193   /**
194    * @param mode1
195    * @param mode2
196    *
197    * @return true if both modes are compatible (both send, or both recv)
198    */
199   public static boolean isCompatibleMode(final int mode1, final int mode2) {
200     return isRecvMode(mode1) && isRecvMode(mode2) ||
201            !isRecvMode(mode1) && !isRecvMode(mode2);
202   }
203 
204   /**
205    * @param headerLength
206    * @param middleLength
207    * @param endLength
208    * @param buf
209    *
210    * @return the new RequestPacket from buffer
211    *
212    * @throws OpenR66ProtocolPacketException
213    */
214   public static RequestPacket createFromBuffer(final int headerLength,
215                                                final int middleLength,
216                                                final int endLength,
217                                                final ByteBuf buf)
218       throws OpenR66ProtocolPacketException {
219     if (headerLength - 1 <= 0) {
220       throw new OpenR66ProtocolPacketException(NOT_ENOUGH_DATA);
221     }
222     if (middleLength <= 1) {
223       throw new OpenR66ProtocolPacketException(NOT_ENOUGH_DATA);
224     }
225     final byte[] bheader = new byte[headerLength - 1];
226     final byte[] bmiddle = new byte[middleLength - 1];// valid is not in bmiddle
227     final byte[] bend = new byte[endLength];
228     buf.readBytes(bheader);
229     final byte valid = buf.readByte();
230     buf.readBytes(bmiddle);
231     if (endLength > 0) {
232       buf.readBytes(bend);
233     }
234     final String sheader = new String(bheader, WaarpStringUtils.UTF8);
235     final String smiddle = new String(bmiddle, WaarpStringUtils.UTF8);
236     final String send = new String(bend, WaarpStringUtils.UTF8);
237 
238     // check if JSON on header since it will directly starts with a JSON, in contrary to middle
239     if (sheader.startsWith(PartnerConfiguration.BAR_JSON_FIELD)) {
240       // JSON
241       logger.debug("Request is using JSON");
242       final ObjectNode map = JsonHandler.getFromString(sheader);
243       final ObjectNode map2 = JsonHandler.getFromString(smiddle);
244       return new RequestPacket(map.path(FIELDS.rule.name()).asText(),
245                                map.path(FIELDS.mode.name()).asInt(),
246                                map2.path(FIELDS.filename.name()).asText(),
247                                map2.path(FIELDS.block.name()).asInt(),
248                                map2.path(FIELDS.rank.name()).asInt(),
249                                map2.path(FIELDS.id.name()).asLong(), valid,
250                                send,
251                                (char) map2.path(FIELDS.code.name()).asInt(),
252                                map2.path(FIELDS.length.name()).asLong(),
253                                // Get speed if it exists if not speed is set to 0
254                                map2.path(FIELDS.limit.name()).asLong(0),
255                                PartnerConfiguration.BAR_JSON_FIELD);
256     }
257 
258     final String[] aheader =
259         sheader.split(PartnerConfiguration.BLANK_SEPARATOR_FIELD);
260     if (aheader.length != 2) {
261       throw new OpenR66ProtocolPacketException(NOT_ENOUGH_DATA);
262     }
263     // FIX to check both ' ' and SEPARATOR_FIELD
264     String[] amiddle = smiddle.split(PartnerConfiguration.BAR_SEPARATOR_FIELD);
265     String sep = PartnerConfiguration.BAR_SEPARATOR_FIELD;
266     if (amiddle.length < 5) {
267       amiddle = smiddle.split(PartnerConfiguration.BLANK_SEPARATOR_FIELD);
268       sep = PartnerConfiguration.BLANK_SEPARATOR_FIELD;
269       if (amiddle.length < 5) {
270         throw new OpenR66ProtocolPacketException(NOT_ENOUGH_DATA);
271       }
272     }
273     int blocksize = Integer.parseInt(amiddle[1]);
274     if (blocksize < 100) {
275       blocksize = Configuration.configuration.getBlockSize();
276     }
277     final int rank = Integer.parseInt(amiddle[2]);
278     final long specialId = Long.parseLong(amiddle[3]);
279     final char code = amiddle[4].charAt(0);
280     long originalSize = -1;
281     if (amiddle.length > 5) {
282       originalSize = Long.parseLong(amiddle[5]);
283     }
284     return new RequestPacket(aheader[0], Integer.parseInt(aheader[1]),
285                              amiddle[0], blocksize, rank, specialId, valid,
286                              send, code, originalSize, sep);
287   }
288 
289   /**
290    * @param rulename
291    * @param mode
292    * @param filename
293    * @param blocksize
294    * @param rank
295    * @param specialId
296    * @param valid
297    * @param transferInformation
298    * @param code
299    * @param originalSize
300    */
301   private RequestPacket(final String rulename, final int mode,
302                         final String filename, final int blocksize,
303                         final int rank, final long specialId, final byte valid,
304                         final String transferInformation, final char code,
305                         final long originalSize, final String separator) {
306     this.rulename = rulename;
307     this.mode = mode;
308     this.filename = filename;
309     if (blocksize < 100) {
310       this.blocksize = Configuration.configuration.getBlockSize();
311     } else {
312       this.blocksize = blocksize;
313     }
314     this.rank = rank;
315     this.specialId = specialId;
316     way = valid;
317     this.transferInformation = transferInformation;
318     this.code = code;
319     this.originalSize = originalSize;
320     this.separator = separator;
321   }
322 
323   /**
324    * @param rulename
325    * @param mode
326    * @param filename
327    * @param blocksize
328    * @param rank
329    * @param specialId
330    * @param transferInformation
331    */
332   public RequestPacket(final String rulename, final int mode,
333                        final String filename, final int blocksize,
334                        final int rank, final long specialId,
335                        final String transferInformation,
336                        final long originalSize, final String separator) {
337     this(rulename, mode, filename, blocksize, rank, specialId, REQVALIDATE,
338          transferInformation, ErrorCode.InitOk.code, originalSize, separator);
339   }
340 
341   /**
342    * Create a Request packet with a speed negociation
343    */
344   private RequestPacket(final String rulename, final int mode,
345                         final String filename, final int blocksize,
346                         final int rank, final long specialId, final byte valid,
347                         final String transferInformation, final char code,
348                         final long originalSize, final long limit,
349                         final String separator) {
350     this(rulename, mode, filename, blocksize, rank, specialId, valid,
351          transferInformation, code, originalSize, separator);
352     this.limit = limit;
353   }
354 
355   @Override
356   public final boolean hasGlobalBuffer() {
357     return false;
358   }
359 
360   @Override
361   public final void createAllBuffers(final LocalChannelReference lcr,
362                                      final int networkHeader) {
363     throw new IllegalStateException("Should not be called");
364   }
365 
366   @Override
367   public final synchronized void createEnd(final LocalChannelReference lcr) {
368     if (transferInformation != null) {
369       end = WaarpNettyUtil.wrappedBuffer(
370           transferInformation.getBytes(WaarpStringUtils.UTF8));
371     }
372   }
373 
374   @Override
375   public final synchronized void createHeader(final LocalChannelReference lcr)
376       throws OpenR66ProtocolPacketException {
377     if (rulename == null || mode <= 0) {
378       throw new OpenR66ProtocolPacketException(NOT_ENOUGH_DATA);
379     }
380     if (lcr.getPartner() != null && lcr.getPartner().useJson()) {
381       logger.debug("Request will use JSON {}", lcr.getPartner());
382       final ObjectNode node = JsonHandler.createObjectNode();
383       JsonHandler.setValue(node, FIELDS.rule, rulename);
384       JsonHandler.setValue(node, FIELDS.mode, mode);
385       header = WaarpNettyUtil.wrappedBuffer(
386           JsonHandler.writeAsString(node).getBytes(WaarpStringUtils.UTF8));
387     } else {
388       header =
389           WaarpNettyUtil.wrappedBuffer(rulename.getBytes(WaarpStringUtils.UTF8),
390                                        PartnerConfiguration.BLANK_SEPARATOR_FIELD.getBytes(
391                                            WaarpStringUtils.UTF8),
392                                        Integer.toString(mode)
393                                               .getBytes(WaarpStringUtils.UTF8));
394     }
395   }
396 
397   @Override
398   public final synchronized void createMiddle(final LocalChannelReference lcr)
399       throws OpenR66ProtocolPacketException {
400     if (filename == null) {
401       throw new OpenR66ProtocolPacketException(NOT_ENOUGH_DATA);
402     }
403     final byte[] away = { way };
404     if (lcr.getPartner() != null && lcr.getPartner().useJson()) {
405       logger.debug("Request {} will use JSON {}", specialId, lcr.getPartner());
406       final ObjectNode node = JsonHandler.createObjectNode();
407       JsonHandler.setValue(node, FIELDS.filename, filename);
408       JsonHandler.setValue(node, FIELDS.block, blocksize);
409       JsonHandler.setValue(node, FIELDS.rank, rank);
410       JsonHandler.setValue(node, FIELDS.id, specialId);
411       JsonHandler.setValue(node, FIELDS.code, code);
412       JsonHandler.setValue(node, FIELDS.length, originalSize);
413       // Add limit if specified
414       JsonHandler.setValue(node, FIELDS.limit, limit);
415       middle = WaarpNettyUtil.wrappedBuffer(away,
416                                             JsonHandler.writeAsString(node)
417                                                        .getBytes(
418                                                            WaarpStringUtils.UTF8));
419     } else {
420       middle = WaarpNettyUtil.wrappedBuffer(away, filename.getBytes(
421                                                 WaarpStringUtils.UTF8), separator.getBytes(WaarpStringUtils.UTF8),
422                                             Integer.toString(blocksize)
423                                                    .getBytes(
424                                                        WaarpStringUtils.UTF8),
425                                             separator.getBytes(
426                                                 WaarpStringUtils.UTF8),
427                                             Integer.toString(rank).getBytes(
428                                                 WaarpStringUtils.UTF8),
429                                             separator.getBytes(
430                                                 WaarpStringUtils.UTF8),
431                                             Long.toString(specialId).getBytes(
432                                                 WaarpStringUtils.UTF8),
433                                             separator.getBytes(
434                                                 WaarpStringUtils.UTF8),
435                                             Character.toString(code).getBytes(
436                                                 WaarpStringUtils.UTF8),
437                                             separator.getBytes(
438                                                 WaarpStringUtils.UTF8),
439                                             Long.toString(originalSize)
440                                                 .getBytes(
441                                                     WaarpStringUtils.UTF8));
442     }
443   }
444 
445   @Override
446   public final byte getType() {
447     return LocalPacketFactory.REQUESTPACKET;
448   }
449 
450   @Override
451   public final String toString() {
452     return "RequestPacket: " + specialId + " : " + rulename + " : " + mode +
453            " :  " + filename + " : " + transferInformation + " : " + blocksize +
454            " : " + rank + " : " + way + " : " + code + " : " + originalSize +
455            " : " + limit;
456   }
457 
458   /**
459    * @return the rulename
460    */
461   public final String getRulename() {
462     return rulename;
463   }
464 
465   /**
466    * @return the filename
467    */
468   public final String getFilename() {
469     return filename;
470   }
471 
472   /**
473    * @return the mode
474    */
475   public final int getMode() {
476     return mode;
477   }
478 
479   /**
480    * @return True if this packet concerns a Retrieve operation
481    */
482   public final boolean isRetrieve() {
483     return isRecvMode(mode);
484   }
485 
486   /**
487    * @return the fifinal Information
488    */
489   public final String getTransferInformation() {
490     return transferInformation;
491   }
492 
493   /**
494    * @return the blocksize
495    */
496   public final int getBlocksize() {
497     return blocksize;
498   }
499 
500   /**
501    * @return the rank
502    */
503   public final int getRank() {
504     return rank;
505   }
506 
507   /**
508    * @param rank the rank to set
509    */
510   public final void setRank(final int rank) {
511     this.rank = rank;
512   }
513 
514   /**
515    * @return the originalSize
516    */
517   public final long getOriginalSize() {
518     return originalSize;
519   }
520 
521   /**
522    * @param originalSize the originalSize to set
523    */
524   public final void setOriginalSize(final long originalSize) {
525     this.originalSize = originalSize;
526   }
527 
528   /**
529    * @param specialId the specialId to set
530    */
531   public final void setSpecialId(final long specialId) {
532     this.specialId = specialId;
533   }
534 
535   /**
536    * @return the specialId
537    */
538   public final long getSpecialId() {
539     return specialId;
540   }
541 
542   /**
543    * @return True if this packet is to be validated
544    */
545   public final boolean isToValidate() {
546     return way == REQVALIDATE;
547   }
548 
549   /**
550    * Validate the request
551    */
552   public final synchronized void validate() {
553     way = REQANSWERVALIDATE;
554     middle = null;
555   }
556 
557   /**
558    * @param filename the filename to set
559    */
560   public void setFilename(final String filename) {
561     this.filename = filename;
562   }
563 
564   /**
565    * @return the code
566    */
567   public final char getCode() {
568     return code;
569   }
570 
571   /**
572    * @param code the code to set
573    */
574   public final void setCode(final char code) {
575     this.code = code;
576   }
577 
578   public final long getLimit() {
579     return limit;
580   }
581 
582   public final void setLimit(final long limit) {
583     this.limit = limit;
584   }
585 }