摸魚快報:golang net/http中的雕蟲小技

以後會開一個板塊,摸魚快報,快速記錄這幾周開發中雕蟲小技。

1. 向開發環境localhost:3000種植cookie

前端使用Create React App腳手架,默認以localhost:3000端口啓動;
後端使用golang-gin框架,使用8034端口啓動。
登錄模塊走的是sso,前後端分離,後端需要向前端寫入認證cookie

c.SetSameSite(http.SameSiteLaxMode)
c.SetCookie("userInfo", userInfoMapString, 60*60*12, "/", cfg.CookieDomain, false, false)
c.SetCookie("access_token", accessToken.(string), 60*60*12, "/", cfg.CookieDomain, false, false)

若種植cookie時設置domain=localhost:3000,實際會發現該cookie被種爲domain=localhost

① golang給出日誌提示:2023/01/12 19:10:48 net/http: invalid Cookie.Domain "localhost:3000"; dropping domain attribute, 該cookie domain被丟棄:

② 如果該cookie沒有domain,該Cookie稱之爲HostOnly Cookie,後續請求只有host與cookie的domain完全相等,才能攜帶這個cookie。

react配置後端地址,要配置爲localhost:8034,而不能是127.0.0.1:8034

經此一役:

  • 源(Origin)是由 URL 中協議、主機名(域名 domain)以及端口共同組成的部分
  • 本次出現的問題在於兩個關鍵cookie屬性 :
    • cookie domain: cookie被種植到哪個域名下?
    • cookie samesite: 請求哪些資源domain時能攜帶該cookie?

2. httpclient timeout報錯經驗

golang net/http httpclientTimeout:
Timeout specifies a time limit for requests made by this Client. The timeout includes connection time, any redirects, and reading the response body. The timer remains running after Get, Head, Post, or Do return and will interrupt reading of the Response.Body.

HttpClient Timeout包括連接、重定向(如果有)、從Response Body讀取的時間,內置定時器會在Get,Head、Post、Do 方法之後繼續運行,並有能力中斷讀取Response.Body.


如果upstream服務器處理超時(upstream_response_time> client設置的timeout),則會返回context deadline exceeded (Client.Timeout exceeded while awaiting headers)

如果客戶端使用io.ReadAll讀取body超時,則會返回context deadline exceeded (Client.Timeout or context cancellation while reading body)

3. url 大小寫敏感

大家使用net/http 建立的http server,默認的請求url path是大小寫敏感的:

s.mux.HandleFunc("/leader", func(w http.ResponseWriter, r *http.Request) {

}

s.mux.HandleFunc("/LEADER", func(w http.ResponseWriter, r *http.Request) {

}

以上會被認定爲不同的路由path。
探究源碼:ServeMux使用map[string]muxEntry 哈希表來存儲路由。


這與aspnet core的路由行爲是不一樣的,/hello、/HELLO都會命中下面的路由。

 app.UseEndpoints(endpoints =>
 {  
    endpoints.MapGet("/hello", async context =>
      {
         await context.Response.WriteAsync("Hello!");
      });
}

w3c官方建議: url大小寫敏感

URLs in general are case-sensitive (with the exception of machine names). There may be URLs, or parts of URLs, where case doesn't matter, but identifying these may not be easy. Users should always consider that URLs are case-sensitive。
大意是說: 除了domain主機名是大小寫不敏感,url一般被認爲是大小寫敏感。

stackoverflow有更清晰的描述

The scheme and host are case-insensitive and normally provided in lowercase; all other components are compared in a case-sensitive manner.

4. golang statuscode被作爲header,一直很費解。

在 Go 語言中,客戶端請求信息都封裝到了Request對象,但是發送給客戶端的響應並不是 Response 對象,而是ResponseWriter

func Home(w http.ResponseWriter, r *http.Request)  {
    io.WriteString(w, "Welcome to my blog site")
}

ResponseWriter是處理器用來創建 HTTP 響應的接口,其源碼結構如下所示:

type ResponseWriter interface {
   // 用於設置/獲取所有響應頭信息
	Header() Header
   // 用於寫入數據到響應實體
	Write([]byte) (int, error)
   // 用於設置響應狀態碼
	WriteHeader(statusCode int)
}

WriteHeader這個方法名有點誤導,其實它並不是用來設置響應頭的,該方法支持傳入一個整型數據用來表示響應狀態碼,如果不調用該方法的話,默認響應狀態碼是 200 OK。

在fasthttp中,設置請求謂詞:req.Header.SetMethod("POST"), 這種將謂詞作爲header的行爲,我也是服氣。

只能設置一次statuscode, 若多次設置statuscode,以前者優先。

例如嘗試以如下方式:

http.NotFound(w, r)   # 會調用WriteHeader(404);Write()寫入body
w.WriteHeader(http.StatusInternalServerError)

會產生一個告警:2023/01/06 19:19:43 http: superfluous response.WriteHeader call from main.ProxyHandler (proxy.go:25), 同時產生404狀態碼。

可以採用如下方式清晰定義狀態碼和body

w.WriteHeader(http.StatusInternalServerError)
fmt.Fprintln(w, "404 page not found")

本週摸魚快報,閱讀時間3min;難度星:3(滿星5星);價值度:3(滿分5分)。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章