22// for details. All rights reserved. Use of this source code is governed by a
33// BSD-style license that can be found in the LICENSE file.
44
5+ import 'dart:async' ;
56import 'dart:convert' ;
67import 'dart:io' ;
78
@@ -28,10 +29,16 @@ Future<int> startImageProxy() async {
2829 return port! ;
2930}
3031
32+ Stream <List <int >> infiniteStream () async * {
33+ while (true ) {
34+ yield List .generate (1000 , (i) => 0 );
35+ }
36+ }
37+
3138Future <int > startImageServer () async {
3239 var i = 0 ;
3340 final server = await shelf_io.serve (
34- (shelf.Request request) {
41+ (shelf.Request request) async {
3542 switch (request.url.path) {
3643 case 'path/to/image.jpg' :
3744 return shelf.Response .ok (
@@ -73,6 +80,42 @@ Future<int> startImageServer() async {
7380 File (jpgImagePath).readAsBytesSync (),
7481 headers: {'content-type' : 'image/jpeg' },
7582 );
83+ case 'timeout' :
84+ await Future .delayed (Duration (hours: 1 ));
85+ return shelf.Response .notFound ('Not found' );
86+ case 'timeoutstreaming' :
87+ late final StreamController lateStreamController;
88+ lateStreamController = StreamController (
89+ onListen: () async {
90+ // Return a single byte, and then stall.
91+ lateStreamController.add ([1 ]);
92+ await Future .delayed (Duration (hours: 1 ));
93+ await lateStreamController.close ();
94+ },
95+ );
96+ return shelf.Response .ok (
97+ lateStreamController.stream,
98+ headers: {'content-type' : 'image/jpeg' },
99+ );
100+ case 'okstreaming' :
101+ return shelf.Response .ok (
102+ // Has no content-length
103+ File (jpgImagePath).openRead (),
104+ headers: {'content-type' : 'image/jpeg' },
105+ );
106+ case 'toobig' :
107+ return shelf.Response .ok (
108+ infiniteStream (),
109+ headers: {
110+ 'content-type' : 'image/jpeg' ,
111+ 'content-length' : '100000000' ,
112+ },
113+ );
114+ case 'toobigstreaming' :
115+ return shelf.Response .ok (
116+ infiniteStream (),
117+ headers: {'content-type' : 'image/jpeg' },
118+ );
76119 default :
77120 return shelf.Response .notFound ('Not found' );
78121 }
@@ -168,6 +211,23 @@ Future<void> main() async {
168211 final expected = sha256.convert (File (svgImagePath).readAsBytesSync ());
169212 expect (hash, expected);
170213 }
214+
215+ {
216+ final response = await getImage (
217+ day: today,
218+ imageProxyPort: imageProxyPort,
219+ imageServerPort: imageServerPort,
220+ // Gives no content-length
221+ pathToImage: 'okstreaming' ,
222+ );
223+ expect (response.statusCode, 200 );
224+ expect (response.headers['content-type' ]! .single, 'image/jpeg' );
225+ final jpgFile = File (jpgImagePath).readAsBytesSync ();
226+ expect (response.contentLength, jpgFile.length);
227+ final hash = await sha256.bind (response).single;
228+ final expected = sha256.convert (jpgFile);
229+ expect (hash, expected);
230+ }
171231 });
172232
173233 test ('Fails on days outside recent range' , () async {
@@ -327,4 +387,62 @@ Future<void> main() async {
327387 expect (hash, expected);
328388 }
329389 });
390+
391+ test ('times out' , () async {
392+ final imageProxyPort = await startImageProxy ();
393+ final imageServerPort = await startImageServer ();
394+ {
395+ final response = await getImage (
396+ imageProxyPort: imageProxyPort,
397+ imageServerPort: imageServerPort,
398+ day: today,
399+ pathToImage: 'timeout' ,
400+ );
401+
402+ expect (response.statusCode, 400 );
403+ // The proxy doesn't cache as long time as the original.
404+ expect (await Utf8Codec ().decodeStream (response), 'No response' );
405+ }
406+ {
407+ final response = await getImage (
408+ imageProxyPort: imageProxyPort,
409+ imageServerPort: imageServerPort,
410+ day: today,
411+ pathToImage: 'timeoutstreaming' ,
412+ );
413+
414+ expect (response.statusCode, 400 );
415+ // The proxy doesn't cache as long time as the original.
416+ expect (await Utf8Codec ().decodeStream (response), 'No response' );
417+ }
418+ });
419+
420+ test ('protects against too big files' , () async {
421+ final imageProxyPort = await startImageProxy ();
422+ final imageServerPort = await startImageServer ();
423+ {
424+ final response = await getImage (
425+ imageProxyPort: imageProxyPort,
426+ imageServerPort: imageServerPort,
427+ day: today,
428+ pathToImage: 'toobig' ,
429+ );
430+
431+ expect (response.statusCode, 400 );
432+ // The proxy doesn't cache as long time as the original.
433+ expect (await Utf8Codec ().decodeStream (response), 'Image too large' );
434+ }
435+ {
436+ final response = await getImage (
437+ imageProxyPort: imageProxyPort,
438+ imageServerPort: imageServerPort,
439+ day: today,
440+ pathToImage: 'toobigstreaming' ,
441+ );
442+
443+ expect (response.statusCode, 400 );
444+ // The proxy doesn't cache as long time as the original.
445+ expect (await Utf8Codec ().decodeStream (response), 'Image too large' );
446+ }
447+ });
330448}
0 commit comments