Skip to content

Commit

Permalink
πŸ“š Doc: Clarify SendFile Docs (#3172)
Browse files Browse the repository at this point in the history
* πŸ“š Doc: Clarify SendFile ContentType header set by file format

* πŸ“š Doc: Make SendFile default value formatting consistent

* πŸ“š Doc: Add missing `fiber.` in SendFile usage docs

* πŸ“š Doc: Hyphenate 'case-sensitive'

* πŸ“š Doc: Clarify `SendFile` behavior for missing/invalid file extensions

* 🚨 Test: Validate `SendFile` Content-Type header

---------

Co-authored-by: Juan Calderon-Perez <[email protected]>
  • Loading branch information
xEricL and gaby authored Oct 28, 2024
1 parent 9a72704 commit 579d9a3
Show file tree
Hide file tree
Showing 10 changed files with 72 additions and 27 deletions.
Binary file added .github/testdata/fs/img/fiberpng
Binary file not shown.
Binary file added .github/testdata/fs/img/fiberpng.jpeg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added .github/testdata/fs/img/fiberpng.notvalidext
Binary file not shown.
2 changes: 1 addition & 1 deletion app.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ type Config struct { //nolint:govet // Aligning the struct fields is not necessa
// Default: false
StrictRouting bool `json:"strict_routing"`

// When set to true, enables case sensitive routing.
// When set to true, enables case-sensitive routing.
// E.g. "/FoO" and "/foo" are treated as different routes.
// By default this is disabled and both "/FoO" and "/foo" will execute the same handler.
//
Expand Down
4 changes: 2 additions & 2 deletions app_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -451,9 +451,9 @@ func Test_App_Use_CaseSensitive(t *testing.T) {
require.NoError(t, err, "app.Test(req)")
require.Equal(t, StatusOK, resp.StatusCode, "Status code")

// check the detected path when the case insensitive recognition is activated
// check the detected path when the case-insensitive recognition is activated
app.config.CaseSensitive = false
// check the case sensitive feature
// check the case-sensitive feature
resp, err = app.Test(httptest.NewRequest(MethodGet, "/AbC", nil))
require.NoError(t, err, "app.Test(req)")
require.Equal(t, StatusOK, resp.StatusCode, "Status code")
Expand Down
17 changes: 9 additions & 8 deletions ctx.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,29 +83,29 @@ type SendFile struct {
// You have to set Content-Encoding header to compress the file.
// Available compression methods are gzip, br, and zstd.
//
// Optional. Default value false
// Optional. Default: false
Compress bool `json:"compress"`

// When set to true, enables byte range requests.
//
// Optional. Default value false
// Optional. Default: false
ByteRange bool `json:"byte_range"`

// When set to true, enables direct download.
//
// Optional. Default: false.
// Optional. Default: false
Download bool `json:"download"`

// Expiration duration for inactive file handlers.
// Use a negative time.Duration to disable it.
//
// Optional. Default value 10 * time.Second.
// Optional. Default: 10 * time.Second
CacheDuration time.Duration `json:"cache_duration"`

// The value for the Cache-Control HTTP-header
// that is set on the file response. MaxAge is defined in seconds.
//
// Optional. Default value 0.
// Optional. Default: 0
MaxAge int `json:"max_age"`
}

Expand Down Expand Up @@ -1496,9 +1496,10 @@ func (c *DefaultCtx) Send(body []byte) error {
return nil
}

// SendFile transfers the file from the given path.
// The file is not compressed by default, enable this by passing a 'true' argument
// Sets the Content-Type response HTTP header field based on the filenames extension.
// SendFile transfers the file from the specified path.
// By default, the file is not compressed. To enable compression, set SendFile.Compress to true.
// The Content-Type response HTTP header field is set based on the file's extension.
// If the file extension is missing or invalid, the Content-Type is detected from the file's format.
func (c *DefaultCtx) SendFile(file string, config ...SendFile) error {
// Save the filename, we will need it in the error message if the file isn't found
filename := file
Expand Down
7 changes: 4 additions & 3 deletions ctx_interface_gen.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

43 changes: 43 additions & 0 deletions ctx_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3122,6 +3122,49 @@ func Test_Ctx_SendFile(t *testing.T) {
app.ReleaseCtx(c)
}

// go test -race -run Test_Ctx_SendFile_ContentType
func Test_Ctx_SendFile_ContentType(t *testing.T) {
t.Parallel()
app := New()

// 1) simple case
c := app.AcquireCtx(&fasthttp.RequestCtx{})
err := c.SendFile("./.github/testdata/fs/img/fiber.png")
// check expectation
require.NoError(t, err)
require.Equal(t, StatusOK, c.Response().StatusCode())
require.Equal(t, "image/png", string(c.Response().Header.Peek(HeaderContentType)))
app.ReleaseCtx(c)

// 2) set by valid file extension, not file header
// see: https://github.com/valyala/fasthttp/blob/d795f13985f16622a949ea9fc3459cf54dc78b3e/fs.go#L1638
c = app.AcquireCtx(&fasthttp.RequestCtx{})
err = c.SendFile("./.github/testdata/fs/img/fiberpng.jpeg")
// check expectation
require.NoError(t, err)
require.Equal(t, StatusOK, c.Response().StatusCode())
require.Equal(t, "image/jpeg", string(c.Response().Header.Peek(HeaderContentType)))
app.ReleaseCtx(c)

// 3) set by file header if extension is invalid
c = app.AcquireCtx(&fasthttp.RequestCtx{})
err = c.SendFile("./.github/testdata/fs/img/fiberpng.notvalidext")
// check expectation
require.NoError(t, err)
require.Equal(t, StatusOK, c.Response().StatusCode())
require.Equal(t, "image/png", string(c.Response().Header.Peek(HeaderContentType)))
app.ReleaseCtx(c)

// 4) set by file header if extension is missing
c = app.AcquireCtx(&fasthttp.RequestCtx{})
err = c.SendFile("./.github/testdata/fs/img/fiberpng")
// check expectation
require.NoError(t, err)
require.Equal(t, StatusOK, c.Response().StatusCode())
require.Equal(t, "image/png", string(c.Response().Header.Peek(HeaderContentType)))
app.ReleaseCtx(c)
}

func Test_Ctx_SendFile_Download(t *testing.T) {
t.Parallel()
app := New()
Expand Down
22 changes: 11 additions & 11 deletions docs/api/ctx.md
Original file line number Diff line number Diff line change
Expand Up @@ -1709,7 +1709,7 @@ app.Get("/", func(c fiber.Ctx) error {

## SendFile

Transfers the file from the given path. Sets the [Content-Type](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Type) response HTTP header field based on the **filenames** extension.
Transfers the file from the given path. Sets the [Content-Type](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Type) response HTTP header field based on the **file** extension or format.

```go title="Config" title="Config"
// SendFile defines configuration options when to transfer file with SendFile.
Expand All @@ -1725,29 +1725,29 @@ type SendFile struct {
// You have to set Content-Encoding header to compress the file.
// Available compression methods are gzip, br, and zstd.
//
// Optional. Default value false
// Optional. Default: false
Compress bool `json:"compress"`
// When set to true, enables byte range requests.
//
// Optional. Default value false
// Optional. Default: false
ByteRange bool `json:"byte_range"`
// When set to true, enables direct download.
//
// Optional. Default: false.
// Optional. Default: false
Download bool `json:"download"`
// Expiration duration for inactive file handlers.
// Use a negative time.Duration to disable it.
//
// Optional. Default value 10 * time.Second.
// Optional. Default: 10 * time.Second
CacheDuration time.Duration `json:"cache_duration"`
// The value for the Cache-Control HTTP-header
// that is set on the file response. MaxAge is defined in seconds.
//
// Optional. Default value 0.
// Optional. Default: 0
MaxAge int `json:"max_age"`
}
```
Expand All @@ -1761,7 +1761,7 @@ app.Get("/not-found", func(c fiber.Ctx) error {
return c.SendFile("./public/404.html");
// Disable compression
return c.SendFile("./static/index.html", SendFile{
return c.SendFile("./static/index.html", fiber.SendFile{
Compress: false,
});
})
Expand All @@ -1783,7 +1783,7 @@ You can set `CacheDuration` config property to `-1` to disable caching.

```go title="Example"
app.Get("/file", func(c fiber.Ctx) error {
return c.SendFile("style.css", SendFile{
return c.SendFile("style.css", fiber.SendFile{
CacheDuration: -1,
})
})
Expand All @@ -1797,16 +1797,16 @@ You can use multiple SendFile with different configurations in single route. Fib
app.Get("/file", func(c fiber.Ctx) error {
switch c.Query("config") {
case "filesystem":
return c.SendFile("style.css", SendFile{
return c.SendFile("style.css", fiber.SendFile{
FS: os.DirFS(".")
})
case "filesystem-compress":
return c.SendFile("style.css", SendFile{
return c.SendFile("style.css", fiber.SendFile{
FS: os.DirFS("."),
Compress: true,
})
case "compress":
return c.SendFile("style.css", SendFile{
return c.SendFile("style.css", fiber.SendFile{
Compress: true,
})
default:
Expand Down
4 changes: 2 additions & 2 deletions router.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ type Route struct {
Name string `json:"name"` // Route's name
//nolint:revive // Having both a Path (uppercase) and a path (lowercase) is fine
Path string `json:"path"` // Original registered route path
Params []string `json:"params"` // Case sensitive param keys
Params []string `json:"params"` // Case-sensitive param keys
Handlers []Handler `json:"-"` // Ctx handlers
routeParser routeParser // Parameter parser
// Data for routing
Expand Down Expand Up @@ -316,7 +316,7 @@ func (app *App) register(methods []string, pathRaw string, group *Group, handler
if pathRaw[0] != '/' {
pathRaw = "/" + pathRaw
}
// Create a stripped path in-case sensitive / trailing slashes
// Create a stripped path in case-sensitive / trailing slashes
pathPretty := pathRaw
// Case-sensitive routing, all to lowercase
if !app.config.CaseSensitive {
Expand Down

1 comment on commit 579d9a3

@github-actions
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Performance Alert ⚠️

Possible performance regression was detected for benchmark.
Benchmark result of this commit is worse than the previous benchmark result exceeding threshold 1.50.

Benchmark suite Current: 579d9a3 Previous: 8c84b0f Ratio
Benchmark_Ctx_Send 6.822 ns/op 0 B/op 0 allocs/op 4.335 ns/op 0 B/op 0 allocs/op 1.57
Benchmark_Ctx_Send - ns/op 6.822 ns/op 4.335 ns/op 1.57
Benchmark_Utils_GetOffer/1_parameter 201.2 ns/op 0 B/op 0 allocs/op 131 ns/op 0 B/op 0 allocs/op 1.54
Benchmark_Utils_GetOffer/1_parameter - ns/op 201.2 ns/op 131 ns/op 1.54
Benchmark_Middleware_BasicAuth - B/op 80 B/op 48 B/op 1.67
Benchmark_Middleware_BasicAuth - allocs/op 5 allocs/op 3 allocs/op 1.67
Benchmark_Middleware_BasicAuth_Upper - B/op 80 B/op 48 B/op 1.67
Benchmark_Middleware_BasicAuth_Upper - allocs/op 5 allocs/op 3 allocs/op 1.67
Benchmark_CORS_NewHandler - B/op 16 B/op 0 B/op +∞
Benchmark_CORS_NewHandler - allocs/op 1 allocs/op 0 allocs/op +∞
Benchmark_CORS_NewHandlerSingleOrigin - B/op 16 B/op 0 B/op +∞
Benchmark_CORS_NewHandlerSingleOrigin - allocs/op 1 allocs/op 0 allocs/op +∞
Benchmark_CORS_NewHandlerPreflight - B/op 104 B/op 0 B/op +∞
Benchmark_CORS_NewHandlerPreflight - allocs/op 5 allocs/op 0 allocs/op +∞
Benchmark_CORS_NewHandlerPreflightSingleOrigin - B/op 104 B/op 0 B/op +∞
Benchmark_CORS_NewHandlerPreflightSingleOrigin - allocs/op 5 allocs/op 0 allocs/op +∞
Benchmark_CORS_NewHandlerPreflightWildcard - B/op 104 B/op 0 B/op +∞
Benchmark_CORS_NewHandlerPreflightWildcard - allocs/op 5 allocs/op 0 allocs/op +∞
Benchmark_Middleware_CSRF_GenerateToken - B/op 520 B/op 327 B/op 1.59
Benchmark_Middleware_CSRF_GenerateToken - allocs/op 10 allocs/op 6 allocs/op 1.67

This comment was automatically generated by workflow using github-action-benchmark.

Please sign in to comment.