@@ -464,6 +464,11 @@ fn follow_cdn_redirect(
464464 dest : & Path ,
465465 job : & media:: CdnRedirect ,
466466) -> Result < String , MtprotoError > {
467+ if job. offset != 0 {
468+ return Err ( MtprotoError :: Message ( "CDN resume requires origin download" . into ( ) ) ) ;
469+ }
470+ let client = get_client ( handle) ?;
471+ let session_id = client. data . lock ( ) . home_session_id ;
467472 let pem = with_client_mut ( handle, |state| {
468473 let cfg: CdnConfig =
469474 api_invoke:: invoke_api ( & mut state. snapshot , api_id, media:: get_cdn_config_request ( ) ) ?;
@@ -503,63 +508,85 @@ fn follow_cdn_redirect(
503508 last_io : std:: time:: Instant :: now ( ) ,
504509 } ) ;
505510 let staged = media:: StagedDownload :: new ( dest) ?;
506- let path = crate :: rpc:: with_live_transport ( & mut slot, || {
511+ crate :: rpc:: with_live_transport ( & mut slot, || {
507512 crate :: rpc:: with_rpc_timeout_secs ( 45 , || {
508- use std:: io:: { Seek , SeekFrom , Write } ;
509- let mut out = std:: fs:: File :: create ( staged. path ( ) )
510- . map_err ( |e| MtprotoError :: Message ( e. to_string ( ) ) ) ?;
511- let mut offset = job. offset ;
512- loop {
513- if media:: download_cancelled ( dest) || crate :: request_control:: check ( ) . is_err ( ) {
514- return Err ( MtprotoError :: Message ( "cancelled" . into ( ) ) ) ;
515- }
516- let mut part = loop {
517- let response: UploadCdnFile = api_invoke:: invoke_api_without_updates (
518- & mut snap,
519- api_id,
520- media:: get_cdn_file_request ( job. file_token . clone ( ) , offset, media:: CHUNK ) ,
521- ) ?;
522- match media:: cdn_file_bytes ( response) ? {
523- Ok ( bytes) => break bytes,
524- Err ( request_token) => {
525- let _ = with_client_mut ( handle, |state| {
526- api_invoke:: invoke_api :: < _ , Vector < Box < FileHash > > > (
527- & mut state. snapshot ,
528- api_id,
529- media:: reupload_cdn_file_request (
530- job. file_token . clone ( ) ,
531- request_token,
532- ) ,
533- )
534- } ) ?;
535- }
536- }
537- } ;
538- media:: decrypt_cdn_part (
539- & job. encryption_key ,
540- & job. encryption_iv ,
541- offset,
542- & mut part,
513+ receive_cdn_parts ( job, dest, & staged, |offset| {
514+ let response: UploadCdnFile = api_invoke:: invoke_api_without_updates (
515+ & mut snap, api_id,
516+ media:: get_cdn_file_request ( job. file_token . clone ( ) , offset, media:: CHUNK ) ,
543517 ) ?;
544- out. seek ( SeekFrom :: Start ( offset as u64 ) )
545- . map_err ( |e| MtprotoError :: Message ( e. to_string ( ) ) ) ?;
546- out. write_all ( & part)
547- . map_err ( |e| MtprotoError :: Message ( e. to_string ( ) ) ) ?;
548- if ( part. len ( ) as i32 ) < media:: CHUNK {
549- break ;
550- }
551- offset += part. len ( ) as i64 ;
552- media:: notify_progress ( & dest. display ( ) . to_string ( ) , offset, 0 ) ;
553- }
554- out. flush ( )
555- . map_err ( |e| MtprotoError :: Message ( e. to_string ( ) ) ) ?;
556- Ok ( staged. path ( ) . display ( ) . to_string ( ) )
518+ media:: cdn_file_bytes ( response)
519+ } , |request_token| {
520+ with_client_mut ( handle, |state| {
521+ api_invoke:: invoke_api :: < _ , Vector < Box < FileHash > > > (
522+ & mut state. snapshot , api_id,
523+ media:: reupload_cdn_file_request ( job. file_token . clone ( ) , request_token) ,
524+ )
525+ } ) . map ( |_| ( ) )
526+ } )
557527 } )
558528 } ) ?;
559- let client = get_client ( handle) ?;
560- let session_id = client. data . lock ( ) . home_session_id ;
561529 publish_media ( & client, session_id, staged, dest) ?;
562- let _ = path;
563530 Ok ( dest. display ( ) . to_string ( ) )
564531}
565532
533+ fn cdn_or_origin < T > (
534+ cdn : impl FnOnce ( ) -> Result < T , MtprotoError > ,
535+ origin : impl FnOnce ( ) -> Result < T , MtprotoError > ,
536+ ) -> Result < T , MtprotoError > {
537+ match cdn ( ) {
538+ Ok ( result) => Ok ( result) ,
539+ Err ( _) => media:: with_cdn_supported ( false , origin) ,
540+ }
541+ }
542+
543+ fn receive_cdn_parts (
544+ job : & media:: CdnRedirect ,
545+ dest : & Path ,
546+ staged : & media:: StagedDownload ,
547+ mut fetch : impl FnMut ( i64 ) -> Result < Result < Vec < u8 > , Vec < u8 > > , MtprotoError > ,
548+ mut reupload : impl FnMut ( Vec < u8 > ) -> Result < ( ) , MtprotoError > ,
549+ ) -> Result < ( ) , MtprotoError > {
550+ use std:: io:: { Seek , SeekFrom , Write } ;
551+ let mut out = std:: fs:: File :: create ( staged. path ( ) )
552+ . map_err ( |e| MtprotoError :: Message ( e. to_string ( ) ) ) ?;
553+ let mut offset = job. offset ;
554+ loop {
555+ if media:: download_cancelled ( dest) || crate :: request_control:: check ( ) . is_err ( ) {
556+ return Err ( MtprotoError :: Message ( "cancelled" . into ( ) ) ) ;
557+ }
558+ let mut reuploads = 0 ;
559+ let mut part = loop {
560+ if media:: download_cancelled ( dest) || crate :: request_control:: check ( ) . is_err ( ) {
561+ return Err ( MtprotoError :: Message ( "cancelled" . into ( ) ) ) ;
562+ }
563+ match fetch ( offset) ? {
564+ Ok ( bytes) => break bytes,
565+ Err ( token) => {
566+ if reuploads >= 3 {
567+ return Err ( MtprotoError :: Message ( "CDN reupload limit exceeded" . into ( ) ) ) ;
568+ }
569+ reuploads += 1 ;
570+ reupload ( token) ?;
571+ }
572+ }
573+ } ;
574+ if media:: download_cancelled ( dest) || crate :: request_control:: check ( ) . is_err ( ) {
575+ return Err ( MtprotoError :: Message ( "cancelled" . into ( ) ) ) ;
576+ }
577+ media:: decrypt_cdn_part ( & job. encryption_key , & job. encryption_iv , offset, & mut part) ?;
578+ if part. len ( ) > media:: CHUNK as usize
579+ || !media:: verify_cdn_part ( & job. file_hashes , offset, & part)
580+ {
581+ return Err ( MtprotoError :: Message ( "CDN hash verification failed" . into ( ) ) ) ;
582+ }
583+ out. seek ( SeekFrom :: Start ( offset as u64 ) )
584+ . map_err ( |e| MtprotoError :: Message ( e. to_string ( ) ) ) ?;
585+ out. write_all ( & part) . map_err ( |e| MtprotoError :: Message ( e. to_string ( ) ) ) ?;
586+ if part. len ( ) < media:: CHUNK as usize { break ; }
587+ offset += part. len ( ) as i64 ;
588+ media:: notify_progress ( & dest. display ( ) . to_string ( ) , offset, 0 ) ;
589+ }
590+ out. flush ( ) . map_err ( |e| MtprotoError :: Message ( e. to_string ( ) ) )
591+ }
592+
0 commit comments