mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-10-29 10:57:44 +09:00 
			
		
		
		
	Make repository response support HTTP range request (#24592)
Replace #20480 Replace #18448 Close #16414
This commit is contained in:
		| @@ -4,71 +4,20 @@ | ||||
| package context | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"net/http" | ||||
| 	"net/url" | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
| 	"time" | ||||
|  | ||||
| 	"code.gitea.io/gitea/modules/httpcache" | ||||
| 	"code.gitea.io/gitea/modules/typesniffer" | ||||
| 	"code.gitea.io/gitea/modules/httplib" | ||||
| ) | ||||
|  | ||||
| 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 | ||||
| } | ||||
| type ServeHeaderOptions httplib.ServeHeaderOptions | ||||
|  | ||||
| // 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)) | ||||
| 	} | ||||
| func (ctx *Context) SetServeHeaders(opt *ServeHeaderOptions) { | ||||
| 	httplib.ServeSetHeaders(ctx.Resp, (*httplib.ServeHeaderOptions)(opt)) | ||||
| } | ||||
|  | ||||
| // ServeContent serves content to http request | ||||
| func (ctx *Context) ServeContent(r io.ReadSeeker, opts *ServeHeaderOptions) { | ||||
| 	ctx.SetServeHeaders(opts) | ||||
| 	httplib.ServeSetHeaders(ctx.Resp, (*httplib.ServeHeaderOptions)(opts)) | ||||
| 	http.ServeContent(ctx.Resp, ctx.Req, opts.Filename, opts.LastModified, r) | ||||
| } | ||||
|   | ||||
| @@ -7,32 +7,16 @@ import ( | ||||
| 	"net/http" | ||||
| 	"testing" | ||||
|  | ||||
| 	"code.gitea.io/gitea/modules/httplib" | ||||
| 	"code.gitea.io/gitea/modules/setting" | ||||
|  | ||||
| 	"github.com/stretchr/testify/assert" | ||||
| ) | ||||
|  | ||||
| type mockResponseWriter struct { | ||||
| 	header http.Header | ||||
| } | ||||
|  | ||||
| func (m *mockResponseWriter) Header() http.Header { | ||||
| 	return m.header | ||||
| } | ||||
|  | ||||
| func (m *mockResponseWriter) Write(bytes []byte) (int, error) { | ||||
| 	panic("implement me") | ||||
| } | ||||
|  | ||||
| func (m *mockResponseWriter) WriteHeader(statusCode int) { | ||||
| 	panic("implement me") | ||||
| } | ||||
|  | ||||
| func TestRemoveSessionCookieHeader(t *testing.T) { | ||||
| 	w := &mockResponseWriter{} | ||||
| 	w.header = http.Header{} | ||||
| 	w.header.Add("Set-Cookie", (&http.Cookie{Name: setting.SessionConfig.CookieName, Value: "foo"}).String()) | ||||
| 	w.header.Add("Set-Cookie", (&http.Cookie{Name: "other", Value: "bar"}).String()) | ||||
| 	w := httplib.NewMockResponseWriter() | ||||
| 	w.Header().Add("Set-Cookie", (&http.Cookie{Name: setting.SessionConfig.CookieName, Value: "foo"}).String()) | ||||
| 	w.Header().Add("Set-Cookie", (&http.Cookie{Name: "other", Value: "bar"}).String()) | ||||
| 	assert.Len(t, w.Header().Values("Set-Cookie"), 2) | ||||
| 	removeSessionCookieHeader(w) | ||||
| 	assert.Len(t, w.Header().Values("Set-Cookie"), 1) | ||||
|   | ||||
		Reference in New Issue
	
	Block a user