【问题标题】:Filtering embed.FS causes ERR_TOO_MANY_REDIRECTS on HTTP server过滤 embed.FS 会导致 HTTP 服务器上的 ERR_TOO_MANY_REDIRECTS
【发布时间】:2022-01-12 01:57:07
【问题描述】:

我的应用程序运行一个 HTTP 服务器,它提供一些静态文件。大多数文件都可以在/static/ 下访问,但有些文件(例如index.html)必须在根目录下才能访问。

此代码尝试通过将文件嵌入embed.FS 来实现这一点(为了演示,我只在此处嵌入index.html):

package main

import (
    "net/http"
    "embed"
    "io/fs"
    "log"
)

//go:embed index.html
var files embed.FS

type primaryFiles struct {}

func (pf *primaryFiles) Open(name string) (fs.File, error) {
    // name will be "." for paths / and /index.html, I guess that's a feature
    if name == "." {
        return files.Open("index.html")
    }
    return nil, fs.ErrNotExist
}

func main() {
    http.Handle("/", http.FileServer(http.FS(&primaryFiles{})))
    http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.FS(files))))
    log.Fatal(http.ListenAndServe(":8080", nil))
}

现在在运行代码时,我可以在http://localhost:8080/static/ 和http://localhost:8080/static/index.html 上查询index.html。但是,在http://localhost:8080/ 和http://localhost:8080/index.html,浏览器会给我ERR_TOO_MANY_REDIRECTS。为什么会这样?我该如何解决?

我已经尝试过传递".",它会生成一个文件列表而不是index.html 内容。我在go version go1.17.3 darwin/arm64。我还试图弄清楚curl 发生了什么:

$ curl -v http://localhost:8080/index.html
*   Trying ::1:8080...
* Connected to localhost (::1) port 8080 (#0)
> GET /index.html HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.77.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 301 Moved Permanently
< Location: ./
< Date: Mon, 06 Dec 2021 22:05:50 GMT
< Content-Length: 0
<
* Connection #0 to host localhost left intact

$ curl -v http://localhost:8080/
*   Trying ::1:8080...
* Connected to localhost (::1) port 8080 (#0)
> GET / HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.77.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 301 Moved Permanently
< Location: ..//
< Date: Mon, 06 Dec 2021 22:05:12 GMT
< Content-Length: 0
<
* Connection #0 to host localhost left intact

这并不能帮助我理解正在发生的事情——好吧。 /index.html 被重定向到./,这似乎是有道理的。但是/ 被重定向到..//……我不知道该怎么做。

【问题讨论】:

    标签: http go


    【解决方案1】:

    当给定"." 时,您的primaryFiles.Open 实现返回一个文件而不是一个目录。这是一个错误。

    fs.FS.Open 上的文档应将您引导至 fs.ValidPath,其 godoc 声明如下。

    package fs // 导入“io/fs”

    func ValidPath(name string) bool
    ValidPath 报告给定的路径名​​是否可用于调用 打开。

    传递给 open 的路径名是 UTF-8 编码的、无根的、斜杠分隔的 路径元素序列,如“x/y/z”。路径名称不得包含 元素是“。”或“..”或空字符串,特殊情况除外 根目录被命名为“.”。路径不得以 斜线:“/x”和“x/”无效。

    请注意,路径在所有系统(甚至 Windows)上都是用斜线分隔的。路径 包含其他字符(如反斜杠和冒号)被接受为 有效,但这些字符绝不能被 FS 解释 实现为路径元素分隔符。

    net/http.FileServer 依赖于在../ 上递归重定向应该eventually get to some directory 的事实,但是根据primaryFiles.Open 的工作方式没有找到目录。可以说这是一个增强net/http.FileServer的机会,但还不清楚。

    【讨论】:

    • 但是查询http://localhost:8080/static/即使查询到目录也能返回文件,而且还要经过http.FileServer。这个答案似乎是一个难题,但从你所说的我无法推断出如何正确实现primaryFiles。
    • http://localhost:8080/static/ 工作文件因为embed.FS 正确实现fs.FS。 primaryFiles 用于需要正确实现fs.FS 的地方,而primaryFiles 没有正确实现fs.FS。
    • 那么问题就在于如何正确地实现它。我确实尝试将"." 查询交给embed.FS,但这只会导致服务器以文件列表响应,这不是我想要的,也不是/static/ 发生的事情。
    • 查看 fs.FS 上的 godoc 以及它引用的 godoc。从问题来看,您似乎在寻求解决 ERR_TOO_MANY_REDIRECTS 错误的帮助。看来您现在遇到的麻烦不仅仅是修复它。如果是这种情况,您应该说明您希望发生的事情,而不是您不想发生的事情。
    • 请参阅go.dev/play/p/0QEfTAbtPMA 以获得修复。虽然我真的怀疑http.FileServer 是不是适合你的抽象,因为你正在尝试做。
    【解决方案2】:

    发生的事情部分记录在http package:

    作为一种特殊情况,返回的文件服务器会将任何以“/index.html”结尾的请求重定向到相同的路径,而不是最终的“index.html”。

    所以在我的Open fn 中看不到对index.html 的任何请求,因为它重定向到.。

    文档没有说明对. 的请求似乎按如下方式处理:

    • . 的目录通过Open 查询。这应该会返回一个目录,而我的原始代码没有这样做并导致了错误。
    • 如果返回目录,则搜索文件index.html,如果存在,向Open 发出另一个请求。这是我错过的。

    所以要修复代码,我需要将请求通过管道传送到 . 和 index.html 到实际文件:

    func (pf *primaryFiles) Open(name string) (fs.File, error) {
        if name == "." || name == "index.html" {
            return files.Open(name)
        }
        return nil, fs.ErrNotExist
    }
    

    【讨论】:

      猜你喜欢
      • 2021-10-18
      • 1970-01-01
      • 1970-01-01
      • 2012-06-25
      • 2016-08-12
      • 1970-01-01
      • 2020-12-20
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多