1 module hunt.http.codec.http.model.MultiPartContentProvider; 2 3 // import hunt.util.Common; 4 // import hunt.http.utils.exception.CommonRuntimeException; 5 import hunt.logging; 6 7 import hunt.io.ByteBuffer; 8 9 // import java.io.ByteArrayOutputStream; 10 // import java.io.Closeable; 11 // module hunt.Exceptions; 12 // import java.nio.charset.StandardCharsets; 13 // import java.util; 14 // import hunt.concurrency.atomic.AtomicBoolean; 15 16 /** 17 * 18 */ 19 // class MultiPartContentProvider :AbstractTypedContentProvider : AsyncContentProvider, Closeable { 20 21 22 // private static byte[] COLON_SPACE_BYTES = new byte[]{':', ' '}; 23 // private static byte[] CR_LF_BYTES = new byte[]{'\r', '\n'}; 24 25 // private List<Part> parts = new ArrayList<>(); 26 // private ByteBuffer firstBoundary; 27 // private ByteBuffer middleBoundary; 28 // private ByteBuffer onlyBoundary; 29 // private ByteBuffer lastBoundary; 30 // private AtomicBoolean closed = new AtomicBoolean(); 31 // private Listener listener; 32 // private long length = -1; 33 34 // MultiPartContentProvider() { 35 // this(makeBoundary()); 36 // } 37 38 // MultiPartContentProvider(string boundary) { 39 // super("multipart/form-data; boundary=" + boundary); 40 // string firstBoundaryLine = "--" + boundary + "\r\n"; 41 // this.firstBoundary = BufferUtils.toBuffer(firstBoundaryLine.getBytes(StandardCharsets.US_ASCII)); 42 // string middleBoundaryLine = "\r\n" + firstBoundaryLine; 43 // this.middleBoundary = BufferUtils.toBuffer(middleBoundaryLine.getBytes(StandardCharsets.US_ASCII)); 44 // string onlyBoundaryLine = "--" + boundary + "--\r\n"; 45 // this.onlyBoundary = BufferUtils.toBuffer(onlyBoundaryLine.getBytes(StandardCharsets.US_ASCII)); 46 // string lastBoundaryLine = "\r\n" + onlyBoundaryLine; 47 // this.lastBoundary = BufferUtils.toBuffer(lastBoundaryLine.getBytes(StandardCharsets.US_ASCII)); 48 // } 49 50 // private static string makeBoundary() { 51 // Random random = new Random(); 52 // StringBuilder builder = new StringBuilder("HuntHttpClientBoundary"); 53 // int length = builder.length(); 54 // while (builder.length() < length + 16) { 55 // long rnd = random.nextLong(); 56 // builder.append(to!long(rnd < 0 ? -rnd : rnd, 36)); 57 // } 58 // builder.setLength(length + 16); 59 // return builder.toString(); 60 // } 61 62 // /** 63 // * <p>Adds a field part with the given {@code name} as field name, and the given 64 // * {@code content} as part content.</p> 65 // * 66 // * @param name the part name 67 // * @param content the part content 68 // * @param fields the headers associated with this part 69 // */ 70 // void addFieldPart(string name, ContentProvider content, HttpFields fields) { 71 // addPart(new Part(name, null, "text/plain", content, fields)); 72 // } 73 74 // /** 75 // * <p>Adds a file part with the given {@code name} as field name, the given 76 // * {@code fileName} as file name, and the given {@code content} as part content.</p> 77 // * 78 // * @param name the part name 79 // * @param fileName the file name associated to this part 80 // * @param content the part content 81 // * @param fields the headers associated with this part 82 // */ 83 // void addFilePart(string name, string fileName, ContentProvider content, HttpFields fields) { 84 // addPart(new Part(name, fileName, "application/octet-stream", content, fields)); 85 // } 86 87 // private void addPart(Part part) { 88 // parts.add(part); 89 // version(HUNT_DEBUG) 90 // tracef("Added %s", part); 91 // } 92 93 // override 94 // void setListener(Listener listener) { 95 // this.listener = listener; 96 // if (closed.get()) 97 // this.length = calculateLength(); 98 // } 99 100 // private long calculateLength() { 101 // // Compute the length, if possible. 102 // if (parts.isEmpty()) { 103 // return onlyBoundary.remaining(); 104 // } else { 105 // long result = 0; 106 // for (int i = 0; i < parts.size(); ++i) { 107 // result += (i == 0) ? firstBoundary.remaining() : middleBoundary.remaining(); 108 // Part part = parts.get(i); 109 // long partLength = part.length; 110 // result += partLength; 111 // if (partLength < 0) { 112 // result = -1; 113 // break; 114 // } 115 // } 116 // if (result > 0) 117 // result += lastBoundary.remaining(); 118 // return result; 119 // } 120 // } 121 122 // override 123 // long getLength() { 124 // return length; 125 // } 126 127 // override 128 // Iterator!ByteBuffer iterator() { 129 // return new MultiPartIterator(); 130 // } 131 132 // override 133 // void close() { 134 // closed.compareAndSet(false, true); 135 // } 136 137 // private static class Part { 138 // private string name; 139 // private string fileName; 140 // private string contentType; 141 // private ContentProvider content; 142 // private HttpFields fields; 143 // private ByteBuffer headers; 144 // private long length; 145 146 // private Part(string name, string fileName, string contentType, ContentProvider content, HttpFields fields) { 147 // this.name = name; 148 // this.fileName = fileName; 149 // this.contentType = contentType; 150 // this.content = content; 151 // this.fields = fields; 152 // this.headers = headers(); 153 // this.length = content.getLength() < 0 ? -1 : headers.remaining() + content.getLength(); 154 // } 155 156 // private ByteBuffer headers() { 157 // try { 158 // // Compute the Content-Disposition. 159 // string contentDisposition = "Content-Disposition: form-data; name=\"" + name + "\""; 160 // if (fileName != null) 161 // contentDisposition += "; filename=\"" + fileName + "\""; 162 // contentDisposition += "\r\n"; 163 164 // // Compute the Content-Type. 165 // string contentType = fields == null ? null : fields.get(HttpHeader.CONTENT_TYPE); 166 // if (contentType == null) { 167 // if (content instanceof Typed) 168 // contentType = ((Typed) content).getContentType(); 169 // else 170 // contentType = this.contentType; 171 // } 172 // contentType = "Content-Type: " + contentType + "\r\n"; 173 174 // if (fields == null || fields.size() == 0) { 175 // string headers = contentDisposition; 176 // headers += contentType; 177 // headers += "\r\n"; 178 // return BufferUtils.toBuffer(headers.getBytes(StandardCharsets.UTF_8)); 179 // } 180 181 // ByteArrayOutputStream buffer = new ByteArrayOutputStream((fields.size() + 1) * contentDisposition.length()); 182 // buffer.write(contentDisposition.getBytes(StandardCharsets.UTF_8)); 183 // buffer.write(contentType.getBytes(StandardCharsets.UTF_8)); 184 // for (HttpField field : fields) { 185 // if (HttpHeader.CONTENT_TYPE.equals(field.getHeader())) 186 // continue; 187 // buffer.write(field.getName().getBytes(StandardCharsets.US_ASCII)); 188 // buffer.write(COLON_SPACE_BYTES); 189 // string value = field.getValue(); 190 // if (value != null) 191 // buffer.write(value.getBytes(StandardCharsets.UTF_8)); 192 // buffer.write(CR_LF_BYTES); 193 // } 194 // buffer.write(CR_LF_BYTES); 195 // return BufferUtils.toBuffer(buffer.toByteArray()); 196 // } catch (IOException x) { 197 // throw new CommonRuntimeException(x); 198 // } 199 // } 200 201 // override 202 // string toString() { 203 // return format("%s@%x[name=%s,fileName=%s,length=%d,headers=%s]", 204 // typeof(this).stringof, 205 // toHash(), 206 // name, 207 // fileName, 208 // content.getLength(), 209 // fields); 210 // } 211 // } 212 213 // private class MultiPartIterator : Iterator!ByteBuffer, Synchronizable, Callback, Closeable { 214 // private Iterator!ByteBuffer iterator; 215 // private int index; 216 // private State state = State.FIRST_BOUNDARY; 217 218 // override 219 // bool hasNext() { 220 // return state != State.COMPLETE; 221 // } 222 223 // override 224 // ByteBuffer next() { 225 // while (true) { 226 // switch (state) { 227 // case FIRST_BOUNDARY: { 228 // if (parts.isEmpty()) { 229 // state = State.COMPLETE; 230 // return onlyBoundary.slice(); 231 // } else { 232 // state = State.HEADERS; 233 // return firstBoundary.slice(); 234 // } 235 // } 236 // case HEADERS: { 237 // Part part = parts.get(index); 238 // ContentProvider content = part.content; 239 // if (content instanceof AsyncContentProvider) 240 // ((AsyncContentProvider) content).setListener(listener); 241 // iterator = content.iterator(); 242 // state = State.CONTENT; 243 // return part.headers.slice(); 244 // } 245 // case CONTENT: { 246 // if (iterator.hasNext()) 247 // return iterator.next(); 248 // ++index; 249 // if (index == parts.size()) 250 // state = State.LAST_BOUNDARY; 251 // else 252 // state = State.MIDDLE_BOUNDARY; 253 // break; 254 // } 255 // case MIDDLE_BOUNDARY: { 256 // state = State.HEADERS; 257 // return middleBoundary.slice(); 258 // } 259 // case LAST_BOUNDARY: { 260 // state = State.COMPLETE; 261 // return lastBoundary.slice(); 262 // } 263 // case COMPLETE: { 264 // throw new NoSuchElementException(); 265 // } 266 // } 267 // } 268 // } 269 270 // override 271 // Object getLock() { 272 // if (iterator instanceof Synchronizable) 273 // return ((Synchronizable) iterator).getLock(); 274 // return this; 275 // } 276 277 // override 278 // void succeeded() { 279 // if (iterator instanceof Callback) 280 // ((Callback) iterator).succeeded(); 281 // } 282 283 // override 284 // void failed(Exception x) { 285 // if (iterator instanceof Callback) 286 // ((Callback) iterator).failed(x); 287 // } 288 289 // override 290 // void close() throws IOException { 291 // if (iterator instanceof Closeable) 292 // ((Closeable) iterator).close(); 293 // } 294 // } 295 296 // private enum State { 297 // FIRST_BOUNDARY, HEADERS, CONTENT, MIDDLE_BOUNDARY, LAST_BOUNDARY, COMPLETE 298 // } 299 // }