cb700aedd1
The "modules/context.go" is too large to maintain. This PR splits it to separate files, eg: context_request.go, context_response.go, context_serve.go This PR will help: 1. The future refactoring for Gitea's web context (eg: simplify the context) 2. Introduce proper "range request" support 3. Introduce context function This PR only moves code, doesn't change any logic.
74 lines
2.1 KiB
Go
74 lines
2.1 KiB
Go
// Copyright 2023 The Gitea Authors. All rights reserved.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package context
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"net/url"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"code.gitea.io/gitea/modules/httpcache"
|
|
"code.gitea.io/gitea/modules/typesniffer"
|
|
)
|
|
|
|
type ServeHeaderOptions struct {
|
|
ContentType string // defaults to "application/octet-stream"
|
|
ContentTypeCharset string
|
|
ContentLength *int64
|
|
Disposition string // defaults to "attachment"
|
|
Filename string
|
|
CacheDuration time.Duration // defaults to 5 minutes
|
|
LastModified time.Time
|
|
}
|
|
|
|
// SetServeHeaders sets necessary content serve headers
|
|
func (ctx *Context) SetServeHeaders(opts *ServeHeaderOptions) {
|
|
header := ctx.Resp.Header()
|
|
|
|
contentType := typesniffer.ApplicationOctetStream
|
|
if opts.ContentType != "" {
|
|
if opts.ContentTypeCharset != "" {
|
|
contentType = opts.ContentType + "; charset=" + strings.ToLower(opts.ContentTypeCharset)
|
|
} else {
|
|
contentType = opts.ContentType
|
|
}
|
|
}
|
|
header.Set("Content-Type", contentType)
|
|
header.Set("X-Content-Type-Options", "nosniff")
|
|
|
|
if opts.ContentLength != nil {
|
|
header.Set("Content-Length", strconv.FormatInt(*opts.ContentLength, 10))
|
|
}
|
|
|
|
if opts.Filename != "" {
|
|
disposition := opts.Disposition
|
|
if disposition == "" {
|
|
disposition = "attachment"
|
|
}
|
|
|
|
backslashEscapedName := strings.ReplaceAll(strings.ReplaceAll(opts.Filename, `\`, `\\`), `"`, `\"`) // \ -> \\, " -> \"
|
|
header.Set("Content-Disposition", fmt.Sprintf(`%s; filename="%s"; filename*=UTF-8''%s`, disposition, backslashEscapedName, url.PathEscape(opts.Filename)))
|
|
header.Set("Access-Control-Expose-Headers", "Content-Disposition")
|
|
}
|
|
|
|
duration := opts.CacheDuration
|
|
if duration == 0 {
|
|
duration = 5 * time.Minute
|
|
}
|
|
httpcache.SetCacheControlInHeader(header, duration)
|
|
|
|
if !opts.LastModified.IsZero() {
|
|
header.Set("Last-Modified", opts.LastModified.UTC().Format(http.TimeFormat))
|
|
}
|
|
}
|
|
|
|
// ServeContent serves content to http request
|
|
func (ctx *Context) ServeContent(r io.ReadSeeker, opts *ServeHeaderOptions) {
|
|
ctx.SetServeHeaders(opts)
|
|
http.ServeContent(ctx.Resp, ctx.Req, opts.Filename, opts.LastModified, r)
|
|
}
|