11package bundle
22
33import (
4+ "bytes"
45 "context"
56 "errors"
67 "fmt"
@@ -29,6 +30,9 @@ const (
2930 // requestTimeout is the time limit for http client requests.
3031 requestTimeout = 10 * time .Second
3132
33+ // maxMetadataSizeBytes is the maximum allowed metadata size in bytes.
34+ maxMetadataSizeBytes = 2 * 1024 // 2 KB
35+
3236 // maxDefaultBundleSizeBytes is the maximum allowed default bundle size
3337 // in bytes.
3438 maxDefaultBundleSizeBytes = 20 * 1024 * 1024 // 20 MB
@@ -295,7 +299,7 @@ func (d *Discovery) downloadBundle(runtimeID common.Namespace, manifestHash hash
295299
296300 for _ , baseURLs := range [][]string {d .runtimeBaseURLs [runtimeID ], d .globalBaseURLs } {
297301 for _ , baseURL := range baseURLs {
298- if err := d .tryDownloadBundle (runtimeID , manifestHash , baseURL ); err != nil {
302+ if err := d .tryDownloadBundle (manifestHash , baseURL ); err != nil {
299303 errs = errors .Join (errs , err )
300304 continue
301305 }
@@ -307,39 +311,49 @@ func (d *Discovery) downloadBundle(runtimeID common.Namespace, manifestHash hash
307311 return errs
308312}
309313
310- func (d * Discovery ) tryDownloadBundle (runtimeID common.Namespace , manifestHash hash.Hash , baseURL string ) error {
311- filename := fmt .Sprintf ("%s%s" , manifestHash .Hex (), FileExtension )
314+ func (d * Discovery ) tryDownloadBundle (manifestHash hash.Hash , baseURL string ) error {
315+ metaURL , err := url .JoinPath (baseURL , manifestHash .Hex ())
316+ if err != nil {
317+ d .logger .Error ("failed to construct metadata URL" ,
318+ "err" , err ,
319+ )
320+ return fmt .Errorf ("failed to construct metadata URL: %w" , err )
321+ }
312322
313- d .logger .Debug ("downloading bundle" ,
314- "runtime_id" , runtimeID ,
315- "base_url" , baseURL ,
316- "filename" , filename ,
323+ d .logger .Debug ("downloading metadata" ,
324+ "url" , metaURL ,
317325 )
318326
319- url , err := url . JoinPath ( baseURL , filename )
327+ bundleURL , err := d . fetchMetadata ( metaURL )
320328 if err != nil {
321- d .logger .Error ("failed to construct URL " ,
329+ d .logger .Error ("failed to download metadata " ,
322330 "err" , err ,
323- "base_url" , baseURL ,
324- "filename" , filename ,
331+ "url" , metaURL ,
325332 )
326- return fmt .Errorf ("failed to construct URL: %w" , err )
333+ return fmt .Errorf ("failed to download metadata: %w" , err )
334+ }
335+
336+ bundleURL , err = validateAndNormalizeURL (bundleURL )
337+ if err != nil {
338+ return err
327339 }
328340
329- src , err := d .fetchBundle (url )
341+ d .logger .Debug ("downloading bundle" ,
342+ "url" , bundleURL ,
343+ )
344+
345+ src , err := d .fetchBundle (bundleURL )
330346 if err != nil {
331347 d .logger .Error ("failed to download bundle" ,
332348 "err" , err ,
333- "url" , url ,
349+ "url" , metaURL ,
334350 )
335351 return fmt .Errorf ("failed to download bundle: %w" , err )
336352 }
337353 defer os .Remove (src )
338354
339355 d .logger .Info ("bundle downloaded" ,
340- "runtime_id" , runtimeID ,
341- "base_url" , baseURL ,
342- "filename" , filename ,
356+ "url" , bundleURL ,
343357 )
344358
345359 if err := d .registry .AddBundle (src , manifestHash ); err != nil {
@@ -349,6 +363,7 @@ func (d *Discovery) tryDownloadBundle(runtimeID common.Namespace, manifestHash h
349363 return fmt .Errorf ("failed to add bundle: %w" , err )
350364 }
351365
366+ filename := fmt .Sprintf ("%s%s" , manifestHash .Hex (), FileExtension )
352367 dst := filepath .Join (d .bundleDir , filename )
353368 if err = os .Rename (src , dst ); err != nil {
354369 d .logger .Error ("failed to move bundle" ,
@@ -357,9 +372,39 @@ func (d *Discovery) tryDownloadBundle(runtimeID common.Namespace, manifestHash h
357372 "dst" , dst ,
358373 )
359374 }
375+
376+ d .logger .Debug ("bundle stored" ,
377+ "dst" , dst ,
378+ )
379+
360380 return nil
361381}
362382
383+ func (d * Discovery ) fetchMetadata (url string ) (string , error ) {
384+ resp , err := d .client .Get (url )
385+ if err != nil {
386+ return "" , fmt .Errorf ("failed to fetch metadata: %w" , err )
387+ }
388+ defer resp .Body .Close ()
389+
390+ if resp .StatusCode != http .StatusOK {
391+ return "" , fmt .Errorf ("failed to fetch metadata: invalid status code %d" , resp .StatusCode )
392+ }
393+
394+ limitedReader := io.LimitedReader {
395+ R : resp .Body ,
396+ N : maxMetadataSizeBytes ,
397+ }
398+
399+ var buffer bytes.Buffer
400+ _ , err = buffer .ReadFrom (& limitedReader )
401+ if err != nil && err != io .EOF {
402+ return "" , fmt .Errorf ("failed to read metadata content: %w" , err )
403+ }
404+
405+ return strings .TrimSpace (buffer .String ()), nil
406+ }
407+
363408func (d * Discovery ) fetchBundle (url string ) (string , error ) {
364409 resp , err := d .client .Get (url )
365410 if err != nil {
@@ -470,15 +515,23 @@ func (d *Discovery) copyBundle(src string) error {
470515 return nil
471516}
472517
518+ func validateAndNormalizeURL (rawURL string ) (string , error ) {
519+ parsedURL , err := url .Parse (rawURL )
520+ if err != nil {
521+ return "" , fmt .Errorf ("invalid URL '%s': %w" , rawURL , err )
522+ }
523+ return parsedURL .String (), nil
524+ }
525+
473526func validateAndNormalizeURLs (rawURLs []string ) ([]string , error ) {
474527 var normalizedURLs []string
475528
476529 for _ , rawURL := range rawURLs {
477- parsedURL , err := url . Parse (rawURL )
530+ normalizedURL , err := validateAndNormalizeURL (rawURL )
478531 if err != nil {
479- return nil , fmt . Errorf ( "invalid URL '%s': %w" , rawURL , err )
532+ return nil , err
480533 }
481- normalizedURLs = append (normalizedURLs , parsedURL . String () )
534+ normalizedURLs = append (normalizedURLs , normalizedURL )
482535 }
483536
484537 return normalizedURLs , nil
0 commit comments