001 /*
002 * Copyright 2009-2013 UnboundID Corp.
003 * All Rights Reserved.
004 */
005 /*
006 * Copyright (C) 2009-2013 UnboundID Corp.
007 *
008 * This program is free software; you can redistribute it and/or modify
009 * it under the terms of the GNU General Public License (GPLv2 only)
010 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
011 * as published by the Free Software Foundation.
012 *
013 * This program is distributed in the hope that it will be useful,
014 * but WITHOUT ANY WARRANTY; without even the implied warranty of
015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
016 * GNU General Public License for more details.
017 *
018 * You should have received a copy of the GNU General Public License
019 * along with this program; if not, see <http://www.gnu.org/licenses>.
020 */
021 package com.unboundid.asn1;
022
023
024
025 import java.io.BufferedInputStream;
026 import java.io.InputStream;
027 import java.io.IOException;
028 import java.net.SocketTimeoutException;
029 import java.util.logging.Level;
030
031 import com.unboundid.util.Mutable;
032 import com.unboundid.util.ThreadSafety;
033 import com.unboundid.util.ThreadSafetyLevel;
034
035 import static com.unboundid.asn1.ASN1Messages.*;
036 import static com.unboundid.util.Debug.*;
037 import static com.unboundid.util.StaticUtils.*;
038
039
040
041 /**
042 * This class provides a mechanism for ASN.1 elements (including sequences and
043 * sets) from an input stream in a manner that allows the data to be decoded on
044 * the fly without constructing {@link ASN1Element} objects if they are not
045 * needed. If any method in this class throws an {@code IOException}, then the
046 * caller must close this reader and must not attempt to use it any more.
047 * {@code ASN1StreamReader} instances are not threadsafe and must not be
048 * accessed concurrently by multiple threads.
049 */
050 @Mutable()
051 @ThreadSafety(level=ThreadSafetyLevel.NOT_THREADSAFE)
052 public final class ASN1StreamReader
053 {
054 // Indicates whether socket timeout exceptions should be ignored for the
055 // initial read of an element.
056 private boolean ignoreInitialSocketTimeout;
057
058 // Indicates whether socket timeout exceptions should be ignored for
059 // subsequent reads of an element.
060 private boolean ignoreSubsequentSocketTimeout;
061
062 // The input stream from which data will be read.
063 private final InputStream inputStream;
064
065 // The maximum element size that will be allowed.
066 private final int maxElementSize;
067
068 // The total number of bytes read from the underlying input stream.
069 private long totalBytesRead;
070
071
072
073 /**
074 * Creates a new ASN.1 stream reader that will read data from the provided
075 * input stream. It will use a maximum element size of
076 * {@code Integer.MAX_VALUE}.
077 *
078 * @param inputStream The input stream from which data should be read. If
079 * the provided input stream does not support the use of
080 * the {@code mark} and {@code reset} methods, then it
081 * will be wrapped with a {@code BufferedInputStream}.
082 */
083 public ASN1StreamReader(final InputStream inputStream)
084 {
085 this(inputStream, Integer.MAX_VALUE);
086 }
087
088
089
090 /**
091 * Creates a new ASN.1 stream reader that will read data from the provided
092 * input stream. It will use a maximum element size of
093 * {@code Integer.MAX_VALUE}.
094 *
095 * @param inputStream The input stream from which data should be read.
096 * If the provided input stream does not support the
097 * use of the {@code mark} and {@code reset} methods,
098 * then it will be wrapped with a
099 * {@code BufferedInputStream}.
100 * @param maxElementSize The maximum size in bytes of an ASN.1 element that
101 * may be read. A value less than or equal to zero
102 * will be interpreted as {@code Integer.MAX_VALUE}.
103 */
104 public ASN1StreamReader(final InputStream inputStream,
105 final int maxElementSize)
106 {
107 if (inputStream.markSupported())
108 {
109 this.inputStream = inputStream;
110 }
111 else
112 {
113 this.inputStream = new BufferedInputStream(inputStream);
114 }
115
116 if (maxElementSize > 0)
117 {
118 this.maxElementSize = maxElementSize;
119 }
120 else
121 {
122 this.maxElementSize = Integer.MAX_VALUE;
123 }
124
125 totalBytesRead = 0L;
126 ignoreInitialSocketTimeout = false;
127 ignoreSubsequentSocketTimeout = false;
128 }
129
130
131
132 /**
133 * Closes this ASN.1 stream reader and the underlying input stream. This
134 * reader must not be used after it has been closed.
135 *
136 * @throws IOException If a problem occurs while closing the underlying
137 * input stream.
138 */
139 public void close()
140 throws IOException
141 {
142 inputStream.close();
143 }
144
145
146
147 /**
148 * Retrieves the total number of bytes read so far from the underlying input
149 * stream.
150 *
151 * @return The total number of bytes read so far from the underlying input
152 * stream.
153 */
154 long getTotalBytesRead()
155 {
156 return totalBytesRead;
157 }
158
159
160
161 /**
162 * Indicates whether to ignore {@code java.net.SocketTimeoutException}
163 * exceptions that may be caught during processing.
164 *
165 * @return {@code true} if {@code SocketTimeoutException} exceptions should
166 * be ignored, or {@code false} if they should not be ignored and
167 * should be propagated to the caller.
168 *
169 * @deprecated Use the {@link #ignoreInitialSocketTimeoutException()} and
170 * {@link #ignoreSubsequentSocketTimeoutException()} methods
171 * instead.
172 */
173 @Deprecated()
174 public boolean ignoreSocketTimeoutException()
175 {
176 return ignoreInitialSocketTimeout;
177 }
178
179
180
181 /**
182 * Indicates whether to ignore {@code java.net.SocketTimeoutException}
183 * exceptions that may be caught while trying to read the first byte of an
184 * element.
185 *
186 * @return {@code true} if {@code SocketTimeoutException} exceptions should
187 * be ignored while trying to read the first byte of an element, or
188 * {@code false} if they should not be ignored and should be
189 * propagated to the caller.
190 */
191 public boolean ignoreInitialSocketTimeoutException()
192 {
193 return ignoreInitialSocketTimeout;
194 }
195
196
197
198 /**
199 * Indicates whether to ignore {@code java.net.SocketTimeoutException}
200 * exceptions that may be caught while trying to read subsequent bytes of an
201 * element (after one or more bytes have already been read for that element).
202 *
203 * @return {@code true} if {@code SocketTimeoutException} exceptions should
204 * be ignored while trying to read subsequent bytes of an element, or
205 * {@code false} if they should not be ignored and should be
206 * propagated to the caller.
207 */
208 public boolean ignoreSubsequentSocketTimeoutException()
209 {
210 return ignoreSubsequentSocketTimeout;
211 }
212
213
214
215 /**
216 * Indicates whether to ignore {@code java.net.SocketTimeoutException}
217 * exceptions that may be caught during processing.
218 *
219 * @param ignoreSocketTimeout Indicates whether to ignore
220 * {@code SocketTimeoutException} exceptions that
221 * may be caught during processing.
222 *
223 * @deprecated Use the {@link #setIgnoreSocketTimeout(boolean,boolean)}
224 * method instead.
225 */
226 @Deprecated()
227 public void setIgnoreSocketTimeout(final boolean ignoreSocketTimeout)
228 {
229 ignoreInitialSocketTimeout = ignoreSocketTimeout;
230 ignoreSubsequentSocketTimeout = ignoreSocketTimeout;
231 }
232
233
234
235 /**
236 * Indicates whether to ignore {@code java.net.SocketTimeoutException}
237 * exceptions that may be caught during processing.
238 *
239 * @param ignoreInitialSocketTimeout Indicates whether to ignore
240 * {@code SocketTimeoutException}
241 * exceptions that may be caught while
242 * trying to read the first byte of an
243 * element.
244 * @param ignoreSubsequentSocketTimeout Indicates whether to ignore
245 * {@code SocketTimeoutException}
246 * exceptions that may be caught while
247 * reading beyond the first byte of an
248 * element.
249 */
250 public void setIgnoreSocketTimeout(final boolean ignoreInitialSocketTimeout,
251 final boolean ignoreSubsequentSocketTimeout)
252 {
253 this.ignoreInitialSocketTimeout = ignoreInitialSocketTimeout;
254 this.ignoreSubsequentSocketTimeout = ignoreSubsequentSocketTimeout;
255 }
256
257
258
259 /**
260 * Peeks at the next byte to be read from the input stream without actually
261 * consuming it.
262 *
263 * @return An integer value encapsulating the BER type of the next element in
264 * the input stream, or -1 if the end of the input stream has been
265 * reached and there is no data to be read. If a value of -1 is
266 * returned, then the input stream will not have been closed since
267 * this method is not intended to have any impact on the underlying
268 * input stream.
269 *
270 * @throws IOException If a problem occurs while reading from the input
271 * stream.
272 */
273 public int peek()
274 throws IOException
275 {
276 inputStream.mark(1);
277 final int byteRead = read(true);
278 inputStream.reset();
279
280 return byteRead;
281 }
282
283
284
285 /**
286 * Reads the BER type of the next element from the input stream. This may not
287 * be called if a previous element has been started but not yet completed.
288 *
289 * @return An integer value encapsulating the BER type of the next element in
290 * the input stream, or -1 if the end of the input stream has been
291 * reached and there is no data to be read. If a value of -1 is
292 * returned, then the input stream will have been closed.
293 *
294 * @throws IOException If a problem occurs while reading from the input
295 * stream.
296 */
297 private int readType()
298 throws IOException
299 {
300 final int typeInt = read(true);
301 if (typeInt < 0)
302 {
303 close();
304 }
305 else
306 {
307 totalBytesRead++;
308 }
309 return typeInt;
310 }
311
312
313
314 /**
315 * Reads the length of the next element from the input stream. This may only
316 * be called after reading the BER type.
317 *
318 * @return The length of the next element from the input stream.
319 *
320 * @throws IOException If a problem occurs while reading from the input
321 * stream, if the end of the stream has been reached, or
322 * if the decoded length is greater than the maximum
323 * allowed length.
324 */
325 private int readLength()
326 throws IOException
327 {
328 int length = read(false);
329 if (length < 0)
330 {
331 throw new IOException(ERR_READ_END_BEFORE_FIRST_LENGTH.get());
332 }
333
334 totalBytesRead++;
335 if (length > 127)
336 {
337 final int numLengthBytes = length & 0x7F;
338 length = 0;
339 if ((numLengthBytes < 1) || (numLengthBytes > 4))
340 {
341 throw new IOException(ERR_READ_LENGTH_TOO_LONG.get(numLengthBytes));
342 }
343
344 for (int i=0; i < numLengthBytes; i++)
345 {
346 final int lengthInt = read(false);
347 if (lengthInt < 0)
348 {
349 throw new IOException(ERR_READ_END_BEFORE_LENGTH_END.get());
350 }
351
352 length <<= 8;
353 length |= (lengthInt & 0xFF);
354 }
355
356 totalBytesRead += numLengthBytes;
357 }
358
359 if ((length < 0) || ((maxElementSize > 0) && (length > maxElementSize)))
360 {
361 throw new IOException(ERR_READ_LENGTH_EXCEEDS_MAX.get(length,
362 maxElementSize));
363 }
364
365 return length;
366 }
367
368
369
370 /**
371 * Skips over the specified number of bytes.
372 *
373 * @param numBytes The number of bytes to skip.
374 *
375 * @throws IOException If a problem occurs while reading from the input
376 * stream, or if the end of the stream is reached before
377 * having skipped the specified number of bytes.
378 */
379 private void skip(final int numBytes)
380 throws IOException
381 {
382 if (numBytes <= 0)
383 {
384 return;
385 }
386
387 long totalBytesSkipped = inputStream.skip(numBytes);
388 while (totalBytesSkipped < numBytes)
389 {
390 final long bytesSkipped = inputStream.skip(numBytes - totalBytesSkipped);
391 if (bytesSkipped <= 0)
392 {
393 while (totalBytesSkipped < numBytes)
394 {
395 final int byteRead = read(false);
396 if (byteRead < 0)
397 {
398 throw new IOException(ERR_READ_END_BEFORE_VALUE_END.get());
399 }
400 totalBytesSkipped++;
401 }
402 }
403 else
404 {
405 totalBytesSkipped += bytesSkipped;
406 }
407 }
408
409 totalBytesRead += numBytes;
410 }
411
412
413
414 /**
415 * Reads a complete ASN.1 element from the input stream.
416 *
417 * @return The ASN.1 element read from the input stream, or {@code null} if
418 * the end of the input stream was reached before any data could be
419 * read. If {@code null} is returned, then the input stream will
420 * have been closed.
421 *
422 * @throws IOException If a problem occurs while reading from the input
423 * stream, if the end of the input stream is reached in
424 * the middle of the element, or or if an attempt is
425 * made to read an element larger than the maximum
426 * allowed size.
427 */
428 public ASN1Element readElement()
429 throws IOException
430 {
431 final int type = readType();
432 if (type < 0)
433 {
434 return null;
435 }
436
437 final int length = readLength();
438
439 int valueBytesRead = 0;
440 int bytesRemaining = length;
441 final byte[] value = new byte[length];
442 while (valueBytesRead < length)
443 {
444 final int bytesRead = read(false, value, valueBytesRead, bytesRemaining);
445 if (bytesRead < 0)
446 {
447 throw new IOException(ERR_READ_END_BEFORE_VALUE_END.get());
448 }
449
450 valueBytesRead += bytesRead;
451 bytesRemaining -= bytesRead;
452 }
453
454 totalBytesRead += length;
455 final ASN1Element e = new ASN1Element((byte) type, value);
456 debugASN1Read(e);
457 return e;
458 }
459
460
461
462 /**
463 * Reads an ASN.1 Boolean element from the input stream and returns the value
464 * as a {@code Boolean}.
465 *
466 * @return The {@code Boolean} value of the ASN.1 Boolean element read, or
467 * {@code null} if the end of the input stream was reached before any
468 * data could be read. If {@code null} is returned, then the input
469 * stream will have been closed.
470 *
471 * @throws IOException If a problem occurs while reading from the input
472 * stream, if the end of the input stream is reached in
473 * the middle of the element, or or if an attempt is
474 * made to read an element larger than the maximum
475 * allowed size.
476 *
477 * @throws ASN1Exception If the data read cannot be parsed as an ASN.1
478 * Boolean element.
479 */
480 public Boolean readBoolean()
481 throws IOException, ASN1Exception
482 {
483 final int type = readType();
484 if (type < 0)
485 {
486 return null;
487 }
488
489 final int length = readLength();
490
491 if (length == 1)
492 {
493 final int value = read(false);
494 if (value < 0)
495 {
496 throw new IOException(ERR_READ_END_BEFORE_VALUE_END.get());
497 }
498
499 totalBytesRead++;
500 return (value != 0);
501 }
502 else
503 {
504 skip(length);
505 throw new ASN1Exception(ERR_BOOLEAN_INVALID_LENGTH.get());
506 }
507 }
508
509
510
511 /**
512 * Reads an ASN.1 enumerated element from the input stream and returns the
513 * value as an {@code Integer}.
514 *
515 * @return The {@code Integer} value of the ASN.1 enumerated element read, or
516 * {@code null} if the end of the input stream was reached before any
517 * data could be read. If {@code null} is returned, then the input
518 * stream will have been closed.
519 *
520 * @throws IOException If a problem occurs while reading from the input
521 * stream, if the end of the input stream is reached in
522 * the middle of the element, or or if an attempt is
523 * made to read an element larger than the maximum
524 * allowed size.
525 *
526 * @throws ASN1Exception If the data read cannot be parsed as an ASN.1
527 * enumerated element.
528 */
529 public Integer readEnumerated()
530 throws IOException, ASN1Exception
531 {
532 return readInteger();
533 }
534
535
536
537 /**
538 * Reads an ASN.1 integer element from the input stream and returns the value
539 * as an {@code Integer}.
540 *
541 * @return The {@code Integer} value of the ASN.1 integer element read, or
542 * {@code null} if the end of the input stream was reached before any
543 * data could be read. If {@code null} is returned, then the input
544 * stream will have been closed.
545 *
546 * @throws IOException If a problem occurs while reading from the input
547 * stream, if the end of the input stream is reached in
548 * the middle of the element, or or if an attempt is
549 * made to read an element larger than the maximum
550 * allowed size.
551 *
552 * @throws ASN1Exception If the data read cannot be parsed as an ASN.1
553 * integer element.
554 */
555 public Integer readInteger()
556 throws IOException, ASN1Exception
557 {
558 final int type = readType();
559 if (type < 0)
560 {
561 return null;
562 }
563
564 final int length = readLength();
565 if ((length == 0) || (length > 4))
566 {
567 skip(length);
568 throw new ASN1Exception(ERR_INTEGER_INVALID_LENGTH.get(length));
569 }
570
571 boolean negative = false;
572 int intValue = 0;
573 for (int i=0; i < length; i++)
574 {
575 final int byteRead = read(false);
576 if (byteRead < 0)
577 {
578 throw new IOException(ERR_READ_END_BEFORE_VALUE_END.get());
579 }
580
581 if (i == 0)
582 {
583 negative = ((byteRead & 0x80) != 0x00);
584 }
585
586 intValue <<= 8;
587 intValue |= (byteRead & 0xFF);
588 }
589
590 if (negative)
591 {
592 switch (length)
593 {
594 case 1:
595 intValue |= 0xFFFFFF00;
596 break;
597 case 2:
598 intValue |= 0xFFFF0000;
599 break;
600 case 3:
601 intValue |= 0xFF000000;
602 break;
603 }
604 }
605
606 totalBytesRead += length;
607 return intValue;
608 }
609
610
611
612 /**
613 * Reads an ASN.1 integer element from the input stream and returns the value
614 * as a {@code Long}.
615 *
616 * @return The {@code Long} value of the ASN.1 integer element read, or
617 * {@code null} if the end of the input stream was reached before any
618 * data could be read. If {@code null} is returned, then the input
619 * stream will have been closed.
620 *
621 * @throws IOException If a problem occurs while reading from the input
622 * stream, if the end of the input stream is reached in
623 * the middle of the element, or or if an attempt is
624 * made to read an element larger than the maximum
625 * allowed size.
626 *
627 * @throws ASN1Exception If the data read cannot be parsed as an ASN.1
628 * integer element.
629 */
630 public Long readLong()
631 throws IOException, ASN1Exception
632 {
633 final int type = readType();
634 if (type < 0)
635 {
636 return null;
637 }
638
639 final int length = readLength();
640 if ((length == 0) || (length > 8))
641 {
642 skip(length);
643 throw new ASN1Exception(ERR_LONG_INVALID_LENGTH.get(length));
644 }
645
646 boolean negative = false;
647 long longValue = 0;
648 for (int i=0; i < length; i++)
649 {
650 final int byteRead = read(false);
651 if (byteRead < 0)
652 {
653 throw new IOException(ERR_READ_END_BEFORE_VALUE_END.get());
654 }
655
656 if (i == 0)
657 {
658 negative = ((byteRead & 0x80) != 0x00);
659 }
660
661 longValue <<= 8;
662 longValue |= (byteRead & 0xFFL);
663 }
664
665 if (negative)
666 {
667 switch (length)
668 {
669 case 1:
670 longValue |= 0xFFFFFFFFFFFFFF00L;
671 break;
672 case 2:
673 longValue |= 0xFFFFFFFFFFFF0000L;
674 break;
675 case 3:
676 longValue |= 0xFFFFFFFFFF000000L;
677 break;
678 case 4:
679 longValue |= 0xFFFFFFFF00000000L;
680 break;
681 case 5:
682 longValue |= 0xFFFFFF0000000000L;
683 break;
684 case 6:
685 longValue |= 0xFFFF000000000000L;
686 break;
687 case 7:
688 longValue |= 0xFF00000000000000L;
689 break;
690 }
691 }
692
693 totalBytesRead += length;
694 return longValue;
695 }
696
697
698
699 /**
700 * Reads an ASN.1 null element from the input stream. No value will be
701 * returned but the null element will be consumed.
702 *
703 * @throws IOException If a problem occurs while reading from the input
704 * stream, if the end of the input stream is reached in
705 * the middle of the element, or or if an attempt is
706 * made to read an element larger than the maximum
707 * allowed size.
708 *
709 * @throws ASN1Exception If the data read cannot be parsed as an ASN.1 null
710 * element.
711 */
712 public void readNull()
713 throws IOException, ASN1Exception
714 {
715 final int type = readType();
716 if (type < 0)
717 {
718 return;
719 }
720
721 final int length = readLength();
722
723 if (length != 0)
724 {
725 skip(length);
726 throw new ASN1Exception(ERR_NULL_HAS_VALUE.get());
727 }
728 }
729
730
731
732 /**
733 * Reads an ASN.1 octet string element from the input stream and returns the
734 * value as a byte array.
735 *
736 * @return The byte array value of the ASN.1 octet string element read, or
737 * {@code null} if the end of the input stream was reached before any
738 * data could be read. If {@code null} is returned, then the input
739 * stream will have been closed.
740 *
741 * @throws IOException If a problem occurs while reading from the input
742 * stream, if the end of the input stream is reached in
743 * the middle of the element, or or if an attempt is
744 * made to read an element larger than the maximum
745 * allowed size.
746 */
747 public byte[] readBytes()
748 throws IOException
749 {
750 final int type = readType();
751 if (type < 0)
752 {
753 return null;
754 }
755
756 final int length = readLength();
757
758 int valueBytesRead = 0;
759 int bytesRemaining = length;
760 final byte[] value = new byte[length];
761 while (valueBytesRead < length)
762 {
763 final int bytesRead = read(false, value, valueBytesRead, bytesRemaining);
764 if (bytesRead < 0)
765 {
766 throw new IOException(ERR_READ_END_BEFORE_VALUE_END.get());
767 }
768
769 valueBytesRead += bytesRead;
770 bytesRemaining -= bytesRead;
771 }
772
773 totalBytesRead += length;
774 return value;
775 }
776
777
778
779 /**
780 * Reads an ASN.1 octet string element from the input stream and returns the
781 * value as a {@code String} using the UTF-8 encoding.
782 *
783 * @return The {@code String} value of the ASN.1 octet string element read,
784 * or {@code null} if the end of the input stream was reached before
785 * any data could be read. If {@code null} is returned, then the
786 * input stream will have been closed.
787 *
788 * @throws IOException If a problem occurs while reading from the input
789 * stream, if the end of the input stream is reached in
790 * the middle of the element, or or if an attempt is
791 * made to read an element larger than the maximum
792 * allowed size.
793 */
794 public String readString()
795 throws IOException
796 {
797 final int type = readType();
798 if (type < 0)
799 {
800 return null;
801 }
802
803 final int length = readLength();
804
805 int valueBytesRead = 0;
806 int bytesRemaining = length;
807 final byte[] value = new byte[length];
808 while (valueBytesRead < length)
809 {
810 final int bytesRead = read(false, value, valueBytesRead, bytesRemaining);
811 if (bytesRead < 0)
812 {
813 throw new IOException(ERR_READ_END_BEFORE_VALUE_END.get());
814 }
815
816 valueBytesRead += bytesRead;
817 bytesRemaining -= bytesRead;
818 }
819
820 totalBytesRead += length;
821 return toUTF8String(value);
822 }
823
824
825
826 /**
827 * Reads the beginning of an ASN.1 sequence from the input stream and
828 * returns a value that can be used to determine when the end of the sequence
829 * has been reached. Elements which are part of the sequence may be read from
830 * this ASN.1 stream reader until the
831 * {@link ASN1StreamReaderSequence#hasMoreElements} method returns
832 * {@code false}.
833 *
834 * @return An object which may be used to determine when the end of the
835 * sequence has been reached, or {@code null} if the end of the input
836 * stream was reached before any data could be read. If {@code null}
837 * is returned, then the input stream will have been closed.
838 *
839 * @throws IOException If a problem occurs while reading from the input
840 * stream, if the end of the input stream is reached in
841 * the middle of the element, or or if an attempt is
842 * made to read an element larger than the maximum
843 * allowed size.
844 */
845 public ASN1StreamReaderSequence beginSequence()
846 throws IOException
847 {
848 final int type = readType();
849 if (type < 0)
850 {
851 return null;
852 }
853
854 final int length = readLength();
855
856 return new ASN1StreamReaderSequence(this, (byte) type, length);
857 }
858
859
860
861 /**
862 * Reads the beginning of an ASN.1 set from the input stream and returns a
863 * value that can be used to determine when the end of the set has been
864 * reached. Elements which are part of the set may be read from this ASN.1
865 * stream reader until the {@link ASN1StreamReaderSet#hasMoreElements} method
866 * returns {@code false}.
867 *
868 * @return An object which may be used to determine when the end of the set
869 * has been reached, or {@code null} if the end of the input stream
870 * was reached before any data could be read. If {@code null} is
871 * returned, then the input stream will have been closed.
872 *
873 * @throws IOException If a problem occurs while reading from the input
874 * stream, if the end of the input stream is reached in
875 * the middle of the element, or or if an attempt is
876 * made to read an element larger than the maximum
877 * allowed size.
878 */
879 public ASN1StreamReaderSet beginSet()
880 throws IOException
881 {
882 final int type = readType();
883 if (type < 0)
884 {
885 return null;
886 }
887
888 final int length = readLength();
889
890 return new ASN1StreamReaderSet(this, (byte) type, length);
891 }
892
893
894
895 /**
896 * Reads a byte of data from the underlying input stream, optionally ignoring
897 * socket timeout exceptions.
898 *
899 * @param initial Indicates whether this is the initial read for an element.
900 *
901 * @return The byte read from the input stream, or -1 if the end of the
902 * input stream was reached.
903 *
904 * @throws IOException If a problem occurs while reading data.
905 */
906 private int read(final boolean initial)
907 throws IOException
908 {
909 try
910 {
911 return inputStream.read();
912 }
913 catch (SocketTimeoutException ste)
914 {
915 debugException(Level.FINEST, ste);
916
917 if ((initial && ignoreInitialSocketTimeout) ||
918 ((! initial) && ignoreSubsequentSocketTimeout))
919 {
920 while (true)
921 {
922 try
923 {
924 return inputStream.read();
925 }
926 catch (SocketTimeoutException ste2)
927 {
928 debugException(Level.FINEST, ste2);
929 }
930 }
931 }
932 else
933 {
934 throw ste;
935 }
936 }
937 }
938
939
940
941 /**
942 * Reads data from the underlying input stream, optionally ignoring socket
943 * timeout exceptions.
944 *
945 * @param initial Indicates whether this is the initial read for an element.
946 * @param buffer The buffer into which the data should be read.
947 * @param offset The position at which to start placing the data that was
948 * read.
949 * @param length The maximum number of bytes to read.
950 *
951 * @return The number of bytes read, or -1 if the end of the input stream
952 * was reached.
953 *
954 * @throws IOException If a problem occurs while reading data.
955 */
956 private int read(final boolean initial, final byte[] buffer, final int offset,
957 final int length)
958 throws IOException
959 {
960 try
961 {
962 return inputStream.read(buffer, offset, length);
963 }
964 catch (SocketTimeoutException ste)
965 {
966 debugException(Level.FINEST, ste);
967 if ((initial && ignoreInitialSocketTimeout) ||
968 ((! initial) && ignoreSubsequentSocketTimeout))
969 {
970 while (true)
971 {
972 try
973 {
974 return inputStream.read(buffer, offset, length);
975 }
976 catch (SocketTimeoutException ste2)
977 {
978 debugException(Level.FINEST, ste2);
979 }
980 }
981 }
982 else
983 {
984 throw ste;
985 }
986 }
987 }
988 }