1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 package org.waarp.icap;
22
23 import com.google.common.io.Files;
24 import org.waarp.common.logging.WaarpLogger;
25 import org.waarp.common.logging.WaarpLoggerFactory;
26 import org.waarp.common.utility.ParametersChecker;
27 import org.waarp.common.utility.WaarpStringUtils;
28
29 import java.io.ByteArrayInputStream;
30 import java.io.Closeable;
31 import java.io.File;
32 import java.io.FileInputStream;
33 import java.io.FileNotFoundException;
34 import java.io.IOException;
35 import java.io.InputStream;
36 import java.io.OutputStream;
37 import java.io.UnsupportedEncodingException;
38 import java.net.ConnectException;
39 import java.net.Socket;
40 import java.net.SocketTimeoutException;
41 import java.net.URLEncoder;
42 import java.util.Arrays;
43 import java.util.HashMap;
44 import java.util.Map;
45
46 import static org.waarp.common.file.filesystembased.FilesystemBasedFileImpl.*;
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65 public class IcapClient implements Closeable {
66
67
68
69 public static final int DEFAULT_ICAP_PORT = 1344;
70 private static final WaarpLogger logger =
71 WaarpLoggerFactory.getLogger(IcapClient.class);
72
73 static final int STD_RECEIVE_LENGTH = 64 * 1024;
74 static final int STD_SEND_LENGTH = 8192;
75 static final int DEFAULT_TIMEOUT = 10 * 60 * 60000;
76 public static final String VERSION = "ICAP/1.0";
77 private static final String USER_AGENT = "Waarp ICAP Client/1.0";
78 public static final String TERMINATOR = "\r\n";
79 public static final String ICAP_TERMINATOR = TERMINATOR + TERMINATOR;
80 public static final String HTTP_TERMINATOR = "0" + TERMINATOR + TERMINATOR;
81 private static final String STATUS_CODE = "StatusCode";
82 private static final String PREVIEW = "Preview";
83 private static final String OPTIONS = "OPTIONS";
84 private static final String HOST_HEADER = "Host: ";
85 private static final String USER_AGENT_HEADER = "User-Agent: ";
86 private static final String RESPMOD = "RESPMOD";
87 private static final String ENCAPSULATED_NULL_BODY =
88 "Encapsulated: null-body=0";
89 static final int MINIMAL_SIZE = 100;
90 private static final String GET_REQUEST = "GET /";
91 private static final String INCOMPATIBLE_ARGUMENT = "Incompatible argument";
92 private static final String TIMEOUT_OCCURS_WITH_THE_SERVER =
93 "Timeout occurs with the Server";
94 private static final String TIMEOUT_OCCURS_WITH_THE_SERVER_SINCE =
95 "Timeout occurs with the Server {}:{} since {}";
96 public static final String EICARTEST = "EICARTEST";
97
98
99 private final String serverIP;
100 private final int port;
101 private final String icapService;
102 private final int setPreviewSize;
103
104
105 private int receiveLength = STD_RECEIVE_LENGTH;
106 private int sendLength = STD_SEND_LENGTH;
107 private String keyIcapPreview = null;
108 private String subStringFromKeyIcapPreview = null;
109 private String substringHttpStatus200 = null;
110 private String keyIcap200 = null;
111 private String subStringFromKeyIcap200 = null;
112 private String keyIcap204 = null;
113 private String subStringFromKeyIcap204 = null;
114 private long maxSize = Integer.MAX_VALUE;
115 private int timeout = DEFAULT_TIMEOUT;
116 private int stdPreviewSize = -1;
117
118
119 private Map<String, String> finalResult = null;
120
121
122 private Socket client;
123 private OutputStream out;
124 InputStream in;
125 private int offset;
126
127
128
129
130
131
132
133
134
135
136 public IcapClient(final String serverIP, final int port,
137 final String icapService) {
138 this(serverIP, port, icapService, -1);
139 }
140
141
142
143
144
145
146
147
148
149
150
151 public IcapClient(final String serverIP, final int port,
152 final String icapService, final int previewSize) {
153 if (ParametersChecker.isEmpty(icapService)) {
154 throw new IllegalArgumentException("IcapService must not be empty");
155 }
156 this.icapService = icapService;
157 if (ParametersChecker.isEmpty(serverIP)) {
158 throw new IllegalArgumentException("Server IP must not be empty");
159 }
160 this.serverIP = serverIP;
161 if (port <= 0) {
162 this.port = DEFAULT_ICAP_PORT;
163 } else {
164 this.port = port;
165 }
166 this.setPreviewSize = previewSize;
167 this.stdPreviewSize = Math.max(0, previewSize);
168 }
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183 public final IcapClient connect() throws IcapException {
184 if (finalResult != null) {
185 finalResult.clear();
186 finalResult = null;
187 }
188 if (client != null) {
189 close();
190 }
191 logger.debug("Try connect to {}:{} service {}", serverIP, port,
192 icapService);
193 try {
194
195 client = new Socket(serverIP, port);
196 client.setReuseAddress(true);
197 client.setKeepAlive(true);
198 client.setSoTimeout(timeout);
199 client.setTcpNoDelay(false);
200
201 out = client.getOutputStream();
202
203 in = client.getInputStream();
204 if (setPreviewSize < 0) {
205 getFromServerPreviewSize();
206 }
207 logger.debug("Connected with Preview Size = {}", stdPreviewSize);
208 return this;
209 } catch (final SocketTimeoutException e) {
210 close();
211 logger.error(TIMEOUT_OCCURS_WITH_THE_SERVER_SINCE, serverIP, port,
212 e.getMessage());
213 throw new IcapException(TIMEOUT_OCCURS_WITH_THE_SERVER, e,
214 IcapError.ICAP_TIMEOUT_ERROR);
215 } catch (final ConnectException e) {
216 close();
217 logger.error("Could not connect to server {}:{} since {}", serverIP, port,
218 e.getMessage());
219 throw new IcapException("Could not connect with the server", e,
220 IcapError.ICAP_CANT_CONNECT);
221 } catch (final IOException e) {
222 close();
223 logger.error("Could not connect to server {}:{} since {}", serverIP, port,
224 e.getMessage());
225 throw new IcapException("Could not connect with the server", e,
226 IcapError.ICAP_NETWORK_ERROR);
227 } catch (final IcapException e) {
228 close();
229 throw e;
230 }
231 }
232
233
234
235
236
237
238 private void getFromServerPreviewSize() throws IcapException {
239
240 final String parseMe = getOptions();
241 finalResult = parseHeader(parseMe);
242 if (checkAgainstIcapHeader(finalResult, STATUS_CODE, "200", false)) {
243 final String tempString = finalResult.get(PREVIEW);
244 if (tempString != null) {
245 stdPreviewSize = Integer.parseInt(tempString);
246 if (stdPreviewSize < 0) {
247 stdPreviewSize = 0;
248 }
249 if (!checkAgainstIcapHeader(finalResult, keyIcapPreview,
250 subStringFromKeyIcapPreview, true)) {
251 close();
252 logger.error("Could not validate preview from server");
253 throw new IcapException("Could not validate preview from server",
254 IcapError.ICAP_SERVER_MISSING_INFO);
255 }
256 } else {
257 close();
258 logger.error("Could not get preview size from server");
259 throw new IcapException("Could not get preview size from server",
260 IcapError.ICAP_SERVER_MISSING_INFO);
261 }
262 } else {
263 close();
264 logger.error("Could not get options from server {}:{} service {}",
265 serverIP, port, icapService);
266 throw new IcapException("Could not get options from server",
267 IcapError.ICAP_SERVER_MISSING_INFO);
268 }
269 }
270
271 @Override
272 public final void close() {
273 if (client != null) {
274 try {
275 client.close();
276 } catch (final IOException ignored) {
277
278 }
279 client = null;
280 }
281 if (in != null) {
282 try {
283 in.close();
284 } catch (final IOException ignored) {
285
286 }
287 in = null;
288 }
289 if (out != null) {
290 try {
291 out.close();
292 } catch (final IOException ignored) {
293
294 }
295 out = null;
296 }
297 }
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314 public final boolean scanFile(final String filename) throws IcapException {
315 if (ParametersChecker.isEmpty(filename)) {
316 throw new IllegalArgumentException("Filename must not be empty");
317 }
318 if (client == null) {
319 connect();
320 }
321 if (finalResult != null) {
322 finalResult.clear();
323 finalResult = null;
324 }
325 InputStream inputStream = null;
326 final long length;
327 if (EICARTEST.equals(filename)) {
328
329 final ClassLoader classLoader = IcapClient.class.getClassLoader();
330 final File fileSrc1 =
331 new File(classLoader.getResource("eicar.com-part1.txt").getFile());
332 final File fileSrc2 =
333 new File(classLoader.getResource("eicar.com-part2.txt").getFile());
334 if (fileSrc1.exists() && fileSrc2.exists()) {
335 try {
336 final byte[] array1 = Files.toByteArray(fileSrc1);
337 final byte[] array2 = Files.toByteArray(fileSrc2);
338 final byte[] array =
339 Arrays.copyOf(array1, array1.length + array2.length);
340 System.arraycopy(array2, 0, array, array1.length, array2.length);
341 inputStream = new ByteArrayInputStream(array);
342 length = array.length;
343 } catch (final IOException e) {
344 logger.error("File EICAR TEST does not exist: {}", e.getMessage());
345 throw new IcapException("File EICAR TEST cannot be found",
346 IcapError.ICAP_ARGUMENT_ERROR);
347 }
348 } else {
349 logger.error("File EICAR TEST does not exist");
350 throw new IcapException("File EICAR TEST cannot be found",
351 IcapError.ICAP_ARGUMENT_ERROR);
352 }
353 } else {
354 final File file = new File(filename);
355 if (!canRead(file)) {
356 logger.error("File does not exist: {}", file.getAbsolutePath());
357 throw new IcapException(
358 "File cannot be found: " + file.getAbsolutePath(),
359 IcapError.ICAP_ARGUMENT_ERROR);
360 }
361 length = file.length();
362 if (length > maxSize) {
363 logger.error("File size {} exceed limit of {}: {}", length, maxSize,
364 file.getAbsolutePath());
365 throw new IcapException(
366 "File exceed limit size: " + file.getAbsolutePath(),
367 IcapError.ICAP_FILE_LENGTH_ERROR);
368 }
369 try {
370 inputStream = new FileInputStream(file);
371 } catch (final FileNotFoundException e) {
372 logger.error("Could not find file {} since {}", filename,
373 e.getMessage());
374 throw new IcapException("File cannot be found: " + filename, e,
375 IcapError.ICAP_ARGUMENT_ERROR);
376 }
377 }
378 try {
379 return scanFile(filename, inputStream, length);
380 } finally {
381 if (inputStream != null) {
382 try {
383 inputStream.close();
384 } catch (final IOException ignored) {
385
386 }
387 }
388 close();
389 }
390 }
391
392
393
394
395 public final String getServerIP() {
396 return serverIP;
397 }
398
399
400
401
402 public final int getPort() {
403 return port;
404 }
405
406
407
408
409 public final String getIcapService() {
410 return icapService;
411 }
412
413
414
415
416 public final int getPreviewSize() {
417 return stdPreviewSize;
418 }
419
420
421
422
423
424
425 public final IcapClient setPreviewSize(final int previewSize) {
426 if (previewSize < 0) {
427 logger.error(INCOMPATIBLE_ARGUMENT);
428 throw new IllegalArgumentException("Preview cannot be 0 or positive");
429 }
430 this.stdPreviewSize = previewSize;
431 return this;
432 }
433
434
435
436
437 public final int getReceiveLength() {
438 return receiveLength;
439 }
440
441
442
443
444
445
446 public final IcapClient setReceiveLength(final int receiveLength) {
447 if (receiveLength < MINIMAL_SIZE) {
448 logger.error(INCOMPATIBLE_ARGUMENT);
449 throw new IllegalArgumentException(
450 "Receive length cannot be less than " + MINIMAL_SIZE);
451 }
452 this.receiveLength = receiveLength;
453 return this;
454 }
455
456
457
458
459 public final int getSendLength() {
460 return sendLength;
461 }
462
463
464
465
466
467
468 public final IcapClient setSendLength(final int sendLength) {
469 if (sendLength < MINIMAL_SIZE) {
470 logger.error(INCOMPATIBLE_ARGUMENT);
471 throw new IllegalArgumentException(
472 "Send length cannot be less than " + MINIMAL_SIZE);
473 }
474 this.sendLength = sendLength;
475 return this;
476 }
477
478
479
480
481 public final long getMaxSize() {
482 return maxSize;
483 }
484
485
486
487
488
489
490 public final IcapClient setMaxSize(final long maxSize) {
491 if (maxSize < MINIMAL_SIZE) {
492 logger.error(INCOMPATIBLE_ARGUMENT);
493 throw new IllegalArgumentException(
494 "Maximum file size length cannot be less than " + MINIMAL_SIZE);
495 }
496 this.maxSize = maxSize;
497 return this;
498 }
499
500
501
502
503 public final long getTimeout() {
504 return timeout;
505 }
506
507
508
509
510
511
512 public final IcapClient setTimeout(final int timeout) {
513 this.timeout = timeout;
514 return this;
515 }
516
517
518
519
520
521 public final String getKeyIcapPreview() {
522 return keyIcapPreview;
523 }
524
525
526
527
528
529
530
531 public final IcapClient setKeyIcapPreview(final String keyIcapPreview) {
532 if (ParametersChecker.isEmpty(keyIcapPreview)) {
533 this.keyIcapPreview = null;
534 } else {
535 this.keyIcapPreview = keyIcapPreview;
536 }
537 return this;
538 }
539
540
541
542
543
544 public final String getSubStringFromKeyIcapPreview() {
545 return subStringFromKeyIcapPreview;
546 }
547
548
549
550
551
552
553
554 public final IcapClient setSubStringFromKeyIcapPreview(
555 final String subStringFromKeyIcapPreview) {
556 if (ParametersChecker.isEmpty(subStringFromKeyIcapPreview)) {
557 this.subStringFromKeyIcapPreview = null;
558 } else {
559 this.subStringFromKeyIcapPreview = subStringFromKeyIcapPreview;
560 }
561 return this;
562 }
563
564
565
566
567
568 public final String getSubstringHttpStatus200() {
569 return substringHttpStatus200;
570 }
571
572
573
574
575
576
577
578 public final IcapClient setSubstringHttpStatus200(
579 final String substringHttpStatus200) {
580 if (ParametersChecker.isEmpty(substringHttpStatus200)) {
581 this.substringHttpStatus200 = null;
582 } else {
583 this.substringHttpStatus200 = substringHttpStatus200;
584 }
585 return this;
586 }
587
588
589
590
591
592 public final String getKeyIcap200() {
593 return keyIcap200;
594 }
595
596
597
598
599
600
601
602 public final IcapClient setKeyIcap200(final String keyIcap200) {
603 if (ParametersChecker.isEmpty(keyIcap200)) {
604 this.keyIcap200 = null;
605 } else {
606 this.keyIcap200 = keyIcap200;
607 }
608 return this;
609 }
610
611
612
613
614
615 public final String getSubStringFromKeyIcap200() {
616 return subStringFromKeyIcap200;
617 }
618
619
620
621
622
623
624
625 public final IcapClient setSubStringFromKeyIcap200(
626 final String subStringFromKeyIcap200) {
627 if (ParametersChecker.isEmpty(subStringFromKeyIcap200)) {
628 this.subStringFromKeyIcap200 = null;
629 } else {
630 this.subStringFromKeyIcap200 = subStringFromKeyIcap200;
631 }
632 return this;
633 }
634
635
636
637
638
639 public final String getKeyIcap204() {
640 return keyIcap204;
641 }
642
643
644
645
646
647
648
649 public final IcapClient setKeyIcap204(final String keyIcap204) {
650 if (ParametersChecker.isEmpty(keyIcap204)) {
651 this.keyIcap204 = null;
652 } else {
653 this.keyIcap204 = keyIcap204;
654 }
655 return this;
656 }
657
658
659
660
661
662 public final String getSubStringFromKeyIcap204() {
663 return subStringFromKeyIcap204;
664 }
665
666
667
668
669
670
671
672 public final IcapClient setSubStringFromKeyIcap204(
673 final String subStringFromKeyIcap204) {
674 if (ParametersChecker.isEmpty(subStringFromKeyIcap204)) {
675 this.subStringFromKeyIcap204 = null;
676 } else {
677 this.subStringFromKeyIcap204 = subStringFromKeyIcap204;
678 }
679 return this;
680 }
681
682
683
684
685 public final Map<String, String> getFinalResult() {
686 return finalResult;
687 }
688
689
690
691
692
693
694
695
696
697 private final String getOptions() throws IcapException {
698
699
700 final StringBuilder builder = new StringBuilder();
701 addIcapUri(builder, OPTIONS);
702 final String requestHeader =
703 builder.append(ENCAPSULATED_NULL_BODY).append(ICAP_TERMINATOR)
704 .toString();
705
706 sendString(requestHeader, true);
707
708 return getHeaderIcap();
709 }
710
711
712
713
714
715
716
717
718
719
720
721
722
723 private boolean scanFile(final String originalFilename,
724 final InputStream fileInStream, final long fileSize)
725 throws IcapException {
726 final int previewSize = sendIcapHttpScanRequest(originalFilename, fileSize);
727
728
729 if (previewSize == 0) {
730
731 logger.debug("Empty PREVIEW");
732 final StringBuilder builder = new StringBuilder();
733 builder.append(Integer.toHexString(previewSize)).append(TERMINATOR);
734 builder.append(HTTP_TERMINATOR);
735 sendString(builder.toString(), true);
736 } else {
737 logger.debug("PREVIEW of {}", previewSize);
738 final byte[] chunk = new byte[previewSize];
739 final int read = readChunk(fileInStream, chunk, previewSize);
740 if (read != previewSize) {
741 logger.warn("Read file size {} is less than preview size {}", read,
742 previewSize);
743 }
744
745 final StringBuilder builder = new StringBuilder();
746 builder.append(Integer.toHexString(read)).append(TERMINATOR);
747 sendString(builder.toString());
748 sendBytes(chunk, read);
749 sendString(TERMINATOR);
750 if (fileSize <= previewSize) {
751 logger.debug("PREVIEW and COMPLETE");
752 sendString("0; ieof" + ICAP_TERMINATOR, true);
753 } else {
754 logger.debug("PREVIEW but could send more");
755 sendString(HTTP_TERMINATOR, true);
756 }
757 }
758
759
760 if (fileSize > previewSize) {
761 final int preview = checkPreview();
762 if (preview != 0) {
763 logger.debug("PREVIEW is enough and status {}", preview == 1);
764 return preview == 1;
765 }
766 logger.debug("PREVIEW is not enough");
767 sendNextFileChunks(fileInStream);
768 }
769 return checkFinalResponse();
770 }
771
772
773
774
775
776
777
778
779
780
781
782 private int sendIcapHttpScanRequest(final String originalFilename,
783 final long fileSize)
784 throws IcapException {
785
786 final String resHeader;
787 final StringBuilder builder = new StringBuilder(GET_REQUEST);
788 try {
789 builder.append(
790 URLEncoder.encode(originalFilename, WaarpStringUtils.UTF_8))
791 .append(" HTTP/1.1").append(TERMINATOR);
792 builder.append(HOST_HEADER).append(serverIP).append(":").append(port)
793 .append(ICAP_TERMINATOR);
794 resHeader = builder.toString();
795 } catch (final UnsupportedEncodingException e) {
796 logger.error("Unsupported Encoding: {}", e.getMessage());
797 throw new IcapException(e.getMessage(), e, IcapError.ICAP_INTERNAL_ERROR);
798 }
799 builder.append("HTTP/1.1 200 OK").append(TERMINATOR);
800 builder.append("Transfer-Encoding: chunked").append(TERMINATOR);
801 builder.append("Content-Length: ").append(fileSize).append(ICAP_TERMINATOR);
802 final String resBody = builder.toString();
803
804 int previewSize = stdPreviewSize;
805 if (fileSize < stdPreviewSize) {
806 previewSize = (int) fileSize;
807 }
808
809
810 builder.setLength(0);
811 addIcapUri(builder, RESPMOD);
812 builder.append(PREVIEW).append(": ").append(previewSize).append(TERMINATOR);
813 builder.append("Encapsulated: req-hdr=0, res-hdr=")
814 .append(resHeader.length()).append(", res-body=")
815 .append(resBody.length()).append(ICAP_TERMINATOR);
816 builder.append(resBody);
817 final String requestBuffer = builder.toString();
818
819 sendString(requestBuffer);
820 return previewSize;
821 }
822
823
824
825
826
827
828
829 private void addIcapUri(final StringBuilder builder, final String method) {
830 builder.append(method).append(" icap://").append(serverIP).append("/")
831 .append(icapService).append(" ").append(VERSION).append(TERMINATOR);
832 builder.append(HOST_HEADER).append(serverIP).append(TERMINATOR);
833 builder.append(USER_AGENT_HEADER).append(USER_AGENT).append(TERMINATOR);
834 builder.append("Allow: 204").append(TERMINATOR);
835 }
836
837
838
839
840
841
842
843
844
845
846 private int checkPreview() throws IcapException {
847 final int status;
848 final String parseMe = getHeaderIcap();
849 finalResult = parseHeader(parseMe);
850
851 final String tempString = finalResult.get(STATUS_CODE);
852 if (tempString != null) {
853 status = Integer.parseInt(tempString);
854 switch (status) {
855 case 100:
856 logger.debug("Recv ICAP Preview Status Continue");
857 return 0;
858 case 200:
859 logger.info("Recv ICAP Preview Status Abort");
860 return -1;
861 case 204:
862 logger.debug("Recv ICAP Preview Status Accepted");
863 return 1;
864 case 404:
865 logger.error("404: ICAP Service not found");
866 throw new IcapException("404: ICAP Service not found",
867 IcapError.ICAP_SERVER_SERVICE_UNKNOWN);
868 default:
869 logger.error("Server returned unknown status code: {}", status);
870 throw new IcapException(
871 "Server returned unknown status code: " + status,
872 IcapError.ICAP_SERVER_UNKNOWN_CODE);
873 }
874 }
875 logger.error("Server returned unknown status code");
876 throw new IcapException("Server returned unknown status code",
877 IcapError.ICAP_SERVER_UNKNOWN_CODE);
878 }
879
880
881
882
883
884
885
886
887
888 private boolean checkFinalResponse() throws IcapException {
889 final int status;
890 String parseMe = getHeaderIcap();
891 finalResult = parseHeader(parseMe);
892
893 final String tempString = finalResult.get(STATUS_CODE);
894 if (tempString != null) {
895 status = Integer.parseInt(tempString);
896
897 if (status == 204) {
898
899 logger.debug("Almost final status is {}", status);
900 return checkAgainstIcapHeader(finalResult, keyIcap204,
901 subStringFromKeyIcap204, true);
902 }
903
904 if (status == 200) {
905
906
907 logger.debug("Almost final status is {}", status);
908 boolean finalStatus = checkAgainstIcapHeader(finalResult, keyIcap200,
909 subStringFromKeyIcap200,
910 false);
911 if (ParametersChecker.isNotEmpty(substringHttpStatus200)) {
912 parseMe = getHeaderHttp();
913 logger.warn("{} contains {} = {}", parseMe, substringHttpStatus200,
914 parseMe.contains(substringHttpStatus200));
915 finalStatus |= parseMe.contains(substringHttpStatus200);
916 } else {
917 if (logger.isTraceEnabled()) {
918 getHeaderHttp();
919 }
920 }
921 logger.info("Final status with check {}", finalStatus);
922 return finalStatus;
923 }
924 }
925 logger.error("Unrecognized or no status code in response header");
926 throw new IcapException("Unrecognized or no status code in response header",
927 IcapError.ICAP_SERVER_UNKNOWN_CODE);
928 }
929
930
931
932
933
934
935
936
937
938
939 private boolean checkAgainstIcapHeader(final Map<String, String> responseMap,
940 final String key,
941 final String subValue,
942 final boolean defaultValue) {
943 if (key != null && subValue != null) {
944 final String value = responseMap.get(key);
945 return value != null && value.contains(subValue);
946 }
947 return defaultValue;
948 }
949
950
951
952
953
954
955
956
957
958 private void sendNextFileChunks(final InputStream fileInStream)
959 throws IcapException {
960
961 final byte[] buffer = new byte[sendLength];
962 int len = readChunk(fileInStream, buffer, sendLength);
963 while (len != -1) {
964 sendString(Integer.toHexString(len) + TERMINATOR);
965 sendBytes(buffer, len);
966 sendString(TERMINATOR);
967 len = readChunk(fileInStream, buffer, sendLength);
968 }
969
970 sendString(HTTP_TERMINATOR, true);
971 logger.debug("End of chunks");
972 }
973
974
975
976
977
978
979
980
981
982
983
984
985
986 final int readChunk(final InputStream fileInputStream, final byte[] buffer,
987 final int length) throws IcapException {
988 if (buffer.length < length) {
989 logger.error("Buffer is too small {} for reading file per {}",
990 buffer.length, length);
991 throw new IcapException("Buffer is too small for reading file",
992 IcapError.ICAP_INTERNAL_ERROR);
993 }
994 int sizeOut = 0;
995 int toRead = length;
996 while (sizeOut < length) {
997 try {
998 final int read = fileInputStream.read(buffer, sizeOut, toRead);
999 if (read <= 0) {
1000 break;
1001 }
1002 sizeOut += read;
1003 toRead -= read;
1004 } catch (final IOException e) {
1005 logger.error("File cannot be read: {}", e.getMessage());
1006 throw new IcapException("File cannot be read", e,
1007 IcapError.ICAP_INTERNAL_ERROR);
1008 }
1009 }
1010 if (sizeOut <= 0) {
1011 return -1;
1012 }
1013 return sizeOut;
1014 }
1015
1016
1017
1018
1019
1020
1021 final String getHeaderHttp() throws IcapException {
1022 final byte[] buffer = new byte[receiveLength];
1023 try {
1024 return getHeader(HTTP_TERMINATOR, buffer);
1025 } catch (final IcapException e) {
1026 final String finalHeaders =
1027 new String(buffer, 0, offset, WaarpStringUtils.UTF8);
1028 switch (e.getError()) {
1029 case ICAP_SERVER_HEADER_WITHOUT_TERMINATOR:
1030
1031 logger.debug("RECV HTTP Headers not ended\n{}", finalHeaders);
1032 return finalHeaders;
1033 case ICAP_SERVER_HEADER_EXCEED_CAPACITY:
1034
1035 logger.debug("RECV HTTP Headers exceed capacity\n{}", finalHeaders);
1036 return finalHeaders;
1037 default:
1038 break;
1039 }
1040 throw e;
1041 }
1042 }
1043
1044
1045
1046
1047
1048
1049
1050 final String getHeaderIcap() throws IcapException {
1051 final byte[] buffer = new byte[receiveLength];
1052 return getHeader(ICAP_TERMINATOR, buffer);
1053 }
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066 private String getHeader(final String terminator, final byte[] buffer)
1067 throws IcapException {
1068 final byte[] endOfHeader = terminator.getBytes(WaarpStringUtils.UTF8);
1069 final int[] endOfHeaderInt = new int[endOfHeader.length];
1070 final int[] marks = new int[endOfHeader.length];
1071 for (int i = 0; i < endOfHeader.length; i++) {
1072 endOfHeaderInt[i] = endOfHeader[i];
1073 marks[i] = -1;
1074 }
1075
1076 int reader = -1;
1077 offset = 0;
1078
1079 try {
1080
1081 while ((offset < receiveLength) && ((reader = in.read()) != -1)) {
1082 marks[0] = marks[1];
1083 marks[1] = marks[2];
1084 marks[2] = marks[3];
1085 if (endOfHeader.length == 4) {
1086 marks[3] = reader;
1087 } else {
1088 marks[3] = marks[4];
1089 marks[4] = reader;
1090 }
1091 buffer[offset] = (byte) reader;
1092 offset++;
1093
1094 if (offset > endOfHeader.length + 13 &&
1095 Arrays.equals(endOfHeaderInt, marks)) {
1096 final String finalHeaders =
1097 new String(buffer, 0, offset, WaarpStringUtils.UTF8);
1098 logger.debug("RECV {} Headers:{}\n{}",
1099 terminator.length() == 4? "ICAP" : "HTTP", offset,
1100 finalHeaders);
1101 return finalHeaders;
1102 }
1103 }
1104 } catch (final SocketTimeoutException e) {
1105 logger.error(TIMEOUT_OCCURS_WITH_THE_SERVER_SINCE, serverIP, port,
1106 e.getMessage());
1107 throw new IcapException(TIMEOUT_OCCURS_WITH_THE_SERVER, e,
1108 IcapError.ICAP_TIMEOUT_ERROR);
1109 } catch (final IOException e) {
1110 logger.error("Response cannot be read: {}", e.getMessage());
1111 throw new IcapException("Response cannot be read", e,
1112 IcapError.ICAP_NETWORK_ERROR);
1113 }
1114 if (reader == -1) {
1115 logger.warn("Response is not complete while reading {}", offset);
1116 throw new IcapException(
1117 "Error in getHeader() method: response is not complete: " + offset,
1118 IcapError.ICAP_SERVER_HEADER_WITHOUT_TERMINATOR);
1119 }
1120 logger.warn("Response cannot be read since size exceed maximum {}",
1121 receiveLength);
1122 throw new IcapException(
1123 "Error in getHeader() method: received message too long",
1124 IcapError.ICAP_SERVER_HEADER_EXCEED_CAPACITY);
1125 }
1126
1127
1128
1129
1130 private Map<String, String> parseHeader(final String response) {
1131 final Map<String, String> headers = new HashMap<String, String>();
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142 final int x = response.indexOf(' ');
1143 final int y = response.indexOf(' ', x + 2);
1144 final String statusCode = response.substring(x + 1, y);
1145 headers.put(STATUS_CODE, statusCode);
1146
1147
1148
1149
1150
1151 int i = response.indexOf(TERMINATOR, y);
1152 i += 2;
1153 while (i + 2 < response.length() && response.substring(i).contains(":")) {
1154 int n = response.indexOf(':', i);
1155 final String key = response.substring(i, n).trim();
1156
1157 n += 2;
1158 i = response.indexOf(TERMINATOR, n);
1159 final String value = response.substring(n, i).trim();
1160
1161 headers.put(key, value);
1162 i += 2;
1163 }
1164 logger.debug("RECV ICAP Headers:\n{}", headers);
1165 return headers;
1166 }
1167
1168
1169
1170
1171
1172
1173
1174
1175 private void sendString(final String requestHeader) throws IcapException {
1176 sendString(requestHeader, false);
1177 }
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187 private void sendString(final String requestHeader, final boolean withFlush)
1188 throws IcapException {
1189 try {
1190 out.write(requestHeader.getBytes(WaarpStringUtils.UTF8));
1191 if (withFlush) {
1192 out.flush();
1193 }
1194 } catch (final SocketTimeoutException e) {
1195 logger.error(TIMEOUT_OCCURS_WITH_THE_SERVER_SINCE, serverIP, port,
1196 e.getMessage());
1197 throw new IcapException(TIMEOUT_OCCURS_WITH_THE_SERVER, e,
1198 IcapError.ICAP_TIMEOUT_ERROR);
1199 } catch (final IOException e) {
1200 logger.error("Client cannot communicate with ICAP Server: {}",
1201 e.getMessage());
1202 throw new IcapException("Client cannot communicate with ICAP Server", e,
1203 IcapError.ICAP_NETWORK_ERROR);
1204 }
1205 }
1206
1207
1208
1209
1210
1211
1212
1213
1214 private void sendBytes(final byte[] chunk, final int length)
1215 throws IcapException {
1216 try {
1217 out.write(chunk, 0, length);
1218 } catch (final SocketTimeoutException e) {
1219 logger.error(TIMEOUT_OCCURS_WITH_THE_SERVER_SINCE, serverIP, port,
1220 e.getMessage());
1221 throw new IcapException(TIMEOUT_OCCURS_WITH_THE_SERVER, e,
1222 IcapError.ICAP_TIMEOUT_ERROR);
1223 } catch (final IOException e) {
1224 logger.error("Client cannot communicate with ICAP Server: {}",
1225 e.getMessage());
1226 throw new IcapException("Writing to ICAP Server cannot be done", e,
1227 IcapError.ICAP_NETWORK_ERROR);
1228 }
1229 }
1230
1231 }