获取当前请求URL的方案

Eth*_*han 5 url http url-scheme go

在Ruby / Rack中,我可以从获取当前请求URL的方案scheme#request。但是,在Go中,http.Request.URL.Scheme返回一个空字符串:

package main

import (
    "fmt"
    "log"
    "net/http"
)

func main() {
    http.HandleFunc("/", handler)
    log.Fatal(http.ListenAndServe(":8080", nil))
}

func handler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "%#v\n", r.URL.Scheme) // Always shows empty string
}
Run Code Online (Sandbox Code Playgroud)

如何获得当前请求URL的方案?

Isa*_*ten 11

要提供HTTPHTTPS服务,您需要调用两个服务函数
http.ListenAndServe()并使用相同http.ListenAndServeTLS()处理程序,因为如果您仅使用其中 1 个(如问题示例),那么您仅列出http和HTTPShttp.ListenAndServe()的1 个协议,如果您将尝试使用它不会通过的不同协议来联系服务器,http.ListenAndServeTLS()

因为HTTPS基于 TLS 的 HTTP,所以*http.Request有一个TLS属性可以返回*tls.ConnectionState有关此请求中使用的 TLS 的信息,那么如果您想知道客户端如何联系服务器,您可以检查请求TLS属性,如果请求是通过HTTPS发出的,它不会为零,如果请求是通过HTTP发出的,则该TLS属性将为nil,因为使用 TLS 发出请求的唯一方式是使用HTTPS协议

func handler(w http.ResponseWriter, r *http.Request) {
   if r.TLS == nil {
       // the scheme was HTTP
   } else {
       // the scheme was HTTPS
   }                 
}  

func main() {
    http.HandleFunc("/", handler)
    go func(){ 
        log.Fatal(http.ListenAndServeTLS(":8443","localhost.crt", "localhost.key", nil)) 
    }()
    log.Fatal(http.ListenAndServe(":8080", nil))
}                                                   

Run Code Online (Sandbox Code Playgroud)


Mic*_*ton 5

一个快速grep显示,r.URL.Scheme除了net/http. 我个人认为应该尽可能,但显然我有少数意见。


如果您自己打开了一个 TLS 侦听器,http.ListenAndServeTLS()那么您大概知道该方案已经是 https 了。r.URL.Scheme在这种情况下,您可以使用一个简单的中间件处理程序来填充。

func AlwaysHTTPS(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        r.URL.Scheme = "https"

        next.ServeHTTP(w, r)
    })
}
Run Code Online (Sandbox Code Playgroud)

如果您在 Web 服务器后面运行,则它可能会在标头中传递请求协议,例如X-Forwarded-Proto. 在这种情况下,您可以使用像 gorilla's handlers这样的处理程序ProxyHeaders () 来填补缺失的字段。

使用 gorilla mux 的示例:

package main

import (
    "log"
    "net/http"

    "github.com/gorilla/handlers"
    "github.com/gorilla/mux"
)

func main() {
    r := mux.NewRouter()
    r.Use(handlers.ProxyHeaders)

    http.Handle("/", r)
    log.Fatal(http.ListenAndServe("[::]:8009", nil))
}
Run Code Online (Sandbox Code Playgroud)

从它的评论:

ProxyHeaders 检查常见的反向代理标头并在 HTTP 请求结构中设置相应的字段。这些是远程(客户端)IP 地址的 X-Forwarded-For 和 X-Real-IP,方案 (http|https) 的 X-Forwarded-Proto 或 X-Forwarded-Scheme 和 RFC7239 转发标头,其中可能包括客户端 IP 和方案。

注意:只有在 nginx、HAProxy 或 Apache 等反向代理后面时才应使用此中间件。不(或配置为不)从客户端请求中剥离这些标头的反向代理,或者从远程客户端“按原样”接受这些标头的反向代理(例如,当 Go 不在代理后面时),可以表现为一个漏洞如果您的应用程序使用这些标头来验证请求的“可信度”。


Mic*_*los 5

由于您使用ListenAndServeand not ListenAndServeTLS,因此您的方案可以安全地假定为 http。如果您同时使用 tls 和非 tls 版本,可以使用r.TLS并检查它是否为 null 来了解 TLS 是否建立。如果您的 go 应用程序在反向代理后面运行,那么您必须检查将请求转发到您的应用程序的 Web 服务器上的文档,以了解如何配置它以将此信息作为标头传递。这是一个描述nginx 配置的链接,它可以实现这一点。您还可以轻松找到其他网络服务器的配置指南。

更好的是,在主 Web 服务器上配置HSTS,这样您就不必完全担心不安全的连接。非 TLS http 的合法用途(如果有的话)也很少。对于 nginx,您会发现这篇文章很有用。对于其他网络服务器,您也可以轻松找到配置指南。

如果您不确定您的网站/应用程序是否需要 https,我建议您阅读此内容


Bri*_*rij -2

这是因为,您正在访问 HTTP 服务器,因此:

GET / HTTP/1.1
Host: localhost:8080
Run Code Online (Sandbox Code Playgroud)

在这种情况下,基于解析您得到的是来自 Go 的 http.Request.URL 的原始 URL。之所以会出现这种情况,是因为您是从相对路径访问 URL,因此 URL 对象中缺少 Host 或 Scheme。

如果您确实想获取 HTTP 主机,则可能必须访问 http.Request 结构的 Host 属性。请参阅http://golang.org/pkg/http/#Request

因为它不是直接可用的,但您仍然可以组装它:

u := r.URL

// The scheme can be http/https because that's depends on protocol your server handles.
u.Scheme = "http"
Run Code Online (Sandbox Code Playgroud)