报错信息
http: URL query contains semicolon, which is no longer a supported separator; parts of the query may be stripped when parsed; see golang.org/issue/25192
高版本http废除了分号做分隔符,会在http库中做报警输出,基础库代码如下:
func (sh serverHandler) ServeHTTP(rw ResponseWriter, req *Request) { handler := sh.srv.Handler if handler == nil { handler = DefaultServeMux } if req.RequestURI == "*" && req.Method == "OPTIONS" { handler = globalOptionsHandler{} } if req.URL != nil && strings.Contains(req.URL.RawQuery, ";") { var allowQuerySemicolonsInUse int32 req = req.WithContext(context.WithValue(req.Context(), silenceSemWarnContextKey, func() { atomic.StoreInt32(&allowQuerySemicolonsInUse, 1) })) defer func() { if atomic.LoadInt32(&allowQuerySemicolonsInUse) == 0 { sh.srv.logf("http: URL query contains semicolon, which is no longer a supported separator; parts of the query may be stripped when parsed; see golang.org/issue/25192") } }() } handler.ServeHTTP(rw, req) }
解决方法有两种
1、http中不要用分号做分隔符,不要含有分号
2、调用库函数http.AllowQuerySemicolons()允许分号,解除报错
由于项目历史版本缘故,无法完全做到第1点,需要做第2点来补充。如果直接调用http.AllowQuerySemicolons,发现其就是简单粗暴将分号直接替换成&(代码如下),从而可能会引发后续http body解析报错
func AllowQuerySemicolons(h Handler) Handler { return HandlerFunc(func(w ResponseWriter, r *Request) { if silenceSemicolonsWarning, ok := r.Context().Value(silenceSemWarnContextKey).(func()); ok { silenceSemicolonsWarning() } if strings.Contains(r.URL.RawQuery, ";") { r2 := new(Request) *r2 = *r r2.URL = new(url.URL) *r2.URL = *r.URL r2.URL.RawQuery = strings.ReplaceAll(r.URL.RawQuery, ";", "&") h.ServeHTTP(w, r2) } else { h.ServeHTTP(w, r) } }) }
最终解决方案
1、将http内容中的分号替换,并记录位置
2、调用http.AllowQuerySemicolons,解除报警输出
3、将1中被替换的分号还原
4、调用handler处理函数,解析参数,进行业务逻辑
最终代码如下:
package main import ( "context" "fmt" "net/http" "regexp" "strings" "time" "unsafe" ) // BytesToString converts byte slice to string. func BytesToString(b []byte) string { return *(*string)(unsafe.Pointer(&b)) } func MarkQuerySemicolons(h http.Handler) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { if strings.Contains(r.URL.RawQuery, ";") { // 正则匹配 将原url中的';'索引位置标记放到context中 semicolonsIndexes := regexp.MustCompile(";").FindAllStringIndex(r.URL.RawQuery, -1) // 索引信息放到context中 在具体处理方法前再根据context中存的下标还原';'的位置 r = r.WithContext(context.WithValue(r.Context(), "semicolonMarks", semicolonsIndexes)) r.URL.RawQuery = strings.ReplaceAll(r.URL.RawQuery, ";", "&") h.ServeHTTP(w, r) } else { h.ServeHTTP(w, r) } } } func RecoverRawQuerySemicolons(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { marksContextV := r.Context().Value("semicolonMarks") semicolonsIndexes, ok := marksContextV.([][]int) if ok { urlReduceSemicolons := []byte(r.URL.RawQuery) for _, index := range semicolonsIndexes { if len(index) != 0 { urlReduceSemicolons[index[0]] = ';' } } r.URL.RawQuery = BytesToString(urlReduceSemicolons) } next.ServeHTTP(w, r) }) } func HandleRawQuerySemicolons(next http.Handler) http.HandlerFunc { return MarkQuerySemicolons(http.AllowQuerySemicolons(RecoverRawQuerySemicolons(next))) } func Health(w http.ResponseWriter, r *http.Request) { fmt.Println(r.URL.Path, r.URL.RawQuery) w.Write([]byte("ok")) } func main() { http.HandleFunc("/health", HandleRawQuerySemicolons(http.HandlerFunc(Health))) http.HandleFunc("/health2", Health) s := &http.Server{ Addr: ":10099", Handler: nil, ReadTimeout: time.Second, WriteTimeout: time.Second, MaxHeaderBytes: 1 << 20, } if err := s.ListenAndServe(); err != nil { fmt.Println(err.Error()) } }
测试情况
分别请求
Health中输出如下:
/health a=1;b=2 /health2 a=1;b=2 2023/05/16 15:17:18 http: URL query contains semicolon, which is no longer a supported separator; parts of the query may be stripped when parsed; see golang.org/issue/25192
测试OK。直接调用Health,引发了报错信息。用HandleRawQuerySemicolons包装的没有报错,且url内容正常。
参考文档
https://www.cnblogs.com/tinfy/archive/2023/01/13/17049049.html
标签:http,semicolon,RawQuery,URL,req,报错,func,query From: https://www.cnblogs.com/langit/p/17405972.html