gobuffalo/buffalo

Memory leak when using render.Download

alexei38 opened this issue · 4 comments

Description

Memory leak when using render.Download

Steps to Reproduce the Problem

  1. Create simple app
package main

import (
	"net/http"
	"os"

	"github.com/gobuffalo/buffalo"
	"github.com/gobuffalo/buffalo/render"
)

const testFilePath = "/tmp/testLargeFile"

func main() {
	app := buffalo.New(buffalo.Options{})
	app.GET("/", downloadHandler)
	app.Serve()
}

func downloadHandler(c buffalo.Context) error {
	f, _ := os.Open(testFilePath)
	return c.Render(http.StatusOK, render.Download(c, f.Name(), f))
}
  1. run app
go run main.go
  1. create large file
dd if=/dev/zero of=/tmp/testLargeFile bs=1M count=20000
  1. download file
curl http://localhost:3000

Waiting for the OOM killer to come

Expected Behavior

The correct work is in http

func downloadHandler(c buffalo.Context) error {
	http.ServeFile(c.Response(), c.Request(), testFilePath)
	return nil
}

Actual Behavior

memory leak when working with large files

Info

-> Go: Checking installation
✓ The `go` executable was found on your system at: /home/amargasov/go/go1.17.8/bin/go

-> Go: Checking minimum version requirements
✓ Your version of Go, 1.17.5, meets the minimum requirements.

-> Go: Checking Package Management
✓ You are using Go Modules (`go`) for package management.

-> Go: Checking PATH
✓ Your PATH contains /home/amargasov/go/bin.

-> Node: Checking installation
✘ The `node` executable could not be found on your system.
For help setting up your Node environment please follow the instructions for you platform at:

https://nodejs.org/en/download/

-> NPM: Checking installation
✘ The `npm` executable could not be found on your system.
For help setting up your NPM environment please follow the instructions for you platform at:

https://docs.npmjs.com/getting-started/configuring-your-local-environment

-> Yarn: Checking installation
✘ The `yarnpkg` executable could not be found on your system.
For help setting up your Yarn environment please follow the instructions for you platform at:

https://yarnpkg.com/en/docs/install

-> PostgreSQL: Checking installation
✘ The `postgres` executable could not be found on your system.
For help setting up your Postgres environment please follow the instructions for you platform at:

https://www.postgresql.org/download/

-> MySQL: Checking installation
✘ The `mysql` executable could not be found on your system.
For help setting up your MySQL environment please follow the instructions for you platform at:

https://www.mysql.com/downloads/

-> SQLite3: Checking installation
✓ The `sqlite3` executable was found on your system at: /usr/bin/sqlite3

-> SQLite3: Checking minimum version requirements
✓ Your version of SQLite3, 3.31.1, meets the minimum requirements.

-> Cockroach: Checking installation
✘ The `cockroach` executable could not be found on your system.
For help setting up your Cockroach environment please follow the instructions for you platform at:

https://www.cockroachlabs.com/docs/stable/

-> Buffalo (CLI): Checking installation
✓ The `buffalo` executable was found on your system at: /usr/local/bin/buffalo

-> Buffalo (CLI): Checking minimum version requirements
✓ Your version of Buffalo (CLI), v0.18.2, meets the minimum requirements.

-> Buffalo: Application Details
Warning: It seems like it is not a buffalo app. (.buffalo.dev.yml not found)

sio4 commented

Hi @alexei38,

Could you please explain the use case for this example? Buffalo App has dedicated function App.ServeFiles() to serve static files in a given http.FileSystem so you can use that function to serve large files. If the goal is serving large files in a directory, you can modify your app as:

func main() {
	app := buffalo.New(buffalo.Options{})
	app.ServeFiles("/", http.Dir("/tmp"))
	app.Serve()
}

Also, as you demonstrated, you can also use the standard method with function http.ServeFile() directly for specific file. (but it is not in a scope of buffalo but your application)

For the render.Download() function, it gets the name of file and io.Reader as arguments without size checking within it, so ensuring the size to prevent OOM (not memory leak) could be a responsibility of users.

Even though this function is not perfect for serving a large static file, it is still useful to serve dynamic content as a file e.g. downloading dynamically created .xls from a database table or similar purpose.

For more discussion, providing a detailed use case could be very helpful.

These are static large files on the disk
Before sending there are permission checks individually for each file and other checks.
Can't use app.ServeFiles (

This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 5 days.

This issue was closed because it has been stalled for 5 days with no activity.