package proxy
import (
"bytes"
"strings"
"golang.org/x/net/html"
)
// rewriteHTMLBody parses HTML and rewrites absolute URLs in known attributes
// so they include the token prefix. This is needed because many sites (e.g.
// GitHub) use Content-Security-Policy that blocks inline scripts, making the
// JS-injection approach unreliable.
func rewriteHTMLBody(body []byte, prefix string, host string, proto string) []byte {
if prefix == "" {
return body
}
doc, err := html.Parse(bytes.NewReader(body))
if err != nil {
return body
}
rewriteNode(doc, prefix, host, proto)
var buf bytes.Buffer
if err := html.Render(&buf, doc); err != nil {
return body
}
return buf.Bytes()
}
// urlAttrs lists element attributes that contain URLs which should be rewritten.
var urlAttrs = []string{
"href",
"src",
"action",
"poster",
"formaction",
"cite",
"longdesc",
"profile",
"background",
"data-url",
"data-href",
}
func rewriteNode(n *html.Node, prefix string, host string, proto string) {
if n.Type == html.ElementNode {
for i := range n.Attr {
attr := &n.Attr[i]
if isURLAttr(attr.Key) {
attr.Val = rewriteURL(attr.Val, prefix)
// 将 href 的相对路径显式写成绝对路径,避免浏览器根据当前
// 页面协议猜测(防止 http 页面中的链接被解析成 https)。
if host != "" && proto != "" &&
strings.EqualFold(attr.Key, "href") &&
strings.HasPrefix(attr.Val, "/") &&
!strings.HasPrefix(attr.Val, "//") {
attr.Val = proto + "://" + host + attr.Val
}
continue
}
//
if strings.EqualFold(n.Data, "meta") &&
strings.EqualFold(attr.Key, "content") &&
isRefreshMeta(n) {
attr.Val = rewriteRefreshContent(attr.Val, prefix)
}
}
// Rewrite url(...) inside