@@ -4,21 +4,23 @@ use crates_io::{
44 models:: Version ,
55 schema:: { crates, readme_renderings, versions} ,
66} ;
7- use std:: path:: PathBuf ;
8- use std:: { io:: Read , path:: Path , sync:: Arc } ;
7+ use futures_util:: { StreamExt , TryStreamExt } ;
8+ use std:: path:: { Path , PathBuf } ;
9+ use std:: { future, sync:: Arc } ;
10+ use tokio:: io:: { AsyncRead , AsyncReadExt } ;
11+ use tokio_util:: compat:: FuturesAsyncReadCompatExt ;
912
13+ use async_compression:: tokio:: bufread:: GzipDecoder ;
1014use chrono:: { NaiveDateTime , Utc } ;
1115use crates_io:: storage:: Storage ;
12- use crates_io:: tasks:: spawn_blocking;
1316use crates_io_markdown:: text_to_html;
1417use crates_io_tarball:: { Manifest , StringOrBool } ;
1518use diesel:: prelude:: * ;
1619use diesel_async:: async_connection_wrapper:: AsyncConnectionWrapper ;
1720use diesel_async:: { AsyncPgConnection , RunQueryDsl } ;
18- use flate2:: read:: GzDecoder ;
1921use reqwest:: { header, Client } ;
2022use std:: str:: FromStr ;
21- use tar :: { self , Archive } ;
23+ use tokio_tar :: { self , Archive } ;
2224
2325const USER_AGENT : & str = "crates-admin" ;
2426
@@ -168,22 +170,25 @@ async fn get_readme(
168170 ) ) ;
169171 }
170172
171- let body = response. bytes ( ) . await ?;
172-
173- spawn_blocking ( move || {
174- let reader = GzDecoder :: new ( & * body) ;
175- let archive = Archive :: new ( reader) ;
176- render_pkg_readme ( archive, & pkg_name)
177- } )
178- . await ?
173+ let reader = response
174+ . bytes_stream ( )
175+ . map_err ( |e| std:: io:: Error :: new ( std:: io:: ErrorKind :: Other , e) )
176+ . into_async_read ( ) ;
177+ let reader = GzipDecoder :: new ( reader. compat ( ) ) ;
178+ let archive = Archive :: new ( reader) ;
179+ render_pkg_readme ( archive, & pkg_name) . await
179180}
180181
181- fn render_pkg_readme < R : Read > ( mut archive : Archive < R > , pkg_name : & str ) -> anyhow:: Result < String > {
182+ async fn render_pkg_readme < R : AsyncRead + Unpin > (
183+ mut archive : Archive < R > ,
184+ pkg_name : & str ,
185+ ) -> anyhow:: Result < String > {
182186 let mut entries = archive. entries ( ) . context ( "Invalid tar archive entries" ) ?;
183187
184188 let manifest: Manifest = {
185189 let path = format ! ( "{pkg_name}/Cargo.toml" ) ;
186190 let contents = find_file_by_path ( & mut entries, Path :: new ( & path) )
191+ . await
187192 . context ( "Failed to read Cargo.toml file" ) ?;
188193
189194 Manifest :: from_str ( & contents) . context ( "Failed to parse manifest file" ) ?
@@ -207,6 +212,7 @@ fn render_pkg_readme<R: Read>(mut archive: Archive<R>, pkg_name: &str) -> anyhow
207212
208213 let path = Path :: new ( pkg_name) . join ( & readme_path) ;
209214 let contents = find_file_by_path ( & mut entries, Path :: new ( & path) )
215+ . await
210216 . with_context ( || format ! ( "Failed to read {} file" , readme_path. display( ) ) ) ?;
211217
212218 // pkg_path_in_vcs Unsupported from admin::render_readmes. See #4095
@@ -226,20 +232,20 @@ fn render_pkg_readme<R: Read>(mut archive: Archive<R>, pkg_name: &str) -> anyhow
226232}
227233
228234/// Search an entry by its path in a Tar archive.
229- fn find_file_by_path < R : Read > (
230- entries : & mut tar :: Entries < ' _ , R > ,
235+ async fn find_file_by_path < R : AsyncRead + Unpin > (
236+ entries : & mut tokio_tar :: Entries < R > ,
231237 path : & Path ,
232238) -> anyhow:: Result < String > {
233239 let mut file = entries
234- . filter_map ( |entry| entry. ok ( ) )
235- . find ( |file| match file. path ( ) {
236- Ok ( p) => p == path,
237- Err ( _) => false ,
238- } )
240+ . filter_map ( |entry| future:: ready ( entry. ok ( ) ) )
241+ . filter ( |entry| future:: ready ( entry. path ( ) . is_ok_and ( |p| p == path) ) )
242+ . next ( )
243+ . await
239244 . ok_or_else ( || anyhow ! ( "Failed to find tarball entry: {}" , path. display( ) ) ) ?;
240245
241246 let mut contents = String :: new ( ) ;
242247 file. read_to_string ( & mut contents)
248+ . await
243249 . context ( "Failed to read file contents" ) ?;
244250
245251 Ok ( contents)
@@ -252,8 +258,8 @@ pub mod tests {
252258
253259 use super :: render_pkg_readme;
254260
255- #[ test]
256- fn test_render_pkg_readme ( ) {
261+ #[ tokio :: test]
262+ async fn test_render_pkg_readme ( ) {
257263 let serialized_archive = TarballBuilder :: new ( )
258264 . add_file (
259265 "foo-0.0.1/Cargo.toml" ,
@@ -267,13 +273,14 @@ readme = "README.md"
267273 . add_file ( "foo-0.0.1/README.md" , b"readme" )
268274 . build_unzipped ( ) ;
269275
270- let result =
271- render_pkg_readme ( tar:: Archive :: new ( & * serialized_archive) , "foo-0.0.1" ) . unwrap ( ) ;
276+ let result = render_pkg_readme ( tokio_tar:: Archive :: new ( & * serialized_archive) , "foo-0.0.1" )
277+ . await
278+ . unwrap ( ) ;
272279 assert ! ( result. contains( "readme" ) )
273280 }
274281
275- #[ test]
276- fn test_render_pkg_no_readme ( ) {
282+ #[ tokio :: test]
283+ async fn test_render_pkg_no_readme ( ) {
277284 let serialized_archive = TarballBuilder :: new ( )
278285 . add_file (
279286 "foo-0.0.1/Cargo.toml" ,
@@ -283,14 +290,13 @@ readme = "README.md"
283290 )
284291 . build_unzipped ( ) ;
285292
286- assert_err ! ( render_pkg_readme(
287- tar:: Archive :: new( & * serialized_archive) ,
288- "foo-0.0.1"
289- ) ) ;
293+ assert_err ! (
294+ render_pkg_readme( tokio_tar:: Archive :: new( & * serialized_archive) , "foo-0.0.1" ) . await
295+ ) ;
290296 }
291297
292- #[ test]
293- fn test_render_pkg_implicit_readme ( ) {
298+ #[ tokio :: test]
299+ async fn test_render_pkg_implicit_readme ( ) {
294300 let serialized_archive = TarballBuilder :: new ( )
295301 . add_file (
296302 "foo-0.0.1/Cargo.toml" ,
@@ -303,13 +309,14 @@ version = "0.0.1"
303309 . add_file ( "foo-0.0.1/README.md" , b"readme" )
304310 . build_unzipped ( ) ;
305311
306- let result =
307- render_pkg_readme ( tar:: Archive :: new ( & * serialized_archive) , "foo-0.0.1" ) . unwrap ( ) ;
312+ let result = render_pkg_readme ( tokio_tar:: Archive :: new ( & * serialized_archive) , "foo-0.0.1" )
313+ . await
314+ . unwrap ( ) ;
308315 assert ! ( result. contains( "readme" ) )
309316 }
310317
311- #[ test]
312- fn test_render_pkg_readme_w_link ( ) {
318+ #[ tokio :: test]
319+ async fn test_render_pkg_readme_w_link ( ) {
313320 let serialized_archive = TarballBuilder :: new ( )
314321 . add_file (
315322 "foo-0.0.1/Cargo.toml" ,
@@ -324,13 +331,14 @@ repository = "https://github.com/foo/foo"
324331 . add_file ( "foo-0.0.1/README.md" , b"readme [link](./Other.md)" )
325332 . build_unzipped ( ) ;
326333
327- let result =
328- render_pkg_readme ( tar:: Archive :: new ( & * serialized_archive) , "foo-0.0.1" ) . unwrap ( ) ;
334+ let result = render_pkg_readme ( tokio_tar:: Archive :: new ( & * serialized_archive) , "foo-0.0.1" )
335+ . await
336+ . unwrap ( ) ;
329337 assert ! ( result. contains( "\" https://github.com/foo/foo/blob/HEAD/./Other.md\" " ) )
330338 }
331339
332- #[ test]
333- fn test_render_pkg_readme_not_at_root ( ) {
340+ #[ tokio :: test]
341+ async fn test_render_pkg_readme_not_at_root ( ) {
334342 let serialized_archive = TarballBuilder :: new ( )
335343 . add_file (
336344 "foo-0.0.1/Cargo.toml" ,
@@ -348,8 +356,9 @@ repository = "https://github.com/foo/foo"
348356 )
349357 . build_unzipped ( ) ;
350358
351- let result =
352- render_pkg_readme ( tar:: Archive :: new ( & * serialized_archive) , "foo-0.0.1" ) . unwrap ( ) ;
359+ let result = render_pkg_readme ( tokio_tar:: Archive :: new ( & * serialized_archive) , "foo-0.0.1" )
360+ . await
361+ . unwrap ( ) ;
353362 assert ! ( result. contains( "docs/readme" ) ) ;
354363 assert ! ( result. contains( "\" https://github.com/foo/foo/blob/HEAD/docs/./Other.md\" " ) )
355364 }
0 commit comments