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 // }