如何在 HTTP header 字段中引用字符串?
How to quote strings for use in HTTP header fields?
TL;DR:给定一个任意文件名作为 Go string
值,创建指定该文件名的 Content-Disposition
header 字段的最佳方法是什么?
我正在编写 Go net/http 处理程序,我想设置 Content-Disposition
header 字段以指定浏览器在保存文件时应使用的文件名。根据MDN,语法为:
Content-Disposition: attachment; filename="filename.jpg"
和 "filename.jpg"
在 HTTP“quoted-string”中。但是,我在 net/http docs 中没有看到任何提及“quote”的地方。仅提及 HTML 和 URL 转义。
quoted-string 是否与 URL 转义相同或至少兼容?我可以为此使用 url.QueryEscape or url.PathEscape 吗?如果是这样,我应该使用哪一个,或者它们都安全吗? HTTP quoted-string 看起来类似于 URL 转义,但我无法立即找到任何说明它们是否兼容,或者是否有需要担心的边缘情况。
或者,是否有我应该使用的 higher-level 包,它可以处理构建包含此类参数的 HTTP header 字段值的细节?
这有什么问题,只是像这样转义和添加其他 header 值?
w.Header().Add("Content-Disposition", "attachment; filename=\"flename.txt\"")
我认为这只是意味着您应该在文件名周围加上正常的引号。假设你想修复文件名,那么你可以根据 MDN 建议设置 header。
不转义引号是可能的,但前提是文件名不包含空格:
w.Header().Set("Content-Disposition", "attachment; filename=testsomething.txt")
文件名两边的引号允许有空格:
w.Header().Set("Content-Disposition", "attachment; filename=\"test something.txt\"")
改用多行引号 (`)
w.Header().Set("Content-Disposition", `attachment; filename="test something.txt"`)
您需要确保文件名不包含任何可以被 OS 解释的字符,这些字符会破坏文件的路径。例如使用 / 或 \ 可能会导致下载出现一些问题,或者文件名太长。
假设文件名未 end-user 定义,您可能没问题。如果使用用户自由文本,那么您可能希望以某种方式进行限制和验证。
一种方法是使用 multipart
包 [1]:
package main
import (
"mime/multipart"
"strings"
)
func main() {
b := new(strings.Builder)
m := multipart.NewWriter(b)
defer m.Close()
m.CreateFormFile("attachment", "filename.jpg")
print(b.String())
}
结果:
--81200ce57413eafde86bb95b1ba47121862043451ba5e55cda9af9573277
Content-Disposition: form-data; name="attachment"; filename="filename.jpg"
Content-Type: application/octet-stream
或者你可以使用这个函数,基于Go源码[2]:
package escape
import "strings"
func escapeQuotes(s string) string {
return strings.NewReplacer(`\`, `\`, `"`, `\"`).Replace(s)
}
HTTP 引号字符串在 RFC 7230:
中定义
quoted-string = DQUOTE *( qdtext / quoted-pair ) DQUOTE
qdtext = HTAB / SP /%x21 / %x23-5B / %x5D-7E / obs-text
obs-text = %x80-FF
quoted-pair = "\" ( HTAB / SP / VCHAR / obs-text )
其中 VCHAR 是任何可见的 ASCII 字符。
根据 RFC 引用以下函数:
// quotedString returns s quoted per quoted-string in RFC 7230.
func quotedString(s string) (string, error) {
var result strings.Builder
result.Grow(len(s) + 2) // optimize for case where no \ are added.
result.WriteByte('"')
for i := 0; i < len(s); i++ {
b := s[i]
if (b < ' ' && b != '\t') || b == 0x7f {
return "", fmt.Errorf("invalid byte %0x", b)
}
if b == '\' || b == '"' {
result.WriteByte('\')
}
result.WriteByte(b)
}
result.WriteByte('"')
return result.String(), nil
}
像这样使用函数:
qf, err := quotedString(f)
if err != nil {
// handle invalid byte in filename f
}
header.Set("Content-Disposition", "attachment; filename=" + qf)
修复无效字节可能比报告错误更方便。清除无效的 UTF8 可能也是个好主意。这是一个执行此操作的引用函数:
// cleanQuotedString returns s quoted per quoted-string in RFC 7230 with invalid
// bytes and invalid UTF8 replaced with _.
func cleanQuotedString(s string) string {
var result strings.Builder
result.Grow(len(s) + 2) // optimize for case where no \ are added.
result.WriteByte('"')
for _, r := range s {
if (r < ' ' && r != '\t') || r == 0x7f || r == 0xfffd {
r = '_'
}
if r == '\' || r == '"' {
result.WriteByte('\')
}
result.WriteRune(r)
}
result.WriteByte('"')
return result.String()
}
如果您知道文件名不包含无效字节,则从mime/multipart package source复制以下代码:
var quoteEscaper = strings.NewReplacer("\", "\\", `"`, "\\"")
func escapeQuotes(s string) string {
return quoteEscaper.Replace(s)
}
标准库代码类似于 中的代码,但标准库代码分配和构建替换器一次,而不是每次调用 escapeQuotes
.
TL;DR:给定一个任意文件名作为 Go string
值,创建指定该文件名的 Content-Disposition
header 字段的最佳方法是什么?
我正在编写 Go net/http 处理程序,我想设置 Content-Disposition
header 字段以指定浏览器在保存文件时应使用的文件名。根据MDN,语法为:
Content-Disposition: attachment; filename="filename.jpg"
和 "filename.jpg"
在 HTTP“quoted-string”中。但是,我在 net/http docs 中没有看到任何提及“quote”的地方。仅提及 HTML 和 URL 转义。
quoted-string 是否与 URL 转义相同或至少兼容?我可以为此使用 url.QueryEscape or url.PathEscape 吗?如果是这样,我应该使用哪一个,或者它们都安全吗? HTTP quoted-string 看起来类似于 URL 转义,但我无法立即找到任何说明它们是否兼容,或者是否有需要担心的边缘情况。
或者,是否有我应该使用的 higher-level 包,它可以处理构建包含此类参数的 HTTP header 字段值的细节?
这有什么问题,只是像这样转义和添加其他 header 值?
w.Header().Add("Content-Disposition", "attachment; filename=\"flename.txt\"")
我认为这只是意味着您应该在文件名周围加上正常的引号。假设你想修复文件名,那么你可以根据 MDN 建议设置 header。
不转义引号是可能的,但前提是文件名不包含空格:
w.Header().Set("Content-Disposition", "attachment; filename=testsomething.txt")
文件名两边的引号允许有空格:
w.Header().Set("Content-Disposition", "attachment; filename=\"test something.txt\"")
改用多行引号 (`)
w.Header().Set("Content-Disposition", `attachment; filename="test something.txt"`)
您需要确保文件名不包含任何可以被 OS 解释的字符,这些字符会破坏文件的路径。例如使用 / 或 \ 可能会导致下载出现一些问题,或者文件名太长。
假设文件名未 end-user 定义,您可能没问题。如果使用用户自由文本,那么您可能希望以某种方式进行限制和验证。
一种方法是使用 multipart
包 [1]:
package main
import (
"mime/multipart"
"strings"
)
func main() {
b := new(strings.Builder)
m := multipart.NewWriter(b)
defer m.Close()
m.CreateFormFile("attachment", "filename.jpg")
print(b.String())
}
结果:
--81200ce57413eafde86bb95b1ba47121862043451ba5e55cda9af9573277
Content-Disposition: form-data; name="attachment"; filename="filename.jpg"
Content-Type: application/octet-stream
或者你可以使用这个函数,基于Go源码[2]:
package escape
import "strings"
func escapeQuotes(s string) string {
return strings.NewReplacer(`\`, `\`, `"`, `\"`).Replace(s)
}
HTTP 引号字符串在 RFC 7230:
中定义 quoted-string = DQUOTE *( qdtext / quoted-pair ) DQUOTE
qdtext = HTAB / SP /%x21 / %x23-5B / %x5D-7E / obs-text
obs-text = %x80-FF
quoted-pair = "\" ( HTAB / SP / VCHAR / obs-text )
其中 VCHAR 是任何可见的 ASCII 字符。
根据 RFC 引用以下函数:
// quotedString returns s quoted per quoted-string in RFC 7230.
func quotedString(s string) (string, error) {
var result strings.Builder
result.Grow(len(s) + 2) // optimize for case where no \ are added.
result.WriteByte('"')
for i := 0; i < len(s); i++ {
b := s[i]
if (b < ' ' && b != '\t') || b == 0x7f {
return "", fmt.Errorf("invalid byte %0x", b)
}
if b == '\' || b == '"' {
result.WriteByte('\')
}
result.WriteByte(b)
}
result.WriteByte('"')
return result.String(), nil
}
像这样使用函数:
qf, err := quotedString(f)
if err != nil {
// handle invalid byte in filename f
}
header.Set("Content-Disposition", "attachment; filename=" + qf)
修复无效字节可能比报告错误更方便。清除无效的 UTF8 可能也是个好主意。这是一个执行此操作的引用函数:
// cleanQuotedString returns s quoted per quoted-string in RFC 7230 with invalid
// bytes and invalid UTF8 replaced with _.
func cleanQuotedString(s string) string {
var result strings.Builder
result.Grow(len(s) + 2) // optimize for case where no \ are added.
result.WriteByte('"')
for _, r := range s {
if (r < ' ' && r != '\t') || r == 0x7f || r == 0xfffd {
r = '_'
}
if r == '\' || r == '"' {
result.WriteByte('\')
}
result.WriteRune(r)
}
result.WriteByte('"')
return result.String()
}
如果您知道文件名不包含无效字节,则从mime/multipart package source复制以下代码:
var quoteEscaper = strings.NewReplacer("\", "\\", `"`, "\\"")
func escapeQuotes(s string) string {
return quoteEscaper.Replace(s)
}
标准库代码类似于 escapeQuotes
.