diff --git a/backend/app/pypi_proxy.py b/backend/app/pypi_proxy.py index 7f07e89..aff7c24 100644 --- a/backend/app/pypi_proxy.py +++ b/backend/app/pypi_proxy.py @@ -578,10 +578,7 @@ async def pypi_download_file( result = storage.store(f) sha256 = result.sha256 size = result.size - - # Read content for response - with open(tmp_path, 'rb') as f: - content = f.read() + s3_key = result.s3_key logger.info(f"PyPI proxy: downloaded {filename}, {size} bytes, sha256={sha256[:12]}") finally: @@ -677,17 +674,31 @@ async def pypi_download_file( db.commit() - # Return the file - return Response( - content=content, - media_type=content_type, - headers={ - "Content-Disposition": f'attachment; filename="{filename}"', - "Content-Length": str(size), - "X-Checksum-SHA256": sha256, - "X-Cache": "MISS", - } - ) + # Stream the file from S3 (don't load into memory) + try: + stream, content_length, _ = storage.get_stream(s3_key) + + def stream_content(): + """Generator that yields chunks from the S3 stream.""" + try: + for chunk in stream.iter_chunks(): + yield chunk + finally: + stream.close() + + return StreamingResponse( + stream_content(), + media_type=content_type, + headers={ + "Content-Disposition": f'attachment; filename="{filename}"', + "Content-Length": str(size), + "X-Checksum-SHA256": sha256, + "X-Cache": "MISS", + } + ) + except Exception as e: + logger.error(f"PyPI proxy: error streaming from S3: {e}") + raise HTTPException(status_code=500, detail=f"Error streaming file: {e}") except httpx.ConnectError as e: raise HTTPException(status_code=502, detail=f"Connection failed: {e}")