[mirror] Scalable static site server for Git forges (like GitHub Pages)

Limit original size of the contents of a site manifest.

The limit is applied to the original size and not compressed size for
predictability and fairness.

Changed files
+16 -4
src
+12 -2
src/manifest.go
··· 283 283 return nil 284 284 } 285 285 286 + var ErrSiteTooLarge = errors.New("site too large") 286 287 var ErrManifestTooLarge = errors.New("manifest too large") 287 288 288 289 // Uploads inline file data over certain size to the storage backend. Returns a copy of ··· 325 326 } 326 327 } 327 328 328 - // Compute the deduplicated storage size. 329 - var blobSizes = make(map[string]int64) 329 + // Compute the total and deduplicated storage size. 330 + totalSize := int64(0) 331 + blobSizes := map[string]int64{} 330 332 for _, entry := range manifest.Contents { 333 + totalSize += entry.GetOriginalSize() 331 334 if entry.GetType() == Type_ExternalFile { 332 335 blobSizes[string(entry.Data)] = entry.GetCompressedSize() 333 336 } 337 + } 338 + if uint64(totalSize) > config.Limits.MaxSiteSize.Bytes() { 339 + return nil, fmt.Errorf("%w: contents size %s exceeds %s limit", 340 + ErrSiteTooLarge, 341 + datasize.ByteSize(totalSize).HR(), 342 + config.Limits.MaxSiteSize.HR(), 343 + ) 334 344 } 335 345 for _, blobSize := range blobSizes { 336 346 *extManifest.StoredSize += blobSize
+4 -2
src/pages.go
··· 599 599 600 600 switch result.outcome { 601 601 case UpdateError: 602 - if errors.Is(result.err, ErrManifestTooLarge) { 603 - w.WriteHeader(http.StatusRequestEntityTooLarge) 602 + if errors.Is(result.err, ErrSiteTooLarge) { 603 + w.WriteHeader(http.StatusUnprocessableEntity) 604 + } else if errors.Is(result.err, ErrManifestTooLarge) { 605 + w.WriteHeader(http.StatusUnprocessableEntity) 604 606 } else if errors.Is(result.err, errArchiveFormat) { 605 607 w.WriteHeader(http.StatusUnsupportedMediaType) 606 608 } else if errors.Is(result.err, ErrArchiveTooLarge) {