mirror of
https://github.com/alireza0/s-ui.git
synced 2026-06-27 19:20:53 +00:00
fix(sub): add content disposition for subscriptions
This commit is contained in:
parent
deb285d8bc
commit
32ca517187
2 changed files with 95 additions and 0 deletions
|
|
@ -1,6 +1,10 @@
|
|||
package sub
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/alireza0/s-ui/logger"
|
||||
"github.com/alireza0/s-ui/service"
|
||||
|
||||
|
|
@ -75,4 +79,33 @@ func (s *SubHandler) addHeaders(c *gin.Context, headers []string) {
|
|||
c.Writer.Header().Set("Subscription-Userinfo", headers[0])
|
||||
c.Writer.Header().Set("Profile-Update-Interval", headers[1])
|
||||
c.Writer.Header().Set("Profile-Title", headers[2])
|
||||
c.Writer.Header().Set("Content-Disposition", contentDispositionHeader(headers[2]))
|
||||
}
|
||||
|
||||
func contentDispositionHeader(name string) string {
|
||||
filename := strings.TrimSpace(name)
|
||||
if filename == "" {
|
||||
filename = "subscription"
|
||||
}
|
||||
|
||||
return fmt.Sprintf("attachment; filename=\"%s\"; filename*=UTF-8''%s", asciiSafeFilename(filename), url.PathEscape(filename))
|
||||
}
|
||||
|
||||
func asciiSafeFilename(filename string) string {
|
||||
var builder strings.Builder
|
||||
for _, r := range filename {
|
||||
switch {
|
||||
case r == '"' || r == '\\':
|
||||
builder.WriteByte('_')
|
||||
case r >= 0x20 && r <= 0x7e:
|
||||
builder.WriteRune(r)
|
||||
}
|
||||
}
|
||||
|
||||
fallback := strings.TrimSpace(builder.String())
|
||||
if fallback == "" {
|
||||
return "subscription"
|
||||
}
|
||||
|
||||
return fallback
|
||||
}
|
||||
|
|
|
|||
62
sub/subHandler_test.go
Normal file
62
sub/subHandler_test.go
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
package sub
|
||||
|
||||
import (
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func TestAddHeadersPreservesSubscriptionHeadersAndAddsContentDisposition(t *testing.T) {
|
||||
gin.SetMode(gin.TestMode)
|
||||
recorder := httptest.NewRecorder()
|
||||
context, _ := gin.CreateTestContext(recorder)
|
||||
|
||||
handler := &SubHandler{}
|
||||
handler.addHeaders(context, []string{
|
||||
"upload=1; download=2; total=3; expire=4",
|
||||
"12",
|
||||
"phj233_vpn",
|
||||
})
|
||||
|
||||
headers := recorder.Header()
|
||||
tests := map[string]string{
|
||||
"Subscription-Userinfo": "upload=1; download=2; total=3; expire=4",
|
||||
"Profile-Update-Interval": "12",
|
||||
"Profile-Title": "phj233_vpn",
|
||||
"Content-Disposition": "attachment; filename=\"phj233_vpn\"; filename*=UTF-8''phj233_vpn",
|
||||
}
|
||||
|
||||
for key, want := range tests {
|
||||
if got := headers.Get(key); got != want {
|
||||
t.Fatalf("header %s = %q, want %q", key, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestContentDispositionHeaderUsesSubscriptionNameWithoutExtension(t *testing.T) {
|
||||
got := contentDispositionHeader("phj233_vpn")
|
||||
want := "attachment; filename=\"phj233_vpn\"; filename*=UTF-8''phj233_vpn"
|
||||
|
||||
if got != want {
|
||||
t.Fatalf("contentDispositionHeader() = %q, want %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestContentDispositionHeaderEscapesUTF8Name(t *testing.T) {
|
||||
got := contentDispositionHeader("蓝胖云 LanPangYun")
|
||||
want := "attachment; filename=\"LanPangYun\"; filename*=UTF-8''%E8%93%9D%E8%83%96%E4%BA%91%20LanPangYun"
|
||||
|
||||
if got != want {
|
||||
t.Fatalf("contentDispositionHeader() = %q, want %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestContentDispositionHeaderFallsBackWhenNameIsEmpty(t *testing.T) {
|
||||
got := contentDispositionHeader(" ")
|
||||
want := "attachment; filename=\"subscription\"; filename*=UTF-8''subscription"
|
||||
|
||||
if got != want {
|
||||
t.Fatalf("contentDispositionHeader() = %q, want %q", got, want)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue