新增访问层级限制

更新功能没写成,还是没想好怎么处理整个流程。

摆在面前有两个问题:

  1. 下载下来的新版本(可执行文件)放在哪
  2. 如何关闭自己然后启动新版本

所以暂时搁置,转头去实现访问层级限制功能。

使用中间件

这次把中间件用上了,还把之前对浏览器的默认行为:请求 favicon.ico 进行拦截的部分放在单独的中间件里,并且应用到全局。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
// Start 函数负责启动 gin 实例,开始提供 HTTP 服务
func Start() {
    gin.SetMode(gin.ReleaseMode)
    r := gin.New()
    r.Use(gin.LoggerWithWriter(config.Output),gin.Recovery())
    // 中间件
    r.Use(middleware.InterceptICO)
    r.Use(middleware.CheckLevel)

    r.NoRoute(controller.NotFound)
    r.GET("/*filename", controller.Handler)
    r.POST("/upload", controller.Upload)

    printInfo()

    err := r.Run(":" + config.Port)
    if _, ok := err.(*net.OpError); ok {
        log.Fatal(color.RedString("指定的 %s 端口已被占用,请换一个端口号。", config.Port))
    }
}

拦截浏览器默认请求行为的中间件:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
package middleware

import (
    "net/http"
    "strings"

    "github.com/gin-gonic/gin"
)

// InterceptICO 拦截浏览器默认请求 favicon.ico 的行为
func InterceptICO(c *gin.Context) {
    if strings.HasSuffix(c.Param("filename"), ".ico") {
        c.Status(http.StatusOK)
        c.Abort()
    }
}

gin 的封装真的让 web 开发变得很方便,中间件也让代码更加清晰,耦合度降低。Nice!

新增参数 -x

这次新增一个访问层级限制的功能,使用参数 -x 或者 --max 可以指定。

例如:

用户指定共享目录为:/a/b , 最大层级为 2,则访问者最深可以访问到 /a/b/c/d/a/b/c/d/file

{% checkbox green checked, /a/b %} {% checkbox green checked, /a/b/file %} {% checkbox green checked, /a/b/c %} {% checkbox green checked, /a/b/c/file %} {% checkbox green checked, /a/b/c/d %} {% checkbox green checked, /a/b/c/d/file %} {% checkbox times red checked, /a/b/c/d/e %} {% checkbox times red checked, /a/b/c/d/e/file %}

其实这个功能有点鸡肋,真实场景下限制访问层级可能不常用,只有一个场景会很常见。

即:共享者只是想开放共享目录那一层给人访问,你可以看到这一层有什么子目录和文件,你可以下载这一层的所有文件,但不能深入访问子目录。

所以我把参数的默认值设置为 0。即只能访问共享目录那一层,如果要开放子目录给人访问,就自己设置 -x 参数。

最核心的就是这个函数:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
// 检查当前访问的路径是否超过限定层级
func isOverLevel(relPath string, isFile bool) bool {
    rel, _ := filepath.Rel(config.RootPath, filepath.Join(config.RootPath, relPath))
    i := strings.Split(rel, config.SEPARATORS)
    level := len(i)
    if i[0] == "." {
        level = 0
    }
    if isFile {
        level--
    }
    return level > int(config.MaxLevel)
}

通过 filepath.Rel 把当前访问路径切出来,按照路径分隔符 config.SEPARATORS 切割,判断切割出来的个数是否超过设定的最大访问层级。

如果访问的是共享目录,filepath.Rel 会返回一个单元素切片 ["."],应该把 level 修正为 0 。 如果访问的是文件,会超出 1 级,应该把 level 自减 1 。

例如 filepath.Rel("/a/b", "/a/b/c/file") 会得到 /c/file,切割得到切片为 ["c" "file"],level 为 2 。但这个 file 实际上是 c 目录下的一个文件,应该是与 c 同一层级 1,所以做一次自减来修正。

最后

今晚想了想,这好像是个 FTP 工具,却用 HTTP 来实现,总感觉怪怪的。有时间研究研究 FTP 协议,看看能不能用 FTP 协议实现吧。