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