Golang URL query contains semicolon 报错解决方案

目录

报错信息

解决方法有两种

最终解决方案

测试情况

参考文档


报错信息

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())
	}
}

测试情况

分别请求

http://127.0.0.1:10099/health?a=1;b=2

http://127.0.0.1:10099/health2?a=1;b=2

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

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值