[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.

+16 -4
+12 -2
src/manifest.go
··· 283 return nil 284 } 285 286 var ErrManifestTooLarge = errors.New("manifest too large") 287 288 // Uploads inline file data over certain size to the storage backend. Returns a copy of ··· 325 } 326 } 327 328 - // Compute the deduplicated storage size. 329 - var blobSizes = make(map[string]int64) 330 for _, entry := range manifest.Contents { 331 if entry.GetType() == Type_ExternalFile { 332 blobSizes[string(entry.Data)] = entry.GetCompressedSize() 333 } 334 } 335 for _, blobSize := range blobSizes { 336 *extManifest.StoredSize += blobSize
··· 283 return nil 284 } 285 286 + var ErrSiteTooLarge = errors.New("site too large") 287 var ErrManifestTooLarge = errors.New("manifest too large") 288 289 // Uploads inline file data over certain size to the storage backend. Returns a copy of ··· 326 } 327 } 328 329 + // Compute the total and deduplicated storage size. 330 + totalSize := int64(0) 331 + blobSizes := map[string]int64{} 332 for _, entry := range manifest.Contents { 333 + totalSize += entry.GetOriginalSize() 334 if entry.GetType() == Type_ExternalFile { 335 blobSizes[string(entry.Data)] = entry.GetCompressedSize() 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 + ) 344 } 345 for _, blobSize := range blobSizes { 346 *extManifest.StoredSize += blobSize
+4 -2
src/pages.go
··· 599 600 switch result.outcome { 601 case UpdateError: 602 - if errors.Is(result.err, ErrManifestTooLarge) { 603 - w.WriteHeader(http.StatusRequestEntityTooLarge) 604 } else if errors.Is(result.err, errArchiveFormat) { 605 w.WriteHeader(http.StatusUnsupportedMediaType) 606 } else if errors.Is(result.err, ErrArchiveTooLarge) {
··· 599 600 switch result.outcome { 601 case UpdateError: 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) 606 } else if errors.Is(result.err, errArchiveFormat) { 607 w.WriteHeader(http.StatusUnsupportedMediaType) 608 } else if errors.Is(result.err, ErrArchiveTooLarge) {