1 module hunt.http.codec.http.model.InputStreamContentProvider;
2 
3 // import hunt.util.Common;
4 // import hunt.io.BufferUtils;
5 // import hunt.stream;
6 // import hunt.logging;
7 
8 
9 // import java.io.Closeable;
10 // import java.io.InputStream;
11 // import hunt.io.ByteBuffer;
12 // import java.util.Iterator;
13 // import java.util.NoSuchElementException;
14 
15 /**
16  * A {@link ContentProvider} for an {@link InputStream}.
17  * <p>
18  * The input stream is read once and therefore fully consumed.
19  * Invocations to the {@link #iterator()} method after the first will return an "empty" iterator
20  * because the stream has been consumed on the first invocation.
21  * <p>
22  * However, it is possible for subclasses to override {@link #onRead(byte[], int, int)} to copy
23  * the content read from the stream to another location (for example a file), and be able to
24  * support multiple invocations of {@link #iterator()}, returning the iterator provided by this
25  * class on the first invocation, and an iterator on the bytes copied to the other location
26  * for subsequent invocations.
27  * <p>
28  * It is possible to specify, at the constructor, a buffer size used to read content from the
29  * stream, by default 4096 bytes.
30  * <p>
31  * The {@link InputStream} passed to the constructor is by default closed when is it fully
32  * consumed (or when an exception is thrown while reading it), unless otherwise specified
33  * to the {@link #InputStreamContentProvider(InputStream, int, bool) constructor}.
34  */
35 // class InputStreamContentProvider : ContentProvider, Callback, Closeable {
36     
37 
38 //     private InputStreamContentProviderIterator iterator = new InputStreamContentProviderIterator();
39 //     private InputStream stream;
40 //     private int bufferSize;
41 //     private bool autoClose;
42 
43 //     this(InputStream stream) {
44 //         this(stream, 4096);
45 //     }
46 
47 //     this(InputStream stream, int bufferSize) {
48 //         this(stream, bufferSize, true);
49 //     }
50 
51 //     this(InputStream stream, int bufferSize, bool autoClose) {
52 //         this.stream = stream;
53 //         this.bufferSize = bufferSize;
54 //         this.autoClose = autoClose;
55 //     }
56 
57 //     override
58 //     long getLength() {
59 //         return -1;
60 //     }
61 
62 //     /**
63 //      * Callback method invoked just after having read from the stream,
64 //      * but before returning the iteration element (a {@link ByteBuffer}
65 //      * to the caller.
66 //      * <p>
67 //      * Subclasses may override this method to copy the content read from
68 //      * the stream to another location (a file, or in memory if the content
69 //      * is known to fit).
70 //      *
71 //      * @param buffer the byte array containing the bytes read
72 //      * @param offset the offset from where bytes should be read
73 //      * @param length the length of the bytes read
74 //      * @return a {@link ByteBuffer} wrapping the byte array
75 //      */
76 //     protected ByteBuffer onRead(byte[] buffer, int offset, int length) {
77 //         if (length <= 0)
78 //             return BufferUtils.EMPTY_BUFFER;
79 //         return BufferUtils.toBuffer(buffer, offset, length);
80 //     }
81 
82 //     /**
83 //      * Callback method invoked when an exception is thrown while reading
84 //      * from the stream.
85 //      *
86 //      * @param failure the exception thrown while reading from the stream.
87 //      */
88 //     protected void onReadFailure(Exception failure) {
89 //     }
90 
91 //     override
92 //     Iterator!ByteBuffer iterator() {
93 //         return iterator;
94 //     }
95 
96 //     override
97 //     void close() {
98 //         if (autoClose) {
99 //             IO.close(stream);
100 //         }
101 //     }
102 
103 //     override
104 //     void failed(Exception failure) {
105 //         // TODO: forward the failure to the iterator.
106 //         close();
107 //     }
108 
109 //     /**
110 //      * Iterating over an {@link InputStream} is tricky, because {@link #hasNext()} must return false
111 //      * if the stream reads -1. However, we don't know what to return until we read the stream, which
112 //      * means that stream reading must be performed by {@link #hasNext()}, which introduces a side-effect
113 //      * on what is supposed to be a simple query method (with respect to the Query Command Separation
114 //      * Principle).
115 //      * <p>
116 //      * Alternatively, we could return {@code true} from {@link #hasNext()} even if we don't know that
117 //      * we will read -1, but then when {@link #next()} reads -1 it must return an empty buffer.
118 //      * However this is problematic, since GETs with no content indication would become GET with chunked
119 //      * content, and not understood by servers.
120 //      * <p>
121 //      * Therefore we need to make sure that {@link #hasNext()} does not perform any side effect (so that
122 //      * it can be called multiple times) until {@link #next()} is called.
123 //      */
124 //     private class InputStreamContentProviderIterator : Iterator!ByteBuffer, Closeable {
125 //         private Exception failure;
126 //         private ByteBuffer buffer;
127 //         private bool hasNext;
128 
129 //         override
130 //         bool hasNext() {
131 //             try {
132 //                 if (hasNext != null)
133 //                     return hasNext;
134 
135 //                 byte[] bytes = new byte[bufferSize];
136 //                 int read = stream.read(bytes);
137 //                 version(HUNT_DEBUG)
138 //                     tracef("Read %s bytes from %s", read, stream);
139 //                 if (read > 0) {
140 //                     hasNext = bool.TRUE;
141 //                     buffer = onRead(bytes, 0, read);
142 //                     return true;
143 //                 } else if (read < 0) {
144 //                     hasNext = bool.FALSE;
145 //                     buffer = null;
146 //                     close();
147 //                     return false;
148 //                 } else {
149 //                     hasNext = bool.TRUE;
150 //                     buffer = BufferUtils.EMPTY_BUFFER;
151 //                     return true;
152 //                 }
153 //             } catch (Exception x) {
154 //                 version(HUNT_DEBUG) {
155 //                     tracef("input stream exception", x);
156 //                 }
157 //                 if (failure == null) {
158 //                     failure = x;
159 //                     onReadFailure(x);
160 //                     // Signal we have more content to cause a call to
161 //                     // next() which will throw NoSuchElementException.
162 //                     hasNext = bool.TRUE;
163 //                     buffer = null;
164 //                     close();
165 //                     return true;
166 //                 }
167 //                 throw new IllegalStateException("");
168 //             }
169 //         }
170 
171 //         override
172 //         ByteBuffer next() {
173 //             if (failure != null) {
174 //                 // Consume the failure so that calls to hasNext() will return false.
175 //                 hasNext = bool.FALSE;
176 //                 buffer = null;
177 //                 throw (NoSuchElementException) new NoSuchElementException().initCause(failure);
178 //             }
179 //             if (!hasNext())
180 //                 throw new NoSuchElementException();
181 
182 //             ByteBuffer result = buffer;
183 //             if (result == null) {
184 //                 hasNext = bool.FALSE;
185 //                 buffer = null;
186 //                 throw new NoSuchElementException();
187 //             } else {
188 //                 hasNext = null;
189 //                 buffer = null;
190 //                 return result;
191 //             }
192 //         }
193 
194 //         override
195 //         void remove() {
196 //             throw new UnsupportedOperationException();
197 //         }
198 
199 //         override
200 //         void close() {
201 //             InputStreamContentProvider.this.close();
202 //         }
203 //     }
204 // }