1111package io .vertx .tests .http .fileupload ;
1212
1313import io .netty .handler .codec .DecoderException ;
14- import io .vertx .core .Context ;
15- import io .vertx .core .Future ;
16- import io .vertx .core .MultiMap ;
17- import io .vertx .core .Vertx ;
14+ import io .vertx .core .*;
1815import io .vertx .core .buffer .Buffer ;
1916import io .vertx .core .file .AsyncFile ;
20- import io .vertx .core .http .HttpConnection ;
21- import io .vertx .core .http .HttpHeaders ;
22- import io .vertx .core .http .HttpMethod ;
23- import io .vertx .core .http .RequestOptions ;
17+ import io .vertx .core .http .*;
18+ import io .vertx .core .internal .ContextInternal ;
19+ import io .vertx .core .streams .WriteStream ;
2420import io .vertx .test .core .TestUtils ;
2521import io .vertx .test .http .HttpTestBase ;
2622import org .junit .Rule ;
@@ -113,6 +109,51 @@ public void testFormUploadLargeFileStreamToDisk() {
113109 testFormUploadFile (TestUtils .randomAlphaString (4 * 1024 * 1024 ), false , true , false , false );
114110 }
115111
112+ @ Test
113+ public void testFormUploadVeryLargeFileStreamToDisk () {
114+ long one_kb = 1024L ;
115+ long one_mb = one_kb * 1024L ;
116+ long one_gb = one_mb * 1024L ;
117+ // long length = one_gb * 10L;
118+ long length = one_mb + 128 ; // 128MB
119+ Content content = new Content () {
120+ @ Override
121+ public long length () {
122+ return length ;
123+ }
124+ Buffer chunk_1k = TestUtils .randomBuffer (1024 );
125+ long chunkLength = chunk_1k .length ();
126+ private void pump (long remaining , WriteStream <Buffer > out , Promise <Void > done ) {
127+ while (!out .writeQueueFull ()) {
128+ if (remaining > chunkLength ) {
129+ out .write (chunk_1k );
130+ remaining -= chunkLength ;
131+ } else {
132+ Buffer last = chunk_1k .slice (0 , (int )remaining );
133+ out .write (last ).onComplete (done );
134+ return ;
135+ }
136+ }
137+ long propagated = remaining ;
138+ // System.out.println("Full - remaining is " + propagated + "M");
139+ out .drainHandler (v -> {
140+ pump (propagated , out , done );
141+ });
142+ }
143+ @ Override
144+ public Future <Void > write (WriteStream <Buffer > out ) {
145+ Promise <Void > done = ((ContextInternal )vertx .getOrCreateContext ()).promise ();
146+ pump (length , out , done );
147+ return done .future ();
148+ }
149+ @ Override
150+ public boolean verify (Buffer expected ) {
151+ return true ;
152+ }
153+ };
154+ testFormUploadFile ("tmp-0.txt" , "tmp-0.txt" , content , false , true , false , false );
155+ }
156+
116157 @ Test
117158 public void testFormUploadWithExtFilename () {
118159 testFormUploadFile (null , "%c2%a3%20and%20%e2%82%ac%20rates" , "the-content" , false , true , false , false );
@@ -182,13 +223,58 @@ private void testFormUploadFile(String contentStr, boolean includeLength, boolea
182223 testFormUploadFile ("tmp-0.txt" , "tmp-0.txt" , contentStr , includeLength , streamToDisk , abortClient , cancelStream );
183224 }
184225
226+ interface Content {
227+
228+ long length ();
229+
230+ Future <Void > write (WriteStream <Buffer > out );
231+
232+ boolean verify (Buffer expected );
233+ }
234+
185235 private void testFormUploadFile (String filename ,
186236 String extFilename ,
187237 String contentStr ,
188238 boolean includeLength ,
189239 boolean streamToDisk ,
190240 boolean abortClient ,
191241 boolean cancelStream ) {
242+ testFormUploadFile (filename , extFilename , Buffer .buffer (contentStr , "UTF-8" ), includeLength ,
243+ streamToDisk , abortClient , cancelStream );
244+ }
245+
246+ private void testFormUploadFile (String filename ,
247+ String extFilename ,
248+ Buffer contentBytes ,
249+ boolean includeLength ,
250+ boolean streamToDisk ,
251+ boolean abortClient ,
252+ boolean cancelStream ) {
253+ Content content = new Content () {
254+ @ Override
255+ public long length () {
256+ return contentBytes .length ();
257+ }
258+ @ Override
259+ public Future <Void > write (WriteStream <Buffer > out ) {
260+ return out .write (contentBytes );
261+ }
262+ @ Override
263+ public boolean verify (Buffer expected ) {
264+ return contentBytes .equals (expected );
265+ }
266+ };
267+ testFormUploadFile (filename , extFilename , content , includeLength , streamToDisk , abortClient , cancelStream );
268+
269+ }
270+
271+ private void testFormUploadFile (String filename ,
272+ String extFilename ,
273+ Content content ,
274+ boolean includeLength ,
275+ boolean streamToDisk ,
276+ boolean abortClient ,
277+ boolean cancelStream ) {
192278 String expectedFilename ;
193279 try {
194280 if (extFilename != null ) {
@@ -203,8 +289,6 @@ private void testFormUploadFile(String filename,
203289
204290 waitFor (2 );
205291
206- Buffer content = Buffer .buffer (contentStr );
207-
208292 AtomicInteger attributeCount = new AtomicInteger ();
209293
210294 AtomicReference <HttpConnection > clientConn = new AtomicReference <>();
@@ -216,6 +300,7 @@ private void testFormUploadFile(String filename,
216300 };
217301
218302 server .requestHandler (req -> {
303+
219304 Context requestContext = vertx .getOrCreateContext ();
220305 if (req .method () == HttpMethod .POST ) {
221306 assertEquals (req .path (), "/form" );
@@ -249,7 +334,7 @@ private void testFormUploadFile(String filename,
249334 });
250335 upload .endHandler (v -> {
251336 assertFalse (abortClient );
252- assertEquals (content , tot );
337+ assertTrue (content . verify ( tot ) );
253338 assertTrue (upload .isSizeAvailable ());
254339 assertEquals (content .length (), upload .size ());
255340 assertNull (upload .file ());
@@ -259,9 +344,15 @@ private void testFormUploadFile(String filename,
259344 uploadedFileName = new File (testDir , UUID .randomUUID ().toString ()).getPath ();
260345 upload .streamToFileSystem (uploadedFileName ).onComplete (ar -> {
261346 if (ar .succeeded ()) {
262- Buffer uploaded = vertx .fileSystem ().readFileBlocking (uploadedFileName );
263- assertEquals (content .length (), uploaded .length ());
264- assertEquals (content , uploaded );
347+ File f = new File (uploadedFileName );
348+ if (f .length () < 10 * 1024 * 1024 ) {
349+ Buffer uploaded = vertx .fileSystem ().readFileBlocking (uploadedFileName );
350+ assertEquals (content .length (), uploaded .length ());
351+ assertTrue (content .verify (uploaded ));
352+ } else {
353+ // We check the size only
354+ assertEquals (f .length (), content .length ());
355+ }
265356 AsyncFile file = upload .file ();
266357 assertNotNull (file );
267358 try {
@@ -278,7 +369,7 @@ private void testFormUploadFile(String filename,
278369 if (cancelStream ) {
279370 BooleanSupplier test = () -> {
280371 File f = new File (uploadedFileName );
281- if (f .length () == contentStr .length () / 2 ) {
372+ if (f .length () == content .length () / 2 ) {
282373 assertTrue (upload .cancelStreamToFileSystem ());
283374 long now = System .currentTimeMillis ();
284375 vertx .setPeriodic (10 , id -> {
@@ -314,42 +405,47 @@ private void testFormUploadFile(String filename,
314405 }
315406 });
316407
317- server .listen (testAddress ).onComplete (onSuccess (s -> {
318- client .request (new RequestOptions (requestOptions )
319- .setMethod (HttpMethod .POST )
320- .setURI ("/form" ))
321- .onComplete (onSuccess (req -> {
322- String boundary = "dLV9Wyq26L_-JQxk6ferf-RT153LhOO" ;
323- String epi = "\r \n " +
324- "--" + boundary + "--\r \n " ;
325- String pro = "--" + boundary + "\r \n " +
326- "Content-Disposition: form-data; name=\" file\" " + (filename == null ? "" : "; filename=\" " + filename + "\" " ) + (extFilename == null ? "" : "; filename*=\" UTF-8''" + extFilename ) + "\" \r \n " +
327- "Content-Type: image/gif\r \n " +
328- (includeLength ? "Content-Length: " + contentStr .length () + "\r \n " : "" ) +
329- "\r \n " ;
330- req .headers ().set ("content-length" , "" + (pro + contentStr + epi ).length ());
331- req .headers ().set ("content-type" , "multipart/form-data; boundary=" + boundary );
332- Future <Void > fut = req .end (pro + contentStr + epi );
333- if (abortClient ) {
334- fut .onComplete (onSuccess (v -> {
335- clientConn .set (req .connection ());
336- checkClose .run ();
337- }));
338- }
339- if (abortClient ) {
340- req .response ().onComplete (ar -> complete ());
341- } else {
342- req .response ().onComplete (onSuccess (resp -> {
343- assertEquals (200 , resp .statusCode ());
344- resp .bodyHandler (body -> {
345- assertEquals (0 , body .length ());
346- });
347- assertEquals (0 , attributeCount .get ());
348- complete ();
349- }));
350- }
408+ server .listen (testAddress ).await ();
409+
410+ HttpClientRequest request = client .request (new RequestOptions (requestOptions )
411+ .setMethod (HttpMethod .POST )
412+ .setURI ("/form" )).await ();
413+ String boundary = "dLV9Wyq26L_-JQxk6ferf-RT153LhOO" ;
414+ String epi = "\r \n " +
415+ "--" + boundary + "--\r \n " ;
416+ String pro = "--" + boundary + "\r \n " +
417+ "Content-Disposition: form-data; name=\" file\" " + (filename == null ? "" : "; filename=\" " + filename + "\" " ) + (extFilename == null ? "" : "; filename*=\" UTF-8''" + extFilename ) + "\" \r \n " +
418+ "Content-Type: image/gif\r \n " +
419+ (includeLength ? "Content-Length: " + Long .toUnsignedString (content .length ()) + "\r \n " : "" ) +
420+ "\r \n " ;
421+
422+ request .headers ().set (HttpHeaders .CONTENT_LENGTH , Long .toUnsignedString ((((long ) pro .length () + content .length () + (long ) epi .length ()))));
423+ // request.setChunked(true);
424+ request .headers ().set (HttpHeaders .CONTENT_TYPE , "multipart/form-data; boundary=" + boundary );
425+
426+ vertx .runOnContext (v1 -> {
427+ request .write (pro );
428+ Future <Void > fut = content .write (request ).compose (v -> request .end (epi ));
429+ if (abortClient ) {
430+ fut .onComplete (onSuccess (v -> {
431+ clientConn .set (request .connection ());
432+ checkClose .run ();
351433 }));
352- }));
434+ }
435+ if (abortClient ) {
436+ request .response ().onComplete (ar -> complete ());
437+ } else {
438+ request .response ().onComplete (onSuccess (resp -> {
439+ assertEquals (200 , resp .statusCode ());
440+ resp .bodyHandler (body -> {
441+ assertEquals (0 , body .length ());
442+ });
443+ assertEquals (0 , attributeCount .get ());
444+ complete ();
445+ }));
446+ }
447+ });
448+
353449 await ();
354450 }
355451
0 commit comments