英文:
Http Over TLS Golang Not Receiving Headers
问题
我从客户端发送了"User-Agent Header",但在服务器端没有收到,但是当我使用浏览器时可以正常工作。
服务器端代码:
package main
import (
"crypto/tls"
"fmt"
"log"
"net/http"
"time"
)
const (
defaultServerDomain = "localhost:443"
certPemPath = "./selfsigned.crt"
privKeyPath = "./selfsigned.key"
)
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/demo/echo", func(w http.ResponseWriter, req *http.Request) {
w.Header().Add("Strict-Transport-Security", "max-age=63072000; includeSubDomains")
w.Write([]byte("This is an example server.\n"))
fmt.Printf("\n\nUserAgent : %s", req.UserAgent())
})
cfg := &tls.Config{
MinVersion: tls.VersionTLS12,
CurvePreferences: []tls.CurveID{tls.CurveP521, tls.CurveP384, tls.CurveP256},
PreferServerCipherSuites: true,
CipherSuites: []uint16{
tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
tls.TLS_RSA_WITH_AES_256_GCM_SHA384,
tls.TLS_RSA_WITH_AES_256_CBC_SHA,
},
}
srv := &http.Server{
ReadHeaderTimeout: 2 * time.Second,
Addr: defaultServerDomain,
Handler: mux,
TLSConfig: cfg,
TLSNextProto: make(map[string]func(*http.Server, *tls.Conn, http.Handler), 0),
}
log.Fatal(srv.ListenAndServeTLS(certPemPath, privKeyPath))
}
客户端代码:
package main
import (
"crypto/tls"
"crypto/x509"
"fmt"
"io"
"log"
"net/http"
"os"
"time"
)
const (
serverDomain = "https://localhost:443/demo/echo"
certPemPath = "./selfsigned.crt"
)
func main() {
cert, err := os.ReadFile(certPemPath)
if err != nil {
log.Fatal(err)
}
certPool := x509.NewCertPool()
if ok := certPool.AppendCertsFromPEM(cert); !ok {
log.Fatalf("unable to parse cert from %s", certPemPath)
}
client := &http.Client{
Timeout: 2 * time.Second,
Transport: &http.Transport{
DisableKeepAlives: false,
TLSClientConfig: &tls.Config{
RootCAs: certPool,
},
},
}
r, err := http.NewRequest("GET", serverDomain, nil)
req, err := client.Do(r)
if err != nil {
log.Fatal(err)
}
req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.7113.93 Safari/537.36")
defer req.Body.Close()
html, err := io.ReadAll(req.Body)
if err != nil {
log.Fatal(err)
}
fmt.Printf("%v\n", req.Status)
fmt.Printf(string(html))
}
期望从服务器端获取的是:
UserAgent : Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:91.0) Gecko/20100101 Firefox/91.0 Waterfox/91.4.2
但实际上我得到的是:
UserAgent : Go-http-client/1.1
但是当我在浏览器中使用Waterfox时,可以正常工作,我得到的是:
UserAgent : Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:91.0) Gecko/20100101 Firefox/91.0 Waterfox/91.4.2
这是代码还是我使用的自签名证书的问题?
英文:
I am sending 'User-Agent Header' from my client but not receiving on server-side but when i use browser it works.
server code :
package main
import (
"crypto/tls"
"fmt"
"log"
"net/http"
"time"
)
const (
defaultServerDomain = "localhost:443"
certPemPath = "./selfsigned.crt"
privKeyPath = "./selfsigned.key"
)
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/demo/echo", func(w http.ResponseWriter, req *http.Request) {
w.Header().Add("Strict-Transport-Security", "max-age=63072000; includeSubDomains")
w.Write([]byte("This is an example server.\n"))
fmt.Printf("\n\nUserAgent : %s", req.UserAgent())
})
cfg := &tls.Config{
MinVersion: tls.VersionTLS12,
CurvePreferences: []tls.CurveID{tls.CurveP521, tls.CurveP384, tls.CurveP256},
PreferServerCipherSuites: true,
CipherSuites: []uint16{
tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
tls.TLS_RSA_WITH_AES_256_GCM_SHA384,
tls.TLS_RSA_WITH_AES_256_CBC_SHA,
},
}
srv := &http.Server{
ReadHeaderTimeout: 2 * time.Second,
Addr: defaultServerDomain,
Handler: mux,
TLSConfig: cfg,
TLSNextProto: make(map[string]func(*http.Server, *tls.Conn, http.Handler), 0),
}
log.Fatal(srv.ListenAndServeTLS(certPemPath, privKeyPath))
}
client code :
package main
import (
"crypto/tls"
"crypto/x509"
"fmt"
"io"
"log"
"net/http"
"os"
"time"
)
const (
serverDomain = "https://localhost:443/demo/echo"
certPemPath = "./selfsigned.crt"
)
func main() {
cert, err := os.ReadFile(certPemPath)
if err != nil {
log.Fatal(err)
}
certPool := x509.NewCertPool()
if ok := certPool.AppendCertsFromPEM(cert); !ok {
log.Fatalf("unable to parse cert from %s", certPemPath)
}
client := &http.Client{
Timeout: 2 * time.Second,
Transport: &http.Transport{
DisableKeepAlives: false,
TLSClientConfig: &tls.Config{
RootCAs: certPool,
},
},
}
r, err := http.NewRequest("GET", serverDomain, nil)
req, err := client.Do(r)
if err != nil {
log.Fatal(err)
}
req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.7113.93 Safari/537.36")
defer req.Body.Close()
html, err := io.ReadAll(req.Body)
if err != nil {
log.Fatal(err)
}
fmt.Printf("%v\n", req.Status)
fmt.Printf(string(html))
}
Suppose to get server-side :
UserAgent : Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:91.0) Gecko/20100101 Firefox/91.0 Waterfox/91.4.2
Instead i get this :
UserAgent : Go-http-client/1.1
But when i use browser in my case waterfox then it works i get
UserAgent : Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:91.0) Gecko/20100101 Firefox/91.0 Waterfox/91.4.2
Is it something to do with code or certificate i am using selfsinged certificate
答案1
得分: 1
我认为问题最有可能出在这里:
r, err := http.NewRequest("GET", serverDomain, nil)
req, err := client.Do(r)
NewRequest
的定义是 func NewRequest(method, url string, body io.Reader) (*Request, error)
,而 Client.Do
的定义是 func (c *Client) Do(req *Request) (*Response, error)
。所以上面的代码可能更容易理解为:
req, err := http.NewRequest("GET", serverDomain, nil)
resp, err := client.Do(req) // 这里返回的是响应,而不是请求
当你调用 Do
方法时,请求会被发送(所以在这之前必须设置好请求头)。根据上面重新表述的代码,当你设置请求头时,应该调用 resp.Header.Set...
(也就是你在修改请求返回的响应头,而不是请求头)。
将你的代码修改为在调用 Do
之前设置请求头,即:
req, err := http.NewRequest("GET", serverDomain, nil)
req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.7113.93 Safari/537.36")
resp, err := client.Do(req)
注意:我没有完整测试过你的代码,所以可能还有其他问题。
英文:
I believe that the issue is most likely here:
r, err := http.NewRequest("GET", serverDomain, nil)
req, err := client.Do(r)
The definition for NewRequest
is func NewRequest(method, url string, body io.Reader) (*Request, error)
and Client.Do
func (c *Client) Do(req *Request) (*Response, error)
. So the code above is probably easier understood as:
req, err := http.NewRequest("GET", serverDomain, nil)
resp, err := client.Do(req) // This returns a response; not the request
When you call Do
the request is made (so the headers must be set before that point). Taking the above rephrasing of your code when you set the headers you would be calling resp.Header.Set...
(i.e. you are changing the headers returned by the request; not the request headers).
Change your code to set the headers before calling Do
i.e.
req, err := http.NewRequest("GET", serverDomain, nil)
req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.7113.93 Safari/537.36")
resp, err := client.Do(req)
Note: I have not tested your code in full so there may be other issues
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论